diff --git a/Building and Running Singularity RDK 1.1.pdf b/Building and Running Singularity RDK 1.1.pdf deleted file mode 100644 index a25dc83..0000000 Binary files a/Building and Running Singularity RDK 1.1.pdf and /dev/null differ diff --git a/Building and Running Singularity RDK 2.0.pdf b/Building and Running Singularity RDK 2.0.pdf new file mode 100644 index 0000000..55be364 Binary files /dev/null and b/Building and Running Singularity RDK 2.0.pdf differ diff --git a/Release notes.pdf b/Release notes.pdf new file mode 100644 index 0000000..1f2c3de Binary files /dev/null and b/Release notes.pdf differ diff --git a/Singularity RDK 1.1 License.pdf b/Singularity RDK 2.0 License.pdf similarity index 100% rename from Singularity RDK 1.1 License.pdf rename to Singularity RDK 2.0 License.pdf diff --git a/base/Applications/AppendFormat/AppendFormat.csproj b/base/Applications/AppendFormat/AppendFormat.csproj index 284eb64..3d42123 100644 --- a/base/Applications/AppendFormat/AppendFormat.csproj +++ b/base/Applications/AppendFormat/AppendFormat.csproj @@ -1,8 +1,6 @@  diff --git a/base/Applications/Benchmarks/BartokH/Child/Child.sg b/base/Applications/Benchmarks/BartokH/Child/Child.sg index d8c4f15..dcad87f 100644 --- a/base/Applications/Benchmarks/BartokH/Child/Child.sg +++ b/base/Applications/Benchmarks/BartokH/Child/Child.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: BartokP.sg -// // Note: Compiler Phase // @@ -95,10 +93,9 @@ namespace Bartok.Child uint region; regionSize = (uint)bytes.Length; unsafe { - fixed (byte *pdst = &bytes[0]) { - region = (uint)pdst; - cs.ReadOpen(region, regionSize); - } + byte *pdst = &bytes[0]; + region = (uint)pdst; + cs.ReadOpen(region, regionSize); } } @@ -119,10 +116,9 @@ namespace Bartok.Child uint region; regionSize = (uint)bytes.Length; unsafe { - fixed (byte *pdst = &bytes[0]) { - region = (uint)pdst; - cs.WriteOpen(region, regionSize); - } + byte *pdst = &bytes[0]; + region = (uint)pdst; + cs.WriteOpen(region, regionSize); } } diff --git a/base/Applications/Benchmarks/BartokH/Contracts/BartokHContracts.csproj b/base/Applications/Benchmarks/BartokH/Contracts/BartokHContracts.csproj index 6f7a166..fa7f3cd 100644 --- a/base/Applications/Benchmarks/BartokH/Contracts/BartokHContracts.csproj +++ b/base/Applications/Benchmarks/BartokH/Contracts/BartokHContracts.csproj @@ -1,5 +1,4 @@  - diff --git a/base/Applications/Benchmarks/BartokH/Contracts/CompilerPhaseContract.sg b/base/Applications/Benchmarks/BartokH/Contracts/CompilerPhaseContract.sg index 4284674..64fc4c6 100644 --- a/base/Applications/Benchmarks/BartokH/Contracts/CompilerPhaseContract.sg +++ b/base/Applications/Benchmarks/BartokH/Contracts/CompilerPhaseContract.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: CompilerPhaseContract.sg -// using Microsoft.Singularity.Channels; using Microsoft.Singularity.Endpoint; diff --git a/base/Applications/Benchmarks/BartokH/Imported.txt b/base/Applications/Benchmarks/BartokH/Imported.txt new file mode 100644 index 0000000..56f3b36 --- /dev/null +++ b/base/Applications/Benchmarks/BartokH/Imported.txt @@ -0,0 +1 @@ + diff --git a/base/Applications/Benchmarks/BartokH/README.TXT b/base/Applications/Benchmarks/BartokH/README.TXT new file mode 100644 index 0000000..5479469 --- /dev/null +++ b/base/Applications/Benchmarks/BartokH/README.TXT @@ -0,0 +1,8 @@ +Hosted Bartok requires the following change to System\RuntimeType.cs: + + public override int GetHashCode() { + return unchecked((int)( + (int)Magic.addressOf(this.classVtable) + + ((int)this.classVtable.arrayOf << 8 + rank) + + (int)this.classVtable.structuralView)); + } diff --git a/base/Applications/Benchmarks/BartokH/host/Bartok.cs b/base/Applications/Benchmarks/BartokH/host/Bartok.cs index 52dc058..ff4fffe 100644 --- a/base/Applications/Benchmarks/BartokH/host/Bartok.cs +++ b/base/Applications/Benchmarks/BartokH/host/Bartok.cs @@ -1,5 +1,5 @@ // -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // #define NONONNULLTYPECHECK // required on Singularity, no affect on other Windows. @@ -422,25 +422,25 @@ namespace Bartok { analysisRegistry)); } - /* - phases.Add - (new DynamicCount - ("VirtualCallsStart", - new DynamicCount.OpcodeFilter - (Operator.OpCodes.CallVirtual, - Operator.OpCodes.InterfaceCall), - DynamicCount.Granularity.PerSite)); - */ + // + //phases.Add + // (new DynamicCount + // ("VirtualCallsStart", + // new DynamicCount.OpcodeFilter + // (Operator.OpCodes.CallVirtual, + // Operator.OpCodes.InterfaceCall), + // DynamicCount.Granularity.PerSite)); + // - /* - phases.Add - (new DynamicCount - ("ASCstart", - new DynamicCount.OpcodeFilter - (Operator.OpCodes.CheckArrayStore, - Operator.OpCodes.CheckVectorStore), - DynamicCount.Granularity.PerSite)); - */ + // + //phases.Add + // (new DynamicCount + // ("ASCstart", + // new DynamicCount.OpcodeFilter + // (Operator.OpCodes.CheckArrayStore, + // Operator.OpCodes.CheckVectorStore), + // DynamicCount.Granularity.PerSite)); + // if (StageControl.PtrAnalysis) { phases.Add(PtrTypeSimpleSystem.CreateAnalysis()); @@ -467,23 +467,23 @@ namespace Bartok { phases.Add(new Convert.ToMir(mask)); } - /* SPOONS: region related phases: - // A temporary pass that cleans up the scratch objects so that the - // DemandAnalysis is not confused by the randomness that the tree - // shaker leaves behind. - phases.Add(new CleanScratch()); - - // Build a "Foo_layout" for each class "Foo"; other setup required - // by DemandAnalysis. - phases.Add(new LayoutBuilder()); - - // Perform first- and last-use analysis; add explicit region - // variables and effects. - phases.Add(new DemandAnalysis()); - - // Print the code - phases.Add(new TypeDataPrettyPhase("After Demand:")); - */ + // SPOONS: region related phases: + //// A temporary pass that cleans up the scratch objects so that the + //// DemandAnalysis is not confused by the randomness that the tree + //// shaker leaves behind. + //phases.Add(new CleanScratch()); +// + //// Build a "Foo_layout" for each class "Foo"; other setup required + //// by DemandAnalysis. + //phases.Add(new LayoutBuilder()); +// + //// Perform first- and last-use analysis; add explicit region + //// variables and effects. + //phases.Add(new DemandAnalysis()); +// + //// Print the code + //phases.Add(new TypeDataPrettyPhase("After Demand:")); + // // Perform first- and last-use analysis; add explicit region // variables and effects. @@ -774,67 +774,67 @@ namespace Bartok { (new Convert.ToMir(Convert.ToMir.Mask.Vararg2))); } - /* - // We do not know where all of the calls will be because lowering can - // introduce calls. We make a best guess here. If we moved all - // call-generating lowering to HIR (via ToMir or equivalent), then - // this could be easily done. We are currently missing calls for the - // following: - // - pinvoke, stubs, etc - // - some arithmetic conversions - // - casts - can't add because not all are calls - // - RC, tryall, atomic - // - others? - phases.Add - (new DynamicCount - ("Calls", - new DynamicCount.OpcodeFilter - (Operator.OpCodes.Call, - Operator.OpCodes.CallIndirect, - Operator.OpCodes.CallVirtual, - Operator.OpCodes.InterfaceCall, - Operator.OpCodes.MonitorEnter, - Operator.OpCodes.MonitorExit, - Operator.OpCodes.IndirectToData, - Operator.OpCodes.CustomGetSize, - Operator.OpCodes.CheckVectorStore, - Operator.OpCodes.CheckVectorElementAddress, - Operator.OpCodes.InitVector, - Operator.OpCodes.CheckArrayStore, - Operator.OpCodes.CheckArrayElementAddress, - Operator.OpCodes.InitArray, - Operator.OpCodes.InitType, - Operator.OpCodes.GetITable, - Operator.OpCodes.NewObject, - Operator.OpCodes.NewVector, - Operator.OpCodes.NewArray), - DynamicCount.Granularity.Global)); - phases.Add(new TypeDataDummyPhase()); - phases.Add - (new DynamicCount - ("VirtualCallsEnd", - new DynamicCount.OpcodeFilter - (Operator.OpCodes.CallVirtual, - Operator.OpCodes.InterfaceCall), - DynamicCount.Granularity.PerSite)); - - phases.Add - (new DynamicCount - ("ASCend", - new DynamicCount.OpcodeFilter - (Operator.OpCodes.CheckArrayStore, - Operator.OpCodes.CheckVectorStore), - DynamicCount.Granularity.PerSite)); - phases.Add(new TypeDataDummyPhase()); - - phases.Add - (new DynamicCount - ("WB", - new DynamicCount.OpcodeFilter - (Operator.OpCodes.LocWriteBarrier), - DynamicCount.Granularity.Global)); - phases.Add(new TypeDataDummyPhase()); - */ + // + //// We do not know where all of the calls will be because lowering can + //// introduce calls. We make a best guess here. If we moved all + //// call-generating lowering to HIR (via ToMir or equivalent), then + //// this could be easily done. We are currently missing calls for the + //// following: + //// - pinvoke, stubs, etc + //// - some arithmetic conversions + //// - casts - can't add because not all are calls + //// - RC, tryall, atomic + //// - others? + //phases.Add + // (new DynamicCount + // ("Calls", + // new DynamicCount.OpcodeFilter + // (Operator.OpCodes.Call, + // Operator.OpCodes.CallIndirect, + // Operator.OpCodes.CallVirtual, + // Operator.OpCodes.InterfaceCall, + // Operator.OpCodes.MonitorEnter, + // Operator.OpCodes.MonitorExit, + // Operator.OpCodes.IndirectToData, + // Operator.OpCodes.CustomGetSize, + // Operator.OpCodes.CheckVectorStore, + // Operator.OpCodes.CheckVectorElementAddress, + // Operator.OpCodes.InitVector, + // Operator.OpCodes.CheckArrayStore, + // Operator.OpCodes.CheckArrayElementAddress, + // Operator.OpCodes.InitArray, + // Operator.OpCodes.InitType, + // Operator.OpCodes.GetITable, + // Operator.OpCodes.NewObject, + // Operator.OpCodes.NewVector, + // Operator.OpCodes.NewArray), + // DynamicCount.Granularity.Global)); + //phases.Add(new TypeDataDummyPhase()); + //phases.Add + // (new DynamicCount + // ("VirtualCallsEnd", + // new DynamicCount.OpcodeFilter + // (Operator.OpCodes.CallVirtual, + // Operator.OpCodes.InterfaceCall), + // DynamicCount.Granularity.PerSite)); +// + //phases.Add + // (new DynamicCount + // ("ASCend", + // new DynamicCount.OpcodeFilter + // (Operator.OpCodes.CheckArrayStore, + // Operator.OpCodes.CheckVectorStore), + // DynamicCount.Granularity.PerSite)); + //phases.Add(new TypeDataDummyPhase()); +// + //phases.Add + // (new DynamicCount + // ("WB", + // new DynamicCount.OpcodeFilter + // (Operator.OpCodes.LocWriteBarrier), + // DynamicCount.Granularity.Global)); + //phases.Add(new TypeDataDummyPhase()); + // if (StageControl.WholeProgram && StageControl.OptRuntimeData) { phases.Add(new RuntimeData()); diff --git a/base/Applications/Benchmarks/BartokH/host/BartokHHost.csproj b/base/Applications/Benchmarks/BartokH/host/BartokHHost.csproj index 00f16d1..93effec 100644 --- a/base/Applications/Benchmarks/BartokH/host/BartokHHost.csproj +++ b/base/Applications/Benchmarks/BartokH/host/BartokHHost.csproj @@ -1,5 +1,4 @@  - diff --git a/base/Applications/Benchmarks/BartokH/host/Proxy.sg b/base/Applications/Benchmarks/BartokH/host/Proxy.sg index a15127a..7f6acb9 100644 --- a/base/Applications/Benchmarks/BartokH/host/Proxy.sg +++ b/base/Applications/Benchmarks/BartokH/host/Proxy.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Proxy.sg -// // Note: Compiler Phase // @@ -57,10 +55,9 @@ namespace Bartok.Regalloc uint region; regionSize = (uint)bytes.Length; unsafe { - fixed (byte *pdst = &bytes[0]) { - region = (uint)pdst; - cs.ReadOpen(region, regionSize); - } + byte *pdst = &bytes[0]; + region = (uint)pdst; + cs.ReadOpen(region, regionSize); } } @@ -81,10 +78,9 @@ namespace Bartok.Regalloc uint region; regionSize = (uint)bytes.Length; unsafe { - fixed (byte *pdst = &bytes[0]) { - region = (uint)pdst; - cs.WriteOpen(region, regionSize); - } + byte *pdst = &bytes[0]; + region = (uint)pdst; + cs.WriteOpen(region, regionSize); } } @@ -131,7 +127,7 @@ namespace Bartok.Regalloc // Start up our child string[] args = new string[3]; - args[0] = "BartokP.x86"; + args[0] = "BartokP"; args[1] = "-where"; args[2] = "!"; Process cproc = new Process(args, (Endpoint * in ExHeap)ep); @@ -388,5 +384,5 @@ namespace Bartok.Regalloc private const string coloringName = "Back End Coloring Coloring"; private static int coloringId; #endif - } /* class GraphColoring */ + } // class GraphColoring } diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.Analysis.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.Analysis.pdb deleted file mode 100644 index 35e4bf3..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.Analysis.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.Backend.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.Backend.pdb deleted file mode 100644 index 27f1b84..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.Backend.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.CfgUtil.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.CfgUtil.pdb deleted file mode 100644 index 80d2018..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.CfgUtil.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.Cheap.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.Cheap.pdb deleted file mode 100644 index 48f438a..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.Cheap.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.Coff.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.Coff.pdb deleted file mode 100644 index fe11439..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.Coff.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.Convert.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.Convert.pdb deleted file mode 100644 index 994861c..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.Convert.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.Datatype.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.Datatype.pdb deleted file mode 100644 index 02d8276..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.Datatype.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.DebugInfo.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.DebugInfo.pdb deleted file mode 100644 index b79fc31..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.DebugInfo.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.Encode.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.Encode.pdb deleted file mode 100644 index cba2032..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.Encode.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.Ir.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.Ir.pdb deleted file mode 100644 index 15507f2..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.Ir.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.Lir.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.Lir.pdb deleted file mode 100644 index b49d630..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.Lir.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.MSIL.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.MSIL.pdb deleted file mode 100644 index 727f8a4..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.MSIL.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.Mangle.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.Mangle.pdb deleted file mode 100644 index 55c9193..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.Mangle.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.Marshal.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.Marshal.pdb deleted file mode 100644 index 412b8ea..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.Marshal.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.Opt.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.Opt.pdb deleted file mode 100644 index 578ae1f..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.Opt.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.Profile.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.Profile.pdb deleted file mode 100644 index 07f6d0b..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.Profile.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.Proxy.dll b/base/Applications/Benchmarks/BartokH/msil/Bartok.Proxy.dll new file mode 100644 index 0000000..c08c4c7 Binary files /dev/null and b/base/Applications/Benchmarks/BartokH/msil/Bartok.Proxy.dll differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.Proxy.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.Proxy.pdb deleted file mode 100644 index 21432fc..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.Proxy.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.Regalloc.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.Regalloc.pdb deleted file mode 100644 index 4c6a2d3..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.Regalloc.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.Regreal.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.Regreal.pdb deleted file mode 100644 index b56f699..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.Regreal.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.Regstub.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.Regstub.pdb deleted file mode 100644 index 12cd1eb..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.Regstub.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.Tables.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.Tables.pdb deleted file mode 100644 index 64589d1..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.Tables.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/BartokH/msil/Bartok.Utility.pdb b/base/Applications/Benchmarks/BartokH/msil/Bartok.Utility.pdb deleted file mode 100644 index 6a4dfa6..0000000 Binary files a/base/Applications/Benchmarks/BartokH/msil/Bartok.Utility.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/Benchmarks.proj b/base/Applications/Benchmarks/Benchmarks.proj index d3e437e..75856b8 100644 --- a/base/Applications/Benchmarks/Benchmarks.proj +++ b/base/Applications/Benchmarks/Benchmarks.proj @@ -1,3 +1,11 @@ + + diff --git a/base/Applications/Benchmarks/CreateProcess/CreateProcess.cs b/base/Applications/Benchmarks/CreateProcess/CreateProcess.cs index 545647c..67004cc 100644 --- a/base/Applications/Benchmarks/CreateProcess/CreateProcess.cs +++ b/base/Applications/Benchmarks/CreateProcess/CreateProcess.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: CreateProcess.cs -// // Note: Singularity micro-benchmark program. // using Microsoft.Singularity; @@ -175,7 +173,7 @@ namespace Microsoft.Singularity.Applications public void Start() { if (!AtRing3) { - disabled = Processor.DisableInterrupts(); + disabled = Processor.DisableLocalPreemption(); } int collectorCount; @@ -218,7 +216,8 @@ namespace Microsoft.Singularity.Applications Reset(1, PerfEvtSel.COUNT | PerfEvtSel.RetiredInstructions); Reset(2, PerfEvtSel.COUNT | PerfEvtSel.RetiredBranchInstructions); Reset(3, PerfEvtSel.COUNT | PerfEvtSel.RequestsToL2Cache | 0x400); - } else { + } + else { // We're not allowed to reset the perf counters, so take note // of their current values; we will subtract from this later. x64_i0 = Processor.ReadPmc(0); @@ -275,7 +274,7 @@ namespace Microsoft.Singularity.Applications endStackRets = stackRets; if (!AtRing3) { - Processor.RestoreInterrupts(disabled); + Processor.RestoreLocalPreemption(disabled); } this.iterations = iterations; @@ -327,7 +326,8 @@ namespace Microsoft.Singularity.Applications EvtSelToString(e1), EvtSelToString(e2), EvtSelToString(e3))); - } else { + } + else { // Subtract from the initial perf-counter values to // get the delta we want x64_p0 -= x64_i0; @@ -353,7 +353,7 @@ namespace Microsoft.Singularity.Applications string [] arguments; for (long i = 0; i < repetitions; i++) { arguments = new string[2]; - arguments[0] = "testpe.x86"; + arguments[0] = "testpe"; arguments[1] = "!"; // Special flag to not notify debugger. TimeProcess(arguments, runQuiet); } diff --git a/base/Applications/Benchmarks/CreateProcess/CreateProcess.csproj b/base/Applications/Benchmarks/CreateProcess/CreateProcess.csproj index d1d0b64..15ea5f9 100644 --- a/base/Applications/Benchmarks/CreateProcess/CreateProcess.csproj +++ b/base/Applications/Benchmarks/CreateProcess/CreateProcess.csproj @@ -1,8 +1,6 @@  + + + + + + Exe + schedbench + true + + + + + + + + + + + + diff --git a/base/Applications/Benchmarks/SchedBench/SchedBench.sg b/base/Applications/Benchmarks/SchedBench/SchedBench.sg new file mode 100644 index 0000000..aee92ee --- /dev/null +++ b/base/Applications/Benchmarks/SchedBench/SchedBench.sg @@ -0,0 +1,1072 @@ +//------------------------------------------------------------------------------ +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Description: $filename$ +// SchedBench is a benchmark based on SingBench that measures the CPU scheduler +// performance. +//------------------------------------------------------------------------------ + +using System; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Diagnostics.Contracts; +using Microsoft.Singularity.Endpoint; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.V1.Services; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Configuration; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.Contracts; +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.Applications; + +[assembly: Transform(typeof(ApplicationResourceTransform))] + +namespace Microsoft.Singularity.Applications.SchedBench +{ + /// Application wrapper class and argument options + [ConsoleCategory(HelpMessage="Singularity Scheduling Benchmark Application", DefaultAction=true)] + internal sealed class Parameters + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [Endpoint] + public readonly TRef memoryRef; + + /// When true break in debugger before running benchmark + [BoolParameter( "b", Default=false , HelpMessage="Break at start of tests.")] + internal bool breakIn; + + /// When set no GC during benchmarks + [BoolParameter( "n", Default=true , HelpMessage="No GC between tests.")] + internal bool allowGC; + + /// Wait for a key between benchmarks + [BoolParameter( "w", Default=false , HelpMessage="Wait for key press between tests.")] + internal bool pauseForKeys; + + /// Produce XML output + [BoolParameter( "x", Default=false , HelpMessage="XML output.")] + internal bool xmlOutput; + + /// Iterations per benchmark (when applicable) + [LongParameter( "i", Default=10000 , HelpMessage="Iterate tests times.")] + internal long iterations; + + reflective internal Parameters(); + + internal int AppMain() { + return SchedBench.AppMain(this); + } + } + + /// CPU Scheduler Benchmark + public class SchedBench + { + /// + /// + /// Display on the console and in the debugger + /// + /// Message displayed + /// + private static void DualWriteLine(string message) + { + Console.WriteLine(message); + DebugStub.WriteLine(message); + } + + // ---------------------------------------------------------------------------------------- + // Yield Tests + // ---------------------------------------------------------------------------------------- + + /// + /// + /// Thread Yield Test - Create threads that each yields a + /// predefined number of times. Report yields per tick. + /// + /// Number of threads yielding + /// Number of yield operations + /// + public static void DoYieldPerf(uint numThreads, uint iterations) + { + Thread[] yieldThreads = new Thread[numThreads]; + + // Set worker thread iteration count + workerIterations = iterations; + + // Initialize sync signal before starting threads + syncStart.Reset(); + + // Create and start yield worker threads + for( uint i = 0; i < numThreads; i++ ) { + yieldThreads[i] = new Thread(YieldThread); + ((!)yieldThreads[i]).Start(); + } + + // Start performance snap + PerfSnap snap = new PerfSnap(); + + snap.Start(); + + // Synchnously start threads by signalling + syncStart.Set(); + + // Wait for threads to complete + for( uint i = 0; i < numThreads; i++ ) { + ((!)yieldThreads[i]).Join(); + } + + snap.Finish(iterations); + + snap.Display("Yield " + numThreads.ToString()); + DualWriteLine(String.Format("Yield {0}:Ticks per yield: {1,2:F}", + numThreads.ToString(), + (float)snap.ElapsedTicks / + (float)(iterations*numThreads))); + } + + /// + /// + /// YieldThread - Yield a number of times and synchnoize with + /// main thread + /// + /// Number of yield operations + /// + public static void YieldThread() + { + // Wait for signal from master thread to start + syncStart.WaitOne(); + + for (uint i = 0; i < workerIterations; i++) { + Thread.Yield(); + } + } + + // ---------------------------------------------------------------------------------------- + // Wait/Set Tests + // ---------------------------------------------------------------------------------------- + + /// + /// + /// Wait / Set Benchmarks - Create pairs of threads that wait and set events + /// + /// Number of threads + /// Number of wait/set operations + /// + public static void DoWaitPerf(uint numThreads, uint iterations) + { + // Number of ping-pong pairs + uint pairs = numThreads / 2; + + // Allocate AutoResetEvents + waitPerfEvent1 = new AutoResetEvent[pairs]; + waitPerfEvent2 = new AutoResetEvent[pairs]; + + for(uint i = 0; i < pairs; i++) { + waitPerfEvent1[i] = new AutoResetEvent(false); + waitPerfEvent2[i] = new AutoResetEvent(false); + } + + // Create threads + Thread[] pingThreads = new Thread[pairs]; + Thread[] pongThreads = new Thread[pairs]; + + // Set worker thread iteration count + workerIterations = iterations; + + // Initialize sync signal before starting threads + syncStart.Reset(); + syncContext.Reset(); + + // Create and start yield worker threads + for( uint i = 0; i < pairs; i++ ) { + // Use curWorkerThread to set the context of worker thread + curWorkerThread = i; + + // Begin ping thread + pingThreads[i] = new Thread(PingThread); + ((!)pingThreads[i]).Start(); + + // Wait for ping thread to notify that has acquired context + syncContext.WaitOne(); + syncContext.Reset(); + + // Begin pong thread + pongThreads[i] = new Thread(PongThread); + ((!)pongThreads[i]).Start(); + + // Wait for pong thread to notify that has acquired context + syncContext.WaitOne(); + syncContext.Reset(); + } + + // Start performance snap + PerfSnap snap = new PerfSnap(); + + snap.Start(); + + // Synchnously start threads by signalling + syncStart.Set(); + + // Wait for threads to complete + for( uint i = 0; i < pairs; i++ ) { + ((!)pingThreads[i]).Join(); + ((!)pongThreads[i]).Join(); + } + + snap.Finish(iterations); + + snap.Display("Wait " + numThreads.ToString()); + DualWriteLine(String.Format("Wait {0}:Ticks per wait: {1,2:F}", + numThreads.ToString(), + (float)snap.ElapsedTicks / + (float)(iterations*numThreads))); + } + + /// + /// + /// Perform a ping operation in the wait handle (Set-Wait) + /// + /// + private static void PingThread() + { + // Acquire context and notify master thread + uint myIdx = curWorkerThread; + syncContext.Set(); + + // Wait for signal from master thread to start + syncStart.WaitOne(); + + for (uint i = 0; i < workerIterations; i++) { + // Set event (ping) + ((!)waitPerfEvent1[myIdx]).Set(); + // Wait event (pong) + ((!)waitPerfEvent2[myIdx]).WaitOne(); + } + } + + /// + /// + /// Perform a pong operation in the wait handle (Wait-Set) + /// + /// + private static void PongThread() + { + // Acquire context and notify master thread + uint myIdx = curWorkerThread; + syncContext.Set(); + + // Wait for signal from master thread to start + syncStart.WaitOne(); + + for (uint i = 0; i < workerIterations; i++) { + // Wait event (pong) + ((!)waitPerfEvent1[myIdx]).WaitOne(); + // Set event (ping) + ((!)waitPerfEvent2[myIdx]).Set(); + } + } + + + // ---------------------------------------------------------------------------------------- + // WaitAny Benchmark + // ---------------------------------------------------------------------------------------- + + /// + /// + /// WaitAny benchmark - Main thread waits for any of the slave thread to set event + /// Measures average time to receive signal. + /// + /// Number of wait/set operations + /// + public static void DoWaitAnyPerf(uint numThreads,uint iterations) + { + + // Allocate AutoResetEvents + waitPerfEvent1 = new AutoResetEvent[numThreads]; + + for(uint i = 0; i < numThreads; i++) { + waitPerfEvent1[i] = new AutoResetEvent(false); + } + + // Create slave threads + Thread[] setEventThread = new Thread[numThreads]; + + // Set worker thread iteration count + workerIterations = iterations; + + // Set operation flag array for worker threads + opFlag = new bool[numThreads]; + + // Initialize barrier + loopBarrier = new SyncBarrier(numThreads); + + // Create and start worker threads + for( uint i = 0; i < numThreads; i++ ) { + // Use curWorkerThread to set the context of worker thread + curWorkerThread = i; + + // Begin SetEvent thread + setEventThread[i] = new Thread(WaitAnyThread); + ((!)setEventThread[i]).Start(); + + // Wait for SetEvent thread to notify that has acquired context + syncContext.WaitOne(); + syncContext.Reset(); + } + + // Start performance snap + PerfSnap snap = new PerfSnap(); + + // Random generator used to pick threads to set event + // Use fix seed to replicate same sequence + Random rnd = new Random(0); + + // Aggregate time for operation + long opTimer = 0; + + snap.Start(); + + // Run the benchmark iteration + for( uint i = 0; i < iterations; i++ ) { + // Number of events to be set + int nevent = 0; + + // Pick random threads to set events + for( uint j = 0; j < numThreads; j++ ) { + // 50% chance to pick thread to set event + opFlag[j] = rnd.NextDouble() < 0.5 ? true : false; + if( opFlag[j] == true ) { + nevent++; + } + } + + // if no events picked (very unlikely) + if( nevent == 0 ) { + // Select one thread randomly + opFlag[rnd.Next(0,(int)numThreads)] = true; + } + + // Join barrier + loopBarrier.Join(); + + // Start timer + long begTicks = DateTime.Now.Ticks; + + WaitHandle.WaitAny(waitPerfEvent1); + + // Start timer + opTimer += DateTime.Now.Ticks - begTicks; + + loopBarrier.Signal(); + } + + snap.Finish(iterations); + + snap.Display("WaitAny " + numThreads.ToString()); + DualWriteLine(String.Format("WaitAny {0}:Ticks per WaitAny: {1,2:F}", + numThreads.ToString(), + (float)opTimer/ (float)iterations)); + } + + /// + /// + /// Set event to contribute in WaitAny + /// + /// + private static void WaitAnyThread() + { + // Acquire context and notify master thread + uint myIdx = curWorkerThread; + syncContext.Set(); + + for (uint i = 0; i < workerIterations; i++) { + // Increase the barrier (completed worker threads) counter + loopBarrier.JoinWorker(); + + // If thread selected to perform set + if( opFlag[myIdx] ) { + // Set event (ping) + ((!)waitPerfEvent1[myIdx]).Set(); + } + + // Wait for randevouz when everybody is done + loopBarrier.Wait(); + + } + } + + // ---------------------------------------------------------------------------------------- + // Context Switch Performance + // ---------------------------------------------------------------------------------------- + + /// + /// + /// Calculate the cost of context switches + /// + /// Number of threads yielding + /// Number of yield operations + /// + public static void DoContextSwitchPerf(uint numThreads, uint iterations) + { + Thread[] workerThreads = new Thread[numThreads]; + + // Set worker thread iteration count + workerIterations = iterations; + + // Initialize sync signal before starting threads + syncStart.Reset(); + syncContext.Reset(); + + // Calculate the minimum mandelbrot time - use fix point (0.0,0.0) + long mbrotTicks = MandelbrotTicks(0.0,0.0,iterations); + + // Create and start worker threads + for( uint i = 0; i < numThreads; i++ ) { + // Initialize wallClock + wallClock[i] = 0; + + // Use curWorkerThread to set the context of worker thread + curWorkerThread = i; + + // Begin worket thread + workerThreads[i] = new Thread(ContextThread); + ((!)workerThreads[i]).Start(); + + // Wait for ping thread to notify that has acquired context + syncContext.WaitOne(); + syncContext.Reset(); + } + + // Start performance snap + PerfSnap snap = new PerfSnap(); + + snap.Start(); + + // Synchonously start threads by signalling + syncStart.Set(); + + // Wait for threads to complete + for( uint i = 0; i < numThreads; i++ ) { + ((!)workerThreads[i]).Join(); + } + + snap.Finish(iterations); + + snap.Display("Context " + numThreads.ToString()); + + // Calculate context overhead + float contextOverhead = 0; + + for( uint i = 0; i < numThreads; i++ ) { + contextOverhead += (float)wallClock[i] / (float)iterations; + } + + contextOverhead = (float)contextOverhead / numThreads - mbrotTicks; + + if( contextOverhead < 0.0 ) { + // It is possible that the context overhead is not valid especially in hosted + // execution environments such as Win32. This might occur if during the + // calculation of the mbrot baseline computation (single thread) an execution + // environment process context switch causes S to stall mbrot operation + DualWriteLine(String.Format("Context {0}:Context overhead: Invalid", + numThreads.ToString())); + } else { + DualWriteLine(String.Format("Context {0}:Context overhead: {1,2:F}", + numThreads.ToString(), contextOverhead)); + } + + // Calculate fairness variation + long min = 0; + long max = 0; + float avg = 0.0F; + float stddev = 0.0F; + + CalcTimeStats(wallClock, numThreads, iterations, out min, out max, out avg, out stddev); + + DualWriteLine(String.Format("Context {0}:Operations per cycle: {1,4:F}", + numThreads.ToString(), + (float)(iterations*numThreads) / snap.ElapsedTicks)); + DualWriteLine(String.Format("Context {0}:Fairness min:{1}", + numThreads.ToString(), min)); + DualWriteLine(String.Format("Context {0}:Fairness max:{1}", + numThreads.ToString(), max)); + DualWriteLine(String.Format("Context {0}:Fairness avg:{1,2:F}", + numThreads.ToString(), avg)); + DualWriteLine(String.Format("Context {0}:Fairness stddev:{1,2:F}", + numThreads.ToString(), stddev)); + } + + + /// + /// + /// Thread that calculate a mandelbrot pixels + /// + /// Number of yield operations + /// + public static void ContextThread() + { + // Acquire context and notify master thread + uint myIdx = curWorkerThread; + syncContext.Set(); + + // Wait for signal from master thread to start + syncStart.WaitOne(); + + // Start timer + long begTicks = DateTime.Now.Ticks; + + for (uint i = 0; i < workerIterations; i++) { + // Calc point - use fix point + ComputePoint(0.0,0.0); + } + + // Add to wall clock + wallClock[myIdx] = DateTime.Now.Ticks - begTicks; + } + + // ---------------------------------------------------------------------------------------- + // Latency Benchmark + // ---------------------------------------------------------------------------------------- + + /// + /// + /// Multiple threads a series of mbrot calculations / Sleeps. The sleep random interval. + /// The latency of the operation is measured. + /// + /// Number of threads yielding + /// Number of operations + /// + public static void DoLatencyPerf(uint numThreads, uint iterations) + { + Thread[] workerThreads = new Thread[numThreads]; + + // Set worker thread iteration count + workerIterations = iterations; + + // Initialize sync signal before starting threads + syncStart.Reset(); + syncContext.Reset(); + + // Create and start worker threads + for( uint i = 0; i < numThreads; i++ ) { + // Initialize wallClock, min and max latencies + wallClock[i] = 0; + + // Use curWorkerThread to set the context of worker thread + curWorkerThread = i; + + // Begin worket thread + workerThreads[i] = new Thread(LatencyThread); + ((!)workerThreads[i]).Start(); + + // Wait for ping thread to notify that has acquired context + syncContext.WaitOne(); + syncContext.Reset(); + } + + // Start performance snap + PerfSnap snap = new PerfSnap(); + + snap.Start(); + + // Synchonously start threads by signalling + syncStart.Set(); + + // Wait for threads to complete + for( uint i = 0; i < numThreads; i++ ) { + ((!)workerThreads[i]).Join(); + } + + snap.Finish(iterations); + + snap.Display("Latency " + numThreads.ToString()); + + // Calculate latency statistics + long min = 0; + long max = 0; + float avg = 0.0F; + float stddev = 0.0F; + + CalcTimeStats(wallClock, numThreads, iterations, out min, out max, out avg, out stddev); + + DualWriteLine(String.Format("Latency {0}:min:{1}", + numThreads.ToString(), min)); + DualWriteLine(String.Format("Latency {0}:max:{1}", + numThreads.ToString(), max)); + DualWriteLine(String.Format("Latency {0}:mean:{1,2:F}", + numThreads.ToString(), avg)); + DualWriteLine(String.Format("Latency {0}:stddev:{1,2:F}", + numThreads.ToString(), stddev)); + } + + /// + /// + /// Thread that calculate a mandelbrot pixels and then sleeps random interval + /// + /// Number of yield operations + /// + public static void LatencyThread() + { + // Max sleep time interval + const uint MaxSleep = 5; + + // Random generator used for a random interval + Random rnd = new Random(0); + + // Acquire context and notify master thread + uint myIdx = curWorkerThread; + syncContext.Set(); + + // Wait for signal from master thread to start + syncStart.WaitOne(); + + for (uint i = 0; i < workerIterations; i++) { + // Start timer + long begTicks = DateTime.Now.Ticks; + + // Calc point - use fix point + ComputePoint(0.0,0.0); + + // Store timers + long computeTime = DateTime.Now.Ticks - begTicks; + wallClock[myIdx] += computeTime; + + // Sleep for random period + Thread.Sleep(rnd.Next(0,MaxSleep)); + } + + } + + // ---------------------------------------------------------------------------------------- + // Mandelbrot computation task and helper functions + // ---------------------------------------------------------------------------------------- + + /// + /// + /// Return the time required to calculate a mandelbrot pixel + /// Run multiple times and return minimum time trying to exclude + /// context switching time. + /// + /// X coordinate + /// Y coordinate + /// Number of iterations + /// + private static long MandelbrotTicks(double x, double y, uint iterations) + { + long ticks = 0; // Min ticks for mandelbrot point calculation + + // Calc the same pixel many times + for( uint i = 0; i < iterations; i++ ) { + //Start timer + long begTicks = DateTime.Now.Ticks; + + // Disable interrupts only mandelbrot is running + bool saved = Processor.DisableLocalPreemption(); + + // Calc point + ComputePoint(x,y); + + Processor.RestoreLocalPreemption(saved); + + //Stop timer + long endTicks = DateTime.Now.Ticks; + + // Save value of minimum + ticks = i == 0 || ticks > endTicks - begTicks ? + endTicks - begTicks : ticks; + + } + + return ticks; + } + + /// + /// + /// Calculate a single mandelbrot point + /// + /// X coordinate + /// Y coordinate + /// + static private int ComputePoint(double x, double y) + { + const int maxIterations = 128; // Maximum number of iterations for mandelbrot loop + const int convergence = 4; // Convergence value + int nIterations = 0; // Result of calculation + + // Temp vars for mandelbrot calculation + double x1 = 0; + double y1 = 0; + double xx; + + // Mandelbrot magic + while (nIterations < maxIterations && ((x1 * x1) + (y1 * y1)) < convergence) { + nIterations++; + xx = (x1 * x1) - (y1 * y1) + x; + y1 = 2 * x1 * y1 + y; + x1 = xx; + } + + return nIterations; + } + + // ---------------------------------------------------------------------------------------- + // Helper functions + // ---------------------------------------------------------------------------------------- + + /// + /// + /// Calculate statistics on wall clock times + /// + /// Wall clock times + /// Number of worker threads (ignore other array entries) + /// Experiment iterations per thread + /// Min value (out) + /// Max value (out) + /// Mean value (out) + /// Standard deviation (out) + /// + private static void CalcTimeStats(long[] wallClock, + uint numThreads, + uint iterations, + out long min, + out long max, + out float avg, + out float stddev) + { + min = 0; + max = 0; + avg = 0; + stddev = 0; + + for( uint i = 0; i < numThreads; i++ ) { + // Calc time per iteration + long clockPerIter= ((!)wallClock)[i] / iterations; + + // Find min / max / avg + min = i == 0 ? clockPerIter : + min > clockPerIter ? clockPerIter : + min; + max = max < clockPerIter ? clockPerIter : max; + avg += clockPerIter; + } + + avg /= numThreads; + + // Standard deviation + for( uint i = 0; i < numThreads; i++ ) { + long clockPerIter= ((!)wallClock)[i] / iterations; + + stddev += (float)Math.Pow((float)clockPerIter - avg,2.0); + } + + stddev = (float)Math.Sqrt(stddev / ((float)numThreads - 1.0) ); + } + + /// Write log trace profile in console + public static int GetTrace() + { +#if false + Tracing.LogEntry * lstart; + Tracing.LogEntry * llimit; + Tracing.LogEntry ** lhead; + byte * tstart; + byte * tlimit; + byte ** thead; + + Tracing.GetTracingHeaders(out lstart, out llimit, out lhead, + out tstart, out tlimit, out thead); + Console.WriteLine("Log: {0:X} {1:X}, {2:X}, {3:X}", + (UIntPtr)lstart, (UIntPtr)llimit, (UIntPtr)lhead, (UIntPtr)(*lhead)); + Console.WriteLine("Text: {0:X} {1:X}, {2:X}, {3:X}", + (UIntPtr)tstart, (UIntPtr)tlimit, (UIntPtr)thead, (UIntPtr)(*thead)); +#endif + return 0; + } + + /// Clean up after each benchmark + public static void ClearEnvironment(MemoryContract.Imp:ReadyState! imp) + { + if (allowGC) { + GC.Collect(); + } + } + + /// Pause between benchmarks + public static void Pause() + { + if (pauseForKeys) { + Console.WriteLine("Press any key to continue."); + Console.Read(); + } + } + + internal static int AppMain(Parameters! config) + { + + // Command-line parsing options + int iterations = (int) config.iterations; + bool xmlOutput = config.xmlOutput; + allowGC = config.allowGC; + pauseForKeys = config.pauseForKeys; + breakIn = config.breakIn; + + if( breakIn ) { + DebugStub.Break(); + } + + // Display CPU privilidge level and set flag + if (Processor.AtKernelPrivilege()) { + atRing3 = false; + DualWriteLine("Schedbench running at KERNEL privilege"); + } else { + atRing3 = true; + DualWriteLine("Schedbench running at USER privilege"); + } + + // Allocate clock arrays for each worker thread + wallClock = new long[threadCountTests[threadCountTests.Length-1]]; + + // Connect to memory channel + MemoryContract.Imp impMem = config.memoryRef.Acquire(); + if (impMem == null) { + throw new ApplicationException("Error: Unable to bind to " + + MemoryContract.ModuleName); + } + + impMem.RecvReady(); + + // Set options for performance snap module + PerfSnap.SetOptions(atRing3, xmlOutput); + + GetTrace(); + + Console.WriteLine("Benchmarks: entered"); + + try { + + // Run yield benchmark + foreach( uint threads in threadCountTests ) { + ClearEnvironment(impMem); + DoYieldPerf(threads,(uint)iterations); + Pause(); + } + + // Run wait-set benchmark + foreach( uint threads in threadCountTests ) { + ClearEnvironment(impMem); + DoWaitPerf(threads, (uint)iterations); + Pause(); + } + + // Run wait-set benchmark + foreach( uint threads in threadCountTests ) { + ClearEnvironment(impMem); + DoWaitAnyPerf(threads, (uint)iterations); + Pause(); + } + + // Run context switch benchmark + foreach( uint threads in threadCountTests ) { + ClearEnvironment(impMem); + DoContextSwitchPerf(threads, (uint)iterations); + Pause(); + } + + // Run latency benchmark + foreach( uint threads in threadCountTests ) { + ClearEnvironment(impMem); + // Because latency test is slow run 1/10th of iterations + DoLatencyPerf(threads, (uint)iterations / 10 ); + Pause(); + } + + } catch (Exception e) { + Console.WriteLine("Caught {0}", e.Message); + delete impMem; + return 1; + } + + delete impMem; + + return 0; + + } // AppMain + + /// + /// Experiments are repeated with the numbers of threads specified in the array + /// + private static readonly uint[] threadCountTests = {25, 50, 100 }; + + /// Signal for notification that worker thread acquired context + private static ManualResetEvent syncContext = new ManualResetEvent(false); + + /// Signal for synchonous start of working threads + private static ManualResetEvent syncStart = new ManualResetEvent(false); + + /// Signal for synchonous start of working threads + private static SyncBarrier loopBarrier; + + /// Iterations of an operation for worker threads + private static uint workerIterations; + + /// + /// Thread id is used to communicate to worker therads its current id. + /// This is necessary because Thread.Start(object) is not supported + /// + private static uint curWorkerThread; + + /// Wall clock for each worker thread + private static long[] wallClock; + + /// True when running in Ring 3 processor mode + private static bool atRing3; + + /// Wait events used in Wait/Set benchmarks + private static AutoResetEvent[] waitPerfEvent1; + + /// Wait events used in Wait/Set benchmarks + private static AutoResetEvent[] waitPerfEvent2; + + /// + /// When opFlag[idx] is set to true, Thread idx performs an operation (e.g. Event.Set) + /// + private static bool[] opFlag; + + private static bool allowGC = false; + private static bool pauseForKeys = false; + private static bool breakIn = false; + + } //class SchedBench + + /// + /// SyncBarrier + /// A master thread runs a loop and assigns work to worker threads. + /// All of the slave threads need to synchnonize at the end of the loop + /// before proceeding to the next iteration. + /// + /// Usage pattern for master thread: + /// - FixedSyncBarrier barrier = new FixedSyncBarrier(numThreads) + /// - Loop + /// barrier.Join() + /// Do work here + /// barrier.Signal() + /// + /// Usage pattern for worker thread: + /// - Loop + /// barrier.JoinWorker() + /// Do work here + /// barrier.Wait() + /// + /// Currently works with fixed number of workers to simplify APIs + /// + public class SyncBarrier + { + + /// Constructor + /// Number of worker threads participating in loop + public SyncBarrier(uint numThreads) + { + this.numThreads = numThreads; + } + + + // ---------------------------------------------------------------------------------------- + // Master thread operations + // ---------------------------------------------------------------------------------------- + + /// Master thread joins barrier at the beggining of the loop + public void Join() + { + // Wait for all threads to be ready + notifyMaster.WaitOne(); + + // Reset ready count and end event - all worker threads are ready for next loop + this.cntJoin = 0; + this.syncEnd.Reset(); + + // Synchonously start threads by signalling + this.syncStart.Set(); + } + + + // Master thread signals workers when everybody is done + public void Signal() + { + // Wait for all threads to complete + notifyMaster.WaitOne(); + + // Reset thread start synchonization objects so no worker gets ahead of master + this.syncStart.Reset(); + + // Signal workers to continue + this.syncEnd.Set(); + + // Reset completed counter + this.cntCompleted = 0; + } + + // ---------------------------------------------------------------------------------------- + // Worker thread operations + // ---------------------------------------------------------------------------------------- + + /// Worker thread joins barrier + public void JoinWorker() + { + // Increase the barrier join cnt (ready worker threads) + int incJoin = Interlocked.Increment(ref this.cntJoin); + + // If this is the last worker notify master thread that we are done + if( incJoin == this.numThreads ) { + // Notify master thread that we are done + notifyMaster.Set(); + } + + // Wait for signal from master thread to proceed + this.syncStart.WaitOne(); + } + + /// Worker thread waits for randevouz signal + public void Wait() + { + // Increase the barrier (completed worker threads) counter + int incCompleted = Interlocked.Increment(ref this.cntCompleted); + + // If this is the last worker notify master thread that we are done + if( incCompleted == this.numThreads ) { + notifyMaster.Set(); + } + + // Wait for signal from master thread to proceed + this.syncEnd.WaitOne(); + } + + + /// Number of working threads + private uint numThreads; + + /// Signal for synchonous start of working threads + private ManualResetEvent syncStart = new ManualResetEvent(false); + + /// Signal for synchonous start of working threads + private ManualResetEvent syncEnd = new ManualResetEvent(false); + + /// Signal master thread that all workers are done + private AutoResetEvent notifyMaster = new AutoResetEvent(false); + + /// Number of worker threads completed the iteration + private int cntCompleted = 0; + + /// Number of worker threads joined in barrier + private int cntJoin = 0; + } // SyncBarrier + + +} // namespace SchedBench diff --git a/base/Applications/Benchmarks/SharedHeap/SharedHeapBench.cs b/base/Applications/Benchmarks/SharedHeap/SharedHeapBench.cs index d0f9695..b76bfba 100644 --- a/base/Applications/Benchmarks/SharedHeap/SharedHeapBench.cs +++ b/base/Applications/Benchmarks/SharedHeap/SharedHeapBench.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: SharedHeapBench.cs -// // Note: Singularity micro-benchmark program. // using Microsoft.Singularity; @@ -24,7 +22,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(HelpMessage="Show attributes associated with a file", DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] diff --git a/base/Applications/Benchmarks/SharedHeap/SharedHeapBench.csproj b/base/Applications/Benchmarks/SharedHeap/SharedHeapBench.csproj index 488507b..33cf754 100644 --- a/base/Applications/Benchmarks/SharedHeap/SharedHeapBench.csproj +++ b/base/Applications/Benchmarks/SharedHeap/SharedHeapBench.csproj @@ -1,8 +1,6 @@  + + + + + + Library + Singbench.Contracts + + + + + + + + + + + + + diff --git a/base/Applications/Benchmarks/SingBench/Contracts/SingBench.Contracts.csproj b/base/Applications/Benchmarks/SingBench/Contracts/SingBench.Contracts.csproj index f20c244..b415650 100644 --- a/base/Applications/Benchmarks/SingBench/Contracts/SingBench.Contracts.csproj +++ b/base/Applications/Benchmarks/SingBench/Contracts/SingBench.Contracts.csproj @@ -1,8 +1,6 @@  + diff --git a/base/Applications/Benchmarks/SingBench/SingBench/Child.sg b/base/Applications/Benchmarks/SingBench/SingBench/Child.sg new file mode 100644 index 0000000..6d0625a --- /dev/null +++ b/base/Applications/Benchmarks/SingBench/SingBench/Child.sg @@ -0,0 +1,127 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Diagnostics.Contracts; +using Microsoft.Singularity.Endpoint; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.V1.Services; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Configuration; + +using System; +using System.Runtime.CompilerServices; +using System.Threading; + +using Microsoft.Contracts; +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.Applications; + +using Allocation = Microsoft.Singularity.V1.Services.SharedHeapService.Allocation; + +namespace Microsoft.Singularity.Applications.Singbench +{ + public sealed class Child + { + public static void ReceivePerf(int iterations) + { + int doit = iterations / 2; + + // Make a new channel. + SendTestContract.Imp! childImp; + SendTestContract.Exp! childExp; + SendTestContract.NewChannel(out childImp, out childExp); + + // Start up our child + string[] args = new string[3]; + args[0] = "BenchChild"; + args[1] = "-where"; + args[2] = "!"; + Process child = new Process(args, (Endpoint * in ExHeap)childExp); + + // set the where parameter to true + ParameterCode code; + code = child.SetStartupBoolArg(0, true); + if (code != ParameterCode.Success) { + Console.WriteLine("unable to set bool index 0. error={0}", code); + delete childImp; + return; + } + + child.Start(); + + childImp.RecvTestReady(); + + PerfSnap snap = new PerfSnap(); + + snap.Start(); + try { + int arg; + for (int i = 0; i < doit; i++) { + arg = i; + childImp.SendReq(arg); + childImp.RecvResp(out arg); + } + } + finally { + snap.Finish(iterations); + } + + childImp.SendReq(-1); + delete childImp; + child.Join(); + + snap.Display("CHILD S/R"); + } + + ////////////////////////////// Child send-receive performance test + // + public static void ReceivePerf2(int iterations, int messageBytes) + { + // Make a new channel. + BufferTestContract.Imp! childImp; + BufferTestContract.Exp! childExp; + BufferTestContract.NewChannel(out childImp, out childExp); + + // Start up our child + string[] args = new string[2]; + args[0] = "BenchChild"; + args[1] = "!"; + Process child = new Process(args, (Endpoint * in ExHeap)childExp); + child.Start(); + + childImp.RecvBufferReady(); + + byte[]! in ExHeap buffer = new [ExHeap] byte[messageBytes]; + + PerfSnap snap = new PerfSnap(); + + snap.Start(); + try { + for (int i = 0; i < iterations; i++) { + childImp.SendReq(iterations - i - 1, buffer); + childImp.RecvResp(out buffer); + } + } + finally { + // Factor of two for one-sided send-receive only + snap.Finish(2 * iterations); + delete buffer; + } + + delete childImp; + child.Join(); + + if (messageBytes < 1024) { + snap.Display(String.Format("Child S/RB-{0}", messageBytes)); + } + else { + snap.Display(String.Format("Child S/RB-{0}K", + messageBytes / 1024)); + } + } + } +} diff --git a/base/Applications/Benchmarks/SingBench/SingBench/Parameters.sg b/base/Applications/Benchmarks/SingBench/SingBench/Parameters.sg new file mode 100644 index 0000000..a89adb2 --- /dev/null +++ b/base/Applications/Benchmarks/SingBench/SingBench/Parameters.sg @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Diagnostics.Contracts; +using Microsoft.Singularity.Endpoint; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.V1.Services; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Configuration; + +using System; +using System.Runtime.CompilerServices; +using System.Threading; + +using Microsoft.Contracts; +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.Applications; + +using Allocation = Microsoft.Singularity.V1.Services.SharedHeapService.Allocation; + +[assembly: Transform(typeof(ApplicationResourceTransform))] +namespace Microsoft.Singularity.Applications.Singbench +{ + [ConsoleCategory(HelpMessage="Singularity Benchmark Application", DefaultAction=true)] + internal sealed class Parameters + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [Endpoint] + public readonly TRef memoryRef; + + [BoolParameter( "b", Default=false , HelpMessage="Break at start of tests.")] + internal bool breakIn; + + [LongParameter( "i", Default=10000 , HelpMessage="Iterate tests times.")] + internal long iterations; + + [BoolParameter( "n", Default=true , HelpMessage="No GC between tests.")] + internal bool allowGC; + + [BoolParameter( "w", Default=false , HelpMessage="Wait for key press between tests.")] + internal bool pauseForKeys; + + [BoolParameter( "x", Default=false , HelpMessage="XML output.")] + internal bool xmlOutput; + + reflective internal Parameters(); + + internal int AppMain() { + return SingBench.AppMain(this); + } + } +} diff --git a/base/Applications/Benchmarks/SingBench/SingBench/PerfSnap.sg b/base/Applications/Benchmarks/SingBench/SingBench/PerfSnap.sg index 11eb7ab..edc492c 100644 --- a/base/Applications/Benchmarks/SingBench/SingBench/PerfSnap.sg +++ b/base/Applications/Benchmarks/SingBench/SingBench/PerfSnap.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: PerfSnap.sg -// // Note: Performance measurer for Singularity Benchmark // @@ -62,7 +60,7 @@ namespace Microsoft.Singularity.Applications.Singbench public void Start() { if (!atRing3) { - disabled = Processor.DisableInterrupts(); + // disabled = Processor.DisableInterrupts(); // flush out pending IO interrupts Thread.Yield(); Thread.Yield(); @@ -111,7 +109,8 @@ namespace Microsoft.Singularity.Applications.Singbench Reset(1, PerfEvtSel.COUNT | PerfEvtSel.RetiredInstructions); Reset(2, PerfEvtSel.COUNT | PerfEvtSel.RetiredBranchInstructions); Reset(3, PerfEvtSel.COUNT | PerfEvtSel.RequestsToL2Cache | 0x400); - } else { + } + else { // We're not allowed to reset the perf counters, so take note // of their current values; we will subtract from this later. x64_i0 = Processor.ReadPmc(0); @@ -168,7 +167,7 @@ namespace Microsoft.Singularity.Applications.Singbench endStackRets = stackRets; if (!atRing3) { - Processor.RestoreInterrupts(disabled); + // Processor.RestoreInterrupts(disabled); } this.iterations = iterations; @@ -178,7 +177,8 @@ namespace Microsoft.Singularity.Applications.Singbench { if (xmlOutput) { DisplayXml(name); - } else { + } + else { DisplayText(name); } } @@ -263,7 +263,8 @@ namespace Microsoft.Singularity.Applications.Singbench EvtSelToString(e1), EvtSelToString(e2), EvtSelToString(e3))); - } else { + } + else { // Subtract from the initial perf-counter values to // get the delta we want x64_p0 -= x64_i0; diff --git a/base/Applications/Benchmarks/SingBench/SingBench/SingBench.sg b/base/Applications/Benchmarks/SingBench/SingBench/SingBench.sg index 899b93c..62b47c8 100644 --- a/base/Applications/Benchmarks/SingBench/SingBench/SingBench.sg +++ b/base/Applications/Benchmarks/SingBench/SingBench/SingBench.sg @@ -4,10 +4,7 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: SingBench.sg -// -// Note: Benchmark suite for Singularity primitives -// + using Microsoft.Singularity.Channels; using Microsoft.Singularity.Diagnostics.Contracts; using Microsoft.Singularity.Endpoint; @@ -23,44 +20,11 @@ using System.Threading; using Microsoft.Contracts; using Microsoft.SingSharp.Reflection; using Microsoft.Singularity.Applications; -[assembly: Transform(typeof(ApplicationResourceTransform))] + +using Allocation = Microsoft.Singularity.V1.Services.SharedHeapService.Allocation; namespace Microsoft.Singularity.Applications.Singbench { - [ConsoleCategory(HelpMessage="Singularity Benchmark Application", DefaultAction=true)] - internal sealed class Parameters - { - [InputEndpoint("data")] - public readonly TRef Stdin; - - [OutputEndpoint("data")] - public readonly TRef Stdout; - - [Endpoint] - public readonly TRef memoryRef; - - [BoolParameter( "b", Default=false , HelpMessage="Break at start of tests.")] - internal bool breakIn; - - [BoolParameter( "n", Default=true , HelpMessage="No GC between tests.")] - internal bool allowGC; - - [BoolParameter( "w", Default=false , HelpMessage="Wait for key press between tests.")] - internal bool pauseForKeys; - - [BoolParameter( "x", Default=false , HelpMessage="XML output.")] - internal bool xmlOutput; - - [LongParameter( "i", Default=10000 , HelpMessage="Iterate tests times.")] - internal long iterations; - - reflective internal Parameters(); - - internal int AppMain() { - return SingBench.AppMain(this); - } - } - public class SingBench { private static bool atRing3; @@ -244,19 +208,22 @@ namespace Microsoft.Singularity.Applications.Singbench PerfSnap snap = new PerfSnap(); - bool saved = Processor.DisableInterrupts(); - System.Diagnostics.Debug.Assert(saved == true); + bool saved = Processor.DisableLocalPreemption(); + // We no longer disable interrupts for this test. + // When the scheduler implements the SIP scheduling, + // we can enable this back after switching to the local preemption + //System.Diagnostics.Debug.Assert(saved == true); try { snap.Start(); for (int i = 0; i < iterations; i++) { - Processor.RestoreInterrupts(saved); - saved = Processor.DisableInterrupts(); + Processor.RestoreLocalPreemption(saved); + saved = Processor.DisableLocalPreemption(); } } finally { snap.Finish(iterations); - Processor.RestoreInterrupts(saved); + Processor.RestoreLocalPreemption(saved); } snap.Display("Irq SVE/RSTR"); @@ -431,6 +398,8 @@ namespace Microsoft.Singularity.Applications.Singbench snap.Display("Wait/Set"); } + //==============START SEND/RECV TESTS================================== + /////////////////////////////////////////// Receive Performance Tests. // public contract ReceivePerfTest @@ -482,7 +451,7 @@ namespace Microsoft.Singularity.Applications.Singbench snap.Start(); - if (breakIn) { + if (breakIn) { DebugStub.Break(); } @@ -557,7 +526,7 @@ namespace Microsoft.Singularity.Applications.Singbench snap.Start(); - if (breakIn) { + if (breakIn) { DebugStub.Break(); } @@ -585,106 +554,6 @@ namespace Microsoft.Singularity.Applications.Singbench } } - /////////////////////////////////////////// Child-Receive performance tests - // - public static void DoChildReceivePerf(int iterations) - { - int doit = iterations / 2; - - // Make a new channel. - SendTestContract.Imp! childImp; - SendTestContract.Exp! childExp; - SendTestContract.NewChannel(out childImp, out childExp); - - // Start up our child - string[] args = new string[3]; - args[0] = "BenchChild.x86"; - args[1] = "-where"; - args[2] = "!"; - Process child = new Process(args, (Endpoint * in ExHeap)childExp); - - // set the where parameter to true - ParameterCode code; - code = child.SetStartupBoolArg(0, true); - if (code != ParameterCode.Success){ - Console.WriteLine("unable to set bool index 0. error={0}", code); - delete childImp; - return; - } - - child.Start(); - - childImp.RecvTestReady(); - - PerfSnap snap = new PerfSnap(); - - snap.Start(); - try { - int arg; - for (int i = 0; i < doit; i++) { - arg = i; - childImp.SendReq(arg); - childImp.RecvResp(out arg); - } - } - finally { - snap.Finish(iterations); - } - - childImp.SendReq(-1); - delete childImp; - child.Join(); - - snap.Display("CHILD S/R"); - } - - ////////////////////////////// Child send-receive performance test - // - public static void DoChildReceivePerf2(int iterations, int messageBytes) - { - // Make a new channel. - BufferTestContract.Imp! childImp; - BufferTestContract.Exp! childExp; - BufferTestContract.NewChannel(out childImp, out childExp); - - // Start up our child - string[] args = new string[2]; - args[0] = "BenchChild.x86"; - args[1] = "!"; - Process child = new Process(args, (Endpoint * in ExHeap)childExp); - child.Start(); - - childImp.RecvBufferReady(); - - byte[]! in ExHeap buffer = new [ExHeap] byte[messageBytes]; - - PerfSnap snap = new PerfSnap(); - - snap.Start(); - try { - for (int i = 0; i < iterations; i++) { - childImp.SendReq(iterations - i - 1, buffer); - childImp.RecvResp(out buffer); - } - } - finally { - // Factor of two for one-sided send-receive only - snap.Finish(2 * iterations); - delete buffer; - } - - delete childImp; - child.Join(); - - if (messageBytes < 1024) { - snap.Display(String.Format("Child S/RB-{0}", messageBytes)); - } - else { - snap.Display(String.Format("Child S/RB-{0}K", - messageBytes / 1024)); - } - } - //////////////////////////////////////////// Switch Performance Tests. // public contract SwitchPerfTest @@ -706,8 +575,7 @@ namespace Microsoft.Singularity.Applications.Singbench peerReady.Set(); for (int i = 0; i != -1;) { - switch receive - { + switch receive { case ep.Req(arg) in s: i = arg; if (i != -1) { @@ -754,11 +622,10 @@ namespace Microsoft.Singularity.Applications.Singbench e.SendReq(0); s.Add(e); for (int i = 0; i < doit; i++) { - switch receive - { + switch receive { case ep.Resp(arg) in s: i = arg; - if (i == doit -1) { + if (i == doit - 1) { ep.SendReq(-1); } else { @@ -782,6 +649,8 @@ namespace Microsoft.Singularity.Applications.Singbench snap.Display("Send/Switch"); } + //==============END SEND/RECV TESTS================================== + //////////////////////////////////////////// Create Directory Channel. // public static void DoPageAllocPerf(int iterations) @@ -927,7 +796,7 @@ namespace Microsoft.Singularity.Applications.Singbench public static void SelectThread() { for (int i = 0; i < SELECT_ITERATIONS; i++) { - for (int j = 0; j < NSELECT; j+=3) { + for (int j = 0; j < NSELECT; j += 3) { SelectTest.Imp imp = ((!)impEndpoints[j]).Acquire(); imp.SendA(); @@ -935,7 +804,7 @@ namespace Microsoft.Singularity.Applications.Singbench ((!)impEndpoints[j]).Release(imp); } - for (int j = 1; j < NSELECT; j+=3) { + for (int j = 1; j < NSELECT; j += 3) { SelectTest.Imp imp = ((!)impEndpoints[j]).Acquire(); imp.SendB(); @@ -943,7 +812,7 @@ namespace Microsoft.Singularity.Applications.Singbench ((!)impEndpoints[j]).Release(imp); } - for (int j = 2; j < NSELECT; j+=3) { + for (int j = 2; j < NSELECT; j += 3) { SelectTest.Imp imp = ((!)impEndpoints[j]).Acquire(); imp.SendC(); @@ -1020,7 +889,7 @@ namespace Microsoft.Singularity.Applications.Singbench bool failed = false; for (int i = 0; i < SELECT_ITERATIONS; i++) { - for(int j = 0; j < NSELECT-2; j++) { + for (int j = 0; j < NSELECT - 2; j++) { SelectTest.Exp a = ((!)expEndpoints[j+0]).Acquire(); SelectTest.Exp b = ((!)expEndpoints[j+1]).Acquire(); SelectTest.Exp c = ((!)expEndpoints[j+2]).Acquire(); @@ -1110,7 +979,7 @@ namespace Microsoft.Singularity.Applications.Singbench bool failed = false; Console.WriteLine(" Starting server loop - {0} iterations", SELECT_ITERATIONS); - for (i = 0 ; i < SELECT_ITERATIONS; i++) { + for (i = 0; i < SELECT_ITERATIONS; i++) { ActuallySelect(eset, 0, ref failed); ActuallySelect(eset, 0, ref failed); ActuallySelect(eset, 0, ref failed); @@ -1177,7 +1046,7 @@ namespace Microsoft.Singularity.Applications.Singbench Process[] process = new Process[iterations]; string[] args = new string[2]; - args[0] = "testpe.x86"; + args[0] = "testpe"; args[1] = "!"; // Special flag to not notify debugger. PerfSnap snap = new PerfSnap(); @@ -1232,6 +1101,8 @@ namespace Microsoft.Singularity.Applications.Singbench // public static int GetTrace() { + +#if false Tracing.LogEntry * lstart; Tracing.LogEntry * llimit; Tracing.LogEntry ** lhead; @@ -1245,6 +1116,7 @@ namespace Microsoft.Singularity.Applications.Singbench (UIntPtr)lstart, (UIntPtr)llimit, (UIntPtr)lhead, (UIntPtr)(*lhead)); Console.WriteLine("Text: {0:X} {1:X}, {2:X}, {3:X}", (UIntPtr)tstart, (UIntPtr)tlimit, (UIntPtr)thead, (UIntPtr)(*thead)); +#endif return 0; } @@ -1279,12 +1151,13 @@ namespace Microsoft.Singularity.Applications.Singbench bool xmlOutput = config.xmlOutput; allowGC = config.allowGC; pauseForKeys = config.pauseForKeys; - breakIn = config.breakIn; - + breakIn = config.breakIn; + if (Processor.AtKernelPrivilege()) { atRing3 = false; DualWriteLine("Singbench running at KERNEL privilege"); - } else { + } + else { atRing3 = true; DualWriteLine("Singbench running at USER privilege"); } @@ -1293,14 +1166,14 @@ namespace Microsoft.Singularity.Applications.Singbench peerBegin = new AutoResetEvent(false); peerFinish = new AutoResetEvent(false); - MemoryContract.Imp impMem = config.memoryRef.Acquire(); + MemoryContract.Imp impMem = config.memoryRef.Acquire(); if (impMem == null) { throw new ApplicationException("Error: Unable to bind to " + MemoryContract.ModuleName); } - impMem.RecvReady(); - + impMem.RecvReady(); + PerfSnap.SetOptions(atRing3, xmlOutput); GetTrace(); @@ -1345,25 +1218,24 @@ namespace Microsoft.Singularity.Applications.Singbench Pause(); ClearEnvironment(impMem); - DoReceivePerf(1000); + DoReceivePerf(1024); Pause(); for (int i = 1; i <= 65536; i *= 2) { ClearEnvironment(impMem); DoReceivePerf2(iterations, i); - Pause(); + Pause(); } // Child-channel tests ClearEnvironment(impMem); - DoChildReceivePerf(1000); + Child.ReceivePerf(1000); Pause(); - for (int i = 1; i <= 65536; i *= 2) { ClearEnvironment(impMem); - DoChildReceivePerf2(iterations, i); - Pause(); + Child.ReceivePerf2(iterations, i); + Pause(); } ClearEnvironment(impMem); @@ -1419,8 +1291,8 @@ namespace Microsoft.Singularity.Applications.Singbench ClearEnvironment(impMem); DoSelectSet(); #endif - - } catch (Exception e) { + } + catch (Exception e) { Console.WriteLine("Caught {0}", e.Message); // How do I work out where an exception was thrown from these days? //Console.WriteLine(e.StackTrace); diff --git a/base/Applications/Benchmarks/SingBench/SingBench/SingBenchApp.csproj b/base/Applications/Benchmarks/SingBench/SingBench/SingBenchApp.csproj index d4dbf6a..c8a5bb6 100644 --- a/base/Applications/Benchmarks/SingBench/SingBench/SingBenchApp.csproj +++ b/base/Applications/Benchmarks/SingBench/SingBench/SingBenchApp.csproj @@ -1,8 +1,6 @@  - + Bartok Exe @@ -50,13 +48,18 @@ Note: This is the template for a basic console app. - + - + + + + + + diff --git a/base/Applications/Benchmarks/bartok/ImportFromBartok.cmd b/base/Applications/Benchmarks/bartok/ImportFromBartok.cmd new file mode 100644 index 0000000..10d763c --- /dev/null +++ b/base/Applications/Benchmarks/bartok/ImportFromBartok.cmd @@ -0,0 +1,29 @@ +@echo. +@echo *** Checking out Bartok MSIL files. +@echo. +sd edit msil\bartok.* +@if errorlevel 1 goto exit + +@echo. +@echo *** Copying files from Bartok enlistment. +@echo. + +@if exist c:\othercode\act\bartok\obj\sing-debug-x86-x86\nul ( + copy c:\othercode\act\bartok\obj\sing-debug-x86-x86\bartok.* msil + @if errorlevel 1 goto exit +) + +@if exist c:\act\bartok\obj\sing-debug-x86-x86\nul ( + copy c:\act\bartok\obj\sing-debug-x86-x86\bartok.* msil + @if errorlevel 1 goto exit +) + +@if exist c:\home\act-dev7\bartok\obj\sing-debug-x86-x86\nul ( + copy c:\home\act-dev7\bartok\obj\sing-debug-x86-x86\bartok.* msil + @if errorlevel 1 goto exit +) + +@echo. +@echo *** Deleting unused files. +@echo. +del msil\bartok.tryallextension.* 2>nul diff --git a/base/Applications/Benchmarks/bartok/Imported.txt b/base/Applications/Benchmarks/bartok/Imported.txt new file mode 100644 index 0000000..56f3b36 --- /dev/null +++ b/base/Applications/Benchmarks/bartok/Imported.txt @@ -0,0 +1 @@ + diff --git a/base/Applications/Benchmarks/bartok/kernel/Diagnostics.Contracts.pdb b/base/Applications/Benchmarks/bartok/kernel/Diagnostics.Contracts.pdb deleted file mode 100644 index 955101f..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/Diagnostics.Contracts.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/kernel/Diagnostics.pdb b/base/Applications/Benchmarks/bartok/kernel/Diagnostics.pdb deleted file mode 100644 index cbcd9d1..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/Diagnostics.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/kernel/Directory.Contracts.pdb b/base/Applications/Benchmarks/bartok/kernel/Directory.Contracts.pdb deleted file mode 100644 index 39dc5df..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/Directory.Contracts.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/kernel/Directory.pdb b/base/Applications/Benchmarks/bartok/kernel/Directory.pdb deleted file mode 100644 index c4515bd..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/Directory.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/kernel/Drivers.pdb b/base/Applications/Benchmarks/bartok/kernel/Drivers.pdb deleted file mode 100644 index 2a98a38..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/Drivers.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/kernel/FileSystem.Contracts.pdb b/base/Applications/Benchmarks/bartok/kernel/FileSystem.Contracts.pdb deleted file mode 100644 index 68e7e6c..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/FileSystem.Contracts.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/kernel/Hal.LegacyPC.pdb b/base/Applications/Benchmarks/bartok/kernel/Hal.LegacyPC.pdb deleted file mode 100644 index 1b8ed56..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/Hal.LegacyPC.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/kernel/Hypercall.Contracts.pdb b/base/Applications/Benchmarks/bartok/kernel/Hypercall.Contracts.pdb deleted file mode 100644 index aa3b935..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/Hypercall.Contracts.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/kernel/Hypercall.pdb b/base/Applications/Benchmarks/bartok/kernel/Hypercall.pdb deleted file mode 100644 index add584c..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/Hypercall.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/kernel/ILHelpers.pdb b/base/Applications/Benchmarks/bartok/kernel/ILHelpers.pdb deleted file mode 100644 index 461e888..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/ILHelpers.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/kernel/Io.Contracts.pdb b/base/Applications/Benchmarks/bartok/kernel/Io.Contracts.pdb deleted file mode 100644 index f529bdd..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/Io.Contracts.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/kernel/IoSystem.pdb b/base/Applications/Benchmarks/bartok/kernel/IoSystem.pdb deleted file mode 100644 index e9cf95c..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/IoSystem.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/kernel/Loader.pdb b/base/Applications/Benchmarks/bartok/kernel/Loader.pdb deleted file mode 100644 index 3ac2c5a..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/Loader.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/kernel/Microsoft.SingSharp.Runtime.pdb b/base/Applications/Benchmarks/bartok/kernel/Microsoft.SingSharp.Runtime.pdb deleted file mode 100644 index 0661fd0..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/Microsoft.SingSharp.Runtime.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/kernel/Security.Contracts.pdb b/base/Applications/Benchmarks/bartok/kernel/Security.Contracts.pdb deleted file mode 100644 index 0d44de1..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/Security.Contracts.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/kernel/Security.pdb b/base/Applications/Benchmarks/bartok/kernel/Security.pdb deleted file mode 100644 index 7c19321..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/Security.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/kernel/SecurityService.pdb b/base/Applications/Benchmarks/bartok/kernel/SecurityService.pdb deleted file mode 100644 index e7119d9..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/SecurityService.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/kernel/Stress.Contracts.pdb b/base/Applications/Benchmarks/bartok/kernel/Stress.Contracts.pdb deleted file mode 100644 index 01b462e..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/Stress.Contracts.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/kernel/Stress.pdb b/base/Applications/Benchmarks/bartok/kernel/Stress.pdb deleted file mode 100644 index 6395486..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/Stress.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/kernel/System.Compiler.Runtime.pdb b/base/Applications/Benchmarks/bartok/kernel/System.Compiler.Runtime.pdb deleted file mode 100644 index dd77d31..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/System.Compiler.Runtime.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/kernel/kernel.pdb b/base/Applications/Benchmarks/bartok/kernel/kernel.pdb deleted file mode 100644 index 3d37732..0000000 Binary files a/base/Applications/Benchmarks/bartok/kernel/kernel.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Analysis.dll b/base/Applications/Benchmarks/bartok/msil/Bartok.Analysis.dll index ad77e57..cd97082 100644 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Analysis.dll and b/base/Applications/Benchmarks/bartok/msil/Bartok.Analysis.dll differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Analysis.pdb b/base/Applications/Benchmarks/bartok/msil/Bartok.Analysis.pdb deleted file mode 100644 index 91a6364..0000000 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Analysis.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Backend.dll b/base/Applications/Benchmarks/bartok/msil/Bartok.Backend.dll index 6c6b6a4..cbabfee 100644 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Backend.dll and b/base/Applications/Benchmarks/bartok/msil/Bartok.Backend.dll differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Backend.pdb b/base/Applications/Benchmarks/bartok/msil/Bartok.Backend.pdb deleted file mode 100644 index 8290412..0000000 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Backend.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.CfgUtil.dll b/base/Applications/Benchmarks/bartok/msil/Bartok.CfgUtil.dll index cbd7f2a..7ea9d66 100644 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.CfgUtil.dll and b/base/Applications/Benchmarks/bartok/msil/Bartok.CfgUtil.dll differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.CfgUtil.pdb b/base/Applications/Benchmarks/bartok/msil/Bartok.CfgUtil.pdb deleted file mode 100644 index afe07cd..0000000 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.CfgUtil.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Coff.dll b/base/Applications/Benchmarks/bartok/msil/Bartok.Coff.dll index 566f09d..ce9601b 100644 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Coff.dll and b/base/Applications/Benchmarks/bartok/msil/Bartok.Coff.dll differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Coff.pdb b/base/Applications/Benchmarks/bartok/msil/Bartok.Coff.pdb deleted file mode 100644 index db6145e..0000000 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Coff.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Convert.dll b/base/Applications/Benchmarks/bartok/msil/Bartok.Convert.dll index 0689e53..49dbd19 100644 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Convert.dll and b/base/Applications/Benchmarks/bartok/msil/Bartok.Convert.dll differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Convert.pdb b/base/Applications/Benchmarks/bartok/msil/Bartok.Convert.pdb deleted file mode 100644 index 82714b2..0000000 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Convert.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Datatype.dll b/base/Applications/Benchmarks/bartok/msil/Bartok.Datatype.dll index 1f87802..3b1c9ad 100644 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Datatype.dll and b/base/Applications/Benchmarks/bartok/msil/Bartok.Datatype.dll differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Datatype.pdb b/base/Applications/Benchmarks/bartok/msil/Bartok.Datatype.pdb deleted file mode 100644 index 192a02d..0000000 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Datatype.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.DebugInfo.dll b/base/Applications/Benchmarks/bartok/msil/Bartok.DebugInfo.dll index a90246e..b9e2b9f 100644 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.DebugInfo.dll and b/base/Applications/Benchmarks/bartok/msil/Bartok.DebugInfo.dll differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.DebugInfo.pdb b/base/Applications/Benchmarks/bartok/msil/Bartok.DebugInfo.pdb deleted file mode 100644 index 3dfa377..0000000 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.DebugInfo.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Encode.dll b/base/Applications/Benchmarks/bartok/msil/Bartok.Encode.dll index 2b2bb0d..8172457 100644 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Encode.dll and b/base/Applications/Benchmarks/bartok/msil/Bartok.Encode.dll differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Encode.pdb b/base/Applications/Benchmarks/bartok/msil/Bartok.Encode.pdb deleted file mode 100644 index 8348722..0000000 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Encode.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Ir.dll b/base/Applications/Benchmarks/bartok/msil/Bartok.Ir.dll index 1302eb7..e25f8ce 100644 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Ir.dll and b/base/Applications/Benchmarks/bartok/msil/Bartok.Ir.dll differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Ir.pdb b/base/Applications/Benchmarks/bartok/msil/Bartok.Ir.pdb deleted file mode 100644 index 2a0b027..0000000 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Ir.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Lir.dll b/base/Applications/Benchmarks/bartok/msil/Bartok.Lir.dll index 3a76816..20fd3f7 100644 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Lir.dll and b/base/Applications/Benchmarks/bartok/msil/Bartok.Lir.dll differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Lir.pdb b/base/Applications/Benchmarks/bartok/msil/Bartok.Lir.pdb deleted file mode 100644 index cefe573..0000000 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Lir.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.MSIL.dll b/base/Applications/Benchmarks/bartok/msil/Bartok.MSIL.dll index 14d3911..9fbaa61 100644 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.MSIL.dll and b/base/Applications/Benchmarks/bartok/msil/Bartok.MSIL.dll differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.MSIL.pdb b/base/Applications/Benchmarks/bartok/msil/Bartok.MSIL.pdb deleted file mode 100644 index ccc1973..0000000 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.MSIL.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Mangle.dll b/base/Applications/Benchmarks/bartok/msil/Bartok.Mangle.dll index b24109d..75bd9f0 100644 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Mangle.dll and b/base/Applications/Benchmarks/bartok/msil/Bartok.Mangle.dll differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Mangle.pdb b/base/Applications/Benchmarks/bartok/msil/Bartok.Mangle.pdb deleted file mode 100644 index ee3587c..0000000 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Mangle.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Opt.dll b/base/Applications/Benchmarks/bartok/msil/Bartok.Opt.dll index 0832b85..9dce515 100644 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Opt.dll and b/base/Applications/Benchmarks/bartok/msil/Bartok.Opt.dll differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Opt.pdb b/base/Applications/Benchmarks/bartok/msil/Bartok.Opt.pdb deleted file mode 100644 index 4fe153f..0000000 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Opt.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Profile.dll b/base/Applications/Benchmarks/bartok/msil/Bartok.Profile.dll index 51c5ed5..7678312 100644 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Profile.dll and b/base/Applications/Benchmarks/bartok/msil/Bartok.Profile.dll differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Profile.pdb b/base/Applications/Benchmarks/bartok/msil/Bartok.Profile.pdb deleted file mode 100644 index 2a85b46..0000000 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Profile.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Regalloc.dll b/base/Applications/Benchmarks/bartok/msil/Bartok.Regalloc.dll index bd5059e..91976f4 100644 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Regalloc.dll and b/base/Applications/Benchmarks/bartok/msil/Bartok.Regalloc.dll differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Regalloc.pdb b/base/Applications/Benchmarks/bartok/msil/Bartok.Regalloc.pdb deleted file mode 100644 index 10fcc94..0000000 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Regalloc.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Runtime.pdb b/base/Applications/Benchmarks/bartok/msil/Bartok.Runtime.pdb deleted file mode 100644 index eaf779f..0000000 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Runtime.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.SystemExtension.dll b/base/Applications/Benchmarks/bartok/msil/Bartok.SystemExtension.dll index a67e204..a37326e 100644 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.SystemExtension.dll and b/base/Applications/Benchmarks/bartok/msil/Bartok.SystemExtension.dll differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.SystemExtension.pdb b/base/Applications/Benchmarks/bartok/msil/Bartok.SystemExtension.pdb deleted file mode 100644 index e6f6105..0000000 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.SystemExtension.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Tables.dll b/base/Applications/Benchmarks/bartok/msil/Bartok.Tables.dll index 18bf6c9..9ca6419 100644 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Tables.dll and b/base/Applications/Benchmarks/bartok/msil/Bartok.Tables.dll differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Tables.pdb b/base/Applications/Benchmarks/bartok/msil/Bartok.Tables.pdb deleted file mode 100644 index 10da284..0000000 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Tables.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Utility.dll b/base/Applications/Benchmarks/bartok/msil/Bartok.Utility.dll index dcbdb4e..ecbaba2 100644 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Utility.dll and b/base/Applications/Benchmarks/bartok/msil/Bartok.Utility.dll differ diff --git a/base/Applications/Benchmarks/bartok/msil/Bartok.Utility.pdb b/base/Applications/Benchmarks/bartok/msil/Bartok.Utility.pdb deleted file mode 100644 index f7c9950..0000000 Binary files a/base/Applications/Benchmarks/bartok/msil/Bartok.Utility.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/bartok/msil/bartok.exe b/base/Applications/Benchmarks/bartok/msil/bartok.exe index e703037..e728626 100644 Binary files a/base/Applications/Benchmarks/bartok/msil/bartok.exe and b/base/Applications/Benchmarks/bartok/msil/bartok.exe differ diff --git a/base/Applications/Benchmarks/bartok/msil/bartok.pdb b/base/Applications/Benchmarks/bartok/msil/bartok.pdb deleted file mode 100644 index 6b2fdf8..0000000 Binary files a/base/Applications/Benchmarks/bartok/msil/bartok.pdb and /dev/null differ diff --git a/base/Applications/Benchmarks/diskreadperf/diskreadperf.csproj b/base/Applications/Benchmarks/diskreadperf/diskreadperf.csproj index 776bf4c..c947eed 100644 --- a/base/Applications/Benchmarks/diskreadperf/diskreadperf.csproj +++ b/base/Applications/Benchmarks/diskreadperf/diskreadperf.csproj @@ -1,8 +1,6 @@  - + - - Exe - diskrw - true - - - - - - - + + Exe + diskrw + true + - + + + + + + + diff --git a/base/Applications/Benchmarks/diskrw/diskrw.sg b/base/Applications/Benchmarks/diskrw/diskrw.sg index 5912f83..edf6291 100644 --- a/base/Applications/Benchmarks/diskrw/diskrw.sg +++ b/base/Applications/Benchmarks/diskrw/diskrw.sg @@ -4,10 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: diskrw.sg -// -// Note: -// using System; using System.Runtime.CompilerServices; @@ -23,12 +19,13 @@ using Microsoft.SingSharp.Reflection; using Microsoft.Singularity.Applications; using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; + [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications.Benchmarks.DiskRW +namespace Microsoft.Singularity.Applications.Benchmarks.DiskRW { [ConsoleCategory(HelpMessage="Disk r/w performance test", DefaultAction=true)] - internal class Parameters + internal class Parameters { [InputEndpoint("data")] public readonly TRef Stdin; @@ -72,11 +69,11 @@ namespace Microsoft.Singularity.Applications.Benchmarks.DiskRW return DiskBenchmark.AppMain(this); } } - + public class DiskBenchmark { - private const uint BytesPerSector = 512; - private const uint SectorsPerMB = 1024 * 1024 / BytesPerSector; + internal const uint BytesPerSector = 512; + internal const uint SectorsPerMB = 1024 * 1024 / BytesPerSector; public static DiskDeviceContract.Imp:Ready OpenDevice(String! devname) { @@ -90,42 +87,39 @@ namespace Microsoft.Singularity.Applications.Benchmarks.DiskRW ns = DirectoryService.NewClientEndpoint(); bool success; success = SdsUtils.Bind(devname,ns, exp, out errorOut); - if (!success) - { + if (!success) { Console.WriteLine("Bind of {0} failed. Reason:{1}\n", - devname, SdsUtils.ErrorCodeToString(errorOut)); + devname, SdsUtils.ErrorCodeToString(errorOut)); delete imp; delete ns; return null; } - switch receive - { - case imp.Success(): - break; - case imp.ContractNotSupported(): - Console.WriteLine("{0} does not support DiskDevice", devname); - delete imp; - delete ns; - return null; - case imp.ChannelClosed(): - Console.WriteLine("DiskDevice channel to {0} closed unexpectedly", devname); - delete imp; - delete ns; - return null; - } + switch receive { + case imp.Success(): + break; + case imp.ContractNotSupported(): + Console.WriteLine("{0} does not support DiskDevice", devname); + delete imp; + delete ns; + return null; + case imp.ChannelClosed(): + Console.WriteLine("DiskDevice channel to {0} closed unexpectedly", devname); + delete imp; + delete ns; + return null; + } delete ns; return imp; } - private static ulong GetRandomOffset(SRandom! rng, + internal static ulong GetRandomOffset(SRandom! rng, ulong diskSectorCount, ulong blockBytes) { ulong maxBlock = diskSectorCount * 512 / blockBytes; ulong offset = 0; - for (int i = 0; i < 64; i += 30) - { + for (int i = 0; i < 64; i += 30) { offset ^= (ulong)(rng.Next() << i); } offset &= 0x7fffffffffffffff; @@ -133,7 +127,7 @@ namespace Microsoft.Singularity.Applications.Benchmarks.DiskRW return offset * blockBytes; } - private static void DisplayPerf(bool didRead, + internal static void DisplayPerf(bool didRead, bool didRandom, TimeSpan elapsed, ulong blockCount, @@ -170,8 +164,7 @@ namespace Microsoft.Singularity.Applications.Benchmarks.DiskRW // Open device // DiskDeviceContract.Imp imp = OpenDevice(deviceName); - if (null == imp) - { + if (null == imp) { Console.WriteLine("Could not open {0}", deviceName); return 1; } @@ -182,15 +175,14 @@ namespace Microsoft.Singularity.Applications.Benchmarks.DiskRW ulong diskSectorCount; imp.SendGetSectorCount(); - switch receive - { - case imp.AckGetSectorCount(sectorCount): - diskSectorCount = sectorCount; - break; - case imp.ChannelClosed(): - Console.WriteLine("Channel closed unexpectedly"); - throw new Exception("Unexpected channel close"); - } + switch receive { + case imp.AckGetSectorCount(sectorCount): + diskSectorCount = sectorCount; + break; + case imp.ChannelClosed(): + Console.WriteLine("Channel closed unexpectedly"); + throw new Exception("Unexpected channel close"); + } // Constrain disk size if user requested if (diskLimitMB != 0 && @@ -201,13 +193,11 @@ namespace Microsoft.Singularity.Applications.Benchmarks.DiskRW diskLimitMB = diskSectorCount / SectorsPerMB; // Constrain block count if it overflows available blocks - if (blockCount * blockBytes > diskSectorCount * BytesPerSector) - { + if (blockCount * blockBytes > diskSectorCount * BytesPerSector) { blockCount = diskSectorCount * BytesPerSector / blockBytes; } - if (runNumber == 0) - { + if (runNumber == 0) { Console.WriteLine("# Type: {0} {1}", doRandom ? "Random" : "Sequential", doRead ? "Read" : "Write"); @@ -224,8 +214,7 @@ namespace Microsoft.Singularity.Applications.Benchmarks.DiskRW SRandom rng = new SRandom(); ulong diskPos = 0; - if (doRandom) - { + if (doRandom) { diskPos = GetRandomOffset(rng, diskSectorCount, blockBytes); } @@ -241,30 +230,25 @@ namespace Microsoft.Singularity.Applications.Benchmarks.DiskRW TimeSpan startTime = ProcessService.GetUpTime(); - for (ulong i = 0; i < blockCount; i++) - { + for (ulong i = 0; i < blockCount; i++) { ulong sector = diskPos / BytesPerSector; byte[]! in ExHeap outBuffer; - if (doRead) - { + if (doRead) { imp.SendRead(buffer, 0, blockBytes, sector); imp.RecvAckRead(out outBuffer); } - else - { + else { imp.SendWrite(buffer, 0, blockBytes, sector); imp.RecvAckWrite(out outBuffer); } buffer = outBuffer; - if (doRandom) - { + if (doRandom) { diskPos = GetRandomOffset(rng, diskSectorCount, blockBytes); } - else - { + else { diskPos += blockBytes; } } @@ -284,11 +268,11 @@ namespace Microsoft.Singularity.Applications.Benchmarks.DiskRW out endGcBytes); Console.WriteLine("[AppGC :- cnt {0} bytes {1} Kern: ints {2} swi {3} gcs {4}]", - endGcCount - startGcCount, - endGcBytes - startGcBytes, - ProcessService.GetKernelInterruptCount() - startIrqCount, - ProcessService.GetContextSwitchCount() - startSwitchCount, - ProcessService.GetKernelGcCount() - startKernelGcCount); + endGcCount - startGcCount, + endGcBytes - startGcBytes, + ProcessService.GetKernelInterruptCount() - startIrqCount, + ProcessService.GetContextSwitchCount() - startSwitchCount, + ProcessService.GetKernelGcCount() - startKernelGcCount); return 0; } @@ -298,24 +282,22 @@ namespace Microsoft.Singularity.Applications.Benchmarks.DiskRW out ulong value) { // arg should look like "[-/][A-z]:[0-9]" - if (arg.Length >= 4) - { - try - { + if (arg.Length >= 4) { + try { value = UInt64.Parse(arg.Substring(3)); return true; } - catch (FormatException) - {} - catch (OverflowException) - {} + catch (FormatException) { + } + catch (OverflowException) { + } } Console.WriteLine("Could not parse {0}", name); value = 0; return false; } - static void Usage() + internal static void Usage() { } @@ -333,38 +315,34 @@ namespace Microsoft.Singularity.Applications.Benchmarks.DiskRW bool doRead = config.doRead; ulong dataMB = (ulong) config.dataMB; - if (blockBytes < 512 || (blockBytes & (blockBytes - 1)) != 0) - { + if (blockBytes < 512 || (blockBytes & (blockBytes - 1)) != 0) { Console.WriteLine("Block size must be a power of two greater than or equal to 512."); Usage(); return -1; } - if (doRead == doWrite) - { + if (doRead == doWrite) { Console.WriteLine("Need to specify either read or write test."); Usage(); return -1; } - else if (doRead) - { + else if (doRead) { diskReadTest = true; } - if (dataMB != 0) - { + if (dataMB != 0) { blockCount = dataMB * 1024 * 1024 / blockBytes; } - for (int run = 0; run < (int)repeatCount; run++) - { - if (run != 0) + for (int run = 0; run < (int)repeatCount; run++) { + if (run != 0) { System.Threading.Thread.Sleep(1000 * (int)pauseSecs); + } + Tracing.Log(Tracing.Debug, "Starting diskrw run {0}", (UIntPtr) run); - if (DoDevice(deviceName, run, blockCount, blockBytes, - diskLimitMB, randomIO, diskReadTest) != 0) - { + if (DoDevice((!)deviceName, run, blockCount, blockBytes, + diskLimitMB, randomIO, diskReadTest) != 0) { return -1; } Tracing.Log(Tracing.Debug, "Ending diskrw run {0}", diff --git a/base/Applications/Benchmarks/diskrw/srandom.cs b/base/Applications/Benchmarks/diskrw/srandom.cs new file mode 100644 index 0000000..fbaa932 --- /dev/null +++ b/base/Applications/Benchmarks/diskrw/srandom.cs @@ -0,0 +1,63 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +using Microsoft.Contracts; + +namespace Microsoft.Singularity.Applications.Benchmarks.DiskRW +{ + /// + /// Low-cost Integer random number generator. The + /// implementation is based on the description of Park and + /// Miller's RNG on page 278 of Numerical Recipes in C, 2nd + /// Edition, by Press, Teukolsky, Vetterling, and Flannery. + /// + /// The same random number generator is used for our disk + /// benchmarks on other platforms. + /// + public class SRandom + { + public const uint Maximum = 0x7fffffffu; + + const ulong m = 2147483647; + const ulong a = 16807; + const uint initialValue = 0x23456789; + uint last; + + public SRandom() + { + Reset(); + } + + [Delayed] + public void Reset() + { + last = initialValue; + } + + public int Next() + { + // Use 64-bit multiplication to preserve high bits. + ulong tmp = (a * last) % m; + last = (uint) tmp; + return (int)(last & Maximum); + } + + public static void Check() + { + const int test_iterations = 10000000; + SRandom r = new SRandom(); + int hash = 0; + for (int i = 0; i < test_iterations; i++) { + int n = r.Next(); + System.Diagnostics.Debug.Assert(n != 0); + hash ^= n; + } + Console.Write("xor sum 1...{0} = {1}\n", test_iterations, hash); + System.Diagnostics.Debug.Assert(hash == 1080076236); + } + } +} diff --git a/base/Applications/Benchmarks/diskrw/srandom.sg b/base/Applications/Benchmarks/diskrw/srandom.sg deleted file mode 100644 index 8752efe..0000000 --- a/base/Applications/Benchmarks/diskrw/srandom.sg +++ /dev/null @@ -1,64 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - - -using System; -using Microsoft.Contracts; - -namespace Microsoft.Singularity.Applications.Benchmarks.DiskRW -{ - /// - /// Low-cost Integer random number generator. The - /// implementation is based on the description of Park and - /// Miller's RNG on page 278 of Numerical Recipes in C, 2nd - /// Edition, by Press, Teukolsky, Vetterling, and Flannery. - /// - /// The same random number generator is used for our disk - /// benchmarks on other platforms. - /// - public class SRandom - { - public const int Maximum = 0x7fffffffu; - - const ulong m = 2147483647; - const ulong a = 16807; - const uint initialValue = 0x23456789; - uint last; - - public SRandom() - { - Reset(); - } - - [Delayed] - public void Reset() - { - last = initialValue; - } - - public int Next() - { - // Use 64-bit multiplication to preserve high bits. - ulong tmp = (a * last) % m; - last = (uint) tmp; - return (int)(last & Maximum); - } - - public static void Check() - { - const int test_iterations = 10000000; - SRandom r = new SRandom(); - int hash = 0; - for (int i = 0; i < test_iterations; i++) - { - int n = r.Next(); - System.Diagnostics.Debug.Assert(n != 0); - hash ^= n; - } - Console.Write("xor sum 1...{0} = {1}\n", test_iterations, hash); - System.Diagnostics.Debug.Assert(hash == 1080076236); - } - } -} diff --git a/base/Applications/Benchmarks/diskrwnull/diskrw.sg b/base/Applications/Benchmarks/diskrwnull/diskrw.sg index 78bb024..220e724 100644 --- a/base/Applications/Benchmarks/diskrwnull/diskrw.sg +++ b/base/Applications/Benchmarks/diskrwnull/diskrw.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: diskrw.sg -// // Note: // @@ -94,8 +92,7 @@ namespace Microsoft.Singularity.Applications.Benchmarks.DiskRWNull { ulong maxBlock = diskSectorCount * 512 / blockBytes; ulong offset = 0; - for (int i = 0; i < 64; i += 30) - { + for (int i = 0; i < 64; i += 30) { offset ^= (ulong)(rng.Next() << i); } offset &= 0x7fffffffffffffff; @@ -132,8 +129,7 @@ namespace Microsoft.Singularity.Applications.Benchmarks.DiskRWNull bool doRead) { - for(int i = 0; i < 4000; i++) - { + for (int i = 0; i < 4000; i++) { Hack.Imp! imp; Hack.Exp! exp; Hack.NewChannel(out imp, out exp); @@ -151,13 +147,11 @@ namespace Microsoft.Singularity.Applications.Benchmarks.DiskRWNull diskLimitMB = diskSectorCount / SectorsPerMB; // Constrain block count if it overflows available blocks - if (blockCount * blockBytes > diskSectorCount * BytesPerSector) - { + if (blockCount * blockBytes > diskSectorCount * BytesPerSector) { blockCount = diskSectorCount * BytesPerSector / blockBytes; } - if (runNumber == 0) - { + if (runNumber == 0) { Console.WriteLine("# Type: {0} {1}", doRandom ? "Random" : "Sequential", doRead ? "Read" : "Write"); @@ -174,8 +168,7 @@ namespace Microsoft.Singularity.Applications.Benchmarks.DiskRWNull SRandom rng = new SRandom(); ulong diskPos = 0; - if (doRandom) - { + if (doRandom) { diskPos = GetRandomOffset(rng, diskSectorCount, blockBytes); } @@ -192,59 +185,58 @@ namespace Microsoft.Singularity.Applications.Benchmarks.DiskRWNull Hack.Imp! imph = himp.Acquire(); Hack.Exp! exph = hexp.Acquire(); - /***/ + /// ESet eset = new ESet(); eset.Add(exph); - /***/ + /// TimeSpan startTime = ProcessService.GetUpTime(); - for (ulong i = 0; i < blockCount; i++) - { + for (ulong i = 0; i < blockCount; i++) { byte[]! in ExHeap outBuffer; - if (doRead) - { + if (doRead) { //ProcessService.GetUpTime(); //imph.SendHak(); //switch receive { case exph.Hak(): exph.SendHak(); break; case unsatisfiable: throw new System.Exception(); } //imph.RecvHak(); - /***/ + /// imph.SendHak(); - switch receive { case ep.Hak() in eset: ep.SendHak(); eset.Add(ep); break; case unsatisfiable: throw new System.Exception(); } + switch receive { + case ep.Hak() in eset: ep.SendHak(); eset.Add(ep); break; case unsatisfiable: throw new System.Exception(); + } imph.RecvHak(); - /***/ + /// //imph.SendHak(); exph.RecvHak(); exph.SendHak(); imph.RecvHak(); outBuffer = buffer; } - else - { + else { outBuffer = buffer; } buffer = outBuffer; - if (doRandom) - { + if (doRandom) { diskPos = GetRandomOffset(rng, diskSectorCount, blockBytes); } - else - { + else { diskPos += blockBytes; } } TimeSpan endTime = ProcessService.GetUpTime(); - /***/ + /// imph.SendHak(); - switch receive { case ep.Hak() in eset: ep.SendHak(); hexp.Release(ep); break; case unsatisfiable: throw new System.Exception(); } + switch receive { + case ep.Hak() in eset: ep.SendHak(); hexp.Release(ep); break; case unsatisfiable: throw new System.Exception(); + } imph.RecvHak(); himp.Release(imph); eset.Dispose(); - /***/ + /// //hexp.Release(exph); //himp.Release(imph); @@ -277,17 +269,15 @@ namespace Microsoft.Singularity.Applications.Benchmarks.DiskRWNull out ulong value) { // arg should look like "[-/][A-z]:[0-9]" - if (arg.Length >= 4) - { - try - { + if (arg.Length >= 4) { + try { value = UInt64.Parse(arg.Substring(3)); return true; } - catch (FormatException) - {} - catch (OverflowException) - {} + catch (FormatException) { + } + catch (OverflowException) { + } } Console.WriteLine("Could not parse {0}", name); value = 0; @@ -316,29 +306,24 @@ namespace Microsoft.Singularity.Applications.Benchmarks.DiskRWNull hexp = new TRef(exp); - if (blockBytes < 512 || (blockBytes & (blockBytes - 1)) != 0) - { + if (blockBytes < 512 || (blockBytes & (blockBytes - 1)) != 0) { Console.WriteLine("Block size must be a power of two greater than or equal to 512."); return -1; } - if (doRead == doWrite) - { + if (doRead == doWrite) { Console.WriteLine("Need to specify either read or write test."); return -1; } - else if (doRead) - { + else if (doRead) { diskReadTest = true; } - if (dataMB != 0) - { + if (dataMB != 0) { blockCount = dataMB * 1024 * 1024 / blockBytes; } - for (int run = 0; run < (int)repeatCount; run++) - { + for (int run = 0; run < (int)repeatCount; run++) { if (run != 0) System.Threading.Thread.Sleep(1000 * (int)pauseSecs); Tracing.Log(Tracing.Debug, "Starting diskrw run {0}", diff --git a/base/Applications/Benchmarks/diskrwnull/diskrwnull.csproj b/base/Applications/Benchmarks/diskrwnull/diskrwnull.csproj index 43dd557..d983cbb 100644 --- a/base/Applications/Benchmarks/diskrwnull/diskrwnull.csproj +++ b/base/Applications/Benchmarks/diskrwnull/diskrwnull.csproj @@ -1,8 +1,6 @@  + + + + + + Exe + bootCount + + + + + + + + + + + + + diff --git a/base/Applications/BounceBackTest/BounceBackClient.csproj b/base/Applications/BounceBackTest/BounceBackClient.csproj new file mode 100644 index 0000000..eb5f75a --- /dev/null +++ b/base/Applications/BounceBackTest/BounceBackClient.csproj @@ -0,0 +1,30 @@ + + + + + + + + Exe + BounceBackClient + + + + + + + + + + + + + + diff --git a/base/Applications/BounceBackTest/BounceBackClient.sg b/base/Applications/BounceBackTest/BounceBackClient.sg new file mode 100644 index 0000000..5e28ee1 --- /dev/null +++ b/base/Applications/BounceBackTest/BounceBackClient.sg @@ -0,0 +1,66 @@ +using System; +using Microsoft.Singularity.V1; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Security; +using Microsoft.Singularity.V1.Processes; +using Microsoft.Singularity.V1.Services; +using Microsoft.Singularity; + + +[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] +[assembly: System.Reflection.AssemblyKeyFileAttribute("public.snk")] +[assembly: System.Reflection.AssemblyDelaySignAttribute(true)] + +namespace Microsoft.Singularity.Applications +{ + public class BounceBackClient + { + public static int Main(String[] args) + { + DirectoryServiceContract.Imp! dirImp; + + dirImp = DirectoryService.NewClientEndpoint(); + string bindPath = "/BounceBackServer"; + + CalculatorContract.Imp! calculatorImp; + CalculatorContract.Exp! calculatorExp; + CalculatorContract.NewChannel(out calculatorImp, out calculatorExp); + + ErrorCode error; + if (!SdsUtils.Bind(bindPath, dirImp, calculatorExp, out error)) { + DebugStub.WriteLine("Bind to calculator server failed error {0}\n", + __arglist(SdsUtils.ErrorCodeToString(error))); + delete dirImp; + delete calculatorImp; + return -1; + } + + + calculatorImp.RecvSuccess(); + + for (int i = 0; i < 5; i++) { + DebugStub.WriteLine("Sending add {0} + {1}\n", __arglist(i, (i + 1))); + calculatorImp.SendAddInteger(i, i+1); + switch receive { + case calculatorImp.IntegerResult(result) : + DebugStub.WriteLine("received {0}\n", + __arglist(result)); + break; + + case calculatorImp.ChannelClosed() : + DebugStub.WriteLine("calculator channel closed\n"); + delete calculatorImp; + delete dirImp; + return -1; + break; + + } + } + + delete calculatorImp; + delete dirImp; + + return 0; + } + } +} diff --git a/base/Applications/BounceBackTest/BounceBackServer.csproj b/base/Applications/BounceBackTest/BounceBackServer.csproj new file mode 100644 index 0000000..5e739ff --- /dev/null +++ b/base/Applications/BounceBackTest/BounceBackServer.csproj @@ -0,0 +1,31 @@ + + + + + + + + Exe + BounceBackServer + Custom + + + + + + + + + + + + + + diff --git a/base/Applications/BounceBackTest/BounceBackServer.sg b/base/Applications/BounceBackTest/BounceBackServer.sg new file mode 100644 index 0000000..6079a1e --- /dev/null +++ b/base/Applications/BounceBackTest/BounceBackServer.sg @@ -0,0 +1,106 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// + +using System; +using System.Collections; +using Microsoft.SingSharp; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.V1; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Security; +using Microsoft.Singularity.V1.Processes; +using Microsoft.Singularity.V1.Services; +using Microsoft.Singularity; + +[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] +[assembly: System.Reflection.AssemblyKeyFileAttribute("public.snk")] +[assembly: System.Reflection.AssemblyDelaySignAttribute(true)] + +[assembly: ApplicationPublisherAttribute("singularity.microsoft.com")] +[assembly: AssertPrivilegeAttribute("$register-privilege.localhost")] + +namespace Microsoft.Singularity.Applications +{ + public class BounceBackServer + { + public static int Main(String[] args) + { + DirectoryServiceContract.Imp! dirImp; + + dirImp = DirectoryService.NewClientEndpoint(); + + ServiceProviderContract.Imp! nsImp; + ServiceProviderContract.Exp! nsExp; + ServiceProviderContract.NewChannel(out nsImp, out nsExp); + + string location = "/BounceBackServer"; + ErrorCode error; + if (!SdsUtils.Register(location, dirImp, nsImp, out error)) { + DebugStub.WriteLine("Failed to register in BSP namespace error {0}\n", + __arglist(SdsUtils.ErrorCodeToString(error))); + delete nsExp; + delete dirImp; + return -1; + } + + // Here is the set of client channels we service + ESet epSet = new ESet(); + while (true) { + switch receive { + + case nsExp.Connect(ServiceContract.Exp:Start! exp) : + CalculatorContract.Exp calculatorExp = exp as CalculatorContract.Exp; + if(calculatorExp == null) { + nsExp.SendNackConnect(exp); + } + else { + DebugStub.Print("Received new ServerControl\n"); + calculatorExp.SendSuccess(); + epSet.Add(calculatorExp); + nsExp.SendAckConnect(); + } + break; + + case ep.AddInteger(int first, int second) in epSet : + DebugStub.Print("Server received add integer request\n"); + int result = first + second; + ep.SendIntegerResult(result); + epSet.Add(ep); + break; + + case ep.SubtractInteger(int first, int second) in epSet : + DebugStub.Print("Server received subtract integer request\n"); + int result = first - second; + ep.SendIntegerResult(result); + epSet.Add(ep); + break; + + case ep.ChannelClosed() in epSet : + delete ep; + break; + + case epSet.Empty() && nsExp.ChannelClosed() : + delete nsExp; + epSet.Dispose(); + delete dirImp; + return -1; + break; + + case unsatisfiable : + DebugStub.Break(); + break; + } + } + delete dirImp; + + return 0; + } + } +} + diff --git a/base/Applications/BounceBackTest/CalculatorContract.sg b/base/Applications/BounceBackTest/CalculatorContract.sg new file mode 100644 index 0000000..e007fac --- /dev/null +++ b/base/Applications/BounceBackTest/CalculatorContract.sg @@ -0,0 +1,26 @@ +using System; +using Microsoft.SingSharp; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Directory; + +namespace Microsoft.Singularity.Applications +{ + public contract CalculatorContract : ServiceContract { + out message Success(); + + in message AddInteger(int first, int second); + out message IntegerResult(int answer); + + in message SubtractInteger(int first, int second); + + override state Start: one { + Success! -> Ready; + } + + state Ready: one { + AddInteger? -> IntegerResult! -> Ready; + + SubtractInteger? -> IntegerResult! -> Ready; + } + } +} diff --git a/base/Applications/CHello/CHello.csproj b/base/Applications/CHello/CHello.csproj index 63ef4a0..9a1aa70 100644 --- a/base/Applications/CHello/CHello.csproj +++ b/base/Applications/CHello/CHello.csproj @@ -1,8 +1,6 @@  - - - - - - Exe - CHello2 - - - true - true - - - - - - - - - diff --git a/base/Applications/CHello2/CHello2.sg b/base/Applications/CHello2/CHello2.sg deleted file mode 100644 index aec2237..0000000 --- a/base/Applications/CHello2/CHello2.sg +++ /dev/null @@ -1,708 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: CHello.cs -// -// Note: Simple Singularity test program. -// -using System; -using System.Runtime.CompilerServices; -using System.Threading; - -using Microsoft.Singularity; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.V1.Services; -using Microsoft.Singularity.Io; - -namespace Microsoft.Singularity.Applications -{ - public class CHello - { - public static int Main(String[] args) - { - Console.WriteLine("Hello World!"); - - ProtocolTester.UnitTest_LDPC o = new ProtocolTester.UnitTest_LDPC(); - - o.Run(); - - return 0; - } - } -} - -namespace Microsoft.SPOT -{ - /// - public abstract class Trace - { - /// - [System.Diagnostics.ConditionalAttribute("TINYCLR_TRACE")] - static public void Print( string text ) - { - Debug.Print( text ); - } - } - - /// - public abstract class Debug - { - static public void Print( string text ) - { - Console.WriteLine( text ); - } - - static public void DumpBuffer( byte[] buf, bool fCRC, bool fOffset ) - { - if(buf == null) return; - - int offset = 0; - int len = buf.Length; - - while(len > 0) - { - int row = len > 16 ? 16 : len; - int i; - - if(fOffset) Console.Write( "{0:X8}:", offset ); - - for(i=0; i<16; i++) - { - if(i= 0x7F) c = (byte)'.'; - - Console.Write( "{0}", (char)c ); - } - - offset += row; - len -= row; - - Console.WriteLine(); - } - - Console.WriteLine(); - } - } - - public abstract class Math - { - static System.Random s_rnd = new System.Random(); - - static public int Random( int modulo ) - { - return s_rnd.Next( modulo ); - } - } -} - -namespace Microsoft.SPOT.Hardware -{ - public abstract class Radio - { - static public void XorData( byte[]! left, byte[]! right ) - { - for(int i=0; i - { - T[]! elements; - int count; - - public CreateNonNullArray( int n ) - { - this.elements = new T[n]; - this.count = 0; - base(); - } - - public void InitNext( T elem ) - { - if(this.count < elements.Length) - { - this.elements[this.count++] = elem; - } - else - { - throw new InvalidOperationException( "Too many elements initialized" ); - } - } - - public T[]! GetArray() - { - if(count != elements.Length) - { - throw new InvalidOperationException("Not all elements initialized"); - } - - return this.elements; - } - } - - public class LDPC - { - public static T[]![]! ConstructArray( int i, int j ) - { - CreateNonNullArray nna = new CreateNonNullArray( i ); - - for(int k=0; k < i; k++) - { - nna.InitNext( new T[j] ); - } - - return nna.GetArray(); - } - - static public byte[]![]! Solve( byte[][] dataPackets, byte[][] eccPackets, int[] eccIndex, int totalEccPackets, int totalDataPackets, int missedDataPackets, int receivedEccPackets, int pktLen ) - { - // dataPackets x0 = actual N - k data packets received (blanks in the missing locations) - // eccPackets rhs = actual kp EC packets received - // eccIndex gotec = array contains indices of EC packet received - // totalEccPackets M = total length of EC stream - // totalDataPackets N = total length of data stream - // missedDataPackets kk = number of data packets missed - // receivedEccPackets kp = number of EC packets received - // AA = M * N matrix used to get the EC packets at encoder - - if(dataPackets == null) throw new NullReferenceException(); - if(eccPackets == null) throw new NullReferenceException(); - if(eccIndex == null) throw new NullReferenceException(); - - int maxMN = (totalEccPackets > totalDataPackets) ? totalEccPackets : totalDataPackets; - bool[] pseudo = new bool[maxMN]; - int i; - int j; - int k; - - for(i=0; i < maxMN; i += 5) pseudo[i] = true; - for(i=0; i < maxMN; i += 7) pseudo[i] = true; - for(i=0; i < maxMN; i += 13) pseudo[i] = true; - for(i=0; i < maxMN; i += 17) pseudo[i] = true; - for(i=0; i < maxMN; i += 19) pseudo[i] = true; - for(i=0; i < maxMN; i += 33) pseudo[i] = true; - for(i=0; i < maxMN; i += 37) pseudo[i] = true; - - //bool[][] A = new bool[receivedEccPackets][]; - //for(i=0; i < receivedEccPackets; i++) - //{ - // A[i] = new bool[missedDataPackets]; - //} // The actual matrix to be inverted (submatrix of AA) - bool[]![]! A = ConstructArray( receivedEccPackets, missedDataPackets ); - - int[]! miss = new int[missedDataPackets + 1]; // Indices of the missing data packets - - int c = 0; - - for(i = 0; i < missedDataPackets; i++) - { - while(c < totalDataPackets && dataPackets[c] != null) - { - c++; - } - - //Microsoft.SPOT.Debug.Print( "Missing: " + c ); - miss[i] = c++; - } - // Get miss array - miss[missedDataPackets] = -1; - - for(i=0; i < receivedEccPackets; i++) - { - byte[] t = eccPackets[i]; if(t == null) throw new NullReferenceException(); - - t = (byte[])t.Clone(); if(t == null) throw new NullReferenceException(); - - eccPackets[i] = t; - - // What's the actual system we have to solve - - for(j=0; j < missedDataPackets; j++) - { - int idx = eccIndex[i] + miss[j]; - - //A[i][j] = AA[eccIndex[i]][miss[j]]; - A[i][j] = pseudo[ idx < maxMN ? idx : idx - maxMN ]; - } - } - - for(j = 0; j < totalDataPackets; j++) - { - byte[] dataPacket = dataPackets[j]; - - if(dataPacket != null) - { - // Packet j is not a missing packet - for(i = 0; i < receivedEccPackets; i++) - { - int idx = eccIndex[i] + j; - - if(pseudo[ idx < maxMN ? idx : idx - maxMN ]) /*AA[eccIndex[i]][j]*/ - { - //Microsoft.SPOT.Debug.Print( "XOR: eccPackets[" + i + "], dataPackets[" + j + "]" ); - byte[] eccPacket = eccPackets[i]; - - if(eccPacket != null) - { - Microsoft.SPOT.Hardware.Radio.XorData( eccPacket, dataPacket ); - } - } - } - } - } - - //DumpMatrix( "Beginning:", A, kp, kk ); - - // Now set up some variables and do the LU decomposition - - //byte[][] y = new byte[missedDataPackets+1][]; // Intermediate solution - //byte[][] xhat = new byte[missedDataPackets+1][]; - // - // - //for(i=0; i < missedDataPackets+1; i++) - //{ - // y [i] = new byte[ pktLen ]; - // xhat[i] = new byte[ pktLen ]; - //} - byte[]![]! y = ConstructArray( missedDataPackets+1, missedDataPackets+1 ); - byte[]![]! xhat = ConstructArray( missedDataPackets+1, missedDataPackets+1 ); - - int i2; - int j2; - int p; - - //DumpMatrix( "Beginning:", A, kp, kk ); - - for(k = 0; k < missedDataPackets; k++) - { - p = k; - - // Find a remaining row with non-zero k-th element - while(A[p][k] == false) - { - p++; - - if(p == receivedEccPackets) - { - //Microsoft.SPOT.Debug.Print( "Non-triangular matrix?" ); - //return null; - throw new Exception( "Non-triangular matrix?" ); - } - } - - - if(p != k) - { - //Microsoft.SPOT.Debug.Print( "Swap: " + p + " " + k ); - // Swap row k and row p - for(j2 = 0; j2 < missedDataPackets; j2++) - { - bool temp = A[k][j2]; - A[k][j2] = A[p][j2]; - A[p][j2] = temp; - } - - byte[] packetTemp = eccPackets[k]; - eccPackets[k] = eccPackets[p]; - eccPackets[p] = packetTemp; - - //DumpMatrix( "After:", A, kp, kk ); - } - - for(i2 = k + 1; i2 < receivedEccPackets; i2++) - { - if(A[i2][k] == A[k][k]) - { - //Microsoft.SPOT.Debug.Print( "Mix rows: " + i2 + " " + k ); - for(j2 = k + 1; j2 < missedDataPackets; j2++) - { - A[i2][j2] ^= A[k][j2]; - } - - //DumpMatrix( "After:", A, kp, kk ); - } - } - } - //DumpMatrix( "After:", A, kp, kk ); - - ////////////////////////////////////////////////////////// - - // Solve Ly = Pb - - for(i2 = 0; i2 < missedDataPackets; i2++) - { - y[i2] = (byte[]!)eccPackets[i2]; - - for(j2 = 0; j2 < i2; j2++) - { - //y[i2] = (sbyte) (((y[i2] ^ (A[i2][j2] * y[j2])) & 0x000000ff)); - - if(A[i2][j2]) - { - //Microsoft.SPOT.Debug.Print( "XOR: y[" + i2 + "], y[" + j2 + "]" ); - Microsoft.SPOT.Hardware.Radio.XorData( y[i2], y[j2] ); - } - } - } - - // Now solve Ux = y - for(i2 = missedDataPackets - 1; i2 >= 0; i2--) - { - xhat[i2] = y[i2]; - - for(j2 = i2 + 1; j2 < missedDataPackets; j2++) - { - //xhat[i2] = (sbyte) (((xhat[i2] ^ (A[i2][j2] * xhat[j2])) & 0x000000ff)); - if(A[i2][j2]) - { - //Microsoft.SPOT.Debug.Print( "XOR: xhat[" + i2 + "], xhat[" + j2 + "]" ); - Microsoft.SPOT.Hardware.Radio.XorData( xhat[i2], xhat[j2] ); - } - } - } - - for(i2 = 0; i2 < missedDataPackets; i2++) - { - // Rogue value to indicate non-invertible - if(A[i2][i2] == false) - { - //return null; - throw new Exception( "Non-invertible matrix?" ); - } - } - - return xhat; - } - - static public byte[][] GenerateEC( byte[][] dataPackets, int totalEccPackets, int totalDataPackets, int pktLen ) - { - int maxMN = (totalEccPackets > totalDataPackets) ? totalEccPackets : totalDataPackets; - int i; - int j; - - if(dataPackets == null) throw new NullReferenceException(); - - bool[] pseudo = new bool[maxMN]; - - for(i=0; i < maxMN; i += 5) pseudo[i] = true; - for(i=0; i < maxMN; i += 7) pseudo[i] = true; - for(i=0; i < maxMN; i += 13) pseudo[i] = true; - for(i=0; i < maxMN; i += 17) pseudo[i] = true; - for(i=0; i < maxMN; i += 19) pseudo[i] = true; - for(i=0; i < maxMN; i += 33) pseudo[i] = true; - for(i=0; i < maxMN; i += 37) pseudo[i] = true; - - byte[][] eccPackets = new byte[totalEccPackets][]; - - for(i=0; i < totalEccPackets; i++) - { - eccPackets[i] = new byte[ pktLen ]; - } - - for(i=0; i < totalEccPackets; i++) - { - // Generate the EC stream - - for(j=0; j < totalDataPackets; j++) - { - //ec[i] = (sbyte) (((ec[i] ^ (AA[i][j] * x[j])) & 0x000000ff)); - - int idx = i + j; - - if(pseudo[ idx < maxMN ? idx : idx - maxMN ]) /*AA[i][j]*/ - { - byte[] eccPacket = eccPackets[i]; - byte[] dataPacket = dataPackets[j]; - - if(eccPacket != null && dataPacket != null) - { - Microsoft.SPOT.Hardware.Radio.XorData( eccPacket, dataPacket ); - } - } - } - } - - return eccPackets; - } - } -} - -namespace ProtocolTester -{ - public class UnitTest_LDPC - { - // main program written by hpleung - // Procedure: - // 1. Generate Random Data Unit - // 2. Generate Error Correction Unit - // 3. Inject Errors in The Random Data Unit - // 4. Decode - // 5. Make sure after decoding data is fully received - // Run it with run.exe nDataPacket nErrorCorrection nPercentageLoss nIteration - // - public void Run() - { - //for quick correctness verification - Microsoft.SPOT.Debug.Print( "LPDC Test Started" ); - -// for(int i=0; i<100; i++) -// { -// RunPass( 40, 40, 40, 106 ); -// } -// return; - for(int nPacketLength=10; nPacketLength <= 120; nPacketLength = nPacketLength + 10) - { - RunPass( nPacketLength, nPacketLength, 40, 106 ); - } - } - - public void RunPass( int nDataPacketCount, int nErrorCorrectionCount, int nPercentageLoss, int nPacketSize ) - { - byte[][] arrDataPacket = new byte[nDataPacketCount][]; - byte[][] arrDataReceived = new byte[nDataPacketCount][]; - byte[][] arrErrorCorrectionStream = null; - int nDataLossCount = 0; - int i; - - //randomly generate some data - PopulatePacket( arrDataPacket, nPacketSize ); - - for(i=0; i= nPacketToLossFix) - { - arrDataReceived[i] = arrDataPacket[i]; - } - else - { - arrDataReceived[i] = null; - } - } -#else - int nPacketToGet = nDataPacketCount - nPacketToLossFix; - while(nPacketToGet > 0) - { - i = Microsoft.SPOT.Math.Random( nDataPacketCount - 1 ); - //Microsoft.SPOT.Debug.Print( "Picked " + i ); - - if(arrDataReceived[i] == null) - { - arrDataReceived[i] = arrDataPacket[i]; - nPacketToGet--; - } - } -#endif - - for(i=0; i (nDataLossCount + 5) * 100) - { - nErrorPacketToReceive = (int)((nDataLossCount * 108)/100) ; - } - else - { - nErrorPacketToReceive = nDataLossCount + 5; - } - - if(nErrorPacketToReceive > nErrorCorrectionCount) - { - nErrorPacketToReceive = nErrorCorrectionCount; - } - - int[]! arrECPacketReceived = new int [nErrorPacketToReceive]; - byte[][] arrRHS = new byte[nErrorPacketToReceive][]; - - // Randomly Pick up Error Packets - for(i=0; i Stdin; @@ -47,6 +45,9 @@ namespace Microsoft.Singularity.Applications [OutputEndpoint("data")] public readonly TRef Stdout; + [Endpoint] + public readonly TRef nsRef; + [StringParameter( "d", Mandatory=true, HelpMessage="Directory in which to gen files")] internal string directory; @@ -173,16 +174,17 @@ public class FILE // TODO get { long size; - NodeType nodeType; - ErrorCode error; + FileAttributesRecord fileAttributes; + ErrorCode error; DirectoryServiceContract.Imp! rootNS = DirectoryService.NewClientEndpoint(); - bool ok = FileUtils.GetAttributes(path, rootNS, out size, out nodeType, out error); - delete rootNS; + bool ok = FileUtils.GetAttributes(path, rootNS, out fileAttributes, out error); + delete rootNS; if (!ok) { throw new Exception("Failed to get attributes"); - } else { - return unchecked((int)size); + } + else { + return unchecked((int)fileAttributes.FileSize); } } } @@ -208,7 +210,7 @@ public class CadGen99 private const int CADFILE_ENTRIES = 360; private const int MAX_CAD_WEIGHT = 75; - private const string AdsFile = "Custom.Ads"; + private static readonly string AdsFile = "Custom.Ads"; private static void Usage() { @@ -221,18 +223,18 @@ public class CadGen99 internal static void AppMain(Parameters! config) { - - string! directory; + + string /*!*/ directory = ""; int pttime = 0; int maxthread = 0; - + int [] expired = new int[2]; - + pttime = (int) config.pttime; - maxthread = (int) config.maxthread; - expired[0] = (int) config.expiredAd1; - expired[1] = (int) config.expiredAd2; - directory = config.directory; + maxthread = (int) config.maxthread; + expired[0] = (int) config.expiredAd1; + expired[1] = (int) config.expiredAd2; + directory = config.directory; if (pttime == 0) { pttime = 1800; @@ -259,29 +261,29 @@ public class CadGen99 int j; int i; - for (j = 0; j> 4) & 0xf; int c2 = (k >> 8) & 0xf; - // int c1 = (k >> 12) & 0xf; // XXX not used in test + // int c1 = (k >> 12) & 0xf; // XXX [oh]: not used in test int gender = (k) % 2; - if ( gender == 1 ) gender = 0x20000000; + if (gender == 1) gender = 0x20000000; else gender = 0x10000000; int age = (k) % 4; - if ( age == 0 ) age = 0x08000000; - else if ( age == 1 ) age = 0x04000000; - else if ( age == 2 ) age = 0x02000000; - else if ( age == 3 ) age = 0x01000000; + if (age == 0) age = 0x08000000; + else if (age == 1) age = 0x04000000; + else if (age == 2) age = 0x02000000; + else if (age == 3) age = 0x01000000; int area = (c4 + c2) % 4; - if ( area == 0 ) area = 0x00800000; - else if ( area == 1 ) area = 0x00400000; - else if ( area == 2 ) area = 0x00200000; - else if ( area == 3 ) area = 0x00100000; + if (area == 0) area = 0x00800000; + else if (area == 1) area = 0x00400000; + else if (area == 2) area = 0x00200000; + else if (area == 3) area = 0x00100000; int interest1 = (c4 + c3) % 10; if (interest1 == 0) interest1 = 0x00080000; @@ -297,7 +299,7 @@ public class CadGen99 int interest2 = c4 % 10; - if (interest2 == 0) interest2 = 0x00000200; + if (interest2 == 0) interest2 = 0x00000200; else if (interest2 == 1) interest2 = 0x00000100; else if (interest2 == 2) interest2 = 0x00000080; else if (interest2 == 3) interest2 = 0x00000040; @@ -320,7 +322,7 @@ public class CadGen99 expired_date[j] = ts; } - for (j=0; j < CADFILE_ENTRIES; j++) { + for (j = 0; j < CADFILE_ENTRIES; j++) { if (j == match1) { minmatch[j] = 1; weight[j] = 0x3ffff; @@ -329,9 +331,9 @@ public class CadGen99 } } - /* Mark two ranges as expired */ + // Mark two ranges as expired j = expired[0]; - for (i=0; i CADFILE_ENTRIES - 1) expired_date[j-CADFILE_ENTRIES] = exp_ts; else @@ -339,7 +341,7 @@ public class CadGen99 j = j+1; } j = expired[1]; - for (i=0; i CADFILE_ENTRIES - 1) expired_date[j-CADFILE_ENTRIES] = exp_ts; else diff --git a/base/Applications/ChildPing/ChildPing.csproj b/base/Applications/ChildPing/ChildPing.csproj index 2dc2914..a1261b1 100644 --- a/base/Applications/ChildPing/ChildPing.csproj +++ b/base/Applications/ChildPing/ChildPing.csproj @@ -1,31 +1,30 @@ - - - - - Exe - ChildPing - + + + ChildPing + Exe + + - + - + + + - - + diff --git a/base/Applications/ChildPing/ChildPing.sg b/base/Applications/ChildPing/ChildPing.sg index f0846a9..138cbe4 100644 --- a/base/Applications/ChildPing/ChildPing.sg +++ b/base/Applications/ChildPing/ChildPing.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: ChildPing.sg -// // Note: Simple ping-pong test child app #1 // @@ -61,7 +59,7 @@ namespace Microsoft.Singularity.PingPong PongContract.NewChannel(out childImp, out childExp); string[] args = new string[1]; - args[0] = "ChildPong.x86"; + args[0] = "ChildPong"; Process child = new Process(args, (Endpoint * in ExHeap)childExp); child.Start(); diff --git a/base/Applications/ChildPong/ChildPong.csproj b/base/Applications/ChildPong/ChildPong.csproj index bd474c0..f17d48d 100644 --- a/base/Applications/ChildPong/ChildPong.csproj +++ b/base/Applications/ChildPong/ChildPong.csproj @@ -1,31 +1,30 @@ - - - - - Exe - ChildPong - + + + ChildPong + Exe + + - + - + + + - - + diff --git a/base/Applications/ChildPong/ChildPong.sg b/base/Applications/ChildPong/ChildPong.sg index 2af2bee..6429a90 100644 --- a/base/Applications/ChildPong/ChildPong.sg +++ b/base/Applications/ChildPong/ChildPong.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: ChildPong.sg -// // Note: Simple ping-pong second child process // @@ -57,7 +55,7 @@ namespace Microsoft.Singularity.PingPong conn.SendPongReady(); try { - while(true) { + while (true) { switch receive { case conn.Ping(int data): conn.SendPong(data); diff --git a/base/Applications/Contracts/Counter.Contracts/CounterContract.sg b/base/Applications/Contracts/Counter.Contracts/CounterContract.sg index abfac1e..5707aa8 100644 --- a/base/Applications/Contracts/Counter.Contracts/CounterContract.sg +++ b/base/Applications/Contracts/Counter.Contracts/CounterContract.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Contracts\Test.Contracts\CounterContract.sg -// // Note: // using System; diff --git a/base/Applications/CredentialsControl/CredentialsControl.csproj b/base/Applications/CredentialsControl/CredentialsControl.csproj index 5cdaf56..64e8486 100644 --- a/base/Applications/CredentialsControl/CredentialsControl.csproj +++ b/base/Applications/CredentialsControl/CredentialsControl.csproj @@ -1,8 +1,6 @@  + + + + + + Exe + DebugPipe + + + + + + + + + diff --git a/base/Applications/EmailServer/AntiVirus/AntiVirus.csproj b/base/Applications/EmailServer/AntiVirus/AntiVirus.csproj new file mode 100644 index 0000000..9ebbf68 --- /dev/null +++ b/base/Applications/EmailServer/AntiVirus/AntiVirus.csproj @@ -0,0 +1,41 @@ + + + + + + + + AntiVirus + Exe + true + true + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/EmailServer/AntiVirus/AntiVirus.sg b/base/Applications/EmailServer/AntiVirus/AntiVirus.sg new file mode 100644 index 0000000..2686da3 --- /dev/null +++ b/base/Applications/EmailServer/AntiVirus/AntiVirus.sg @@ -0,0 +1,419 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: Anti-virus scanner. +// + +using System; +using System.Collections; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading; + +using Microsoft.SingSharp; +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Crypto; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Email.Contracts; +using Microsoft.Singularity.Endpoint; +using Microsoft.Singularity.Extending; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Security; +using Microsoft.Singularity.V1.Services; + +[assembly: Transform(typeof(ApplicationResourceTransform))] +[assembly: ApplicationPublisherAttribute("singularity.microsoft.com")] +[assembly: AssertPrivilegeAttribute("$register-privilege.localhost")] + +namespace Microsoft.Singularity.Email +{ + // VirusDef + // + // This class holds the information from the virus definition files, + // such as the virus name, pattern, and type of file to which the + // definition applies. Some virus definitions refer to MD5 hashes, + // in which case the pattern represents the hash. + + class VirusDefError : ApplicationException { } + + class VirusDef + { + public VirusDef(string name, int length, Pattern pattern) + { + Name = name; + Type = TargetType.TYPE_ANY; + IsMD5 = true; + AnyOffset = false; + Offset = 0; + Length = length; + Pattern = pattern; + } + + public VirusDef(string name, int type, string! offset, Pattern pattern) + { + Name = name; + Type = (TargetType)type; + IsMD5 = false; + Length = 0; + Pattern = pattern; + + if (offset == "*") { + AnyOffset = true; + Offset = 0; + } else if (offset.Length >= 4 && offset.Substring(0, 4) == "EOF-") { + AnyOffset = false; + Offset = Int32.Parse(offset.Substring(3)); + } else if (Type != TargetType.TYPE_PE && Type != TargetType.TYPE_ELF) { + AnyOffset = false; + Offset = Int32.Parse(offset); + } else { + AnyOffset = false; + Offset = 0; + } + } + + public enum TargetType + { + TYPE_ANY = 0, + TYPE_PE = 1, + TYPE_OLE = 2, + TYPE_HTML = 3, + TYPE_MAIL = 4, + TYPE_GRAPHICS = 5, + TYPE_ELF = 6, + } + + public string Name; + public TargetType Type; + public bool IsMD5; + public bool AnyOffset; + public int Offset; + public int Length; + public Pattern Pattern; + } + + // AntiVirus + // + // This class contains the main appliation logic for the virus scanner. + // We load three virus definition files from disk, set up the pattern + // matcher, and then wait for incoming requests for virus checking. + + [ConsoleCategory(HelpMessage="Anti-Virus Service", DefaultAction=true)] + internal class Parameters + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [Endpoint] + public readonly TRef nsRef; + + [StringParameter("db", Mandatory=false, Default="/init/virus.db", + HelpMessage="File with universal virus signatures (.db).")] + internal string dbfile; + + [StringParameter("hdb", Mandatory=false, Default="/init/virus.hdb", + HelpMessage="File with h virus signatures (.hdb).")] + internal string hdbfile; + + [StringParameter("ndb", Mandatory=false, Default="/init/virus.ndb", + HelpMessage="File with n virus signatures (.ndb).")] + internal string ndbfile; + + reflective internal Parameters(); + + internal int AppMain() { + return AntiVirus.AppMain(this); + } + } + + class AntiVirus + { + internal static int AppMain(Parameters! config) + { + // Publish our service endpoint before loading virus definitions as + // they can take minutes to load. + DirectoryServiceContract.Imp ds = (config.nsRef).Acquire(); + if (ds == null) { + throw new Exception("AntiVirus: Unable to acquire handle to Directory Service."); + } + ds.RecvSuccess(); + + ServiceProviderContract.Imp! nsImp; + ServiceProviderContract.Exp! nsExp; + ServiceProviderContract.NewChannel(out nsImp, out nsExp); + + try { + ds.SendRegister(Bitter.FromString2(AntiVirusContract.ModuleName), nsImp); + + switch receive { + case ds.AckRegister() : + // All is well. + break; + + case ds.NakRegister(ServiceProviderContract.Imp:Start rejectedEP, error) : + // All is very much not well; abort. + Console.WriteLine("AntiVirus: Failed to register endpoint as {0}.", + AntiVirusContract.ModuleName); + delete nsExp; + delete rejectedEP; + return -1; + + case ds.ChannelClosed(): + Console.WriteLine("AntiVirus: ds channel closed"); + delete nsExp; + return -1; + } + } + finally { + delete ds; + } + + Console.WriteLine("AntiVirus: Loading virus definition files."); + LoadDefinitions(config.dbfile); + LoadDefinitions(config.hdbfile); + LoadDefinitions(config.ndbfile); + + PrepareMatcher(); + + Console.WriteLine("AntiVirus: Ready at {0}", AntiVirusContract.ModuleName); + + // Here is the set of client channels we service + + for (;;) { + switch receive { + // ------------------------------- Requests for new connections + + case nsExp.Connect(ServiceContract.Exp:Start! newEp): + // We expect people top give us AntiVirusContract.Exp instances + AntiVirusContract.Exp newClient = newEp as AntiVirusContract.Exp; + + if (newClient == null) { + // Invalid contract type. Fail. + nsExp.SendNackConnect(newEp); + } + else { + // Signal ready and start servicing this contract + nsExp.SendAckConnect(); + + // Launch the thread to handle the incoming requests. + AntiVirus child = new AntiVirus(newClient); + Thread thread = new Thread(child.Run); + thread.Start(); + } + break; + + case nsExp.ChannelClosed(): + // The namespace channel is closed so quit. + delete nsExp; + return -1; + } + } + return 0; + } + + ////////////////////////////////////////////////////////////////////////////// + // + public AntiVirus([Claims]AntiVirusContract.Exp:Start! ep) + { + epRef = new TRef(ep); + } + + private TRef epRef; + + public void Run() + { + AntiVirusContract.Exp ep = epRef.Acquire(); + + if (ep == null) { + return; + } + + ep.SendAntiVirusReady(); + int arg; + + for (;;) { + switch receive { + case ep.CheckFile(byte[]! in ExHeap buffer): +#if false + Console.WriteLine("AnitVirus: CheckFile"); +#endif + // decide if the buffer contains a virus + // possible crack the MIME content. + string virus = CheckData(Bitter.ToByteArray(buffer)); + + delete buffer; + if (virus != null) { + ep.SendFileContaminated(Bitter.FromString2(virus)); + } + else { + ep.SendFileClean(); + } + break; + + case ep.ChannelClosed(): + delete ep; + return; + } + } + } + + delegate VirusDef VirusDefParser(string! line); + + static void LoadDefinitions(string! filename) + { + VirusDefParser parser = null; + + string ext = GetExtension(filename); + switch (ext) { + case "db": + parser = LoadDB; + break; + case "hdb": + parser = LoadHDB; + break; + case "ndb": + parser = LoadNDB; + break; + default: + Console.WriteLine("Invalid definition file {0}.", filename); + break; + } + + if (parser != null) { + FileStream fsInput = new FileStream(filename, FileMode.Open, FileAccess.Read); + StreamReader reader = new StreamReader(fsInput); + // StreamReader reader = new StreamReader(filename); + string line; + int linenum = 1; + + while ((line = reader.ReadLine()) != null) { + try { + VirusDef def = parser(line); + if (def != null) { + defs.Add(def); + } + } catch (VirusDefError) { + Console.WriteLine("Invalid virus definition at {0}:{1}.", filename, linenum); + } + linenum++; + } + } + } + + static string GetExtension(string! filename) + { + int dot = filename.LastIndexOf('.'); + return (dot >= 0) ? filename.Substring(dot + 1) : null; + } + + static VirusDef LoadDB(string! line) + { + VirusDef def = null; + + string[] split = line.Split(new char[] { '=' }); + if (split.Length == 2) { + string name = (!)split[0]; + string pattern = (!)split[1]; + def = new VirusDef(name, 0, "*", parser.Parse(name, pattern)); + } else { + throw new VirusDefError(); + } + + return def; + } + + static VirusDef LoadHDB(string! line) + { + VirusDef def = null; + + string[] split = line.Split(new char[] { ':' }); + if (split.Length == 3) { + string hash = (!)split[0]; + string length = (!)split[1]; + string name = (!)split[2]; + def = new VirusDef(name, Int32.Parse(length), parser.Parse(name, hash)); + } else { + throw new VirusDefError(); + } + + return def; + } + + static VirusDef LoadNDB(string! line) + { + VirusDef def = null; + + string[] split = line.Split(new char[] { ':' }); + if (split.Length >= 4 && split.Length <= 6) { + string name = (!)split[0]; + string type = (!)split[1]; + string offset = (!)split[2]; + string pattern = (!)split[3]; + + def = new VirusDef(name, Int32.Parse(type), offset, parser.Parse(name, pattern)); + + // Discard PE, ELF, and HTML definitions for now + if (def.Type == VirusDef.TargetType.TYPE_PE || + def.Type == VirusDef.TargetType.TYPE_ELF || + def.Type == VirusDef.TargetType.TYPE_HTML) { + def = null; + } + } else { + throw new VirusDefError(); + } + + return def; + } + + static void PrepareMatcher() + { + // TODO: Handle Length & Type fields. + + foreach (VirusDef! def in defs) { + if (def.IsMD5) { + Debug.Assert(!def.AnyOffset); + md5Matcher.AddPattern(def.Pattern, def.Offset); + } else { + if (def.AnyOffset) { + matcher.AddPattern(def.Pattern); + } else { + matcher.AddPattern(def.Pattern, def.Offset); + } + } + } + } + + static string CheckData(byte[]! data) + { + byte[] hash = (!)hasher.Hash(data); + + string found; + + found = md5Matcher.Match(hash); + if (found == null) { + found = matcher.Match(data); + } + + return found; + } + + static private MD5 hasher = new MD5(); + + static private ArrayList defs = new ArrayList(); + + static private PatternParser parser = new PatternParser(); + + static private PatternMatcher matcher = new PatternMatcher(); + static private PatternMatcher md5Matcher = new PatternMatcher(); + } +} diff --git a/base/Applications/EmailServer/AntiVirus/AntiVirusContracts.csproj b/base/Applications/EmailServer/AntiVirus/AntiVirusContracts.csproj new file mode 100644 index 0000000..62df35c --- /dev/null +++ b/base/Applications/EmailServer/AntiVirus/AntiVirusContracts.csproj @@ -0,0 +1,29 @@ + + + + + + + Library + AntiVirusContracts + + + + + + + + + + + + + diff --git a/base/Applications/EmailServer/AntiVirus/AntiVirusContracts.sg b/base/Applications/EmailServer/AntiVirus/AntiVirusContracts.sg new file mode 100644 index 0000000..50d243c --- /dev/null +++ b/base/Applications/EmailServer/AntiVirus/AntiVirusContracts.sg @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: Contract used by SMTP Agents to talk to storage. +// + +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Endpoint; + +using Microsoft.Singularity.Extending; + +namespace Microsoft.Singularity.Email.Contracts +{ + public contract AntiVirusContract : ExtensionContract //ServiceContract + { + public const string ModuleName = "/service/antivirus"; + + out message AntiVirusReady(); + + in message CheckFile(byte[]! in ExHeap buffer); + out message FileClean(); + out message FileContaminated(char[]! in ExHeap virus); + + override state Start : AntiVirusReady! -> ReadyState; + state ReadyState : CheckFile? -> (FileClean! or FileContaminated!) -> ReadyState; + + } +} diff --git a/base/Applications/EmailServer/AntiVirus/Matcher.sg b/base/Applications/EmailServer/AntiVirus/Matcher.sg new file mode 100644 index 0000000..c2e8bbf --- /dev/null +++ b/base/Applications/EmailServer/AntiVirus/Matcher.sg @@ -0,0 +1,312 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: Anti-virus pattern matcher. +// + +using System; +using System.Collections; +using System.Diagnostics; +using System.IO; +using System.Text; + +namespace Microsoft.Singularity.Email +{ + // FutureTable + // + // The future table stores a list of patterns that we should try to match + // against an upcoming range of bytes in the input. This range is + // indicated by lower and upper bounds (inclusive). + // + // Callers can add patterns to the table with AddPattern, and they can + // match against the patterns in the table by calling Start. + // + // Internally, the future table stores a list of futures sorted by lower + // bound. + + class Future + { + public Future(Pattern pattern, int lower, int upper) + { + Pattern = pattern; + Lower = lower; + Upper = upper; + } + + public Pattern Pattern; + public int Lower; + public int Upper; + + public Future Next; + } + + class FutureTable + { + // AddPattern + // + // Add a pattern with specified file offset bounds to the table. + // We first attempt to consolidate with any overlapping entries for + // this pattern that are already in the table. We then insert the + // new future at the appropriate location. + + public void AddPattern(Pattern pattern, int lower, int upper) + { + Future prev; + Future cur; + + // Remove any overlapping ranges for this pattern, updating lower and upper as appropriate. + for (prev = null, cur = head; + cur != null && cur.Lower - 1 <= upper; + prev = cur, cur = cur.Next) { + // If the new pattern overlaps with an existing one... + if (pattern == cur.Pattern && lower - 1 <= cur.Upper) { + // Remove it. + if (prev != null) { + prev.Next = cur.Next; + } else { + head = cur.Next; + } + // Widen the bounds if necessary. + lower = Math.Min(lower, cur.Lower); + upper = Math.Max(upper, cur.Upper); + } + } + + // Find the location where we should insert this new item. + for (prev = null, cur = head; + cur != null && cur.Lower < lower; + prev = cur, cur = cur.Next) { + // Do nothing; we're just searching for the insert location. + } + + // Insert the item. + Future item = new Future(pattern, lower, upper); + item.Next = cur; + if (prev != null) { + prev.Next = item; + } else { + head = item; + } + } + + // Start + // + // Given an input file and an index in that file, try to match all + // applicable patterns. We use the fact that the list of futures + // is sorted in order to avoid trying to match against all patterns. + // We also discard any patterns that will no longer be matched. + // + // We assume that the index will increase with each call to Start. + + public void Start(byte[]! input, int index, EnqueueDelegate! enqueue) + { + ArrayList runnable = new ArrayList(); + + for (Future prev = null, cur = head; + cur != null && cur.Lower <= index; + prev = cur, cur = cur.Next) { + if (index <= cur.Upper) { + // The index is between lower and upper, so check this pattern. + runnable.Add(cur.Pattern); + } else { + // This pattern is no longer relevant; remove it. + if (prev != null) { + prev.Next = cur.Next; + } else { + head = cur.Next; + } + } + } + + foreach (Pattern! pattern in runnable) { + pattern.Match(input, index, enqueue); + } + } + + public void Clear() + { + head = null; + } + + public int Count() + { + int i = 0; + for (Future cur = head; cur != null; cur = cur.Next) { + i += 1; + } + return i; + } + + // List of future patterns to match, sorted by lower bound. + private Future head; + } + + // OffsetTable + // + // The offset table stores patterns that should be applied at a particular + // offset in the file. This offset can be positive or negative, where + // the latter indicates an offset from the end of the file. + // + // Callers can add pattersn to the table with AddPattern, and they can + // match against patterns in the table with Start. + + class OffsetTable + { + public void AddPattern(Pattern pattern, int offset) + { + if (table[offset] == null) { + table[offset] = new ArrayList(); + } + + ArrayList list = (!)(table[offset] as ArrayList); + list.Add(pattern); + } + + public void Start(byte[]! input, int index, EnqueueDelegate! enqueue) + { + StartList(table[index] as ArrayList, input, index, enqueue); + StartList(table[index - input.Length] as ArrayList, input, index, enqueue); + } + + private void StartList(ArrayList list, byte[]! input, int index, EnqueueDelegate! enqueue) + { + if (list != null) { + foreach (Pattern! pattern in list) { + pattern.Match(input, index, enqueue); + } + } + } + + private Hashtable table = new Hashtable(); + } + + // StartTable + // + // The start table stores patterns that should be applied at *any* offset + // in the input file. For efficiency, we construct an n-level table + // mapping the first n bytes of the input to the relevant set of patterns. + // If a pattern doesn't start with a sequence of n bytes (e.g., it has a + // wildcard in front), we'll look for a shorter prefix. + // + // Callers can add patterns to the table with AddPattern, and they can + // match against the patterns in the table with Start. + + class StartTable + { + public StartTable(int d) + { + Debug.Assert(d >= 0); + depth = d; + } + + public void AddPattern(Pattern pattern) + { + AddPattern(pattern, 0); + } + + private void AddPattern(Pattern pattern, int offset) + { + SeqPattern seq = pattern as SeqPattern; + if (depth > 0 && seq != null && offset < seq.Bytes.Length) { + byte b = seq.Bytes[offset]; + if (table == null) { + table = new StartTable[256]; + } + if (table[b] == null) { + table[b] = new StartTable(depth - 1); + } + StartTable subTable = (!)table[b]; + subTable.AddPattern(pattern, offset + 1); + } else { + patterns.Add(pattern); + } + } + + public void Start(byte[]! input, int index, EnqueueDelegate! enqueue) + { + Start(input, index, 0, enqueue); + } + + private void Start(byte[]! input, int index, int offset, EnqueueDelegate! enqueue) + { + foreach (Pattern! pattern in patterns) { + pattern.Match(input, index, enqueue); + } + + if (index + offset < input.Length) { + byte b = input[index + offset]; + if (table != null && table[b] != null) { + StartTable subTable = (!)table[b]; + subTable.Start(input, index, offset + 1, enqueue); + } + } + } + + private int depth; + private StartTable[] table; + private ArrayList patterns = new ArrayList(); + } + + // PatternMatcher + // + // The pattern matcher stores one of each of the above tables: + // + // FutureTable: Pattern matches in progress. + // OffsetTable: Patterns to be matched at a specific offset. + // StartTable: Patterns to be matched at every offset. + // + // We set up the matcher by calling AddPattern, which adds patterns + // to the offset table or the start table. We then call Match to + // match an input array against the provided patterns. + + class PatternMatcher + { + public void AddPattern(Pattern pattern) + { + startTable.AddPattern(pattern); + } + + public void AddPattern(Pattern pattern, int offset) + { + offsetTable.AddPattern(pattern, offset); + } + + public string Match(byte[]! input) + { + found = null; + futureTable.Clear(); + + int length = input.Length; + for (int index = 0; found == null && index < length; index++) { + startTable.Start(input, index, Enqueue); + offsetTable.Start(input, index, Enqueue); + futureTable.Start(input, index, Enqueue); + } + + futureTable.Clear(); + + return found; + } + + private void Enqueue(Pattern pattern, int lower, int upper) + { + Debug.Assert(pattern != null); + + if (pattern is EmptyPattern) { + found = ((EmptyPattern)pattern).Name; + } else { + futureTable.AddPattern(pattern, lower, upper); + } + } + + private string found; + + const int TableDepth = 2; + private StartTable startTable = new StartTable(TableDepth); + private OffsetTable offsetTable = new OffsetTable(); + private FutureTable futureTable = new FutureTable(); + } +} diff --git a/base/Applications/EmailServer/AntiVirus/Pattern.sg b/base/Applications/EmailServer/AntiVirus/Pattern.sg new file mode 100644 index 0000000..ff3f71b --- /dev/null +++ b/base/Applications/EmailServer/AntiVirus/Pattern.sg @@ -0,0 +1,368 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: Anti-virus patterns. +// + +using System; +using System.Collections; +using System.Diagnostics; +using System.IO; +using System.Text; + +namespace Microsoft.Singularity.Email +{ + // Patterns + // + // A pattern is a restricted regular expression that can be matched + // against a location in an input file. It is represented as a linked + // list of Pattern subclasses that should be matched in sequence. + // + // EmptyPattern: + // Terminates every pattern, indicating successful match. + // Stores the name of the matched virus. + // + // SeqPattern: + // Holds a sequence of bytes that must be matched in order. + // + // AltPattern: + // Holds an array of bytes, one of which must match the next + // input byte. + // + // WildPattern: + // Matches an arbitrary sequence of bytes whose length is between + // lower and upper (inclusive). + // + // NibblePattern: + // Matches a masked version of the next input byte. + // + // Each pattern contains a Match method, which matches the pattern + // against an input array at a given index. If the pattern matches, it + // should call the "enqueue" delegate to queue up the next pattern in + // the list for matching. This delegate is also given the lower and + // upper bounds (inclusive) where the next pattern might potentially + // start. + + class PatternError : ApplicationException + { + public PatternError(string message) { } + public PatternError(string message, Object o1) + : this(String.Format(message, o1)) { } + public PatternError(string message, Object o1, Object o2) + : this(String.Format(message, o1, o2)) { } + } + + delegate void EnqueueDelegate(Pattern pattern, int lower, int upper); + + abstract class Pattern + { + public Pattern Next; + + abstract public void Match(byte[]! input, int index, EnqueueDelegate! enqueue); + + virtual public string GetName() + { + return Next != null ? Next.GetName() : null; + } + } + + class EmptyPattern : Pattern + { + public EmptyPattern(string name) + { + Name = name; + } + + override public void Match(byte[]! input, int index, EnqueueDelegate! enqueue) + { + Debug.Assert(false); + } + + override public string GetName() + { + return Name; + } + + public string Name; + } + + class SeqPattern : Pattern + { + public SeqPattern(byte[] bytes) + { + Bytes = bytes; + } + + override public void Match(byte[]! input, int index, EnqueueDelegate! enqueue) + { + int patternLength = Bytes.Length; + int inputLength = input.Length; + + if (index + patternLength <= inputLength) { + for (int i = 0; i < patternLength; i++) { + if (input[index + i] != Bytes[i]) { + return; + } + } + + enqueue(Next, index + patternLength, index + patternLength); + } + } + + public byte[] Bytes; + } + + class AltPattern : Pattern + { + public AltPattern(byte[] options) + { + Options = options; + } + + override public void Match(byte[]! input, int index, EnqueueDelegate! enqueue) + { + int length = Options.Length; + for (int i = 0; i < length; i++) { + if (input[index] == Options[i]) { + enqueue(Next, index + 1, index + 1); + return; + } + } + } + + public byte[] Options; + } + + class WildPattern : Pattern + { + public WildPattern(int lower, int upper) + { + Lower = lower; + Upper = upper; + } + + override public void Match(byte[]! input, int index, EnqueueDelegate! enqueue) + { + // Make sure upper doesn't overflow. + int lower = index + Lower; + int upper = index <= Int32.MaxValue - Upper ? index + Upper : Int32.MaxValue; + enqueue(Next, lower, upper); + + // If the lower bound is zero, we need to try matching with the next pattern, too. + if (Lower == 0) { + Next.Match(input, index, enqueue); + } + } + + public int Lower; + public int Upper; + } + + class NibblePattern : Pattern + { + public NibblePattern(int mask, int val) + { + Mask = mask; + Value = val; + } + + override public void Match(byte[]! input, int index, EnqueueDelegate! enqueue) + { + if ((input[index] & Mask) == Value) { + enqueue(Next, index + 1, index + 1); + } + } + + public int Mask; + public int Value; + } + + // PatternParser + // + // Given the name of a virus and an input pattern, construct a Pattern + // object as described above. + + class PatternParser + { + public Pattern Parse(string name, string! input) + { + Debug.Assert(curPattern == null); + + int index = 0; + + while (index < input.Length) { + char cur = input[index]; + if (cur == '*') { + Add(new WildPattern(0, Int32.MaxValue)); + index += 1; + } else if (cur == '?') { + char next = input[index + 1]; // TODO: exists? + if (next == '?') { + Add(new WildPattern(1, 1)); + } else if (IsHex(next)) { + Add(new NibblePattern(0xf, ToByte(next))); + } else { + throw new PatternError("Invalid byte: {0}{1}", cur, next); + } + index += 2; + } else if (IsHex(cur)) { + char next = input[index + 1]; // TODO: exists? + if (next == '?') { + Add(new NibblePattern(0xf0, ToByte(cur))); + } else if (IsHex(next)) { + AddByte(ToByte(cur, next)); + } else { + throw new PatternError("Invalid byte: {0}{1}", cur, next); + } + index += 2; + } else if (cur == '(') { + Debug.Assert(byteOptions.Count == 0); + + do { + index += 1; + char first = input[index]; + char second = input[index + 1]; + if (IsHex(first) && IsHex(second)) { + AddOption(ToByte(first, second)); + } else { + throw new PatternError("Alternation does not contain literal byte: {0}{1}", first, second); + } + index += 2; + } while (input[index] == '|'); + + if (input[index] != ')') { + throw new PatternError("Alternation not terminated by paren: {0}", input[index]); + } + index += 1; + + FinishOptions(); + } else if (cur == '{') { + index += 1; + + int lower = ParseNum(input, ref index); + + int upper; + if (input[index] == '-') { + index += 1; + int num = ParseNum(input, ref index); + upper = num >= 0 ? num : Int32.MaxValue; + } else { + upper = lower; + } + + if (input[index] != '}') { + throw new PatternError("Wildcard not terminated by brace: {0}", input[index]); + } + index += 1; + + Add(new WildPattern(lower, upper)); + } else { + throw new PatternError("Unrecognized character: {0}", cur); + } + } + + FinishSequence(); + + Add(new EmptyPattern(name)); + + Debug.Assert(byteBuffer.Count == 0); + Debug.Assert(byteOptions.Count == 0); + + Pattern result = curPattern; + curPattern = null; + + return Reverse(result); + } + + private int ParseNum(string! input, ref int index) + { + int endIndex = input.IndexOfAny(new char[] { '-', '}' }, index); + string numStr = input.Substring(index, endIndex - index); + index = endIndex; + return numStr.Length > 0 ? Int32.Parse(numStr) : -1; + } + + private void Add(Pattern! pattern) + { + FinishSequence(); + AddCore(pattern); + } + + private void AddCore(Pattern! pattern) + { + Debug.Assert(pattern.Next == null); + pattern.Next = curPattern; + curPattern = pattern; + } + + private void AddByte(byte val) + { + byteBuffer.Add(val); + } + + private void AddOption(byte val) + { + byteOptions.Add(val); + } + + private void FinishOptions() + { + Add(new AltPattern((byte[])byteOptions.ToArray(typeof(byte)))); + byteOptions.Clear(); + } + + private void FinishSequence() + { + if (byteBuffer.Count > 0) { + AddCore(new SeqPattern((byte[])byteBuffer.ToArray(typeof(byte)))); + byteBuffer.Clear(); + } + } + + private Pattern Reverse(Pattern pattern) + { + Pattern reversed = null; + + while (pattern != null) { + Pattern next = pattern.Next; + pattern.Next = reversed; + reversed = pattern; + pattern = next; + } + + return reversed; + } + + private bool IsHex(char c) + { + return ('0' <= c && c <= '9') || + ('a' <= c && c <= 'f') || + ('A' <= c && c <= 'F'); + } + + private byte ToByte(char c) + { + if ('0' <= c && c <= '9') { + return (byte)(c - '0'); + } else if ('a' <= c && c <= 'f') { + return (byte)(c - 'a' + 10); + } else if ('A' <= c && c <= 'F') { + return (byte)(c - 'A' + 10); + } else { + throw new PatternError("Invalid hex digit: {0}", c); + } + } + + private byte ToByte(char c1, char c2) + { + return (byte)((ToByte(c1) << 4) | ToByte(c2)); + } + + private ArrayList byteOptions = new ArrayList(); + private ArrayList byteBuffer = new ArrayList(); + private Pattern curPattern = null; + } +} diff --git a/base/Applications/EmailServer/EmailServer.proj b/base/Applications/EmailServer/EmailServer.proj new file mode 100644 index 0000000..bda5c86 --- /dev/null +++ b/base/Applications/EmailServer/EmailServer.proj @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + diff --git a/base/Applications/EmailServer/MailStore/MailStore.csproj b/base/Applications/EmailServer/MailStore/MailStore.csproj new file mode 100644 index 0000000..2afe275 --- /dev/null +++ b/base/Applications/EmailServer/MailStore/MailStore.csproj @@ -0,0 +1,39 @@ + + + + + + + Exe + MailStore + true + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/EmailServer/MailStore/MailStore.sg b/base/Applications/EmailServer/MailStore/MailStore.sg new file mode 100644 index 0000000..d222ac7 --- /dev/null +++ b/base/Applications/EmailServer/MailStore/MailStore.sg @@ -0,0 +1,387 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: Mail storage server. +// + + +using System; +using System.Collections; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Threading; + +using Microsoft.SingSharp; +using Microsoft.Contracts; +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity; +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Email.Contracts; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Security; + +[assembly: Transform(typeof(ApplicationResourceTransform))] +[assembly: ApplicationPublisherAttribute("singularity.microsoft.com")] +[assembly: AssertPrivilegeAttribute("$register-privilege.localhost")] + +namespace Microsoft.Singularity.Email +{ + [ConsoleCategory(HelpMessage="Mail Store Service", DefaultAction=true)] + internal class Parameters + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [Endpoint] + public readonly TRef nsRef; + + [Endpoint] + public readonly TRef avRef; + + [StringParameter("accounts", Mandatory=false, Default="/init/accounts", + HelpMessage="File with email accounts.")] + internal string accountPath; + + [StringParameter("files", Mandatory=false, Default="/fs/email", + HelpMessage="Directory for email files.")] + internal string fileRoot; + + reflective internal Parameters(); + + internal int AppMain() { + return Store.AppMain(this); + } + } + + public class Account + { + public string address; + public string path; + public int file; + + public static SortedList accounts; + + public Account(string address, string path) + { + this.address = address; + this.path = path; + this.file = 0; + } + + public string NextFile() + { + return this.path + "/" + (this.file++).ToString(); + } + + public static void ReadAllFromFile(string path) + { + SortedList list = new SortedList(256); + // Open file RO for now + FileStream fsInput = new FileStream(path, FileMode.Open, FileAccess.Read); + StreamReader srInput = new StreamReader(fsInput); + String line; + + while ((line = srInput.ReadLine()) != null) { + int colon = line.IndexOf(':'); + if (colon > 0) { + string address = line.Substring(colon + 1); + string storage = line.Substring(0, colon); + + if (!list.ContainsKey(address)) { + list.Add(address, new Account(address, storage)); + } + } + } + //closing a stream reader will close the file stream under the covers + //if you close the file stream afterwards, you will deadlock. + srInput.Close(); + accounts = list; + } + + public static string AddressList() + { + string list = null; + foreach (Account account in accounts.Values) { + if (account != null) { + if (list != null) { + list = list + ";" + account.address; + } + else { + list = account.address; + } + } + } + return list; + } + } + + public class Store + { + public static TRef avEp; + + // This function initializes the store, registers with the name service and + // starts a new thread per SMTP agent. + // + internal static int AppMain(Parameters! config) + { + DirectoryServiceContract.Imp ds = (config.nsRef).Acquire(); + if (ds == null) { + throw new Exception("Unable to acquire handle to the Directory Service root"); + } + ds.RecvSuccess(); + + // Here is the channel we use to communicate with the NameServer + ServiceProviderContract.Imp! nsImp; + ServiceProviderContract.Exp! nsExp; + ServiceProviderContract.NewChannel(out nsImp, out nsExp); + + try { + ds.SendRegister(Bitter.FromString2(MailStoreContract.ModuleName), nsImp); + + switch receive { + case ds.AckRegister() : + // All is well. + break; + + case ds.NakRegister(ServiceProviderContract.Imp:Start rejectedEP, error) : + // All is very much not well; abort. + Console.WriteLine("MailStore: Failed to register as {0}: {1}", + MailStoreContract.ModuleName, error); + delete nsExp; + delete rejectedEP; + return -1; + + case ds.ChannelClosed(): + Console.WriteLine("mailStore: ds channel closed"); + delete nsExp; + return -1; + } + + Console.WriteLine("MailStore: Reading account list."); + Account.ReadAllFromFile(config.accountPath); + // Make sure we have a directory for each account. + { + ErrorCode error; + // Directory.CreateDirectory(config.fileRoot); + if (SdsUtils.CreateDirectory(config.fileRoot, ds, out error)) { + Console.WriteLine("MailStore: Created {0}", config.fileRoot); + } + } + + foreach (Account account in Account.accounts.Values) { + if (account == null || account.path == null) { + continue; + } + account.path = config.fileRoot + "/" + account.path; + + try { + ErrorCode error; + string path = account.path; + if (SdsUtils.CreateDirectory(path, ds, out error)) { + Console.WriteLine("MailStore: Created {0}", path); + } + + int count = 0; + for (;;) { + string file = path + "/" + count.ToString(); + FileAttributesRecord atr; + if (!SdsUtils.GetAttributes(file, ds, out atr, out error)) { + if (count > 0) { + Console.WriteLine("MailStore: {0}", file); + } + break; + } + count++; + } + account.file = count; + } + catch (IOException) { + } + } + } + finally { + delete ds; + } + + Console.WriteLine("MailStore: Connecting to AntiVirus."); + AntiVirusContract.Imp! avImp = config.avRef.Acquire(); + avImp.RecvAntiVirusReady(); + avEp = new TRef(avImp); + + Console.WriteLine("MailStore: Ready at {0}", MailStoreContract.ModuleName); + + // Here is the set of client channels we service + + for (;;) { + switch receive { + // ------------------------------- Requests for new connections + + case nsExp.Connect(ServiceContract.Exp:Start! newEp): + // We expect people top give us MailStoreContract.Exp instances + MailStoreContract.Exp newClient = newEp as MailStoreContract.Exp; + + if (newClient == null) { + // Invalid contract type. Fail. + nsExp.SendNackConnect(newEp); + } + else { + // Signal ready and start servicing this contract + nsExp.SendAckConnect(); + + // Launch the thread to handle the incoming requests. + Store child = new Store(newClient); + Thread thread = new Thread(child.Run); + thread.Start(); + } + break; + + case nsExp.ChannelClosed(): + // The namespace channel is closed so quit. + delete nsExp; + return -1; + } + } + + // Close the Anti-Virus Scanner. + avImp = avEp.Acquire(); + if (avImp != null) { + delete avImp; + } + + return 0; + } + + public static string PopNextAddress(ref string content) + { + if (content == null || content.Length == 0) { + return null; + } + + int i = content.IndexOf(';'); + string ret = (i > 0) ? content.Substring(0, i) : content; + content = (i > 0) ? content.Substring(i + 1) : ""; + return ret; + } + + ////////////////////////////////////////////////////////////////////////////// + // + public Store([Claims] MailStoreContract.Exp:Start! ep) + { + epRef = new TRef(ep); + } + + private TRef epRef; + + public void Run() + { + MailStoreContract.Exp ep = epRef.Acquire(); + + if (ep == null) { + return; + } + + ep.SendMailStoreReady(); + int arg; + + for (;;) { + switch receive { + case ep.SaveMessage(char[]! in ExHeap boxes, byte[]! in ExHeap buffer): + // Do something with the message. + int error = 0; +#if false + Console.WriteLine("MailStore: Processing Message"); +#endif + byte[] message = Bitter.ToByteArray(buffer); + string accountList = Bitter.ToString(boxes); + delete boxes; + + AntiVirusContract.Imp avImp = avEp.Acquire(); + avImp.SendCheckFile(buffer); + string virus = null; + switch receive + { + case avImp.FileClean(): + break; + + case avImp.FileContaminated(virusVector): + virus = Bitter.ToString(virusVector); + delete virusVector; + break; + + case avImp.ChannelClosed(): + virus = "**Scanner Failed**"; + break; + } + avEp.Release(avImp); + + if (virus != null) { + // save the message to each of the boxes. + Console.WriteLine("MailStore: "+ + "Discarding message contaminated with {0}.", + virus); + error = 666; + } + else { + error = 0; + string address; + while ((address = PopNextAddress(ref accountList)) != null) { +#if false + Console.WriteLine("MailStore: Receive for {0} of {1} bytes", + address, message.Length); +#endif + int key = Account.accounts.IndexOfKey(address); + if (key >= 0) { + Account! account = (Account!)Account.accounts.GetByIndex(key); + string path = account.NextFile(); + + try { + FileStream! sw = (!)File.Create(path); + sw.Write(message, 0, message.Length); + sw.Flush(); + sw.Close(); + } + catch (IOException) { + Console.WriteLine("MailStore: Write to {0} failed.", path); + error = 665; + } + } + else { + Console.WriteLine("MailStore: Bad address {0}", address); + } + } + } + + if (error == 0) { + ep.SendSaveAck(); + } + else { + ep.SendSaveNak(error); + } + break; + + + case ep.GetAddressList(): + Console.WriteLine("MailStore: Providing list of email addresses."); + string list = Account.AddressList(); + char[]! in ExHeap buffer = (!)Bitter.FromString(list); + ep.SendGetAck(buffer); + break; + + case ep.ChannelClosed(): + delete ep; + return; + } + } + } + } +} diff --git a/base/Applications/EmailServer/MailStore/MailStoreContracts.csproj b/base/Applications/EmailServer/MailStore/MailStoreContracts.csproj new file mode 100644 index 0000000..7e9d5a7 --- /dev/null +++ b/base/Applications/EmailServer/MailStore/MailStoreContracts.csproj @@ -0,0 +1,29 @@ + + + + + + + Library + MailStoreContracts + + + + + + + + + + + + + diff --git a/base/Applications/EmailServer/MailStore/MailStoreContracts.sg b/base/Applications/EmailServer/MailStore/MailStoreContracts.sg new file mode 100644 index 0000000..4852ea3 --- /dev/null +++ b/base/Applications/EmailServer/MailStore/MailStoreContracts.sg @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: Contract used by SMTP Agents to talk to the mail store. +// + +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Endpoint; + +using Microsoft.Singularity.Extending; + +namespace Microsoft.Singularity.Email.Contracts +{ + public contract MailStoreContract : ExtensionContract //ServiceContract + { + public const string ModuleName = "/service/mailstore"; + + out message MailStoreReady(); + + in message GetAddressList(); + out message GetAck(char[]! in ExHeap addresses); + + in message SaveMessage(char[]! in ExHeap accounts, + byte[]! in ExHeap buffer); + out message SaveAck(); + out message SaveNak(int error); + + override state Start : MailStoreReady! -> ReadyState; + state ReadyState : one { + SaveMessage? -> (SaveAck! or SaveNak!) -> ReadyState; + GetAddressList? -> (GetAck!) -> ReadyState; + } + } +} diff --git a/base/Applications/EmailServer/MailStore/accounts b/base/Applications/EmailServer/MailStore/accounts new file mode 100644 index 0000000..4417243 --- /dev/null +++ b/base/Applications/EmailServer/MailStore/accounts @@ -0,0 +1,135 @@ +alewis:andrew.lewis@enron.com +allen:k..allen@enron.com +arnold:john.arnold@enron.com +arora:harry.arora@enron.com +badeer:robert.badeer@enron.com +bagwell:jennifer.bagwell@enron.com +bailey:susan.bailey@enron.com +bass:eric.bass@enron.com +baughman:don.baughman@enron.com +beck:sally.beck@enron.com +benson:robert.benson@enron.com +blair:lynn.blair@enron.com +brawner:f..brawner@enron.com +brawners:sandra.brawner@enron.com +buy:rick.buy@enron.com +campbell:f..campbell@enron.com +carson:mike.carson@enron.com +cash:michelle.cash@enron.com +causholli:monika.causholli@enron.com +corman:shelley.corman@enron.com +crandall:sean.crandall@enron.com +cuilla:martin.cuilla@enron.com +dasovich:jeff.dasovich@enron.com +davis:dana.davis@enron.com +dean:craig.dean@enron.com +delainey:w..delainey@enron.com +derrick:james.derrick@enron.com +donoho:lindy.donoho@enron.com +donohoe:tom.donohoe@enron.com +dorland:chris.dorland@enron.com +ermis:frank.ermis@enron.com +evans:casey.evans@enron.com +farmer:j..farmer@enron.com +fischer:mary.fischer@enron.com +forney:m..forney@enron.com +gang:lisa.gang@enron.com +gay:l..gay@enron.com +geaccone:tracy.geaccone@enron.com +germany:chris.germany@enron.com +gilbertsmith:doug.gilbert-smith@enron.com +giron:c..giron@enron.com +griffith:john.griffith@enron.com +grigsby:mike.grigsby@enron.com +haedicke:e..haedicke@enron.com +hayslett:rod.hayslett@enron.com +heard:marie.heard@enron.com +hendrickson:scott.hendrickson@enron.com +hernandez:juan.hernandez@enron.com +hlewis:h..lewis@enron.com +hodge:john.hodge@enron.com +holst:keith.holst@enron.com +horton:stanley.horton@enron.com +hyatt:kevin.hyatt@enron.com +jones:tana.jones@enron.com +kaminski:j.kaminski@enron.com +kean:j..kean@enron.com +keavey:peter.keavey@enron.com +keaveyf:f..keavey@enron.com +keiser:kam.keiser@enron.com +king:jeff.king@enron.com +kitchen:louise.kitchen@enron.com +kuykendall:tori.kuykendall@enron.com +lavorato:lavorato@enron.com +lay:kenneth.lay@enron.com +lenhart:matthew.lenhart@enron.com +lokay:michelle.lokay@enron.com +lokey:teb.lokey@enron.com +love:m..love@enron.com +lucci:t..lucci@enron.com +maggi:mike.maggi@enron.com +mann:kay.mann@enron.com +martin:a..martin@enron.com +may:larry.may@enron.com +mccarty:danny.mccarty@enron.com +mcconnell:mark.mcconnell@enron.com +mckayb:brad.mckay@enron.com +mckayj:jonathan.mckay@enron.com +mclaughlin:errol.mclaughlin@enron.com +meyers:albert.meyers@enron.com +mims:l..mims@enron.com +motley:matt.motley@enron.com +neal:scott.neal@enron.com +nemec:gerald.nemec@enron.com +panus:stephanie.panus@enron.com +parks:joe.parks@enron.com +pereira:w..pereira@enron.com +perlingiere:debra.perlingiere@enron.com +pimenov:vladi.pimenov@enron.com +platter:phillip.platter@enron.com +presto:m..presto@enron.com +quenet:joe.quenet@enron.com +quigley:dutch.quigley@enron.com +rapp:bill.rapp@enron.com +reitmeyer:jay.reitmeyer@enron.com +richey:cooper.richey@enron.com +ringa:andrea.ring@enron.com +ringr:richard.ring@enron.com +rogers:benjamin.rogers@enron.com +ruscitti:kevin.ruscitti@enron.com +sager:elizabeth.sager@enron.com +saibi:eric.saibi@enron.com +salisbury:holden.salisbury@enron.com +sanchez:monique.sanchez@enron.com +sanders:b..sanders@enron.com +scholtes:diana.scholtes@enron.com +schoolcraft:darrell.schoolcraft@enron.com +schwieger:jim.schwieger@enron.com +scott:susan.scott@enron.com +semperger:cara.semperger@enron.com +shackleton:sara.shackleton@enron.com +shankman:a..shankman@enron.com +shankmanj:jeffrey.a.shankman@enron.com +shapiro:richard.shapiro@enron.com +shively:s..shively@enron.com +skilling:jeff.skilling@enron.com +slinger:ryan.slinger@enron.com +smith:matt.smith@enron.com +solberg:geir.solberg@enron.com +staab:theresa.staab@enron.com +steffes:d..steffes@enron.com +stepenovitch:joe.stepenovitch@enron.com +storey:geoff.storey@enron.com +sturm:j..sturm@enron.com +swerzbin:mike.swerzbin@enron.com +tholt:jane.tholt@enron.com +thomas:d..thomas@enron.com +townsend:judy.townsend@enron.com +tycholiz:barry.tycholiz@enron.com +ward:kim.ward@enron.com +watson:kimberly.watson@enron.com +weldon:charles.weldon@enron.com +whalley:greg.whalley@enron.com +white:w..white@enron.com +whitt:mark.whitt@enron.com +williams:jason.williams@enron.com diff --git a/base/Applications/EmailServer/SmtpAgent/Session.sg b/base/Applications/EmailServer/SmtpAgent/Session.sg new file mode 100644 index 0000000..67af244 --- /dev/null +++ b/base/Applications/EmailServer/SmtpAgent/Session.sg @@ -0,0 +1,258 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: SMTP Session. +// + + +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; + +namespace Microsoft.Singularity.Email +{ + public class SmtpSession + { + private Socket socket; + private byte[] ibuf; + private int iend; + private int ibeg; + private byte[] obuf; + private int odata; + private int result; + private bool verbose; + + public SmtpSession(Socket socket) + { + this.socket = socket; + this.ibuf = new byte [4096]; + this.ibeg = 0; + this.iend = 0; + this.obuf = new byte [4096]; + this.odata = 0; + this.result = 0; + this.verbose = false; + } + + private int HaveLine() + { + int left = iend - ibeg; + for (int i = 0; i + 1< left; i++) { + if (ibuf[ibeg + i] == '\r' && ibuf[ibeg + i + 1] == '\n') { + // return the # of character (not \r\n) in line. + return i; + } + } + return -1; + } + + private int ReadLine() + { + for (;;) { + if (ibeg < iend) { + + // See if there is a line already in the buffer. + int line = HaveLine(); + if (line >= 0) { + return line; + } + + // Make sure the remaining data is at the start of buffer. + if (ibeg > 0) { + Array.Copy(ibuf, ibeg, ibuf, 0, iend - ibeg); + iend -= ibeg; + ibeg = 0; + } + } + else { + ibeg = 0; + iend = 0; + } + + int read = socket.Receive(ibuf, iend, ibuf.Length - iend, SocketFlags.None); + if (read == 0) { + // Should probably throw an exception. + return -1; + } + iend += read; + } + } + + public int Result + { + get { return result; } + } + + public bool Verbose + { + get { return verbose; } + set { verbose = value; } + } + + public string ReadLine7() + { + result = 0; + int line = ReadLine(); + if (line >= 0) { + string data = Encoding.ASCII.GetString(ibuf, ibeg, line); + for (int i = 0; i < line && ibuf[ibeg + i] >= '0' && ibuf[ibeg + i] <= '9'; i++) { + result = result * 10 + (ibuf[ibeg + i] - '0'); + } + ibeg += line + 2; + if (verbose) { + Console.WriteLine("S: {0}", data); + } + return data; + } + if (verbose) { + Console.WriteLine("S: "); + } + return null; + } + + public int ReadLine8(byte[] buffer, int offset) + { + result = 0; + int line = ReadLine(); + if (line >= 0) { + Array.Copy(ibuf, ibeg, buffer, offset, line); + ibeg += line + 2; + if (verbose) { + Console.WriteLine("S: <8bit>"); + } + return line; + } + if (verbose) { + Console.WriteLine("S: "); + } + return -1; + } + + public void WritePrefix(byte prefix) + { + obuf[odata++] = prefix; + } + + public void WriteLine7(string line) + { + WriteLine7(line, null, null, null); + } + + public void WriteLine7(string a, string b) + { + WriteLine7(a, b, null, null); + } + + public void WriteLine7(string a, string b, string c) + { + WriteLine7(a, b, c, null); + } + + public void WriteLine7(string a, string b, string c, string d) + { + if (verbose) { + Console.WriteLine("C: {0}", a + b + c + d); + } + if (a != null) { + odata += Encoding.ASCII.GetBytes(a, 0, a.Length, obuf, odata); + } + if (b != null) { + odata += Encoding.ASCII.GetBytes(b, 0, b.Length, obuf, odata); + } + if (c != null) { + odata += Encoding.ASCII.GetBytes(c, 0, c.Length, obuf, odata); + } + if (d != null) { + odata += Encoding.ASCII.GetBytes(d, 0, d.Length, obuf, odata); + } + obuf[odata++] = (byte)'\r'; + obuf[odata++] = (byte)'\n'; + socket.Send(obuf, 0, odata, SocketFlags.None); + odata = 0; + } + + public void WriteLine8(byte[] line) + { + if (verbose) { + Console.WriteLine("C: <8bit>"); + } + if (odata > 0) { + socket.Send(obuf, 0, odata, SocketFlags.None); + odata = 0; + } + socket.Send(line); + obuf[odata++] = (byte)'\r'; + obuf[odata++] = (byte)'\n'; + socket.Send(obuf, 0, odata, SocketFlags.None); + odata = 0; + } + + public int Quit() + { + int save = result; + WriteLine7("QUIT"); + ReadLine7(); + socket.Shutdown(SocketShutdown.Both); + + if (save == 0) { + return 999; + } + return save; + } + + public int Close() + { + int save = result; + socket.Shutdown(SocketShutdown.Both); + + if (save == 0) { + return 999; + } + return save; + } + + private void Dump(byte[] array, int beg, int end) + { + for (int i = beg; i < end; i += 16) { + Console.Write(" :: "); + for (int j = i; j < i + 16; j++) { + if (j < end) { + Console.Write("{0:x2}", array[j]); + } + else { + Console.Write(" "); + } + } + Console.Write(" :: "); + for (int j = i; j < i + 16; j++) { + if (j < end) { + if (array[j] >= 32 && array[j] < 127) { + Console.Write("{0}", (char)array[j]); + } + else { + Console.Write("."); + } + } + else { + Console.Write(" "); + } + } + Console.WriteLine(); + } + } + + public void Dump() + { + Console.WriteLine(":::: Input {0}..{1}", ibeg, iend); + Dump(ibuf, ibeg, iend); + Console.WriteLine(":::: Output {0}..{1}", 0, odata); + Dump(obuf, 0, odata); + Console.WriteLine(":::: Result {0}", result); + } + } +} diff --git a/base/Applications/EmailServer/SmtpAgent/SmtpAgent.csproj b/base/Applications/EmailServer/SmtpAgent/SmtpAgent.csproj new file mode 100644 index 0000000..bd90e06 --- /dev/null +++ b/base/Applications/EmailServer/SmtpAgent/SmtpAgent.csproj @@ -0,0 +1,34 @@ + + + + + + + Exe + SmtpAgent + 2614,2637,2638,2613,2639 + + + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/EmailServer/SmtpAgent/SmtpAgent.sg b/base/Applications/EmailServer/SmtpAgent/SmtpAgent.sg new file mode 100644 index 0000000..cf29c64 --- /dev/null +++ b/base/Applications/EmailServer/SmtpAgent/SmtpAgent.sg @@ -0,0 +1,507 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: SMTP Server. +// +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Diagnostics.Contracts; +using Microsoft.Singularity.Endpoint; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.V1.Services; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Configuration; + +using Microsoft.Contracts; +using Microsoft.SingSharp.Reflection; + +using System; +using System.Collections; +using System.Net; +using System.Net.Sockets; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using System.Diagnostics; + +using Microsoft.Singularity.Email.Contracts; + +[assembly: Transform(typeof(ApplicationResourceTransform))] + +namespace Microsoft.Singularity.Email +{ + [ConsoleCategory(HelpMessage="SMTP Mail Transfer Agent", DefaultAction=true)] + internal class Parameters + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; +#if false + [Endpoint] + public readonly TRef msRef; +#endif + [Endpoint] + public readonly TRef nsRef; + + [StringParameter("mailstore service", Mandatory=false, Default="/service/mailstore", + HelpMessage="Location of mail store service.")] + internal string mailstorePath; + + [StringParameter("server", Mandatory=false, Default="0.0.0.0", + HelpMessage="Service IP Address to give to clients.")] + internal string server; + + reflective internal Parameters(); + + internal int AppMain() { + return SmtpServer.AppMain(this); + } + } + + public class Buffer + { + private byte[] data; + private int size; + + public static Buffer Allocate(int bytes) + { + Buffer buffer = new Buffer(bytes); + return buffer; + } + + private Buffer(int bytes) + { + this.data = new byte[bytes]; + this.size = 0; + } + + public byte[] Data + { + get { return data; } + } + + public int Size + { + get { return size; } + } + + public int Prepare(int bytes) + { + if (size + bytes > data.Length) { + byte[] dst = new byte[data.Length * 2]; + Array.Copy(data, dst, size); + data = dst; + } + return size; + } + + public void Save(int bytes) + { + if (size + bytes + 2 <= data.Length) { + size += bytes; + data[size++] = (byte)'\r'; + data[size++] = (byte)'\n'; + } + else { + throw new Exception("Overflow"); + } + } + + public void Release() + { + data = null; + size = 0; + } + } + + public class ServerSession : SmtpSession + { + private static int idCount = 0; + private int id; + private string command; + private string server; + + public ServerSession(Socket socket, String server) + : base(socket) + { + this.id = idCount++; + this.server = server; + } + + public string GetCommand(String line) + { + int i = line.IndexOf(' '); + if (i < 0) { + command = line; + return null; + } + command = line.Substring(0, i).ToUpper(); + return command; + } + + private enum CurrentState { + HAD_NONE = 0, + HAD_HELO = 1, + HAD_MAIL = 2, + HAD_RCPT = 3, + }; + + public void Loop() + { + String client; + String line; + CurrentState cstate = CurrentState.HAD_NONE; + String from = null; + String to = null; + + //Open a new connection to the mailstore for each smtp connection. + //Each client connection stays allive until all emails are sent. + + DirectoryServiceContract.Imp! dsImp = SmtpServer.dsEp.Acquire(); + + MailStoreContract.Imp! msImp; + MailStoreContract.Exp! msExp; + MailStoreContract.NewChannel(out msImp, out msExp); + + ErrorCode errorCode; + if (!SdsUtils.Bind(SmtpServer.mailstorePath, dsImp, msExp, out errorCode)) { + DebugStub.WriteLine("Failed to bind to mail store...error {0}\n", + __arglist(SdsUtils.ErrorCodeToString(errorCode))); + Console.WriteLine("Failed to bind to mail store...error {0}\n", + SdsUtils.ErrorCodeToString(errorCode)); + + DebugStub.Break(); + SmtpServer.dsEp.Release(dsImp); + delete msImp; + return; + } + SmtpServer.dsEp.Release(dsImp); + + // MailStoreContract.Imp! msImp = config.msRef.Acquire(); + msImp.RecvMailStoreReady(); + + WriteLine7("220 ", server, " Singularity Simple SMTP Service Ready"); + + bool abort = false; + while (!abort) { + line = ReadLine7(); + if (line == null) { + break; + } + + GetCommand(line); + + switch (command) { + case "EHLO": + client = line.Split(new Char[] {' '})[1]; + Console.WriteLine(":{0}: EHLO {1}", id, client); + WriteLine7("250-", server, " greets ", client); + WriteLine7("250-8BITMIME"); + WriteLine7("250 HELP"); + from = null; + to = null; + cstate = CurrentState.HAD_HELO; + // DebugStub.WriteLine("Got EHLO\n"); + break; + + case "HELO": + client = line.Split(new Char[] {' '})[1]; + Console.WriteLine(":{0}: HELO {1}", id, client); + WriteLine7("250 ", server, " greets ", client); + from = null; + to = null; + cstate = CurrentState.HAD_HELO; + // DebugStub.WriteLine("Got HELO\n"); + break; + + case "MAIL": + if (cstate != CurrentState.HAD_HELO) { + goto default; + } + + if (line.StartsWith("MAIL FROM:")) { + int pos = line.IndexOf("<") + 1; + int beg = line.LastIndexOf(':'); + if (beg < pos) { + beg = pos; + } + int end = line.IndexOf('>'); + from = line.Substring(beg, end - beg).ToLower(); + WriteLine7("250 OK"); + cstate = CurrentState.HAD_MAIL; + // DebugStub.WriteLine("Got MAIL\n"); + } + else { + WriteLine7("501 Syntax error in parameters"); + } + break; + + case "RCPT": + if (cstate != CurrentState.HAD_MAIL && cstate != CurrentState.HAD_RCPT) { + goto default; + } + // DebugStub.WriteLine("Got RCPT\n"); + if (line.StartsWith("RCPT TO:")) { + int pos = line.IndexOf("<") + 1; + int beg = line.LastIndexOf(':'); + if (beg < pos) { + beg = pos; + } + int end = line.IndexOf('>'); + string address = line.Substring(beg, end - beg).ToLower(); + + if (SmtpServer.addresses.ContainsKey(address)) { + WriteLine7("250 OK"); + if (to != null) { + to = to + ";" + address; + } + else { + to = address; + } + cstate = CurrentState.HAD_RCPT; + } + else { + WriteLine7("250 Accepting for forwarding."); + cstate = CurrentState.HAD_RCPT; + if (to != null) { + to = to + ";" + "mail_forward@enron.com"; + } + else { + to = "mail_forward@enron.com"; + } + } + } + else { + WriteLine7("501 Syntax error in parameters"); + } + break; + + case "DATA": + if (cstate != CurrentState.HAD_RCPT) { + goto default; + } + // DebugStub.WriteLine("Got DATA\n"); + WriteLine7("354 Start mail input; end with ."); + + bool old = Verbose; + Verbose = false; + Buffer buffer = Buffer.Allocate(65536); + bool good = false; + + for (;;) { + int off = buffer.Prepare(1024); + int len = ReadLine8(buffer.Data, off); + + if (len < 0) { + break; + } + + if (len == 1 && buffer.Data[off] == '.') { + good = true; + break; + } + else { +#if false + Console.WriteLine("S: {0}", + Encoding.ASCII.GetString(buffer.Data, off, len)); +#endif + buffer.Save(len); + } + } + Verbose = old; + + if (good) { + bool succeeded = false; + char[] in ExHeap! addresses = Bitter.FromString(to); + byte[] in ExHeap! data + = Bitter.FromByteArray(buffer.Data, 0, buffer.Size); + + // MailStoreContract.Imp msImp = SmtpServer.msEp.Acquire(); + // DebugStub.WriteLine("Saving data\n"); + Console.WriteLine("SmtpAgaint: Email from {0} to {1}", from, to); + msImp.SendSaveMessage(addresses, data); + switch receive + { + case msImp.SaveAck(): + succeeded = true; + break; + + case msImp.SaveNak(error): + Console.WriteLine("SmtpAgent: Server dropped email, "+ + "error={0}", error); + break; + + case msImp.ChannelClosed(): + break; + } + // SmtpServer.msEp.Release(msImp); + if (succeeded) { + WriteLine7("250 OK"); + } + else { + WriteLine7("554 Transaction failed"); + } + } + else { + WriteLine7("554 Transaction failed"); + Dump(); + throw new Exception("554 Transaction failed"); + } + + buffer.Release(); + cstate = CurrentState.HAD_HELO; + from = null; + to = null; + break; + + case "NOOP": + WriteLine7("250 OK"); + break; + + case "HELP": + WriteLine7("250 OK"); + break; + + case "RSET": + WriteLine7("250 OK"); + from = null; + to = null; + cstate = CurrentState.HAD_HELO; + break; + + case "QUIT": + WriteLine7("221 ", server, " Service closing transmission channel"); + abort = true; + break; + + default: + WriteLine7("503 Unrecognized command [", command, "]"); + abort = true; + break; + } + } + Console.WriteLine(":{0}: Session closed", id); + delete msImp; + Close(); + } + } + + public class SmtpServer + { + public const int _port = 25; + public static bool verbose; + + // public static TRef msEp; + public static TRef dsEp; + + public static SortedList addresses; + public static string mailstorePath; + + internal static int AppMain(Parameters! config) + { + string server = config.server; + if (server[0] >= '0' && server[0] <= '9') { + server = "[" + server + "]"; + } + + // Connect to the MailStore. + Console.WriteLine("SmtpAgent: Connecting to MailStore."); + DirectoryServiceContract.Imp! dsImp = config.nsRef.Acquire(); + dsImp.RecvSuccess(); + + MailStoreContract.Imp! msImp; + MailStoreContract.Exp! msExp; + MailStoreContract.NewChannel(out msImp, out msExp); + + mailstorePath = config.mailstorePath; + + ErrorCode error; + if (!SdsUtils.Bind(config.mailstorePath, dsImp, msExp, out error)) { + DebugStub.WriteLine("Failed to bind to mail store...error {0}\n", + __arglist(SdsUtils.ErrorCodeToString(error))); + Console.WriteLine("Failed to bind to mail store...error {0}\n", + SdsUtils.ErrorCodeToString(error)); + + delete dsImp; + delete msImp; + return -1; + } + + + // MailStoreContract.Imp! msImp = config.msRef.Acquire(); + msImp.RecvMailStoreReady(); + + // Retrieve vaild address list. + char[] in ExHeap! buffer; + msImp.SendGetAddressList(); + msImp.RecvGetAck(out buffer); + addresses = ReadUniqueList(Bitter.ToString(buffer)); + delete buffer; + + delete msImp; + // Save the endpoint. + // msEp = new TRef(msImp); + dsEp = new TRef(dsImp); + + // Connect to the network. + Console.WriteLine("SmtpAgent: Opening TCP port {0}", _port); + Socket target = new Socket(AddressFamily.InterNetwork, + SocketType.Stream, ProtocolType.Tcp); + target.Bind(new IPEndPoint(IPAddress.Any, _port)); + target.Listen((int)SocketOptionName.MaxConnections); + + for (bool stop = false; !stop;) { + Console.WriteLine(":: {0} waiting for accept.", server); + Socket socket = target.Accept(); + + // Create the Session and kick it off in its own thread. + ServerSession conn = new ServerSession(socket, server); + + conn.Verbose = verbose; + Thread thread = new Thread(conn.Loop); + thread.Start(); + } + target.Close(); + + // Close the Store engine. + dsImp = dsEp.Acquire(); + if (dsImp != null) { + delete dsImp; + } + + return 0; + } + + public static string PopNextAddress(ref string content) + { + if (content.Length == 0) { + return null; + } + + int i = content.IndexOf(';'); + string ret = (i > 0) ? content.Substring(0, i) : content; + content = (i > 0) ? content.Substring(i + 1) : ""; + return ret; + } + + public static SortedList ReadUniqueList(string content) + { + SortedList list = new SortedList(); + string address; + + while ((address = PopNextAddress(ref content)) != null) { + if (!list.ContainsKey(address)) { + list.Add(address, address); + } + } + return list; + } + } +} + + diff --git a/base/Applications/EmailServer/SmtpAgent/accounts b/base/Applications/EmailServer/SmtpAgent/accounts new file mode 100644 index 0000000..c070f27 --- /dev/null +++ b/base/Applications/EmailServer/SmtpAgent/accounts @@ -0,0 +1,136 @@ +alewis:andrew.lewis@enron.com +allen:k..allen@enron.com +arnold:john.arnold@enron.com +arora:harry.arora@enron.com +badeer:robert.badeer@enron.com +bagwell:jennifer.bagwell@enron.com +bailey:susan.bailey@enron.com +bass:eric.bass@enron.com +baughman:don.baughman@enron.com +beck:sally.beck@enron.com +benson:robert.benson@enron.com +blair:lynn.blair@enron.com +brawner:f..brawner@enron.com +brawners:sandra.brawner@enron.com +buy:rick.buy@enron.com +campbell:f..campbell@enron.com +carson:mike.carson@enron.com +cash:michelle.cash@enron.com +causholli:monika.causholli@enron.com +corman:shelley.corman@enron.com +crandall:sean.crandall@enron.com +cuilla:martin.cuilla@enron.com +dasovich:jeff.dasovich@enron.com +davis:dana.davis@enron.com +dean:craig.dean@enron.com +delainey:w..delainey@enron.com +derrick:james.derrick@enron.com +donoho:lindy.donoho@enron.com +donohoe:tom.donohoe@enron.com +dorland:chris.dorland@enron.com +ermis:frank.ermis@enron.com +evans:casey.evans@enron.com +farmer:j..farmer@enron.com +fischer:mary.fischer@enron.com +forney:m..forney@enron.com +gang:lisa.gang@enron.com +gay:l..gay@enron.com +geaccone:tracy.geaccone@enron.com +germany:chris.germany@enron.com +gilbertsmith:doug.gilbert-smith@enron.com +giron:c..giron@enron.com +griffith:john.griffith@enron.com +grigsby:mike.grigsby@enron.com +haedicke:e..haedicke@enron.com +hayslett:rod.hayslett@enron.com +heard:marie.heard@enron.com +hendrickson:scott.hendrickson@enron.com +hernandez:juan.hernandez@enron.com +hlewis:h..lewis@enron.com +hodge:john.hodge@enron.com +holst:keith.holst@enron.com +horton:stanley.horton@enron.com +hyatt:kevin.hyatt@enron.com +jones:tana.jones@enron.com +kaminski:j.kaminski@enron.com +kean:j..kean@enron.com +keavey:peter.keavey@enron.com +keaveyf:f..keavey@enron.com +keiser:kam.keiser@enron.com +king:jeff.king@enron.com +kitchen:louise.kitchen@enron.com +kuykendall:tori.kuykendall@enron.com +lavorato:lavorato@enron.com +lay:kenneth.lay@enron.com +lenhart:matthew.lenhart@enron.com +lokay:michelle.lokay@enron.com +lokey:teb.lokey@enron.com +love:m..love@enron.com +lucci:t..lucci@enron.com +maggi:mike.maggi@enron.com +mann:kay.mann@enron.com +martin:a..martin@enron.com +may:larry.may@enron.com +mccarty:danny.mccarty@enron.com +mcconnell:mark.mcconnell@enron.com +mckayb:brad.mckay@enron.com +mckayj:jonathan.mckay@enron.com +mclaughlin:errol.mclaughlin@enron.com +meyers:albert.meyers@enron.com +mims:l..mims@enron.com +motley:matt.motley@enron.com +neal:scott.neal@enron.com +nemec:gerald.nemec@enron.com +panus:stephanie.panus@enron.com +parks:joe.parks@enron.com +pereira:w..pereira@enron.com +perlingiere:debra.perlingiere@enron.com +pimenov:vladi.pimenov@enron.com +platter:phillip.platter@enron.com +presto:m..presto@enron.com +quenet:joe.quenet@enron.com +quigley:dutch.quigley@enron.com +rapp:bill.rapp@enron.com +reitmeyer:jay.reitmeyer@enron.com +richey:cooper.richey@enron.com +ringa:andrea.ring@enron.com +ringr:richard.ring@enron.com +rogers:benjamin.rogers@enron.com +ruscitti:kevin.ruscitti@enron.com +sager:elizabeth.sager@enron.com +saibi:eric.saibi@enron.com +salisbury:holden.salisbury@enron.com +sanchez:monique.sanchez@enron.com +sanders:b..sanders@enron.com +scholtes:diana.scholtes@enron.com +schoolcraft:darrell.schoolcraft@enron.com +schwieger:jim.schwieger@enron.com +scott:susan.scott@enron.com +semperger:cara.semperger@enron.com +shackleton:sara.shackleton@enron.com +shankman:a..shankman@enron.com +shankmanj:jeffrey.a.shankman@enron.com +shapiro:richard.shapiro@enron.com +shively:s..shively@enron.com +skilling:jeff.skilling@enron.com +slinger:ryan.slinger@enron.com +smith:matt.smith@enron.com +solberg:geir.solberg@enron.com +staab:theresa.staab@enron.com +steffes:d..steffes@enron.com +stepenovitch:joe.stepenovitch@enron.com +storey:geoff.storey@enron.com +sturm:j..sturm@enron.com +swerzbin:mike.swerzbin@enron.com +tholt:jane.tholt@enron.com +thomas:d..thomas@enron.com +townsend:judy.townsend@enron.com +tycholiz:barry.tycholiz@enron.com +ward:kim.ward@enron.com +watson:kimberly.watson@enron.com +weldon:charles.weldon@enron.com +whalley:greg.whalley@enron.com +white:w..white@enron.com +whitt:mark.whitt@enron.com +williams:jason.williams@enron.com +forwarding:mail_forward@enron.com diff --git a/base/Applications/EmailServer/SmtpAgentNoNet/SmtpAgentNoNet.csproj b/base/Applications/EmailServer/SmtpAgentNoNet/SmtpAgentNoNet.csproj new file mode 100644 index 0000000..a81389f --- /dev/null +++ b/base/Applications/EmailServer/SmtpAgentNoNet/SmtpAgentNoNet.csproj @@ -0,0 +1,35 @@ + + + + + + + Exe + SmtpAgentNoNet + 2614,2637,2638,2613,2639 + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/EmailServer/SmtpAgentNoNet/SmtpAgentNoNet.sg b/base/Applications/EmailServer/SmtpAgentNoNet/SmtpAgentNoNet.sg new file mode 100644 index 0000000..c70b1e8 --- /dev/null +++ b/base/Applications/EmailServer/SmtpAgentNoNet/SmtpAgentNoNet.sg @@ -0,0 +1,1004 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: SMTP Server. +// +//#define SMTPAGENT_VERBOSE +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Diagnostics.Contracts; +using Microsoft.Singularity.Endpoint; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.V1.Services; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Configuration; + +using Microsoft.Contracts; +using Microsoft.SingSharp.Reflection; + +using System; +using System.IO; +using System.Collections; +using System.Net; +using System.Net.Sockets; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using System.Diagnostics; + +using Microsoft.Singularity.Email.Contracts; + +[assembly: Transform(typeof(ApplicationResourceTransform))] + +namespace Microsoft.Singularity.Email_NoNet +{ + [ConsoleCategory(HelpMessage="SMTP Mail Transfer Agent", DefaultAction=true)] + internal class Parameters + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [StringParameter("cnts", Mandatory=false, Default="/init/contents.txt", + HelpMessage="Contents of emails to be sent.")] + internal string emailCorpus; + + [StringParameter("stl", Mandatory=false, Default="/init/stl.txt", + HelpMessage="file containing starting loc in corpus for each conn")] + internal string startPositionsFile; + + [Endpoint] + public readonly TRef nsRef; + + [StringParameter("mailstore service", Mandatory=false, Default="/service/mailstore", + HelpMessage="Location of mail store service.")] + internal string mailstorePath; + + [LongParameter("nts", Mandatory=false, Default= 1, + HelpMessage="Number of emails to send.")] + internal long numToSend; + + [LongParameter("nc", Mandatory=false, Default=1, + HelpMessage="Number of concurrent connections.")] + internal long numConn; + + [StringParameter("server", Mandatory=false, Default="0.0.0.0", + HelpMessage="Service IP Address to give to clients.")] + internal string server; + + reflective internal Parameters(); + + internal int AppMain() { + return SmtpServer.AppMain(this); + } + } + + + public class Buffer + { + private byte[] data; + private int size; + + public static Buffer Allocate(int bytes) + { + Buffer buffer = new Buffer(bytes); + return buffer; + } + + private Buffer(int bytes) + { + this.data = new byte[bytes]; + this.size = 0; + } + + public byte[] Data + { + get { return data; } + } + + public int Size + { + get { return size; } + } + + public int Prepare(int bytes) + { + if (size + bytes > data.Length) { + byte[] dst = new byte[data.Length * 2]; + Array.Copy(data, dst, size); + data = dst; + } + return size; + } + + public void Save(int bytes) + { + if (size + bytes + 2 <= data.Length) { + size += bytes; + data[size++] = (byte)'\r'; + data[size++] = (byte)'\n'; + } + else { + throw new Exception("Overflow"); + } + } + + public void Release() + { + data = null; + size = 0; + } + } + + + + public class EmailClientError : ApplicationException {} + + //Baically, instantiating this class + //goes through the state machine with the server + public class EmailClient + { + [Conditional("SMTPAGENT_VERBOSE")] + public static void DebugWriteLine(string format) + { + DebugStub.WriteLine(format); + } + + [Conditional("SMTPAGENT_VERBOSE")] + public static void DebugWriteLine(string format, __arglist) + { + DebugStub.WriteLine(format, new ArgIterator(__arglist)); + } + + private static void DumpEmail(string[] email) + { + DebugStub.WriteLine("Dumping email...\n"); + foreach(string line in email) { + DebugStub.WriteLine("{0}", __arglist(line)); + } + } + + //client side state machine + private enum ReceivedState { + RECEIVED_NONE = 0, + RECEIVED_220 = 1, + RECEIVED_250 = 2, + RECEIVED_354 = 3, + } + + private enum SentState { + SENT_NONE = 0, + SENT_EHLO = 1, + SENT_NOOP = 2, + SENT_MAIL = 3, + BEGAN_RCPT = 4, + FINISHED_RCPT = 5, + BEGAN_DATA = 6, + FINISHED_DATA = 7, + }; + + + private string[][] corpus; + private int nxtEmail; + private string[] emailContents; + private int dataIndex; + private ReceivedState rcvState; + private SentState sentState; + private int rcptIndex; + private string[] rcpts; + + private static string GetCommand(String line) + { + int len = line.Length; + int i; + for (i = 0; i < len && line[i] >= '0' && line[i] <= '9'; i++); + + return line.Substring(0, i); + } + + [NotDelayed] + public EmailClient(String[][] corpus, int startIndex) + { + + this.corpus = corpus; + this.nxtEmail = startIndex; + DebugStub.WriteLine("Email client starting up startindex {0}\n", __arglist(startIndex)); + DebugStub.WriteLine("corpus length is {0}\n", __arglist(corpus.Length)); + + rcvState = ReceivedState.RECEIVED_NONE; + sentState = SentState.SENT_NONE; + + emailContents = corpus[nxtEmail]; + nxtEmail++; + if (nxtEmail == corpus.Length) { + nxtEmail = 0; + } + } + + //Command from SMTP server to client + //Advances state machine + public void SendLine(string line) + { + DebugWriteLine("EmailClient.SendLine received {0}\n", __arglist(line)); + String command = GetCommand(line); + + switch(command) { + case "220": + if (rcvState == ReceivedState.RECEIVED_NONE) { + DebugWriteLine("EmailClient received 220 in correct state\n"); + rcvState = ReceivedState.RECEIVED_220; + return; + } + DebugStub.WriteLine("EmailClient recieved 220 while in state {0}\n", + __arglist(rcvState)); + DebugStub.Break(); + throw new EmailClientError(); + break; + case "250": + DebugWriteLine("EmailClient received 250\n"); + rcvState = ReceivedState.RECEIVED_250; + break; + case "354": + DebugWriteLine("EmailClient received 354\n"); + rcvState = ReceivedState.RECEIVED_354; + break; + case "501": + DebugWriteLine("Uhoh! received 501\n"); + DebugStub.Break(); + break; + default: + DebugStub.WriteLine("Got unexpected command {0}\n", __arglist(command)); + DebugStub.Break(); + break; + } + } + + //Get line from client to server + public string GetLine() + { + switch(sentState) { + case SentState.SENT_NONE: + DebugWriteLine("EmailClient GetLine while in state SENT_NONE\n"); + string result = "EHLO Nobody\r\n"; + sentState = SentState.SENT_EHLO; + rcvState = 0; + return result; + break; + + case SentState.SENT_EHLO: + if (rcvState != ReceivedState.RECEIVED_250) { + break; + } + DebugWriteLine("GetLine while in state SENT_EHLO...sending MAIL\n"); + + sentState = SentState.SENT_MAIL; + rcvState = 0; + rcptIndex = 0; + string from = FindFrom(emailContents).ToLower(); + return "MAIL FROM: <" + from + ">\r\n"; + break; + + case SentState.SENT_MAIL: + DebugWriteLine("GetLine while in state SENT_MAIL...sending RCPT\n"); + if (rcvState != ReceivedState.RECEIVED_250) { + break; + } + string to = FindTo("To: ", emailContents); + string cc = FindTo("Cc: ", emailContents); + string bcc = FindTo("Bcc: ", emailContents); + if (to == null) { + to = cc; + cc = null; + } + if (to == null) { + to = bcc; + bcc = null; + } + + if (cc != null) { + to = to + "," + cc; + } + if (bcc != null) { + to = to + "," + bcc; + } + + + if (to == null || to.Length == 0) { + //Sometimes the "to" is blank with no cc or bcc because of + //an interoffice dl? + to = "mail_forward@enron.com"; +#if false + DebugStub.WriteLine(":: Couldn't find receipients."); + DumpEmail(emailContents); + DebugStub.Break(); + throw new EmailClientError(); +#endif + } + + rcpts = to.Split(new Char[]{','}); + for (int i = 0; i < rcpts.Length; i++) { + rcpts[i] = rcpts[i].Trim().ToLower(); + if (rcpts[i].Length == 0) { + rcpts[i] = null; + } + } + + for (int i = 0; i < rcpts.Length; i++) { + for (int j = i + 1; j < rcpts.Length; j++) { + if (rcpts[i] == rcpts[j]) { + rcpts[j] = null; + } + } + } + rcvState = 0; + + string rcpt = "RCPT TO: <" + rcpts[rcptIndex] + ">\r\n"; + rcptIndex++; + if (rcptIndex == rcpts.Length) { + sentState = SentState.FINISHED_RCPT; + } + else { + sentState = SentState.BEGAN_RCPT; + } + return rcpt; + break; + + case SentState.BEGAN_RCPT: + DebugWriteLine("GetLine while in state BEGAN_RCPT...sending RCPT\n"); + if (rcvState != ReceivedState.RECEIVED_250) { + break; + } + rcpt = "RCPT TO: <" + rcpts[rcptIndex] + ">\r\n"; + rcptIndex++; + rcvState = 0; + if (rcptIndex == rcpts.Length) { + sentState = SentState.FINISHED_RCPT; + } + else { + sentState = SentState.BEGAN_RCPT; + } + return rcpt; + break; + + case SentState.FINISHED_RCPT: + DebugWriteLine("GetLine while in state FINISHED_RCPT...sending DATA\n"); + if (rcvState != ReceivedState.RECEIVED_250) { + break; + } + string dataCommand = "DATA \r\n"; + dataIndex = 0; + rcvState = 0; + sentState = SentState.BEGAN_DATA; + return dataCommand; + break; + + case SentState.BEGAN_DATA: + DebugWriteLine("GetLine while in state BEGAN_DATA...sending DATA\n"); + if (rcvState != ReceivedState.RECEIVED_354) { + break; + } + string data; + if (dataIndex == emailContents.Length) { + DebugWriteLine("Completed reading email...sending .\n"); + data = "."; + sentState = SentState.SENT_EHLO; + rcvState = 0; + + emailContents = corpus[nxtEmail]; + if(emailContents == null) { + DebugStub.WriteLine("null email! nxtEmail {0}\n", __arglist(nxtEmail)); + DebugStub.Break(); + } + nxtEmail++; + if (nxtEmail == corpus.Length) { + nxtEmail = 0; + } + + } + else { + data = emailContents[dataIndex]; + if ((data.Length == 1) && data[0] == '.') { + //prepend another . to avoid false positives that this is the last line + data = "." + data; + } + dataIndex++; + //DebugWriteLine("Got {0}\n", __arglist(data)); + } + return data; + break; + default: + DebugStub.WriteLine("In unexpected state {0}\n", __arglist(sentState)); + DebugStub.Break(); + break; + } + DebugStub.WriteLine("EmailClient GetLine in state {0} never got 250\n", __arglist(sentState)); + DumpEmail(emailContents); + throw new EmailClientError(); + } + + + public static String FindFrom(String[] lines) + { + foreach (string line in lines) { + if (line.Length == 0) { + return null; // Header ended. + } + if (line.StartsWith("From: ")) { + return line.Substring(6); + } + } + return null; + } + + public static string FindTo(String prefix, String[] lines) + { + for (int i = 0; i < lines.Length; i++) { + if (lines[i].Length == 0) { + return null; // Header ended. + } + if (lines[i].StartsWith(prefix)) { + string to = lines[i++].Substring(prefix.Length); + while (lines[i].Length > 0 && (lines[i][0] == ' ' || lines[i][0] == '\t')) { + to = to + lines[i++]; + } + return to; + } + } + return null; + } + } + + public class ServerSession + { + private static int idCount = 0; + private int id; + private string command; + private string server; + private EmailClient emailClient; + private long emailLimit; + + [Conditional("SMTPAGENT_VERBOSE")] + public static void DebugWriteLine(string format) + { + DebugStub.WriteLine(format); + } + + [Conditional("SMTPAGENT_VERBOSE")] + public static void DebugWriteLine(string format, __arglist) + { + DebugStub.WriteLine(format, new ArgIterator(__arglist)); + } + + public ServerSession(string[][] corpus, long limit, int startIndex) + { + this.emailClient = new EmailClient(corpus, startIndex); + this.id = idCount++; + this.server = "0.0.0.0"; + this.emailLimit = limit; + } + + public string GetCommand(String line) + { + int i = line.IndexOf(' '); + if (i < 0) { + command = line; + return null; + } + command = line.Substring(0, i).ToUpper(); + return command; + } + + private enum CurrentState { + HAD_NONE = 0, + HAD_HELO = 1, + HAD_MAIL = 2, + HAD_RCPT = 3, + }; + + public void Loop() + { + String client; + String line; + CurrentState cstate = CurrentState.HAD_NONE; + String from = null; + String to = null; + + //Open a new connection to the mailstore for each smtp connection. + //Each client connection stays allive until all emails are sent. + + DirectoryServiceContract.Imp! dsImp = SmtpServer.dsEp.Acquire(); + + MailStoreContract.Imp! msImp; + MailStoreContract.Exp! msExp; + MailStoreContract.NewChannel(out msImp, out msExp); + + ErrorCode errorCode; + if (!SdsUtils.Bind(SmtpServer.mailstorePath, dsImp, msExp, out errorCode)) { + DebugWriteLine("Failed to bind to mail store...error {0}\n", + __arglist(SdsUtils.ErrorCodeToString(errorCode))); + Console.WriteLine("Failed to bind to mail store...error {0}\n", + SdsUtils.ErrorCodeToString(errorCode)); + + DebugStub.Break(); + SmtpServer.dsEp.Release(dsImp); + delete msImp; + return; + } + SmtpServer.dsEp.Release(dsImp); + + msImp.RecvMailStoreReady(); + + emailClient.SendLine("220 " + server + " Singularity Simple SMTP Service Ready"); + + long numReceived = 0; + bool abort = false; + DateTime beginEpoch = DateTime.Now; + DateTime endEpoch; + + while (!abort) { + line = emailClient.GetLine(); + if (line == null) { + break; + } + + GetCommand(line); + + switch (command) { + case "EHLO": + client = line.Split(new Char[] {' '})[1]; + Console.WriteLine(":{0}: EHLO {1}", id, client); + + emailClient.SendLine("250-" + server + " greets " + client); + emailClient.SendLine("250-8BITMIME"); + emailClient.SendLine("250 HELP"); + from = null; + to = null; + cstate = CurrentState.HAD_HELO; + DebugWriteLine("SMTPServer: Got EHLO\n"); + break; + + case "HELO": + client = line.Split(new Char[] {' '})[1]; + Console.WriteLine(":{0}: HELO {1}", id, client); + emailClient.SendLine("250 " + server + " greets " + client); + from = null; + to = null; + cstate = CurrentState.HAD_HELO; + DebugWriteLine("SMTPServer: Got HELO\n"); + break; + + case "MAIL": + if (cstate != CurrentState.HAD_HELO) { + goto default; + } + + if (line.StartsWith("MAIL FROM:")) { + int pos = line.IndexOf("<") + 1; + int beg = line.LastIndexOf(':'); + if (beg < pos) { + beg = pos; + } + int end = line.IndexOf('>'); + from = line.Substring(beg, end - beg).ToLower(); + emailClient.SendLine("250 OK"); + cstate = CurrentState.HAD_MAIL; + DebugWriteLine("SMTPServer: Got MAIL from: {0}\n", __arglist(from)); + } + else { + emailClient.SendLine("501 Syntax error in parameters"); + } + break; + + case "RCPT": + if (cstate != CurrentState.HAD_MAIL && cstate != CurrentState.HAD_RCPT) { + goto default; + } + DebugWriteLine("Got RCPT\n"); + if (line.StartsWith("RCPT TO:")) { + int pos = line.IndexOf("<") + 1; + int beg = line.LastIndexOf(':'); + if (beg < pos) { + beg = pos; + } + int end = line.IndexOf('>'); + string address = line.Substring(beg, end - beg).ToLower(); + + if (SmtpServer.addresses.ContainsKey(address)) { + emailClient.SendLine("250 OK"); + if (to != null) { + to = to + ";" + address; + } + else { + to = address; + } + cstate = CurrentState.HAD_RCPT; + } + else { + emailClient.SendLine("250 Accepting for forwarding."); + cstate = CurrentState.HAD_RCPT; + if (to != null) { + to = to + ";" + "mail_forward@enron.com"; + } + else { + to = "mail_forward@enron.com"; + } + } + DebugWriteLine("Got address to: {0}\n", __arglist(to)); + } + else { + emailClient.SendLine("501 Syntax error in parameters"); + } + break; + + case "DATA": + if (cstate != CurrentState.HAD_RCPT) { + goto default; + } + DebugWriteLine("Got DATA\n"); + emailClient.SendLine("354 Start mail input; end with ."); + + Buffer buffer = Buffer.Allocate(65536); + bool good = false; + + for (;;) { + int off = buffer.Prepare(1024); + string buffLine = emailClient.GetLine(); + int len = buffLine.Length; + Encoding.ASCII.GetBytes(buffLine, 0, len, buffer.Data, off); + + if (len < 0) { + break; + } + + if (len == 1 && buffer.Data[off] == '.') { + DebugWriteLine("Got final . char\n"); + good = true; + break; + } + else { + DebugWriteLine("S: {0}", + __arglist(Encoding.ASCII.GetString(buffer.Data, off, len))); + buffer.Save(len); + } + } + + if (good) { + bool succeeded = false; + char[] in ExHeap! addresses = Bitter.FromString(to); + byte[] in ExHeap! data + = Bitter.FromByteArray(buffer.Data, 0, buffer.Size); + + // Console.WriteLine("SmtpAgaint: Email from {0} to {1}", from, to); + msImp.SendSaveMessage(addresses, data); + switch receive + { + case msImp.SaveAck(): + succeeded = true; + break; + + case msImp.SaveNak(error): + Console.WriteLine("SmtpAgent: Server dropped email, "+ + "error={0}", error); + break; + + case msImp.ChannelClosed(): + break; + } + if (succeeded) { + numReceived++; + if ((numReceived % 100) == 0) { + Console.Write("."); + } + emailClient.SendLine("250 OK"); + } + else { + emailClient.SendLine("554 Transaction failed"); + } + } + else { + emailClient.SendLine("554 Transaction failed"); + // Dump(); + throw new Exception("554 Transaction failed"); + } + + buffer.Release(); + cstate = CurrentState.HAD_HELO; + from = null; + to = null; + if (numReceived == emailLimit) { + abort = true; + } + + break; + + case "NOOP": + emailClient.SendLine("250 OK"); + break; + + case "HELP": + emailClient.SendLine("250 OK"); + break; + + case "RSET": + emailClient.SendLine("250 OK"); + from = null; + to = null; + cstate = CurrentState.HAD_HELO; + break; + + case "QUIT": + emailClient.SendLine("221 " + server + " Service closing transmission channel"); + abort = true; + break; + + default: + emailClient.SendLine("503 Unrecognized command [" + command + "]"); + abort = true; + break; + } + } + endEpoch = DateTime.Now; + Console.WriteLine(":{0}: Session closed", id); + TimeSpan delta = endEpoch - beginEpoch; + if (delta.TotalSeconds > 0) { + Console.WriteLine( + "\nDuration(s:ms) {0:f2} emails {1} rate() {2:e3} eps", + delta.TotalSeconds, + emailLimit, + emailLimit/ delta.TotalSeconds + ); + } + if (delta.TotalMilliseconds == 0) { + Console.WriteLine("Too fast! < 1 milliseconds to run\n"); + } + else { + Console.WriteLine("Duration ms {0:f2} rate {1:f2} epms {2:e3} eps\n", + delta.TotalMilliseconds, + (float) ((float)emailLimit / delta.TotalMilliseconds), + emailLimit / (delta.TotalMilliseconds / 1000) + ); + } + delete msImp; + } + } + + public class SmtpServer + { + public static String[][] corpus; + public static int numCorpusEmails = 12754; + public static int[] startPos; + public const int _port = 25; + public static bool verbose; + + public static TRef dsEp; + + public static SortedList addresses; + public static string mailstorePath; + + [Conditional("SMTPAGENT_VERBOSE")] + public static void DebugWriteLine(string format) + { + DebugStub.WriteLine(format); + } + + [Conditional("SMTPAGENT_VERBOSE")] + public static void DebugWriteLine(string format, __arglist) + { + DebugStub.WriteLine(format, new ArgIterator(__arglist)); + } + + + //replicating c# Array.Resize that does not appear to be available + //in singularity. + private static void Resize(ref string[] oldString, int newSize) + { + if(newSize < 0) { + DebugStub.WriteLine("Resize size < 0!!\n"); + DebugStub.Break(); + throw new EmailClientError(); + } + + string[] newString = new string[newSize]; + int length; + if (oldString.Length < newSize) { + length = oldString.Length; + } + else { + length = newSize; + } + + for(int i = 0; i < length; i++) { + newString[i] = oldString[i]; + } + + oldString = newString; + } + + public static void GenerateCorpus(string path, long numberEmails) + { + DebugWriteLine("Reading test corpus at path {0}\n", __arglist(path)); + FileStream fsInput = new FileStream(path, FileMode.Open, FileAccess.Read); + StreamReader reader = new StreamReader(fsInput); + + corpus = new string[numCorpusEmails][]; + + int arrSize; + int numLines; + string line; + string[] emailContents; + //XXX currently we hard code the size of the corpus into the app + //we do this because we have generated a list of random + //starting positions for each connection that is built against + //the corpus... + for(int i = 0; i < numCorpusEmails; i++) { + arrSize = 1024; + emailContents = new string[arrSize]; + numLines = 0; + while((line = reader.ReadLine()) != null) { + if (line.Equals("DEADBEEFDEADBEEFDEADBEEF")) { + break; + } + if (numLines == arrSize) { + arrSize = arrSize * 2; + Resize(ref emailContents, arrSize); + } + emailContents[numLines] = line; + numLines++; + } + if(line == null) { + DebugStub.WriteLine("line null!\n"); + if (numLines == 0 || numLines == 1) { + DebugStub.WriteLine("numlines {0}\n", __arglist(numLines)); + DebugStub.Break(); + } + } + Resize(ref emailContents, numLines); + corpus[i] = emailContents; +#if false + DebugStub.WriteLine("Parsed email {0} contents:\n", __arglist(i)); + foreach (string newline in emailContents) { + DebugStub.WriteLine("line {0}\n", __arglist(newline)); + } +#endif + } + reader.Close(); + DebugStub.WriteLine("Generate corpus complete\n"); + } + + private static int StringToInt(string line) + { + int result = 0; + for (int i = 0; i < line.Length && line[i] >= '0' && line[i] <= '9'; i++) { + result = result * 10 + (line[i] - '0'); + } + + return result; + } + + + public static void GenerateStartPositions(string path, long numConn) + { + DebugWriteLine("Reading test corpus at path {0}\n", __arglist(path)); + startPos = new int[100]; + + FileStream fsInput = new FileStream(path, FileMode.Open, FileAccess.Read); + StreamReader reader = new StreamReader(fsInput); + + int cnt = 0; + string line; + while((line = reader.ReadLine()) != null) { + startPos[cnt] = StringToInt(line); + if (startPos[cnt] < 0) { + DebugStub.Break(); + } + cnt++; + } + } + + internal static int AppMain(Parameters! config) + { + string server = config.server; + if (server[0] >= '0' && server[0] <= '9') { + server = "[" + server + "]"; + } + if (config.numConn < 1 || config.numConn > 100) { + Console.WriteLine("numConn must be between [1...100]\n"); + return -1; + } + + // Connect to the MailStore. + Console.WriteLine("SmtpAgent: Connecting to MailStore."); + DirectoryServiceContract.Imp! dsImp = config.nsRef.Acquire(); + dsImp.RecvSuccess(); + + MailStoreContract.Imp! msImp; + MailStoreContract.Exp! msExp; + MailStoreContract.NewChannel(out msImp, out msExp); + + mailstorePath = config.mailstorePath; + + ErrorCode error; + if (!SdsUtils.Bind(config.mailstorePath, dsImp, msExp, out error)) { + DebugStub.WriteLine("Failed to bind to mail store...error {0}\n", + __arglist(SdsUtils.ErrorCodeToString(error))); + Console.WriteLine("Failed to bind to mail store...error {0}\n", + SdsUtils.ErrorCodeToString(error)); + + delete dsImp; + delete msImp; + return -1; + } + + msImp.RecvMailStoreReady(); + + // Retrieve vaild address list. + char[] in ExHeap! buffer; + msImp.SendGetAddressList(); + msImp.RecvGetAck(out buffer); + addresses = ReadUniqueList(Bitter.ToString(buffer)); + delete buffer; + + delete msImp; + // Save the endpoint. + dsEp = new TRef(dsImp); + + GenerateCorpus(config.emailCorpus, config.numToSend); + GenerateStartPositions(config.startPositionsFile, config.numConn); + Console.WriteLine("SmtpAgent: Spawning {0} threads sending {1} emails per connection", + config.numConn, config.numToSend); + + Thread[] workers = new Thread[config.numConn]; + ServerSession[] conns = new ServerSession[config.numConn]; + + for(int i = 0; i < config.numConn; i++) { + conns[i] = new ServerSession(corpus, config.numToSend, startPos[i]); + workers[i] = new Thread(conns[i].Loop); + workers[i].Start(); + } + + for(int i = 0; i < config.numConn; i++) { + workers[i].Join(); + } + + // Close the Store engine. + dsImp = dsEp.Acquire(); + if (dsImp != null) { + delete dsImp; + } + + return 0; + } + + public static string PopNextAddress(ref string content) + { + if (content.Length == 0) { + return null; + } + + int i = content.IndexOf(';'); + string ret = (i > 0) ? content.Substring(0, i) : content; + content = (i > 0) ? content.Substring(i + 1) : ""; + return ret; + } + + public static SortedList ReadUniqueList(string content) + { + SortedList list = new SortedList(); + string address; + + while ((address = PopNextAddress(ref content)) != null) { + if (!list.ContainsKey(address)) { + list.Add(address, address); + } + } + return list; + } + } +} + + diff --git a/base/Applications/Eventing/EvStress/EvStress.csproj b/base/Applications/Eventing/EvStress/EvStress.csproj new file mode 100644 index 0000000..5a5b914 --- /dev/null +++ b/base/Applications/Eventing/EvStress/EvStress.csproj @@ -0,0 +1,40 @@ + + + + + + + + + EvStress + true + Exe + + + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/Eventing/EvStress/EvStress.sg b/base/Applications/Eventing/EvStress/EvStress.sg new file mode 100644 index 0000000..579df75 --- /dev/null +++ b/base/Applications/Eventing/EvStress/EvStress.sg @@ -0,0 +1,272 @@ + +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +using System.Diagnostics; +using System.Threading; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +using Microsoft.Singularity; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.V1.Services; + +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.UnitTest; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Test.Contracts; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Eventing; +using Microsoft.Singularity.TestEvent; + +namespace Microsoft.Singularity +{ + + [TestClass] + public class EvStress : TestClass + { + + internal class StressLauncher + { + static EvStress1 Log; + static uint Loops; + static int Threads; + static AutoResetEvent WaitEvent; + + public StressLauncher( string testName, + uint storageSize, + uint storageOptions, + uint sourceOptions, + uint iterations) + { + Console.WriteLine("Launching stress {0}: StorageSize={1} bytes, StorageOptions={2:x}, SourceOptions={3:x}", + testName, + storageSize, + storageOptions, + sourceOptions); + + Loops = iterations; + + WaitEvent = new AutoResetEvent(false); + + Log = EvStress1.Create(testName, storageSize, storageOptions, sourceOptions); + } + + public static void ChildThread() + { + if (WaitEvent == null || Log == null) { + + return; + } + + for (int i = 0; i < Loops; i++) { + + Log.Log(100); + } + + if (Interlocked.Decrement(ref Threads) == 0) { + + WaitEvent.Set(); + } + } + + public static void StartStress(int threads) + { + if (WaitEvent == null || Log == null) { + + Console.WriteLine("Failed to allocate resources"); + return; + } + + Threads = threads; + Console.WriteLine("Stress started with {0} threads", Threads); + + DateTime startTime = DateTime.Now; + for (uint i = 0; i < threads; i++) { + + Thread peer = new Thread(new ThreadStart(ChildThread)); + + if (peer!= null) { + + peer.Start(); + } else { + + WaitEvent.Set(); + break; + } + } + + WaitEvent.WaitOne(); + + DateTime endTime = DateTime.Now; + TimeSpan t = endTime-startTime; + Console.WriteLine("Test completed in {0} msec", t.TotalMilliseconds); + } + + } + + + + [ClassInitialize] + public void Init() + { + } + + [TestMethod] + public void StressNoStacks4k() + { + StressLauncher stress1 = new StressLauncher("StressNoStacks4k", + 4096, + QualityOfService.RecyclableEvents, + EventSource.ENABLE_ALL_MASK, + 1000000); + if (stress1 != null) { + + for (int i = 1; i <= 16; i <<= 1) { + + StressLauncher.StartStress(i); + } + } + } + + [TestMethod] + public void StressNoStacks1M() + { + StressLauncher stress1 = new StressLauncher("StressNoStacks1M", + 1024*1024, + QualityOfService.RecyclableEvents, + EventSource.ENABLE_ALL_MASK, + 1000000); + if (stress1 != null) { + + for (int i = 1; i <= 16; i <<= 1) { + + StressLauncher.StartStress(i); + } + } + } + + [TestMethod] + public void StressStacks4k() + { + StressLauncher stress1 = new StressLauncher("StressStacks4k", + 4096, + QualityOfService.RecyclableEvents, + EventSource.CAPTURE_STACK_TRACE | EventSource.ENABLE_ALL_MASK, + 1000000); + if (stress1 != null) { + + for (int i = 1; i <= 16; i <<= 1) { + + StressLauncher.StartStress(i); + } + } + } + + [TestMethod] + public void StressStacks1M() + { + StressLauncher stress1 = new StressLauncher("StressStacks1M", + 1024*1024, + QualityOfService.RecyclableEvents, + EventSource.CAPTURE_STACK_TRACE | EventSource.ENABLE_ALL_MASK, + 1000000); + if (stress1 != null) { + + for (int i = 1; i <= 16; i <<= 1) { + + StressLauncher.StartStress(i); + } + } + } + + [TestMethod] + public void StressPermNoStacks4k() + { + StressLauncher stress1 = new StressLauncher("StressPermNoStacks4k", + 4096, + QualityOfService.PermanentEvents, + EventSource.ENABLE_ALL_MASK, + 1000000); + if (stress1 != null) { + + for (int i = 1; i <= 16; i <<= 1) { + + StressLauncher.StartStress(i); + } + } + } + + [TestMethod] + public void StressPermNoStacks1M() + { + StressLauncher stress1 = new StressLauncher("StressPermNoStacks1M", + 1024*1024, + QualityOfService.PermanentEvents, + EventSource.ENABLE_ALL_MASK, + 1000000); + if (stress1 != null) { + + for (int i = 1; i <= 16; i <<= 1) { + + StressLauncher.StartStress(i); + } + } + } + + [TestMethod] + public void StressPermStacks4k() + { + StressLauncher stress1 = new StressLauncher("StressPermStacks4k", + 4096, + QualityOfService.PermanentEvents, + EventSource.CAPTURE_STACK_TRACE | EventSource.ENABLE_ALL_MASK, + 1000000); + if (stress1 != null) { + + for (int i = 1; i <= 16; i <<= 1) { + + StressLauncher.StartStress(i); + } + } + } + + [TestMethod] + public void StressPermStacks1M() + { + StressLauncher stress1 = new StressLauncher("StressPermStacks1M", + 1024*1024, + QualityOfService.PermanentEvents, + EventSource.CAPTURE_STACK_TRACE | EventSource.ENABLE_ALL_MASK, + 1000000); + if (stress1 != null) { + + for (int i = 1; i <= 16; i <<= 1) { + + StressLauncher.StartStress(i); + } + } + } + + [TestMethod] + public void TestAll() + { + StressNoStacks4k(); + StressNoStacks1M(); + StressStacks4k(); + StressStacks1M(); + + StressPermNoStacks4k(); + StressPermNoStacks1M(); + StressPermStacks4k(); + StressPermStacks1M(); + } + } +} + + diff --git a/base/Applications/Eventing/EvStress/EvStress.tst b/base/Applications/Eventing/EvStress/EvStress.tst new file mode 100644 index 0000000..abb5b61 --- /dev/null +++ b/base/Applications/Eventing/EvStress/EvStress.tst @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/base/Applications/Eventing/EventActive/EventActive.csproj b/base/Applications/Eventing/EventActive/EventActive.csproj new file mode 100644 index 0000000..30a000c --- /dev/null +++ b/base/Applications/Eventing/EventActive/EventActive.csproj @@ -0,0 +1,38 @@ + + + + + + + + + EventActive + true + Exe + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/Eventing/EventActive/EventActive.sg b/base/Applications/Eventing/EventActive/EventActive.sg new file mode 100644 index 0000000..1bbc367 --- /dev/null +++ b/base/Applications/Eventing/EventActive/EventActive.sg @@ -0,0 +1,142 @@ + +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +using System.Diagnostics; +using System.Threading; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +using Microsoft.Singularity; +using Microsoft.Singularity.Io; + +using Microsoft.Singularity.V1.Services; + +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.UnitTest; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Test.Contracts; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Eventing; + +namespace Microsoft.Singularity +{ + + [CLSCompliant(false)] + public class TestActiveCounter : ActiveSource + { + + //[ActiveCounter=InterruptCounter, ArraySize=256] + public struct ActiveCounter + { + + public int Hits; + public int Count; + } + + // ???? CODEGEN from here + + public static TestActiveCounter Create(string sourceName, int size) + { + TestActiveCounter Logger = new TestActiveCounter(sourceName, size, ENABLE_ALL_MASK); + + if (Logger != null) { + Logger.Register(); + } + + return Logger; + } + + internal ActiveCounter[] Buffer; + + public override bool Register() + { + Buffer = new ActiveCounter[Count]; + + if (Buffer == null) { + return false; + } + + assert HostController != null; + + if (HostController.RegisterEvent("ActiveCounter", + "ActiveCounter: Hits={0}, Count={1}", + ref EventTypeHandle)) { + + HostController.RegisterEventField(EventTypeHandle, "Hits", 0, DataType.__int); + HostController.RegisterEventField(EventTypeHandle, "Count", 0, DataType.__int); + + } else { + + // The event might have been registered already + // Check whether we foundit already in the table or not + + if (EventTypeHandle == 0) { + return false; + } + } + + unsafe { + + fixed (void * ptr = &Buffer[0]) { + + DebugBufferAddress = (UIntPtr)ptr; + } + } + + // After all internal fields are setup, we can go ahead and register with the controller + + if (!base.Register()) { + + return false; + } + + return true; + } + + TestActiveCounter(string sourceName, int size, uint controlFlags) + :base(sourceName, size, controlFlags) + { + unsafe { + EntrySize = (ushort)sizeof(ActiveCounter); + } + } + } + + [TestClass] + public class EventActive : TestClass + { + + [ClassInitialize] + public void Init() + { + } + + [TestMethod] + public void SimpleActiveCounterTest() + { + TestActiveCounter activeCounter = TestActiveCounter.Create("TestActiveCounter1", 256); + + if (activeCounter != null) { + assert activeCounter.Buffer != null; + for (int i = 0; i < 256; i++) { + activeCounter.Buffer[i].Hits += 1; + activeCounter.Buffer[i].Count = i; + } + } + } + + [TestMethod] + public void TestAll() + { + SimpleActiveCounterTest(); + } + } +} + + diff --git a/base/Applications/Eventing/EventActive/EventActive.tst b/base/Applications/Eventing/EventActive/EventActive.tst new file mode 100644 index 0000000..9ce564e --- /dev/null +++ b/base/Applications/Eventing/EventActive/EventActive.tst @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/base/Applications/Eventing/EventTest/Event.tst b/base/Applications/Eventing/EventTest/Event.tst new file mode 100644 index 0000000..0f0caf4 --- /dev/null +++ b/base/Applications/Eventing/EventTest/Event.tst @@ -0,0 +1,7 @@ + + + + + + + diff --git a/base/Applications/Eventing/EventTest/EventTest.csproj b/base/Applications/Eventing/EventTest/EventTest.csproj new file mode 100644 index 0000000..25a61ea --- /dev/null +++ b/base/Applications/Eventing/EventTest/EventTest.csproj @@ -0,0 +1,37 @@ + + + + + + + EventTest + true + Exe + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/Eventing/EventTest/EventTest.sg b/base/Applications/Eventing/EventTest/EventTest.sg new file mode 100644 index 0000000..d806f57 --- /dev/null +++ b/base/Applications/Eventing/EventTest/EventTest.sg @@ -0,0 +1,926 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +using System.Diagnostics; +using System.Threading; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +using Microsoft.Singularity; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.V1.Services; + +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.UnitTest; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Test.Contracts; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Eventing; +using Microsoft.Singularity.TestEvent; + +namespace Microsoft.Singularity +{ + + [TestClass] + public class EventTest : TestClass + { + uint Loops = 1000; + + // needed to workaround Phoenix MSIL/PDB reader bug + public EventTest() + { + } + + [ClassInitialize] + public void Init() + { + DetermineLoopCount(); + } + + + internal TimeSpan LogEntryLoop(TestEv Log, ref uint succeeded) + { + succeeded = 0; + DateTime startTime = DateTime.Now; + //DebugStub.Break(); + if (Log != null) { + + for (int i = 0; i < Loops; i++) { + if (Log.Log(i)) { + succeeded += 1; + } + } + } + DateTime endTime = DateTime.Now; + TimeSpan t = endTime-startTime; + return t; + } + + internal void DetermineLoopCount() + { + + TestEv Log; + uint succeeded = 0; + + Console.WriteLine("Determining the number of iterations"); + + Log = TestEv.Create("Test calibration", + 64*1024, + QualityOfService.RecyclableEvents, + EventSource.ENABLE_ALL_MASK); + + if (Log == null) { + + Console.WriteLine("Failure to create test", DateTime.Now); + return; + } + + TimeSpan t; + do { + + Loops *= 10; + t = LogEntryLoop(Log, ref succeeded); + Console.WriteLine("Loop of {0} completed in {1} msec", Loops, t.TotalMilliseconds); + + } while (t.TotalMilliseconds < 1000); + + Console.WriteLine("Calibration completed. Tests will perform {0} operations",Loops); + + } + + internal class QueryContext : EnumerationContext{ + + public Int32 PreviousValue = -1; + public int Count = 0; + public bool Forward; + + public QueryContext(bool forward) + { + Forward = forward; + } + } + + static internal bool fieldDelegate(FieldDescriptor fieldDescriptor, + object obj, + ref EnumerationContext context) + { + if ((fieldDescriptor != null) && (obj != null)) { + + Console.WriteLine(" {0}:{1}:{2} = {3}", + fieldDescriptor.Offset, + fieldDescriptor.GetTypeName(), + fieldDescriptor.Name, + obj.ToString()); + } + + return true; + } + + + static internal bool EntryDelegate(EventDescriptor currentEntry, + QueryBuffer buffer, + ref EnumerationContext context) + { + + QueryContext ctx = context as QueryContext; + + if (ctx == null) { + + Console.WriteLine("Invalid context received"); + DebugStub.Break(); + return true; + } + + + object o = null; + + if ((currentEntry != null) && (buffer != null)) { + + o = currentEntry.GetProperty(buffer, "EventId"); + } + + if (o == null) { + + Console.WriteLine("Invalid property read"); + DebugStub.Break(); + return true; + } + + Int32 value = (Int32)o; + + ctx.Count += 1; + + if (ctx.PreviousValue != -1) { + + if (ctx.Forward) { + + if (ctx.PreviousValue != (value - 1)) { + + Console.WriteLine("Invalid sequence {0} != {1}", ctx.PreviousValue, (value - 1)); + DebugStub.Break(); + } + + } else { + + if (ctx.PreviousValue != (value + 1)) { + + Console.WriteLine("Invalid sequence {0} != {1}", ctx.PreviousValue, (value + 1)); + DebugStub.Break(); + } + } + } + + ctx.PreviousValue = value; + return true; + } + + internal void TestQuery(TestEv Log, bool forward) + { + if ((Log != null) && (Log.Storage != null)) { + + QuerySession query = new QuerySession(); + + QueryContext context = new QueryContext(forward); + EnumerationContext enumContext = context; + + + if ((query != null) && (context != null) && + query.Initialize(Log.Storage, forward)) { + + query.EnumerateEntries(new QueryEntryDelegate(EntryDelegate), + ref enumContext); + } + Console.WriteLine("Enumeration returned {0} entries. Last value = {1}", + context.Count, + context.PreviousValue); + } + + } + + internal bool TestQueryLimit(TestEv Log, UIntPtr queryHandle, bool forward, int limit) + { + if ((Log != null) && (Log.Storage != null)) { +#if TEST_QUERY + QuerySession query = new QuerySession(); + + if ((query != null) && query.Initialize(Log.Storage, forward)) { + + query.EnumerateEntries(new QueryEntryDelegate(EntryDelegate)); + } + + + int value = 0; + unsafe { + + UIntPtr type; + ulong timeStamp; + ushort size; + byte [] memoryBuffer = new byte[256]; + + UIntPtr entryHandle; + + fixed(byte * buffer = &memoryBuffer[0]) { + + entryHandle = Log.Storage.QueryNextEntry(queryHandle, + &type, + &timeStamp, + &size, + buffer, + 256); + + value = *((int *)buffer); + } + + if (entryHandle != 0) { + + if (forward) { + + } else { + + // Check for the invariant on walking the entries + // backward. We should not see any of the new entries + // after we started the query + + if (value > limit) { + + Console.WriteLine("Invalid entry {0} != {1}", value, limit); + DebugStub.Break(); + } + + } + + return true; + + } else { + + return false; + } + } +#endif // TEST_QUERY + } + + return false; + } + + internal TimeSpan LogQueryEntryLoop(TestEv Log, + uint loops, + uint queryStep, + bool forward, + ref uint succeeded, ref uint queried) + { + succeeded = 0; + DateTime startTime = DateTime.Now; + UIntPtr queryHandle = 0; + queried = 0; + if (Log != null) { + + for (int i = 0; i < loops; i++) { + + if (Log.Log(i)) { + + succeeded += 1; + } + + if (i == queryStep) { + + // Start query at this point in log. Verify the consistency of entries + // as we add more items to the log + + if (Log.Storage != null) { + + queryHandle = Log.Storage.CreateQueryView(forward); + } + } + + if (queryHandle != 0) { + + if (TestQueryLimit(Log, queryHandle, forward, (int)queryStep)) { + + queried += 1; + } + } + } + } + DateTime endTime = DateTime.Now; + TimeSpan t = endTime-startTime; + + if (queryHandle != 0) { + + if ((Log != null) && (Log.Storage != null)) { + Log.Storage.DeleteQueryView(queryHandle); + } + } + + return t; + } + + + internal TimeSpan TestQueryPermutations(string testName, + uint storageSize, + uint storageOptions, + uint sourceOptions, + uint loopCount, + uint queryStep, + bool forward, + uint wrapThreshold) + { + + TestEv Log; + uint succeeded = 0; + uint queried = 0; + bool errorOccured; + TimeSpan t; + + do { + + errorOccured = false; + + Log = TestEv.Create(testName, storageSize, storageOptions, sourceOptions); + + if (Log == null) { + + Console.WriteLine("Failure to create test"); + return TimeSpan.Zero; + } + + t = LogQueryEntryLoop(Log, + loopCount, + queryStep, + forward, + ref succeeded, + ref queried); + + if (succeeded != loopCount) { + + Console.WriteLine("Failure to log entries. {0} succeeded instead of {1}", + succeeded, + loopCount); + errorOccured = true; + DebugStub.Break(); + } + + if (forward) { + + if ((queried == 0) && (queryStep != loopCount)) { + + Console.WriteLine("Invalid test invariant: Step = {0}, Queried = {1}, Loops = {2}", + queryStep, + queried, + loopCount); + errorOccured = true; + DebugStub.Break(); + + } + + } else { + + if (((queryStep + queried) != loopCount) + && + ((queryStep + 1) != queried) + && + (loopCount < wrapThreshold)) { + + Console.WriteLine("Invalid test invariant: Step = {0}, Queried = {1}, Loops = {2}", + queryStep, + queried, + loopCount); + errorOccured = true; + DebugStub.Break(); + } + } + + if (errorOccured) { + + if (t.TotalMilliseconds != 0) { + + Console.WriteLine("Succeeded = {0}. Queried = {1}. Elapsed time {2} msec. Rate = {3} KOps/sec\n", + succeeded, + queried, + t.TotalMilliseconds, + Loops/t.TotalMilliseconds); + } else { + + Console.WriteLine("Succeeded = {0}. Queried = {1}. Elapsed time {2} msec\n", + succeeded, + queried, + t.TotalMilliseconds); + } + } + + } while (errorOccured); + + return t; + + } + + + + internal TimeSpan TestLoop(string testName, uint storageSize, uint storageOptions, uint sourceOptions) + { + + TestEv Log; + uint succeeded = 0; + + Console.WriteLine("Test {0}: StorageSize={1} bytes, StorageOptions={2:x}, SourceOptions={3:x}", + testName, + storageSize, + storageOptions, + sourceOptions); + + Log = TestEv.Create(testName, storageSize, storageOptions, sourceOptions); + + if (Log == null) { + + Console.WriteLine("Failure to create test"); + return TimeSpan.Zero; + } + + TimeSpan t = LogEntryLoop(Log, ref succeeded); + + if (t.TotalMilliseconds != 0) { + + Console.WriteLine("Succeeded = {0}. Elapsed time {1} msec. Rate = {2} KOps/sec\n", + succeeded, + t.TotalMilliseconds, + Loops/t.TotalMilliseconds); + } else { + + Console.WriteLine("Succeeded = {0}. Elapsed time {1} msec\n", + succeeded, + t.TotalMilliseconds); + } + + TestQuery(Log, false); + TestQuery(Log, true); + + return t; + + } + + [TestMethod] + public void TestMonitoring() + { + TestEv Log; + + Console.WriteLine("Test Monitoring.Log(Monitoring.Provider.Processor...)"); + + DateTime startTime = DateTime.Now; + + for (int i = 0; i < Loops; i++) { + + Monitoring.Log(Monitoring.Provider.Processor, + (ushort)2, 0, + (uint)0, 0, 0, 0, 0); + + } + + DateTime endTime = DateTime.Now; + TimeSpan t = endTime-startTime; + + Console.WriteLine("Elapsed time {0} msec. Rate = {1} KOps/sec\n", + t.TotalMilliseconds, + Loops/t.TotalMilliseconds); + } + + [TestMethod] + public void TestTracing() + { + TestEv Log; + + Console.WriteLine("Test Tracing.Log(Tracing.Debug,string[100])"); + + DateTime startTime = DateTime.Now; + + for (int i = 0; i < Loops; i++) { + + Tracing.Log(Tracing.Debug, //100 chars + "____________________________________________________________________________________________________"); + } + + DateTime endTime = DateTime.Now; + TimeSpan t = endTime-startTime; + + Console.WriteLine("Elapsed time {0} msec. Rate = {1} KOps/sec\n", + t.TotalMilliseconds, + Loops/t.TotalMilliseconds); + + Console.WriteLine("Test Tracing.Log(Tracing.Debug,string[1])"); + + startTime = DateTime.Now; + + for (int i = 0; i < Loops; i++) { + + Tracing.Log(Tracing.Debug,"1"); + } + + endTime = DateTime.Now; + t = endTime-startTime; + + Console.WriteLine("Elapsed time {0} msec. Rate = {1} KOps/sec\n", + t.TotalMilliseconds, + Loops/t.TotalMilliseconds); + + // one argument + + Console.WriteLine("Test Tracing.Log(Tracing.Debug,Event={0})"); + + startTime = DateTime.Now; + + for (int i = 0; i < Loops; i++) { + + Tracing.Log(Tracing.Debug,"Event={0}", 100); + } + + endTime = DateTime.Now; + t = endTime-startTime; + + Console.WriteLine("Elapsed time {0} msec. Rate = {1} KOps/sec\n", + t.TotalMilliseconds, + Loops/t.TotalMilliseconds); + + // two arguments + + Console.WriteLine("Test Tracing.Log(Tracing.Debug,Event1={0}, Event2={1})"); + + startTime = DateTime.Now; + + for (int i = 0; i < Loops; i++) { + + Tracing.Log(Tracing.Debug,"Event1={0}, Event2={1}", 100, 200); + } + + endTime = DateTime.Now; + t = endTime-startTime; + + Console.WriteLine("Elapsed time {0} msec. Rate = {1} KOps/sec\n", + t.TotalMilliseconds, + Loops/t.TotalMilliseconds); + // three arguments + + Console.WriteLine("Test Tracing.Log(Tracing.Debug,Event1={0}, Event2={1}, Event3={2})"); + + startTime = DateTime.Now; + + for (int i = 0; i < Loops; i++) { + + Tracing.Log(Tracing.Debug,"Event1={0}, Event2={1}, Event3={2}", 100, 200, 300); + } + + endTime = DateTime.Now; + t = endTime-startTime; + + Console.WriteLine("Elapsed time {0} msec. Rate = {1} KOps/sec\n", + t.TotalMilliseconds, + Loops/t.TotalMilliseconds); + + } + + [TestMethod] + public void TestRecNoStack4() + { + TestLoop("TestLogger:TestRecyclable-NoStacks-4k", + 4096, + QualityOfService.RecyclableEvents, + EventSource.ENABLE_ALL_MASK); + } + + [TestMethod] + public void TestRecNoStack64() + { + TestLoop("TestLogger:TestRecyclable-NoStacks-64k", + 4096 * 16, + QualityOfService.RecyclableEvents, + EventSource.ENABLE_ALL_MASK); + } + + [TestMethod] + public void TestRecNoStack1M() + { + TestLoop("TestLogger:TestRecyclable-NoStacks-1M", + 1024*1024, + QualityOfService.RecyclableEvents, + EventSource.ENABLE_ALL_MASK); + } + + [TestMethod] + public void TestRecNoStack10M() + { + TestLoop("TestLogger:TestRecyclable-NoStacks-10M", + 10*1024*1024, + QualityOfService.RecyclableEvents, + EventSource.ENABLE_ALL_MASK); + } + + [TestMethod] + public void TestRecStack4() + { + TestLoop("TestLogger:TestRecyclable-Stacks-4k", + 4096, + QualityOfService.RecyclableEvents, + EventSource.CAPTURE_STACK_TRACE | EventSource.ENABLE_ALL_MASK); + } + + [TestMethod] + public void TestRecStack64() + { + TestLoop("TestLogger:TestRecyclable-Stacks-64k", + 4096 * 16, + QualityOfService.RecyclableEvents, + EventSource.CAPTURE_STACK_TRACE | EventSource.ENABLE_ALL_MASK); + } + + [TestMethod] + public void TestRecStack1M() + { + TestLoop("TestLogger:TestRecyclable-Stacks-1M", + 1024*1024, + QualityOfService.RecyclableEvents, + EventSource.CAPTURE_STACK_TRACE | EventSource.ENABLE_ALL_MASK); + } + + [TestMethod] + public void TestRecStack10M() + { + TestLoop("TestLogger:TestRecyclable-Stacks-10M", + 10*1024*1024, + QualityOfService.RecyclableEvents, + EventSource.CAPTURE_STACK_TRACE | EventSource.ENABLE_ALL_MASK); + } + + [TestMethod] + public void TestPermNoStack4() + { + TestLoop("TestLogger:TestPermanent-NoStacks-4k", + 4096, + QualityOfService.PermanentEvents, + EventSource.ENABLE_ALL_MASK); + } + + [TestMethod] + public void TestPermNoStack64() + { + TestLoop("TestLogger:TestPermanent-NoStacks-64k", + 4096 * 16, + QualityOfService.PermanentEvents, + EventSource.ENABLE_ALL_MASK); + } + + + [TestMethod] + public void TestPermNoStack1M() + { + TestLoop("TestLogger:TestPermanent-NoStacks-1M", + 1024*1024 , + QualityOfService.PermanentEvents, + EventSource.ENABLE_ALL_MASK); + } + + + [TestMethod] + public void TestPermNoStack10M() + { + TestLoop("TestLogger:TestPermanent-NoStacks-10M", + 1024*1024 * 10, + QualityOfService.PermanentEvents, + EventSource.ENABLE_ALL_MASK); + } + + + [TestMethod] + public void TestPermStack4() + { + TestLoop("TestLogger:TestPermanent-Stacks-4k", + 4096, + QualityOfService.PermanentEvents, + EventSource.CAPTURE_STACK_TRACE | EventSource.ENABLE_ALL_MASK); + } + + [TestMethod] + public void TestPermStack64() + { + TestLoop("TestLogger:TestPermanent-Stacks-64k", + 4096 * 16, + QualityOfService.PermanentEvents, + EventSource.CAPTURE_STACK_TRACE | EventSource.ENABLE_ALL_MASK); + } + + + [TestMethod] + public void TestPermStack1M() + { + TestLoop("TestLogger:TestPermanent-Stacks-1M", + 1024*1024 , + QualityOfService.PermanentEvents, + EventSource.CAPTURE_STACK_TRACE | EventSource.ENABLE_ALL_MASK); + } + + + [TestMethod] + public void TestPermStack10M() + { + TestLoop("TestLogger:TestPermanent-Stacks-10M", + 1024*1024 * 10, + QualityOfService.PermanentEvents, + EventSource.CAPTURE_STACK_TRACE | EventSource.ENABLE_ALL_MASK); + } + + + [TestMethod] + public void TestRecDisabled() + { + TestLoop("TestLogger:TestRecyclable-Disabled-4k", + 4096, + QualityOfService.RecyclableEvents, + 0); + } + + [TestMethod] + public void ExhaustiveQueryTest() + { + TimeSpan t; + + for (uint i = 1; i < 150; i += 1) { + + Console.WriteLine("Iteration {0}", i); + + for (uint j = 0; j <= i; j += 1) { + + t = TestQueryPermutations("TestLogger:BackQueryPermutations:"+i+":"+j, + 4096, + QualityOfService.RecyclableEvents, + EventSource.ENABLE_ALL_MASK, + i, + j, + false, + 100); + + if (t.TotalMilliseconds > 1000) { + + Console.WriteLine("Test stopped at iteration {0}", i); + return; + } + + t = TestQueryPermutations("TestLogger:ForwardQueryPermutations:"+i+":"+j, + 4096, + QualityOfService.RecyclableEvents, + EventSource.ENABLE_ALL_MASK, + i, + j, + true, + 100); + + if (t.TotalMilliseconds > 1000) { + + Console.WriteLine("Test stopped at iteration {0}", i); + return; + } + } + } + } + + // + // Source query routines + // + + void TestSourceQuery() + { + Controller hostController = Controller.GetLocalController(); + + if (hostController == null) { + return; + } + + QueryBuffer entryBuffer = new QueryBuffer(256); + + if (entryBuffer == null) { + return; + } + + int currentSize = 100; + UIntPtr [] sourceArray = new UIntPtr[currentSize]; + + if (sourceArray != null) { + + int sourceCount = hostController.QuerySourcesList(sourceArray, currentSize); + + if (sourceCount > currentSize) { + + sourceCount = currentSize; + } + + for (int i = 0; i < sourceCount; i++) { + UIntPtr sourceHandle = sourceArray[i]; + UIntPtr storageHandle = 0; + UIntPtr eventType = 0; + UInt16 count = 0; + string bufferName = ""; + + if (hostController.GetSourceInformation(sourceHandle, ref storageHandle, ref eventType, ref count, ref bufferName)) { + + Console.WriteLine("Source Handle={0}, Storage={1}, type={2}, name={3}, count={4}", + sourceHandle, storageHandle, eventType, count, bufferName); + + int index = 0; + + if (storageHandle == 0) { + + EventDescriptor descriptor = new EventDescriptor(hostController, eventType); + + if ((descriptor != null) && descriptor.Initialize()) { + for (;;) { + + if (!hostController.QueryActiveEntry(sourceHandle, index++, ref entryBuffer)) { + break; + } + } + + + } + } + + } + } + } + } + + void TestEnums() + { + + EvTest2 log2 = EvTest2.Create("AutoEnumTest2", + 4096, + QualityOfService.RecyclableEvents, + EventSource.ENABLE_ALL_MASK | EventSource.CAPTURE_DEBUG_PRINT); + + if (log2 == null) { + + Console.WriteLine("Failure to create test"); + return ; + } + + log2.SaveEvent(2, EvTest2.MyEnum.Val1); + log2.SaveEvent(8, EvTest2.MyEnum.Val2); + + log2.SaveEventFlags(4, 1, EvTest2.MyFlagsEnum.Flag0001); + log2.SaveEventFlags(5, 2, EvTest2.MyFlagsEnum.Flag0008); + log2.SaveEventFlags(6, 3, EvTest2.MyFlagsEnum.FlagdifferentSize); + + log2.SaveEventCombination(256, 22, EvTest2.MyEnum.Val1, EvTest2.MyFlagsEnum.Flag0001); + + + } + + [TestMethod] + public void TestAll() + { + + // Legacy mechanisms + + TestEnums(); + + TestMonitoring(); + TestTracing(); + +// DiagnosisService.TestKernelStorage(); + + // Recyclable events (ring buffers) + + TestRecNoStack4(); + TestRecNoStack64(); + TestRecNoStack1M(); + TestRecNoStack10M(); + + // Recyclable events (ring buffers) + stacks + + TestRecStack4(); + TestRecStack64(); + TestRecStack1M(); + TestRecStack10M(); + + // Permanent events (drop new events at OOM) + + TestPermNoStack4(); + TestPermNoStack64(); + TestPermNoStack1M(); + TestPermNoStack10M(); + + // Permanent events (drop new events at OOM) + stacks + + TestPermStack4(); + TestPermStack64(); + TestPermStack1M(); + TestPermStack10M(); + + // Overhead for test condition and functio call in tracing + + TestRecDisabled(); + +// ExhaustiveQueryTest(); + } + } +} + diff --git a/base/Applications/Eventing/testdef/testdef.cs b/base/Applications/Eventing/testdef/testdef.cs new file mode 100644 index 0000000..89cc077 --- /dev/null +++ b/base/Applications/Eventing/testdef/testdef.cs @@ -0,0 +1,93 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +using System.Runtime.CompilerServices; +using Microsoft.Singularity.Transform; +using Microsoft.Singularity.Eventing; + +namespace Microsoft.Singularity +{ + + public class EventEnumAttribute : Attribute + { + // mark classes & methods for eventing + } + + [Event] + public abstract class TestEv : EventSource + { + [Event] + public abstract bool Log(int EventId); + public static string Log_Format = "EventLogger: EventId={0}"; + + } + + [Event] + public abstract class EvStress1 : EventSource + { + [Event] + public abstract bool Log(int Arg1); + public static string Log_Format = "EvStress: seq value={0}"; + } + + // + // Test source that includes enums, and multiple event functions per same source + // The declaration of the schema for enum is still manual, but the rest of the code can leverage + // the existing codegen support + // + + [Event] + public abstract class EvTest2 : EventSource + { + [Event] + public abstract bool SaveEvent(int Arg1, MyEnum en); + public static string SaveEvent_Format = "testenum: arg={0}, enum=[{1}]"; + + [Event] + public abstract bool SaveEventFlags(int Arg1, int Arg2, MyFlagsEnum flag ); + public static string SaveEventFlags_Format = "FlagsEnum: arg={0}, {1}; enum=[{2}]"; + + [Event] + public abstract bool SaveEventCombination(int Arg1, int Arg2, MyEnum en, MyFlagsEnum flag); + public static string SaveEventCombination_Format = "Combinations: arg={0},{1}; enum=[{2}], Flags = [{3}]"; + + [EventEnum] + public enum MyEnum{ + Val1, + Val2 + }; + + public static UIntPtr MyEnum_Handle; + + [EventEnum] + public enum MyFlagsEnum{ + Flag0001 = 0x0001, + Flag0008 = 0x0008, + FlagdifferentSize = 16, + }; + + public static UIntPtr MyFlagsEnum_Handle; + + public override void RegisterEnumSymbols() + { + base.RegisterEnumSymbols(); + + if (HostController.RegisterEnum("MyEnum", DataType.__int32, ref MyEnum_Handle)) { + + HostController.RegisterEnumValue(MyEnum_Handle, "Val1", (UInt64)MyEnum.Val1, 0); + HostController.RegisterEnumValue(MyEnum_Handle, "Val2", (UInt64)MyEnum.Val2, 0); + } + + if (HostController.RegisterEnum("MyFlagsEnum", DataType.__int32, ref MyFlagsEnum_Handle)) { + + HostController.RegisterEnumValue(MyFlagsEnum_Handle, "Flag0001", (UInt64)MyFlagsEnum.Flag0001, (byte)'A'); + HostController.RegisterEnumValue(MyFlagsEnum_Handle, "Flag0008", (UInt64)MyFlagsEnum.Flag0008, (byte)'B'); + HostController.RegisterEnumValue(MyFlagsEnum_Handle, "FlagdifferentSize", (UInt64)MyFlagsEnum.FlagdifferentSize, (byte)'Z'); + } + } + } +} diff --git a/base/Applications/Eventing/testdef/testdef.csproj b/base/Applications/Eventing/testdef/testdef.csproj new file mode 100644 index 0000000..17a3566 --- /dev/null +++ b/base/Applications/Eventing/testdef/testdef.csproj @@ -0,0 +1,32 @@ + + + + + + + + Library + TestEvent + C# + + + + + + + + + + + + diff --git a/base/Applications/Godot/GOdot.csproj b/base/Applications/Godot/GOdot.csproj index 0b7b3d1..40bd623 100644 --- a/base/Applications/Godot/GOdot.csproj +++ b/base/Applications/Godot/GOdot.csproj @@ -1,8 +1,6 @@  + + + + + + Exe + Hello2 + + + + + + + + + diff --git a/base/Applications/HelloMp/HelloMp.cs b/base/Applications/HelloMp/HelloMp.cs index 6ced2be..0ddab3c 100644 --- a/base/Applications/HelloMp/HelloMp.cs +++ b/base/Applications/HelloMp/HelloMp.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: HelloMp.cs -// // Note: Simple Singularity test program. // using System; diff --git a/base/Applications/HelloMp/HelloMp.csproj b/base/Applications/HelloMp/HelloMp.csproj index 8867b2a..3fceff9 100644 --- a/base/Applications/HelloMp/HelloMp.csproj +++ b/base/Applications/HelloMp/HelloMp.csproj @@ -1,5 +1,12 @@ - - + + + diff --git a/base/Applications/HelloMpAsm/HelloMpAsm.cpp b/base/Applications/HelloMpAsm/HelloMpAsm.cpp index f4cb8ba..04dd523 100644 --- a/base/Applications/HelloMpAsm/HelloMpAsm.cpp +++ b/base/Applications/HelloMpAsm/HelloMpAsm.cpp @@ -1,8 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // - +// ---------------------------------------------------------------------------- int main () { diff --git a/base/Applications/Iltest/ilstart.cpp b/base/Applications/Iltest/ilstart.cpp new file mode 100644 index 0000000..f4472b8 --- /dev/null +++ b/base/Applications/Iltest/ilstart.cpp @@ -0,0 +1,77 @@ +////////////////////////////////////////////////////////////////////////////// +// +// ilstart.cpp - Singularity Hardware Abstraction Layer +// +// Copyright Microsoft Corporation. +// + +typedef unsigned short bartok_char; +typedef signed short int16; +typedef signed int int32; +typedef __int64 int64; +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef unsigned __int64 uint64; + +struct Struct_System_ObjectHeader +{ + int32 syncBlockValue; +}; + +struct Class_System_Object +{ + void * f_vtable; +}; + +struct Class_System_VTable : Class_System_Object +{ +}; + +struct Struct_Microsoft_Singularity_V1_Services_DebugService +{ + static void g_Break(); + static void g_Print(bool); + static void g_Print(bartok_char); + static void g_Print(int32); + static void g_Print(int64); + static void g_Print(uint32); + static void g_Print(uint64); + static void g_Print(uint8); +}; + +////////////////////////////////////////////////////////////////////////////// +// +struct Class_Microsoft_Singularity_Iltest +{ + static int g_Main(); + static int g_ThreadMain(int threadIndex); +}; + +extern "C" void __cdecl _throwDispatcherExplicitAddrAfter() +{ + // ecx = exception + // dex = throw addr + Struct_Microsoft_Singularity_V1_Services_DebugService::g_Print('E'); +} + +extern "C" int32 __fastcall IlStart(int32 threadIndex) +{ + Struct_Microsoft_Singularity_V1_Services_DebugService::g_Print('-'); + Struct_Microsoft_Singularity_V1_Services_DebugService::g_Print('I'); + Struct_Microsoft_Singularity_V1_Services_DebugService::g_Print('\n'); + if (threadIndex == -1) { + return Class_Microsoft_Singularity_Iltest::g_Main(); + } + else { + return Class_Microsoft_Singularity_Iltest::g_ThreadMain(threadIndex); + } +} + +extern "C" void __cdecl _pushStackMark() +{ +} + +extern "C" void __cdecl _popStackMark() +{ +} diff --git a/base/Applications/Iltest/iltest.cs b/base/Applications/Iltest/iltest.cs new file mode 100644 index 0000000..a0b51a0 --- /dev/null +++ b/base/Applications/Iltest/iltest.cs @@ -0,0 +1,195 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: This file contains the main entry point for the C# +// portion of Singularity. +// + +using Microsoft.Singularity.V1.Services; +using System; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace Microsoft.Singularity +{ + [CCtorIsRunDuringStartup] + public class Iltest + { +#if TEST_MEMORY_ABI + public static uint Probe(uint addr) + { + uint begin; + uint bytes; + bool used = PageTableService.Query(addr, out begin, out bytes); + DebugService.Print(' '); + DebugService.Print((ulong)begin); + DebugService.Print('.'); + DebugService.Print((ulong)begin + bytes); + DebugService.Print(' '); + if (used) { + DebugService.Print('u'); + } + DebugService.Print('\n'); + return begin + bytes; + } +#endif // TEST_MEMORY_ABI + + public static int ThreadMain(int threadIndex) + { + DebugService.Print('t'); + DebugService.Print(threadIndex); + DebugService.Print('\n'); +#if TEST_STACK_ABI + Test1(1); +#endif // TEST_STACK_ABI + DebugService.Print('.'); + return 998; + } + + public static int Main() + { + int i = 10; + long l = 100; + byte b = 1; + bool o = true; + + DebugService.Print('H'); + DebugService.Print(b); + DebugService.Print(','); + DebugService.Print(i); + DebugService.Print(','); + DebugService.Print(l); + DebugService.Print(','); + DebugService.Print(o); + DebugService.Print('\n'); + +#if TEST_MEMORY_ABI + uint tag = PageTableService.GetProcessTag(); + DebugService.Print('p'); + DebugService.Print((ulong)tag); + DebugService.Print('\n'); + + uint pages = PageTableService.GetPageCount(); + DebugService.Print('n'); + DebugService.Print(pages); + DebugService.Print('\n'); + + uint addr = PageTableService.Allocate(0x1000); + DebugService.Print('a'); + DebugService.Print((ulong)addr); + DebugService.Print('\n'); + + Probe(addr); + + addr = 1; + for (i = 0; i < 20 && addr != 0 && addr < 0xc0000000; i++) { + addr = Probe(addr); + } +#endif // TEST_MEMORY_ABI +#if TEST_STACK_ABI + Test1(1); +#endif // TEST_STACK_ABI + + return 999; + } + +#if TEST_STACK_ABI + [NoInline] + internal static int Test1(int a) + { + DebugService.Print('1'); + DebugService.Print('('); DebugService.Print(a); + DebugService.Print(')'); + a = Test2(a, a + 1) + 1; + DebugService.Print('='); + DebugService.Print(a); + DebugService.Print('\n'); + return a; + } + + [NoInline] + internal static int Test2(int a, int b) + { + DebugService.Print('2'); + DebugService.Print('('); DebugService.Print(a); + DebugService.Print(','); DebugService.Print(b); + DebugService.Print(')'); + a = Test3(a, a + 1, a + 2) + 1; + DebugService.Print('='); + DebugService.Print(a); + DebugService.Print('\n'); + return a; + } + + [NoInline] + internal static int Test3(int a, int b, int c) + { + DebugService.Print('3'); + DebugService.Print('('); DebugService.Print(a); + DebugService.Print(','); DebugService.Print(b); + DebugService.Print(','); DebugService.Print(c); + DebugService.Print(')'); + a = Test4(a, a + 1, a + 2, a + 3) + 1; + DebugService.Print('='); + DebugService.Print(a); + DebugService.Print('\n'); + return a; + } + + [NoInline] + internal static int Test4(int a, int b, int c, int d) + { + DebugService.Print('4'); + DebugService.Print('('); DebugService.Print(a); + DebugService.Print(','); DebugService.Print(b); + DebugService.Print(','); DebugService.Print(c); + DebugService.Print(','); DebugService.Print(d); + DebugService.Print(')'); + a = Test5(a, a + 1, a + 2, a + 3, a + 4) + 1; + DebugService.Print('='); + DebugService.Print(a); + DebugService.Print('\n'); + return a; + } + + [NoInline] + internal static int Test5(int a, int b, int c, int d, int e) + { + DebugService.Print('5'); + DebugService.Print('('); DebugService.Print(a); + DebugService.Print(','); DebugService.Print(b); + DebugService.Print(','); DebugService.Print(c); + DebugService.Print(','); DebugService.Print(d); + DebugService.Print(','); DebugService.Print(e); + DebugService.Print(')'); + a = Test6(a, a + 1, a + 2, a + 3, a + 4, a + 5) + 1; + DebugService.Print('='); + DebugService.Print(a); + DebugService.Print('\n'); + return a; + } + + [NoInline] + internal static int Test6(int a, int b, int c, int d, int e, int f) + { + DebugService.Print('6'); + DebugService.Print('('); DebugService.Print(a); + DebugService.Print(','); DebugService.Print(b); + DebugService.Print(','); DebugService.Print(c); + DebugService.Print(','); DebugService.Print(d); + DebugService.Print(','); DebugService.Print(e); + DebugService.Print(','); DebugService.Print(f); + DebugService.Print(')'); + a = a + 100; + DebugService.WalkStack(); + DebugService.Print('='); + DebugService.Print(a); + DebugService.Print('\n'); + return a; + } +#endif // TEST_STACK_ABI + } +} diff --git a/base/Applications/IoLibtest/IoLibTest.csproj b/base/Applications/IoLibtest/IoLibTest.csproj index decf47a..166fbcb 100644 --- a/base/Applications/IoLibtest/IoLibTest.csproj +++ b/base/Applications/IoLibtest/IoLibTest.csproj @@ -1,8 +1,6 @@  + diff --git a/base/Applications/NameSpace/SDSTest/SDSTest.csproj b/base/Applications/NameSpace/SDSTest/SDSTest.csproj new file mode 100644 index 0000000..d886890 --- /dev/null +++ b/base/Applications/NameSpace/SDSTest/SDSTest.csproj @@ -0,0 +1,29 @@ + + + + + + + Exe + SDSTest + true + + + + + + + + + + + + + + diff --git a/base/Applications/NameSpace/SDSTest/SDSTest.sg b/base/Applications/NameSpace/SDSTest/SDSTest.sg new file mode 100644 index 0000000..0cf551d --- /dev/null +++ b/base/Applications/NameSpace/SDSTest/SDSTest.sg @@ -0,0 +1,565 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// + +using System; +using System.Threading; +using Microsoft.Singularity; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Security; +using Microsoft.Singularity.V1.Services; +using Microsoft.SingSharp; +using Microsoft.SingSharp.Runtime; +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.Configuration; + +using Microsoft.Contracts; +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Io; + +[assembly: Transform(typeof(ApplicationResourceTransform))] +[assembly: ApplicationPublisherAttribute("singularity.microsoft.com")] +[assembly: AssertPrivilegeAttribute("$register-privilege.localhost")] + +namespace Microsoft.Singularity.Applications +{ + [ConsoleCategory(HelpMessage="Test SIP-based Directory Service", DefaultAction=true)] + internal class Parameters { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [Endpoint] + public readonly TRef nsRef; + + [StringParameter( "mountPoint", Mandatory=true, Position=0, HelpMessage="Location of dsp to test")] + internal string mountPoint; + + [BoolParameter( "debug", Default=false, HelpMessage="Send results to debugger")] + internal bool printDebug; + + reflective internal Parameters(); + + internal int AppMain() { + return DirectoryMain.AppMain(this); + } + } + + + public contract Dummy : ServiceContract + { + out message Garbage(); + + override state Start: one + { + Garbage! -> End; + } + state End: one {} + } + + public class DirectoryServiceTests + { + public int passCount; + public int failCount; + private bool nakLoaded; + private bool dspLoaded; + private bool doDebug; + + public DirectoryServiceTests (bool nakLoaded, bool dspLoaded, bool doDebug) + { + failCount = 0; + passCount = 0; + this.nakLoaded = nakLoaded; + this.dspLoaded= dspLoaded; + this.doDebug = doDebug; + } + + public void CheckError( string! verb, string path, ErrorCode expected, ErrorCode actual, bool ok) + { if (ok){ + //Console.WriteLine("{0} on {1} was successful.", verb, path); + } + if (expected != actual) { + failCount++; + string s = String.Format("FAIL: {0} on {1}. expected={2}, actual={3}. ok={4}", + verb, + path, + SdsUtils.ErrorCodeToString(expected), + SdsUtils.ErrorCodeToString(actual), + ok ? "true" : "false" + ); + Console.WriteLine(s); + if (doDebug) DebugStub.WriteLine(s); + //DebugStub.Break(); + } + else { + passCount++; + string s = String.Format("PASS: {0} on {1} ({2}) ok={3}.", + verb, + path, + SdsUtils.ErrorCodeToString(expected), + ok ? "true" : "false" + ); + Console.WriteLine(s); + if (doDebug) DebugStub.WriteLine(s); + } + } + + public void CheckError( string! verb, string path, NodeType expected, NodeType actual, bool ok) + { if (ok){ + //Console.WriteLine("{0} on {1} was successful.", verb, path); + } + if (expected != actual) { + failCount++; + Console.WriteLine("FAIL: {0} on {1}. expected={2}, actual={3}. ok={4}", + verb, + path, + SdsUtils.NodeTypeToString(expected), + SdsUtils.NodeTypeToString(actual), + ok ? "true" : "false" + ); + } + else { + passCount++; + Console.WriteLine("PASS: {0} on {1} ({2}) ok={3}.", + verb, + path, + SdsUtils.NodeTypeToString(expected), + ok ? "true" : "false" + ); + } + } + + public bool DoCreateDirectory(DirectoryServiceContract.Imp! ds, string! path, ErrorCode expected) + { + ErrorCode errorOut; + bool ok = SdsUtils.CreateDirectory(path, ds, out errorOut); + CheckError("CreateDirectory", path, expected, errorOut, ok); + return ok; + } + + public void DoDeleteDirectory(DirectoryServiceContract.Imp! ds, string! path, ErrorCode expected) + { + ErrorCode errorOut; + bool ok = SdsUtils.DeleteDirectory(path, ds, out errorOut); + CheckError("DeleteDirectory", path, expected, errorOut, ok); + } + + public void DoCreateLink(DirectoryServiceContract.Imp! ds, string! path, string! value, ErrorCode expected) + { + ErrorCode errorOut; + bool ok = SdsUtils.CreateLink(path, value, ds, out errorOut); + CheckError("CreateLink", path, expected, errorOut, ok); + } + + public void DoGetLink(DirectoryServiceContract.Imp! ds, string! path, string expectedValue, ErrorCode expected) + { + ErrorCode errorOut; + string linkValue; + bool ok = SdsUtils.GetLinkValue(path, ds, out linkValue, out errorOut); + CheckError("GetLinkValue", path, expected, errorOut, ok); + if (ok) { + if (expectedValue != linkValue) { + Console.WriteLine(" expected({0}) got({1})", expectedValue, linkValue); + } + } + } + + public bool DoBind(DirectoryServiceContract.Imp! ds, string! path, + [Claims]ServiceContract.Exp! exp, ErrorCode expected) + { + ErrorCode errorOut; + string linkValue; + bool ok = SdsUtils.Bind(path, ds, exp, out errorOut); + CheckError("Bind", path, expected, errorOut, ok); + return ok; + } + + //simulate FileOpen in FsUtils + public bool DoBind(string! path, + [Claims]ServiceContract.Exp! exp, ErrorCode expected) + { + + DirectoryServiceContract.Imp! ds = DirectoryService.NewClientEndpoint(); + ErrorCode errorOut; + string linkValue; + bool ok = SdsUtils.Bind(path, ds, exp, out errorOut); + CheckError("Bind", path, expected, errorOut, ok); + delete ds; + return ok; + } + + public void DoDeleteLink(DirectoryServiceContract.Imp! ds, string! path, ErrorCode expected) + { + ErrorCode errorOut; + bool ok = SdsUtils.DeleteLink(path, ds, out errorOut); + CheckError("DeleteLink", path, expected, errorOut, ok); + } + + public void DoRegister(DirectoryServiceContract.Imp! ds, + string! path, + [Claims] ServiceProviderContract.Imp! service, + ErrorCode expected) + { + ErrorCode errorOut; + bool ok = SdsUtils.Register(path, ds, service, out errorOut); + CheckError("Register", path, expected, errorOut, ok); + } + + public void DoDeregister(DirectoryServiceContract.Imp! ds, string! path, ErrorCode expected) + { + ErrorCode errorOut; + ServiceProviderContract.Imp:Start service; + + bool ok = SdsUtils.Deregister(path, ds, out service, out errorOut); + CheckError("Deregister", path, expected, errorOut, ok); + delete service; + } + + public void DoGetAttributes(DirectoryServiceContract.Imp! ds, + string! path, + ErrorCode expected, + NodeType expectedNodeType) + { + ErrorCode errorOut; + long length; + NodeType nodeType; + FileAttributesRecord fileAttributes; + + bool ok = SdsUtils.GetAttributes(path, ds, out fileAttributes, out errorOut); + CheckError("GetAttributes", path, expectedNodeType, fileAttributes.Type, ok); + CheckError("GetAttributes", path, expected, errorOut, ok); + } + + public void AttributesTests(DirectoryServiceContract.Imp! ds) + { + DoCreateDirectory(ds, "/d1", ErrorCode.NoError); + DoCreateDirectory(ds, "/d2", ErrorCode.NoError); + DoCreateLink(ds, "/d1/Link1", "/d2", ErrorCode.NoError); + DoCreateDirectory(ds, "/d1/Link1/d2", ErrorCode.NoError); + + DoGetAttributes(ds, "/d1", ErrorCode.NoError, NodeType.Directory); + DoGetAttributes(ds, "/d1/Link1", ErrorCode.NoError, NodeType.SymLink); + DoGetAttributes(ds, "/init/testpe/testpe", ErrorCode.NoError, NodeType.IoMemory); + DoGetAttributes(ds, "/stress", ErrorCode.NoError, NodeType.ServiceProvider); + DoGetAttributes(ds, "/d1/Link1/d2", ErrorCode.NoError, NodeType.Directory); + DoGetAttributes(ds, "/d1/garbage", ErrorCode.NotFound, NodeType.BadNode); + + DoDeleteLink(ds, "/d1/Link1",ErrorCode.NoError); + DoDeleteDirectory(ds, "/d1", ErrorCode.NoError); + DoDeleteDirectory(ds, "/d2/d2", ErrorCode.NoError); + DoDeleteDirectory(ds, "/d2", ErrorCode.NoError); + } + + public void ProviderTests(DirectoryServiceContract.Imp! ds) + { + + ServiceProviderContract.Imp! c1; + ServiceProviderContract.Exp! s1; + ServiceProviderContract.NewChannel(out c1, out s1); + + ServiceProviderContract.Imp! c2; + ServiceProviderContract.Exp! s2; + ServiceProviderContract.NewChannel(out c2, out s2); + + ServiceProviderContract.Imp! c3; + ServiceProviderContract.Exp! s3; + ServiceProviderContract.NewChannel(out c3, out s3); + + DoRegister(ds, "/dev/sp1", c1, ErrorCode.NoError); + DoRegister(ds, "/dev/sp1", c2, ErrorCode.AlreadyExists); + DoRegister(ds, "/notFound/sp1", c3, ErrorCode.NotFound); + DoDeregister(ds, "/dev/sp1", ErrorCode.NoError); + + // manipulate providers within an SDS provider (DSP) + if (dspLoaded) { + ServiceProviderContract.Imp! c4; + ServiceProviderContract.Exp! s4; + ServiceProviderContract.NewChannel(out c4, out s4); + + ServiceProviderContract.Imp! c5; + ServiceProviderContract.Exp! s5; + ServiceProviderContract.NewChannel(out c5, out s5); + + ServiceProviderContract.Imp! c6; + ServiceProviderContract.Exp! s6; + ServiceProviderContract.NewChannel(out c6, out s6); + + DoRegister(ds, "/dsp/sp1", c4, ErrorCode.NoError); + DoRegister(ds, "/dsp/sp1", c5, ErrorCode.AlreadyExists); + DoDeregister(ds, "/dsp/sp1", ErrorCode.NoError); + DoCreateDirectory(ds, "/dsp/dir1", ErrorCode.NoError); + DoRegister(ds, "/dsp/dir1/sp1", c6, ErrorCode.NoError); + DoDeregister(ds, "/dsp/dir1/sp1", ErrorCode.NoError); + DoDeleteDirectory(ds, "/dsp/dir1", ErrorCode.NoError); + delete s4; + delete s5; + delete s6; + } + delete s1; + delete s2; + delete s3; + } + + public int DirectoryTests(DirectoryServiceContract.Imp! ds) + { + // create and delete directories with and without symbolic links + + //create and delete some directiores + DoCreateDirectory(ds, "/d1", ErrorCode.NoError); + DoCreateDirectory(ds, "/d1", ErrorCode.AlreadyExists); + DoCreateDirectory(ds, "/d2", ErrorCode.NoError); + DoCreateDirectory(ds, "/d1/d1", ErrorCode.NoError); + + + // add a symbolic link to the mix + DoCreateLink(ds, "/d1/Link1", "/d2", ErrorCode.NoError); + DoCreateDirectory(ds, "/d1/Link1/d2", ErrorCode.NoError); + + DoDeleteDirectory(ds, "/d1/Link1/d2", ErrorCode.NoError); + DoDeleteDirectory(ds, "/d1/Link1/d2", ErrorCode.NotFound); + + // create a link to a link + DoCreateLink(ds, "/d1/LinkToLink", "/d2/Link1", ErrorCode.NoError); + DoCreateLink(ds, "/d2/Link1", "/d3", ErrorCode.NoError); + DoCreateDirectory(ds, "/d3", ErrorCode.NoError); + DoCreateDirectory(ds, "/d1/LinkToLink/d3", ErrorCode.NoError); + + // try and delete a non-empty directory + DoDeleteDirectory(ds, "/d1", ErrorCode.DirectoryNotEmpty); + + // Attempt to delete non directories + + //service provider + DoDeleteDirectory(ds, "/stress", ErrorCode.NotDirectory); + //link + DoDeleteDirectory(ds, "/d2/Link1", ErrorCode.NotDirectory); + //IoMemory + DoDeleteDirectory(ds, "/init/testpe/manifest", ErrorCode.NotDirectory); + + + // clean up residule files + DoDeleteDirectory(ds, "/d1/d1", ErrorCode.NoError); + DoDeleteLink(ds, "/d1/Link1",ErrorCode.NoError); + DoDeleteLink(ds, "/d1/LinkToLink",ErrorCode.NoError); + DoDeleteDirectory(ds, "/d1", ErrorCode.NoError); + DoDeleteDirectory(ds, "/d1", ErrorCode.NotFound); + + DoDeleteLink(ds, "/d2/Link1",ErrorCode.NoError); + DoDeleteDirectory(ds, "/d2", ErrorCode.NoError); + + DoDeleteDirectory(ds, "/d3/d3", ErrorCode.NoError); + DoDeleteDirectory(ds, "/d3", ErrorCode.NoError); + + return 0; + } + + public void LinkTests(DirectoryServiceContract.Imp! ds) + { + DoCreateLink(ds, "/d2/Link1", "/d3", ErrorCode.NotFound); // no /dir1 + DoCreateDirectory(ds, "/d2", ErrorCode.NoError); + DoCreateLink(ds, "/d2/Link1", "/d3", ErrorCode.NoError); + DoCreateLink(ds, "/d2/Link1", "/d3", ErrorCode.AlreadyExists); + + DoGetLink(ds, "/d2/Link1", "/d3", ErrorCode.NoError); + DoGetLink(ds, "/d2", "", ErrorCode.NotLink); + DoDeleteLink(ds, "/d2/Link1",ErrorCode.NoError); + DoDeleteLink(ds, "/d2/Link1",ErrorCode.NotFound); + + DoDeleteDirectory(ds, "/d2", ErrorCode.NoError); + + if (dspLoaded) { + DoCreateLink(ds, "/dsp/rootlink", "/d2", ErrorCode.NoError); + DoCreateLink(ds, "/dsp/rootlink", "/d2", ErrorCode.AlreadyExists); + DoCreateDirectory(ds, "/dsp/d1", ErrorCode.NoError); + DoCreateLink(ds, "/dsp/d1/Link1", "/d2", ErrorCode.NoError); + DoDeleteLink(ds, "/dsp/d1/Link1",ErrorCode.NoError); + DoDeleteLink(ds, "/dsp/rootlink",ErrorCode.NoError); + DoDeleteDirectory(ds, "dsp/d1", ErrorCode.NoError); + } + } + + public void BindTests(DirectoryServiceContract.Imp! ds) + { + // try to bind to the various node types within the root dsp + + FileContract.Imp! fc; + FileContract.Exp! fs; + + ServiceProviderContract.Imp! sc; + ServiceProviderContract.Exp! ss; + + DirectoryServiceContract.Imp! imp; + DirectoryServiceContract.Exp! exp; + + Dummy.Imp! dC; + Dummy.Exp! dS; + + FileContract.NewChannel(out fc, out fs); + DoBind(ds,"/init/testpe/testpe",fs,ErrorCode.NoError); + delete fc; + + Dummy.NewChannel(out dC, out dS); + DoBind(ds,"/init/testpe/testpe",dS,ErrorCode.ContractNotSupported); + delete dC; + + DirectoryServiceContract.NewChannel(out imp , out exp); + DoBind(ds,"/init/testpe/testpe",exp,ErrorCode.ContractNotSupported); + delete imp; + + DoCreateLink(ds, "/link", "/init", ErrorCode.NoError); + FileContract.NewChannel(out fc, out fs); + DoBind(ds,"/link/testpe/testpe",fs,ErrorCode.NoError); + delete fc; + DoDeleteLink(ds, "/link", ErrorCode.NoError); + + FileContract.NewChannel(out fc, out fs); + // bind to dir with File + DoBind(ds,"/init",fs,ErrorCode.ContractNotSupported); + delete fc; + + FileContract.NewChannel(out fc, out fs); + //bind to ServiceProvider with File + DoBind(ds,"/FsCtrl",fs,ErrorCode.ContractNotSupported); + delete fc; + + FileContract.NewChannel(out fc, out fs); + + delete fc; + delete fs; + + //ServiceProvider.NewChannel(out sc, out ss); + // try binding to a dir with all the other types + // bind through links + if (dspLoaded) { + DoCreateDirectory(ds, "/dsp/BindTest", ErrorCode.NoError); + DoCreateLink(ds, "/dsp/BindTest/link", "/BindTest/d2", ErrorCode.NoError); + DoCreateDirectory(ds, "/dsp/BindTest/d1", ErrorCode.NoError); + DoCreateDirectory(ds, "/dsp/BindTest/d2", ErrorCode.NoError); + + DirectoryServiceContract.NewChannel(out imp, out exp); + DoBind(ds,"/dsp/BindTest/d1",exp,ErrorCode.NoError); + delete imp; + + DirectoryServiceContract.NewChannel(out imp, out exp); + DoBind(ds,"/dsp/BindTest/link",exp,ErrorCode.NoError); + delete imp; + + //DoCreateLink(ds, "/dsp/d1/link", "/d2", ErrorCode.NoError); + + + DoDeleteLink(ds, "/dsp/BindTest/link",ErrorCode.NoError); + //DoDeleteLink(ds, "/dsp/d1/link",ErrorCode.NoError); + DoDeleteDirectory(ds, "/dsp/BindTest/d1", ErrorCode.NoError); + DoDeleteDirectory(ds, "/dsp/BindTest/d2", ErrorCode.NoError); + DoDeleteDirectory(ds, "/dsp/BindTest",ErrorCode.NoError); + } + if (nakLoaded) { + // attempt to bind to nak service + // attempt to bind to a closed channel + } + } + } + + public class DirectoryMain + { + private static Process LoadProcess(string[]! arguments) + { + try { + Process process = new Process(arguments); + if (process == null) { + Console.WriteLine("Unable to create process {0}",arguments[0]); + return null; + } + else { + process.Start(); + return process; + } + } + catch (ProcessCreateException) { + Console.WriteLine("Unable to create process {0}",arguments[0]); + } + return null; + } + + internal static int AppMain(Parameters! config) + { + Process dsp = null; + ErrorCode errorOut; + + DirectoryServiceContract.Imp ds = config.nsRef.Acquire(); + if (ds == null) { + throw new Exception("Unable to acquire handle to the Directory Service root"); + } + + ds.RecvSuccess(); + + // see if the test DSP is loaded by attempting to bind to it + // if not present load it + + bool ok; + /// + /// TODO: Fix. SdsUtils FlushCache needs to be re-implemented + /// in order for us to be able to wait for NakService termination + /// + + DirectoryServiceContract.Imp! dspImp; + DirectoryServiceContract.Exp! dspExp; + DirectoryServiceContract.NewChannel(out dspImp, out dspExp); + + string dspMountPoint = config.mountPoint; + ok = SdsUtils.Bind(dspMountPoint, ds, dspExp, out errorOut); + delete dspImp; + + bool dspLoaded = false; + if (!ok) { + Console.WriteLine(" unable to bind to {0}. Reason={1}", + dspMountPoint, SdsUtils.ErrorCodeToString(errorOut)); + delete ds; + return 1; + } + else dspLoaded = true; + + DirectoryServiceTests t = new DirectoryServiceTests(false ,dspLoaded, config.printDebug); + + try { + t.DirectoryTests(ds); + t.AttributesTests(ds); + t.ProviderTests(ds); + t.LinkTests(ds); + t.BindTests(ds); + + Console.WriteLine(" pass={0}, fail={1}", t.passCount, t.failCount); + + + //Thread.Sleep(TimeSpan.FromMilliseconds(1000)); + + if (dsp != null) { + t.DoDeregister(ds,dspMountPoint, ErrorCode.NoError); + } + delete ds; + + //Thread.Sleep(TimeSpan.FromMilliseconds(1000)); + + if (dsp != null) { + dsp.Join(); + } + } + finally { + } + return 0; + } + + private static void Usage(string name) + { + Console.WriteLine("{0} creates a directory.",name); + } + } // class Test +} diff --git a/base/Applications/NameSpace/TestDSP/DirectoryService.sg b/base/Applications/NameSpace/TestDSP/DirectoryService.sg index 197c858..d509677 100644 --- a/base/Applications/NameSpace/TestDSP/DirectoryService.sg +++ b/base/Applications/NameSpace/TestDSP/DirectoryService.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Directory.sg -// // Note: // @@ -29,7 +27,8 @@ using Microsoft.Singularity.Configuration; [assembly: ApplicationPublisherAttribute("singularity.microsoft.com")] [assembly: AssertPrivilegeAttribute("$register-privilege.localhost")] -namespace Microsoft.Application.DSP { +namespace Microsoft.Application.DSP +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] @@ -145,30 +144,30 @@ namespace Microsoft.Application.DSP { // for now, we will just add a rule to allow **any** access private static void InstallAcls(ISdsAcl! aclT) { - /* - // read the rules section from the config - XmlNode node = config.GetChild(PolicyListXmlTag); - if (node == null) { - return; - } - - foreach (XmlNode! rule in node.Children) { - if (rule.Name != "rule") { - continue; - } - - string! resource = (!)rule.GetAttribute("resource", ""); - if (resource.Equals("")) { - continue; - } - string! aclstr = (!)rule.GetAttribute("acl", ""); - if (aclstr.Equals("")) { - continue; - } - - policy.AddRule(resource, aclstr); - } - */ + // + //// read the rules section from the config + //XmlNode node = config.GetChild(PolicyListXmlTag); + //if (node == null) { + // return; + //} +// + //foreach (XmlNode! rule in node.Children) { + // if (rule.Name != "rule") { + // continue; + // } +// + // string! resource = (!)rule.GetAttribute("resource", ""); + // if (resource.Equals("")) { + // continue; + // } + // string! aclstr = (!)rule.GetAttribute("acl", ""); + // if (aclstr.Equals("")) { + // continue; + // } +// + // policy.AddRule(resource, aclstr); + //} + // //policy.AddRule("/", "{/groups/anyall}"); aclT.Set("/", new Acl("{$dsanyrw}|{$dsregister}")); } @@ -188,7 +187,7 @@ namespace Microsoft.Application.DSP { ServiceProviderContract.Imp! imp; ServiceProviderContract.Exp! s; ServiceProviderContract.NewChannel(out imp, out s); - status = Register(config.mountPoint, imp); + status = Register((!)config.mountPoint, imp); if (status != 0) { delete s; diff --git a/base/Applications/NameSpace/TestDSP/TestDSP.csproj b/base/Applications/NameSpace/TestDSP/TestDSP.csproj index 12d5d6e..b09169e 100644 --- a/base/Applications/NameSpace/TestDSP/TestDSP.csproj +++ b/base/Applications/NameSpace/TestDSP/TestDSP.csproj @@ -1,8 +1,6 @@  + @@ -10,6 +18,7 @@ + diff --git a/base/Applications/Network/NetworkCommand.targets b/base/Applications/Network/NetworkCommand.targets index 8e2f29e..39eff62 100644 --- a/base/Applications/Network/NetworkCommand.targets +++ b/base/Applications/Network/NetworkCommand.targets @@ -1,6 +1,13 @@ + + - Exe diff --git a/base/Applications/Network/Route/Route.cs b/base/Applications/Network/Route/Route.cs index 6785575..2e99040 100644 --- a/base/Applications/Network/Route/Route.cs +++ b/base/Applications/Network/Route/Route.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Route.cs -// // Note: Simple Singularity test program. // using System; @@ -26,7 +24,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications.Network { +namespace Microsoft.Singularity.Applications.Network +{ [ConsoleCategory(Action="show", HelpMessage="Show network routing information")] internal class ShowConfig { @@ -77,7 +76,7 @@ namespace Microsoft.Singularity.Applications.Network { } } - [ConsoleCategory(Action="delete", HelpMessage="Show network routing information")] + [ConsoleCategory(Action="delete", HelpMessage="Remove network routing information")] internal class DeleteConfig { [Endpoint] public readonly TRef Stdin; @@ -88,10 +87,10 @@ namespace Microsoft.Singularity.Applications.Network { [Endpoint] public readonly TRef routingRef; - [StringParameter( "address", Mandatory=true, Position=0, HelpMessage="Address to be deleted")] + [StringParameter( "address", Mandatory=true, Position=0, HelpMessage="Address to be removed")] internal string address; - [StringParameter( "destination", Mandatory=true, Position=0, HelpMessage="Address to be added")] + [StringParameter( "destination", Mandatory=true, Position=0, HelpMessage="destination to be removed")] internal string destination; reflective internal DeleteConfig(); @@ -110,7 +109,7 @@ namespace Microsoft.Singularity.Applications.Network { { RouteEntry[] in ExHeap routes; - RoutingContract.Imp routeConn = (config.routingRef).Acquire(); + RoutingContract.Imp routeConn = ((!)config.routingRef).Acquire(); if (routeConn == null) { Console.WriteLine("Could not initialize routing endpoint."); return 1; @@ -123,8 +122,7 @@ namespace Microsoft.Singularity.Applications.Network { if (routes == null) throw new Exception("routes is null"); - for (int i = 0; i < routes.Length; i++) - { + for (int i = 0; i < routes.Length; i++) { Console.WriteLine("Network : " + ChannelUtils.NetworkToString(routes[i].network)); Console.WriteLine("Gateway : " + ChannelUtils.AddressToString(routes[i].gateway)); Console.WriteLine("Interface address : " + ChannelUtils.AddressToString(routes[i].ifaddr)); @@ -141,34 +139,30 @@ namespace Microsoft.Singularity.Applications.Network { { IPv4 gateway; - RoutingContract.Imp routeConn = (config.routingRef).Acquire(); + RoutingContract.Imp routeConn = ((!)config.routingRef).Acquire(); if (routeConn == null) { Console.WriteLine("Could not initialize routing endpoint."); return 1; } routeConn.RecvReady(); - if (IPv4.Parse(config.gateway, out gateway) == false) - { + if (IPv4.Parse(config.gateway, out gateway) == false) { Console.WriteLine("Could not parse gateway address."); delete routeConn; return -1; } - try - { - // note for some reason the compiler doesn't + try { + // NOTE: for some reason the compiler doesn't // realize that ifaddr will definitely be assigned if // we survive the if/switch-receive sequence. Work // around that by initializing. IPv4 ifaddr = new IPv4(0); - if (config.ifAddress == null) - { + if (config.ifAddress == null) { routeConn.SendFindHostRoute((uint)gateway); - switch receive - { + switch receive { case routeConn.Route(RouteEntry r) : ifaddr = new IPv4(r.ifaddr); break; @@ -184,43 +178,37 @@ namespace Microsoft.Singularity.Applications.Network { throw new Exception("routeConn channel closed."); } } - else if (IPv4.Parse(config.ifAddress, out ifaddr) == false) - { + else if (IPv4.Parse(config.ifAddress, out ifaddr) == false) { Console.WriteLine("Could not parse interface address."); delete routeConn; return -1; } - IPContract.Imp ipConn = (config.ipRef).Acquire(); - if (ipConn == null) - { + IPContract.Imp ipConn = ((!)config.ipRef).Acquire(); + if (ipConn == null) { Console.WriteLine("Could not initialize IP endpoint."); delete routeConn; return -1; } - try - { + try { bool isLocal; ipConn.SendIsLocalAddress((uint)ifaddr); ipConn.RecvIsLocal(out isLocal); - if (!isLocal) - { + if (!isLocal) { Console.WriteLine("Proposed interface address is not bound to an interface."); delete routeConn; return -1; } } - finally - { + finally { delete ipConn; ipConn = null; } IPv4Network destination; - if (IPv4Network.Parse(config.destination, out destination) == false) - { + if (IPv4Network.Parse(config.destination, out destination) == false) { Console.WriteLine("Could not parse destination address."); delete routeConn; return -1; @@ -231,8 +219,7 @@ namespace Microsoft.Singularity.Applications.Network { nwrk.netmask = (uint)destination.NetMask; routeConn.SendAddRoute(nwrk, (uint)gateway, (uint)ifaddr); - switch receive - { + switch receive { case routeConn.Err() : Console.WriteLine("Route could not be added -- it may already exist."); break; @@ -245,8 +232,7 @@ namespace Microsoft.Singularity.Applications.Network { throw new Exception("routeConn channel closed"); } } - catch (Exception e) - { + catch (Exception e) { Console.WriteLine("Exception: {0}", e); } delete routeConn; @@ -255,25 +241,22 @@ namespace Microsoft.Singularity.Applications.Network { internal static int Delete(DeleteConfig! config) { - RoutingContract.Imp routeConn = (config.routingRef).Acquire(); - if (routeConn == null) - { + RoutingContract.Imp routeConn = ((!)config.routingRef).Acquire(); + if (routeConn == null) { Console.WriteLine("Could not initialize routing endpoint."); return 1; } routeConn.RecvReady(); IPv4Network destination; - if (IPv4Network.Parse(config.destination, out destination) == false) - { + if (IPv4Network.Parse(config.destination, out destination) == false) { Console.WriteLine("Could not parse destination address."); } Network destNet = ChannelUtils.NetworkToChannelNetwork(destination); routeConn.SendFindSpecificNetRoute(destNet); - switch receive - { + switch receive { case routeConn.NoRouteFound() : Console.WriteLine("No route for destination."); delete routeConn; @@ -290,8 +273,7 @@ namespace Microsoft.Singularity.Applications.Network { routeConn.SendDeleteRoute(destNet); - switch receive - { + switch receive { case routeConn.NoRouteFound() : Console.WriteLine("Unexpected error attempting to delete the route"); break; diff --git a/base/Applications/Network/Route/Route.csproj b/base/Applications/Network/Route/Route.csproj index acf4023..6d1a213 100644 --- a/base/Applications/Network/Route/Route.csproj +++ b/base/Applications/Network/Route/Route.csproj @@ -1,8 +1,6 @@  - + Exe UdpBlast - + diff --git a/base/Applications/Network/UdpGulp/UdpGulp.cs b/base/Applications/Network/UdpGulp/UdpGulp.cs index e533446..a9a2199 100644 --- a/base/Applications/Network/UdpGulp/UdpGulp.cs +++ b/base/Applications/Network/UdpGulp/UdpGulp.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: UdpGulp.cs -// // Note: Simple Singularity test program. // using System; @@ -69,7 +67,8 @@ namespace Microsoft.Singularity.Applications.Network long totalSeconds ) { - int bytesThisRound = 0; + int bytesThisRound = 0; + int packetsThisRound = 0; DateTime now = DateTime.Now; DateTime epochEnd = now + TimeSpan.FromSeconds(1); @@ -84,24 +83,32 @@ namespace Microsoft.Singularity.Applications.Network // necessarily mean the connection // is closed. Thread.Yield(); - break; - case udpConn.Data(uint addr, ushort port2, - byte[]! in ExHeap data): - bytesThisRound += data.Length + FixedHeaderBytes; - delete data; - break; - case udpConn.ChannelClosed(): - throw new Exception("udpConn channel closed"); + break; + case udpConn.Data(uint addr, ushort port2, byte[]! in ExHeap data): + bytesThisRound += data.Length + FixedHeaderBytes; + packetsThisRound++; + delete data; + break; + case udpConn.ChannelClosed(): + throw new Exception("udpConn channel closed"); } now = DateTime.Now; } TimeSpan delta = now - epochStart; - Console.WriteLine( - "Duration(s) {0:f2} rate(bps) {1:e3} bps", + string msg = String.Format( + "Duration(s) {0:f2} rate {1:e3} bps, {2:e3} bytes/sec, {3:e3} pps", delta.TotalSeconds, - 8.0 * bytesThisRound / delta.TotalSeconds); + 8.0 * bytesThisRound / delta.TotalSeconds, + 8.0 * bytesThisRound / delta.TotalSeconds, + 1.0 * packetsThisRound / delta.TotalSeconds + ); + + Console.WriteLine(msg); + DebugStub.WriteLine(msg); + bytesThisRound = 0; + packetsThisRound = 0; epochStart = DateTime.Now; epochEnd = epochStart + TimeSpan.FromSeconds(1); } @@ -137,8 +144,7 @@ namespace Microsoft.Singularity.Applications.Network Console.WriteLine("Closing connection"); udpConn.SendClose(); } - catch (Exception e) - { + catch (Exception e) { Console.WriteLine("Unexpected exception: {0}", e); } finally { @@ -165,7 +171,7 @@ namespace Microsoft.Singularity.Applications.Network } ushort port = (ushort) config.port; - UdpContract.Imp udpConn = (config.udpRef).Acquire(); + UdpContract.Imp udpConn = ((!)config.udpRef).Acquire(); if (udpConn == null) { Console.WriteLine("Could not initialize TCP endpoint."); return 1; diff --git a/base/Applications/Network/UdpGulp/UdpGulp.csproj b/base/Applications/Network/UdpGulp/UdpGulp.csproj index 1613eb9..dc40cec 100644 --- a/base/Applications/Network/UdpGulp/UdpGulp.csproj +++ b/base/Applications/Network/UdpGulp/UdpGulp.csproj @@ -1,8 +1,6 @@  - - - - - - Exe - PingPongIntTest - - - - - - - - - diff --git a/base/Applications/PingPongIntTest/PingPongIntTest.sg b/base/Applications/PingPongIntTest/PingPongIntTest.sg deleted file mode 100644 index f937d2d..0000000 --- a/base/Applications/PingPongIntTest/PingPongIntTest.sg +++ /dev/null @@ -1,38 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: PingPongIntTest.cs -// -// Note: Simple Singularity test program. -// -using System; -using Microsoft.Singularity.V1.Services; -using Microsoft.Singularity.Processor; - - -namespace Microsoft.Singularity.Applications -{ - public class PingPongIntTest - { - public static int Main(String[] args) - { - Console.WriteLine("PingPongIntTest World!"); - - //uint a, b, c, d; - //Processor.ReadCpuid(0, out a, out b, out c, out d); - - int retval = ProcessService.RunPingPongInt(7); - if (retval == 0) { - Console.WriteLine(" Retval: {0} -- successful", retval); - } - else { - Console.WriteLine(" Retval: {0} -- fail", retval); - } - - return 0; - } - } -} diff --git a/base/Applications/Play/Play.csproj b/base/Applications/Play/Play.csproj index 79b6801..7db73b6 100644 --- a/base/Applications/Play/Play.csproj +++ b/base/Applications/Play/Play.csproj @@ -1,8 +1,6 @@  + + + diff --git a/base/Applications/Pong/Pong.sg b/base/Applications/Pong/Pong.sg index f4ea4d8..6619c75 100644 --- a/base/Applications/Pong/Pong.sg +++ b/base/Applications/Pong/Pong.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Pong.cs -// // Note: // @@ -108,28 +106,27 @@ namespace Microsoft.Singularity.Applications ErrorCode errorOut; bool ok = SdsUtils.Bind(devName, ns, exp, out errorOut); if (!ok) { - if (errorOut == ErrorCode.ChannelClosed) - { + if (errorOut == ErrorCode.ChannelClosed) { throw new Exception("Encountered a ChannelClosed while opening Keyboard"); } } else { success = true; } - /* - // ns.SendBind(Bitter.FromString2(devName),exp); - switch receive { - case ns.AckBind(): - success = true; - break; - case ns.NakBind(rejected,error): - delete rejected; - break; - case ns.ChannelClosed(): - delete ns; - throw new Exception("ns unexpectedly closed"); - } - */ + // + //// ns.SendBind(Bitter.FromString2(devName),exp); + //switch receive { + //case ns.AckBind(): + //success = true; + //break; + //case ns.NakBind(rejected,error): + //delete rejected; + //break; + //case ns.ChannelClosed(): + //delete ns; + //throw new Exception("ns unexpectedly closed"); + //} + // if (!success) { DebugStub.WriteLine("OpenKeyboard lookup of {0} failed\n", __arglist(devName)); @@ -446,8 +443,7 @@ namespace Microsoft.Singularity.Applications while (!done) { uint key = 0; keyboard.SendPollKey(); - switch receive - { + switch receive { case keyboard.AckKey(ikey): key = ikey; break; diff --git a/base/Applications/Runtime/AppRuntime.proj b/base/Applications/Runtime/AppRuntime.proj deleted file mode 100644 index 9baac18..0000000 --- a/base/Applications/Runtime/AppRuntime.proj +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/base/Applications/Runtime/Corlib.Native.proj b/base/Applications/Runtime/Corlib.Native.proj deleted file mode 100644 index adea05c..0000000 --- a/base/Applications/Runtime/Corlib.Native.proj +++ /dev/null @@ -1,337 +0,0 @@ - - - - - - $(APPRUNTIMENATIVEDIR)\Corlib.obj - $(OBJROOT)\$(OBJRELRUNTIMEDIR) - $(CFLAGS) /Gr /I$(BINDIR) /I$(SINGULARITY_ROOT)\boot\include /DSINGULARITY_PROCESS=1 - $(AFLAGS) /DSINGULARITY_PROCESS - $(APPRUNTIMEDIR)\Corlib.dll - $(SINGULARITY_ROOT)\Kernel - $(APPRUNTIMENATIVEDIR)\native.lib - $(COLLECTOR_APP) - - - - - - - - - - - $(BARTOK_FLAGS) /Singularity - $(BARTOK_FLAGS) /UnnameTracedPtrs=true - $(BARTOK_FLAGS) /OmitFramePointer=false - $(BARTOK_FLAGS) /GCInlineAllocations=false - $(BARTOK_FLAGS) /GCInlineWriteBarrier=false - $(BARTOK_FLAGS) /GenCoffLineNumber=false - $(BARTOK_FLAGS) /WholeProgram=true - $(BARTOK_FLAGS) /Warnings=true - $(BARTOK_FLAGS) /DisablePInvoke=true - $(BARTOK_FLAGS) /BackEndComments=true - $(BARTOK_FLAGS) $(BARTOK_COLLECTOR) - - - $(CORLIB_BARTOK_FLAGS) - $(CORLIB_BARTOK_FLAGS) /CompileOnly=true - $(CORLIB_BARTOK_FLAGS) /Warnings=true - $(CORLIB_BARTOK_FLAGS) $(BARTOK_COLLECTOR) - $(CORLIB_BARTOK_FLAGS) /r:$(LIBSDIR)\Singularity.V1.dll - $(CORLIB_BARTOK_FLAGS) /r:$(ILLSDIR)\Console.ill - $(CORLIB_BARTOK_FLAGS) /r:$(APPRUNTIMEDIR)\ILHelpers.dll - $(CORLIB_BARTOK_FLAGS) /out: $(APPRUNTIMENATIVEDIR)\CorLib.obj - $(CORLIB_BARTOK_FLAGS) /outdir: $(APPRUNTIMENATIVEDIR) - $(CORLIB_BARTOK_FLAGS) $(APPRUNTIMEDIR)\Corlib.dll - - - /verbosity:silence - $(TESTAPP_BARTOK_FLAGS) /GenAsmHeader="$(APPRUNTIMENATIVEDIR)\halclass.inc" - $(TESTAPP_BARTOK_FLAGS) /GenCppHeader="$(APPRUNTIMENATIVEDIR)\halclass.h" - $(TESTAPP_BARTOK_FLAGS) /IrTreeShake=false - $(TESTAPP_BARTOK_FLAGS) /IrTreeShakeLate=false - $(TESTAPP_BARTOK_FLAGS) /out:$(APPRUNTIMENATIVEDIR)\testapp.obj - $(TESTAPP_BARTOK_FLAGS) /outdir:$(APPRUNTIMENATIVEDIR) - - - - - - - - - - - - - - - - $(BARTOK_FLAGS) /IrSimpleInliner=false - - - - - - - - - $(BARTOK_FLAGS) /LinkedStacks=true - $(BARTOK_FLAGS) /SymbolicDebug=true - $(MASTER)\Native - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/base/Applications/Runtime/Full/App.Corlib.csproj b/base/Applications/Runtime/Full/App.Corlib.csproj new file mode 100644 index 0000000..0417e09 --- /dev/null +++ b/base/Applications/Runtime/Full/App.Corlib.csproj @@ -0,0 +1,362 @@ + + + + + + + true + true + true + false + SINGULARITY_PROCESS;ENDPOINT_STRUCT + $(DefineConstants);PTR_SIZE_64 + 169,649 + Corlib + Library + none + $(SINGULARITY_ROOT)\Kernel + ..\..\..\Libraries + C# + true + {F4C22616-B154-496C-A4CF-C49401E52C87} + + + + + + $(DefineConstants);PAGING + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(COLLECTOR_APP) + true + + + + + diff --git a/base/Applications/Runtime/Full/AppRuntime.proj b/base/Applications/Runtime/Full/AppRuntime.proj new file mode 100644 index 0000000..f966b4d --- /dev/null +++ b/base/Applications/Runtime/Full/AppRuntime.proj @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/Runtime/Full/Corlib.Contracts.csproj b/base/Applications/Runtime/Full/Corlib.Contracts.csproj new file mode 100644 index 0000000..9ea25bc --- /dev/null +++ b/base/Applications/Runtime/Full/Corlib.Contracts.csproj @@ -0,0 +1,60 @@ + + + + + + + + + + Corlib.Contracts + Library + true + ENDPOINT_STRUCT;SINGULARITY;PTR_SIZE_32;_NEW_CLASSLOADER;NODEFAULTLIB;NOOWNERSHIPCHECK + $(APPRUNTIMEDIR)\Corlib.dll + false + true + $(APPRUNTIMEDIR) + true + $(SINGULARITY_ROOT)\Kernel\SpecSharp.Contracts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/Runtime/Full/Corlib.Native.proj b/base/Applications/Runtime/Full/Corlib.Native.proj new file mode 100644 index 0000000..5b34ee9 --- /dev/null +++ b/base/Applications/Runtime/Full/Corlib.Native.proj @@ -0,0 +1,500 @@ + + + + + + + + + + $(APPRUNTIMENATIVEDIR) + $(APPRUNTIMENATIVEDIR)\Corlib.obj + $(OBJROOT)\$(OBJRELRUNTIMEDIR) + $(CFLAGS) /I"$(BINDIR)" /DSINGULARITY_PROCESS=1 + $(AFLAGS) /DSINGULARITY_PROCESS + $(APPRUNTIMEDIR)\Corlib.dll + $(SINGULARITY_ROOT)\Kernel + $(APPRUNTIMENATIVEDIR)\native.$(Runtime).lib + $(COLLECTOR_APP) + + + + $(CFLAGS) /Gr /DISA_X86=1 /DPTR_SIZE_32=1 + $(ACFLAGS) /DISA_X86=1 /DPTR_SIZE_32=1 + + + + $(CFLAGS) /Gr /DISA_X64=1 /DPTR_SIZE_64=1 + $(ACFLAGS) /DISA_X64=1 /DPTR_SIZE_64=1 + + + + $(CFLAGS) /QRarch5 /QRimplicit-import- /DISA_ARM=1 /DPTR_SIZE_32=1 + $(ACFLAGS) /DISA_ARM=1 /DPTR_SIZE_32=1 + + + + + + + + $(BARTOK_FLAGS) /Singularity + $(BARTOK_FLAGS) /UnnameTracedPtrs=true + $(BARTOK_FLAGS) /OmitFramePointer=false + $(BARTOK_FLAGS) /GCInlineArrayAllocations=false + $(BARTOK_FLAGS) /GCInlineFixedAllocations=false + $(BARTOK_FLAGS) /GCIntrinsicFixedAllocations=false + $(BARTOK_FLAGS) /GCInlineWriteBarrier=false + $(BARTOK_FLAGS) /GenCoffLineNumber=false + $(BARTOK_FLAGS) /WholeProgram=true + $(BARTOK_FLAGS) /Warnings=true + $(BARTOK_FLAGS) /DisablePInvoke=true + $(BARTOK_FLAGS) $(BARTOK_COLLECTOR) + + + $(CORLIB_BARTOK_FLAGS) + $(CORLIB_BARTOK_FLAGS) /CompileOnly=true + $(CORLIB_BARTOK_FLAGS) /Warnings=true + $(CORLIB_BARTOK_FLAGS) $(BARTOK_COLLECTOR) + $(CORLIB_BARTOK_FLAGS) /r:"$(LIBSDIR)\Singularity.V1.App.dll" + $(CORLIB_BARTOK_FLAGS) /r:"$(APPILLSDIR)\Console.App.ill" + $(CORLIB_BARTOK_FLAGS) /out: "$(APPRUNTIMENATIVEDIR)\CorLib.obj" + $(CORLIB_BARTOK_FLAGS) /outdir: "$(APPRUNTIMENATIVEDIR)" + $(CORLIB_BARTOK_FLAGS) "$(APPRUNTIMEDIR)\Corlib.dll" + + + /verbosity:silence + $(TESTAPP_BARTOK_FLAGS) /GenAsmHeader="$(APPRUNTIMENATIVEDIR)\halclass.inc" + $(TESTAPP_BARTOK_FLAGS) /GenCppHeader="$(APPRUNTIMENATIVEDIR)\halclass.h" + $(TESTAPP_BARTOK_FLAGS) /IrTreeShake=false + $(TESTAPP_BARTOK_FLAGS) /IrTreeShakeLate=false + $(TESTAPP_BARTOK_FLAGS) /out:"$(APPRUNTIMENATIVEDIR)\testapp.obj" + $(TESTAPP_BARTOK_FLAGS) /outdir:"$(APPRUNTIMENATIVEDIR)" + + + + + + + + + + + + + $(BARTOK_FLAGS) /IrSimpleInliner=false + $(BARTOK_FLAGS) /CoalesceExceptionThrow=false + + + + + $(BARTOK_FLAGS) /LinkedStacks=true + $(BARTOK_FLAGS) /StackOverflowChecks=true + $(BARTOK_FLAGS) /SymbolicDebug=true + $(AFLAGS) /I"$(SINGULARITY_ROOT)\kernel\native\ix" + $(AFLAGS) /I"$(SINGULARITY_ROOT)\kernel\native\ix86" + + + + $(BARTOK_FLAGS) /PhoenixMirOpts=false + $(BARTOK_FLAGS) /TargetArch=ARM + $(BARTOK_FLAGS) /EnableIrExposeAllocationCall=true + $(AFLAGS) /I"$(SINGULARITY_ROOT)\kernel\native\arm" + + + + $(BARTOK_FLAGS) /x64 + $(BARTOK_FLAGS) /LinkedStacks=true + $(BARTOK_FLAGS) /StackOverflowChecks=true + $(AFLAGS) /I"$(SINGULARITY_ROOT)\kernel\native\ix" + $(AFLAGS) /I"$(SINGULARITY_ROOT)\kernel\native\ix64" + + + + + + Kernel\Native\ + Kernel\Native\ + Kernel\Native\ + Kernel\Native\ + Kernel\Native\ + Kernel\Native\ + Kernel\Native\ + Kernel\Native\ + Kernel\Native\ + Kernel\Native\ + Kernel\Native\ + Kernel\Native\ + Kernel\Native\ + Kernel\Native\ + Kernel\Native\ix64\ + Kernel\Native\ix86\ + Kernel\Singularity\Isal\ + + Bartok\runtime\shared\native\arch\$(BARTOK_MACHINE)\ + Bartok\runtime\singularity\native\arch\$(BARTOK_MACHINE)\ + Bartok\runtime\singularity\native\arch\$(BARTOK_MACHINE)\ + + + + + Kernel\Native\ix\ + Kernel\Native\ix\ + Kernel\Native\ix\ + Kernel\Singularity\Isal\ix\ + + + + Kernel\Native\ix86\ + Kernel\Native\ix86\ + Kernel\Native\ix86\ + Kernel\Native\ix86\ + Kernel\Native\ix86\ + Kernel\Native\ix86\ + Kernel\Native\ix86\ + Kernel\Native\ix86\ + Kernel\Native\ix86\ + Kernel\Native\ix86\ + Kernel\Native\ix86\ + Kernel\Native\ix86\ + + + + Kernel\Native\ix64\ + Kernel\Native\ix64\ + Kernel\Native\ix64\ + Kernel\Native\ix64\ + Kernel\Native\ix64\ + + + + Native\arm\ + Kernel\Native\arm\ + Kernel\Native\arm\ + Kernel\Native\arm\ + Kernel\Native\arm\ + Kernel\Singularity\Isal\Arm\ + Kernel\Singularity\Isal\Arm\ + + + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + Kernel\Native\Arm\Crt\ + + + + Kernel\Native\arm\ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/Runtime/Corlibsg.csproj b/base/Applications/Runtime/Full/Corlibsg.csproj similarity index 83% rename from base/Applications/Runtime/Corlibsg.csproj rename to base/Applications/Runtime/Full/Corlibsg.csproj index e8a90b5..33b0c79 100644 --- a/base/Applications/Runtime/Corlibsg.csproj +++ b/base/Applications/Runtime/Full/Corlibsg.csproj @@ -1,15 +1,13 @@ - + Corlibsg @@ -19,8 +17,7 @@ - - + @@ -31,7 +28,7 @@ - + true diff --git a/base/Applications/Runtime/Full/Microsoft.SingSharp.Runtime.App.csproj b/base/Applications/Runtime/Full/Microsoft.SingSharp.Runtime.App.csproj new file mode 100644 index 0000000..efa0adf --- /dev/null +++ b/base/Applications/Runtime/Full/Microsoft.SingSharp.Runtime.App.csproj @@ -0,0 +1,74 @@ + + + + + + + + Microsoft.SingSharp.Runtime + Library + true + NOOWNERSHIPCHECK;NODEFAULTLIB + true + $(SINGULARITY_ROOT)\Kernel\SingSharp.Runtime + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/Runtime/Full/Native/arm/liesapp.asm b/base/Applications/Runtime/Full/Native/arm/liesapp.asm new file mode 100644 index 0000000..41a1233 --- /dev/null +++ b/base/Applications/Runtime/Full/Native/arm/liesapp.asm @@ -0,0 +1,66 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity ARM Bootstrap +;;; +;;; + + IMPORT |?brtmain@@3P6AHPAUClassVector_Class_System_String@@@ZA| + +|defining ?g_CallMain@Class_Microsoft_Singularity_AppRuntime@@SAHPAUClassVector_Class_System_String@@@Z| EQU 1 + + include hal.inc + + MACRO + BREAKPOINT + ;; bkpt 0xffff + swi 0xffff03 + MEND + + + TEXTAREA + +;;; +;;; "void __cdecl Draw(unsigned char)" +;;; + EXPORT |?Draw@@YAXE@Z| +|?Draw@@YAXE@Z| PROC + mov pc, lr + ENDP + +;;; +;;; "public: static int Class_Microsoft_Singularity_AppRuntime::g_CallMain(struct ClassVector_Class_System_String *)" +;;; + LEAF_ENTRY ?g_CallMain@Class_Microsoft_Singularity_AppRuntime@@SAHPAUClassVector_Class_System_String@@@Z + + ldr r1, brtmain + ldr r1, [r1] + mov pc, r1 + +brtmain DCD |?brtmain@@3P6AHPAUClassVector_Class_System_String@@@ZA| + NESTED_END + +;;; +;;; +;;; + LEAF_ENTRY fmod + BREAKPOINT + bx lr + LEAF_END + +;;; +;;; +;;; + LEAF_ENTRY fmodf + BREAKPOINT + bx lr + LEAF_END + +;;; +;;; +;;; + LEAF_ENTRY ?Halt@@YAXXZ + DCD 0xe320f003 ;; WFI Note: ARMv7 Specific. + bx lr + LEAF_END + + END diff --git a/base/Applications/Runtime/Full/Singularity.V1.App.csproj b/base/Applications/Runtime/Full/Singularity.V1.App.csproj new file mode 100644 index 0000000..e1c049d --- /dev/null +++ b/base/Applications/Runtime/Full/Singularity.V1.App.csproj @@ -0,0 +1,67 @@ + + + + + + + + Singularity.V1.App + Library + 2 + true + true + true + C# + $(LIBSDIR) + $(SINGULARITY_ROOT)\Libraries\Singularity.V1 + + + + $(DefineConstants); + $(DefineConstants)CE_PC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/Runtime/Full/Singularity/AppRuntime.cs b/base/Applications/Runtime/Full/Singularity/AppRuntime.cs new file mode 100644 index 0000000..fc6a662 --- /dev/null +++ b/base/Applications/Runtime/Full/Singularity/AppRuntime.cs @@ -0,0 +1,303 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// + +using System; +using System.Collections; +using System.Reflection; +using System.Threading; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using Microsoft.Bartok.Runtime; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.V1.Services; + +using Microsoft.Singularity.Eventing; + +[assembly: AssemblyTitle("Microsoft.Singularity")] +[assembly: AssemblyProduct("Microsoft Research Singularity Runtime")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] + +namespace Microsoft.Singularity +{ + + [NoCCtor] + [CLSCompliant(false)] + [AccessedByRuntime("referenced from hal.cpp")] + public class AppRuntime + { + // Note: This function is called by Hal.cpp. + [AccessedByRuntime("referenced from hal.cpp")] + public static unsafe int AppStart(Type userClass) + { + System.GCs.Transitions.ThreadStart(); + + int result = 0; + + string arg0 = "(unknown)"; + + try { + Tracing.Log(Tracing.Audit, "Runtime.Main()"); + + // Initialize the primitive runtime, which calls the + // class constructor for Runtime(). + VTable.Initialize((RuntimeType)typeof(AppRuntime)); + //VTable.ParseArgs(args); + + Tracing.Log(Tracing.Audit, "Enabling GC Heap"); + GC.EnableHeap(); + Controller.InitializeSystem(); + GCProfilerLogger.StartProfiling(); + + InitializeConsole(); + + SetDebuggerPresence(DebugService.IsDebuggerPresent()); + + int argCount = 0; + int argMaxLen = 0; + for (;; argCount++) { + int len = ProcessService.GetStartupArg(argCount, null, 0); + if (len == 0) { + break; + } + if (argMaxLen < len) { + argMaxLen = len; + } + } + char[] argArray = new char [argMaxLen]; + string[] args = new string[argCount]; + for (int arg = 0; arg < argCount; arg++) { + fixed (char *argptr = &argArray[0]) { + int len = ProcessService.GetStartupArg(arg, + argptr, + argArray.Length); + args[arg] = String.StringCTOR(argptr, 0, len); + if (arg == 0) + arg0 = args[arg]; + } + } + +#if DEBUG || true + // Record the first argument passed to this program, under the assumption that + // this is the application name. We use this string in DebugStub.WriteLine. + // Also, if the name has an extension, such as ".x86", chop it off. + // Also chop off any path prefix, such as "/init/". + appName = arg0; + int index = appName.LastIndexOf('.'); + if (index != -1) + appName = appName.Substring(0, index); + index = appName.LastIndexOf('/'); + if (index != -1) + appName = appName.Substring(index + 1); + + // The default DebugName value for the main thread is "main"; + // apps can override this by setting Thread.CurrentThread.DebugName. + Thread mainThread = Thread.CurrentThread; + if (mainThread != null) { + mainThread.DebugName = "main"; + } +#endif + + if (userClass != null) { + VTable.initType((RuntimeType)userClass); + } + + result = CallMain(args); + if (!MainReturnsInt()) result = 0; + + Tracing.Log(Tracing.Audit, "Main thread exited [{0}]", + (UIntPtr)unchecked((uint)result)); + } + catch (Exception e) { + Tracing.Log(Tracing.Fatal, "Failed with exception {0}.{1}", + e.GetType().Namespace, e.GetType().Name); + Tracing.Log(Tracing.Trace, "Exception message was {0}", + e.ToString()); + TopLevelException(e); + result = -1; + } + + Thread.RemoveThread(Thread.CurrentThread.threadIndex); + Thread.JoinAll(); + + try { + FinalizeConsole(); + } + catch (Exception e) { + Tracing.Log(Tracing.Fatal, "An exception occurred while shutting down the console: {0}", + e.ToString()); + } + + Controller.Finalize(); + Tracing.Log(Tracing.Audit, "Runtime shutdown started."); + VTable.Shutdown(result); + Tracing.Log(Tracing.Audit, "Runtime exiting [{0}]", + (UIntPtr)unchecked((uint)result)); + return result; + } + + + /// + /// This method runs when the "main" thread terminates by throwing an exception. + /// It is a separate method, rather than simply code within the AppStart method, + /// in order to make life easier during debugging. + /// + /// + private static void TopLevelException(Exception/*!*/ ex) + { + DebugStub.WriteLine("AppRuntime: Main app thread terminated with exception:"); + for (Exception focus = ex; focus != null; focus = focus.InnerException) { + DebugStub.WriteLine("{0}: {1}", __arglist(focus.GetType().FullName, focus.Message)); + } + } + +#if DEBUG || true + /// + /// Returns the name of the application, as determined by the first parameter + /// passed in the argv block. This property is only present on DEBUG builds + /// (Debug and Prototype). It is provided for DebugStub.WriteLine, to provide + /// useful debugging spew. + /// + public static string AppName + { + [NoHeapAllocation] + get + { + return appName; + } + } + + private static string appName; +#endif + + [AccessedByRuntime("output to header : defined in hal.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(256)] + private static extern int CallMain(String[] args); + + [AccessedByRuntime("output to header: defined in hal.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(256)] + private static extern bool MainReturnsInt(); + + [AccessedByRuntime("output to header: defined in hal.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(256)] + private static extern void SetDebuggerPresence(bool debuggerPresent); + + internal static void Shutdown(int exitCode) + { + // + // Gracefully close down the process. + // + Tracing.Log(Tracing.Audit, "Runtime.Shutdown({0})", + (UIntPtr)unchecked((uint)exitCode)); + + DebugStub.WriteLine("Runtime.Shutdown({0})", __arglist(exitCode)); + + VTable.Shutdown(exitCode); + Tracing.Log(Tracing.Audit, "Runtime.Shutdown({0}) terminating", + (UIntPtr)unchecked((uint)exitCode)); + ProcessService.Stop(exitCode); + } + + internal static void Stop(int exitCode) + { + // + // Halt the process immediately. + // + Tracing.Log(Tracing.Audit, "Runtime.Stop({0})", + (UIntPtr)unchecked((uint)exitCode)); + + DebugStub.WriteLine("Runtime.Stop({0})", __arglist(exitCode)); + + ProcessService.Stop(exitCode); + } + + ////////////////////////////////////////////////////////////////////// + // + [NoHeapAllocation] + public static UIntPtr AddressOf(Object o) + { + return Magic.addressOf(o); + } + + [NoHeapAllocation] + public static UIntPtr SizeOf(Object o) + { + return System.GCs.ObjectLayout.Sizeof(o); + } + + public static void InitType(Type ty) + { + VTable.initType((RuntimeType) ty); + } + + public static void DumpPageTable() + { + System.GCs.PageTable.Dump("PageTable"); + } + + ////////////////////////////////////////////////////////////////////// + // + public static bool EnableGCVerify + { + get { + return VTable.enableGCVerify; + } + set { + VTable.enableGCVerify = value; + } + } + + public static bool EnableGCAccounting + { + get { + return VTable.enableGCAccounting; + } + set { + VTable.enableGCAccounting = value; + if (value == true) { + System.GCs.MemoryAccounting.Initialize(GC.gcType); + } + } + } + + public static uint GCPerfCounter + { + get { + return System.GC.perfCounter; + } + set { + System.GC.perfCounter = value; + } + } + + /// + /// This stub method is provided for so that System.Console.dll can override it for those + /// apps that want a console. This is necessary, in order to initialize and shutdown the + /// console at the right time. + /// + private static void InitializeConsole() + { + } + + /// + /// This stub method is provided for so that System.Console.dll can override it for those + /// apps that want a console. This is necessary, in order to initialize and shutdown the + /// console at the right time. + /// + private static void FinalizeConsole() + { + } + } +} diff --git a/base/Applications/Runtime/Full/Singularity/CorlibSgAssemblyInfo.sg b/base/Applications/Runtime/Full/Singularity/CorlibSgAssemblyInfo.sg new file mode 100644 index 0000000..f707cbf --- /dev/null +++ b/base/Applications/Runtime/Full/Singularity/CorlibSgAssemblyInfo.sg @@ -0,0 +1,8 @@ +using System.Reflection; + +[assembly: AssemblyTitle("Corlibsg")] +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Applications/Runtime/Full/Singularity/DebugStub.cs b/base/Applications/Runtime/Full/Singularity/DebugStub.cs new file mode 100644 index 0000000..2e282d8 --- /dev/null +++ b/base/Applications/Runtime/Full/Singularity/DebugStub.cs @@ -0,0 +1,364 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// 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.Singularity.V1.Services; + +namespace Microsoft.Singularity +{ + [NoCCtor] + [CLSCompliant(false)] + [AccessedByRuntime("referenced in halkd.cpp")] + public class DebugStub + { + /////////////////////////////////////////////////////// Print Methods. + // + [NoHeapAllocation] + public static void Print(byte value) + { + Print("{0:x2}", __arglist(value)); + } + + [NoHeapAllocation] + public static void Print(ushort value) + { + Print("{0:x4}", __arglist(value)); + } + + [NoHeapAllocation] + public static void Print(uint value) + { + Print("{0:x8}", __arglist(value)); + } + + [NoHeapAllocation] + public static void Print(ulong value) + { + Print("{0:x}", __arglist(value)); + } + + [NoHeapAllocation] + public static void Print(UIntPtr value) + { + Print("{0:x8}", __arglist(value)); + } + + [NoHeapAllocation] + public static void Print(sbyte value) + { + Print("{0}", __arglist(value)); + } + + [NoHeapAllocation] + public static void Print(short value) + { + Print("{0}", __arglist(value)); + } + + [NoHeapAllocation] + public static void Print(int value) + { + Print("{0}", __arglist(value)); + } + + [NoHeapAllocation] + public static void Print(long value) + { + Print("{0}", __arglist(value)); + } + + ////////////////////////////////////////////////////////////////////// + // + + [NoHeapAllocation] + public static void Print(String value) + { + if (value != null) { + Print(value, new ArgIterator()); + } + } + + [NoHeapAllocation] + public static void Print(String format, __arglist) + { + Print(format, new ArgIterator(__arglist)); + } + + [NoHeapAllocation] + public static unsafe void Print(String format, ArgIterator args) + { + WriteLine(format, args); + } + + ////////////////////////////////////////////////////////////////////// + // + + [NoHeapAllocation] + public static void Write(String value) + { + if (value != null) { + WriteLine(value, new ArgIterator()); + } + } + + [NoHeapAllocation] + public static void Write(String format, __arglist) + { + Write(format, new ArgIterator(__arglist)); + } + + [NoHeapAllocation] + public static unsafe void Write(String format, ArgIterator args) + { + WriteLine(format, args); + } + + ////////////////////////////////////////////////////////////////////// + // + [NoHeapAllocation] + public static void WriteLine() + { + WriteLine("", new ArgIterator()); + } + + [NoHeapAllocation] + public static void WriteLine(String value) + { + if (value != null) { + WriteLine(value, new ArgIterator()); + } + } + + [NoHeapAllocation] + public static void WriteLine(String format, __arglist) + { + WriteLine(format, new ArgIterator(__arglist)); + } + + [NoHeapAllocation] + public static unsafe void WriteLine(String format, ArgIterator args) + { + char *buffer; + int length; + int used = 0; + + DebugService.PrintBegin(out buffer, out length); + try { + if (buffer != null) { + AddLineHeader(buffer, ref used, length); + int format_used = String.LimitedFormatTo(format, args, buffer+used, length-used); + used += format_used; + + // If the line had \r or \n at the start, replace with spaces. + for (int i = 0; i < used; i++) { + char c = buffer[i]; + if (c == '\r' || c == '\n') { + buffer[i] = ' '; + } + else { + break; + } + } + + // Remove whitespace (including \r\n) from the end. + while (used > 0) { + char c = buffer[used - 1]; + if (c == ' ' || c == '\r' || c == '\n' || c == '\t') { + used--; + } + else { + break; + } + } + + // Add our own \r\n + if (used < length) { + buffer[used++] = '\n'; + } + } + } + finally { + DebugService.PrintComplete(buffer, used); + } + } + + /// + /// This method adds prefix to each line sent to the debugger, containing the app name, + /// process id, thread id, and thread name. The thread name can be changed by setting + /// the DebugName property of a thread. + /// + [NoHeapAllocation] + private static unsafe void AddLineHeader(char* buffer, ref int length, int maxLength) + { + uint processId = ProcessService.GetCurrentProcessId(); + uint threadId = unchecked((uint)Thread.GetCurrentThreadIndex()); + + string appName; + string threadName; +#if DEBUG + appName = AppRuntime.AppName; + if (appName == null) + appName = "?"; + + Thread currentThread = Thread.CurrentThread; + if (currentThread != null) { + threadName = currentThread.DebugName; + if (threadName == null) { + threadName = ""; + } + } + else { + threadName = ""; + } +#else + appName = "?"; + threadName = ""; +#endif + + InsertFormat(buffer, ref length, maxLength, "[{0,10} p{1,2} t{2,2} {3,6}] ", __arglist(appName, processId, threadId, threadName)); + } + + [NoHeapAllocation] + private static unsafe void InsertFormat(char* buffer, ref int length, int maxLength, String format, __arglist) + { + int count = String.LimitedFormatTo(format, new ArgIterator(__arglist), &buffer[length], maxLength - length); + length += count; + } + + ////////////////////////////////////////////////////// Assert Methods. + // + [NoHeapAllocation] + public static void NotImplemented() + { + failAssert("Not implemented."); + } + + [NoHeapAllocation] + public static void NotImplemented(String msg) + { + failAssert(/*"Not implemented: "+*/msg); + } + + [Conditional("DEBUG")] + [NoInline] + [NoHeapAllocation] + public static void NotReached() + { + failAssert("Unreachable code reached."); + } + + [Conditional("DEBUG")] + [NoInline] + [NoHeapAllocation] + public static void NotReached(String msg) + { + failAssert(/*"Unreachable code reached: "+*/msg); + } + + [Conditional("DEBUG")] + [NoInline] + [ManualRefCounts] + [NoHeapAllocation] + public static void Assert(bool expr) + { + if (!expr) { + failAssert(null); + } + } + + [Conditional("DEBUG")] + [NoInline] + [ManualRefCounts] + [NoHeapAllocation] + public static void Deny(bool expr) + { + if (expr) { + failAssert(null); + } + } + + [Conditional("DEBUG")] + [NoInline] + [ManualRefCounts] + [NoHeapAllocation] + public static void Assert(bool expr, String s) + { + if (!expr) { + failAssert(s); + } + } + + [Conditional("DEBUG")] + [NoInline] + [NoHeapAllocation] + public static void Deny(bool expr, String s) + { + if (expr) { + failAssert(s); + } + } + + [ManualRefCounts] + [NoHeapAllocation] + private static void failAssert(String s) + { + if (s != null) { + Print("Assertion failed: {0}", __arglist(s)); + } + else { + Print("Assertion failed."); + } + Break(); + } + + ////////////////////////////////////////////////////////////////////// + // + [NoHeapAllocation] + public static ulong ReadPerfCounter(uint which) + { + return DebugService.ReadPerfCounter(which); + } + + + [NoHeapAllocation] + public static bool WritePerfCounter(uint which, ulong value) + { + return DebugService.WritePerfCounter(which, value); + } + + [NoHeapAllocation] + public static bool AddToPerfCounter(uint which, ulong value) + { + return DebugService.AddToPerfCounter(which, value); + } + + /////////////////////////////////////////////////////// State Methods. + // +#if true + [AccessedByRuntime("output to header : defined in halkd.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(638)] + [NoHeapAllocation] + public static extern void Break(); +#else + [NoHeapAllocation] + public static void Break() + { + DebugService.Break(); + } +#endif + } +} diff --git a/base/Applications/Runtime/Full/Singularity/Directory/NameService.sg b/base/Applications/Runtime/Full/Singularity/Directory/NameService.sg new file mode 100644 index 0000000..760f53f --- /dev/null +++ b/base/Applications/Runtime/Full/Singularity/Directory/NameService.sg @@ -0,0 +1,28 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// +// This file provides an implementation of Naming.DirectoryService.NewClientEndpoint +// that calls through the ABI to retrieve the name-service channel endpoint. +// This is for use by applications; kernel components can call the "real" +// DirectoryService class directly. +// + +using Microsoft.Singularity; +using Microsoft.Singularity.Naming; +using Microsoft.Singularity.V1.Services; + +namespace Microsoft.Singularity.Directory +{ + public class DirectoryService + { + public static unsafe DirectoryServiceContract.Imp!:Ready NewClientEndpoint() + { + return (DirectoryServiceContract.Imp!)ProcessService.GetNamespaceEndpoint(); + } + } +} diff --git a/base/Applications/Runtime/Singularity/Io/IoIrq.cs b/base/Applications/Runtime/Full/Singularity/Io/IoIrq.cs similarity index 82% rename from base/Applications/Runtime/Singularity/Io/IoIrq.cs rename to base/Applications/Runtime/Full/Singularity/Io/IoIrq.cs index 326c90e..147e979 100644 --- a/base/Applications/Runtime/Singularity/Io/IoIrq.cs +++ b/base/Applications/Runtime/Full/Singularity/Io/IoIrq.cs @@ -4,7 +4,7 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: IoIrq.cs - Process Code +// Note: Process Code // using System; @@ -27,8 +27,7 @@ namespace Microsoft.Singularity.Io ~IoIrq() { - if (handle.id != UIntPtr.Zero) - { + if (handle.id != UIntPtr.Zero) { ReleaseInterrupt(); } } @@ -55,8 +54,7 @@ namespace Microsoft.Singularity.Io public bool WaitForInterrupt() { - if (handle.id != UIntPtr.Zero) - { + if (handle.id != UIntPtr.Zero) { return InterruptHandle.Wait(handle); } return false; @@ -64,16 +62,14 @@ namespace Microsoft.Singularity.Io public void Pulse() { - if (handle.id != UIntPtr.Zero) - { + if (handle.id != UIntPtr.Zero) { InterruptHandle.Pulse(handle); } } public bool AckInterrupt() { - if (handle.id != UIntPtr.Zero) - { + if (handle.id != UIntPtr.Zero) { return InterruptHandle.Ack(handle); } return false; diff --git a/base/Applications/Runtime/Full/Singularity/Io/PciPort.cs b/base/Applications/Runtime/Full/Singularity/Io/PciPort.cs new file mode 100644 index 0000000..5a6b075 --- /dev/null +++ b/base/Applications/Runtime/Full/Singularity/Io/PciPort.cs @@ -0,0 +1,103 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; + +using Microsoft.Singularity.V1.Services; + +namespace Microsoft.Singularity.Io +{ + [CLSCompliant(false)] + public sealed class PciPort + { + private PciPortHandle handle; + + internal PciPort(PciPortHandle pciPortHandle) + { + this.handle = pciPortHandle; + } + + ~PciPort() + { + } + + public void Write8(int offset, byte value) + { + if (!PciPortHandle.Write8(handle, offset, value)) { + DebugStub.Break(); + } + } + + public void Write16(int offset, ushort value) + { + if (!PciPortHandle.Write16(handle, offset, value)) { + DebugStub.Break(); + } + } + + public void Write32(int offset, uint value) + { + if (!PciPortHandle.Write32(handle, offset, value)) { + DebugStub.Break(); + } + } + + public byte Read8(int offset) + { + return Read8Impl(offset); + } + + public ushort Read16(int offset) + { + return Read16Impl(offset); + } + + public uint Read32(int offset) + { + return Read32Impl(offset); + } + + private unsafe byte Read8Impl(int offset) + { + byte v; + + if (PciPortHandle.Read8Impl(handle, offset, &v)) { + return v; + } + else { + DebugStub.Break(); + return 0; + } + } + + private unsafe ushort Read16Impl(int offset) + { + ushort v; + + if (PciPortHandle.Read16Impl(handle, offset, &v)) { + return v; + } + else { + DebugStub.Break(); + return 0; + } + } + + private unsafe uint Read32Impl(int offset) + { + uint v; + + if (PciPortHandle.Read32Impl(handle, offset, &v)) { + return v; + } + else { + DebugStub.Break(); + return 0; + } + } + } +} diff --git a/base/Applications/Runtime/Singularity/Io/PipeLookAhead.sg b/base/Applications/Runtime/Full/Singularity/Io/PipeLookAhead.sg similarity index 84% rename from base/Applications/Runtime/Singularity/Io/PipeLookAhead.sg rename to base/Applications/Runtime/Full/Singularity/Io/PipeLookAhead.sg index e73c2da..54557fb 100644 --- a/base/Applications/Runtime/Singularity/Io/PipeLookAhead.sg +++ b/base/Applications/Runtime/Full/Singularity/Io/PipeLookAhead.sg @@ -4,21 +4,21 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: PipeLookAhead.sg -// // Note: Implements a selectable abstraction that looks ahead on a pipe channels // for control-C and exports separate logical receivable messages for -// non-control-C characters and control-C, effectively allowing one to -// consume Ctrl-C's out of order. +// non-control-C characters and control-C, effectively allowing one to +// consume Ctrl-C's out of order. // + using System; using Microsoft.SingSharp; using Microsoft.Singularity; using Microsoft.Singularity.Channels; using Microsoft.Singularity.V1.Threads; -using Tty = Microsoft.Singularity.Io.Tty2006; +using Tty = Microsoft.Singularity.Io.Tty; -namespace Microsoft.Singularity.Io { +namespace Microsoft.Singularity.Io +{ public sealed class PipeLookAhead : ITracked, ISelectable { @@ -60,7 +60,7 @@ namespace Microsoft.Singularity.Io { } } - + public CircularBuffer(int lookAhead) { this.buffer = new[ExHeap] char[lookAhead+256]; this.nextReadIndex = 0; @@ -82,7 +82,7 @@ namespace Microsoft.Singularity.Io { } public void Add(char[]! in ExHeap buffer, int index, int count) { EnsureCapacity(count); - for (int i=0; ithread; + } + } + + ////////////////////////////////////////////////////////////////////// + // + // These methods are currently marked external because they are used + // by device drivers. We need a tool to verify that device drivers + // are in fact using them correctly! + // + + [NoHeapAllocation] + [AccessedByRuntime("referenced from c++")] + public static bool DisableLocalPreemption() + { + // BUGBUG: this is a placeholder until the new scheduler work + // gets implemented, and we'll be able to control the local scheduling + + // The current usage in SIPs seem only to rely on disabling preemption for + // performance reasons, not for corectness. + // return PlatformService.DisableInterrupts(); + + return false; + } + + [NoHeapAllocation] + [AccessedByRuntime("referenced from c++")] + public static void RestoreLocalPreemption(bool enabled) + { + // Same as above, use the interrupts disabled temporary, until the scheduller + // changes with a SIP hierarcy get integrated. + + // The current usage in SIPs seem only to rely on disabling preemption for + // performance reasons, not for corectness. + // PlatformService.RestoreInterrupts(enabled); + } + + } +} diff --git a/base/Kernel/System.Compiler.Runtime/System.Compiler.Runtime.App.csproj b/base/Applications/Runtime/Full/System.Compiler.Runtime.App.csproj similarity index 81% rename from base/Kernel/System.Compiler.Runtime/System.Compiler.Runtime.App.csproj rename to base/Applications/Runtime/Full/System.Compiler.Runtime.App.csproj index 3e57277..edf704a 100644 --- a/base/Kernel/System.Compiler.Runtime/System.Compiler.Runtime.App.csproj +++ b/base/Applications/Runtime/Full/System.Compiler.Runtime.App.csproj @@ -5,8 +5,6 @@ # # Copyright (c) Microsoft Corporation. All rights reserved. # -# File: Kernel\System.Compiler.Runtime\System.Compiler.Runtime.App.csproj -# # Note: This project builds the copy of System.Runtime.Compiler.dll # that applications use (executables outside of the kernel). # @@ -25,12 +23,12 @@ - - + + - + diff --git a/base/Kernel/System/ApplicationException.cs b/base/Applications/Runtime/Full/System/ApplicationException.cs similarity index 81% rename from base/Kernel/System/ApplicationException.cs rename to base/Applications/Runtime/Full/System/ApplicationException.cs index 3f01af0..696d2f0 100644 --- a/base/Kernel/System/ApplicationException.cs +++ b/base/Applications/Runtime/Full/System/ApplicationException.cs @@ -3,19 +3,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================================= -** -** Class: ApplicationException -** -** -** Purpose: The base class for all "less serious" exceptions that must be -** declared or caught. -** -** Date: March 11, 1998 -** -=============================================================================*/ +//============================================================================= +// +// Class: ApplicationException +// +// Purpose: The base class for all "less serious" exceptions that must be +// declared or caught. +// +//============================================================================= -namespace System { +namespace System +{ // The ApplicationException is the base class for nonfatal, // application errors that occur. These exceptions are generated diff --git a/base/Applications/Runtime/Full/System/ArgIterator.cs b/base/Applications/Runtime/Full/System/ArgIterator.cs new file mode 100644 index 0000000..4dac82c --- /dev/null +++ b/base/Applications/Runtime/Full/System/ArgIterator.cs @@ -0,0 +1,161 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +namespace System +{ + + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + using Microsoft.Bartok.Runtime; + + [NoCCtor] + [StructLayout(LayoutKind.Auto)] + public unsafe struct ArgIterator + { + private IntPtr * nextArg; + private int remainingArgs; + + [NoHeapAllocation] + public ArgIterator(RuntimeArgumentHandle arglist) + { + IntPtr* p = (IntPtr *) arglist.Pointer; + this.remainingArgs = (int) *p; + this.nextArg = p + 1; + } + + [NoHeapAllocation] + public int GetRemainingCount() { + return remainingArgs; + } + + public int Length { + [NoHeapAllocation] + get { return remainingArgs; } + } + + [NoHeapAllocation] + internal RuntimeType GetArg(int arg, out IntPtr value) + { + if (arg < 0 || arg >= remainingArgs) { + value = IntPtr.Zero; + return null; + } +#if ISA_IX86 + value = nextArg[arg * 2]; + return Magic.toRuntimeType(Magic.fromAddress((UIntPtr)nextArg[arg * 2 + 1])); +#elif ISA_IX64 + IntPtr * ptr = (IntPtr *) nextArg[arg]; + value = *ptr; + return Magic.toRuntimeType(Magic.fromAddress((UIntPtr)(*(ptr+1)))); +#elif ISA_ARM + // TODO: Correct implementation + value = nextArg[arg * 2]; + return Magic.toRuntimeType(Magic.fromAddress((UIntPtr)nextArg[arg * 2 + 1])); +#else +#error Undefined architecture +#endif + } + + [NoHeapAllocation] + public Object GetObjectArg(int arg) + { + IntPtr pvalue; + RuntimeType rt = GetArg(arg, out pvalue); + + return (rt != null && + pvalue != IntPtr.Zero && + rt.classVtable.structuralView == StructuralType.Reference) + ? Magic.fromAddress(*(UIntPtr *)pvalue) + : null; + } + + [NoHeapAllocation] + internal RuntimeType PopNextArg(out IntPtr value) + { + if (remainingArgs == 0) { + value = IntPtr.Zero; + return null; + } + else { + RuntimeType type; +#if ISA_IX86 + value = *nextArg++; + type = Magic.toRuntimeType(Magic.fromAddress((UIntPtr)(*nextArg++))); +#elif ISA_IX64 + IntPtr * ptr = (IntPtr *) *nextArg++; + value = *ptr; + type = Magic.toRuntimeType(Magic.fromAddress((UIntPtr)(*(ptr+1)))); +#elif ISA_ARM + // TODO: Correct implementation + value = *nextArg++; + type = Magic.toRuntimeType(Magic.fromAddress((UIntPtr)(*nextArg++))); +#else +#error Undefined architecture +#endif + + remainingArgs--; + + return type; + } + } + + [CLSCompliant(false)] + public TypedReference GetNextArg() + { + if (remainingArgs == 0) { + throw new InvalidOperationException + ("GetNextArg: No more arguments"); + } + +#if ISA_IX86 + IntPtr value = *nextArg++; + RuntimeType type = Magic.toRuntimeType( + Magic.fromAddress((UIntPtr)(*nextArg++))); +#elif ISA_IX64 + IntPtr * ptr = (IntPtr *) *nextArg++; + IntPtr value = *ptr; + RuntimeType type = Magic.toRuntimeType( + Magic.fromAddress((UIntPtr)(*(ptr+1)))); +#elif ISA_ARM + // TODO: Correct implementation + IntPtr value = *nextArg++; + RuntimeType type = Magic.toRuntimeType( + Magic.fromAddress((UIntPtr)(*nextArg++))); +#else +#error Undefined architecture +#endif + remainingArgs--; + + return new TypedReference(value, type); + } + + [CLSCompliant(false)] + public RuntimeTypeHandle GetNextArgType() + { + if (remainingArgs == 0) { + throw new InvalidOperationException + ("GetNextArgType: No more arguments"); + } +#if ISA_IX86 + return new RuntimeTypeHandle(Magic.toRuntimeType( + Magic.fromAddress( + (UIntPtr)(nextArg[1])))); +#elif ISA_IX64 + IntPtr * ptr = (IntPtr *) *nextArg; + return new RuntimeTypeHandle(Magic.toRuntimeType( + Magic.fromAddress( + (UIntPtr)(*(ptr+1))))); +#elif ISA_ARM + // TODO: Correct implementation + return new RuntimeTypeHandle(Magic.toRuntimeType( + Magic.fromAddress( + (UIntPtr)(nextArg[1])))); +#else +#error Undefined architecture +#endif + } + } +} diff --git a/base/Kernel/System/ArgumentException.cs b/base/Applications/Runtime/Full/System/ArgumentException.cs similarity index 78% rename from base/Kernel/System/ArgumentException.cs rename to base/Applications/Runtime/Full/System/ArgumentException.cs index 22ae860..e04aba2 100644 --- a/base/Kernel/System/ArgumentException.cs +++ b/base/Applications/Runtime/Full/System/ArgumentException.cs @@ -3,18 +3,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================================= -** -** Class: ArgumentException -** -** -** Purpose: Exception class for invalid arguments to a method. -** -** Date: March 24, 1998 -** -=============================================================================*/ +//============================================================================= +// +// Class: ArgumentException +// +// Purpose: Exception class for invalid arguments to a method. +// +//============================================================================= -namespace System { +namespace System +{ using System; @@ -64,7 +62,7 @@ namespace System { { get { String s = base.Message; - if (! ((m_paramName == null) || + if (!((m_paramName == null) || (m_paramName.Length == 0)) ) return s + Environment.NewLine + String.Format("{0}", m_paramName); else @@ -72,16 +70,16 @@ namespace System { } } - /* - public String ToString() - { - String s = super.ToString(); - if (m_paramName != null) - return s + "Parameter name: "+m_paramName+"\tActual value: "+(m_actualValue==null ? "" : m_actualValue.ToString()); - else - return s; - } - */ + // + //public String ToString() + //{ + // String s = super.ToString(); + // if (m_paramName != null) + // return s + "Parameter name: "+m_paramName+"\tActual value: "+(m_actualValue==null ? "" : m_actualValue.ToString()); + // else + // return s; + //} + // //| public virtual String ParamName { diff --git a/base/Kernel/System/ArgumentNullException.cs b/base/Applications/Runtime/Full/System/ArgumentNullException.cs similarity index 82% rename from base/Kernel/System/ArgumentNullException.cs rename to base/Applications/Runtime/Full/System/ArgumentNullException.cs index dfdec41..ecbeaeb 100644 --- a/base/Kernel/System/ArgumentNullException.cs +++ b/base/Applications/Runtime/Full/System/ArgumentNullException.cs @@ -3,18 +3,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================================= -** -** Class: ArgumentNullException -** -** -** Purpose: Exception class for null arguments to a method. -** -** Date: April 28, 1999 -** -=============================================================================*/ +//============================================================================= +// +// Class: ArgumentNullException +// +// Purpose: Exception class for null arguments to a method. +// +//============================================================================= -namespace System { +namespace System +{ using System; diff --git a/base/Applications/Runtime/Full/System/ArgumentOutOfRangeException.cs b/base/Applications/Runtime/Full/System/ArgumentOutOfRangeException.cs new file mode 100644 index 0000000..a3db41f --- /dev/null +++ b/base/Applications/Runtime/Full/System/ArgumentOutOfRangeException.cs @@ -0,0 +1,88 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: ArgumentOutOfRangeException +// +// Purpose: Exception class for method arguments outside of the legal range. +// +//============================================================================= + +namespace System +{ + + using System; + using System.Runtime.CompilerServices; + + // The ArgumentOutOfRangeException is thrown when an argument + // is outside the legal range for that argument. This may often be caused + // by + // + //| + public partial class ArgumentOutOfRangeException : ArgumentException { + + private static String _rangeMessage; + private Object m_actualValue; + + private static String RangeMessage { + get { + if (_rangeMessage == null) + _rangeMessage = "Arg_ArgumentOutOfRangeException"; + return _rangeMessage; + } + } + + // Creates a new ArgumentOutOfRangeException with its message + // string set to a default message explaining an argument was out of range. + //| + public ArgumentOutOfRangeException() + : base(RangeMessage) { + } + + //| + public ArgumentOutOfRangeException(String paramName) + : base(RangeMessage, paramName) { + } + + //| + public ArgumentOutOfRangeException(String paramName, String message) + : base(message, paramName) { + } + + // We will not use this in the classlibs, but we'll provide it for + // anyone that's really interested so they don't have to stick a bunch + // of printf's in their code. + //| + public ArgumentOutOfRangeException(String paramName, Object actualValue, String message) + : base(message, paramName) { + m_actualValue = actualValue; + } + + //| + public override String Message + { + get { + String s = base.Message; + if (m_actualValue != null) { + String valueMessage = String.Format("ArgumentOutOfRange_ActualValue", m_actualValue.ToString()); + if (s == null) + return valueMessage; + return s + Environment.NewLine + valueMessage; + } + return s; + } + } + + // Gets the value of the argument that caused the exception. + // Note - we don't set this anywhere in the class libraries in + // version 1, but it might come in handy for other developers who + // want to avoid sticking printf's in their code. + //| + public virtual Object ActualValue { + get { return m_actualValue; } + } + } +} diff --git a/base/Applications/Runtime/Full/System/ArithmeticException.cs b/base/Applications/Runtime/Full/System/ArithmeticException.cs new file mode 100644 index 0000000..d4bf46c --- /dev/null +++ b/base/Applications/Runtime/Full/System/ArithmeticException.cs @@ -0,0 +1,48 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: ArithmeticException +// +// Purpose: Exception class for bad arithmetic conditions! +// +//============================================================================= + +namespace System +{ + + using System; + using System.Runtime.CompilerServices; + + // The ArithmeticException is thrown when overflow or underflow + // occurs. + // + //| + public partial class ArithmeticException : SystemException + { + // Creates a new ArithmeticException with its message string set to + // the empty string, its HRESULT set to COR_E_ARITHMETIC, + // and its ExceptionInfo reference set to null. + //| + public ArithmeticException() + : base("Arg_ArithmeticException") { + } + + // Creates a new ArithmeticException with its message string set to + // message, its HRESULT set to COR_E_ARITHMETIC, + // and its ExceptionInfo reference set to null. + // + //| + public ArithmeticException(String message) + : base(message) { + } + + //| + public ArithmeticException(String message, Exception innerException) + : base(message, innerException) { + } + } +} diff --git a/base/Applications/Runtime/Full/System/Array.cs b/base/Applications/Runtime/Full/System/Array.cs new file mode 100644 index 0000000..e841c47 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Array.cs @@ -0,0 +1,2759 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================ +// +// Class: Array +// +// Purpose: Base class which can be used to access any array +// +//=========================================================== +namespace System +{ + + using Microsoft.Bartok.Runtime; + using System; + using System.Collections; + using System.GCs; + using System.Diagnostics; + using System.Runtime.InteropServices; + using System.Runtime.CompilerServices; + using System.Threading; + + // REVIEW: to be moved/integrated elsewhere + // REVIEW: see System.Variant::ClassTypes + internal class TypeInfo { + internal static VTable[] arrayOfBox = { + null, // not array + null, // reference + null, // untraced pointer + null, // struct + ((RuntimeType)typeof(Boolean)).classVtable, + ((RuntimeType)typeof(Char)).classVtable, + ((RuntimeType)typeof(SByte)).classVtable, + ((RuntimeType)typeof(Int16)).classVtable, + ((RuntimeType)typeof(Int32)).classVtable, + ((RuntimeType)typeof(Int64)).classVtable, + ((RuntimeType)typeof(Byte)).classVtable, + ((RuntimeType)typeof(UInt16)).classVtable, + ((RuntimeType)typeof(UInt32)).classVtable, + ((RuntimeType)typeof(UInt64)).classVtable, + ((RuntimeType)typeof(Single)).classVtable, + ((RuntimeType)typeof(Double)).classVtable, + ((RuntimeType)typeof(IntPtr)).classVtable, + ((RuntimeType)typeof(UIntPtr)).classVtable + }; + } + + //| + [StructLayout(LayoutKind.Sequential)] + public abstract class Array : ICloneable, IList + { + // [spacer] means 4 byte insertion if elements are 8-aligned + + // Vector layout: + // length, [spacer], elem0, ... + + // Rect layout: + // rank, total_length, base0, length0, ..., basen, lengthn, elem0, ... + + // VTable vtable is inherited from Object + + internal int field1; + private int field2; + private int field3; + private int field4; + + /// + private Array() {} + + // Create instance will create an array + //| + public static Array CreateInstance(Type elementType, int length) + { + if (elementType == null) + throw new ArgumentNullException("elementType"); + RuntimeType t = elementType.UnderlyingSystemType as RuntimeType; + if (t == null) + throw new ArgumentException("Arg_MustBeType","elementType"); + if (length < 0) + throw new ArgumentOutOfRangeException("length", "ArgumentOutOfRange_NeedNonNegNum"); + VTable v = t.classVtable.vectorClass; + if (v == null) { + throw new InvalidCastException("Arg_VectorClassNotFound"); + } + return GC.AllocateVector(v, length); + } + + private static void VectorCopy(Object srcObject, int srcOffset, + Object dstObject, int dstOffset, + int length) { + StructuralType srcArrayOf = srcObject.vtable.arrayOf; + StructuralType dstArrayOf = dstObject.vtable.arrayOf; + if ((srcArrayOf == StructuralType.None) + || (srcArrayOf != dstArrayOf) ) { + throw new ArrayTypeMismatchException(); + } + if ((srcOffset < 0) + || (dstOffset < 0) + || (length < 0) ) { + VTable.throwNewArgumentOutOfRangeException(); + } + switch (srcArrayOf) { + case StructuralType.None: + throw new ArrayTypeMismatchException(); + + case StructuralType.Bool: { + bool[] srcArray = (bool[])srcObject; + bool[] dstArray = (bool[])dstObject; + Copy(srcArray,srcOffset,dstArray,dstOffset,length); + return; + } + case StructuralType.Char: { + char[] srcArray = (char[])srcObject; + char[] dstArray = (char[])dstObject; + Copy(srcArray,srcOffset,dstArray,dstOffset,length); + return; + } + case StructuralType.Int8: { + sbyte[] srcArray = (sbyte[])srcObject; + sbyte[] dstArray = (sbyte[])dstObject; + Copy(srcArray,srcOffset,dstArray,dstOffset,length); + return; + } + case StructuralType.Int16: { + short[] srcArray = (short[])srcObject; + short[] dstArray = (short[])dstObject; + Copy(srcArray,srcOffset,dstArray,dstOffset,length); + return; + } + case StructuralType.Int32: { + int[] srcArray = (int[])srcObject; + int[] dstArray = (int[])dstObject; + Copy(srcArray,srcOffset,dstArray,dstOffset,length); + return; + } + case StructuralType.Int64: { + long[] srcArray = (long[])srcObject; + long[] dstArray = (long[])dstObject; + Copy(srcArray,srcOffset,dstArray,dstOffset,length); + return; + } + case StructuralType.UInt8: { + byte[] srcArray = (byte[])srcObject; + byte[] dstArray = (byte[])dstObject; + Copy(srcArray,srcOffset,dstArray,dstOffset,length); + return; + } + case StructuralType.UInt16: { + ushort[] srcArray = (ushort[])srcObject; + ushort[] dstArray = (ushort[])dstObject; + Copy(srcArray,srcOffset,dstArray,dstOffset,length); + return; + } + case StructuralType.UInt32: { + uint[] srcArray = (uint[])srcObject; + uint[] dstArray = (uint[])dstObject; + Copy(srcArray,srcOffset,dstArray,dstOffset,length); + return; + } + case StructuralType.UInt64: { + ulong[] srcArray = (ulong[])srcObject; + ulong[] dstArray = (ulong[])dstObject; + Copy(srcArray,srcOffset,dstArray,dstOffset,length); + return; + } + case StructuralType.Float32: { + float[] srcArray = (float[])srcObject; + float[] dstArray = (float[])dstObject; + Copy(srcArray,srcOffset,dstArray,dstOffset,length); + return; + } + case StructuralType.Float64: { + double[] srcArray = (double[])srcObject; + double[] dstArray = (double[])dstObject; + Copy(srcArray,srcOffset,dstArray,dstOffset,length); + return; + } + case StructuralType.Reference: { + Object[] srcArray = (Object[])srcObject; + Object[] dstArray = (Object[])dstObject; + Copy(srcArray,srcOffset,dstArray,dstOffset,length); + return; + } + default: + VTable.DebugBreak(); + return; + } + } + +#if DEBUG_ARRAY + private void debug(Object srcArray,int srcOffset,Object dstArray,int dstOffset, + int length) + { +#if TODO + DebugPrint("System.Copy("); + DebugPrint(srcArray.getClass().getName()); + DebugPrint(","); + DebugPrint(srcOffset); + DebugPrint(","); + DebugPrint(dstArray.getClass().getName()); + DebugPrint(","); + DebugPrint(dstOffset); + DebugPrint(","); + DebugPrint(length); + DebugPrintln(")"); +#endif + } +#endif + + public static void Copy(bool[] srcArray, int srcOffset, bool[] dstArray, int dstOffset, int length) + { +#if DEBUG_ARRAY + debug(srcArray,srcOffset,dstArray,dstOffset,length); +#endif + int srcLength = srcArray.Length; + int dstLength = dstArray.Length; + +#if TODO + if (VTable.enableLibraryOptions) { + Assert(VTable.enableLibraryOptions && EnableLibraryAsserts() && + srcArray.vtable.arrayOf == Array.OfBool); + } +#endif + + if ((length < 0) + || (srcOffset < 0) + || (dstOffset < 0) + || (srcOffset+length > srcLength) + || (dstOffset+length > dstLength) ) { + VTable.throwNewArgumentOutOfRangeException(); + } + if ((srcArray == dstArray) + && (srcOffset < dstOffset) + && (dstOffset < srcOffset+length) ) { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyBoolDown(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyBoolDown(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + else { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyBoolUp(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyBoolUp(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + return; + } + + public static void Copy(char[] srcArray, int srcOffset, char[] dstArray, int dstOffset, int length) + { +#if DEBUG_ARRAY + debug(srcArray,srcOffset,dstArray,dstOffset,length); +#endif + int srcLength = srcArray.Length; + int dstLength = dstArray.Length; +#if TODO + if (VTable.enableLibraryOptions) { + Assert(VTable.enableLibraryOptions && EnableLibraryAsserts() && + srcArray.vtable.arrayOf == Array.OfChar); + } +#endif + if ((length < 0) + || (srcOffset < 0) + || (dstOffset < 0) + || (srcOffset+length > srcLength) + || (dstOffset+length > dstLength) ) { + VTable.throwNewArgumentOutOfRangeException(); + } + if ((srcArray == dstArray) + && (srcOffset < dstOffset) + && (dstOffset < srcOffset+length) ) { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyCharDown(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyCharDown(srcArray, srcOffset, dstArray, dstOffset, length); + } + + } + else { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyCharUp(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyCharUp(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + return; + } + + [CLSCompliant(false)] + public static void Copy(sbyte[] srcArray, int srcOffset, sbyte[] dstArray, int dstOffset, int length) + { +#if DEBUG_ARRAY + debug(srcArray,srcOffset,dstArray,dstOffset,length); +#endif + int srcLength = srcArray.Length; + int dstLength = dstArray.Length; +#if TODO + if (VTable.enableLibraryOptions) { + Assert(VTable.enableLibraryOptions && EnableLibraryAsserts() && + srcArray.vtable.arrayOf == Array.OfByte); + } +#endif + if ((length < 0) + || (srcOffset < 0) + || (dstOffset < 0) + || (srcOffset+length > srcLength) + || (dstOffset+length > dstLength) ) { + VTable.throwNewArgumentOutOfRangeException(); + } + if ((srcArray == dstArray) + && (srcOffset < dstOffset) + && (dstOffset < srcOffset+length) ) { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyInt8Down(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyInt8Down(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + else { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyInt8Up(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyInt8Up(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + } + + public static void Copy(short[] srcArray, int srcOffset, short[] dstArray, int dstOffset, int length) + { +#if DEBUG_ARRAY + debug(srcArray,srcOffset,dstArray,dstOffset,length); +#endif + int srcLength = srcArray.Length; + int dstLength = dstArray.Length; +#if TODO + if (VTable.enableLibraryOptions) { + Assert(VTable.enableLibraryOptions && EnableLibraryAsserts() && + srcArray.vtable.arrayOf == Array.OfShort); + } +#endif + if ((length < 0) + || (srcOffset < 0) + || (dstOffset < 0) + || (srcOffset+length > srcLength) + || (dstOffset+length > dstLength) ) { + VTable.throwNewArgumentOutOfRangeException(); + } + if ((srcArray == dstArray) + && (srcOffset < dstOffset) + && (dstOffset < srcOffset+length) ) { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyInt16Down(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyInt16Down(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + else { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyInt16Up(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyInt16Up(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + return; + } + + public static void Copy(int[] srcArray, int srcOffset, int[] dstArray, int dstOffset, int length) + { +#if DEBUG_ARRAY + debug(srcArray,srcOffset,dstArray,dstOffset,length); +#endif + int srcLength = srcArray.Length; + int dstLength = dstArray.Length; +#if TODO + if (VTable.enableLibraryOptions) { + Assert(VTable.enableLibraryOptions && EnableLibraryAsserts() && + srcArray.vtable.arrayOf == Array.OfInt); + } +#endif + if ((length < 0) + || (srcOffset < 0) + || (dstOffset < 0) + || (srcOffset+length > srcLength) + || (dstOffset+length > dstLength) ) { + VTable.throwNewArgumentOutOfRangeException(); + } + if ((srcArray == dstArray) + && (srcOffset < dstOffset) + && (dstOffset < srcOffset+length) ) { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyInt32Down(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyInt32Down(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + else { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.CopyInt32Up(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyInt32Up(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + return; + } + + public static void Copy(long[] srcArray, int srcOffset, long[] dstArray, int dstOffset, int length) + { +#if DEBUG_ARRAY + debug(srcArray,srcOffset,dstArray,dstOffset,length); +#endif + int srcLength = srcArray.Length; + int dstLength = dstArray.Length; +#if TODO + if (VTable.enableLibraryOptions) { + Assert(VTable.enableLibraryOptions && EnableLibraryAsserts() && + srcArray.vtable.arrayOf == Array.OfLong); + } +#endif + if ((length < 0) + || (srcOffset < 0) + || (dstOffset < 0) + || (srcOffset+length > srcLength) + || (dstOffset+length > dstLength) ) { + VTable.throwNewArgumentOutOfRangeException(); + } + if ((srcArray == dstArray) + && (srcOffset < dstOffset) + && (dstOffset < srcOffset+length) ) { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyInt64Down(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyInt64Down(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + else { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyInt64Up(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyInt64Up(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + return; + } + + public static void Copy(byte[] srcArray, int srcOffset, byte[] dstArray, int dstOffset, int length) + { +#if DEBUG_ARRAY + debug(srcArray,srcOffset,dstArray,dstOffset,length); +#endif + int srcLength = srcArray.Length; + int dstLength = dstArray.Length; +#if TODO + if (VTable.enableLibraryOptions) { + Assert(VTable.enableLibraryOptions && EnableLibraryAsserts() && + srcArray.vtable.arrayOf == Array.OfByte); + } +#endif + if ((length < 0) + || (srcOffset < 0) + || (dstOffset < 0) + || (srcOffset+length > srcLength) + || (dstOffset+length > dstLength) ) { + VTable.throwNewArgumentOutOfRangeException(); + } + if ((srcArray == dstArray) + && (srcOffset < dstOffset) + && (dstOffset < srcOffset+length) ) { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyUInt8Down(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyUInt8Down(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + else { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyUInt8Up(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyUInt8Up(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + } + + [CLSCompliant(false)] + public static void Copy(ushort[] srcArray, int srcOffset, ushort[] dstArray, int dstOffset, int length) + { +#if DEBUG_ARRAY + debug(srcArray,srcOffset,dstArray,dstOffset,length); +#endif + int srcLength = srcArray.Length; + int dstLength = dstArray.Length; +#if TODO + if (VTable.enableLibraryOptions) { + Assert(VTable.enableLibraryOptions && EnableLibraryAsserts() && + srcArray.vtable.arrayOf == Array.OfShort); + } +#endif + if ((length < 0) + || (srcOffset < 0) + || (dstOffset < 0) + || (srcOffset+length > srcLength) + || (dstOffset+length > dstLength) ) { + VTable.throwNewArgumentOutOfRangeException(); + } + if ((srcArray == dstArray) + && (srcOffset < dstOffset) + && (dstOffset < srcOffset+length) ) { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyUInt16Down(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyUInt16Down(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + else { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyUInt16Up(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyUInt16Up(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + return; + } + + [CLSCompliant(false)] + public static void Copy(uint[] srcArray, int srcOffset, uint[] dstArray, int dstOffset, int length) + { +#if DEBUG_ARRAY + debug(srcArray,srcOffset,dstArray,dstOffset,length); +#endif + int srcLength = srcArray.Length; + int dstLength = dstArray.Length; +#if TODO + if (VTable.enableLibraryOptions) { + Assert(VTable.enableLibraryOptions && EnableLibraryAsserts() && + srcArray.vtable.arrayOf == Array.OfInt); + } +#endif + if ((length < 0) + || (srcOffset < 0) + || (dstOffset < 0) + || (srcOffset+length > srcLength) + || (dstOffset+length > dstLength) ) { + VTable.throwNewArgumentOutOfRangeException(); + } + if ((srcArray == dstArray) + && (srcOffset < dstOffset) + && (dstOffset < srcOffset+length) ) { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyUInt32Down(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyUInt32Down(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + else { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.CopyUInt32Up(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyUInt32Up(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + return; + } + + [CLSCompliant(false)] + public static void Copy(ulong[] srcArray, int srcOffset, ulong[] dstArray, int dstOffset, int length) + { +#if DEBUG_ARRAY + debug(srcArray,srcOffset,dstArray,dstOffset,length); +#endif + int srcLength = srcArray.Length; + int dstLength = dstArray.Length; +#if TODO + if (VTable.enableLibraryOptions) { + Assert(VTable.enableLibraryOptions && EnableLibraryAsserts() && + srcArray.vtable.arrayOf == Array.OfLong); + } +#endif + if ((length < 0) + || (srcOffset < 0) + || (dstOffset < 0) + || (srcOffset+length > srcLength) + || (dstOffset+length > dstLength) ) { + VTable.throwNewArgumentOutOfRangeException(); + } + if ((srcArray == dstArray) + && (srcOffset < dstOffset) + && (dstOffset < srcOffset+length) ) { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyUInt64Down(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyUInt64Down(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + else { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyUInt64Up(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyUInt64Up(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + return; + } + + public static void Copy(float[] srcArray, int srcOffset, float[] dstArray, int dstOffset, int length) + { +#if DEBUG_ARRAY + debug(srcArray,srcOffset,dstArray,dstOffset,length); +#endif + int srcLength = srcArray.Length; + int dstLength = dstArray.Length; +#if TODO + if (VTable.enableLibraryOptions) { + Assert(VTable.enableLibraryOptions && EnableLibraryAsserts() && + srcArray.vtable.arrayOf == Array.OfFloat); + } +#endif + if ((length < 0) + || (srcOffset < 0) + || (dstOffset < 0) + || (srcOffset+length > srcLength) + || (dstOffset+length > dstLength) ) { + VTable.throwNewArgumentOutOfRangeException(); + } + if ((srcArray == dstArray) + && (srcOffset < dstOffset) + && (dstOffset < srcOffset+length) ) { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyFloat32Down(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyFloat32Down(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + else { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyFloat32Up(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyFloat32Up(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + return; + } + + public static void Copy(double[] srcArray, int srcOffset, double[] dstArray, int dstOffset, int length) + { +#if DEBUG_ARRAY + debug(srcArray,srcOffset,dstArray,dstOffset,length); +#endif + int srcLength = srcArray.Length; + int dstLength = dstArray.Length; +#if TODO + if (VTable.enableLibraryOptions) { + Assert(VTable.enableLibraryOptions && EnableLibraryAsserts() && + srcArray.vtable.arrayOf == Array.OfDouble); + } +#endif + if ((length < 0) + || (srcOffset < 0) + || (dstOffset < 0) + || (srcOffset+length > srcLength) + || (dstOffset+length > dstLength) ) { + VTable.throwNewArgumentOutOfRangeException(); + } + if ((srcArray == dstArray) + && (srcOffset < dstOffset) + && (dstOffset < srcOffset+length) ) { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyFloat64Down(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyFloat64Down(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + else { + if ((srcOffset + length <= srcLength) + && (dstOffset+length <= dstLength) ) { + ArrayHelper.UncheckedCopyFloat64Up(srcArray, srcOffset, dstArray, dstOffset, length); + } + else { + ArrayHelper.CopyFloat64Up(srcArray, srcOffset, dstArray, dstOffset, length); + } + } + return; + } + + public static void Copy(Object[] srcArray, int srcOffset, Object[] dstArray, int dstOffset, int length) + { +#if DEBUG_ARRAY + debug(srcArray,srcOffset,dstArray,dstOffset,length); +#endif + int srcLength = srcArray.Length; + int dstLength = dstArray.Length; +#if TODO + if (VTable.enableLibraryOptions) { + Assert(VTable.enableLibraryOptions && EnableLibraryAsserts() && + srcArray.vtable.arrayOf == Array.OfReference); + } +#endif + if ((length < 0) + || (srcOffset < 0) + || (dstOffset < 0) + || (srcOffset+length > srcLength) + || (dstOffset+length > dstLength) ) { + VTable.throwNewArgumentOutOfRangeException(); + } + if ((srcArray == dstArray) + && (srcOffset < dstOffset) + && (dstOffset < srcOffset+length) ) { + int dx = dstOffset+length-1; + int sx = srcOffset+length-1; + for (int i = length - 1; i >= 0; i--) { + dstArray[dx] = srcArray[sx]; + sx--; + dx--; + } + // for (int i=0; i= 0; i--) { + dstArray[dx] = srcArray[sx]; + sx++; + dx++; + } + // for (int i=0; i= 0 && + destArray != null && destIndex >= 0 && len >= 0); + VTable destVT = destArray.vtable.arrayElementClass; + VTable.Assert(destVT != null); + // Cache last cast test to speed up cast checks. + VTable lastVT = null; + + //const BOOL destIsArray = destTH.IsArray(); + fixed (int *srcField = &sourceArray.field1) { + fixed (int *dstField = &destArray.field1) { + UIntPtr* sourceArrayPtr =(UIntPtr*) + sourceArray.GetFirstElementAddress(srcField); + UIntPtr* destArrayPtr = (UIntPtr*) + destArray.GetFirstElementAddress(dstField); + Object obj; + for (int i = srcIndex; i < srcIndex + len; ++i) { + UIntPtr objAddress = sourceArrayPtr[i]; + obj = Magic.fromAddress(objAddress); + // Now that we have grabbed obj, we are no longer + // subject to races from another mutator thread. + if (obj != null) { + VTable objVT = obj.vtable; + if (objVT != lastVT && objVT != destVT) { + lastVT = objVT; + // do cast check + if(!VTable.isValidAssignment + (destVT.vtableType, objVT.vtableType)) { + throw new InvalidCastException("InvalidCast_DownCastArrayElement"); + } + } + } + VTable.Assert(destArray.vtable.arrayOf != + StructuralType.Struct); + if (destArray.vtable.arrayOf == + StructuralType.Reference) { +#if REFERENCE_COUNTING_GC + GCs.ReferenceCountingCollector. + IncrementRefCount(obj); + UIntPtr dstAddr = + destArrayPtr[i-srcIndex+destIndex]; + Object dst = Magic.fromAddress(dstAddr); + GCs.ReferenceCountingCollector. + DecrementRefCount(dst); + destArrayPtr[i - srcIndex + destIndex] = objAddress; +#elif DEFERRED_REFERENCE_COUNTING_GC + GCs.DeferredReferenceCountingCollector. + IncrementRefCount(obj); + UIntPtr dstAddr = + destArrayPtr[i-srcIndex+destIndex]; + Object dst = Magic.fromAddress(dstAddr); + GCs.DeferredReferenceCountingCollector. + DecrementRefCount(dst); + destArrayPtr[i - srcIndex + destIndex] = objAddress; +#else + UIntPtr *elementPtr = + destArrayPtr + i - srcIndex + destIndex; + Barrier.StoreIndirect(elementPtr, obj, 0); +#endif // REFERENCE_COUNTING_GC + } + else { + destArrayPtr[i - srcIndex + destIndex] = objAddress; + } + // N.B. CLR uses layers of #defines/methods to do this + // assignment, starting with SetObjectReference + } + } + } + } + + private static void BoxEachElement(Array sourceArray, int srcIndex, + Array destArray, int destIndex, + int len) { + // For now, use this silly implementation that leans on + // SetValue to do the right thing. + for (int i = 0; i < len; ++i) { + destArray.SetValue(sourceArray.GetValue(srcIndex + i), destIndex + i); + } + } + + private unsafe static void UnBoxEachElement(Array sourceArray, + int srcIndex, + Array destArray, + int destIndex, + int len, + bool castEachElement) + { + VTable.Assert(sourceArray != null && srcIndex >= 0 && + destArray != null && destIndex >= 0 && len >= 0); + VTable destVT = destArray.vtable.arrayElementClass; + VTable.Assert(destVT != null); + int elementSize = destArray.vtable.arrayElementSize; + // Cache last cast test to speed up cast checks. + VTable lastVT = null; + + //const BOOL destIsArray = destTH.IsArray(); + fixed (int *srcField = &sourceArray.field1) { + fixed (int *dstField = &destArray.field1) { + UIntPtr* sourceArrayPtr =(UIntPtr*) + sourceArray.GetFirstElementAddress(srcField); + byte* destArrayPtr = (byte*) + destArray.GetFirstElementAddress(dstField); + Object obj; + for (int i = srcIndex; i < srcIndex + len; ++i) { + UIntPtr objAddr = sourceArrayPtr[i]; + obj = Magic.fromAddress(objAddr); + if (castEachElement) { + //obj = Magic.fromAddress(objAddr); + // Now that we have grabbed obj, we are no longer + // subject to races from another mutator thread. + if (obj != null) { + VTable objVT = obj.vtable; + if (objVT != lastVT && objVT != destVT) { + lastVT = objVT; + // do cast check + if(!VTable.isValidAssignment + (destVT.vtableType, objVT.vtableType)) { + throw new InvalidCastException + ("InvalidCast_DownCastArrayElement"); + } + } + } + } + int postHeaderSize = PostHeader.Size; + UIntPtr src = objAddr + postHeaderSize; + int offset = (i-srcIndex + destIndex) * elementSize; + UIntPtr dst = (UIntPtr) destArrayPtr + offset; + VTable.Assert(destArray.vtable.arrayOf != + StructuralType.Reference); + if (destArray.vtable.arrayOf == + StructuralType.Struct) { + UIntPtr dstAddr = dst - postHeaderSize; +#if REFERENCE_COUNTING_GC + GCs.ReferenceCountingCollector. + IncrementReferentRefCounts(objAddr, obj.vtable); + GCs.ReferenceCountingCollector. + DecrementReferentRefCounts(dstAddr, destVT); + Buffer.MoveMemory(dst, src, elementSize); +#elif DEFERRED_REFERENCE_COUNTING_GC + GCs.DeferredReferenceCountingCollector. + IncrementReferentRefCounts(objAddr, obj.vtable); + GCs.DeferredReferenceCountingCollector. + DecrementReferentRefCounts(dstAddr, destVT); + Buffer.MoveMemory(dst, src, elementSize); +#else + Barrier.CopyStruct(obj, destArray, destVT, src, dst); +#endif // REFERENCE_COUNTING_GC + } + else { + Buffer.MoveMemory((byte *) dst, (byte *) src, + (UIntPtr) elementSize); + } + } + } + } + } + + private static void PrimitiveWiden(Array sourceArray, int srcIndex, + Array destArray, int destIndex, + int len) { + throw new Exception("System.Array.PrimitiveWiden not implemented in Bartok!"); + } + + // Copies length elements from sourceArray, starting at index 0, to + // destinationArray, starting at index 0. + // + //| + public static void Copy(Array sourceArray, Array destinationArray, int length) + { + if (sourceArray == null) + throw new ArgumentNullException("sourceArray"); + if (destinationArray == null) + throw new ArgumentNullException("destinationArray"); + Copy(sourceArray, sourceArray.GetLowerBound(0), destinationArray, destinationArray.GetLowerBound(0), length); + } + + // Copies length elements from sourceArray, starting at sourceIndex, to + // destinationArray, starting at destinationIndex. + // + //| + + + //| + public static void Copy(Array sourceArray, Array destinationArray, long length) + { + if (length > Int32.MaxValue || length < Int32.MinValue) + throw new ArgumentOutOfRangeException("length", "ArgumentOutOfRange_HugeArrayNotSupported"); + + Array.Copy(sourceArray, destinationArray, (int) length); + } + + //| + public unsafe static void Copy(Array sourceArray, int sourceIndex, + Array destinationArray, + int destinationIndex, int length) { + if (sourceArray == null) { + throw new ArgumentNullException("sourceArray"); + } + if (destinationArray == null) { + throw new ArgumentNullException("destinationArray"); + } + if (sourceArray.Rank != destinationArray.Rank) { + throw new RankException("Ranks must match"); + } + + bool castEachElement = false; + bool boxEachElement = false; + bool unboxEachElement = false; + bool primitiveWiden = false; + + AssignArray r; + + // Small perf optimization - we copy from one portion of an array + // back to itself a lot when resizing collections, etc. The cost of + // doing the type checking is significant for copying small numbers + // of bytes (~half of the time for copying 1 byte within one array + // from element 0 to element 1). + if (sourceArray == destinationArray) { + r = AssignArray.WillWork; + } + else { + r = CanAssignArrayType(sourceArray, destinationArray); + } + + // Specialized vector copy + + // Undo support adds logging to array copies which can trigger GCs + // at inopportune times so for now we use the specialized copy + // routine when possible for correctness. + // BUGBUG: arraycopy of struct + logging may lose GC information + + if (r == AssignArray.WillWork + && sourceArray.IsVector + && (sourceArray.vtable.arrayOf != StructuralType.Struct)) { + VectorCopy(sourceArray,sourceIndex,destinationArray, + destinationIndex,length); + return; + } + else { + switch (r) { + case AssignArray.WrongType: + throw new ArrayTypeMismatchException("ArrayTypeMismatch_CantAssignType"); + case AssignArray.MustCast: + castEachElement = true; + break; + case AssignArray.WillWork: + break; + case AssignArray.BoxValueClassOrPrimitive: + boxEachElement = true; + break; + case AssignArray.UnboxValueClassOrPrimitive: + castEachElement = true; + unboxEachElement = true; + break; + case AssignArray.PrimitiveWiden: + primitiveWiden = true; + break; + default: + VTable.NotReached("Fell through switch in Array.Copy!"); + break; + } + } + if (length < 0) { + throw new ArgumentOutOfRangeException("length", "ArgumentOutOfRange_NeedNonNegNum"); + } + + int sourceLower = sourceArray.GetLowerBound(0); + int destinationLower = destinationArray.GetLowerBound(0); + if (sourceIndex < sourceLower || + sourceIndex + length > sourceLower + sourceArray.Length) { + throw new ArgumentException("Illegal range from sourceArray"); + } + if (destinationIndex < destinationLower || + destinationIndex + length > destinationLower + destinationArray.Length) { + throw new ArgumentException("Illegal range from destinationArray"); + } + + if (length > 0) { + VTable.Assert(!boxEachElement || !castEachElement); + if (unboxEachElement) { + UnBoxEachElement(sourceArray, sourceIndex - sourceLower, + destinationArray, + destinationIndex - destinationLower, + length, castEachElement); + } + else if (boxEachElement) { + BoxEachElement(sourceArray, sourceIndex - sourceLower, + destinationArray, + destinationIndex - destinationLower, length); + } + else if (castEachElement) { + VTable.Assert(!unboxEachElement); // handled above + CastCheckEachElement(sourceArray, sourceIndex - sourceLower, + destinationArray, + destinationIndex - destinationLower, + length); + } + else if (primitiveWiden) { + PrimitiveWiden(sourceArray, sourceIndex - sourceLower, + destinationArray, + destinationIndex - destinationLower, length); + } + else { +#if !REFERENCE_COUNTING_GC && !DEFERRED_REFERENCE_COUNTING_GC + Barrier.ArrayCopy(sourceArray, + sourceIndex - sourceLower, + destinationArray, + destinationIndex - destinationLower, + length); +#else + fixed (int *sourceFieldPtr = &sourceArray.field1) { + fixed (int *dstFieldPtr = &destinationArray.field1) { + byte* src = (byte*) sourceArray.GetFirstElementAddress(sourceFieldPtr); + byte* dst = (byte*) destinationArray.GetFirstElementAddress(dstFieldPtr); + int size = sourceArray.vtable.arrayElementSize; + Buffer.MoveMemory(dst + ((destinationIndex - destinationLower)*size), + src + ((sourceIndex - sourceLower)*size), + length*size); + } + } +#endif + } + } + } + + // Sets length elements in array to 0 (or null for Object arrays), starting + // at index. + // + //| + public unsafe static void Clear(Array array, int index, int length) { + // cannot pass null for array + if (array == null) { + throw new ArgumentNullException("array", "ArgumentNull_Array"); + } + + // array bounds checking + int lb = array.GetLowerBound(0); + // are the first two redundant or is there an overflow issue? + if (index < lb || (index - lb) < 0 || length < 0) { + throw new IndexOutOfRangeException(); + } + if (index - lb > array.Length - length) { + throw new IndexOutOfRangeException(); + } + + if (length > 0) { + fixed (int *fieldPtr = &array.field1) { + byte* arrayPtr = (byte*) + array.GetFirstElementAddress(fieldPtr); + int size = array.vtable.arrayElementSize; + VTable.Assert(size >= 1); + // REVIEW: what about structs? + VTable.Assert(size <= 8); + Buffer.ZeroMemory(arrayPtr + (index-lb) * size, + length * size); + } + } + } + + // The various Get values... + //| + public Object GetValue(params int[] indices) + { + if (indices == null) + throw new ArgumentNullException("indices"); + if (Rank != indices.Length) + throw new ArgumentException("Arg_RankIndices"); + return InternalGetValueEx(indices); + } + + //| + public Object GetValue(int index) + { + if (Rank != 1) + throw new ArgumentException("Arg_Need1DArray"); + return InternalGetValue(index,0,0); + } + + //| + public Object GetValue(int index1, int index2) + { + if (Rank != 2) + throw new ArgumentException("Arg_Need2DArray"); + return InternalGetValue(index1,index2,0); + } + + //| + public Object GetValue(int index1, int index2, int index3) + { + if (Rank != 3) + throw new ArgumentException("Arg_Need3DArray"); + return InternalGetValue(index1,index2,index3); + } + + //| + public Object GetValue(long index) + { + if (index > Int32.MaxValue || index < Int32.MinValue) + throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_HugeArrayNotSupported"); + + return this.GetValue((int) index); + } + + //| + public Object GetValue(long index1, long index2) + { + if (index1 > Int32.MaxValue || index1 < Int32.MinValue) + throw new ArgumentOutOfRangeException("index1", "ArgumentOutOfRange_HugeArrayNotSupported"); + if (index2 > Int32.MaxValue || index2 < Int32.MinValue) + throw new ArgumentOutOfRangeException("index2", "ArgumentOutOfRange_HugeArrayNotSupported"); + + return this.GetValue((int) index1, (int) index2); + } + + //| + public Object GetValue(long index1, long index2, long index3) + { + if (index1 > Int32.MaxValue || index1 < Int32.MinValue) + throw new ArgumentOutOfRangeException("index1", "ArgumentOutOfRange_HugeArrayNotSupported"); + if (index2 > Int32.MaxValue || index2 < Int32.MinValue) + throw new ArgumentOutOfRangeException("index2", "ArgumentOutOfRange_HugeArrayNotSupported"); + if (index3 > Int32.MaxValue || index3 < Int32.MinValue) + throw new ArgumentOutOfRangeException("index3", "ArgumentOutOfRange_HugeArrayNotSupported"); + + return this.GetValue((int) index1, (int) index2, (int) index3); + } + + //| + public Object GetValue(params long[] indices) + { + int[] intIndices = new int[indices.Length]; + + for (int i = 0; i < indices.Length; ++i) { + long index = indices[i]; + if (index > Int32.MaxValue || index < Int32.MinValue) + throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_HugeArrayNotSupported"); + intIndices[i] = (int) index; + } + + return this.GetValue(intIndices); + } + + + //| + public void SetValue(Object value,int index) + { + if (Rank != 1) + throw new ArgumentException("Arg_Need1DArray"); + InternalSetValue(value,index,0,0); + } + + //| + public void SetValue(Object value,int index1, int index2) + { + if (Rank != 2) + throw new ArgumentException("Arg_Need2DArray"); + InternalSetValue(value,index1,index2,0); + } + + //| + public void SetValue(Object value,int index1, int index2, int index3) + { + if (Rank != 3) + throw new ArgumentException("Arg_Need3DArray"); + InternalSetValue(value,index1,index2,index3); + } + + //| + public void SetValue(Object value,params int[] indices) + { + if (indices == null) + throw new ArgumentNullException("indices"); + if (Rank != indices.Length) + throw new ArgumentException("Arg_RankIndices"); + InternalSetValueEx(value,indices); + } + + //| + public void SetValue(Object value, long index) + { + if (index > Int32.MaxValue || index < Int32.MinValue) + throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_HugeArrayNotSupported"); + + this.SetValue(value, (int) index); + } + + //| + public void SetValue(Object value, long index1, long index2) + { + if (index1 > Int32.MaxValue || index1 < Int32.MinValue) + throw new ArgumentOutOfRangeException("index1", "ArgumentOutOfRange_HugeArrayNotSupported"); + if (index2 > Int32.MaxValue || index2 < Int32.MinValue) + throw new ArgumentOutOfRangeException("index2", "ArgumentOutOfRange_HugeArrayNotSupported"); + + this.SetValue(value, (int) index1, (int) index2); + } + + //| + public void SetValue(Object value, long index1, long index2, long index3) + { + if (index1 > Int32.MaxValue || index1 < Int32.MinValue) + throw new ArgumentOutOfRangeException("index1", "ArgumentOutOfRange_HugeArrayNotSupported"); + if (index2 > Int32.MaxValue || index2 < Int32.MinValue) + throw new ArgumentOutOfRangeException("index2", "ArgumentOutOfRange_HugeArrayNotSupported"); + if (index3 > Int32.MaxValue || index3 < Int32.MinValue) + throw new ArgumentOutOfRangeException("index3", "ArgumentOutOfRange_HugeArrayNotSupported"); + + this.SetValue(value, (int) index1, (int) index2, (int) index3); + } + + //| + public void SetValue(Object value, params long[] indices) + { + int[] intIndices = new int[indices.Length]; + + for (int i = 0; i < indices.Length; ++i) { + long index = indices[i]; + if (index > Int32.MaxValue || index < Int32.MinValue) + throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_HugeArrayNotSupported"); + intIndices[i] = (int) index; + } + + this.SetValue(value, intIndices); + } + + // These functions are provided to help device drivers. + // + [CLSCompliant(false)] + [NoHeapAllocation] + public unsafe UIntPtr AddressOfContent() + { + if (!IsVector) { + return UIntPtr.Zero; + } + + // This operation is only legal on a pinned object! + UIntPtr addr = UIntPtr.Zero; + fixed (int *fieldPtr = &field1) { + addr = (UIntPtr)GetFirstElementAddress(fieldPtr); + } + return addr; + } + + [CLSCompliant(false)] + public UIntPtr SizeOfContent() + { + if (!IsVector) { + return UIntPtr.Zero; + } + return (UIntPtr)(GetLengthVector() * vtable.arrayElementSize); + } + + // This is the set of native routines that implement the real + // Get/Set Value. The arguments have been verified that they exist + // before we get to this point. + //| + public int GetLength(int dimension) { + if (IsVector) { + return GetLengthVector(); + } + else { + return GetLengthRectangleArray(dimension); + } + } + + //| + public int Length { + get { + if (IsVector) { + return GetLengthVector(); + } + else { + return GetLengthRectangleArray(); + } + } + } + + //| + public long LongLength { + get { return Length; } + } + + //| + public long GetLongLength(int dimension) { + return GetLength(dimension); + } + + //| + public int Rank { + get { + if (IsVector) { + return 1; + } + else { + return RankRectangleArray; + } + } + } + + // This is to be used only when allocating a vector! + [Inline] + [NoBarriers] + internal void InitializeVectorLength(int numElements) { + this.field1 = numElements; + } + + // This is to be used only when allocating an array! + [Inline] + [NoBarriers] + internal void InitializeArrayLength(int rank, int totalElements) { + this.field1 = rank; + this.field2 = totalElements; + } + + internal bool IsVector { + [NoHeapAllocation] + get { + // REVIEW: information can be closer + return this.vtable.vtableType.IsVector; + } + } + private bool IsRectangleArray { + [NoHeapAllocation] + get { + // REVIEW: information can be closer + return this.vtable.vtableType.IsRectangleArray; + } + } + + //| + public int GetUpperBound(int dimension) { + if (dimension >= Rank) { + throw new IndexOutOfRangeException("GetUpperBound dimension"); + } + return GetLowerBound(dimension) + GetLength(dimension) - 1; + } + + //| + public int GetLowerBound(int dimension) { + if (dimension >= Rank) { + throw new IndexOutOfRangeException("GetLowerBound dimension"); + } + if (IsVector) { // zero - based + return 0; + } + // IsRectangleArray() == true + return GetLowerBoundRectangleArray(dimension); + } + + // Number of elements in the Array. + //| + int ICollection.Count + { get { return Length; } } + + + // Returns an object appropriate for synchronizing access to this + // Array. + //| + public virtual Object SyncRoot + { get { return this; } } + + // Is this Array read-only? + //| + public virtual bool IsReadOnly + { get { return false; } } + + //| + public virtual bool IsFixedSize { + get { return true; } + } + + // Is this Array synchronized (i.e., thread-safe)? If you want a synchronized + // collection, you can use SyncRoot as an object to synchronize your + // collection with. You could also call GetSynchronized() + // to get a synchronized wrapper around the Array. + //| + public virtual bool IsSynchronized + { get { return false; } } + + + //| + Object IList.this[int index] { + get { return GetValue(index); } + set { SetValue(value, index); } + } + + //| + int IList.Add(Object value) + { + throw new NotSupportedException("NotSupported_FixedSizeCollection"); + } + + //| + bool IList.Contains(Object value) + { + return Array.IndexOf(this, value) >= this.GetLowerBound(0); + } + + //| + void IList.Clear() + { + Array.Clear(this, 0, this.Length); + } + + //| + int IList.IndexOf(Object value) + { + return Array.IndexOf(this, value); + } + + //| + void IList.Insert(int index, Object value) + { + throw new NotSupportedException("NotSupported_FixedSizeCollection"); + } + + //| + void IList.Remove(Object value) + { + throw new NotSupportedException("NotSupported_FixedSizeCollection"); + } + + //| + void IList.RemoveAt(int index) + { + throw new NotSupportedException("NotSupported_FixedSizeCollection"); + } + + // Make a new array which is a deep copy of the original array. + // + //| + public virtual Object Clone() + { + return MemberwiseClone(); + } + + // Searches an array for a given element using a binary search algorithm. + // Elements of the array are compared to the search value using the + // IComparable interface, which must be implemented by all elements + // of the array and the given search value. This method assumes that the + // array is already sorted according to the IComparable interface; + // if this is not the case, the result will be incorrect. + // + // The method returns the index of the given value in the array. If the + // array does not contain the given value, the method returns a negative + // integer. The bitwise complement operator (~) can be applied to a + // negative result to produce the index of the first element (if any) that + // is larger than the given search value. + // + //| + public static int BinarySearch(Array array, Object value) { + if (array == null) + throw new ArgumentNullException("array"); + int lb = array.GetLowerBound(0); + return BinarySearch(array, lb, array.Length, value, null); + } + + // Searches a section of an array for a given element using a binary search + // algorithm. Elements of the array are compared to the search value using + // the IComparable interface, which must be implemented by all + // elements of the array and the given search value. This method assumes + // that the array is already sorted according to the IComparable + // interface; if this is not the case, the result will be incorrect. + // + // The method returns the index of the given value in the array. If the + // array does not contain the given value, the method returns a negative + // integer. The bitwise complement operator (~) can be applied to a + // negative result to produce the index of the first element (if any) that + // is larger than the given search value. + // + //| + public static int BinarySearch(Array array, int index, int length, Object value) { + return BinarySearch(array, index, length, value, null); + } + + // Searches an array for a given element using a binary search algorithm. + // Elements of the array are compared to the search value using the given + // IComparer interface. If comparer is null, elements of the + // array are compared to the search value using the IComparable + // interface, which in that case must be implemented by all elements of the + // array and the given search value. This method assumes that the array is + // already sorted; if this is not the case, the result will be incorrect. + // + // The method returns the index of the given value in the array. If the + // array does not contain the given value, the method returns a negative + // integer. The bitwise complement operator (~) can be applied to a + // negative result to produce the index of the first element (if any) that + // is larger than the given search value. + // + //| + public static int BinarySearch(Array array, Object value, IComparer comparer) { + if (array == null) + throw new ArgumentNullException("array"); + int lb = array.GetLowerBound(0); + return BinarySearch(array, lb, array.Length, value, comparer); + } + + // Searches a section of an array for a given element using a binary search + // algorithm. Elements of the array are compared to the search value using + // the given IComparer interface. If comparer is null, + // elements of the array are compared to the search value using the + // IComparable interface, which in that case must be implemented by + // all elements of the array and the given search value. This method + // assumes that the array is already sorted; if this is not the case, the + // result will be incorrect. + // + // The method returns the index of the given value in the array. If the + // array does not contain the given value, the method returns a negative + // integer. The bitwise complement operator (~) can be applied to a + // negative result to produce the index of the first element (if any) that + // is larger than the given search value. + // + //| + public static int BinarySearch(Array array, int index, int length, Object value, IComparer comparer) { + if (array == null) + throw new ArgumentNullException("array"); + int lb = array.GetLowerBound(0); + if (index < lb || length < 0) + throw new ArgumentOutOfRangeException((index> 1; + int c; + try { + c = comparer.Compare(objArray[i], value); + } + catch (Exception e) { + throw new InvalidOperationException("InvalidOperation_IComparerFailed", e); + } + if (c == 0) return i; + if (c < 0) { + lo = i + 1; + } + else { + hi = i - 1; + } + } + } + else { + while (lo <= hi) { + int i = (lo + hi) >> 1; + int c; + try { + c = comparer.Compare(array.GetValue(i), value); + } + catch (Exception e) { + throw new InvalidOperationException("InvalidOperation_IComparerFailed", e); + } + if (c == 0) return i; + if (c < 0) { + lo = i + 1; + } + else { + hi = i - 1; + } + } + } + return ~lo; + } + + private static int TrySZBinarySearch(Array sourceArray, int sourceIndex, int count, Object value, out int retVal) { + // BUGBUG: was fast native primitive type method + // returning false (0) means we couldn't handle it + retVal = -1; + return 0; + } + + // CopyTo copies a collection into an Array, starting at a particular + // index into the array. + // + // This method is to support the ICollection interface, and calls + // Array.Copy internally. If you aren't using ICollection explicitly, + // call Array.Copy to avoid an extra indirection. + // + //| + public virtual void CopyTo(Array array, int index) + { + if (array != null && array.Rank != 1) + throw new ArgumentException("Arg_RankMultiDimNotSupported"); + // Note: Array.Copy throws a RankException and we want a consistent ArgumentException for all the IList CopyTo methods. + Array.Copy(this, GetLowerBound(0), array, index, Length); + } + + //| + public virtual void CopyTo(Array array, long index) + { + if (index > Int32.MaxValue || index < Int32.MinValue) + throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_HugeArrayNotSupported"); + + this.CopyTo(array, (int) index); + } + + + // GetEnumerator returns an IEnumerator over this Array. + // + // Currently, only one dimensional arrays are supported. + // + //| + public virtual IEnumerator GetEnumerator() + { + int lowerBound = GetLowerBound(0); + if (Rank == 1 && lowerBound == 0) + return new SZArrayEnumerator(this); + else + return new ArrayEnumerator(this, lowerBound, Length); + } + + // Returns the index of the first occurrence of a given value in an array. + // The array is searched forwards, and the elements of the array are + // compared to the given value using the Object.Equals method. + // + //| + public static int IndexOf(Array array, Object value) { + if (array == null) + throw new ArgumentNullException("array"); + int lb = array.GetLowerBound(0); + return IndexOf(array, value, lb, array.Length); + } + + // Returns the index of the first occurrence of a given value in a range of + // an array. The array is searched forwards, starting at index + // startIndex and ending at the last element of the array. The + // elements of the array are compared to the given value using the + // Object.Equals method. + // + //| + public static int IndexOf(Array array, Object value, int startIndex) { + if (array == null) + throw new ArgumentNullException("array", "ArgumentNull_Array"); + int lb = array.GetLowerBound(0); + return IndexOf(array, value, startIndex, array.Length - startIndex + lb); + } + + // Returns the index of the first occurrence of a given value in a range of + // an array. The array is searched forwards, starting at index + // startIndex and up to count elements. The + // elements of the array are compared to the given value using the + // Object.Equals method. + // + //| + public static int IndexOf(Array array, Object value, int startIndex, int count) { + if (array == null) + throw new ArgumentNullException("array"); + int lb = array.GetLowerBound(0); + if (startIndex < lb || startIndex > array.Length + lb) + throw new ArgumentOutOfRangeException("startIndex", "ArgumentOutOfRange_Index"); + if (count < 0 || count > array.Length - startIndex + lb) + throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_Count"); + if (array.Rank != 1) + throw new RankException("Rank_MultiDimNotSupported"); + + // Try calling a quick native method to handle primitive types. + int retVal; + int r = TrySZIndexOf(array, startIndex, count, value, out retVal); + if (r != 0) + return retVal; + + Object[] objArray = array as Object[]; + int endIndex = startIndex + count; + if (objArray != null) { + if (value == null) { + for (int i = startIndex; i < endIndex; i++) { + if (objArray[i] == null) return i; + } + } + else { + for (int i = startIndex; i < endIndex; i++) { + Object obj = objArray[i]; + if (obj != null && value.Equals(obj)) return i; + } + } + } + else { + // This is an array of value classes + Debug.Assert(array.GetType().GetElementType().IsValueType, "array.GetType().GetUnderlyingType().IsValueType"); + if (value == null) + return -1; + for (int i = startIndex; i < endIndex; i++) { + Object obj = array.GetValue(i); + if (obj != null && value.Equals(obj)) return i; + } + } + // Return one less than the lower bound of the array. This way, + // for arrays with a lower bound of -1 we will not return -1 when the + // item was not found. And for SZArrays (the vast majority), -1 still + // works for them. + return lb-1; + } + + private static int TrySZIndexOf(Array sourceArray, int sourceIndex, int count, Object value, out int retVal) { + // BUGBUG: was fast native primitive type method + // returning false (0) means we couldn't handle it + retVal = -1; + return 0; + } + + // Returns the index of the last occurrence of a given value in an array. + // The array is searched backwards, and the elements of the array are + // compared to the given value using the Object.Equals method. + // + //| + public static int LastIndexOf(Array array, Object value) { + if (array == null) + throw new ArgumentNullException("array", "ArgumentNull_Array"); + int lb = array.GetLowerBound(0); + return LastIndexOf(array, value, array.Length - 1 + lb, array.Length); + } + + // Returns the index of the last occurrence of a given value in a range of + // an array. The array is searched backwards, starting at index + // startIndex and ending at index 0. The elements of the array are + // compared to the given value using the Object.Equals method. + // + //| + public static int LastIndexOf(Array array, Object value, int startIndex) { + if (array == null) + throw new ArgumentNullException("array"); + int lb = array.GetLowerBound(0); + return LastIndexOf(array, value, startIndex, startIndex + 1 - lb); + } + + // Returns the index of the last occurrence of a given value in a range of + // an array. The array is searched backwards, starting at index + // startIndex and counting uptocount elements. The elements of + // the array are compared to the given value using the Object.Equals + // method. + // + //| + public static int LastIndexOf(Array array, Object value, int startIndex, int count) { + if (array == null) + throw new ArgumentNullException("array"); + if (array.Length == 0) { + return -1; + } + int lb = array.GetLowerBound(0); + if (startIndex < lb || startIndex >= array.Length + lb) + throw new ArgumentOutOfRangeException("startIndex", "ArgumentOutOfRange_Index"); + if (count < 0) + throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_Count"); + if (count > startIndex - lb + 1) + throw new ArgumentOutOfRangeException("endIndex", "ArgumentOutOfRange_EndIndexStartIndex"); + if (array.Rank != 1) + throw new RankException("Rank_MultiDimNotSupported"); + + // Try calling a quick native method to handle primitive types. + int retVal; + int r = TrySZLastIndexOf(array, startIndex, count, value, out retVal); + if (r != 0) + return retVal; + + Object[] objArray = array as Object[]; + int endIndex = startIndex - count + 1; + if (objArray != null) { + if (value == null) { + for (int i = startIndex; i >= endIndex; i--) { + if (objArray[i] == null) return i; + } + } + else { + for (int i = startIndex; i >= endIndex; i--) { + Object obj = objArray[i]; + if (obj != null && value.Equals(obj)) return i; + } + } + } + else { + // This is an array of value classes + Debug.Assert(array.GetType().GetElementType().IsValueType, "array.GetType().GetUnderlyingType().IsValueType"); + if (value == null) + return -1; + for (int i = startIndex; i >= endIndex; i--) { + Object obj = array.GetValue(i); + if (obj != null && value.Equals(obj)) return i; + } + } + return lb-1; // Return lb-1 for arrays with negative lower bounds. + } + + private static int TrySZLastIndexOf(Array sourceArray, int sourceIndex, int count, Object value, out int retVal) { + // BUGBUG: was fast native primitive type method + // returning false (0) means we couldn't handle it + retVal = -1; + return 0; + } + + // Reverses all elements of the given array. Following a call to this + // method, an element previously located at index i will now be + // located at index length - i - 1, where length is the + // length of the array. + // + //| + public static void Reverse(Array array) { + if (array == null) + throw new ArgumentNullException("array", "ArgumentNull_Array"); + Reverse(array, array.GetLowerBound(0), array.Length); + } + + // Reverses the elements in a range of an array. Following a call to this + // method, an element in the range given by index and count + // which was previously located at index i will now be located at + // index index + (index + count - i - 1). + // + //| + public static void Reverse(Array array, int index, int length) { + if (array == null) + throw new ArgumentNullException("array"); + if (index < array.GetLowerBound(0) || length < 0) + throw new ArgumentOutOfRangeException((index<0 ? "index" : "length"), "ArgumentOutOfRange_NeedNonNegNum"); + if (array.Length - (index - array.GetLowerBound(0)) < length) + throw new ArgumentException("Argument_InvalidOffLen"); + if (array.Rank != 1) + throw new RankException("Rank_MultiDimNotSupported"); + + bool r = TrySZReverse(array, index, length); + if (r) + return; + + int i = index; + int j = index + length - 1; + Object[] objArray = array as Object[]; + if (objArray != null) { + while (i < j) { + Object temp = objArray[i]; + objArray[i] = objArray[j]; + objArray[j] = temp; + i++; + j--; + } + } + else { + while (i < j) { + Object temp = array.GetValue(i); + array.SetValue(array.GetValue(j), i); + array.SetValue(temp, j); + i++; + j--; + } + } + } + + private static bool TrySZReverse(Array array, int index, int count) { + // BUGBUG: was fast native primitive type method + // returning false means we couldn't handle it + return false; + } + + // Sorts the elements of an array. The sort compares the elements to each + // other using the IComparable interface, which must be implemented + // by all elements of the array. + // + //| + public static void Sort(Array array) { + if (array == null) + throw new ArgumentNullException("array", "ArgumentNull_Array"); + Sort(array, null, array.GetLowerBound(0), array.Length, null); + } + + // Sorts the elements of two arrays based on the keys in the first array. + // Elements in the keys array specify the sort keys for + // corresponding elements in the items array. The sort compares the + // keys to each other using the IComparable interface, which must be + // implemented by all elements of the keys array. + // + //| + public static void Sort(Array keys, Array items) { + if (keys == null) + throw new ArgumentNullException("keys"); + Sort(keys, items, keys.GetLowerBound(0), keys.Length, null); + } + + // Sorts the elements in a section of an array. The sort compares the + // elements to each other using the IComparable interface, which + // must be implemented by all elements in the given section of the array. + // + //| + public static void Sort(Array array, int index, int length) { + Sort(array, null, index, length, null); + } + + // Sorts the elements in a section of two arrays based on the keys in the + // first array. Elements in the keys array specify the sort keys for + // corresponding elements in the items array. The sort compares the + // keys to each other using the IComparable interface, which must be + // implemented by all elements of the keys array. + // + //| + public static void Sort(Array keys, Array items, int index, int length) { + Sort(keys, items, index, length, null); + } + + // Sorts the elements of an array. The sort compares the elements to each + // other using the given IComparer interface. If comparer is + // null, the elements are compared to each other using the + // IComparable interface, which in that case must be implemented by + // all elements of the array. + // + //| + public static void Sort(Array array, IComparer comparer) { + if (array == null) + throw new ArgumentNullException("array", "ArgumentNull_Array"); + Sort(array, null, array.GetLowerBound(0), array.Length, comparer); + } + + // Sorts the elements of two arrays based on the keys in the first array. + // Elements in the keys array specify the sort keys for + // corresponding elements in the items array. The sort compares the + // keys to each other using the given IComparer interface. If + // comparer is null, the elements are compared to each other using + // the IComparable interface, which in that case must be implemented + // by all elements of the keys array. + // + //| + public static void Sort(Array keys, Array items, IComparer comparer) { + if (keys == null) + throw new ArgumentNullException("keys"); + Sort(keys, items, keys.GetLowerBound(0), keys.Length, comparer); + } + + // Sorts the elements in a section of an array. The sort compares the + // elements to each other using the given IComparer interface. If + // comparer is null, the elements are compared to each other using + // the IComparable interface, which in that case must be implemented + // by all elements in the given section of the array. + // + //| + public static void Sort(Array array, int index, int length, IComparer comparer) { + Sort(array, null, index, length, comparer); + } + + // Sorts the elements in a section of two arrays based on the keys in the + // first array. Elements in the keys array specify the sort keys for + // corresponding elements in the items array. The sort compares the + // keys to each other using the given IComparer interface. If + // comparer is null, the elements are compared to each other using + // the IComparable interface, which in that case must be implemented + // by all elements of the given section of the keys array. + // + //| + public static void Sort(Array keys, Array items, int index, int length, IComparer comparer) { + if (keys == null) + throw new ArgumentNullException("keys"); + if (keys.Rank != 1 || (items != null && items.Rank != 1)) + throw new RankException("Rank_MultiDimNotSupported"); + if (items != null && keys.GetLowerBound(0) != items.GetLowerBound(0)) + throw new ArgumentException("Arg_LowerBoundsMustMatch"); + if (index < keys.GetLowerBound(0) || length < 0) + throw new ArgumentOutOfRangeException((length<0 ? "length" : "index"), "ArgumentOutOfRange_NeedNonNegNum"); + if (keys.Length - (index + keys.GetLowerBound(0)) < length || (items != null && index > items.Length - length)) + throw new ArgumentException("Argument_InvalidOffLen"); + + + + if (length > 1) { + if (comparer == Comparer.Default || comparer == null) { + int r = TrySZSort(keys, items, index, index + length - 1); + if (r != 0) + return; + } + + Object[] objKeys = keys as Object[]; + Object[] objItems = null; + if (objKeys != null) + objItems = items as Object[]; + if (objKeys != null && (items == null || objItems != null)) { + SorterObjectArray sorter = new SorterObjectArray(objKeys, objItems, comparer); + sorter.QuickSort(index, index + length - 1); + } + else { + SorterGenericArray sorter = new SorterGenericArray(keys, items, comparer); + sorter.QuickSort(index, index + length - 1); + } + } + } + + private static int TrySZSort(Array keys, Array items, int left, int right) { + // BUGBUG: was fast native primitive type method + // returning false (0) means we couldn't handle it + return 0; + } + + // Private class used by the Sort methods. + private class SorterObjectArray + { + private Object[] keys; + private Object[] items; + private IComparer comparer; + + + public SorterObjectArray(Object[] keys, Object[] items, IComparer comparer) { + if (comparer == null) comparer = Comparer.Default; + this.keys = keys; + this.items = items; + this.comparer = comparer; + } + + public virtual void QuickSort(int left, int right) { + // Can use the much faster jit helpers for array access. + do { + int i = left; + int j = right; + Object x = keys[(i + j) >> 1]; + do { + // Add a try block here to detect IComparers (or their + // underlying IComparables, etc) that are bogus. + try { + while (comparer.Compare(keys[i], x) < 0) i++; + while (comparer.Compare(x, keys[j]) < 0) j--; + } + catch (IndexOutOfRangeException) { + throw new ArgumentException(String.Format("Arg_BogusIComparer", x, x.GetType().Name, comparer)); + } + catch (Exception e) { + throw new InvalidOperationException("InvalidOperation_IComparerFailed", e); + } + Debug.Assert(i>=left && j<=right, "(i>=left && j<=right) Sort failed - Is your IComparer bogus?"); + if (i > j) break; + if (i < j) { + Object key = keys[i]; + keys[i] = keys[j]; + keys[j] = key; + if (items != null) { + Object item = items[i]; + items[i] = items[j]; + items[j] = item; + } + } + i++; + j--; + } while (i <= j); + if (j - left <= right - i) { + if (left < j) QuickSort(left, j); + left = i; + } + else { + if (i < right) QuickSort(i, right); + right = j; + } + } while (left < right); + } + } + + // Private class used by the Sort methods for instances of System.Array. + // This is slower than the one for Object[], since we can't use the JIT helpers + // to access the elements. We must use GetValue & SetValue. + private class SorterGenericArray + { + private Array keys; + private Array items; + private IComparer comparer; + + public SorterGenericArray(Array keys, Array items, IComparer comparer) { + if (comparer == null) comparer = Comparer.Default; + this.keys = keys; + this.items = items; + this.comparer = comparer; + } + + public virtual void QuickSort(int left, int right) { + // Must use slow Array accessors (GetValue & SetValue) + do { + int i = left; + int j = right; + Object x = keys.GetValue((i + j) >> 1); + do { + // Add a try block here to detect IComparers (or their + // underlying IComparables, etc) that are bogus. + try { + while (comparer.Compare(keys.GetValue(i), x) < 0) i++; + while (comparer.Compare(x, keys.GetValue(j)) < 0) j--; + } + catch (IndexOutOfRangeException) { + throw new ArgumentException(String.Format("Arg_BogusIComparer", x, x.GetType().Name, comparer)); + } + catch (Exception e) { + throw new InvalidOperationException("InvalidOperation_IComparerFailed", e); + } + Debug.Assert(i>=left && j<=right, "(i>=left && j<=right) Sort failed - Is your IComparer bogus?"); + if (i > j) break; + if (i < j) { + Object key = keys.GetValue(i); + keys.SetValue(keys.GetValue(j), i); + keys.SetValue(key, j); + if (items != null) { + Object item = items.GetValue(i); + items.SetValue(items.GetValue(j), i); + items.SetValue(item, j); + } + } + i++; + j--; + } while (i <= j); + if (j - left <= right - i) { + if (left < j) QuickSort(left, j); + left = i; + } + else { + if (i < right) QuickSort(i, right); + right = j; + } + } while (left < right); + } + } + + private class SZArrayEnumerator : IEnumerator, ICloneable + { + private Array _array; + private int _index; + private int _endIndex; // cache array length, since it's a little slow. + + internal SZArrayEnumerator(Array array) { + Debug.Assert(array.Rank == 1 && array.GetLowerBound(0) == 0, "SZArrayEnumerator only works on single dimension arrays w/ a lower bound of zero."); + _array = array; + _index = -1; + _endIndex = array.Length; + } + + public virtual Object Clone() + { + return MemberwiseClone(); + } + + public virtual bool MoveNext() { + if (_index < _endIndex) { + _index++; + return (_index < _endIndex); + } + return false; + } + + public virtual Object Current { + get { + if (_index < 0) throw new InvalidOperationException("InvalidOperation_EnumNotStarted"); + if (_index >= _endIndex) throw new InvalidOperationException("InvalidOperation_EnumEnded"); + return _array.GetValue(_index); + } + } + + public virtual void Reset() { + _index = -1; + } + } + + private class ArrayEnumerator : IEnumerator, ICloneable + { + private Array array; + private int index; + private int endIndex; + private int startIndex; // Save for Reset. + private int[] _indices; // The current position in a multidimensional array + private bool _complete; + + internal ArrayEnumerator(Array array, int index, int count) { + this.array = array; + this.index = index - 1; + startIndex = index; + endIndex = index + count; + _indices = new int[array.Rank]; + int checkForZero = 1; // Check for dimensions of size 0. + for (int i = 0; i < array.Rank; i++) { + _indices[i] = array.GetLowerBound(i); + checkForZero *= array.GetLength(i); + } + // To make MoveNext simpler, decrement least significant index. + _indices[_indices.Length-1]--; + _complete = (checkForZero == 0); + } + + private void IncArray() { + // This method advances us to the next valid array index, + // handling all the multiple dimension & bounds correctly. + // Think of it like an odometer in your car - we start with + // the last digit, increment it, and check for rollover. If + // it rolls over, we set all digits to the right and including + // the current to the appropriate lower bound. Do these overflow + // checks for each dimension, and if the most significant digit + // has rolled over its upper bound, we're done. + // + // @TODO: Figure out if this slower and/or more complex than + // sticking a private method on Array to access it as a flat + // structure (ie, reference from 0 to length, ignoring bounds & + // dimensions). + int rank = array.Rank; + _indices[rank-1]++; + for (int dim = rank - 1; dim >= 0; dim--) { + if (_indices[dim] > array.GetUpperBound(dim)) { + if (dim == 0) { + _complete = true; + break; + } + for (int j = dim; j < rank; j++) + _indices[j] = array.GetLowerBound(j); + _indices[dim-1]++; + } + } + } + + public virtual Object Clone() + { + return MemberwiseClone(); + } + + public virtual bool MoveNext() { + if (_complete) { + index = endIndex; + return false; + } + index++; + IncArray(); + return !_complete; + } + + public virtual Object Current { + get { + if (index < startIndex) throw new InvalidOperationException("InvalidOperation_EnumNotStarted"); + if (_complete) throw new InvalidOperationException("InvalidOperation_EnumEnded"); + return array.GetValue(_indices); + } + } + + public virtual void Reset() { + index = startIndex - 1; + int checkForZero = 1; + for (int i = 0; i < array.Rank; i++) { + _indices[i] = array.GetLowerBound(i); + checkForZero *= array.GetLength(i); + } + _complete = (checkForZero == 0); + // To make MoveNext simpler, decrement least significant index. + _indices[_indices.Length-1]--; + } + } + + + // if this is an array of value classes and that value class has a default constructor + // then this calls this default constructor on every element in the value class array. + // otherwise this is a no-op. Generally this method is called automatically by the compiler + //| + public unsafe void Initialize() + { + // if it is an array of struct, we need to call the default + // constructor for the struct + if (this.vtable.arrayOf == StructuralType.Struct) { + RuntimeType runtimeType = this.vtable.arrayElementClass.vtableType; + UIntPtr fn = runtimeType.ctor; + if (fn == UIntPtr.Zero) + return; + + int total = 0; + for (int i = 0; i < Rank; i++) { + total += GetLength(i); + } + fixed(int *srcField = &this.field1) { + UIntPtr elementAddress = + new UIntPtr(this.GetFirstElementAddress(srcField)); + UIntPtr elementSize = + new UIntPtr(this.vtable.arrayElementSize); + for (int i = 0; i < total; i++) { + Magic.calli(runtimeType.ctor, elementAddress); + elementAddress = elementAddress + elementSize; + } + } + } + } + + private int RankRectangleArray { + get { + return field1; + } + } + private int Length0RectangleArray { + get { + return field4; + } + } + // PRE: this fixed + // REVIEW: return type + internal unsafe void* GetFirstDimInfoRectangleArray() { + fixed(int* ptr = &field3) { + return (void*)ptr; + } + } + + private unsafe int GetLowerBoundRectangleArray(int dimension) { + fixed(int* ptr = &field3) { + return ptr[2*dimension]; + } + } + + private int GetLengthVector() { + return field1; + } + + private unsafe int GetLengthRectangleArray(int dimension) { + fixed(int* ptr = &field3) { + return ptr[2*dimension+1]; + } + } + + private unsafe int GetLengthRectangleArray() { + return field2; + } + + // PRE: this fixed + // REVIEW: return type + [NoHeapAllocation] + internal unsafe void* GetFirstElementAddress(int *field1Addr) { + return (void*)(((byte*)field1Addr) + this.vtable.baseLength - + (PreHeader.Size + PostHeader.Size)); + } + + // Calculates the offset from the first element of the array to the + // desired element. This offset is a count -- that is, it does not take + // the size of the elements into account (e.g. int_array[2] would lead + // to 2, not 2*sizeof(int) = 8). + private unsafe int InternalGetOffset(int[] indices) { + if (IsVector) { + return indices[0]; + } + else { + int rank = RankRectangleArray; + int dwOffset = 0; + int dwMultiplier = 1; + + fixed(int* ptr = &field3) { + for (int i = rank - 1; i >= 0; i--) { + int dwIndex = indices[i] - ptr[2*i]; + + if (dwIndex >= ptr[2*i + 1]) { + throw new IndexOutOfRangeException(); + } + + dwOffset += dwIndex * dwMultiplier; + dwMultiplier *= ptr[2*i+1]; + } + return dwOffset; + } + } + } + + // See comment by the other InternalGetOffset + private unsafe int InternalGetOffset(int index1, int index2, + int index3) { + if (IsVector) { + VTable.Assert(index2 == 0); + VTable.Assert(index3 == 0); + return index1; + } + else { + int rank = RankRectangleArray; + int dwOffset = 0; + int dwMultiplier = 1; + + VTable.Assert(rank <= 3); + + fixed(int* ptr = &field3) { + for (int i = rank - 1; i >= 0; i--) { + int dwIndex; + if (i == 2) { + dwIndex = index3 - ptr[2*i]; + } + else if(i == 1) { + dwIndex = index2 - ptr[2*i]; + } + else { + dwIndex = index1 - ptr[2*i]; + } + + if (dwIndex >= ptr[2*i + 1]) { + throw new IndexOutOfRangeException(); + } + dwOffset += dwIndex * dwMultiplier; + dwMultiplier *= ptr[2*i+1]; + } + return dwOffset; + } + } + } + + // Given an array (this), a pointer to its first element (EA), and + // the offset (count of elements -- see InternalGetOffset comment for + // what this means), compute the desired element's address, get the + // value, box it if necessary, and return it + // PRE: 'this' fixed + private unsafe Object InternalGetValueHelper(byte *EA, int dwOffset) { + int arrayElementSize = this.vtable.arrayElementSize; + EA += dwOffset * arrayElementSize; + + // now we have the address of the array element + // if it's already an object, return it + if (this.vtable.arrayOf == StructuralType.Reference) { + return Magic.fromAddress(*(UIntPtr*)EA); + } + + // BUGBUG: we won't have the enum type anywhere! + + Object result; + switch (this.vtable.arrayOf) { + case StructuralType.Struct: + // possible GC point -- but 'this' is fixed so EA won't get + // invalidated + + Thread thread = Thread.CurrentThread; + result = GC.AllocateObject(vtable.arrayElementClass); + + // HACK: depends on value being at same spot in all value types + // REVIEW: result is not fixed + UIntPtr dst = Magic.addressOf(result) + PostHeader.Size; + VTable arrayElementClass = this.vtable.arrayElementClass; +#if REFERENCE_COUNTING_GC + UIntPtr objAddr = (UIntPtr)(EA-PostHeader.Size); + GCs.ReferenceCountingCollector. + IncrementReferentRefCounts(objAddr, arrayElementClass); + System.IO.__UnmanagedMemoryStream.memcpyimpl(EA, (byte *) dst, + arrayElementSize); +#elif DEFERRED_REFERENCE_COUNTING_GC + UIntPtr objAddr = + (UIntPtr)(EA-Object.VTABLE_OFFSET-UIntPtr.Size); + GCs.DeferredReferenceCountingCollector. + IncrementReferentRefCounts(objAddr, arrayElementClass); + System.IO.__UnmanagedMemoryStream.memcpyimpl(EA, (byte *) dst, + arrayElementSize); +#else + Barrier.CopyStruct(this, result, arrayElementClass, + (UIntPtr) EA, dst); +#endif // REFERENCE_COUNTING_GC + break; + case StructuralType.Bool: + result = *(bool*)EA; + break; + case StructuralType.Char: + result = *(char*)EA; + break; + case StructuralType.Int8: + result = *(sbyte*)EA; + break; + case StructuralType.Int16: + result = *(short*)EA; + break; + case StructuralType.Int32: + result = *(int*)EA; + break; + case StructuralType.Int64: + result = *(long*)EA; + break; + case StructuralType.UInt8: + result = *(byte*)EA; + break; + case StructuralType.UInt16: + result = *(ushort*)EA; + break; + case StructuralType.UInt32: + result = *(uint*)EA; + break; + case StructuralType.UInt64: + result = *(ulong*)EA; + break; + case StructuralType.Float32: + result = *(float*)EA; + break; + case StructuralType.Float64: + result = *(double*)EA; + break; + default: + VTable.Assert(false, "unreachable array get box"); + return null; + } + return result; + } + + // Given an array (this), a pointer to its first element (EA), and + // the offset (count of elements -- see m_InternalGetOffset comment for + // what this means), compute the desired element's address, typecheck + // the value against the actual array type, unbox it if necessary, and + // put it in the array + // PRE: 'this' fixed + // BUGBUG: need to support widening of primitives + private unsafe void InternalSetValueHelper(byte *EA, int dwOffset, + Object value) { + int arrayElementSize = this.vtable.arrayElementSize; + EA += dwOffset * arrayElementSize; + + // have address to store at, but need to typecheck + if (this.vtable.arrayOf == StructuralType.Reference) { + VTable.checkArrayStore(this, value); + +#if REFERENCE_COUNTING_GC + GCs.ReferenceCountingCollector.IncrementRefCount(value); + Object old = Magic.fromAddress(*(UIntPtr*)EA); + GCs.ReferenceCountingCollector.DecrementRefCount(old); + *(UIntPtr *)EA = Magic.addressOf(value); +#elif DEFERRED_REFERENCE_COUNTING_GC + GCs.DeferredReferenceCountingCollector.IncrementRefCount(value); + Object old = Magic.fromAddress(*(UIntPtr*)EA); + GCs.DeferredReferenceCountingCollector.DecrementRefCount(old); + *(UIntPtr *)EA = Magic.addressOf(value); +#else + Barrier.StoreIndirect((UIntPtr *) EA, value, 0); +#endif // REFERENCE_COUNTING_GC + return; + } + + // BUGBUG: enum type lost + + VTable vtable; + if (this.vtable.arrayOf == StructuralType.Struct) { + vtable = this.vtable.arrayElementClass; + } + else { + // assert primitive + vtable = TypeInfo.arrayOfBox[(int)this.vtable.arrayOf]; + } + + // BUGBUG: need to also widening of primitives + if (vtable != value.vtable) { + throw new InvalidCastException("InvalidCast_StoreArrayElement"); + } + + // HACK: depends on value being at same spot in all value types + // REVIEW: value is not fixed + UIntPtr src = Magic.addressOf(value) + PostHeader.Size; + if (this.vtable.arrayOf == StructuralType.Struct) { +#if REFERENCE_COUNTING_GC + GCs.ReferenceCountingCollector. + IncrementReferentRefCounts(Magic.addressOf(value), vtable); + UIntPtr objAddr = + (UIntPtr)(EA-Object.VTABLE_OFFSET-UIntPtr.Size); + GCs.ReferenceCountingCollector. + DecrementReferentRefCounts(objAddr, vtable); +#elif DEFERRED_REFERENCE_COUNTING_GC + GCs.DeferredReferenceCountingCollector. + IncrementReferentRefCounts(Magic.addressOf(value), vtable); + UIntPtr objAddr = + (UIntPtr)(EA-Object.VTABLE_OFFSET-UIntPtr.Size); + GCs.DeferredReferenceCountingCollector. + DecrementReferentRefCounts(objAddr, vtable); +#else + Barrier.CopyStruct(value, this, vtable, src, (UIntPtr) EA); + return; +#endif // REFERENCE_COUNTING_GC + } + + Buffer.MoveMemory((byte *) EA, (byte *) src, + (UIntPtr) arrayElementSize); + } + + + // This is the core set routines for Get/Set Value. The arguments have + // been verified that they exist before we get to this point. + + // PRE: rank has been checked (e.g. if rank==1, then index2==index3==0) + private unsafe Object InternalGetValue(int index1, int index2, int index3) { + fixed(int *o = &this.field1) { + byte *EA = (byte*) GetFirstElementAddress(o); + int dwOffset = InternalGetOffset(index1, index2, index3); + return InternalGetValueHelper(EA, dwOffset); + } + } + + // PRE: rank has been checked + private unsafe Object InternalGetValueEx(int[] indices) { + fixed(int *o = &this.field1) { + byte *EA = (byte*) GetFirstElementAddress(o); + int dwOffset = InternalGetOffset(indices); + return InternalGetValueHelper(EA, dwOffset); + } + } + + + // PRE: rank has been checked (e.g. if rank==1, then index2==index3==0) + // REVIEW: check ordering of errors (e.g. if you try to store the wrong + // types of object in an array at an illegal offset, what should happen?) + private unsafe void InternalSetValue(Object value, int index1, + int index2, int index3) { + fixed(int *o = &this.field1) { + byte *EA = (byte*) GetFirstElementAddress(o); + int dwOffset = InternalGetOffset(index1, index2, index3); + InternalSetValueHelper(EA, dwOffset, value); + } + } + + private unsafe void InternalSetValueEx(Object value,int[] indices) { + fixed(int *o = &this.field1) { + byte *EA = (byte*) GetFirstElementAddress(o); + int dwOffset = InternalGetOffset(indices); + InternalSetValueHelper(EA, dwOffset, value); + } + } + + [MethodImpl(MethodImplOptions.InternalCall)] + extern private static void UnsafeUpdateArray(Array a,Object value,int index); + [MethodImpl(MethodImplOptions.InternalCall)] + extern private static Object UnsafeReadArray(Array arr,int index); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern Array InternalQuickCreateEx(VTable array_type,int[] lengths,int[] lowerBounds); + + } +} diff --git a/base/Applications/Runtime/Full/System/ArrayTypeMismatchException.cs b/base/Applications/Runtime/Full/System/ArrayTypeMismatchException.cs new file mode 100644 index 0000000..aca4af3 --- /dev/null +++ b/base/Applications/Runtime/Full/System/ArrayTypeMismatchException.cs @@ -0,0 +1,40 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: ArrayTypeMismatchException +// +// Purpose: The arrays are of different primitive types. +// +//============================================================================= + +namespace System +{ + + using System; + using System.Runtime.CompilerServices; + + // The ArrayMismatchException is thrown when an attempt to store + // an object of the wrong type within an array occurs. + // + //| + public partial class ArrayTypeMismatchException : SystemException { + + // Creates a new ArrayMismatchException with its message string set to + // message, its HRESULT set to COR_E_ARRAYTYPEMISMATCH, + // and its ExceptionInfo reference set to null. + // + //| + public ArrayTypeMismatchException(String message) + : base(message) { + } + + //| + public ArrayTypeMismatchException(String message, Exception innerException) + : base(message, innerException) { + } + } +} diff --git a/base/Applications/Runtime/Full/System/Attribute.cs b/base/Applications/Runtime/Full/System/Attribute.cs new file mode 100644 index 0000000..2dc1460 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Attribute.cs @@ -0,0 +1,30 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================ +// +// Class: Attribute +// +// Purpose: The class used as an attribute to denote that +// another class can be used as an attribute. +// +//=========================================================== +namespace System +{ + + using System; + using System.Reflection; + using System.Collections; + + // This class is a placeholder for the compiler only! + // Singularity and Bartok don't support runtime access to custom attributes. + + //| + [AttributeUsageAttribute(AttributeTargets.All, Inherited = true, AllowMultiple=false)] // Base class for all attributes + public abstract class Attribute { + //| + protected Attribute(){} + } +} diff --git a/base/Kernel/System/AttributeTargets.cs b/base/Applications/Runtime/Full/System/AttributeTargets.cs similarity index 96% rename from base/Kernel/System/AttributeTargets.cs rename to base/Applications/Runtime/Full/System/AttributeTargets.cs index 77dc941..e294721 100644 --- a/base/Kernel/System/AttributeTargets.cs +++ b/base/Applications/Runtime/Full/System/AttributeTargets.cs @@ -5,7 +5,8 @@ // ==--== //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -namespace System { +namespace System +{ using System; diff --git a/base/Kernel/System/AttributeUsageAttribute.cs b/base/Applications/Runtime/Full/System/AttributeUsageAttribute.cs similarity index 78% rename from base/Kernel/System/AttributeUsageAttribute.cs rename to base/Applications/Runtime/Full/System/AttributeUsageAttribute.cs index fb83974..6ecf6af 100644 --- a/base/Kernel/System/AttributeUsageAttribute.cs +++ b/base/Applications/Runtime/Full/System/AttributeUsageAttribute.cs @@ -3,20 +3,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: AttributeUsageAttribute -** -** -** Purpose: The class denotes how to specify the usage of an attribute -** -** Date: December 7, 1999 -** -===========================================================*/ -namespace System { +//============================================================ +// +// Class: AttributeUsageAttribute +// +// Purpose: The class denotes how to specify the usage of an attribute +// +//=========================================================== +namespace System +{ using System.Reflection; - /* By default, attributes are inherited and multiple attributes are not allowed */ + // By default, attributes are inherited and multiple attributes are not allowed //| [AttributeUsage(AttributeTargets.Class, Inherited = true)] public sealed class AttributeUsageAttribute : Attribute diff --git a/base/Applications/Runtime/Full/System/BitConverter.cs b/base/Applications/Runtime/Full/System/BitConverter.cs new file mode 100644 index 0000000..18081e7 --- /dev/null +++ b/base/Applications/Runtime/Full/System/BitConverter.cs @@ -0,0 +1,544 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================ +// +// Class: BitConverter +// +// Purpose: Allows developers to view the base data types as +// an arbitrary array of bits. +// +//=========================================================== +namespace System +{ + + using System; + using System.Runtime.InteropServices; + using System.Runtime.CompilerServices; + + // The BitConverter class contains methods for + // converting an array of bytes to one of the base data + // types, as well as for converting a base data type to an + // array of bytes. + // + //| + + [CCtorIsRunDuringStartup] + public sealed class BitConverter { + + // This field indicates the "endianness" of the architecture. + // The value is set to true if the architecture is + // little endian; false if it is big endian. + //| + public static readonly bool IsLittleEndian; + + static BitConverter() { + byte [] ba = GetBytes((short)0xF); + if (ba[0] == 0xF) { + IsLittleEndian=true; + } + else { + IsLittleEndian=false; + } + } + + // This class only contains static methods and may not be instantiated. + private BitConverter() { + } + + // ====================================================================== + // Convert from primitive types to byte array slices. These functions + // do not perform memory allocation and so are suitable for use in + // interrupt handlers and other no-allocation-allowed contexts. -- mbj + // ====================================================================== + + // Converts a byte into an existing array slice of bytes with length one. + public static void GetBytes(bool value, byte[] array, int startIndex) { + const int bytesNeeded = 1; + if (startIndex < 0) { + throw new ArgumentOutOfRangeException("startIndex is negative"); + } + if (startIndex + bytesNeeded > array.Length) { + throw new ArgumentException("Array is too small"); + } + array[startIndex] = (value ? (byte)Boolean.True : (byte)Boolean.False ); + } + + // Converts a char into an existing array slice of bytes with length two. + public static unsafe void GetBytes(char value, byte[] array, int startIndex) { + const int bytesNeeded = 2; + if (startIndex < 0) { + throw new ArgumentOutOfRangeException("startIndex is negative"); + } + if (startIndex + bytesNeeded > array.Length) { + throw new ArgumentException("Array is too small"); + } + fixed (byte *ptr = &array[startIndex]) { + *((char *) ptr) = value; + } + } + + // Converts a short into an existing array slice of bytes with length two. + public static unsafe void GetBytes(short value, byte[] array, int startIndex) { + const int bytesNeeded = 2; + if (startIndex < 0) { + throw new ArgumentOutOfRangeException("startIndex is negative"); + } + if (startIndex + bytesNeeded > array.Length) { + throw new ArgumentException("Array is too small"); + } + fixed (byte *ptr = &array[startIndex]) { + *((short *) ptr) = value; + } + } + + // Converts an int into an existing array slice of bytes with length four. + public static unsafe void GetBytes(int value, byte[] array, int startIndex) { + const int bytesNeeded = 4; + if (startIndex < 0) { + throw new ArgumentOutOfRangeException("startIndex is negative"); + } + if (startIndex + bytesNeeded > array.Length) { + throw new ArgumentException("Array is too small"); + } + fixed (byte *ptr = &array[startIndex]) { + *((int *) ptr) = value; + } + } + + // Converts a long into an existing array slice of bytes with length eight. + public static unsafe void GetBytes(long value, byte[] array, int startIndex) { + const int bytesNeeded = 8; + if (startIndex < 0) { + throw new ArgumentOutOfRangeException("startIndex is negative"); + } + if (startIndex + bytesNeeded > array.Length) { + throw new ArgumentException("Array is too small"); + } + fixed (byte *ptr = &array[startIndex]) { + *((long *) ptr) = value; + } + } + + // Converts an ushort into an existing array slice of bytes with length two. + [CLSCompliant(false)] + public static unsafe void GetBytes(ushort value, byte[] array, int startIndex) { + const int bytesNeeded = 2; + if (startIndex < 0) { + throw new ArgumentOutOfRangeException("startIndex is negative"); + } + if (startIndex + bytesNeeded > array.Length) { + throw new ArgumentException("Array is too small"); + } + fixed (byte *ptr = &array[startIndex]) + { + *((ushort *) ptr) = value; + } + } + + // Converts an uint into an existing array slice of bytes with length four. + [CLSCompliant(false)] + public static unsafe void GetBytes(uint value, byte[] array, int startIndex) { + const int bytesNeeded = 4; + if (startIndex < 0) { + throw new ArgumentOutOfRangeException("startIndex is negative"); + } + if (startIndex + bytesNeeded > array.Length) { + throw new ArgumentException("Array is too small"); + } + fixed (byte *ptr = &array[startIndex]) + { + *((uint *) ptr) = value; + } + } + + // Converts an unsigned long into an existing array slice of bytes with length eight. + [CLSCompliant(false)] + public static unsafe void GetBytes(ulong value, byte[] array, int startIndex) { + const int bytesNeeded = 8; + if (startIndex < 0) { + throw new ArgumentOutOfRangeException("startIndex is negative"); + } + if (startIndex + bytesNeeded > array.Length) { + throw new ArgumentException("Array is too small"); + } + fixed (byte *ptr = &array[startIndex]) + { + *((ulong *) ptr) = value; + } + } + + // Converts a float into an existing array slice of bytes with length four. + public unsafe static void GetBytes(float value, byte[] array, int startIndex) + { + const int bytesNeeded = 4; + if (startIndex < 0) { + throw new ArgumentOutOfRangeException("startIndex is negative"); + } + if (startIndex + bytesNeeded > array.Length) { + throw new ArgumentException("Array is too small"); + } + fixed (byte *ptr = &array[startIndex]) + *((float*) ptr) = value; + } + + // Converts a double into an existing array slice of bytes with length eight. + public unsafe static void GetBytes(double value, byte[] array, int startIndex) + { + const int bytesNeeded = 8; + if (startIndex < 0) { + throw new ArgumentOutOfRangeException("startIndex is negative"); + } + if (startIndex + bytesNeeded > array.Length) { + throw new ArgumentException("Array is too small"); + } + fixed (byte *ptr = &array[startIndex]) + *((double*) ptr) = value; + } + + // ====================================================================== + // Convert from primitive types to new byte arrays. These functions + // perform memory allocation and so are unsuitable for use in + // interrupt handlers and other no-allocation-allowed contexts. -- mbj + // ====================================================================== + + // Converts a byte into an array of bytes with length one. + //| + public static byte[] GetBytes(bool value) { + byte[] result = new byte[1]; + GetBytes(value, result, 0); + return result; + } + + // Converts a char into an array of bytes with length two. + //| + public static unsafe byte[] GetBytes(char value) { + // See also Lightning\Src\VM\COMUtilNative.cpp::CharToBytes + byte[] result = new byte[2]; + GetBytes(value, result, 0); + return result; + } + + // Converts a short into an array of bytes with length + // two. + //| + public static unsafe byte[] GetBytes(short value) { + // See also Lightning\Src\VM\COMUtilNative.cpp::I2ToBytes + byte[] result = new byte[2]; + GetBytes(value, result, 0); + return result; + } + + // Converts an int into an array of bytes with length + // four. + //| + public static unsafe byte[] GetBytes(int value) { + // See also Lightning\Src\VM\COMUtilNative.cpp::I4ToBytes + byte[] result = new byte[4]; + GetBytes(value, result, 0); + return result; + } + + // Converts a long into an array of bytes with length + // eight. + //| + public static unsafe byte[] GetBytes(long value) { + // See also Lightning\Src\VM\COMUtilNative.cpp::I8ToBytes + byte[] result = new byte[8]; + GetBytes(value, result, 0); + return result; + } + + // Converts an ushort into an array of bytes with + // length two. + //| + [CLSCompliant(false)] + public static unsafe byte[] GetBytes(ushort value) { + // See also Lightning\Src\VM\COMUtilNative.cpp::U2ToBytes + byte[] result = new byte[2]; + GetBytes(value, result, 0); + return result; + } + + // Converts an uint into an array of bytes with + // length four. + //| + [CLSCompliant(false)] + public static unsafe byte[] GetBytes(uint value) { + // See also Lightning\Src\VM\COMUtilNative.cpp::U4ToBytes + byte[] result = new byte[4]; + GetBytes(value, result, 0); + return result; + } + + // Converts an unsigned long into an array of bytes with + // length eight. + //| + [CLSCompliant(false)] + public static unsafe byte[] GetBytes(ulong value) { + // See also Lightning\Src\VM\COMUtilNative.cpp::U8ToBytes + byte[] result = new byte[8]; + GetBytes(value, result, 0); + return result; + } + + // Converts a float into an array of bytes with length + // four. + //| + public unsafe static byte[] GetBytes(float value) + { + byte[] result = new byte[4]; + GetBytes(value, result, 0); + return result; + } + + // Converts a double into an array of bytes with length + // eight. + //| + public unsafe static byte[] GetBytes(double value) + { + byte[] result = new byte[8]; + GetBytes(value, result, 0); + return result; + } + + // ============ Convert from byte array slices to primitive types ============ + + // Converts an array of bytes into a char. + //| + public static unsafe char ToChar(byte[] value, int startIndex) { + // See also Lightning\Src\VM\COMUtilNative.cpp::BytesToChar + char result; + fixed (byte *ptr = value) { + result = *((char *) (ptr + startIndex)); + } + return result; + } + + // Converts an array of bytes into a short. + //| + public static unsafe short ToInt16(byte[] value, int startIndex) + { + if (value == null) { + throw new ArgumentNullException("value"); + } + if (((uint)startIndex) >= (uint)value.Length) { + throw new ArgumentOutOfRangeException("startIndex"); + } + if (startIndex > (value.Length - 2)) { + throw new ArgumentException("startIndex"); + } + fixed (byte* p = &value[startIndex]) { + if ((startIndex % 2) == 0) { + return *((short*)p); + } + if (IsLittleEndian) { + return (short)(p[0] | (p[1] << 8)); + } + else { + return (short)(p[1] | (p[0] << 8)); + } + } + } + + // Converts an array of bytes into an int. + //| + public static unsafe int ToInt32(byte[]value, int startIndex) + { + if (value == null) { + throw new ArgumentNullException("value"); + } + if (((uint)startIndex) >= (uint)value.Length) { + throw new ArgumentOutOfRangeException("startIndex"); + } + if (startIndex > (value.Length - 4)) { + throw new ArgumentException("startIndex"); + } + fixed (byte* p = &value[startIndex]) { + if ((startIndex % 4) == 0) { + return *((int*)p); + } + if (IsLittleEndian) { + return ( + ((int)p[0]) | + (((int)p[1]) << 8) | + (((int)p[2]) << 16) | + (((int)p[3]) << 24) + ); + } + else { + return ( + ((int)p[3]) | + (((int)p[2]) << 8) | + (((int)p[1]) << 16) | + (((int)p[0]) << 24) + ); + } + } + } + + // Converts an array of bytes into a long. + //| + public static unsafe long ToInt64(byte[] value, int startIndex) + { + if (value == null) { + throw new ArgumentNullException("value"); + } + if (((uint)startIndex) >= (uint)value.Length) { + throw new ArgumentOutOfRangeException("startIndex"); + } + if (startIndex > (value.Length - 8)) { + throw new ArgumentException("startIndex"); + } + fixed (byte* p = &value[startIndex]) { + if ((startIndex % 8) == 0) { + return *((long*)p); + } + if (IsLittleEndian) { + int lo = (((int)p[0]) | + (((int)p[1]) << 8) | + (((int)p[2]) << 16) | + (((int)p[3]) << 24) + ); + int hi = (((int)p[4]) | + (((int)p[5]) << 8) | + (((int)p[6]) << 16) | + (((int)p[7]) << 24) + ); + return (long)(((ulong)lo) | ((ulong)hi) << 32); + } + else { + int hi = (((int)p[3]) | + (((int)p[2]) << 8) | + (((int)p[1]) << 16) | + (((int)p[0]) << 24) + ); + int lo = (((int)p[7]) | + (((int)p[6]) << 8) | + (((int)p[5]) << 16) | + (((int)p[4]) << 24) + ); + return (long)(((ulong)lo) | ((ulong)hi) << 32); + } + } + } + + // Converts an array of bytes into an ushort. + // + //| + [CLSCompliant(false)] + public static ushort ToUInt16(byte[] value, int startIndex) + { + unchecked { + return (ushort)ToInt16(value, startIndex); + } + } + + // Converts an array of bytes into an uint. + // + //| + [CLSCompliant(false)] + public static uint ToUInt32(byte[] value, int startIndex) + { + unchecked { + return (uint)ToInt32(value, startIndex); + } + } + + // Converts an array of bytes into an unsigned long. + // + //| + [CLSCompliant(false)] + public static unsafe ulong ToUInt64(byte[] value, int startIndex) + { + unchecked { + return (ulong)ToInt64(value, startIndex); + } + } + + // Converts an array of bytes into a float. + //| + public static unsafe float ToSingle(byte[]value, int startIndex) { + // See also Lightning\Src\VM\COMUtilNative.cpp::BytesToR4 + float result; + fixed (byte *ptr = value) { + result = *((float *) (ptr + startIndex)); + } + return result; + } + + // Converts an array of bytes into a double. + //| + public static unsafe double ToDouble(byte []value, int startIndex) { + // See also Lightning\Src\VM\COMUtilNative.cpp::BytesToR8 + double result; + fixed (byte *ptr = value) { + result = *((double *) (ptr + startIndex)); + } + return result; + } + + //==================================ToBoolean=================================== + //Action: Convert an array of bytes to a boolean value. We treat this array + // as if the first 4 bytes were an Int4 an operate on this value. + //Returns: True if the Int4 value of the first 4 bytes is non-zero. + //Arguments: value -- The byte array + // startIndex -- The position within the array. + //Exceptions: See ToInt4. + //============================================================================== + // Converts an array of bytes into a boolean. + //| + public static bool ToBoolean(byte[]value, int startIndex) { + if (value == null) + throw new ArgumentNullException("value"); + if (startIndex < 0) + throw new ArgumentOutOfRangeException("startIndex", "ArgumentOutOfRange_NeedNonNegNum"); + if (startIndex > value.Length - 1) + throw new ArgumentOutOfRangeException("ArgumentOutOfRange_Index"); + + return (value[startIndex]==0)?false:true; + } + + //| + [Inline] + public static unsafe long DoubleToInt64Bits(double value) { + return *((long *)&value); + } + + //| + [Inline] + public static unsafe double Int64BitsToDouble(long value) { + return *((double *)&value); + } + + //| + [Inline] + [CLSCompliant(false)] + public static unsafe ulong DoubleToUInt64Bits(double value) { + return *((ulong *)&value); + } + + //| + [Inline] + [CLSCompliant(false)] + public static unsafe double UInt64BitsToDouble(ulong value) { + return *((double *)&value); + } + + //| + [Inline] + [CLSCompliant(false)] + public static unsafe uint SingleToUInt32Bits(float value) { + return *((uint *)&value); + } + + //| + [Inline] + [CLSCompliant(false)] + public static unsafe float UInt32BitsToSingle(uint value) { + return *((float *)&value); + } + } +} diff --git a/base/Applications/Runtime/Full/System/Boolean.cs b/base/Applications/Runtime/Full/System/Boolean.cs new file mode 100644 index 0000000..03b954c --- /dev/null +++ b/base/Applications/Runtime/Full/System/Boolean.cs @@ -0,0 +1,164 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================ +// +// Class: Boolean +// +// Purpose: The boolean class serves as a wrapper for the primitive +// type boolean. +// +//=========================================================== +namespace System +{ + + using System; + using System.Globalization; + using System.Runtime.CompilerServices; + + // The Boolean class provides the + // object representation of the boolean primitive type. + //| + public struct Boolean : IComparable { + + // + // Member Variables + // + private bool m_value; + + + // + // Public Constants + // + + // The true value. + // + internal const int True = 1; + + // The false value. + // + internal const int False = 0; + + // The string representation of true. + // + //| + public static readonly String TrueString = "True"; + + // The string representation of false. + // + //| + public static readonly String FalseString = "False"; + + // + // Overridden Instance Methods + // + //=================================GetHashCode================================== + //Args: None + //Returns: 1 or 0 depending on whether this instance represents true or false. + //Exceptions: None + //Overridden From: Value + //============================================================================== + // Provides a hash code for this instance. + //| + public override int GetHashCode() { + return (m_value)?True:False; + } + + //===================================ToString=================================== + //Args: None + //Returns: "True" or "False" depending on the state of the boolean. + //Exceptions: None. + //============================================================================== + // Converts the boolean value of this instance to a String. + //| + public override String ToString() { + if (false == m_value) { + return FalseString; + } + return TrueString; + } + + // Determines whether two Boolean objects are equal. + //| + public override bool Equals (Object obj) { + //If it's not a boolean, we're definitely not equal + if (!(obj is Boolean)) { + return false; + } + + return (m_value==((Boolean)obj).m_value); + } + + // Compares this object to another object, returning an integer that + // indicates the relationship. For booleans, false sorts before true. + // null is considered to be less than any instance. + // If object is not of type boolean, this method throws an ArgumentException. + // + // Returns a value less than zero if this object + // + //| + public int CompareTo(Object obj) { + if (obj == null) { + return 1; + } + if (!(obj is Boolean)) { + throw new ArgumentException ("Arg_MustBeBoolean"); + } + + if (m_value ==((Boolean)obj).m_value) { + return 0; + } + else if (m_value == false) { + return -1; + } + return 1; + + } + + // + // Static Methods + // + + // Determines whether a String represents true or false. + // + //| + public static bool Parse(String value) { + if (value==null) throw new ArgumentNullException("value"); + // For perf reasons, let's first see if they're equal, then do the + // trim to get rid of white space, and check again. + if (0 == String.Compare(value, TrueString,true)) + return true; + if (0 == String.Compare(value,FalseString,true)) + return false; + + value = value.Trim(); // Remove leading & trailing white space. + if (0 == String.Compare(value, TrueString,true)) + return true; + if (0 == String.Compare(value,FalseString,true)) + return false; + throw new FormatException("Format_BadBoolean"); + } + + // + // IValue implementation + // + + //| + [NoHeapAllocation] + public override TypeCode GetTypeCode() { + return TypeCode.Boolean; + } + + // + // This is just designed to prevent compiler warnings. + // This field is used from native, but we need to prevent the compiler warnings. + // +#if _DEBUG + private void DontTouchThis() { + m_value = m_value; + } +#endif + } +} diff --git a/base/Applications/Runtime/Full/System/Buffer.cs b/base/Applications/Runtime/Full/System/Buffer.cs new file mode 100644 index 0000000..d690b17 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Buffer.cs @@ -0,0 +1,388 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +// +// Note: These routines assume that the processor supports +// 32-bit aligned accesses. Migration to non-x86 platforms will +// need to examine and tune the implementations accordingly. +// + +namespace System +{ + + using System; + using System.Runtime.CompilerServices; + //| + + public sealed partial class Buffer + { + [NoHeapAllocation] + [Inline] + private static unsafe void WriteHelperByte(byte* dst, byte val, + bool vol) { + if (vol) { + System.Threading.Thread.VolatileWriteUnsafe(dst, val); + } + else { + *dst = val; + } + } + + [NoHeapAllocation] + [Inline] + private static unsafe byte ReadHelperByte(byte* src, bool vol) { + if (vol) { + return System.Threading.Thread.VolatileReadUnsafe(src); + } + else { + return *src; + } + } + + [NoHeapAllocation] + [Inline] + private static unsafe void WriteHelperShort(short* dst, short val, + bool vol) { + if (vol) { + System.Threading.Thread.VolatileWriteUnsafe(dst, val); + } + else { + *dst = val; + } + } + + [NoHeapAllocation] + [Inline] + private static unsafe short ReadHelperShort(short* src, bool vol) { + if (vol) { + return System.Threading.Thread.VolatileReadUnsafe(src); + } + else { + return *src; + } + } + + [NoHeapAllocation] + [Inline] + private static unsafe void WriteHelperInt(int* dst, int val, bool vol) { + if (vol) { + System.Threading.Thread.VolatileWriteUnsafe(dst, val); + } + else { + *dst = val; + } + } + + [NoHeapAllocation] + [Inline] + private static unsafe int ReadHelperInt(int* src, bool vol) { + if (vol) { + return System.Threading.Thread.VolatileReadUnsafe(src); + } + else { + return *src; + } + } + + [NoHeapAllocation] + public static unsafe void MoveMemory(byte* dmem, byte* smem, + UIntPtr size) + { + MoveMemory(dmem, smem, (int)size); + } + + // This is a replacement for the memmove intrinsic. + // It performs better than the CRT one and the inline version + // originally from Lightning\Src\VM\COMSystem.cpp + [NoHeapAllocation] + [Inline] + private static unsafe void MoveMemoryImpl(byte* dmem, byte* smem, + int size, bool vol) { + if (dmem <= smem) { + // take the slow path if the source and dest and co-aligned. + if (((int)smem & 0x3) != ((int)dmem & 0x3)) { + for (; size > 0; size--) { + WriteHelperByte(dmem++, ReadHelperByte(smem++, vol), vol); + } + return; + } + + // make sure the destination is dword aligned + while ((((int)dmem) & 0x3) != 0 && size >= 3) { + WriteHelperByte(dmem++, ReadHelperByte(smem++, vol), vol); + size -= 1; + } + // copy 16 bytes at a time + if (size >= 16) { + size -= 16; + do { + WriteHelperInt(&((int*)dmem)[0], + ReadHelperInt(&((int*)smem)[0], vol), + vol); + WriteHelperInt(&((int*)dmem)[1], + ReadHelperInt(&((int*)smem)[1], vol), + vol); + WriteHelperInt(&((int*)dmem)[2], + ReadHelperInt(&((int*)smem)[2], vol), + vol); + WriteHelperInt(&((int*)dmem)[3], + ReadHelperInt(&((int*)smem)[3], vol), + vol); + dmem += 16; + smem += 16; + } + while ((size -= 16) >= 0); + } + + // still 8 bytes or more left to copy? + if ((size & 8) != 0) { + WriteHelperInt(&((int *)dmem)[0], + ReadHelperInt(&((int *)smem)[0], vol), vol); + WriteHelperInt(&((int *)dmem)[1], + ReadHelperInt(&((int *)smem)[1], vol), vol); + dmem += 8; + smem += 8; + } + + // still 4 bytes or more left to copy? + if ((size & 4) != 0) { + WriteHelperInt(&((int *)dmem)[0], + ReadHelperInt(&((int *)smem)[0], vol), vol); + dmem += 4; + smem += 4; + } + + // still 2 bytes or more left to copy? + if ((size & 2) != 0) { + WriteHelperShort(&((short *)dmem)[0], + ReadHelperShort(&((short *)smem)[0], vol), + vol); + dmem += 2; + smem += 2; + } + + // still 1 byte left to copy? + if ((size & 1) != 0) { + WriteHelperByte(dmem, ReadHelperByte(smem, vol), vol); + dmem += 1; + smem += 1; + } + } + else { + smem += size; + dmem += size; + + // take the slow path if the source and dest and co-aligned. + if (((int)smem & 0x3) != ((int)dmem & 0x3)) { + for (; size > 0; size--) { + WriteHelperByte(--dmem, ReadHelperByte(--smem, vol), vol); + } + return; + } + + // make sure the destination is dword aligned + while ((((int)dmem) & 0x3) != 0 && size >= 3) { + WriteHelperByte(--dmem, ReadHelperByte(--smem, vol), vol); + size -= 1; + } + + // copy 16 bytes at a time + if (size >= 16) { + size -= 16; + do { + dmem -= 16; + smem -= 16; + WriteHelperInt(&((int *)dmem)[3], + ReadHelperInt(&((int *)smem)[3], vol), + vol); + WriteHelperInt(&((int *)dmem)[2], + ReadHelperInt(&((int *)smem)[2], vol), + vol); + WriteHelperInt(&((int *)dmem)[1], + ReadHelperInt(&((int *)smem)[1], vol), + vol); + WriteHelperInt(&((int *)dmem)[0], + ReadHelperInt(&((int *)smem)[0], vol), + vol); + } + while ((size -= 16) >= 0); + } + + // still 8 bytes or more left to copy? + if ((size & 8) != 0) { + dmem -= 8; + smem -= 8; + WriteHelperInt(&((int *)dmem)[1], + ReadHelperInt(&((int *)smem)[1], vol), vol); + WriteHelperInt(&((int *)dmem)[0], + ReadHelperInt(&((int *)smem)[0], vol), vol); + } + + // still 4 bytes or more left to copy? + if ((size & 4) != 0) { + dmem -= 4; + smem -= 4; + WriteHelperInt(&((int *)dmem)[0], + ReadHelperInt(&((int *)smem)[0], vol), vol); + } + + // still 2 bytes or more left to copy? + if ((size & 2) != 0) { + dmem -= 2; + smem -= 2; + WriteHelperShort(&((short *)dmem)[0], + ReadHelperShort(&((short *)smem)[0], vol), + vol); + } + + // still 1 byte left to copy? + if ((size & 1) != 0) { + dmem -= 1; + smem -= 1; + WriteHelperByte(dmem, + ReadHelperByte(smem, vol), vol); + } + } + } + + // Copies from one primitive array to another primitive array without + // respecting types. This calls memmove internally. + //| + public static void BlockCopy(Array src, int srcOffset, + Array dst, int dstOffset, int count) { + if (src == null) { + throw new ArgumentNullException("src"); + } + if (dst == null) { + throw new ArgumentNullException("dst"); + } + InternalBlockCopy(src, srcOffset, dst, dstOffset, count); + } + + // A very simple and efficient array copy that assumes all of the + // parameter validation has already been done. All counts here are + // in bytes. + internal static unsafe void InternalBlockCopy(Array src, int srcOffset, + Array dst, int dstOffset, + int count) { + VTable.Assert(src != null); + VTable.Assert(dst != null); + + // Unfortunately, we must do a check to make sure we're writing + // within the bounds of the array. This will ensure that we don't + // overwrite memory elsewhere in the system nor do we write out junk. + // This can happen if multiple threads screw with our IO classes + // simultaneously without being threadsafe. Throw here. + int srcLen = src.Length * src.vtable.arrayElementSize; + if (srcOffset < 0 || dstOffset < 0 || count < 0 || + srcOffset > srcLen - count) + throw new IndexOutOfRangeException + ("IndexOutOfRange_IORaceCondition"); + if (src == dst) { + if (dstOffset > srcLen - count) + throw new IndexOutOfRangeException + ("IndexOutOfRange_IORaceCondition"); + } + else { + int dstLen = dst.Length * dst.vtable.arrayElementSize; + if (dstOffset > dstLen - count) + throw new IndexOutOfRangeException + ("IndexOutOfRange_IORaceCondition"); + } + + // Copy the data. + // Call our faster version of memmove, not the CRT one. + fixed (int *srcFieldPtr = &src.field1) { + fixed (int *dstFieldPtr = &dst.field1) { + byte *srcPtr = (byte *) + src.GetFirstElementAddress(srcFieldPtr); + byte *dstPtr = (byte *) + dst.GetFirstElementAddress(dstFieldPtr); + MoveMemory(dstPtr + dstOffset, srcPtr + srcOffset, count); + } + } + } + + [NoHeapAllocation] + internal static unsafe void ZeroMemory(byte* dst, UIntPtr len) + { + ZeroMemory(dst, (int)len); + } + + [Inline] + [NoHeapAllocation] + internal unsafe static void InitMemoryImpl(byte* dest, byte value, + int len, bool vol) { + int intValue = (value == 0) ? 0 : + (value | (value << 8) | (value << 16) | (value << 24)); + // This is based on Peter Sollich's faster memcpy implementation, + // from COMString.cpp. + while ((((int)dest) & 0x03) != 0 && len >= 3) { + WriteHelperByte(dest++, value, vol); + len -= 1; + } + + if (len >= 16) { + len -= 16; + do { + WriteHelperInt(&((int*)dest)[0], intValue, vol); + WriteHelperInt(&((int*)dest)[1], intValue, vol); + WriteHelperInt(&((int*)dest)[2], intValue, vol); + WriteHelperInt(&((int*)dest)[3], intValue, vol); + dest += 16; + } while ((len -= 16) >= 0); + } + if ((len & 8) > 0) { + WriteHelperInt(&((int*)dest)[0], intValue, vol); + WriteHelperInt(&((int*)dest)[1], intValue, vol); + dest += 8; + } + if ((len & 4) > 0) { + WriteHelperInt(&((int*)dest)[0], intValue, vol); + dest += 4; + } + if ((len & 2) != 0) { + short shortValue = (value == 0) ? (short) 0 : + (short)(value | value << 8) ; + dest += 2; + } + if ((len & 1) != 0) + WriteHelperByte(dest++, value, vol); + } + + // Gets a particular byte out of the array. The array must be an + // array of primitives. + // + // This essentially does the following: + // return ((byte*)array) + index. + // + //| + [MethodImpl(MethodImplOptions.InternalCall)] + [NoHeapAllocation] + public static extern byte GetByte(Array array, int index); + + // Sets a particular byte in an the array. The array must be an + // array of primitives. + // + // This essentially does the following: + // *(((byte*)array) + index) = value. + // + //| + [MethodImpl(MethodImplOptions.InternalCall)] + [NoHeapAllocation] + public static extern void SetByte(Array array, int index, byte value); + + // Gets a particular byte out of the array. The array must be an + // array of primitives. + // + // This essentially does the following: + // return array.length * sizeof(array.UnderlyingElementType). + // + //| + [MethodImpl(MethodImplOptions.InternalCall)] + [NoHeapAllocation] + public static extern int ByteLength(Array array); + } +} diff --git a/base/Kernel/System/Byte.cs b/base/Applications/Runtime/Full/System/Byte.cs similarity index 89% rename from base/Kernel/System/Byte.cs rename to base/Applications/Runtime/Full/System/Byte.cs index 249e9db..424996a 100644 --- a/base/Kernel/System/Byte.cs +++ b/base/Applications/Runtime/Full/System/Byte.cs @@ -3,19 +3,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: Byte -** -** -** Purpose: This class will encapsulate a byte and provide an -** Object representation of it. -** -** Date: August 3, 1998 -** -===========================================================*/ +//============================================================ +// +// Class: Byte +// +// Purpose: This class will encapsulate a byte and provide an +// Object representation of it. +// +//=========================================================== -namespace System { +namespace System +{ using System; using System.Globalization; diff --git a/base/Applications/Runtime/Full/System/CLSCompliantAttribute.cs b/base/Applications/Runtime/Full/System/CLSCompliantAttribute.cs new file mode 100644 index 0000000..f396a03 --- /dev/null +++ b/base/Applications/Runtime/Full/System/CLSCompliantAttribute.cs @@ -0,0 +1,36 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: CLSCompliantAttribute +// +// Purpose: Container for assemblies. +// +//============================================================================= + +namespace System +{ + //| + [AttributeUsage (AttributeTargets.All, Inherited=true, AllowMultiple=false)] + public sealed class CLSCompliantAttribute : Attribute + { + private bool m_compliant; + + //| + public CLSCompliantAttribute (bool isCompliant) + { + m_compliant = isCompliant; + } + //| + public bool IsCompliant + { + get + { + return m_compliant; + } + } + } +} diff --git a/base/Kernel/System/Char.cs b/base/Applications/Runtime/Full/System/Char.cs similarity index 77% rename from base/Kernel/System/Char.cs rename to base/Applications/Runtime/Full/System/Char.cs index 9eed763..5067d5a 100644 --- a/base/Kernel/System/Char.cs +++ b/base/Applications/Runtime/Full/System/Char.cs @@ -3,18 +3,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: Char -** -** -** Purpose: This is the value class representing a Unicode character -** Char methods until we create this functionality. -** -** Date: August 3, 1998 -** -===========================================================*/ -namespace System { +//============================================================ +// +// Class: Char +// +// Purpose: This is the value class representing a Unicode character +// Char methods until we create this functionality. +// +//=========================================================== +namespace System +{ using System; using System.Globalization; @@ -71,7 +69,7 @@ namespace System { // //| public int CompareTo(Object value) { - if (value==null) { + if (value == null) { return 1; } if (!(value is Char)) { @@ -91,9 +89,9 @@ namespace System { // Formatting Methods // - /*===================================ToString=================================== - **This static methods takes a character and returns the String representation of it. - ==============================================================================*/ + //===================================ToString=================================== + //This static methods takes a character and returns the String representation of it. + //============================================================================== // Provides a string representation of a character. //| public unsafe static String ToString(char c) { @@ -103,10 +101,10 @@ namespace System { //| public static char Parse(String s) { - if (s==null) { + if (s == null) { throw new ArgumentNullException("s"); } - if (s.Length!=1) { + if (s.Length != 1) { throw new FormatException("Format_NeedSingleChar"); } return s[0]; @@ -115,30 +113,30 @@ namespace System { // // Static Methods // - /*=================================ISDIGIT====================================== - **A wrapper for Char. Returns a boolean indicating whether ** - **character c is considered to be a digit. ** - ==============================================================================*/ + //=================================ISDIGIT====================================== + //A wrapper for Char. Returns a boolean indicating whether ** + //character c is considered to be a digit. ** + //============================================================================== // Determines whether a character is a digit. //| public static bool IsDigit(char c) { return CharacterInfo.IsDigit(c); } - /*=================================ISLETTER===================================== - **A wrapper for Char. Returns a boolean indicating whether ** - **character c is considered to be a letter. ** - ==============================================================================*/ + //=================================ISLETTER===================================== + //A wrapper for Char. Returns a boolean indicating whether ** + //character c is considered to be a letter. ** + //============================================================================== // Determines whether a character is a letter. //| public static bool IsLetter(char c) { return CharacterInfo.IsLetter(c); } - /*===============================ISWHITESPACE=================================== - **A wrapper for Char. Returns a boolean indicating whether ** - **character c is considered to be a whitespace character. ** - ==============================================================================*/ + //===============================ISWHITESPACE=================================== + //A wrapper for Char. Returns a boolean indicating whether ** + //character c is considered to be a whitespace character. ** + //============================================================================== // Determines whether a character is whitespace. //| public static bool IsWhiteSpace(char c) { @@ -146,30 +144,30 @@ namespace System { } - /*===================================IsUpper==================================== - **Arguments: c -- the character to be checked. - **Returns: True if c is an uppercase character. - ==============================================================================*/ + //===================================IsUpper==================================== + //Arguments: c -- the character to be checked. + //Returns: True if c is an uppercase character. + //============================================================================== // Determines whether a character is upper-case. //| public static bool IsUpper(char c) { return CharacterInfo.IsUpper(c); } - /*===================================IsLower==================================== - **Arguments: c -- the character to be checked. - **Returns: True if c is an lowercase character. - ==============================================================================*/ + //===================================IsLower==================================== + //Arguments: c -- the character to be checked. + //Returns: True if c is an lowercase character. + //============================================================================== // Determines whether a character is lower-case. //| public static bool IsLower(char c) { return CharacterInfo.IsLower(c); } - /*================================IsPunctuation================================= - **Arguments: c -- the character to be checked. - **Returns: True if c is an punctuation mark - ==============================================================================*/ + //================================IsPunctuation================================= + //Arguments: c -- the character to be checked. + //Returns: True if c is an punctuation mark + //============================================================================== // Determines whether a character is a punctuation mark. //| public static bool IsPunctuation(char c){ @@ -188,11 +186,11 @@ namespace System { || uc == UnicodeCategory.DecimalDigitNumber); } - /*=================================TOUPPER====================================== - **A wrapper for Char.toUpperCase. Converts character c to its ** - **uppercase equivalent. If c is already an uppercase character or is not an ** - **alphabetic, nothing happens. ** - ==============================================================================*/ + //=================================TOUPPER====================================== + //A wrapper for Char.toUpperCase. Converts character c to its ** + //uppercase equivalent. If c is already an uppercase character or is not an ** + //alphabetic, nothing happens. ** + //============================================================================== // Converts a character to upper-case for the default culture. // //| @@ -201,11 +199,11 @@ namespace System { } - /*=================================TOLOWER====================================== - **A wrapper for Char.toLowerCase. Converts character c to its ** - **lowercase equivalent. If c is already a lowercase character or is not an ** - **alphabetic, nothing happens. ** - ==============================================================================*/ + //=================================TOLOWER====================================== + //A wrapper for Char.toLowerCase. Converts character c to its ** + //lowercase equivalent. If c is already a lowercase character or is not an ** + //alphabetic, nothing happens. ** + //============================================================================== // Converts a character to lower-case for the default culture. //| public static char ToLower(char c) { @@ -230,7 +228,7 @@ namespace System { //| public static bool IsControl(String s, int index) { - if (s==null) + if (s == null) throw new ArgumentNullException("s"); if (((uint)index)>=((uint)s.Length)) { throw new ArgumentOutOfRangeException("index"); @@ -241,7 +239,7 @@ namespace System { //| public static bool IsDigit(String s, int index) { - if (s==null) + if (s == null) throw new ArgumentNullException("s"); if (((uint)index)>=((uint)s.Length)) { throw new ArgumentOutOfRangeException("index"); @@ -252,7 +250,7 @@ namespace System { //| public static bool IsLetter(String s, int index) { - if (s==null) + if (s == null) throw new ArgumentNullException("s"); if (((uint)index)>=((uint)s.Length)) { throw new ArgumentOutOfRangeException("index"); @@ -263,7 +261,7 @@ namespace System { //| public static bool IsLetterOrDigit(String s, int index) { - if (s==null) + if (s == null) throw new ArgumentNullException("s"); if (((uint)index)>=((uint)s.Length)) { throw new ArgumentOutOfRangeException("index"); @@ -274,7 +272,7 @@ namespace System { //| public static bool IsLower(String s, int index) { - if (s==null) + if (s == null) throw new ArgumentNullException("s"); if (((uint)index)>=((uint)s.Length)) { throw new ArgumentOutOfRangeException("index"); @@ -291,7 +289,7 @@ namespace System { //| public static bool IsNumber(String s, int index) { - if (s==null) + if (s == null) throw new ArgumentNullException("s"); if (((uint)index)>=((uint)s.Length)) { throw new ArgumentOutOfRangeException("index"); @@ -302,7 +300,7 @@ namespace System { //| public static bool IsPunctuation (String s, int index) { - if (s==null) + if (s == null) throw new ArgumentNullException("s"); if (((uint)index)>=((uint)s.Length)) { throw new ArgumentOutOfRangeException("index"); @@ -319,7 +317,7 @@ namespace System { //| public static bool IsSeparator(String s, int index) { - if (s==null) + if (s == null) throw new ArgumentNullException("s"); if (((uint)index)>=((uint)s.Length)) { throw new ArgumentOutOfRangeException("index"); @@ -336,7 +334,7 @@ namespace System { //| public static bool IsSurrogate(String s, int index) { - if (s==null) + if (s == null) throw new ArgumentNullException("s"); if (((uint)index)>=((uint)s.Length)) { throw new ArgumentOutOfRangeException("index"); @@ -353,7 +351,7 @@ namespace System { //| public static bool IsSymbol(String s, int index) { - if (s==null) + if (s == null) throw new ArgumentNullException("s"); if (((uint)index)>=((uint)s.Length)) { throw new ArgumentOutOfRangeException("index"); @@ -365,7 +363,7 @@ namespace System { //| public static bool IsUpper(String s, int index) { - if (s==null) + if (s == null) throw new ArgumentNullException("s"); if (((uint)index)>=((uint)s.Length)) { throw new ArgumentOutOfRangeException("index"); @@ -376,7 +374,7 @@ namespace System { //| public static bool IsWhiteSpace(String s, int index) { - if (s==null) + if (s == null) throw new ArgumentNullException("s"); if (((uint)index)>=((uint)s.Length)) { throw new ArgumentOutOfRangeException("index"); @@ -393,7 +391,7 @@ namespace System { //| public static UnicodeCategory GetUnicodeCategory(String s, int index) { - if (s==null) + if (s == null) throw new ArgumentNullException("s"); if (((uint)index)>=((uint)s.Length)) { throw new ArgumentOutOfRangeException("index"); diff --git a/base/Kernel/System/CharEnumerator.cs b/base/Applications/Runtime/Full/System/CharEnumerator.cs similarity index 82% rename from base/Kernel/System/CharEnumerator.cs rename to base/Applications/Runtime/Full/System/CharEnumerator.cs index 32615f1..373cdc9 100644 --- a/base/Kernel/System/CharEnumerator.cs +++ b/base/Applications/Runtime/Full/System/CharEnumerator.cs @@ -3,18 +3,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: CharEnumerator -** -** -** Purpose: Enumerates the characters on a string. skips range -** checks. -** -** Date: January 3, 2001 -** -============================================================*/ -namespace System { +//============================================================ +// +// Class: CharEnumerator +// +// Purpose: Enumerates the characters on a string. skips range +// checks. +// +//============================================================ +namespace System +{ using System.Collections; @@ -36,7 +34,7 @@ namespace System { //| public bool MoveNext() { - if (index < (str.Length-1)) { + if (index < (str.Length - 1)) { index++; currentElement = str[index]; return true; diff --git a/base/Kernel/System/Collections/ArrayList.cs b/base/Applications/Runtime/Full/System/Collections/ArrayList.cs similarity index 95% rename from base/Kernel/System/Collections/ArrayList.cs rename to base/Applications/Runtime/Full/System/Collections/ArrayList.cs index 6b3c49e..8db591e 100644 --- a/base/Kernel/System/Collections/ArrayList.cs +++ b/base/Applications/Runtime/Full/System/Collections/ArrayList.cs @@ -3,19 +3,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: ArrayList -** -** -** Purpose: Implements a dynamically sized List as an array, -** and provides many convenience methods for treating -** an array as an IList. -** -** Date: October, 1999 -** -===========================================================*/ -namespace System.Collections { +//============================================================ +// +// Class: ArrayList +// +// Purpose: Implements a dynamically sized List as an array, +// and provides many convenience methods for treating +// an array as an IList. +// +//=========================================================== +namespace System.Collections +{ using System; // Implements a variable-size List that uses an array of objects to store the @@ -24,8 +22,7 @@ namespace System.Collections { // of the ArrayList is automatically increased as required by reallocating the // internal array. // - // By Anders Hejlsberg - // version 1.00 8/13/98 + // version 1.00 //| public class ArrayList : IList, ICloneable { @@ -65,7 +62,7 @@ namespace System.Collections { // //| public ArrayList(ICollection c) { - if (c==null) + if (c == null) throw new ArgumentNullException("c", "ArgumentNull_Collection"); _items = new Object[c.Count]; AddRange(c); @@ -148,7 +145,7 @@ namespace System.Collections { // //| public static ArrayList Adapter(IList list) { - if (list==null) + if (list == null) throw new ArgumentNullException("list"); return new IListWrapper(list); } @@ -245,14 +242,14 @@ namespace System.Collections { // //| public virtual bool Contains(Object item) { - if (item==null) { - for(int i=0; i<_size; i++) + if (item == null) { + for (int i = 0; i < _size; i++) if (_items[i]==null) return true; return false; } else { - for(int i=0; i<_size; i++) + for (int i = 0; i < _size; i++) if (item.Equals(_items[i])) return true; return false; @@ -309,7 +306,7 @@ namespace System.Collections { // //| public static IList FixedSize(IList list) { - if (list==null) + if (list == null) throw new ArgumentNullException("list"); return new FixedSizeList(list); } @@ -319,7 +316,7 @@ namespace System.Collections { // //| public static ArrayList FixedSize(ArrayList list) { - if (list==null) + if (list == null) throw new ArgumentNullException("list"); return new FixedSizeArrayList(list); } @@ -417,7 +414,7 @@ namespace System.Collections { // //| public virtual void InsertRange(int index, ICollection c) { - if (c==null) + if (c == null) throw new ArgumentNullException("c", "ArgumentNull_Collection"); if (index < 0 || index > _size) throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_Index"); int count = c.Count; @@ -496,7 +493,7 @@ namespace System.Collections { // //| public static IList ReadOnly(IList list) { - if (list==null) + if (list == null) throw new ArgumentNullException("list"); return new ReadOnlyList(list); } @@ -505,7 +502,7 @@ namespace System.Collections { // //| public static ArrayList ReadOnly(ArrayList list) { - if (list==null) + if (list == null) throw new ArgumentNullException("list"); return new ReadOnlyArrayList(list); } @@ -516,7 +513,7 @@ namespace System.Collections { //| public virtual void Remove(Object obj) { int index = IndexOf(obj); - if (index >=0) + if (index >= 0) RemoveAt(index); } @@ -568,7 +565,7 @@ namespace System.Collections { throw new ArgumentOutOfRangeException("count","ArgumentOutOfRange_NeedNonNegNum"); ArrayList list = new ArrayList((count>_defaultCapacity)?count:_defaultCapacity); - for(int i=0; i public static IList Synchronized(IList list) { - if (list==null) + if (list == null) throw new ArgumentNullException("list"); return new SyncIList(list); } @@ -669,7 +666,7 @@ namespace System.Collections { // //| public static ArrayList Synchronized(ArrayList list) { - if (list==null) + if (list == null) throw new ArgumentNullException("list"); return new SyncArrayList(list); } @@ -690,7 +687,7 @@ namespace System.Collections { // //| public virtual Array ToArray(Type type) { - if (type==null) + if (type == null) throw new ArgumentNullException("type"); Array array = Array.CreateInstance(type, _size); Array.Copy(_items, 0, array, 0, _size); @@ -815,7 +812,7 @@ namespace System.Collections { } public override void CopyTo(int index, Array array, int arrayIndex, int count) { - if (array==null) + if (array == null) throw new ArgumentNullException("array"); if (index < 0 || arrayIndex < 0) throw new ArgumentOutOfRangeException((index < 0) ? "index" : "arrayIndex", "ArgumentOutOfRange_NeedNonNegNum"); @@ -826,7 +823,7 @@ namespace System.Collections { if (array.Rank != 1) throw new ArgumentException("Arg_RankMultiDimNotSupported"); - for(int i=index; i _list.Count) throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_Index"); IEnumerator en = c.GetEnumerator(); - while(en.MoveNext()) { + while (en.MoveNext()) { _list.Insert(index++, en.Current); } } @@ -901,12 +899,13 @@ namespace System.Collections { int endIndex = startIndex - count + 1; if (value == null) { - for(int i=startIndex; i >= endIndex; i--) + for (int i = startIndex; i >= endIndex; i--) if (_list[i] == null) return i; return -1; - } else { - for(int i=startIndex; i >= endIndex; i--) + } + else { + for (int i = startIndex; i >= endIndex; i--) if (value.Equals(_list[i])) return i; return -1; @@ -927,7 +926,7 @@ namespace System.Collections { if (_list.Count - index < count) throw new ArgumentException("Argument_InvalidOffLen"); - while(count > 0) { + while (count > 0) { _list.RemoveAt(index); count--; } @@ -941,8 +940,7 @@ namespace System.Collections { int i = index; int j = index + count - 1; - while (i < j) - { + while (i < j) { Object tmp = _list[i]; _list[i++] = _list[j]; _list[j--] = tmp; @@ -950,14 +948,14 @@ namespace System.Collections { } public override void SetRange(int index, ICollection c) { - if (c==null) + if (c == null) throw new ArgumentNullException("c", "ArgumentNull_Collection"); if (index < 0 || index >= _list.Count) throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_Index"); if (_list.Count - index < c.Count) throw new ArgumentException("Argument_InvalidOffLen"); IEnumerator en = c.GetEnumerator(); - while(en.MoveNext()) { + while (en.MoveNext()) { _list[index++] = en.Current; } } @@ -979,7 +977,7 @@ namespace System.Collections { Object [] array = new Object[count]; CopyTo(index, array, 0, count); Array.Sort(array, 0, count, comparer); - for(int i=0; i 0 && _en.MoveNext()); + while (startIndex-- > 0 && _en.MoveNext()); _remaining = count; _firstCall = true; } @@ -1063,7 +1061,7 @@ namespace System.Collections { public virtual void Reset() { _en.Reset(); int startIndex = _initialStartIndex; - while(startIndex-- > 0 && _en.MoveNext()); + while (startIndex-- > 0 && _en.MoveNext()); _remaining = _initialCount; _firstCall = true; } @@ -1903,8 +1901,7 @@ namespace System.Collections { index++; return true; } - else - { + else { index = endIndex + 1; currentElement = list; } @@ -1995,8 +1992,7 @@ namespace System.Collections { public override void Clear() { InternalUpdateRange(); - if (_baseSize != 0) - { + if (_baseSize != 0) { _baseList.RemoveRange(_baseIndex, _baseSize); _baseVersion++; _baseSize = 0; @@ -2012,14 +2008,14 @@ namespace System.Collections { public override bool Contains(Object item) { InternalUpdateRange(); - if (item==null) { - for(int i=0; i<_baseSize; i++) + if (item == null) { + for (int i = 0; i < _baseSize; i++) if (_baseList[_baseIndex + i]==null) return true; return false; } else { - for(int i=0; i<_baseSize; i++) + for (int i = 0; i < _baseSize; i++) if (item.Equals(_baseList[_baseIndex + i])) return true; return false; @@ -2028,7 +2024,7 @@ namespace System.Collections { public override void CopyTo(Array array, int index) { InternalUpdateRange(); - if (array==null) + if (array == null) throw new ArgumentNullException("array"); if (array.Rank != 1) throw new ArgumentException("Arg_RankMultiDimNotSupported"); @@ -2041,7 +2037,7 @@ namespace System.Collections { public override void CopyTo(int index, Array array, int arrayIndex, int count) { InternalUpdateRange(); - if (array==null) + if (array == null) throw new ArgumentNullException("array"); if (array.Rank != 1) throw new ArgumentException("Arg_RankMultiDimNotSupported"); @@ -2248,7 +2244,7 @@ namespace System.Collections { public override Array ToArray(Type type) { InternalUpdateRange(); - if (type==null) + if (type == null) throw new ArgumentNullException("type"); Array array = Array.CreateInstance(type, _baseSize); Array.Copy(_baseList._items, _baseIndex, array, 0, _baseSize); @@ -2283,7 +2279,7 @@ namespace System.Collections { public virtual bool MoveNext() { if (version != list._version) throw new InvalidOperationException("InvalidOperation_EnumFailedVersion"); - if (index < (list.Count-1)) { + if (index < (list.Count - 1)) { index++; currentElement = list[index]; return true; diff --git a/base/Applications/Runtime/Full/System/Collections/BitArray.cs b/base/Applications/Runtime/Full/System/Collections/BitArray.cs new file mode 100644 index 0000000..85c44e6 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Collections/BitArray.cs @@ -0,0 +1,488 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: BitArray +// +// Purpose: The BitArray class manages a compact array of bit values. +// +//============================================================================= +namespace System.Collections +{ + // @Consider: is _ShrinkThreshold (256) ints the correct threshold for shrinking? + using System; + using System.Diagnostics; + + // A vector of bits. Use this to store bits efficiently, without having to do bit + // shifting yourself. + //| + public sealed class BitArray : ICollection, ICloneable { + private BitArray() { + } + + //========================================================================= + // Allocates space to hold length bit values. All of the values in the bit + // array are set to false. + // + // Exceptions: ArgumentException if length < 0. + //========================================================================= + //| + public BitArray(int length) + : this(length, false) { + } + + //========================================================================= + // Allocates space to hold length bit values. All of the values in the bit + // array are set to defaultValue. + // + // Exceptions: ArgumentOutOfRangeException if length < 0. + //========================================================================= + //| + public BitArray(int length, bool defaultValue) { + if (length < 0) { + throw new ArgumentOutOfRangeException("ArgumentOutOfRange_NeedNonNegNum"); + } + + m_array = new int[(length + 31) / 32]; + m_length = length; + + int fillValue = defaultValue ? unchecked(((int)0xffffffff)) : 0; + for (int i = 0; i < m_array.Length; i++) { + m_array[i] = fillValue; + } + + _version = 0; + } + + //========================================================================= + // Allocates space to hold the bit values in bytes. bytes[0] represents + // bits 0 - 7, bytes[1] represents bits 8 - 15, etc. The LSB of each byte + // represents the lowest index value; bytes[0] & 1 represents bit 0, + // bytes[0] & 2 represents bit 1, bytes[0] & 4 represents bit 2, etc. + // + // Exceptions: ArgumentException if bytes == null. + //========================================================================= + //| + public BitArray(byte[] bytes) { + if (bytes == null) { + throw new ArgumentNullException("bytes"); + } + + m_array = new int[(bytes.Length + 3) / 4]; + m_length = bytes.Length * 8; + + int i = 0; + int j = 0; + while (bytes.Length - j >= 4) { + m_array[i++] = (bytes[j] & 0xff) | + ((bytes[j + 1] & 0xff) << 8) | + ((bytes[j + 2] & 0xff) << 16) | + ((bytes[j + 3] & 0xff) << 24); + j += 4; + } + + Debug.Assert(bytes.Length - j >= 0, "BitArray byteLength problem"); + Debug.Assert(bytes.Length - j < 4, "BitArray byteLength problem #2"); + + switch (bytes.Length - j) { + case 3: + m_array[i] = ((bytes[j + 2] & 0xff) << 16); + goto case 2; + // fall through + case 2: + m_array[i] |= ((bytes[j + 1] & 0xff) << 8); + goto case 1; + // fall through + case 1: + m_array[i] |= (bytes[j] & 0xff); + break; + } + + _version = 0; + } + + //| + public BitArray(bool[] values) { + if (values == null) { + throw new ArgumentNullException("values"); + } + + m_array = new int[(values.Length + 31) / 32]; + m_length = values.Length; + + for (int i = 0; i < values.Length; i++) { + if (values[i]) + m_array[i/32] |= (1 << (i%32)); + } + + _version = 0; + + } + + //========================================================================= + // Allocates space to hold the bit values in values. values[0] represents + // bits 0 - 31, values[1] represents bits 32 - 63, etc. The LSB of each + // integer represents the lowest index value; values[0] & 1 represents bit + // 0, values[0] & 2 represents bit 1, values[0] & 4 represents bit 2, etc. + // + // Exceptions: ArgumentException if values == null. + //========================================================================= + //| + public BitArray(int[] values) { + if (values == null) { + throw new ArgumentNullException("values"); + } + + m_array = new int[values.Length]; + m_length = values.Length * 32; + + Array.Copy(values, m_array, values.Length); + + _version = 0; + } + + //========================================================================= + // Allocates a new BitArray with the same length and bit values as bits. + // + // Exceptions: ArgumentException if bits == null. + //========================================================================= + //| + public BitArray(BitArray bits) { + if (bits == null) { + throw new ArgumentNullException("bits"); + } + + m_array = new int[(bits.m_length + 31) / 32]; + m_length = bits.m_length; + + Array.Copy(bits.m_array, m_array, (bits.m_length + 31) / 32); + + _version = bits._version; + } + + //| + public bool this[int index] { + get { + return Get(index); + } + set { + Set(index,value); + } + } + + //========================================================================= + // Returns the bit value at position index. + // + // Exceptions: ArgumentOutOfRangeException if index < 0 or + // index >= GetLength(). + //========================================================================= + //| + public bool Get(int index) { + if (index < 0 || index >= m_length) { + throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_Index"); + } + + return (m_array[index / 32] & (1 << (index % 32))) != 0; + } + + //========================================================================= + // Sets the bit value at position index to value. + // + // Exceptions: ArgumentOutOfRangeException if index < 0 or + // index >= GetLength(). + //========================================================================= + //| + public void Set(int index, bool value) { + if (index < 0 || index >= m_length) { + throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_Index"); + } + + if (value) { + m_array[index / 32] |= (1 << (index % 32)); + } + else { + m_array[index / 32] &= ~(1 << (index % 32)); + } + + _version++; + } + + //========================================================================= + // Sets all the bit values to value. + //========================================================================= + //| + public void SetAll(bool value) { + int fillValue = value ? unchecked(((int)0xffffffff)) : 0; + int ints = (m_length + 31) / 32; + for (int i = 0; i < ints; i++) { + m_array[i] = fillValue; + } + + _version++; + } + + //========================================================================= + // Returns a reference to the current instance ANDed with value. + // + // Exceptions: ArgumentException if value == null or + // value.Length != this.Length. + //========================================================================= + //| + public BitArray And(BitArray value) { + if (value == null) + throw new ArgumentNullException("value"); + if (m_length != value.m_length) + throw new ArgumentException("Arg_ArrayLengthsDiffer"); + + int ints = (m_length + 31) / 32; + for (int i = 0; i < ints; i++) { + m_array[i] &= value.m_array[i]; + } + + _version++; + return this; + } + + //========================================================================= + // Returns a reference to the current instance ORed with value. + // + // Exceptions: ArgumentException if value == null or + // value.Length != this.Length. + //========================================================================= + //| + public BitArray Or(BitArray value) { + if (value == null) + throw new ArgumentNullException("value"); + if (m_length != value.m_length) + throw new ArgumentException("Arg_ArrayLengthsDiffer"); + + int ints = (m_length + 31) / 32; + for (int i = 0; i < ints; i++) { + m_array[i] |= value.m_array[i]; + } + + _version++; + return this; + } + + //========================================================================= + // Returns a reference to the current instance XORed with value. + // + // Exceptions: ArgumentException if value == null or + // value.Length != this.Length. + //========================================================================= + //| + public BitArray Xor(BitArray value) { + if (value == null) + throw new ArgumentNullException("value"); + if (m_length != value.m_length) + throw new ArgumentException("Arg_ArrayLengthsDiffer"); + + int ints = (m_length + 31) / 32; + for (int i = 0; i < ints; i++) { + m_array[i] ^= value.m_array[i]; + } + + _version++; + return this; + } + + //========================================================================= + // Inverts all the bit values. On/true bit values are converted to + // off/false. Off/false bit values are turned on/true. The current instance + // is updated and returned. + //========================================================================= + //| + public BitArray Not() { + int ints = (m_length + 31) / 32; + for (int i = 0; i < ints; i++) { + m_array[i] = ~m_array[i]; + } + + _version++; + return this; + } + + + //| + public int Length { + get { return m_length; } + set { + if (value < 0) { + throw new ArgumentOutOfRangeException("ArgumentOutOfRange_NeedNonNegNum"); + } + + int newints = (value + 31) / 32; + if (newints > m_array.Length || newints + _ShrinkThreshold < m_array.Length) { + // grow or shrink (if wasting more than _ShrinkThreshold ints) + int[] newarray = new int[newints]; + Array.Copy(m_array, newarray, newints > m_array.Length ? m_array.Length : newints); + m_array = newarray; + } + + if (value > m_length) { + // clear high bit values in the last int + int last = ((m_length + 31) / 32) - 1; + int bits = m_length % 32; + if (bits > 0) { + m_array[last] &= (1 << bits) - 1; + } + + // clear remaining int values + Array.Clear(m_array, last + 1, newints - last - 1); + } + + m_length = value; + _version++; + } + } + + // ICollection implementation + //| + public void CopyTo(Array array, int index) + { + if (array == null) + throw new ArgumentNullException("array"); + + if (index < 0) + throw new ArgumentOutOfRangeException("ArgumentOutOfRange_NeedNonNegNum"); + + if (array.Rank != 1) + throw new ArgumentException("Arg_RankMultiDimNotSupported"); + + + if (array is int[]) { + Array.Copy(m_array, 0, array, index, (m_length + 31) / 32); + } + else if (array is byte[]) { + if ((array.Length - index) < (m_length + 7)/8) + throw new ArgumentException("Argument_InvalidOffLen"); + + byte [] b = (byte[])array; + for (int i = 0; i <(m_length + 7)/8; i++) + b[index + i] = (byte)((m_array[i/4] >> ((i%4)*8)) & 0x000000FF); // Shift to bring the required byte to LSB, then mask + } + else if (array is bool[]) { + if (array.Length - index < m_length) + throw new ArgumentException("Argument_InvalidOffLen"); + + bool [] b = (bool[])array; + for (int i = 0; i < m_length; i++) + b[index + i] = ((m_array[i/32] >> (i%32)) & 0x00000001) != 0; + } + else + throw new ArgumentException("Arg_BitArrayTypeUnsupported"); + } + + //| + public int Count + { + get + { + return m_length; + } + } + + //| + public Object Clone() + { + BitArray bitArray = new BitArray(m_array); + bitArray._version = _version; + bitArray.m_length = m_length; + return bitArray; + } + + //| + public Object SyncRoot + { + get + { + return this; + } + } + + //| + public bool IsReadOnly + { + get + { + return false; + } + } + + + //| + public bool IsSynchronized + { + get + { + return false; + } + } + + //| + public IEnumerator GetEnumerator() + { + return new BitArrayEnumeratorSimple(this); + } + + // For a straightforward enumeration of the entire ArrayList, + // this is faster, because it's smaller. Patrick showed + // this with a benchmark. + private class BitArrayEnumeratorSimple : IEnumerator, ICloneable + { + private BitArray bitarray; + private int index; + private int version; + private bool currentElement; + + internal BitArrayEnumeratorSimple(BitArray bitarray) { + this.bitarray = bitarray; + this.index = -1; + version = bitarray._version; + } + + public Object Clone() { + return MemberwiseClone(); + } + + public virtual bool MoveNext() { + if (version != bitarray._version) throw new InvalidOperationException("InvalidOperation_EnumFailedVersion"); + if (index < (bitarray.Count - 1)) { + index++; + currentElement = bitarray.Get(index); + return true; + } + else + index = bitarray.Count; + + return false; + } + + public virtual Object Current { + get { + if (index == -1) + throw new InvalidOperationException("InvalidOperation_EnumNotStarted"); + if (index >= bitarray.Count) + throw new InvalidOperationException("InvalidOperation_EnumEnded"); + return currentElement; + } + } + + public void Reset() { + if (version != bitarray._version) throw new InvalidOperationException("InvalidOperation_EnumFailedVersion"); + index = -1; + } + } + + private int[] m_array; + private int m_length; + private int _version; + + private const int _ShrinkThreshold = 256; + } + +} diff --git a/base/Kernel/System/Collections/CaseInsensitiveComparer.cs b/base/Applications/Runtime/Full/System/Collections/CaseInsensitiveComparer.cs similarity index 85% rename from base/Kernel/System/Collections/CaseInsensitiveComparer.cs rename to base/Applications/Runtime/Full/System/Collections/CaseInsensitiveComparer.cs index 80c85a3..56cae9c 100644 --- a/base/Kernel/System/Collections/CaseInsensitiveComparer.cs +++ b/base/Applications/Runtime/Full/System/Collections/CaseInsensitiveComparer.cs @@ -3,15 +3,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: CaseInsensitiveComparer -** -** -** Date: May 17, 2000 -** -============================================================*/ -namespace System.Collections { +//============================================================ +// +// Class: CaseInsensitiveComparer +// +//============================================================ +namespace System.Collections +{ using System; using System.Collections; using System.Globalization; diff --git a/base/Applications/Runtime/Full/System/Collections/CaseInsensitiveHashCodeProvider.cs b/base/Applications/Runtime/Full/System/Collections/CaseInsensitiveHashCodeProvider.cs new file mode 100644 index 0000000..ebcd013 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Collections/CaseInsensitiveHashCodeProvider.cs @@ -0,0 +1,55 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================ +// +// Class: CaseInsensitiveHashCodeProvider +// +// Purpose: Designed to support hashtables which require +// case-insensitive behavior while still maintaining case, +// this provides an efficient mechanism for getting the +// hashcode of the string ignoring case. +// +//============================================================ +namespace System.Collections +{ + using System; + using System.Collections; + using System.Globalization; + + //| + public class CaseInsensitiveHashCodeProvider : IHashCodeProvider { + + public static readonly CaseInsensitiveHashCodeProvider Default + = new CaseInsensitiveHashCodeProvider(); + + //| + public CaseInsensitiveHashCodeProvider() { + } + + //| + public static CaseInsensitiveHashCodeProvider DefaultInvariant + { + get + { + return Default; + } + } + + //| + public int GetHashCode(Object obj) { + if (obj == null) { + throw new ArgumentNullException("obj"); + } + + String s = obj as String; + if (s == null) { + return obj.GetHashCode(); + } + + return TextInfo.GetCaseInsensitiveHashCode(s); + } + } +} diff --git a/base/Kernel/System/Collections/CollectionBase.cs b/base/Applications/Runtime/Full/System/Collections/CollectionBase.cs similarity index 96% rename from base/Kernel/System/Collections/CollectionBase.cs rename to base/Applications/Runtime/Full/System/Collections/CollectionBase.cs index 009f1ab..f1f4e8b 100644 --- a/base/Kernel/System/Collections/CollectionBase.cs +++ b/base/Applications/Runtime/Full/System/Collections/CollectionBase.cs @@ -6,7 +6,8 @@ //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -namespace System.Collections { +namespace System.Collections +{ using System; // Useful base class for typed read/write collections where items derive from object diff --git a/base/Kernel/System/Collections/Comparer.cs b/base/Applications/Runtime/Full/System/Collections/Comparer.cs similarity index 82% rename from base/Kernel/System/Collections/Comparer.cs rename to base/Applications/Runtime/Full/System/Collections/Comparer.cs index 9cc3de5..b232e32 100644 --- a/base/Kernel/System/Collections/Comparer.cs +++ b/base/Applications/Runtime/Full/System/Collections/Comparer.cs @@ -3,17 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: Comparer -** -** -** Purpose: Default IComparer implementation. -** -** Date: October 9, 1999 -** -===========================================================*/ -namespace System.Collections { +//============================================================ +// +// Class: Comparer +// +// Purpose: Default IComparer implementation. +// +//=========================================================== +namespace System.Collections +{ using System; using System.Globalization; diff --git a/base/Kernel/System/Collections/DictionaryBase.cs b/base/Applications/Runtime/Full/System/Collections/DictionaryBase.cs similarity index 96% rename from base/Kernel/System/Collections/DictionaryBase.cs rename to base/Applications/Runtime/Full/System/Collections/DictionaryBase.cs index c423aa4..02026f3 100644 --- a/base/Kernel/System/Collections/DictionaryBase.cs +++ b/base/Applications/Runtime/Full/System/Collections/DictionaryBase.cs @@ -6,7 +6,8 @@ //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -namespace System.Collections { +namespace System.Collections +{ using System; diff --git a/base/Kernel/System/Collections/DictionaryEntry.cs b/base/Applications/Runtime/Full/System/Collections/DictionaryEntry.cs similarity index 79% rename from base/Kernel/System/Collections/DictionaryEntry.cs rename to base/Applications/Runtime/Full/System/Collections/DictionaryEntry.cs index 027cc7a..1981fc6 100644 --- a/base/Kernel/System/Collections/DictionaryEntry.cs +++ b/base/Applications/Runtime/Full/System/Collections/DictionaryEntry.cs @@ -3,17 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Interface: DictionaryEntry -** -** -** Purpose: Return Value for IDictionaryEnumerator::GetEntry -** -** Date: September 20, 1999 -** -===========================================================*/ -namespace System.Collections { +//============================================================ +// +// Interface: DictionaryEntry +// +// Purpose: Return Value for IDictionaryEnumerator::GetEntry +// +//=========================================================== +namespace System.Collections +{ using System; // A DictionaryEntry holds a key and a value from a dictionary. @@ -32,7 +30,7 @@ namespace System.Collections { //| public DictionaryEntry(Object key, Object value) { - if (key==null) + if (key == null) throw new ArgumentNullException("key"); _key = key; _value = value; diff --git a/base/Kernel/System/Collections/Hashtable.cs b/base/Applications/Runtime/Full/System/Collections/Hashtable.cs similarity index 89% rename from base/Kernel/System/Collections/Hashtable.cs rename to base/Applications/Runtime/Full/System/Collections/Hashtable.cs index 5580f45..9898e44 100644 --- a/base/Kernel/System/Collections/Hashtable.cs +++ b/base/Applications/Runtime/Full/System/Collections/Hashtable.cs @@ -3,17 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: Hashtable -** -** -** Purpose: Hash table implementation -** -** Date: September 25, 1999 -** -===========================================================*/ -namespace System.Collections { +//============================================================ +// +// Class: Hashtable +// +// Purpose: Hash table implementation +// +//=========================================================== +namespace System.Collections +{ using System; using System.Diagnostics; using System.Runtime.CompilerServices; @@ -64,69 +62,67 @@ namespace System.Collections { // clear the hash code & collision field, then the key then value field // (at least value should be set last). // - // By Brian Grunkemeyer, algorithm by Patrick Dussud. - // Version 1.30 2/20/2000 + // Version 1.30 //| [CCtorIsRunDuringStartup] public class Hashtable : IDictionary, ICloneable { - /* - Implementation Notes: - - This Hashtable uses double hashing. There are hashsize buckets in - the table, and each bucket can contain 0 or 1 element. We a bit to - mark whether there's been a collision when we inserted multiple - elements (ie, an inserted item was hashed at least a second time and - we probed this bucket, but it was already in use). Using the - collision bit, we can terminate lookups & removes for elements that - aren't in the hash table more quickly. We steal the most - significant bit from the hash code to store the collision bit. - - Our hash function is of the following form: - - h(key, n) = h1(key) + n*h2(key) - - where n is the number of times we've hit a collided bucket and - rehashed (on this particular lookup). Here are our hash functions: - - h1(key) = GetHash(key); // default implementation calls key.GetHashCode(); - h2(key) = 1 + (((h1(key) >> 5) + 1) % (hashsize - 1)); - - The h1 can return any number. h2 must return a number between 1 and - hashsize - 1 that is relatively prime to hashsize (not a problem if - hashsize is prime). (Knuth's Art of Computer Programming, Vol. 3, - p. 528-9) - - If this is true, then we are guaranteed to visit every bucket in - exactly hashsize probes, since the least common multiple of hashsize - and h2(key) will be hashsize * h2(key). (This is the first number - where adding h2 to h1 mod hashsize will be 0 and we will search the - same bucket twice). - - We previously used a different h2(key, n) that was not constant. - That is a horrifically bad idea, unless you can prove that series - will never produce any identical numbers that overlap when you mod - them by hashsize, for all subranges from i to i+hashsize, for all i. - It's not worth investigating, since there was no clear benefit from - using that hash function, and it was broken. - - For efficiency reasons, we've implemented this by storing h1 and h2 - in a temporary, and setting a variable called seed equal to h1. We - do a probe, and if we collided, we simply add h2 to seed each time - through the loop. - - A good test for h2() is to subclass Hashtable, provide your own - implementation of GetHash() that returns a constant, then add many - items to the hash table. Make sure Count equals the number of items - you inserted. - - Note that when we remove an item from the hash table, we set the key - equal to buckets, if there was a collision in this bucket. - Otherwise we'd either wipe out the collision bit, or we'd still have - an item in the hash table. - - -- Brian Grunkemeyer, 10/28/1999 - */ + // + //Implementation Notes: +// + //This Hashtable uses double hashing. There are hashsize buckets in + //the table, and each bucket can contain 0 or 1 element. We a bit to + //mark whether there's been a collision when we inserted multiple + //elements (ie, an inserted item was hashed at least a second time and + //we probed this bucket, but it was already in use). Using the + //collision bit, we can terminate lookups & removes for elements that + //aren't in the hash table more quickly. We steal the most + //significant bit from the hash code to store the collision bit. +// + //Our hash function is of the following form: +// + //h(key, n) = h1(key) + n*h2(key) +// + //where n is the number of times we've hit a collided bucket and + //rehashed (on this particular lookup). Here are our hash functions: +// + //h1(key) = GetHash(key); // default implementation calls key.GetHashCode(); + //h2(key) = 1 + (((h1(key) >> 5) + 1) % (hashsize - 1)); +// + //The h1 can return any number. h2 must return a number between 1 and + //hashsize - 1 that is relatively prime to hashsize (not a problem if + //hashsize is prime). (Knuth's Art of Computer Programming, Vol. 3, + //p. 528-9) +// + //If this is true, then we are guaranteed to visit every bucket in + //exactly hashsize probes, since the least common multiple of hashsize + //and h2(key) will be hashsize * h2(key). (This is the first number + //where adding h2 to h1 mod hashsize will be 0 and we will search the + //same bucket twice). +// + //We previously used a different h2(key, n) that was not constant. + //That is a horrifically bad idea, unless you can prove that series + //will never produce any identical numbers that overlap when you mod + //them by hashsize, for all subranges from i to i+hashsize, for all i. + //It's not worth investigating, since there was no clear benefit from + //using that hash function, and it was broken. +// + //For efficiency reasons, we've implemented this by storing h1 and h2 + //in a temporary, and setting a variable called seed equal to h1. We + //do a probe, and if we collided, we simply add h2 to seed each time + //through the loop. +// + //A good test for h2() is to subclass Hashtable, provide your own + //implementation of GetHash() that returns a constant, then add many + //items to the hash table. Make sure Count equals the number of items + //you inserted. +// + //Note that when we remove an item from the hash table, we set the key + //equal to buckets, if there was a collision in this bucket. + //Otherwise we'd either wipe out the collision bit, or we'd still have + //an item in the hash table. +// + // // Table of prime numbers to use as hash table sizes. Each entry is the // smallest prime number larger than twice the previous entry. @@ -230,13 +226,13 @@ namespace System.Collections { public Hashtable(int capacity) : this(capacity, 100) { } - // + // [Bartok]: public Hashtable(int capacity, float loadFactor) : this(capacity, (int)(loadFactor * 100), null, null) { } - // + // [Bartok]: public Hashtable(int capacity, float loadFactor, IHashCodeProvider hcp, IComparer comparer) : this(capacity, (int)(loadFactor * 100), hcp, comparer) { @@ -337,7 +333,7 @@ namespace System.Collections { //| public Hashtable(IDictionary d, int loadFactorPerc, IHashCodeProvider hcp, IComparer comparer) : this((d != null ? d.Count : 0), loadFactorPerc, hcp, comparer) { - if (d==null) + if (d == null) throw new ArgumentNullException("d", "ArgumentNull_Dictionary"); IDictionaryEnumerator e = d.GetEnumerator(); @@ -377,7 +373,7 @@ namespace System.Collections { if (count == 0) return; - for (int i = 0; i < buckets.Length; i++){ + for (int i = 0; i < buckets.Length; i++) { buckets[i].hash_coll = 0; buckets[i].key = null; buckets[i].val = null; @@ -403,7 +399,7 @@ namespace System.Collections { while (bucket > 0) { bucket--; Object keyv = lbuckets[bucket].key; - if ((keyv!= null) && (keyv != lbuckets)) { + if ((keyv != null) && (keyv != lbuckets)) { ht[keyv] = lbuckets[bucket].val; } } @@ -440,7 +436,7 @@ namespace System.Collections { if (b.key == null) { return false; } - if (((b.hash_coll & 0x7FFFFFFF) == hashcode) && + if (((b.hash_coll & 0x7FFFFFFF) == hashcode) && KeyEquals (b.key, key)) return true; seed += incr; @@ -467,7 +463,7 @@ namespace System.Collections { else { for (int i = buckets.Length; --i >= 0;) { Object val = buckets[i].val; - if (val!=null && value.Equals(val)) return true; + if (val != null && value.Equals(val)) return true; } } return false; @@ -480,7 +476,7 @@ namespace System.Collections { bucket[] lbuckets = buckets; for (int i = lbuckets.Length; --i >= 0;) { Object keyv = lbuckets[i].key; - if ((keyv != null) && (keyv != buckets)){ + if ((keyv != null) && (keyv != buckets)) { array.SetValue(keyv, arrayIndex++); } } @@ -493,7 +489,7 @@ namespace System.Collections { bucket[] lbuckets = buckets; for (int i = lbuckets.Length; --i >= 0;) { Object keyv = lbuckets[i].key; - if ((keyv != null) && (keyv != buckets)){ + if ((keyv != null) && (keyv != buckets)) { DictionaryEntry entry = new DictionaryEntry(keyv,lbuckets[i].val); array.SetValue(entry, arrayIndex++); } @@ -523,7 +519,7 @@ namespace System.Collections { bucket[] lbuckets = buckets; for (int i = lbuckets.Length; --i >= 0;) { Object keyv = lbuckets[i].key; - if ((keyv != null) && (keyv != buckets)){ + if ((keyv != null) && (keyv != buckets)) { array.SetValue(lbuckets[i].val, arrayIndex++); } } @@ -557,7 +553,7 @@ namespace System.Collections { KeyEquals (key, b.key)) return b.val; seed += incr; - }while (b.hash_coll < 0 && ++ntry < lbuckets.Length); + } while (b.hash_coll < 0 && ++ntry < lbuckets.Length); return null; } set { @@ -596,9 +592,9 @@ namespace System.Collections { // rehash table into new buckets int nb; - for (nb = 0; nb < buckets.Length; nb++){ + for (nb = 0; nb < buckets.Length; nb++) { bucket oldb = buckets[nb]; - if ((oldb.key != null) && (oldb.key != buckets)){ + if ((oldb.key != null) && (oldb.key != buckets)) { putEntry(newBuckets, oldb.key, oldb.val, oldb.hash_coll & 0x7FFFFFFF); } } @@ -641,7 +637,7 @@ namespace System.Collections { //| protected virtual int GetHash(Object key) { - if (hcp!=null) + if (hcp != null) return hcp.GetHashCode(key); return key.GetHashCode(); } @@ -670,7 +666,7 @@ namespace System.Collections { //| protected virtual bool KeyEquals(Object item, Object key) { - if (comparer!=null) + if (comparer != null) return comparer.Compare(item, key)==0; return item.Equals(key); } @@ -740,7 +736,7 @@ namespace System.Collections { // that once contained an entry and also has had a collision. // We need to search this entire collision chain because we have to ensure that there are no // duplicate entries in the table. - if (emptySlotNumber == -1 && (buckets[bucketNumber].key == buckets) && (buckets[bucketNumber].hash_coll < 0))//(((buckets[bucketNumber].hash_coll & unchecked(0x80000000))!=0))) + if (emptySlotNumber == -1 && (buckets[bucketNumber].key == buckets) && (buckets[bucketNumber].hash_coll < 0))//(((buckets[bucketNumber].hash_coll & unchecked(0x80000000)) != 0))) emptySlotNumber = bucketNumber; // Insert the key/value pair into this bucket if this bucket is empty and has never contained an entry @@ -781,7 +777,7 @@ namespace System.Collections { // UNLESS // we have remembered an available slot previously. if (emptySlotNumber == -1) {// We don't need to set the collision bit here since we already have an empty slot - if( buckets[bucketNumber].hash_coll >= 0 ) { + if (buckets[bucketNumber].hash_coll >= 0) { buckets[bucketNumber].hash_coll |= unchecked((int)0x80000000); occupancy++; } @@ -790,8 +786,7 @@ namespace System.Collections { } while (++ntry < buckets.Length); // This code is here if and only if there were no buckets without a collision bit set in the entire table - if (emptySlotNumber != -1) - { + if (emptySlotNumber != -1) { // We pretty much have to insert in this order. Don't set hash // code until the value & key are set appropriately. buckets[emptySlotNumber].val = nvalue; @@ -827,12 +822,12 @@ namespace System.Collections { return; } - if( newBuckets[bucketNumber].hash_coll >= 0 ) { + if (newBuckets[bucketNumber].hash_coll >= 0) { newBuckets[bucketNumber].hash_coll |= unchecked((int)0x80000000); occupancy++; } seed += incr; - } while( true ); + } while (true); } // Removes an entry from this hashtable. If an entry with the specified @@ -893,7 +888,7 @@ namespace System.Collections { // //| public static Hashtable Synchronized(Hashtable table) { - if (table==null) + if (table == null) throw new ArgumentNullException("table"); return new SyncHashtable(table); } @@ -909,7 +904,7 @@ namespace System.Collections { } public virtual void CopyTo(Array array, int arrayIndex) { - if (array==null) + if (array == null) throw new ArgumentNullException("array"); if (array.Rank != 1) throw new ArgumentException("Arg_RankMultiDimNotSupported"); @@ -952,7 +947,7 @@ namespace System.Collections { } public virtual void CopyTo(Array array, int arrayIndex) { - if (array==null) + if (array == null) throw new ArgumentNullException("array"); if (array.Rank != 1) throw new ArgumentException("Arg_RankMultiDimNotSupported"); @@ -1136,7 +1131,7 @@ namespace System.Collections { while (bucket > 0) { bucket--; Object keyv = hashtable.buckets[bucket].key; - if ((keyv!= null) && (keyv != hashtable.buckets)) { + if ((keyv != null) && (keyv != hashtable.buckets)) { currentKey = keyv; currentValue = hashtable.buckets[bucket].val; current = true; @@ -1159,9 +1154,9 @@ namespace System.Collections { get { if (current == false) throw new InvalidOperationException("InvalidOperation_EnumOpCantHappen"); - if (getObjectRetType==Keys) + if (getObjectRetType == Keys) return currentKey; - else if (getObjectRetType==Values) + else if (getObjectRetType == Values) return currentValue; else return new DictionaryEntry(currentKey, currentValue); diff --git a/base/Kernel/System/Collections/ICollection.cs b/base/Applications/Runtime/Full/System/Collections/ICollection.cs similarity index 87% rename from base/Kernel/System/Collections/ICollection.cs rename to base/Applications/Runtime/Full/System/Collections/ICollection.cs index 22f1531..d69251d 100644 --- a/base/Kernel/System/Collections/ICollection.cs +++ b/base/Applications/Runtime/Full/System/Collections/ICollection.cs @@ -3,17 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Interface: ICollection -** -** -** Purpose: Base interface for all collections. -** -** Date: June 11, 1999 -** -===========================================================*/ -namespace System.Collections { +//============================================================ +// +// Interface: ICollection +// +// Purpose: Base interface for all collections. +// +//=========================================================== +namespace System.Collections +{ using System; // Base interface for all collections, defining enumerators, size, and diff --git a/base/Applications/Runtime/Full/System/Collections/IComparer.cs b/base/Applications/Runtime/Full/System/Collections/IComparer.cs new file mode 100644 index 0000000..f7a3ee7 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Collections/IComparer.cs @@ -0,0 +1,30 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================ +// +// Interface: IComparer +// +// Purpose: Interface for comparing two Objects. +// +//=========================================================== +namespace System.Collections +{ + + using System; + // The IComparer interface implements a method that compares two objects. It is + // used in conjunction with the Sort and BinarySearch methods on + // the Array and List classes. + // + //| + public interface IComparer { + // Compares two objects. An implementation of this method must return a + // value less than zero if x is less than y, zero if x is equal to y, or a + // value greater than zero if x is greater than y. + // + //| + int Compare(Object x, Object y); + } +} diff --git a/base/Kernel/System/Collections/IDictionary.cs b/base/Applications/Runtime/Full/System/Collections/IDictionary.cs similarity index 86% rename from base/Kernel/System/Collections/IDictionary.cs rename to base/Applications/Runtime/Full/System/Collections/IDictionary.cs index ad48f50..f26d13c 100644 --- a/base/Kernel/System/Collections/IDictionary.cs +++ b/base/Applications/Runtime/Full/System/Collections/IDictionary.cs @@ -3,17 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Interface: IDictionary -** -** -** Purpose: Base interface for all dictionarys. -** -** Date: September 21, 1999 -** -===========================================================*/ -namespace System.Collections { +//============================================================ +// +// Interface: IDictionary +// +// Purpose: Base interface for all dictionarys. +// +//=========================================================== +namespace System.Collections +{ using System; // An IDictionary is a possibly unordered set of key-value pairs. diff --git a/base/Kernel/System/Collections/IDictionaryEnumerator.cs b/base/Applications/Runtime/Full/System/Collections/IDictionaryEnumerator.cs similarity index 89% rename from base/Kernel/System/Collections/IDictionaryEnumerator.cs rename to base/Applications/Runtime/Full/System/Collections/IDictionaryEnumerator.cs index f549476..4e253c4 100644 --- a/base/Kernel/System/Collections/IDictionaryEnumerator.cs +++ b/base/Applications/Runtime/Full/System/Collections/IDictionaryEnumerator.cs @@ -3,17 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Interface: IDictionaryEnumerator -** -** -** Purpose: Base interface for dictionary enumerators. -** -** Date: September 20, 1999 -** -===========================================================*/ -namespace System.Collections { +//============================================================ +// +// Interface: IDictionaryEnumerator +// +// Purpose: Base interface for dictionary enumerators. +// +//=========================================================== +namespace System.Collections +{ using System; // This interface represents an enumerator that allows sequential access to the diff --git a/base/Applications/Runtime/Full/System/Collections/IEnumerable.cs b/base/Applications/Runtime/Full/System/Collections/IEnumerable.cs new file mode 100644 index 0000000..72a2bd0 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Collections/IEnumerable.cs @@ -0,0 +1,27 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================ +// +// Interface: IEnumerable +// +// Purpose: Interface for classes providing IEnumerators +// +//=========================================================== +namespace System.Collections +{ + using System; + using System.Runtime.InteropServices; + // Implement this interface if you need to support VB's foreach semantics. + // Also, COM classes that support an enumerator will also implement this interface. + //| + public interface IEnumerable + { + // Returns an IEnumerator for this enumerable Object. The enumerator provides + // a simple way to access all the contents of a collection. + //| + IEnumerator GetEnumerator(); + } +} diff --git a/base/Kernel/System/Collections/IEnumerator.cs b/base/Applications/Runtime/Full/System/Collections/IEnumerator.cs similarity index 84% rename from base/Kernel/System/Collections/IEnumerator.cs rename to base/Applications/Runtime/Full/System/Collections/IEnumerator.cs index f1a2f77..c05e00e 100644 --- a/base/Kernel/System/Collections/IEnumerator.cs +++ b/base/Applications/Runtime/Full/System/Collections/IEnumerator.cs @@ -3,17 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Interface: IEnumerator -** -** -** Purpose: Base interface for all enumerators. -** -** Date: June 14, 1999 -** -===========================================================*/ -namespace System.Collections { +//============================================================ +// +// Interface: IEnumerator +// +// Purpose: Base interface for all enumerators. +// +//=========================================================== +namespace System.Collections +{ using System; using System.Runtime.InteropServices; diff --git a/base/Applications/Runtime/Full/System/Collections/IHashCodeProvider.cs b/base/Applications/Runtime/Full/System/Collections/IHashCodeProvider.cs new file mode 100644 index 0000000..650ae7b --- /dev/null +++ b/base/Applications/Runtime/Full/System/Collections/IHashCodeProvider.cs @@ -0,0 +1,27 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================ +// +// Interface: IHashCodeProvider +// +// Purpose: A bunch of strings. +// +//=========================================================== +namespace System.Collections +{ + + using System; + // Provides a mechanism for a hash table user to override the default + // GetHashCode() function on Objects, providing their own hash function. + //| + public interface IHashCodeProvider + { + // Returns a hash code for the given object. + // + //| + int GetHashCode (Object obj); + } +} diff --git a/base/Kernel/System/Collections/IList.cs b/base/Applications/Runtime/Full/System/Collections/IList.cs similarity index 87% rename from base/Kernel/System/Collections/IList.cs rename to base/Applications/Runtime/Full/System/Collections/IList.cs index d76243d..82bcbc0 100644 --- a/base/Kernel/System/Collections/IList.cs +++ b/base/Applications/Runtime/Full/System/Collections/IList.cs @@ -3,17 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Interface: IList -** -** -** Purpose: Base interface for all Lists. -** -** Date: September 21, 1999 -** -===========================================================*/ -namespace System.Collections { +//============================================================ +// +// Interface: IList +// +// Purpose: Base interface for all Lists. +// +//=========================================================== +namespace System.Collections +{ using System; // An IList is an ordered collection of objects. The exact ordering diff --git a/base/Kernel/System/Collections/Queue.cs b/base/Applications/Runtime/Full/System/Collections/Queue.cs similarity index 91% rename from base/Kernel/System/Collections/Queue.cs rename to base/Applications/Runtime/Full/System/Collections/Queue.cs index 5e6887d..e6c6b8a 100644 --- a/base/Kernel/System/Collections/Queue.cs +++ b/base/Applications/Runtime/Full/System/Collections/Queue.cs @@ -3,18 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================================= -** -** Class: Queue -** -** Original implementation by Derek Yenser -** -** Purpose: A circular-array implementation of a queue. -** -** Date: October 4, 1999 -** -=============================================================================*/ -namespace System.Collections { +//============================================================================= +// +// Class: Queue +// +// Purpose: A circular-array implementation of a queue. +// +//============================================================================= +namespace System.Collections +{ using System; // A simple Queue of objects. Internally it is implemented as a circular @@ -70,10 +67,10 @@ namespace System.Collections { //| public Queue(ICollection col) : this((col==null ? 32 : col.Count)) { - if (col==null) + if (col == null) throw new ArgumentNullException("col"); IEnumerator en = col.GetEnumerator(); - while(en.MoveNext()) + while (en.MoveNext()) Enqueue(en.Current); } @@ -124,7 +121,7 @@ namespace System.Collections { //| public virtual void CopyTo(Array array, int index) { - if (array==null) + if (array == null) throw new ArgumentNullException("array"); if (array.Rank != 1) throw new ArgumentException("Arg_RankMultiDimNotSupported"); @@ -135,11 +132,12 @@ namespace System.Collections { throw new ArgumentException("Argument_InvalidOffLen"); int numToCopy = (arrayLen-index < _size) ? arrayLen-index : _size; - if (numToCopy==0) + if (numToCopy == 0) return; if (_head < _tail) { Array.Copy(_array, _head, array, index, numToCopy); - } else { + } + else { int firstPart = (_array.Length - _head < numToCopy) ? _array.Length - _head : numToCopy; Array.Copy(_array, _head, array, index, firstPart); numToCopy -= firstPart; @@ -200,6 +198,14 @@ namespace System.Collections { return _array[_head]; } + public virtual Object PeekAt(int index) + { + if (index < 0 || index >= _size) { + throw new ArgumentOutOfRangeException("index"); + } + return GetElement(index); + } + // Returns a synchronized Queue. Returns a synchronized wrapper // class around the queue - the caller must not use references to the // original queue. @@ -207,7 +213,7 @@ namespace System.Collections { //| public static Queue Synchronized(Queue queue) { - if (queue==null) + if (queue == null) throw new ArgumentNullException("queue"); return new SynchronizedQueue(queue); } @@ -225,7 +231,8 @@ namespace System.Collections { if (obj == null) { if (_array[index] == null) return true; - } else if (obj.Equals(_array[index])) { + } + else if (obj.Equals(_array[index])) { return true; } index = (index + 1) % _array.Length; @@ -247,12 +254,13 @@ namespace System.Collections { public virtual Object[] ToArray() { Object[] arr = new Object[_size]; - if (_size==0) + if (_size == 0) return arr; if (_head < _tail) { Array.Copy(_array, _head, arr, 0, _size); - } else { + } + else { Array.Copy(_array, _head, arr, 0, _array.Length - _head); Array.Copy(_array, 0, arr, _array.Length - _head, _tail); } @@ -268,7 +276,8 @@ namespace System.Collections { if (_size > 0) { if (_head < _tail) { Array.Copy(_array, _head, newarray, 0, _size); - } else { + } + else { Array.Copy(_array, _head, newarray, 0, _array.Length - _head); Array.Copy(_array, 0, newarray, _array.Length - _head, _tail); } @@ -420,8 +429,7 @@ namespace System.Collections { public virtual Object Current { get { - if (currentElement == _q._array) - { + if (currentElement == _q._array) { if (_index == 0) throw new InvalidOperationException("InvalidOperation_EnumNotStarted"); else diff --git a/base/Kernel/System/Collections/ReadOnlyCollectionBase.cs b/base/Applications/Runtime/Full/System/Collections/ReadOnlyCollectionBase.cs similarity index 95% rename from base/Kernel/System/Collections/ReadOnlyCollectionBase.cs rename to base/Applications/Runtime/Full/System/Collections/ReadOnlyCollectionBase.cs index 909900b..620c045 100644 --- a/base/Kernel/System/Collections/ReadOnlyCollectionBase.cs +++ b/base/Applications/Runtime/Full/System/Collections/ReadOnlyCollectionBase.cs @@ -6,7 +6,8 @@ //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -namespace System.Collections { +namespace System.Collections +{ using System; diff --git a/base/Kernel/System/Collections/SortedList.cs b/base/Applications/Runtime/Full/System/Collections/SortedList.cs similarity index 95% rename from base/Kernel/System/Collections/SortedList.cs rename to base/Applications/Runtime/Full/System/Collections/SortedList.cs index c9a4106..3ccb2de 100644 --- a/base/Kernel/System/Collections/SortedList.cs +++ b/base/Applications/Runtime/Full/System/Collections/SortedList.cs @@ -3,18 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: SortedList -** -** Original implementation by Anders Hejlsberg -** -** Purpose: A sorted dictionary. -** -** Date: September 21, 1999 -** -===========================================================*/ -namespace System.Collections { +//============================================================ +// +// Class: SortedList +// +// Purpose: A sorted dictionary. +// +//=========================================================== +namespace System.Collections +{ using System; // The SortedList class implements a sorted list of keys and values. Entries in @@ -56,8 +53,7 @@ namespace System.Collections { // has a constructor that allows a specific IComparer implementation to // be specified. // - // By Anders Hejlsberg, further work by Brian Grunkemeyer - // version 1.10 9/10/98 + // version 1.10 //| public class SortedList : IDictionary, ICloneable { @@ -153,7 +149,7 @@ namespace System.Collections { //| public SortedList(IDictionary d, IComparer comparer) : this(comparer, (d != null ? d.Count : 0)) { - if (d==null) + if (d == null) throw new ArgumentNullException("d", "ArgumentNull_Dictionary"); d.Keys.CopyTo(keys, 0); d.Values.CopyTo(values, 0); @@ -322,7 +318,7 @@ namespace System.Collections { throw new ArgumentOutOfRangeException("arrayIndex", "ArgumentOutOfRange_NeedNonNegNum"); if (array.Length - arrayIndex < Count) throw new ArgumentException("Arg_ArrayPlusOffTooSmall"); - for (int i = 0; i public static SortedList Synchronized(SortedList list) { - if (list==null) + if (list == null) throw new ArgumentNullException("list"); return new SyncSortedList(list); } @@ -756,9 +752,9 @@ namespace System.Collections { get { if (current == false) throw new InvalidOperationException("InvalidOperation_EnumOpCantHappen"); - if (getObjectRetType==Keys) + if (getObjectRetType == Keys) return key; - else if (getObjectRetType==Values) + else if (getObjectRetType == Values) return value; else return new DictionaryEntry(key, value); @@ -849,7 +845,7 @@ namespace System.Collections { } public virtual int IndexOf(Object key) { - if (key==null) + if (key == null) throw new ArgumentNullException("key", "ArgumentNull_Key"); int i = Array.BinarySearch(sortedList.keys, 0, diff --git a/base/Kernel/System/Collections/Specialized/ListDictionary.cs b/base/Applications/Runtime/Full/System/Collections/Specialized/ListDictionary.cs similarity index 95% rename from base/Kernel/System/Collections/Specialized/ListDictionary.cs rename to base/Applications/Runtime/Full/System/Collections/Specialized/ListDictionary.cs index 0912d24..bede4b6 100644 --- a/base/Kernel/System/Collections/Specialized/ListDictionary.cs +++ b/base/Applications/Runtime/Full/System/Collections/Specialized/ListDictionary.cs @@ -4,7 +4,8 @@ // //------------------------------------------------------------------------------ -namespace System.Collections.Specialized { +namespace System.Collections.Specialized +{ using System.Collections; @@ -213,7 +214,7 @@ namespace System.Collections.Specialized { /// [To be supplied.] /// public void CopyTo(Array array, int index) { - if (array==null) + if (array == null) throw new ArgumentNullException("array"); if (index < 0) throw new ArgumentOutOfRangeException("index"); @@ -259,7 +260,8 @@ namespace System.Collections.Specialized { } if (node == head) { head = node.next; - } else { + } + else { last.next = node.next; } count--; @@ -347,7 +349,7 @@ namespace System.Collections.Specialized { } void ICollection.CopyTo(Array array, int index) { - if (array==null) + if (array == null) throw new ArgumentNullException("array"); if (index < 0) throw new ArgumentOutOfRangeException("index"); diff --git a/base/Kernel/System/Collections/Specialized/StringDictionary.cs b/base/Applications/Runtime/Full/System/Collections/Specialized/StringDictionary.cs similarity index 92% rename from base/Kernel/System/Collections/Specialized/StringDictionary.cs rename to base/Applications/Runtime/Full/System/Collections/Specialized/StringDictionary.cs index a3a3083..4607497 100644 --- a/base/Kernel/System/Collections/Specialized/StringDictionary.cs +++ b/base/Applications/Runtime/Full/System/Collections/Specialized/StringDictionary.cs @@ -4,9 +4,10 @@ // //------------------------------------------------------------------------------ -/* - */ -namespace System.Collections.Specialized { +// +// +namespace System.Collections.Specialized +{ using System.Runtime.InteropServices; using System.Diagnostics; using System; @@ -53,14 +54,14 @@ namespace System.Collections.Specialized { /// public virtual string this[string key] { get { - if( key == null ) { + if (key == null) { throw new ArgumentNullException("key"); } return (string) contents[key.ToLower()]; } set { - if( key == null ) { + if (key == null) { throw new ArgumentNullException("key"); } @@ -100,7 +101,7 @@ namespace System.Collections.Specialized { /// Adds an entry with the specified key and value into the System.Windows.Forms.StringDictionary. /// public virtual void Add(string key, string value) { - if( key == null ) { + if (key == null) { throw new ArgumentNullException("key"); } @@ -118,7 +119,7 @@ namespace System.Collections.Specialized { /// Determines if the string dictionary contains a specific key /// public virtual bool ContainsKey(string key) { - if( key == null ) { + if (key == null) { throw new ArgumentNullException("key"); } @@ -151,7 +152,7 @@ namespace System.Collections.Specialized { /// Removes the entry with the specified key from the string dictionary. /// public virtual void Remove(string key) { - if( key == null ) { + if (key == null) { throw new ArgumentNullException("key"); } diff --git a/base/Kernel/System/Collections/Stack.cs b/base/Applications/Runtime/Full/System/Collections/Stack.cs similarity index 91% rename from base/Kernel/System/Collections/Stack.cs rename to base/Applications/Runtime/Full/System/Collections/Stack.cs index 47c396c..643a56c 100644 --- a/base/Kernel/System/Collections/Stack.cs +++ b/base/Applications/Runtime/Full/System/Collections/Stack.cs @@ -3,17 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================================= -** -** Class: Stack -** -** -** Purpose: An array implementation of a stack. -** -** Date: October 7, 1999 -** -=============================================================================*/ -namespace System.Collections { +//============================================================================= +// +// Class: Stack +// +// Purpose: An array implementation of a stack. +// +//============================================================================= +namespace System.Collections +{ using System; // A simple stack of objects. Internally it is implemented as an array, @@ -52,10 +50,10 @@ namespace System.Collections { //| public Stack(ICollection col) : this((col==null ? 32 : col.Count)) { - if (col==null) + if (col == null) throw new ArgumentNullException("col"); IEnumerator en = col.GetEnumerator(); - while(en.MoveNext()) + while (en.MoveNext()) Push(en.Current); } @@ -110,7 +108,7 @@ namespace System.Collections { // Copies the stack into an array. //| public virtual void CopyTo(Array array, int index) { - if (array==null) + if (array == null) throw new ArgumentNullException("array"); if (array.Rank != 1) throw new ArgumentException("Arg_RankMultiDimNotSupported"); @@ -122,13 +120,13 @@ namespace System.Collections { int i = 0; if (array is Object[]) { Object[] objArray = (Object[]) array; - while(i < _size) { + while (i < _size) { objArray[i+index] = _array[_size-i-1]; i++; } } else { - while(i < _size) { + while (i < _size) { array.SetValue(_array[_size-i-1], i+index); i++; } @@ -145,7 +143,7 @@ namespace System.Collections { // is empty, Peek throws an InvalidOperationException. //| public virtual Object Peek() { - if (_size==0) + if (_size == 0) throw new InvalidOperationException("InvalidOperation_EmptyStack"); return _array[_size-1]; } @@ -179,7 +177,7 @@ namespace System.Collections { // //| public static Stack Synchronized(Stack stack) { - if (stack==null) + if (stack == null) throw new ArgumentNullException("stack"); return new SyncStack(stack); } @@ -191,7 +189,7 @@ namespace System.Collections { { Object[] objArray = new Object[_size]; int i = 0; - while(i < _size) { + while (i < _size) { objArray[i] = _array[_size-i-1]; i++; } @@ -304,14 +302,14 @@ namespace System.Collections { public virtual bool MoveNext() { bool retval; if (_version != _stack._version) throw new InvalidOperationException("InvalidOperation_EnumFailedVersion"); - if (_index == -2) { // First call to enumerator. + if (_index == -2) { // First call to enumerator. _index = _stack._size-1; retval = ( _index >= 0); if (retval) currentElement = _stack._array[_index]; return retval; } - if (_index == -1) { // End of enumeration. + if (_index == -1) { // End of enumeration. return false; } diff --git a/base/Applications/Runtime/Full/System/Console.cs b/base/Applications/Runtime/Full/System/Console.cs new file mode 100644 index 0000000..2372e72 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Console.cs @@ -0,0 +1,320 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace System +{ + using Microsoft.Singularity; +#if !MINRUNTIME + using Microsoft.Singularity.Io; +#endif + using System; + using System.Text; + using System.Globalization; + using System.Runtime.CompilerServices; + + using System.Runtime.InteropServices; + + // Provides static fields for console input and output. Use + // Console.In for input from the standard input stream (stdin), + // Console.Out for output to stdout, and Console.Error + // for output to stderr. If any of those console streams are + // redirected from the command line, these streams will be redirected. + // A program can also redirect its own output or input with the + // SetIn, SetOut, and SetError methods. + // + // The distinction between Console.Out and Console.Error is useful + // for programs that redirect output to a file or a pipe. Note that + // stdout and stderr can be output to different files at the same + // time from the DOS command line: + // + // someProgram 1> out 2> err + // + //| + public sealed class Console + { + private Console() + { + throw new NotSupportedException("NotSupported_Constructor"); + } + + static Console() { + } + + + private static void WriteInternal(char[] buffer, int index, int count) + { +#if !MINRUNTIME + ConsoleOutput.Write(buffer, index, count); +#else + for (int i = 0; i < count; i++) { + DebugStub.Print(buffer[index + i]); + } +#endif + } + + private static void WriteInternal(String value) + { +#if !MINRUNTIME + ConsoleOutput.Write(value); +#else + DebugStub.Print(value); +#endif + } + + private static void WriteNewLine() + { + WriteInternal(Environment.NewLine); + } + + //| + public static void WriteLine() + { + WriteNewLine(); + } + + //| + public static void WriteLine(String value) + { + WriteInternal(value); + WriteNewLine(); + } + + //| + public static void WriteLine(bool value) + { + WriteInternal(value.ToString()); + WriteNewLine(); + } + + //| + public static void WriteLine(char value) + { + WriteInternal(value.ToString()); + WriteNewLine(); + } + + //| + public static void WriteLine(char[] buffer) + { + WriteInternal(buffer, 0, buffer.Length); + WriteNewLine(); + } + + //| + public static void WriteLine(char[] buffer, int index, int count) + { + WriteInternal(buffer, index, count); + WriteNewLine(); + } + + //| + public static void WriteLine(decimal value) + { + WriteInternal(value.ToString()); + WriteNewLine(); + } + + //| + public static void WriteLine(double value) + { + WriteInternal(value.ToString()); + WriteNewLine(); + } + + //| + public static void WriteLine(float value) + { + WriteInternal(value.ToString()); + WriteNewLine(); + } + + //| + public static void WriteLine(int value) + { + WriteInternal(value.ToString()); + WriteNewLine(); + } + + //| + [CLSCompliant(false)] + public static void WriteLine(uint value) + { + WriteInternal(value.ToString()); + WriteNewLine(); + } + + //| + public static void WriteLine(long value) + { + WriteInternal(value.ToString()); + WriteNewLine(); + } + + //| + [CLSCompliant(false)] + public static void WriteLine(ulong value) + { + WriteInternal(value.ToString()); + WriteNewLine(); + } + + //| + public static void WriteLine(Object value) + { + WriteInternal(value.ToString()); + WriteNewLine(); + } + + //| + public static void WriteLine(String format, Object arg0) + { + WriteInternal(String.Format(format, arg0)); + WriteNewLine(); + } + + //| + public static void WriteLine(String format, Object arg0, Object arg1) + { + WriteInternal(String.Format(format, arg0, arg1)); + WriteNewLine(); + } + + //| + public static void WriteLine(String format, Object arg0, Object arg1, Object arg2) + { + WriteInternal(String.Format(format, arg0, arg1, arg2)); + WriteNewLine(); + } + + //| + public static void WriteLine(String format, params Object[] args) + { + WriteInternal(String.Format(format, args)); + WriteNewLine(); + } + + //| + public static void Write(String format, Object arg0) + { + WriteInternal(String.Format(format, arg0)); + } + + //| + public static void Write(String format, Object arg0, Object arg1) + { + WriteInternal(String.Format(format, arg0, arg1)); + } + + //| + public static void Write(String format, Object arg0, Object arg1, Object arg2) + { + WriteInternal(String.Format(format, arg0, arg1, arg2)); + } + + //| + public static void Write(String format, params Object[] args) + { + WriteInternal(String.Format(format, args)); + } + + //| + public static void Write(bool value) + { + WriteInternal(value.ToString()); + } + + //| + public static void Write(char value) + { + WriteInternal(value.ToString()); + } + + //| + public static void Write(char[] buffer) + { + WriteInternal(buffer, 0, buffer.Length); + } + + //| + public static void Write(char[] buffer, int index, int count) + { + WriteInternal(buffer, index, count); + } + + //| + public static void Write(double value) + { + WriteInternal(value.ToString()); + } + + //| + public static void Write(decimal value) + { + WriteInternal(value.ToString()); + } + + //| + public static void Write(float value) + { + WriteInternal(value.ToString()); + } + + //| + public static void Write(int value) + { + WriteInternal(value.ToString()); + } + + //| + [CLSCompliant(false)] + public static void Write(uint value) + { + WriteInternal(value.ToString()); + } + + //| + public static void Write(long value) + { + WriteInternal(value.ToString()); + } + + //| + [CLSCompliant(false)] + public static void Write(ulong value) + { + WriteInternal(value.ToString()); + } + + //| + public static void Write(Object value) + { + WriteInternal(value.ToString()); + } + + //| + public static void Write(String value) + { + WriteInternal(value); + } + + public static int Read() + { +#if !MINRUNTIME + return ConsoleInput.ReadChar(); +#else + return -1; +#endif + } + public static string ReadLine() + { +#if !MINRUNTIME + return ConsoleInput.ReadLine(); +#else + return ""; +#endif + } + } +} diff --git a/base/Applications/Runtime/System/DateTime.cs b/base/Applications/Runtime/Full/System/DateTime.cs similarity index 96% rename from base/Applications/Runtime/System/DateTime.cs rename to base/Applications/Runtime/Full/System/DateTime.cs index 72ccac3..e9c6bdb 100644 --- a/base/Applications/Runtime/System/DateTime.cs +++ b/base/Applications/Runtime/Full/System/DateTime.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System { +namespace System +{ using System; using System.Threading; @@ -108,10 +109,11 @@ namespace System { [NoHeapAllocation] private DateTime(long ticksFound, int ignoreMe) { this.ticks = ticksFound; - if ((ulong)ticks>(ulong)MaxTicks) { - if (ticks>MaxTicks) { + if ((ulong)ticks >(ulong)MaxTicks) { + if (ticks > MaxTicks) { ticks = MaxTicks; - } else { + } + else { ticks = MinTicks; } } @@ -331,8 +333,7 @@ namespace System { { //TimeSpan.TimeToTicks is a family access function which does no error checking, so //we need to put some error checking out here. - if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >=0 && second < 60) - { + if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60) { return (TimeSpan.TimeToTicks(hour, minute, second)); } throw new ArgumentOutOfRangeException("ArgumentOutOfRange_BadHourMinuteSecond"); @@ -664,7 +665,8 @@ namespace System { return String.Format("{0:s}, {1:d02} {2:s} {3:d} {4:d02}:{5:d02}:{6:d02} GMT", GetShortWeekday(), Day, GetShortMonth(), Year, Hour, Minute, Second); - } else { + } + else { return String.Format("{0:d}/{1:d}/{2:d} {3:d}:{4:d}:{5:d}", Month, Day, Year, Hour, Minute, Second); } @@ -746,7 +748,7 @@ namespace System { private string GetShortWeekday() { // Hardcoded USA-English until we support globalization - switch(DayOfWeek) { + switch (DayOfWeek) { case DayOfWeek.Monday : return "Mon"; case DayOfWeek.Tuesday : return "Tue"; case DayOfWeek.Wednesday : return "Wed"; @@ -762,7 +764,7 @@ namespace System { private string GetShortMonth() { // Hardcoded USA-English until we support globalization - switch(Month) { + switch (Month) { case 1 : return "Jan"; case 2 : return "Feb"; case 3 : return "Mar"; diff --git a/base/Applications/Runtime/Full/System/DayOfWeek.cs b/base/Applications/Runtime/Full/System/DayOfWeek.cs new file mode 100644 index 0000000..4fe0008 --- /dev/null +++ b/base/Applications/Runtime/Full/System/DayOfWeek.cs @@ -0,0 +1,33 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================ +// +// Class: DayOfWeek +// +// Purpose: Enum for the day of the week. +// +//============================================================ +namespace System +{ + + //| + public enum DayOfWeek { + //| + Sunday = 0, + //| + Monday = 1, + //| + Tuesday = 2, + //| + Wednesday = 3, + //| + Thursday = 4, + //| + Friday = 5, + //| + Saturday = 6, + } +} diff --git a/base/Kernel/System/Decimal.cs b/base/Applications/Runtime/Full/System/Decimal.cs similarity index 92% rename from base/Kernel/System/Decimal.cs rename to base/Applications/Runtime/Full/System/Decimal.cs index bcb6b0b..51678d9 100644 --- a/base/Kernel/System/Decimal.cs +++ b/base/Applications/Runtime/Full/System/Decimal.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System { +namespace System +{ using System; using System.Diagnostics; @@ -47,6 +48,7 @@ namespace System { // the range of the Decimal type. //| [StructLayout(LayoutKind.Sequential)] + [AccessedByRuntime("referenced in Decimal.cpp")] public struct Decimal : IFormattable, IComparable { // Sign mask for the flags field. A value of zero in this bit indicates a @@ -202,7 +204,7 @@ namespace System { // //| public Decimal(float value) { - // rusa: see also Lightning\Src\VM\COMDecimal.cpp::InitSingle + // See also Lightning\Src\VM\COMDecimal.cpp::InitSingle if (DecimalFromFloat(value, out this) < 0) { throw new OverflowException("Overflow_Decimal"); } @@ -219,7 +221,7 @@ namespace System { // //| public Decimal(double value) { - // rusa: see also Lightning\Src\VM\COMDecimal.cpp::InitDouble + // See also Lightning\Src\VM\COMDecimal.cpp::InitDouble if (DecimalFromDouble(value, out this) < 0) { throw new OverflowException("Overflow_Decimal"); } @@ -252,7 +254,7 @@ namespace System { // //| public Decimal(int[] bits) { - if (bits==null) + if (bits == null) throw new ArgumentNullException("bits"); if (bits.Length == 4) { int f = bits[3]; @@ -302,7 +304,7 @@ namespace System { // //| public static Decimal Add(Decimal d1, Decimal d2) { - // rusa: see also Lightning\Src\VM\COMDecimal.cpp::Add + // See also Lightning\Src\VM\COMDecimal.cpp::Add Decimal result; if (DecimalAdd(ref d2, ref d1, out result) < 0) { throw new OverflowException("Decimal.Add"); @@ -323,7 +325,7 @@ namespace System { // //| public static int Compare(Decimal d1, Decimal d2) { - // rusa: see also Lightning\Src\VM\COMDecimal.cpp::Compare + // See also Lightning\Src\VM\COMDecimal.cpp::Compare return (DecimalCompare(ref d2, ref d1) - 1); } @@ -353,7 +355,7 @@ namespace System { // //| public static Decimal Divide(Decimal d1, Decimal d2) { - // rusa: see also Lightning\Src\VM\COMDecimal.cpp::Divide + // See also Lightning\Src\VM\COMDecimal.cpp::Divide Decimal result; if (DecimalDivide(ref d2, ref d1, out result) < 0) { throw new OverflowException("Decimal.Divide"); @@ -385,7 +387,7 @@ namespace System { // //| public override int GetHashCode() { - // rusa: see also Lightning\Src\VM\COMDecimal.cpp::GetHashCode + // See also Lightning\Src\VM\COMDecimal.cpp::GetHashCode double dbl; if (DoubleFromDecimal(ref this, out dbl) != 0) { throw new Exception("Decimal.GetHashCode failure"); @@ -407,7 +409,7 @@ namespace System { // //| public static Decimal Floor(Decimal d) { - // rusa: see also Lightning\Src\VM\COMDecimal.cpp::Floor + // See also Lightning\Src\VM\COMDecimal.cpp::Floor Decimal result; DecimalFloor(ref d, out result); return result; @@ -451,21 +453,21 @@ namespace System { return Number.ParseDecimal(s, style); } - /* Returns a binary representation of a Decimal. The return value is an - * integer array with four elements. Elements 0, 1, and 2 contain the low, - * middle, and high 32 bits of the 96-bit integer part of the Decimal. - * Element 3 contains the scale factor and sign of the Decimal: bits 0-15 - * (the lower word) are unused; bits 16-23 contain a value between 0 and - * 28, indicating the power of 10 to divide the 96-bit integer part by to - * produce the Decimal value; bits 24-30 are unused; and finally bit 31 - * indicates the sign of the Decimal value, 0 meaning positive and 1 - * meaning negative. - * - * @param d A Decimal value. - * @return An integer array with four elements containing the binary - * representation of the argument. - * @see Decimal(int[]) - */ + // Returns a binary representation of a Decimal. The return value is an + // integer array with four elements. Elements 0, 1, and 2 contain the low, + // middle, and high 32 bits of the 96-bit integer part of the Decimal. + // Element 3 contains the scale factor and sign of the Decimal: bits 0-15 + // (the lower word) are unused; bits 16-23 contain a value between 0 and + // 28, indicating the power of 10 to divide the 96-bit integer part by to + // produce the Decimal value; bits 24-30 are unused; and finally bit 31 + // indicates the sign of the Decimal value, 0 meaning positive and 1 + // meaning negative. + // + // @param d A Decimal value. + // @return An integer array with four elements containing the binary + // representation of the argument. + // @see Decimal(int[]) + // //| public static int[] GetBits(Decimal d) { return new int[] {d.lo, d.mid, d.hi, d.flags}; @@ -526,7 +528,7 @@ namespace System { //| public static Decimal Remainder(Decimal d1, Decimal d2) { - // rusa: see also Lightning\Src\VM\COMDecimal.cpp::Remainder + // See also Lightning\Src\VM\COMDecimal.cpp::Remainder Decimal tmp = Truncate(Divide(d1, d2)); return Subtract(d1, Multiply(d2, tmp)); } @@ -535,7 +537,7 @@ namespace System { // //| public static Decimal Multiply(Decimal d1, Decimal d2) { - // rusa: see also Lightning\Src\VM\COMDecimal.cpp::Multiply + // See also Lightning\Src\VM\COMDecimal.cpp::Multiply Decimal result; if (DecimalMultiply(ref d1, ref d2, out result) < 0) { throw new OverflowException("Decimal.Multiply"); @@ -571,7 +573,7 @@ namespace System { // //| public static Decimal Round(Decimal d1, int decimals) { - // rusa: see also Lightning\Src\VM\COMDecimal.cpp::Round + // See also Lightning\Src\VM\COMDecimal.cpp::Round Decimal result; if (decimals < 0 || decimals > 28) { throw new ArgumentOutOfRangeException("decimals"); @@ -593,7 +595,7 @@ namespace System { // //| public static Decimal Subtract(Decimal d1, Decimal d2) { - // rusa: see also Lightning\Src\VM\COMDecimal.cpp::Subtract + // See also Lightning\Src\VM\COMDecimal.cpp::Subtract Decimal result; if (DecimalSubtract(ref d1, ref d2, out result) < 0) { throw new OverflowException("Decimal.Subtract"); @@ -648,7 +650,7 @@ namespace System { // //| public static double ToDouble(Decimal d) { - // rusa: see also Lightning\Src\VM\COMDecimal.cpp + // See also Lightning\Src\VM\COMDecimal.cpp double result; DoubleFromDecimal(ref d, out result); return result; @@ -748,7 +750,7 @@ namespace System { // //| public static float ToSingle(Decimal d) { - // rusa: see also Lightning\Src\VM\COMDecimal.cpp + // See also Lightning\Src\VM\COMDecimal.cpp float result; FloatFromDecimal(ref d, out result); return result; @@ -939,16 +941,16 @@ namespace System { return Remainder(d1, d2); } - /*private static bool operator equals(Decimal d1, Decimal d2) { - return Compare(d1, d2) == 0; - } - - private static int operator compare(Decimal d1, Decimal d2) { - int c = Compare(d1, d2); - if (c < 0) return -1; - if (c > 0) return 1; - return 0; - }*/ + //private static bool operator equals(Decimal d1, Decimal d2) { + //return Compare(d1, d2) == 0; + //} +// + //private static int operator compare(Decimal d1, Decimal d2) { + //int c = Compare(d1, d2); + //if (c < 0) return -1; + //if (c > 0) return 1; + //return 0; + //} //| public static bool operator ==(Decimal d1, Decimal d2) { diff --git a/base/Applications/Runtime/Full/System/Delegate.cs b/base/Applications/Runtime/Full/System/Delegate.cs new file mode 100644 index 0000000..5c7e40c --- /dev/null +++ b/base/Applications/Runtime/Full/System/Delegate.cs @@ -0,0 +1,493 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +// Verbose runtime tracing +//#define ENABLE_DEBUG_PRINT + +// This class provides runtime support for managing delegates that may have +// been passed in to native code. +// +// The problem with these delegates is that the GC may move the delegate +// object without being aware of references from native code. Rather than +// pinning the objects (which currently operates at a page granularity), we +// allocate GC-stable indexes ("delegate idxs") which the native code uses in +// place of the underlying Delegate reference. +// +// The entry points from compiled code are: +// +// GetCodePtr, which allocates a short function to enter a delegate, passing +// in the delegate idx. +// +// ReleaseCodePtr, which deallocates the function and releases the delegate +// idx. +// +// The NativeDelegateTable is used to map delegate idxs to Delegate references. +// References from this table are ordinary strong references as far as the GC +// is concerned. +namespace System +{ + + using System.Reflection; + using System.Runtime.InteropServices; + using System.Runtime.CompilerServices; + using System.Threading; + + //| + public abstract partial class Delegate : ICloneable + { + private IntPtr CodePtr; + + // equals returns true IIF the delegate is not null and has the + // same target, method and invocation list as this object + //| + public override bool Equals(Object obj) + { + if (obj != null && obj is Delegate) { + Delegate d = (Delegate) obj; + if (IsStatic()) { + if (_methodPtrAux == d._methodPtrAux) { + return true; + } + } + else { + if (d._target == _target && + d._methodPtr == _methodPtr) { + return true; + } + } + } + return false; + } + + //| + public override int GetHashCode() + { + if (IsStatic()) + return this._methodPtrAux.ToInt32(); + else + return this._methodPtr.ToInt32(); + } + + // Combine creates a new delegate based upon the contents of the + // delegates passed in. + //| + public static Delegate Combine(Delegate a, Delegate b) + { + //@TODO: Should this really check to see that they are both multicast + // because it really is an error to try and combine non-multicasts? + // Spec says that it returns null and only does the check if a and b + // are both null. + + // boundary conditions -- if either (or both) delegates is null + // return the other. + if (a == null) + return b; + if (b == null) + return a; + + // Verify that the types are the same... + if (a.GetType() != b.GetType()) + throw new ArgumentException("Arg_DlgtTypeMis"); + return a.CombineImpl(b); + } + + // This method creates a new delegate based upon the passed + // array of delegates. + //| + public static Delegate Combine(Delegate[] delegates) + { + if (delegates == null || delegates.Length == 0) + return null; + + Delegate d = delegates[0]; + for (int i = 1; i < delegates.Length; i++) + d = Combine(d,delegates[i]); + return d; + } + + // Return an array of delegates that represent the invocation list. + // This is basically THIS for a Delegate. MulticastDelegates may + // have multiple members. + //| + public virtual Delegate[] GetInvocationList() { + Delegate[] d = new Delegate[1]; + d[0] = this; + return d; + } + + // This routine will return the target + //| + public Object Target + { + get {return IsStatic() ? null : _target;} + } + + + + //A quick test to see if this is a delegate to a static method. + //@ToDo: Verify that this is a sufficient test. + private bool IsStatic() { + if (_target is Delegate) { + return true; + } + return false; + } + + // This will remove the value delegate from the source delegate + // if it found. + //| + public static Delegate Remove(Delegate source, Delegate value) + { + if (source == null) + return null; + if (value == null) + return source; + return source.RemoveImpl(value); + } + + //| + public static Delegate RemoveAll(Delegate source, Delegate value) + { + Delegate newDelegate = null; + do { + newDelegate = source; + source = Remove(source, value); + } while (newDelegate != source); + return newDelegate; + } + + // This is an internal routine that is called to do the combine. We + // use this to do the combine because the Combine routines are static + // final methods. In Delegate, this simply throws a MulticastNotSupportedException + // error. Multicast delegate must implement this. + //| + protected virtual Delegate CombineImpl(Delegate d) + { + throw new MulticastNotSupportedException("Multicast_Combine"); + } + + // This is an internal routine that is called to do the remove. We use this + // to do the remove because Remove is a static final method. Here we simply + // make sure that d is equal to this and return null or this. + //| + protected virtual Delegate RemoveImpl(Delegate d) + { + if (_target == d._target && _methodPtr == d._methodPtr) + return null; + else + return this; + } + + //| + public virtual Object Clone() + { + return MemberwiseClone(); + } + + //| + public static bool operator ==(Delegate d1, Delegate d2) { + if ((Object)d1 == null) + return (Object)d2 == null; + return d1.Equals(d2); + } + + //| + public static bool operator != (Delegate d1, Delegate d2) { + if ((Object)d1 == null) + return (Object)d2 != null; + return !d1.Equals(d2); + } + + // GetCodePtr should be called by the marshalling code with the + // delegate and the address of the call back stub for that + // delegate class. + [RequiredByBartok] + private static IntPtr GetCodePtr(Delegate d,IntPtr callBackStub) + { + IntPtr codePtr = d.CodePtr; + if (codePtr == IntPtr.Zero) { + + CriticalSection.AcquireMutex(); + try { + codePtr = d.CodePtr; + if (codePtr == IntPtr.Zero) { + int idx = AllocateNativeDelegateRecord(d); + codePtr = CreateCodePtr(idx, callBackStub); + d.CodePtr = codePtr; + } + } + finally { + CriticalSection.ReleaseMutex(); + } + } + return codePtr; + } + + ////////////////////////////////////////////////////////////////////// + // + internal static IntPtr FixedAlloc(int sizetdwBytes) + { + VTable.DebugBreak(); + return IntPtr.Zero; + } + + internal static void FixedFree(IntPtr handle) + { + VTable.DebugBreak(); + } + + // Create a little piece of assembly code that adds idx onto the + // argument list (by pushing it onto the stack) and jumps to the + // call back stub. + + // The idx is added as the first argument. The calling + // convention is assumed to be STDCALL. + // + // The call back stub is a function created by the Bartok code + // generator and: + // 1. Uses the idx to get to the delegate via the NativeDelegateTable + // 2. Calls the invoke method of the delegate + + private static unsafe IntPtr CreateCodePtr(int idx, + IntPtr callBackStub) { + IntPtr buffer = FixedAlloc(32); + if (buffer == IntPtr.Zero) { + return IntPtr.Zero; + } + // The desired instructions: + // push [sp] + // mov [sp+4], idx + // jmp callBackStub + // + byte* codeBuffer = (byte *) buffer.ToPointer(); + // push [sp] + *(codeBuffer) = 0xFF; + *(codeBuffer+1) = 0x34; + *(codeBuffer+2) = 0x24; + // mov [sp+4], idx + *(codeBuffer+3) = 0xC7; + *(codeBuffer+4) = 0x44; + *(codeBuffer+5) = 0x24; + *(codeBuffer+6) = 0x04; + *((int*) (codeBuffer+7)) = idx; + // jmp callBackStub + *(codeBuffer+11) = 0xE9; + IntPtr disp = callBackStub - buffer; + *((int *) (codeBuffer+12)) = (disp - 16).ToInt32(); + // + // pad the rest of the buffer with int 3 + // + + for (int i = 16; i < 32; i++) { + *(codeBuffer+i) = 0xcc; + } + return buffer; + } + + // This method accepts a code pointer and allows the NativeDelegateRecord + // associated with it to be collected. In order to call this method + // safely it must be certain that no references to the code ptr exist + // in native code. + private static unsafe void ReleaseCodePtr(IntPtr codePtr) { + byte* codeBuffer = (byte *)codePtr.ToPointer(); + int idx = *(int*)(codeBuffer+7); + Delegate del = IdxToDelegate(idx); + del.CodePtr = IntPtr.Zero; + CriticalSection.AcquireMutex(); + try { + FreeNativeDelegateRecord(idx); + } + finally { + CriticalSection.ReleaseMutex(); + } + FixedFree(codePtr); + } + + // + // The CriticalSection protects the CodePtr fields during + // initialization and the NativeDelegateTable arrays when claiming + // indexes in them. + // + private static readonly Mutex CriticalSection; + + // + // The NativeDelegateTable is held as a triangular array, starting + // with small arrays of NativeDelegateRecord structures and using + // successively larger arrays as the table is expanded. This avoids + // frequent allocations in programs that pass a lot of delegates + // to native code, and allows an idx to map to a (table,offset) pair + // in O(log n) steps. + // + private static NativeDelegateRecord[][] NativeDelegateTable; + + // In a debug build, start the NativeDelegateTable size very small in order to + // force testing of table expansion +#if DEBUG + private const int FIRST_TABLE_SIZE = 1; +#else + private const int FIRST_TABLE_SIZE = 16; +#endif + + // + // FreeListStartIdx is the idx is the head of the list of NativeDelegateTable + // entries that have been freed. -1 indicates that the free list is empty. + // + private static int FreeListStartIdx; + private const int FREE_LIST_EMPTY = -1; + + // + // FreeExtentStartIdx is the idx of the start of the contiguous + // extent of NativeDelegateTable entries. These are logically + // considered at the end of the free list: we use entries from the free + // list in preference to using 'pristine' space from the free extent. + // + private static int FreeExtentStartIdx; + + // + // Each entry in the NativeDelegateTable currently holds an ordinary + // reference to the Delegate and an integer used to form the free list + // of unused records. We could overload a single field, but note that + // (a) we would then need to special-case the GC visiting code and + // that (b) during GC we could not simply iterate through the arrays because + // we couldn't distinguish free-list entries for allocated ones. + // + // Of course, we could steal a bit in the object reference to distinguish + // the free list, using odd values for the free list to avoid needing + // to mask the contents in IdxToDelegate. + // + // Seems like needless complexity unless we find the table is getting + // large. + // + private struct NativeDelegateRecord { + Delegate del; + int nextIdx; + + internal void Allocate(Delegate del) { + VTable.Assert(this.del == null); + this.del = del; + } + + internal void DeAllocate(int nextIdx) { + VTable.Assert(this.del != null); + this.del = null; + this.nextIdx = nextIdx; + } + + internal Delegate Delegate() { + VTable.Assert(this.del != null); + return this.del; + } + + internal int NextIdx() { + VTable.Assert(this.del == null); + return this.nextIdx; + } + } + + // Allocate a native delegate record for "del". The caller must hold + // the CriticalSection. + private static int AllocateNativeDelegateRecord(Delegate del) { + int idx; + int table; + int offset; + + DebugPrint("AllocateNativeDelegateRecord (list={0}, extent {1})\n", + __arglist(FreeListStartIdx, FreeExtentStartIdx)); + + if (FreeListStartIdx != FREE_LIST_EMPTY) { + // There's space in the free list: use that + int nextFreeIdx; + idx = FreeListStartIdx; + SplitIdx (idx, out table, out offset); + nextFreeIdx = NativeDelegateTable[table][offset].NextIdx(); + FreeListStartIdx = nextFreeIdx; + } + else { + // There's no space in the free list, use the next slot in the + // free extent. + idx = FreeExtentStartIdx; + SplitIdx (idx, out table, out offset); + FreeExtentStartIdx ++; + if (offset + 1 == NativeDelegateTable[table].Length) { + // We've used the last slot in the free extent: allocate some + // more. + int currentLength = NativeDelegateTable[table].Length; + int nextLength = currentLength * 2; + DebugPrint("AllocateNativeDelegateRecord expanding to {0} len {1}\n", + __arglist(table + 1, nextLength)); + NativeDelegateTable[table + 1] = new NativeDelegateRecord[nextLength]; + } + } + + NativeDelegateTable[table][offset].Allocate(del); + + DebugPrint("AllocateNativeDelegateRecord got {0} (list={1}, extent {2})\n", + __arglist(idx, FreeListStartIdx, FreeExtentStartIdx)); + + return idx; + } + + private static void FreeNativeDelegateRecord(int idx) { + int table; + int offset; + + DebugPrint("FreeNativeDelegateRecord {0} (list={1}, extent {2})\n", + __arglist(idx, FreeListStartIdx, FreeExtentStartIdx)); + + SplitIdx (idx, out table, out offset); + NativeDelegateTable[table][offset].DeAllocate(FreeListStartIdx); + FreeListStartIdx = idx; + } + + [RequiredByBartok] + private static Delegate IdxToDelegate(int idx) { + int table; + int offset; + Delegate result; + + SplitIdx (idx, out table, out offset); + result = NativeDelegateTable[table][offset].Delegate(); + DebugPrint("IdxToDelegate {0} -> {1}\n", __arglist(idx, result)); + + return result; + } + + private static void SplitIdx(int idx, out int table, out int offset) { + table = 0; + offset = idx; + while (offset >= NativeDelegateTable[table].Length) { + offset -= NativeDelegateTable[table].Length; + table ++; + } + DebugPrint("SplitIdx {0} -> {1}.{2}\n", __arglist(idx, table, offset)); + } + + static Delegate() { + CriticalSection = new Mutex(); + NativeDelegateTable = new NativeDelegateRecord[24][]; + NativeDelegateTable[0] = new NativeDelegateRecord[FIRST_TABLE_SIZE]; + FreeExtentStartIdx = 0; + FreeListStartIdx = FREE_LIST_EMPTY; + +#if FALSE // Enable this code to force testing of the FreeNativeDelegateRecord fn + for (int i = 0; i < FIRST_TABLE_SIZE * 2; i ++) { + int idx = AllocateNativeDelegateRecord(CriticalSection); + VTable.Assert (idx == i); + } + for (int i = 0; i < FIRST_TABLE_SIZE * 2; i ++) { + FreeNativeDelegateRecord(i); + } +#endif + } + + [System.Diagnostics.Conditional("ENABLE_DEBUG_PRINT")] + [NoInline] + internal static void DebugPrint(String v, __arglist) + { + VTable.DebugPrint(v, new ArgIterator(__arglist)); + } + } +} diff --git a/base/Kernel/System/Diagnostics/Assert.cs b/base/Applications/Runtime/Full/System/Diagnostics/Assert.cs similarity index 90% rename from base/Kernel/System/Diagnostics/Assert.cs rename to base/Applications/Runtime/Full/System/Diagnostics/Assert.cs index bc0b919..1fa322b 100644 --- a/base/Kernel/System/Diagnostics/Assert.cs +++ b/base/Applications/Runtime/Full/System/Diagnostics/Assert.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System.Diagnostics { +namespace System.Diagnostics +{ using System; using System.Reflection; using System.Runtime.CompilerServices; @@ -26,8 +27,7 @@ namespace System.Diagnostics { //| public static void AddFilter(AssertFilter filter) { - if (iFilterArraySize <= iNumOfFilters) - { + if (iFilterArraySize <= iNumOfFilters) { AssertFilter[] newFilterArray = new AssertFilter [iFilterArraySize+2]; if (iNumOfFilters > 0) @@ -46,8 +46,7 @@ namespace System.Diagnostics { //| public static void Check(bool condition, String conditionString, String message) { - if (!condition) - { + if (!condition) { Fail (conditionString, message); } } @@ -61,13 +60,11 @@ namespace System.Diagnostics { // one filter to handle the assert. int iTemp = iNumOfFilters; - while (iTemp > 0) - { + while (iTemp > 0) { AssertFilters iResult = ListOfFilters [--iTemp].AssertFailure (conditionString, message); - if (iResult == AssertFilters.FailDebug) - { + if (iResult == AssertFilters.FailDebug) { #if SINGULARITY_KERNEL DebugStub.Break(); #elif SINGULARITY_PROCESS diff --git a/base/Kernel/System/Diagnostics/AssertFilter.cs b/base/Applications/Runtime/Full/System/Diagnostics/AssertFilter.cs similarity index 95% rename from base/Kernel/System/Diagnostics/AssertFilter.cs rename to base/Applications/Runtime/Full/System/Diagnostics/AssertFilter.cs index 33e4f95..818f150 100644 --- a/base/Kernel/System/Diagnostics/AssertFilter.cs +++ b/base/Applications/Runtime/Full/System/Diagnostics/AssertFilter.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System.Diagnostics { +namespace System.Diagnostics +{ using System; diff --git a/base/Applications/Runtime/Full/System/Diagnostics/AssertFilters.cs b/base/Applications/Runtime/Full/System/Diagnostics/AssertFilters.cs new file mode 100644 index 0000000..3d96049 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Diagnostics/AssertFilters.cs @@ -0,0 +1,25 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +namespace System.Diagnostics +{ + + // + // FailDebug indicates the debugger should be invoked + // FailIgnore indicates the failure should be ignored & the + // program continued + // FailTerminate indicates that the program should be terminated + // FailContinue indicates that no decision is made - + // the previous Filter should be invoked + // + using System; + internal enum AssertFilters + { + FailDebug = 0, + FailIgnore = 1, + FailTerminate = 2, + FailContinueFilter = 3, + } +} diff --git a/base/Kernel/System/Diagnostics/ConditionalAttribute.cs b/base/Applications/Runtime/Full/System/Diagnostics/ConditionalAttribute.cs similarity index 92% rename from base/Kernel/System/Diagnostics/ConditionalAttribute.cs rename to base/Applications/Runtime/Full/System/Diagnostics/ConditionalAttribute.cs index 1bbd402..359714b 100644 --- a/base/Kernel/System/Diagnostics/ConditionalAttribute.cs +++ b/base/Applications/Runtime/Full/System/Diagnostics/ConditionalAttribute.cs @@ -5,7 +5,8 @@ // ==--== using System; -namespace System.Diagnostics { +namespace System.Diagnostics +{ //| [AttributeUsage(AttributeTargets.Method, AllowMultiple=true)] public sealed class ConditionalAttribute : Attribute diff --git a/base/Applications/Runtime/Full/System/Diagnostics/Debug.cs b/base/Applications/Runtime/Full/System/Diagnostics/Debug.cs new file mode 100644 index 0000000..d188a56 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Diagnostics/Debug.cs @@ -0,0 +1,38 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using Microsoft.Singularity; + +namespace System.Diagnostics +{ + + public sealed class Debug { + + [Conditional("DEBUG")] + public static void Assert(bool truth) + { + VTable.Assert(truth); + } + + [Conditional("DEBUG")] + public static void Assert(bool truth, string statement) + { + VTable.Assert(truth, statement); + } + + public static void AssertValidReference(Object obj) { + VTable.Assert(obj == null || obj.vtable != null); + } + + public static void WriteLine(string text) + { + if (text == null) + text = ""; + DebugStub.WriteLine(text); + } + + } +} diff --git a/base/Applications/Runtime/Full/System/Diagnostics/Debugger.cs b/base/Applications/Runtime/Full/System/Diagnostics/Debugger.cs new file mode 100644 index 0000000..5c0ce4a --- /dev/null +++ b/base/Applications/Runtime/Full/System/Diagnostics/Debugger.cs @@ -0,0 +1,41 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using Microsoft.Singularity; +using System.Runtime.CompilerServices; + +namespace System.Diagnostics +{ + + /// + /// This class provides some of the methods defined by the CLR's version of + /// the Debugger static class. This makes writing portable apps easier. + /// + public /*static*/ sealed class Debugger { + private Debugger() { } + + public static void Break() + { + DebugStub.Break(); + } + + public static bool IsAttached + { + [NoHeapAllocation] + get + { +#if SINGULARITY_KERNEL + return DebugStub.IsDebuggerPresent(); +#elif SINGULARITY_PROCESS + return true; +#else +#error No environment has been specified. +#endif + } + } + + } +} diff --git a/base/Kernel/System/Diagnostics/DebuggerAttributes.cs b/base/Applications/Runtime/Full/System/Diagnostics/DebuggerAttributes.cs similarity index 88% rename from base/Kernel/System/Diagnostics/DebuggerAttributes.cs rename to base/Applications/Runtime/Full/System/Diagnostics/DebuggerAttributes.cs index 3bd0a76..427decb 100644 --- a/base/Kernel/System/Diagnostics/DebuggerAttributes.cs +++ b/base/Applications/Runtime/Full/System/Diagnostics/DebuggerAttributes.cs @@ -3,20 +3,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: DebuggerAttributes -** -** -** Purpose: Attributes for debugger -** -** Date: Feb 01, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: DebuggerAttributes +// +// Purpose: Attributes for debugger +// +//=========================================================== -namespace System.Diagnostics { +namespace System.Diagnostics +{ //| [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)] public sealed class DebuggerStepThroughAttribute : Attribute diff --git a/base/Applications/Runtime/Full/System/DivideByZeroException.cs b/base/Applications/Runtime/Full/System/DivideByZeroException.cs new file mode 100644 index 0000000..0a18e5c --- /dev/null +++ b/base/Applications/Runtime/Full/System/DivideByZeroException.cs @@ -0,0 +1,39 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: DivideByZeroException +// +// Purpose: Exception class for bad arithmetic conditions! +// +//============================================================================= + +namespace System +{ + + using System; + using System.Runtime.CompilerServices; + + [AccessedByRuntime("referenced from halasm.asm")] + //| + public partial class DivideByZeroException : ArithmeticException { + //| + [AccessedByRuntime("referenced from halasm.asm")] + public DivideByZeroException() + : base("Arg_DivideByZero") { + } + + //| + public DivideByZeroException(String message) + : base(message) { + } + + //| + public DivideByZeroException(String message, Exception innerException) + : base(message, innerException) { + } + } +} diff --git a/base/Kernel/System/Double.cs b/base/Applications/Runtime/Full/System/Double.cs similarity index 90% rename from base/Kernel/System/Double.cs rename to base/Applications/Runtime/Full/System/Double.cs index 21391e5..e4a5243 100644 --- a/base/Kernel/System/Double.cs +++ b/base/Applications/Runtime/Full/System/Double.cs @@ -3,18 +3,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: Double -** -** -** Purpose: A representation of an IEEE double precision -** floating point number. -** -** Date: July 23, 1998 -** -===========================================================*/ -namespace System { +//============================================================ +// +// Class: Double +// +// Purpose: A representation of an IEEE double precision +// floating point number. +// +//=========================================================== +namespace System +{ using System; using System.Globalization; @@ -78,7 +76,7 @@ namespace System { //| public static bool IsNaN(double d) { - // rusa: see also Lightning\Src\ClassLibNative\Float\COMFloat::IsNAN + // See also Lightning\Src\ClassLibNative\Float\COMFloat::IsNAN ulong v = BitConverter.DoubleToUInt64Bits(d); return (((v & PositiveInfinityAsUInt64) == v) && ((v & MantissaAsUInt64) != 0)); @@ -156,7 +154,8 @@ namespace System { public static double Parse(String s, NumberStyles style) { try { return Number.ParseDouble(s, style); - } catch (FormatException) { + } + catch (FormatException) { //If we caught a FormatException, it may be from one of our special strings. //Check the three with which we're concerned and rethrow if it's not one of //those strings. @@ -186,11 +185,14 @@ namespace System { String sTrim = s.Trim(); if (sTrim.Equals(NumberFormatInfo.positiveInfinitySymbol)) { result = PositiveInfinity; - } else if (sTrim.Equals(NumberFormatInfo.negativeInfinitySymbol)) { + } + else if (sTrim.Equals(NumberFormatInfo.negativeInfinitySymbol)) { result = NegativeInfinity; - } else if (sTrim.Equals(NumberFormatInfo.nanSymbol)) { + } + else if (sTrim.Equals(NumberFormatInfo.nanSymbol)) { result = NaN; - } else + } + else return false; // We really failed } return true; diff --git a/base/Kernel/System/Empty.cs b/base/Applications/Runtime/Full/System/Empty.cs similarity index 93% rename from base/Kernel/System/Empty.cs rename to base/Applications/Runtime/Full/System/Empty.cs index e700835..7775f5c 100644 --- a/base/Kernel/System/Empty.cs +++ b/base/Applications/Runtime/Full/System/Empty.cs @@ -8,7 +8,8 @@ // This class represents an empty variant //////////////////////////////////////////////////////////////////////////////// -namespace System { +namespace System +{ using System; diff --git a/base/Kernel/System/Enum.cs b/base/Applications/Runtime/Full/System/Enum.cs similarity index 96% rename from base/Kernel/System/Enum.cs rename to base/Applications/Runtime/Full/System/Enum.cs index 4fd4333..0a50b3e 100644 --- a/base/Kernel/System/Enum.cs +++ b/base/Applications/Runtime/Full/System/Enum.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System { +namespace System +{ using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; diff --git a/base/Applications/Runtime/Full/System/Environment.cs b/base/Applications/Runtime/Full/System/Environment.cs new file mode 100644 index 0000000..3218622 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Environment.cs @@ -0,0 +1,71 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================ +// +// Class: Environment +// +// Purpose: Provides some basic access to some environment +// functionality. +// +//============================================================ +namespace System +{ + using System.Globalization; + using System.Collections; + using System.Text; + using System.Runtime.InteropServices; + using System.Runtime.CompilerServices; + using System.Reflection; + using System.Diagnostics; + using Microsoft.Singularity; + using Microsoft.Singularity.V1.Services; + + //| + public sealed class Environment { + + private Environment() {} // Prevent from begin created + + //==================================TickCount=================================== + //Action: Gets the number of ticks since the system was started. + //Returns: The number of ticks since the system was started. + //Arguments: None + //Exceptions: None + //============================================================================== + //| + public static int TickCount { + get { + return unchecked((int)(ProcessService.GetUpTime().Ticks / 10000)); + } + } + + //===================================NewLine==================================== + //Action: A property which returns the appropriate newline string for the given + // platform. + //Returns: \r\n on Win32. + //Arguments: None. + //Exceptions: None. + //============================================================================== + //| + public static String NewLine { + get { + return "\r\n"; + } + } + + //| + public static void Exit(int exitCode) { + ProcessService.Stop(exitCode); + } + + // [Bartok]: + public static String StackTrace + { + get { + return " + public delegate void EventHandler(Object sender, EventArgs e); +} diff --git a/base/Applications/Runtime/Full/System/Exception.cs b/base/Applications/Runtime/Full/System/Exception.cs new file mode 100644 index 0000000..3e266e2 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Exception.cs @@ -0,0 +1,157 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: Exception +// +// Purpose: The base class for all exceptional conditions. +// +//============================================================================= + +namespace System +{ + using System; + using System.Runtime.InteropServices; + using System.Runtime.CompilerServices; + using System.Diagnostics; + using System.Text; + using System.Reflection; +#if SINGULARITY_KERNEL + using Microsoft.Singularity; +#elif SINGULARITY_PROCESS + using Microsoft.Singularity.V1.Services; +#endif // SINGULARITY_PROCESS + + + //| + [AccessedByRuntime("referenced from halexn.cpp")] + public partial class Exception + { + //| + public Exception() { + _message = null; +#if SINGULARITY_KERNEL + //DebugStub.Break(); +#elif SINGULARITY_PROCESS + //DebugService.Break(); +#endif // SINGULARITY_PROCESS + } + + // Creates a new Exception. All derived classes should + // provide this constructor. + // Note: the stack trace is not started until the exception + // is thrown + // + //| + public Exception (String message, Exception innerException) + { + // TODO: The innerException will be provided by the runtime + // in the M9 time frame. Until then, we need this method. + _message = message; + _innerException = innerException; + } + + //| + public virtual String Message { + get { + if (_message == null) { + return "Exception_WasThrown"; + } + else { + return _message; + } + } + } + + private String GetClassName() { + return this.vtable.vtableType.FullName; + } + + // Retrieves the lowest exception (inner most) for the given Exception. + // This will traverse exceptions using the innerException property. + // + //| + public virtual Exception GetBaseException() + { + Exception inner = InnerException; + Exception back = this; + + while (inner != null) { + back = inner; + inner = inner.InnerException; + } + + return back; + } + + // Returns the inner exception contained in this exception + // + //| + public Exception InnerException { + get { return _innerException; } + } + + //| + public override String ToString() { + String message = Message; + + if (_innerException != null) { + message = message + " ---> " + _innerException.ToString() + "\r\n" + " " + "Exception_EndOfInnerExceptionStack"; + } + + return message; + } + + [AccessedByRuntime("referenced from halasm.asm")] + internal static unsafe bool IsUnlinkStack(UIntPtr throwAddr) { + UIntPtr unlinkBegin; + UIntPtr unlinkLimit; + + fixed (byte *begin = &Microsoft.Singularity.Memory.Stacks.LinkStackBegin) { + unlinkBegin = (UIntPtr)begin; + } + fixed (byte *limit = &Microsoft.Singularity.Memory.Stacks.LinkStackLimit) { + unlinkLimit = (UIntPtr)limit; + } + + if (throwAddr >= unlinkBegin && throwAddr <= unlinkLimit) { + return true; + } + else { + return false; + } + } + + // This is the function that users can override + + // BUGBUG: provide a better default (probably deep copy). The + // current one does not deal with references well, and so, is + // not even a good shallow copy (it should at least null out the + // references). + virtual protected Exception cloneForUndo() { + return (Exception)this.MemberwiseClone(); + } + + [AccessedByRuntime("referenced from halexn.cpp")] + internal String _message; + + private Exception _innerException; + + [AccessedByRuntime("referenced from halexn.cpp")] + private UIntPtr _throwAddress; + + [AccessedByRuntime("referenced from halexn.cpp")] + private bool _notifiedDebugger; // True if debugger first chance already thrown. + + // [Bartok]: + public string StackTrace + { + get { + return ""; + } + } + } +} diff --git a/base/Kernel/System/FlagsAttribute.cs b/base/Applications/Runtime/Full/System/FlagsAttribute.cs similarity index 94% rename from base/Kernel/System/FlagsAttribute.cs rename to base/Applications/Runtime/Full/System/FlagsAttribute.cs index 7c5e639..3ab7dae 100644 --- a/base/Kernel/System/FlagsAttribute.cs +++ b/base/Applications/Runtime/Full/System/FlagsAttribute.cs @@ -5,7 +5,8 @@ // ==--== //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -namespace System { +namespace System +{ using System; // Custom attribute to indicate that the enum diff --git a/base/Applications/Runtime/Full/System/FormatException.cs b/base/Applications/Runtime/Full/System/FormatException.cs new file mode 100644 index 0000000..c44f19e --- /dev/null +++ b/base/Applications/Runtime/Full/System/FormatException.cs @@ -0,0 +1,34 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================ +// +// Class: FormatException +// +// Purpose: Exception to designate an illegal argument to Format. +// +//=========================================================== +namespace System +{ + + using System; + //| + public class FormatException : SystemException { + //| + public FormatException() + : base("Arg_FormatException") { + } + + //| + public FormatException(String message) + : base(message) { + } + + //| + public FormatException(String message, Exception innerException) + : base(message, innerException) { + } + } +} diff --git a/base/Kernel/System/Globalization/CharacterInfo.cs b/base/Applications/Runtime/Full/System/Globalization/CharacterInfo.cs similarity index 91% rename from base/Kernel/System/Globalization/CharacterInfo.cs rename to base/Applications/Runtime/Full/System/Globalization/CharacterInfo.cs index 318bcc9..52864d5 100644 --- a/base/Kernel/System/Globalization/CharacterInfo.cs +++ b/base/Applications/Runtime/Full/System/Globalization/CharacterInfo.cs @@ -7,16 +7,14 @@ // // Class: CharacterInfo // -// // Purpose: This class implements a set of methods for retrieving // character type information. Character type information is // independent of culture and region. // -// Date: August 12, 1998 -// //////////////////////////////////////////////////////////////////////////// -namespace System.Globalization { +namespace System.Globalization +{ using System; using System.Runtime.CompilerServices; @@ -76,13 +74,13 @@ namespace System.Globalization { } - /*=================================IsMark========================== - **Action: Returns true if and only if the character c has one of the properties NonSpacingMark, SpacingCombiningMark, - ** or EnclosingMark. Marks usually modify one or more other - **Returns: - **Arguments: - **Exceptions: - ============================================================================*/ + //=================================IsMark========================== + //Action: Returns true if and only if the character c has one of the properties NonSpacingMark, SpacingCombiningMark, + // or EnclosingMark. Marks usually modify one or more other + //Returns: + //Arguments: + //Exceptions: + //============================================================================ public static bool IsMark(char ch) { UnicodeCategory uc = GetUnicodeCategory(ch); @@ -91,14 +89,14 @@ namespace System.Globalization { || uc == UnicodeCategory.EnclosingMark); } - /*=================================IsNumber========================== - **Action: Returns true if and only if the character c has one of the properties DecimalDigitNumber, - ** LetterNumber, or OtherNumber. Use the GetNumericValue method to determine the numeric value. - **Returns: - **Arguments: - **Exceptions: - **Note: GetNuemricValue is not yet implemented. - ============================================================================*/ + //=================================IsNumber========================== + //Action: Returns true if and only if the character c has one of the properties DecimalDigitNumber, + // LetterNumber, or OtherNumber. Use the GetNumericValue method to determine the numeric value. + //Returns: + //Arguments: + //Exceptions: + //Note: GetNuemricValue is not yet implemented. + //============================================================================ public static bool IsNumber(char ch) { UnicodeCategory uc = GetUnicodeCategory(ch); return (uc == UnicodeCategory.DecimalDigitNumber @@ -106,49 +104,49 @@ namespace System.Globalization { || uc == UnicodeCategory.OtherNumber); } - /*=================================IsDigit========================== - **Action: Returns true if and only if the character c has the property DecimalDigitNumber. - ** Use the GetNumericValue method to determine the numeric value. - **Returns: - **Arguments: - **Exceptions: - **Note: GetNuemricValue is not yet implemented. - ============================================================================*/ + //=================================IsDigit========================== + //Action: Returns true if and only if the character c has the property DecimalDigitNumber. + // Use the GetNumericValue method to determine the numeric value. + //Returns: + //Arguments: + //Exceptions: + //Note: GetNuemricValue is not yet implemented. + //============================================================================ public static bool IsDigit(char ch) { UnicodeCategory uc = GetUnicodeCategory(ch); return (uc == UnicodeCategory.DecimalDigitNumber); } - /*=================================IsSeparator========================== - **Action: Returns true if and only if the character c has one of the properties SpaceSeparator, LineSeparator - **or ParagraphSeparator. - **Returns: - **Arguments: - **Exceptions: - ============================================================================*/ + //=================================IsSeparator========================== + //Action: Returns true if and only if the character c has one of the properties SpaceSeparator, LineSeparator + //or ParagraphSeparator. + //Returns: + //Arguments: + //Exceptions: + //============================================================================ public static bool IsSeparator(char ch) { UnicodeCategory uc = GetUnicodeCategory(ch); return (uc == UnicodeCategory.SpaceSeparator || uc == UnicodeCategory.LineSeparator || uc == UnicodeCategory.ParagraphSeparator); } - /*=================================IsControl========================== - **Action: Returns true if and only if the character c has the property Control. - **Returns: - **Arguments: - **Exceptions: - ============================================================================*/ + //=================================IsControl========================== + //Action: Returns true if and only if the character c has the property Control. + //Returns: + //Arguments: + //Exceptions: + //============================================================================ public static bool IsControl(char ch) { return (GetUnicodeCategory(ch) == UnicodeCategory.Control); } - /*=================================IsSurrogate========================== - **Action: Returns true if and only if the character c has the property PrivateUse. - **Returns: - **Arguments: - **Exceptions: - ============================================================================*/ + //=================================IsSurrogate========================== + //Action: Returns true if and only if the character c has the property PrivateUse. + //Returns: + //Arguments: + //Exceptions: + //============================================================================ public static bool IsSurrogate(char ch) { return (GetUnicodeCategory(ch) == UnicodeCategory.Surrogate); @@ -292,17 +290,17 @@ namespace System.Globalization { return (false); } - /*=================================IsCombiningCharacter========================== - **Action: Returns if the specified character is a combining character. - **Returns: - ** TRUE if ch is a combining character. - **Arguments: - ** ch a Unicode character - **Exceptions: - ** None - **Notes: - ** Used by StringInfo.ParseCombiningCharacter. - ============================================================================*/ + //=================================IsCombiningCharacter========================== + //Action: Returns if the specified character is a combining character. + //Returns: + // TRUE if ch is a combining character. + //Arguments: + // ch a Unicode character + //Exceptions: + // None + //Notes: + // Used by StringInfo.ParseCombiningCharacter. + //============================================================================ internal static bool IsCombiningCharacter(char ch) { UnicodeCategory uc = CharacterInfo.GetUnicodeCategory(ch); return ( diff --git a/base/Kernel/System/Globalization/CompareInfo.cs b/base/Applications/Runtime/Full/System/Globalization/CompareInfo.cs similarity index 95% rename from base/Kernel/System/Globalization/CompareInfo.cs rename to base/Applications/Runtime/Full/System/Globalization/CompareInfo.cs index f38007b..86d620f 100644 --- a/base/Kernel/System/Globalization/CompareInfo.cs +++ b/base/Applications/Runtime/Full/System/Globalization/CompareInfo.cs @@ -7,15 +7,13 @@ // // Class: CompareInfo // -// // Purpose: This class implements a set of methods for comparing // strings. // -// Date: August 12, 1998 -// //////////////////////////////////////////////////////////////////////////// -namespace System.Globalization { +namespace System.Globalization +{ // // NOTE NOTE NOTE @@ -132,7 +130,8 @@ namespace System.Globalization { // equal, then the longer string is greater. return CompareOrdinal(string1, 0, string1.Length, string2, 0, string2.Length); - } else { + } + else { throw new ArgumentException("options", "Argument_CompareOptionOrdinal"); } } @@ -147,7 +146,7 @@ namespace System.Globalization { // change is for adding the null-embedded string support in // CompareString(). - // (markples) REVIEW: CLR has C# pass in a C++ object pointer that + // REVIEW: CLR has C# pass in a C++ object pointer that // it finds this CompareString() method in. Do we need to do this, // or do we have enough state in the CompareInfo object? @@ -228,19 +227,19 @@ namespace System.Globalization { // OffsetX >= 0 // LengthX >= 0 || LengthX == -1 (that is, LengthX >= -1) // If LengthX >= 0, OffsetX + LengthX <= realLenX - if (offset1<0) { + if (offset1 < 0) { throw new ArgumentOutOfRangeException ("offset1", "ArgumentOutOfRange_Index"); } - if (offset2<0) { + if (offset2 < 0) { throw new ArgumentOutOfRangeException ("offset2", "ArgumentOutOfRange_Index"); } - if (length1 >= 0 && length1>realLen1 - offset1) { + if (length1 >= 0 && length1 > realLen1 - offset1) { throw new ArgumentOutOfRangeException ("string1", "ArgumentOutOfRange_OffsetLength"); } - if (length2 >= 0 && length2>realLen2 - offset2){ + if (length2 >= 0 && length2 > realLen2 - offset2) { throw new ArgumentOutOfRangeException ("string2", "ArgumentOutOfRange_OffsetLength"); } @@ -296,10 +295,10 @@ namespace System.Globalization { if (count > Length2) count = Length2; - // markples: simplified and removed optimizations + // simplified and removed optimizations while (--count >= 0) { - if(string1[strIndex1] != string2[strIndex2]) { + if (string1[strIndex1] != string2[strIndex2]) { return string1[strIndex1] - string2[strIndex2]; } ++strIndex1; @@ -310,12 +309,12 @@ namespace System.Globalization { } - /*============================CompareString========================== - **Action: Compare two string in a linguistic way. - **Returns: - **Arguments: - **Exceptions: - =======================================================================*/ + //============================CompareString========================== + //Action: Compare two string in a linguistic way. + //Returns: + //Arguments: + //Exceptions: + //======================================================================= private static int CompareString(CompareOptions dwCmpFlags, String lpString1, int lpStringIndex1, @@ -385,8 +384,8 @@ namespace System.Globalization { // _ASSERTE(pString1 != null && pString2 != null); // Do a wchar by wchar compare. - // markples: collapsed 8 times unrolled loop - while((cchCount1 != 0 && cchCount2 != 0) && + // collapsed 8 times unrolled loop + while ((cchCount1 != 0 && cchCount2 != 0) && (pString1[pStringIndex1] == pString2[pStringIndex2])) { pStringIndex1++; pStringIndex2++; @@ -411,7 +410,8 @@ namespace System.Globalization { // no flags and the ignore case flag. if (dwCmpFlags == CompareOptions.None) { Mask = CMP_MASKOFF_NONE; - } else { + } + else { Mask = CMP_MASKOFF_CW; } @@ -472,7 +472,8 @@ namespace System.Globalization { if (fIgnorePunct) { pStringIndex1++; cchCount1--; fContinue = true; - } else if (sm2 != ScriptMember_Punctuation) { + } + else if (sm2 != ScriptMember_Punctuation) { // The character in the second string is // NOT punctuation. if (WhichPunct2 != 0) { @@ -485,7 +486,8 @@ namespace System.Globalization { // the string. WhichPunct2 = CSTR_GREATER_THAN; fIgnorePunct = true; - } else { + } + else { // Set WP 1 to show that string 2 is smaller, // and that string 1 has had a punctuation // char - since no punctuation chars have @@ -519,7 +521,8 @@ namespace System.Globalization { // Pointer 2 will be advanced after if-else // statement. ; - } else if (sm1 != ScriptMember_Punctuation) { + } + else if (sm1 != ScriptMember_Punctuation) { // The character in the first string is // NOT punctuation. if (WhichPunct1 != 0) { @@ -533,7 +536,8 @@ namespace System.Globalization { // string. WhichPunct1 = CSTR_LESS_THAN; fIgnorePunct = true; - } else { + } + else { // Set WP 2 to show that string 1 is smaller, // and that string 2 has had a punctuation // char - since no punctuation chars have @@ -543,7 +547,8 @@ namespace System.Globalization { // Pointer 2 will be advanced after if-else // statement. - } else { + } + else { // Both code points are punctuation. // // See if either of the strings has encountered @@ -553,12 +558,14 @@ namespace System.Globalization { // it should be the smaller string (since // both have punctuation chars). WhichPunct1 = CSTR_LESS_THAN; - } else if (WhichPunct2 != 0) { + } + else if (WhichPunct2 != 0) { // String 2 has had a punctuation char, so // it should be the smaller string (since // both have punctuation chars). WhichPunct2 = CSTR_GREATER_THAN; - } else { + } + else { // Position is the same, so compare the // special weights. Set WhichPunct1 to // the smaller special weight. @@ -658,7 +665,8 @@ namespace System.Globalization { return (WhichPunct2); } return (CSTR_EQUAL); - } else { + } + else { // String 2 is longer. pString1 = pString2; pStringIndex1 = pStringIndex2; @@ -944,13 +952,14 @@ namespace System.Globalization { } if (count == -1) { count = stringLen - startIndex; - } else if (count < 0 || count + startIndex > stringLen) { + } + else if (count < 0 || count + startIndex > stringLen) { throw new ArgumentOutOfRangeException("count"); } int endIndex = startIndex + count - 1; bool fAscii = false; if (options != CompareOptions.Ordinal) { - if(source.StringState == StringState.Undetermined) { + if (source.StringState == StringState.Undetermined) { source.CheckHighChars(); } fAscii = ((source.IsFastIndex() && value < 0x7f) || value == 0); @@ -963,7 +972,8 @@ namespace System.Globalization { } } return -1; - } else if (fAscii && options == CompareOptions.IgnoreCase) { + } + else if (fAscii && options == CompareOptions.IgnoreCase) { char lowerValue = value; if (value >= 'A' && value <= 'Z') { lowerValue = (char) (value | 0x20); @@ -978,7 +988,8 @@ namespace System.Globalization { } } return -1; - } else { + } + else { return SlowIndexOfString(source, String.StringCTOR(value, 1), startIndex, endIndex, options); } @@ -998,7 +1009,8 @@ namespace System.Globalization { if (sourceLen == 0) { if (valueLen == 0) { return 0; - } else { + } + else { return -1; } } @@ -1007,7 +1019,8 @@ namespace System.Globalization { } if (count == -1) { count = sourceLen - startIndex; - } else if (count < 0 || count + startIndex > sourceLen) { + } + else if (count < 0 || count + startIndex > sourceLen) { throw new ArgumentOutOfRangeException("count"); } if (valueLen == 0) { @@ -1015,7 +1028,7 @@ namespace System.Globalization { } int endIndex = startIndex + count - 1; - // NOTE Currently we do not support cultures, so make + // NOTE: Currently we do not support cultures, so make // requests for default comparisons the same as 'Ordinal' (fast, // unicode-value-based). When we add proper support for // cultures, take out the == CompareOptions.None case. @@ -1041,7 +1054,7 @@ namespace System.Globalization { for (int i = startIndex; i <= endPattern; i++) { int j = 0; - while (source[i+j] == value[j]) { + while (source[i + j] == value[j]) { j++; if (j == valueLen) { return i; @@ -1059,27 +1072,27 @@ namespace System.Globalization { char srcChar, patChar; int endPattern = endIndex - pattern.Length + 1; - if(endPattern < 0) { + if (endPattern < 0) { return -1; } - for(int ctrSrc = startIndex; ctrSrc <= endPattern; ctrSrc++) { + for (int ctrSrc = startIndex; ctrSrc <= endPattern; ctrSrc++) { int ctrPat; - for(ctrPat = 0; ctrPat < pattern.Length; ctrPat++) { + for (ctrPat = 0; ctrPat < pattern.Length; ctrPat++) { srcChar = source[ctrSrc + ctrPat]; - if(srcChar >= 'A' && srcChar <= 'Z') { + if (srcChar >= 'A' && srcChar <= 'Z') { srcChar |= (char)0x20; } patChar = pattern[ctrPat]; - if(patChar >= 'A' && patChar <= 'Z') { + if (patChar >= 'A' && patChar <= 'Z') { patChar |= (char)0x20; } - if(srcChar != patChar) { + if (srcChar != patChar) { break; } } - if(ctrPat == pattern.Length) { + if (ctrPat == pattern.Length) { return ctrSrc; } } @@ -1243,14 +1256,16 @@ namespace System.Globalization { if (count == -1) { endIndex = 0; count = startIndex + 1; // REVIEW: not the same as BCL code - } else if (count < 0 || count > startIndex + 1) { + } + else if (count < 0 || count > startIndex + 1) { throw new ArgumentOutOfRangeException("count"); - } else { + } + else { endIndex = startIndex - count + 1; } bool fAscii = false; if (options != CompareOptions.Ordinal) { - if(source.StringState == StringState.Undetermined) { + if (source.StringState == StringState.Undetermined) { source.CheckHighChars(); } fAscii = (source.IsFastIndex() && value < 0x7f) || value == 0; @@ -1263,7 +1278,8 @@ namespace System.Globalization { } } return -1; - } else if (fAscii && options == CompareOptions.IgnoreCase) { + } + else if (fAscii && options == CompareOptions.IgnoreCase) { char lowerValue = value; if (value >= 'A' && value <= 'Z') { lowerValue = (char) (value | 0x20); @@ -1278,8 +1294,9 @@ namespace System.Globalization { } } return -1; - } else { - // probably wrong, but needed for Bartok. + } + else { + // Probably wrong, but needed for Bartok. // throw new Exception("CompareInfo.SlowLastIndexOfChar not implemented in Bartok!"); for (int i = startIndex; i >= endIndex; i--) { if (source[i] == value) { @@ -1306,7 +1323,8 @@ namespace System.Globalization { if (valueLen == 0) { matchEndIndex = 0; return 0; - } else { + } + else { matchEndIndex = -1; // REVIEW: not the same as BCL return -1; } @@ -1318,9 +1336,11 @@ namespace System.Globalization { if (count == -1) { endIndex = 0; count = startIndex + 1; // REVIEW: not the same as BCL - } else if (count < 0 || count - 1 > startIndex) { + } + else if (count < 0 || count - 1 > startIndex) { throw new ArgumentOutOfRangeException("count"); - } else { + } + else { endIndex = startIndex - count + 1; } if (valueLen == 0) { @@ -1332,7 +1352,7 @@ namespace System.Globalization { value, out matchEndIndex); } - // NOTE Currently we do not support cultures, so make + // NOTE: Currently we do not support cultures, so make // requests for default comparisons the same as 'Ordinal' (fast, // unicode-value-based). When we add proper support for // cultures, take out the == CompareOptions.None case. diff --git a/base/Kernel/System/Globalization/CultureInfo.cs b/base/Applications/Runtime/Full/System/Globalization/CultureInfo.cs similarity index 96% rename from base/Kernel/System/Globalization/CultureInfo.cs rename to base/Applications/Runtime/Full/System/Globalization/CultureInfo.cs index 37aa036..2b87594 100644 --- a/base/Kernel/System/Globalization/CultureInfo.cs +++ b/base/Applications/Runtime/Full/System/Globalization/CultureInfo.cs @@ -7,18 +7,16 @@ // // Class: CultureInfo // -// // Purpose: This class represents the software preferences of a particular // culture or community. It includes information such as the // language, writing system, and a calendar used by the culture // as well as methods for common operations such as printing // dates and sorting strings. // -// Date: March 31, 1999 -// //////////////////////////////////////////////////////////////////////////// -namespace System.Globalization { +namespace System.Globalization +{ using System; using System.Threading; using System.Runtime.CompilerServices; diff --git a/base/Kernel/System/Globalization/NumberFormatInfo.cs b/base/Applications/Runtime/Full/System/Globalization/NumberFormatInfo.cs similarity index 95% rename from base/Kernel/System/Globalization/NumberFormatInfo.cs rename to base/Applications/Runtime/Full/System/Globalization/NumberFormatInfo.cs index 0330d8c..d240841 100644 --- a/base/Kernel/System/Globalization/NumberFormatInfo.cs +++ b/base/Applications/Runtime/Full/System/Globalization/NumberFormatInfo.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System.Globalization { +namespace System.Globalization +{ using System; //| diff --git a/base/Kernel/System/Globalization/NumberStyles.cs b/base/Applications/Runtime/Full/System/Globalization/NumberStyles.cs similarity index 89% rename from base/Kernel/System/Globalization/NumberStyles.cs rename to base/Applications/Runtime/Full/System/Globalization/NumberStyles.cs index 7d37055..6f39b81 100644 --- a/base/Kernel/System/Globalization/NumberStyles.cs +++ b/base/Applications/Runtime/Full/System/Globalization/NumberStyles.cs @@ -3,18 +3,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Enum: NumberStyles.cool -** -** -** Purpose: Contains valid formats for Numbers recognized by -** the Number class' parsing code. -** -** Date: August 9, 1999 -** -===========================================================*/ -namespace System.Globalization { +//============================================================ +// +// Enum: NumberStyles.cool +// +// Purpose: Contains valid formats for Numbers recognized by +// the Number class' parsing code. +// +//=========================================================== +namespace System.Globalization +{ using System; //| diff --git a/base/Kernel/System/Globalization/TextInfo.cs b/base/Applications/Runtime/Full/System/Globalization/TextInfo.cs similarity index 98% rename from base/Kernel/System/Globalization/TextInfo.cs rename to base/Applications/Runtime/Full/System/Globalization/TextInfo.cs index ac92144..7136c63 100644 --- a/base/Kernel/System/Globalization/TextInfo.cs +++ b/base/Applications/Runtime/Full/System/Globalization/TextInfo.cs @@ -7,16 +7,14 @@ // // Class: TextInfo // -// // Purpose: This Class defines behaviors specific to a writing system. // A writing system is the collection of scripts and // orthographic rules required to represent a language as text. // -// Date: March 31, 1999 -// //////////////////////////////////////////////////////////////////////////// -namespace System.Globalization { +namespace System.Globalization +{ using System.Text; using System; using System.Runtime.CompilerServices; diff --git a/base/Kernel/System/Globalization/UnicodeCategory.cs b/base/Applications/Runtime/Full/System/Globalization/UnicodeCategory.cs similarity index 92% rename from base/Kernel/System/Globalization/UnicodeCategory.cs rename to base/Applications/Runtime/Full/System/Globalization/UnicodeCategory.cs index 96b6b8d..6a7c68e 100644 --- a/base/Kernel/System/Globalization/UnicodeCategory.cs +++ b/base/Applications/Runtime/Full/System/Globalization/UnicodeCategory.cs @@ -3,17 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: UnicodeCategory -** -** -** Purpose: -** -** Date: -** -============================================================*/ -namespace System.Globalization { +//============================================================ +// +// Class: UnicodeCategory +// +// Purpose: +// +//============================================================ +namespace System.Globalization +{ //| public enum UnicodeCategory { //| diff --git a/base/Kernel/System/Guid.cs b/base/Applications/Runtime/Full/System/Guid.cs similarity index 85% rename from base/Kernel/System/Guid.cs rename to base/Applications/Runtime/Full/System/Guid.cs index f314556..7df3cc3 100644 --- a/base/Kernel/System/Guid.cs +++ b/base/Applications/Runtime/Full/System/Guid.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System { +namespace System +{ using System; using System.Diagnostics; @@ -43,7 +44,7 @@ namespace System { //| public Guid(byte[] b) { - if (b==null) + if (b == null) throw new ArgumentNullException("b"); if (b.Length != 16) throw new ArgumentException(String.Format("Arg_GuidArrayCtor", "16")); @@ -95,9 +96,13 @@ namespace System { _k = 0; if (!blank) { // Create a new Guid... - VTable.DebugPrint("CreateGuid.Guid()\n"); + Counter++; + _k = (byte) (Counter & 0xff); + _j = (byte) (Counter >> 8); + VTable.DebugPrint("CreateGuid.Guid(" + Counter + ")\n"); } } + private static int Counter = 0; // Creates a new guid based on the value in the string. The value is made up // of hex digits speared by the dash ("-"). The string may begin and end with @@ -110,7 +115,7 @@ namespace System { //| public Guid(String g) { - if (g==null) + if (g == null) throw new ArgumentNullException("g", "ArgumentNull_String"); int startPos=0; @@ -120,25 +125,21 @@ namespace System { byte []dstartArray; byte []dArray; - try - { + try { // Check if it's of the form dddddddd-dddd-dddd-dddd-dddddddddddd - if(g.IndexOf('-', 0) >= 0) - { + if (g.IndexOf('-', 0) >= 0) { String guidString = g.Trim(); //Remove Whitespace // check to see that it's the proper length - if (guidString[0]=='{') { - if (guidString.Length!=38 || guidString[37]!='}') { + if (guidString[0] =='{') { + if (guidString.Length != 38 || guidString[37]!='}') { throw new FormatException("Format_GuidInvLen"); } startPos=1; } - else if (guidString[0]=='(') - { - if (guidString.Length!=38 || guidString[37]!=')') - { + else if (guidString[0] =='(') { + if (guidString.Length != 38 || guidString[37]!=')') { throw new FormatException("Format_GuidInvLen"); } startPos=1; @@ -164,7 +165,7 @@ namespace System { spArray[0]++; //Increment past the '-'; startPos=spArray[0]; _dTemp = ParseNumbers.StringToLong(guidString, 16, 0, spArray); - if (spArray[0]-startPos!=12) { + if (spArray[0] - startPos != 12) { throw new FormatException(String.Format("Format_GuidInvLen")); } dstartArray = BitConverter.GetBytes(_dStartTemp); @@ -179,7 +180,8 @@ namespace System { _i = dArray[2]; _j = dArray[1]; _k = dArray[0]; - } else { + } + else { _d = dstartArray[0]; _e = dstartArray[1]; _f = dArray[0]; @@ -193,8 +195,7 @@ namespace System { // Else check if it is of the form // {0xdddddddd,0xdddd,0xdddd,{0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd}} - else if(g.IndexOf('{', 0) >= 0) - { + else if(g.IndexOf('{', 0) >= 0) { int numStart = 0; int numLen = 0; @@ -205,17 +206,17 @@ namespace System { g = EatAllWhitespace(g); // Check for leading '{' - if(g[0] != '{') + if (g[0] != '{') throw new FormatException("Format_GuidBrace"); // Check for '0x' - if(!IsHexPrefix(g, 1)) + if (!IsHexPrefix(g, 1)) throw new FormatException(String.Format("Format_GuidHexPrefix", "{0xdddddddd, etc}")); // Find the end of this hex number (since it is not fixed length) numStart = 3; numLen = g.IndexOf(',', numStart) - numStart; - if(numLen <= 0) + if (numLen <= 0) throw new FormatException("Format_GuidComma"); // Read in the number @@ -224,13 +225,13 @@ namespace System { ParseNumbers.IsTight); // tight parsing // Check for '0x' - if(!IsHexPrefix(g, numStart+numLen+1)) + if (!IsHexPrefix(g, numStart + numLen + 1)) throw new FormatException(String.Format("Format_GuidHexPrefix", "{0xdddddddd, 0xdddd, etc}")); // +3 to get by ',0x' numStart = numStart + numLen + 3; numLen = g.IndexOf(',', numStart) - numStart; - if(numLen <= 0) + if (numLen <= 0) throw new FormatException("Format_GuidComma"); // Read in the number @@ -240,13 +241,13 @@ namespace System { ParseNumbers.IsTight); // tight parsing // Check for '0x' - if(!IsHexPrefix(g, numStart+numLen+1)) + if (!IsHexPrefix(g, numStart + numLen + 1)) throw new FormatException(String.Format("Format_GuidHexPrefix", "{0xdddddddd, 0xdddd, 0xdddd, etc}")); // +3 to get by ',0x' numStart = numStart + numLen + 3; numLen = g.IndexOf(',', numStart) - numStart; - if(numLen <= 0) + if (numLen <= 0) throw new FormatException("Format_GuidComma"); // Read in the number @@ -256,24 +257,23 @@ namespace System { ParseNumbers.IsTight); // tight parsing // Check for '{' - if(g.Length <= numStart+numLen+1 || g[numStart+numLen+1] != '{') + if (g.Length <= numStart + numLen + 1 || g[numStart + numLen + 1] != '{') throw new FormatException("Format_GuidBrace"); // Prepare for loop numLen++; byte[] bytes = new byte[8]; - for(int i = 0; i < 8; i++) - { + for (int i = 0; i < 8; i++) { // Check for '0x' - if(!IsHexPrefix(g, numStart+numLen+1)) + if (!IsHexPrefix(g, numStart + numLen + 1)) throw new FormatException(String.Format("Format_GuidHexPrefix", "{... { ... 0xdd, ...}}")); // +3 to get by ',0x' or '{0x' for first case numStart = numStart + numLen + 3; // Calculate number length - if(i < 7) // first 7 cases + if (i < 7) // first 7 cases { numLen = g.IndexOf(',', numStart) - numStart; } @@ -281,7 +281,7 @@ namespace System { { numLen = g.IndexOf('}', numStart) - numStart; } - if(numLen <= 0) + if (numLen <= 0) throw new FormatException("Format_GuidComma"); // Read in the number @@ -300,7 +300,7 @@ namespace System { _k = bytes[7]; // Check for last '}' - if(numStart+numLen+1 >= g.Length || g[numStart+numLen+1] != '}') + if (numStart + numLen + 1 >= g.Length || g[numStart + numLen + 1] != '}') throw new FormatException("Format_GuidEndBrace"); return; @@ -310,7 +310,7 @@ namespace System { { String guidString = g.Trim(); //Remove Whitespace - if(guidString.Length != 32) { + if (guidString.Length != 32) { throw new FormatException("Format_GuidInvLen"); } @@ -333,7 +333,7 @@ namespace System { startPos += 4; spArray[0] = startPos; _dTemp = ParseNumbers.StringToLong(guidString, 16, startPos, spArray); - if (spArray[0]-startPos!=12) { + if (spArray[0] - startPos != 12) { throw new FormatException(String.Format("Format_GuidInvLen")); } dstartArray = BitConverter.GetBytes(_dStartTemp); @@ -348,7 +348,8 @@ namespace System { _i = dArray[2]; _j = dArray[1]; _k = dArray[0]; - } else { + } + else { _d = dstartArray[0]; _e = dstartArray[1]; _f = dArray[0]; @@ -360,8 +361,7 @@ namespace System { } } } - catch(IndexOutOfRangeException) - { + catch (IndexOutOfRangeException) { throw new FormatException("Format_GuidUnrecognized"); } } @@ -371,10 +371,10 @@ namespace System { //| public Guid(int a, short b, short c, byte[] d) { - if (d==null) + if (d == null) throw new ArgumentNullException("d"); // Check that array is not too big - if(d.Length != 8) + if (d.Length != 8) throw new ArgumentException(String.Format("Arg_GuidArrayCtor", "8")); _a = a; @@ -413,17 +413,15 @@ namespace System { private static int TryParse(String str, int []parsePos, int requiredLength) { int currStart = parsePos[0]; int retVal; - try - { + try { retVal = ParseNumbers.StringToInt(str, 16, 0, parsePos); } - catch (FormatException) - { + catch (FormatException) { throw new FormatException("Format_GuidUnrecognized"); } //If we didn't parse enough characters, there's clearly an error. - if (parsePos[0]-currStart!=requiredLength) { + if (parsePos[0] - currStart != requiredLength) { throw new FormatException("Format_GuidUnrecognized"); } return retVal; @@ -436,11 +434,9 @@ namespace System { char curChar; // Now get each char from str and if it is not whitespace add it to chArr - for(int i = 0; i < str.Length; i++) - { + for (int i = 0; i < str.Length; i++) { curChar = str[i]; - if(!Char.IsWhiteSpace(curChar)) - { + if (!Char.IsWhiteSpace(curChar)) { chArr[newLength++] = curChar; } } @@ -451,7 +447,7 @@ namespace System { private static bool IsHexPrefix(String str, int i) { - if(str[i] == '0' && (Char.ToLower(str[i+1]) == 'x')) + if (str[i] == '0' && (Char.ToLower(str[i + 1]) == 'x')) return true; else return false; @@ -464,13 +460,13 @@ namespace System { { byte[] g = new byte[16]; byte[] tmp = BitConverter.GetBytes(_a); - for(int i=0; i<4; i++) + for (int i = 0; i < 4; i++) g[i] = tmp[i]; tmp = BitConverter.GetBytes(_b); - for(int i=0; i<2; i++) + for (int i = 0; i < 2; i++) g[i+4] = tmp[i]; tmp = BitConverter.GetBytes(_c); - for(int i=0; i<2; i++) + for (int i = 0; i < 2; i++) g[i+6] = tmp[i]; g[8] = _d; @@ -506,16 +502,16 @@ namespace System { { Guid g; // Check that o is a Guid first - if(o == null || !(o is Guid)) + if (o == null || !(o is Guid)) return false; else g = (Guid) o; // Now compare each of the elements - if(g._a != _a) + if (g._a != _a) return false; - if(g._b != _b) + if (g._b != _b) return false; - if(g._c != _c) + if (g._c != _c) return false; if (g._d != _d) return false; @@ -538,7 +534,7 @@ namespace System { } private int GetResult(uint me, uint them) { - if (me public static bool operator ==(Guid a, Guid b) @@ -645,27 +641,23 @@ namespace System { byte[] tmp; int i; - if (format.Equals("N") || format.Equals("n")) - { + if (format.Equals("N") || format.Equals("n")) { guidChars = new char[32]; tmp = BitConverter.GetBytes(_a); - for (i = 3; i >= 0; i--) - { + for (i = 3; i >= 0; i--) { guidChars[offset++] = HexToChar((tmp[i]>>4) & 0xf); guidChars[offset++] = HexToChar(tmp[i] & 0xf); } tmp = BitConverter.GetBytes(_b); - for (i = 1; i >= 0; i--) - { + for (i = 1; i >= 0; i--) { guidChars[offset++] = HexToChar((tmp[i]>>4) & 0xf); guidChars[offset++] = HexToChar(tmp[i] & 0xf); } tmp = BitConverter.GetBytes(_c); - for (i = 1; i >= 0; i--) - { + for (i = 1; i >= 0; i--) { guidChars[offset++] = HexToChar((tmp[i]>>4) & 0xf); guidChars[offset++] = HexToChar(tmp[i] & 0xf); } @@ -697,31 +689,26 @@ namespace System { } int strLength = 38; - if (format.Equals("B") || format.Equals("b")) - { + if (format.Equals("B") || format.Equals("b")) { guidChars = new char[38]; guidChars[offset++] = '{'; guidChars[37] = '}'; } - if (format.Equals("P") || format.Equals("p")) - { + if (format.Equals("P") || format.Equals("p")) { guidChars = new char[38]; guidChars[offset++] = '('; guidChars[37] = ')'; } - if (format.Equals("D") || format.Equals("d")) - { + if (format.Equals("D") || format.Equals("d")) { guidChars = new char[36]; strLength = 36; } - else - { + else { throw new FormatException("Format_InvalidGuidFormatSpecification"); } tmp = BitConverter.GetBytes(_a); - for (i = 3; i >= 0; i--) - { + for (i = 3; i >= 0; i--) { guidChars[offset++] = HexToChar((tmp[i]>>4) & 0xf); guidChars[offset++] = HexToChar(tmp[i] & 0xf); } @@ -729,8 +716,7 @@ namespace System { guidChars[offset++] = '-'; tmp = BitConverter.GetBytes(_b); - for (i = 1; i >= 0; i--) - { + for (i = 1; i >= 0; i--) { guidChars[offset++] = HexToChar((tmp[i]>>4) & 0xf); guidChars[offset++] = HexToChar(tmp[i] & 0xf); @@ -739,8 +725,7 @@ namespace System { guidChars[offset++] = '-'; tmp = BitConverter.GetBytes(_c); - for (i = 1; i >= 0; i--) - { + for (i = 1; i >= 0; i--) { guidChars[offset++] = HexToChar((tmp[i]>>4) & 0xf); guidChars[offset++] = HexToChar(tmp[i] & 0xf); } diff --git a/base/Applications/Runtime/Full/System/IAsyncResult.cs b/base/Applications/Runtime/Full/System/IAsyncResult.cs new file mode 100644 index 0000000..96216bb --- /dev/null +++ b/base/Applications/Runtime/Full/System/IAsyncResult.cs @@ -0,0 +1,39 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================ +// +// Interface: IAsyncResult +// +// Purpose: Interface to encapsulate the results of an async +// operation +// +//=========================================================== +namespace System +{ + + using System; + using System.Threading; + //| + [CLSCompliant(false)] + public interface IAsyncResult + { + //| + bool IsCompleted { get; } + + //| + WaitHandle AsyncWaitHandle { get; } + + + //| + Object AsyncState { get; } + + //| + bool CompletedSynchronously { get; } + + + } + +} diff --git a/base/Kernel/System/ICloneable.cs b/base/Applications/Runtime/Full/System/ICloneable.cs similarity index 76% rename from base/Kernel/System/ICloneable.cs rename to base/Applications/Runtime/Full/System/ICloneable.cs index facae1e..544c8e4 100644 --- a/base/Kernel/System/ICloneable.cs +++ b/base/Applications/Runtime/Full/System/ICloneable.cs @@ -3,14 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: ICloneable -** -** This interface is implemented by classes that support cloning. -** -===========================================================*/ -namespace System { +//============================================================ +// +// Class: ICloneable +// +// This interface is implemented by classes that support cloning. +// +//=========================================================== +namespace System +{ using System; // Defines an interface indicating that an object may be cloned. Only objects diff --git a/base/Kernel/System/IComparable.cs b/base/Applications/Runtime/Full/System/IComparable.cs similarity index 95% rename from base/Kernel/System/IComparable.cs rename to base/Applications/Runtime/Full/System/IComparable.cs index 07babdc..64ba49e 100644 --- a/base/Kernel/System/IComparable.cs +++ b/base/Applications/Runtime/Full/System/IComparable.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System { +namespace System +{ using System; // The IComparable interface is implemented by classes that support an diff --git a/base/Kernel/System/IDisposable.cs b/base/Applications/Runtime/Full/System/IDisposable.cs similarity index 89% rename from base/Kernel/System/IDisposable.cs rename to base/Applications/Runtime/Full/System/IDisposable.cs index 1682d4d..bd31827 100644 --- a/base/Kernel/System/IDisposable.cs +++ b/base/Applications/Runtime/Full/System/IDisposable.cs @@ -3,17 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Interface: IDisposable -** -** -** Purpose: Interface for assisting with deterministic finalization. -** -** Date: September 29, 2000 -** -===========================================================*/ -namespace System { +//============================================================ +// +// Interface: IDisposable +// +// Purpose: Interface for assisting with deterministic finalization. +// +//=========================================================== +namespace System +{ // IDisposable is an attempt at helping to solve problems with deterministic // finalization. The GC of course doesn't leave any way to deterministically // know when a finalizer will run. This forces classes that hold onto OS diff --git a/base/Kernel/System/IFormattable.cs b/base/Applications/Runtime/Full/System/IFormattable.cs similarity index 90% rename from base/Kernel/System/IFormattable.cs rename to base/Applications/Runtime/Full/System/IFormattable.cs index 5a8fd65..44ec1ef 100644 --- a/base/Kernel/System/IFormattable.cs +++ b/base/Applications/Runtime/Full/System/IFormattable.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System { +namespace System +{ using System; //| diff --git a/base/Applications/Runtime/Full/System/IndexOutOfRangeException.cs b/base/Applications/Runtime/Full/System/IndexOutOfRangeException.cs new file mode 100644 index 0000000..308307e --- /dev/null +++ b/base/Applications/Runtime/Full/System/IndexOutOfRangeException.cs @@ -0,0 +1,37 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: IndexOutOfRangeException +// +// Purpose: Exception class for invalid array indices. +// +//============================================================================= + +namespace System +{ + + using System; + using System.Runtime.CompilerServices; + + //| + public sealed partial class IndexOutOfRangeException : SystemException { + //| + public IndexOutOfRangeException() + : base("Arg_IndexOutOfRangeException") { + } + + //| + public IndexOutOfRangeException(String message) + : base(message) { + } + + //| + public IndexOutOfRangeException(String message, Exception innerException) + : base(message, innerException) { + } + } +} diff --git a/base/Kernel/System/Int16.cs b/base/Applications/Runtime/Full/System/Int16.cs similarity index 87% rename from base/Kernel/System/Int16.cs rename to base/Applications/Runtime/Full/System/Int16.cs index eb45e48..4fd3e11 100644 --- a/base/Kernel/System/Int16.cs +++ b/base/Applications/Runtime/Full/System/Int16.cs @@ -3,19 +3,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: Int16.cool -** -** -** Purpose: This class will encapsulate a short and provide an -** Object representation of it. -** -** Date: August 3, 1998 -** -===========================================================*/ +//============================================================ +// +// Class: Int16.cool +// +// Purpose: This class will encapsulate a short and provide an +// Object representation of it. +// +//=========================================================== -namespace System { +namespace System +{ using System; using System.Globalization; @@ -74,7 +72,7 @@ namespace System { //| public String ToString(String format) { - if (m_value<0 && format!=null && format.Length>0 && (format[0]=='X' || format[0]=='x')) { + if (m_value < 0 && format != null && format.Length > 0 && (format[0] =='X' || format[0] =='x')) { uint temp = (uint)(m_value & 0x0000FFFF); return Number.FormatUInt32(temp,format); } diff --git a/base/Kernel/System/Int32.cs b/base/Applications/Runtime/Full/System/Int32.cs similarity index 90% rename from base/Kernel/System/Int32.cs rename to base/Applications/Runtime/Full/System/Int32.cs index 6a5aacd..cfa8a3b 100644 --- a/base/Kernel/System/Int32.cs +++ b/base/Applications/Runtime/Full/System/Int32.cs @@ -3,18 +3,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: Int32 -** -** -** Purpose: A representation of a 32 bit 2's complement -** integer. -** -** Date: July 23, 1998 -** -===========================================================*/ -namespace System { +//============================================================ +// +// Class: Int32 +// +// Purpose: A representation of a 32 bit 2's complement +// integer. +// +//=========================================================== +namespace System +{ using System; using System.Globalization; diff --git a/base/Kernel/System/Int64.cs b/base/Applications/Runtime/Full/System/Int64.cs similarity index 89% rename from base/Kernel/System/Int64.cs rename to base/Applications/Runtime/Full/System/Int64.cs index c10d2aa..ea80aa3 100644 --- a/base/Kernel/System/Int64.cs +++ b/base/Applications/Runtime/Full/System/Int64.cs @@ -3,18 +3,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: Int64.cool -** -** -** Purpose: This class will encapsulate a long and provide an -** Object representation of it. -** -** Date: August 3, 1998 -** -===========================================================*/ -namespace System { +//============================================================ +// +// Class: Int64.cool +// +// Purpose: This class will encapsulate a long and provide an +// Object representation of it. +// +//=========================================================== +namespace System +{ using System; using System.Globalization; diff --git a/base/Kernel/System/IntPtr.cs b/base/Applications/Runtime/Full/System/IntPtr.cs similarity index 90% rename from base/Kernel/System/IntPtr.cs rename to base/Applications/Runtime/Full/System/IntPtr.cs index 38df30c..b672c9b 100644 --- a/base/Kernel/System/IntPtr.cs +++ b/base/Applications/Runtime/Full/System/IntPtr.cs @@ -3,18 +3,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: IntPtr -** -** -** Purpose: Platform neutral integer -** -** Date: July 21, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: IntPtr +// +// Purpose: Platform neutral integer +// +//=========================================================== -namespace System { +namespace System +{ using System; using System.Globalization; @@ -74,7 +72,7 @@ namespace System { [Inline] public unsafe override String ToString() { - if(sizeof(IntPtr) == 4) { + if (sizeof(IntPtr) == 4) { return this.ToInt32().ToString(); } return this.ToInt64().ToString(); @@ -85,7 +83,7 @@ namespace System { [Intrinsic] public extern static unsafe explicit operator void* (IntPtr value); - [CLSCompliant(false)] // + [CLSCompliant(false)] [Intrinsic] public extern static explicit operator UIntPtr (IntPtr value); @@ -132,7 +130,7 @@ namespace System { [Intrinsic] public extern static IntPtr operator -- (IntPtr value); - [CLSCompliant(false)] // + [CLSCompliant(false)] [Intrinsic] public extern static unsafe sbyte * operator + (sbyte* value1, IntPtr value2); diff --git a/base/Applications/Runtime/Full/System/Internal.cs b/base/Applications/Runtime/Full/System/Internal.cs new file mode 100644 index 0000000..b847bc2 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Internal.cs @@ -0,0 +1,16 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================ +// +// This file exists to contain module-level custom attributes +// for the BCL. +// +//=========================================================== +using System.Runtime.InteropServices; + +[assembly:Guid("BED7F4EA-1A96-11d2-8F08-00A0C9A6186D")] +[assembly:System.CLSCompliantAttribute(true)] +[assembly:System.Reflection.AssemblyDescriptionAttribute("Common Language Runtime Library")] diff --git a/base/Applications/Runtime/Full/System/InvalidCastException.cs b/base/Applications/Runtime/Full/System/InvalidCastException.cs new file mode 100644 index 0000000..965da3f --- /dev/null +++ b/base/Applications/Runtime/Full/System/InvalidCastException.cs @@ -0,0 +1,32 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: InvalidCastException +// +// Purpose: Exception class for bad cast conditions! +// +//============================================================================= + +namespace System +{ + + using System; + using System.Runtime.CompilerServices; + + //| + public partial class InvalidCastException : SystemException { + //| + public InvalidCastException(String message) + : base(message) { + } + + //| + public InvalidCastException(String message, Exception innerException) + : base(message, innerException) { + } + } +} diff --git a/base/Applications/Runtime/Full/System/InvalidOperationException.cs b/base/Applications/Runtime/Full/System/InvalidOperationException.cs new file mode 100644 index 0000000..d00a8b3 --- /dev/null +++ b/base/Applications/Runtime/Full/System/InvalidOperationException.cs @@ -0,0 +1,38 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: InvalidOperationException +// +// Purpose: Exception class for denoting an object was in a state that +// made calling a method illegal. +// +//============================================================================= +namespace System +{ + + using System; + + //| + public class InvalidOperationException : SystemException + { + //| + public InvalidOperationException() + : base("Arg_InvalidOperationException") { + } + + //| + public InvalidOperationException(String message) + : base(message) { + } + + //| + public InvalidOperationException(String message, Exception innerException) + : base(message, innerException) { + } + } +} + diff --git a/base/Kernel/System/Math.cs b/base/Applications/Runtime/Full/System/Math.cs similarity index 92% rename from base/Kernel/System/Math.cs rename to base/Applications/Runtime/Full/System/Math.cs index 7d9a92c..8afa787 100644 --- a/base/Kernel/System/Math.cs +++ b/base/Applications/Runtime/Full/System/Math.cs @@ -3,17 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: Math -** -** -** Purpose: Some floating-point math operations -** -** Date: July 8, 1998 -** -===========================================================*/ -namespace System { +//============================================================ +// +// Class: Math +// +// Purpose: Some floating-point math operations +// +//=========================================================== +namespace System +{ using System; using System.Runtime.CompilerServices; @@ -21,6 +19,7 @@ namespace System { //| [CCtorIsRunDuringStartup] + [AccessedByRuntime("output to header : defined in Math.cpp")] public sealed class Math { // Prevent from begin created @@ -121,7 +120,7 @@ namespace System { } private static double InternalRound(double d, int digits) { - // rusa: see also Lightning\Src\ClassLibNative\Float\COMFloat.cpp::RoundDigits + // See also Lightning\Src\ClassLibNative\Float\COMFloat.cpp::RoundDigits // Managed code ensures that (digits >= 0 && digits <= 15) if (Abs(d) < 1E16) { d *= dblPower10[digits]; @@ -222,9 +221,9 @@ namespace System { return val; } - /*================================Abs========================================= - **Returns the absolute value of its argument. - ============================================================================*/ + //================================Abs========================================= + //Returns the absolute value of its argument. + //============================================================================ //| [CLSCompliant(false)] public static sbyte Abs(sbyte value) { @@ -339,9 +338,9 @@ namespace System { return Decimal.Abs(value); } - /*================================MAX========================================= - **Returns the larger of val1 and val2 - ============================================================================*/ + //================================MAX========================================= + //Returns the larger of val1 and val2 + //============================================================================ //| [CLSCompliant(false)] public static sbyte Max(sbyte val1, sbyte val2) { @@ -413,9 +412,9 @@ namespace System { return Decimal.Max(val1, val2); } - /*================================MIN========================================= - **Returns the smaller of val1 and val2. - ============================================================================*/ + //================================MIN========================================= + //Returns the smaller of val1 and val2. + //============================================================================ //| [CLSCompliant(false)] public static sbyte Min(sbyte val1, sbyte val2) { @@ -487,9 +486,9 @@ namespace System { return Decimal.Min(val1, val2); } - /*=====================================Log====================================== - ** - ==============================================================================*/ + //=====================================Log====================================== + // + //============================================================================== //| public static double Log(double a, double newBase) { return (Log(a) / Log(newBase)); diff --git a/base/Applications/Runtime/Full/System/MulticastDelegate.cs b/base/Applications/Runtime/Full/System/MulticastDelegate.cs new file mode 100644 index 0000000..1714fba --- /dev/null +++ b/base/Applications/Runtime/Full/System/MulticastDelegate.cs @@ -0,0 +1,191 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +namespace System +{ + using System; + using System.Reflection; + using System.Runtime.CompilerServices; + + //| + public abstract partial class MulticastDelegate : Delegate + { + internal MulticastDelegate Previous + { + get{return _prev;} + set{_prev = value;} + } + + // equals returns true IIF the delegate is not null and has the + // same target, method and invocation list as this object + //| + public override sealed bool Equals(Object obj) + { + if (!base.Equals(obj)) + return false; + if (_prev != null) + return _prev.InvocationListEquals(((MulticastDelegate) obj)._prev); + else { + // if we got here, "this" is a Multicast with only one listener. + if (obj is MulticastDelegate) + return ((MulticastDelegate)obj)._prev == null; + else if (obj is Delegate) + return true; + else + return false; + } + } + + // Recursive function which will check for equality of the invocation list. + private bool InvocationListEquals(MulticastDelegate d) + { + if (!base.Equals(d)) + return false; + if (_prev == d._prev) + return true; + if (_prev == null) + return (d._prev == null) ? true : false; + return _prev.InvocationListEquals(d._prev); + } + + // This method will combine this delegate with the passed delegate + // to form a new delegate. + //| + protected override sealed Delegate CombineImpl(Delegate follow) + { + // Verify that the types are the same... + if (this.GetType() != follow.GetType()) + throw new ArgumentException("Arg_DlgtTypeMis"); + + // We always clone the delegate because this delegate is + // not changed by combine and remove. We can safely tack + // the follow delegate onto the end of the copy. + MulticastDelegate d = (MulticastDelegate) ((MulticastDelegate) follow).MemberwiseClone(); + MulticastDelegate root = d; + while (d._prev != null) { + d._prev = (MulticastDelegate) d._prev.MemberwiseClone(); + d = d._prev; + } + d._prev = (MulticastDelegate) this; + return root; + } + + // This method currently looks backward on the invocation list + // for an element that has Delegate based equality with value. (Doesn't + // look at the invocation list.) If this is found we remove it from + // this list and return a new delegate. If its not found a copy of the + // current list is returned. + //| + protected override sealed Delegate RemoveImpl(Delegate value) + { + // There is a special case were we are removing using a delegate as + // the value we need to check for this case + // + if (!(value is MulticastDelegate)) { + if (base.Equals(value)) + return _prev; + return this; + } + + // Look for the delegate... + MulticastDelegate v = (MulticastDelegate) value; + if (InternalEquals(v)) { + int size = v.DelSize(); + MulticastDelegate p = _prev; + while (--size != 0) + p = p._prev; + return p; + } + + MulticastDelegate d = (MulticastDelegate) this.MemberwiseClone(); + MulticastDelegate root = d; + while (d._prev != null && d._prev.InternalEquals(v) != true) { + d._prev = (MulticastDelegate) d._prev.MemberwiseClone(); + d = d._prev; + } + + if (d._prev != null) { + int size = v.DelSize(); + MulticastDelegate p = d._prev._prev; + while (--size != 0) + p = p._prev; + d._prev = p; + } + return root; + } + private int DelSize() + { + int i=0; + MulticastDelegate d = this; + while (d != null) { + i++; + d = d._prev; + } + return i; + } + + + // Stupid helper function to check equality based upon the super class. + private bool InternalEquals(Delegate d) + { + if (!base.Equals(d)) + return false; + if (((MulticastDelegate) d)._prev != null) { + if (_prev == null) + return false; + return _prev.InternalEquals(((MulticastDelegate) d)._prev); + } + return true; + } + + + // This method returns the Invocation list of this multicast delegate. + //| + public override sealed Delegate[] GetInvocationList() { + int i = 0; + MulticastDelegate p; + + // How big is the invocation list? + for (p = this; p != null; p = p._prev) + i++; + + // Create an array of delegate copies and each + // element into the array (Need to reverse the order and make sure + // we set the _prev to null. + Delegate[] del = new Delegate[i]; + for (p = this; p != null; p = p._prev) { + del[--i] = (Delegate) p.MemberwiseClone(); + ((MulticastDelegate) del[i])._prev = null; + } + return del; + } + + // private static bool operator equals(MulticastDelegate d1, MulticastDelegate d2) { + // if ((Object)d1 == null) + // return (Object)d2 == null; + // return d1.Equals(d2); + // } + + //| + public static bool operator ==(MulticastDelegate d1, MulticastDelegate d2) { + if ((Object)d1 == null) + return (Object)d2 == null; + return d1.Equals(d2); + } + + //| + public static bool operator !=(MulticastDelegate d1, MulticastDelegate d2) { + if ((Object)d1 == null) + return (Object)d2 != null; + return !d1.Equals(d2); + } + + //| + public override sealed int GetHashCode() { + return base.GetHashCode(); + } + + } +} diff --git a/base/Kernel/System/MulticastNotSupportedException.cs b/base/Applications/Runtime/Full/System/MulticastNotSupportedException.cs similarity index 95% rename from base/Kernel/System/MulticastNotSupportedException.cs rename to base/Applications/Runtime/Full/System/MulticastNotSupportedException.cs index 1fbd40e..04fb551 100644 --- a/base/Kernel/System/MulticastNotSupportedException.cs +++ b/base/Applications/Runtime/Full/System/MulticastNotSupportedException.cs @@ -8,7 +8,8 @@ // This is thrown when you add multiple callbacks to a non-multicast delegate. //////////////////////////////////////////////////////////////////////////////// -namespace System { +namespace System +{ using System; diff --git a/base/Applications/Runtime/Full/System/NotImplementedException.cs b/base/Applications/Runtime/Full/System/NotImplementedException.cs new file mode 100644 index 0000000..7bc5090 --- /dev/null +++ b/base/Applications/Runtime/Full/System/NotImplementedException.cs @@ -0,0 +1,22 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +namespace System +{ + + public class NotImplementedException : SystemException { + public NotImplementedException() + : base("Not implemented") { + } + + public NotImplementedException(String message) + : base(message) { + } + + public NotImplementedException(String message, Exception innerException) + : base(message, innerException) { + } + } +} diff --git a/base/Applications/Runtime/Full/System/NotSupportedException.cs b/base/Applications/Runtime/Full/System/NotSupportedException.cs new file mode 100644 index 0000000..e295530 --- /dev/null +++ b/base/Applications/Runtime/Full/System/NotSupportedException.cs @@ -0,0 +1,37 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: NotSupportedException +// +// Purpose: For methods that should be implemented on subclasses. +// +//============================================================================= + +namespace System +{ + + using System; + + //| + public class NotSupportedException : SystemException + { + //| + public NotSupportedException() + : base("Arg_NotSupportedException") { + } + + //| + public NotSupportedException(String message) + : base(message) { + } + + //| + public NotSupportedException(String message, Exception innerException) + : base(message, innerException) { + } + } +} diff --git a/base/Applications/Runtime/Full/System/NullReferenceException.cs b/base/Applications/Runtime/Full/System/NullReferenceException.cs new file mode 100644 index 0000000..9f9f297 --- /dev/null +++ b/base/Applications/Runtime/Full/System/NullReferenceException.cs @@ -0,0 +1,39 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: NullReferenceException +// +// Purpose: Exception class for dereferencing a null reference. +// +//============================================================================= + +namespace System +{ + + using System; + using System.Runtime.CompilerServices; + + //| + [AccessedByRuntime("referenced from halasm.asm")] + public partial class NullReferenceException : SystemException { + //| + [AccessedByRuntime("referenced from halasm.asm")] + public NullReferenceException() + : base("Arg_NullReferenceException") { + } + + //| + public NullReferenceException(String message) + : base(message) { + } + + //| + public NullReferenceException(String message, Exception innerException) + : base(message, innerException) { + } + } +} diff --git a/base/Kernel/System/Number.cs b/base/Applications/Runtime/Full/System/Number.cs similarity index 92% rename from base/Kernel/System/Number.cs rename to base/Applications/Runtime/Full/System/Number.cs index c4daef1..44c4cab 100644 --- a/base/Kernel/System/Number.cs +++ b/base/Applications/Runtime/Full/System/Number.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System { +namespace System +{ using System; using System.Globalization; @@ -280,6 +281,8 @@ namespace System { // specified. Note, however, that the Parse methods do not accept // NaNs or Infinities. // + + [AccessedByRuntime("functions defined in Number.cpp")] internal class Number { private Number() { @@ -309,7 +312,8 @@ namespace System { this.precision = INT32_PRECISION; if (value >= 0) { this.negative = false; - } else { + } + else { this.negative = true; value = -value; } @@ -349,7 +353,8 @@ namespace System { this.precision = INT64_PRECISION; if (value >= 0) { this.negative = false; - } else { + } + else { this.negative = true; value = -value; } @@ -399,14 +404,13 @@ namespace System { return index; } - // markples: see also Lightning\Src\VM\COMNumber.cpp:: + // See also Lightning\Src\VM\COMNumber.cpp:: // wchar* MatchChars(wchar* p, wchar* str) // will now return -1 instead of NULL on failure private static int MatchChars(String str1, int p, String str) { int str_i=0; if (EndString(str,str_i)) return -1; - for (; !EndString(str,str_i); p++, str_i++) - { + for (; !EndString(str,str_i); p++, str_i++) { if (str1[p] != str[str_i]) //We only hurt the failure case { if ((str[str_i] == 0xA0) && (str1[p] == 0x20)) @@ -421,7 +425,7 @@ namespace System { return p; } - // REVIEW: (markples) why is this the only thing that is hardcoded + // REVIEW: why is this the only thing that is hardcoded // anywhere in the system? private bool ISWHITE(char ch) { return (((ch) == 0x20)||((ch) >= 0x09 && (ch) <= 0x0D)); @@ -460,6 +464,7 @@ namespace System { }; private static String posNumberFormat = "#"; + [AccessedByRuntime("defined in Number.cpp")] [MethodImpl(MethodImplOptions.InternalCall)] [GCAnnotation(GCOption.NOGC)] [StackBound(1024)] @@ -469,7 +474,7 @@ namespace System { out int decimalpoint, out bool negative); - // markples: see also Lightning\Src\VM\COMNumber.cpp:: + // See also Lightning\Src\VM\COMNumber.cpp:: // void DoubleToNumber(double value, int precision, NUMBER* number) private Number(double value, int precision) { this.precision = precision; @@ -481,7 +486,7 @@ namespace System { return i1000) { + if (exp > 1000) { exp=9999; - while(ch>='0' && ch<='9') { + while (ch >='0' && ch <='9') { ch = Get(str, ++p); } } } while (ch >= '0' && ch <= '9'); if (negExp) exp = -exp; this.scale += exp; - } else { + } + else { p = temp; ch = Get(str, p); } @@ -607,7 +621,8 @@ namespace System { if (ISWHITE(ch) && ((style & NumberStyles.AllowTrailingWhite) != 0)) { // do nothing - } else if ((signflag = + } + else if ((signflag = (((style & NumberStyles.AllowTrailingSign) != 0) && !((state & STATE_SIGN) != 0))) && @@ -615,15 +630,18 @@ namespace System { MatchChars(str, p, NumberFormatInfo.positiveSign)) != -1) { state |= STATE_SIGN; p = next - 1; - } else if (signflag && + } + else if (signflag && (next = MatchChars(str, p, NumberFormatInfo.negativeSign)) != -1) { state |= STATE_SIGN; this.negative = true; p = next - 1; - } else if (ch == ')' && ((state & STATE_PARENS) != 0)) { + } + else if (ch == ')' && ((state & STATE_PARENS) != 0)) { state &= ~STATE_PARENS; - } else { + } + else { break; } ch = Get(str, ++p); @@ -646,7 +664,7 @@ namespace System { } - // markples: see also Lightning\Src\VM\COMNumber.cpp:: + // See also Lightning\Src\VM\COMNumber.cpp:: //void StringToNumber(STRINGREF str, int options, // NUMBER* number, NUMFMTREF numfmt) private Number(String str, NumberStyles style) { @@ -668,7 +686,7 @@ namespace System { } public static String FormatDecimal(Decimal value, String format) { - // rusa: see also Lightning\Src\VM\COMNumber.cpp::FormatDecimal + // See also Lightning\Src\VM\COMNumber.cpp::FormatDecimal if (format == null) { throw new ArgumentNullException("format"); } @@ -677,7 +695,7 @@ namespace System { } public static String FormatDouble(double value, String format) { - // rusa: see also Lightning\Src\VM\COMNumber.cpp::FormatDouble + // See also Lightning\Src\VM\COMNumber.cpp::FormatDouble Number number; int digits; @@ -736,7 +754,7 @@ namespace System { } public static String FormatInt32(int value, String format) { - // rusa: see also Lightning\Src\VM\COMNumber.cpp::FormatInt32 + // See also Lightning\Src\VM\COMNumber.cpp::FormatInt32 int digits; char fmt = ParseFormatSpecifier(format, out digits); switch (fmt) { @@ -762,13 +780,14 @@ namespace System { Number number = new Number(value); if (fmt == 0) { return number.ToStringFormat(format); - } else { + } + else { return number.ToString(fmt, digits); } } public static String FormatUInt32(uint value, String format) { - // rusa: see also Lightning\Src\VM\COMNumber.cpp::FormatUInt32 + // See also Lightning\Src\VM\COMNumber.cpp::FormatUInt32 int digits; char fmt = ParseFormatSpecifier(format, out digits); switch (fmt) { @@ -794,13 +813,14 @@ namespace System { Number number = new Number(value); if (fmt == 0) { return number.ToStringFormat(format); - } else { + } + else { return number.ToString(fmt, digits); } } public static String FormatInt64(long value, String format) { - // rusa: see also Lightning\Src\VM\COMNumber.cpp::FormatInt64 + // See also Lightning\Src\VM\COMNumber.cpp::FormatInt64 int digits; char fmt = ParseFormatSpecifier(format, out digits); switch (fmt) { @@ -826,13 +846,14 @@ namespace System { Number number = new Number(value); if (fmt == 0) { return number.ToStringFormat(format); - } else { + } + else { return number.ToString(fmt, digits); } } public static String FormatUInt64(ulong value, String format) { - // rusa: see also Lightning\Src\VM\COMNumber.cpp::FormatUInt64 + // See also Lightning\Src\VM\COMNumber.cpp::FormatUInt64 int digits; char fmt = ParseFormatSpecifier(format, out digits); switch (fmt) { @@ -856,13 +877,14 @@ namespace System { Number number = new Number(value); if (fmt == 0) { return number.ToStringFormat(format); - } else { + } + else { return number.ToString(fmt, digits); } } public static String FormatSingle(float value, String format) { - // rusa: see also Lightning\Src\VM\COMNumber.cpp::FormatSingle + // See also Lightning\Src\VM\COMNumber.cpp::FormatSingle Number number; int digits; @@ -927,7 +949,7 @@ namespace System { } public static Decimal ParseDecimal(String s, NumberStyles style) { - // rusa: see also Lightning\Src\VM\COMNumber.cpp::ParseDecimal + // See also Lightning\Src\VM\COMNumber.cpp::ParseDecimal Number number = new Number(s, style); Decimal result; if (!NumberToDecimal(number, out result)) { @@ -944,7 +966,7 @@ namespace System { } public static int ParseInt32(String s, NumberStyles style) { - // rusa: see also Lightning\Src\VM\COMNumber.cpp::ParseInt32 + // See also Lightning\Src\VM\COMNumber.cpp::ParseInt32 Number number = new Number(s, style); int result; if ((style & NumberStyles.AllowHexSpecifier) != 0) { @@ -953,7 +975,8 @@ namespace System { throw new OverflowException("Overflow_Int32"); } result = unchecked((int) temp); - } else { + } + else { if (!NumberToInt32(number, out result)) { throw new OverflowException("Overflow_Int32"); } @@ -962,14 +985,15 @@ namespace System { } public static uint ParseUInt32(String s, NumberStyles style) { - // rusa: see also Lightning\Src\VM\COMNumber.cpp::ParseUInt32 + // See also Lightning\Src\VM\COMNumber.cpp::ParseUInt32 Number number = new Number(s, style); uint result; if ((style & NumberStyles.AllowHexSpecifier) != 0) { if (!HexNumberToUInt32(number, out result)) { throw new OverflowException("Overflow_UInt32"); } - } else { + } + else { if (!NumberToUInt32(number, out result)) { throw new OverflowException("Overflow_UInt32"); } @@ -978,7 +1002,7 @@ namespace System { } public static long ParseInt64(String s, NumberStyles style) { - // rusa: see also Lightning\Src\VM\COMNumber.cpp::ParseInt64 + // See also Lightning\Src\VM\COMNumber.cpp::ParseInt64 Number number = new Number(s, style); long result; if ((style & NumberStyles.AllowHexSpecifier) != 0) { @@ -987,7 +1011,8 @@ namespace System { throw new OverflowException("Overflow_UInt32"); } result = unchecked((long) temp); - } else { + } + else { if (!NumberToInt64(number, out result)) { throw new OverflowException("Overflow_UInt32"); } @@ -996,14 +1021,15 @@ namespace System { } public static ulong ParseUInt64(String s, NumberStyles style) { - // rusa: see also Lightning\Src\VM\COMNumber.cpp::ParseUInt64 + // See also Lightning\Src\VM\COMNumber.cpp::ParseUInt64 Number number = new Number(s, style); ulong result; if ((style & NumberStyles.AllowHexSpecifier) != 0) { if (!HexNumberToUInt64(number, out result)) { throw new OverflowException("Overflow_UInt32"); } - } else { + } + else { if (!NumberToUInt64(number, out result)) { throw new OverflowException("Overflow_UInt32"); } @@ -1193,6 +1219,7 @@ namespace System { throw new Exception("System.Number.NumberToDecimal not implemented in Bartok!"); } + [AccessedByRuntime("defined in Number.cpp")] [MethodImpl(MethodImplOptions.InternalCall)] [GCAnnotation(GCOption.NOGC)] [StackBound(1024)] @@ -1204,10 +1231,11 @@ namespace System { int index = 0; char[] src = number.digits; if (number.negative) buffer[index++] = (byte) '-'; - for (int j=0; j INT32_PRECISION || i < number.precision) goto broken; char[] c = number.digits; @@ -1252,7 +1280,8 @@ namespace System { if (number.negative) { n = -n; if (n > 0) goto broken; - } else { + } + else { if (n < 0) goto broken; } value = n; @@ -1264,7 +1293,7 @@ namespace System { } private static bool NumberToUInt32(Number number, out uint value) { - // markples: see also Lightning\Src\VM\COMNumber.cpp::NumberToInt32 + // See also Lightning\Src\VM\COMNumber.cpp::NumberToInt32 int i = number.scale; if (i > UINT32_PRECISION || i < number.precision) goto broken; if (number.negative) goto broken; @@ -1285,7 +1314,7 @@ namespace System { } private static bool NumberToInt64(Number number, out long value) { - // markples: see also Lightning\Src\VM\COMNumber.cpp::NumberToInt32 + // See also Lightning\Src\VM\COMNumber.cpp::NumberToInt32 int i = number.scale; if (i > UINT64_PRECISION || i < number.precision) goto broken; char[] c = number.digits; @@ -1299,7 +1328,8 @@ namespace System { if (number.negative) { n = -n; if (n > 0) goto broken; - } else { + } + else { if (n < 0) goto broken; } value = n; @@ -1312,7 +1342,7 @@ namespace System { private static bool NumberToUInt64(Number number, out ulong value) { - // markples: see also Lightning\Src\VM\COMNumber.cpp::NumberToInt32 + // See also Lightning\Src\VM\COMNumber.cpp::NumberToInt32 int i = number.scale; if (i > INT64_PRECISION || i < number.precision) goto broken; if (number.negative) goto broken; @@ -1334,7 +1364,7 @@ namespace System { private static bool HexNumberToUInt32(Number number, out uint value) { - // markples: see also Lightning\Src\VM\COMNumber.cpp::HexNumberToInt32 + // See also Lightning\Src\VM\COMNumber.cpp::HexNumberToInt32 int i = number.scale; if (i > UINT32_PRECISION || i < number.precision) { goto broken; @@ -1369,7 +1399,7 @@ namespace System { } private static bool HexNumberToUInt64(Number number, out ulong value) { - // markples: see also Lightning\Src\VM\COMNumber.cpp::HexNumberToInt32 + // See also Lightning\Src\VM\COMNumber.cpp::HexNumberToInt32 int i = number.scale; if (i > INT64_PRECISION || i < number.precision) { goto broken; @@ -1437,7 +1467,7 @@ namespace System { return 'G'; } - // rusa: see also Lightning\Src\VM\COMNumber.cpp::NumberToStringFormat + // See also Lightning\Src\VM\COMNumber.cpp::NumberToStringFormat private String ToStringFormat(String format) { int thousandCount = 0; int section = (this.digits[0] == 0 ? 2 : (this.negative ? 1 : 0)); @@ -1536,7 +1566,8 @@ namespace System { if (thousandPos >= 0) { if (thousandPos == decimalPos) { scaleAdjust -= thousandCount * 3; - } else { + } + else { thousandSeps = 1; } } @@ -1553,7 +1584,8 @@ namespace System { continue; } } - } else { + } + else { this.negative = false; } break; @@ -1565,7 +1597,8 @@ namespace System { if (scientific) { digPos = decimalPos; adjust = 0; - } else { + } + else { digPos = (this.scale > decimalPos) ? this.scale : decimalPos; adjust = this.scale - decimalPos; } @@ -1593,7 +1626,8 @@ namespace System { int groupSizeLen = NumberFormatInfo.numberGroupSizes.Length; if (groupSizeLen == 0) { thousandSeps = 0; - } else { + } + else { thousandSepPos = new int[bufferLength]; long groupTotalSizeCount = NumberFormatInfo.numberGroupSizes[0]; int groupSizeIndex = 0; @@ -1674,7 +1708,8 @@ namespace System { if (adjust < 0) { adjust++; ch = (digPos <= firstDigit) ? '0' : '\0'; - } else { + } + else { ch = ((this.digits[digitOffset] != 0) ? this.digits[digitOffset++] : ((digPos > lastDigit) ? '0' : '\0')); @@ -1713,7 +1748,7 @@ namespace System { buffer[dst] = Get(format, src); dst++; src++; - if (dst == bufferLen-1) { + if (dst == bufferLen - 1) { if ((ulong) (bufferLen - dst) < maxStrIncLen) { bufferLen *= 2; char[] oldBuffer = buffer; @@ -1742,13 +1777,16 @@ namespace System { if (scientific) { if (Get(format, src) == '0') { i++; - } else if (Get(format, src) == '+' && + } + else if (Get(format, src) == '+' && Get(format, src+1) == '0') { sign = NumberFormatInfo.positiveSign; - } else if (Get(format, src) == '-' && + } + else if (Get(format, src) == '-' && Get(format, src+1) == '0') { // Do nothing - } else { + } + else { buffer[dst] = ch; dst++; break; @@ -1764,7 +1802,8 @@ namespace System { (this.scale - decimalPos)); dst = FormatExponent(buffer, dst, exp, ch, sign, NumberFormatInfo.negativeSign, i); scientific = false; - } else { + } + else { buffer[dst] = ch; dst++; if (Get(format, src) == '+' || Get(format, src) == '-') { @@ -1826,7 +1865,7 @@ namespace System { } } - // markples: see also Lightning\Src\VM\COMNumber.cpp:: + // See also Lightning\Src\VM\COMNumber.cpp:: // STRINGREF Int32ToDecStr(int value, int digits, STRINGREF sNegative) private static String Int32ToDecString(int value, int digits, String sign) { //THROWSCOMPLUSEXCEPTION(); @@ -1901,7 +1940,7 @@ namespace System { return (uint)((((ulong)x) & 0xFFFFFFFF00000000L) >> 32); } - // markples: see also Lightning\Src\VM\COMNumber.cpp:: + // See also Lightning\Src\VM\COMNumber.cpp:: //STRINGREF Int64ToDecStr(__int64 value, int digits, STRINGREF sNegative) private static String Int64ToDecString(long value, int digits, String sign) { //THROWSCOMPLUSEXCEPTION(); @@ -1931,7 +1970,7 @@ namespace System { } Int32ToDecChars(buffer, ref p, LO32(unchecked((ulong)value)), digits); if (signNum < 0) { - for (int i=sign.Length - 1; i >= 0; i--) { + for (int i = sign.Length - 1; i >= 0; i--) { buffer[--p] = sign[i]; } } @@ -1976,7 +2015,7 @@ namespace System { return String.StringCTOR(buffer, p, bufferLength-p); } - // markples: see also Lightning\Src\VM\COMNumber.cpp:: + // See also Lightning\Src\VM\COMNumber.cpp:: // wchar* Int32ToDecChars(wchar* p, unsigned int value, int digits) // There's a x86 asm version there too. private static void Int32ToDecChars(char[] buffer, ref int bufferIndex, @@ -1995,7 +2034,7 @@ namespace System { } } - // markples: see also Lightning\Src\VM\COMNumber.cpp:: + // See also Lightning\Src\VM\COMNumber.cpp:: // void RoundNumber(NUMBER* number, int pos) private void RoundNumber(int pos) { int i = 0; @@ -2004,12 +2043,14 @@ namespace System { while (i > 0 && this.digits[i - 1] == '9') i--; if (i > 0) { this.digits[i - 1]++; - } else { + } + else { this.scale++; this.digits[0] = '1'; i = 1; } - } else { + } + else { while (i > 0 && this.digits[i - 1] == '0') i--; } if (i == 0) { @@ -2030,7 +2071,8 @@ namespace System { if (value < 0) { bufferIndex = AddStringRef(buffer, bufferIndex, negSignStr); value = -value; - } else { + } + else { if (posSignStr != null) { bufferIndex = AddStringRef(buffer, bufferIndex, posSignStr); } @@ -2110,8 +2152,8 @@ namespace System { // REVIEW: call the real wcslen? private static int wcslen(char[] c, int i) { int j; - for(j=i; j=0; i--) { + for (int i = digPos - 1; i >= 0; i--) { buffer[p--] = (i 0) { digitCount++; if (digitCount == groupSize && i != 0) { - for (int j = groupSeparatorLen - 1; j >=0; j--) { + for (int j = groupSeparatorLen - 1; j >= 0; j--) { buffer[p--] = sGroup[j]; } @@ -2199,7 +2241,8 @@ namespace System { } bufferIndex += bufferSize; dig += digStart; - } else { + } + else { do { buffer[bufferIndex++] = this.digits[dig] != 0 ? this.digits[dig++] : '0'; @@ -2311,7 +2354,7 @@ namespace System { return result; } - // markples: see also Lightning\Src\VM\COMNumber.cpp:: + // See also Lightning\Src\VM\COMNumber.cpp:: // unsigned int Int64DivMod1E9(unsigned __int64* value) // There's a x86 asm version there too. // The interface is different because Bartok does not support diff --git a/base/Applications/Runtime/Full/System/Object.cs b/base/Applications/Runtime/Full/System/Object.cs new file mode 100644 index 0000000..42cd2a5 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Object.cs @@ -0,0 +1,301 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================ +// +// Class: Object +// +// Object is the root class for all CLR objects. This class +// defines only the basics. +// +//=========================================================== + +namespace System +{ + using System; + using System.Runtime.InteropServices; + using System.Runtime.CompilerServices; + using CultureInfo = System.Globalization.CultureInfo; + using System.Threading; + + using Microsoft.Bartok.Runtime; + + // The Object is the root class for all object in the CLR System. Object + // is the super class for all other CLR objects and provide a set of methods and low level + // services to subclasses. These services include object synchronization and support for clone + // operations. + // + //| + [AccessedByRuntime("Accessed from halexn.cpp")] + public partial class Object + { + internal PreHeader preHeader; + [AccessedByRuntime("Accessed from halexn.cpp")] + internal PostHeader postHeader; + + // Allow us to pretend that the vtable field lives directly in Object. + internal extern VTable vtable { + [NoHeapAllocation] + [Inline] + get; + [NoHeapAllocation] + [Inline] + set; + } + + internal unsafe extern UIntPtr* VTableFieldAddr { + [NoHeapAllocation] + get; + } + +#if REFERENCE_COUNTING_GC + internal uint REF_STATE { + [Inline] + [ManualRefCounts] + [NoHeapAllocation] + get { + return this.postHeader.refState; + } + [Inline] + [ManualRefCounts] + [NoHeapAllocation] + set { + this.postHeader.refState = value; + } + } +#else // REFERENCE_COUNTING_GC + internal uint REF_STATE { + [NoHeapAllocation] + [Inline] + get { + return 1; + } + [NoHeapAllocation] + [Inline] + set { + } + } +#endif + + // Creates a new instance of an Object. + //| + [Inline] + public Object() + { + } + + // Returns a String which represents the object instance. The default + // for an object is to return the fully qualified name of the class. + // + //| + public virtual String ToString() + { + return GetType().FullName; + } + + //| + public static bool Equals(Object objA, Object objB) { + if (objA == objB) { + return true; + } + if (objA == null || objB == null) { + return false; + } + return objA.Equals(objB); + } + + //| + public static bool ReferenceEquals (Object objA, Object objB) { + return objA == objB; + } + + // GetHashCode is intended to serve as a hash function for this object. + // Based on the contents of the object, the hash function will return a suitable + // value with a relatively random distribution over the various inputs. + // + // The default implementation returns the sync block index for this instance. + // Calling it on the same object multiple times will return the same value, so + // it will technically meet the needs of a hash function, but it's pretty lame. + // Objects (& especially value classes) should override this method. + // + //| + public virtual int GetHashCode() + { + return MultiUseWord.GetHashCode(this); + } + + /// + /// Test and set the state of the GC mark bit to be the same + /// as the passed flag. Note that this operation is not + /// synchronized so it is possible for multiple marking threads + /// to 'mark' the same object. + /// + [NoHeapAllocation] + internal unsafe bool GcMark(UIntPtr flag) { + UIntPtr *loc = this.VTableFieldAddr; + UIntPtr val = *loc; + VTable.Deny(val == UIntPtr.Zero); + + if ((val & (UIntPtr)3) != flag) { + *loc = (val & ~(UIntPtr)3) + flag; + return true; + } + return false; + } + + /// + /// Return the current state of the GC mark bit. + /// + [NoHeapAllocation] + internal unsafe UIntPtr GcMark() { + UIntPtr *loc = this.VTableFieldAddr; + UIntPtr val = *loc; + return (val & 3); + } + + internal unsafe VTable GcUnmarkedVTable { + [Inline] + [NoHeapAllocation] + get { + UIntPtr *loc = this.VTableFieldAddr; + return Magic.toVTable(Magic.fromAddress(~(UIntPtr)3 & *loc)); + } + } + + // Returns a Type object which represent this object instance. + // + //| + [NoHeapAllocation] + public Type GetType() + { + return vtable.vtableType; + } + + [NoHeapAllocation] + public virtual TypeCode GetTypeCode() + { + return TypeCode.Object; + } + + // Returns a new object instance that is a memberwise copy of this + // object. This is always a shallow copy of the instance. The method is protected + // so that other object may only call this method on themselves. It is intended to + // support the ICloneable interface. + // + //| + // BUGBUG: maybe we can try harder to mess up the GC? + protected Object MemberwiseClone() + { + if (this is String) { + return this; + // REVIEW: ok, but what in the world is the CLR doing? + } + Thread thread = Thread.CurrentThread; + if (this is Array) { + Array srcArray = (Array) this; + Array cloneArray; + int srcLength = srcArray.Length; + if (srcArray.IsVector) { + cloneArray = GC.AllocateVector(srcArray.vtable, srcLength); + CloneVectorContents(srcArray, cloneArray); + } + else { + int rank = srcArray.Rank; + cloneArray = + GC.AllocateArray(srcArray.vtable, rank, srcLength); + CloneArrayContents(srcArray, cloneArray); + } + return cloneArray; + } + else { + Object clone = GC.AllocateObject(this.vtable); + CloneObjectContents(this, clone); + return clone; + } + } + +#if !REFERENCE_COUNTING_GC && !DEFERRED_REFERENCE_COUNTING_GC + + private unsafe static void CloneObjectContents(Object src, Object dst) + { + System.GCs.Barrier.Clone(src, dst); + } + + private unsafe static void CloneVectorContents(Array srcArray, + Array dstArray) + { + System.GCs.Barrier.Clone(srcArray, dstArray); + } + + private unsafe static void CloneArrayContents(Array srcArray, + Array dstArray) + { + System.GCs.Barrier.Clone(srcArray, dstArray); + } + +#else + + private unsafe static void CloneObjectContents(Object src, Object dst) + { + byte * dstNOTFIXED = (byte *)(Magic.addressOf(dst) + PostHeader.Size); + byte * srcNOTFIXED = (byte *)(Magic.addressOf(src) + PostHeader.Size); + int size = unchecked((int) src.vtable.baseLength); + // We don't copy the header fields, the vtable or the RS field! + size -= (PreHeader.Size + PostHeader.Size); +#if REFERENCE_COUNTING_GC + GCs.ReferenceCountingCollector. + IncrementReferentRefCounts(Magic.addressOf(src), src.vtable); + GCs.ReferenceCountingCollector. + DecrementReferentRefCounts(Magic.addressOf(dst), dst.vtable); +#elif DEFERRED_REFERENCE_COUNTING_GC + GCs.DeferredReferenceCountingCollector. + IncrementReferentRefCounts(Magic.addressOf(src), src.vtable); + GCs.DeferredReferenceCountingCollector. + DecrementReferentRefCounts(Magic.addressOf(dst), dst.vtable); +#endif // REFERENCE_COUNTING_GC + Buffer.MoveMemory(dstNOTFIXED,srcNOTFIXED,size); + } + + private unsafe static void CloneVectorContents(Array srcArray, + Array dstArray) + { + int srcLength = srcArray.Length; + fixed (int *srcFieldPtr = &srcArray.field1) { + fixed (int *dstFieldPtr = &dstArray.field1) { + byte *srcDataPtr = (byte *) + srcArray.GetFirstElementAddress(srcFieldPtr); + byte *dstDataPtr = (byte *) + dstArray.GetFirstElementAddress(dstFieldPtr); + int size = srcArray.vtable.arrayElementSize * srcLength; + Buffer.MoveMemory(dstDataPtr, srcDataPtr, size); + } + } + } + + private unsafe static void CloneArrayContents(Array srcArray, + Array dstArray) + { + int srcLength = srcArray.Length; + fixed (int *srcFieldPtr = &srcArray.field1) { + fixed (int *dstFieldPtr = &dstArray.field1) { + byte *srcDataPtr = (byte *) + srcArray.GetFirstElementAddress(srcFieldPtr); + byte *dstDataPtr = (byte *) + dstArray.GetFirstElementAddress(dstFieldPtr); + byte *srcDimPtr = (byte *) + srcArray.GetFirstDimInfoRectangleArray(); + int dimInfoSize = (int) (srcDataPtr - srcDimPtr); + int size = srcArray.vtable.arrayElementSize * srcLength; + Buffer.MoveMemory(dstDataPtr - dimInfoSize, + srcDataPtr - dimInfoSize, + size + dimInfoSize); + } + } + } + +#endif + + } +} diff --git a/base/Kernel/System/ObjectDisposedException.cs b/base/Applications/Runtime/Full/System/ObjectDisposedException.cs similarity index 96% rename from base/Kernel/System/ObjectDisposedException.cs rename to base/Applications/Runtime/Full/System/ObjectDisposedException.cs index a6663fd..9cd676b 100644 --- a/base/Kernel/System/ObjectDisposedException.cs +++ b/base/Applications/Runtime/Full/System/ObjectDisposedException.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System { +namespace System +{ using System; //| diff --git a/base/Kernel/System/ObsoleteAttribute.cs b/base/Applications/Runtime/Full/System/ObsoleteAttribute.cs similarity index 85% rename from base/Kernel/System/ObsoleteAttribute.cs rename to base/Applications/Runtime/Full/System/ObsoleteAttribute.cs index f53a3fb..e0ab64e 100644 --- a/base/Kernel/System/ObsoleteAttribute.cs +++ b/base/Applications/Runtime/Full/System/ObsoleteAttribute.cs @@ -3,17 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: ObsoleteAttribute -** -** -** Purpose: Attribute for functions, etc that will be removed. -** -** Date: September 22, 1999 -** -===========================================================*/ -namespace System { +//============================================================ +// +// Class: ObsoleteAttribute +// +// Purpose: Attribute for functions, etc that will be removed. +// +//=========================================================== +namespace System +{ using System; // This attribute is attached to members that are not to be used any longer. diff --git a/base/Applications/Runtime/Full/System/OutOfMemoryException.cs b/base/Applications/Runtime/Full/System/OutOfMemoryException.cs new file mode 100644 index 0000000..a82cdc6 --- /dev/null +++ b/base/Applications/Runtime/Full/System/OutOfMemoryException.cs @@ -0,0 +1,37 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: OutOfMemoryException +// +// Purpose: The exception class for OOM. +// +//============================================================================= + +namespace System +{ + + using System; + using System.Runtime.CompilerServices; + + //| + public class OutOfMemoryException : SystemException { + //| + public OutOfMemoryException() + : base("Arg_OutOfMemoryException") { + } + + //| + public OutOfMemoryException(String message) + : base(message) { + } + + //| + public OutOfMemoryException(String message, Exception innerException) + : base(message, innerException) { + } + } +} diff --git a/base/Applications/Runtime/Full/System/OverflowException.cs b/base/Applications/Runtime/Full/System/OverflowException.cs new file mode 100644 index 0000000..f7729fe --- /dev/null +++ b/base/Applications/Runtime/Full/System/OverflowException.cs @@ -0,0 +1,39 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: OverflowException +// +// Purpose: Exception class for Arithmetic Overflows. +// +//============================================================================= + +namespace System +{ + + using System; + using System.Runtime.CompilerServices; + + //| + [AccessedByRuntime("referenced from halasm.asm")] + public partial class OverflowException : ArithmeticException { + //| + [AccessedByRuntime("referenced from halasm.asm")] + public OverflowException() + : base("Arg_OverflowException") { + } + + //| + public OverflowException(String message) + : base(message) { + } + + //| + public OverflowException(String message, Exception innerException) + : base(message, innerException) { + } + } +} diff --git a/base/Applications/Runtime/Full/System/ParamArrayAttribute.cs b/base/Applications/Runtime/Full/System/ParamArrayAttribute.cs new file mode 100644 index 0000000..948ba97 --- /dev/null +++ b/base/Applications/Runtime/Full/System/ParamArrayAttribute.cs @@ -0,0 +1,22 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: ParamArrayAttribute +// +// Purpose: Container for assemblies. +// +//============================================================================= +namespace System +{ + //| + [AttributeUsage (AttributeTargets.Parameter, Inherited=true, AllowMultiple=false)] + public sealed class ParamArrayAttribute : Attribute + { + //| + public ParamArrayAttribute () {} + } +} diff --git a/base/Kernel/System/ParseNumbers.cs b/base/Applications/Runtime/Full/System/ParseNumbers.cs similarity index 78% rename from base/Kernel/System/ParseNumbers.cs rename to base/Applications/Runtime/Full/System/ParseNumbers.cs index 3790732..f4afa6a 100644 --- a/base/Kernel/System/ParseNumbers.cs +++ b/base/Applications/Runtime/Full/System/ParseNumbers.cs @@ -3,18 +3,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: ParseNumbers -** -** -** Purpose: Methods for Parsing numbers and Strings. -** All methods are implemented in native. -** -** Date: April 7, 1998 -** -===========================================================*/ -namespace System { +//============================================================ +// +// Class: ParseNumbers +// +// Purpose: Methods for Parsing numbers and Strings. +// All methods are implemented in native. +// +//=========================================================== +namespace System +{ using System; using System.Runtime.CompilerServices; @@ -60,7 +58,7 @@ namespace System { internal const int MaxRadix=36; static private int EatWhiteSpace(String buffer, int length, int i) { - for (; i maxVal || ((int)result)<0) { + if (result > maxVal || ((int)result) < 0) { throw new OverflowException("Overflow_Int32"); } result = result * radix + value; i++; } - if ((int)result<0 && result!=0x80000000) { + if ((int)result < 0 && result != 0x80000000) { throw new OverflowException("Overflow_Int32"); } - } else { + } + else { maxVal = UInt32.MaxValue / radix; while (i < length && IsDigit(buffer[i], radix, out value)) { // Read all of the digits and convert to a number @@ -124,7 +123,8 @@ namespace System { if ((long) result < 0 && result != 0x8000000000000000) { throw new OverflowException("Overflow_Int64"); } - } else { + } + else { maxVal = unchecked((ulong) -1L) / radix; while (i < length && IsDigit(buffer[i], radix, out value)) { // Read all of the digits and convert to a number @@ -140,14 +140,16 @@ namespace System { } static bool IsDigit(char c, uint radix, out uint result) { - if (c>='0' && c<='9') { + if (c >='0' && c <='9') { result = (uint) (c-'0'); - } else { + } + else { char d = Char.ToLower(c); - if (d>='a' && d<='z') { + if (d >='a' && d <='z') { //+10 is necessary because a is actually 10, etc. result = (uint) (d-'a'+10); - } else { + } + else { result = 0; return false; } @@ -166,24 +168,24 @@ namespace System { //A radix of -1 says to use whatever base is spec'd on the number. //Parse in Base10 until we figure out what the base actually is. uint r = (-1==radix)?10U:(uint)radix; - if (r!=2 && r!=10 && r!=8 && r!=16) { + if (r != 2 && r != 10 && r != 8 && r != 16) { throw new ArgumentException("Arg_InvalidBase"); } int length = s.Length; - if (i <0 || i>=length) { + if (i < 0 || i >= length) { throw new ArgumentOutOfRangeException("startIndex(currPos[0])"); } // Get rid of the whitespace and then check that we've still got // some digits to parse. if ((flags & IsTight) != 0) { i = EatWhiteSpace(s,length,i); - if (i==length) { + if (i == length) { throw new FormatException("Format_EmptyInputString"); } } int sign = 1; bool isUnsigned = ((flags & TreatAsUnsigned) != 0); - if (s[i]=='-') { // Check for a sign + if (s[i] =='-') { // Check for a sign if (r != 10) { throw new ArgumentException("ArgCannotHaveNegativeValue"); } @@ -192,25 +194,26 @@ namespace System { } sign = -1; i++; - } else if (s[i]=='+') { + } + else if (s[i] =='+') { i++; } //Consume the 0x if we're in an unknown base or in base 16. - if ((radix==-1||radix==16) && (i+1= 0x80){ + if (result >= 0x80) { sign = -1; } - } else if ((flags & TreatAsI2) != 0) { + } + else if ((flags & TreatAsI2) != 0) { if (result > 0xFFFF) { throw new OverflowException("Overflow_Int16"); } @@ -233,12 +237,14 @@ namespace System { if (result >= 0x8000) { sign = -1; } - } else if (result == 0x80000000 && sign==1 && r==10) { + } + else if (result == 0x80000000 && sign == 1 && r == 10) { throw new OverflowException("Overflow_Int32"); } if (r == 10) { return unchecked((int)result) * sign; - } else { + } + else { return unchecked((int) result); } } @@ -252,7 +258,7 @@ namespace System { //A radix of -1 says to use whatever base is spec'd on the number. //Parse in Base10 until we figure out what the base actually is. uint r = (-1==radix)?10U:(uint)radix; - if (r!=2 && r!=10 && r!=8 && r!=16) { + if (r != 2 && r != 10 && r != 8 && r != 16) { throw new ArgumentException("Arg_InvalidBase"); } int length = s.Length; @@ -278,12 +284,13 @@ namespace System { } sign = -1; i++; - } else if (s[i] == '+') { + } + else if (s[i] == '+') { i++; } //Consume the 0x if we're in an unknown base or in base 16. - if ((radix==-1||radix==16) && (i+1MaxRadix) { + if (radix < MinRadix || radix > MaxRadix) { throw new ArgumentException("Argument_InvalidBase"); //COMPlusThrowArgumentException(L"radix", L"Arg_InvalidBase"); } @@ -387,15 +396,16 @@ namespace System { //If the number is negative, make it positive and remember the sign. //If the number is MIN_VALUE, this will still be negative, //so we'll have to special case this later. - if (n<0) { + if (n < 0) { isNegative=true; // For base 10, write out -num, but other bases write out the // 2's complement bit pattern - if (10==radix) + if (10 == radix) l = (uint)(-n); else - l = (uint)n; // REVIEW: markples: comment suggests ~n wanted - } else { + l = (uint)n; // REVIEW: comment suggests ~n wanted + } + else { l=(uint)n; } @@ -404,62 +414,71 @@ namespace System { //number. if ((flags & PrintAsI1) != 0) { l = l&0xFF; - } else if ((flags & PrintAsI2) != 0) { + } + else if ((flags & PrintAsI2) != 0) { l = l&0xFFFF; - } else if ((flags & PrintAsI4) != 0) { + } + else if ((flags & PrintAsI4) != 0) { l=l&0xFFFFFFFF; } - if (0==l) { //Special case the 0. + if (0 == l) { //Special case the 0. buffer[--index]='0'; - } else { + } + else { do { charVal = (int) (l%(uint)radix); l=l/(uint)radix; - if (charVal<10) { + if (charVal < 10) { buffer[--index] = (char)(charVal + '0'); - } else { + } + else { buffer[--index] = (char)(charVal + 'a' - 10); } - } while (l!=0); + } while (l != 0); } //If they want the base, append that to the string (in reverse order) - if (radix!=10 && ((flags & PrintBase)!=0)) { - if (16==radix) { + if (radix != 10 && ((flags & PrintBase) != 0)) { + if (16 == radix) { buffer[--index]='x'; buffer[--index]='0'; - } else if (8==radix) { + } + else if (8 == radix) { buffer[--index]='0'; } } - if (10==radix) { + if (10 == radix) { if (isNegative) { //If it was negative, append the sign. buffer[--index]='-'; - } else if ((flags&PrintSign)!=0) { + } + else if ((flags & PrintSign) != 0) { //else if they requested, add the '+'; buffer[--index]='+'; - } else if ((flags&PrefixSpace)!=0) { + } + else if ((flags & PrefixSpace) != 0) { //If they requested a leading space, put it on. buffer[--index]=' '; } } //Figure out the size of our string. - if (width<=bufSize-index) { + if (width <= bufSize - index) { buffLength=bufSize-index; - } else { + } + else { buffLength=width; } String local = String.StringCTOR(buffer, index, bufSize-index); - //markples: Pad if necessary, open up access to String if you + // Pad if necessary, open up access to String if you //don't want to make a new string here - if ((flags&LeftAlign)!=0) { + if ((flags & LeftAlign) != 0) { local = local.PadRight(buffLength,paddingChar); - } else { + } + else { local = local.PadLeft(buffLength,paddingChar); } @@ -470,7 +489,7 @@ namespace System { // INT64 n, WCHAR paddingChar, INT32 flags) public static String LongToString(long n, int radix, int width, char paddingChar, int flags) { - // rusa: see also Lightning\Src\VM\COMUtilNative.cpp::LongToString, + // See also Lightning\Src\VM\COMUtilNative.cpp::LongToString, bool isNegative = false; int charVal; ulong l; @@ -482,88 +501,99 @@ namespace System { char[] buffer = new char[67]; int index=bufSize; - if (radixMaxRadix) { + if (radix < MinRadix || radix > MaxRadix) { throw new ArgumentException("Argument_InvalidBase"); //COMPlusThrowArgumentException(L"radix", L"Arg_InvalidBase"); } //If the number is negative, make it positive and remember the sign. - if (n<0) { + if (n < 0) { isNegative=true; // For base 10, write out -num, but other bases write out the // 2's complement bit pattern - if (10==radix) + if (10 == radix) l = (ulong)(-n); else - l = (ulong)n; // REVIEW: markples: comment suggests ~n wanted - } else { + l = (ulong)n; // REVIEW: comment suggests ~n wanted + } + else { l=(ulong)n; } if ((flags & PrintAsI1) != 0) { l = l&0xFF; - } else if ((flags&PrintAsI2) != 0) { + } + else if ((flags & PrintAsI2) != 0) { l = l&0xFFFF; - } else if ((flags&PrintAsI4) != 0) { + } + else if ((flags & PrintAsI4) != 0) { l=l&0xFFFFFFFF; } //Special case the 0. - if (0==l) { + if (0 == l) { buffer[--index]='0'; - } else { + } + else { //Pull apart the number and put the digits (in //reverse order) into the buffer. - for ( ; l>0; l=l/(ulong)radix) { - if ((charVal=(int)(l%(ulong)radix))<10) { + for (; l > 0; l = l/(ulong)radix) { + if ((charVal =(int)(l%(ulong)radix)) < 10) { buffer[--index] = (char)(charVal + '0'); - } else { + } + else { buffer[--index] = (char)(charVal + 'a' - 10); } } } //If they want the base, append that to the string (in reverse order) - if (radix!=10 && ((flags&PrintBase)!=0)) { - if (16==radix) { + if (radix != 10 && ((flags & PrintBase) != 0)) { + if (16 == radix) { buffer[--index]='x'; buffer[--index]='0'; - } else if (8==radix) { + } + else if (8 == radix) { buffer[--index]='0'; - } else if ((flags&PrintRadixBase)!=0) { + } + else if ((flags & PrintRadixBase) != 0) { buffer[--index]='#'; buffer[--index]=(char)((radix%10)+'0'); buffer[--index]=(char)((radix/10)+'0'); } } - if (10==radix) { + if (10 == radix) { if (isNegative) { //If it was negative, append the sign. buffer[--index]='-'; - } else if ((flags&PrintSign)!=0) { + } + else if ((flags & PrintSign) != 0) { //else if they requested, add the '+'; buffer[--index]='+'; - } else if ((flags&PrefixSpace)!=0) { + } + else if ((flags & PrefixSpace) != 0) { //If they requested a leading space, put it on. buffer[--index]=' '; } } //Figure out the size of our string. - if (width<=bufSize-index) { + if (width <= bufSize - index) { buffLength=bufSize-index; - } else { + } + else { buffLength=width; } String local = String.StringCTOR(buffer, index, bufSize-index); - //markples: Pad if necessary, open up access to String if you + //Pad if necessary, open up access to String if you //don't want to make a new string here - if ((flags&LeftAlign)!=0) { + if ((flags & LeftAlign) != 0) { local = local.PadRight(buffLength,paddingChar); - } else { + } + else { local = local.PadLeft(buffLength,paddingChar); } diff --git a/base/Applications/Runtime/System/Principal.sg b/base/Applications/Runtime/Full/System/Principal.sg similarity index 82% rename from base/Applications/Runtime/System/Principal.sg rename to base/Applications/Runtime/Full/System/Principal.sg index 5337411..f80cf6c 100644 --- a/base/Applications/Runtime/System/Principal.sg +++ b/base/Applications/Runtime/Full/System/Principal.sg @@ -1,7 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // +// ---------------------------------------------------------------------------- namespace Microsoft.Singularity.Security { @@ -46,6 +47,11 @@ namespace Microsoft.Singularity.Security return new Principal(ep->PeerPrincipalHandle); } + public static Principal EndpointOwner(Endpoint*! in ExHeap ep) + { + return new Principal(ep->OwnerPrincipalHandle); + } + public static string ExpandAclIndirection (string! name) { return PrincipalHandle.ExpandAclIndirection(name); diff --git a/base/Applications/Runtime/System/Process.sg b/base/Applications/Runtime/Full/System/Process.sg similarity index 94% rename from base/Applications/Runtime/System/Process.sg rename to base/Applications/Runtime/Full/System/Process.sg index ceab039..cef543c 100644 --- a/base/Applications/Runtime/System/Process.sg +++ b/base/Applications/Runtime/Full/System/Process.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Process.sg -// // Note: // @@ -86,7 +84,7 @@ namespace System null, out count, out totalCharCount); - //Review for now NotSet is ignored and we return a string[1] below + // REVIEW: for now NotSet is ignored and we return a string[1] below if (code != ParameterCode.Success && code != ParameterCode.NotSet) { arguments = new string[0]; return code; @@ -128,8 +126,8 @@ namespace System value = null; return code; } - if ( len == 0 ) { - // should this be "" or null? + if (len == 0) { + // REVIEW: should this be "" or null? value = null; return ParameterCode.Success; } @@ -198,7 +196,7 @@ namespace System // need to copy until the kernel and system are compiled // with the same compiler char[] c = new char[value.Length]; - for (int i=0; i < value.Length; i++){ + for (int i = 0; i < value.Length; i++) { c[i] = value[i]; } fixed ( char *argptr = &c[0] ) { @@ -306,6 +304,8 @@ namespace System String action, String role) { + if (cmd.Length == 0) + throw new ArgumentException("The 'cmd' argument is required, and cannot be null or empty.", "cmd"); char[]! cmdVector; char[]! actionVector; @@ -321,7 +321,7 @@ namespace System cmd.CopyTo(0, cmdVector, 0, cmdLength); // package the action parameter(if present) - if (action != null) { + if (action != null && action.Length != 0) { actionLength = action.Length; actionVector = new char[actionLength]; action.CopyTo(0, actionVector, 0, actionLength); @@ -332,7 +332,7 @@ namespace System } // package the role parameters (if present) - if (role != null) { + if (role != null && role.Length != 0) { roleLength = role.Length; roleVector = new char[roleLength]; role.CopyTo(0, roleVector, 0, roleLength); @@ -396,7 +396,7 @@ namespace System public unsafe Process(String[]! arguments, String role, [Claims] Endpoint* in ExHeap endpoint) - : this((!)arguments[0], null, null) + : this((!)arguments[0], null, role) { ProcessHandle.SetStartupEndpoint(handle, 0, (SharedHeapService.Allocation *)endpoint); @@ -413,8 +413,8 @@ namespace System [Microsoft.Contracts.NotDelayed] public unsafe Process(String[]! arguments, - String roll) - : this(arguments, roll, 0) + String role) + : this(arguments, role, 0) { } @@ -557,8 +557,7 @@ namespace System { get { - if (handle.id == 0) - { + if (handle.id == 0) { throw new ProcessStateException("disposed"); } Principal p = diff --git a/base/Applications/Runtime/System/ProcessCreateException.cs b/base/Applications/Runtime/Full/System/ProcessCreateException.cs similarity index 100% rename from base/Applications/Runtime/System/ProcessCreateException.cs rename to base/Applications/Runtime/Full/System/ProcessCreateException.cs diff --git a/base/Kernel/System/ProcessExitCode.cs b/base/Applications/Runtime/Full/System/ProcessExitCode.cs similarity index 100% rename from base/Kernel/System/ProcessExitCode.cs rename to base/Applications/Runtime/Full/System/ProcessExitCode.cs diff --git a/base/Kernel/System/ProcessStateException.cs b/base/Applications/Runtime/Full/System/ProcessStateException.cs similarity index 100% rename from base/Kernel/System/ProcessStateException.cs rename to base/Applications/Runtime/Full/System/ProcessStateException.cs diff --git a/base/Applications/Runtime/Full/System/Random.cs b/base/Applications/Runtime/Full/System/Random.cs new file mode 100644 index 0000000..a1fd677 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Random.cs @@ -0,0 +1,194 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================ +// +// Class: Random.cool +// +// Purpose: A random number generator. +// +//=========================================================== +namespace System +{ + + using System; + using System.Runtime.CompilerServices; + //| + + public class Random { + // + // Private Constants + // + private const int MBIG = Int32.MaxValue; + private const int MSEED = 161803398; + private const int MZ = 0; + + + // + // Member Variables + // + private int inext, inextp; + private int[] SeedArray = new int[56]; + + // + // Public Constants + // + + // + // Native Declarations + // + + // + // Constructors + // + + //| + public Random() + : this(Environment.TickCount) { + } + + //| + public Random(int Seed) { + int ii; + int mj, mk; + + //Initialize our Seed array. + //This algorithm comes from Numerical Recipes in C (2nd Ed.) + mj = MSEED - Math.Abs(Seed); + SeedArray[55]=mj; + mk=1; + for (int i = 1; i < 55; i++) { //Apparently the range [1..55] is special (Knuth) and so we're wasting the 0'th position. + ii = (21*i)%55; + SeedArray[ii]=mk; + mk = mj - mk; + if (mk < 0) mk += MBIG; + mj=SeedArray[ii]; + } + for (int k = 1; k < 5; k++) { + for (int i = 1; i < 56; i++) { + SeedArray[i] -= SeedArray[1+(i+30)%55]; + if (SeedArray[i] < 0) SeedArray[i] += MBIG; + } + } + inext=0; + inextp = 21; + Seed = 1; + } + + // + // Package Private Methods + // + + //====================================Sample==================================== + //Action: Return a new random number [0..1) and reSeed the Seed array. + //Returns: A double [0..1) + //Arguments: None + //Exceptions: None + //============================================================================== + //| + protected virtual double Sample() { + int retVal; + int locINext = inext; + int locINextp = inextp; + + if (++locINext >= 56) locINext = 1; + if (++locINextp >= 56) locINextp = 1; + + retVal = SeedArray[locINext]-SeedArray[locINextp]; + + if (retVal < 0) retVal += MBIG; + + SeedArray[locINext]=retVal; + + inext = locINext; + inextp = locINextp; + + //Including this division at the end gives us significantly improved + //random number distribution. + return (retVal*(1.0/MBIG)); + } + + // + // Public Instance Methods + // + + + //=====================================Next===================================== + //Returns: An int [0.._int4.MaxValue) + //Arguments: None + //Exceptions: None. + //============================================================================== + //| + public virtual int Next() { + return (int)(Sample()*Int32.MaxValue); + } + + //=====================================Next===================================== + //Returns: An int [minvalue..maxvalue) + //Arguments: minValue -- the least legal value for the Random number. + // maxValue -- the greatest legal return value. + //Exceptions: None. + //============================================================================== + //| + public virtual int Next(int minValue, int maxValue) { + if (minValue > maxValue) { + throw new ArgumentOutOfRangeException("minValue",String.Format("Argument_MinMaxValue", "minValue", "maxValue")); + } + + int range = (maxValue-minValue); + + //This is the case where we flipped around (e.g. MaxValue-MinValue); + if (range < 0) { + long longRange = (long)maxValue-(long)minValue; + return (int)(((long)(Sample()*((double)longRange)))+minValue); + } + + return ((int)(Sample()*(range)))+minValue; + } + + + //=====================================Next===================================== + //Returns: An int [0..maxValue) + //Arguments: maxValue -- the greatest legal return value. + //Exceptions: None. + //============================================================================== + //| + public virtual int Next(int maxValue) { + if (maxValue < 0) { + throw new ArgumentOutOfRangeException("maxValue", String.Format("ArgumentOutOfRange_MustBePositive", "maxValue")); + } + return (int)(Sample()*maxValue); + } + + + //=====================================Next===================================== + //Returns: A double [0..1) + //Arguments: None + //Exceptions: None + //============================================================================== + //| + public virtual double NextDouble() { + return Sample(); + } + + + //==================================NextBytes=================================== + //Action: Fills the byte array with random bytes [0..0x7f]. The entire array is filled. + //Returns:Void + //Arguments: buffer -- the array to be filled. + //Exceptions: None + //============================================================================== + //| + public virtual void NextBytes(byte [] buffer){ + if (buffer==null) throw new ArgumentNullException("buffer"); + for (int i = 0; i < buffer.Length; i++) { + buffer[i]=(byte)(Sample()*(Byte.MaxValue+1)); + } + } + } + + + +} diff --git a/base/Applications/Runtime/Full/System/RankException.cs b/base/Applications/Runtime/Full/System/RankException.cs new file mode 100644 index 0000000..edc2b0d --- /dev/null +++ b/base/Applications/Runtime/Full/System/RankException.cs @@ -0,0 +1,38 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: RankException +// +// Purpose: For methods that are passed arrays with the wrong number of +// dimensions. +// +//============================================================================= + +namespace System +{ + + using System; + + //| + public class RankException : SystemException + { + //| + public RankException() + : base("Arg_RankException") { + } + + //| + public RankException(String message) + : base(message) { + } + + //| + public RankException(String message, Exception innerException) + : base(message, innerException) { + } + } +} diff --git a/base/Applications/Runtime/Full/System/Reflection/Assembly.cs b/base/Applications/Runtime/Full/System/Reflection/Assembly.cs new file mode 100644 index 0000000..90fdab5 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Reflection/Assembly.cs @@ -0,0 +1,106 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: Assembly +// +// Purpose: For Assembly-related stuff. +// +//============================================================================= + +namespace System.Reflection +{ + + using System; + using System.Runtime.CompilerServices; + + //| + public partial class Assembly + { + // ---------- Bartok code ---------- + + public AssemblyName Name + { + get { + return this.assemblyName; + } + } + + internal String nGetSimpleName() { + return this.assemblyName.Name; + } + + private String GetFullName() { + String name = + this.nGetSimpleName() + + ", Version=" + this.assemblyName.Version.Major + + "." + this.assemblyName.Version.Minor + + "." + this.assemblyName.Version.Build + + "." + this.assemblyName.Version.Revision + + ", Culture=" + + (this.assemblyName.Culture != "" + ? this.assemblyName.Culture + : "neutral") + + ", PublicKeyToken=" + + (this.assemblyName.GetPublicKeyToken() != null + ? (Assembly.EncodeHexString + (this.assemblyName.GetPublicKeyToken())) + : "null"); + return name; + } + + // ---------- copied from mscorlib System.Security.Util.Hex ---------- + + // changed to lowercase to avoid ToLower call since the code is no + // longer shared in Util.Hex + private static char[] hexValues = + { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f' }; + + private static String EncodeHexString(byte[] sArray) + { + String result = null; + + if (sArray != null) { + char[] hexOrder = new char[sArray.Length * 2]; + + int digit; + for (int i = 0, j = 0; i < sArray.Length; i++) { + digit = (int)((sArray[i] & 0xf0) >> 4); + hexOrder[j++] = hexValues[digit]; + digit = (int)(sArray[i] & 0x0f); + hexOrder[j++] = hexValues[digit]; + } + result = new String(hexOrder); + } + return result; + } + + // ---------- mscorlib code ---------- + // (some modifications to pull in less code) + + //| + public virtual String FullName { + get { + // If called by Object.ToString(), return val may be NULL. + String s; + + // not implementing InternalCache for now + //if ((s = (String)Cache[CacheObjType.AssemblyName]) != null) + // return s; + // + + s = GetFullName(); + // not implementing InternalCache for now + //if (s != null) + // Cache[CacheObjType.AssemblyName] = s; + // + + return s; + } + } + } +} diff --git a/base/Kernel/System/Reflection/AssemblyAttributes.cs b/base/Applications/Runtime/Full/System/Reflection/AssemblyAttributes.cs similarity index 90% rename from base/Kernel/System/Reflection/AssemblyAttributes.cs rename to base/Applications/Runtime/Full/System/Reflection/AssemblyAttributes.cs index c8c7b50..ce3e755 100644 --- a/base/Kernel/System/Reflection/AssemblyAttributes.cs +++ b/base/Applications/Runtime/Full/System/Reflection/AssemblyAttributes.cs @@ -3,18 +3,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================================= -** -** File: AssemblyAttributes -** -** -** Purpose: For Assembly-related custom attributes. -** -** Date: April 12, 2000 -** -=============================================================================*/ +//============================================================================= +// +// Purpose: For Assembly-related custom attributes. +// +//============================================================================= -namespace System.Reflection { +namespace System.Reflection +{ using System; diff --git a/base/Applications/Runtime/Full/System/Reflection/AssemblyName.cs b/base/Applications/Runtime/Full/System/Reflection/AssemblyName.cs new file mode 100644 index 0000000..03224f8 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Reflection/AssemblyName.cs @@ -0,0 +1,57 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================ +// +// Purpose: Used for binding and retrieving info about an assembly +// +//=========================================================== +namespace System.Reflection +{ + using System.Runtime.CompilerServices; + + public partial class AssemblyName { + public String Culture { + get { return _Culture; } + } + + // Set and get the name of the assembly. If this is a weak Name + // then it optionally contains a site. For strong assembly names, + // the name partitions up the strong name's namespace + //| + public String Name + { + get { return _Name; } + // not needed for now + //set { _Name = value; } + // + } + + //| + public Version Version + { + get { + return _Version; + } + // not needed for now + //set { + // _Version = value; + //} + // + } + + // The compressed version of the public key formed from a truncated hash. + //| + public byte[] GetPublicKeyToken() + { + // not needed for now + //if ((_PublicKeyToken == null) && + // (_Flags & AssemblyNameFlags.PublicKey) != 0) + // _PublicKeyToken = nGetPublicKeyToken(); + // + return _PublicKeyToken; + } + } +} diff --git a/base/Kernel/System/Reflection/AssemblyReflectionAttributes.cs b/base/Applications/Runtime/Full/System/Reflection/AssemblyReflectionAttributes.cs similarity index 92% rename from base/Kernel/System/Reflection/AssemblyReflectionAttributes.cs rename to base/Applications/Runtime/Full/System/Reflection/AssemblyReflectionAttributes.cs index 9f4e121..963e88d 100644 --- a/base/Kernel/System/Reflection/AssemblyReflectionAttributes.cs +++ b/base/Applications/Runtime/Full/System/Reflection/AssemblyReflectionAttributes.cs @@ -3,18 +3,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================================= -** -** File: AssemblyReflectionAttributes.cool -** -** -** Purpose: For Assembly-related reflection custom attributes. -** -** Date: April 12, 2000 -** -=============================================================================*/ +//============================================================================= +// +// Purpose: For Assembly-related reflection custom attributes. +// +//============================================================================= -namespace System.Reflection { +namespace System.Reflection +{ using System; diff --git a/base/Kernel/System/Reflection/DefaultMemberAttribute.cs b/base/Applications/Runtime/Full/System/Reflection/DefaultMemberAttribute.cs similarity index 94% rename from base/Kernel/System/Reflection/DefaultMemberAttribute.cs rename to base/Applications/Runtime/Full/System/Reflection/DefaultMemberAttribute.cs index 2242cdc..7a126d9 100644 --- a/base/Kernel/System/Reflection/DefaultMemberAttribute.cs +++ b/base/Applications/Runtime/Full/System/Reflection/DefaultMemberAttribute.cs @@ -10,9 +10,8 @@ // member used by Type.InvokeMember. The default member is simply a name given // to a type. // -// Date: Oct 99 -// -namespace System.Reflection { +namespace System.Reflection +{ using System; diff --git a/base/Kernel/System/Reflection/TypeAttributes.cs b/base/Applications/Runtime/Full/System/Reflection/TypeAttributes.cs similarity index 97% rename from base/Kernel/System/Reflection/TypeAttributes.cs rename to base/Applications/Runtime/Full/System/Reflection/TypeAttributes.cs index 8c451de..8955930 100644 --- a/base/Kernel/System/Reflection/TypeAttributes.cs +++ b/base/Applications/Runtime/Full/System/Reflection/TypeAttributes.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System.Reflection { +namespace System.Reflection +{ using System.Runtime.InteropServices; using System; // This Enum matches the CorTypeAttr defined in CorHdr.h diff --git a/base/Kernel/System/Runtime/CompilerServices/DecimalConstantAttribute.cs b/base/Applications/Runtime/Full/System/Runtime/CompilerServices/DecimalConstantAttribute.cs similarity index 100% rename from base/Kernel/System/Runtime/CompilerServices/DecimalConstantAttribute.cs rename to base/Applications/Runtime/Full/System/Runtime/CompilerServices/DecimalConstantAttribute.cs diff --git a/base/Kernel/System/Runtime/CompilerServices/IndexerNameAttribute.cs b/base/Applications/Runtime/Full/System/Runtime/CompilerServices/IndexerNameAttribute.cs similarity index 100% rename from base/Kernel/System/Runtime/CompilerServices/IndexerNameAttribute.cs rename to base/Applications/Runtime/Full/System/Runtime/CompilerServices/IndexerNameAttribute.cs diff --git a/base/Kernel/System/Runtime/CompilerServices/IsVolatile.cs b/base/Applications/Runtime/Full/System/Runtime/CompilerServices/IsVolatile.cs similarity index 100% rename from base/Kernel/System/Runtime/CompilerServices/IsVolatile.cs rename to base/Applications/Runtime/Full/System/Runtime/CompilerServices/IsVolatile.cs diff --git a/base/Kernel/System/Runtime/CompilerServices/MethodImplAttribute.cs b/base/Applications/Runtime/Full/System/Runtime/CompilerServices/MethodImplAttribute.cs similarity index 95% rename from base/Kernel/System/Runtime/CompilerServices/MethodImplAttribute.cs rename to base/Applications/Runtime/Full/System/Runtime/CompilerServices/MethodImplAttribute.cs index 7819c7e..b4d46d6 100644 --- a/base/Kernel/System/Runtime/CompilerServices/MethodImplAttribute.cs +++ b/base/Applications/Runtime/Full/System/Runtime/CompilerServices/MethodImplAttribute.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System.Runtime.CompilerServices { +namespace System.Runtime.CompilerServices +{ using System; diff --git a/base/Kernel/System/Runtime/CompilerServices/RuntimeHelpers.cs b/base/Applications/Runtime/Full/System/Runtime/CompilerServices/RuntimeHelpers.cs similarity index 92% rename from base/Kernel/System/Runtime/CompilerServices/RuntimeHelpers.cs rename to base/Applications/Runtime/Full/System/Runtime/CompilerServices/RuntimeHelpers.cs index f61fe5d..d40cb4c 100644 --- a/base/Kernel/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/base/Applications/Runtime/Full/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -9,9 +9,8 @@ // RuntimeHelpers // This class defines a set of static methods that provide support for compilers. // -// Date: April 2000 -// -namespace System.Runtime.CompilerServices { +namespace System.Runtime.CompilerServices +{ using System; using System.Runtime.CompilerServices; @@ -36,7 +35,7 @@ namespace System.Runtime.CompilerServices { // or pass it as a parameter. The goal is to make sure that boxed // value types work identical to unboxed value types - ie, they get // cloned when you pass them around, and are always passed by value. - // Of course, reference types are not cloned. -- BrianGru 7/12/2001 + // Of course, reference types are not cloned. // //| [MethodImpl(MethodImplOptions.InternalCall)] @@ -65,6 +64,7 @@ namespace System.Runtime.CompilerServices { //| public static int OffsetToStringData { + [NoHeapAllocation] get { // Number of bytes from the address pointed to by a reference to // a String to the first 16-bit character in the String. Skip @@ -72,7 +72,7 @@ namespace System.Runtime.CompilerServices { // length. Of course, the String reference points to the memory // after the sync block, so don't count that. // This property allows C#'s fixed statement to work on Strings. - // On 64 bit platforms, this should be 16. -- BrianGru + // On 64 bit platforms, this should be 16. #if PTR_SIZE_32 return 12; #else diff --git a/base/Applications/Runtime/Full/System/Runtime/InteropServices/Attributes.cs b/base/Applications/Runtime/Full/System/Runtime/InteropServices/Attributes.cs new file mode 100644 index 0000000..72214df --- /dev/null +++ b/base/Applications/Runtime/Full/System/Runtime/InteropServices/Attributes.cs @@ -0,0 +1,96 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +namespace System.Runtime.InteropServices +{ + + using System; + + //| + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Struct | AttributeTargets.Delegate, Inherited = false)] + public sealed class GuidAttribute : Attribute + { + internal String _val; + //| + public GuidAttribute(String guid) + { + _val = guid; + } + //| + public String Value { get {return _val;} } + } + + //| + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + public sealed class InAttribute : Attribute + { + //| + public InAttribute() + { + } + } + + //| + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + public sealed class OutAttribute : Attribute + { + //| + public OutAttribute() + { + } + } + + //| + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + public sealed class OptionalAttribute : Attribute + { + //| + public OptionalAttribute() + { + } + } + + //| + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)] + public sealed class StructLayoutAttribute : Attribute + { + internal LayoutKind _val; + //| + public StructLayoutAttribute(LayoutKind layoutKind) + { + _val = layoutKind; + } + //| + public StructLayoutAttribute(short layoutKind) + { + _val = (LayoutKind)layoutKind; + } + //| + public LayoutKind Value { get {return _val;} } + //| + public int Pack; + //| + public int Size; + //| + public CharSet CharSet; + } + + //| + [AttributeUsage(AttributeTargets.Field, Inherited = false)] + public sealed class FieldOffsetAttribute : Attribute + { + internal int _val; + //| + public FieldOffsetAttribute(int offset) + { + _val = offset; + } + //| + public int Value { get {return _val;} } + } + +} diff --git a/base/Kernel/System/Runtime/InteropServices/CharSet.cs b/base/Applications/Runtime/Full/System/Runtime/InteropServices/CharSet.cs similarity index 93% rename from base/Kernel/System/Runtime/InteropServices/CharSet.cs rename to base/Applications/Runtime/Full/System/Runtime/InteropServices/CharSet.cs index 036d548..50cf748 100644 --- a/base/Kernel/System/Runtime/InteropServices/CharSet.cs +++ b/base/Applications/Runtime/Full/System/Runtime/InteropServices/CharSet.cs @@ -5,7 +5,8 @@ // ==--== //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -namespace System.Runtime.InteropServices { +namespace System.Runtime.InteropServices +{ using System; //| public enum CharSet diff --git a/base/Applications/Runtime/Full/System/Runtime/InteropServices/GCHandle.cs b/base/Applications/Runtime/Full/System/Runtime/InteropServices/GCHandle.cs new file mode 100644 index 0000000..8eb9708 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Runtime/InteropServices/GCHandle.cs @@ -0,0 +1,284 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +namespace System.Runtime.InteropServices +{ + + using System; + using System.Threading; + using System.Runtime.CompilerServices; + + // These are the types of handles used by the EE. IMPORTANT: These must + // match the definitions in ObjectHandle.h in the EE. + //| + public enum GCHandleType + { + //| + Weak = 0, + //| + WeakTrackResurrection = 1, + //| + Normal = 2, + //| + Pinned = 3 + } + + // This class allows you to create an opaque, GC handle to any + // managed object. A GC handle is used when an object reference must be + // reachable from unmanaged memory. There are 3 kinds of roots: + // Normal - keeps the object from being collected. + // Weak - allows object to be collected and handle contents will be zeroed. + // Weak references are zeroed before the finalizer runs, so if the + // object is resurrected in the finalizer the weak reference is + // still zeroed. + // WeakTrackResurrection - Same as weak, but stays until after object is + // really gone. + // Pinned - same as normal, but allows the address of the actual object + // to be taken. + // + //| + [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)] + public struct GCHandle + { + // Allocate a handle storing the object and the type. + internal GCHandle(Object value, GCHandleType type) + { + m_handle = InternalAlloc(value, type); + + // Record if the handle is pinned. + if (type == GCHandleType.Pinned) + m_handle |= 1; + } + + // Used in the conversion functions below. + internal GCHandle(IntPtr handle) + { + InternalCheckDomain((int) handle); + m_handle = (int)handle; + } + + // Creates a new GC handle for an object. + // + // value - The object that the GC handle is created for. + // type - The type of GC handle to create. + // + // returns a new GC handle that protects the object. + //| + public static GCHandle Alloc(Object value) + { + return new GCHandle(value, GCHandleType.Normal); + } + + //| + public static GCHandle Alloc(Object value, GCHandleType type) + { + return new GCHandle(value, type); + } + + // Frees a GC handle. The caller must provide synchronization to + // prevent multiple threads from executing this simultaneously for + // a given handle. If you modify this method please modify the + // __InternalFree also which is the internal without the link-time check. + //| + public void Free() + { + // Check if the handle was never initialized for was freed. + if (m_handle == 0) + throw new InvalidOperationException("InvalidOperation_HandleIsNotInitialized"); + + // Free the handle. + InternalFree(m_handle & ~0x1); + m_handle = 0; + } + + internal void __InternalFree() + { + // Check if the handle was never initialized for was freed. + if (m_handle == 0) + throw new InvalidOperationException("InvalidOperation_HandleIsNotInitialized"); + + // Free the handle. + InternalFree(m_handle & ~0x1); + m_handle = 0; + } + + // Target property - allows getting / updating of the handle's referent. If you modify this method + + // then modify the __InternalTarget too which is the internal method without the link-time check. + //| + public Object Target + { + get + { + // Check if the handle was never initialized or was freed. + if (m_handle == 0) + throw new InvalidOperationException("InvalidOperation_HandleIsNotInitialized"); + + return InternalGet(m_handle & ~0x1); + } + + set + { + // Check if the handle was never initialized or was freed. + if (m_handle == 0) + throw new InvalidOperationException("InvalidOperation_HandleIsNotInitialized"); + + InternalSet(m_handle & ~0x1, value, (m_handle & 0x1) != 0 /* isPinned */); + } + } + + internal Object __InternalTarget + { + get + { + // Check if the handle was never initialized or was freed. + if (m_handle == 0) + throw new InvalidOperationException("InvalidOperation_HandleIsNotInitialized"); + + return InternalGet(m_handle & ~0x1); + } + } + + // Retrieve the address of an object in a Pinned handle. This throws + // an exception if the handle is any type other than Pinned. + //| + public IntPtr AddrOfPinnedObject() + { + // Check if the handle was not a pinned handle. + if ((m_handle & 1) == 0) { + // Check if the handle was never initialized for was freed. + if (m_handle == 0) + throw new InvalidOperationException("InvalidOperation_HandleIsNotInitialized"); + + // You can only get the address of pinned handles. + throw new InvalidOperationException("InvalidOperation_HandleIsNotPinned"); + } + + // Get the address. + return InternalAddrOfPinnedObject(m_handle & ~0x1); + } + + // Determine whether this handle has been allocated or not. + //| + public bool IsAllocated + { + get + { + return m_handle != 0; + } + } + + // Used to create a GCHandle from an int. This is intended to + // be used with the reverse conversion. + //| + public static explicit operator GCHandle(IntPtr value) + { + return new GCHandle(value); + } + + // Used to get the internal integer representation of the handle out. + //| + public static explicit operator IntPtr(GCHandle value) + { + return (IntPtr)value.m_handle; + } + + // Internal native calls that this implementation uses. + internal static int InternalAlloc(Object value, GCHandleType type) + { + int result, newIndex; + if (type == GCHandleType.Pinned) { + throw new Exception("GCHandle for pinned objects not yet implemented"); + } + do { + if (nextFreeIndex < 0) { + lock (handleToken) { + if (nextFreeIndex < 0) { + if (handleTable == null) { + handleTable = new HandleData[3]; + handleTable[0].nextIndex = -1; + handleTable[1].nextIndex = 2; + handleTable[2].nextIndex = -1; + nextFreeIndex = 1; + } + else { + int oldLength = handleTable.Length; + int newLength = oldLength * 2; + HandleData[] newTable = new HandleData[newLength]; + Array.Copy(handleTable, 0, newTable, 0, oldLength); + handleTable = newTable; + for (int i = oldLength + 1; i < newLength; i++) { + handleTable[i].nextIndex = i + 1; + } + handleTable[newLength-1].nextIndex = -1; + nextFreeIndex = oldLength+1; + } + } + } + } + result = nextFreeIndex; + newIndex = handleTable[result].nextIndex; + } while (Interlocked.CompareExchange(ref nextFreeIndex, newIndex, result) != result); + handleTable[result].obj = value; + handleTable[result].type = type; + // TODO: We should probably maintain a list of handles of each type + return result; + } + + internal static void InternalFree(int handle) + { + handleTable[handle].obj = null; + int oldFreeIndex = nextFreeIndex; + handleTable[handle].nextIndex = oldFreeIndex; + while (Interlocked.CompareExchange(ref nextFreeIndex, handle, oldFreeIndex) != oldFreeIndex) { + oldFreeIndex = nextFreeIndex; + handleTable[handle].nextIndex = oldFreeIndex; + } + } + + internal static Object InternalGet(int handle) + { + return handleTable[handle].obj; + } + + internal static void InternalSet(int handle, Object value, bool isPinned) + { + throw new Exception("System.Runtime.InteropServices.GCHandle.InternalSet not implemented in Bartok!"); + } + + internal static void InternalCompareExchange(int handle, Object value, Object oldValue, bool isPinned) + { + if (isPinned) { + throw new Exception("CompareExchange on a pinned object?!?"); + } + Interlocked.CompareExchange(ref handleTable[handle].obj, value, oldValue); + } + + internal static IntPtr InternalAddrOfPinnedObject(int handle) + { + throw new Exception("System.Runtime.InteropServices.GCHandle.InternalAddrOfPinnedObject not implemented in Bartok!"); + } + + internal static void InternalCheckDomain(int handle) + { + throw new Exception("System.Runtime.InteropServices.GCHandle.InternalCheckDomain not implemented in Bartok!"); + } + + // The actual integer handle value that the EE uses internally. + private int m_handle; + + private static HandleData[] handleTable; + + private static int nextFreeIndex = -1; + + private static Object handleToken = new Object(); + + private struct HandleData { + public Object obj; + public GCHandleType type; + public int nextIndex; + } + } +} diff --git a/base/Kernel/System/Runtime/InteropServices/LayoutKind.cs b/base/Applications/Runtime/Full/System/Runtime/InteropServices/LayoutKind.cs similarity index 92% rename from base/Kernel/System/Runtime/InteropServices/LayoutKind.cs rename to base/Applications/Runtime/Full/System/Runtime/InteropServices/LayoutKind.cs index 86167da..dd42de7 100644 --- a/base/Kernel/System/Runtime/InteropServices/LayoutKind.cs +++ b/base/Applications/Runtime/Full/System/Runtime/InteropServices/LayoutKind.cs @@ -5,7 +5,8 @@ // ==--== //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -namespace System.Runtime.InteropServices { +namespace System.Runtime.InteropServices +{ using System; // Used in the StructLayoutAttribute class //| diff --git a/base/Kernel/System/Runtime/InteropServices/Marshal.cs b/base/Applications/Runtime/Full/System/Runtime/InteropServices/Marshal.cs similarity index 83% rename from base/Kernel/System/Runtime/InteropServices/Marshal.cs rename to base/Applications/Runtime/Full/System/Runtime/InteropServices/Marshal.cs index 5e47596..3c0d5ae 100644 --- a/base/Kernel/System/Runtime/InteropServices/Marshal.cs +++ b/base/Applications/Runtime/Full/System/Runtime/InteropServices/Marshal.cs @@ -3,19 +3,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================================= -** -** Class: Marshal -** -** -** Purpose: This class contains methods that are mainly used to marshal -** between unmanaged and managed types. -** -** Date: January 31, 2000 -** -=============================================================================*/ +//============================================================================= +// +// Class: Marshal +// +// Purpose: This class contains methods that are mainly used to marshal +// between unmanaged and managed types. +// +//============================================================================= -namespace System.Runtime.InteropServices { +namespace System.Runtime.InteropServices +{ using Microsoft.Bartok.Runtime; using System; @@ -41,10 +39,12 @@ namespace System.Runtime.InteropServices { if (vtable.arrayOf == StructuralType.None) { if (vtable.Equals("string".vtable)) { throw new Exception("SizeOf not implemented for string objects"); - } else { + } + else { return vtable.marshalSize; } - } else { + } + else { int elementSize = vtable.arrayElementSize; int elementMask = elementSize - 1; int numElements = ((Array) structure).Length; @@ -62,7 +62,8 @@ namespace System.Runtime.InteropServices { if (vtable.arrayOf == StructuralType.None && !t.Equals("string".GetType())) { return vtable.marshalSize; - } else { + } + else { throw new Exception("SizeOf not implemented for type "+t); } } @@ -77,7 +78,8 @@ namespace System.Runtime.InteropServices { if (vtable.arrayOf == StructuralType.None && !t.Equals("string".GetType())) { return vtable.arrayElementSize; - } else { + } + else { throw new Exception("SizeOf not implemented for type "+t); } } diff --git a/base/Applications/Runtime/Full/System/RuntimeArgumentHandle.cs b/base/Applications/Runtime/Full/System/RuntimeArgumentHandle.cs new file mode 100644 index 0000000..af74f33 --- /dev/null +++ b/base/Applications/Runtime/Full/System/RuntimeArgumentHandle.cs @@ -0,0 +1,25 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +namespace System +{ + + using System; + using System.Runtime.CompilerServices; + // This value type is used for constructing System.ArgIterator. + // + // SECURITY : m_ptr cannot be set to anything other than null by untrusted + // code. + // + // This corresponds to EE VARARGS cookie. + + //| + public unsafe partial struct RuntimeArgumentHandle { + internal IntPtr Pointer { + [NoHeapAllocation] + get { return (IntPtr) (UIntPtr) m_ptr; } + } + } +} diff --git a/base/Kernel/System/RuntimeFieldHandle.cs b/base/Applications/Runtime/Full/System/RuntimeFieldHandle.cs similarity index 93% rename from base/Kernel/System/RuntimeFieldHandle.cs rename to base/Applications/Runtime/Full/System/RuntimeFieldHandle.cs index 046fa2e..93d8322 100644 --- a/base/Kernel/System/RuntimeFieldHandle.cs +++ b/base/Applications/Runtime/Full/System/RuntimeFieldHandle.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System { +namespace System +{ using System; using System.Reflection; diff --git a/base/Kernel/System/RuntimeMethodHandle.cs b/base/Applications/Runtime/Full/System/RuntimeMethodHandle.cs similarity index 93% rename from base/Kernel/System/RuntimeMethodHandle.cs rename to base/Applications/Runtime/Full/System/RuntimeMethodHandle.cs index 01d85cb..e9037b9 100644 --- a/base/Kernel/System/RuntimeMethodHandle.cs +++ b/base/Applications/Runtime/Full/System/RuntimeMethodHandle.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System { +namespace System +{ using System; using System.Reflection; diff --git a/base/Kernel/System/RuntimeTypeHandle.cs b/base/Applications/Runtime/Full/System/RuntimeTypeHandle.cs similarity index 88% rename from base/Kernel/System/RuntimeTypeHandle.cs rename to base/Applications/Runtime/Full/System/RuntimeTypeHandle.cs index e2c6314..80be078 100644 --- a/base/Kernel/System/RuntimeTypeHandle.cs +++ b/base/Applications/Runtime/Full/System/RuntimeTypeHandle.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System { +namespace System +{ using Microsoft.Bartok.Runtime; @@ -32,6 +33,8 @@ namespace System { //| public IntPtr Value { + // BUGBUG: What if the handle is a field in a class? --Bjarne + [NoBarriers] [NoHeapAllocation] get { return m_ptr; diff --git a/base/Kernel/System/SByte.cs b/base/Applications/Runtime/Full/System/SByte.cs similarity index 90% rename from base/Kernel/System/SByte.cs rename to base/Applications/Runtime/Full/System/SByte.cs index 7809280..97f447e 100644 --- a/base/Kernel/System/SByte.cs +++ b/base/Applications/Runtime/Full/System/SByte.cs @@ -3,17 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: SByte -** -** -** Purpose: -** -** Date: March 15, 1998 -** -===========================================================*/ -namespace System { +//============================================================ +// +// Class: SByte +// +// Purpose: +// +//=========================================================== +namespace System +{ using System.Globalization; using System; using System.Runtime.CompilerServices; @@ -78,7 +76,7 @@ namespace System { //| public String ToString(String format) { - if (m_value<0 && format!=null && format.Length>0 && (format[0]=='X' || format[0]=='x')) { + if (m_value < 0 && format != null && format.Length > 0 && (format[0] =='X' || format[0] =='x')) { uint temp = (uint)(m_value & 0x000000FF); return Number.FormatUInt32(temp,format); } diff --git a/base/Kernel/System/SchedulerTime.cs b/base/Applications/Runtime/Full/System/SchedulerTime.cs similarity index 96% rename from base/Kernel/System/SchedulerTime.cs rename to base/Applications/Runtime/Full/System/SchedulerTime.cs index 6938cee..414ea98 100644 --- a/base/Kernel/System/SchedulerTime.cs +++ b/base/Applications/Runtime/Full/System/SchedulerTime.cs @@ -8,7 +8,8 @@ // Used to keep track of timeouts and deadline in thread scheduling // // ==--== -namespace System { +namespace System +{ using Microsoft.Singularity; @@ -24,8 +25,8 @@ namespace System { #endif // This value type represents an absolute time on the scheduler timeline, - // which is kernel time increasing monotonically and independently of - // the world time the machine thinks it is on. + // which is kernel time increasing monotonically and independently of + // the world time the machine thinks it is on. // The reason to separate world time and scheduler time is that world time can // change at any time due to users changing the time and date, or due to // adjustments for accuracy or daylight-savings. @@ -213,8 +214,7 @@ namespace System { { //TimeSpan.TimeToTicks is a family access function which does no error checking, so //we need to put some error checking out here. - if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >=0 && second < 60) - { + if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60) { return (TimeSpan.TimeToTicks(hour, minute, second)); } throw new ArgumentOutOfRangeException("ArgumentOutOfRange_BadHourMinuteSecond"); diff --git a/base/Kernel/System/Single.cs b/base/Applications/Runtime/Full/System/Single.cs similarity index 92% rename from base/Kernel/System/Single.cs rename to base/Applications/Runtime/Full/System/Single.cs index cb1f1e4..456bfb6 100644 --- a/base/Kernel/System/Single.cs +++ b/base/Applications/Runtime/Full/System/Single.cs @@ -3,17 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: Single -** -** -** Purpose: A wrapper class for the primitive type float. -** -** Date: August 3, 1998 -** -===========================================================*/ -namespace System { +//============================================================ +// +// Class: Single +// +// Purpose: A wrapper class for the primitive type float. +// +//=========================================================== +namespace System +{ using System.Globalization; using System; @@ -75,7 +73,7 @@ namespace System { //| + public sealed partial class String : IComparable, ICloneable, IEnumerable { + // + //Native Static Methods + // + + // Joins an array of strings together as one string with a separator between each original string. + // + //| + public static String Join(String separator, String[] value) { + if (value == null) { + throw new ArgumentNullException("value"); + } + return Join(separator, value, 0, value.Length); + } + + /// + // Joins an array of strings together as one string with a separator + // between each original string. + // + // @param separator the string used as a separator. + // @param value the array of strings to be joined. + // @param startIndex the position within the array to start using + // strings. + // @param count the number of strings to take from the array. + // + // @return a string consisting of the strings contained in value + // from startIndex to startIndex + count + // joined together to form a single string, with each of the original + // strings separated by separator. + // + //| + public static String Join(String separator, String[] value, + int startIndex, int count) + { + // See also Lightning\Src\VM\COMString.cpp::JoinArray + if (separator == null) { + separator = String.Empty; + } + if (value == null) { + throw new ArgumentNullException("value array is null"); + } + if (startIndex < 0) { + throw new ArgumentOutOfRangeException("startIndex is negative"); + } + if (count < 0) { + throw new ArgumentOutOfRangeException("count is negative"); + } + if (startIndex + count > value.Length) { + throw new ArgumentOutOfRangeException("startIndex+count>value.Length"); + } + // Special case the empty list of strings + if (count == 0) { + return String.Empty; + } + // Compute the length of the new string + int newLength = 0; + int limit = startIndex + count; + for (int i = startIndex; i < limit; i++) { + String s = value[i]; + if (s != null) { + newLength += s.m_stringLength; + } + } + newLength += (count - 1) * separator.m_stringLength; + // Create a new String + String result = FastAllocateString(newLength); + if (newLength == 0) { + return result; + } + // Fill in the string + int dstIndex = 0; + String firstString = value[startIndex]; + if (firstString != null) { + FillString(result, 0, firstString); + dstIndex = firstString.m_stringLength; + } + for (int i = startIndex + 1; i < limit; i++) { + FillString(result, dstIndex, separator); + dstIndex += separator.m_stringLength; + String elementString = value[i]; + if (elementString != null) { + FillString(result, dstIndex, elementString); + dstIndex += elementString.m_stringLength; + } + } + return result; + } + + private unsafe static bool CaseInsensitiveCompHelper(char * strAChars, + char * strBChars, + int aLength, + int bLength, + out int result) { + char charA; + char charB; + char *strAStart; + + strAStart = strAChars; + + result = 0; + + // setup the pointers so that we can always increment them. + // We never access these pointers at the negative offset. + strAChars--; + strBChars--; + + do { + strAChars++; strBChars++; + + charA = *strAChars; + charB = *strBChars; + + //Case-insensitive comparison on chars greater than 0x80 requires + //a locale-aware casing operation and we're not going there. + if (charA >= 0x80 || charB >= 0x80) { + // TODO: We should be able to fix this. + return false; + } + + // Do the right thing if they differ in case only. + // We depend on the fact that the uppercase and lowercase letters + // in the range which we care about (A-Z,a-z) differ only by the + // 0x20 bit. + // The check below takes the xor of the two characters and + // determines if this bit is only set on one of them. + // If they're different cases, we know that we need to execute + // only one of the conditions within block. + if (((charA^charB) & 0x20) != 0) { + if (charA >='A' && charA <='Z') { + charA |= (char)0x20; + } + else if (charB >='A' && charB <='Z') { + charB |= (char)0x20; + } + } + } while (charA == charB && charA != 0); + + // Return the (case-insensitive) difference between them. + if (charA != charB) { + result = (int)(charA-charB); + return true; + } + + // The length of b was unknown because it was just a pointer to a + // null-terminated string. + // If we get here, we know that both A and B are pointing at a null. + // However, A can have an embedded null. Check the number of + // characters that we've walked in A against the expected length. + if (bLength == -1) { + if ((strAChars - strAStart) != aLength) { + result = 1; + return true; + } + result=0; + return true; + } + + result = (aLength - bLength); + return true; + } + + internal unsafe static int nativeCompareOrdinal(String strA, String strB, + bool bIgnoreCase) + { + // See also Lightning\Src\VM\COMString.cpp::FCCompareOrdinal + int strALength = strA.Length; + int strBLength = strB.Length; + fixed(char * strAChars = &strA.m_firstChar) { + fixed(char * strBCharsStart = &strB.m_firstChar) { + + // Need copy because fixed vars are readonly + char * strBChars = strBCharsStart; + + // Handle the comparison where we wish to ignore case. + if (bIgnoreCase) { + int result; + if (CaseInsensitiveCompHelper(strAChars, strBChars, + strALength, strBLength, + out result)) { + return result; + } + else { + // This will happen if we have characters greater + // than 0x7F. + throw new ArgumentException(); + } + } + + // If the strings are the same length, compare exactly the + // right # of chars. If they are different, compare the + // shortest # + 1 (the '\0'). + int count = strALength; + if (count > strBLength) + count = strBLength; + + long diff = (byte*)strAChars - (byte*)strBChars; + + // Loop comparing a DWORD at a time. + while ((count -= 2) >= 0) { + if (((*(int*)((byte*)strBChars + diff)) + - *(int*)strBChars) != 0) { + char * ptr1 = (char*)((byte*)strBChars + diff); + char * ptr2 = strBChars; + if (*ptr1 != *ptr2) { + return ((int)*ptr1 - (int)*ptr2); + } + return ((int)*(ptr1+1) - (int)*(ptr2+1)); + } + + // differs from COMString.cpp because they have DWORD* + // and we use char* + strBChars += 2; + } + + // Handle an extra WORD. + int c; + if (count == -1) { + c = *((char*)((byte*)strBChars+diff)) - *strBChars; + if (c != 0) { + return c; + } + } + return strALength - strBLength; + } + } + } + + internal static int nativeCompareOrdinalEx(String strA, int indexA, + String strB, int indexB, + int count) + { + // See also Lightning\Src\VM\COMString.cpp::CompareOrdinalEx + throw new Exception("System.String.nativeCompareOrdinalEx not implemented in Bartok!"); + } + + // + // This is a helper method for the security team. They need to uppercase some strings (guaranteed to be less + // than 0x80) before security is fully initialized. Without security initialized, we can't grab resources (the nlp's) + // from the assembly. This provides a workaround for that problem and should NOT be used anywhere else. + // + internal static String SmallCharToUpper(String strA) + { + String newString = FastAllocateString(strA.Length); + nativeSmallCharToUpper(strA, newString); + return newString; + } + + private static void nativeSmallCharToUpper(String strIn, String strOut) + { + // See also Lightning\Src\VM\COMString.cpp::SmallCharToUpper + throw new Exception("System.String.nativeSmallCharToUpper not implemented in Bartok!"); + } + + // This is a helper method for the security team. They need to construct strings from a char[] + // within their homebrewed XML parser. They guarantee that the char[] they pass in isn't null and + // that the provided indices are valid so we just stuff real fast. + internal static String CreateFromCharArray(char[] array, int start, int count) + { + String newString = FastAllocateString(count); + FillStringArray(newString, 0, array, start, count); + return newString; + } + + // + // + // NATIVE INSTANCE METHODS + // + // + + // + // Search/Query methods + // + + // Determines whether two strings match. + //| + public override bool Equals(Object obj) { + if (obj is String) { + return this.Equals((String) obj); + } + else { + return false; + } + } + + // Determines whether two strings match. + //| + public unsafe bool Equals(String value) { + if (value == null) { + return false; + } + if (this.m_stringLength != value.m_stringLength) { + return false; + } + fixed (char *thisCharPtr = &this.m_firstChar) { + fixed (char *valueCharPtr = &value.m_firstChar) { + char *thisCursor = thisCharPtr; + char *valueCursor = valueCharPtr; + for (int i = this.m_stringLength; i > 0; i--) { + if (*thisCursor != *valueCursor) { + return false; + } + thisCursor++; + valueCursor++; + } + } + } + return true; + } + + // Determines whether two Strings match. + //| + public static bool Equals(String a, String b) { + if ((Object)a ==(Object)b) { + return true; + } + + if ((Object)a == null || (Object)b == null) { + return false; + } + + return a.Equals(b); + } + + //| + public static bool operator == (String a, String b) + { + return String.Equals(a, b); + } + + //| + public static bool operator != (String a, String b) + { + return !String.Equals(a, b); + } + + internal unsafe char InternalGetChar(int index) + { + // See also Lightning\Src\VM\COMString.cpp::GetCharAt + if ((uint) index >= (uint) this.m_stringLength) { + throw new IndexOutOfRangeException(); + } + fixed (char *charPtr = &this.m_firstChar) { + return charPtr[index]; + } + } + + // Gets the character at a specified position. + // + //| + public char this[int index] { + get { return InternalGetChar(index); } + } + + internal unsafe int InternalGetChars(char *output, int maxput) + { + fixed (char *charPtr = &this.m_firstChar) { + int i; + for (i = 0; i < m_stringLength && i < maxput; i++) { + output[i] = charPtr[i]; + } + return i; + } + } + + // Converts a substring of this string to an array of characters. Copies the + // characters of this string beginning at position startIndex and ending at + // startIndex + length - 1 to the character array buffer, beginning + // at bufferStartIndex. + // + //| + public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) + { + if (destination == null) + throw new ArgumentNullException("destination"); + if (count < 0) + throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_NegativeCount"); + if (sourceIndex < 0) + throw new ArgumentOutOfRangeException("sourceIndex", "ArgumentOutOfRange_Index"); + if (count > Length - sourceIndex) + throw new ArgumentOutOfRangeException("sourceIndex", "ArgumentOutOfRange_IndexCount"); + if (destinationIndex > destination.Length - count || destinationIndex < 0) + throw new ArgumentOutOfRangeException("destinationIndex", "ArgumentOutOfRange_IndexCount"); + InternalCopyTo(sourceIndex, destination, destinationIndex, count); + } + + internal unsafe void InternalCopyTo(int sourceIndex, char[] destination, + int destinationIndex, int count) { + // See also Lightning\Src\VM\COMString.cpp::GetPreallocatedCharArray + if (sourceIndex + count > this.m_stringLength) { + throw new ArgumentOutOfRangeException(); + } + if (count > 0) { + // Necessary test as &destination[0] is illegal for empty array + fixed (char *srcPtrFixed = &this.m_firstChar) { + fixed (char *dstPtrFixed = destination) { + char *srcPtr = srcPtrFixed + sourceIndex; + char *dstPtr = dstPtrFixed + destinationIndex; + Buffer.MoveMemory((byte *)dstPtr, (byte *)srcPtr, + count * sizeof(char)); + } + } + } + } + + internal unsafe void CopyToByteArray(int sourceIndex, byte[] destination, + int destinationIndex, int charCount) + { + // See also Lightning\Src\VM\COMString.cpp::InternalCopyToByteArray + // Called by System.Text.UnicodeEncoding.GetBytes() for little-endian Unicode. + + if (destination == null) + throw new ArgumentNullException("destination"); + if (charCount < 0) + throw new ArgumentOutOfRangeException("charCount"); + if (destinationIndex < 0) + throw new ArgumentOutOfRangeException("destinationIndex"); + if (sourceIndex < 0) + throw new ArgumentOutOfRangeException("sourceIndex"); + if (sourceIndex + charCount > this.m_stringLength) + throw new ArgumentOutOfRangeException("charCount"); + + int byteCount = charCount * 2; + if (destinationIndex + byteCount > destination.Length) + throw new ArgumentOutOfRangeException("destinationLength"); + + if (charCount > 0) { + fixed (byte* dest = &destination[destinationIndex]) + fixed (char* src_char0 = &this.m_firstChar) { + byte* src_bytes = (byte*)(&src_char0[sourceIndex]); + Buffer.MoveMemory(dest, src_bytes, byteCount); + } + } + } + + // Returns the entire string as an array of characters. + //| + public char[] ToCharArray() { + return ToCharArray(0,Length); + } + + // Returns a substring of this string as an array of characters. + // + //| + public char[] ToCharArray(int startIndex, int length) + { + // Range check everything. + if (startIndex < 0 || startIndex > Length || startIndex > Length - length) + throw new ArgumentOutOfRangeException("startIndex", "ArgumentOutOfRange_Index"); + if (length < 0) + throw new ArgumentOutOfRangeException("length", "ArgumentOutOfRange_Index"); + + char[] chars = new char[length]; + InternalCopyTo(startIndex, chars, 0, length); + return chars; + } + + // Gets a hash code for this string. If strings A and B are such that A.Equals(B), then + // they will return the same hash code. + //| + public unsafe override int GetHashCode() { + // See also Lightning\Src\VM\COMString.cpp::GetHashCode + fixed (char *charPtrFixed = &this.m_firstChar) { + char *charPtr = charPtrFixed; + if (charPtr[this.m_stringLength] != 0) { + throw new Exception("Bartok string is not null terminated"); + } + // See also Lightning\Src\UtilCode.h::HashString + uint hash = 5381; + char c = *charPtr; + while (c != 0) { + hash = ((hash << 5) + hash) ^ c; + charPtr++; + c = *charPtr; + } + return (int) hash; + } + } + + // Gets the length of this string + //| + public int Length { + [NoHeapAllocation] + get { return InternalLength(); } + } + + /// + internal int ArrayLength { + [NoHeapAllocation] + get { return (m_arrayLength); } + } + + // Used by StringBuilder + internal int Capacity { + [NoHeapAllocation] + get { return (m_arrayLength - 1); } + } + + // Creates an array of strings by splitting this string at each + // occurrence of a separator. The separator is searched for, and if found, + // the substring preceding the occurrence is stored as the first element in + // the array of strings. We then continue in this manner by searching + // the substring that follows the occurrence. On the other hand, if the separator + // is not found, the array of strings will contain this instance as its only element. + // If the separator is null + // whitespace (i.e., Character.IsWhitespace) is used as the separator. + // + //| + public String [] Split(params char [] separator) { + return Split(separator, Int32.MaxValue); + } + + //==============================MakeSeparatorList======================== + //Args: baseString -- the string to parse for the given list of + // separator chars. + // Separator -- A string containing all of the split characters. + // list -- a pointer to a caller-allocated array of ints for + // split char indices. + // listLength -- the number of slots allocated in list. + //Returns: A list of all of the places within baseString where instances + // of characters in Separator occur. + //Exceptions: None. + //N.B.: This just returns silently if the caller hasn't allocated + // enough space for the int list. + //======================================================================= + + // WCHAR * -> char * + // int * -> int[] + + // CHARARRAYREF --> char[] + // c->GetNumComponents --> c.Length + + // STRINGREF --> String + // s->GetStringLength --> s.Length + // s->GetBuffer --> fixed(&s.m_firstChar) + + // COMNlsInfo::nativeIsWhiteSpace --> Char.IsWhiteSpace + // ArrayContains(char,char* start,char* end) + // --> ArrayContains(char, char[] buf) + private unsafe static int MakeSeparatorList(String baseString, char[] Separator, int[] list, int listLength) { + // From Lightning\Src\VM\COMString.cpp::MakeSeparatorList + int i; + int foundCount=0; + fixed (char *thisChars = &baseString.m_firstChar) { + int thisLength = baseString.Length; + + if (Separator == null || Separator.Length == 0) { + //If they passed null or an empty string, + //look for whitespace. + for (i = 0; i < thisLength && foundCount < listLength; i++) { + // was nativeIsWhiteSpace() + if (Char.IsWhiteSpace(thisChars[i])) { + list[foundCount++]=i; + } + } + } + else { + //WCHAR *searchChars = (WCHAR *)Separator->GetDataPtr(); + + int searchLength = Separator.Length; + //If they passed in a string of chars, + //actually look for those chars. + for (i = 0; i < thisLength && foundCount < listLength; i++) { + if (ArrayContains(thisChars[i],Separator) >= 0) { + list[foundCount++]=i; + } + } + } + return foundCount; + } + } + + // Creates an array of strings by splitting this string at each + // occurrence of a separator. The separator is searched for, and if found, + // the substring preceding the occurrence is stored as the first element in + // the array of strings. We then continue in this manner by searching + // the substring that follows the occurrence. On the other hand, if the separator + // is not found, the array of strings will contain this instance as its only element. + // If the separator is the empty string (i.e., String.Empty), then + // whitespace (i.e., Character.IsWhitespace) is used as the separator. + // If there are more than count different strings, the last n-(count-1) + // elements are concatenated and added as the last String. + // + //| + //| + + // STRINGREF --> String + // PTRARRAYREF --> String[] + // p->SetAt(0, v) --> p[0] = v + // + // LPVOID --> gone + // CQuickBytes --> gone + + // AllocateObjectArray(x,g_pStringClass) --> new String[x] + // NewString(&ref, index, size) --> ref.Substring(index, size) + + public String[] Split(char[] separator, int count) { + // See also Lightning\Src\VM\COMString.cpp::Split + // This implementation based on COMString + + int numReplaces; + int numActualReplaces; + int[] sepList; + int currIndex=0; + int arrIndex=0; + //char *thisChars; + int thisLength; + int i; + String[] splitStrings; + String temp; + + if (count < 0) { + throw new ArgumentOutOfRangeException + ("count", "ArgumentOutOfRange_Negative"); + } + + //Allocate space and fill an array of ints with a list of everyplace + //within our String that a separator character occurs. + sepList = new int[this.Length]; + numReplaces = MakeSeparatorList(this, separator, sepList, this.Length); + //Handle the special case of no replaces. + if (0 == numReplaces) { + splitStrings = new String[1]; + splitStrings[0] = this; + return splitStrings; + } + thisLength = Length; + + count--; + numActualReplaces = (numReplaces + public String Substring(int startIndex) { + return this.Substring (startIndex, Length-startIndex); + } + + // Returns a substring of this string. + // + //| + public String Substring(int startIndex, int length) { + + int thisLength = Length; + + //Bounds Checking. + if (startIndex < 0) { + throw new ArgumentOutOfRangeException("startIndex", "ArgumentOutOfRange_StartIndex"); + } + + if (length < 0) { + throw new ArgumentOutOfRangeException("length", "ArgumentOutOfRange_NegativeLength"); + } + + if (startIndex > thisLength - length) { + throw new ArgumentOutOfRangeException("length", "ArgumentOutOfRange_IndexLength"); + } + + String s = FastAllocateString(length); + FillSubstring(s, 0, this, startIndex, length); + + return s; + } + + internal String TrimHelper(char[] trimChars, int trimType) + { + // See also Lighting\Src\VM\COMString.cpp::TrimHelper + int stringLength = this.m_stringLength; + int iLeft = 0; + int iRight = stringLength - 1; + this.TrimHelper(trimChars, trimType, ref iLeft, ref iRight); + int newLength = iRight - iLeft + 1; + if (newLength == stringLength) { + return this; + } + else if (newLength == 0) { + return String.Empty; + } + else { + String result = FastAllocateString(newLength); + FillSubstring(result, 0, this, iLeft, newLength); + return result; + } + } + + private unsafe void TrimHelper(char[] trimChars, int trimType, + ref int iLeft, ref int iRight) + { + fixed (char *charPtr = &this.m_firstChar) { + if (trimType == String.TrimHead || + trimType == String.TrimBoth) { + while (iLeft <= iRight && + ArrayContains(charPtr[iLeft], trimChars) >= 0) { + iLeft++; + } + } + if (trimType == String.TrimTail || + trimType == String.TrimBoth) { + while (iRight >= iLeft && + ArrayContains(charPtr[iRight], trimChars) >= 0) { + iRight--; + } + } + } + } + + private static int ArrayContains(char c, char[] charArray) { + int limit = charArray.Length; + for (int i = 0; i < limit; i++) { + if (charArray[i] == c) { + return i; + } + } + return -1; + } + + /// + // Creates a new string from the characters in a subarray. The new + // string will be created from the characters in value between + // startIndex and startIndex + length - 1. + // + // @param value an array of characters. + // @param startIndex the index at which the subarray begins. + // @param length the length of the subarray. + // + // @exception ArgumentException if value is null. + // @exception ArgumentException if startIndex or + // startIndex+length-1 are not valid indices of + // value. + // + //| + + public static String StringCTOR(char[] value, int startIndex, + int length) { + // See also Lightning\Src\VM\COMString.cpp::StringInitCharArray + if (value == null) { + throw new ArgumentNullException("array value is null"); + } + if (startIndex < 0) { + throw new ArgumentOutOfRangeException("startIndex is negative"); + } + if (length < 0) { + throw new ArgumentOutOfRangeException("length is negative"); + } + if (length == 0) { + return String.Empty; + } + else { + String result = FastAllocateString(length); + FillStringArray(result, 0, value, startIndex, length); + return result; + } + } + + /// + // Creates a new string from the characters in a subarray. The new + // string will be created from the characters in value. + // + // @param value an array of characters. + // + // @exception ArgumentException if value is null. + // + //| + public static String StringCTOR(char[] value) { + // See also Lightning\Src\VM\COMString.cpp::StringInitChars + if (value == null) { + return String.Empty; + } + int length = value.Length; + if (length == 0) { + return String.Empty; + } + String result = FastAllocateString(length); + FillStringArray(result, 0, value, 0, length); + return result; + } + + internal static String NewString(String curr,int start,int copyLen, + int capacity) { + String result = FastAllocateString(capacity); + FillSubstring(result, 0, curr, start, copyLen); + return result; + } + + //| + public static String StringCTOR(char c, int count) { + // See also Lightning\Src\VM\COMString.cpp::StringInitCharCount + if (count < 0) { + throw new ArgumentOutOfRangeException("count is negative"); + } + if (count == 0) { + return String.Empty; + } + else { + String result = FastAllocateString(count); + FillStringChar(result, 0, c, count); + return result; + } + } + + // Removes a string of characters from the ends of this string. + //| + public String Trim(params char[] trimChars) { + if (null == trimChars || trimChars.Length == 0) { + trimChars=CharacterInfo.WhitespaceChars; + } + return TrimHelper(trimChars,TrimBoth); + } + + // Removes a string of characters from the beginning of this string. + //| + public String TrimStart(params char[] trimChars) { + if (null == trimChars || trimChars.Length == 0) { + trimChars=CharacterInfo.WhitespaceChars; + } + return TrimHelper(trimChars,TrimHead); + } + + + // Removes a string of characters from the end of this string. + //| + public String TrimEnd(params char[] trimChars) { + if (null == trimChars || trimChars.Length == 0) { + trimChars=CharacterInfo.WhitespaceChars; + } + return TrimHelper(trimChars,TrimTail); + } + + unsafe static private String CreateString(sbyte *value, int startIndex, int length, Encoding enc) { + if (enc == null) + return new String(value, startIndex, length); // default to ANSI + if (length < 0) + throw new ArgumentOutOfRangeException("length","ArgumentOutOfRange_NeedNonNegNum"); + if (startIndex < 0) { + throw new ArgumentOutOfRangeException("startIndex","ArgumentOutOfRange_StartIndex"); + } + if ((value + startIndex) < value) { + // overflow check + throw new ArgumentOutOfRangeException("startIndex","ArgumentOutOfRange_PartialWCHAR"); + } + byte [] b = new byte[length]; + if (length > 0) { + fixed(byte* pDest = b) { + Buffer.MoveMemory(pDest, ((byte*)value)+startIndex, length); + } + } + return enc.GetString(b); + } + + // For ASCIIEncoding::GetString() + unsafe static internal String CreateStringFromASCII(byte[] bytes, int startIndex, int length) { + Debug.Assert(bytes != null, "need a byte[]."); + Debug.Assert(startIndex >= 0 && (startIndex < bytes.Length || bytes.Length == 0), "startIndex >= 0 && startIndex < bytes.Length"); + Debug.Assert(length >= 0 && length <= bytes.Length - startIndex, "length >= 0 && length <= bytes.Length - startIndex"); + if (length == 0) + return String.Empty; + String s = FastAllocateString(length); + fixed(char* pChars = &s.m_firstChar) { + for (int i = 0; i < length; i++) + pChars[i] = (char) (bytes[i+startIndex] & 0x7f); + } + return s; + } + + // For Latin1Encoding::GetString() + unsafe static internal String CreateStringFromLatin1(byte[] bytes, int startIndex, int length) { + Debug.Assert(bytes != null, "need a byte[]."); + Debug.Assert(startIndex >= 0 && (startIndex < bytes.Length || bytes.Length == 0), "startIndex >= 0 && startIndex < bytes.Length"); + Debug.Assert(length >= 0 && length <= bytes.Length - startIndex, "length >= 0 && length <= bytes.Length - startIndex"); + if (length == 0) + return String.Empty; + String s = FastAllocateString(length); + fixed(char* pChars = &s.m_firstChar) { + for (int i = 0; i < length; i++) + pChars[i] = (char) (bytes[i+startIndex]); + } + return s; + } + + private static String FastAllocateString(int stringLength) { + return GC.AllocateString(stringLength); + } + + [Inline] + internal void InitializeStringLength(int stringLength) { + this.m_arrayLength = stringLength+1; + this.m_stringLength = stringLength; + } + + private unsafe static void FillString(String dest, int destPos, + String src) { + fixed (char *srcPtr = &src.m_firstChar) { + FillStringCharPtr(dest, destPos, srcPtr, src.m_stringLength); + } + } + + private unsafe static void FillStringChecked(String dest, int destPos, + String src) { + if (!(src.Length <= dest.ArrayLength - destPos)) { + throw new IndexOutOfRangeException(); // REVIEW: param? + } + fixed (char *srcPtr = &src.m_firstChar) { + FillStringCharPtr(dest, destPos, srcPtr, src.m_stringLength); + } + } + + private unsafe static void FillStringEx(String dest, int destPos, + String src,int srcLength) { + // ASSERT(srcLength <= dest.ArrayLength - destPos); + fixed (char *srcPtr = &src.m_firstChar) { + FillStringCharPtr(dest, destPos, srcPtr, srcLength); + } + } + + private unsafe static void FillStringChar(String dest, int destPos, + char c, int count) { + fixed (char *destPtr = &dest.m_firstChar) { + char *charPtr = destPtr + destPos; + // Set the odd leader char if necessary + if (destPos % 2 == 1) { + *charPtr = c; + charPtr++; + count--; + } + // Set the buffer 2 chars at a time + int c2 = (c << 16) | c; + int *intPtr = (int *) charPtr; + count--; // Prevent overruns from odd lengths + while (count > 0) { + *intPtr = c2; + intPtr++; + count -= 2; + } + // Set the odd trailer char if necessary + if (count == 0) { + *((char *) intPtr) = c; + } + } + } + + private unsafe static void FillStringCharPtr(String dest, int destPos, + char *srcPtr, int count) { + fixed (char *charPtr = &dest.m_firstChar) { + char *destPtr = charPtr + destPos; + Buffer.MoveMemory((byte *)destPtr, (byte *)srcPtr, count * sizeof(char)); + } + } + + private unsafe static void FillStringBytePtr(String dest, int destPos, + byte *srcPtr, int count) { + fixed (char *charPtr = &dest.m_firstChar) { + char *destPtr = charPtr + destPos; + for (int i = 0; i < count; i++) { + *destPtr++ = (char)*srcPtr++; + } + } + } + + private unsafe static void FillStringArray(String dest, int stringStart, + char[] array, int charStart, + int count) { + fixed (char *destPtr = &array[charStart]) { + FillStringCharPtr(dest, stringStart, destPtr, count); + } + } + + private unsafe static void FillSubstring(String dest, int destPos, + String src, int startPos, + int count) { + fixed (char *srcPtr = &src.m_firstChar) { + FillStringCharPtr(dest, destPos, srcPtr + startPos, count); + } + } + + // + // + // INSTANCE METHODS + // + // + + // Provides a culture-correct string comparison. StrA is compared to StrB + // to determine whether it is lexicographically less, equal, or greater, and then returns + // either a negative integer, 0, or a positive integer; respectively. + // + //| + public static int Compare(String strA, String strB) { + return CompareInfo.Compare(strA, strB, CompareOptions.None); + } + + // Provides a culture-correct string comparison. strA is compared to strB + // to determine whether it is lexicographically less, equal, or greater, and then a + // negative integer, 0, or a positive integer is returned; respectively. + // The case-sensitive option is set by ignoreCase + // + //| + public static int Compare(String strA, String strB, bool ignoreCase) { + if (ignoreCase) { + return CompareInfo.Compare(strA, strB, CompareOptions.IgnoreCase); + } + return CompareInfo.Compare(strA, strB, CompareOptions.None); + } + + // [Bartok]: + public static int Compare(String strA, String strB, bool ignoreCase, + CultureInfo info) + { + return Compare(strA, strB, ignoreCase); + } + + // Determines whether two string regions match. The substring of strA beginning + // at indexA of length count is compared with the substring of strB + // beginning at indexB of the same length. + // + //| + public static int Compare(String strA, int indexA, String strB, int indexB, int length) { + int lengthA = length; + int lengthB = length; + + if (strA != null) { + if (strA.Length - indexA < lengthA) { + lengthA = (strA.Length - indexA); + } + } + + if (strB != null) { + if (strB.Length - indexB < lengthB) { + lengthB = (strB.Length - indexB); + } + } + return CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.None); + } + + + // Determines whether two string regions match. The substring of strA beginning + // at indexA of length count is compared with the substring of strB + // beginning at indexB of the same length. Case sensitivity is determined by the ignoreCase boolean. + // + //| + public static int Compare(String strA, int indexA, String strB, int indexB, int length, bool ignoreCase) { + int lengthA = length; + int lengthB = length; + + if (strA != null) { + if (strA.Length - indexA < lengthA) { + lengthA = (strA.Length - indexA); + } + } + + if (strB != null) { + if (strB.Length - indexB < lengthB) { + lengthB = (strB.Length - indexB); + } + } + + if (ignoreCase) { + return CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.IgnoreCase); + } + return CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.None); + } + + // Compares this object to another object, returning an integer that + // indicates the relationship. This method returns a value less than 0 if this is less than value, 0 + // if this is equal to value, or a value greater than 0 + // if this is greater than value. Strings are considered to be + // greater than all non-String objects. Note that this means sorted + // arrays would contain nulls, other objects, then Strings in that order. + // + //| + public int CompareTo(Object value) { + if (value == null) { + return 1; + } + + if (!(value is String)) { + throw new ArgumentException("Arg_MustBeString"); + } + + return String.Compare(this,(String)value); + } + + // Determines the sorting relation of StrB to the current instance. + // + //| + public int CompareTo(String strB) { + if (strB == null) { + return 1; + } + return CompareInfo.Compare(this, strB, 0); + } + + // Compares strA and strB using an ordinal (code-point) comparison. + // + //| + public static int CompareOrdinal(String strA, String strB) { + if (strA == null || strB == null) { + if ((Object)strA ==(Object)strB) { //they're both null; + return 0; + } + return (strA==null)? -1 : 1; //-1 if A is null, 1 if B is null. + } + + return nativeCompareOrdinal(strA, strB, false); + } + + // Compares strA and strB using an ordinal (code-point) comparison. + // + //| + public static int CompareOrdinal(String strA, String strB, bool ignoreCase) { + if (strA == null || strB == null) { + if ((Object)strA ==(Object)strB) { //they're both null; + return 0; + } + return (strA==null)? -1 : 1; //-1 if A is null, 1 if B is null. + } + + return nativeCompareOrdinal(strA, strB, ignoreCase); + } + + // Compares strA and strB using an ordinal (code-point) comparison. + // + //| + public static int CompareOrdinal(String strA, int indexA, String strB, int indexB, int length) { + if (strA == null || strB == null) { + if ((Object)strA ==(Object)strB) { //they're both null; + return 0; + } + + return (strA==null)? -1 : 1; //-1 if A is null, 1 if B is null. + } + + return nativeCompareOrdinalEx(strA, indexA, strB, indexB, length); + } + + + // Determines whether a specified string is a suffix of the the current instance. + // + // The case-sensitive and culture-sensitive option is set by options, + // and the default culture is used. + // + //| + public bool EndsWith(String value) { + if (null == value) { + throw new ArgumentNullException("value"); + } + int valueLen = value.Length; + int thisLen = this.Length; + if (valueLen > thisLen) { + return false; + } + return (0==Compare(this, thisLen-valueLen, value, 0, valueLen)); + } + + public bool EndsWith(char value) { + int thisLen = this.Length; + if (thisLen != 0) { + if (this[thisLen - 1] == value) + return true; + } + return false; + } + + + // Returns the index of the first occurrence of value in the current instance. + // The search starts at startIndex and runs thorough the next count characters. + // + //| + public int IndexOf(char value) { + return IndexOf(value, 0, this.Length); + } + + //| + public int IndexOf(char value, int startIndex) { + return IndexOf(value, startIndex, this.Length - startIndex); + } + + //| + public unsafe int IndexOf(char value, int startIndex, int count) { + // See also Lightning\Src\VM\COMString.cpp::IndexOfChar + if (startIndex < 0 || startIndex > this.m_stringLength) { + throw new ArgumentOutOfRangeException("startIndex out of range"); + } + if (count < 0 || startIndex + count > this.m_stringLength) { + throw new ArgumentOutOfRangeException("count out of range"); + } + fixed (char *charPtr = &this.m_firstChar) { + char *cursor = charPtr + startIndex; + for (int i = count; i > 0; i--) { + if (*cursor == value) { + return (startIndex + (count - i)); + } + cursor++; + } + } + return -1; + } + + // Returns the index of the first occurrence of any character in value in the current instance. + // The search starts at startIndex and runs to endIndex-1. [startIndex,endIndex). + // + + //| + public int IndexOfAny(char [] anyOf) { + return IndexOfAny(anyOf,0, this.Length); + } + + //| + public int IndexOfAny(char [] anyOf, int startIndex) { + return IndexOfAny(anyOf, startIndex, this.Length - startIndex); + } + + //| + public unsafe int IndexOfAny(char [] anyOf, int startIndex, int count) { + // See also Lightning\Src\VM\COMString.cpp::IndexOfCharArray + if (anyOf == null) { + throw new ArgumentNullException("anyOf array is null"); + } + if (startIndex < 0 || startIndex > this.m_stringLength) { + throw new ArgumentOutOfRangeException("startIndex out of range"); + } + if (count < 0 || startIndex + count > this.m_stringLength) { + throw new ArgumentOutOfRangeException("count out of range"); + } + fixed (char *charPtr = &this.m_firstChar) { + char *cursor = charPtr + startIndex; + for (int i = count; i > 0; i--) { + if (ArrayContains(*cursor, anyOf) >= 0) { + return (startIndex + (count - i)); + } + cursor++; + } + } + return -1; + } + + // Determines the position within this string of the first occurrence of the specified + // string, according to the specified search criteria. The search begins at + // the first character of this string, it is case-sensitive and culture-sensitive, + // and the default culture is used. + // + //| + public int IndexOf(String value) { + return CompareInfo.IndexOf(this,value); + } + + // Determines the position within this string of the first occurrence of the specified + // string, according to the specified search criteria. The search begins at + // startIndex, it is case-sensitive and culture-sensitve, and the default culture is used. + // + //| + public int IndexOf(String value, int startIndex){ + return CompareInfo.IndexOf(this,value,startIndex); + } + + // Determines the position within this string of the first occurrence of the specified + // string, according to the specified search criteria. The search begins at + // startIndex, ends at endIndex and the default culture is used. + // + //| + public int IndexOf(String value, int startIndex, int count){ + if (startIndex + count > this.Length) { + throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_Index"); + } + return CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.None); + } + + + // Returns the index of the last occurrence of value in the current instance. + // The search starts at startIndex and runs to endIndex. [startIndex,endIndex]. + // The character at position startIndex is included in the search. startIndex is the larger + // index within the string. + // + //| + public int LastIndexOf(char value) { + return LastIndexOf(value, this.Length-1, this.Length); + } + + //| + public int LastIndexOf(char value, int startIndex){ + return LastIndexOf(value,startIndex,startIndex + 1); + } + + //| + public unsafe int LastIndexOf(char value, int startIndex, int count) { + // See also Lightning\Src\VM\COMString.cpp::LastIndexOfChar + if (this.m_stringLength == 0) { + return -1; + } + if (startIndex < 0 || startIndex >= this.m_stringLength) { + throw new ArgumentOutOfRangeException("startIndex out of range"); + } + if (count < 0 || count - 1 > startIndex) { + throw new ArgumentOutOfRangeException("count out of range"); + } + fixed (char *charPtr = &this.m_firstChar) { + char *cursor = charPtr + startIndex; + for (int i = count; i > 0; i--) { + if (*cursor == value) { + return (startIndex - (count - i)); + } + cursor--; + } + } + return -1; + } + + // Returns the index of the last occurrence of any character in value in the current instance. + // The search starts at startIndex and runs to endIndex. [startIndex,endIndex]. + // The character at position startIndex is included in the search. startIndex is the larger + // index within the string. + // + + //| + public int LastIndexOfAny(char [] anyOf) { + return LastIndexOfAny(anyOf,this.Length-1,this.Length); + } + + //| + public int LastIndexOfAny(char [] anyOf, int startIndex) { + return LastIndexOfAny(anyOf,startIndex,startIndex + 1); + } + + //| + public unsafe int LastIndexOfAny(char [] anyOf, int startIndex, + int count) { + // See also Lightning\Src\VM\COMString.cpp::LastIndexOfCharArray + if (anyOf == null) { + throw new ArgumentNullException("anyOf array is null"); + } + if (this.m_stringLength == 0) { + return -1; + } + if (startIndex < 0 || startIndex >= this.m_stringLength) { + throw new ArgumentOutOfRangeException("startIndex out of range"); + } + if (count < 0 || count - 1 > startIndex) { + throw new ArgumentOutOfRangeException("count out of range"); + } + fixed (char *charPtr = &this.m_firstChar) { + char *cursor = charPtr + startIndex; + for (int i = count; i > 0; i--) { + if (ArrayContains(*cursor, anyOf) >= 0) { + return (startIndex - (count - 1)); + } + cursor--; + } + } + return -1; + } + + // Returns the index of the last occurrence of any character in value in the current instance. + // The search starts at startIndex and runs to endIndex. [startIndex,endIndex]. + // The character at position startIndex is included in the search. startIndex is the larger + // index within the string. + // + //| + public int LastIndexOf(String value) { + return LastIndexOf(value, this.Length-1,this.Length); + } + + //| + public int LastIndexOf(String value, int startIndex) { + return LastIndexOf(value, startIndex, startIndex + 1); + } + + //| + public int LastIndexOf(String value, int startIndex, int count) { + if (count < 0) { + throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_Count"); + } + return CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.None); + } + + + // + // + //| + public String PadLeft(int totalWidth) { + return PadHelper(totalWidth, ' ', false); + } + + //| + public String PadLeft(int totalWidth, char paddingChar) { + return PadHelper(totalWidth, paddingChar, false); + } + + //| + public String PadRight(int totalWidth) { + return PadHelper(totalWidth, ' ', true); + } + + //| + public String PadRight(int totalWidth, char paddingChar) { + return PadHelper(totalWidth, paddingChar, true); + } + + private String PadHelper(int totalWidth, char paddingChar, + bool isRightPadded) { + // See also Lightning\Src\VM\COMString.cpp::PadHelper + if (totalWidth < 0) { + throw new ArgumentOutOfRangeException("totalWidth is negative"); + } + if (totalWidth <= this.m_stringLength) { + return this; + } + int padCount = totalWidth - this.m_stringLength; + String result = FastAllocateString(totalWidth); + if (isRightPadded) { + FillString(result, 0, this); + FillStringChar(result, this.m_stringLength, + paddingChar, padCount); + } + else { + + FillStringChar(result, 0, paddingChar, padCount); + FillString(result, padCount, this); + } + return result; + } + + // Determines whether a specified string is a prefix of the current instance + // + //| + public bool StartsWith(String value) { + if (null == value) { + throw new ArgumentNullException("value"); + } + if (this.Length < value.Length) { + return false; + } + return (0==Compare(this,0, value,0, value.Length)); + } + + // Creates a copy of this string in lower case. + //| + public String ToLower() { + return TextInfo.ToLower(this); + } + + // Creates a copy of this string in upper case. + //| + public String ToUpper() { + return TextInfo.ToUpper(this); + } + + // Returns this string. + //| + public override String ToString() { + return this; + } + + // Method required for the ICloneable interface. + // There's no point in cloning a string since they're immutable, so we simply return this. + //| + public Object Clone() { + return this; + } + + + // Trims the whitespace from both ends of the string. Whitespace is defined by + // CharacterInfo.WhitespaceChars. + // + //| + public String Trim() { + return this.Trim(CharacterInfo.WhitespaceChars); + } + + // + // + //| + public String Insert(int startIndex, String value) { + // See also Lightning\Src\VM\COMString.cpp::Insert + if (startIndex < 0 || startIndex > this.m_stringLength) { + throw new ArgumentOutOfRangeException("startIndex out of range"); + } + if (value == null) { + throw new ArgumentNullException("value string is null"); + } + int newLength = this.m_stringLength + value.m_stringLength; + String result = FastAllocateString(newLength); + FillSubstring(result, 0, this, 0, startIndex); + FillSubstring(result, startIndex, value, 0, value.m_stringLength); + FillSubstring(result, startIndex + value.m_stringLength, + this, startIndex, this.m_stringLength - startIndex); + return result; + } + + internal unsafe void InsertHoleInString(int startIndex, int holeSize) { + int tail = this.m_stringLength - startIndex; + this.m_stringLength += holeSize; + if (tail > 0) { + fixed (char *charPtr = &this.m_firstChar) { + char *srcPtr = charPtr + startIndex; + char *dstPtr = charPtr + holeSize; + Buffer.MoveMemory((byte *)dstPtr, (byte *)srcPtr, + tail * sizeof(char)); + } + } + } + + internal unsafe void InsertStringOverwrite(String value, int startIndex, int valueLength) { + fixed (char *charPtr = &this.m_firstChar) { + char *dstPtr = charPtr + startIndex; + fixed (char *srcPtr = &value.m_firstChar) { + Buffer.MoveMemory((byte *)dstPtr, (byte *)srcPtr, + valueLength * sizeof(char)); + } + } + } + + // Replaces all instances of oldChar with newChar. + // + //| + public String Replace(char oldChar, char newChar) { + // See also Lightning\Src\VM\COMString.cpp::Replace + String result = FastAllocateString(this.m_stringLength); + this.Replace(oldChar, newChar, result); + return result; + } + + private unsafe void Replace(char oldChar, char newChar, String newString) { + fixed (char *srcPtr = &this.m_firstChar) { + fixed (char *destPtr = &newString.m_firstChar) { + char *srcCursor = srcPtr; + char *destCursor = destPtr; + for (int i = this.m_stringLength; i > 0; i--) { + char c = *srcCursor; + *destCursor = (c == oldChar) ? newChar : c; + srcCursor++; + destCursor++; + } + } + } + } + + // This method contains the same functionality as StringBuilder Replace. The only difference is that + // a new String has to be allocated since Strings are immutable + //| + public String Replace(String oldValue, String newValue) { + // See also Lightning\Src\VM\COMString.cpp::ReplaceString + if (oldValue == null) { + throw new ArgumentNullException("oldString is null"); + } + if (newValue == null) { + newValue = String.Empty; + } + if (oldValue.m_stringLength == 0) { + throw new ArgumentException("oldString is empty string"); + } + int matchIndex = LocalIndexOfString(oldValue, 0); + if (matchIndex < 0) { + return this; + } + // Compute a table of where to insert newValue + int replaceCount = 0; + int[] replacementTable = new int[(this.m_stringLength - matchIndex) / oldValue.m_stringLength + 1]; + do { + replacementTable[replaceCount] = matchIndex; + replaceCount++; + matchIndex = LocalIndexOfString(oldValue, matchIndex+oldValue.m_stringLength); + } while (matchIndex >= 0); + int newLength = this.m_stringLength + replaceCount * (newValue.m_stringLength - oldValue.m_stringLength); + String result = FastAllocateString(newLength); + int srcIndex = 0; + int destIndex = 0; + for (int replIndex = 0; replIndex < replaceCount; replIndex++) { + int count = replacementTable[replIndex] - srcIndex; + FillSubstring(result, destIndex, this, srcIndex, count); + srcIndex += count + oldValue.m_stringLength; + destIndex += count; + FillString(result, destIndex, newValue); + destIndex += newValue.m_stringLength; + } + FillSubstring(result, destIndex, this, srcIndex, + this.m_stringLength - srcIndex); + return result; + } + + private unsafe int LocalIndexOfString(String pattern, int startPos) { + fixed (char *stringPtr = &this.m_firstChar) { + fixed (char *patternPtr = &pattern.m_firstChar) { + char *stringCharPtr = stringPtr + startPos; + int count = this.m_stringLength - pattern.m_stringLength - + startPos + 1; + for (int index = 0; index < count; index++) { + char *stringCursor = stringCharPtr + index; + char *patternCursor = patternPtr; + int i = pattern.m_stringLength; + while (i > 0 && *stringCursor == *patternCursor) { + i--; + stringCursor++; + patternCursor++; + } + if (i == 0) { + return (startPos + index); + } + } + } + } + return -1; + } + + //| + public String Remove(int startIndex, int count) { + if (count < 0) { + throw new ArgumentOutOfRangeException("count is negative"); + } + if (startIndex < 0) { + throw new ArgumentOutOfRangeException("startIndex is negative"); + } + if (startIndex + count > this.m_stringLength) { + throw new ArgumentOutOfRangeException("startIndex+count>Length"); + } + int newLength = this.m_stringLength - count; + String result = FastAllocateString(newLength); + FillSubstring(result, 0, this, 0, startIndex); + FillSubstring(result, startIndex, this, startIndex + count, + newLength - startIndex); + return result; + } + + internal unsafe void RemoveRange(int startIndex, int count) { + int tail = this.m_stringLength - startIndex; + this.m_stringLength -= count; + if (tail > 0) { + fixed (char *charPtr = &this.m_firstChar) { + char *dstPtr = charPtr + startIndex; + char *srcPtr = dstPtr + count; + Buffer.MoveMemory((byte *)dstPtr, (byte *)srcPtr, + tail * sizeof(char)); + } + } + } + + //| + public static String Format(String format, Object arg0) { + return Format(format, new Object[] {arg0}); + } + + //| + public static String Format(String format, Object arg0, Object arg1) { + return Format(format, new Object[] {arg0, arg1}); + } + + //| + public static String Format(String format, Object arg0, Object arg1, Object arg2) { + return Format(format, new Object[] {arg0, arg1, arg2}); + } + + //| + public static String Format(String format, params Object[] args) { + if (format == null || args == null) + throw new ArgumentNullException((format==null)?"format":"args"); + StringBuilder sb = new StringBuilder(format.Length + args.Length * 8); + sb.AppendFormat(format,args); + return sb.ToString(); + } + + //| + public static String Copy (String str) { + if (str == null) { + throw new ArgumentNullException("str"); + } + + int length = str.Length; + + String result = FastAllocateString(length); + FillString(result, 0, str); + return result; + } + + // Used by StringBuilder to avoid data corruption + internal static String InternalCopy (String str) { + int length = str.Length; + String result = FastAllocateString(length); + FillStringEx(result, 0, str, length); // The underlying's String can changed length is StringBuilder + return result; + } + + //| + public static String Concat(Object arg0) { + if (arg0 == null) { + return String.Empty; + } + return arg0.ToString(); + } + + //| + public static String Concat(Object arg0, Object arg1) { + if (arg0 == null) { + arg0 = String.Empty; + } + + if (arg1 == null) { + arg1 = String.Empty; + } + return Concat(arg0.ToString(), arg1.ToString()); + } + + //| + public static String Concat(Object arg0, Object arg1, Object arg2) { + if (arg0 == null) { + arg0 = String.Empty; + } + + if (arg1 == null) { + arg1 = String.Empty; + } + + if (arg2 == null) { + arg2 = String.Empty; + } + + return Concat(arg0.ToString(), arg1.ToString(), arg2.ToString()); + } + + //| + public static String Concat(params Object[] args) { + if (args == null) { + throw new ArgumentNullException("args"); + } + + String[] sArgs = new String[args.Length]; + int totalLength=0; + + for (int i = 0; i < args.Length; i++) { + object value = args[i]; + sArgs[i] = ((value==null)?(String.Empty):(value.ToString())); + totalLength += sArgs[i].Length; + // check for overflow + if (totalLength < 0) { + throw new OutOfMemoryException(); + } + } + return ConcatArray(sArgs, totalLength); + } + + + //| + public static String Concat(String str0, String str1) { + if (str0 == null) { + if (str1 == null) { + return String.Empty; + } + return str1; + } + + if (str1 == null) { + return str0; + } + + int str0Length = str0.Length; + + String result = FastAllocateString(str0Length + str1.Length); + + FillStringChecked(result, 0, str0); + FillStringChecked(result, str0Length, str1); + + return result; + } + + //| + public static String Concat(String str0, String str1, String str2) { + if (str0 == null && str1 == null && str2 == null) { + return String.Empty; + } + + if (str0 == null) { + str0 = String.Empty; + } + + if (str1 == null) { + str1 = String.Empty; + } + + if (str2 == null) { + str2 = String.Empty; + } + + int totalLength = str0.Length + str1.Length + str2.Length; + + String result = FastAllocateString(totalLength); + FillStringChecked(result, 0, str0); + FillStringChecked(result, str0.Length, str1); + FillStringChecked(result, str0.Length + str1.Length, str2); + + return result; + } + + //| + public static String Concat(String str0, String str1, String str2, String str3) { + if (str0 == null && str1 == null && str2 == null && str3 == null) { + return String.Empty; + } + + if (str0 == null) { + str0 = String.Empty; + } + + if (str1 == null) { + str1 = String.Empty; + } + + if (str2 == null) { + str2 = String.Empty; + } + + if (str3 == null) { + str3 = String.Empty; + } + + int totalLength = str0.Length + str1.Length + str2.Length + str3.Length; + + String result = FastAllocateString(totalLength); + FillStringChecked(result, 0, str0); + FillStringChecked(result, str0.Length, str1); + FillStringChecked(result, str0.Length + str1.Length, str2); + FillStringChecked(result, str0.Length + str1.Length + str2.Length, str3); + + return result; + } + + private static String ConcatArray(String[] values, int totalLength) { + String result = FastAllocateString(totalLength); + int currPos=0; + + for (int i = 0; i < values.Length; i++) { + Debug.Assert((currPos + values[i].Length <= totalLength), + "[String.ConcatArray](currPos + values[i].Length <= totalLength)"); + + FillStringChecked(result, currPos, values[i]); + currPos+=values[i].Length; + } + + return result; + } + + // poor man's varargs for String.Concat: 8 strings + public static String Concat(object obj0, object obj1, object obj2, + object obj3, string obj4, string obj5, + string obj6, string obj7) { + String[] strs = new String[8]; + strs[0] = obj0.ToString(); + strs[1] = obj1.ToString(); + strs[2] = obj2.ToString(); + strs[3] = obj3.ToString(); + strs[4] = obj4.ToString(); + strs[5] = obj5.ToString(); + strs[6] = obj6.ToString(); + strs[7] = obj7.ToString(); + return Join(null,strs,0,8); + } + + private static String[] CopyArrayOnNull(String[] values, out int totalLength) { + totalLength = 0; + + String[] outValues = new String[values.Length]; + + for (int i = 0; i < values.Length; i++) { + outValues[i] = ((values[i]==null)?String.Empty:values[i]); + totalLength += outValues[i].Length; + } + return outValues; + } + + //| + public static String Concat(params String[] values) { + int totalLength=0; + + if (values == null) { + throw new ArgumentNullException("values"); + } + + // Always make a copy to prevent changing the array on another thread. + String[] internalValues = new String[values.Length]; + + for (int i = 0; i < values.Length; i++) { + string value = values[i]; + internalValues[i] = ((value==null)?(String.Empty):(value)); + totalLength += internalValues[i].Length; + // check for overflow + if (totalLength < 0) { + throw new OutOfMemoryException(); + } + } + + return ConcatArray(internalValues, totalLength); + } + + //| + public static String Intern(String str) { + str.CheckHighChars(); + if (str == null) { + throw new ArgumentNullException("str"); + } + if (internedStringVector == null) { + InitializeInternedTable(); + } + int code = str.GetHashCode(); + lock (internedStringVector) { + int[] codeTable= internedStringHashCodes; + String[] stringTable = internedStringVector; + int tableSize = codeTable.Length; + int indexMask = tableSize - 1; + int hx = code & indexMask; + //Scan up from our initial hash index guess + while (true) { // It is guaranteed there are holes in table + int tableCode = codeTable[hx]; + if (tableCode == code) { + String s = stringTable[hx]; + if (str.Equals(s)) { + return s; + } + } + else if (tableCode == 0 && stringTable[hx] == null) { + // We need to insert the string here! + int stringLength = str.Length; + if (str.m_arrayLength > stringLength + 1) { + str = NewString(str, 0, stringLength, stringLength); + } + internedStringCount++; + stringTable[hx] = str; // Set string entry first + codeTable[hx] = code; // then set code! + if (internedStringCount >= internedStringBound) { + RehashInternedStrings(); + } + return str; + } + hx++; + hx &= indexMask; + } + } + } + + //| + public static String IsInterned(String str) { + if (str == null) { + throw new ArgumentNullException("str"); + } + if (internedStringVector == null) { + InitializeInternedTable(); + } + int code = str.GetHashCode(); + int[] codeTable; + String[] stringTable; + do { + codeTable = internedStringHashCodes; + stringTable = internedStringVector; + } while (codeTable.Length != stringTable.Length); + int tableSize = codeTable.Length; + int indexMask = tableSize - 1; + int hx = code & indexMask; + // Scan up from our initial hash index guess + while (true) { // It is guaranteed there are holes in table + int tableCode = codeTable[hx]; + if (tableCode == code) { + String s = stringTable[hx]; + if (str.Equals(s)) { + return s; + } + } + else if (tableCode == 0 && stringTable[hx] == null) { + return null; + } + hx++; + hx &= indexMask; + } + } + + private static void InitializeInternedTable() { + lock (typeof(String)) { + if (internedStringVector != null) { + return; + } + int tableSize = 128; + int constantCount = internedStringConstants.Length; + while (tableSize <= constantCount) { + tableSize <<= 1; + } + int indexMask = tableSize - 1; + String[] stringTable = new String[tableSize]; + int[] codeTable = new int[tableSize]; + for (int i = 0; i < constantCount; i++) { + String s = internedStringConstants[i]; + int code = s.GetHashCode(); + int hx = code & indexMask; + while (true) { + int tableCode = codeTable[hx]; + VTable.Assert(tableCode != code || + !s.Equals(stringTable[hx]), + "Duplicate interned strings!"); + if (tableCode == 0 && stringTable[hx] == null) { + internedStringCount++; + stringTable[hx] = s; + codeTable[hx] = code; + break; + } + hx++; + hx &= indexMask; + } + } + internedStringBound = tableSize >> 1; + internedStringHashCodes = codeTable; + internedStringVector = stringTable; + } + } + + private static void RehashInternedStrings() { + int[] oldCodeTable = internedStringHashCodes; + String[] oldStringTable = internedStringVector; + int oldSize = oldCodeTable.Length; + int oldIndexMask = oldSize - 1; + int newSize = oldSize << 1; + int newIndexMask = newSize -1; + int[] newCodeTable = new int[newSize]; + String[] newStringTable = new String[newSize]; + for (int i = 0; i < oldSize; i++) { + String s = oldStringTable[i]; + if (s != null) { + int code = oldCodeTable[i]; + int hx = code & newIndexMask; + while (true) { + if (newCodeTable[hx] == 0 && + newStringTable[hx] == null) { + newStringTable[hx] = s; + newCodeTable[hx] = code; + break; + } + hx++; + hx &= newIndexMask; + } + } + } + internedStringBound = newSize >> 1; + internedStringHashCodes = newCodeTable; + internedStringVector = newStringTable; + } + + private static int internedStringCount; + private static int internedStringBound; + private static int[] internedStringHashCodes; + private static String[] internedStringVector; + private static String[] internedStringConstants; + + private static readonly bool [] HighCharTable = { + false, // 0x00, 0x0 + true, // 0x01, . + true, // 0x02, . + true, // 0x03, . + true, // 0x04, . + true, // 0x05, . + true, // 0x06, . + true, // 0x07, . + true, // 0x08, . + false, // 0x09, + false, // 0x0A, + false, // 0x0B, . + false, // 0x0C, . + false, // 0x0D, + true, // 0x0E, . + true, // 0x0F, . + true, // 0x10, . + true, // 0x11, . + true, // 0x12, . + true, // 0x13, . + true, // 0x14, . + true, // 0x15, . + true, // 0x16, . + true, // 0x17, . + true, // 0x18, . + true, // 0x19, . + true, // 0x1A, + true, // 0x1B, . + true, // 0x1C, . + true, // 0x1D, . + true, // 0x1E, . + true, // 0x1F, . + false, // 0x20, + false, // 0x21, ! + false, // 0x22, " + false, // 0x23, # + false, // 0x24, $ + false, // 0x25, % + false, // 0x26, & + true, // 0x27, ' + false, // 0x28, ( + false, // 0x29, ) + false, // 0x2A * + false, // 0x2B, + + false, // 0x2C, , + true, // 0x2D, - + false, // 0x2E, . + false, // 0x2F, / + false, // 0x30, 0 + false, // 0x31, 1 + false, // 0x32, 2 + false, // 0x33, 3 + false, // 0x34, 4 + false, // 0x35, 5 + false, // 0x36, 6 + false, // 0x37, 7 + false, // 0x38, 8 + false, // 0x39, 9 + false, // 0x3A, : + false, // 0x3B, ; + false, // 0x3C, < + false, // 0x3D, = + false, // 0x3E, > + false, // 0x3F, ? + false, // 0x40, @ + false, // 0x41, A + false, // 0x42, B + false, // 0x43, C + false, // 0x44, D + false, // 0x45, E + false, // 0x46, F + false, // 0x47, G + false, // 0x48, H + false, // 0x49, I + false, // 0x4A, J + false, // 0x4B, K + false, // 0x4C, L + false, // 0x4D, M + false, // 0x4E, N + false, // 0x4F, O + false, // 0x50, P + false, // 0x51, Q + false, // 0x52, R + false, // 0x53, S + false, // 0x54, T + false, // 0x55, U + false, // 0x56, V + false, // 0x57, W + false, // 0x58, X + false, // 0x59, Y + false, // 0x5A, Z + false, // 0x5B, [ + false, // 0x5C, \ + false, // 0x5D, ] + false, // 0x5E, ^ + false, // 0x5F, _ + false, // 0x60, ` + false, // 0x61, a + false, // 0x62, b + false, // 0x63, c + false, // 0x64, d + false, // 0x65, e + false, // 0x66, f + false, // 0x67, g + false, // 0x68, h + false, // 0x69, i + false, // 0x6A, j + false, // 0x6B, k + false, // 0x6C, l + false, // 0x6D, m + false, // 0x6E, n + false, // 0x6F, o + false, // 0x70, p + false, // 0x71, q + false, // 0x72, r + false, // 0x73, s + false, // 0x74, t + false, // 0x75, u + false, // 0x76, v + false, // 0x77, w + false, // 0x78, x + false, // 0x79, y + false, // 0x7A, z + false, // 0x7B, { + false, // 0x7C, | + false, // 0x7D, } + false, // 0x7E, ~ + true, // 0x7F,  + }; + + // + // IValue implementation + // + + //| + [NoHeapAllocation] + public override TypeCode GetTypeCode() { + return TypeCode.String; + } + + // Is this a string that can be compared quickly (that is it has only characters > 0x80 + // and not a - or ' + internal bool IsFastSort() { + return ((StringState & StringState.SpecialSort) != 0); + } + + internal bool IsSlowSort() { + return !IsFastSort(); + } + + internal bool IsFastOpsExceptSort() { + return ((StringState & StringState.SpecialSort) != 0 || + (StringState & StringState.FastOps) != 0); + } + + internal bool IsFastCasing() { + return ((StringState & StringState.SpecialSort) != 0 || + (StringState & StringState.FastOps) != 0); + } + + internal bool IsFastIndex() { + return ((StringState & StringState.SpecialSort) != 0 || + (StringState & StringState.FastOps) != 0); + } + + internal bool IsStringStateUndetermined() { + return (StringState == StringState.Undetermined); + } + + internal bool HasHighChars() { + return (StringState == StringState.HighChars); + } + + + /// + /// Get or set the string state for this string. + /// + internal unsafe StringState StringState { + [NoLoggingForUndo] + get { + StringState result = MultiUseWord.GetStringState(this); + return (StringState)result; + } + [NoLoggingForUndo] + set { + MultiUseWord.SetStringState(this, value); + } + } + + internal unsafe StringState CheckHighChars() { + char c; + StringState ss = StringState.FastOps; + int length = m_stringLength; + fixed(char *charPtr = &this.m_firstChar) { + for (int i = 0; i < length; i++) { + c = charPtr[i]; + if (c >= 0x80) { + StringState = StringState.HighChars; + return StringState; + } + else if (HighCharTable[(int)c]) { + ss = StringState.SpecialSort; + } + } + } + + StringState = ss; + return StringState; + } + + /// + unsafe internal void SetChar(int index, char value) + { +#if _DEBUG + Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); +#endif + + //Bounds check and then set the actual bit. + if ((UInt32)index >= (UInt32)Length) + throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_Index"); + + fixed (char *p = &this.m_firstChar) { + // Set the character. + p[index] = value; + } + } + +#if _DEBUG + // Only used in debug build. Insure that the HighChar state information for a string is not set as known + [MethodImpl(MethodImplOptions.InternalCall)] + private bool ValidModifiableString() { + throw new Exception("System.String.ValidModifiableString not implemented in Bartok!"); + } +#endif + + /// + unsafe internal void AppendInPlace(char value,int currentLength) + { + Debug.Assert(currentLength < m_arrayLength, "[String.AppendInPlace(char)currentLength < m_arrayLength"); +#if _DEBUG + Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); +#endif + + fixed (char *p = &this.m_firstChar) + { + // Append the character. + p[currentLength] = value; + p[++currentLength] = '\0'; + m_stringLength = currentLength; + } + } + + + /// + unsafe internal void AppendInPlace(char value, int repeatCount, int currentLength) + { + Debug.Assert(currentLength+repeatCount < m_arrayLength, "[String.AppendInPlace]currentLength+repeatCount < m_arrayLength"); +#if _DEBUG + Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); +#endif + + int newLength = currentLength + repeatCount; + + + fixed (char *p = &this.m_firstChar) + { + int i; + for (i = currentLength; i < newLength; i++) { + p[i] = value; + } + p[i] = '\0'; + } + this.m_stringLength = newLength; + } + + /// + internal unsafe void AppendInPlace(String value, int currentLength) { + Debug.Assert(value!=null, "[String.AppendInPlace]value!=null"); + Debug.Assert(value.Length + currentLength < this.m_arrayLength, "[String.AppendInPlace]Length is wrong."); +#if _DEBUG + Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); +#endif + + FillString(this, currentLength, value); + SetLength(currentLength + value.Length); + NullTerminate(); + } + + internal void AppendInPlace(String value, int startIndex, int count, int currentLength) { + Debug.Assert(value!=null, "[String.AppendInPlace]value!=null"); + Debug.Assert(count + currentLength < this.m_arrayLength, "[String.AppendInPlace]count + currentLength < this.m_arrayLength"); + Debug.Assert(count>=0, "[String.AppendInPlace]count>=0"); + Debug.Assert(startIndex>=0, "[String.AppendInPlace]startIndex>=0"); + Debug.Assert(startIndex <= (value.Length - count), "[String.AppendInPlace]startIndex <= (value.Length - count)"); +#if _DEBUG + Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); +#endif + + FillSubstring(this, currentLength, value, startIndex, count); + SetLength(currentLength + count); + NullTerminate(); + } + + internal unsafe void AppendInPlace(char *value, int count,int currentLength) { + Debug.Assert(value!=null, "[String.AppendInPlace]value!=null"); + Debug.Assert(count + currentLength < this.m_arrayLength, "[String.AppendInPlace]count + currentLength < this.m_arrayLength"); + Debug.Assert(count>=0, "[String.AppendInPlace]count>=0"); +#if _DEBUG + Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); +#endif + fixed(char *p = &this.m_firstChar) { + int i; + for (i = 0; i < count; i++) { + p[currentLength+i] = value[i]; + } + } + SetLength(currentLength + count); + NullTerminate(); + } + + + /// + internal unsafe void AppendInPlace(char[] value, int start, int count, int currentLength) { + Debug.Assert(value!=null, "[String.AppendInPlace]value!=null"); + Debug.Assert(count + currentLength < this.m_arrayLength, "[String.AppendInPlace]Length is wrong."); + Debug.Assert(value.Length-count>=start, "[String.AppendInPlace]value.Length-count>=start"); +#if _DEBUG + Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); +#endif + + FillStringArray(this, currentLength, value, start, count); + this.m_stringLength = (currentLength + count); + this.NullTerminate(); + } + + + /// + unsafe internal void ReplaceCharInPlace(char oldChar, char newChar, int startIndex, int count,int currentLength) { + Debug.Assert(startIndex>=0, "[String.ReplaceCharInPlace]startIndex>0"); + Debug.Assert(startIndex<=currentLength, "[String.ReplaceCharInPlace]startIndex>=Length"); + Debug.Assert((startIndex<=currentLength-count), "[String.ReplaceCharInPlace]count>0 && startIndex<=currentLength-count"); +#if _DEBUG + Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); +#endif + + int endIndex = startIndex+count; + + fixed (char *p = &this.m_firstChar) { + for (int i = startIndex; i < endIndex; i++) { + if (p[i] == oldChar) { + p[i]=newChar; + } + } + } + } + + + /// + internal static String GetStringForStringBuilder(String value, int capacity) { + Debug.Assert(value!=null, "[String.GetStringForStringBuilder]value!=null"); + Debug.Assert(capacity>=value.Length, "[String.GetStringForStringBuilder]capacity>=value.Length"); + + String newStr = FastAllocateString(capacity); + if (value.Length == 0) { + newStr.m_stringLength=0; + newStr.m_firstChar='\0'; + return newStr; + } + FillString(newStr, 0, value); + newStr.m_stringLength = value.m_stringLength; + return newStr; + } + + /// + private unsafe void NullTerminate() { + fixed(char *p = &this.m_firstChar) { + p[m_stringLength] = '\0'; + } + } + + /// + unsafe internal void ClearPostNullChar() { + int newLength = Length+1; + if (newLength < m_arrayLength) { + fixed(char *p = &this.m_firstChar) { + p[newLength] = '\0'; + } + } + } + + /// + internal void SetLength(int newLength) { + Debug.Assert(newLength <= m_arrayLength, "newLength<=m_arrayLength"); + m_stringLength = newLength; + } + + + + //| + public CharEnumerator GetEnumerator() { + return new CharEnumerator(this); + } + + //| + /// + IEnumerator IEnumerable.GetEnumerator() { + return new CharEnumerator(this); + } + + // + // This is just designed to prevent compiler warnings. + // This field is used from native, but we need to prevent the compiler warnings. + // +#if _DEBUG + private void DontTouchThis() { + m_arrayLength = 0; + m_stringLength = 0; + m_firstChar = m_firstChar; + } +#endif + + internal unsafe void InternalSetCharNoBoundsCheck(int index, char value) { + fixed (char *p = &this.m_firstChar) { + p[index] = value; + } + } + + +#if false // Unused + + // NOTE: len is not the length in characters, but the length in bytes + + // Copies the source String (byte buffer) to the destination IntPtr memory allocated with len bytes. + internal unsafe static void InternalCopy(String src, IntPtr dest,int len) + { + if (len == 0) + return; + fixed(char* charPtr = &src.m_firstChar) { + byte* srcPtr = (byte*) charPtr; + byte* dstPtr = (byte*) dest.ToPointer(); + Buffer.MoveMemory(dstPtr, srcPtr, len); + } + } + + // memcopies characters inside a String. + internal unsafe static void InternalMemCpy(String src, int srcOffset, String dst, int destOffset, int len) + { + if (len == 0) + return; + fixed(char* srcPtr = &src.m_firstChar) { + fixed(char* dstPtr = &dst.m_firstChar) { + Buffer.MoveMemory((byte*)(dstPtr + destOffset), + (byte*)(srcPtr + srcOffset), + len); + } + } + } +#endif + + // Copies the source String to the destination byte[] + internal unsafe static void InternalCopy(String src, byte[] dest, int stringLength) + { + if (stringLength == 0) + return; + int len = stringLength * sizeof(char); + + Debug.Assert(dest.Length >= len); + + fixed(char* charPtr = &src.m_firstChar) { + fixed(byte* destPtr = &dest[0]) { + byte* srcPtr = (byte*) charPtr; + Buffer.MoveMemory(destPtr, srcPtr, len); + } + } + } + + internal unsafe void InsertInPlace(int index, String value, int repeatCount, int currentLength, int requiredLength) { + Debug.Assert(requiredLength < m_arrayLength, "[String.InsertString] requiredLength < m_arrayLength"); + Debug.Assert(index + value.Length * repeatCount < m_arrayLength, "[String.InsertString] index + value.Length * repeatCount < m_arrayLength"); +#if _DEBUG + Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); +#endif + //Copy the old characters over to make room and then insert the new characters. + fixed(char* srcPtr = &this.m_firstChar) { + fixed(char* valuePtr = &value.m_firstChar) { + Buffer.MoveMemory((byte*)(srcPtr + index + value.Length * repeatCount), + (byte*)(srcPtr + index), + (currentLength - index) * sizeof(char)); + for (int i = 0; i < repeatCount; i++) { + Buffer.MoveMemory((byte*)(srcPtr + index + i * value.Length), + (byte*)valuePtr, + value.Length * sizeof(char)); + } + } + srcPtr[requiredLength] = '\0'; + } + this.m_stringLength = requiredLength; + } + + [CLSCompliant(false)] + [NoHeapAllocation] + public unsafe int LimitedFormatTo(char *buffer, int length) + { + unchecked { + fixed (char *fmtBeg = &m_firstChar) { + char *fmtPtr = fmtBeg; + char *fmtEnd = fmtBeg + m_stringLength; + + char *outPtr = buffer; + char *outEnd = buffer + length; + + while (fmtPtr < fmtEnd && outPtr < outEnd) { + *outPtr++ = *fmtPtr++; + } + return (int)(outPtr - buffer); + } + } + } + + [CLSCompliant(false)] + [NoHeapAllocation] + public static unsafe int LimitedFormatTo(String format, + ArgIterator args, + char *buffer, + int length) + { + unchecked { + + fixed (char *fmtBeg = &format.m_firstChar) { + char *fmtPtr = fmtBeg; + char *fmtEnd = fmtBeg + format.m_stringLength; + + char *outPtr = buffer; + char *outEnd = buffer + length; + + while (fmtPtr < fmtEnd && outPtr < outEnd) { + if (*fmtPtr == '{') { + char * fmtPos = fmtPtr; + bool bad = false; + fmtPtr++; + + int ndx = 0; + int aln = 0; + int wid = 0; + char fmt = 'd'; + + if (*fmtPtr == '{') { + *outPtr++ = *fmtPtr++; + } + else if (*fmtPtr >= '0' && *fmtPtr <= '9') { + + // {index,alignment:type width} + // Get Index + while (*fmtPtr >= '0' && *fmtPtr <= '9') { + ndx = ndx * 10 + (*fmtPtr++ - '0'); + } + + // Get Alignment + if (*fmtPtr == ',') { + fmtPtr++; + if (*fmtPtr == '-') { + // We just ignore left alignment for now. + fmtPtr++; + } + while (*fmtPtr >= '0' && *fmtPtr <= '9') { + aln = aln * 10 + (*fmtPtr++ - '0'); + } + } + + // Get FormatString + if (*fmtPtr == ':') { + fmtPtr++; + if (*fmtPtr >= 'a' && *fmtPtr <= 'z') { + fmt = *fmtPtr++; + } + else if (*fmtPtr >= 'A' && *fmtPtr <= 'Z') { + fmt = (char)(*fmtPtr++ - ('A' + 'a')); + } + while (*fmtPtr >= '0' && *fmtPtr <= '9') { + wid = wid * 10 + (*fmtPtr++ - '0'); + } + } + + // Get closing brace. + if (*fmtPtr == '}') { + fmtPtr++; + } + else { + bad = true; + } + + if (ndx >= args.Length) { + bad = true; + } + + if (bad) { + for (; fmtPos < fmtPtr; fmtPos++) { + if (outPtr < outEnd) { + *outPtr++ = *fmtPos; + } + } + } + else { + // Get the value + char cvalue = '\0'; + long ivalue = 0; + string svalue = null; + ulong value = 0; + IntPtr pvalue; + RuntimeType type; + + type = args.GetArg(ndx, out pvalue); + + switch (type.classVtable.structuralView) { + case StructuralType.Bool: + svalue =*(bool *)pvalue ? "true" : "false"; + break; + case StructuralType.Char: + cvalue = *(char *)pvalue; + break; + case StructuralType.Int8: + ivalue = *(sbyte *)pvalue; + break; + case StructuralType.Int16: + ivalue = *(short *)pvalue; + break; + case StructuralType.Int32: + ivalue = *(int *)pvalue; + break; + case StructuralType.Int64: + ivalue = *(long *)pvalue; + break; + case StructuralType.UInt8: + value = *(byte *)pvalue; + break; + case StructuralType.UInt16: + value = *(ushort *)pvalue; + break; + case StructuralType.UInt32: + value = *(uint *)pvalue; + break; + case StructuralType.UInt64: + value = *(ulong *)pvalue; + break; + case StructuralType.IntPtr: + value = (ulong)*(IntPtr *)pvalue; + break; + case StructuralType.UIntPtr: + value = (ulong)*(UIntPtr *)pvalue; + break; + case StructuralType.Reference: + if (type == typeof(String)) { + svalue = Magic.toString( + Magic.fromAddress(*(UIntPtr *)pvalue)); + } + else { + svalue = type.Name; + } + break; + default: + svalue = "???"; + break; + } + + if (cvalue != '\0') { + outPtr = AddChar(outPtr, outEnd, *(char *)pvalue, aln); + } + else if (svalue != null) { + outPtr = AddString(outPtr, outEnd, svalue, aln); + } + else { + if (aln < wid) { + aln = wid; + } + + if (fmt == 'x') { + if (ivalue != 0) { + value = (ulong)ivalue; + } + outPtr = AddNumber(outPtr, outEnd, value, aln, 16, '0', '\0'); + } + else { + char sign = '\0'; + if (ivalue < 0) { + sign = '-'; + value = unchecked((ulong)-ivalue); + } + else if (ivalue > 0) { + value = unchecked((ulong)ivalue); + } + outPtr = AddNumber(outPtr, outEnd, value, aln, 10, ' ', sign); + } + } + } + } + } + else if (*fmtPtr == '}') { + if (outPtr < outEnd) { + *outPtr++ = *fmtPtr; + } + fmtPtr++; + } + else { + if (outPtr < outEnd) { + *outPtr++ = *fmtPtr; + } + fmtPtr++; + } + } + return (int)(outPtr - buffer); + } + } + } + + [NoHeapAllocation] + private static int CountDigits(ulong value, uint valueBase) + { + int used = 0; + do { + value /= valueBase; + used++; + } while (value != 0); + + return used; + } + + [NoHeapAllocation] + private static unsafe char * AddNumber(char *output, char *limit, + ulong value, int width, uint valueBase, + char fill, char sign) + { + // Make sure we won't overfill the buffer. + if (output >= limit) { + return output; + } + + // Figure out how wide the string will be. + int used = CountDigits(value, valueBase); + + // Check for overflow. + if (output + used + ((sign != '\0') ? 1 : 0) > limit) { + while (width > 0) { + *output++ = '#'; + width--; + } + return output; + } + + // Handle sign and space fill. + if (sign != '\0' && used < width) { + if (fill == ' ') { + while (width > used + 1) { + *output++ = fill; + width--; + } + fill = '\0'; + } + *output++ = sign; + } + + // Handle other non-zero fills. + if (fill != '\0') { + while (width > used) { + *output++ = fill; + width--; + } + } + + // Write the characters into the buffer. + char *outp = output + used; + do { + uint digit = (uint)(value % valueBase); + value /= valueBase; + + if (digit >= 0 && digit <= 9) { + *--outp = (char)('0' + digit); + } + else { + *--outp = (char)('a' + (digit - 10)); + } + } while (value != 0); + + return output + used; + } + + [NoHeapAllocation] + private static unsafe char * AddString(char *output, char *limit, + string value, int width) + { + fixed (char *pwString = &value.m_firstChar) { + return AddString(output, limit, pwString, value.m_stringLength, width); + } + } + + [NoHeapAllocation] + private static unsafe char * AddString(char *output, char *limit, + char *pwString, int cwString, + int width) + { + // width < 0: left justified at -width. + // width = 0: no specified width. + // width > 0: right justified at width. + if (width == 0) { + width = cwString; + } + else if (width < 0) { + width = -width; + for (; width > cwString; width--) { + if (output < limit) { + *output++ = ' '; + } + } + } + + while (cwString > 0 && width > 0) { + if (output < limit) { + *output++ = (char)*pwString++; + } + width--; + cwString--; + } + + for (; width > 0; width--) { + if (output < limit) { + *output++ = ' '; + } + } + return output; + } + + [NoHeapAllocation] + private static unsafe char * AddChar(char *output, char *limit, char value, int width) + { + // width < 0: left justified at -width. + // width = 0: no specified width. + // width > 0: right justified at width. + if (width == 0) { + width = 1; + } + else if (width < 0) { + width = -width; + for (; width > 1; width--) { + if (output < limit) { + *output++ = ' '; + } + } + } + + if (width > 0) { + if (output < limit) { + *output++ = (char)value; + } + width--; + } + + for (; width > 0; width--) { + if (output < limit) { + *output++ = ' '; + } + } + return output; + } + } +} diff --git a/base/Kernel/System/SystemException.cs b/base/Applications/Runtime/Full/System/SystemException.cs similarity index 94% rename from base/Kernel/System/SystemException.cs rename to base/Applications/Runtime/Full/System/SystemException.cs index eda1ec6..64b5761 100644 --- a/base/Kernel/System/SystemException.cs +++ b/base/Applications/Runtime/Full/System/SystemException.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System { +namespace System +{ using System; //| diff --git a/base/Applications/Runtime/Full/System/Text/ASCIIEncoding.cs b/base/Applications/Runtime/Full/System/Text/ASCIIEncoding.cs new file mode 100644 index 0000000..c2d44ca --- /dev/null +++ b/base/Applications/Runtime/Full/System/Text/ASCIIEncoding.cs @@ -0,0 +1,208 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +namespace System.Text +{ + using System.Text; + using System; + using System.Runtime.CompilerServices; + + //| + public partial class ASCIIEncoding : Encoding + { + private const int ASCII_CODEPAGE=20127; + + //| + public ASCIIEncoding() : base(ASCII_CODEPAGE) { + } + + //| + public override int GetByteCount(char[] chars, int index, int count) { + if (chars == null) { + throw new ArgumentNullException("chars", "ArgumentNull_Array"); + } + if (index < 0 || count < 0) { + throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), + "ArgumentOutOfRange_NeedNonNegNum"); + } + if (chars.Length - index < count) { + throw new ArgumentOutOfRangeException("chars", + "ArgumentOutOfRange_IndexCountBuffer"); + } + + return (count); + } + + //| + public override int GetByteCount(String chars) { + if (chars == null) { + throw new ArgumentNullException("chars", "ArgumentNull_Array"); + } + return chars.Length; + } + + //| + public override int GetBytes(char[] chars, int charIndex, int charCount, + byte[] bytes, int byteIndex) { + if (chars == null || bytes == null) { + throw new ArgumentNullException((chars == null ? "chars" : "bytes"), + "ArgumentNull_Array"); + } + if (charIndex < 0 || charCount < 0) { + throw new ArgumentOutOfRangeException((charIndex<0 ? "charIndex" : "charCount"), + "ArgumentOutOfRange_NeedNonNegNum"); + } + if (chars.Length - charIndex < charCount) { + throw new ArgumentOutOfRangeException("chars", + "ArgumentOutOfRange_IndexCountBuffer"); + } + if (byteIndex < 0 || byteIndex > bytes.Length) { + throw new ArgumentOutOfRangeException("byteIndex", + "ArgumentOutOfRange_Index"); + } + if (bytes.Length - byteIndex < charCount) { + throw new ArgumentException("Argument_ConversionOverflow"); + } + int charEnd = charIndex + charCount; + + while (charIndex < charEnd) { + char ch = chars[charIndex++]; + if (ch >= 0x0080) ch = '?'; + bytes[byteIndex++] = (byte)ch; + } + return charCount; + } + + //| + public override int GetBytes(String chars, int charIndex, int charCount, + byte[] bytes, int byteIndex) { + if (chars == null || bytes == null) { + throw new ArgumentNullException((chars == null ? "chars" : "bytes"), + "ArgumentNull_Array"); + } + if (charIndex < 0 || charCount < 0) { + throw new ArgumentOutOfRangeException((charIndex<0 ? "charIndex" : "charCount"), + "ArgumentOutOfRange_NeedNonNegNum"); + } + if (chars.Length - charIndex < charCount) { + throw new ArgumentOutOfRangeException("chars", + "ArgumentOutOfRange_IndexCount"); + } + if (byteIndex < 0 || byteIndex > bytes.Length) { + throw new ArgumentOutOfRangeException("byteIndex", + "ArgumentOutOfRange_Index"); + } + if (bytes.Length - byteIndex < charCount) { + throw new ArgumentException("Argument_ConversionOverflow"); + } + int charEnd = charIndex + charCount; + + while (charIndex < charEnd) { + char ch = chars[charIndex++]; + if (ch >= 0x0080) ch = '?'; + bytes[byteIndex++] = (byte)ch; + } + return charCount; + } + + //| + public override int GetCharCount(byte[] bytes, int index, int count) { + if (bytes == null) { + throw new ArgumentNullException("bytes", + "ArgumentNull_Array"); + } + if (index < 0 || count < 0) { + throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), + "ArgumentOutOfRange_NeedNonNegNum"); + } + if (bytes.Length - index < count) { + throw new ArgumentOutOfRangeException("bytes", + "ArgumentOutOfRange_IndexCountBuffer"); + } + return (count); + + } + + //| + public override int GetChars(byte[] bytes, int byteIndex, int byteCount, + char[] chars, int charIndex) { + if (bytes == null || chars == null) { + throw new ArgumentNullException((bytes == null ? "bytes" : "chars"), + "ArgumentNull_Array"); + } + if (byteIndex < 0 || byteCount < 0) { + throw new ArgumentOutOfRangeException((byteIndex<0 ? "byteIndex" : "byteCount"), + "ArgumentOutOfRange_NeedNonNegNum"); + } + if (bytes.Length - byteIndex < byteCount) { + throw new ArgumentOutOfRangeException("bytes", + "ArgumentOutOfRange_IndexCountBuffer"); + } + if (charIndex < 0 || charIndex > chars.Length) { + throw new ArgumentOutOfRangeException("charIndex", + "ArgumentOutOfRange_Index"); + } + if (chars.Length - charIndex < byteCount) { + throw new ArgumentException("Argument_ConversionOverflow"); + } + int byteEnd = byteIndex + byteCount; + while (byteIndex < byteEnd) { + byte b = bytes[byteIndex++]; + if (b > 0x7f) { + // This is an invalid byte in the ASCII encoding. + chars[charIndex++] = '?'; + } + else { + chars[charIndex++] = (char)b; + } + } + return (byteCount); + + } + + //| + public override String GetString(byte[] bytes) + { + if (bytes == null) + throw new ArgumentNullException("bytes", "ArgumentNull_Array"); + return String.CreateStringFromASCII(bytes, 0, bytes.Length); + } + + //| + public override String GetString(byte[] bytes, int byteIndex, int byteCount) + { + if (bytes == null) + throw new ArgumentNullException("bytes", "ArgumentNull_Array"); + if (byteIndex < 0 || byteCount < 0) { + throw new ArgumentOutOfRangeException((byteIndex<0 ? "byteIndex" : "byteCount"), + "ArgumentOutOfRange_NeedNonNegNum"); + } + if (bytes.Length - byteIndex < byteCount) { + throw new ArgumentOutOfRangeException("bytes", + "ArgumentOutOfRange_IndexCountBuffer"); + } + return String.CreateStringFromASCII(bytes, byteIndex, byteCount); + } + + //| + public override int GetMaxByteCount(int charCount) { + if (charCount < 0) { + throw new ArgumentOutOfRangeException("charCount", + "ArgumentOutOfRange_NeedNonNegNum"); + } + + return (charCount); + } + + //| + public override int GetMaxCharCount(int byteCount) { + if (byteCount < 0) { + throw new ArgumentOutOfRangeException("byteCount", + "ArgumentOutOfRange_NeedNonNegNum"); + } + return byteCount; + } + } +} diff --git a/base/Kernel/System/Text/Decoder.cs b/base/Applications/Runtime/Full/System/Text/Decoder.cs similarity index 96% rename from base/Kernel/System/Text/Decoder.cs rename to base/Applications/Runtime/Full/System/Text/Decoder.cs index 68c3eb2..de2bd23 100644 --- a/base/Kernel/System/Text/Decoder.cs +++ b/base/Applications/Runtime/Full/System/Text/Decoder.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System.Text { +namespace System.Text +{ using System.Text; using System; // A Decoder is used to decode a sequence of blocks of bytes into a diff --git a/base/Kernel/System/Text/Encoder.cs b/base/Applications/Runtime/Full/System/Text/Encoder.cs similarity index 97% rename from base/Kernel/System/Text/Encoder.cs rename to base/Applications/Runtime/Full/System/Text/Encoder.cs index 0ef5d16..8f7e349 100644 --- a/base/Kernel/System/Text/Encoder.cs +++ b/base/Applications/Runtime/Full/System/Text/Encoder.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System.Text { +namespace System.Text +{ using System.Text; using System; // An Encoder is used to encode a sequence of blocks of characters into diff --git a/base/Kernel/System/Text/Encoding.cs b/base/Applications/Runtime/Full/System/Text/Encoding.cs similarity index 97% rename from base/Kernel/System/Text/Encoding.cs rename to base/Applications/Runtime/Full/System/Text/Encoding.cs index a6e11fe..1c2cc16 100644 --- a/base/Kernel/System/Text/Encoding.cs +++ b/base/Applications/Runtime/Full/System/Text/Encoding.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System.Text { +namespace System.Text +{ using System; using System.Collections; @@ -64,7 +65,6 @@ namespace System.Text { // generally executes faster. // //| - [RequiredByBartok] public abstract class Encoding { private static Encoding unicodeEncoding; @@ -85,7 +85,7 @@ namespace System.Text { //| protected Encoding(int codePage) { - if (codePage<0) { + if (codePage < 0) { throw new ArgumentOutOfRangeException("codePage"); } m_codePage = codePage; @@ -99,7 +99,7 @@ namespace System.Text { //| public static byte[] Convert(Encoding srcEncoding, Encoding dstEncoding, byte[] bytes) { - if (bytes==null) + if (bytes == null) throw new ArgumentNullException("bytes"); return Convert(srcEncoding, dstEncoding, bytes, 0, bytes.Length); } @@ -155,7 +155,7 @@ namespace System.Text { //| public virtual int GetByteCount(String s) { - if (s==null) + if (s == null) throw new ArgumentNullException("s"); char[] chars = s.ToCharArray(); return GetByteCount(chars, 0, chars.Length); @@ -219,7 +219,7 @@ namespace System.Text { public virtual int GetBytes(String s, int charIndex, int charCount, byte[] bytes, int byteIndex) { - if (s==null) + if (s == null) throw new ArgumentNullException("s"); return GetBytes(s.ToCharArray(), charIndex, charCount, bytes, byteIndex); } diff --git a/base/Kernel/System/Text/RegularExpressions/regex.cs b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regex.cs similarity index 93% rename from base/Kernel/System/Text/RegularExpressions/regex.cs rename to base/Applications/Runtime/Full/System/Text/RegularExpressions/regex.cs index 69d6df4..7597f23 100644 --- a/base/Kernel/System/Text/RegularExpressions/regex.cs +++ b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regex.cs @@ -1,9 +1,5 @@ //------------------------------------------------------------------------------ -// -// // Copyright (c) Microsoft Corporation. All rights reserved. -// -// //------------------------------------------------------------------------------ // The Regex class represents a single compiled instance of a regular @@ -11,7 +7,8 @@ // #define ECMA -namespace System.Text.RegularExpressions { +namespace System.Text.RegularExpressions +{ using System; using System.Threading; @@ -114,9 +111,9 @@ namespace System.Text.RegularExpressions { throw new ArgumentNullException("pattern"); - if (options < RegexOptions.None || ( ((int) options) >> MaxOptionShift) != 0) + if (options < RegexOptions.None || (((int) options) >> MaxOptionShift) != 0) throw new ArgumentOutOfRangeException("options"); - if ((options & RegexOptions.ECMAScript) != 0 + if ((options & RegexOptions.ECMAScript) != 0 && (options & ~(RegexOptions.ECMAScript | RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled #if DBG | RegexOptions.Debug @@ -166,36 +163,36 @@ namespace System.Text.RegularExpressions { cachedentry = cached; } -/* - * Singularity does not support compilation of regular expressions - // if the compile option is set, then compile the code if it's not already - if (UseOptionC() && factory == null) { - factory = Compile(code, roptions); - cachedentry.AddCompiled(factory); - code = null; - } - */ +// +// Singularity does not support compilation of regular expressions +// // if the compile option is set, then compile the code if it's not already +// if (UseOptionC() && factory == null) { +// factory = Compile(code, roptions); +// cachedentry.AddCompiled(factory); +// code = null; +// } +// } -/* - // ISerializable constructor - private Regex(SerializationInfo info, StreamingContext context) : this(info.GetString("pattern"), (RegexOptions) info.GetInt32("options")) { - } - - // ISerializable method - //| - /// - void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) { - si.AddValue("pattern", this.ToString()); - si.AddValue("options", this.Options); - } - // This method is here for perf reasons: if the call to RegexCompiler is NOT in the - // Regex constructor, we don't load RegexCompiler and its reflection classes when - // instantiating a non-compiled regex - // This method is internal virtual so the jit does not inline it. - internal virtual RegexRunnerFactory Compile(RegexCode code, RegexOptions roptions) { - return RegexCompiler.Compile(code, roptions); - } -*/ +// +// // ISerializable constructor +// private Regex(SerializationInfo info, StreamingContext context) : this(info.GetString("pattern"), (RegexOptions) info.GetInt32("options")) { +// } +// +// // ISerializable method +// //| +// /// +// void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) { +// si.AddValue("pattern", this.ToString()); +// si.AddValue("options", this.Options); +// } +// // This method is here for perf reasons: if the call to RegexCompiler is NOT in the +// // Regex constructor, we don't load RegexCompiler and its reflection classes when +// // instantiating a non-compiled regex +// // This method is internal virtual so the jit does not inline it. +// internal virtual RegexRunnerFactory Compile(RegexCode code, RegexOptions roptions) { +// return RegexCompiler.Compile(code, roptions); +// } +// // No refs -> we can release our ref on the cached code //| /// @@ -220,7 +217,7 @@ namespace System.Text.RegularExpressions { /// /// public static String Escape(String str) { - if (str==null) + if (str == null) throw new ArgumentNullException("str"); return RegexParser.Escape(str); @@ -234,7 +231,7 @@ namespace System.Text.RegularExpressions { /// /// public static String Unescape(String str) { - if (str==null) + if (str == null) throw new ArgumentNullException("str"); return RegexParser.Unescape(str); @@ -723,7 +720,7 @@ namespace System.Text.RegularExpressions { /// /// public String Replace(String input, MatchEvaluator evaluator) { - if (input==null) + if (input == null) throw new ArgumentNullException("input"); return Replace(input, evaluator, -1, UseOptionR() ? input.Length : 0); @@ -739,7 +736,7 @@ namespace System.Text.RegularExpressions { /// /// public String Replace(String input, MatchEvaluator evaluator, int count) { - if (input==null) + if (input == null) throw new ArgumentNullException("input"); return Replace(input, evaluator, count, UseOptionR() ? input.Length : 0); @@ -755,7 +752,7 @@ namespace System.Text.RegularExpressions { /// /// public String Replace(String input, MatchEvaluator evaluator, int count, int startat) { - if (input==null) + if (input == null) throw new ArgumentNullException("input"); return RegexReplacement.Replace(evaluator, this, input, count, startat); @@ -794,7 +791,7 @@ namespace System.Text.RegularExpressions { /// /// public String[] Split(String input) { - if (input==null) + if (input == null) throw new ArgumentNullException("input"); return Split(input, 0, UseOptionR() ? input.Length : 0); @@ -809,7 +806,7 @@ namespace System.Text.RegularExpressions { /// /// public String[] Split(String input, int count) { - if (input==null) + if (input == null) throw new ArgumentNullException("input"); return RegexReplacement.Split(this, input, count, UseOptionR() ? input.Length : 0); @@ -824,7 +821,7 @@ namespace System.Text.RegularExpressions { /// /// public String[] Split(String input, int count, int startat) { - if (input==null) + if (input == null) throw new ArgumentNullException("input"); return RegexReplacement.Split(this, input, count, startat); @@ -1081,15 +1078,15 @@ namespace System.Text.RegularExpressions { return(_references == 0); } - /* - internal void AddCompiled(RegexRunnerFactory factory) { - lock (this) { - _code = null; - _factory = factory; - _references += 1; // will never be balanced since we never unload the type - } - } - */ + // + //internal void AddCompiled(RegexRunnerFactory factory) { + // lock (this) { + // _code = null; + // _factory = factory; + // _references += 1; // will never be balanced since we never unload the type + // } + //} + // } // Used to cache one exclusive weak reference diff --git a/base/Kernel/System/Text/RegularExpressions/regexboyermoore.cs b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexboyermoore.cs similarity index 95% rename from base/Kernel/System/Text/RegularExpressions/regexboyermoore.cs rename to base/Applications/Runtime/Full/System/Text/RegularExpressions/regexboyermoore.cs index eb0199a..696a12f 100644 --- a/base/Kernel/System/Text/RegularExpressions/regexboyermoore.cs +++ b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexboyermoore.cs @@ -1,9 +1,5 @@ //------------------------------------------------------------------------------ -// -// // Copyright (c) Microsoft Corporation. All rights reserved. -// -// //------------------------------------------------------------------------------ // The RegexBoyerMoore object precomputes the Boyer-Moore @@ -49,9 +45,8 @@ namespace System.Text.RegularExpressions int match; char ch; - - if (caseInsensitive) - { + + if (caseInsensitive) { // pattern = pattern.ToLower(culture); pattern = pattern.ToLower(); } @@ -60,7 +55,7 @@ namespace System.Text.RegularExpressions _rightToLeft = rightToLeft; _caseInsensitive = caseInsensitive; _culture = culture; - + if (!rightToLeft) { beforefirst = -1; last = pattern.Length - 1; @@ -73,7 +68,7 @@ namespace System.Text.RegularExpressions } // PART I - the good-suffix shift table - // + // // compute the positive requirement: // if char "i" is the first one from the right that doesn't match, // then we know the matcher can advance by _positive[i]. @@ -105,7 +100,7 @@ namespace System.Text.RegularExpressions if (scan == beforefirst || pattern[match] != pattern[scan]) { // at the end of the match, note the difference in _positive // this is not the length of the match, but the distance from the internal match - // to the tail suffix. + // to the tail suffix. if (_positive[match] == 0) _positive[match] = match - scan; @@ -137,10 +132,10 @@ namespace System.Text.RegularExpressions //System.Diagnostics.Debug.WriteLine("good suffix shift table:"); //for (int i=0; i<_positive.Length; i++) // System.Diagnostics.Debug.WriteLine("\t_positive[" + i + "] = " + _positive[i]); - + // PART II - the bad-character shift table - // + // // compute the negative requirement: // if char "ch" is the reject character when testing position "i", // we can slide up by _negative[ch]; @@ -202,7 +197,7 @@ namespace System.Text.RegularExpressions // When a regex is anchored, we can do a quick IsMatch test instead of a Scan internal bool IsMatch(String text, int index, int beglimit, int endlimit) { - + if (!_rightToLeft) { if (index < beglimit || endlimit - index < _pattern.Length) return false; diff --git a/base/Kernel/System/Text/RegularExpressions/regexcapture.cs b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexcapture.cs similarity index 89% rename from base/Kernel/System/Text/RegularExpressions/regexcapture.cs rename to base/Applications/Runtime/Full/System/Text/RegularExpressions/regexcapture.cs index 6c78f0b..7342535 100644 --- a/base/Kernel/System/Text/RegularExpressions/regexcapture.cs +++ b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexcapture.cs @@ -1,9 +1,5 @@ //------------------------------------------------------------------------------ -// -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// +// Copyright (c) 2002 Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ // Capture is just a location/length pair that indicates the @@ -12,15 +8,16 @@ // RegexGroup. // -namespace System.Text.RegularExpressions { +namespace System.Text.RegularExpressions +{ //| /// - /// + /// /// Represents the results from a single subexpression capture. The object represents /// one substring for a single successful capture. /// - //[ Serializable() ] + //[ Serializable() ] public class Capture { internal String _text; internal int _index; @@ -71,7 +68,7 @@ namespace System.Text.RegularExpressions { //| /// /// - /// Returns + /// Returns /// the substring that was matched. /// /// diff --git a/base/Kernel/System/Text/RegularExpressions/regexcapturecollection.cs b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexcapturecollection.cs similarity index 94% rename from base/Kernel/System/Text/RegularExpressions/regexcapturecollection.cs rename to base/Applications/Runtime/Full/System/Text/RegularExpressions/regexcapturecollection.cs index e7f0cad..7b96981 100644 --- a/base/Kernel/System/Text/RegularExpressions/regexcapturecollection.cs +++ b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexcapturecollection.cs @@ -1,16 +1,13 @@ //------------------------------------------------------------------------------ -// -// // Copyright (c) Microsoft Corporation. All rights reserved. -// -// //------------------------------------------------------------------------------ // The CaptureCollection lists the captured Capture numbers // contained in a compiled Regex. // -namespace System.Text.RegularExpressions { +namespace System.Text.RegularExpressions +{ using System.Collections; @@ -24,7 +21,7 @@ namespace System.Text.RegularExpressions { /// to return the set of captures done by a single capturing group. /// /// - //[ Serializable() ] + //[ Serializable() ] public class CaptureCollection : ICollection { internal Group _group; internal int _capcount; @@ -147,7 +144,7 @@ namespace System.Text.RegularExpressions { // This non-public enumerator lists all the captures // Should it be public? - //[ Serializable() ] + //[ Serializable() ] internal class CaptureEnumerator : IEnumerator { internal CaptureCollection _rcc; internal int _curindex; diff --git a/base/Kernel/System/Text/RegularExpressions/regexcharclass.cs b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexcharclass.cs similarity index 92% rename from base/Kernel/System/Text/RegularExpressions/regexcharclass.cs rename to base/Applications/Runtime/Full/System/Text/RegularExpressions/regexcharclass.cs index 293eb5f..0bdc1f2 100644 --- a/base/Kernel/System/Text/RegularExpressions/regexcharclass.cs +++ b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexcharclass.cs @@ -1,9 +1,5 @@ //------------------------------------------------------------------------------ -// -// // Copyright (c) Microsoft Corporation. All rights reserved. -// -// //------------------------------------------------------------------------------ // This RegexCharClass class provides the "set of Unicode chars" functionality @@ -25,7 +21,8 @@ // #define ECMA -namespace System.Text.RegularExpressions { +namespace System.Text.RegularExpressions +{ using System.Collections; using System.Globalization; @@ -48,7 +45,7 @@ namespace System.Text.RegularExpressions { internal const short NotSpaceConst = -100; internal static readonly String Space = ((char) SpaceConst).ToString(); internal static readonly String NotSpace = NegateCategory(Space); - + internal const String ECMASpace = "\u0009\u000E\u0020\u0021"; internal const String NotECMASpace = "\0\u0009\u000E\u0020\u0021"; internal const String ECMAWord = "\u0030\u003A\u0041\u005B\u005F\u0060\u0061\u007B\u0130\u0131"; @@ -63,10 +60,10 @@ namespace System.Text.RegularExpressions { internal bool _canonical; internal bool _negate; - + static RegexCharClass() { _definedCategories = new Hashtable(31); - + char[] groups = new char[9]; StringBuilder word = new StringBuilder(11); @@ -74,14 +71,14 @@ namespace System.Text.RegularExpressions { groups[0] = GroupChar; // We need the UnicodeCategory enum values as a char so we can put them in a string - // in the hashtable. In order to get there, we first must cast to an int, + // in the hashtable. In order to get there, we first must cast to an int, // then cast to a char - // Also need to distinguish between positive and negative values. UnicodeCategory is zero + // Also need to distinguish between positive and negative values. UnicodeCategory is zero // based, so we add one to each value and subtract it off later // Others groups[1] = (char) ((int) UnicodeCategory.Control + 1); - _definedCategories["Cc"] = groups[1].ToString(); // Control + _definedCategories["Cc"] = groups[1].ToString(); // Control groups[2] = (char) ((int) UnicodeCategory.Format + 1); _definedCategories["Cf"] = groups[2].ToString(); // Format groups[3] = (char) ((int) UnicodeCategory.OtherNotAssigned + 1); @@ -100,7 +97,7 @@ namespace System.Text.RegularExpressions { groups[2] = (char) ((int) UnicodeCategory.ModifierLetter + 1); _definedCategories["Lm"] = groups[2].ToString(); // Modifier groups[3] = (char) ((int) UnicodeCategory.OtherLetter + 1); - _definedCategories["Lo"] = groups[3].ToString(); // Other + _definedCategories["Lo"] = groups[3].ToString(); // Other groups[4] = (char) ((int) UnicodeCategory.TitlecaseLetter + 1); _definedCategories["Lt"] = groups[4].ToString(); // Titlecase groups[5] = (char) ((int) UnicodeCategory.UppercaseLetter + 1); @@ -111,14 +108,14 @@ namespace System.Text.RegularExpressions { word.Append(groups[1]); word.Append(new String(groups, 3, 3)); - // Marks + // Marks groups[1] = (char) ((int) UnicodeCategory.SpacingCombiningMark + 1); _definedCategories["Mc"] = groups[1].ToString(); // Spacing combining groups[2] = (char) ((int) UnicodeCategory.EnclosingMark + 1); _definedCategories["Me"] = groups[2].ToString(); // Enclosing groups[3] = (char) ((int) UnicodeCategory.NonSpacingMark + 1); _definedCategories["Mn"] = groups[3].ToString(); // Non-spacing - + groups[4] = GroupChar; _definedCategories["M"] = new String(groups, 0, 5); //word.Append(groups[1]); @@ -130,7 +127,7 @@ namespace System.Text.RegularExpressions { groups[2] = (char) ((int) UnicodeCategory.LetterNumber + 1); _definedCategories["Nl"] = groups[2].ToString(); // Letter groups[3] = (char) ((int) UnicodeCategory.OtherNumber + 1); - _definedCategories["No"] = groups[3].ToString(); // Other + _definedCategories["No"] = groups[3].ToString(); // Other //groups[4] = GroupChar; _definedCategories["N"] = new String(groups, 0, 5); @@ -156,7 +153,7 @@ namespace System.Text.RegularExpressions { groups[8] = GroupChar; _definedCategories["P"] = new String(groups, 0, 9); word.Append(groups[1]); - + // Symbols groups[1] = (char) ((int) UnicodeCategory.CurrencySymbol + 1); _definedCategories["Sc"] = groups[1].ToString(); // Currency @@ -177,7 +174,7 @@ namespace System.Text.RegularExpressions { _definedCategories["Zp"] = groups[2].ToString(); // Paragraph groups[3] = (char) ((int) UnicodeCategory.SpaceSeparator + 1); _definedCategories["Zs"] = groups[3].ToString(); // Space - + groups[4] = GroupChar; _definedCategories["Z"] = new String(groups, 0, 5); @@ -189,9 +186,9 @@ namespace System.Text.RegularExpressions { #if DBG // make sure the _propTable is correctly ordered int len = _propTable.GetLength(0); - for (int i=0; i 0 && cc.RangeCount() > 0 && + if (_canonical && RangeCount() > 0 && cc.RangeCount() > 0 && cc.Range(cc.RangeCount() - 1)._last <= Range(RangeCount() - 1)._last) _canonical = false; @@ -255,7 +252,7 @@ namespace System.Text.RegularExpressions { internal void AddSet(String set) { int i; - if (_canonical && RangeCount() > 0 && set.Length > 0 && + if (_canonical && RangeCount() > 0 && set.Length > 0 && set[0] <= Range(RangeCount() - 1)._last) _canonical = false; @@ -280,11 +277,11 @@ namespace System.Text.RegularExpressions { } internal string Category { - get { + get { //if (_negate) // return NegateCategory(_categories.ToString()); //else - return _categories.ToString(); + return _categories.ToString(); } } @@ -302,7 +299,7 @@ namespace System.Text.RegularExpressions { if (categoryName.Equals("Lu") || categoryName.Equals("Lt")) catstr = /*catstr +*/ (string) _definedCategories["Ll"]; } - + if (invert) catstr = NegateCategory(catstr); // negate the category @@ -317,33 +314,33 @@ namespace System.Text.RegularExpressions { } - /*************************************************************************** - Let U be the set of Unicode character values and let L be the lowercase - function, mapping from U to U. To perform case insensitive matching of - character sets, we need to be able to map an interval I in U, say - - I = [chMin, chMax] = { ch : chMin <= ch <= chMax } - - to a set A such that A contains L(I) and A is contained in the union of - I and L(I). - - The table below partitions U into intervals on which L is non-decreasing. - Thus, for any interval J = [a, b] contained in one of these intervals, - L(J) is contained in [L(a), L(b)]. - - It is also true that for any such J, [L(a), L(b)] is contained in the - union of J and L(J). This does not follow from L being non-decreasing on - these intervals. It follows from the nature of the L on each interval. - On each interval, L has one of the following forms: - - (1) L(ch) = constant (LowercaseSet) - (2) L(ch) = ch + offset (LowercaseAdd) - (3) L(ch) = ch | 1 (LowercaseBor) - (4) L(ch) = ch + (ch & 1) (LowercaseBad) - - It is easy to verify that for any of these forms [L(a), L(b)] is - contained in the union of [a, b] and L([a, b]). - ***************************************************************************/ + //////////////////////////////////////////////////////////////////////////// + // Let U be the set of Unicode character values and let L be the lowercase + // function, mapping from U to U. To perform case insensitive matching of + // character sets, we need to be able to map an interval I in U, say + // + // I = [chMin, chMax] = { ch : chMin <= ch <= chMax } + // + // to a set A such that A contains L(I) and A is contained in the union of + // I and L(I). + // + // The table below partitions U into intervals on which L is non-decreasing. + // Thus, for any interval J = [a, b] contained in one of these intervals, + // L(J) is contained in [L(a), L(b)]. + // + // It is also true that for any such J, [L(a), L(b)] is contained in the + // union of J and L(J). This does not follow from L being non-decreasing on + // these intervals. It follows from the nature of the L on each interval. + // On each interval, L has one of the following forms: + // + // (1) L(ch) = constant (LowercaseSet) + // (2) L(ch) = ch + offset (LowercaseAdd) + // (3) L(ch) = ch | 1 (LowercaseBor) + // (4) L(ch) = ch + (ch & 1) (LowercaseBad) + // + // It is easy to verify that for any of these forms [L(a), L(b)] is + // contained in the union of [a, b] and L([a, b]). + ////////////////////////////////////////////////////////////////////////// internal const int LowercaseSet = 0; // Set to arg. internal const int LowercaseAdd = 1; // Add arg. @@ -498,7 +495,7 @@ namespace System.Text.RegularExpressions { return; } - for (i = 0, iMax = _lcTable.Length; i < iMax; ) { + for (i = 0, iMax = _lcTable.Length; i < iMax;) { iMid = (i + iMax) / 2; if (_lcTable[iMid]._chMax < chMin) i = iMid + 1; @@ -509,7 +506,7 @@ namespace System.Text.RegularExpressions { if (i >= _lcTable.Length) return; - for ( ; i < _lcTable.Length && (lc = _lcTable[i])._chMin <= chMax; i++) { + for (; i < _lcTable.Length && (lc = _lcTable[i])._chMin <= chMax; i++) { if ((chMinT = lc._chMin) < chMin) chMinT = chMin; @@ -687,11 +684,11 @@ namespace System.Text.RegularExpressions { setJ = swap; } - OuterContinue: + OuterContinue: ; } - OuterBreak: + OuterBreak: ; return sb.ToString(); @@ -700,7 +697,7 @@ namespace System.Text.RegularExpressions { internal static String CategoryUnion(string catI, string catJ) { return catI + catJ; } - + // SetFromChar() // // Builds the string representations of a class with a single character. @@ -756,7 +753,7 @@ namespace System.Text.RegularExpressions { internal static bool CharInSet(char ch, String set, String category) { bool b = CharInSetInternal(ch, set, category); - + if (set.Length >= 2 && (set[0] == 0) && (set[1] == 0)) return !b; else @@ -782,21 +779,21 @@ namespace System.Text.RegularExpressions { min = mid + 1; } - if ((min & 0x1) != 0) + if ((min & 0x1) != 0) return true; else return CharInCategory(ch, category); } internal static bool CharInCategory(char ch, string category) { - + if (category.Length == 0) return false; UnicodeCategory chcategory = char.GetUnicodeCategory(ch); int i=0; - while (i 0) { // greater than zero is a positive case - if (curcat == SpaceConst) { + if (curcat == SpaceConst) { if (Char.IsWhiteSpace(ch)) return true; else { @@ -830,7 +827,7 @@ namespace System.Text.RegularExpressions { continue; } } - + curcat = -curcat; --curcat; @@ -889,7 +886,7 @@ namespace System.Text.RegularExpressions { StringBuilder sb = new StringBuilder(); - for (int i=0; i 1) { Done = false; - for (i = 1, j = 0; ; i++) { - for (last = ((SingleRange)_rangelist[j])._last; ; i++) { + for (i = 1, j = 0;; i++) { + for (last = ((SingleRange)_rangelist[j])._last;; i++) { if (i == _rangelist.Count || last == Lastchar) { Done = true; break; @@ -981,23 +978,23 @@ namespace System.Text.RegularExpressions { } } - // The property table contains all the block definitions defined in the - // XML schema spec (http://www.w3.org/TR/2001/PR-xmlschema-2-20010316/#charcter-classes), Unicode 3.0 spec (www.unicode.org), - // and Perl 5.6 (see Programming Perl, 3rd edition page 167). Three blocks defined by Perl (and here) may - // not be in the Unicode: IsHighPrivateUseSurrogates, IsHighSurrogates, and IsLowSurrogates. - // - // In addition, there was some inconsistency in the definition of IsTibetan and IsArabicPresentationForms-B. + // The property table contains all the block definitions defined in the + // XML schema spec (http://www.w3.org/TR/2001/PR-xmlschema-2-20010316/#charcter-classes), Unicode 3.0 spec (www.unicode.org), + // and Perl 5.6 (see Programming Perl, 3rd edition page 167). Three blocks defined by Perl (and here) may + // not be in the Unicode: IsHighPrivateUseSurrogates, IsHighSurrogates, and IsLowSurrogates. + // + // In addition, there was some inconsistency in the definition of IsTibetan and IsArabicPresentationForms-B. // Regex goes with with the XML spec on both of these, since it seems to be (oddly enough) more correct than the Unicode spec! // // This is what we use: // IsTibetan: 0xF00 - 0x0FFF // IsArabicPresentationForms-B: 0xFE70-0xFEFE // - // The Unicode spec is inconsistent for IsTibetan. Its range is 0x0F00 - 0x0FBF. However, it clearly defines + // The Unicode spec is inconsistent for IsTibetan. Its range is 0x0F00 - 0x0FBF. However, it clearly defines // Tibetan characters above 0x0FBF. This appears to be an error between the 2.0 and 3.0 spec. // - // The Unicode spec is also unclear on IsArabicPresentationForms-B, defining it as 0xFE70-0xFEFF. - // There is only one character different here, 0xFEFF, which is a byte-order mark character and + // The Unicode spec is also unclear on IsArabicPresentationForms-B, defining it as 0xFE70-0xFEFF. + // There is only one character different here, 0xFEFF, which is a byte-order mark character and // is labeled in the spec as special. I have excluded it from IsArabicPresentationForms-B and left it in IsSpecial. // Has to be sorted by the first column private static readonly String[,] _propTable = { @@ -1113,7 +1110,7 @@ namespace System.Text.RegularExpressions { return invert ? SetInverse(set): set; } } - throw new ArgumentException("Unknown property");//XXX: SR.GetString(SR.MakeException, pattern, SR.GetString(SR.UnknownProperty, capname)), pattern); + throw new ArgumentException("Unknown property");//XXX: SR.GetString(SR.MakeException, pattern, SR.GetString(SR.UnknownProperty, capname)), pattern); //return invert ? Any : Empty ; } diff --git a/base/Kernel/System/Text/RegularExpressions/regexcode.cs b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexcode.cs similarity index 94% rename from base/Kernel/System/Text/RegularExpressions/regexcode.cs rename to base/Applications/Runtime/Full/System/Text/RegularExpressions/regexcode.cs index ad4b4dd..69d40fa 100644 --- a/base/Kernel/System/Text/RegularExpressions/regexcode.cs +++ b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexcode.cs @@ -1,9 +1,5 @@ //------------------------------------------------------------------------------ -// -// // Copyright (c) Microsoft Corporation. All rights reserved. -// -// //------------------------------------------------------------------------------ // This RegexCode class is internal to the regular expression package. @@ -21,7 +17,8 @@ // Strings and sets are indices into a string table. #define ECMA -namespace System.Text.RegularExpressions { +namespace System.Text.RegularExpressions +{ using System.Collections; using System.Diagnostics; @@ -144,8 +141,8 @@ namespace System.Text.RegularExpressions { case Lazybranch: case Branchmark: case Lazybranchmark: - case Nullcount: - case Setcount: + case Nullcount: + case Setcount: case Branchcount: case Lazybranchcount: case Setmark: @@ -311,7 +308,7 @@ namespace System.Text.RegularExpressions { case Capturemark: sb.Append("Index = "); sb.Append(_codes[offset+1]); - if (_codes[offset+2] != -1) { + if (_codes[offset + 2] != -1) { sb.Append(", Unindex = "); sb.Append(_codes[offset+2]); } @@ -375,12 +372,12 @@ namespace System.Text.RegularExpressions { Debug.WriteLine("Anchors: " + RegexFCD.AnchorDescription(_anchors)); Debug.WriteLine("Scanchars: " + (_scPrefix == null ? "n/a" : RegexCharClass.SetDescription(_scPrefix.Prefix))); Debug.WriteLine(""); - /* - if (_bmPrefix != null) { - Debug.WriteLine("BoyerMoore:"); - Debug.WriteLine(_bmPrefix.Dump(" ")); - } - */ + // + //if (_bmPrefix != null) { + // Debug.WriteLine("BoyerMoore:"); + // Debug.WriteLine(_bmPrefix.Dump(" ")); + //} + // for (i = 0; i < _codes.Length;) { Debug.WriteLine(OpcodeDescription(i)); i += OpcodeSize(_codes[i]); diff --git a/base/Kernel/System/Text/RegularExpressions/regexcompilationinfo.cs b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexcompilationinfo.cs similarity index 90% rename from base/Kernel/System/Text/RegularExpressions/regexcompilationinfo.cs rename to base/Applications/Runtime/Full/System/Text/RegularExpressions/regexcompilationinfo.cs index 40da0bf..ab913a0 100644 --- a/base/Kernel/System/Text/RegularExpressions/regexcompilationinfo.cs +++ b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexcompilationinfo.cs @@ -1,12 +1,9 @@ //------------------------------------------------------------------------------ -// -// // Copyright (c) Microsoft Corporation. All rights reserved. -// -// //------------------------------------------------------------------------------ -namespace System.Text.RegularExpressions { +namespace System.Text.RegularExpressions +{ using System; //| @@ -15,8 +12,8 @@ namespace System.Text.RegularExpressions { /// [To be supplied] /// /// - //[ Serializable() ] - public class RegexCompilationInfo { + //[ Serializable() ] + public class RegexCompilationInfo { private String pattern; private RegexOptions options; private String name; @@ -45,7 +42,7 @@ namespace System.Text.RegularExpressions { /// public String Pattern { get { return pattern; } - set { + set { if (value == null) throw new ArgumentNullException("value"); pattern = value; @@ -71,7 +68,7 @@ namespace System.Text.RegularExpressions { /// public String Name { get { return name; } - set { + set { if (value == null) throw new ArgumentNullException("value"); name = value; @@ -86,7 +83,7 @@ namespace System.Text.RegularExpressions { /// public String Namespace { get { return nspace; } - set { + set { if (value == null) throw new ArgumentNullException("value"); nspace = value; diff --git a/base/Kernel/System/Text/RegularExpressions/regexfcd.cs b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexfcd.cs similarity index 95% rename from base/Kernel/System/Text/RegularExpressions/regexfcd.cs rename to base/Applications/Runtime/Full/System/Text/RegularExpressions/regexfcd.cs index b45826f..665b105 100644 --- a/base/Kernel/System/Text/RegularExpressions/regexfcd.cs +++ b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexfcd.cs @@ -1,9 +1,5 @@ //------------------------------------------------------------------------------ -// -// // Copyright (c) Microsoft Corporation. All rights reserved. -// -// //------------------------------------------------------------------------------ // This RegexFCD class is internal to the Regex package. @@ -12,20 +8,21 @@ // // Implementation notes: -// +// // This step is as simple as walking the tree and emitting // sequences of codes. // #define ECMA -namespace System.Text.RegularExpressions { +namespace System.Text.RegularExpressions +{ using System.Collections; using System.Globalization; - + internal sealed class RegexFCD { internal int[] _intStack; - internal int _intDepth; + internal int _intDepth; internal RegexFC[] _fcStack; internal int _fcDepth; internal bool _earlyexit; @@ -55,7 +52,7 @@ namespace System.Text.RegularExpressions { if (fc._nullable) return null; - + CultureInfo culture = ((t._options & RegexOptions.CultureInvariant) != 0) ? CultureInfo.InvariantCulture : CultureInfo.CurrentCulture; return new RegexPrefix(fc.GetFirstChars(culture), fc.IsCaseInsensitive()); } @@ -270,14 +267,14 @@ namespace System.Text.RegularExpressions { // Convert anchor type to anchor bit. internal static int AnchorFromType(int type) { switch (type) { - case RegexNode.Bol: return Bol; - case RegexNode.Eol: return Eol; - case RegexNode.Boundary: return Boundary; + case RegexNode.Bol: return Bol; + case RegexNode.Eol: return Eol; + case RegexNode.Boundary: return Boundary; case RegexNode.ECMABoundary: return ECMABoundary; - case RegexNode.Beginning: return Beginning; - case RegexNode.Start: return Start; - case RegexNode.EndZ: return EndZ; - case RegexNode.End: return End; + case RegexNode.Beginning: return Beginning; + case RegexNode.Start: return Start; + case RegexNode.EndZ: return EndZ; + case RegexNode.End: return End; default: return 0; } } @@ -615,7 +612,7 @@ namespace System.Text.RegularExpressions { internal String GetFirstChars(CultureInfo culture) { return _cc.ToSetCi(_caseInsensitive, culture); } - + internal bool IsCaseInsensitive() { return _caseInsensitive; } diff --git a/base/Kernel/System/Text/RegularExpressions/regexgroup.cs b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexgroup.cs similarity index 89% rename from base/Kernel/System/Text/RegularExpressions/regexgroup.cs rename to base/Applications/Runtime/Full/System/Text/RegularExpressions/regexgroup.cs index b23db75..357eadc 100644 --- a/base/Kernel/System/Text/RegularExpressions/regexgroup.cs +++ b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexgroup.cs @@ -1,9 +1,5 @@ //------------------------------------------------------------------------------ -// -// // Copyright (c) Microsoft Corporation. All rights reserved. -// -// //------------------------------------------------------------------------------ // Group represents the substring or substrings that @@ -11,20 +7,21 @@ // regular expression match. // -namespace System.Text.RegularExpressions { +namespace System.Text.RegularExpressions +{ //| /// - /// Group + /// Group /// represents the results from a single capturing group. A capturing group can /// capture zero, one, or more strings in a single match because of quantifiers, so - /// Group supplies a collection of Capture objects. + /// Group supplies a collection of Capture objects. /// - //[ Serializable() ] + //[ Serializable() ] public class Group : Capture { // the empty group object internal static Group _emptygroup = new Group(String.Empty, new int[0], 0); - + internal int[] _caps; internal int _capcount; internal CaptureCollection _capcoll; @@ -70,7 +67,7 @@ namespace System.Text.RegularExpressions { // Convert to a thread-safe object by precomputing cache contents //| /// - /// Returns + /// Returns /// a Group object equivalent to the one supplied that is safe to share between /// multiple threads. /// diff --git a/base/Kernel/System/Text/RegularExpressions/regexgroupcollection.cs b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexgroupcollection.cs similarity index 94% rename from base/Kernel/System/Text/RegularExpressions/regexgroupcollection.cs rename to base/Applications/Runtime/Full/System/Text/RegularExpressions/regexgroupcollection.cs index 1f7863e..94fc227 100644 --- a/base/Kernel/System/Text/RegularExpressions/regexgroupcollection.cs +++ b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexgroupcollection.cs @@ -1,16 +1,13 @@ //------------------------------------------------------------------------------ -// -// // Copyright (c) Microsoft Corporation. All rights reserved. -// -// //------------------------------------------------------------------------------ // The GroupCollection lists the captured Capture numbers // contained in a compiled Regex. // -namespace System.Text.RegularExpressions { +namespace System.Text.RegularExpressions +{ using System.Collections; @@ -21,7 +18,7 @@ namespace System.Text.RegularExpressions { /// to return the set of captures done by a single capturing group. /// /// - //[ Serializable() ] + //[ Serializable() ] public class GroupCollection : ICollection { internal Match _match; internal Hashtable _captureMap; @@ -111,13 +108,13 @@ namespace System.Text.RegularExpressions { o = _captureMap[groupnum]; if (o == null) return Group._emptygroup; - //throw new ArgumentOutOfRangeException("groupnum"); + //throw new ArgumentOutOfRangeException("groupnum"); return GetGroupImpl((int)o); } else { //if (groupnum >= _match._regex.CapSize || groupnum < 0) - // throw new ArgumentOutOfRangeException("groupnum"); + // throw new ArgumentOutOfRangeException("groupnum"); if (groupnum >= _match._matchcount.Length || groupnum < 0) return Group._emptygroup; diff --git a/base/Kernel/System/Text/RegularExpressions/regexinterpreter.cs b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexinterpreter.cs similarity index 95% rename from base/Kernel/System/Text/RegularExpressions/regexinterpreter.cs rename to base/Applications/Runtime/Full/System/Text/RegularExpressions/regexinterpreter.cs index e01a010..f895c08 100644 --- a/base/Kernel/System/Text/RegularExpressions/regexinterpreter.cs +++ b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexinterpreter.cs @@ -1,9 +1,5 @@ //------------------------------------------------------------------------------ -// -// // Copyright (c) Microsoft Corporation. All rights reserved. -// -// //------------------------------------------------------------------------------ // This RegexInterpreter class is internal to the RegularExpression package. @@ -450,9 +446,9 @@ namespace System.Text.RegularExpressions Advance(2); - /* - - */ + // +// + // continue; case RegexCode.Capturemark | RegexCode.Back: @@ -471,12 +467,12 @@ namespace System.Text.RegularExpressions matched = Textpos() - Stacked(0); - if (matched != 0) { // Nonempty match -> loop now + if (matched != 0) { // Nonempty match->loop now Track(Stacked(0), Textpos()); // Save old mark, textpos Stack(Textpos()); // Make new mark Goto(Operand(0)); // Loop } - else { // Empty match -> straight now + else { // Empty match->straight now Track2(Stacked(0)); // Save old mark Advance(1); // Straight } @@ -503,10 +499,10 @@ namespace System.Text.RegularExpressions matched = Textpos() - Stacked(0); - if (matched != 0) { // Nonempty match -> next loop + if (matched != 0) { // Nonempty match->next loop Track(Stacked(0), Textpos()); // Save old mark, textpos } - else { // Empty match -> no loop + else { // Empty match->no loop Track2(Stacked(0)); // Save old mark } Advance(1); @@ -562,11 +558,11 @@ namespace System.Text.RegularExpressions int count = Stacked(1); int matched = Textpos() - mark; - if (count >= Operand(1) || (matched == 0 && count >= 0)) { // Max loops or empty match -> straight now + if (count >= Operand(1) || (matched == 0 && count >= 0)) { // Max loops or empty match->straight now Track2(mark, count); // Save old mark, count Advance(2); // Straight } - else { // Nonempty match -> count+loop now + else { // Nonempty match->count + loop now Track(mark); // remember mark Stack(Textpos(), count + 1); // Make new mark, incr count Goto(Operand(0)); // Loop @@ -582,7 +578,7 @@ namespace System.Text.RegularExpressions // 1: Count Trackframe(1); Stackframe(2); - if (Stacked(1) > 0) { // Positive -> can go straight + if (Stacked(1) > 0) { // Positive->can go straight Textto(Stacked(0)); // Zap to mark Track2(Tracked(0), Stacked(1) - 1); // Save old mark, old count Advance(2); // Straight @@ -609,12 +605,12 @@ namespace System.Text.RegularExpressions int mark = Stacked(0); int count = Stacked(1); - if (count < 0) { // Negative count -> loop now + if (count < 0) { // Negative count->loop now Track2(mark); // Save old mark Stack(Textpos(), count + 1); // Make new mark, incr count Goto(Operand(0)); // Loop } - else { // Nonneg count -> straight now + else { // Nonneg count->straight now Track(mark, count, Textpos()); // Save mark, count, position Advance(2); // Straight } @@ -630,14 +626,14 @@ namespace System.Text.RegularExpressions Trackframe(3); int mark = Tracked(0); int textpos = Tracked(2); - if (Tracked(1) <= Operand(1) && textpos != mark) { // Under limit and not empty match -> loop + if (Tracked(1) <= Operand(1) && textpos != mark) { // Under limit and not empty match->loop Textto(textpos); // Recall position Stack(textpos, Tracked(1) + 1); // Make new mark, incr count Track2(mark); // Save old mark Goto(Operand(0)); // Loop continue; } - else { // Max loops or empty match -> backtrack + else { // Max loops or empty match->backtrack Stack(Tracked(0), Tracked(1)); // Recall old mark, count break; // backtrack } @@ -793,7 +789,8 @@ namespace System.Text.RegularExpressions if (IsMatched(capnum)) { if (!Refmatch(MatchIndex(capnum), MatchLength(capnum))) break; - } else { + } + else { if ((runregex.roptions & RegexOptions.ECMAScript) == 0) break; } diff --git a/base/Kernel/System/Text/RegularExpressions/regexmatch.cs b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexmatch.cs similarity index 95% rename from base/Kernel/System/Text/RegularExpressions/regexmatch.cs rename to base/Applications/Runtime/Full/System/Text/RegularExpressions/regexmatch.cs index 489b530..eee03a3 100644 --- a/base/Kernel/System/Text/RegularExpressions/regexmatch.cs +++ b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexmatch.cs @@ -1,9 +1,5 @@ //------------------------------------------------------------------------------ -// -// // Copyright (c) Microsoft Corporation. All rights reserved. -// -// //------------------------------------------------------------------------------ // Match is the result class for a regex search. @@ -11,7 +7,8 @@ // the entire match as well as every captured group. // -namespace System.Text.RegularExpressions { +namespace System.Text.RegularExpressions +{ using System.Collections; using System.Diagnostics; @@ -19,15 +16,15 @@ namespace System.Text.RegularExpressions { //| /// /// - /// Represents + /// Represents /// the results from a single regular expression match. /// /// - //[ Serializable() ] + //[ Serializable() ] public class Match : Group { internal static Match _empty = new Match(null, 1, String.Empty, 0, 0, 0); internal GroupCollection _groupcoll; - + // input to the match internal Regex _regex; internal int _textbeg; @@ -71,9 +68,9 @@ namespace System.Text.RegularExpressions { //if (_textbeg < 0 || _textstart < _textbeg || _textend < _textstart || _text.Length < _textend) // throw new ArgumentOutOfRangeException(); - System.Diagnostics.Debug.Assert(!(_textbeg < 0 || _textstart < _textbeg || _textend < _textstart || _text.Length < _textend), + System.Diagnostics.Debug.Assert(!(_textbeg < 0 || _textstart < _textbeg || _textend < _textstart || _text.Length < _textend), "The parameters are out of range."); - + } // Nonpublic set-text method @@ -262,9 +259,9 @@ namespace System.Text.RegularExpressions { return _matches[cap][-3 - i]; } - /* - */ - + // + // + // Nonpublic: tidy the match so that it can be used as an immutable result internal virtual void Tidy(int textpos) { diff --git a/base/Kernel/System/Text/RegularExpressions/regexmatchcollection.cs b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexmatchcollection.cs similarity index 94% rename from base/Kernel/System/Text/RegularExpressions/regexmatchcollection.cs rename to base/Applications/Runtime/Full/System/Text/RegularExpressions/regexmatchcollection.cs index 145dacd..8df6ec2 100644 --- a/base/Kernel/System/Text/RegularExpressions/regexmatchcollection.cs +++ b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexmatchcollection.cs @@ -1,16 +1,13 @@ //------------------------------------------------------------------------------ -// -// // Copyright (c) Microsoft Corporation. All rights reserved. -// -// //------------------------------------------------------------------------------ // The MatchCollection lists the successful matches that // result when searching a string for a regular expression. // -namespace System.Text.RegularExpressions { +namespace System.Text.RegularExpressions +{ using System.Collections; @@ -25,7 +22,7 @@ namespace System.Text.RegularExpressions { /// names in a regular expression. /// /// - //[ Serializable() ] + //[ Serializable() ] public class MatchCollection : ICollection { internal Regex _regex; internal ArrayList _matches; @@ -178,7 +175,7 @@ namespace System.Text.RegularExpressions { // This non-public enumerator lists all the group matches. // Should it be public? - //[ Serializable() ] + //[ Serializable() ] internal class MatchEnumerator : IEnumerator { internal MatchCollection _matchcoll; internal Match _match = null; @@ -207,7 +204,7 @@ namespace System.Text.RegularExpressions { // The current match public Object Current { - get { + get { if (_match == null) throw new InvalidOperationException("Enum not Started");//XXX: SR.GetString(SR.EnumNotStarted)); return _match; diff --git a/base/Kernel/System/Text/RegularExpressions/regexnode.cs b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexnode.cs similarity index 96% rename from base/Kernel/System/Text/RegularExpressions/regexnode.cs rename to base/Applications/Runtime/Full/System/Text/RegularExpressions/regexnode.cs index bb92460..2b7a2a7 100644 --- a/base/Kernel/System/Text/RegularExpressions/regexnode.cs +++ b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexnode.cs @@ -1,9 +1,5 @@ //------------------------------------------------------------------------------ -// -// // Copyright (c) Microsoft Corporation. All rights reserved. -// -// //------------------------------------------------------------------------------ // This RegexNode class is internal to the Regex package. @@ -11,7 +7,7 @@ // // Implementation notes: -// +// // Since the node tree is a temporary data structure only used // during compilation of the regexp to integer codes, it's // designed for clarity and convenience rather than @@ -46,14 +42,15 @@ // #define ECMA -namespace System.Text.RegularExpressions { +namespace System.Text.RegularExpressions +{ using System.Collections; using System.Diagnostics; internal sealed class RegexNode { // RegexNode types - // + // // the following are leaves, and correspond to primitive operations @@ -114,7 +111,7 @@ namespace System.Text.RegularExpressions { internal const int infinite = 0x7FFFFFFF; // RegexNode data members - // + // internal int _type; @@ -232,7 +229,7 @@ namespace System.Text.RegularExpressions { // Simple optimization. If a concatenation or alternation has only // one child strip out the intermediate node. If it has zero children, // turn it into an empty. - // + // internal RegexNode StripEnation(int emptyType) { switch (ChildCount()) { @@ -253,7 +250,7 @@ namespace System.Text.RegularExpressions { internal RegexNode ReduceGroup() { RegexNode u; - for (u = this; u.Type() == Group; ) + for (u = this; u.Type() == Group;) u = u.Child(0); return u; @@ -394,7 +391,7 @@ namespace System.Text.RegularExpressions { prev._str = RegexCharClass.SetFromChar(prev._ch); } - if (at._type == RegexNode.One) + if (at._type == RegexNode.One) prev._str = RegexCharClass.SetUnion(prev._str, RegexCharClass.SetFromChar(at._ch)); // no categories to worry about else { prev._str = RegexCharClass.SetUnion(prev._str, at._str); @@ -525,7 +522,7 @@ namespace System.Text.RegularExpressions { } } - + internal void AddChild(RegexNode newChild) { RegexNode reducedChild; @@ -622,7 +619,7 @@ namespace System.Text.RegularExpressions { case Loop: case Lazyloop: ArgSb.Append("(Min = " + _m.ToString() + ", Max = " + (_n == infinite ? "inf" : Convert.ToString(_n)) + ")"); - break; + break; } return ArgSb.ToString(); diff --git a/base/Kernel/System/Text/RegularExpressions/regexoptions.cs b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexoptions.cs similarity index 92% rename from base/Kernel/System/Text/RegularExpressions/regexoptions.cs rename to base/Applications/Runtime/Full/System/Text/RegularExpressions/regexoptions.cs index c6f94fa..d1410fc 100644 --- a/base/Kernel/System/Text/RegularExpressions/regexoptions.cs +++ b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexoptions.cs @@ -1,14 +1,11 @@ //------------------------------------------------------------------------------ -// -// // Copyright (c) Microsoft Corporation. All rights reserved. -// -// //------------------------------------------------------------------------------ #define ECMA -namespace System.Text.RegularExpressions { +namespace System.Text.RegularExpressions +{ using System; @@ -22,7 +19,7 @@ using System; /// /// [To be supplied.] /// - None = 0x0000, + None = 0x0000, //| /// @@ -35,31 +32,31 @@ using System; /// [To be supplied.] /// Multiline = 0x0002, // "m" - + //| /// /// [To be supplied.] /// ExplicitCapture = 0x0004, // "n" - + //| /// /// [To be supplied.] /// Compiled = 0x0008, // "c" - + //| /// /// [To be supplied.] /// Singleline = 0x0010, // "s" - + //| /// /// [To be supplied.] /// IgnorePatternWhitespace = 0x0020, // "x" - + //| /// /// [To be supplied.] diff --git a/base/Kernel/System/Text/RegularExpressions/regexparser.cs b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexparser.cs similarity index 96% rename from base/Kernel/System/Text/RegularExpressions/regexparser.cs rename to base/Applications/Runtime/Full/System/Text/RegularExpressions/regexparser.cs index 7d7c646..6bcaa1d 100644 --- a/base/Kernel/System/Text/RegularExpressions/regexparser.cs +++ b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexparser.cs @@ -1,9 +1,5 @@ //------------------------------------------------------------------------------ -// -// // Copyright (c) Microsoft Corporation. All rights reserved. -// -// //------------------------------------------------------------------------------ // This RegexParser class is internal to the Regex package. @@ -16,7 +12,8 @@ // ScanBlank() calls are just kind of duct-taped in. #define ECMA -namespace System.Text.RegularExpressions { +namespace System.Text.RegularExpressions +{ using System.Collections; using System.Globalization; @@ -228,7 +225,8 @@ namespace System.Text.RegularExpressions { else if (IsSpecial(ch = RightChar())) { isQuantifier = IsQuantifier(ch); RightNext(); - } else + } + else ch = ' '; // nonspecial, means at ordinary char if (startpos < endpos) { @@ -801,10 +799,10 @@ namespace System.Text.RegularExpressions { throw MakeException("Alternation cannnot have comment"); //XXX: SR.GetString(SR.AlternationCantHaveComment)); // disallow named capture group (?<..>..) in the condition - if (rightchar2 == '\'' ) + if (rightchar2 == '\'') throw MakeException("Alternation cannot capture"); //XXX: SR.GetString(SR.AlternationCantCapture)); else { - if (charsRight >=4 && (rightchar2 == '<' && RightChar(3) != '!' && RightChar(3) != '=')) + if (charsRight >= 4 && (rightchar2 == '<' && RightChar(3) != '!' && RightChar(3) != '=')) throw MakeException("Alternation cannot capture"); //XXX: SR.GetString(SR.AlternationCantCapture)); } } @@ -1019,8 +1017,8 @@ namespace System.Text.RegularExpressions { } if (capnum >= 0) return new RegexNode(RegexNode.Ref, _options, capnum); - } else - { + } + else { int capnum = ScanDecimal(); if (IsCaptureSlot(capnum)) @@ -1093,8 +1091,7 @@ namespace System.Text.RegularExpressions { if (capnum >= 0) return new RegexNode(RegexNode.Ref, _options, capnum); } - else - { + else { int capnum = ScanDecimal(); if (!angled || CharsRight() > 0 && RightCharNext() == '}') { if (IsCaptureSlot(capnum)) @@ -1572,7 +1569,7 @@ namespace System.Text.RegularExpressions { _capnumlist = new Object[_capcount]; int i = 0; - for (IDictionaryEnumerator de = _caps.GetEnumerator(); de.MoveNext(); ) + for (IDictionaryEnumerator de = _caps.GetEnumerator(); de.MoveNext();) _capnumlist[i++] = de.Key; //XXX: System.Array.Sort(_capnumlist, InvariantComparer.Default); @@ -1870,8 +1867,8 @@ namespace System.Text.RegularExpressions { _group.AddChild(_alternation); } - /* - */ + // + // _unit = _group; } diff --git a/base/Kernel/System/Text/RegularExpressions/regexreplacement.cs b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexreplacement.cs similarity index 95% rename from base/Kernel/System/Text/RegularExpressions/regexreplacement.cs rename to base/Applications/Runtime/Full/System/Text/RegularExpressions/regexreplacement.cs index f4fe1f3..a325952 100644 --- a/base/Kernel/System/Text/RegularExpressions/regexreplacement.cs +++ b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexreplacement.cs @@ -1,9 +1,5 @@ //------------------------------------------------------------------------------ -// -// // Copyright (c) Microsoft Corporation. All rights reserved. -// -// //------------------------------------------------------------------------------ // The RegexReplacement class represents a substitution string for @@ -11,7 +7,8 @@ // a sequence intermixed (1) constant strings and (2) group numbers. // -namespace System.Text.RegularExpressions { +namespace System.Text.RegularExpressions +{ using System.Collections; @@ -68,8 +65,8 @@ namespace System.Text.RegularExpressions { strings.Add(sb.ToString()); } - _strings = strings; - _rules = rules; + _strings = strings; + _rules = rules; } internal String _rep; @@ -84,7 +81,7 @@ namespace System.Text.RegularExpressions { internal const int LastGroup = -3; internal const int WholeString = -4; - + // Given a Match, emits into the StringBuilder the evaluated // substitution pattern. private void ReplacementImpl(StringBuilder sb, Match match) { @@ -145,7 +142,7 @@ namespace System.Text.RegularExpressions { if (count < -1) throw new ArgumentOutOfRangeException("count", "Count too Small");//XXX: SR.GetString(SR.CountTooSmall)); - if (startat < 0 || startat > input.Length) + if (startat < 0 || startat > input.Length) throw new ArgumentOutOfRangeException("startat", "Begin Index Not Negative");//SR.GetString(SR.BeginIndexNotNegative)); if (count == 0) @@ -306,9 +303,9 @@ namespace System.Text.RegularExpressions { if (count < 0) throw new ArgumentOutOfRangeException("count", ""); // XXX: SR.GetString(SR.CountTooSmall)); - if (startat < 0 || startat > input.Length) + if (startat < 0 || startat > input.Length) throw new ArgumentOutOfRangeException("startat", ""); // XXX: SR.GetString(SR.BeginIndexNotNegative)); - + if (count == 1) { result = new String[1]; result[0] = input; @@ -334,7 +331,7 @@ namespace System.Text.RegularExpressions { al.Add(input.Substring(prevat, match.Index - prevat)); prevat = match.Index + match.Length; - + // add all matched capture groups to the list. int i = 1; while (match.IsMatched(i)) { diff --git a/base/Kernel/System/Text/RegularExpressions/regexrunner.cs b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexrunner.cs similarity index 96% rename from base/Kernel/System/Text/RegularExpressions/regexrunner.cs rename to base/Applications/Runtime/Full/System/Text/RegularExpressions/regexrunner.cs index 32dd6bb..c237498 100644 --- a/base/Kernel/System/Text/RegularExpressions/regexrunner.cs +++ b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexrunner.cs @@ -1,16 +1,12 @@ //------------------------------------------------------------------------------ -// -// // Copyright (c) Microsoft Corporation. All rights reserved. -// -// //------------------------------------------------------------------------------ // This RegexRunner class is a base class for compiled regex code. // // Implementation notes: -// +// // RegexRunner provides a common calling convention and a common // runtime environment for the interpreter and the compiled code. // @@ -23,7 +19,8 @@ // backtracked results from) the Match instance. #define ECMA -namespace System.Text.RegularExpressions { +namespace System.Text.RegularExpressions +{ using System.Collections; using System.Diagnostics; diff --git a/base/Kernel/System/Text/RegularExpressions/regextree.cs b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regextree.cs similarity index 88% rename from base/Kernel/System/Text/RegularExpressions/regextree.cs rename to base/Applications/Runtime/Full/System/Text/RegularExpressions/regextree.cs index ab30793..7678d47 100644 --- a/base/Kernel/System/Text/RegularExpressions/regextree.cs +++ b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regextree.cs @@ -1,16 +1,13 @@ //------------------------------------------------------------------------------ -// -// // Copyright (c) Microsoft Corporation. All rights reserved. -// -// //------------------------------------------------------------------------------ // RegexTree is just a wrapper for a node tree with some // global information attached. // -namespace System.Text.RegularExpressions { +namespace System.Text.RegularExpressions +{ using System.Collections; diff --git a/base/Kernel/System/Text/RegularExpressions/regexwriter.cs b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexwriter.cs similarity index 96% rename from base/Kernel/System/Text/RegularExpressions/regexwriter.cs rename to base/Applications/Runtime/Full/System/Text/RegularExpressions/regexwriter.cs index 193eaca..09f59fb 100644 --- a/base/Kernel/System/Text/RegularExpressions/regexwriter.cs +++ b/base/Applications/Runtime/Full/System/Text/RegularExpressions/regexwriter.cs @@ -1,9 +1,5 @@ //------------------------------------------------------------------------------ -// -// // Copyright (c) Microsoft Corporation. All rights reserved. -// -// //------------------------------------------------------------------------------ // This RegexWriter class is internal to the Regex package. @@ -12,18 +8,19 @@ // // Implementation notes: -// +// // This step is as simple as walking the tree and emitting // sequences of codes. // #define ECMA -namespace System.Text.RegularExpressions { +namespace System.Text.RegularExpressions +{ using System.Collections; //using System.Collections.Specialized; using System.Globalization; - + internal sealed class RegexWriter { internal int[] _intStack; internal int _depth; @@ -159,7 +156,7 @@ namespace System.Text.RegularExpressions { if (str == null) str = String.Empty; - + if (_stringhash.Contains(str)) { i = (Int32)_stringhash[str]; } @@ -381,7 +378,7 @@ namespace System.Text.RegularExpressions { Emit(RegexCode.Getmark); Emit(RegexCode.Forejump); break; - case 1: + case 1: int Branchpos = PopInt(); PushInt(CurPos()); Emit(RegexCode.Goto, 0); @@ -501,7 +498,7 @@ namespace System.Text.RegularExpressions { if (node._m > 0) Emit(RegexCode.Setrep | bits, StringCode(node._str), StringCode(node._str2), node._m); if (node._n > node._m) - Emit(node._type | bits, StringCode(node._str), StringCode(node._str2), + Emit(node._type | bits, StringCode(node._str), StringCode(node._str2), (node._n == infinite) ? infinite : node._n - node._m); break; diff --git a/base/Applications/Runtime/Full/System/Text/StringBuilder.cs b/base/Applications/Runtime/Full/System/Text/StringBuilder.cs new file mode 100644 index 0000000..ebcca4d --- /dev/null +++ b/base/Applications/Runtime/Full/System/Text/StringBuilder.cs @@ -0,0 +1,1273 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================ +// +// Class: StringBuilder +// +// Purpose: A prototype implementation of the StringBuilder +// class. +// +//=========================================================== +namespace System.Text +{ + using System.Text; + using System.Threading; + using System; + using System.Diagnostics; + using System.Runtime.CompilerServices; + + // This class represents a mutable string. It is convenient for situations in + // which it is desirable to modify a string, perhaps by removing, replacing, or + // inserting characters, without creating a new String subsequent to + // each modification. + // + // The methods contained within this class do not return a new StringBuilder + // object unless specified otherwise. This class may be used in conjunction with the String + // class to carry out modifications upon strings. + // + // When passing null into a constructor in VJ and VC, the null + // should be explicitly type cast. + // For Example: + // StringBuilder sb1 = new StringBuilder((StringBuilder)null); + // StringBuilder sb2 = new StringBuilder((String)null); + // + //| + public sealed partial class StringBuilder { + + // + // + // CLASS VARIABLES + // + // + internal int m_currentThread = InternalGetCurrentThread(); + internal int m_MaxCapacity = 0; + + + // + // + // STATIC CONSTANTS + // + // + private const int DEFAULT_CAPACITY = 16; + private const int DEFAULT_MAX_CAPACITY = 0x7FFFFFFF; + + // + // + //CONSTRUCTORS + // + // + + // Creates a new empty string builder (i.e., it represents String.Empty) + // with the default capacity (16 characters). + //| + public StringBuilder() + : this(DEFAULT_CAPACITY) { + } + + // Create a new empty string builder (i.e., it represents String.Empty) + // with the specified capacity. + //| + public StringBuilder(int capacity) { + if (capacity < 0) { + throw new ArgumentOutOfRangeException("capacity"); + } + + if (capacity == 0) { // MakeFromString enforces this + capacity = DEFAULT_CAPACITY; + } + + m_StringValue = String.GetStringForStringBuilder(String.Empty, capacity); + m_MaxCapacity = Int32.MaxValue; + } + + //=======================CalculateCapacity=============================== + // Calculates the new capacity of our buffer. If the size of the + // buffer is less than a fixed number (10000 in this case), we just + // double the buffer until we have enough space. Once we get larger + // than 10000, we use a series of heuristics to determine the most + // appropriate size. + //====================================================================== + private int CalculateCapacity(int currentCapacity, int neededCapacity) + { + // See also Lightning\Src\VM\COMStringBuffer.cpp::CalculateCapaity + int newCapacity = neededCapacity; + if (newCapacity == 0) { + newCapacity = DEFAULT_CAPACITY; + } + if (neededCapacity > this.m_MaxCapacity) { + throw new ArgumentOutOfRangeException("exceeding max capacity"); + } + while (newCapacity < neededCapacity && newCapacity > 0) { + newCapacity <<= 1; + } + // Check for overflow + if (newCapacity <= 0) { + // The C++ code throws on overflow, but it really should not + newCapacity = this.m_MaxCapacity; + } + else if (newCapacity > this.m_MaxCapacity) { + // We doubled past the max capacity, so back down a bit + newCapacity = this.m_MaxCapacity; + } + return newCapacity; + } + + // Creates a new string builder from the specified string. If value + // is a null String (i.e., if it represents String.NullString) + // then the new string builder will also be null (i.e., it will also represent + // String.NullString). + // + //| + public StringBuilder(String value){ + MakeFromString(value, 0, -1, -1); + } + + // Creates a new string builder from the specified string with the specified + // capacity. If value is a null String (i.e., if it represents + // String.NullString) then the new string builder will also be null + // (i.e., it will also represent String.NullString). + // The maximum number of characters this string may contain is set by capacity. + // + //| + public StringBuilder(String value, int capacity) { + if (capacity < 0) { + throw new ArgumentOutOfRangeException("capacity", + String.Format("ArgumentOutOfRange_MustBePositive", "capacity")); + } + + MakeFromString(value, 0, -1, capacity); + } + // Creates a new string builder from the specified substring with the specified + // capacity. The maximum number of characters is set by capacity. + // + + //| + public StringBuilder(String value, int startIndex, int length, int capacity) { + if (capacity < 0) { + throw new ArgumentOutOfRangeException("capacity", + String.Format("ArgumentOutOfRange_MustBePositive", "capacity")); + } + if (length < 0) { + throw new ArgumentOutOfRangeException("length", + String.Format("ArgumentOutOfRange_MustBeNonNegNum", "length")); + } + + MakeFromString(value, startIndex, length, capacity); + } + + // Creates an empty StringBuilder with a minimum capacity of capacity + // and a maximum capacity of maxCapacity. + //| + public StringBuilder(int capacity, int maxCapacity) { + if (capacity > maxCapacity) { + throw new ArgumentOutOfRangeException("capacity", "ArgumentOutOfRange_Capacity"); + } + if (maxCapacity < 1) { + throw new ArgumentOutOfRangeException("maxCapacity", "ArgumentOutOfRange_SmallMaxCapacity"); + } + + if (capacity < 0) { + throw new ArgumentOutOfRangeException("capacity", + String.Format("ArgumentOutOfRange_MustBePositive", "capacity")); + } + if (capacity == 0) { + capacity = DEFAULT_CAPACITY; + } + + m_StringValue = String.GetStringForStringBuilder(String.Empty, capacity); + m_MaxCapacity = maxCapacity; + + } + + private String GetThreadSafeString(out int tid) { + String temp = m_StringValue; + tid = InternalGetCurrentThread(); + if (m_currentThread == tid) + return temp; + return String.GetStringForStringBuilder(temp, temp.Capacity); + } + + private static int InternalGetCurrentThread() + { + return Thread.CurrentThread.GetThreadId(); + } + + // + // Private native functions + // + //=========================MakeFromString================================ + // If value is null, we simply create an empty string with a default + // length. If it does contain data, we allocate space for twice this + // amount of data, copy the data, associate it with the StringBuffer + // and clear the CopyOnWrite bit. + //======================================================================= + private void MakeFromString(String value, int startIndex, int length, int capacity) + { + // See also Lightning\Src\VM\COMStringBuffer.cpp::MakeFromString + if (capacity <= 0) { + capacity = DEFAULT_CAPACITY; + } + this.m_MaxCapacity = DEFAULT_MAX_CAPACITY; + if (value != null) { + if (startIndex < 0) { + throw new ArgumentOutOfRangeException("negative startIndex"); + } + int stringLength = value.Length; + if (length == -1) { + length = stringLength - startIndex; + } + else if (length + startIndex > stringLength) { + throw new ArgumentOutOfRangeException("exceeding string length"); + } + int newCapacity = this.CalculateCapacity(capacity, length); + String newString = + String.GetStringForStringBuilder(String.Empty, newCapacity); + newString.AppendInPlace(value, startIndex, length, 0); + this.m_StringValue = newString; + } + else if (capacity == 0) { + this.m_StringValue = String.Empty; + } + else { + this.m_StringValue = + String.GetStringForStringBuilder(String.Empty, capacity); + } + } + + //| + public int Capacity { + get {return m_StringValue.Capacity;} //-1 to account for terminating null. + set {InternalSetCapacity(value);} + } + + + //| + public int MaxCapacity { + get { return m_MaxCapacity; } + + } + + // Read-Only Property + // Ensures that the capacity of this string builder is at least the specified value. + // If capacity is greater than the capacity of this string builder, then the capacity + // is set to capacity; otherwise the capacity is unchanged. + // + //| + public int EnsureCapacity(int capacity) { + if (capacity < 0) { + throw new ArgumentOutOfRangeException("capacity", "ArgumentOutOfRange_NeedPosCapacity"); + } + + int tid; + String currentString = GetThreadSafeString(out tid); + + //If we need more space or the COW bit is set, copy the buffer. + if (!NeedsAllocation(currentString,capacity)) { + return currentString.Capacity; + } + + String newString = GetNewString(currentString,capacity); + ReplaceString(tid,newString); + return newString.Capacity; + } + + //Sets the capacity to be capacity. If capacity is less than the current + //instance an ArgumentException is thrown. If capacity is greater than the current + //instance, memory is allocated to allow the StringBuilder to grow. + // + internal int InternalSetCapacity(int capacity) + { + // See also Lighting\Src\VM\COMStringBuffer.cpp::SetCapacity + // The return value of the native code is a pointer to 'this', but + // since it isn't ever used, we return 0; + + int tid; + String thisString = GetThreadSafeString(out tid); + if (capacity < 0) { + throw new ArgumentOutOfRangeException("capacity is negative"); + } + if (capacity < thisString.Length) { + throw new ArgumentOutOfRangeException("capacity lesser than size"); + } + if (capacity > this.m_MaxCapacity) { + throw new ArgumentOutOfRangeException("exceeds max capacity"); + } + if (capacity != this.m_StringValue.ArrayLength - 1) { + this.m_StringValue = CopyString(this,thisString,capacity); + this.m_currentThread = tid; + } + return 0; + } + + private String CopyString(StringBuilder thisRef,String CurrString, + int newCapacity) + { + int CurrLen = CurrString.Length; + int copyLength; + if (newCapacity >= CurrLen) { + copyLength = CurrLen; + } + else { + copyLength = newCapacity; + } + return String.NewString(CurrString,0,copyLength,newCapacity); + } + + //| + public override String ToString() { + String currentString = m_StringValue; + int currentThread = m_currentThread; + if (currentThread != 0 && currentThread != InternalGetCurrentThread()) { + return String.InternalCopy(currentString); + } + + if ((2 * currentString.Length) < currentString.ArrayLength) { + return String.InternalCopy(currentString); + } + + currentString.ClearPostNullChar(); + m_currentThread = 0; + return currentString; + } + + // Converts a substring of this string builder to a String. + //| + public String ToString(int startIndex, int length) { + return m_StringValue.Substring(startIndex, length); + } + + // Sets the length of the String in this buffer. If length is less than the current + // instance, the StringBuilder is truncated. If length is greater than the current + // instance, nulls are appended. The capacity is adjusted to be the same as the length. + + //| + public int Length { + get { + return m_StringValue.Length; + } + set { + int tid; + String currentString = GetThreadSafeString(out tid); + + if (value == 0) { //the user is trying to clear the string + currentString.SetLength(0); + ReplaceString(tid,currentString); + return; + } + + int currentLength = currentString.Length; + int newlength = value; + //If our length is less than 0 or greater than our Maximum capacity, bail. + if (newlength < 0) { + throw new ArgumentOutOfRangeException("newlength", "ArgumentOutOfRange_NegativeLength"); + } + + if (newlength > MaxCapacity) { + throw new ArgumentOutOfRangeException("capacity", "ArgumentOutOfRange_SmallCapacity"); + } + + //Jump out early if our requested length our currentlength. + //This will be a pretty rare branch. + if (newlength == currentLength) { + return; + } + + + //If the StringBuilder has never been converted to a string, simply set the length + //without allocating a new string. + if (newlength <= currentString.Capacity) { + if (newlength > currentLength) { + for (int i = currentLength; i < newlength; i++) // This is a rare case anyway. + currentString.InternalSetCharNoBoundsCheck(i,'\0'); + } + + currentString.InternalSetCharNoBoundsCheck(newlength,'\0'); //Null terminate. + currentString.SetLength(newlength); + ReplaceString(tid,currentString); + + return; + } + + // CopyOnWrite set we need to allocate a String + int newCapacity = (newlength>currentString.Capacity)?newlength:currentString.Capacity; + String newString = String.GetStringForStringBuilder(currentString, newCapacity); + + //We know exactly how many characters we need, so embed that knowledge in the String. + newString.SetLength(newlength); + ReplaceString(tid,newString); + } + } + + //| + public char this[int index] { + get { + return m_StringValue[index]; + } + set { + int tid; + String currentString = GetThreadSafeString(out tid); + currentString.SetChar(index, value); + ReplaceString(tid,currentString); + } + } + + // Appends a character at the end of this string builder. The capacity is adjusted as needed. + //| + public StringBuilder Append(char value, int repeatCount) { + if (repeatCount == 0) { + return this; + } + if (repeatCount < 0) { + throw new ArgumentOutOfRangeException("repeatCount", "ArgumentOutOfRange_NegativeCount"); + } + + + int tid; + String currentString = GetThreadSafeString(out tid); + + int currentLength = currentString.Length; + int requiredLength = currentLength + repeatCount; + + if (requiredLength < 0) + throw new OutOfMemoryException(); + + if (!NeedsAllocation(currentString,requiredLength)) { + currentString.AppendInPlace(value, repeatCount,currentLength); + ReplaceString(tid,currentString); + return this; + } + + String newString = GetNewString(currentString,requiredLength); + newString.AppendInPlace(value, repeatCount,currentLength); + ReplaceString(tid,newString); + return this; + } + + // Appends an array of characters at the end of this string builder. The capacity is adjusted as needed. + //| + public StringBuilder Append(char[] value, int startIndex, int charCount) { + int requiredLength; + + if (value == null) { + if (startIndex == 0 && charCount == 0) { + return this; + } + throw new ArgumentNullException("value"); + } + + if (charCount == 0) { + return this; + } + + if (startIndex < 0) { + throw new ArgumentOutOfRangeException("startIndex", "ArgumentOutOfRange_GenericPositive"); + } + if (charCount < 0) { + throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_GenericPositive"); + } + if (charCount > value.Length - startIndex) { + throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_Index"); + } + + int tid; + String currentString = GetThreadSafeString(out tid); + + int currentLength = currentString.Length; + requiredLength = currentLength + charCount; + if (NeedsAllocation(currentString,requiredLength)) { + String newString = GetNewString(currentString,requiredLength); + newString.AppendInPlace(value, startIndex, charCount,currentLength); + ReplaceString(tid,newString); + } + else { + currentString.AppendInPlace(value, startIndex, charCount,currentLength); + ReplaceString(tid,currentString); + } + + return this; + } + + // Appends a copy of this string at the end of this string builder. + //| + public StringBuilder Append(String value) { + //If the value being added is null, eat the null + //and return. + if (value == null) { + return this; + } + + int tid; + // hand inlining of GetThreadSafeString + String currentString = m_StringValue; + tid = InternalGetCurrentThread(); + if (m_currentThread != tid) + currentString = String.GetStringForStringBuilder(currentString, currentString.Capacity); + + int currentLength = currentString.Length; + + int requiredLength = currentLength + value.Length; + + if (NeedsAllocation(currentString,requiredLength)) { + String newString = GetNewString(currentString,requiredLength); + newString.AppendInPlace(value,currentLength); + ReplaceString(tid,newString); + } + else { + currentString.AppendInPlace(value,currentLength); + ReplaceString(tid,currentString); + } + + return this; + } + + internal unsafe StringBuilder Append(char *value, int count) { + //If the value being added is null, eat the null + //and return. + if (value == null) { + return this; + } + + + int tid; + String currentString = GetThreadSafeString(out tid); + int currentLength = currentString.Length; + + int requiredLength = currentLength + count; + + if (NeedsAllocation(currentString,requiredLength)) { + String newString = GetNewString(currentString,requiredLength); + newString.AppendInPlace(value, count,currentLength); + ReplaceString(tid,newString); + } + else { + currentString.AppendInPlace(value,count,currentLength); + ReplaceString(tid,currentString); + } + + return this; + } + + private bool NeedsAllocation(String currentString,int requiredLength) { + //<= accounts for the terminating 0 which we require on strings. + return (currentString.ArrayLength<=requiredLength); + } + + private String GetNewString(String currentString, int requiredLength) { + int newCapacity; + + requiredLength++; //Include the terminating null. + + if (requiredLength < 0) { + throw new OutOfMemoryException(); + } + + if (requiredLength > m_MaxCapacity) { + throw new ArgumentOutOfRangeException("ArgumentOutOfRange_NegativeCapacity", + "requiredLength"); + } + + newCapacity = ( currentString.Capacity)*2; // To force a predictable growth of 160,320 etc. for testing purposes + + if (newCapacity < requiredLength) { + newCapacity = requiredLength; + } + + if (newCapacity > m_MaxCapacity) { + newCapacity = m_MaxCapacity; + } + + if (newCapacity <= 0) { + throw new ArgumentOutOfRangeException("ArgumentOutOfRange_NegativeCapacity"); + } + + return String.GetStringForStringBuilder( currentString, newCapacity); + } + + private void ReplaceString(int tid, String value) { + Debug.Assert(value!=null, "[StringBuilder.ReplaceString]value!=null"); + + m_currentThread = tid; // new owner + m_StringValue = value; + } + + // Appends a copy of the characters in value from startIndex to startIndex + + // count at the end of this string builder. + //| + public StringBuilder Append(String value, int startIndex, int count) { + //If the value being added is null, eat the null + //and return. + if (value == null) { + if (startIndex == 0 && count == 0) { + return this; + } + throw new ArgumentNullException("value"); + } + + if (count <= 0) { + if (count == 0) { + return this; + } + throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_GenericPositive"); + } + + if (startIndex < 0 || (startIndex > value.Length - count)) { + throw new ArgumentOutOfRangeException("startIndex", "ArgumentOutOfRange_Index"); + } + + int tid; + String currentString = GetThreadSafeString(out tid); + int currentLength = currentString.Length; + + int requiredLength = currentLength + count; + + if (NeedsAllocation(currentString,requiredLength)) { + String newString = GetNewString(currentString,requiredLength); + newString.AppendInPlace(value, startIndex, count, currentLength); + ReplaceString(tid,newString); + } + else { + currentString.AppendInPlace(value, startIndex, count, currentLength); + ReplaceString(tid,currentString); + } + + return this; + } + + // Inserts multiple copies of a string into this string builder at the specified position. + // Existing characters are shifted to make room for the new text. + // The capacity is adjusted as needed. If value equals String.Empty, this + // string builder is not changed. Inserts ";<;no object>;"; if value + // is null. + // + //| + public unsafe StringBuilder Insert(int index, String value, int count) { + int tid; + String currentString = GetThreadSafeString(out tid); + int currentLength = currentString.Length; + + //If value isn't null, get all of our required values. + if (value == null) { + if (index == 0 && count == 0) { + return this; + } + throw new ArgumentNullException("ArgumentNull_String"); + } + + //Range check the index. + if (index < 0 || index > currentLength) { + throw new ArgumentOutOfRangeException("index","ArgumentOutOfRange_Index"); + } + + if (count < 1) { + throw new ArgumentOutOfRangeException("count","ArgumentOutOfRange_GenericPositive"); + } + + //Calculate the new length, ensure that we have the space and set the space variable for this buffer + int requiredLength; + try { + requiredLength = checked(currentLength + (value.Length * count)); + } + catch (Exception) { + throw new OutOfMemoryException(); + } + + if (NeedsAllocation(currentString,requiredLength)) { + String newString = GetNewString(currentString,requiredLength); + newString.InsertInPlace(index, value, count, currentLength, requiredLength); + ReplaceString(tid,newString); + } + else { + currentString.InsertInPlace(index, value, count, currentLength, requiredLength); + ReplaceString(tid,currentString); + } + return this; + } + + + + // Property. + // Removes the specified characters from this string builder. + // The length of this string builder is reduced by + // length, but the capacity is unaffected. + // + //| + public StringBuilder Remove(int startIndex, int length) + { + // See also Lightning\Src\VM\COMStringBuffer.cpp::Remove + int tid; + String thisString = GetThreadSafeString(out tid); + int thisLength = thisString.ArrayLength-1; + if (length < 0) { + throw new ArgumentOutOfRangeException("Negative length"); + } + if (startIndex < 0) { + throw new ArgumentOutOfRangeException("Negative start index"); + } + if (startIndex + length > thisLength) { + throw new ArgumentOutOfRangeException("Exceeding string length"); + } + thisString.RemoveRange(startIndex, length); + this.m_StringValue = thisString; + this.m_currentThread = tid; + return this; + } + + // + // + // PUBLIC INSTANCE FUNCTIONS + // + // + + //====================================Append==================================== + // + //============================================================================== + // Appends a boolean to the end of this string builder. + // The capacity is adjusted as needed. + //| + public StringBuilder Append(bool value) { + return Append(value.ToString()); + } + + // Appends an sbyte to this string builder. + // The capacity is adjusted as needed. + //| + [CLSCompliant(false)] + public StringBuilder Append(sbyte value) { + return Append(value.ToString()); + } + + // Appends a ubyte to this string builder. + // The capacity is adjusted as needed. + //| + public StringBuilder Append(byte value) { + return Append(value.ToString()); + } + + // Appends a character at the end of this string builder. The capacity is adjusted as needed. + //| + public StringBuilder Append(char value) { + int tid; + + // hand inlining of GetThreadSafeString + String currentString = m_StringValue; + tid = InternalGetCurrentThread(); + if (m_currentThread != tid) + currentString = String.GetStringForStringBuilder(currentString, currentString.Capacity); + + int currentLength = currentString.Length; + if (!NeedsAllocation(currentString,currentLength + 1)) { + currentString.AppendInPlace(value,currentLength); + ReplaceString(tid,currentString); + return this; + } + + String newString = GetNewString(currentString,currentLength+1); + newString.AppendInPlace(value,currentLength); + ReplaceString(tid,newString); + return this; + } + + // Appends a short to this string builder. + // The capacity is adjusted as needed. + //| + public StringBuilder Append(short value) { + return Append(value.ToString()); + } + + // Appends an int to this string builder. + // The capacity is adjusted as needed. + //| + public StringBuilder Append(int value) { + return Append(value.ToString()); + } + + // Appends a long to this string builder. + // The capacity is adjusted as needed. + //| + public StringBuilder Append(long value) { + return Append(value.ToString()); + } + + // Appends a float to this string builder. + // The capacity is adjusted as needed. + //| + public StringBuilder Append(float value) { + return Append(value.ToString()); + } + + // Appends a double to this string builder. + // The capacity is adjusted as needed. + //| + public StringBuilder Append(double value) { + return Append(value.ToString()); + } + + //| + public StringBuilder Append(decimal value) { + return Append(value.ToString()); + } + + // Appends an ushort to this string builder. + // The capacity is adjusted as needed. + //| + [CLSCompliant(false)] + public StringBuilder Append(ushort value) { + return Append(value.ToString()); + } + + // Appends an uint to this string builder. + // The capacity is adjusted as needed. + //| + [CLSCompliant(false)] + public StringBuilder Append(uint value) { + return Append(value.ToString()); + } + + // Appends an unsigned long to this string builder. + // The capacity is adjusted as needed. + + //| + [CLSCompliant(false)] + public StringBuilder Append(ulong value) { + return Append(value.ToString()); + } + + // Appends an Object to this string builder. + // The capacity is adjusted as needed. + //| + public StringBuilder Append(Object value) { + if (null == value) { + //Appending null is now a no-op. + return this; + } + return Append(value.ToString()); + } + + // Appends all of the characters in value to the current instance. + //| + public StringBuilder Append(char[] value) { + if (null == value) { + return this; + } + + int valueLength = value.Length; + + int tid; + String currentString = GetThreadSafeString(out tid); + + int currentLength = currentString.Length; + int requiredLength = currentLength + value.Length; + if (NeedsAllocation(currentString,requiredLength)) { + String newString = GetNewString(currentString,requiredLength); + newString.AppendInPlace(value, 0, valueLength,currentLength); + ReplaceString(tid,newString); + } + else { + currentString.AppendInPlace(value, 0, valueLength, currentLength); + ReplaceString(tid,currentString); + } + return this; + } + + //====================================Insert==================================== + // + //============================================================================== + + // Returns a reference to the StringBuilder with ; value inserted into + // the buffer at index. Existing characters are shifted to make room for the new text. + // The capacity is adjusted as needed. If value equals String.Empty, the + // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value + // is null. + // + //| + public StringBuilder Insert(int index, String value) { + if (value == null) // This is to do the index validation + return Insert(index,value,0); + else + return Insert(index,value,1); + } + + // Returns a reference to the StringBuilder with ; value inserted into + // the buffer at index. Existing characters are shifted to make room for the new text. + // The capacity is adjusted as needed. If value equals String.Empty, the + // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value + // is null. + // + //| + public StringBuilder Insert( int index, bool value) { + return Insert(index,value.ToString(),1); + } + + // Returns a reference to the StringBuilder with ; value inserted into + // the buffer at index. Existing characters are shifted to make room for the new text. + // The capacity is adjusted as needed. If value equals String.Empty, the + // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value + // is null. + // + //| + [CLSCompliant(false)] + public StringBuilder Insert(int index, sbyte value) { + return Insert(index,value.ToString(),1); + } + + // Returns a reference to the StringBuilder with ; value inserted into + // the buffer at index. Existing characters are shifted to make room for the new text. + // The capacity is adjusted as needed. If value equals String.Empty, the + // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value + // is null. + // + //| + public StringBuilder Insert(int index, byte value) { + return Insert(index,value.ToString(),1); + } + + // Returns a reference to the StringBuilder with ; value inserted into + // the buffer at index. Existing characters are shifted to make room for the new text. + // The capacity is adjusted as needed. If value equals String.Empty, the + // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value + // is null. + // + //| + public StringBuilder Insert(int index, short value) { + return Insert(index,value.ToString(),1); + } + + // Returns a reference to the StringBuilder with ; value inserted into + // the buffer at index. Existing characters are shifted to make room for the new text. + // The capacity is adjusted as needed. If value equals String.Empty, the + // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value + // is null. + // + //| + public StringBuilder Insert(int index, char value) { + return Insert(index,Char.ToString(value),1); + } + + // Returns a reference to the StringBuilder with ; value inserted into + // the buffer at index. Existing characters are shifted to make room for the new text. + // The capacity is adjusted as needed. If value equals String.Empty, the + // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value + // is null. + // + //| + public StringBuilder Insert(int index, char[] value) { + if (null == value) { + return Insert(index, value, 0, 0); + } + return Insert(index, value, 0, value.Length); + } + + // Returns a reference to the StringBuilder with charCount characters from + // value inserted into the buffer at index. Existing characters are shifted + // to make room for the new text and capacity is adjusted as required. If value is null, the StringBuilder + // is unchanged. Characters are taken from value starting at position startIndex. + //| + public StringBuilder Insert(int index, char []value, int startIndex, int charCount) + { + throw new Exception("System.Text.StringBuilder.Insert not implemented in Bartok"); + } + + // Returns a reference to the StringBuilder with ; value inserted into + // the buffer at index. Existing characters are shifted to make room for the new text. + // The capacity is adjusted as needed. If value equals String.Empty, the + // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value + // is null. + // + //| + public StringBuilder Insert(int index, int value){ + return Insert(index,value.ToString(),1); + } + + // Returns a reference to the StringBuilder with ; value inserted into + // the buffer at index. Existing characters are shifted to make room for the new text. + // The capacity is adjusted as needed. If value equals String.Empty, the + // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value + // is null. + // + //| + public StringBuilder Insert(int index, long value) { + return Insert(index,value.ToString(),1); + } + + // Returns a reference to the StringBuilder with ; value inserted into + // the buffer at index. Existing characters are shifted to make room for the new text. + // The capacity is adjusted as needed. If value equals String.Empty, the + // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value + // is null. + // + //| + public StringBuilder Insert(int index, float value) { + return Insert(index,value.ToString(),1); + } + + + // Returns a reference to the StringBuilder with ; value inserted into + // the buffer at index. Existing characters are shifted to make room for the new text. + // The capacity is adjusted as needed. If value equals String.Empty, the + // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value + // is null. + // + //| + public StringBuilder Insert(int index, double value) { + return Insert(index,value.ToString(),1); + } + + //| + public StringBuilder Insert(int index, decimal value) { + return Insert(index,value.ToString(),1); + } + + // Returns a reference to the StringBuilder with value inserted into + // the buffer at index. Existing characters are shifted to make room for the new text. + // The capacity is adjusted as needed. + // + //| + [CLSCompliant(false)] + public StringBuilder Insert(int index, ushort value) { + return Insert(index, value.ToString(),1); + } + + + // Returns a reference to the StringBuilder with value inserted into + // the buffer at index. Existing characters are shifted to make room for the new text. + // The capacity is adjusted as needed. + // + //| + [CLSCompliant(false)] + public StringBuilder Insert(int index, uint value) { + return Insert(index, value.ToString(), 1); + } + + // Returns a reference to the StringBuilder with value inserted into + // the buffer at index. Existing characters are shifted to make room for the new text. + // The capacity is adjusted as needed. + // + //| + [CLSCompliant(false)] + public StringBuilder Insert(int index, ulong value) { + return Insert(index, value.ToString(), 1); + } + + // Returns a reference to this string builder with value inserted into + // the buffer at index. Existing characters are shifted to make room for the + // new text. The capacity is adjusted as needed. If value equals String.Empty, the + // StringBuilder is not changed. No changes are made if value is null. + // + //| + public StringBuilder Insert(int index, Object value) { + //If we get a null + if (null == value) { + return this; + } + return Insert(index,value.ToString(),1); + } + + //| + public StringBuilder AppendFormat(String format, Object arg0) { + return AppendFormat(format, new Object[] {arg0}); + } + + //| + public StringBuilder AppendFormat(String format, Object arg0, Object arg1) { + return AppendFormat(format, new Object[] {arg0, arg1}); + } + + //| + public StringBuilder AppendFormat(String format, Object arg0, Object arg1, Object arg2) { + return AppendFormat(format, new Object[] {arg0, arg1, arg2}); + } + + private static void FormatError() { + throw new FormatException("Format_InvalidString"); + } + + //| + public StringBuilder AppendFormat(String format, params Object[] args) { + if (format == null || args == null) { + throw new ArgumentNullException((format==null)?"format":"args"); + } + char[] chars = format.ToCharArray(0, format.Length); + int pos = 0; + int len = chars.Length; + char ch = '\x0'; + + while (true) { + int p = pos; + int i = pos; + while (pos < len) { + ch = chars[pos]; + + pos++; + if (ch == '}') { + if (pos < len && chars[pos] =='}') // Treat as escape character for }} + pos++; + else + FormatError(); + } + + if (ch == '{') { + if (pos < len && chars[pos] =='{') // Treat as escape character for {{ + pos++; + else { + pos--; + break; + } + } + + chars[i++] = ch; + } + if (i > p) Append(chars, p, i - p); + if (pos == len) break; + pos++; + if (pos == len || (ch = chars[pos]) < '0' || ch > '9') FormatError(); + int index = 0; + do { + index = index * 10 + ch - '0'; + pos++; + if (pos == len) FormatError(); + ch = chars[pos]; + } while (ch >= '0' && ch <= '9' && index < 1000000); + if (index >= args.Length) throw new FormatException("Format_IndexOutOfRange"); + while (pos < len && (ch = chars[pos]) == ' ') pos++; + bool leftJustify = false; + int width = 0; + if (ch == ',') { + pos++; + while (pos < len && chars[pos] == ' ') pos++; + + if (pos == len) FormatError(); + ch = chars[pos]; + if (ch == '-') { + leftJustify = true; + pos++; + if (pos == len) FormatError(); + ch = chars[pos]; + } + if (ch < '0' || ch > '9') FormatError(); + do { + width = width * 10 + ch - '0'; + pos++; + if (pos == len) FormatError(); + ch = chars[pos]; + } while (ch >= '0' && ch <= '9' && width < 1000000); + } + + while (pos < len && (ch = chars[pos]) == ' ') pos++; + Object arg = args[index]; + String fmt = null; + if (ch == ':') { + pos++; + p = pos; + i = pos; + while (true) { + if (pos == len) FormatError(); + ch = chars[pos]; + pos++; + if (ch == '{') { + if (pos < len && chars[pos] =='{') // Treat as escape character for {{ + pos++; + else + FormatError(); + } + else if (ch == '}') { + if (pos < len && chars[pos] =='}') // Treat as escape character for }} + pos++; + else { + pos--; + break; + } + } + + chars[i++] = ch; + } + if (i > p) fmt = new String(chars, p, i - p); + } + if (ch != '}') FormatError(); + pos++; + String s = null; + + if (s == null) { + if (arg is IFormattable) { + s = ((IFormattable)arg).ToString(fmt); + } + else if (arg != null) { + s = arg.ToString(); + } + } + + if (s == null) s = String.Empty; + int pad = width - s.Length; + if (!leftJustify && pad > 0) Append(' ', pad); + Append(s); + if (leftJustify && pad > 0) Append(' ', pad); + } + return this; + } + + // Returns a reference to the current StringBuilder with all instances of oldString + // replaced with newString. If startIndex and count are specified, + // we only replace strings completely contained in the range of startIndex to startIndex + + // count. The strings to be replaced are checked on an ordinal basis (e.g. not culture aware). If + // newValue is null, instances of oldValue are removed (e.g. replaced with nothing.). + // + //| + public StringBuilder Replace(String oldValue, String newValue) { + return Replace(oldValue, newValue, 0, Length); + } + + //| + public StringBuilder Replace(String oldValue, String newValue, int startIndex, int count) + { + throw new Exception("System.Text.StringBuilder.Replace not implemented in Bartok"); + } + + //| + public bool Equals(StringBuilder sb) + { + if (sb == null) + return false; + return ((this.Capacity == sb.Capacity) && (this.MaxCapacity == sb.MaxCapacity) && (this. m_StringValue.Equals(sb. m_StringValue))); + } + + // Returns a StringBuilder with all instances of oldChar replaced with + // newChar. The size of the StringBuilder is unchanged because we're only + // replacing characters. If startIndex and count are specified, we + // only replace characters in the range from startIndex to startIndex+count + // + //| + public StringBuilder Replace(char oldChar, char newChar) { + return Replace(oldChar, newChar, 0, Length); + } + //| + public StringBuilder Replace(char oldChar, char newChar, int startIndex, int count) { + int tid; + String currentString = GetThreadSafeString(out tid); + int currentLength = currentString.Length; + + if ((uint)startIndex > (uint)currentLength) { + throw new ArgumentOutOfRangeException("startIndex", "ArgumentOutOfRange_Index"); + } + + if (count < 0 || startIndex > currentLength - count) { + throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_Index"); + } + + if (!NeedsAllocation(currentString,currentLength)) { + currentString.ReplaceCharInPlace(oldChar, newChar, startIndex, count, currentLength); + ReplaceString(tid,currentString); + return this; + } + + String newString = GetNewString(currentString,currentLength); + newString.ReplaceCharInPlace(oldChar, newChar, startIndex, count, currentLength); + ReplaceString(tid,newString); + return this; + } + } +} + + + diff --git a/base/Kernel/System/Text/UTF8Encoding.cs b/base/Applications/Runtime/Full/System/Text/UTF8Encoding.cs similarity index 92% rename from base/Kernel/System/Text/UTF8Encoding.cs rename to base/Applications/Runtime/Full/System/Text/UTF8Encoding.cs index c6f18e2..ce821bf 100644 --- a/base/Kernel/System/Text/UTF8Encoding.cs +++ b/base/Applications/Runtime/Full/System/Text/UTF8Encoding.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System.Text { +namespace System.Text +{ using System; using System.Diagnostics; @@ -19,18 +20,18 @@ namespace System.Text { // switch the byte orderings. //| public class UTF8Encoding : Encoding { - /* - bytes bits UTF-8 representation - ----- ---- ----------------------------------- - 1 7 0vvvvvvv - 2 11 110vvvvv 10vvvvvv - 3 16 1110vvvv 10vvvvvv 10vvvvvv - 4 21 11110vvv 10vvvvvv 10vvvvvv 10vvvvvv - ----- ---- ----------------------------------- - - Surrogate: - Real Unicode value = (HighSurrogate - 0xD800) * 0x400 + (LowSurrogate - 0xDC00) + 0x10000 - */ + // + // bytes bits UTF-8 representation + // ----- ---- ----------------------------------- + // 1 7 0vvvvvvv + // 2 11 110vvvvv 10vvvvvv + // 3 16 1110vvvv 10vvvvvv 10vvvvvv + // 4 21 11110vvv 10vvvvvv 10vvvvvv 10vvvvvv + // ----- ---- ----------------------------------- +// + // Surrogate: + // Real Unicode value = (HighSurrogate - 0xD800) * 0x400 + (LowSurrogate - 0xDC00) + 0x10000 + // private const int UTF8_CODEPAGE=65001; @@ -89,7 +90,7 @@ namespace System.Text { //| public unsafe override int GetByteCount(String chars) { - if (chars==null) + if (chars == null) throw new ArgumentNullException("chars"); int retVal = -1; fixed (char *p = chars) { @@ -138,7 +139,8 @@ namespace System.Text { // Encode the previous high-surrogate char. byteCount += 3; // The isSurrogate is still true, because this could be the start of another valid surrogate pair. - } else { + } + else { if (isThrowException) { throw new ArgumentException("Argument_InvalidHighSurrogate"); } @@ -148,11 +150,13 @@ namespace System.Text { inSurrogate = false; index--; } - } else if (ch < 0x0080) + } + else if (ch < 0x0080) byteCount++; else if (ch < 0x0800) { byteCount += 2; - } else { + } + else { if (CharacterInfo.IsHighSurrogate(ch)) { // // Found the start of a surrogate. @@ -216,7 +220,7 @@ namespace System.Text { } int retVal = -1; - if (chars.Length==0) { + if (chars.Length == 0) { return 0; } fixed (char *p = chars) { @@ -299,7 +303,8 @@ namespace System.Text { bytes[byteIndex++] = (byte)(0x80 | (surrogateChar >> 6) & 0x3F); bytes[byteIndex++] = (byte)(0x80 | surrogateChar & 0x3F); surrogateChar = -1; - } else if (CharacterInfo.IsHighSurrogate(ch)) { + } + else if (CharacterInfo.IsHighSurrogate(ch)) { // We have two high surrogate. if (isThrowException) { throw new ArgumentException("Argument_InvalidHighSurrogate"); @@ -307,7 +312,8 @@ namespace System.Text { // Encode the previous high-surrogate char. EncodeThreeBytes(surrogateChar, bytes, ref byteIndex); surrogateChar = ch; - } else { + } + else { if (isThrowException) { throw new ArgumentException("Argument_InvalidHighSurrogate"); } @@ -318,19 +324,24 @@ namespace System.Text { surrogateChar = -1; charIndex--; } - } else if (ch < 0x0080) { + } + else if (ch < 0x0080) { bytes[byteIndex++] = (byte)ch; - } else if (ch < 0x0800) { + } + else if (ch < 0x0800) { bytes[byteIndex++] = (byte)(0xC0 | ch >> 6 & 0x1F); bytes[byteIndex++] = (byte)(0x80 | ch & 0x3F); - } else if (CharacterInfo.IsHighSurrogate(ch)) { + } + else if (CharacterInfo.IsHighSurrogate(ch)) { // // Found the start of a surrogate. // surrogateChar = ch; - } else if (CharacterInfo.IsLowSurrogate(ch) && isThrowException) { + } + else if (CharacterInfo.IsLowSurrogate(ch) && isThrowException) { throw new ArgumentException("Argument_InvalidLowSurrogate"); - } else { //we now know that the char is >=0x0800 and isn't a high surrogate + } + else { //we now know that the char is >= 0x0800 and isn't a high surrogate bytes[byteIndex++] = (byte)(0xE0 | ch >> 12 & 0x0F); bytes[byteIndex++] = (byte)(0x80 | ch >> 6 & 0x3F); bytes[byteIndex++] = (byte)(0x80 | ch & 0x3F); @@ -348,7 +359,8 @@ namespace System.Text { EncodeThreeBytes(surrogateChar, bytes, ref byteIndex); } } - } catch (IndexOutOfRangeException) { + } + catch (IndexOutOfRangeException) { throw new ArgumentException("Argument_ConversionOverflow"); } @@ -397,7 +409,8 @@ namespace System.Text { if ((b & 0x80) == 0) { // This is an ASCII. charCount++; - } else { + } + else { byte temp = b; while ((temp & 0x80) != 0) { temp <<= 1; @@ -429,12 +442,14 @@ namespace System.Text { if (isThrowException) { throw new ArgumentException("Argument_InvalidByteSequence"); } - } else { + } + else { bits = temp >> trailCount; trailCount--; } } - } else { + } + else { // We are expecting to see trailing bytes like 10vvvvvv if ((b & 0xC0) != 0x80) { // If not, this is NOT a valid sequence. @@ -444,7 +459,8 @@ namespace System.Text { index--; trailCount = 0; isSurrogate = false; - } else { + } + else { switch (byteSequence) { case 3: // Check 3-byte sequence for non-shortest form. @@ -469,7 +485,8 @@ namespace System.Text { } trailCount = -1; } - } else if ((bits & 0x04) != 0) { + } + else if ((bits & 0x04) != 0) { // Make sure that the resulting Unicode is within the valid surrogate range. // The 4 byte code sequence can hold up to 21 bits, and the maximum valid code point range // that Unicode (with surrogate) could represent are from U+000000 ~ U+10FFFF. @@ -517,8 +534,7 @@ namespace System.Text { throw new ArgumentOutOfRangeException((byteIndex<0 ? "byteIndex" : "byteCount"), "ArgumentOutOfRange_NeedNonNegNum"); } - if ( bytes.Length - byteIndex < byteCount) - { + if (bytes.Length - byteIndex < byteCount) { throw new ArgumentOutOfRangeException("bytes", "ArgumentOutOfRange_IndexCountBuffer"); } @@ -557,7 +573,8 @@ namespace System.Text { // Found an ASCII character. // chars[charIndex++] = (char)b; - } else { + } + else { // Check if this is a valid starting byte. byte temp = (byte)b; while ((temp & 0x80) != 0) { @@ -592,13 +609,15 @@ namespace System.Text { if (isThrowException) { throw new ArgumentException("Argument_InvalidByteSequence"); } - } else { + } + else { isSurrogate = (trailCount == 4); bits = temp >> trailCount; trailCount--; } } - } else { + } + else { // We are expecting to see bytes like 10vvvvvv if ((b & 0xC0) != 0x80) { // If not, this is NOT a valid sequence. @@ -613,7 +632,8 @@ namespace System.Text { byteIndex--; bits = 0; trailCount = 0; - } else { + } + else { switch (byteSequence) { case 3: // Check 3-byte sequence for non-shortest form. @@ -637,7 +657,8 @@ namespace System.Text { } trailCount = -1; } - } else if ((bits & 0x04) != 0) { + } + else if ((bits & 0x04) != 0) { // Make sure that the resulting Unicode is within the valid surrogate range. // The 4 byte code sequence can hold up to 21 bits, and the maximum valid code point range // that Unicode (with surrogate) could represent are from U+000000 ~ U+10FFFF. @@ -673,7 +694,8 @@ namespace System.Text { } } } - } catch (IndexOutOfRangeException) { + } + catch (IndexOutOfRangeException) { throw new ArgumentException("Argument_ConversionOverflow"); } if (decoder != null) { diff --git a/base/Kernel/System/Text/UnicodeEncoding.cs b/base/Applications/Runtime/Full/System/Text/UnicodeEncoding.cs similarity index 95% rename from base/Kernel/System/Text/UnicodeEncoding.cs rename to base/Applications/Runtime/Full/System/Text/UnicodeEncoding.cs index 24c5b2b..56f13ce 100644 --- a/base/Kernel/System/Text/UnicodeEncoding.cs +++ b/base/Applications/Runtime/Full/System/Text/UnicodeEncoding.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System.Text { +namespace System.Text +{ using System; //| public class UnicodeEncoding : Encoding @@ -53,7 +54,7 @@ namespace System.Text { //| public override int GetByteCount(String s) { - if (s==null) + if (s == null) throw new ArgumentNullException("s"); int byteCount = s.Length * CharSize; @@ -149,7 +150,7 @@ namespace System.Text { // @TODO: Consider pinning the String here and then using a managed // memcpy implementation (see VTable). This would // require a C# compiler that can get an interior pointer to a - // String, probably via the fixed statement. -- BrianGru, 12/6/2000 + // String, probably via the fixed statement. s.CopyToByteArray(charIndex, bytes, byteIndex, charCount); } return byteCount; @@ -165,8 +166,7 @@ namespace System.Text { throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), "ArgumentOutOfRange_NeedNonNegNum"); } - if ( bytes.Length - index < count) - { + if (bytes.Length - index < count) { throw new ArgumentOutOfRangeException("bytes", "ArgumentOutOfRange_IndexCountBuffer"); } @@ -185,8 +185,7 @@ namespace System.Text { throw new ArgumentOutOfRangeException((byteIndex<0 ? "byteIndex" : "byteCount"), "ArgumentOutOfRange_NeedNonNegNum"); } - if ( bytes.Length - byteIndex < byteCount) - { + if (bytes.Length - byteIndex < byteCount) { throw new ArgumentOutOfRangeException("bytes", "ArgumentOutOfRange_IndexCountBuffer"); } @@ -314,8 +313,7 @@ namespace System.Text { throw new ArgumentOutOfRangeException((byteIndex<0 ? "byteIndex" : "byteCount"), "ArgumentOutOfRange_NeedNonNegNum"); } - if (bytes.Length - byteIndex < byteCount) - { + if (bytes.Length - byteIndex < byteCount) { throw new ArgumentOutOfRangeException("bytes", "ArgumentOutOfRange_IndexCountBuffer"); } diff --git a/base/Applications/Runtime/System/Threading/AutoResetEvent.cs b/base/Applications/Runtime/Full/System/Threading/AutoResetEvent.cs similarity index 95% rename from base/Applications/Runtime/System/Threading/AutoResetEvent.cs rename to base/Applications/Runtime/Full/System/Threading/AutoResetEvent.cs index 39996fa..33b58c4 100644 --- a/base/Applications/Runtime/System/Threading/AutoResetEvent.cs +++ b/base/Applications/Runtime/Full/System/Threading/AutoResetEvent.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System.Threading { +namespace System.Threading +{ using System; using System.Runtime.CompilerServices; diff --git a/base/Kernel/System/Threading/HandleCreateException.cs b/base/Applications/Runtime/Full/System/Threading/HandleCreateException.cs similarity index 100% rename from base/Kernel/System/Threading/HandleCreateException.cs rename to base/Applications/Runtime/Full/System/Threading/HandleCreateException.cs diff --git a/base/Applications/Runtime/Full/System/Threading/Interlocked.cs b/base/Applications/Runtime/Full/System/Threading/Interlocked.cs new file mode 100644 index 0000000..a9dec5f --- /dev/null +++ b/base/Applications/Runtime/Full/System/Threading/Interlocked.cs @@ -0,0 +1,329 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +namespace System.Threading +{ + + using System; + using System.Runtime.CompilerServices; + +#if SINGULARITY_KERNEL + using Microsoft.Singularity.Memory; +#endif +#if ISA_ARM + using Microsoft.Singularity; + using Microsoft.Singularity.Isal; +#endif + + //| +#if ISA_ARM + [AccessedByRuntime("Routines defined in interlocked.cpp")] +#endif + public sealed class Interlocked + { + private Interlocked() { + } + + //| +#if ISA_IX + [Intrinsic] +#elif ISA_ARM + [AccessedByRuntime("defined in interlocked.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(64)] +#endif + [NoHeapAllocation] + public static extern int Increment(ref int location); + + //| +#if ISA_IX + [Intrinsic] +#elif ISA_ARM + [AccessedByRuntime("defined in interlocked.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(64)] +#endif + [NoHeapAllocation] + public static extern int Decrement(ref int location); + + //| + [NoHeapAllocation] + public static long Increment(ref long location) { + long value = location; + while (CompareExchange(ref location, value + 1, value) != value) { + value = location; + } + return value+1; + } + + //| + [NoHeapAllocation] + public static long Decrement(ref long location) { + long value = location; + while (CompareExchange(ref location, value - 1, value) != value) { + value = location; + } + return value-1; + } + + //| + [CLSCompliant(false)] + [NoHeapAllocation] + public static ulong Increment(ref ulong location) { + ulong value = location; + while (CompareExchange(ref location, value + 1, value) != value) { + value = location; + } + return value+1; + } + + //| + [CLSCompliant(false)] + [NoHeapAllocation] + public static ulong Decrement(ref ulong location) { + ulong value = location; + while (CompareExchange(ref location, value - 1, value) != value) { + value = location; + } + return value-1; + } + + [CLSCompliant(false)] + [NoHeapAllocation] + public static ulong Add(ref ulong location, ulong value) { + ulong tmp = location; + ulong newValue = tmp + value; + + while (CompareExchange(ref location, newValue, tmp) != tmp) { + tmp = location; + newValue = tmp + value; + } + return newValue; + } + + [CLSCompliant(false)] + [NoHeapAllocation] + public static ulong Subtract(ref ulong location, ulong value) { + ulong tmp = location; + ulong newValue = tmp - value; + + while (CompareExchange(ref location, newValue, tmp) != tmp) { + tmp = location; + newValue = tmp - value; + } + return newValue; + } + + //| +#if ISA_IX + [Intrinsic] +#elif ISA_ARM + [AccessedByRuntime("defined in interlocked.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(64)] +#endif + [NoHeapAllocation] + public static extern int Exchange(ref int location1, int value); + + + //| +#if ISA_IX + [Intrinsic] +#elif ISA_ARM + [AccessedByRuntime("defined in interlocked.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(64)] +#endif + [NoHeapAllocation] + public static extern int CompareExchange(ref int location1, int value, int comparand); + + //| +#if ISA_IX + [Intrinsic] +#elif ISA_ARM + [AccessedByRuntime("defined in interlocked.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(64)] +#endif + [NoHeapAllocation] + public static extern float Exchange(ref float location1, float value); + + + //| +#if ISA_IX + [Intrinsic] +#elif ISA_ARM + [AccessedByRuntime("defined in interlocked.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(64)] +#endif + [NoHeapAllocation] + public static extern float CompareExchange(ref float location1, float value, float comparand); + + // Added for thread initialization. +#if ISA_IX + [Intrinsic] +#elif ISA_ARM + [AccessedByRuntime("defined in interlocked.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(64)] +#endif + [NoHeapAllocation] + public static extern ThreadState CompareExchange(ref ThreadState location1, ThreadState value, ThreadState comparand); + + +#if ISA_IX + [Intrinsic] +#elif ISA_ARM + [AccessedByRuntime("defined in interlocked.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(64)] +#endif + [NoHeapAllocation] + public static extern long CompareExchange(ref long location1, long value, long comparand); + + [CLSCompliant(false)] +#if ISA_IX + [Intrinsic] +#elif ISA_ARM + [AccessedByRuntime("defined in interlocked.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(64)] +#endif + [NoHeapAllocation] + public static extern ulong CompareExchange(ref ulong location1, ulong value, ulong comparand); + + //| +#if ISA_IX + [Intrinsic] +#elif ISA_ARM + [AccessedByRuntime("defined in interlocked.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(64)] +#endif + [NoHeapAllocation] + public static extern Object Exchange(ref Object location1, Object value); + + //| +#if ISA_IX + [Intrinsic] +#elif ISA_ARM + [AccessedByRuntime("defined in interlocked.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(64)] +#endif + [NoHeapAllocation] + public static extern Object CompareExchange(ref Object location1, Object value, Object comparand); + + [CLSCompliant(false)] +#if ISA_IX + [Intrinsic] +#elif ISA_ARM + [AccessedByRuntime("defined in interlocked.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(64)] +#endif + [NoHeapAllocation] + public static extern uint Exchange(ref uint location1, uint value); + + [CLSCompliant(false)] +#if ISA_IX + [Intrinsic] +#elif ISA_ARM + [AccessedByRuntime("defined in interlocked.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(64)] +#endif + [NoHeapAllocation] + public static extern uint CompareExchange(ref uint location1, uint value, uint comparand); + + [CLSCompliant(false)] +#if ISA_IX + [Intrinsic] +#elif ISA_ARM + // [AccessedByRuntime("defined in interlocked.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(64)] +#endif + [NoHeapAllocation] + public static extern UIntPtr Exchange(ref UIntPtr location1, UIntPtr value); + + [CLSCompliant(false)] +#if ISA_IX + [Intrinsic] +#elif ISA_ARM + // [AccessedByRuntime("defined in interlocked.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(64)] +#endif + [NoHeapAllocation] + public static extern UIntPtr CompareExchange(ref UIntPtr location1, UIntPtr value, UIntPtr comparand); + + [CLSCompliant(false)] +#if ISA_IX + [Intrinsic] +#elif ISA_ARM + [AccessedByRuntime("defined in interlocked.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(64)] +#endif + [NoHeapAllocation] + public static extern unsafe UIntPtr Exchange(UIntPtr * location1, UIntPtr value); + + [CLSCompliant(false)] +#if ISA_IX + [Intrinsic] +#elif ISA_ARM + [AccessedByRuntime("defined in interlocked.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(64)] +#endif + [NoHeapAllocation] + public static extern unsafe UIntPtr CompareExchange(UIntPtr * location1, UIntPtr value, UIntPtr comparand); + + + [CLSCompliant(false)] +#if ISA_IX + [Intrinsic] +#elif ISA_ARM + [AccessedByRuntime("defined in interlocked.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(64)] +#endif + [NoHeapAllocation] + public static extern unsafe void * CompareExchange(ref void * location1, void * value, void * comparand); + +#if SINGULARITY_KERNEL + [CLSCompliant(false)] +#if ISA_IX + [Intrinsic] +#elif ISA_ARM + [AccessedByRuntime("defined in interlocked.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(64)] +#endif + [NoHeapAllocation] + internal static extern unsafe HandleTable.HandlePage * CompareExchange( + ref HandleTable.HandlePage * location1, + HandleTable.HandlePage * value, + HandleTable.HandlePage * comparand); + + [CLSCompliant(false)] +#if ISA_IX + [Intrinsic] +#elif ISA_ARM + [AccessedByRuntime("defined in interlocked.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(64)] +#endif + [NoHeapAllocation] + internal static extern unsafe HandleTable.HandleEntry * CompareExchange( + ref HandleTable.HandleEntry * location1, + HandleTable.HandleEntry * value, + HandleTable.HandleEntry * comparand); +#endif + + } +} diff --git a/base/Applications/Runtime/System/Threading/ManualResetEvent.cs b/base/Applications/Runtime/Full/System/Threading/ManualResetEvent.cs similarity index 95% rename from base/Applications/Runtime/System/Threading/ManualResetEvent.cs rename to base/Applications/Runtime/Full/System/Threading/ManualResetEvent.cs index 3452d2c..d0e999f 100644 --- a/base/Applications/Runtime/System/Threading/ManualResetEvent.cs +++ b/base/Applications/Runtime/Full/System/Threading/ManualResetEvent.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System.Threading { +namespace System.Threading +{ using System; using System.Runtime.CompilerServices; diff --git a/base/Applications/Runtime/Full/System/Threading/Monitor.cs b/base/Applications/Runtime/Full/System/Threading/Monitor.cs new file mode 100644 index 0000000..2aecbcb --- /dev/null +++ b/base/Applications/Runtime/Full/System/Threading/Monitor.cs @@ -0,0 +1,393 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +namespace System.Threading +{ + + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + using Microsoft.Bartok.Runtime; + + using Microsoft.Singularity; + + /// + /// A monitor is used for synchronization. Only a single thread can + /// hold the monitor at any given time. + /// + /// The monitor maintains two lists of threads: one for threads waiting + /// to enter the monitor, and one for threads waiting for a pulse within + /// the monitor. + /// + public sealed partial class Monitor { + /// + /// Private so that only we can create instances. + /// + internal Monitor() + { + this.mutex = new Mutex(); + this.depth = 0; + } + + /// + /// Wake up a thread waiting on the monitor. + /// + public static void Pulse(Object obj) + { + Monitor monitor = GetMonitorFromObject(obj); + monitor.Pulse(); + } + + /// + /// Wake up all threads waiting on the monitor. + /// + public static void PulseAll(Object obj) + { + Monitor monitor = GetMonitorFromObject(obj); + monitor.PulseAll(); + } + + /// + /// Attempt to enter the monitor, returning immediately if it is + /// already held by another thread. + /// + public static bool TryEnter(Object obj) + { + Monitor monitor = GetMonitorFromObject(obj); + return monitor.TryEnter(); + } + + /// + /// Attempt to enter the monitor, returning if it can not be taken + /// within the specified timeout. + /// + public static bool TryEnter(Object obj, TimeSpan timeout) + { + Monitor monitor = GetMonitorFromObject(obj); + return monitor.TryEnter(SchedulerTime.Now + timeout); + } + + /// + /// Wait to be woken up by a holder of the monitor. + /// + public static bool Wait(Object obj) + { + Monitor monitor = GetMonitorFromObject(obj); + return monitor.Wait(SchedulerTime.MaxValue); + } + + /// + /// Wait to be woken up by a holder of the monitor. Give up after + /// a specified timeout. + /// + public static bool Wait(Object obj, TimeSpan timeout) + { + Monitor monitor = GetMonitorFromObject(obj); + return monitor.Wait(SchedulerTime.Now + timeout); + } + + /// + /// Wait to be woken up by a holder of the monitor. Give up after + /// a specified timeout. + /// + /// Overload exists to match the CLR. Exit Context not supported. + /// + public static bool Wait(Object obj, + TimeSpan timeout, + bool exitContext) + { + if (exitContext) { + DebugStub.Break(); + throw new NotSupportedException("exitContext not supported!"); + } + Monitor monitor = GetMonitorFromObject(obj); + return monitor.Wait(SchedulerTime.Now + timeout); + } + + /// + /// Enter the monitor, blocking until it is held. + /// + internal void Enter() + { + TryEnter(SchedulerTime.MaxValue); + } + + /// + /// Exit the monitor. + /// + internal void Exit() + { + if (!mutex.IsOwnedByCurrentThread()) { + DebugStub.Break(); + throw new SynchronizationLockException("Monitor not held on Exit"); + } + + depth--; + if (depth == 0) { + mutex.ReleaseMutex(); + } + } + + /// + /// Wake up a single thread waiting on the monitor. + /// + internal void Pulse() + { + if (!mutex.IsOwnedByCurrentThread()) { + DebugStub.Break(); + throw new SynchronizationLockException("Monitor not held on Pulse"); + } + + // Wake up thread at the head of the wait list. + if (waitListHead != null) { + Thread t = Dequeue(); + if (t != null) { + t.nextThread = null; + t.SignalMonitor(); + } + } + } + + /// + /// Wake up all threads waiting on the monitor. + /// + internal void PulseAll() + { + if (!mutex.IsOwnedByCurrentThread()) { + DebugStub.Break(); + throw new SynchronizationLockException("Monitor not held on PulseAll"); + } + + // Wake up all threads the wait list. + if (waitListHead != null) { + Thread t = waitListHead; + while (t != null) { + Thread next = t.nextThread; + t.nextThread = null; + t.SignalMonitor(); + t = next; + } + waitListHead = null; + waitListTail = null; + } + } + + /// + /// Try to enter the monitor, returning immediately if it is + /// already held. + /// + internal bool TryEnter() + { + return TryEnter(new SchedulerTime(0)); + } + + /// + /// Try to enter the monitor, giving up if it cannot be + /// entered after a timeout. + /// + internal bool TryEnter(SchedulerTime stop) + { + if (mutex.IsOwnedByCurrentThread()) { + depth++; + return true; + } + + if (mutex.AcquireMutex(stop)) { + depth = 1; + return true; + } + return false; + } + + /// + /// Wait within the monitor for a Pulse. + /// + internal bool Wait(SchedulerTime stop) + { + Thread currentThread = Thread.CurrentThread; + if (!mutex.IsOwnedByCurrentThread()) { + DebugStub.Break(); + throw new SynchronizationLockException("Monitor not held on Wait"); + } + + int rememberedDepth = depth; + depth = 0; + + // Add me onto the waiting list. + Enqueue(currentThread); + + // Exit the monitor + mutex.ReleaseMutex(); + + // Wait + currentThread.WaitForMonitor(stop); + + // Re-enter the monitor + mutex.AcquireMutex(); + depth = rememberedDepth; + + bool success = !Remove(currentThread); + + if (!success && stop == SchedulerTime.MaxValue) { + VTable.DebugBreak(); + } + + return success; + } + + /// + /// Ensure that the passed object has a monitor (and associated + /// SyncBlock) allocated. + /// + internal static void CreateMonitor(Object obj) + { + GetMonitorFromObject(obj); + } + + // BUGBUG: The garbage collectors will not collect monitors that are + // in use. Use a very defensive strategy for now. + internal bool IsInUse() { + return true; + } + + /// + /// Internal Type conversion method. + /// Note: we don't use VTable.fromAddress because we + /// cannot do a checked cast from Object to Monitor during GC + /// (because the GC may be using the vtable word) + /// + /// + internal static Monitor FromAddress(UIntPtr v) { + return Magic.toMonitor(Magic.fromAddress(v)); + } + + /// + /// Look up the Monitor for the specified object in the SyncBlock + /// tables. If no Monitor exists for the object then one is created. + /// + private static Monitor GetMonitorFromObject(Object obj) + { + if (obj == null) { + DebugStub.Break(); + throw new ArgumentNullException("obj"); + } + Monitor result = MultiUseWord.GetMonitor(obj); + return result; + } + + ////////////////////////////////////////////////////////////////////// + // + // Linked list of threads waiting for a Pulse in a monitor. + private Thread waitListHead; + private Thread waitListTail; + + /// + /// Dequeue a thread from the singly linked list from head to tail, + /// acquiring the ListLock if necessary. + /// + /// If the list is empty then this method returns null. + /// + [Inline] + private Thread Dequeue() + { + Thread result; + if (waitListHead == null) { + // Empty list + result = null; + } + else if (waitListHead == waitListTail) { + // Single entry on list + VTable.Assert(waitListHead.nextThread == null); + result = waitListHead; + waitListHead = waitListTail = null; + } + else { + // Multiple entries on list + result = waitListHead; + waitListHead = waitListHead.nextThread; + } + return result; + } + + /// + /// Search the linked list and remove the specified thread if + /// it is linked in. + /// + /// Acquires the ListLock if necessary. + /// + [Inline] + private bool Remove(Thread target) + { + if (waitListHead == null) { + return false; + } + + if (waitListHead == waitListTail) { + // Single entry on list + VTable.Assert(waitListHead.nextThread == null); + + if (waitListHead != target) { + // Not on list + return false; + } + + waitListHead = waitListTail = null; + } + else if (waitListHead == target) { + // At waitListHead of list + waitListHead = target.nextThread; + target.nextThread = null; + } + else { + // Multiple entries on list + Thread next = waitListHead; + while (next != null && next.nextThread != target) { + next = next.nextThread; + } + + if (next == null) { + // Not on list + return false; + } + + if (waitListTail == target) { + // Update the waitListTail + waitListTail = next; + } + + next.nextThread = target.nextThread; + target.nextThread = null; + } + return true; + } + + /// + /// Append a thread at the tail of a queue. If the queue is + /// currently null this method initializes it. + /// + /// Acquires the ListLock if necessary. + /// + [Inline] + private void Enqueue(Thread target) + { + if (waitListHead == null) { + waitListHead = waitListTail = target; + } + else { + waitListTail.nextThread = target; + waitListTail = target; + } + } + + /// + /// The recursion depth of the current holder of the monitor. + /// + private int depth; + + /// + /// The mutex that is held by the thread that holds the monitor + /// + private Mutex mutex; + } +} diff --git a/base/Applications/Runtime/System/Threading/Mutex.cs b/base/Applications/Runtime/Full/System/Threading/Mutex.cs similarity index 100% rename from base/Applications/Runtime/System/Threading/Mutex.cs rename to base/Applications/Runtime/Full/System/Threading/Mutex.cs diff --git a/base/Applications/Runtime/Full/System/Threading/SynchronizationLockException.cs b/base/Applications/Runtime/Full/System/Threading/SynchronizationLockException.cs new file mode 100644 index 0000000..49a2788 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Threading/SynchronizationLockException.cs @@ -0,0 +1,39 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: SynchronizationLockException +// +// Purpose: Wait(), Notify() or NotifyAll() was called from an unsynchronized +// block of code. +// +//============================================================================= + +namespace System.Threading +{ + + using System; + + //| + public class SynchronizationLockException : SystemException { + //| + public SynchronizationLockException() + : base("Arg_SynchronizationLockException") { + } + + //| + public SynchronizationLockException(String message) + : base(message) { + } + + //| + public SynchronizationLockException(String message, Exception innerException) + : base(message, innerException) { + } + } +} + + diff --git a/base/Applications/Runtime/Full/System/Threading/Thread.cs b/base/Applications/Runtime/Full/System/Threading/Thread.cs new file mode 100644 index 0000000..6fba1e7 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Threading/Thread.cs @@ -0,0 +1,1296 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +// #define DEBUG_SWITCH + +namespace System.Threading +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.GCs; + using System.Globalization; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using System.Threading; + + using Microsoft.Bartok.Runtime; + + using Microsoft.Singularity; + using Microsoft.Singularity.Channels; + using Microsoft.Singularity.V1.Services; + using Microsoft.Singularity.V1.Threads; + + //| + public sealed partial class Thread + { + // Singularity specific fields + // Need CPU context + // Need stack + + internal AutoResetEvent processGcEvent; + + private AutoResetEvent autoEvent; + private ManualResetEvent joinEvent; + + internal Thread blockingCctorThread; + + // Singularity specific fields + [AccessedByRuntime("referenced from halforgc.asm")] + internal unsafe ThreadContext * context; + internal ThreadHandle threadHandle; + + // Most recently thrown exception object that the thread + // did not catch at all (i.e. that propagated to the bottom + // of the stack or to a kernel/process boundary without + // encountering an appropriate catch clause). + private Exception lastUncaughtException; + + private bool ignoredByJoinAll; // for "service" threads + + private Object m_ExceptionStateInfo; // Exception info latched to the thread on a thread abort + + // Bartok specific fields + internal Thread nextThread; // Link for linked lists of threads + + // This array is of length 1 and contains a queue item for this + // thread. It allows WaitOne and the scheduler to add this thread + // to their queues without allocating memory. + internal ThreadQueueItem[] singleQueueItem; + + // MultiUseWord (object header) fields. + internal UIntPtr externalMultiUseObjAllocListHead; + internal UIntPtr externalMultiUseObjAllocListTail; + + // Bartok specific fields + internal int threadIndex; + private ThreadStart threadStart; + private ThreadState threadState; + internal int waitingCriticalSectionDepth; + internal Object waitingObject; // Not used, but useful for debugging + + private static bool closing; + private static long totalArrayAllocations; + private static long totalArrayBytesAllocated; + private static long totalBytes; + private static long totalStructBytesAllocated; + + internal static Thread[] threadTable; + private static SpinLock threadTableLock; + + private static LocalDataStore localDataStore; + private object m_userScheduler; + + internal static Thread initialThread; + + //========================================================================= + // This manager is responsible for storing the global data that is + // shared amongst all the thread local stores. + //========================================================================= + static private LocalDataStoreMgr m_LocalDataStoreMgr; + + internal const int maxThreads = 1024; // Must be power of 2 >= 64 + private static int threadIndexGenerator; + + //========================================================================= + // Creates a new Thread object which will begin execution at + // start.ThreadStart on a new thread when the Start method is called. + // + // Exceptions: ArgumentNullException if start == null. + //========================================================================= + //| + public unsafe Thread(ThreadStart start) + { + Tracing.Log(Tracing.Audit, "Application Thread()"); + + if (start == null) { + throw new ArgumentNullException("start"); + } + + threadIndex = -1; + threadState = ThreadState.Unstarted; + threadStart = start; + + // Create the event for the thread to wait upon + autoEvent = new AutoResetEvent(false); + joinEvent = new ManualResetEvent(false); + processGcEvent = new AutoResetEvent(false); + singleQueueItem = new ThreadQueueItem [1] { new ThreadQueueItem(this) }; + + // Find a usable entry in the thread table + // Disable local preemption while holding the lock. This might also be a + // the property of the lock + + bool disabled = Processor.DisableLocalPreemption(); + Thread.threadTableLock.Acquire(CurrentThread); + try { + for (int i = 0; i < threadTable.Length; i++) { + int index = (threadIndexGenerator + i) % threadTable.Length; + if (threadTable[index] == null) { + threadTable[index] = this; + this.threadIndex = index; + threadIndexGenerator = index + 1; + // NB: We call this once, afterwards the GC visitor calls it. + break; + } + } + } + finally { + Thread.threadTableLock.Release(CurrentThread); + Processor.RestoreLocalPreemption(disabled); + } + + VTable.Assert(threadIndex >= 0, "Out of thread slots!"); + + //MemoryBarrier(); + + // Must check closing after being insert into table to avoid race condition. + if (closing) { + threadState = ThreadState.Stopped; + joinEvent.Set(); + Tracing.Log(Tracing.Warning, "Aborting: Runtime closing."); + return; + } + + ThreadHandle handleOnStack; + UIntPtr threadContext; + if (!ThreadHandle.Create(threadIndex, + new ContainerHandle(), + out handleOnStack, + out threadContext)) { + Tracing.Log(Tracing.Warning, "Aborting: ThreadHandle.Create failed."); + threadState = ThreadState.Stopped; + joinEvent.Set(); + return; + } + this.threadHandle = handleOnStack; + this.context = (ThreadContext *) threadContext; + this.context->threadIndex = unchecked((ushort) threadIndex); + this.context->UpdateAfterGC(this); + } + + + + /// + /// Finalizer is responsible for freeing handle that keeps corresponding + /// kernel object live. + /// + ~Thread() { + if (this.threadHandle.id != 0) { + ThreadHandle.Dispose(this.threadHandle); + this.threadHandle = new ThreadHandle(); + } + } + + //========================================================================= + // Spawns off a new thread which will begin executing at the ThreadStart + // method on the IThreadable interface passed in the constructor. Once the + // thread is dead, it cannot be restarted with another call to Start. + // + // Exceptions: ThreadStateException if the thread has already been started. + //========================================================================= + //| + public void Start() + { + lock ((Object) this) { + if (closing) { + throw new ThreadStateException("Cannot start thread when closing"); + } + + ThreadState oldState = threadState; + if (oldState != ThreadState.Unstarted) { + throw new ThreadStateException("Cannot start thread in state "+oldState); + } + + threadState = ThreadState.Running; + + // Tell the GC that we have created the thread + GC.NewThreadNotification(this, false); + + ThreadHandle.Start(threadHandle); + GC.KeepAlive(this); + } + } + + // HalInitContext sets ThreadStub as the first process code to be + // executed in a new thread context. + [AccessedByRuntime("referenced from hal.cpp")] + private static unsafe void ThreadStub(int threadIndex) + { + Transitions.ThreadStart(); + GC.ThreadStartNotification(threadIndex); + Thread currentThread = threadTable[threadIndex]; + if (AddThread(threadIndex)) { + // Log Thread Start. + Tracing.Log(Tracing.Audit, "ThreadStub(atid={0}) Entered", + (UIntPtr)unchecked((uint)threadIndex)); + + ThreadStart startFun = currentThread.threadStart; + + try { + startFun(); + } + catch (Exception uncaughtException) { + // Save Uncaught Exception in Thread instance. + currentThread.lastUncaughtException = uncaughtException; + + // Dump (possibly nested) Exceptions to the Debugger. + // Note: The first line identifies the outer exception, + // later lines identify the inner exceptions nesting level. + string formatString; + uint nestingLevel = 0; + Exception currentException = uncaughtException; + formatString = "Thread{0,3} Outer" + + " Exception: Type '{2}', Message '{3}'."; + while (currentException != null) { + DebugStub.WriteLine( + formatString, + __arglist(threadIndex, + nestingLevel, + currentException.GetType().ToString(), + currentException.Message)); + currentException = currentException.InnerException; + formatString = "Thread{0,3} Inner{1,2}" + + " Exception: Type '{2}', Message '{3}'."; + if (++nestingLevel > 16) break; + } + + // Assert (always asserts as exception is never null here). + string message = uncaughtException.Message; + if (message == null) message = String.Empty; + + VTable.Assert(uncaughtException == null, + "Thread " + threadIndex + + " failed with Exception Type '" + + uncaughtException.GetType().ToString() + + "', Message '" + message + "'."); + } + + // Log Thread Exit. + Tracing.Log(Tracing.Audit, "ThreadStub(atid={0}) Exiting", + (UIntPtr)unchecked((uint)threadIndex)); + } + + currentThread.joinEvent.Set(); + RemoveThread(threadIndex); + GC.DeadThreadNotification(currentThread); + + bool disabled = Processor.DisableLocalPreemption(); + // REVIEW: this is a dangerous locking strategy as the transition code + // may decide it needs to wake up another thread. + Thread.threadTableLock.Acquire(currentThread); + try { + Transitions.ThreadEnd(threadIndex); + // You may not do any calls out of proc after this point! + threadTable[threadIndex] = null; + Transitions.DeadThreadNotification(threadIndex); + } + finally { + Thread.threadTableLock.Release(currentThread); + Processor.RestoreLocalPreemption(disabled); + } + } + + //==================================================================== + // Support for service threads. Service threads should not count + // towards keeping a process alive. When all non-service threads + // have terminated, the service threads are asked to stop themselves + // so the process can terminate gracefully. + //=================================================================== + + // The number of non-service threads running in the process + private static int threadCount; + private static Object notificationTableObject; + private static Object[] notificationTable; + + public delegate void StopServiceNotice(); + + public void MakeServiceThread(StopServiceNotice notification) + { + Tracing.Log(Tracing.Audit, "MakeServiceThread {0}", + (UIntPtr) threadIndex); + if (this != Thread.CurrentThread) { + throw new Exception("Only the thread itself may call MakeServiceThread"); + } + if (notificationTable == null) { + int tableSize = threadTable.Length; + Interlocked.CompareExchange(ref notificationTableObject, + new Object[tableSize], + null); + notificationTable = (Object[]) notificationTableObject; + } + Tracing.Log(Tracing.Audit, " previous notification: {0:x8}", + Magic.addressOf(notificationTable[threadIndex])); + // BUGBUG: Should have been Interlocked.Exchange, but that + // doesn't work due to a Bartok codegen bug. + Object oldNotification = notificationTable[threadIndex]; + while (Interlocked.CompareExchange(ref notificationTable[threadIndex], notification, oldNotification) != oldNotification) { + oldNotification = notificationTable[threadIndex]; + } + if (oldNotification == null) { + // We made the thread a service thread for the first time. + if (Interlocked.Decrement(ref threadCount) == 0) { + NotifyServiceThreads(); + } + } + VTable.Assert(threadCount >= 0); + } + + public void ClearServiceThread(Thread thread) + { + Tracing.Log(Tracing.Audit, "ClearServiceThread"); + if (this != Thread.CurrentThread) { + throw new Exception("Only the thread itself may call ClearServiceThread"); + } + if (notificationTable == null) { + return; + } + if (Interlocked.Exchange(ref notificationTable[threadIndex], + null) != null) { + // We cleared the notification + Interlocked.Increment(ref threadCount); + } + VTable.Assert(threadCount >= 0); + } + + private static bool AddThread(int index) + { + Tracing.Log(Tracing.Audit, "AddThread {0} ({1})", + (UIntPtr) index, (UIntPtr) threadCount); + VTable.Assert(threadCount >= 0); + if (Interlocked.Increment(ref threadCount) == 1 && + notificationTable != null) { + // The thread was started after we started sending out + // notifications, so indicate that the thread should not + // really be started + return false; + } + else { + return true; + } + } + + internal static void RemoveThread(int index) + { + Tracing.Log(Tracing.Audit, "RemoveThread {0} ({1})", + (UIntPtr) index, (UIntPtr) threadCount); + if (Interlocked.Decrement(ref threadCount) == 0) { + NotifyServiceThreads(); + } + VTable.Assert(threadCount >= 0); + } + + private static void NotifyServiceThreads() + { + Tracing.Log(Tracing.Audit, "NotifyServiceThreads"); + if (notificationTable == null) { + return; + } + for (int i = 0; i < notificationTable.Length; i++) { + if (notificationTable[i] != null) { + Tracing.Log(Tracing.Audit, " Notifying thread {0}", + (UIntPtr) i); + ((StopServiceNotice)notificationTable[i])(); + } + } + } + + + //========================================================================= + // Returns true if the thread has been started and is not dead. + //========================================================================= + //| + public bool IsAlive { + [NoHeapAllocation] + get { return (threadState != ThreadState.Unstarted && + threadState != ThreadState.Stopped); } + } + + + //========================================================================= + // Waits for the thread to die. + // + // Exceptions: ThreadStateException if the thread has not been started yet. + //========================================================================= + //| + public void Join() + { + Join(SchedulerTime.MaxValue); + } + + //========================================================================= + // Waits for the thread to die or for timeout milliseconds to elapse. + // Returns true if the thread died, or false if the wait timed out. + // + // Exceptions: ArgumentException if timeout < 0. + // ThreadStateException if the thread has not been started yet. + //========================================================================= + //| + public bool Join(TimeSpan timeout) + { + if (threadState == ThreadState.Unstarted) { + throw new ThreadStateException(); + } + return joinEvent.WaitOne(timeout); + } + + public bool Join(SchedulerTime timeout) + { + if (threadState == ThreadState.Unstarted) { + throw new ThreadStateException(); + } + return joinEvent.WaitOne(timeout); + } + + internal static bool JoinAll() + { + // To avoid races, join all does the following: + // 1) Wait for all known peer threads to terminate. + // 2) Set the closing flag to disallow creating of new threads. + // 3) Wait for any threads that have started in the mean time. + + for (uint iteration = 0; iteration < 2; iteration++) { + for (int i = 0; i < threadTable.Length; i++) { + Thread thread = null; + + bool disabled = Processor.DisableLocalPreemption(); + Thread.threadTableLock.Acquire(CurrentThread); + try { + thread = threadTable[i]; + } + finally { + Thread.threadTableLock.Release(CurrentThread); + Processor.RestoreLocalPreemption(disabled); + } + + if (thread != null && + thread != CurrentThread && + thread.threadState != ThreadState.Unstarted && + !thread.ignoredByJoinAll) { + thread.Join(); + } + } + closing = true; + } + return true; + } + + //========================================================================= + // Suspends the current thread for timeout milliseconds. If timeout == 0, + // forces the thread to give up the remainder of its timeslice. + // + // Exceptions: ArgumentException if timeout < 0. + //========================================================================= + + public static void Sleep(int milliseconds) + { + Sleep(TimeSpan.FromMilliseconds(milliseconds)); + } + + //| + public static void Sleep(TimeSpan timeout) + { + ThreadHandle.Sleep(timeout); + } + + // wait for a length of time proportional to 'iterations'. Each iteration is should + // only take a few machine instructions. Calling this API is preferable to coding + // an explicit busy loop because the hardware can be informed that it is busy waiting. + + //| + [NoHeapAllocation] + public static void SpinWait(int iterations) + { + for (int i = iterations; i > 0; i--) { + // Ensure that the optimizer doesn't remove this + NativeNoOp(); + } + } + + /// + /// + /// Notify thread that it acquired spinlock of specific rank + /// + /// + /// Type of spinlock + /// + [NoHeapAllocation] + internal void NotifySpinLockAboutToAcquire(int type) + { + // Add rank verification and etc .. + } + + /// + /// + /// Notify thread that released a spinlock of specific rank + /// + /// + /// Type of spinlock + /// + [NoHeapAllocation] + internal void NotifySpinLockReleased(int type) + { + // Add rank verification and etc .. + } + + /// + /// + /// Given a thread id, return actual thread + /// + /// + /// Thread id + /// + [NoHeapAllocation] + internal static Thread GetThreadFromThreadId(int threadId) + { + // Assert preconditions: threadId has to be in range and thread can't be null + VTable.Assert(threadId < threadTable.Length); + VTable.Assert(threadTable[threadId] != null); + + return threadTable[threadId]; + } + + + [Intrinsic] + [NoHeapAllocation] + public static extern void NativeNoOp(); + + internal static int GetCurrentProcessIndex() { + return ProcessService.GetCurrentProcessId(); + } + + internal bool WaitForMonitor(SchedulerTime timeOut) + { + return autoEvent.WaitOne(timeOut); + } + + internal bool WaitForEvent(SchedulerTime timeOut) + { + DebugStub.Break(); + return autoEvent.WaitOne(timeOut); + } + + internal bool WaitForEvent(TimeSpan timeout) + { + DebugStub.Break(); + return autoEvent.WaitOne(timeout); + } + + internal static unsafe bool WaitForGCEvent(int currentThreadIndex) + { + AutoResetEvent are = + threadTable[currentThreadIndex].processGcEvent; + // BUGBUG: The restoration of the gcState should be taken + // care of by the compiler. + return are.WaitOneNoGC(); + } + + internal void SignalMonitor() + { + autoEvent.Set(); + } + + internal void SignalEvent() + { + DebugStub.Break(); + autoEvent.Set(); + } + + [Inline] + internal static void SignalGCEvent(int currentThreadIndex, + int threadIndex) + { + SignalGCEvent(threadIndex); + } + + internal static unsafe void SignalGCEvent(int threadIndex) + { + Thread thread = threadTable[threadIndex]; + if (thread == null) { + return; + } + thread.processGcEvent.SetNoGC(); + } + + //| + public static Thread CurrentThread + { + [NoHeapAllocation] + [NoStackLinkCheckTrans] + get { + return Processor.GetCurrentThread(); + } + } + + internal ThreadHandle Handle + { + [NoHeapAllocation] + get { return threadHandle; } + } + + [NoStackLinkCheckTrans] + [RequiredByBartok] + [NoHeapAllocation] + private static Thread GetCurrentThreadNative() + { + return Processor.GetCurrentThread(); + } + + [NoStackLinkCheckTrans] + [RequiredByBartok] + [NoHeapAllocation] + internal static int GetCurrentThreadIndex() + { + return Processor.GetCurrentThread().threadIndex; + } + + //========================================================================= + // Return the thread state as a consistent set of bits. This is more + // general then IsAlive or IsBackground. + //========================================================================= + //| + public ThreadState ThreadState + { + [NoHeapAllocation] + get { return threadState; } + } + + [NoHeapAllocation] + public int GetThreadId() + { + return threadIndex; + } + + public object UserScheduler + { + get { + return m_userScheduler; + } + set { + if (null != m_userScheduler) { + // TODO make this a security exception + throw new Exception("User scheduler may not be changed"); + } + m_userScheduler = value; + } + } + + public int Affinity + { + get { return ThreadHandle.GetAffinity(threadHandle); } + set { ThreadHandle.SetAffinity(threadHandle, value); } + } + + + //========================================================================= + // Allocates an un-named data slot. The slot is allocated on ALL the + // threads. + //========================================================================= + //| + public static LocalDataStoreSlot AllocateDataSlot() + { + return m_LocalDataStoreMgr.AllocateDataSlot(); + } + + //========================================================================= + // Allocates a named data slot. The slot is allocated on ALL the + // threads. Named data slots are "public" and can be manipulated by + // anyone. + //========================================================================= + //| + public static LocalDataStoreSlot AllocateNamedDataSlot(String name) + { + return m_LocalDataStoreMgr.AllocateNamedDataSlot(name); + } + + //========================================================================= + // Looks up a named data slot. If the name has not been used, a new slot is + // allocated. Named data slots are "public" and can be manipulated by + // anyone. + //========================================================================= + //| + public static LocalDataStoreSlot GetNamedDataSlot(String name) + { + return m_LocalDataStoreMgr.GetNamedDataSlot(name); + } + + //========================================================================= + // Frees a named data slot. The slot is allocated on ALL the + // threads. Named data slots are "public" and can be manipulated by + // anyone. + //========================================================================= + //| + public static void FreeNamedDataSlot(String name) + { + m_LocalDataStoreMgr.FreeNamedDataSlot(name); + } + + //========================================================================= + // Retrieves the value from the specified slot on the current thread. + //========================================================================= + //| + public static Object GetData(LocalDataStoreSlot slot) + { + m_LocalDataStoreMgr.ValidateSlot(slot); + + if (localDataStore != null) { + return localDataStore.GetData(slot); + } + return null; + } + + //========================================================================= + // Sets the data in the specified slot on the currently running thread. + //========================================================================= + //| + public static void SetData(LocalDataStoreSlot slot, Object data) + { + // Create new DLS if one hasn't been created for this thread. + if (localDataStore == null) { + localDataStore = m_LocalDataStoreMgr.CreateLocalDataStore(); + } + localDataStore.SetData(slot, data); + } + + //============================================================= + + internal Object ExceptionState + { + [NoHeapAllocation] + get { return m_ExceptionStateInfo;} + [NoHeapAllocation] + set { m_ExceptionStateInfo = value;} + } + + // + // This is just designed to prevent compiler warnings. + // This field is used from native, but we need to prevent the compiler warnings. + // +#if _DEBUG + private void DontTouchThis() + { + threadStart = null; + m_Priority = 0; + } +#endif + //========================================================================= + // Volatile Read & Write and MemoryBarrier methods. + // Provides the ability to read and write values ensuring that the values + // are read/written each time they are accessed. + //========================================================================= + + //| + [Intrinsic] + [NoHeapAllocation] + public static extern byte VolatileRead(ref byte address); + + //| + [Intrinsic] + [NoHeapAllocation] + public static extern short VolatileRead(ref short address); + + //| + [Intrinsic] + [NoHeapAllocation] + public static extern int VolatileRead(ref int address); + + //| + [Intrinsic] + [NoHeapAllocation] + public static extern long VolatileRead(ref long address); + + //| + [CLSCompliant(false)] + [Intrinsic] + [NoHeapAllocation] + public static extern sbyte VolatileRead(ref sbyte address); + + //| + [CLSCompliant(false)] + [Intrinsic] + [NoHeapAllocation] + public static extern ushort VolatileRead(ref ushort address); + + //| + [CLSCompliant(false)] + [Intrinsic] + [NoHeapAllocation] + public static extern uint VolatileRead(ref uint address); + + //| + [Intrinsic] + [NoHeapAllocation] + public static extern IntPtr VolatileRead(ref IntPtr address); + + //| + [CLSCompliant(false)] + [Intrinsic] + [NoHeapAllocation] + public static extern UIntPtr VolatileRead(ref UIntPtr address); + + //| + [CLSCompliant(false)] + [Intrinsic] + [NoHeapAllocation] + public static extern ulong VolatileRead(ref ulong address); + + //| + [Intrinsic] + [NoHeapAllocation] + public static extern float VolatileRead(ref float address); + + //| + [Intrinsic] + [NoHeapAllocation] + public static extern double VolatileRead(ref double address); + + //| + [Intrinsic] + [NoHeapAllocation] + public static extern Object VolatileRead(ref Object address); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern byte VolatileReadUnsafe(byte* address); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern short VolatileReadUnsafe(short* address); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern int VolatileReadUnsafe(int* address); + + //| + [Intrinsic] + [NoHeapAllocation] + public static extern void VolatileWrite(ref byte address, byte value); + + //| + [Intrinsic] + [NoHeapAllocation] + public static extern void VolatileWrite(ref short address, short value); + + //| + [Intrinsic] + [NoHeapAllocation] + public static extern void VolatileWrite(ref int address, int value); + + //| + [Intrinsic] + [NoHeapAllocation] + public static extern void VolatileWrite(ref long address, long value); + + //| + [CLSCompliant(false)] + [Intrinsic] + [NoHeapAllocation] + public static extern void VolatileWrite(ref sbyte address, sbyte value); + + //| + [CLSCompliant(false)] + [Intrinsic] + [NoHeapAllocation] + public static extern void VolatileWrite(ref ushort address, ushort value); + + //| + [CLSCompliant(false)] + [Intrinsic] + [NoHeapAllocation] + public static extern void VolatileWrite(ref uint address, uint value); + + //| + [Intrinsic] + [NoHeapAllocation] + public static extern void VolatileWrite(ref IntPtr address, IntPtr value); + + //| + [CLSCompliant(false)] + [Intrinsic] + [NoHeapAllocation] + public static extern void VolatileWrite(ref UIntPtr address, UIntPtr value); + + //| + [CLSCompliant(false)] + [Intrinsic] + [NoHeapAllocation] + public static extern void VolatileWrite(ref ulong address, ulong value); + + //| + [Intrinsic] + [NoHeapAllocation] + public static extern void VolatileWrite(ref float address, float value); + + //| + [Intrinsic] + [NoHeapAllocation] + public static extern void VolatileWrite(ref double address, double value); + + //| + [Intrinsic] + [NoHeapAllocation] + public static extern void VolatileWrite(ref Object address, Object value); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern void VolatileWriteUnsafe(int* address, + int value); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern void VolatileWriteUnsafe(short* address, + short value); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern void VolatileWriteUnsafe(byte* address, + byte value); + + //| + [Intrinsic] + [NoHeapAllocation] + public static extern void MemoryBarrier(); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [GCAnnotation(GCOption.NOGC)] + [StackBound(32)] + [NoHeapAllocation] + private static unsafe extern Thread HalGetThread(); + + [NoHeapAllocation] + public static void Yield() + { + ThreadHandle.Yield(); + } + + [NoHeapAllocation] + public bool IsStopped() + { + return (threadState == ThreadState.Stopped); + } + + [PreInitRefCounts] + static unsafe Thread() + { + threadIndexGenerator = 1; + + // Enable Thread.CurrentThread as soon as we can! + initialThread = Magic.toThread(BootstrapMemory.Allocate(typeof(Thread))); + initialThread.threadState = ThreadState.Running; + initialThread.threadIndex = 0; + + // Allocate tables for thread management + threadTable = (Thread[]) + BootstrapMemory.Allocate(typeof(Thread[]), maxThreads); + + // Initialize the thread and event tables + threadTable[initialThread.threadIndex] = initialThread; + initialThread.context = Processor.GetCurrentThreadContext(); + initialThread.context->threadIndex = + unchecked((ushort) initialThread.threadIndex); + initialThread.context->UpdateAfterGC(initialThread); + + Tracing.Log(Tracing.Debug, "InitialThread={0:x8}", + Magic.addressOf(initialThread)); + } + + internal static unsafe void FinishInitializeThread() + { + int threadIndex = initialThread.threadIndex; + // Get the GC ready for initialThread + Transitions.RuntimeInitialized(); + Transitions.ThreadStart(); + initialThread.processGcEvent = new AutoResetEvent(false); + initialThread.autoEvent = new AutoResetEvent(false); + initialThread.joinEvent = new ManualResetEvent(false); + initialThread.singleQueueItem = + new ThreadQueueItem [1] { new ThreadQueueItem(initialThread) }; + // Use CurrentThread to find our initial handle: + VTable.Assert(initialThread == CurrentThread); + initialThread.threadHandle = ThreadHandle.CurrentThread(); + // Instantiate the static variable that needs to be initialized + m_LocalDataStoreMgr = new LocalDataStoreMgr(); + AddThread(threadIndex); + } + + public TimeSpan ExecutionTime + { + get { + TimeSpan t = ThreadHandle.GetExecutionTime(threadHandle); + GC.KeepAlive(this); + return t; + } + } + + internal static + void VisitBootstrapData(GCs.ReferenceVisitor visitor) + { + visitor.VisitReferenceFields(Thread.initialThread); + visitor.VisitReferenceFields(Thread.threadTable); + } + + internal static unsafe void UpdateAfterGC() + { + // Update all the thread pointers in the thread contexts + for (int i = 0; i < threadTable.Length; i++) { + Thread thread = threadTable[i]; + if (thread != null) { + thread.context->UpdateAfterGC(thread); + } + } + } + + // Cache for ABI synchronization + private SyncHandle[] syncHandles; + internal SyncHandle[] GetSyncHandles(int length) + { + if (syncHandles == null || + syncHandles.Length < length) { + + syncHandles = new SyncHandle[length + 8]; + } + return syncHandles; + } + + + // Caches for Select synchronization + // We use stacks, because selectable abstractions might + // internally implement HeadMatches using select receive + // which is called from within an outer select. + // NOTE however that internal selects should never block + // (use timeout) + private Stack selectBoolsStack; + private Stack selectObjectsStack; + private Stack selectSyncHandlesStack; + + public bool[] PopSelectBools(int size) + { + if (selectBoolsStack == null) { + selectBoolsStack = new Stack(); + } + if (selectBoolsStack.Count == 0) { + return new bool [size]; + } + bool[] selectBools = (bool[])selectBoolsStack.Pop(); + if (selectBools.Length < size) { + return new bool [size]; + } + return selectBools; + } + + public void PushSelectBools(bool[] cache) { + selectBoolsStack.Push(cache); + } + + public ISelectable[] PopSelectObjects(int size) + { + if (selectObjectsStack == null) { + selectObjectsStack = new Stack(); + } + if (selectObjectsStack.Count == 0) { + return new ISelectable [size]; + } + ISelectable[] selectObjects = (ISelectable[])selectObjectsStack.Pop(); + if (selectObjects.Length < size) { + return new ISelectable [size]; + } + return selectObjects; + } + public void PushSelectObjects(ISelectable[] cache) { + for (int i=0; ikernelMarkers; + CallStack.TransitionRecord *secondMarker = context->stackMarkers; + UIntPtr topMarkerPtr = (UIntPtr) topMarker; + // If the top marker is in our frame, we've reached a boundary: + if (esp < topMarkerPtr && topMarkerPtr <= ebp) { + context->uncaughtFlag = true; + Thread.CurrentThread.lastUncaughtException = exn; + Processor.GetCurrentThreadContext()->SetKernelMode(); + return 1; + } + else { + return 0; + } + } + + // Most recently thrown exception object that the thread + // did not catch at all (i.e. that propagated to the bottom + // of the stack without encountering an appropriate catch clause). + public Exception LastUncaughtException + { + [NoHeapAllocation] + get { + return lastUncaughtException; + } + } + + // Tell JoinAll not to block waiting for this thread to exit. + // Some special threads (e.g. the finalizer thread) need to run + // only as long as there are other threads running, and should + // not be considered by JoinAll. + [NoHeapAllocation] + internal void SetIgnoredByJoinAll() + { + ignoredByJoinAll = true; + } + +#if DEBUG || true + string debugName; + public string DebugName + { + [NoHeapAllocation] + get { return debugName; } + + [NoHeapAllocation] + set { debugName = value; } + } +#endif + } + + // This class is designed to support queues whose enqueue, + // dequeue, and remove operations do not allocate memory. + // This feature is useful when writing code that needs to + // do such operations with interrupts off. + [CLSCompliant(false)] + public class ThreadQueue + { + private ThreadQueueItem head = null; + private ThreadQueueItem tail = null; + + [NoHeapAllocation] + public void Enqueue(ThreadQueueItem item) + { + VTable.Assert(item.Next == null); + VTable.Assert(item.Prev == null); + VTable.Assert(item.Queue == null); + + item.Queue = this; + item.Prev = tail; + + if (tail != null) { + VTable.Assert(tail.Next == null); + tail.Next = item; + } + else { + VTable.Assert(head == null); + head = item; + } + + tail = item; + } + + [NoHeapAllocation] + public ThreadQueueItem Dequeue() + { + ThreadQueueItem item = head; + + if (item != null) { + Remove(item); + } + + return item; + } + + [NoHeapAllocation] + public void Remove(ThreadQueueItem item) + { + VTable.Assert(item.Queue == this); + + if (item.Next != null) { + item.Next.Prev = item.Prev; + } + else { + VTable.Assert(item == tail); + tail = item.Prev; + } + + if (item.Prev != null) { + item.Prev.Next = item.Next; + } + else { + VTable.Assert(item == head); + head = item.Next; + } + + item.Next = null; + item.Prev = null; + item.Queue = null; + } + + [NoHeapAllocation] + public bool IsEmpty() + { + return (head == null); + } + } + + [CLSCompliant(false)] + public class ThreadQueueItem + { + public readonly Thread Thread = null; + + public ThreadQueueItem Next = null; + public ThreadQueueItem Prev = null; + + public ThreadQueue Queue = null; + + public ThreadQueueItem(Thread thread) + { + Thread = thread; + } + + [NoHeapAllocation] + public void Remove() + { + if (Queue != null) { + Queue.Remove(this); + } + + VTable.Assert(Next == null); + VTable.Assert(Prev == null); + VTable.Assert(Queue == null); + } + } +} diff --git a/base/Applications/Runtime/Full/System/Threading/ThreadState.cs b/base/Applications/Runtime/Full/System/Threading/ThreadState.cs new file mode 100644 index 0000000..bcca2bd --- /dev/null +++ b/base/Applications/Runtime/Full/System/Threading/ThreadState.cs @@ -0,0 +1,59 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: ThreadState +// +// Purpose: Enum to represent the different thread states +// +//============================================================================= + +#if ISA_ARM +using System.Runtime.CompilerServices; +#endif + +namespace System.Threading +{ + + /// + /// + /// Scheduler thread state + /// + /// +#if ISA_ARM + [AccessedByRuntime("Referenced in interlocked.cpp")] +#endif + public enum ThreadState:byte + { + Undefined = 0x0, + Running = 0x1, + Unstarted = 0x2, + Stopped = 0x4, + Suspended = 0x8, + Blocked = 0x10, + Runnable = 0x20, + } + + /// + /// + /// Scheduler thread's actions + /// + /// + public enum ThreadActions + { + Undefined = 0x0, + Run = 0x1, + Unstart = 0x2, + Stop = 0x4, + Suspend = 0x8, + Block = 0x10, + MakeRunnable = 0x20, + Freeze = 0x40, + UnFreeze = 0x80, + DelayAbort = 0x100, + UndoDelayAbort = 0x200 + } +} diff --git a/base/Applications/Runtime/Full/System/Threading/ThreadStateException.cs b/base/Applications/Runtime/Full/System/Threading/ThreadStateException.cs new file mode 100644 index 0000000..cecc158 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Threading/ThreadStateException.cs @@ -0,0 +1,36 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: ThreadStateException +// +// Purpose: An exception class to indicate that the Thread class is in an +// invalid state for the method. +// +//============================================================================= + +namespace System.Threading +{ + using System; + + //| + public class ThreadStateException : SystemException { + //| + public ThreadStateException() + : base("Arg_ThreadStateException") { + } + + //| + public ThreadStateException(String message) + : base(message) { + } + + //| + public ThreadStateException(String message, Exception innerException) + : base(message, innerException) { + } + } +} diff --git a/base/Applications/Runtime/Full/System/Threading/Timeout.cs b/base/Applications/Runtime/Full/System/Threading/Timeout.cs new file mode 100644 index 0000000..ba4f4fc --- /dev/null +++ b/base/Applications/Runtime/Full/System/Threading/Timeout.cs @@ -0,0 +1,26 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace System.Threading +{ + using System.Threading; + using System; + // A constant used by methods that take a timeout (Object.Wait, Thread.Sleep + // etc) to indicate that no timeout should occur. + // + // @todo: this should become an enum. + //| + public sealed class Timeout + { + private Timeout() + { + } + + //| + public static readonly TimeSpan Infinite = TimeSpan.Infinite; + } + +} diff --git a/base/Applications/Runtime/Full/System/Threading/UserSpinlock.cs b/base/Applications/Runtime/Full/System/Threading/UserSpinlock.cs new file mode 100644 index 0000000..463f10b --- /dev/null +++ b/base/Applications/Runtime/Full/System/Threading/UserSpinlock.cs @@ -0,0 +1,265 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// User spinlock functionality +// +using System; +using Microsoft.Singularity; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace System.Threading +{ + [NoCCtor] + [CLSCompliant(false)] + [AccessedByRuntime("referenced from halidt.asm")] + public struct SpinLock + { + /// + /// + /// Acquire spinlock + /// + /// + [NoHeapAllocation] + [Inline] + public void Acquire() + { + Thread thread = Thread.CurrentThread; + AcquireInternal(thread, thread.GetThreadId()); + } + + /// + /// + /// Acquire spinlock + /// + /// + /// Thread's Id acquiring spinlock + /// + [NoHeapAllocation] + [Inline] + public void Acquire(int threadId) + { + AcquireInternal(Thread.GetThreadFromThreadId(threadId), threadId); + } + + /// + /// + /// Acquire spinlock + /// + /// + /// Thread acquiring spinlock + /// + [NoHeapAllocation] + public void Acquire(Thread thread) + { + AcquireInternal(thread, thread.GetThreadId()); + } + + /// + /// + /// Release spinlock + /// + /// + [NoHeapAllocation] + public void Release() + { + Thread thread = Thread.CurrentThread; + + // Release spinlock + ReleaseInternal(thread, thread.GetThreadId()); + } + + /// + /// + /// Release spinlock + /// + /// + /// Thread's Id releasing spinlock + /// + [NoHeapAllocation] + public void Release(int threadId) + { + // Release spinlock + ReleaseInternal(Thread.GetThreadFromThreadId(threadId), threadId); + } + + /// + /// + /// Release spinlock + /// + /// + /// Thread releasing spinlock + /// + [NoHeapAllocation] + public void Release(Thread thread) + { + // Release spinlock + ReleaseInternal(thread, thread.GetThreadId()); + } + + /// + /// + /// Try to acquire the spin lock. Always return immediately. + /// + /// + /// true if the spin lock is acquired. + [NoHeapAllocation] + public bool TryAcquire() + { + Thread thread = Thread.CurrentThread; + return TryAcquireInternal(thread, thread.GetThreadId()); + } + + /// + /// + /// Try to acquire the spin lock. Always return immediately. + /// + /// + /// true if the spin lock is acquired. + /// + /// Thread acquiring spinlock + /// + + [NoHeapAllocation] + public bool TryAcquire(Thread thread) + { + int threadId = thread.GetThreadId(); + + return TryAcquireInternal(thread, thread.GetThreadId()); + } + + /// + /// + /// Method to find out if spinlock is held by specified thread + /// + /// true if the spin lock is acquired. + /// + /// Thread to verify possible spinlock's ownership + /// + [NoHeapAllocation] + public bool IsHeldBy(Thread thread) + { + return baseLock.IsHeldBy(thread.GetThreadId()+1); + } + + /// + /// + /// Method to find out if spinlock is held by specified thread + /// + /// true if the spin lock is acquired. + /// + /// Thread's Id to verify possible spinlock's ownership + /// + [NoHeapAllocation] + public bool IsHeldBy(int threadId) + { + return baseLock.IsHeldBy(threadId+1); + } + + /// + /// + /// Assert thatf spinlock is held by specified thread + /// + /// + /// Thread to verify possible spinlock's ownership + /// + [System.Diagnostics.Conditional("DEBUG")] + [NoHeapAllocation] + public void AssertHeldBy(Thread thread) + { + VTable.Assert(IsHeldBy(thread)); + } + + /// + /// + /// Assert thatf spinlock is held by specified thread + /// + /// + /// Thread's Id to verify possible spinlock's ownership + /// + [System.Diagnostics.Conditional("DEBUG")] + [NoHeapAllocation] + public void AssertHeldBy(int threadId) + { + VTable.Assert(IsHeldBy(threadId)); + } + + /// + /// + /// Try to acquire the spin lock. Always return immediately. + /// + /// true if the spin lock is acquired. + /// + /// Thread acquiring spinlock + /// Thread's Id acquiring spinlock + /// + [NoHeapAllocation] + private bool TryAcquireInternal(Thread thread, int threadId) + { + bool result; + + VTable.Assert(thread != null); + + // Notify thread that we are about to acquire spinlock + thread.NotifySpinLockAboutToAcquire(this.baseLock.Type); + + result = baseLock.TryAcquire(threadId+1); + + // If we didn't acquire spinlock -we should notify thread about it: Just use release + // notification + if (!result) { + thread.NotifySpinLockReleased(this.baseLock.Type); + } + + return result; + } + + /// + /// + /// Acquire the spin lock. + /// + /// + /// Thread acquiring spinlock + /// Thread's Id acquiring spinlock + /// + [NoHeapAllocation] + private void AcquireInternal(Thread thread, int threadId) + { + VTable.Assert(thread != null); + + // Thread has to be notified if we are about to acquire spinlock + thread.NotifySpinLockAboutToAcquire(this.baseLock.Type); + + // Get lock + baseLock.Acquire(threadId+1); + } + + /// + /// + /// Release the spin lock. + /// + /// + /// Thread releasing spinlock + /// Thread's Id releasing spinlock + /// + [NoHeapAllocation] + private void ReleaseInternal(Thread thread, int threadId) + { + // Assert preconditions: Thread can't be null + VTable.Assert(thread != null); + + // Release spinlock + baseLock.Release(threadId+1); + + // Don't forget to notify thread that it just released spinlock + thread.NotifySpinLockReleased(this.baseLock.Type); + } + + /// Actual mechanism implementing spinlock + private SpinLockBase baseLock; + } +} diff --git a/base/Applications/Runtime/Full/System/Threading/WaitHandle.cs b/base/Applications/Runtime/Full/System/Threading/WaitHandle.cs new file mode 100644 index 0000000..269147e --- /dev/null +++ b/base/Applications/Runtime/Full/System/Threading/WaitHandle.cs @@ -0,0 +1,145 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: WaitHandle (this name is NOT definitive) +// +// Purpose: Class to represent all synchronization objects in the runtime (that allow multiple wait) +// +//============================================================================= + +namespace System.Threading +{ + using System; + using System.Threading; + using System.Runtime.CompilerServices; + using System.Collections; + using Microsoft.Singularity; + using Microsoft.Singularity.V1.Threads; + using Microsoft.Singularity.V1.Services; + + //| + [CLSCompliant(false)] + public abstract class WaitHandle : IDisposable + { + //| + public const int WaitTimeout = -1; + + protected WaitHandle() + { + } + + protected abstract SyncHandle Handle{ + get; + } + + //| + public abstract bool WaitOne(TimeSpan timeout); + + //| + public abstract bool WaitOne(SchedulerTime stop); + + [Obsolete("Do not use DateTime for scheduling. It is meaningless. Use SchedulerTime")] + public bool WaitOne(DateTime stop) { + SchedulerTime st = SchedulerTime.MinValue + (stop - DateTime.BootTime); + return WaitOne(st); + } + + //| + public abstract bool WaitOne(); + + //| + public static unsafe int WaitAny(WaitHandle[] waitHandles, + TimeSpan timeout) + { + SyncHandle[] handles = Thread.CurrentThread.GetSyncHandles(waitHandles.Length); + for (int i = 0; i < waitHandles.Length; i++) { + handles[i] = waitHandles[i].Handle; + } + fixed (SyncHandle *array = &handles[0]) { + return SyncHandle.WaitAny(array, waitHandles.Length, timeout); + } + } + + //| + public static unsafe int WaitAny(WaitHandle[] waitHandles, + SchedulerTime stop) + { + SyncHandle[] handles = Thread.CurrentThread.GetSyncHandles(waitHandles.Length); + for (int i = 0; i < waitHandles.Length; i++) { + handles[i] = waitHandles[i].Handle; + } + fixed (SyncHandle *array = &handles[0]) { + return SyncHandle.WaitAny(array, waitHandles.Length, stop); + } + } + + //| + public static unsafe int WaitAny(WaitHandle[] waitHandles) + { + SyncHandle[] handles = Thread.CurrentThread.GetSyncHandles(waitHandles.Length); + for (int i = 0; i < waitHandles.Length; i++) { + handles[i] = waitHandles[i].Handle; + } + fixed (SyncHandle *array = &handles[0]) { + return SyncHandle.WaitAny(array, waitHandles.Length); + } + } + + //| + protected abstract void Dispose(bool explicitDisposing); + + //| + public virtual void Close() + { + Dispose(true); + GC.nativeSuppressFinalize(this); + } + + //| + /// + void IDisposable.Dispose() + { + Dispose(true); + GC.nativeSuppressFinalize(this); + } + + //| + ~WaitHandle() + { + Dispose(false); + } + + public static implicit operator WaitHandle(SyncHandle s) { + return new SyncWaitHandle(s); + } + + } + + internal class SyncWaitHandle : WaitHandle + { + private readonly SyncHandle _sync; + public SyncWaitHandle(SyncHandle s) { + _sync = s; + } + protected override SyncHandle Handle { + get { return _sync; } + } + public override bool WaitOne(TimeSpan timeout) { + return SyncHandle.WaitOne(Handle, timeout); + } + public override bool WaitOne(SchedulerTime stop) { + return SyncHandle.WaitOne(Handle, stop); + } + public override bool WaitOne() { + return SyncHandle.WaitOne(Handle); + } + protected override void Dispose(bool explicitDisposing) { + // todo: implement + } + } + +} diff --git a/base/Kernel/System/TimeSpan.cs b/base/Applications/Runtime/Full/System/TimeSpan.cs similarity index 89% rename from base/Kernel/System/TimeSpan.cs rename to base/Applications/Runtime/Full/System/TimeSpan.cs index a4b9e5a..bbc3854 100644 --- a/base/Kernel/System/TimeSpan.cs +++ b/base/Applications/Runtime/Full/System/TimeSpan.cs @@ -3,7 +3,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -namespace System { +namespace System +{ using System.Text; using System.Runtime.CompilerServices; using System; @@ -99,53 +100,70 @@ namespace System { //| public int Days { + [NoHeapAllocation] get { return (int)(_ticks / TicksPerDay); } } //| - public int Hours { + public int Hours + { + [NoHeapAllocation] get { return (int)((_ticks / TicksPerHour) % 24); } } //| - public int Milliseconds { + public int Milliseconds + { + [NoHeapAllocation] get { return (int)((_ticks / TicksPerMillisecond) % 1000); } } //| - public int Minutes { + public int Minutes + { + [NoHeapAllocation] get { return (int)((_ticks / TicksPerMinute) % 60); } } //| - public int Seconds { + public int Seconds + { + [NoHeapAllocation] get { return (int)((_ticks / TicksPerSecond) % 60); } } //| - public long TotalDays { + public long TotalDays + { + [NoHeapAllocation] get { return _ticks / TicksPerDay; } } //| - public long TotalHours { + public long TotalHours + { + [NoHeapAllocation] get { return _ticks / TicksPerHour; } } //| - public long TotalMilliseconds { - get { - return _ticks / TicksPerMillisecond; - } + public long TotalMilliseconds + { + [NoHeapAllocation] + get { return _ticks / TicksPerMillisecond; } } //| - public long TotalMinutes { + public long TotalMinutes + { + [NoHeapAllocation] get { return _ticks / TicksPerMinute; } } //| - public long TotalSeconds { + public long TotalSeconds + { + [NoHeapAllocation] get { return _ticks / TicksPerSecond; } } @@ -176,7 +194,9 @@ namespace System { // relationship. // //| - public static int Compare(TimeSpan t1, TimeSpan t2) { + [NoHeapAllocation] + public static int Compare(TimeSpan t1, TimeSpan t2) + { if (t1._ticks > t2._ticks) return 1; if (t1._ticks < t2._ticks) return -1; return 0; @@ -215,12 +235,15 @@ namespace System { } //| - public static bool Equals(TimeSpan t1, TimeSpan t2) { + [NoHeapAllocation] + public static bool Equals(TimeSpan t1, TimeSpan t2) + { return t1._ticks == t2._ticks; } //| - public override int GetHashCode() { + public override int GetHashCode() + { return (int)_ticks ^ (int)(_ticks >> 32); } @@ -249,7 +272,7 @@ namespace System { //| public TimeSpan Negate() { - if (_ticks==TimeSpan.MinValue._ticks) + if (_ticks == TimeSpan.MinValue._ticks) throw new OverflowException("Overflow_NegateTwosCompNum"); return new TimeSpan(-_ticks); } @@ -279,7 +302,9 @@ namespace System { } //| - public static TimeSpan FromTicks(long value) { + [NoHeapAllocation] + public static TimeSpan FromTicks(long value) + { return new TimeSpan(value); } @@ -325,7 +350,7 @@ namespace System { //| public static TimeSpan operator -(TimeSpan t) { - if (t._ticks==TimeSpan.MinValue._ticks) + if (t._ticks == TimeSpan.MinValue._ticks) throw new OverflowException("Overflow_NegateTwosCompNum"); return new TimeSpan(-t._ticks); } @@ -336,7 +361,9 @@ namespace System { } //| - public static TimeSpan operator +(TimeSpan t) { + [NoHeapAllocation] + public static TimeSpan operator +(TimeSpan t) + { return t; } @@ -352,26 +379,35 @@ namespace System { } //| - public static bool operator !=(TimeSpan t1, TimeSpan t2) { + [NoHeapAllocation] + public static bool operator !=(TimeSpan t1, TimeSpan t2) + { return t1._ticks != t2._ticks; } //| - public static bool operator <(TimeSpan t1, TimeSpan t2) { + [NoHeapAllocation] + public static bool operator <(TimeSpan t1, TimeSpan t2) + { return t1._ticks < t2._ticks; } //| - public static bool operator <=(TimeSpan t1, TimeSpan t2) { + [NoHeapAllocation] + public static bool operator <=(TimeSpan t1, TimeSpan t2) + { return t1._ticks <= t2._ticks; } //| - public static bool operator >(TimeSpan t1, TimeSpan t2) { + [NoHeapAllocation] + public static bool operator >(TimeSpan t1, TimeSpan t2) + { return t1._ticks > t2._ticks; } //| + [NoHeapAllocation] public static bool operator >=(TimeSpan t1, TimeSpan t2) { return t1._ticks >= t2._ticks; } diff --git a/base/Applications/Runtime/Full/System/Type.cs b/base/Applications/Runtime/Full/System/Type.cs new file mode 100644 index 0000000..4622424 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Type.cs @@ -0,0 +1,420 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// Type is the root of all reflection and the Object that represents +// a type inside the system. Type is an abstract base class that allows multiple +// implementations. The system will always provide the subclass __RuntimeType. +// In Reflection all of the __RuntimeXXX classes are created only once per object +// in the system and support == comparisons. +// +namespace System +{ + + using System; + using System.Reflection; + using System.Runtime.InteropServices; + using System.Runtime.CompilerServices; + using DebuggerStepThroughAttribute = System.Diagnostics.DebuggerStepThroughAttribute; + + //| + [AccessedByRuntime("C++ needs to know relationship to RuntimeType")] + public abstract partial class Type + { + //| + public static readonly char Delimiter = '.'; + + // Prevent from begin created, and allow subclass + // to create. + //| + protected Type() {} + + // GetTypeCode + // This method will return a TypeCode for the passed + // type. + //| + [NoHeapAllocation] + public static TypeCode GetTypeCode(Type type) + { + if (type == null) + return TypeCode.Empty; + return type.GetTypeCodeInternal(); + } + + [NoHeapAllocation] + internal virtual TypeCode GetTypeCodeInternal() + { + Type type = this; + + if (type != type.UnderlyingSystemType) + return Type.GetTypeCode(type.UnderlyingSystemType); + + return TypeCode.Object; + } + + //| + [Inline] + [NoBarriers] + [NoHeapAllocation] + public static Type GetTypeFromHandle(RuntimeTypeHandle handle) + { + return RuntimeType.GetTypeFromHandleImpl(handle); + } + + // Property representing the name of the Member. + //| + public abstract String Name { + [NoHeapAllocation] get; + } + + // Return the fully qualified name. The name does contain the namespace. + //| + public abstract String FullName { + get; + } + + // Return the name space of the class. + //| + public abstract String Namespace { + [NoHeapAllocation] + get; + } + + public abstract Assembly Assembly { + [NoHeapAllocation] + get; + } + + //| + public abstract String AssemblyQualifiedName { + get; + } + + // @TODO: Next integration make this method abstract + //| + public virtual int GetArrayRank() { + throw new NotSupportedException("NotSupported_SubclassOverride"); + } + + // Returns the base class for a class. If this is an interface or has + // no base class null is returned. Object is the only Type that does not + // have a base class. + //| + public abstract Type BaseType { + [NoHeapAllocation] + get; + } + + + // GetInterfaces + // This method will return all of the interfaces implemented by a + // class + //| + abstract public Type[] GetInterfaces(); + + //////////////////////////////////////////////////////////////////////////////// + // + // Attributes + // + // The attributes are all treated as read-only properties on a class. Most of + // these boolean properties have flag values defined in this class and act like + // a bit mask of attributes. There are also a set of boolean properties that + // relate to the classes relationship to other classes and to the state of the + // class inside the runtime. + // + //////////////////////////////////////////////////////////////////////////////// + + // The attribute property on the Type. + //| + public TypeAttributes Attributes { + [NoHeapAllocation] + get {return GetAttributeFlagsImpl();} + } + //| + public bool IsNotPublic { + [NoHeapAllocation] + get {return ((GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NotPublic);} + } + //| + public bool IsPublic { + [NoHeapAllocation] + get {return ((GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.Public);} + } + //| + public bool IsNestedPublic { + [NoHeapAllocation] + get {return ((GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic);} + } + //| + public bool IsNestedPrivate { + [NoHeapAllocation] + get {return ((GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPrivate);} + } + //| + public bool IsNestedFamily { + [NoHeapAllocation] + get {return ((GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily);} + } + //| + public bool IsNestedFamANDAssem { + [NoHeapAllocation] + get {return ((GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamANDAssem);} + } + //| + public bool IsNestedFamORAssem{ + [NoHeapAllocation] + get {return ((GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamORAssem);} + } + + //| + public bool IsAutoLayout { + [NoHeapAllocation] + get {return ((GetAttributeFlagsImpl() & TypeAttributes.LayoutMask) == TypeAttributes.AutoLayout);} + } + //| + public bool IsLayoutSequential { + [NoHeapAllocation] + get {return ((GetAttributeFlagsImpl() & TypeAttributes.LayoutMask) == TypeAttributes.SequentialLayout);} + } + //| + public bool IsExplicitLayout { + [NoHeapAllocation] + get {return ((GetAttributeFlagsImpl() & TypeAttributes.LayoutMask) == TypeAttributes.ExplicitLayout);} + } + + //| + public bool IsClass { + [NoHeapAllocation] + get {return ((GetAttributeFlagsImpl() & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Class && !IsSubclassOf(Type.valueType));} + } + //| + public bool IsInterface { + [NoHeapAllocation] + get {return ((GetAttributeFlagsImpl() & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface);} + } + //| + public bool IsValueType { + [NoHeapAllocation] + get {return IsValueTypeImpl();} + } + + //| + public bool IsAbstract { + [NoHeapAllocation] + get {return ((GetAttributeFlagsImpl() & TypeAttributes.Abstract) != 0);} + } + //| + public bool IsSealed { + [NoHeapAllocation] + get {return ((GetAttributeFlagsImpl() & TypeAttributes.Sealed) != 0);} + } + //| + public bool IsEnum { + [NoHeapAllocation] + get {return IsSubclassOf(Type.enumType);} + } + //| + public bool IsSpecialName { + [NoHeapAllocation] + get {return ((GetAttributeFlagsImpl() & TypeAttributes.SpecialName) != 0);} + } + //| + public bool IsImport { + [NoHeapAllocation] + get {return ((GetAttributeFlagsImpl() & TypeAttributes.Import) != 0);} + } + + //| + public bool IsAnsiClass { + [NoHeapAllocation] + get {return ((GetAttributeFlagsImpl() & TypeAttributes.StringFormatMask) == TypeAttributes.AnsiClass);} + } + //| + public bool IsUnicodeClass { + [NoHeapAllocation] + get {return ((GetAttributeFlagsImpl() & TypeAttributes.StringFormatMask) == TypeAttributes.UnicodeClass);} + } + //| + public bool IsAutoClass { + [NoHeapAllocation] + get {return ((GetAttributeFlagsImpl() & TypeAttributes.StringFormatMask) == TypeAttributes.AutoClass);} + } + + // These are not backed up by attributes. Instead they are implemented + // based internally. + //| + public bool IsArray { + [NoHeapAllocation] + get {return IsArrayImpl();} + } + + //| + public bool IsPrimitive { + [NoHeapAllocation] + get {return IsPrimitiveImpl();} + } + //| + public bool HasElementType { + [NoHeapAllocation] + get {return HasElementTypeImpl();} + } + + // Protected routine to determine if this class represents a value class + //| + [NoHeapAllocation] + protected virtual bool IsValueTypeImpl() { + Type type = this; + if (type == Type.valueType || type == Type.enumType) { + return false; + } + return IsSubclassOf(Type.valueType); + } + + // Protected routine to get the attributes. + //| + [NoHeapAllocation] + abstract protected TypeAttributes GetAttributeFlagsImpl(); + + // Protected routine to determine if this class represents an Array + //| + [NoHeapAllocation] + abstract protected bool IsArrayImpl(); + + // Protected routine to determine if this class represents a primitive type + //| + [NoHeapAllocation] + abstract protected bool IsPrimitiveImpl(); + + //| + [NoHeapAllocation] + abstract public Type GetElementType(); + + //| + [NoHeapAllocation] + abstract protected bool HasElementTypeImpl(); + + // Return the underlying Type that represents the IReflect Object. For expando object, + // this is the (Object) IReflectInstance.GetType(). For Type object it is this. + //| + public abstract Type UnderlyingSystemType { + [NoHeapAllocation] + get; + } + + // Returns true if this class is a true subclass of c. Everything + // else returns false. If this class and c are the same class false is + // returned. + // + //| + [NoHeapAllocation] + public virtual bool IsSubclassOf(Type c) + { + Type p = this; + if (p == c) { + return false; + } + while (p != null) { + if (p == c) { + return true; + } + p = p.BaseType; + } + return false; + } + + // Returns true if an instance of Type c may be assigned + // to an instance of this class. Return false otherwise. + // + //| + public virtual bool IsAssignableFrom(Type c) + { + if (c == null) { + return false; + } + + // This excludes the TypeBuilder logic since Singularity does not + // have TypeBuilder in our fork of the URT sources. + + // Check for interfaces + if (IsInterface) { + Type[] ifaces = c.GetInterfaces(); + for (int i=0;i + public override String ToString() + { + return "Type: "+Name; + } + + // This method will return an array of classes based upon the array of + // types. + //| + public static Type[] GetTypeArray(Object[] args) + { + if (args == null) { + throw new ArgumentNullException("args"); + } + Type[] cls = new Type[args.Length]; + for (int i = 0; i < cls.Length; i++) { + cls[i] = args[i].GetType(); + } + return cls; + } + + //| + public override bool Equals(Object o) + { + if (o == null) { + return false; + } + if (!(o is Type)) { + return false; + } + return (this.UnderlyingSystemType == ((Type) o).UnderlyingSystemType); + } + + //| + public bool Equals(Type o) + { + if (o == null) { + return false; + } + return (this.UnderlyingSystemType == o.UnderlyingSystemType); + } + + //| + public override int GetHashCode() + { + Type SystemType = UnderlyingSystemType; + if (SystemType != this) { + return SystemType.GetHashCode(); + } + return base.GetHashCode(); + } + + // private convenience data + private static readonly Type valueType = typeof(System.ValueType); + private static readonly Type enumType = typeof(System.Enum); + + public abstract Microsoft.Singularity.V1.Types.SystemType GetSystemType(); + } +} diff --git a/base/Kernel/System/TypeCode.cs b/base/Applications/Runtime/Full/System/TypeCode.cs similarity index 97% rename from base/Kernel/System/TypeCode.cs rename to base/Applications/Runtime/Full/System/TypeCode.cs index e4b18af..fd6578d 100644 --- a/base/Kernel/System/TypeCode.cs +++ b/base/Applications/Runtime/Full/System/TypeCode.cs @@ -21,7 +21,8 @@ // These types of values are instead represented as classes. When the type code // of an object is TypeCode.Object, a further instance-of check can be used to // determine if the object is one of these values. -namespace System { +namespace System +{ //| public enum TypeCode { //| diff --git a/base/Applications/Runtime/Full/System/TypeInitializationException.cs b/base/Applications/Runtime/Full/System/TypeInitializationException.cs new file mode 100644 index 0000000..91845c5 --- /dev/null +++ b/base/Applications/Runtime/Full/System/TypeInitializationException.cs @@ -0,0 +1,52 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: TypeInitializationException +// +// Purpose: The exception class to wrap exceptions thrown by +// a type's class initializer (.cctor). This is sufficiently +// distinct from a TypeLoadException, which means we couldn't +// find the type. +// +//============================================================================= +using System; + +namespace System +{ + //| + public sealed class TypeInitializationException : SystemException { + private String _typeName; + + // This exception is not creatable without specifying the + // inner exception. + private TypeInitializationException() + : base("TypeInitialization_Default") { + } + + // This is called from within the runtime. I believe this is necessary + // for Interop only, though it's not particularly useful. + private TypeInitializationException(String message) : base(message) { + } + + //| + public TypeInitializationException(String fullTypeName, Exception innerException) + : base(String.Format("An exception occurred while initializing the type '{0}'.", fullTypeName), innerException) { + _typeName = fullTypeName; + } + + //| + public String TypeName + { + get { + if (_typeName == null) { + return String.Empty; + } + return _typeName; + } + } + } +} diff --git a/base/Kernel/System/UInt16.cs b/base/Applications/Runtime/Full/System/UInt16.cs similarity index 88% rename from base/Kernel/System/UInt16.cs rename to base/Applications/Runtime/Full/System/UInt16.cs index 71b3d4c..bf3190d 100644 --- a/base/Kernel/System/UInt16.cs +++ b/base/Applications/Runtime/Full/System/UInt16.cs @@ -3,19 +3,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: UInt16 -** -** and Daryl Olander (darylo) -** -** Purpose: This class will encapsulate a short and provide an -** Object representation of it. -** -** Date: March 4, 1999 -** -===========================================================*/ -namespace System { +//============================================================ +// +// Class: UInt16 +// +// Purpose: This class will encapsulate a short and provide an +// Object representation of it. +// +//=========================================================== +namespace System +{ using System.Globalization; using System; using System.Runtime.CompilerServices; diff --git a/base/Kernel/System/UInt32.cs b/base/Applications/Runtime/Full/System/UInt32.cs similarity index 89% rename from base/Kernel/System/UInt32.cs rename to base/Applications/Runtime/Full/System/UInt32.cs index 2a7e434..96b80ee 100644 --- a/base/Kernel/System/UInt32.cs +++ b/base/Applications/Runtime/Full/System/UInt32.cs @@ -3,18 +3,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: UInt32 -** -** -** Purpose: This class will encapsulate an uint and -** provide an Object representation of it. -** -** Date: March 4, 1999 -** -===========================================================*/ -namespace System { +//============================================================ +// +// Class: UInt32 +// +// Purpose: This class will encapsulate an uint and +// provide an Object representation of it. +// +//=========================================================== +namespace System +{ using System.Globalization; using System; using System.Runtime.CompilerServices; diff --git a/base/Kernel/System/UInt64.cs b/base/Applications/Runtime/Full/System/UInt64.cs similarity index 88% rename from base/Kernel/System/UInt64.cs rename to base/Applications/Runtime/Full/System/UInt64.cs index 7120a63..ca322a6 100644 --- a/base/Kernel/System/UInt64.cs +++ b/base/Applications/Runtime/Full/System/UInt64.cs @@ -3,19 +3,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: UInt64 -** -** and Daryl Olander (darylo) -** -** Purpose: This class will encapsulate an unsigned long and -** provide an Object representation of it. -** -** Date: March 4, 1999 -** -===========================================================*/ -namespace System { +//============================================================ +// +// Class: UInt64 +// +// Purpose: This class will encapsulate an unsigned long and +// provide an Object representation of it. +// +//=========================================================== +namespace System +{ using System.Globalization; using System; using System.Runtime.CompilerServices; diff --git a/base/Kernel/System/UIntPtr.cs b/base/Applications/Runtime/Full/System/UIntPtr.cs similarity index 96% rename from base/Kernel/System/UIntPtr.cs rename to base/Applications/Runtime/Full/System/UIntPtr.cs index 1904e62..ac9d30d 100644 --- a/base/Kernel/System/UIntPtr.cs +++ b/base/Applications/Runtime/Full/System/UIntPtr.cs @@ -4,7 +4,8 @@ // // ==--== -namespace System { +namespace System +{ using System; using System.Globalization; diff --git a/base/Applications/Runtime/Full/System/ValueType.cs b/base/Applications/Runtime/Full/System/ValueType.cs new file mode 100644 index 0000000..61f046e --- /dev/null +++ b/base/Applications/Runtime/Full/System/ValueType.cs @@ -0,0 +1,48 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================ +// +// Class: ValueType +// +// Purpose: Base class for all value classes. +// +//=========================================================== +namespace System +{ + using System; + using System.Runtime.CompilerServices; + + //| + [NoCCtor] + public abstract class ValueType { + + //| + public override bool Equals (Object obj) { + throw new Exception("System.ValueType.Equals not implemented in Bartok!"); + } + + //=================================GetHashCode================================== + //Action: Our algorithm for returning the hashcode is a little bit complex. We look + // for the first non-static field and get its hashcode. If the type has no + // non-static fields, we return the hashcode of the type. We can't take the + // hashcode of a static member because if that member is of the same type as + // the original type, we'll end up in an infinite loop. + //Returns: The hashcode for the type. + //Arguments: None. + //Exceptions: None. + //============================================================================== + //| + public override int GetHashCode() { + throw new Exception("System.ValueType.GetHashCode not implemented in Bartok!"); + } + + //| + public override String ToString() { + Type thisType = this.GetType(); + return thisType.FullName; + } + } +} diff --git a/base/Applications/Runtime/Full/System/Version.cs b/base/Applications/Runtime/Full/System/Version.cs new file mode 100644 index 0000000..6f41785 --- /dev/null +++ b/base/Applications/Runtime/Full/System/Version.cs @@ -0,0 +1,295 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================ +// +// Purpose: +// +//=========================================================== +namespace System +{ + + using System.Runtime.CompilerServices; + using CultureInfo = System.Globalization.CultureInfo; + + // A Version object contains four hierarchical numeric components: major, minor, + // build and revision. Build and revision may be unspecified, which is represented + // internally as a -1. By definition, an unspecified component matches anything + // (both unspecified and specified), and an unspecified component is "less than" any + // specified component. + + //| + public sealed partial class Version : ICloneable, IComparable + { + private int _Major; + private int _Minor; + private int _Build = -1; + private int _Revision = -1; + + //| + public Version(int major, int minor, int build, int revision) { + if (major < 0) + throw new ArgumentOutOfRangeException("major","ArgumentOutOfRange_Version"); + + if (minor < 0) + throw new ArgumentOutOfRangeException("minor","ArgumentOutOfRange_Version"); + + if (build < 0) + throw new ArgumentOutOfRangeException("build","ArgumentOutOfRange_Version"); + + if (revision < 0) + throw new ArgumentOutOfRangeException("revision","ArgumentOutOfRange_Version"); + + _Major = major; + _Minor = minor; + _Build = build; + _Revision = revision; + } + + //| + public Version(int major, int minor, int build) { + if (major < 0) + throw new ArgumentOutOfRangeException("major","ArgumentOutOfRange_Version"); + + if (minor < 0) + throw new ArgumentOutOfRangeException("minor","ArgumentOutOfRange_Version"); + + if (build < 0) + throw new ArgumentOutOfRangeException("build","ArgumentOutOfRange_Version"); + + + _Major = major; + _Minor = minor; + _Build = build; + } + + //| + public Version(int major, int minor) { + if (major < 0) + throw new ArgumentOutOfRangeException("major","ArgumentOutOfRange_Version"); + + if (minor < 0) + throw new ArgumentOutOfRangeException("minor","ArgumentOutOfRange_Version"); + + _Major = major; + _Minor = minor; + } + + //| + public Version(String version) { + if ((Object) version == null) + throw new ArgumentNullException("version"); + + String[] parsedComponents = version.Split(new char[] {'.'}); + int parsedComponentsLength = parsedComponents.Length; + if ((parsedComponentsLength < 2) || (parsedComponentsLength > 4)) throw new ArgumentException("Arg_VersionString"); + _Major = Int32.Parse(parsedComponents[0]); + if (_Major < 0) + throw new ArgumentOutOfRangeException("version","ArgumentOutOfRange_Version"); + + _Minor = Int32.Parse(parsedComponents[1]); + if (_Minor < 0) + throw new ArgumentOutOfRangeException("version","ArgumentOutOfRange_Version"); + + parsedComponentsLength -= 2; + if (parsedComponentsLength > 0) { + _Build = Int32.Parse(parsedComponents[2]); + if (_Build < 0) + throw new ArgumentOutOfRangeException("build","ArgumentOutOfRange_Version"); + + parsedComponentsLength--; + if (parsedComponentsLength > 0) { + _Revision = Int32.Parse(parsedComponents[3]); + if (_Revision < 0) + throw new ArgumentOutOfRangeException("revision","ArgumentOutOfRange_Version"); + } + } + } + + //| + public Version() + { + _Major = 0; + _Minor = 0; + } + + // Properties for setting and getting version numbers + //| + public int Major { + get { return _Major; } + } + + //| + public int Minor { + get { return _Minor; } + } + + //| + public int Build { + get { return _Build; } + } + + //| + public int Revision { + get { return _Revision; } + } + + //| + public Object Clone() { + Version v = new Version(); + v._Major = _Major; + v._Minor = _Minor; + v._Build = _Build; + v._Revision = _Revision; + return(v); + } + + //| + public int CompareTo(Object version) + { + if (version == null) + return 1; + + if (!(version is Version)) + throw new ArgumentException("Arg_MustBeVersion"); + + Version v = (Version) version; + + if (this._Major != v._Major) + if (this._Major > v._Major) + return 1; + else + return -1; + + if (this._Minor != v._Minor) + if (this._Minor > v._Minor) + return 1; + else + return -1; + + if (this._Build != v._Build) + if (this._Build > v._Build) + return 1; + else + return -1; + + if (this._Revision != v._Revision) + if (this._Revision > v._Revision) + return 1; + else + return -1; + + return 0; + } + + //| + public override bool Equals(Object obj) { + if (((Object) obj == null) || + (!(obj is Version))) + return false; + + Version v = (Version) obj; + // check that major, minor, build & revision numbers match + if ((this._Major != v._Major) || + (this._Minor != v._Minor) || + (this._Build != v._Build) || + (this._Revision != v._Revision)) + return false; + + return true; + } + + //| + public override int GetHashCode() + { + // Let's assume that most version numbers will be pretty small and just + // OR some lower order bits together. + + int accumulator = 0; + + accumulator |= (this._Major & 0x0000000F) << 28; + accumulator |= (this._Minor & 0x000000FF) << 20; + accumulator |= (this._Build & 0x000000FF) << 12; + accumulator |= (this._Revision & 0x00000FFF); + + return accumulator; + } + + //| + public override String ToString() { + if (_Build == -1) return(ToString(2)); + if (_Revision == -1) return(ToString(3)); + return(ToString(4)); + } + + //| + public String ToString(int fieldCount) { + switch (fieldCount) { + case 0: + return(String.Empty); + case 1: + return(String.Concat(_Major)); + case 2: + return(String.Concat(_Major,".",_Minor)); + default: + if (_Build == -1) + throw new ArgumentException(String.Format("ArgumentOutOfRange_Bounds_Lower_Upper", "0", "2"), "fieldCount"); + if (fieldCount == 3) + return( _Major + "." + _Minor + "." + _Build ); + + if (_Revision == -1) + throw new ArgumentException(String.Format("ArgumentOutOfRange_Bounds_Lower_Upper", "0", "3"), "fieldCount"); + + if (fieldCount == 4) + return( Major + "." + _Minor + "." + _Build + "." + _Revision ); + + throw new ArgumentException(String.Format("ArgumentOutOfRange_Bounds_Lower_Upper", "0", "4"), "fieldCount"); + } + } + + //| + public static bool operator ==(Version v1, Version v2) { + if ((Object) v1 == null) { + if ((Object) v2 == null) + return true; + else + return false; + } + if ((Object) v2 == null) + return false; + + return v1.Equals(v2); + } + + //| + public static bool operator !=(Version v1, Version v2) { + return !(v1 == v2); + } + + //| + public static bool operator <(Version v1, Version v2) { + if ((Object) v1 == null) + throw new ArgumentNullException("v1"); + return (v1.CompareTo(v2) < 0); + } + + //| + public static bool operator <=(Version v1, Version v2) { + if ((Object) v1 == null) + throw new ArgumentNullException("v1"); + return (v1.CompareTo(v2) <= 0); + } + + //| + public static bool operator >(Version v1, Version v2) { + return (v2 < v1); + } + + //| + public static bool operator >=(Version v1, Version v2) { + return (v2 <= v1); + } + } +} diff --git a/base/Kernel/System/Void.cs b/base/Applications/Runtime/Full/System/Void.cs similarity index 91% rename from base/Kernel/System/Void.cs rename to base/Applications/Runtime/Full/System/Void.cs index fe00308..508a2f6 100644 --- a/base/Kernel/System/Void.cs +++ b/base/Applications/Runtime/Full/System/Void.cs @@ -8,7 +8,8 @@ // This class represents the void return type //////////////////////////////////////////////////////////////////////////////// -namespace System { +namespace System +{ using System; //| diff --git a/base/Applications/Runtime/Full/System/_LocalDataStore.cs b/base/Applications/Runtime/Full/System/_LocalDataStore.cs new file mode 100644 index 0000000..eff6818 --- /dev/null +++ b/base/Applications/Runtime/Full/System/_LocalDataStore.cs @@ -0,0 +1,126 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: LocalDataStore +// +// Purpose: Class that stores local data. This class is used in cooperation +// with the _LocalDataStoreMgr class. +// +//============================================================================= + +namespace System +{ + + using System; + + internal class LocalDataStore + { + //========================================================================= + // DON'T CHANGE THESE UNLESS YOU MODIFY LocalDataStoreBaseObject in vm\object.h + //========================================================================= + private Object[] m_DataTable; + private LocalDataStoreMgr m_Manager; + + //========================================================================= + // Initialize the data store. + //========================================================================= + public LocalDataStore(LocalDataStoreMgr mgr, int InitialCapacity) + { + if (null == mgr) + throw new ArgumentNullException("mgr"); + + // Store the manager of the local data store. + m_Manager = mgr; + + // Allocate the array that will contain the data. + m_DataTable = new Object[InitialCapacity]; + } + + //========================================================================= + // Retrieves the value from the specified slot. + //========================================================================= + public Object GetData(LocalDataStoreSlot slot) + { + Object o = null; + + // Validate the slot. + m_Manager.ValidateSlot(slot); + + // Cache the slot index to avoid synchronization issues. + int slotIdx = slot.Slot; + + if (slotIdx >= 0) { + // Delay expansion of m_DataTable if we can + if (slotIdx >= m_DataTable.Length) + return null; + + // Retrieve the data from the given slot. + o = m_DataTable[slotIdx]; + } + + // Check if the slot has become invalid. + if (!slot.IsValid()) + throw new InvalidOperationException("InvalidOperation_SlotHasBeenFreed"); + return o; + } + + //========================================================================= + // Sets the data in the specified slot. + //========================================================================= + public void SetData(LocalDataStoreSlot slot, Object data) + { + // Validate the slot. + m_Manager.ValidateSlot(slot); + + // I can't think of a way to avoid the race described in the + // LocalDataStoreSlot finalizer method without a lock. + lock (m_Manager) { + if (!slot.IsValid()) + throw new InvalidOperationException("InvalidOperation_SlotHasBeenFreed"); + + // Do the actual set operation. + SetDataInternal(slot.Slot, data, true /*bAlloc*/ ); + } + } + + //========================================================================= + // This method does the actual work of setting the data. + //========================================================================= + internal void SetDataInternal(int slot, Object data, bool bAlloc) + { + // We try to delay allocate the dataTable (in cases like the manager clearing a + // just-freed slot in all stores + if (slot >= m_DataTable.Length) { + if (!bAlloc) + return; + SetCapacity(m_Manager.GetSlotTableLength()); + } + + // Set the data on the given slot. + m_DataTable[slot] = data; + } + + //========================================================================= + // Method used to set the capacity of the local data store. + //========================================================================= + private void SetCapacity(int capacity) + { + // Validate that the specified capacity is larger than the current one. + if (capacity < m_DataTable.Length) + throw new ArgumentException("Argument_ALSInvalidCapacity"); + + // Allocate the new data table. + Object[] NewDataTable = new Object[capacity]; + + // Copy all the objects into the new table. + Array.Copy(m_DataTable, NewDataTable, m_DataTable.Length); + + // Save the new table. + m_DataTable = NewDataTable; + } + } +} diff --git a/base/Applications/Runtime/Full/System/_LocalDataStoreMgr.cs b/base/Applications/Runtime/Full/System/_LocalDataStoreMgr.cs new file mode 100644 index 0000000..60dc8a8 --- /dev/null +++ b/base/Applications/Runtime/Full/System/_LocalDataStoreMgr.cs @@ -0,0 +1,285 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//============================================================================= +// +// Class: LocalDataStoreMgr +// +// Purpose: Class that manages stores of local data. This class is used in +// cooperation with the LocalDataStore class. +// +//============================================================================= +namespace System +{ + + using System; + using System.Collections; + + // This is a cheesy internal helper class that is used to make sure memory + // is actually being accessed and not some cached copy of a field in a + // register. + // WARNING: If we every do type analysis to eliminate virtual functions, + // this will break. + internal class LdsSyncHelper + { + internal virtual int Get(ref int slot) + { + return slot; + } + } + + // This class is an encapsulation of a slot so that it is managed in a secure fashion. + // It is constructed by the LocalDataStoreManager, holds the slot and the manager + // and cleans up when it is finalized. + //| + public sealed class LocalDataStoreSlot + { + private static LdsSyncHelper m_helper = new LdsSyncHelper(); + + private LocalDataStoreMgr m_mgr; + private int m_slot; + + // Construct the object to encapsulate the slot. + internal LocalDataStoreSlot(LocalDataStoreMgr mgr, int slot) + { + m_mgr = mgr; + m_slot = slot; + } + + // Accessors for the two fields of this class. + internal LocalDataStoreMgr Manager + { + get + { + return m_mgr; + } + } + internal int Slot + { + get + { + return m_slot; + } + } + + // This is used to make sure we are actually reading and writing to + // memory to fetch the slot (rather than possibly using a value + // cached in a register). + internal bool IsValid() + { + return m_helper.Get(ref m_slot) != -1; + } + + // Release the slot reserved by this object when this object goes away. + // There is a race condition that can happen in the face of + // resurrection where another thread is fetching values or assigning + // while the finalizer thread is here. We are counting on the fact + // that code that fetches values calls IsValid after fetching a value + // and before giving it to anyone. See LocalDataStore for the other + // half of this. We are also counting on code that sets values locks + // the manager. + //| + ~LocalDataStoreSlot() + { + int slot = m_slot; + + // This lock fixes synchronization with the assignment of values. + lock (m_mgr) { + // Mark the slot as free. + m_slot = -1; + + m_mgr.FreeDataSlot(slot); + } + } + } + + internal class LocalDataStoreMgr + { + private const byte DataSlotOccupied = 0x01; + + private const int InitialSlotTableSize = 64; + private const int SlotTableDoubleThreshold = 512; + private const int LargeSlotTableSizeIncrease = 128; + + //========================================================================= + // Create a data store to be managed by this manager and add it to the + // list. The initial size of the new store matches the number of slots + // allocated in this manager. + //========================================================================= + public LocalDataStore CreateLocalDataStore() + { + // Create a new local data store. + LocalDataStore Store = new LocalDataStore(this, m_SlotInfoTable.Length); + + lock (this) { + // Add the store to the array list and return it. + m_ManagedLocalDataStores.Add(Store); + } + return Store; + } + + //========================================================================= + // Remove the specified store from the list of managed stores.. + //========================================================================= + public void DeleteLocalDataStore(LocalDataStore store) + { + lock (this) { + // Remove the store to the array list and return it. + m_ManagedLocalDataStores.Remove(store); + } + } + + //========================================================================= + // Allocates a data slot by finding an available index and wrapping it + // an object to prevent clients from manipulating it directly, allowing us + // to make assumptions its integrity. + //========================================================================= + public LocalDataStoreSlot AllocateDataSlot() + { + lock (this) { + int i; + LocalDataStoreSlot slot; + + // Retrieve the current size of the table. + int SlotTableSize = m_SlotInfoTable.Length; + + // Check if there are any slots left. + if (m_FirstAvailableSlot < SlotTableSize) { + // Save the first available slot. + slot = new LocalDataStoreSlot(this, m_FirstAvailableSlot); + m_SlotInfoTable[m_FirstAvailableSlot] = DataSlotOccupied; + + // Find the next available slot. + for (i = m_FirstAvailableSlot + 1; i < SlotTableSize; ++i) + if (0 == (m_SlotInfoTable[i] & DataSlotOccupied)) + break; + + // Save the new "first available slot". + m_FirstAvailableSlot = i; + + // Return the slot index. + return slot; + } + + // The table is full so we need to increase its size. + int NewSlotTableSize; + if (SlotTableSize < SlotTableDoubleThreshold) { + // The table is still relatively small so double it. + NewSlotTableSize = SlotTableSize * 2; + } + else { + // The table is relatively large so simply increase its size by a given amount. + NewSlotTableSize = SlotTableSize + LargeSlotTableSizeIncrease; + } + + // Allocate the new slot info table. + byte[] NewSlotInfoTable = new byte[NewSlotTableSize]; + + // Copy the old array into the new one. + Array.Copy(m_SlotInfoTable, NewSlotInfoTable, SlotTableSize); + m_SlotInfoTable = NewSlotInfoTable; + + // SlotTableSize is the index of the first empty slot in the expanded table. + slot = new LocalDataStoreSlot(this, SlotTableSize); + m_SlotInfoTable[SlotTableSize] = DataSlotOccupied; + m_FirstAvailableSlot = SlotTableSize + 1; + + // Return the selected slot + return slot; + } + } + + //========================================================================= + // Allocate a slot and associate a name with it. + //========================================================================= + public LocalDataStoreSlot AllocateNamedDataSlot(String name) + { + lock (this) { + // Allocate a normal data slot. + LocalDataStoreSlot slot = AllocateDataSlot(); + + // Insert the association between the name and the data slot number + // in the hash table. + m_KeyToSlotMap.Add(name, slot); + return slot; + } + } + + //========================================================================= + // Retrieve the slot associated with a name, allocating it if no such + // association has been defined. + //========================================================================= + public LocalDataStoreSlot GetNamedDataSlot(String name) + { + lock (this) { + // Lookup in the hashtable to try find a slot for the name. + LocalDataStoreSlot slot = (LocalDataStoreSlot) m_KeyToSlotMap[name]; + + // If the name is not yet in the hashtable then add it. + if (null == slot) + return AllocateNamedDataSlot(name); + + // The name was in the hashtable so return the associated slot. + return slot; + } + } + + //========================================================================= + // Eliminate the association of a name with a slot. The actual slot will + // be reclaimed when the finalizer for the slot object runs. + //========================================================================= + public void FreeNamedDataSlot(String name) + { + lock (this) { + // Remove the name slot association from the hashtable. + m_KeyToSlotMap.Remove(name); + } + } + + //========================================================================= + // Free's a previously allocated data slot on ALL the managed data stores. + //========================================================================= + internal void FreeDataSlot(int slot) + { + lock (this) { + // Go thru all the managed stores and set the data on the specified slot to 0. + for (int i = 0; i < m_ManagedLocalDataStores.Count; i++) { + ((LocalDataStore)m_ManagedLocalDataStores[i]).SetDataInternal( + slot, + null, + false); + } + + // Mark the slot as being no longer occupied. + m_SlotInfoTable[slot] = 0; + if (slot < m_FirstAvailableSlot) + m_FirstAvailableSlot = slot; + } + } + + //========================================================================= + // Return the number of allocated slots in this manager. + //========================================================================= + public void ValidateSlot(LocalDataStoreSlot slot) + { + // Make sure the slot was allocated for this store. + if (slot == null || slot.Manager != this) + throw new ArgumentException("Argument_ALSInvalidSlot"); + } + + //========================================================================= + // Return the number of allocated slots in this manager. + //========================================================================= + internal int GetSlotTableLength() + { + return m_SlotInfoTable.Length; + } + + private byte[] m_SlotInfoTable = new byte[InitialSlotTableSize]; + private int m_FirstAvailableSlot = 0; + private ArrayList m_ManagedLocalDataStores = new ArrayList(); + private Hashtable m_KeyToSlotMap = new Hashtable(); + } +} diff --git a/base/Applications/Runtime/Full/TestApp.csproj b/base/Applications/Runtime/Full/TestApp.csproj new file mode 100644 index 0000000..ee2bef1 --- /dev/null +++ b/base/Applications/Runtime/Full/TestApp.csproj @@ -0,0 +1,43 @@ + + + + + + + + + + testapp.$(Runtime) + Exe + $(APPRUNTIMEDIR) + C# + + + + + + true + + + + + + + + + + + + + + diff --git a/base/Applications/Runtime/testapp.cs b/base/Applications/Runtime/Full/testapp.cs similarity index 93% rename from base/Applications/Runtime/testapp.cs rename to base/Applications/Runtime/Full/testapp.cs index 8486a53..a0d7621 100644 --- a/base/Applications/Runtime/testapp.cs +++ b/base/Applications/Runtime/Full/testapp.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: testapp.cs -// // Note: This file contains the main entry point for the C# // portion of Singularity. // diff --git a/base/Applications/Runtime/ILHelpers.csproj b/base/Applications/Runtime/ILHelpers.csproj deleted file mode 100644 index 3ee4ef6..0000000 --- a/base/Applications/Runtime/ILHelpers.csproj +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - $(APPRUNTIMEDIR) - $(SINGULARITY_ROOT)\Kernel - $(OutputPath)\ILHelpers.dll - - - - - - - - - - false - - - - - - - - - - - - - - - - - - - - - - diff --git a/base/Applications/Runtime/Singularity/AppRuntime.cs b/base/Applications/Runtime/Singularity/AppRuntime.cs deleted file mode 100644 index 1cee5b6..0000000 --- a/base/Applications/Runtime/Singularity/AppRuntime.cs +++ /dev/null @@ -1,224 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Runtime.cs -// -// Note: -// - -using System; -using System.Collections; -using System.Reflection; -using System.Threading; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -using Microsoft.Bartok.Runtime; - -#if !MINRUNTIME - using Microsoft.Singularity.Io; -#endif - using Microsoft.Singularity.V1.Services; - -[assembly: AssemblyTitle("Microsoft.Singularity")] -[assembly: AssemblyProduct("Microsoft Research Singularity Runtime")] -[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 AppRuntime - { - // Note: This function is called by Hal.cpp. - [AccessedByRuntime("referenced from hal.cpp")] - public static unsafe int AppStart(Type userClass) - { - System.GCs.Transitions.ThreadStart(); - - int result = 0; - - try { - Tracing.Log(Tracing.Audit, "Runtime.Main()"); - - // Initialize the primitive runtime, which calls the - // class constructor for Runtime(). - VTable.Initialize((RuntimeType)typeof(AppRuntime)); - /*VTable.ParseArgs(args);*/ - - Tracing.Log(Tracing.Audit, "Enabling GC Heap"); - GC.EnableHeap(); - -#if !MINRUNTIME - ConsoleOutput.Initialize(); - ConsoleInput.Initialize(); -#endif - - SetDebuggerPresence(DebugService.IsDebuggerPresent()); - - int argCount = 0; - int argMaxLen = 0; - for (;; argCount++) { - int len = ProcessService.GetStartupArg(argCount, null, 0); - if (len == 0) { - break; - } - if (argMaxLen < len) { - argMaxLen = len; - } - } - char[] argArray = new char [argMaxLen]; - string[] args = new string[argCount]; - for (int arg = 0; arg < argCount; arg++) { - fixed (char *argptr = &argArray[0]) { - int len = ProcessService.GetStartupArg(arg, - argptr, - argArray.Length); - args[arg] = String.StringCTOR(argptr, 0, len); - } - } - - if (userClass != null) { - VTable.initType((RuntimeType)userClass); - } - - result = CallMain(args); - if (!MainReturnsInt()) result = 0; - Thread.RemoveThread(Thread.CurrentThread.threadIndex); - - Thread.JoinAll(); - -#if !MINRUNTIME - ConsoleOutput.Finalize(); - ConsoleInput.Finalize(); -#endif - Tracing.Log(Tracing.Audit, "Main thread exited [{0}]", - (UIntPtr)unchecked((uint)result)); - } - catch (Exception e) { - Tracing.Log(Tracing.Fatal, "Failed with exception {0}.{1}", - e.GetType().Namespace, e.GetType().Name); - Tracing.Log(Tracing.Trace, "Exception message was {0}", - e.ToString()); - DebugStub.WriteLine("Caught {0}", __arglist(e.Message)); - result = -1; - } - - Tracing.Log(Tracing.Audit, "Runtime shutdown started."); - VTable.Shutdown(result); - Tracing.Log(Tracing.Audit, "Runtime exiting [{0}]", - (UIntPtr)unchecked((uint)result)); - return result; - } - - [AccessedByRuntime("output to header : defined in hal.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [StackBound(256)] - private static extern int CallMain(String[] args); - - [AccessedByRuntime("output to header: defined in hal.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [StackBound(256)] - private static extern bool MainReturnsInt(); - - [AccessedByRuntime("output to header: defined in hal.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [StackBound(256)] - private static extern void SetDebuggerPresence(bool debuggerPresent); - - internal static void Shutdown(int exitCode) - { - // - // Gracefully close down the process. - // - Tracing.Log(Tracing.Audit, "Runtime.Shutdown({0})", - (UIntPtr)unchecked((uint)exitCode)); - - DebugStub.WriteLine("Runtime.Shutdown({0})", __arglist(exitCode)); - - VTable.Shutdown(exitCode); - Tracing.Log(Tracing.Audit, "Runtime.Shutdown({0}) terminating", - (UIntPtr)unchecked((uint)exitCode)); - ProcessService.Stop(exitCode); - } - - internal static void Stop(int exitCode) - { - // - // Halt the process immediately. - // - Tracing.Log(Tracing.Audit, "Runtime.Stop({0})", - (UIntPtr)unchecked((uint)exitCode)); - - DebugStub.WriteLine("Runtime.Stop({0})", __arglist(exitCode)); - - ProcessService.Stop(exitCode); - } - - ////////////////////////////////////////////////////////////////////// - // - [NoHeapAllocation] - public static UIntPtr AddressOf(Object o) - { - return Magic.addressOf(o); - } - - [NoHeapAllocation] - public static UIntPtr SizeOf(Object o) - { - return System.GCs.ObjectLayout.Sizeof(o); - } - - public static void InitType(Type ty) - { - VTable.initType((RuntimeType) ty); - } - - public static void DumpPageTable() - { - System.GCs.PageTable.Dump("PageTable"); - } - - ////////////////////////////////////////////////////////////////////// - // - public static bool EnableGCVerify - { - get { - return VTable.enableGCVerify; - } - set { - VTable.enableGCVerify = value; - } - } - - public static bool EnableGCAccounting - { - get { - return VTable.enableGCAccounting; - } - set { - VTable.enableGCAccounting = value; - if (value == true) { - System.GCs.MemoryAccounting.Initialize(GC.gcType); - } - } - } - - public static uint GCPerfCounter - { - get { - return System.GC.perfCounter; - } - set { - System.GC.perfCounter = value; - } - } - } -} diff --git a/base/Applications/Runtime/Singularity/DebugStub.cs b/base/Applications/Runtime/Singularity/DebugStub.cs deleted file mode 100644 index af809b2..0000000 --- a/base/Applications/Runtime/Singularity/DebugStub.cs +++ /dev/null @@ -1,337 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Debug.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.Singularity.V1.Services; - -namespace Microsoft.Singularity -{ - [NoCCtor] - [CLSCompliant(false)] - public class DebugStub - { - /////////////////////////////////////////////////////// Print Methods. - // - [NoHeapAllocation] - public static void Print(byte value) - { - Print("{0:x2}", __arglist(value)); - } - - [NoHeapAllocation] - public static void Print(ushort value) - { - Print("{0:x4}", __arglist(value)); - } - - [NoHeapAllocation] - public static void Print(uint value) - { - Print("{0:x8}", __arglist(value)); - } - - [NoHeapAllocation] - public static void Print(ulong value) - { - Print("{0:x}", __arglist(value)); - } - - [NoHeapAllocation] - public static void Print(UIntPtr value) - { - Print("{0:x8}", __arglist(value)); - } - - [NoHeapAllocation] - public static void Print(sbyte value) - { - Print("{0}", __arglist(value)); - } - - [NoHeapAllocation] - public static void Print(short value) - { - Print("{0}", __arglist(value)); - } - - [NoHeapAllocation] - public static void Print(int value) - { - Print("{0}", __arglist(value)); - } - - [NoHeapAllocation] - public static void Print(long value) - { - Print("{0}", __arglist(value)); - } - - ////////////////////////////////////////////////////////////////////// - // - - [NoHeapAllocation] - public static void Print(String value) - { - if (value != null) { - Print(value, new ArgIterator()); - } - } - - [NoHeapAllocation] - public static void Print(String format, __arglist) - { - Print(format, new ArgIterator(__arglist)); - } - -#if PAGING -[AccessedByRuntime("output to header : defined in debugstub.cpp")] -[MethodImpl(MethodImplOptions.InternalCall)] -[StackBound(64)] -[NoHeapAllocation] -public static extern void Foo(); -//static int ccc = 0; -#endif - [NoHeapAllocation] - public static unsafe void Print(String format, ArgIterator args) - { -#if PAGING -Foo(); -/* -if (ccc == 0) { - DebugStub.Break(); - ccc = 1; -} -*/ -#endif - char *buffer; - int length; - int used = 0; - - DebugService.PrintBegin(out buffer, out length); - try { - if (buffer != null) { - used = String.LimitedFormatTo(format, args, buffer, length); - } - } - finally { - DebugService.PrintComplete(buffer, used); - } - } - - ////////////////////////////////////////////////////////////////////// - // - - [NoHeapAllocation] - public static void Write(String value) - { - if (value != null) { - Write(value, new ArgIterator()); - } - } - - [NoHeapAllocation] - public static void Write(String format, __arglist) - { - Write(format, new ArgIterator(__arglist)); - } - - [NoHeapAllocation] - public static unsafe void Write(String format, ArgIterator args) - { - char *buffer; - int length; - int used = 0; - - DebugService.PrintBegin(out buffer, out length); - try { - if (buffer != null) { - used = String.LimitedFormatTo(format, args, buffer, length); - } - } - finally { - DebugService.PrintComplete(buffer, used); - } - } - - ////////////////////////////////////////////////////////////////////// - // - [NoHeapAllocation] - public static void WriteLine() - { - WriteLine("", new ArgIterator()); - } - - [NoHeapAllocation] - public static void WriteLine(String value) - { - if (value != null) { - WriteLine(value, new ArgIterator()); - } - } - - [NoHeapAllocation] - public static void WriteLine(String format, __arglist) - { - WriteLine(format, new ArgIterator(__arglist)); - } - - [NoHeapAllocation] - public static unsafe void WriteLine(String format, ArgIterator args) - { - char *buffer; - int length; - int used = 0; - - DebugService.PrintBegin(out buffer, out length); - try { - if (buffer != null) { - used = String.LimitedFormatTo(format, args, buffer, length); - if (used < length) { - buffer[used++] = '\n'; - } - } - } - finally { - DebugService.PrintComplete(buffer, used); - } - } - - ////////////////////////////////////////////////////// Assert Methods. - // - [NoHeapAllocation] - public static void NotImplemented() - { - failAssert("Not implemented."); - } - - [NoHeapAllocation] - public static void NotImplemented(String msg) - { - failAssert(/*"Not implemented: "+*/msg); - } - - [Conditional("DEBUG")] - [NoInline] - [NoHeapAllocation] - public static void NotReached() - { - failAssert("Unreachable code reached."); - } - - [Conditional("DEBUG")] - [NoInline] - [NoHeapAllocation] - public static void NotReached(String msg) - { - failAssert(/*"Unreachable code reached: "+*/msg); - } - - [Conditional("DEBUG")] - [NoInline] - [ManualRefCounts] - [NoHeapAllocation] - public static void Assert(bool expr) - { - if (!expr) { - failAssert(null); - } - } - - [Conditional("DEBUG")] - [NoInline] - [ManualRefCounts] - [NoHeapAllocation] - public static void Deny(bool expr) - { - if (expr) { - failAssert(null); - } - } - - [Conditional("DEBUG")] - [NoInline] - [ManualRefCounts] - [NoHeapAllocation] - public static void Assert(bool expr, String s) - { - if (!expr) { - failAssert(s); - } - } - - [Conditional("DEBUG")] - [NoInline] - [NoHeapAllocation] - public static void Deny(bool expr, String s) - { - if (expr) { - failAssert(s); - } - } - - [ManualRefCounts] - [NoHeapAllocation] - private static void failAssert(String s) - { - if (s != null) { - Print("Assertion failed: {0}", __arglist(s)); - } - else { - Print("Assertion failed."); - } - Break(); - } - - ////////////////////////////////////////////////////////////////////// - // - [NoHeapAllocation] - public static ulong ReadPerfCounter(uint which) - { - return DebugService.ReadPerfCounter(which); - } - - - [NoHeapAllocation] - public static bool WritePerfCounter(uint which, ulong value) - { - return DebugService.WritePerfCounter(which, value); - } - - [NoHeapAllocation] - public static bool AddToPerfCounter(uint which, ulong value) - { - return DebugService.AddToPerfCounter(which, value); - } - - /////////////////////////////////////////////////////// State Methods. - // -#if true - [AccessedByRuntime("output to header : defined in halkd.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [StackBound(638)] - [NoHeapAllocation] - public static extern void Break(); -#else - [NoHeapAllocation] - public static void Break() - { - DebugService.Break(); - } -#endif - } -} diff --git a/base/Applications/Runtime/Singularity/Directory/NameService.sg b/base/Applications/Runtime/Singularity/Directory/NameService.sg deleted file mode 100644 index 1118e59..0000000 --- a/base/Applications/Runtime/Singularity/Directory/NameService.sg +++ /dev/null @@ -1,30 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: DirectoryService.sg -// -// Note: -// -// This file provides an implementation of Naming.DirectoryService.NewClientEndpoint -// that calls through the ABI to retrieve the name-service channel endpoint. -// This is for use by applications; kernel components can call the "real" -// DirectoryService class directly. -// - -using Microsoft.Singularity; -using Microsoft.Singularity.Naming; -using Microsoft.Singularity.V1.Services; - -namespace Microsoft.Singularity.Directory -{ - public class DirectoryService - { - public static unsafe DirectoryServiceContract.Imp!:Ready NewClientEndpoint() - { - return (DirectoryServiceContract.Imp!)ProcessService.GetNamespaceEndpoint(); - } - } -} diff --git a/base/Applications/Runtime/Singularity/Io/ConsoleInput.sg b/base/Applications/Runtime/Singularity/Io/ConsoleInput.sg deleted file mode 100644 index 20d6e19..0000000 --- a/base/Applications/Runtime/Singularity/Io/ConsoleInput.sg +++ /dev/null @@ -1,318 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: ConsoleInput.sg -// -// Note: Read a line or a char of the Console -// - -using DirectoryService.Utils; -using System.GC; -using System.Runtime.Remoting; -using System.Threading; -using System.Collections; -using System.Diagnostics; -using Microsoft.Singularity.V1.Services; - - -namespace Microsoft.Singularity.Io { - using Microsoft.SingSharp; - using Microsoft.Singularity; - using Microsoft.Singularity.V1.Services; - using Microsoft.Singularity.V1.Threads; - using Microsoft.Singularity.Channels; - using Microsoft.Singularity.Directory; - - using System; - using System.Text; - using System.Globalization; - using System.Runtime.CompilerServices; - - using Microsoft.Singularity.Extending; - using Microsoft.Singularity.Runtime; - using Microsoft.Singularity.Io; - using System.Runtime.InteropServices; - - - - public sealed class ConsoleInput /* : ITracked, ISelectable */ - { - private static TContainer consoleContainer; - // protected by TContainer lock. - private static char[]! readLineBuffer = new char[16]; - - /* - - private char[]! in ExHeap side; - private UnicodePipeContract.Exp conn; - private int charCount; - private int charPos; - - private ConsoleInput([Claims] UnicodePipeContract.Exp? pipe) - { - this.side = new[ExHeap] char [280]; - this.charCount = 0; - this.charPos = 0; - this.conn = pipe; - - base(); - } - */ - - private ConsoleInput() {} - - private static UnicodePipeContract.Exp? GetPipe() - { - UnicodePipeContract.Exp con = null; - - Endpoint * in ExHeap pipeep = Process.GetStartupEndpoint(0); - if (pipeep != null) { - con = pipeep as UnicodePipeContract.Exp; - if (con == null) { - // this cannot possibly work! the process is already started -#if DEBUG - DebugStub.WriteLine("ConsoleInput: slot 0 is not a pipe."); -#endif - Process.RetStartupEndpoint(0,pipeep); - return null; - } - else { - //DebugStub.WriteLine("ConsoleInput: connected to pipe."); - return con; - } - } - return null; - } - - - public static void Initialize() - { - UnicodePipeContract.Exp pipe = GetPipe(); - PipeLookAhead bufferedPipe = new PipeLookAhead(pipe, 100); - ConsoleInput.consoleContainer = new TContainer(bufferedPipe); - } - - /* - public void Dispose() - { - delete this.side; - delete this.conn; - } - - void ITracked.Acquire() { - Tracing.Log(Tracing.Debug,"ConsoleInput.Acquire"); - } - void ITracked.Release() { - Tracing.Log(Tracing.Debug,"ConsoleInput.Release"); - } - void ITracked.Expose() { - Tracing.Log(Tracing.Debug,"ConsoleInput.Expose"); - } - void ITracked.UnExpose() { - Tracing.Log(Tracing.Debug,"ConsoleInput.UnExpose"); - } - */ - - public static void Finalize() - { -#if DEBUG - DebugStub.WriteLine("consoleInput: finalizer called"); -#endif - // TContainer chandle = consoleContainer; - TContainer chandle = consoleContainer; - consoleContainer = null; - - if (chandle == null) return; - - PipeLookAhead co = chandle.Acquire(); - co.Dispose(); - } - - /* - private int InternalReadChar() - { - expose (this) { - if (this.conn != null) { - if (this.charCount == 0) { - UnicodePipeContract.Exp pipe = this.conn; - - tryAgain: - switch receive { - case pipe.Write(buffer,a,b): - if (b == 0) { - pipe.SendAckWrite(buffer); - goto tryAgain; - } - if (b == 1) { - int theChar = (int) buffer[a]; - pipe.SendAckWrite(buffer); - return theChar; - } - else { - // switch our buffer with theirs - pipe.SendAckWrite(this.side); - this.side = buffer; - this.charPos = a+1; - this.charCount = b-1; - return (int)buffer[a]; - } - - case pipe.ChannelClosed(): - return -1; - } - } - else { - // there are more chars in the buffer - int theChar = this.side[this.charPos++]; - this.charCount--; - return theChar; - } - } - } //expose - return -1; - } - */ - - public static void Close() - { - } - - public static int ReadChar() - { - if (consoleContainer == null) return -1; - PipeLookAhead co = consoleContainer.Acquire(); - int key = co.ReadChar(); - if (key == -1) { - co.Dispose(); - consoleContainer = null; - return key; - } - consoleContainer.Release(co); - return key; - } //ReadChar - - //////////////////////////////////////////////////////// - // - public static string ReadLine() - { - if (consoleContainer == null) return null; - PipeLookAhead bufferedPipe = consoleContainer.Acquire(); - - int pos = 0; - int key; - - do { - key = bufferedPipe.ReadChar(); - if (key == -1) { - bufferedPipe.Dispose(); - consoleContainer = null; - return null; - } - if (key == '\r') { - // DebugStub.WriteLine("key {0}", key); - continue; - } - if (key == '\n') { - // DebugStub.WriteLine("key {0}", key); - break; - } - if (pos+1 >= readLineBuffer.Length){ - char[] temp = new char[readLineBuffer.Length *2]; - Array.Copy(readLineBuffer,temp,readLineBuffer.Length); - readLineBuffer = temp; - } - readLineBuffer[pos++] = (char) key; - if (key > 31 && key < 126) { - // DebugStub.WriteLine("key {0}", key); - } - } while (key != -1 ); - - string s = new string(readLineBuffer,0,pos); - //DebugStub.WriteLine("in: count={0}, s=({1})",__arglist(pos, s)); - - consoleContainer.Release(bufferedPipe); - return s; - } //ReadLine - - /* - internal enum ReceiveTag { - Any = 1, - GetChar = 2, - } - - [Selectable((int)ReceiveTag.GetChar)] - public void RecvGetChar(out int key) { - key = this.InternalReadChar(); - } - - /// - /// Tests if the message at the head of the queue matches the given tag. If so, - /// returns true. - /// - /// If no match is possible in the future given the current message, possible must - /// be set to false. - /// Implementations are disallowed from setting possible to true - /// so that the context can chain them. - /// - /// If the underlying object is an endpoint set, it also returns an object that - /// serves to extract the endpoint having the match from the set later. - /// - /// Again, implementations are disallowed from setting match setMatch to null - /// so that the context can chain the calls. - /// - /// - bool ISelectable.HeadMatches(int tag, ref bool possible, ref object setMatch) { - // Only handle pipe case - expose (this) { - if (this.conn != null) { - if (this.charCount == 0) { - UnicodePipeContract.Exp pipe = this.conn; - - // negative tag is channel closed - int pipeTag = (tag < 0)?tag : UnicodePipeContract.Tags.Write; - - return pipe.HeadMatches(pipeTag, ref possible, ref setMatch); - } - else { - // there are more chars in the buffer - if (tag < 0) { - // not closed - possible = false; - return false; - } - return true; - } - } - else { - // no pipe - possible = false; - return false; - } - } //expose - } - - SyncHandle ISelectable.GetWaitHandle() { - expose (this) { - assert(this.conn != null); - return this.conn.GetWaitHandle(); - } - } - - */ - public static PipeLookAhead! AcquireInput() { - assert consoleContainer != null; - - PipeLookAhead bufferedPipe = consoleContainer.Acquire(); - return bufferedPipe; - } - - public static void ReleaseInput([Claims] PipeLookAhead! bufferedPipe) { - assert consoleContainer != null; - - consoleContainer.Release(bufferedPipe); - } - - } //ConsoleInput -}//namespace diff --git a/base/Applications/Runtime/Singularity/Io/ConsoleOutput.sg b/base/Applications/Runtime/Singularity/Io/ConsoleOutput.sg deleted file mode 100644 index 9fbdeb9..0000000 --- a/base/Applications/Runtime/Singularity/Io/ConsoleOutput.sg +++ /dev/null @@ -1,461 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: ConsoleOutput.sg -// -// Note: Uses /dev/conout to display console output. -// - -namespace Microsoft.Singularity.Io { - using Microsoft.SingSharp; - using Microsoft.Singularity; - using Microsoft.Singularity.V1.Services; - using Microsoft.Singularity.Channels; - using Microsoft.Singularity.Directory; - - using System; - using System.Text; - using System.Globalization; - using System.Runtime.CompilerServices; - - using System.Runtime.InteropServices; - - public sealed class ConsoleOutput : ITracked - { - private static TContainer consoleContainer; - - private struct PipeData : ITracked { - public UnicodePipeContract.Imp channel; - public void Dispose() - { - delete channel; - } - void ITracked.Acquire() {} - void ITracked.Release() {} - public void Expose() {} - public void UnExpose() {} - - public UnicodePipeContract.Imp:READY Swap([Claims] UnicodePipeContract.Imp:READY newPipe) { - expose (this) { - UnicodePipeContract.Imp result = this.channel; - this.channel = newPipe; - return result; - } - } - } - - /* - private struct DeviceData : ITracked { - public ConsoleDeviceContract.Imp:Ready channel; - public void Dispose() - { - delete channel; - } - void ITracked.Acquire() {} - void ITracked.Release() {} - public void Expose() {} - public void UnExpose() {} - } - */ - - private char[]! in ExHeap side; - private PipeData pipe; - /* - private DeviceData device; - */ - private ConsoleOutput([Claims] UnicodePipeContract.Imp? pipe) - { - this.side = new[ExHeap] char [280]; - this.pipe.channel = pipe; - } - - private static UnicodePipeContract.Imp GetPipe() - { - UnicodePipeContract.Imp con = null; - - Endpoint* in ExHeap pipeep = Process.GetStartupEndpoint(1); - if (pipeep != null) { - con = pipeep as UnicodePipeContract.Imp; - if (con == null) { - Process.RetStartupEndpoint(1,pipeep); - return null; - } - return con; - } - return null; - } - - public static void Initialize() - { - /* - ConsoleDeviceContract.Imp imp = null; - - imp = OpenConsole("/dev/video-text"); - if (imp == null) { - imp = OpenConsole("/dev/conout"); - if (imp == null) { - DebugStub.Print("Couldn't open console.\n"); - return; - } - } - */ - UnicodePipeContract.Imp pipe = GetPipe(); - ConsoleOutput co = new ConsoleOutput(pipe); - consoleContainer = new TContainer(co); - } - - public void Dispose() - { - this.pipe.Dispose(); - // this.device.Dispose(); - delete this.side; - } - - void ITracked.Acquire() { - //Tracing.Log(Tracing.Debug,"ConsoleOutput.Acquire"); - } - void ITracked.Release() { - //Tracing.Log(Tracing.Debug,"ConsoleOutput.Release"); - } - void ITracked.Expose() { - //Tracing.Log(Tracing.Debug,"ConsoleOutput.Expose"); - } - void ITracked.UnExpose() { - //Tracing.Log(Tracing.Debug,"ConsoleOutput.UnExpose"); - } - - public static void Finalize() - { - TContainer chandle = consoleContainer; - consoleContainer = null; - - if (chandle == null) return; - - ConsoleOutput co = chandle.Acquire(); - if (co != null) co.Dispose(); - } - - /* - private static ConsoleDeviceContract.Imp - OpenConsoleInternal(DirectoryServiceContract.Imp! nsImp, - [Claims] char[]! in ExHeap deviceName) - { - ConsoleDeviceContract.Exp! exp; - ConsoleDeviceContract.Imp! imp; - ConsoleDeviceContract.NewChannel(out imp, out exp); - - nsImp.SendBind(deviceName, exp); - switch receive - { - case nsImp.AckBind(): - return imp; - break; - case nsImp.NakBind(rejectImp, error): - delete rejectImp; - delete imp; - break; - case nsImp.ChannelClosed(): - throw new Exception("Channel closed during Console bind."); - delete imp; - break; - case nsImp.NakBindReparse(path, rest, linked, backExp): - assert linked == true; - assert rest == null; - delete backExp; - delete imp; - - return OpenConsoleInternal(nsImp, path); - break; - } - return null; - } - - private static ConsoleDeviceContract.Imp OpenConsole(string! devName) - { - DirectoryServiceContract.Imp ns = DirectoryService.NewClientEndpoint(); - ConsoleDeviceContract.Imp imp = - OpenConsoleInternal(ns, Bitter.FromString2(devName)); - delete ns; - - if (imp != null) { - switch receive - { - case imp.Success(): - break; - case imp.ContractNotSupported(): - throw new Exception("ConsoleOutput: ContractNotSupported"); - break; - case imp.ChannelClosed(): - throw new Exception("ConsoleOutput: ChannelClosed"); - break; - } - } - return imp; - } - - public static void Clear() - { - if (consoleContainer == null) return; - - ConsoleOutput co = consoleContainer.Acquire(); - - expose (co) { - ConsoleDeviceContract.Imp ep = co.device.channel; - if (ep != null) - { - ep.SendClear(); - switch receive - { - case ep.AckClear(): - break; - case ep.NakClear(): - break; - case ep.ChannelClosed(): - delete ep; - co.device.channel = null; - break; - } - } - } - consoleContainer.Release(co); - } - - public static void CursorFlash() - { - if (consoleContainer == null) return; - - ConsoleOutput co = consoleContainer.Acquire(); - - expose (co) { - ConsoleDeviceContract.Imp ep = co.device.channel; - if (ep != null) - { - ep.SendCursorFlash(); - switch receive - { - case ep.AckCursorFlash(): - break; - case ep.NakCursorFlash(): - break; - case ep.ChannelClosed(): - delete ep; - co.device.channel = null; - break; - } - } - } - consoleContainer.Release(co); - } - - public static void CursorHide() - { - if (consoleContainer == null) return; - - ConsoleOutput co = consoleContainer.Acquire(); - - expose (co) { - ConsoleDeviceContract.Imp ep = co.device.channel; - if (ep != null) { - ep.SendCursorHide(); - switch receive { - case ep.AckCursorHide(): - break; - case ep.NakCursorHide(): - break; - case ep.ChannelClosed(): - delete ep; - co.device.channel = null; - break; - } - } - } - consoleContainer.Release(co); - } - - public static void CursorShow() - { - if (consoleContainer == null) return; - - ConsoleOutput co = consoleContainer.Acquire(); - - expose (co) { - ConsoleDeviceContract.Imp ep = co.device.channel; - if (ep != null) { - ep.SendCursorShow(); - switch receive { - case ep.AckCursorShow(): - break; - case ep.NakCursorShow(): - break; - case ep.ChannelClosed(): - delete ep; - co.device.channel = null; - break; - } - } - } - consoleContainer.Release(co); - } - */ - - public static char[]! in ExHeap Write([Claims] char[]! in ExHeap buffer, - int index, int count) - { - if (consoleContainer == null) return buffer; - ConsoleOutput co = consoleContainer.Acquire(); - try { - expose (co) { - if (co.pipe.channel != null) { - return Write(buffer, index, count, ref co.pipe); - } - else { - return buffer; - } - } - } - finally { - consoleContainer.Release(co); - } - } - - /* - private static char[]! in ExHeap Write([Claims] char[]! in ExHeap buffer, - int index, int count, - ref DeviceData device) - { - expose (device) { - ConsoleDeviceContract.Imp channel = device.channel; - if (channel != null) - { - channel.SendWrite(buffer, index, count); - switch receive { - case channel.AckWrite(localside): - return localside; - - case channel.NakWrite(localside): - return localside; - - case channel.ChannelClosed(): - delete channel; - device.channel = null; - return new[ExHeap] char[0]; - } - } - else { - return buffer; - } - } - } - */ - - private static char[]! in ExHeap Write([Claims] char[]! in ExHeap buffer, - int index, int count, - ref PipeData pipe) - { - expose (pipe) { - UnicodePipeContract.Imp channel = pipe.channel; - if (channel != null) { - // char[]! in ExHeap buffer=Bitter.FromString2(value); - channel.SendWrite(buffer, index, count); - switch receive { - case channel.AckWrite(i_buffer): - return i_buffer; - - case channel.ChannelClosed(): - delete channel; - pipe.channel = null; - return new[ExHeap] char[0]; - - case unsatisfiable: - DebugStub.Break(); - throw new Exception("Pipe unsatisfiable "); - } - } - else { - return buffer; - } - } - } - - public static void Write(char[]! buffer, int index, int count) - { - if (consoleContainer == null) return; - - ConsoleOutput co = consoleContainer.Acquire(); - - expose (co) { - char[] in ExHeap side = co.side; - - if (side.Length < count) { - delete side; - co.side = side = new[ExHeap] char [count]; - } - - for (int i = 0; i < count; i++) { - side[i] = buffer[index + i]; - } - - if (co.pipe.channel != null) { - co.side = Write(side, index, count, ref co.pipe); - } - /* - else { - co.side = Write(side, index, count, ref co.device); - } - */ - } - consoleContainer.Release(co); - } - - public static void Write(String! value) - { - if (consoleContainer == null) return; - - ConsoleOutput co = consoleContainer.Acquire(); - - expose (co) { - - char[] in ExHeap side = co.side; - if (side.Length < value.Length) { - delete side; - co.side = side = new[ExHeap] char [value.Length]; - } - - for (int i = 0; i < value.Length; i++) { - side[i] = value[i]; - } - - if (co.pipe.channel != null) { - co.side = Write(side, 0, value.Length, ref co.pipe); - } - /* - else { - co.side = Write(side, 0, value.Length, ref co.device); - } - */ - } - consoleContainer.Release(co); - return; - } - - public static UnicodePipeContract.Imp:READY Swap([Claims] UnicodePipeContract.Imp:READY newOutput) { - if (consoleContainer == null) { - delete newOutput; - return null; - } - - ConsoleOutput co = consoleContainer.Acquire(); - - UnicodePipeContract.Imp result; - expose(co) { - result = co.pipe.Swap(newOutput); - } - consoleContainer.Release(co); - return result; - } - - } -} diff --git a/base/Applications/Runtime/Singularity/Memory/Stacks.cs b/base/Applications/Runtime/Singularity/Memory/Stacks.cs deleted file mode 100644 index 5388274..0000000 --- a/base/Applications/Runtime/Singularity/Memory/Stacks.cs +++ /dev/null @@ -1,161 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Stacks.cs - Primitive stack segment manager -// -// Note: -// - -namespace Microsoft.Singularity.Memory { - - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using Microsoft.Singularity; - using Microsoft.Singularity.X86; - using Microsoft.Singularity.V1.Services; - - [NoCCtor] - [CLSCompliant(false)] - [RequiredByBartok] - internal class Stacks { - - internal static unsafe void Initialize() - { - DebugStub.Print("Stacks.Initialize() called\n"); - } - - internal static unsafe void Finalize() - { - DebugStub.Print("Stacks.Finalize() called\n"); - } - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - [RequiredByBartok] - internal static extern void LinkStack0(); // Copy 0 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - [RequiredByBartok] - internal static extern void LinkStack4(); // Copy 4 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - [RequiredByBartok] - internal static extern void LinkStack8(); // Copy 8 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - [RequiredByBartok] - internal static extern void LinkStack12(); // Copy 12 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - [RequiredByBartok] - internal static extern void LinkStack16(); // Copy 16 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - [RequiredByBartok] - internal static extern void LinkStack20(); // Copy 20 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - [RequiredByBartok] - internal static extern void LinkStack24(); // Copy 24 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - [RequiredByBartok] - internal static extern void LinkStack28(); // Copy 28 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - [RequiredByBartok] - internal static extern void LinkStack32(); // Copy 32 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - [RequiredByBartok] - internal static extern void LinkStack36(); // Copy 36 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - [RequiredByBartok] - internal static extern void LinkStack40(); // Copy 40 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - [RequiredByBartok] - internal static extern void LinkStack44(); // Copy 44 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - [RequiredByBartok] - internal static extern void LinkStack48(); // Copy 48 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - [RequiredByBartok] - internal static extern void LinkStack52(); // Copy 52 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - [RequiredByBartok] - internal static extern void LinkStack56(); // Copy 56 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - [RequiredByBartok] - internal static extern void LinkStack60(); // Copy 60 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - [RequiredByBartok] - internal static extern void LinkStack64(); // Copy 64 bytes of arguments on stack. - - [ExternalStaticData] - internal static byte UnlinkStackBegin; - - [ExternalStaticData] - internal static byte UnlinkStackLimit; - } -} diff --git a/base/Applications/Runtime/Singularity/Naming/NameService.sg b/base/Applications/Runtime/Singularity/Naming/NameService.sg deleted file mode 100644 index c7ecf1f..0000000 --- a/base/Applications/Runtime/Singularity/Naming/NameService.sg +++ /dev/null @@ -1,30 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: DirectoryService.sg -// -// Note: -// -// This file provides an implementation of Directory.DirectoryService.NewClientEndpoint -// that calls through the ABI to retrieve the name-service channel endpoint. -// This is for use by applications; kernel components can call the "real" -// DirectoryService class directly. -// - -using Microsoft.Singularity; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.V1.Services; - -namespace Microsoft.Singularity.Directory -{ - public class DirectoryService - { - public static unsafe DirectoryServiceContract.Imp!:Ready NewClientEndpoint() - { - return (DirectoryServiceContract.Imp!)ProcessService.GetNamespaceEndpoint(); - } - } -} diff --git a/base/Applications/Runtime/Singularity/Processor.cs b/base/Applications/Runtime/Singularity/Processor.cs deleted file mode 100644 index 9ef1c6f..0000000 --- a/base/Applications/Runtime/Singularity/Processor.cs +++ /dev/null @@ -1,237 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Processor.cs -// -// Note: -// - -namespace Microsoft.Singularity -{ - using System; - using System.Runtime.InteropServices; - using System.Runtime.CompilerServices; - using System.Threading; - - using Microsoft.Singularity.X86; - using Microsoft.Singularity.V1.Services; - - [CLSCompliant(false)] - [NoCCtor] - public class Processor - { - public static long CyclesPerSecond - { - [NoHeapAllocation] - get { return ProcessService.GetCyclesPerSecond(); } - } - - public static ulong CycleCount - { - [NoHeapAllocation] - get { return GetCycleCount(); } - } - - //////////////////////////////////////////////////// External Methods. - // - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [StackBound(32)] - [NoHeapAllocation] - public static extern ulong GetCycleCount(); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [StackBound(32)] - [NoHeapAllocation] - internal static unsafe extern UIntPtr GetFrameEip(UIntPtr ebp); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [StackBound(32)] - [NoHeapAllocation] - internal static unsafe extern UIntPtr GetFrameEbp(UIntPtr ebp); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(32)] - [NoHeapAllocation] - internal static extern UIntPtr GetStackPointer(); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(32)] - [NoHeapAllocation] - internal static extern UIntPtr GetFramePointer(); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(32)] - [NoHeapAllocation] - internal static unsafe extern ThreadContext * GetCurrentThreadContext(); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(32)] - [NoHeapAllocation] - internal static unsafe extern ProcessorContext * GetCurrentProcessorContext(); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(32)] - [NoHeapAllocation] - internal static extern Thread GetCurrentThread(); - - ////////////////////////////////////////////////////////////////////////////// - // - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] - [NoHeapAllocation] - public static extern void WriteMsr(uint offset, - ulong value); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] - [NoHeapAllocation] - public static extern ulong ReadMsr(uint offset); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [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); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] - [NoHeapAllocation] - public static extern ulong ReadPmc(uint offset); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(32)] - [NoHeapAllocation] - internal static extern void EnterRing3(); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(32)] - [NoHeapAllocation] - public static extern bool AtKernelPrivilege(); - - ////////////////////////////////////////////////////////////////////// - // - // - // These methods are currently marked external because they are used - // by device drivers. We need a tool to verify that device drivers - // are in fact using them correctly! - // - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] - [NoHeapAllocation] - public static extern bool DisableInterrupts(); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] - [NoHeapAllocation] - public static extern void RestoreInterrupts(bool enabled); - - // Use this method for assertions only! - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] - [NoHeapAllocation] - public static extern bool InterruptsDisabled(); - - ////////////////////////////////////////////////////////////////////// - // - // - // These methods are public and safe to use from any where provided - // there's at least 2 call frame on the stack. - // - [NoHeapAllocation] - public static UIntPtr GetCallerEip() - { - UIntPtr currentFrame = GetFramePointer(); - UIntPtr callerFrame = GetFrameEbp(currentFrame); - if (callerFrame == UIntPtr.Zero) { - return UIntPtr.Zero; - } - UIntPtr callersCaller = GetFrameEip(callerFrame); - return callersCaller; - } - - /// - /// Provides a mini stack trace starting from the caller of the caller - /// of this method. - /// - [NoHeapAllocation] - public static void GetStackEips(out UIntPtr pc1, out UIntPtr pc2, out UIntPtr pc3) - { - pc1 = UIntPtr.Zero; - pc2 = UIntPtr.Zero; - pc3 = UIntPtr.Zero; - - UIntPtr currentFrame = GetFramePointer(); - UIntPtr callerFrame = GetFrameEbp(currentFrame); - if (callerFrame == UIntPtr.Zero) { - return; - } - pc1 = GetFrameEip(callerFrame); - callerFrame = GetFrameEbp(callerFrame); - if (callerFrame == UIntPtr.Zero) { - return; - } - pc2 = GetFrameEip(callerFrame); - callerFrame = GetFrameEbp(callerFrame); - if (callerFrame == UIntPtr.Zero) { - return; - } - pc3 = GetFrameEip(callerFrame); - } - - /// - /// Provides the full stack trace starting from the caller of the caller - /// of this method. - /// - /// Eip values in stack array from top to bottom - [NoHeapAllocation] - public static void GetStackEips(UIntPtr[] stack) - { - if (stack == null) { - return; - } - UIntPtr currentFrame = GetFramePointer(); - UIntPtr callerFrame = GetFrameEbp(currentFrame); - for (int index = 0; callerFrame != UIntPtr.Zero && index < stack.Length; index++) { - stack[index] = GetFrameEip(callerFrame); - callerFrame = GetFrameEbp(callerFrame); - } - } - } -} diff --git a/base/Applications/Runtime/System/Console.cs b/base/Applications/Runtime/System/Console.cs deleted file mode 100644 index 0cd4eb6..0000000 --- a/base/Applications/Runtime/System/Console.cs +++ /dev/null @@ -1,319 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== - -namespace System { - using Microsoft.Singularity; -#if !MINRUNTIME - using Microsoft.Singularity.Io; -#endif - using System; - using System.Text; - using System.Globalization; - using System.Runtime.CompilerServices; - - using System.Runtime.InteropServices; - - // Provides static fields for console input and output. Use - // Console.In for input from the standard input stream (stdin), - // Console.Out for output to stdout, and Console.Error - // for output to stderr. If any of those console streams are - // redirected from the command line, these streams will be redirected. - // A program can also redirect its own output or input with the - // SetIn, SetOut, and SetError methods. - // - // The distinction between Console.Out and Console.Error is useful - // for programs that redirect output to a file or a pipe. Note that - // stdout and stderr can be output to different files at the same - // time from the DOS command line: - // - // someProgram 1> out 2> err - // - //| - public sealed class Console - { - private Console() - { - throw new NotSupportedException("NotSupported_Constructor"); - } - - static Console() { - } - - - private static void WriteInternal(char[] buffer, int index, int count) - { -#if !MINRUNTIME - ConsoleOutput.Write(buffer, index, count); -#else - for (int i = 0; i < count; i++) { - DebugStub.Print(buffer[index + i]); - } -#endif - } - - private static void WriteInternal(String value) - { -#if !MINRUNTIME - ConsoleOutput.Write(value); -#else - DebugStub.Print(value); -#endif - } - - private static void WriteNewLine() - { - WriteInternal(Environment.NewLine); - } - - //| - public static void WriteLine() - { - WriteNewLine(); - } - - //| - public static void WriteLine(String value) - { - WriteInternal(value); - WriteNewLine(); - } - - //| - public static void WriteLine(bool value) - { - WriteInternal(value.ToString()); - WriteNewLine(); - } - - //| - public static void WriteLine(char value) - { - WriteInternal(value.ToString()); - WriteNewLine(); - } - - //| - public static void WriteLine(char[] buffer) - { - WriteInternal(buffer, 0, buffer.Length); - WriteNewLine(); - } - - //| - public static void WriteLine(char[] buffer, int index, int count) - { - WriteInternal(buffer, index, count); - WriteNewLine(); - } - - //| - public static void WriteLine(decimal value) - { - WriteInternal(value.ToString()); - WriteNewLine(); - } - - //| - public static void WriteLine(double value) - { - WriteInternal(value.ToString()); - WriteNewLine(); - } - - //| - public static void WriteLine(float value) - { - WriteInternal(value.ToString()); - WriteNewLine(); - } - - //| - public static void WriteLine(int value) - { - WriteInternal(value.ToString()); - WriteNewLine(); - } - - //| - [CLSCompliant(false)] - public static void WriteLine(uint value) - { - WriteInternal(value.ToString()); - WriteNewLine(); - } - - //| - public static void WriteLine(long value) - { - WriteInternal(value.ToString()); - WriteNewLine(); - } - - //| - [CLSCompliant(false)] - public static void WriteLine(ulong value) - { - WriteInternal(value.ToString()); - WriteNewLine(); - } - - //| - public static void WriteLine(Object value) - { - WriteInternal(value.ToString()); - WriteNewLine(); - } - - //| - public static void WriteLine(String format, Object arg0) - { - WriteInternal(String.Format(format, arg0)); - WriteNewLine(); - } - - //| - public static void WriteLine(String format, Object arg0, Object arg1) - { - WriteInternal(String.Format(format, arg0, arg1)); - WriteNewLine(); - } - - //| - public static void WriteLine(String format, Object arg0, Object arg1, Object arg2) - { - WriteInternal(String.Format(format, arg0, arg1, arg2)); - WriteNewLine(); - } - - //| - public static void WriteLine(String format, params Object[] args) - { - WriteInternal(String.Format(format, args)); - WriteNewLine(); - } - - //| - public static void Write(String format, Object arg0) - { - WriteInternal(String.Format(format, arg0)); - } - - //| - public static void Write(String format, Object arg0, Object arg1) - { - WriteInternal(String.Format(format, arg0, arg1)); - } - - //| - public static void Write(String format, Object arg0, Object arg1, Object arg2) - { - WriteInternal(String.Format(format, arg0, arg1, arg2)); - } - - //| - public static void Write(String format, params Object[] args) - { - WriteInternal(String.Format(format, args)); - } - - //| - public static void Write(bool value) - { - WriteInternal(value.ToString()); - } - - //| - public static void Write(char value) - { - WriteInternal(value.ToString()); - } - - //| - public static void Write(char[] buffer) - { - WriteInternal(buffer, 0, buffer.Length); - } - - //| - public static void Write(char[] buffer, int index, int count) - { - WriteInternal(buffer, index, count); - } - - //| - public static void Write(double value) - { - WriteInternal(value.ToString()); - } - - //| - public static void Write(decimal value) - { - WriteInternal(value.ToString()); - } - - //| - public static void Write(float value) - { - WriteInternal(value.ToString()); - } - - //| - public static void Write(int value) - { - WriteInternal(value.ToString()); - } - - //| - [CLSCompliant(false)] - public static void Write(uint value) - { - WriteInternal(value.ToString()); - } - - //| - public static void Write(long value) - { - WriteInternal(value.ToString()); - } - - //| - [CLSCompliant(false)] - public static void Write(ulong value) - { - WriteInternal(value.ToString()); - } - - //| - public static void Write(Object value) - { - WriteInternal(value.ToString()); - } - - //| - public static void Write(String value) - { - WriteInternal(value); - } - - public static int Read() - { -#if !MINRUNTIME - return ConsoleInput.ReadChar(); -#else - return -1; -#endif - } - public static string ReadLine() - { -#if !MINRUNTIME - return ConsoleInput.ReadLine(); -#else - return ""; -#endif - } - } -} diff --git a/base/Applications/Runtime/System/Environment.cs b/base/Applications/Runtime/System/Environment.cs deleted file mode 100644 index 9eb2883..0000000 --- a/base/Applications/Runtime/System/Environment.cs +++ /dev/null @@ -1,73 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================ -** -** Class: Environment -** -** -** Purpose: Provides some basic access to some environment -** functionality. -** -** Date: March 3, 2000 -** -============================================================*/ -namespace System { - using System.Globalization; - using System.Collections; - using System.Text; - using System.Runtime.InteropServices; - using System.Runtime.CompilerServices; - using System.Reflection; - using System.Diagnostics; - using Microsoft.Singularity; - using Microsoft.Singularity.V1.Services; - - //| - public sealed class Environment { - - private Environment() {} // Prevent from begin created - - /*==================================TickCount=================================== - **Action: Gets the number of ticks since the system was started. - **Returns: The number of ticks since the system was started. - **Arguments: None - **Exceptions: None - ==============================================================================*/ - //| - public static int TickCount { - get { - return unchecked((int)(ProcessService.GetUpTime().Ticks / 10000)); - } - } - - /*===================================NewLine==================================== - **Action: A property which returns the appropriate newline string for the given - ** platform. - **Returns: \r\n on Win32. - **Arguments: None. - **Exceptions: None. - ==============================================================================*/ - //| - public static String NewLine { - get { - return "\r\n"; - } - } - - //| - public static void Exit(int exitCode) { - ProcessService.Stop(exitCode); - } - - // - public static String StackTrace - { - get { - return " - [CCtorIsRunDuringStartup] - [StructLayout(LayoutKind.Sequential)] - [CLSCompliant(false)] - [RequiredByBartok] - public sealed class Thread - { - // Singularity specific fields - // Need CPU context - // Need stack - - // Bartok specific fields - [RequiredByBartok] // Thread-specific alloc heap - internal SegregatedFreeList segregatedFreeList; - [RequiredByBartok] // Thread-specific bump allocator - internal BumpAllocator bumpAllocator; - - internal AutoResetEvent processGcEvent; - - private AutoResetEvent autoEvent; - private ManualResetEvent joinEvent; - - internal Thread blockingCctorThread; - - // Singularity specific fields - [AccessedByRuntime("referenced from halforgc.asm")] - internal unsafe ThreadContext * context; - internal ThreadHandle threadHandle; - - // Most recently thrown exception object that the thread - // did not catch at all (i.e. that propagated to the bottom - // of the stack or to a kernel/process boundary without - // encountering an appropriate catch clause). - private Exception lastUncaughtException; - - private bool ignoredByJoinAll; // for "service" threads - - private Object m_ExceptionStateInfo; // Exception info latched to the thread on a thread abort - - // Bartok specific fields - internal Thread nextThread; // Link for linked lists of threads - - // This array is of length 1 and contains a queue item for this - // thread. It allows WaitOne and the scheduler to add this thread - // to their queues without allocating memory. - internal ThreadQueueItem[] singleQueueItem; - - // MultiUseWord (object header) fields. - internal UIntPtr externalMultiUseObjAllocListHead; - internal UIntPtr externalMultiUseObjAllocListTail; - - // Bartok specific fields - internal int threadIndex; - private ThreadStart threadStart; - private ThreadState threadState; - internal int waitingCriticalSectionDepth; - internal Object waitingObject; // Not used, but useful for debugging - - [RequiredByBartok] - internal TryAllManager tryAllManager; - - private static bool closing; - private static long totalArrayAllocations; - private static long totalArrayBytesAllocated; - private static long totalBytes; - private static long totalStructBytesAllocated; - - internal static Thread[] threadTable; - private static SpinLock threadTableLock; - - private static LocalDataStore localDataStore; - - internal static Thread initialThread; - - // This is used by the Bartok backend. When Bartok tries to generate - // callback stub for delegate, it checks to see if it is - // ThreadProc, if it is not, Bartok adds leaveGCSafeState and - // enterGCSafeState around the delegate call. - [RequiredByBartok] - private unsafe delegate uint ThreadProc(void *param); - - /*========================================================================= - ** This manager is responsible for storing the global data that is - ** shared amongst all the thread local stores. - =========================================================================*/ - static private LocalDataStoreMgr m_LocalDataStoreMgr; - - internal const int maxThreads = 1024; // Must be power of 2 >= 64 - private static int threadIndexGenerator; - - /*========================================================================= - ** Creates a new Thread object which will begin execution at - ** start.ThreadStart on a new thread when the Start method is called. - ** - ** Exceptions: ArgumentNullException if start == null. - =========================================================================*/ - //| - public unsafe Thread(ThreadStart start) - { - Tracing.Log(Tracing.Audit, "Application Thread()"); - - if (start == null) { - throw new ArgumentNullException("start"); - } - - threadIndex = -1; - threadState = ThreadState.Unstarted; - threadStart = start; - - // Create the event for the thread to wait upon - autoEvent = new AutoResetEvent(false); - joinEvent = new ManualResetEvent(false); - processGcEvent = new AutoResetEvent(false); - singleQueueItem = new ThreadQueueItem [1] { new ThreadQueueItem(this) }; - - // Find a usable entry in the thread table - bool disabled = Processor.DisableInterrupts(); - Thread.threadTableLock.Acquire(CurrentThread); - try { - for (int i = 0; i < threadTable.Length; i++) { - int index = (threadIndexGenerator + i) % threadTable.Length; - if (threadTable[index] == null) { - threadTable[index] = this; - this.threadIndex = index; - threadIndexGenerator = index + 1; - // NB: We call this once, afterwards the GC visitor calls it. - break; - } - } - } finally { - Thread.threadTableLock.Release(CurrentThread); - Processor.RestoreInterrupts(disabled); - } - - VTable.Assert(threadIndex >= 0, "Out of thread slots!"); - - //MemoryBarrier(); - - // Must check closing after being insert into table to avoid race condition. - if (closing) { - threadState = ThreadState.Stopped; - joinEvent.Set(); - Tracing.Log(Tracing.Warning, "Aborting: Runtime closing."); - return; - } - - ThreadHandle handleOnStack; - UIntPtr threadContext; - if (!ThreadHandle.Create(threadIndex, - new ContainerHandle(), - out handleOnStack, - out threadContext)) { - Tracing.Log(Tracing.Warning, "Aborting: ThreadHandle.Create failed."); - threadState = ThreadState.Stopped; - joinEvent.Set(); - return; - } - this.threadHandle = handleOnStack; - this.context = (ThreadContext *) threadContext; - this.context->threadIndex = unchecked((ushort) threadIndex); - this.context->UpdateAfterGC(this); - } - - - - /// - /// Finalizer is responsible for freeing handle that keeps corresponding - /// kernel object live. - /// - ~Thread() { - if (this.threadHandle.id != 0) { - ThreadHandle.Dispose(this.threadHandle); - this.threadHandle = new ThreadHandle(); - } - } - - /*========================================================================= - ** Spawns off a new thread which will begin executing at the ThreadStart - ** method on the IThreadable interface passed in the constructor. Once the - ** thread is dead, it cannot be restarted with another call to Start. - ** - ** Exceptions: ThreadStateException if the thread has already been started. - =========================================================================*/ - //| - public void Start() - { - lock ((Object) this) { - if (closing) { - throw new ThreadStateException("Cannot start thread when closing"); - } - - ThreadState oldState = threadState; - if (oldState != ThreadState.Unstarted) { - throw new ThreadStateException("Cannot start thread in state "+oldState); - } - - threadState = ThreadState.Running; - - // Tell the GC that we have created the thread - GC.NewThreadNotification(this, false); - - ThreadHandle.Start(threadHandle); - GC.KeepAlive(this); - } - } - - // HalInitContext sets ThreadStub as the first process code to be - // executed in a new thread context. - [AccessedByRuntime("referenced from hal.cpp")] - private static unsafe void ThreadStub(int threadIndex) - { - Transitions.ThreadStart(); - GC.ThreadStartNotification(threadIndex); - Thread currentThread = threadTable[threadIndex]; - if (AddThread(threadIndex)) { - Tracing.Log(Tracing.Audit, "ThreadStub(atid={0}) Entered", - (UIntPtr)unchecked((uint)threadIndex)); - - ThreadStart startFun = currentThread.threadStart; - - try { - startFun(); - } catch (Exception e) { - DebugStub.WriteLine("Thread {0} failed with exception {1}\n", - __arglist(threadIndex, e)); - currentThread.lastUncaughtException = e; - VTable.Assert(e == null, "Thread "+threadIndex+ - " failed with exception "+e); - } - Tracing.Log(Tracing.Audit, "ThreadStub(atid={0}) Exiting", - (UIntPtr)unchecked((uint)threadIndex)); - } - - currentThread.joinEvent.Set(); - RemoveThread(threadIndex); - GC.DeadThreadNotification(currentThread); - - bool disabled = Processor.DisableInterrupts(); - // this is a dangerous locking strategy as the transition code - // may decide it needs to wake up another thread. - Thread.threadTableLock.Acquire(currentThread); - try { - Transitions.ThreadEnd(threadIndex); - // You may not do any calls out of proc after this point! - threadTable[threadIndex] = null; - Transitions.DeadThreadNotification(threadIndex); - } finally { - Thread.threadTableLock.Release(currentThread); - Processor.RestoreInterrupts(disabled); - } - } - - /*==================================================================== - * Support for service threads. Service threads should not count - * towards keeping a process alive. When all non-service threads - * have terminated, the service threads are asked to stop themselves - * so the process can terminate gracefully. - *===================================================================*/ - - // The number of non-service threads running in the process - private static int threadCount; - private static Object notificationTableObject; - private static Object[] notificationTable; - - public delegate void StopServiceNotice(); - - public void MakeServiceThread(StopServiceNotice notification) - { - Tracing.Log(Tracing.Audit, "MakeServiceThread {0}", - (UIntPtr) threadIndex); - if (this != Thread.CurrentThread) { - throw new Exception("Only the thread itself may call MakeServiceThread"); - } - if (notificationTable == null) { - int tableSize = threadTable.Length; - Interlocked.CompareExchange(ref notificationTableObject, - new Object[tableSize], - null); - notificationTable = (Object[]) notificationTableObject; - } - Tracing.Log(Tracing.Audit, " previous notification: {0:x8}", - Magic.addressOf(notificationTable[threadIndex])); - // BUGBUG: Should have been Interlocked.Exchange, but that - // doesn't work due to a Bartok codegen bug. - Object oldNotification = notificationTable[threadIndex]; - while (Interlocked.CompareExchange(ref notificationTable[threadIndex], notification, oldNotification) != oldNotification) { - oldNotification = notificationTable[threadIndex]; - } - if (oldNotification == null) { - // We made the thread a service thread for the first time. - if (Interlocked.Decrement(ref threadCount) == 0) { - NotifyServiceThreads(); - } - } - VTable.Assert(threadCount >= 0); - } - - public void ClearServiceThread(Thread thread) - { - Tracing.Log(Tracing.Audit, "ClearServiceThread"); - if (this != Thread.CurrentThread) { - throw new Exception("Only the thread itself may call ClearServiceThread"); - } - if (notificationTable == null) { - return; - } - if (Interlocked.Exchange(ref notificationTable[threadIndex], - null) != null) { - // We cleared the notification - Interlocked.Increment(ref threadCount); - } - VTable.Assert(threadCount >= 0); - } - - private static bool AddThread(int index) - { - Tracing.Log(Tracing.Audit, "AddThread {0} ({1})", - (UIntPtr) index, (UIntPtr) threadCount); - VTable.Assert(threadCount >= 0); - if (Interlocked.Increment(ref threadCount) == 1 && - notificationTable != null) { - // The thread was started after we started sending out - // notifications, so indicate that the thread should not - // really be started - return false; - } else { - return true; - } - } - - internal static void RemoveThread(int index) - { - Tracing.Log(Tracing.Audit, "RemoveThread {0} ({1})", - (UIntPtr) index, (UIntPtr) threadCount); - if (Interlocked.Decrement(ref threadCount) == 0) { - NotifyServiceThreads(); - } - VTable.Assert(threadCount >= 0); - } - - private static void NotifyServiceThreads() - { - Tracing.Log(Tracing.Audit, "NotifyServiceThreads"); - if (notificationTable == null) { - return; - } - for (int i = 0; i < notificationTable.Length; i++) { - if (notificationTable[i] != null) { - Tracing.Log(Tracing.Audit, " Notifying thread {0}", - (UIntPtr) i); - ((StopServiceNotice)notificationTable[i])(); - } - } - } - - - /*========================================================================= - ** Returns true if the thread has been started and is not dead. - =========================================================================*/ - //| - public bool IsAlive { - [NoHeapAllocation] - get { return (threadState != ThreadState.Unstarted && - threadState != ThreadState.Stopped); } - } - - - /*========================================================================= - ** Waits for the thread to die. - ** - ** Exceptions: ThreadStateException if the thread has not been started yet. - =========================================================================*/ - //| - public void Join() - { - Join(SchedulerTime.MaxValue); - } - - /*========================================================================= - ** Waits for the thread to die or for timeout milliseconds to elapse. - ** Returns true if the thread died, or false if the wait timed out. - ** - ** Exceptions: ArgumentException if timeout < 0. - ** ThreadStateException if the thread has not been started yet. - =========================================================================*/ - //| - public bool Join(TimeSpan timeout) - { - if (threadState == ThreadState.Unstarted) { - throw new ThreadStateException(); - } - return joinEvent.WaitOne(timeout); - } - - public bool Join(SchedulerTime timeout) - { - if (threadState == ThreadState.Unstarted) { - throw new ThreadStateException(); - } - return joinEvent.WaitOne(timeout); - } - - internal static bool JoinAll() - { - // To avoid races, join all does the following: - // 1) Wait for all known peer threads to terminate. - // 2) Set the closing flag to disallow creating of new threads. - // 3) Wait for any threads that have started in the mean time. - - for (uint iteration = 0; iteration < 2; iteration++) { - for (int i = 0; i < threadTable.Length; i++) { - Thread thread = null; - - bool disabled = Processor.DisableInterrupts(); - Thread.threadTableLock.Acquire(CurrentThread); - try { - thread = threadTable[i]; - } - finally { - Thread.threadTableLock.Release(CurrentThread); - Processor.RestoreInterrupts(disabled); - } - - if (thread != null && - thread != CurrentThread && - thread.threadState != ThreadState.Unstarted && - !thread.ignoredByJoinAll) { - thread.Join(); - } - } - closing = true; - } - return true; - } - - /*========================================================================= - ** Suspends the current thread for timeout milliseconds. If timeout == 0, - ** forces the thread to give up the remainder of its timeslice. - ** - ** Exceptions: ArgumentException if timeout < 0. - =========================================================================*/ - - public static void Sleep(int milliseconds) - { - Sleep(TimeSpan.FromMilliseconds(milliseconds)); - } - - //| - public static void Sleep(TimeSpan timeout) - { - ThreadHandle.Sleep(timeout); - } - - /* wait for a length of time proportional to 'iterations'. Each iteration is should - only take a few machine instructions. Calling this API is preferable to coding - an explicit busy loop because the hardware can be informed that it is busy waiting. */ - - //| - [NoHeapAllocation] - public static void SpinWait(int iterations) - { - for (int i = iterations; i > 0; i--) { - // Ensure that the optimizer doesn't remove this - NativeNoOp(); - } - } - - [Intrinsic] - [NoHeapAllocation] - public static extern void NativeNoOp(); - - internal static int GetCurrentProcessIndex() { - return ProcessService.GetCurrentProcessId(); - } - - internal bool WaitForMonitor(SchedulerTime timeOut) - { - return autoEvent.WaitOne(timeOut); - } - - internal bool WaitForEvent(SchedulerTime timeOut) - { - DebugStub.Break(); - return autoEvent.WaitOne(timeOut); - } - - internal bool WaitForEvent(TimeSpan timeout) - { - DebugStub.Break(); - return autoEvent.WaitOne(timeout); - } - - internal static unsafe bool WaitForGCEvent(int currentThreadIndex) - { - AutoResetEvent are = - threadTable[currentThreadIndex].processGcEvent; - // BUGBUG: The restoration of the gcState should be taken - // care of by the compiler. - return are.WaitOneNoGC(); - } - - internal void SignalMonitor() - { - autoEvent.Set(); - } - - internal void SignalEvent() - { - DebugStub.Break(); - autoEvent.Set(); - } - - [Inline] - internal static void SignalGCEvent(int currentThreadIndex, - int threadIndex) - { - SignalGCEvent(threadIndex); - } - - internal static unsafe void SignalGCEvent(int threadIndex) - { - Thread thread = threadTable[threadIndex]; - if (thread == null) { - return; - } - thread.processGcEvent.SetNoGC(); - } - - //| - public static Thread CurrentThread - { - [NoHeapAllocation] - [NoStackLinkCheck] - get { - return Processor.GetCurrentThread(); - } - } - - internal ThreadHandle Handle - { - [NoHeapAllocation] - get { return threadHandle; } - } - - [NoStackLinkCheck] - [RequiredByBartok] - [NoHeapAllocation] - private static Thread GetCurrentThreadNative() - { - return Processor.GetCurrentThread(); - } - - [NoStackLinkCheck] - [RequiredByBartok] - [NoHeapAllocation] - internal static int GetCurrentThreadIndex() - { - return Processor.GetCurrentThread().threadIndex; - } - - /*========================================================================= - ** Return the thread state as a consistent set of bits. This is more - ** general then IsAlive or IsBackground. - =========================================================================*/ - //| - public ThreadState ThreadState - { - [NoHeapAllocation] - get { return threadState; } - } - - [NoHeapAllocation] - public int GetThreadId() - { - return threadIndex; - } - - /*========================================================================= - ** Allocates an un-named data slot. The slot is allocated on ALL the - ** threads. - =========================================================================*/ - //| - public static LocalDataStoreSlot AllocateDataSlot() - { - return m_LocalDataStoreMgr.AllocateDataSlot(); - } - - /*========================================================================= - ** Allocates a named data slot. The slot is allocated on ALL the - ** threads. Named data slots are "public" and can be manipulated by - ** anyone. - =========================================================================*/ - //| - public static LocalDataStoreSlot AllocateNamedDataSlot(String name) - { - return m_LocalDataStoreMgr.AllocateNamedDataSlot(name); - } - - /*========================================================================= - ** Looks up a named data slot. If the name has not been used, a new slot is - ** allocated. Named data slots are "public" and can be manipulated by - ** anyone. - =========================================================================*/ - //| - public static LocalDataStoreSlot GetNamedDataSlot(String name) - { - return m_LocalDataStoreMgr.GetNamedDataSlot(name); - } - - /*========================================================================= - ** Frees a named data slot. The slot is allocated on ALL the - ** threads. Named data slots are "public" and can be manipulated by - ** anyone. - =========================================================================*/ - //| - public static void FreeNamedDataSlot(String name) - { - m_LocalDataStoreMgr.FreeNamedDataSlot(name); - } - - /*========================================================================= - ** Retrieves the value from the specified slot on the current thread. - =========================================================================*/ - //| - public static Object GetData(LocalDataStoreSlot slot) - { - m_LocalDataStoreMgr.ValidateSlot(slot); - - if (localDataStore != null) { - return localDataStore.GetData(slot); - } - return null; - } - - /*========================================================================= - ** Sets the data in the specified slot on the currently running thread. - =========================================================================*/ - //| - public static void SetData(LocalDataStoreSlot slot, Object data) - { - // Create new DLS if one hasn't been created for this thread. - if (localDataStore == null) { - localDataStore = m_LocalDataStoreMgr.CreateLocalDataStore(); - } - localDataStore.SetData(slot, data); - } - - /*=============================================================*/ - - internal Object ExceptionState - { - [NoHeapAllocation] - get { return m_ExceptionStateInfo;} - [NoHeapAllocation] - set { m_ExceptionStateInfo = value;} - } - - // - // This is just designed to prevent compiler warnings. - // This field is used from native, but we need to prevent the compiler warnings. - // -#if _DEBUG - private void DontTouchThis() - { - threadStart = null; - m_Priority = 0; - } -#endif - /*========================================================================= - ** Volatile Read & Write and MemoryBarrier methods. - ** Provides the ability to read and write values ensuring that the values - ** are read/written each time they are accessed. - =========================================================================*/ - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern byte VolatileRead(ref byte address); - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern short VolatileRead(ref short address); - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern int VolatileRead(ref int address); - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern long VolatileRead(ref long address); - - //| - [CLSCompliant(false)] - [Intrinsic] - [NoHeapAllocation] - public static extern sbyte VolatileRead(ref sbyte address); - - //| - [CLSCompliant(false)] - [Intrinsic] - [NoHeapAllocation] - public static extern ushort VolatileRead(ref ushort address); - - //| - [CLSCompliant(false)] - [Intrinsic] - [NoHeapAllocation] - public static extern uint VolatileRead(ref uint address); - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern IntPtr VolatileRead(ref IntPtr address); - - //| - [CLSCompliant(false)] - [Intrinsic] - [NoHeapAllocation] - public static extern UIntPtr VolatileRead(ref UIntPtr address); - - //| - [CLSCompliant(false)] - [Intrinsic] - [NoHeapAllocation] - public static extern ulong VolatileRead(ref ulong address); - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern float VolatileRead(ref float address); - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern double VolatileRead(ref double address); - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern Object VolatileRead(ref Object address); - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern void VolatileWrite(ref byte address, byte value); - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern void VolatileWrite(ref short address, short value); - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern void VolatileWrite(ref int address, int value); - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern void VolatileWrite(ref long address, long value); - - //| - [CLSCompliant(false)] - [Intrinsic] - [NoHeapAllocation] - public static extern void VolatileWrite(ref sbyte address, sbyte value); - - //| - [CLSCompliant(false)] - [Intrinsic] - [NoHeapAllocation] - public static extern void VolatileWrite(ref ushort address, ushort value); - - //| - [CLSCompliant(false)] - [Intrinsic] - [NoHeapAllocation] - public static extern void VolatileWrite(ref uint address, uint value); - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern void VolatileWrite(ref IntPtr address, IntPtr value); - - //| - [CLSCompliant(false)] - [Intrinsic] - [NoHeapAllocation] - public static extern void VolatileWrite(ref UIntPtr address, UIntPtr value); - - //| - [CLSCompliant(false)] - [Intrinsic] - [NoHeapAllocation] - public static extern void VolatileWrite(ref ulong address, ulong value); - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern void VolatileWrite(ref float address, float value); - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern void VolatileWrite(ref double address, double value); - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern void VolatileWrite(ref Object address, Object value); - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern void MemoryBarrier(); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(12)] - [NoHeapAllocation] - private static unsafe extern Thread HalGetThread(); - - [NoHeapAllocation] - public static void Yield() - { - ThreadHandle.Yield(); - } - - [NoHeapAllocation] - public bool IsStopped() - { - return (threadState == ThreadState.Stopped); - } - - [PreInitRefCounts] - static unsafe Thread() - { - threadIndexGenerator = 1; - - // Enable Thread.CurrentThread as soon as we can! - initialThread = Magic.toThread(BootstrapMemory.Allocate(typeof(Thread))); - initialThread.threadState = ThreadState.Running; - initialThread.threadIndex = 0; - - // Allocate tables for thread management - threadTable = (Thread[]) - BootstrapMemory.Allocate(typeof(Thread[]), maxThreads); - - // Initialize the thread and event tables - threadTable[initialThread.threadIndex] = initialThread; - initialThread.context = Processor.GetCurrentThreadContext(); - initialThread.context->threadIndex = - unchecked((ushort) initialThread.threadIndex); - initialThread.context->UpdateAfterGC(initialThread); - - Tracing.Log(Tracing.Debug, "InitialThread={0:x8}", - Magic.addressOf(initialThread)); - } - - internal static unsafe void FinishInitializeThread() - { - int threadIndex = initialThread.threadIndex; - // Get the GC ready for initialThread - Transitions.RuntimeInitialized(); - Transitions.ThreadStart(); - initialThread.processGcEvent = new AutoResetEvent(false); - initialThread.autoEvent = new AutoResetEvent(false); - initialThread.joinEvent = new ManualResetEvent(false); - initialThread.singleQueueItem = - new ThreadQueueItem [1] { new ThreadQueueItem(initialThread) }; - // Use CurrentThread to find our initial handle: - VTable.Assert(initialThread == CurrentThread); - initialThread.threadHandle = ThreadHandle.CurrentThread(); - // Instantiate the static variable that needs to be initialized - m_LocalDataStoreMgr = new LocalDataStoreMgr(); - AddThread(threadIndex); - } - - public TimeSpan ExecutionTime - { - get { - TimeSpan t = ThreadHandle.GetExecutionTime(threadHandle); - GC.KeepAlive(this); - return t; - } - } - - internal static - void VisitBootstrapData(GCs.NonNullReferenceVisitor visitor) - { - visitor.VisitReferenceFields(Thread.initialThread); - visitor.VisitReferenceFields(Thread.threadTable); - } - - internal static unsafe void UpdateAfterGC() - { - // Update all the thread pointers in the thread contexts - for (int i = 0; i < threadTable.Length; i++) { - Thread thread = threadTable[i]; - if (thread != null) { - thread.context->UpdateAfterGC(thread); - } - } - } - - // Cache for ABI synchronization - private SyncHandle[] syncHandles; - internal SyncHandle[] GetSyncHandles(int length) - { - if (syncHandles == null || - syncHandles.Length < length) { - - syncHandles = new SyncHandle[length + 8]; - } - return syncHandles; - } - - - // Caches for Select synchronization - // We use stacks, because selectable abstractions might - // internally implement HeadMatches using select receive - // which is called from within an outer select. - // NOTE however that internal selects should never block - // (use timeout) - private Stack selectBoolsStack; - private Stack selectObjectsStack; - private Stack selectSyncHandlesStack; - - public bool[] PopSelectBools(int size) - { - if (selectBoolsStack == null) { - selectBoolsStack = new Stack(); - } - if (selectBoolsStack.Count == 0) { - return new bool [size]; - } - bool[] selectBools = (bool[])selectBoolsStack.Pop(); - if (selectBools.Length < size) { - return new bool [size]; - } - return selectBools; - } - - public void PushSelectBools(bool[] cache) { - selectBoolsStack.Push(cache); - } - - public ISelectable[] PopSelectObjects(int size) - { - if (selectObjectsStack == null) { - selectObjectsStack = new Stack(); - } - if (selectObjectsStack.Count == 0) { - return new ISelectable [size]; - } - ISelectable[] selectObjects = (ISelectable[])selectObjectsStack.Pop(); - if (selectObjects.Length < size) { - return new ISelectable [size]; - } - return selectObjects; - } - public void PushSelectObjects(ISelectable[] cache) { - selectObjectsStack.Push(cache); - } - - public SyncHandle[] PopSelectSyncHandles(int size) - { - if (selectSyncHandlesStack == null) { - selectSyncHandlesStack = new Stack(); - } - if (selectSyncHandlesStack.Count == 0) { - return new SyncHandle [size]; - } - SyncHandle[] selectSyncHandles = (SyncHandle[])selectSyncHandlesStack.Pop(); - if (selectSyncHandles.Length < size) { - return new SyncHandle [size]; - } - return selectSyncHandles; - } - public void PushSelectSyncHandles(SyncHandle[] cache) { - selectSyncHandlesStack.Push(cache); - } - - - - // Given a frame's range in memory (its esp/ebp), check whether - // the frame contains the top transition record. If so, - // prepare for making a transition from process mode back - // to kernel mode. - [AccessedByRuntime("referenced from halasm.asm")] - [NoStackLinkCheck] // We don't want to throw an exception here; - // Therefore, we cannot risk allocating stack segments, - // and we should only call other NoStackLinkCheck functions (XXX). - internal static unsafe UIntPtr CheckKernelProcessBoundary(UIntPtr esp, UIntPtr ebp, Exception exn) - { - ThreadContext *context = Processor.GetCurrentThreadContext(); - CallStack.TransitionRecord *topMarker = context->kernelMarkers; - CallStack.TransitionRecord *secondMarker = context->stackMarkers; - UIntPtr topMarkerPtr = (UIntPtr) topMarker; - // If the top marker is in our frame, we've reached a boundary: - if (esp < topMarkerPtr && topMarkerPtr <= ebp) { - context->uncaughtFlag = true; - Thread.CurrentThread.lastUncaughtException = exn; - Processor.GetCurrentThreadContext()->SetKernelMode(); - return 1; - } - else { - return 0; - } - } - - // Most recently thrown exception object that the thread - // did not catch at all (i.e. that propagated to the bottom - // of the stack without encountering an appropriate catch clause). - public Exception LastUncaughtException - { - [NoHeapAllocation] - get { - return lastUncaughtException; - } - } - - // Tell JoinAll not to block waiting for this thread to exit. - // Some special threads (e.g. the finalizer thread) need to run - // only as long as there are other threads running, and should - // not be considered by JoinAll. - [NoHeapAllocation] - internal void SetIgnoredByJoinAll() - { - ignoredByJoinAll = true; - } - } - - // This class is designed to support queues whose enqueue, - // dequeue, and remove operations do not allocate memory. - // This feature is useful when writing code that needs to - // do such operations with interrupts off. - [CLSCompliant(false)] - public class ThreadQueue - { - private ThreadQueueItem head = null; - private ThreadQueueItem tail = null; - - [NoHeapAllocation] - public void Enqueue(ThreadQueueItem item) - { - VTable.Assert(item.Next == null); - VTable.Assert(item.Prev == null); - VTable.Assert(item.Queue == null); - - item.Queue = this; - item.Prev = tail; - - if (tail != null) { - VTable.Assert(tail.Next == null); - tail.Next = item; - } else { - VTable.Assert(head == null); - head = item; - } - - tail = item; - } - - [NoHeapAllocation] - public ThreadQueueItem Dequeue() - { - ThreadQueueItem item = head; - - if (item != null) { - Remove(item); - } - - return item; - } - - [NoHeapAllocation] - public void Remove(ThreadQueueItem item) - { - VTable.Assert(item.Queue == this); - - if (item.Next != null) { - item.Next.Prev = item.Prev; - } else { - VTable.Assert(item == tail); - tail = item.Prev; - } - - if (item.Prev != null) { - item.Prev.Next = item.Next; - } else { - VTable.Assert(item == head); - head = item.Next; - } - - item.Next = null; - item.Prev = null; - item.Queue = null; - } - - [NoHeapAllocation] - public bool IsEmpty() - { - return (head == null); - } - } - - [CLSCompliant(false)] - public class ThreadQueueItem - { - public readonly Thread Thread = null; - - public ThreadQueueItem Next = null; - public ThreadQueueItem Prev = null; - - public ThreadQueue Queue = null; - - public ThreadQueueItem(Thread thread) - { - Thread = thread; - } - - [NoHeapAllocation] - public void Remove() - { - if (Queue != null) { - Queue.Remove(this); - } - - VTable.Assert(Next == null); - VTable.Assert(Prev == null); - VTable.Assert(Queue == null); - } - } -} diff --git a/base/Applications/Runtime/System/Threading/Timeout.cs b/base/Applications/Runtime/System/Threading/Timeout.cs deleted file mode 100644 index c670431..0000000 --- a/base/Applications/Runtime/System/Threading/Timeout.cs +++ /dev/null @@ -1,25 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== - -namespace System.Threading { - using System.Threading; - using System; - // A constant used by methods that take a timeout (Object.Wait, Thread.Sleep - // etc) to indicate that no timeout should occur. - // - // @todo: this should become an enum. - //| - public sealed class Timeout - { - private Timeout() - { - } - - //| - public static readonly TimeSpan Infinite = TimeSpan.Infinite; - } - -} diff --git a/base/Applications/Runtime/System/Threading/WaitHandle.cs b/base/Applications/Runtime/System/Threading/WaitHandle.cs deleted file mode 100644 index b2f1b06..0000000 --- a/base/Applications/Runtime/System/Threading/WaitHandle.cs +++ /dev/null @@ -1,119 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================================= -** -** Class: WaitHandle (this name is NOT definitive) -** -** -** Purpose: Class to represent all synchronization objects in the runtime (that allow multiple wait) -** -** Date: August, 1999 -** -=============================================================================*/ - -namespace System.Threading { - using System; - using System.Threading; - using System.Runtime.CompilerServices; - using System.Collections; - using Microsoft.Singularity; - using Microsoft.Singularity.V1.Threads; - using Microsoft.Singularity.V1.Services; - - //| - [CLSCompliant(false)] - public abstract class WaitHandle : IDisposable - { - //| - public const int WaitTimeout = -1; - - protected WaitHandle() - { - } - - protected abstract SyncHandle Handle{ - get; - } - - //| - public abstract bool WaitOne(TimeSpan timeout); - - //| - public abstract bool WaitOne(SchedulerTime stop); - - [Obsolete("Do not use DateTime for scheduling. It is meaningless. Use SchedulerTime")] - public bool WaitOne(DateTime stop) { - SchedulerTime st = SchedulerTime.MinValue + (stop - DateTime.BootTime); - return WaitOne(st); - } - - //| - public abstract bool WaitOne(); - - //| - public static unsafe int WaitAny(WaitHandle[] waitHandles, - TimeSpan timeout) - { - SyncHandle[] handles = Thread.CurrentThread.GetSyncHandles(waitHandles.Length); - for (int i = 0; i < waitHandles.Length; i++) { - handles[i] = waitHandles[i].Handle; - } - fixed (SyncHandle *array = &handles[0]) { - return SyncHandle.WaitAny(array, waitHandles.Length, timeout); - } - } - - //| - public static unsafe int WaitAny(WaitHandle[] waitHandles, - SchedulerTime stop) - { - SyncHandle[] handles = Thread.CurrentThread.GetSyncHandles(waitHandles.Length); - for (int i = 0; i < waitHandles.Length; i++) { - handles[i] = waitHandles[i].Handle; - } - fixed (SyncHandle *array = &handles[0]) { - return SyncHandle.WaitAny(array, waitHandles.Length, stop); - } - } - - //| - public static unsafe int WaitAny(WaitHandle[] waitHandles) - { - SyncHandle[] handles = Thread.CurrentThread.GetSyncHandles(waitHandles.Length); - for (int i = 0; i < waitHandles.Length; i++) { - handles[i] = waitHandles[i].Handle; - } - fixed (SyncHandle *array = &handles[0]) { - return SyncHandle.WaitAny(array, waitHandles.Length); - } - } - - //| - protected abstract void Dispose(bool explicitDisposing); - - //| - public virtual void Close() - { - Dispose(true); - GC.nativeSuppressFinalize(this); - } - - //| - /// - void IDisposable.Dispose() - { - Dispose(true); - GC.nativeSuppressFinalize(this); - } - - //| - ~WaitHandle() - { - Dispose(false); - } - - } -} diff --git a/base/Applications/Runtime/TestApp.csproj b/base/Applications/Runtime/TestApp.csproj deleted file mode 100644 index 812cb39..0000000 --- a/base/Applications/Runtime/TestApp.csproj +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - testapp - Exe - $(APPRUNTIMEDIR) - C# - - - - - - true - - - - - - - - - - - - - - diff --git a/base/Applications/SeattleTrafficProxy/SeattleTrafficProxy.csproj b/base/Applications/SeattleTrafficProxy/SeattleTrafficProxy.csproj index bd0b639..6c13ed0 100644 --- a/base/Applications/SeattleTrafficProxy/SeattleTrafficProxy.csproj +++ b/base/Applications/SeattleTrafficProxy/SeattleTrafficProxy.csproj @@ -1,8 +1,6 @@  + + + + + + Exe + dlgchild + true + + + + + + + + + + + + + + + diff --git a/base/Applications/Security/dlg/dlgtest.csproj b/base/Applications/Security/dlg/dlgtest.csproj new file mode 100644 index 0000000..b25d40e --- /dev/null +++ b/base/Applications/Security/dlg/dlgtest.csproj @@ -0,0 +1,30 @@ + + + + + + + Exe + dlgtest + true + + + + + + + + + + + + + + + diff --git a/base/Applications/Security/security.proj b/base/Applications/Security/security.proj index 39754cf..8d7fda2 100644 --- a/base/Applications/Security/security.proj +++ b/base/Applications/Security/security.proj @@ -1,3 +1,11 @@ + + diff --git a/base/Applications/Security/stats/SecStats.sg b/base/Applications/Security/stats/SecStats.sg index 5a5e0cf..c1e15e5 100644 --- a/base/Applications/Security/stats/SecStats.sg +++ b/base/Applications/Security/stats/SecStats.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: SecStats.sg -// // Note: Singularity micro-benchmark program. // using System; @@ -65,10 +63,12 @@ namespace Microsoft.Singularity.Applications if (config.doClear) { sd.ClearStatistics(); Console.Write("Statistics cleared.\n"); - } else if (config.doDisable) { + } + else if (config.doDisable) { sd.Disable(true); Console.Write("Kernel ACL checks disabled.\n"); - } else if (config.doEnable) { + } + else if (config.doEnable) { sd.Disable(false); Console.Write("Kernel ACL checks enabled.\n"); } diff --git a/base/Applications/Security/stats/stats.csproj b/base/Applications/Security/stats/stats.csproj index 66c74c0..0c2c0df 100644 --- a/base/Applications/Security/stats/stats.csproj +++ b/base/Applications/Security/stats/stats.csproj @@ -1,8 +1,6 @@  - - - - - - Exe - LogBench - true - - - - - - - - - - - - - - - - - - diff --git a/base/Applications/ServiceManager/Benchmark/LogBenchmark.sg b/base/Applications/ServiceManager/Benchmark/LogBenchmark.sg deleted file mode 100644 index c878ba6..0000000 --- a/base/Applications/ServiceManager/Benchmark/LogBenchmark.sg +++ /dev/null @@ -1,310 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Applications\ServiceManager\Benchmark\LogBenchmark.sg -// -// Note: -// -using System; -using System.Text; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.SingSharp.Reflection; -using Microsoft.Singularity; -using Microsoft.Singularity.Applications; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Configuration; -using Microsoft.Singularity.Diagnostics.Contracts; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Io; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Services; -[assembly: Transform(typeof(ApplicationResourceTransform))] - -namespace Microsoft.Singularity.Applications.ServiceManager -{ - [ConsoleCategory(HelpMessage="Benchmark client", DefaultAction=true)] - internal class DefaultConfig - { - [InputEndpoint("data")] - public readonly TRef Stdin; - - [OutputEndpoint("data")] - public readonly TRef Stdout; - - [Endpoint] - public readonly TRef processRef; - - [Endpoint] - public readonly TRef memoryRef; - - [Endpoint] - public readonly TRef channelRef; - - reflective internal DefaultConfig(); - - internal int AppMain() - { - return LogBenchmark.DefaultMain(this); - } - } - - internal sealed class BenchmarkServerInfo - { - private readonly string! serviceName; - private readonly string! binaryName; - private readonly ServiceType type; - - internal BenchmarkServerInfo(string! serviceName, string! binaryName, - ServiceType type) - { - this.serviceName = serviceName; - this.binaryName= binaryName; - this.type = type; - } - - internal string! ServiceName - { - get { return serviceName; } - private set {} - } - - internal string! BinaryName - { - get { return binaryName; } - private set{} - } - - internal ServiceType Type - { - get { return type; } - private set{} - } - } - - internal sealed class LogBenchmark - { - private BenchmarkServerInfo[] serverList; - - private TRef serverControlRef; - private TRef manageRef; - private TRef processRef; - private TRef memoryRef; - private TRef channelRef; - - internal LogBenchmark(BenchmarkServerInfo[] info, - [Claims]ProcessContract.Imp:Start! pep, - [Claims]MemoryContract.Imp:Start! mep, - [Claims]ChannelContract.Imp:Start! cep) - { - serverList = info; - pep.RecvReady(); - processRef = new TRef(pep); - mep.RecvReady(); - memoryRef = new TRef(mep); - cep.RecvReady(); - channelRef = new TRef(cep); - } - - internal void Start() - { - ServiceControlContract.Imp:Ready! control; - - foreach (BenchmarkServerInfo server in serverList) { - if (server == null) { - continue; - } - - // Launch the benchmark server - if (!StartService(server)) { - return; - } - - // Wait for the server registered. - Thread.Sleep(2000); - - // Start the evaluation - JobSet.Start(server, serverControlRef); - - // shutdown server - if (!StopService()) { - return; - } - Thread.Sleep(2000); - } - } - - private bool StartService(BenchmarkServerInfo! info) - { - bool success = false; - - ServiceInfo* in ExHeap serviceInfo; - ServiceManagementContract.Imp! ep; - ServiceControlContract.Imp! client; - ServiceControlContract.Exp! server; - - Console.Write("Starting service: " + info.ServiceName + " ..."); - GetEndpoint(out ep); - ep.RecvSuccess(); - - Console.Write("."); - ServiceControlContract.NewChannel(out client, out server); - serviceInfo = new [ExHeap] ServiceInfo(0, - info.ServiceName, - info.BinaryName, - info.Type); - ep.SendBind(serviceInfo, server); - switch receive { - case ep.AckBind(): - { - break; - } - case ep.NotFound(rejected): - { - delete rejected; - Console.WriteLine("Doesn't exist."); - goto exit; - break; - } - case ep.PermissionDenied(rejected): - { - delete rejected; - Console.WriteLine("Permission denied"); - goto exit; - break; - } - } - - client.RecvSuccess(); - client.SendStartService(); - switch receive { - case client.RecvAckStartService(): - { - Console.WriteLine(" done."); - success = true; - break; - } - case client.NakStartService(error): - { - Console.WriteLine(" error: " + error); - break; - } - case client.ChannelClosed(): - { - Console.WriteLine(" Channel is closed."); - break; - } - } - -exit: - if (success) { - serverControlRef = - new TRef(client); - manageRef = - new TRef(ep); - } - else { - delete client; - delete ep; - } - return success; - } - - private bool StopService() - { - bool success = false; - ServiceControlContract.Imp! client; - - Console.Write("Stopping service ..."); - - client = serverControlRef.Acquire(); - client.SendStopService(); - switch receive { - case client.AckStopService(): - { - Console.WriteLine(" done."); - success = true; - break; - } - case client.NakStopService(error): - { - Console.WriteLine(" error: " + error); - break; - } - case unsatisfiable: - { - break; - } - } - delete client; - delete manageRef.Acquire(); - return success; - } - - private void GetEndpoint(out ServiceManagementContract.Imp! ep) - { - ErrorCode error; - DirectoryServiceContract.Imp ds; - ServiceManagementContract.Exp! scServer; - ServiceManagementContract.NewChannel(out ep, out scServer); - - ds = DirectoryService.NewClientEndpoint(); - SdsUtils.Bind(ServiceManagementContract.ModuleName, - ds, - scServer, out error); - delete ds; - } - - internal static int DefaultMain(DefaultConfig! config) - { - LogBenchmark bench; - BenchmarkServerInfo[] serverInfo = new BenchmarkServerInfo[4] { - new BenchmarkServerInfo("RBench", "RBenchWorker", - ServiceType.Default), - new BenchmarkServerInfo("RBench", "RBenchWorker", - ServiceType.Resilient), - new BenchmarkServerInfo("RBench", "RBenchHoisted", - ServiceType.Default), - new BenchmarkServerInfo("RBench", "RBenchHoisted", - ServiceType.Resilient), - }; - - StringBuilder builder = new StringBuilder(); - ulong[] time = new ulong[4]; - Object obj; - - for (int i = 0; i < 4; i++) { - time[i] = 0; - } - for (int i = 0; i < 100; i++) { - time[0] += Processor.GetCycleCount(); - obj = new Object(); - time[1] += Processor.GetCycleCount(); - obj.ToString(); - time[2] += Processor.GetCycleCount(); - obj.ToString(); - time[3] += Processor.GetCycleCount(); - } - for (int i = 0; i < 4; i++) { - time[i] /= 100; - } - builder.AppendFormat("New object: {0}\n", time[1] - time[0]); - builder.AppendFormat("ToString1: {0}\n", time[2] - time[1]); - builder.AppendFormat("ToString2: {0}\n", time[3] - time[2]); - - Console.WriteLine(builder.ToString()); - DebugStub.WriteLine(builder.ToString()); - - bench = new LogBenchmark(serverInfo, - config.processRef.Acquire(), - config.memoryRef.Acquire(), - config.channelRef.Acquire()); - bench.Start(); - - return 0; - } - } -} diff --git a/base/Applications/ServiceManager/Benchmark/Message.sg b/base/Applications/ServiceManager/Benchmark/Message.sg deleted file mode 100644 index ea3184c..0000000 --- a/base/Applications/ServiceManager/Benchmark/Message.sg +++ /dev/null @@ -1,150 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Applications\ServiceManager\Benchmark\Message.sg -// -// Note: -// -using System; -using System.Threading; -using System.Text; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Io; -using Microsoft.Singularity.Services; - -namespace Microsoft.Singularity.Applications.ServiceManager -{ - internal class Message - { - private const int MaxArguments = 9; - private static int[] n = new int[MaxArguments]; - - static Message() - { - Random r = new Random(); - for (int i = 0; i < MaxArguments; i++) { - n[i] = r.Next(); - } - } - - internal static bool SendReceive(BenchmarkContract.Imp:Ready! ep, - int num, - out ulong time1, - out ulong time2) - { - bool success = false; - - time1 = 0; - time2 = 0; - if (n == null) { - return false; - } - - switch (num) { - case 0: - time1 = Processor.GetCycleCount(); - ep.SendNull(); - break; - case 1: - time1 = Processor.GetCycleCount(); - ep.SendOne(n[0]); - break; - case 2: - time1 = Processor.GetCycleCount(); - ep.SendTwo(n[0], n[1]); - break; - case 3: - time1 = Processor.GetCycleCount(); - ep.SendThree(n[0], n[1], n[2]); - break; - case 4: - time1 = Processor.GetCycleCount(); - ep.SendFour(n[0], n[1], n[2], n[3]); - break; - case 5: - time1 = Processor.GetCycleCount(); - ep.SendFive(n[0], n[1], n[2], n[3], n[4]); - break; - case 6: - time1 = Processor.GetCycleCount(); - ep.SendSix(n[0], n[1], n[2], n[3], n[4], n[5]); - break; - case 7: - time1 = Processor.GetCycleCount(); - ep.SendSeven(n[0], n[1], n[2], n[3], n[4], n[5], n[6]); - break; - case 8: - time1 = Processor.GetCycleCount(); - ep.SendEight(n[0], n[1], n[2], n[3], n[4], n[5], n[6], - n[7]); - break; - case 9: - time1 = Processor.GetCycleCount(); - ep.SendNine(n[0], n[1], n[2], n[3], n[4], n[5], n[6], - n[7], n[8]); - break; - default: - time1 = 0; - time2 = 0; - return false; - break; - } - - switch receive { - case ep.AckNull(): - time2 = Processor.GetCycleCount(); - success = true; - break; - case ep.AckOne(num1): - time2 = Processor.GetCycleCount(); - success = true; - break; - case ep.AckTwo(num1, num2): - time2 = Processor.GetCycleCount(); - success = true; - break; - case ep.AckThree(num1, num2, num3): - time2 = Processor.GetCycleCount(); - success = true; - break; - case ep.AckFour(num1, num2, num3, num4): - time2 = Processor.GetCycleCount(); - success = true; - break; - case ep.AckFive(num1, num2, num3, num4, num5): - time2 = Processor.GetCycleCount(); - success = true; - break; - case ep.AckSix(num1, num2, num3, num4, num5, num6): - time2 = Processor.GetCycleCount(); - success = true; - break; - case ep.AckSeven(num1, num2, num3, num4, num5, num6, num7): - time2 = Processor.GetCycleCount(); - success = true; - break; - case ep.AckEight(num1, num2, num3, num4, num5, num6, num7, - num8): - time2 = Processor.GetCycleCount(); - success = true; - break; - case ep.AckNine(num1, num2, num3, num4, num5, num6, num7, - num8, num9): - time2 = Processor.GetCycleCount(); - success = true; - break; - case ep.ChannelClosed(): - Console.WriteLine("Job: Server channel closed.\n"); - success = false; - break; - } - return success; - } - - } // class -} diff --git a/base/Applications/ServiceManager/Benchmark/MultiThreadJob.sg b/base/Applications/ServiceManager/Benchmark/MultiThreadJob.sg deleted file mode 100644 index 32fac44..0000000 --- a/base/Applications/ServiceManager/Benchmark/MultiThreadJob.sg +++ /dev/null @@ -1,325 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Applications\ServiceManager\Benchmark\MultiThreadJob.sg -// -// Note: -// -using System; -using System.Collections; -using System.Text; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Io; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Services; - -namespace Microsoft.Singularity.Applications.ServiceManager -{ - internal sealed class RandomJob : Job - { - private readonly int id; - private bool keepAlive; - private ulong messageCount; - private ulong firstHalf; - private Thread thread; - - [ Microsoft.Contracts.NotDelayed ] - internal RandomJob(BenchmarkServerInfo! info, int tid) - { - base(info); - this.id = tid; - thread = new Thread(new ThreadStart(this.RunThread)); - } - - internal override bool Run() - { - thread.Start(); - return true; - } - - internal void Join() - { - thread.Join(); - } - - internal void Join(int timeout) - { - thread.Join(new TimeSpan(0, 0, 0, timeout)); - } - - internal void RunThread() - { - ulong time1; - ulong time2; - - DebugStub.Print("RandomJob {0} start\n", __arglist(id)); - - BenchmarkContract.Imp:Ready! imp; - if (!Bind(out imp)) { - goto exit; - } - - messageCount = 0; - keepAlive = true; - while (keepAlive) { - imp.SendNull(); - switch receive { - case imp.AckNull(): - break; - case unsatisfiable: - goto exit; - break; - } - messageCount += 2; - } - - /* - imp.SendEndOfBenchmark(); - switch receive { - case imp.AckEnd(): - break; - case imp.ChannelClosed(): - break; - } - */ -exit: - delete imp; - DebugStub.Print("RandomJob {0} stop\n", __arglist(id)); - } - - internal void Stop() - { - keepAlive = false; - } - - internal void SwitchRecord() - { - firstHalf = messageCount; - } - - internal ulong MessageCount - { - get { return messageCount; } - private set {} - } - - internal ulong FirstHalf - { - get { return firstHalf; } - private set{} - } - - internal ulong SecondHalf - { - get {return messageCount - firstHalf; } - private set{} - } - } - - internal sealed class MultiThreadJob : Job - { - private const int Order = 4; - private const int NSamples = 10; - private ulong[,] firstHalf = new ulong[Order, NSamples]; - private ulong[,] totalMessages = new ulong[Order, NSamples]; - private ArrayList jobList; - - private TRef serverRef; - - internal MultiThreadJob(BenchmarkServerInfo! info, - TRef! tref) - { - base(info); - this.jobList = new ArrayList(); - this.serverRef = tref; - } - - internal override bool Run() - { - bool success = false; - - Console.WriteLine("MultiThreadJob start"); - DebugStub.WriteLine("-- MultiThreadJob start"); - - for (int i = 0; i < Order; i++) { - for (int j = 0; j < NSamples; j++) { - CreateJobs(1 << i); - StartJobs(); - Thread.Sleep(5000); - - Start2ndHalf(); - if (base.info.Type == ServiceType.Resilient) { - success = RestartService(); - } - else { - DummyOperation(); - success = true; - } - Thread.Sleep(5000); - DebugStub.WriteLine("Stop jobs..."); - StopJobs(); - CollectResults(i, j); - DebugStub.WriteLine("Delete jobs..."); - DeleteJobs(); - if (!success) { - break; - } - Thread.Sleep(2000); - } - } - return success; - } - - private void CreateJobs(int numberOfJobs) - { - RandomJob job; - - for (int i = 0; i < numberOfJobs; i++) { - job = new RandomJob(base.info, i); - jobList.Add(job); - } - } - - private void StartJobs() - { - RandomJob job; - - foreach (Object obj in jobList) { - if (obj != null) { - job = obj as RandomJob; - if (job != null) { - job.Run(); - } - } - } - } - - private void Start2ndHalf() - { - RandomJob job; - - foreach (Object obj in jobList) { - if (obj != null) { - job = obj as RandomJob; - if (job != null) { - job.SwitchRecord(); - } - } - } - } - - private void StopJobs() - { - RandomJob job; - - foreach (Object obj in jobList) { - if (obj != null) { - job = obj as RandomJob; - if (job != null) { - job.Stop(); - } - } - } - - foreach (Object obj in jobList) { - if (obj != null) { - job = obj as RandomJob; - if (job != null) { - job.Join(2000); - } - } - } - } - - private void DeleteJobs() - { - jobList.Clear(); - } - - private bool RestartService() - { - bool success = false; - - ServiceControlContract.Imp:Ready! ep; - - ep = serverRef.Acquire(); - ep.SendRestartService(); - switch receive { - case ep.AckRestartService(): - { - success = true; - break; - } - case ep.ChannelClosed(): - { - success = false; - break; - } - } - serverRef.Release(ep); - return success; - } - - private void DummyOperation() - { - ServiceControlContract.Imp:Ready! ep; - ep = serverRef.Acquire(); - ep.SendStopPoll(); - switch receive { - case ep.AckStopPoll(): - break; - case ep.ChannelClosed(): - break; - } - serverRef.Release(ep); - } - - private void CollectResults(int order, int sample) - { - RandomJob job; - - foreach (Object obj in jobList) { - if (obj != null) { - job = obj as RandomJob; - if (job != null) { - firstHalf[order, sample] = job.FirstHalf; - totalMessages[order, sample] = job.MessageCount; - } - } - } - } - - public override String! ToString() - { - ulong half, total; - StringBuilder builder = new StringBuilder(); - - builder.Append("-- MultiThread Test (100 samples for each)"); - builder.Append("(5 seconds for each half) --\n"); - builder.Append("Client(s) FirstHalf SecondHalf Total\n"); - builder.Append("--------- --------- ---------- -------\n"); - for (int i = 0; i < Order; i++) { - half = 0; - total = 0; - for (int j = 0; j < NSamples; j++) { - half += firstHalf[i, j]; - total += totalMessages[i, j]; - } - half /= NSamples; - total /= NSamples; - - builder.AppendFormat("{0, -9} {1, 9} {2, 10} {3, 7}\n", - 1 << i, half, total - half, total); - } - builder.Append("\n"); - return builder.ToString(); - } - } -} diff --git a/base/Applications/ServiceManager/Count/Count.csproj b/base/Applications/ServiceManager/Count/Count.csproj deleted file mode 100644 index 19ab1cc..0000000 --- a/base/Applications/ServiceManager/Count/Count.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - Exe - Count - - - - - - - - - - - diff --git a/base/Applications/ServiceManager/Count/Count.sg b/base/Applications/ServiceManager/Count/Count.sg deleted file mode 100644 index b89b678..0000000 --- a/base/Applications/ServiceManager/Count/Count.sg +++ /dev/null @@ -1,89 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Applications\ServiceManager\CountUp\CountUp.sg -// -// Note: -// -using System; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.SingSharp.Reflection; -using Microsoft.Singularity; -using Microsoft.Singularity.Applications; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Configuration; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Io; -using Microsoft.Singularity.Services; -[assembly: Transform(typeof(ApplicationResourceTransform))] - -namespace Microsoft.Singularity.Applications -{ - [ConsoleCategory(HelpMessage="Counter client", DefaultAction=true)] - internal class DefaultConfig - { - [InputEndpoint("data")] - public readonly TRef Stdin; - - [OutputEndpoint("data")] - public readonly TRef Stdout; - - [LongParameter("interval", Position=0, Default=500)] - internal long interval; - - reflective internal DefaultConfig(); - - internal int AppMain() - { - return CountUp.DefaultMain(this); - } - } - - public class CountUp - { - internal static int DefaultMain(DefaultConfig! config) - { - bool next; - ErrorCode error; - DirectoryServiceContract.Imp ds; - CounterContract.Imp! ep; - CounterContract.Exp! server; - CounterContract.NewChannel(out ep, out server); - - ds = DirectoryService.NewClientEndpoint(); - SdsUtils.Bind(CounterContract.ModuleName, ds, server, out error); - - ep.RecvSuccess(); - - Console.WriteLine("Start count"); - ep.SendBeginCount(); - do { - switch receive { - case ep.RecvCurrent(number): - Console.WriteLine("Count: " + number); - Thread.Sleep((int)config.interval); - ep.SendNext(); - next = true; - break; - case ep.RecvTerminated(): - Console.WriteLine("Terminated..."); - next = false; - break; - case ep.ChannelClosed(): - Console.WriteLine("Channel closed..."); - next = false; - break; - } - } while (next); - - delete ds; - delete ep; - - return 0; - } - } -} diff --git a/base/Applications/ServiceManager/Replace/ReplaceGame.csproj b/base/Applications/ServiceManager/Replace/ReplaceGame.csproj deleted file mode 100644 index 122bc57..0000000 --- a/base/Applications/ServiceManager/Replace/ReplaceGame.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - Exe - ReplaceGame - true - - - - - - - - - - - - - - diff --git a/base/Applications/ServiceManager/Replace/ReplaceGame.sg b/base/Applications/ServiceManager/Replace/ReplaceGame.sg deleted file mode 100644 index 6f2ad8e..0000000 --- a/base/Applications/ServiceManager/Replace/ReplaceGame.sg +++ /dev/null @@ -1,399 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Applications\ServiceManager\Replace\ReplaceGame.sg -// -// Note: -// -using System; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.SingSharp.Reflection; -using Microsoft.Singularity; -using Microsoft.Singularity.Applications; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Configuration; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Io; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Services; -[assembly: Transform(typeof(ApplicationResourceTransform))] - -namespace Microsoft.Singularity.Applications -{ - [ConsoleCategory(HelpMessage="Replace Game Client", DefaultAction=true)] - internal class DefaultConfig - { - [InputEndpoint("data")] - public readonly TRef Stdin; - - [OutputEndpoint("data")] - public readonly TRef Stdout; - - [LongParameter("interval", Default=500)] - internal long interval; - - [BoolParameter("test", Default=true)] - internal bool test; - - reflective internal DefaultConfig(); - - internal int AppMain() - { - return ReplaceGame.DefaultMain(this); - } - } - - internal sealed class ReplaceGame - { - private const int DefaultCount = 50; - private int count; - private String![]! names; - private int self; - private Random! random; - - private const int NumberOfActions = 4; - private enum Action : int { Overwrite, Protect, Score, Pose }; - - private static TRef! controlRef; - private static TRef! manageRef; - - internal ReplaceGame(String![]! competitors, int self) - { - this.count = DefaultCount; - this.names = competitors; - this.self = self; - Thread.Sleep(1); // To get a different seed - this.random = new Random(); - } - - internal void Run() - { - int action; - GamePlayerContract.Imp! ep; - - if (!Connect(out ep)) { - goto exit; - } - - switch receive { - case ep.Success(): - break; - case unsatisfiable: - goto exit; - break; - } - - Score(ep); - Console.WriteLine(names[self] + " start!!"); - try { - while (count-- > 0) { - action = random.Next(NumberOfActions); - switch ((Action)action) { - case Action.Overwrite: - Console.WriteLine(names[self] + " overwrite"); - Overwrite(ep); - break; - case Action.Protect: - Console.WriteLine(names[self] + " protect"); - Protect(ep); - break; - case Action.Score: - Console.WriteLine(names[self] + " score"); - Score(ep); - break; - case Action.Pose: - Pose(); - break; - } - } - Console.WriteLine(names[self] + " end!!"); - Score(ep); - } - catch (Exception e) { - DebugStub.Print(e.ToString()); - } -exit: - delete ep; - } - - private void Overwrite(GamePlayerContract.Imp:Ready! ep) - { - int pos = -1; - DebugStub.Print("Enter overwrite\n"); - - ep.SendSearch(Bitter.FromString2(GetSomeone())); - switch receive { - case ep.AckSearch(position): - pos = position; - break; - case ep.ChannelClosed(): - //throw new ChannelClosedException(); - break; - } - - if (pos >= 0) { - ep.SendOverwrite(Bitter.FromString2(names[self]), pos); - switch receive { - case ep.AckOverwrite(): - break; - case ep.ChannelClosed(): - //throw new ChannelClosedException(); - break; - } - } - DebugStub.Print("Exit overwrite\n"); - } - - private void Protect(GamePlayerContract.Imp:Ready! ep) - { - int pos = -1; - ep.SendSearch(Bitter.FromString2(names[self])); - switch receive { - case ep.AckSearch(position): - pos = position; - break; - case ep.ChannelClosed(): - //throw new ChannelClosedException(); - break; - } - if (pos >= 0) { - ep.SendProtect(pos); - switch receive { - case ep.AckProtect(): - break; - case ep.ChannelClosed(): - //throw new ChannelClosedException(); - break; - } - } - } - - private void Score(GamePlayerContract.Imp:Ready! ep) - { - ep.SendScore(Bitter.FromString2(names[self])); - switch receive { - case ep.AckScore(score): - Console.Write("{0, -10} : {1}\n", names[self], score); - break; - case ep.ChannelClosed(): - //throw new ChannelClosedException(); - break; - } - } - - private void Pose() - { - Thread.Sleep(100); - } - - private String! GetSomeone() - { - int index; - do { - index = random.Next(names.Length); - } while (index == self); - return names[index]; - } - - internal bool Connect(out GamePlayerContract.Imp! ep) - { - DirectoryServiceContract.Imp! ds; - GamePlayerContract.Imp! imp; - GamePlayerContract.Exp! exp; - ErrorCode error; - - ds = DirectoryService.NewClientEndpoint(); - GamePlayerContract.NewChannel(out imp, out exp); - SdsUtils.Bind(GamePlayerContract.ModuleName, ds, exp, out error); - ep = imp; - delete ds; - return (error == ErrorCode.NoError); - } - - //-------------------------------------------------------------------- - // Test Code - //-------------------------------------------------------------------- - - private static bool StartService() - { - bool success = false; - - ServiceInfo* in ExHeap serviceInfo; - ServiceManagementContract.Imp! ep; - ServiceControlContract.Imp! imp; - ServiceControlContract.Exp! exp; - - Console.Write("Starting service: " + - GamePlayerContract.ModuleName + " ..."); - GetEndpoint(out ep); - ep.RecvSuccess(); - - ServiceControlContract.NewChannel(out imp, out exp); - serviceInfo = new [ExHeap] ServiceInfo(0, - GamePlayerContract.ModuleName, - GamePlayerContract.ModuleName, - ServiceType.Resilient); - ep.SendBind(serviceInfo, exp); - switch receive { - case ep.AckBind(): - { - break; - } - case ep.NotFound(rejected): - { - delete rejected; - Console.WriteLine("Doesn't exist."); - goto exit; - break; - } - case ep.PermissionDenied(rejected): - { - delete rejected; - Console.WriteLine("Permission denied"); - goto exit; - break; - } - } - - imp.RecvSuccess(); - imp.SendStartService(); - switch receive { - case imp.RecvAckStartService(): - { - Console.WriteLine(" done."); - success = true; - break; - } - case imp.NakStartService(error): - { - Console.WriteLine(" error: " + error); - break; - } - case imp.ChannelClosed(): - { - Console.WriteLine(" Channel is closed."); - break; - } - } - -exit: - if (success) { - controlRef = - new TRef(imp); - manageRef = - new TRef(ep); - } - else { - delete imp; - delete ep; - } - return success; - } - - private static bool RestartService() - { - bool success = false; - ServiceControlContract.Imp:Ready! ep; - - ep = controlRef.Acquire(); - ep.SendRestartService(); - switch receive { - case ep.AckRestartService(): - { - success = true; - break; - } - case ep.ChannelClosed(): - { - success = false; - break; - } - } - controlRef.Release(ep); - return success; - } - - private static bool StopService() - { - bool success = false; - ServiceControlContract.Imp:Ready! ep; - - Console.Write("Stopping service ..."); - - ep = controlRef.Acquire(); - ep.SendStopService(); - switch receive { - case ep.AckStopService(): - { - Console.WriteLine(" done."); - success = true; - break; - } - case ep.NakStopService(error): - { - Console.WriteLine(" error: " + error); - break; - } - } - delete ep; - delete manageRef.Acquire(); - return success; - } - - private static void GetEndpoint(out ServiceManagementContract.Imp! ep) - { - ErrorCode error; - DirectoryServiceContract.Imp ds; - ServiceManagementContract.Exp! scServer; - ServiceManagementContract.NewChannel(out ep, out scServer); - - DebugStub.Print("GetEndpoint\n"); - ds = DirectoryService.NewClientEndpoint(); - SdsUtils.Bind(ServiceManagementContract.ModuleName, ds, scServer, - out error); - DebugStub.Print("GetEndpoint: {0}\n", __arglist((int)error)); - delete ds; - } - - - internal static int DefaultMain(DefaultConfig! config) - { - // bang! bang! bang! - String![]! names = new String![4] {"Microsoft", "Google", "Yahoo", "Apple"}; - ReplaceGame![]! game = new ReplaceGame![4]; - Thread![]! players = new Thread![4]; - - if (config.test) { - StartService(); - Thread.Sleep(2000); - } - - for (int i = 0; i < 4; i++) { - game[i] = new ReplaceGame(names, i); - players[i] = new Thread(new ThreadStart(game[i].Run)); - } - foreach (Thread! player in players) { - player.Start(); - } - - if (config.test) { - Thread.Sleep(8000); - RestartService(); - } - - foreach (Thread! player in players) { - player.Join(); - } - - if (config.test) { - StopService(); - } - - return 0; - } - } -} diff --git a/base/Applications/ServiceManager/SMSClient/SMSClient.cs b/base/Applications/ServiceManager/SMSClient/SMSClient.cs index ae62b40..2e0d83c 100644 --- a/base/Applications/ServiceManager/SMSClient/SMSClient.cs +++ b/base/Applications/ServiceManager/SMSClient/SMSClient.cs @@ -4,11 +4,11 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Applications\ServiceManager\SMSClient\SMSClient.sg -// // Note: Service Manager client program // using System; +using System.Threading; + using Microsoft.SingSharp; using Microsoft.SingSharp.Reflection; using Microsoft.Singularity; @@ -36,7 +36,8 @@ namespace Microsoft.Singularity.Applications.ServiceManager internal int AppMain() { - return SMSClient.DefaultMain(this); + Console.WriteLine("Use -? for help."); + return 0; } } @@ -50,44 +51,21 @@ namespace Microsoft.Singularity.Applications.ServiceManager public readonly TRef Stdout; [StringParameter("service", Mandatory=true, Position=0)] - internal string service; + internal string serviceName; - [StringParameter("type", Mandatory=false, Position=1)] - internal string type; + // [BoolParameter("w", Mandatory=false, HelpMessage="Wait for service to start.")] + public bool wait; reflective internal StartConfig(); internal int AppMain() { - return SMSClient.StartService(this); + return SMSClient.StartService((!)serviceName, wait); } } [ConsoleCategory(Action="stop", HelpMessage="Stop a service")] - internal class StopConfig - { - [InputEndpoint("data")] - public readonly TRef Stdin; - - [OutputEndpoint("data")] - public readonly TRef Stdout; - - //[StringParameter("service", Mandatory=true, Position=0)] - //internal string service; - - [LongParameter("id", Mandatory=true, Position=0)] - internal long id; - - reflective internal StopConfig(); - - internal int AppMain() - { - return SMSClient.StopService(this); - } - } - - [ConsoleCategory(Action="restart", HelpMessage="Restart a service")] - internal class RestartConfig + internal class StopServiceCommand { [InputEndpoint("data")] public readonly TRef Stdin; @@ -96,13 +74,16 @@ namespace Microsoft.Singularity.Applications.ServiceManager public readonly TRef Stdout; [StringParameter("service", Mandatory=true, Position=0)] - internal string service; + internal string serviceName; - reflective internal RestartConfig(); + // [BoolParameter("w", Mandatory=false, HelpMessage="Wait for service to stop.")] + public bool wait; + + reflective internal StopServiceCommand(); internal int AppMain() { - return SMSClient.RestartService(this); + return SMSClient.StopService((!)serviceName, wait); } } @@ -123,191 +104,789 @@ namespace Microsoft.Singularity.Applications.ServiceManager } } + [ConsoleCategory(Action="show", HelpMessage="Show details about a specific service.")] + internal class ShowServiceParameters + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [StringParameter("service", Position=0, Mandatory=true, HelpMessage="The service to examine.")] + public string ServiceName; + + reflective internal ShowServiceParameters(); + + internal int AppMain() + { + if (ServiceName == null) { + Console.WriteLine("The 'service' parameter is required, but has not been provided."); + return -1; + } + + ServiceManagerContract.Imp! svmanager = SMSClient.ConnectServiceManager(); + try { + ServiceError error = SMSClient.SelectService(svmanager, this.ServiceName); + if (error != ServiceError.None) + return -1; + + bool errors = false; + + svmanager.SendQueryServiceConfig(); + switch receive { + case svmanager.CurrentServiceConfig(config): + SMSClient.ShowConfigDetailed(config); + config.Dispose(); + break; + + case svmanager.RequestFailed(err): + if (err == ServiceError.ServiceNotFound) { + Console.WriteLine("There is no service with name '{0}'.", this.ServiceName); + return -1; + } + + Console.WriteLine("Failed to query service configuration."); + SMSClient.ShowServiceError(err); + errors = true; + break; + + case svmanager.ChannelClosed(): + Console.WriteLine("The Service Manager closed its channel unexpectedly."); + return -1; + } + + svmanager.SendQueryServiceStatus(); + switch receive { + case svmanager.CurrentServiceStatus(status): + Console.WriteLine(); + SMSClient.ShowStatusDetailed(status); + status.Dispose(); + break; + + case svmanager.RequestFailed(err): + Console.WriteLine("Failed to query service status."); + SMSClient.ShowServiceError(err); + errors = true; + break; + + case svmanager.ChannelClosed(): + Console.WriteLine("The Service Manager closed its channel unexpectedly."); + return -1; + } + + if (errors) + return -1; + else + return 0; + + } + finally { + delete svmanager; + } + } + } + + [ConsoleCategory(Action="watch", HelpMessage="Watch the status of a service")] + internal class WatchConfig + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [StringParameter("service", Mandatory=true, Position=0)] + internal string serviceName; + + reflective internal WatchConfig(); + + internal int AppMain() + { + return SMSClient.WatchService((!)serviceName); + } + } + + [ConsoleCategory(Action="watchall", HelpMessage="Watch the status of the Service Manager")] + internal class WatchAllConfig + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + +#if false + [BoolParameter("c", Mandatory=false, HelpMessage="Watch all service configuration changes")] + public bool watchConfigChanges; + + [BoolParameter("s", Mandatory=false, HelpMessage="Watch all service status changes")] + public bool watchStatusChanges; +#endif + + reflective internal WatchAllConfig(); + + internal int AppMain() + { +#if false + ServiceManagerEventMask desiredEvents = 0; + + if (watchConfigChanges) + desiredEvents |= ServiceManagerEventMask.AnyServiceConfig; + + if (watchStatusChanges) + desiredEvents |= ServiceManagerEventMask.AnyServiceStatus; + + if (desiredEvents == 0) + desiredEvents = ServiceManagerEventMask.AnyServiceConfig | ServiceManagerEventMask.AnyServiceStatus; +#else + ServiceManagerEventMask desiredEvents = ServiceManagerEventMask.AnyServiceConfig | ServiceManagerEventMask.AnyServiceStatus; +#endif + + ServiceManagerContract.Imp! svmanager = SMSClient.ConnectServiceManager(); + + svmanager.SendWatchServiceManager(desiredEvents); + + switch receive { + case svmanager.Ok(): + break; + + case svmanager.RequestFailed(error): + Console.WriteLine("The Service Manager rejected the subscription request."); + SMSClient.ShowServiceError(error); + delete svmanager; + return -1; + } + + Console.WriteLine("Watching Service Manager..."); + + for (;;) { + Console.WriteLine("Sending WaitNextServiceManagerChange"); + svmanager.SendWaitNextServiceManagerChange(); + + Console.WriteLine("switch-receive"); + + switch receive { + case svmanager.ServiceManagerChanged(events): + Console.WriteLine("Service Manager events fired: {0:x8}", ((uint)events)); + break; + } + } + } + } + + [ConsoleCategory(Action="create", HelpMessage="Create a new service entry")] + internal class CreateServiceCommand + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [StringParameter("service", Mandatory=true, Position=0)] + internal string serviceName; + + [StringParameter("exe", Mandatory=false, HelpMessage="Executable to use; if omitted, uses service name.")] + public string executableName; + + [StringParameter("display", Mandatory=false, HelpMessage="Display name to use; if omitted, uses service name.")] + public string displayName; + + [BoolParameter("disabled", Mandatory=false, HelpMessage="Create in a disabled state.")] + public bool isAdministrativelyDisabled; + + reflective internal CreateServiceCommand(); + + internal int AppMain() + { + assert serviceName != null; + if (serviceName.Length == 0) { + Console.WriteLine("Invalid service name."); + return -1; + } + + if (executableName == null || executableName.Length == 0) + executableName = serviceName; + + if (displayName == null || displayName.Length == 0) + displayName = serviceName; + + ServiceManagerContract.Imp! svmanager = SMSClient.ConnectServiceManager(); + try { + + ServiceConfig config = new ServiceConfig(); + config.ServiceName = Bitter.FromString2(serviceName); + config.ExecutableName = Bitter.FromString2(executableName); + config.DisplayName = Bitter.FromString2(displayName); + config.IsAdministrativelyDisabled = isAdministrativelyDisabled; + config.MinProcesses = 0; + config.MaxProcesses = 1; + config.MaxClientsPerProcess = ServiceConfig.UnlimitedClientsPerProcess; + config.MaxProcessAgeInSeconds = ServiceConfig.UnlimitedProcessAge; + + svmanager.SendCreateService(config); + + switch receive { + case svmanager.Ok(): + Console.WriteLine("Service was successfully created."); + return 0; + + case svmanager.RequestFailed(error): + Console.WriteLine("Failed to create service."); + SMSClient.ShowServiceError(error); + return -1; + } + + } + finally { + delete svmanager; + } + } + } + + [ConsoleCategory(Action="delete", HelpMessage="Delete an existing service")] + internal class DeleteServiceCommand + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [StringParameter("service", Mandatory=true, Position=0)] + internal string serviceName; + + reflective internal DeleteServiceCommand(); + + internal int AppMain() + { + assert serviceName != null; + if (serviceName.Length == 0) { + Console.WriteLine("Invalid service name."); + return -1; + } + + ServiceManagerContract.Imp! svmanager = SMSClient.ConnectServiceManager(); + try { + + ServiceError error = SMSClient.SelectService(svmanager, serviceName); + if (error != ServiceError.None) + return -1; + + svmanager.SendDeleteService(); + + switch receive { + case svmanager.Ok(): + Console.WriteLine("Service was successfully deleted."); + return 0; + + case svmanager.RequestFailed(err): + Console.WriteLine("Failed to delete service."); + SMSClient.ShowServiceError(err); + return -1; + } + + } + finally { + delete svmanager; + } + } + } + + [ConsoleCategory(Action="enable", HelpMessage="Enable a service")] + internal class EnableServiceCommand + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [StringParameter("service", Mandatory=true, Position=0)] + internal string serviceName; + + reflective internal EnableServiceCommand(); + + internal int AppMain() + { + assert serviceName != null; + if (serviceName.Length == 0) { + Console.WriteLine("Invalid service name."); + return -1; + } + + ServiceManagerContract.Imp! svmanager = SMSClient.ConnectServiceManager(); + try { + + ServiceError error = SMSClient.SelectService(svmanager, serviceName); + if (error != ServiceError.None) + return -1; + + svmanager.SendEnableService(true); + + switch receive { + case svmanager.Ok(): + Console.WriteLine("Service was successfully enabled."); + return 0; + + case svmanager.RequestFailed(err): + Console.WriteLine("Failed to enable service."); + SMSClient.ShowServiceError(err); + return -1; + } + + } + finally { + delete svmanager; + } + } + } + + [ConsoleCategory(Action="disable", HelpMessage="Disable a service")] + internal class DisableServiceCommand + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [StringParameter("service", Mandatory=true, Position=0)] + internal string serviceName; + + reflective internal DisableServiceCommand(); + + internal int AppMain() + { + assert serviceName != null; + if (serviceName.Length == 0) { + Console.WriteLine("Invalid service name."); + return -1; + } + + ServiceManagerContract.Imp! svmanager = SMSClient.ConnectServiceManager(); + try { + + ServiceError error = SMSClient.SelectService(svmanager, serviceName); + if (error != ServiceError.None) + return -1; + + svmanager.SendEnableService(false); + + switch receive { + case svmanager.Ok(): + Console.WriteLine("Service was successfully disabled."); + return 0; + + case svmanager.RequestFailed(err): + Console.WriteLine("Failed to disable service."); + SMSClient.ShowServiceError(err); + return -1; + } + + } + finally { + delete svmanager; + } + } + } + + [ConsoleCategory(Action="kill", HelpMessage="Terminate the process(es) of a service.")] + internal class TerminateServiceCommand + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [StringParameter("service", Mandatory=true, Position=0)] + internal string serviceName; + + [LongParameter("pid", Mandatory=false, Position=1, Default=-1)] + internal long processId; + + reflective internal TerminateServiceCommand(); + + internal int AppMain() + { + assert serviceName != null; + if (serviceName.Length == 0) { + Console.WriteLine("Invalid service name."); + return -1; + } + + ServiceManagerContract.Imp! svmanager = SMSClient.ConnectServiceManager(); + try { + + ServiceError error = SMSClient.SelectService(svmanager, serviceName); + if (error != ServiceError.None) + return -1; + + if (processId != -1) + svmanager.SendTerminateServiceProcess((int)processId); + else + svmanager.SendTerminateServiceAllProcesses(); + + switch receive { + case svmanager.Ok(): + return 0; + + case svmanager.RequestFailed(err): + SMSClient.ShowServiceError(err); + return -1; + } + + } + finally { + delete svmanager; + } + } + } + + public class SMSClient { - private static void GetEndpoint(out ServiceManagementContract.Imp! ep) + internal static ServiceManagerContract.Imp! ConnectServiceManager() { - ErrorCode error; - DirectoryServiceContract.Imp ds; - ServiceManagementContract.Exp! scServer; - ServiceManagementContract.NewChannel(out ep, out scServer); + using (ImpatientWatcher watcher = new ImpatientWatcher("ConnectServiceManager", "create channel", 250)) { + ErrorCode error; + ServiceManagerContract.Imp! manager_imp; + ServiceManagerContract.Exp! manager_exp; + ServiceManagerContract.NewChannel(out manager_imp, out manager_exp); - ds = DirectoryService.NewClientEndpoint(); - SdsUtils.Bind(ServiceManagementContract.ModuleName, ds, - scServer, out error); - delete ds; + watcher.NextStep("NewClientEndpoint", 250); + + DirectoryServiceContract.Imp! rootds = DirectoryService.NewClientEndpoint(); + try { + watcher.NextStep("SdsUtils.Bind", 1000); + if (SdsUtils.Bind(ServiceManagerContract.ModuleName, rootds, manager_exp, out error)) { + watcher.NextStep("RecvSuccess", 250); + manager_imp.RecvSuccess(); + return manager_imp; + } + else { + delete manager_imp; + Console.WriteLine("Failed to contact the Service Manager. Error: " + SdsUtils.ErrorCodeToString(error)); + throw new Exception("Failed to connect to Service Manager."); + } + } + finally { + delete rootds; + } + } } - internal static int StartService(StartConfig! config) + internal static ServiceError SelectService(ServiceManagerContract.Imp! svmanager, string! serviceName) { - int res = 1; - ServiceType type = ServiceType.Unknown; - ServiceInfo* in ExHeap info; - ServiceManagementContract.Imp! ep; - ServiceControlContract.Imp! client; - ServiceControlContract.Exp! server; - - switch (config.type) - { - case "Resilient": - type = ServiceType.Resilient; - break; - case "Default": - type = ServiceType.Default; - break; - default: - type = ServiceType.Default; - break; - } - - Console.Write("Starting service: " + config.service + " ... "); - GetEndpoint(out ep); - ep.RecvSuccess(); - - ServiceControlContract.NewChannel(out client, out server); - info = new[ExHeap] ServiceInfo(0, config.service, config.service, - type); - ep.SendBind(info, server); + svmanager.SendSelectService(Bitter.FromString2(serviceName)); switch receive { - case ep.AckBind(): - break; - case ep.NotFound(rejected): - delete rejected; - Console.WriteLine("Doesn't exist."); - goto exit; - break; - case ep.PermissionDenied(rejected): - delete rejected; - Console.WriteLine("Permission denied"); - goto exit; - break; - } + case svmanager.Ok(): + // Console.WriteLine("Successfully selected service '{0}'.", serviceName); + return ServiceError.None; - client.RecvSuccess(); - client.SendStartService(); - switch receive { - case client.RecvAckStartService(): - Console.WriteLine("done."); - res = 0; - break; - case client.NakStartService(error): - Console.WriteLine("Error code: " + error); - break; - case client.ChannelClosed(): - Console.WriteLine("Channel is closed."); - break; + case svmanager.RequestFailed(error): + Console.WriteLine("Failed to select service '{0}'.", serviceName); + ShowServiceError(error); + return error; + + case svmanager.ChannelClosed(): + throw new Exception("Service Manager closed channel before responding."); } -exit: - delete client; - delete ep; - return res; } - internal static int StopService(StopConfig! config) + internal static void ShowServiceError(ServiceError error) { - int res = 1; - ServiceManagementContract.Imp! ep; - ServiceControlContract.Imp! client; - ServiceControlContract.Exp! server; - - Console.Write("Stopping service: " + config.id + " ... "); - GetEndpoint(out ep); - ep.RecvSuccess(); - - ServiceControlContract.NewChannel(out client, out server); - ep.SendGetControl((int)config.id, server); - switch receive { - case ep.RecvAckGetControl(): - break; - case ep.NotFound(rejected): - delete rejected; - Console.WriteLine("Doesn't exist."); - goto exit; - break; - case ep.PermissionDenied(rejected): - delete rejected; - Console.WriteLine("Permission denied"); - goto exit; - break; - case ep.TryAgain(rejected): - delete rejected; - Console.WriteLine("Busy!"); - goto exit; - break; - } - - client.RecvSuccess(); - client.SendStopService(); - switch receive { - case client.AckStopService(): - Console.WriteLine("done."); - res = 0; - break; - case client.NakStopService(error): - Console.WriteLine("Error code: " + error); - break; - } -exit: - delete client; - delete ep; - return res; + Console.WriteLine("ServiceError: " + ServiceEnums.ToString(error)); } - internal static int RestartService(RestartConfig! config) + static internal int StartService(string! serviceName, bool wait) { - Console.WriteLine("-- Sorry, not implemented yet."); - return 1; + ServiceManagerContract.Imp! svmanager = ConnectServiceManager(); + + try { + + ServiceError error = SelectService(svmanager, serviceName); + if (error != ServiceError.None) { + return -1; + } + + if (wait) { + svmanager.SendStartServiceWait(); + + for (;;) { + switch receive { + case svmanager.RequestFailed(err): + ShowServiceError(err); + return -1; + + case svmanager.ServiceStarting(): + Console.WriteLine("Service Manager accepted request to start service '{0}'.", serviceName); + return 0; + + case timeout(TimeSpan.FromSeconds(10)): + Console.WriteLine("waiting..."); + break; + } + } + } + else { + svmanager.SendStartServiceNoWait(); + + switch receive { + case svmanager.RequestFailed(err): + ShowServiceError(err); + return -1; + + case svmanager.ServiceStarting(): + Console.WriteLine("Service Manager accepted request to start service '{0}'.", serviceName); + return 0; + } + } + } + finally { + delete svmanager; + } } + internal static int StopService(string! serviceName, bool wait) + { + ServiceManagerContract.Imp! svmanager = ConnectServiceManager(); + + try { + + ServiceError error = SelectService(svmanager, serviceName); + if (error != ServiceError.None) + return -1; + + if (wait) { + svmanager.SendStopServiceWait(); + + for (;;) { + switch receive { + case svmanager.RequestFailed(err): + ShowServiceError(err); + if (err == ServiceError.CannotStopService) { + Console.WriteLine("If this service is an 'always active' service, then the stop command cannot be used."); + Console.WriteLine("Instead, use the 'disable' command."); + } + return -1; + + case svmanager.ServiceStopping(): + Console.WriteLine("Service Manager accepted request to stop service '{0}'.", serviceName); + return 0; + + case timeout(TimeSpan.FromSeconds(10)): + Console.WriteLine("waiting..."); + break; + } + } + } + else { + svmanager.SendStopServiceNoWait(); + + switch receive { + case svmanager.RequestFailed(err): + ShowServiceError(err); + return -1; + + case svmanager.ServiceStopping(): + Console.WriteLine("Service Manager accepted request to stop service '{0}'.", serviceName); + return 0; + } + } + } + finally { + delete svmanager; + } + } + + internal static int WatchService(string! serviceName) + { + ServiceManagerContract.Imp! svmanager = ConnectServiceManager(); + + try { + + ServiceError error = SelectService(svmanager, serviceName); + if (error != ServiceError.None) { + return -1; + } + + svmanager.SendWatchServiceStatus(); + + switch receive { + case svmanager.RequestFailed(err): + ShowServiceError(err); + return -1; + + case svmanager.Ok(): + Console.WriteLine("Service Manager accepted request to watch service '{0}'.", serviceName); + break; + } + + for (;;) { + svmanager.SendWaitServiceChange(); + + switch receive { + case svmanager.ServiceStatusChanged(ServiceStatus status, bool missedChange): + Console.WriteLine("Status changed: "); + Console.WriteLine(" State: " + ServiceEnums.ToString(status.State)); + if (missedChange) { + Console.WriteLine(" Note: At least one status change was missed."); + } + break; + + case svmanager.RequestFailed(err): + Console.WriteLine("Request failed."); + ShowServiceError(err); + return -1; + + case svmanager.ChannelClosed(): + Console.WriteLine("Service Manager closed channel!"); + return -1; + } + } + + } + finally { + delete svmanager; + } + } + + const string ListServiceFormat = "{0,-20} {1,-10} {2,-6} {3}"; + internal static int ListServices(ListConfig! config) { - bool next = false; - int count = 0; - ServiceManagementContract.Imp! ep; + ServiceManagerContract.Imp! svmanager = ConnectServiceManager(); - GetEndpoint(out ep); - ep.RecvSuccess(); - ep.SendBeginEnumeration(); - Console.WriteLine(); - Console.WriteLine("PID Task Name"); - Console.WriteLine("=== ==================="); - do { - switch receive { - case ep.RecvCurrent(info): - ++count; - Console.WriteLine("{0,3} {1,-19}", - info->Id, info->Name); - delete info; - ep.SendMoveNext(); - next = true; - break; - case ep.RecvEnumerationTerminated(): - next = false; - break; - case ep.ChannelClosed(): - next = false; - break; + try { + Console.WriteLine(ListServiceFormat, "Name", "State", "PID", "Display Name"); + Console.WriteLine(ListServiceFormat, + new String('-', 20), + new String('-', 10), + new String('-', 6), + new String('-', 30)); + + ServiceInfo[]! in ExHeap first_entries = new[ExHeap] ServiceInfo[40]; + svmanager.SendEnumerateServices(first_entries); + + for (;;) { + switch receive { + case svmanager.EnumerationTerminated(entries, count): + ListServices(entries, count); + delete entries; + return 0; + + case svmanager.NextServiceInfo(entries, count): + ListServices(entries, count); + svmanager.SendEnumerateServices(entries); + break; + + case svmanager.ChannelClosed(): + Console.WriteLine("Service Manager channel closed"); + return -1; + + } } - } while (next); - Console.WriteLine(); - Console.WriteLine(count + " services are found."); - - delete ep; - return 0; + } + finally { + delete svmanager; + } } - internal static int DefaultMain(DefaultConfig! config) + static void ListServices(ServiceInfo[]! in ExHeap entries, int count) { - Console.WriteLine("Usage: svconf [name] [type]"); - Console.WriteLine(" @start Start the service"); - Console.WriteLine(" @stop Stop the service"); - //Console.WriteLine(" @restart Restart the service"); - Console.WriteLine(" @list Show a list of running services"); - Console.WriteLine(" type: Default or Resilient"); + for (int i = 0; i < count; i++) { + expose(entries[i]) + { + string! serviceName = ToString(entries[i].Config.ServiceName); + string! displayName = ToString(entries[i].Config.DisplayName); + string! stateString = ServiceEnums.ToString(entries[i].Status.State); + string! processIdString = entries[i].Status.ProcessId != -1 ? + (!)entries[i].Status.ProcessId.ToString() : ""; + Console.WriteLine(ListServiceFormat, serviceName, stateString, processIdString, displayName); + } + } + } - return 0; + public static string! ToString(char[] in ExHeap str) + { + if (str != null) + return Bitter.ToString2(str); + else + return ""; + } + + public static void ShowConfigDetailed(ServiceConfig config) + { + if (config.ServiceName == null) { + Console.WriteLine("Error: A ServiceConfig structure had a null ServiceName field."); + return; + } + string! serviceName = Bitter.ToString2(config.ServiceName); + + string! executableName = config.ExecutableName != null ? Bitter.ToString2(config.ExecutableName) : ""; + string! displayName = config.DisplayName != null ? Bitter.ToString2(config.DisplayName) : ""; + + Console.WriteLine("Service Configuration"); + Console.WriteLine("---------------------"); + Console.WriteLine(); + Console.WriteLine(DetailFormat, "Service Name", serviceName); + Console.WriteLine(DetailFormat, "Executable", executableName); + Console.WriteLine(DetailFormat, "Display Name", displayName); + Console.WriteLine(DetailFormat, "Activation Mode", ServiceEnums.ToString(config.ActivationMode)); + Console.WriteLine(DetailFormat, "Is Disabled?", config.IsAdministrativelyDisabled.ToString()); + + Console.WriteLine(DetailFormat, "Min/Max Processes", String.Format("min {0} / {1}", + config.MinProcesses, + config.MaxProcesses == ServiceConfig.UnlimitedProcesses ? "no max" : "max " + config.MaxProcesses)); + + string! max_age_text; + if (config.MaxProcessAgeInSeconds == ServiceConfig.UnlimitedProcessAge) + max_age_text = "unlimited"; + else { + TimeSpan limit = TimeSpan.FromSeconds(config.MaxProcessAgeInSeconds); + max_age_text = (!)limit.ToString(); + } + Console.WriteLine(DetailFormat, "Max Process Age", max_age_text); + + string clients_per_process_text; + if (config.MaxClientsPerProcess == ServiceConfig.UnlimitedClientsPerProcess) { + clients_per_process_text = "unlimited"; + } + else { + clients_per_process_text = config.MaxClientsPerProcess.ToString(); + } + Console.WriteLine(DetailFormat, "Max Clients per Process", clients_per_process_text); + + } + + const string DetailFormat = "{0,-25}: {1}"; + + public static void ShowStatusDetailed(ServiceStatus status) + { + Console.WriteLine("Service Status"); + Console.WriteLine("--------------"); + Console.WriteLine(); + Console.WriteLine(DetailFormat, "State", ServiceEnums.ToString(status.State)); + if (status.State != ServiceState.Stopped) { + Console.WriteLine(DetailFormat, "Process ID", status.ProcessId); + } + + // This is not yet accurate. + // Console.WriteLine(DetailFormat, "Total Active Clients", status.TotalActiveClients); + + Console.WriteLine(DetailFormat, "Total Active Processes", status.TotalActiveProcesses); + Console.WriteLine(DetailFormat, "Connect Queue Length", status.ConnectQueueLength); + + Console.WriteLine(DetailFormat, "Last Process Start", status.LastStartFailed ? "FAILED" : "Succeeded"); + if (status.LastStartFailed) { + Console.WriteLine(DetailFormat, "Last Start Error", ServiceEnums.ToString(status.LastStartError)); + } } } + } diff --git a/base/Applications/ServiceManager/SMSClient/SMSClient.csproj b/base/Applications/ServiceManager/SMSClient/SMSClient.csproj index 8f459c7..57d374b 100644 --- a/base/Applications/ServiceManager/SMSClient/SMSClient.csproj +++ b/base/Applications/ServiceManager/SMSClient/SMSClient.csproj @@ -1,8 +1,6 @@  + diff --git a/base/Applications/ServiceManager/TryEnter/Main.sg b/base/Applications/ServiceManager/TryEnter/Main.sg deleted file mode 100644 index c5d18f7..0000000 --- a/base/Applications/ServiceManager/TryEnter/Main.sg +++ /dev/null @@ -1,110 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -using System; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.SingSharp.Reflection; -using Microsoft.Singularity; -using Microsoft.Singularity.Applications; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Configuration; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Io; - -[assembly: Transform(typeof(ApplicationResourceTransform))] - -namespace Microsoft.Singularity.Applications.ServiceManager -{ - [ConsoleCategory(HelpMessage="TryEnter Test", DefaultAction=true)] - internal class DefaultConfig - { - [InputEndpoint("data")] - public readonly TRef Stdin; - - [OutputEndpoint("data")] - public readonly TRef Stdout; - - reflective internal DefaultConfig(); - - internal int AppMain() - { - return Main.AppMain(this); - } - } - - - internal class Worker - { - private Main! parent; - private int id; - - internal Worker(Main! parent, int id) - { - this.parent = parent; - this.id = id; - } - - internal void Run() - { - Console.WriteLine("Thread {0} start\n", id); - for (int i = 0; i < 10; i++) { - parent.Enter(id); - Thread.Sleep(100); - } - Console.WriteLine("Thread {0} end\n", id); - } - } - - internal class Main - { - private Object! mutex = new Object(); - private int count = 0; - - public void Start(int number) - requires number >= 0; - { - Thread![] thread = new Thread![number]; - Worker[] worker = new Worker[number]; - - for (int i = 0; i < number; i++) { - worker[i] = new Worker(this, i); - thread[i] = new Thread(new ThreadStart(worker[i].Run)); - } - - for (int i = 0; i < number; i++) { - thread[i].Start(); - } - - for (int i = 0; i < number; i++) { - thread[i].Join(); - } - } - - internal void Enter(int id) - { - assert mutex != null; - - Console.WriteLine("{0} try enter", id); - if (!Monitor.TryEnter(mutex)) { - // Never reached here - Console.WriteLine("{0} couldn't get monitor", id); - ++count; - return; - } - Console.WriteLine("{0} got monitor", id); - Thread.Sleep(1000); - Monitor.Exit(mutex); - Console.WriteLine("{0} exit (count: {1})", id, count); - } - - internal static int AppMain(DefaultConfig! config) - { - new Main().Start(4); - - return 0; - } - } -} diff --git a/base/Applications/ServiceManager/TryEnter/TryEnterTest.csproj b/base/Applications/ServiceManager/TryEnter/TryEnterTest.csproj deleted file mode 100644 index 594c18d..0000000 --- a/base/Applications/ServiceManager/TryEnter/TryEnterTest.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - Exe - TryEnterTest - true - - - - - - - - - - - - - diff --git a/base/Applications/Shell/Breaker.cs b/base/Applications/Shell/Breaker.cs index 91921c7..ff03bd9 100644 --- a/base/Applications/Shell/Breaker.cs +++ b/base/Applications/Shell/Breaker.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Breaker.cs -// // Note: // diff --git a/base/Applications/Shell/Manifest.sg b/base/Applications/Shell/Manifest.sg deleted file mode 100644 index 165ef5a..0000000 --- a/base/Applications/Shell/Manifest.sg +++ /dev/null @@ -1,292 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Manifest.cs -// -// Note: Creates and binds endpoints using a manifest. -// - -using System; -using System.Text; -using System.Collections; - -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Extending; -using Microsoft.Singularity.Io; -using Microsoft.Singularity.Memory; -using Microsoft.Singularity.Xml; - -namespace Microsoft.Singularity.Applications -{ - - /// - /// This is a placeholder class for an application's manifest. - /// At present the application manifest is represented by a hashtable. - /// The hashtable has several important properties: - /// - name: the name of the application - /// - path: path to the application executable - /// - public class Manifest - { - /// - /// We treat the application manifest as a collection of - /// key, value pairs. - /// - - - private Hashtable! data = new Hashtable(); - private const string pipeContract = "Microsoft.Singularity.Io.UnicodePipeContract"; - - private string name; - private string path; - - private readonly XmlNode! applicationNode; - private readonly XmlNode! processNode; - private readonly XmlNode actionNode; - private readonly XmlNode categoriesNode; - private readonly XmlNode stringParametersNode; - private readonly XmlNode stringArrayParametersNode; - private readonly XmlNode longParametersNode; - private readonly XmlNode boolParametersNode; - private static StringBuilder sb; - - /// - /// Create a manifest object from the given xml representation - /// - - [Microsoft.Contracts.NotDelayed] - public Manifest(byte[] xml) - { - XmlReader! reader = new XmlReader(xml); - XmlNode! doc = (!)reader.Parse(); - - this(doc); - } - - [Microsoft.Contracts.NotDelayed] - private Manifest(XmlNode! doc) - { - XmlNode app = (!)doc.GetChild("application"); - name = app["name"]; - XmlNode proc = null; - foreach (XmlNode! process in app.Children) { - if (process.Name != "process") { - continue; - } - if (process.GetAttribute("main", "false") == "true") { - path = process["path"]; - proc = process; - break; - } - } - applicationNode = (!)app; - XmlNode! processNode = this.processNode = (!)proc; - base(); - - categoriesNode = processNode.GetChild("categories"); - - if (categoriesNode != null) { - return; - } - else { - DebugStub.WriteLine("::: no category"); - } - } - - public XmlNode GetCategoriesNode() - { - return categoriesNode; - } - - - public void SetProperty(object! name, object value) - { - data[name] = value; - } - - public string Name - { - get { - return name; - } - } - - public string Path - { - get { - return path; - } - } - - public string GetHelpMessage(XmlNode categoryNode) - { - return categoryNode == null? null : categoryNode.GetAttribute("HelpMessage", null); - } - - - // given an action find the corresponding category node - public XmlNode GetCategoryNode(string actionName) { - if (categoriesNode == null) { - //Console.WriteLine("GetCategoryNode: no categories!"); - return null; - } - if (actionName == null) { - // return the 1st one if there is one - //Console.WriteLine("GetCategoryNode: Selecting default!"); - XmlNode n = categoriesNode.GetChild("category"); - if (n == null) { - return null; - } - else return n; - } - else { - foreach (XmlNode! c in categoriesNode.Children) { - string a = c.GetAttribute("Action",null); - //Console.WriteLine("action= {0}.",a); - if ( a != null && a == actionName) { - //Console.WriteLine("found a match for action {0}",actionName); - return c; - } - } - return null; - } - } - - public XmlNode GetBoolParameterNode(XmlNode! category) - { - return category.GetChild("BoolParameters"); - } - - public XmlNode GetStringArrayParameterNode(XmlNode! category) - { - return category.GetChild("StringArrayParameters"); - } - - public XmlNode GetStringParameterNode(XmlNode! category) - { - return category.GetChild("StringParameters"); - } - - public XmlNode GetLongParameterNode(XmlNode! category) - { - return category.GetChild("LongParameters"); - } - - public bool HasParameters() - { - return (categoriesNode != null); - } - -#if false - public void ShowParameters() - { - if (categoryNode == null) { - Console.WriteLine("::: no category"); - return; - } - if (stringParametersNode == null) { - Console.WriteLine("::: no strings"); - } - if (longParametersNode == null) { - Console.WriteLine("::: no integers"); - } - } -#endif - public int SetEndpoints(Process process, string action, bool show) - { - // walk the XmlTree looking endpoints - // these will be used by the binder to setup and transfer to - // the starting process much like what is done for drivers - - XmlNode categoryNode = GetCategoryNode(action); - //Console.WriteLine(" Set endpoints"); - if (categoryNode == null) { - Console.WriteLine("No endpoints in console app!"); - //DebugStub.Break(); - return 0; - } - - XmlNode endpoints = categoryNode.GetChild("endpoints"); - if (endpoints == null) { - //DebugStub.Break(); - return 0; - } - int endpointCount = endpoints.GetAttribute("length", 0); - if (endpointCount == 0) { - DebugStub.Break(); - return 0; - } - - //fill in ep set - foreach (XmlNode! endpoint in endpoints.Children) { - string! contract - = (!)endpoint.GetAttribute("contractName", ""); - int index = endpoint.GetAttribute("id", -1); - - if (endpoint.Name == "endpoint") { - // FIXFIX -- if it is a pipe ignore it for now - if (show) { - Console.WriteLine(" endpoint: {0}", contract); - } - else { - if (contract != pipeContract) { - // get a pre-bound generic endpoint to a service from the - // resource tracker - if (process == null) { - throw new Exception("Process arg was null."); - } - if (!Binder.BindServiceUser(process, index, contract, endpoint)) { - return -1; - } - } - else { - DebugStub.WriteLine("shell.manifest.SetEndpoints: skipping pipe at {0}", __arglist(index)); - } - } - } - } - return endpointCount; - } - -#if false - public override string! ToString() - { - StringBuilder sb = new StringBuilder(); - - sb.AppendFormat("", Name); - - // the main executable - sb.AppendFormat("", Path); - - foreach(DataItem item in data.Values) { - if (item.Directory == null) { - sb.AppendFormat("", - item.Name, item.Value); - } - else { - sb.AppendFormat("", - item.Directory, item.Name, item.Value); - } - } - - sb.Append(""); - sb.Append(""); - sb.Append(""); - sb.Append(""); - - return sb.ToString(); - } - - public byte[] Serialize() - { - return (new UTF8Encoding()).GetBytes(ToString()); - } -#endif - } -} diff --git a/base/Applications/Shell/Parser.cs b/base/Applications/Shell/Parser.cs index 83311d2..ef04dc8 100644 --- a/base/Applications/Shell/Parser.cs +++ b/base/Applications/Shell/Parser.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; using System.Collections; @@ -13,15 +11,15 @@ using Microsoft.Contracts; namespace Microsoft.Singularity.Applications { - /** - * This is a general lr parser driver. - * Parses the programming by first lexing it into a number - * of tokens for a token stack. Then parser then pops tokens off - * this stack, consults the parse tables and push the corresponding - * nonterminals and states on the operand stack. This is the same - * algorithm from the dragon book with some tweaks to support optional - * tokens (i.e. newlines). - **/ + /// + // This is a general lr parser driver. + // Parses the programming by first lexing it into a number + // of tokens for a token stack. Then parser then pops tokens off + // this stack, consults the parse tables and push the corresponding + // nonterminals and states on the operand stack. This is the same + // algorithm from the dragon book with some tweaks to support optional + // tokens (i.e. newlines). + // abstract class Parser { private const int END_INPUT_MARKER_ID = 2; @@ -66,16 +64,18 @@ namespace Microsoft.Singularity.Applications leftStack.Push(st); leftStack.Push(tok); leftStack.Push(new State(action.stateOrProduction)); - } else if (action.type == ActionType.REDUCE) { + } + else if (action.type == ActionType.REDUCE) { Production production = (!)ProductionTable[action.stateOrProduction]; Object value = production.Reduction(leftStack); StackElement topLeft = (StackElement)leftStack.Pop(); State previousState; if (topLeft is State) { previousState = (State)topLeft; - } else { + } + else { leftStack.Push(topLeft); - previousState = st; /* took an epsilon transition */ + previousState = st; // took an epsilon transition } State nextState = GotoTable[previousState.id, production.NonterminalType]; if (nextState == null) throw new Exception("missing state"); @@ -83,7 +83,8 @@ namespace Microsoft.Singularity.Applications leftStack.Push(new Nonterminal(production.NonterminalType, value)); leftStack.Push(nextState); rightStack.Push(tok); - } else if (action.type == ActionType.ACCEPT) { + } + else if (action.type == ActionType.ACCEPT) { break; } } @@ -121,7 +122,8 @@ namespace Microsoft.Singularity.Applications spec.Lexer(token); if (token.value != null) tokens.Add(token); - } else { + } + else { tokens.Add(new Token(spec.Type, match.Value, lineNumber, charIndex)); } int lastIndex; @@ -129,7 +131,8 @@ namespace Microsoft.Singularity.Applications if (occurrences > 0) { lineNumber += occurrences; charIndex = match.Value.Length - (lastIndex + 1); - } else { + } + else { charIndex += match.Value.Length; } goto outer; @@ -159,10 +162,13 @@ namespace Microsoft.Singularity.Applications public readonly int id; public Object value; public StackElement(int id) { this.id = id; } +#if false + // Error: Bartok can not handle complicated override pattern of System.Object.ToString. [Specifically, it was overridden via a MethodImpl or replaced via interface method reimplementation, effectively merging two virtual slots. Then the original slot was overridden by Microsoft.Singularity.Applications.Parser.Token.ToString, effectively splitting the slots again. Bartok currently can not analyze the split.] public override string! ToString() { return id.ToString(); } +#endif } public class State : StackElement { @@ -183,10 +189,13 @@ namespace Microsoft.Singularity.Applications this.optional = optional; } +#if false + // Error: Bartok can not handle complicated override pattern of System.Object.ToString. [Specifically, it was overridden via a MethodImpl or replaced via interface method reimplementation, effectively merging two virtual slots. Then the original slot was overridden by Microsoft.Singularity.Applications.Parser.Token.ToString, effectively splitting the slots again. Bartok currently can not analyze the split.] public override string! ToString() { return value + ":" + id; } +#endif } private class Nonterminal : StackElement { diff --git a/base/Applications/Shell/ScriptEngine.cs b/base/Applications/Shell/ScriptEngine.cs index cf7005f..d4d29ef 100644 --- a/base/Applications/Shell/ScriptEngine.cs +++ b/base/Applications/Shell/ScriptEngine.cs @@ -1,14 +1,11 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; using System.Collections; -using System.IO; using System.Text.RegularExpressions; using System.Text; using Microsoft.Singularity; @@ -17,25 +14,25 @@ using Microsoft.Contracts; namespace Microsoft.Singularity.Applications { - /** - * Provides a reasonable script engine implementation. - * This engine is composed of two distinct parts: the parser - * and the interpreter. Parser.cs and ScriptParser.cs provide - * the parsing functionality. Parser.cs is the general handwritten slr - * driver while ScriptParser contains the automatically generated - * parse tables and semantic action functions for productions - * and lex operations of this specific scripting language. This file - * provides the interpretation mechanism that runs over an ast produced - * by the parser. - **/ + /// + // Provides a reasonable script engine implementation. + // This engine is composed of two distinct parts: the parser + // and the interpreter. Parser.cs and ScriptParser.cs provide + // the parsing functionality. Parser.cs is the general handwritten slr + // driver while ScriptParser contains the automatically generated + // parse tables and semantic action functions for productions + // and lex operations of this specific scripting language. This file + // provides the interpretation mechanism that runs over an ast produced + // by the parser. + // class ScriptEngine { private String script; private Block parsedScript; public const String LAST_COMMAND_EXIT_SYMBOL = "?"; public const String NUM_ARGUMENTS_SYM = "#"; - public delegate int CommandLineRunner(ShellControl! shellControl, - String[] commandLine, + public delegate int CommandLineRunner(ShellControl! shellControl, + String[] commandLine, bool isBackground); private CommandLineRunner runner; @@ -52,21 +49,26 @@ namespace Microsoft.Singularity.Applications ScriptEngine engine = new ScriptEngine(script, runner); try { engine.Parse(); - } catch (Parser.ParseException e) { + } + catch (Parser.ParseException e) { Console.WriteLine(e.Message); return -1; - } catch (Exception e) { + } + catch (Exception e) { Console.WriteLine(e.Message); return -1; } try { return engine.Run(shellControl, arguments); - } catch (Interpretation.InterpretationException e) { + } + catch (Interpretation.InterpretationException e) { Console.WriteLine(e.Message); - } catch (ScriptException e) { + } + catch (ScriptException e) { Console.WriteLine(e.Message); - } catch (Exception e) { + } + catch (Exception e) { Console.WriteLine(e.Message); } return -1; @@ -123,8 +125,10 @@ namespace Microsoft.Singularity.Applications { get { Object exitCode = LookupSymbol(LAST_COMMAND_EXIT_SYMBOL); - if (exitCode == null) { return 0; } - return (Int32) exitCode; + if (exitCode == null) { + return 0; + } + return (Int32) exitCode; } set { SetSymbol(LAST_COMMAND_EXIT_SYMBOL, value); } } @@ -178,7 +182,7 @@ namespace Microsoft.Singularity.Applications public DivideByZeroException() : base("Division by zero undefined.") { } } - + } public abstract class Element { } @@ -219,9 +223,11 @@ namespace Microsoft.Singularity.Applications Object value = GetValue(interp); if (value is String) { return Int32.Parse((String)value); - } else if (value is Int32) { + } + else if (value is Int32) { return (int)value; - } else if (value is bool) { + } + else if (value is bool) { return ((bool)value) ? 1 : 0; } throw new Interpretation.UnknownTypeException(value.GetType().ToString()); @@ -233,14 +239,17 @@ namespace Microsoft.Singularity.Applications if (value is String) { try { return Boolean.Parse((String)value); - } catch (FormatException) { + } + catch (FormatException) { throw new Interpretation.TypeConversionException((String)value, "bool"); } - } else if (value is Int32) { + } + else if (value is Int32) { return ((int)value) == 0 ? true : false; - } else if (value is Boolean) { + } + else if (value is Boolean) { return (bool)value; } throw new Interpretation.UnknownTypeException(value.GetType().ToString()); @@ -369,15 +378,21 @@ namespace Microsoft.Singularity.Applications private static int CompareTo(Object left, Expression e, Interpretation! interp) { if (left == null) { - if (e == null) { return 0; } + if (e == null) { + return 0; + } return -1; } - if (e == null) { return 1; } + if (e == null) { + return 1; + } if (left is String) { return ((String)left).CompareTo(e.StringValue(interp)); - } else if (left is int) { + } + else if (left is int) { return ((int)left).CompareTo(e.IntValue(interp)); - } else if (left is Boolean) { + } + else if (left is Boolean) { return ((Boolean)left).CompareTo(e.BoolValue(interp)); } throw new Interpretation.UnknownTypeException("unsupported type"); @@ -477,7 +492,8 @@ namespace Microsoft.Singularity.Applications String value = StringValue(interp); try { return Int32.Parse(value); - } catch (FormatException) { + } + catch (FormatException) { throw new Interpretation.TypeConversionException(value, "String"); } } @@ -487,7 +503,8 @@ namespace Microsoft.Singularity.Applications String value = StringValue(interp); try { return Boolean.Parse(value); - } catch (FormatException) { + } + catch (FormatException) { throw new Interpretation.TypeConversionException(value, "String"); } } @@ -504,7 +521,7 @@ namespace Microsoft.Singularity.Applications ArrayList chunks = new ArrayList(); char last = (char) -1; int lastIndex = 0; - for (int i = 0; i < interpolation.Length; last = interpolation[i], ++i) { + for (int i = 0; i < interpolation.Length; last = interpolation[i], ++i) { if (interpolation[i] == '$' && last != '\\') { chunks.Add(interpolation.Substring(lastIndex, i - lastIndex)); lastIndex = i + 1; @@ -535,7 +552,8 @@ namespace Microsoft.Singularity.Applications } var = split_i.Substring(1, index - 1); rest = index + 1 < split_i.Length ? split_i.Substring(index + 1) : ""; - } else { + } + else { Match m = whitespace.Match(split_i); assert m != null; int index = m.Success ? m.Index : split_i.Length; @@ -637,7 +655,8 @@ namespace Microsoft.Singularity.Applications { if (condition.BoolValue(interp)) { trueBlock.Interpret(shellControl, interp); - } else { + } + else { falseBlock.Interpret(shellControl, interp); } } @@ -706,19 +725,25 @@ namespace Microsoft.Singularity.Applications try { int exitCode = Int32.Parse(arguments[1]); interp.ExitCode = exitCode; - } catch (FormatException) { + } + catch (FormatException) { throw new Interpretation.TypeConversionException((String)arguments[1], "integer"); } } interp.Exit = true; - } else { - try { - int exitCode = interp.runner(shellControl, arguments, async); - interp.ExitCode = exitCode; - } - catch (FileNotFoundException) { - interp.Exit = true; - } + } + else if (false) { + // + //arguments[0].Trim() == "read") { + //string input = Console.ReadLine(); + //String[] split = whitespace.Split(input); + //for (int j = 1, k = 0; j < arguments.Length && k < split.Length; ++j, ++k) { + //interp.SetSymbol(arguments[j], split[k]); + // + } + else { + int exitCode = interp.runner(shellControl, arguments, async); + interp.ExitCode = exitCode; } } } diff --git a/base/Applications/Shell/Shell.csproj b/base/Applications/Shell/Shell.csproj index f3c3159..df9ece1 100644 --- a/base/Applications/Shell/Shell.csproj +++ b/base/Applications/Shell/Shell.csproj @@ -1,8 +1,6 @@ - - - - - - Exe - Sleep - - - - - - - - - diff --git a/base/Applications/Sleep/sleep.csproj b/base/Applications/Sleep/sleep.csproj new file mode 100644 index 0000000..ce4cf3d --- /dev/null +++ b/base/Applications/Sleep/sleep.csproj @@ -0,0 +1,23 @@ + + + + + + + Exe + sleep + + + + + + + + + diff --git a/base/Applications/Slides/DemoDeck.pdf b/base/Applications/Slides/DemoDeck.pdf new file mode 100644 index 0000000..650e3a8 Binary files /dev/null and b/base/Applications/Slides/DemoDeck.pdf differ diff --git a/base/Applications/Slides/DemoDeck.ppt b/base/Applications/Slides/DemoDeck.ppt deleted file mode 100644 index 016306a..0000000 Binary files a/base/Applications/Slides/DemoDeck.ppt and /dev/null differ diff --git a/base/Applications/Slides/Resources.cpp b/base/Applications/Slides/Resources.cpp index 65e4917..8596b5d 100644 --- a/base/Applications/Slides/Resources.cpp +++ b/base/Applications/Slides/Resources.cpp @@ -1,7 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // +// ---------------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////////// // diff --git a/base/Applications/Slides/Slide.bmp b/base/Applications/Slides/Slide.bmp deleted file mode 100644 index 42f8b89..0000000 Binary files a/base/Applications/Slides/Slide.bmp and /dev/null differ diff --git a/base/Applications/Slides/Slides.csproj b/base/Applications/Slides/Slides.csproj index d5bf87c..d1317f4 100644 --- a/base/Applications/Slides/Slides.csproj +++ b/base/Applications/Slides/Slides.csproj @@ -1,8 +1,6 @@  + + + + + + Exe + StartProcess + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/SysInfo/SysInfo.cs b/base/Applications/SysInfo/SysInfo.cs new file mode 100644 index 0000000..5ab118d --- /dev/null +++ b/base/Applications/SysInfo/SysInfo.cs @@ -0,0 +1,235 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: Utility program to query the system information +// +using System; +using System.Diagnostics; +using System.Net.IP; +using System.Text.RegularExpressions; + +using Microsoft.Singularity; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Directory; +using Microsoft.SingSharp; +using NetStack.Contracts; +using NetStack.Channels.Public; + +using Microsoft.Contracts; +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Eventing; + +[assembly: Transform(typeof(ApplicationResourceTransform))] + +namespace Microsoft.Singularity.Applications +{ + + [ConsoleCategory(HelpMessage="Test utility application", DefaultAction=true)] + internal class DefaultConfig + { + [Endpoint] + public readonly TRef Stdin; + + [Endpoint] + public readonly TRef Stdout; + + [StringParameter( "S", Mandatory=false, HelpMessage="Source filter")] + internal string SourceFilter; + + [BoolParameter( "a", Default=false, HelpMessage="display all events")] + internal bool DumpAll; + + reflective internal DefaultConfig(); + + internal int AppMain() { + return SysInfo.DefaultMain(this); + } + } + + public class SysInfo + { + + static string SourceFilter; + static bool DumpAll; + + [CLSCompliant(false)] + static internal bool ActiveEntryDelegate(UIntPtr sourceHandle, + int index, + EventDescriptor descriptor, + QueryBuffer entryBuffer, + ref EnumerationContext context) + { + if (!DumpAll) { + + return false; + } + + if (descriptor != null) { + Console.Write("{0:d5}", index); + descriptor.EnumerateFields(new QueryFieldDelegate(ActiveFieldDelegate), entryBuffer, ref context); + Console.WriteLine(""); + } + + return true; + } + + static internal bool TableHeadDelegate(FieldDescriptor fieldDescriptor, + object obj, + ref EnumerationContext ctx) + { + if (fieldDescriptor != null) { + Console.Write(" {0}", fieldDescriptor.Name); + } + return true; + } + + static internal bool ActiveFieldDelegate(FieldDescriptor fieldDescriptor, + object obj, + ref EnumerationContext ctx) + { + if (obj != null) { + + Console.Write(" {0}", obj.ToString()); + } + return true; + } + + static internal bool SourceDelegate(UIntPtr sourceHandle, + UIntPtr storageHandle, + UIntPtr eventType, + UInt16 count, + string bufferName, + EventDescriptor descriptor, + ref EnumerationContext context) + { + if (SourceFilter != null) { + + if (!Regex.IsMatch(bufferName, SourceFilter)) { + + return true; + } + } + + if (descriptor != null) { + Console.WriteLine("Source: \"{0}\". {1} entries of type {2}", + bufferName, count, descriptor.GetName()); + + if (DumpAll) { + Console.Write("Index"); + descriptor.EnumerateFields(new QueryFieldDelegate(TableHeadDelegate), null, ref context); + Console.WriteLine(""); + Console.Write("-----"); + + for (int i = 0; i < descriptor.GetFieldsCount(); i++) { + Console.Write("----------"); + } + Console.WriteLine(""); + } + + } else { + Console.WriteLine("Source: \"{0}\". Storage handle ={1}", + bufferName, sourceHandle); + } + return true; + } + + static internal bool fieldDelegate(FieldDescriptor fieldDescriptor, + object obj, + ref EnumerationContext context) + { + if ((fieldDescriptor != null) && (obj != null)) { + + Console.WriteLine(" {0}:{1}:{2} = {3}", + fieldDescriptor.Offset, + fieldDescriptor.GetTypeName(), + fieldDescriptor.Name, + obj.ToString()); + } + + return true; + } + + + static internal bool EntryDelegate(EventDescriptor currentEntry, + QueryBuffer buffer, + ref EnumerationContext context) + { + if (!DumpAll) { + + return false; + } + + if ((currentEntry != null) && (buffer != null)) { + + currentEntry.EnumerateFields(new QueryFieldDelegate(fieldDelegate), buffer, ref context); + } + + return true; + } + + public static void ListActiveSources() + { + Controller hostController = Controller.GetSystemController(); + EnumerationContext ctx = new EnumerationContext(); + + if ((hostController == null) || (ctx == null)) { + return; + } + + LoadEventTypes(hostController); + + hostController.EnumerateActiveSources( + new QuerySourceDelegate(SourceDelegate), + new ActiveSourceEntryDelegate(ActiveEntryDelegate), + new QueryEntryDelegate(EntryDelegate), + 0, + ref ctx); + } + + public static void LoadEventTypes(Controller! hostController) + { + int currentSize = 100; + UIntPtr [] eventTypeArray = new UIntPtr[currentSize]; + + if (eventTypeArray != null) { + + QuerySession.FlushCaches(); + + int eventTypeCount = hostController.QueryEventTypeList(eventTypeArray, currentSize); + + while (eventTypeCount > currentSize) { + + eventTypeArray = new UIntPtr[eventTypeCount]; + eventTypeCount = hostController.QueryEventTypeList(eventTypeArray, currentSize); + } + + for (int i = 0; i < eventTypeCount; i++) { + + + EventDescriptor descriptor = QuerySession.GetEventDescriptor(hostController, + eventTypeArray[i]); + + if (descriptor != null) { + + Console.WriteLine("Event type \"{0}\"", descriptor.GetName()); + } + + } + } + } + + + internal static int DefaultMain(DefaultConfig! config) + { + SourceFilter = config.SourceFilter; + DumpAll = config.DumpAll; + + ListActiveSources(); + return 0; + } + } // end class SysInfo +} diff --git a/base/Applications/SysInfo/SysInfo.csproj b/base/Applications/SysInfo/SysInfo.csproj new file mode 100644 index 0000000..c7462b7 --- /dev/null +++ b/base/Applications/SysInfo/SysInfo.csproj @@ -0,0 +1,23 @@ + + + + + + + Exe + SysInfo + + + + + + + + + diff --git a/base/Applications/TaskList/TaskList.csproj b/base/Applications/TaskList/TaskList.csproj index ce0f69e..a590330 100644 --- a/base/Applications/TaskList/TaskList.csproj +++ b/base/Applications/TaskList/TaskList.csproj @@ -1,8 +1,6 @@  + + + + + + Exe + TestUtil + + + + + + + + + diff --git a/base/Applications/Tester/TestDriver.sg b/base/Applications/Tester/TestDriver.sg new file mode 100644 index 0000000..7f44ec8 --- /dev/null +++ b/base/Applications/Tester/TestDriver.sg @@ -0,0 +1,747 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +//using System.Threading; +using Microsoft.SingSharp; +using Microsoft.Singularity; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Test.Contracts; +using Microsoft.Singularity.Xml; + +using FileSystem.Utils; +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Io; +//using System.Collections; + +using Microsoft.Singularity.V1.Services; +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Applications; +using Microsoft.Contracts; +using Microsoft.Singularity.Configuration; + +[assembly: Transform(typeof(ApplicationResourceTransform))] + +namespace Microsoft.Singularity.Test +{ + + [ConsoleCategory(HelpMessage="Test harness", DefaultAction=true)] + internal sealed class Parameters { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [Endpoint] + public readonly TRef nsRef; + + [StringParameter("TestProfile", Mandatory=true, Position=0, + HelpMessage="Name of test profile to run") ] + internal string profileName; + + [BoolParameter("FailFast", Default=false, + HelpMessage="Terminate testing on first failure")] + public bool failFast; + + [BoolParameter("Verbose", Default=false, + HelpMessage="Report assertions even when they pass")] + public bool reportAssertions; + + [BoolParameter("Timings", Default=false, + HelpMessage="Prepend a fixed profix and timing information")] + public bool reportTimes; + + [LongParameter("TestTimeout", Default=60000L, + HelpMessage="Default timeout in millisecond for each test (default=60 seconds)")] + public long defaultTimeout; + + [LongParameter("Iterations", Default=1L, + HelpMessage="Number of times to run the profiles (default = 1)")] + public long iterations; + + [LongParameter("Pass", Default=0L, + HelpMessage="Only run tests from the profile that are designated for this pass (default Pass 0).")] + public long testPass; + + reflective internal Parameters(); + + internal int AppMain() { + try { + return TestDriver.AppMain(this); + } + catch (Exception e) { + Console.WriteLine("Execution failed: {0}", e); + return -1; + } + } + } + + [ConsoleCategory(HelpMessage="Run a single test case", Action="manual")] + internal sealed class SingleParameters { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [Endpoint] + public readonly TRef nsRef; + + [StringParameter("Module", Mandatory=true, Position=0, HelpMessage="Name of test module") ] + internal string moduleName; + [StringParameter("Suite", Mandatory=true, Position=1, HelpMessage="Name of test suite") ] + internal string suiteName; + [StringArrayParameter("Tests", Mandatory=false, HelpMessage="Names of tests to run") ] + internal string[] testNames; + + [BoolParameter("FailFast", Default=false, + HelpMessage="Terminate testing on first failure.")] + public bool failFast; + + [BoolParameter("Verbose", Default=false, + HelpMessage="Report assertions even when they pass")] + public bool reportAssertions; + + [BoolParameter("Timings", Default=false, + HelpMessage="Prepend a fixed profix and timing information")] + public bool reportTimes; + + [LongParameter("TestTimeout", Default=60000L, + HelpMessage="Default timeout in millisecond for each test (default=60 seconds)")] + public long defaultTimeout; + + [LongParameter("Iterations", Default=1L, + HelpMessage="Number of times to run the profiles (default = 1)")] + public long iterations; + + reflective internal SingleParameters(); + + internal int AppMain() { + try { + return TestDriver.SingleMain(this); + } + catch (Exception e) { + Console.WriteLine("Execution failed: {0}", e); + return -1; + } + } + } + + internal struct Progress + { + public readonly int passed; + public readonly int skipped; + public readonly int failures; + public readonly int knownFailures; + + public Progress(int p, int s, int f, int k) { + passed = p; + skipped = s; + failures = f; + knownFailures = k; + } + } + + public class TestDriver + { + internal const long CYCLE_ADJUST = 10000; + // 3 second delay for a process to stop + private readonly TimeSpan CHILD_STOP_WAIT = new TimeSpan(3000); + internal const string PASSED = "PASSED"; + internal const string FAILED = "FAILED"; + internal const string SKIPPED = "SKIPPED"; + internal const string PARTIAL = "PARTIAL"; + + private const string INFO = "INFO"; + private const string CHANNEL_CLOSED = "CONNECTION CLOSED"; + private const string TIMED_OUT = "test timed out"; + private const string STOP_TIMED_OUT = "MODULE SHUTDOWN TIMED OUT"; + private const string PREVIOUS_FAILURE = "previous failures"; + private const string SETUP_FAILED = "previous setup failure"; + private const string SETUP_SKIPPED = "previous setup skipped"; + private const string ASSERT_FAILED = "ASSERTION FAILED"; + + internal const string BEGIN = "BEGIN"; + internal const string END = "END"; + internal const string INIT = "INIT"; + internal const string CLEANUP = "CLEANUP"; + + internal const string PROFILE = "PROFILE"; + internal const string MODULE = "MODULE"; + internal const string SUITE = "SUITE"; + internal const string TEST = "TEST"; + + private const string DEFAULT_FOLDER = "/init/"; + private const string DEFAULT_EXT = ".tst"; + + // STATE //////////////////////////////////////////// + + private bool m_reportAsserts; + private bool m_failFast; + private int m_testPass; + + private XmlNode! m_profile; + private TestContext! m_root; + + private int m_passed; + private int m_failures; + private int m_knownFailures; + private int m_skipped; + + // CREATION ////////////////////////////////////// + public TestDriver(string! profileName, + XmlNode! node, + long timeout, + bool failFast, + bool reportAll, + bool reportTimes, + int testPass) + { + m_root = new TestContext((!)node.GetAttribute("Name", profileName), + node, + TimeSpan.FromMilliseconds(timeout), + reportTimes); + m_profile = node; + m_failFast = failFast; + m_reportAsserts = reportAll; + m_testPass = testPass; + } + + // QUERIES ///////////////////////////////////////// + + internal Progress SoFar + { + get { + return new Progress(m_passed, m_skipped, m_failures, m_knownFailures); + } + } + + // OPERATIONS ////////////////////////////////////// + + internal static int AppMain(Parameters! config) + { + DirectoryServiceContract.Imp ds = config.nsRef.Acquire(); + if (ds == null) { + throw new Exception("Unable to acquire handle to the Directory Service root"); + } + ds.RecvSuccess(); + + string! name; + string! fname = PathAndName(config.profileName, out name); + XmlReader reader = new XmlReader(); + XmlNode! root; + try { + root = (!) reader.Parse(fname); + } + catch (Exception ex) { + Console.WriteLine(ex.ToString()); + throw new Exception("Unable to load test profile: " + config.profileName); + } + + PipeMultiplexer outputMux = MuxOut(); + if (outputMux == null) { + delete ds; + return -1; + } + + int problems = 0; + try { + for (int i = 0; config.iterations == -1 || i < config.iterations; i++) { + TestDriver driver = + new TestDriver(name, + root, + config.defaultTimeout, + config.failFast, + config.reportAssertions, + config.reportTimes, + (int)config.testPass); + driver.RunProfile(ds, outputMux); + problems += driver.m_failures + driver.m_skipped; + } + } + catch (Exception ex) { + Console.WriteLine(ex.ToString()); + throw; + } + finally { + outputMux.Dispose(); + delete ds; + } + return problems; + } + + internal static int SingleMain(SingleParameters! config) + { + DirectoryServiceContract.Imp ds = config.nsRef.Acquire(); + if (ds == null) { + throw new Exception("Unable to acquire handle to the Directory Service root"); + } + ds.RecvSuccess(); + PipeMultiplexer outputMux = MuxOut(); + + XmlNode! root = new XmlNode(""); + XmlNode! profile = new XmlNode("Profile"); + root.AddAttribute("Name", "manual"); + root.AddChild(profile); + XmlNode! module = new XmlNode("Module"); + module.AddAttribute("Name", config.moduleName); + profile.AddChild(module); + XmlNode! suite = new XmlNode("Suite"); + suite.AddAttribute("Name", config.suiteName); + module.AddChild(suite); + foreach (string! t in config.testNames) { + XmlNode! tx = new XmlNode("Test"); + tx.AddAttribute("Name", t); + suite.AddChild(tx); + } + + int problems = 0; + try { + for (int i = 0; config.iterations == -1 || i < config.iterations; i++) { + TestDriver driver = + new TestDriver("manual", + root, + config.defaultTimeout, + config.failFast, + config.reportAssertions, + config.reportTimes, + 0); + driver.RunProfile(ds, outputMux); + problems += driver.m_failures + driver.m_skipped; + } + } + catch (Exception ex) { + Console.WriteLine(ex.ToString()); + throw; + } + finally { + outputMux.Dispose(); + delete ds; + } + return problems; + } + + // Redirect our standard output into a multiplexer so we can interleave + // output from child processes + static private PipeMultiplexer! MuxOut() + { + // Swap our real stdOut with a newly created one + UnicodePipeContract.Exp! newOutputExp; + UnicodePipeContract.Imp! newOutputImp; + UnicodePipeContract.NewChannel(out newOutputImp, out newOutputExp); + UnicodePipeContract.Imp stdOut = ConsoleOutput.Swap(newOutputImp); + if (stdOut == null) { + // TODO may not require stdout once we report to debugStub + delete newOutputExp; + throw new Exception("test expects a STDOUT pipe"); + } + // Use a mux to splice our own output together with the child + // processes we will run. + return PipeMultiplexer.Start(stdOut, newOutputExp); + } + + // return a full path, and output the core name (without extension or path) + static private string! PathAndName(string! arg, out string! name) + { + string! res = arg.StartsWith("/") ? arg : DEFAULT_FOLDER + arg; + int ext = res.LastIndexOf('.'); + if (ext == -1) { + ext = res.Length; + res += DEFAULT_EXT; + } + int nStart = res.LastIndexOf("/") + 1; + name = res.Substring(nStart, ext - nStart); + return res; + } + + internal void RunProfile(DirectoryServiceContract.Imp! ds, + PipeMultiplexer! outputMux) + { + Progress current = SoFar; + m_root.Nest(INIT, m_root.Node).Report(BEGIN, PROFILE); + foreach (XmlNode p in (!) m_root.Node.Children) { + if (p != null && p.Name == "Profile") { + foreach (XmlNode child in (!) p.Children) { + if (child != null && child.Name == "Module") { + SpawnModule(m_root.Nest(child), ds, outputMux); + if (m_failures > 0 && m_failFast) { + break; + } + } + } + } + } + ReportEnd(m_root, PROFILE, current); + // TODO do we want to count the tests skipped? + //if (skips > 0) { + // Report(SKIPPED, skips + FAIL_SKIPS); + // m_skipped += skips; + // } + // TODO + // Report(SKIPPED, skips + FAIL_SKIPS); + + } + + internal void SpawnModule(TestContext! ctx, + DirectoryServiceContract.Imp! ds, + PipeMultiplexer! outputMux) + { + TestContext setupCtx = ctx.Nest(INIT, ctx.Node); + setupCtx.Report(BEGIN, MODULE); + + // This is a test app, so we expect to run it with the + // "test" action. If that doesn't work, something is wrong + // REVIEW: how is failure signaled? + string name = ctx.Name; + string[] args = new string[2]; + args[0] = name; + args[1] = "@test"; + + Manifest manifest; + Process child = Binder.CreateProcess(ds, args, outputMux, out manifest); + if (null == child) { + ctx.Report(SKIPPED, "Test module or manifest missing: " + ctx.Name); + // TODO count the number of tests skipped + return; + } + + // Make a channel for the test module + // The child gets the pipe that goes into our output MUX + ModuleTesterContract.Exp! testerExp; + ModuleTesterContract.Imp! tester; + ModuleTesterContract.NewChannel(out tester, out testerExp); + // Make a channel for the test module + LogContract.Exp! logExp; + LogContract.Imp! log; + LogContract.NewChannel(out log, out logExp); + + Debug("Setting slot 2 in test SIP", name); + child.SetStartupEndpoint(2, testerExp); + Debug("Starting test SIP", name); + child.Start(); + Debug("Started test SIP", name); + Progress current = SoFar; + string! summary = MODULE; + try { + tester.RecvGetLogger(); + Debug("Sending logger", name); + tester.SendLogger(logExp, m_reportAsserts); + + Debug("Beginning Test", name); + if (WaitResult(setupCtx, tester, log)) { + RunSuites(ctx, tester, log); + CleanupModule(ctx.Nest(CLEANUP, ctx.Node), tester, log); + } + } + catch (Exception ex) { + summary = (!) ex.ToString(); + } + finally { + delete tester; + delete log; + if (! child.Join(CHILD_STOP_WAIT)) { + // TODO count this as a failure m_failures++; + ctx.Report(INFO, STOP_TIMED_OUT); + child.Stop(); + } + Debug("Terminating test sip", name); + ReportEnd(ctx, summary, current); + } + } + + private static void Debug(string msg, string data) + { +#if DEBUG_TEST_SETUP + DebugStub.WriteLine("{0} [{1}]", __arglist(msg, data)); +#endif + } + + internal void CleanupModule(TestContext! ctx, + ModuleTesterContract.Imp:DO_MODULE! tester, + LogContract.Imp! log) + { + tester.SendCleanupModule(); + WaitResult(ctx, tester, log); + } + + internal void RunSuites(TestContext! ctx, + ModuleTesterContract.Imp:DO_MODULE! tester, + LogContract.Imp! log) + { + foreach (XmlNode n in (!) ctx.Node.Children) { + if (n != null && n.Name == "Suite") { + RunSuite(ctx.Nest(n), tester, log); + if (m_failures > 0 && m_failFast) { + break; + } + } + } + // TODO do we want to count the tests skipped? + //if (skips > 0) { + // Report(SKIPPED, skips + FAIL_SKIPS); + // m_skipped += skips; + // } + } + + internal void RunSuite(TestContext! ctx, + ModuleTesterContract.Imp:DO_MODULE! tester, + LogContract.Imp! log) + { + TestContext setupCtx = ctx.Nest(INIT, ctx.Node); + setupCtx.Report(BEGIN, SUITE); + Progress current = SoFar; + bool setup = setupCtx.SkipReason == null; + if (setup) { + tester.SendInitSuite(Bitter.FromString2(ctx.Name)); + if (!WaitResult(setupCtx, tester, log)) { + setup = false; + ctx.StartSkipping(SETUP_FAILED); + } + } + else { + setupCtx.Report(SKIPPED, ctx.SkipReason); + } + // We will at least visit each test, and perhaps just report + // skipping it there + RunTests(ctx, tester, log); + if (setup) { + //only cleanup if we actually setup + CleanupSuite(ctx.Nest(CLEANUP, ctx.Node), tester, log); + } + ReportEnd(ctx, SUITE, current); + } + + internal void CleanupSuite(TestContext! ctx, + ModuleTesterContract.Imp:DO_MODULE! tester, + LogContract.Imp! log) + { + tester.SendCleanupSuite(); + WaitResult(ctx, tester, log); + } + + internal void RunTests(TestContext! suite, + ModuleTesterContract.Imp:DO_MODULE! tester, + LogContract.Imp! log) + { + foreach (XmlNode n in (!) suite.Node.Children) { + if (n != null && n.Name == "Test") { + RunTest(suite.Nest(n), tester, log); + } + } + } + + internal void RunTest(TestContext! ctx, + ModuleTesterContract.Imp:DO_SUITE! tester, + LogContract.Imp! log) + { + if (ctx.Pass != m_testPass) { + // Ignore the test entry + return; + } + if (ctx.SkipReason != null) { + m_skipped++; + // TODO should this support fail recursion as well? + ctx.Report(SKIPPED, ctx.SkipReason); + return; + } + ctx.Nest(INIT, ctx.Node).Report(BEGIN, TEST); + tester.SendRunTest(Bitter.FromString2(ctx.Name)); + WaitResult(ctx, tester, log); + } + + internal bool WaitResult(TestContext! ctx, + ModuleTesterContract.Imp:DO_SUITE! tester, + LogContract.Imp! log) + { + while (true) { + switch receive { + case log.Log(char[]! in ExHeap msg, long atCycle, long atTime): + // TODO decide whether to check the report flag + ctx.Report(INFO, Bitter.ToString2(msg), atCycle, atTime); + delete msg; + log.SendOK(); + break; + + case tester.Passed(long cycles, long duration): + ctx.Report(PASSED, "", cycles, duration); + m_passed++; + return true; + + case tester.Failed(char[]! in ExHeap error, long cycles, long duration): + string err = Bitter.ToString2(error); + delete error; + ctx.Report(FAILED, PrepareFailure(ctx, err), cycles, duration); + FailFast(); + return false; + + case tester.Skipped(char[]! in ExHeap why): + // TODO does this also need to handle known failures? + ctx.Report(SKIPPED, Bitter.ToString2(why)); + delete why; + m_skipped++; + return false; + + case tester.ChannelClosed(): + ctx.Report(FAILED, PrepareFailure(ctx, CHANNEL_CLOSED)); + throw new ChannelClosedException(); + case timeout(ctx.Timeout): + ctx.Report(FAILED, PrepareFailure(ctx, TIMED_OUT)); + throw new Exception(TIMED_OUT); + } + } + } + + private string! PrepareFailure(TestContext! ctx, string! msg) + { + if (ctx.KnownFailure == null) { + m_failures++; + return msg; + } + else { + m_knownFailures++; + return string.Format("KNOWN FAILURE({0}): {1}", ctx.KnownFailure, msg); + } + } + + private string! PrepareSkip(TestContext! ctx, string! msg) + { + if (ctx.KnownFailure == null) { + m_skipped++; + return msg; + } + else { + m_knownFailures++; + return string.Format("KNOWN FAILURE({0}): {1}", ctx.KnownFailure, msg); + } + } + + /// Throw if we are supposed to abort after the first failure + internal void FailFast() { + if (m_failFast && m_failures > 0) { + throw new Exception("Test Failed"); + } + } + + private void ReportEnd(TestContext! ctx, string! tag, Progress before) + { + // TODO deal with skipped + int passed = m_passed - before.passed; + int failed = m_failures - before.failures; + int known = m_knownFailures - before.knownFailures; + int skipped = m_skipped - before.skipped; + string fmt = known > 0 + ? "{0} Passed: {1} Skipped: {2} Failed: {3} +{4} known failure" + : "{0} Passed: {1} Skipped: {2} Failed: {3}"; + string msg = string.Format(fmt, tag, passed, skipped, failed, known); + string status = failed > 0 ? FAILED : (skipped + known > 0) ? PARTIAL : PASSED; + ctx.Report(status, msg); + } + } + + internal class TestContext + { + internal TimeSpan Timeout; + internal int Pass; + internal string! Name; + internal string! Current; + internal XmlNode! Node; + // If non-null, then tests should be skipped for that reason + internal string SkipReason; + internal string KnownFailure; + private bool m_reportTimes; + + public TestContext(string! name, XmlNode! node, TimeSpan timeout, bool reportTimes) + { + Timeout = timeout; + Current = name; + Name = name; + Node = node; + SkipReason = null; + KnownFailure = node.GetAttribute("KnownFailure", null); + m_reportTimes = reportTimes; + } + private TestContext(string! name, XmlNode! node, TestContext! prev) + { + Node = node; + Name = name; + Current = prev.Current + ":" + name; + Timeout = TimeSpan.FromMilliseconds(node.GetAttribute("Timeout", prev.Timeout.TotalMilliseconds)); + Pass = node.GetAttribute("Pass", prev.Pass); + SkipReason = prev.SkipReason; + KnownFailure = node.GetAttribute("KnownFailure", prev.KnownFailure); + m_reportTimes = prev.m_reportTimes; + } + + // QUERIES /////////////////////////////////////////////////// + public static long CycleCount + { + get { +#if AFFINITY_SCHEDULER + return Processor.CycleCount; +#else + // If we're not using the affinity scheduler, substitute + // TickCount for CycleCount. TickCount will be consistent + // across processors. + return (long) Environment.TickCount; +#endif + } + } + + public static long Ticks + { + get { return DateTime.Now.Ticks; } + } + + public TestContext! Nest(string! sub, XmlNode! node) + { + return new TestContext(sub, node, this); + } + + public TestContext! Nest(XmlNode! node) + { + return Nest((!) node.GetAttribute("Name", "unknown"), node); + } + + override public string! ToString() + { + return Current; + } + + public void Report(string tag, string msg, long cycles, long time) + { + DateTime t = new DateTime(time); + // HACK Singularity doesn't support string formats + // string ts = t.ToString("yyyy/MM/ddTHH:mm:ss:fff"); + string ts = String.Format("{0:d04}/{1:d02}/{2:d02}T{3:d02}:{4:d02}:{5:d02}:{6:d03}", + t.Year, t.Month, t.Day, t.Hour, t.Minute, t.Second, t.Millisecond); + string fmt = m_reportTimes ? "TEST>{3}# {4} {0} {1} {2}" + : "{0} {1} {2}"; + // show on console for any fail/skip or for END of a non INIT/CLEANUP + if ((tag != TestDriver.PASSED && tag != TestDriver.BEGIN) || + (tag != TestDriver.BEGIN && Name != TestDriver.INIT && Name != TestDriver.CLEANUP)) + { + Console.WriteLine(fmt, tag, Current.Substring(Current.IndexOf(":")+1), msg, cycles, ts); + } + + DebugStub.WriteLine(""); + DebugStub.WriteLine(fmt, __arglist(tag, Current, msg, cycles, ts)); + } + + public void Report(string tag, string msg) + { + Report(tag, msg, CycleCount, Ticks); + } + + // OPERATIONS //////////////////////////////////////////////// + public void StartSkipping(string! reason) + { + SkipReason = reason; + } + + } +} diff --git a/base/Applications/Tester/Tester.csproj b/base/Applications/Tester/Tester.csproj new file mode 100644 index 0000000..ef89825 --- /dev/null +++ b/base/Applications/Tester/Tester.csproj @@ -0,0 +1,39 @@ + + + + + + + SingUnit + Exe + true + false + true + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/Tests/AdjustTest/AdjustTest.cs b/base/Applications/Tests/AdjustTest/AdjustTest.cs new file mode 100644 index 0000000..b8ca4bc --- /dev/null +++ b/base/Applications/Tests/AdjustTest/AdjustTest.cs @@ -0,0 +1,41 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; + +namespace Microsoft.Singularity.AdjustTest +{ + public struct Holder : IDisposable + { + int handle; + public Holder(int i) + { + handle = i; + } + public void Dispose() + { + Console.WriteLine("Disposed {0}", handle); + } + } + + public class Test + { + public static Holder Hold(int i) + { + return new Holder(i); + } + + //[ShellCommand("adjusttest", "Test code rewriting.")] + public static void Main() + { + Console.WriteLine("starting"); + using (Hold(3)) { + Console.WriteLine("using"); + } + Console.WriteLine("used"); + } + } +} diff --git a/base/Applications/Tests/AdjustTest/AdjustTest.csproj b/base/Applications/Tests/AdjustTest/AdjustTest.csproj new file mode 100644 index 0000000..83afdcb --- /dev/null +++ b/base/Applications/Tests/AdjustTest/AdjustTest.csproj @@ -0,0 +1,36 @@ + + + + + + + + Exe + AdjustTest + + Full + + + + + + + + + + + + + + + diff --git a/base/Applications/Tests/Bartok/Bug00013/Bug00013.cs b/base/Applications/Tests/Bartok/Bug00013/Bug00013.cs index f368011..d4a006f 100644 --- a/base/Applications/Tests/Bartok/Bug00013/Bug00013.cs +++ b/base/Applications/Tests/Bartok/Bug00013/Bug00013.cs @@ -1,5 +1,5 @@ // -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // // Some sort of threads/locking test. @@ -16,7 +16,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace MarmotBugs { +namespace MarmotBugs +{ [ConsoleCategory(HelpMessage="Show attributes associated with a file", DefaultAction=true)] internal class Parameters { @@ -37,7 +38,8 @@ namespace MarmotBugs { } } - class DebugPrintSpin { + class DebugPrintSpin + { private static Object locker = new Object(); public static void Lock() { // Class_java_lang_Thread *currentThread = CurrentThread(); @@ -52,7 +54,8 @@ namespace MarmotBugs { } } - class Bug00013 { //: Runnable { + class Bug00013 + { //: Runnable { static int totalThreads = Int32.MaxValue; @@ -79,7 +82,7 @@ namespace MarmotBugs { Assert(b, "Assertion failed"); } private static void Assert(bool b, String s) { - if(!b) { + if (!b) { DebugPrintSpin.Lock(); Console.WriteLine(s); Console.WriteLine("Failed Bug00013"); @@ -239,13 +242,14 @@ namespace MarmotBugs { Console.WriteLine(newThread.GetHashCode()); DebugPrintSpin.Unlock(); - /* - new WatchDog().start(); - try { - Thread.sleep(1000); - } catch(InterruptedException e) { - } - /**/ + // + //new WatchDog().start(); + //try { + // Thread.sleep(1000); + //} + //catch (InterruptedException e) { + //} + ///* newThread.Start(); @@ -267,7 +271,8 @@ namespace MarmotBugs { + " deadman expired"); DebugDumpThreadAnchorTable(4); DebugBreak(); - } else { + } + else { Console.WriteLine(); Console.Write(" ?<0,"); Console.Write(numThreadsAlive); @@ -291,12 +296,12 @@ namespace MarmotBugs { links = new Bug00013[maxLinks]; } - /* - public override String ToString() { - if (nodename != null) return(nodename); - return(base.ToString()); - } - */ + // + //public override String ToString() { + // if (nodename != null) return(nodename); + // return(base.ToString()); + //} + // public void run() { int myNumThreadsAlive; @@ -329,7 +334,8 @@ namespace MarmotBugs { lock (this) { if (random == null) { random = new Random(); - } else { + } + else { conflict = true; } } @@ -340,10 +346,10 @@ namespace MarmotBugs { Bug00013 node = new Bug00013(); anchor.addLink(this, node); - for (int i=0; i= 0); if (traversalsLeft > traversalLimit) { traversalsLeft = traversalLimit; @@ -449,7 +455,8 @@ namespace MarmotBugs { break; } } - } else { + } + else { pendingQueue = this; } } @@ -542,7 +549,7 @@ namespace MarmotBugs { checkConsistency(); if (last.numLinks < maxLinks) { lastLinked = true; - for (int i=0; i0, "addlink() last.numLinks should be > 0"); last.numLinks--; - for (int i=0; ii; j--) { + for (int j = numUndeadThreadids; j > i; j--) { undeadThreadids[j] = undeadThreadids[j-1]; } undeadThreadids[i] = threadid; @@ -631,7 +638,7 @@ namespace MarmotBugs { static void DebugPrintUndead() { char sep = ':'; - for (int i=0; i> 8) & 0xfff; int v = (n >> 8) & Config.KEY_SPACE_MASK; @@ -106,19 +107,16 @@ namespace Test { RBNode temp = root; RBNode parent = null; - while(temp != sentinelNode) - { // find Parent + while (temp != sentinelNode) { + // find Parent parent = temp; - if ( key == temp.Value) - { + if (key == temp.Value) { return false; } - else if (key > temp.Value) - { + else if (key > temp.Value) { temp = temp.Right; } - else - { + else { temp = temp.Left; } } @@ -131,10 +129,8 @@ namespace Test { node.Right = sentinelNode; // insert node into tree starting at parent's location - if(node.Parent != null) - { - if (node.Value > node.Parent.Value) - { + if (node.Parent != null) { + if (node.Value > node.Parent.Value) { node.Parent.Right = node; } else @@ -157,19 +153,15 @@ namespace Test { bool result = false; // traverse tree until node is found - while(node != sentinelNode) - { - if (key == node.Value) - { + while (node != sentinelNode) { + if (key == node.Value) { result = true; break; } - else if (key < node.Value) - { + else if (key < node.Value) { node = node.Left; } - else - { + else { node = node.Right; } } @@ -186,23 +178,19 @@ namespace Test { RBNode node; node = root; - while(node != sentinelNode) - { - if (key == node.Value) - { + while (node != sentinelNode) { + if (key == node.Value) { break; } - else if (key < node.Value) - { + else if (key < node.Value) { node = node.Left; } - else - { + else { node = node.Right; } } - if(node == sentinelNode) + if (node == sentinelNode) return false; // key not found Delete(node); @@ -228,22 +216,21 @@ namespace Test { // find the replacement node (the successor to x) - the node one with // at *most* one child. - if(z.Left == sentinelNode || z.Right == sentinelNode) + if (z.Left == sentinelNode || z.Right == sentinelNode) y = z; // node has sentinel as a child - else - { + else { // z has two children, find replacement node which will // be the leftmost node greater than z y = z.Right; // traverse right subtree - while(y.Left != sentinelNode) // to find next node in sequence + while (y.Left != sentinelNode) // to find next node in sequence y = y.Left; } - // at this point, y contains the replacement node. Its content will be copied + // at this point, y contains the replacement node. it's content will be copied // to the values in the node to be deleted // x (y's only child) is the node that will be linked to y's old parent. - if(y.Left != sentinelNode) + if (y.Left != sentinelNode) x = y.Left; else x = y.Right; @@ -252,7 +239,7 @@ namespace Test { // link x to proper subtree in parent // this removes y from the chain x.Parent = y.Parent; - if(y.Parent != null) + if (y.Parent != null) if(y == y.Parent.Left) y.Parent.Left = x; else @@ -262,12 +249,11 @@ namespace Test { // copy the values from y (the replacement node) to the node being deleted. // note: this effectively deletes the node. - if(y != z) - { + if (y != z) { z.Value = y.Value; } - if(y.Color == Color.BLACK) + if (y.Color == Color.BLACK) RestoreAfterDelete(x); } @@ -283,13 +269,12 @@ namespace Test { RBNode y; - while(x != root && x.Color == Color.BLACK) - { - if(x == x.Parent.Left) // determine sub tree from parent + while (x != root && x.Color == Color.BLACK) { + if (x == x.Parent.Left) // determine sub tree from parent { y = x.Parent.Right; // y is x's sibling - if(y.Color == Color.RED) - { // x is black, y is red - make both black and rotate + if (y.Color == Color.RED) { + // x is black, y is red - make both black and rotate y.Color = Color.BLACK; x.Parent.Color = Color.RED; RotateLeft(x.Parent); @@ -301,10 +286,8 @@ namespace Test { y.Color = Color.RED; // change parent to red x = x.Parent; // move up the tree } - else - { - if(y.Right.Color == Color.BLACK) - { + else { + if (y.Right.Color == Color.BLACK) { y.Left.Color = Color.BLACK; y.Color = Color.RED; RotateRight(y); @@ -317,11 +300,10 @@ namespace Test { x = root; } } - else - { // right subtree - same as code above with right and left swapped + else { + // right subtree - same as code above with right and left swapped y = x.Parent.Left; - if(y.Color == Color.RED) - { + if (y.Color == Color.RED) { y.Color = Color.BLACK; x.Parent.Color = Color.RED; RotateRight (x.Parent); @@ -333,10 +315,8 @@ namespace Test { y.Color = Color.RED; x = x.Parent; } - else - { - if(y.Left.Color == Color.BLACK) - { + else { + if (y.Left.Color == Color.BLACK) { y.Right.Color = Color.BLACK; y.Color = Color.RED; RotateLeft(y); @@ -358,14 +338,13 @@ namespace Test { RBNode y; // maintain red-black tree properties after adding x - while(x != root && x.Parent.Color == Color.RED) - { + while (x != root && x.Parent.Color == Color.RED) { // Parent node is .Colored red; - if(x.Parent == x.Parent.Parent.Left) // determine traversal path + if (x.Parent == x.Parent.Parent.Left) // determine traversal path { // is it on the Left or Right subtree? y = x.Parent.Parent.Right; // get uncle - if(y!= null && y.Color == Color.RED) - { // uncle is red; change x's Parent and uncle to black + if (y != null && y.Color == Color.RED) { + // uncle is red; change x's Parent and uncle to black x.Parent.Color = Color.BLACK; y.Color = Color.BLACK; // grandparent must be red. Why? Every red node that is not @@ -373,11 +352,10 @@ namespace Test { x.Parent.Parent.Color = Color.RED; x = x.Parent.Parent; // continue loop with grandparent } - else - { + else { // uncle is black; determine if x is greater than Parent - if(x == x.Parent.Right) - { // yes, x is greater than Parent; rotate Left + if (x == x.Parent.Right) { + // yes, x is greater than Parent; rotate Left // make x a Left child x = x.Parent; RotateLeft(x); @@ -388,21 +366,18 @@ namespace Test { RotateRight(x.Parent.Parent); // rotate right } } - else - { // x's Parent is on the Right subtree + else { + // x's Parent is on the Right subtree // this code is the same as above with "Left" and "Right" swapped y = x.Parent.Parent.Left; - if(y!= null && y.Color == Color.RED) - { + if (y != null && y.Color == Color.RED) { x.Parent.Color = Color.BLACK; y.Color = Color.BLACK; x.Parent.Parent.Color = Color.RED; x = x.Parent.Parent; } - else - { - if(x == x.Parent.Left) - { + else { + if (x == x.Parent.Left) { x = x.Parent; RotateRight(x); } @@ -431,25 +406,25 @@ namespace Test { x.Right = y.Left; // y's Left child's becomes x's Right child // modify parents - if(y.Left != sentinelNode) + if (y.Left != sentinelNode) y.Left.Parent = x; // sets y's Left Parent to x - if(y != sentinelNode) + if (y != sentinelNode) y.Parent = x.Parent; // set y's Parent to x's Parent - if(x.Parent != null) - { // determine which side of its Parent x was on - if(x == x.Parent.Left) + if (x.Parent != null) { + // determine which side of it's Parent x was on + if (x == x.Parent.Left) x.Parent.Left = y; // set Left Parent to y else x.Parent.Right = y; // set Right Parent to y } - else + else root = y; // at root, set it to y // link x and y y.Left = x; // put x on y's Left - if(x != sentinelNode) // set y as x's Parent + if (x != sentinelNode) // set y as x's Parent x.Parent = y; } @@ -469,25 +444,25 @@ namespace Test { x.Left = y.Right; // y's Right child becomes x's Left child // modify parents - if(y.Right != sentinelNode) + if (y.Right != sentinelNode) y.Right.Parent = x; // sets y's Right Parent to x - if(y != sentinelNode) + if (y != sentinelNode) y.Parent = x.Parent; // set y's Parent to x's Parent - if(x.Parent != null) // null=root, could also have used root + if (x.Parent != null) // null = root, could also have used root { // determine which side of its Parent x was on - if(x == x.Parent.Right) + if (x == x.Parent.Right) x.Parent.Right = y; // set Right Parent to y else x.Parent.Left = y; // set Left Parent to y } - else + else root = y; // at root, set it to y // link x and y y.Right = x; // put x on y's Right - if(x != sentinelNode) // set y as x's Parent + if (x != sentinelNode) // set y as x's Parent x.Parent = y; } @@ -522,20 +497,16 @@ namespace Test { // Check left side RBNode left = root.Left; - if (sentinelNode != left) - { - if (left.Color == Color.RED && rootcolor == Color.RED) - { + if (sentinelNode != left) { + if (left.Color == Color.RED && rootcolor == Color.RED) { Console.WriteLine("Two consecutive red nodes!"); return; } - if (left.Value >= root.Value) - { + if (left.Value >= root.Value) { Console.WriteLine("Tree values out of order!"); return; } - if (left.Marked) - { + if (left.Marked) { Console.WriteLine("Cycle in tree structure!"); return; } @@ -544,20 +515,16 @@ namespace Test { // Check right side RBNode right = root.Right; - if (sentinelNode != right) - { - if (right.Color == Color.RED && rootcolor == Color.RED) - { + if (sentinelNode != right) { + if (right.Color == Color.RED && rootcolor == Color.RED) { Console.WriteLine("Two consecutive red nodes!"); return; } - if (right.Value <= root.Value) - { + if (right.Value <= root.Value) { Console.WriteLine("Tree values out of order!"); return; } - if (right.Marked) - { + if (right.Marked) { Console.WriteLine("Cycle in tree structure!"); return; } @@ -565,10 +532,8 @@ namespace Test { } // Check black node count - if (sentinelNode == root.Left || sentinelNode == root.Right) - { - if (soFar != blackNodes) - { + if (sentinelNode == root.Left || sentinelNode == root.Right) { + if (soFar != blackNodes) { Console.WriteLine("Variable number of black nodes to leaves!"); return; } @@ -580,7 +545,7 @@ namespace Test { public class RBNode { - /** creates new tree node **/ + /// creates new tree node * internal int Value = 0; internal Color Color = Color.RED; internal bool Marked; @@ -590,7 +555,8 @@ namespace Test { } } - class STMTest { + class STMTest + { static Object o = new Object(); @@ -602,7 +568,7 @@ namespace Test { int i; try { - for (i = 0 ; ((Config.OP_COUNT == -1) || (i < Config.OP_COUNT)); i ++) { + for (i = 0; ((Config.OP_COUNT == -1) || (i < Config.OP_COUNT)); i ++) { int n = random.Next(); int v = (n >> 8) & Config.KEY_SPACE_MASK; @@ -611,43 +577,70 @@ namespace Test { if (Config.KIND == RunKind.Atomic) { try { r = t.Contains(v); - } catch (AtomicFakeException) { } - } else if (Config.KIND == RunKind.TryAll) { + } + catch (AtomicFakeException) { + + } + } + else if (Config.KIND == RunKind.TryAll) { try { r = t.Contains(v); - } catch (TryAllFakeException) { } - } else { + } + catch (TryAllFakeException) { + + } + } + else { r = t.Contains(v); } - } else if ((n & 0xff) < (Config.LOOKUP_FRAC + Config.REMOVE_FRAC)) { + } + else if ((n & 0xff) < (Config.LOOKUP_FRAC + Config.REMOVE_FRAC)) { if (Config.KIND == RunKind.Atomic) { try { r = t.Remove(v); - } catch (AtomicFakeException) { } - } else if (Config.KIND == RunKind.TryAll) { + } + catch (AtomicFakeException) { + + } + } + else if (Config.KIND == RunKind.TryAll) { try { r = t.Remove(v); - } catch (TryAllFakeException) { } - } else { + } + catch (TryAllFakeException) { + + } + } + else { r = t.Remove(v); } if (r) my_shadow -= v; - } else { + } + else { if (Config.KIND == RunKind.Atomic) { try { r = t.Insert(v); - } catch (AtomicFakeException) { } - } else if (Config.KIND == RunKind.TryAll) { + } + catch (AtomicFakeException) { + + } + } + else if (Config.KIND == RunKind.TryAll) { try { r = t.Insert(v); - } catch (TryAllFakeException) { } - } else { + } + catch (TryAllFakeException) { + + } + } + else { r = t.Insert(v); } if (r) my_shadow += v; } } - } catch (Exception e) { + } + catch (Exception e) { lock (o) { Console.WriteLine("Failed with " + e); } @@ -692,14 +685,16 @@ namespace Test { private static int NextArgInt(string[] args, ref int nextArg, int min) { try { int x = Int32.Parse(NextArg(args, ref nextArg)); - if(x < min) { + if (x < min) { Usage("parameter " + (nextArg-1) + " should be >= " + min); } return x; - } catch(FormatException) { + } + catch (FormatException) { Usage("illegal format in parameter " + (nextArg-1)); return 0; - } catch(OverflowException) { + } + catch (OverflowException) { Usage("overflow in parameter " + (nextArg-1)); return 0; } @@ -712,7 +707,7 @@ namespace Test { } int argIndex = 0; - if(argIndex < args.Length) { + if (argIndex < args.Length) { if ((String.Compare(args[0], "tree", true) == 0) || (String.Compare(args[0], "tree.exe", true) == 0) || (String.Compare(args[0], "tree.x86", true) == 0)) { @@ -800,7 +795,8 @@ namespace Test { if (Config.DETERMINISTIC) { Console.WriteLine("Shadow - Actual = " + (s - a)); - } else { + } + else { Console.WriteLine("Shadow = " + s); Console.WriteLine("Actual = " + a); } diff --git a/base/Applications/Tests/CLink/CLink.sg b/base/Applications/Tests/CLink/CLink.sg index d99162f..bf73d0a 100644 --- a/base/Applications/Tests/CLink/CLink.sg +++ b/base/Applications/Tests/CLink/CLink.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: CLink.sg -// // Note: Simple ping-pong second child process // @@ -53,7 +51,7 @@ namespace Microsoft.Singularity.Applications cin.SendPongReady(); try { - while(true) { + while (true) { switch receive { case cin.Ping(int data): Console.WriteLine("CLink{0,2}: Ping({1}) to Ping...", id, data); diff --git a/base/Applications/Tests/CPing/CPing.sg b/base/Applications/Tests/CPing/CPing.sg index 5d29262..64d957c 100644 --- a/base/Applications/Tests/CPing/CPing.sg +++ b/base/Applications/Tests/CPing/CPing.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: CPing.sg -// // Note: Simple ping-pong test child app #1 // @@ -46,7 +44,7 @@ namespace Microsoft.Singularity.Applications Console.WriteLine("CPing : Starting cpong."); string[] cargs = new string[1]; - cargs[0] = "CPong.x86"; + cargs[0] = "CPong"; Process child = new Process(cargs, null, 1); child.SetStartupEndpoint(0, (Endpoint * in ExHeap)pongExp); child.Start(); @@ -60,7 +58,7 @@ namespace Microsoft.Singularity.Applications Console.WriteLine("CPing : Starting clink[{0}].", i); cargs = new string[2]; - cargs[0] = "CLink.x86"; + cargs[0] = "CLink"; cargs[1] = i.ToString(); child = new Process(cargs, null, 2); diff --git a/base/Applications/Tests/CPong/CPong.sg b/base/Applications/Tests/CPong/CPong.sg index 40e1fe3..9565dfb 100644 --- a/base/Applications/Tests/CPong/CPong.sg +++ b/base/Applications/Tests/CPong/CPong.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: CPong.sg -// // Note: Simple ping-pong second child process // @@ -36,7 +34,7 @@ namespace Microsoft.Singularity.Applications conn.SendPongReady(); try { - while(true) { + while (true) { switch receive { case conn.Ping(int data): Console.WriteLine("CPong : Ping({0}) to Pong...", data); diff --git a/base/Applications/Tests/Cast/Cast.cs b/base/Applications/Tests/Cast/Cast.cs index 6fb0ab5..e33245a 100644 --- a/base/Applications/Tests/Cast/Cast.cs +++ b/base/Applications/Tests/Cast/Cast.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Cast.cs -// // Note: Simple Singularity test program. // using System; @@ -18,7 +16,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] diff --git a/base/Applications/Tests/ChannelDemo/ChannelDemo.csproj b/base/Applications/Tests/ChannelDemo/ChannelDemo.csproj index 480c35d..995bd4f 100644 --- a/base/Applications/Tests/ChannelDemo/ChannelDemo.csproj +++ b/base/Applications/Tests/ChannelDemo/ChannelDemo.csproj @@ -5,8 +5,6 @@ Microsoft Research Singularity Copyright (c) Microsoft Corporation. All rights reserved. -File: Applications\Tests\ChannelDemo\ChannelDemo.csproj - Note: Tests channel I/O ############################################################################## diff --git a/base/Applications/Tests/ChannelDemo/ChannelDemo.sg b/base/Applications/Tests/ChannelDemo/ChannelDemo.sg index 4ec717b..3095871 100644 --- a/base/Applications/Tests/ChannelDemo/ChannelDemo.sg +++ b/base/Applications/Tests/ChannelDemo/ChannelDemo.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: ChannelDemo.cs -// // Note: Simple Singularity test program. // using System; @@ -20,7 +18,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] diff --git a/base/Applications/Tests/ChannelPerf/ChannelPerf.sg b/base/Applications/Tests/ChannelPerf/ChannelPerf.sg index 3a22208..01454f1 100644 --- a/base/Applications/Tests/ChannelPerf/ChannelPerf.sg +++ b/base/Applications/Tests/ChannelPerf/ChannelPerf.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: ChannelPerf.cs -// // Note: Simple Singularity test program. // using System; @@ -19,7 +17,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] @@ -93,11 +92,9 @@ namespace Microsoft.Singularity.Applications { for (int i = 0; i < num; i++) { arg = i; e2.SendReq(arg); - switch receive - { + switch receive { case e2.Resp(reply): - if (doAlloc) - { + if (doAlloc) { foo = new byte[20000]; foo[1] = 0x3; // keep the compiler happy } diff --git a/base/Applications/Tests/ClockTest/ClockTest.cs b/base/Applications/Tests/ClockTest/ClockTest.cs index ecb655c..faaf1b4 100644 --- a/base/Applications/Tests/ClockTest/ClockTest.cs +++ b/base/Applications/Tests/ClockTest/ClockTest.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: ClockTest.cs -// // Note: Simple Singularity test program. // using System; @@ -19,7 +17,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] diff --git a/base/Applications/Tests/Collect/Collect.cs b/base/Applications/Tests/Collect/Collect.cs index 1a7b1ad..fd882b6 100644 --- a/base/Applications/Tests/Collect/Collect.cs +++ b/base/Applications/Tests/Collect/Collect.cs @@ -4,22 +4,20 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Collect.cs -// // Note: Simple Singularity test program. // using System; using Microsoft.Singularity.V1.Services; using Microsoft.Contracts; using Microsoft.Singularity.Channels; -using Microsoft.Contracts; using Microsoft.SingSharp.Reflection; using Microsoft.Singularity.Applications; using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] @@ -52,12 +50,10 @@ namespace Microsoft.Singularity.Applications { prev = _prev; value = i; - if (i <= 1) - { + if (i <= 1) { next = null; } - else - { + else { next = new LinkedList(i - 1, this); } data = new byte [24576 + 1024 * i]; @@ -84,8 +80,7 @@ namespace Microsoft.Singularity.Applications { { Console.WriteLine(" Value = {0} [Next] {1}, {2} bytes payload", value, next.value, data.Length); - if (next.value != _value) - { + if (next.value != _value) { next.PrintNext(_value); } } @@ -100,16 +95,14 @@ namespace Microsoft.Singularity.Applications { requires prev != null; { Console.WriteLine(" Value = {0} [Prev] {1}", value, prev.value); - if (prev.value != _value) - { + if (prev.value != _value) { prev.PrintPrev(_value); } } private LinkedList! Last() { - if (next != null) - { + if (next != null) { return next.Last(); } return this; diff --git a/base/Applications/Tests/ConsoleDemo/ConsoleDemo.sg b/base/Applications/Tests/ConsoleDemo/ConsoleDemo.sg index 6dae956..590b04c 100644 --- a/base/Applications/Tests/ConsoleDemo/ConsoleDemo.sg +++ b/base/Applications/Tests/ConsoleDemo/ConsoleDemo.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: ChannelDemo.cs -// // Note: Simple Singularity test program. // @@ -21,11 +19,11 @@ using Microsoft.Singularity.Channels; using Microsoft.Contracts; using Microsoft.SingSharp.Reflection; using Microsoft.Singularity.Applications; -using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications.Tests { +namespace Microsoft.Singularity.Applications.Tests +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] @@ -52,8 +50,7 @@ namespace Microsoft.Singularity.Applications.Tests { ConsoleDeviceContract.NewChannel(out imp, out exp); nsImp.SendBind(deviceName, exp); - switch receive - { + switch receive { case nsImp.AckBind(): return imp; break; @@ -85,8 +82,7 @@ namespace Microsoft.Singularity.Applications.Tests { delete ns; if (imp != null) { - switch receive - { + switch receive { case imp.Success(): break; case imp.ContractNotSupported(): @@ -166,7 +162,7 @@ namespace Microsoft.Singularity.Applications.Tests { int cc = columns / 2; int cr = rows / 2; int edge = Math.Min(cc, cr); - for (int i = 0 ; i < edge; i++) { + for (int i = 0; i < edge; i++) { DrawLine(imp, cc - i, cr - i, cc - i, cr + i, '*'); DrawLine(imp, cc - i, cr + i, cc + i, cr + i, '*'); DrawLine(imp, cc + i, cr + i, cc + i, cr - i, '*'); @@ -187,7 +183,7 @@ namespace Microsoft.Singularity.Applications.Tests { int cr = rows / 2; int r = 2 * Math.Min(cc, cr) / 3; double tau = 2 * Math.PI / twoPiSteps; - for (int i = 0 ; i < 5 * twoPiSteps; i++) { + for (int i = 0; i < 5 * twoPiSteps; i++) { double theta = i * tau; int x = (int)(r * Math.Sin(theta)); int y = (int)(r * Math.Cos(theta)); diff --git a/base/Applications/Tests/DumpPages/DumpPages.cs b/base/Applications/Tests/DumpPages/DumpPages.cs deleted file mode 100644 index b38a6d4..0000000 --- a/base/Applications/Tests/DumpPages/DumpPages.cs +++ /dev/null @@ -1,24 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: DumpPages.cs -// -// Note: Simple Singularity test program. -// -using System; - -namespace Microsoft.Singularity.Applications -{ - public class DumpPages - { - //[ShellCommand("dump", "Dump page table")] - public static int Main(String[] args) - { - AppRuntime.DumpPageTable(); - return 0; - } - } -} diff --git a/base/Applications/Tests/DumpPages/DumpPages.csproj b/base/Applications/Tests/DumpPages/DumpPages.csproj deleted file mode 100644 index 3b29a89..0000000 --- a/base/Applications/Tests/DumpPages/DumpPages.csproj +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - DumpPages - Exe - true - true - - - - - - - - - - - - - diff --git a/base/Applications/Tests/GcStress/GcStress.sg b/base/Applications/Tests/GcStress/GcStress.sg index afedb9d..188e9ba 100644 --- a/base/Applications/Tests/GcStress/GcStress.sg +++ b/base/Applications/Tests/GcStress/GcStress.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: GcStress.sg -// // Note: Simple Singularity test program. // using System; @@ -23,7 +21,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] @@ -54,15 +53,13 @@ namespace Microsoft.Singularity.Applications { { const int ARGS_START = 1; - if (!config.doKernelTest) - { + if (!config.doKernelTest) { new GcStress().Run(); } else { DirectoryServiceContract.Imp! epNS = DirectoryService.NewClientEndpoint(); - try - { + try { StressContract.Imp! imp; StressContract.Exp! exp; StressContract.NewChannel(out imp, out exp); @@ -73,15 +70,13 @@ namespace Microsoft.Singularity.Applications { Console.Write("failure on lookup of name " + StressContract.ModuleName + "reason: " + SdsUtils.ErrorCodeToString(errorOut)); delete imp; - if (errorOut == ErrorCode.ChannelClosed) - { + if (errorOut == ErrorCode.ChannelClosed) { throw new Exception("Encountered a ChannelClosed"); } return 1; } else { - switch receive - { + switch receive { case imp.Ready() : break; case imp.ContractNotSupported() : @@ -94,10 +89,8 @@ namespace Microsoft.Singularity.Applications { return 1; } imp.SendGcStress(); - while (true) - { - switch receive - { + while (true) { + switch receive { case imp.Print(char[] in ExHeap s) : Console.Write(Bitter.ToString(s)); imp.SendAckPrint(); @@ -114,60 +107,55 @@ namespace Microsoft.Singularity.Applications { } } } -/* -// epNS.SendBind(Bitter.FromString2(StressContract.ModuleName), exp); - - switch receive - { - case epNS.NakBind(rejected, error) : - // failure - Console.Write("failure on lookup of name " + StressContract.ModuleName); - delete imp; - delete rejected; - return 1; - - case epNS.AckBind() : - // success - switch receive - { - case imp.Ready() : - break; - case imp.ContractNotSupported() : - Console.Write("failure: contract not supported"); - delete imp; - return 1; - case imp.ChannelClosed() : - Console.Write("failure: channel closed prematurely"); - delete imp; - return 1; - } - imp.SendGcStress(); - while (true) - { - switch receive - { - case imp.Print(char[] in ExHeap s) : - Console.Write(Bitter.ToString(s)); - imp.SendAckPrint(); - delete s; - break; - - case imp.GcStressDone() : - delete imp; - return 0; - - case imp.ChannelClosed() : - delete imp; - return 0; - } - } - case epNS.ChannelClosed() : - throw new Exception("epNS channel closed"); - } -*/ +// +//// epNS.SendBind(Bitter.FromString2(StressContract.ModuleName), exp); +// +// switch receive { +// case epNS.NakBind(rejected, error) : +// // failure +// Console.Write("failure on lookup of name " + StressContract.ModuleName); +// delete imp; +// delete rejected; +// return 1; +// +// case epNS.AckBind() : +// // success +// switch receive { +// case imp.Ready() : +// break; +// case imp.ContractNotSupported() : +// Console.Write("failure: contract not supported"); +// delete imp; +// return 1; +// case imp.ChannelClosed() : +// Console.Write("failure: channel closed prematurely"); +// delete imp; +// return 1; +// } +// imp.SendGcStress(); +// while (true) { +// switch receive { +// case imp.Print(char[] in ExHeap s) : +// Console.Write(Bitter.ToString(s)); +// imp.SendAckPrint(); +// delete s; +// break; +// +// case imp.GcStressDone() : +// delete imp; +// return 0; +// +// case imp.ChannelClosed() : +// delete imp; +// return 0; +// } +// } +// case epNS.ChannelClosed() : +// throw new Exception("epNS channel closed"); +// } +// } - finally - { + finally { delete epNS; } } diff --git a/base/Applications/Tests/Hog/Hog.cs b/base/Applications/Tests/Hog/Hog.cs index d7820cf..3297a39 100644 --- a/base/Applications/Tests/Hog/Hog.cs +++ b/base/Applications/Tests/Hog/Hog.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Hog.cs -// // Note: Simple Singularity test program. // using System; @@ -19,7 +17,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] diff --git a/base/Applications/Tests/IoTestApp/IoTestApp.csproj b/base/Applications/Tests/IoTestApp/IoTestApp.csproj new file mode 100644 index 0000000..25ba2d3 --- /dev/null +++ b/base/Applications/Tests/IoTestApp/IoTestApp.csproj @@ -0,0 +1,24 @@ + + + + + + + Exe + IoTestApp + + + + + + + + + + diff --git a/base/Applications/Tests/IoTestApp/IoTestApp.sg b/base/Applications/Tests/IoTestApp/IoTestApp.sg new file mode 100644 index 0000000..831a7d9 --- /dev/null +++ b/base/Applications/Tests/IoTestApp/IoTestApp.sg @@ -0,0 +1,246 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// +// This is the top level unit/iotest driver that sends and receives I/O +// on the IoTest device drivers contract. +// +using System; +using System.Runtime.CompilerServices; +using System.Threading; + +using Microsoft.Singularity; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.V1.Services; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Channels; + +using Microsoft.Contracts; +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Configuration; +[assembly: Transform(typeof(ApplicationResourceTransform))] + +namespace Microsoft.Singularity.Applications +{ + [ConsoleCategory(HelpMessage="IoTest Application for IoTest driver", DefaultAction=true)] + internal class Parameters + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [Endpoint] + public readonly TRef nsRef; + + //[StringParameter( "device", Mandatory=true, Position=0 , HelpMessage="Device name for test (usually /dev/iotest0)")] + [StringParameter( "device", Default="/dev/iotest0", Position=0 , HelpMessage="Device name for test (usually /dev/iotest0)")] + internal string deviceName; + + [LongParameter( "size", Default=4096, HelpMessage="Size for each I/O Transaction")] + internal long chunksize; + + [LongParameter( "count", Default=50000, HelpMessage="Repetition count.")] + internal long count; + + reflective internal Parameters(); + + internal int AppMain() { + return IoTest.AppMain(this); + } + } + + public class IoTest + { + public static IoTestContract.Imp:Ready OpenDevice(String! devname) + { + IoTestContract.Exp! exp; + IoTestContract.Imp! imp; + IoTestContract.NewChannel(out imp, out exp); + DirectoryServiceContract.Imp ns; + + // get NS endpoint + ns = DirectoryService.NewClientEndpoint(); + bool success = false; + ErrorCode error; + success = SdsUtils.Bind(devname, ns, exp, out error); + if (!success) { + Console.WriteLine("Bind of {0} failed\n", devname); + delete imp; + delete ns; + return null; + } + switch receive { + case imp.Success(): + break; + case imp.ContractNotSupported(): + Console.WriteLine("{0} does not support IoTestDevice", devname); + delete imp; + delete ns; + return null; + case imp.ChannelClosed(): + Console.WriteLine("IoTestDevice channel to {0} closed unexpectedly", devname); + delete imp; + delete ns; + return null; + } + + delete ns; + return imp; + } + + private static void DisplayPerf(long ticksDelta, long ticksPerSecond, long iters, long chunkSize) + { + long numBytes = iters * chunkSize; + long opsPerSec = ticksDelta == 0 ? long.MaxValue : iters * ticksPerSecond / ticksDelta; + long bytesPerSec = ticksDelta == 0 ? long.MaxValue : numBytes * ticksPerSecond / ticksDelta; + long mbPerSec = bytesPerSec / (1024*1024); + long elapsedSeconds = ticksDelta / ticksPerSecond; + + Console.WriteLine("Ops/s: {0} MB/s: {1} Bytes/Sec {2} Elapsed: {3}", + opsPerSec, mbPerSec, bytesPerSec, elapsedSeconds); + } + + internal static int AppMain(Parameters! config) + { + long chunkSize = config.chunksize; + long iters = config.count; + + IoTestContract.Imp imp; + string! devName = (!)config.deviceName; + + imp = OpenDevice(devName); + if (null == imp) { + return 1; + } + + DateTime startDateTime = ProcessService.GetUtcTime(); + long startIrqCount = ProcessService.GetKernelInterruptCount(); + long startSwitchCount = ProcessService.GetContextSwitchCount(); + long startKernelGcCount = ProcessService.GetKernelGcCount(); + int startGcCount; + long startGcMillis; + long startGcBytes; + GC.PerformanceCounters(out startGcCount, + out startGcMillis, + out startGcBytes); + + // Allocate an exchange heap buffer for write + byte[] in ExHeap writeBuffer = new [ExHeap] byte [chunkSize]; + + // Set a known pattern for comparison on readback + for (int i = 0; i < writeBuffer.Length; i++) { + writeBuffer[i] = (byte)i; + } + + // Allocate an exchange heap buffer for read + byte[] in ExHeap readBuffer = new [ExHeap] byte [chunkSize]; + + // Clear the read buffer + for (int i = 0; i < readBuffer.Length; i++) { + readBuffer[i] = 0; + } + + for (long count = 0; count < iters; count++) { + ulong lengthWritten; + ulong lengthRead; + + // Declare a reference to an output buffer from the exchange heap + byte[]! in ExHeap outWriteBuffer; + byte[]! in ExHeap outReadBuffer; + + // First do a write, we lose ownership of buffer + imp.SendWrite(writeBuffer, 0, (ulong)chunkSize); + + // Receive the write ack, we receive ownership of outBuffer + imp.RecvAckWrite(out outWriteBuffer, out lengthWritten); // Fix writeOut, not needed??? + + // What happens if the real response is RecvNakWrite()? + // (an exception occurs) + + if (lengthWritten != (ulong)chunkSize) { + Console.WriteLine("Error: lengthWritten {0} does not match request size {1}", + lengthWritten, chunkSize); + } + + // Compare the pattern + for (int i = 0; i < outWriteBuffer.Length; i++) { + if (outWriteBuffer[i] != (byte)i) { + Console.WriteLine("Error: returned buffer pattern mismatch on Write! index={0}, value={1}, sb={2}", + i, outWriteBuffer[i], (byte)i); + } + } + + writeBuffer = outWriteBuffer; + + // Clear the read buffer + for (int i = 0; i < readBuffer.Length; i++) { + readBuffer[i] = 0; + } + + // Send the Read + imp.SendRead(readBuffer, 0, (ulong)chunkSize); + + // Receive the Ack for the read + imp.RecvAckRead(out outReadBuffer, out lengthRead); + + // What happens if the real response is RecvNakRead()? + // (an exception occurs) + + if (lengthRead != (ulong)chunkSize) { + Console.WriteLine("Error: lengthRead {0} does not match request size {1}", + lengthRead, chunkSize); + } + + // Compare the pattern + for (int i = 0; i < outReadBuffer.Length; i++) { + if (outReadBuffer[i] != (byte)i) { + Console.WriteLine("Error: returned buffer pattern mismatch on Read! index={0}, value={1}, sb={2}", + i, outReadBuffer[i], (byte)i); + } + } + + readBuffer = outReadBuffer; + } + + int endGcCount; + long endGcMillis; + long endGcBytes; + long endCycleCount; + + DateTime endDateTime = ProcessService.GetUtcTime(); + + GC.PerformanceCounters(out endGcCount, + out endGcMillis, + out endGcBytes); + + long ticksDelta = endDateTime.Ticks - startDateTime.Ticks; + + double elapsedSeconds; + + DisplayPerf(ticksDelta, DateTime.TicksPerSecond, iters, chunkSize); + + Console.WriteLine("[AppGC :- cnt {0} bytes {1} Kern: ints {2} swi {3} gcs {4}]", + endGcCount - startGcCount, + endGcBytes - startGcBytes, + ProcessService.GetKernelInterruptCount() - startIrqCount, + ProcessService.GetContextSwitchCount() - startSwitchCount, + ProcessService.GetKernelGcCount() - startKernelGcCount); + + // Rundown exchange heap buffers + delete readBuffer; + delete writeBuffer; + + // Rundown endpoints + delete imp; + + return 0; + } + } +} diff --git a/base/Applications/Tests/KPTest/KPTest.csproj b/base/Applications/Tests/KPTest/KPTest.csproj deleted file mode 100644 index 679c07a..0000000 --- a/base/Applications/Tests/KPTest/KPTest.csproj +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - KPTest - Exe - true - true - true - - - - - - - - - - diff --git a/base/Applications/Tests/KPTest/KPTest.sg b/base/Applications/Tests/KPTest/KPTest.sg deleted file mode 100644 index 0a61118..0000000 --- a/base/Applications/Tests/KPTest/KPTest.sg +++ /dev/null @@ -1,223 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: KPTest.sg -// -// Note: Tests the kernel-process boundary. -// -// Feel free to add more commands to this file. - -using System; -using System.Collections; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Memory; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.V1.Services; -using Microsoft.Singularity.Stress.Contracts; - -using Microsoft.Singularity.Channels; -using Microsoft.Contracts; -using Microsoft.SingSharp.Reflection; -using Microsoft.Singularity.Applications; -using Microsoft.Singularity.Io; -using Microsoft.Singularity.Configuration; -[assembly: Transform(typeof(ApplicationResourceTransform))] - -namespace Microsoft.Singularity.Applications { - [ConsoleCategory(DefaultAction=true)] - internal class Parameters { - [InputEndpoint("data")] - public readonly TRef Stdin; - - [OutputEndpoint("data")] - public readonly TRef Stdout; - - [BoolParameter( "help", Default=false, HelpMessage="Display Extended help message.")] - internal bool doHelp; - - [StringArrayParameter( "args", HelpMessage="arg bucket")] - internal string[] args; - - reflective internal Parameters(); - - internal int AppMain() { - return KPTest.AppMain(this); - } - } - - public class KPTest : Microsoft.Singularity.Stress.KPTestBase - { - static Hashtable! processes = new Hashtable(); // maps strings to Processes - - internal static int AppMain(Parameters! config) - { - assume config.args != null; - string[] args; - if(config.doHelp) { - Console.WriteLine("usage:"); - Console.WriteLine(" kptest command1 ... commandn"); - Console.WriteLine("where each command is one of:"); - Console.WriteLine(" print s {print s; ...remaining commands...;}"); - Console.WriteLine(" print- s {...remaining commands...; print s}"); - Console.WriteLine(" print^ s {print s; ...remaining commands...; print s}"); - Console.WriteLine(" catch s {try {...remaining commands...} catch(Exception) {print s}}"); - Console.WriteLine(" finally s {try {...remaining commands...} finally {print s}}"); - Console.WriteLine(" break {break; ...remaining commands...; break}"); - Console.WriteLine(" kernel {enter kernel; ...remaining commands...; leave kernel"); - Console.WriteLine(" frames n {make n stack frames; ...remaining commands...; free frames}"); - Console.WriteLine(" throw {throw ArgumentException}"); - Console.WriteLine(" throwstop {throw ProcessStopException} (kernel only)"); - Console.WriteLine(" delay n {for i=1 to n {compute} ...remaining commands...}"); - Console.WriteLine(" sleep n {sleep n milliseconds ...remaining commands...}"); - Console.WriteLine(" yield {yield; ...remaining commands...}"); - Console.WriteLine(" wall {block forever}"); - Console.WriteLine(" (* n ... ) {repeat n {...}; ...remaining commands...}}"); - Console.WriteLine(" (| ... ) {new thread(...); ...remaining commands...}} (process only)"); - Console.WriteLine(" (& s ... ) {new proc s(...); ...remaining commands...}} (process only)"); - Console.WriteLine(" suspend s {suspend proc s; ...remaining commands...} (process only)"); - Console.WriteLine(" resume s {resume proc s; ...remaining commands...} (process only)"); - Console.WriteLine(" stop s {stop proc s; ...remaining commands...} (process only)"); - Console.WriteLine("where s is any string and n is any nonnegative integer"); - Console.WriteLine("example: kptest print hello catch foo frames 1000 kernel finally bar throw"); - Console.WriteLine("example: kptest print 1 (& p print hi delay 100 print bye ) delay 50 stop p"); - Console.WriteLine("example: kptest 'print 1 (& p print hi delay 100 print bye ) delay 50 stop p'"); - return 0; - } - else if (config.args.Length == 1) { - // Turn a single quoted string into a list of arguments. - // Beware: this doesn't handle multiple spaces between commands. - string foo = config.args[0] ; - assert foo != null; - args = foo.Split(new char[] {' '}); - } - else args = config.args; - - Console.WriteLine("return value: {0:x}", new KPTest().Go(args, 0)); - return 0; - } - - public KPTest() - { - } - - private string[] args; - - private KPTest(String[]! args) - { - this.args = args; - } - - private void Run() - requires this.args != null; - { - Console.WriteLine("new thread running"); - Console.WriteLine("thread return value: {0:x}", Go(args, 0)); - } - - public override uint Go(string[]! args, int i) - { - if (i >= args.Length) return 0x12345678; - string arg = args[i]; - if (arg == "kernel" || arg == "_kernel") - { - ArgList* in ExHeap exArgs = null; - for(int j = args.Length - 1; j >= 0; j--) - { - exArgs = new[ExHeap] ArgList(Bitter.FromString(args[j]), exArgs); - } - - if (arg == "kernel") - { - Console.WriteLine("calling kernel"); - } - unsafe - { - uint r = Microsoft.Singularity.V1.Stress.StressDirect.KPTest((SharedHeapService.Allocation*) exArgs, i + 1); - if (arg == "kernel") - { - Console.WriteLine("returned from kernel"); - } - return r; - } - } - else if (arg == "(|") - { - int rParen = FindClosingRParen((!) args, i + 1); - string[] before = new string[rParen - (i + 1)]; - Array.Copy(args, i + 1, before, 0, before.Length); - string[] after = new string[args.Length - (rParen + 1)]; - Array.Copy(args, rParen + 1, after, 0, after.Length); - new Thread(new ThreadStart(new KPTest(before).Run)).Start(); - return Go(after, 0); - } - else if (arg == "(&") - { - string! name = (!)args[i + 1]; - int rParen = FindClosingRParen((!) args, i + 2); - string[] before = new string[rParen - (i + 2) + 1]; - before[0] = "kptest"; - Array.Copy(args, i + 2, before, 1, before.Length - 1); - string[] after = new string[args.Length - (rParen + 1)]; - Array.Copy(args, rParen + 1, after, 0, after.Length); - Process p = new Process(before); - processes[name] = p; - p.Start(); - return Go(after, 0); - } - else if (arg == "suspend" || arg == "_suspend") - { - string! name = (!)args[i + 1]; - Process! p = (!)(processes[name] as Process); - if (arg == "suspend") - { - Console.WriteLine("suspending " + name); - } - p.Suspend(false); - if (arg == "suspend") - { - Console.WriteLine("suspended " + name); - } - return Go(args, i + 2); - } - else if (arg == "resume" || arg == "_resume") - { - string! name = (!)args[i + 1]; - Process! p = (!)(processes[name] as Process); - if (arg == "resume") - { - Console.WriteLine("resuming " + name); - } - p.Resume(false); - if (arg == "resume") - { - Console.WriteLine("resumed " + name); - } - return Go(args, i + 2); - } - else if (arg == "stop" || arg == "_stop") - { - string! name = (!)args[i + 1]; - Process! p = (!)(processes[name] as Process); - if (arg == "stop") - { - Console.WriteLine("stopping " + name); - } - p.Stop(); - if (arg == "stop") - { - Console.WriteLine("stopped " + name); - } - return Go(args, i + 2); - } - else - { - return base.Go(args, i); - } - } - } -} diff --git a/base/Applications/Tests/KPTest/StressLib.cs b/base/Applications/Tests/KPTest/StressLib.cs deleted file mode 100644 index 1f7162d..0000000 --- a/base/Applications/Tests/KPTest/StressLib.cs +++ /dev/null @@ -1,30 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: KPTest.sg -// -// Note: Tests the kernel-process boundary. -// -// Feel free to add more commands to this file. - -using System; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Stress; -using Microsoft.Singularity.Memory; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.V1.Services; -using System.Runtime.CompilerServices; - -namespace Microsoft.Singularity.Stress -{ - public class StressDirect - { - [OutsideGCDomain] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern unsafe uint KPTest(SharedHeapService.Allocation* sharedArgs, int i); - } -} diff --git a/base/Applications/Tests/Keys/Keys.sg b/base/Applications/Tests/Keys/Keys.sg index edbc2bc..bb31aa2 100644 --- a/base/Applications/Tests/Keys/Keys.sg +++ b/base/Applications/Tests/Keys/Keys.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Keys.sg -// // Note: Simple Singularity test program. // @@ -36,8 +34,7 @@ namespace Microsoft.Singularity.Applications int y; uint key = 0; keyboard.SendGetKey(); - switch receive - { + switch receive { case keyboard.AckKey(ikey): key = ikey; break; @@ -82,8 +79,7 @@ namespace Microsoft.Singularity.Applications KeyboardDeviceContract.NewChannel(out keyImp, out keyExp); nsImp.SendBind(devName, keyExp); - switch receive - { + switch receive { case nsImp.AckBind(): return keyImp; break; @@ -123,8 +119,7 @@ namespace Microsoft.Singularity.Applications return null; } - switch receive - { + switch receive { case imp.Success(): break; case imp.ContractNotSupported(): diff --git a/base/Applications/Tests/MathTest/MathTest.cs b/base/Applications/Tests/MathTest/MathTest.cs index 5507df3..ad4e76f 100644 --- a/base/Applications/Tests/MathTest/MathTest.cs +++ b/base/Applications/Tests/MathTest/MathTest.cs @@ -4,8 +4,7 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: MathTest.cs -// + using System; namespace Microsoft.Singularity.Applications diff --git a/base/Applications/Tests/MemStress/MemStress.cs b/base/Applications/Tests/MemStress/MemStress.cs new file mode 100644 index 0000000..1dbc45b --- /dev/null +++ b/base/Applications/Tests/MemStress/MemStress.cs @@ -0,0 +1,384 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: Perform short (bvt) and long running stress of OutOfMemory +// behavior of the system. +// + +// +// Tests to support: +// +// - Overcommit of heap memory within a SIP +// +// Create lots of managed array objects until the OS can no +// longer satisfy the request and terminates the SIP. +// +// +// - Overcommit of stack memory within a SIP +// +// Implement a recursive function that results in stack overflow +// and termination of the SIP. +// +// - Overcommit of heap memory within the kernel +// +// Since we can't directly allocate kernel heap memory, +// we do this by creating lots of kernel objects that occupy +// the heap (such as new threads) +// +// - Overcommit kernel heap memory from within the kernel in multiple +// threads to find races in any resource reservation strategies that +// may be timing dependent. +// + +using System; +using System.Threading; + +using Microsoft.Contracts; +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.UnitTest; +using Microsoft.Singularity.V1.Services; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Configuration; + +[assembly: Transform(typeof(ApplicationResourceTransform))] + +namespace Microsoft.Singularity.Applications +{ + + [ConsoleCategory(DefaultAction=true)] + internal class Parameters { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [Endpoint] + public readonly TRef nsRef; + + [BoolParameter( "sip_so", Default=false, HelpMessage="Create Stack Overflow in SIP")] + internal bool sipSo; + + [BoolParameter( "sip_oom", Default=false, HelpMessage="Create Out Of Memory in SIP")] + internal bool sipOom; + + [BoolParameter( "kernel_oom", Default=false, HelpMessage="Create Kernel Out Of Memory")] + internal bool kernelOom; + + [BoolParameter( "kernel_oom_stress", Default=false, HelpMessage="Create Kernel Out Of Memory Stress")] + internal bool kernelOomStress; + + // + // The noProcess parameter runs the test without creating a sub-process. + // + // Since success for this test is running out of memory and having the SIP + // die, there is no way to return a success status to a top level test script. + // The script thinks that the SIP failure due to OOM is a failure of the test. + // + // So by default, the tests are run in a sub-process which we can then monitor + // and return a proper result to the top level test script. + // + // This parameter allows the same executable to be used for both parts + // of the test. + // + [BoolParameter( "noprocess", Default=false, HelpMessage="Run directly without a sub-process")] + internal bool noProcess; + + reflective internal Parameters(); + + internal int AppMain() { + return MemStress.AppMain(this); + } + } + + public class MemStress + { + internal static int AppMain(Parameters! config) + { + +#if PAGING + if (true) { + Console.WriteLine("MemStress does not work on PAGING builds"); + return -1; + } +#endif + DumpMemInfo(); + + SipMemoryStresser sipms = new SipMemoryStresser(); + + if (!config.noProcess) { + if (config.sipSo) { + sipms.RunTestAsProcess(config, "-sip_so"); + } + else if (config.sipOom) { + sipms.RunTestAsProcess(config, "-sip_oom"); + } + else if (config.kernelOom) { + sipms.RunTestAsProcess(config, "-kernel_oom"); + } + else if (config.kernelOomStress) { + sipms.RunTestAsProcess(config, "-kernel_oom_stress"); + } + else { + Console.WriteLine("Usage: MemStress [-sip_oom] [-sip_so] [-kernel_oom] [-kernel_oom_stress]"); + Console.WriteLine("Must pick one option"); + } + } + else { + if (config.sipSo) { + sipms.RunSOTest(); + } + else if (config.sipOom) { + sipms.RunOOMTest(); + } + else if (config.kernelOom) { + sipms.KernelOOMTest(); + } + else if (config.kernelOomStress) { + sipms.KernelOOMStress(); + } + else { + Console.WriteLine("Usage: MemStress [-sip_oom] [-sip_so] [-kernel_oom] [-kernel_oom_stress]"); + Console.WriteLine("Must pick one option"); + } + } + + return 0; + } + + public static void DumpMemInfo() + { + int result; + + ulong totalMemoryFree = 0; + ulong totalMemoryInUse = 0; + ulong kernelHeapInUse = 0; + ulong kernelStackInUse = 0; + ulong totalSIPHeapInUse = 0; + ulong totalSIPStackInUse = 0; + ulong kernelStackReservation = 0; + ulong kernelHeapReservation = 0; + + result = MemoryInfoService.MemoryUsageInfo( + out totalMemoryFree, + out totalMemoryInUse, + out kernelHeapInUse, + out kernelStackInUse, + out totalSIPHeapInUse, + out totalSIPStackInUse, + out kernelStackReservation, + out kernelHeapReservation + ); + + // TODO: Use standard ErrorCode's + if (result != 0) { + Console.WriteLine("Error {0} retrieving MemoryUsageInfo"); + } + else { + Console.WriteLine("TotalMemoryFree 0x{0:x8}, TotalMemoryInUse {1:x8}", totalMemoryFree, totalMemoryInUse); + Console.WriteLine("KernelHeapInUse {0:x8}, KernelStackInUse {1:x8}", kernelHeapInUse, kernelStackInUse); + Console.WriteLine("TotalSIPHeapInUse {0:x8}, TotalSIPStackInUse {1:x8}", totalSIPHeapInUse, totalSIPStackInUse); + Console.WriteLine("KernelStackReservation {0:x8}, KernelHeapReservation {1:x8}", kernelStackReservation, kernelHeapReservation); + Console.WriteLine(""); + } + } + } + + public class SipMemoryStresser + { + // + // Run out of memory test, eventually terminates the SIP due + // to fail fast. + // + internal void RunTestAsProcess(Parameters! config, string test) { + + Process p = null; + + // + // Create a subprocess run of "memstress -testparameter" + // + string[] args = new string[3]; + args[0] = "memstress"; + args[1] = test; + args[2] = "-noprocess"; + + Console.WriteLine("Creating subprocess memstress {0} -noprocess", test); + + try { + p = ProcessLauncher.CreateSubProcess(config, args); + } + catch (Exception e) { + Console.WriteLine("Exception from CreateSubProcess: " + e.Message); + return; + } + + if (p == null) { + Console.WriteLine("Error creating process"); + return; + } + + Console.WriteLine("Process returned with ExitCode={0}", p.ExitCode); + } + + public void RunOOMTest() { + + object[] TopLevel; + object[] tmp; + + TopLevel = new Object[1]; + + // + // Currrently the test handles the OOM exception showing + // that this works. Additional testing should create multiple + // threads and have OOM failures at random places in the runtime + // without try/except handlers so we can test the case when + // the exception bubbles up to the top of the stack. + // + try { + for (int count = 65535;; count *= 2) { + + tmp = new object[count]; + + tmp[0] = TopLevel; + TopLevel = tmp; + + for (int i = 1; i < count; i++) { + TopLevel[i] = new object[count]; + } + + MemStress.DumpMemInfo(); + } + } + catch (OutOfMemoryException e) { + e=e; + //Thread.CurrentProcess.Stop(1); + return; + } + } + + // Run stack overflow test, terminates the SIP + public void RunSOTest() { + + // Test the fail fast path directly + //Microsoft.Singularity.V1.Services.StackService.StackOverflow(); + + RunSOTest(); + } + + // + // Invoke a kernel heap out of memory condition by creating + // lots of kernel objects + // + public void KernelOOMTest() { + + IdleThread[] threads; + + int threadCount = 1000; + + while (true) { + + System.Console.WriteLine("Creating {0} threads", threadCount); + + threads = new IdleThread[threadCount]; + if (threads == null) { + System.Console.WriteLine("Error allocating Thread[]"); + return; + } + + for (int i = 0; i < threadCount; i++) { + threads[i] = new IdleThread(i); + if (threads[i] == null) { + System.Console.WriteLine("Error creating thread"); + return; + } + } + + // Start them running + for (int i = 0; i < threadCount; i++) { + ((!)threads[i]).RunThread(); + } + } + } + + // + // Use multiple threads to stress the system and induce + // kernel heap OOM to test the ability for the system + // to remain running. + // + public void KernelOOMStress() { + Console.WriteLine("Not implemented yet"); + return; + } + } + + // Generic class that creates the thread + public class ThreadRunner + { + private Thread thread; + + public ThreadRunner() { + } + + public Thread Thread { + get { + return thread; + } + } + + // Creates thread, returns with it running + public Thread RunThread() { + + thread = new Thread(new ThreadStart(ThreadMain)); + + thread.Start(); + + return thread; + } + + public virtual void ThreadMain() { + + // This exits the thread + return; + } + } + + // + // Implementation classes represent different thread creation + // parameters, and actions + // + + // + // This creates an idle thread that just waits. + // + // The goal is to create lots of kernel objects and use up + // memory, not jam the system with many threads until + // it stalls. + // + public class IdleThread : ThreadRunner + { + private long argValue; + + public IdleThread(long arg) : base() { + argValue = arg; + } + + public override void ThreadMain() { + + // Wait for ever + while (true) { + Thread.Sleep(60*60*1000); + } + + // This exits the thread + return; + } + } +} + + diff --git a/base/Applications/Tests/MemStress/MemStress.csproj b/base/Applications/Tests/MemStress/MemStress.csproj new file mode 100644 index 0000000..5d17adf --- /dev/null +++ b/base/Applications/Tests/MemStress/MemStress.csproj @@ -0,0 +1,40 @@ + + + + + + + MemStress + Exe + + true + true + + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/Tests/MemStress/SubProcess.sg b/base/Applications/Tests/MemStress/SubProcess.sg new file mode 100644 index 0000000..f5bfd3a --- /dev/null +++ b/base/Applications/Tests/MemStress/SubProcess.sg @@ -0,0 +1,94 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + + using System; +//using System.Threading; +using Microsoft.SingSharp; +using Microsoft.Singularity; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Test.Contracts; +using Microsoft.Singularity.Xml; + +using FileSystem.Utils; +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Io; +//using System.Collections; + +using Microsoft.Singularity.V1.Services; +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.Directory; +using Microsoft.Contracts; + +namespace Microsoft.Singularity.Applications +{ + + public class ProcessLauncher { + + internal static Process CreateSubProcess( + Parameters! config, + string[] args + ) + { + DirectoryServiceContract.Imp ds = config.nsRef.Acquire(); + if (ds == null) { + throw new Exception("Unable to acquire handle to the Directory Service root"); + } + ds.RecvSuccess(); + + PipeMultiplexer outputMux = MuxOut(); + if (outputMux == null) { + delete ds; + return null; + } + + Manifest manifest; + + Process child = Binder.CreateProcess(ds, args, outputMux, out manifest); + if (child == null) { + Console.WriteLine("Error creating process"); + DebugStub.Print("Error creating process"); + outputMux.Dispose(); + delete ds; + return null; + } + + child.Start(); + + child.Join(); + + outputMux.Dispose(); + delete ds; + + if (null == child) { + return null; + } + + // Allow the caller to the get childs exit status + return child; + } + + // Redirect our standard output into a multiplexer so we can interleave + // output from child processes + static private PipeMultiplexer MuxOut() + { + // Swap our real stdOut with a newly created one + UnicodePipeContract.Exp! newOutputExp; + UnicodePipeContract.Imp! newOutputImp; + UnicodePipeContract.NewChannel(out newOutputImp, out newOutputExp); + UnicodePipeContract.Imp stdOut = ConsoleOutput.Swap(newOutputImp); + if (stdOut == null) { + // TODO may not require stdout once we report to debugStub + Console.WriteLine("test expects a STDOUT pipe"); + delete newOutputExp; + return null; + } + // Use a mux to splice our own output together with the child + // processes we will run. + return PipeMultiplexer.Start(stdOut, newOutputExp); + } + } +} + diff --git a/base/Applications/Tests/MonitorTest/MonitorTest.cs b/base/Applications/Tests/MonitorTest/MonitorTest.cs deleted file mode 100644 index ce98728..0000000 --- a/base/Applications/Tests/MonitorTest/MonitorTest.cs +++ /dev/null @@ -1,63 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: MonitorTest.cs -// -// Note: Some basic tests of the monitor code. -// - -using System; -using System.Threading; - -using Microsoft.Singularity.UnitTest; - -using Microsoft.Singularity.Channels; -using Microsoft.Contracts; -using Microsoft.SingSharp.Reflection; -using Microsoft.Singularity.Applications; -using Microsoft.Singularity.Io; -using Microsoft.Singularity.Configuration; -[assembly: Transform(typeof(ApplicationResourceTransform))] - -namespace Microsoft.Singularity.Applications { - [ConsoleCategory(DefaultAction=true)] - internal class Parameters { - [InputEndpoint("data")] - public readonly TRef Stdin; - - [OutputEndpoint("data")] - public readonly TRef Stdout; - - reflective internal Parameters(); - - internal int AppMain() { - return MonitorTest.AppMain(this); - } - } - - public class MonitorTest - { - internal static int AppMain(Parameters! config) - { - for (int i = 0; i < 2; i++) { - UnitTest.Add("Many Threads PulseAll", - new UnitTest.TestDelegate(PulseAllTest.ManyThreadsTest)); - - UnitTest.Add("Few Threads Pulse", - new UnitTest.TestDelegate(PulseAllTest.FewThreadsTest)); - - UnitTest.Add("Low-density Pulse", - new UnitTest.TestDelegate(PulseTest.LowDensityTest)); - UnitTest.Add("Medium-density Pulse", - new UnitTest.TestDelegate(PulseTest.MediumDensityTest)); - UnitTest.Add("High-density Pulse", - new UnitTest.TestDelegate(PulseTest.HighDensityTest)); - } - return ((UnitTest.Run(true) == UnitTest.Result.Passed) && - Assert.Failures == 0) ? 0 : 1; - } - } -} diff --git a/base/Applications/Tests/MonitorTest/MonitorTest.csproj b/base/Applications/Tests/MonitorTest/MonitorTest.csproj index 24f633c..b4a0e42 100644 --- a/base/Applications/Tests/MonitorTest/MonitorTest.csproj +++ b/base/Applications/Tests/MonitorTest/MonitorTest.csproj @@ -5,32 +5,26 @@ Microsoft Research Singularity Copyright (c) Microsoft Corporation. All rights reserved. -File: Applications\Tests\MonitorTest\MonitorTest.csproj - -Note: MonitorTest test +Note: ############################################################################## --> - + MonitorTest Exe - true + + true true - - - - - - + diff --git a/base/Applications/Tests/MonitorTest/PulseAllTest.cs b/base/Applications/Tests/MonitorTest/PulseAllTest.cs index 3216b04..5a86174 100644 --- a/base/Applications/Tests/MonitorTest/PulseAllTest.cs +++ b/base/Applications/Tests/MonitorTest/PulseAllTest.cs @@ -4,14 +4,13 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: PulseTest.cs -// // Note: // using System; using System.Threading; +using Microsoft.Singularity; using Microsoft.Singularity.UnitTest; namespace Microsoft.Singularity.Applications @@ -20,8 +19,26 @@ namespace Microsoft.Singularity.Applications /// A test of Monitor.PulseAll. A collection of threads waits on /// a single monitor for a pulse all. /// - internal sealed class PulseAllTest + [TestClass] + public class PulseAllTest : TestClass { + [TestMethod] + public void FewThreadsTest() + { + (new Pulser(8, 2, Expect)).RunTest(); + } + + [TestMethod] + public void ManyThreadsTest() + { + (new Pulser(50, 2, Expect)).RunTest(); + } + } + + internal sealed class Pulser + { + TestLog Expect; + int waiterCount = 50; int timeoutSeconds = 2; @@ -37,6 +54,15 @@ namespace Microsoft.Singularity.Applications object! monitor = new object(); bool []! visited; + + public Pulser(int threadCount, int timeoutSeconds, TestLog expect) + { + this.waiterCount = threadCount; + this.visited = new bool [threadCount]; + this.timeoutSeconds = timeoutSeconds; + this.Expect = expect; + } + private static void Yield() { #if SINGULARITY @@ -48,7 +74,7 @@ namespace Microsoft.Singularity.Applications public void WatchdogThreadMain() { - TimeSpan delta = TimeSpan.FromSeconds(timeoutSeconds); + TimeSpan delta = TimeSpan.FromMilliseconds(500); int now = this.generation; int last = 0; @@ -59,18 +85,15 @@ namespace Microsoft.Singularity.Applications } Yield(); now = this.generation; - } while (last != now); - - DebugStub.Break(); - - this.timedOut = true; + Expect.NotEqual(last, now, "progress was made in the last cycle"); + } while (true); } public void ControllerThreadMain() { const int iterations = 1000; - const int YieldFudge = 50; + const int YieldFudge = 150; // Signal worker threads to start controllerReady.Set(); @@ -78,13 +101,6 @@ namespace Microsoft.Singularity.Applications for (int i = 1; i <= iterations; i++) { barrierEvent.WaitOne(); - // There is a potential - // race between the last thread reaching the barrier - // setting the barrierEvent and reaching the Monitor.Wait - // call. By yielding a few times we should give the - // thread enough time to get there. - for (int n = 0; n < YieldFudge; n++) - Yield(); lock (this.monitor) { // Reset check state this.passedBarrier = 0; @@ -107,19 +123,18 @@ namespace Microsoft.Singularity.Applications while (!stop) { Monitor.Enter(this.monitor); try { - Assert.True(waiterRunning == false, - "Two waiters have monitor."); + Expect.False(waiterRunning, "Only one waiter is in the monitor"); waiterRunning = true; - Assert.False(this.visited[this.passedBarrier], - "Thread already reached barrier."); + Expect.False(this.visited[this.passedBarrier], + "Thread is before the barrier"); this.visited[this.passedBarrier] = true; this.passedBarrier++; this.generation++; - Assert.LessOrEqual(this.passedBarrier, waiterCount, - "More waiters passed barrier than expected."); + Expect.LessOrEqual(this.passedBarrier, waiterCount, + "Not too many waiters passed the barrier"); if (this.passedBarrier == this.waiterCount) { barrierEvent.Set(); @@ -153,26 +168,9 @@ namespace Microsoft.Singularity.Applications } while (!controller.Join(TimeSpan.FromMilliseconds(100))) { - Assert.False(this.timedOut, "Timed out!"); + Expect.False(this.timedOut, "Completed before timeout"); } watchdog.Join(); } - - public PulseAllTest(int threadCount, int timeoutSeconds) - { - this.waiterCount = threadCount; - this.timeoutSeconds = timeoutSeconds; - this.visited = new bool [threadCount]; - } - - public static void FewThreadsTest() - { - (new PulseAllTest(8, 2)).RunTest(); - } - - public static void ManyThreadsTest() - { - (new PulseAllTest(50, 2)).RunTest(); - } } } diff --git a/base/Applications/Tests/MonitorTest/PulseTest.cs b/base/Applications/Tests/MonitorTest/PulseTest.cs index 94f4f6a..f541eca 100644 --- a/base/Applications/Tests/MonitorTest/PulseTest.cs +++ b/base/Applications/Tests/MonitorTest/PulseTest.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: PulseTest.cs -// // Note: // @@ -23,22 +21,75 @@ namespace Microsoft.Singularity.Applications /// a collection of threads then enters, spins, pulses, and exits. /// /// - internal sealed class PulseTest + [TestClass] + public class PulseTest : TestClass { - internal class Node + [ClassInitialize] + public void Init() + { + // HACK + PulseHelper.Expect = Expect; + } + + [TestMethod] + public void LowDensityTest() + { + PulseHelper p = new PulseHelper(128, 17, 20); + p.Initialize(); + p.RunTest(); + } + + [TestMethod] + public void MediumDensityTest() + { + PulseHelper p = new PulseHelper(32, 32, 20); + p.Initialize(); + p.RunTest(); + } + + [TestMethod] + public void HighDensityTest() + { + PulseHelper p = new PulseHelper(4, 128, 20); + p.Initialize(); + p.RunTest(); + } + } + + internal sealed class PulseHelper + { + + public static TestLog Expect; + + internal PulseHelper(int numberOfNodes, int numberOfThreads, int iterations) + { + this.nodes = new Node[numberOfNodes]; + this.threadCount = numberOfThreads; + this.maxCycles = iterations; + this.startedCount = 0; + this.finishedCount = 0; + this.finishedEvent = new ManualResetEvent(false); + } + + internal class Node { volatile bool inVisit = false; + // workaround Phoenix MSIL/PDB reader bug 1083 + public Node() + { + } + internal void BeginVisit() { Monitor.Enter(this); - Assert.False(inVisit, "Node is visited by two threads"); + Expect.False(inVisit, "This is the first thread into the node"); inVisit = true; } internal void EndVisit() { - Assert.True(inVisit, "Unpaired visit ending"); + Expect.True(inVisit, "EndVisit has a corresponding BeginVisit"); Monitor.Pulse(this); inVisit = false; Monitor.Exit(this); @@ -74,7 +125,7 @@ namespace Microsoft.Singularity.Applications int r = this.path[n]; this.remainLength--; - this.path[r] = this.path[this.remainLength]; + this.path[n] = this.path[this.remainLength]; this.path[this.remainLength] = r; if (this.remainLength == 0) { @@ -127,8 +178,9 @@ namespace Microsoft.Singularity.Applications // Yield up to number of threads to give other // threads a chance to run int yieldCount = rng.Next(this.threadCount); - while (yieldCount-- > 0) + while (yieldCount-- > 0) { Yield(); + } n.EndVisit(); // Console.Write("[{0}]", threadNumber); @@ -146,28 +198,28 @@ namespace Microsoft.Singularity.Applications private void WatchdogThreadMain() { int last = this.generation; + + // give the visitor threads a chance to initialize + const int maxInitializationTime = 120; + int loopCount = 0; + while (startedCount != this.threadCount && ++loopCount < maxInitializationTime) { + Thread.Sleep(1000); + } + for (;;) { Thread.Sleep(TimeSpan.FromSeconds(5)); if (this.finishedCount == this.threadCount) { return; } int now = this.generation; - Assert.True(last != now, "Deadlock detected."); + if (last == now) { + DebugStub.Break(); + } last = now; } } - internal PulseTest(int numberOfNodes, int numberOfThreads, int iterations) - { - this.nodes = new Node[numberOfNodes]; - this.threadCount = numberOfThreads; - this.maxCycles = iterations; - this.startedCount = 0; - this.finishedCount = 0; - this.finishedEvent = new ManualResetEvent(false); - } - - private void Initialize() + internal void Initialize() { for (int i = 0; i < nodes.Length; i++) { nodes[i] = new Node(); @@ -184,28 +236,6 @@ namespace Microsoft.Singularity.Applications finishedEvent.WaitOne(); watchdog.Join(); - Assert.True(Assert.Failures == 0, "Failures within threads."); - } - - internal static void LowDensityTest() - { - PulseTest p = new PulseTest(128, 17, 20); - p.Initialize(); - p.RunTest(); - } - - internal static void MediumDensityTest() - { - PulseTest p = new PulseTest(32, 32, 20); - p.Initialize(); - p.RunTest(); - } - - internal static void HighDensityTest() - { - PulseTest p = new PulseTest(4, 128, 20); - p.Initialize(); - p.RunTest(); } } } diff --git a/base/Applications/Tests/MpStress/MpStress.cs b/base/Applications/Tests/MpStress/MpStress.cs new file mode 100644 index 0000000..b2b4d5f --- /dev/null +++ b/base/Applications/Tests/MpStress/MpStress.cs @@ -0,0 +1,294 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: Perform short (bvt) and long running stress of multiprocessor +// sensitive areas of the system. Used MonitorTest.cs as an +// application template +// + +using System; +using System.Threading; + +using Microsoft.Singularity.UnitTest; + +using Microsoft.Singularity.Channels; +using Microsoft.Contracts; +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Configuration; +[assembly: Transform(typeof(ApplicationResourceTransform))] + +namespace Microsoft.Singularity.Applications +{ + [ConsoleCategory(DefaultAction=true)] + internal class Parameters { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [LongParameter("cpucount", Default=2, HelpMessage="CPU Count for test")] + internal long cpuCount; + + [LongParameter("passcount", Default=10, HelpMessage="Pass Count for test")] + internal long passCount; + + reflective internal Parameters(); + + internal int AppMain() { + return MpStress.AppMain(this); + } + } + + public class MpStress + { + internal static int AppMain(Parameters! config) + { + int result; + + Console.WriteLine("Running MpStress for CPU Count {0}", config.cpuCount); + + ThreadTest tt = new ThreadTest(); + + result = tt.RunTest(config.cpuCount, config.passCount); + if (result != 0) { + return result; + } + + return 0; + } + } + + public class ThreadTest + { + + public int RunTest(long cpuCount, long passCount) { + + // Start the thread create/destroy test + //ThreadTestsThread threadTests = new ThreadTestsThread(cpuCount); + + // Run pulse tests + PulseTestsThread pulseTests = new PulseTestsThread(cpuCount, passCount); + pulseTests.RunThread(); + + // Start the background threads + //threadTests.RunThread(); + + // Wait for pulse tests to complete + ((!)pulseTests.Thread).Join(); + + return 0; + } + } + + // + // Implementation classes represent different thread creation + // parameters, and actions + // + + public class PrintThread : ThreadRunner + { + private long argValue; + + public PrintThread(long arg) : base() { + argValue = arg; + } + + public override void ThreadMain() { + + //System.Console.WriteLine("PrintThread: Thread {0}, ThreadIndex {1}\n", Thread.CurrentThread.GetThreadId(), argValue); + + // Call some additional "interesting" system calls here in order to disturb + // the system state, cause lock contention, flesh out races and unprotected code + // regions, etc. + // + + // This exits the thread + //Console.Write("-"); + return; + } + } + + public class ThreadTestsThread : ThreadRunner + { + private long cpuCount; + + public ThreadTestsThread(long cpuCount) : base() { + this.cpuCount = cpuCount; + } + + public override void ThreadMain() { + + const long Timeout = 1000 * 10; // 10 seconds + long threadCount = 0; + PrintThread[] threads; + + threadCount = cpuCount * 2; + + // + // Run forever, process will exit when the ApMain thread + // is done with the test sequence + // + for (;;) { + + //Console.Write("."); + + threads = new PrintThread[threadCount]; + if (threads == null) { + System.Console.WriteLine("Error allocating Thread[]"); + return; + } + + for (int i = 0; i < threadCount; i++) { + threads[i] = new PrintThread(i); + if (threads[i] == null) { + System.Console.WriteLine("Error creating thread"); + return; + } + } + + // Start them running + for (int i = 0; i < threadCount; i++) { + ((!)threads[i]).RunThread(); + } + + TimeSpan timeout = TimeSpan.FromMilliseconds(Timeout); + + for (int i = 0; i < threadCount; i++) { + if (!((!)(((!)threads[i]).Thread)).Join(timeout)) { + Console.WriteLine("Timeout waiting for thread exit"); + return; + } + //Console.Write("+"); + } + } + } + } + + public class PulseTestsThread : ThreadRunner + { + private long cpuCount; + private long passes; + private int threadCount; + private int maxThreadCount; + private const int threadsPerCpu = 64; + + public PulseTestsThread(long cpuCount, long passes) : base() { + this.cpuCount = cpuCount; + this.passes = passes; + maxThreadCount = threadsPerCpu * (int)cpuCount; + + // Limit max threads, otherwise we will overflow the kernel's thread table + if (maxThreadCount > 512) { + maxThreadCount = 512; + } + } + + public override void ThreadMain() { + + System.Console.WriteLine("Running PulseTests: Thread {0}\n", Thread.CurrentThread.GetThreadId()); + Console.Write("passes={0}", passes); + Console.WriteLine(" cpuCount={0}", cpuCount); + + for (int pass = 0; pass < passes; pass++) { + + Console.WriteLine("pass={0}", pass); + + UnitTest.Clear(); + threadCount = 0; + + for (int i = 0; i < cpuCount; i++) { + + // 50 threads + if (!CheckMaxThreads(50)) { + UnitTest.Add("Many Threads PulseAll", + new UnitTest.TestDelegate(PulseAllTest.ManyThreadsTest)); + } + + // 8 threads + if (!CheckMaxThreads(8)) { + UnitTest.Add("Few Threads PulseAll", + new UnitTest.TestDelegate(PulseAllTest.FewThreadsTest)); + } + + // 17 threads + if (!CheckMaxThreads(17)) { + UnitTest.Add("Low-density Pulse", + new UnitTest.TestDelegate(PulseTest.LowDensityTest)); + } + + // 32 threads + if (!CheckMaxThreads(32)) { + UnitTest.Add("Medium-density Pulse", + new UnitTest.TestDelegate(PulseTest.MediumDensityTest)); + } + + // 128 threads + if (!CheckMaxThreads(128)) { + UnitTest.Add("High-density Pulse", + new UnitTest.TestDelegate(PulseTest.HighDensityTest)); + } + } + + Console.WriteLine("{0} PulseThreads", threadCount); + + // Wait for the sub-threads to run + if (UnitTest.Run(true) != UnitTest.Result.Passed) { + Console.WriteLine("PulseTest failed!\n"); + } + } + + // This exits the thread + return; + } + + // We must keep total thread count at a reasonable level + // otherwise we overflow the kernel thread table that is + // hardcoded at 1024. We will set 512 max threads for now. + private bool CheckMaxThreads(int count) + { + if ((count + threadCount) <= maxThreadCount) { + threadCount += count; + return false; + } + + return true; + } + } + + // Generic class that creates the thread + public class ThreadRunner + { + private Thread thread; + + public ThreadRunner() { + } + + public Thread Thread { + get { + return thread; + } + } + + // Creates thread, returns with it running + public Thread RunThread() { + + thread = new Thread(new ThreadStart(ThreadMain)); + + thread.Start(); + + return thread; + } + + public virtual void ThreadMain() { + + // This exits the thread + return; + } + } +} diff --git a/base/Applications/Tests/MpStress/MpStress.csproj b/base/Applications/Tests/MpStress/MpStress.csproj new file mode 100644 index 0000000..c8346eb --- /dev/null +++ b/base/Applications/Tests/MpStress/MpStress.csproj @@ -0,0 +1,36 @@ + + + + + + + MpStress + Exe + + true + true + + + + + + + + + + + + + + + diff --git a/base/Applications/Tests/MpStress/PulseAllTest.cs b/base/Applications/Tests/MpStress/PulseAllTest.cs new file mode 100644 index 0000000..016e3eb --- /dev/null +++ b/base/Applications/Tests/MpStress/PulseAllTest.cs @@ -0,0 +1,174 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// + +using System; +using System.Threading; + +using Microsoft.Singularity.UnitTest; + +namespace Microsoft.Singularity.Applications +{ + /// + /// A test of Monitor.PulseAll. A collection of threads waits on + /// a single monitor for a pulse all. + /// + internal sealed class PulseAllTest + { + int waiterCount = 50; + int timeoutSeconds = 2; + + int passedBarrier = 0; + bool waiterRunning = false; + volatile bool timedOut = false; + volatile bool stop = false; + volatile int generation = 0; + + ManualResetEvent controllerReady = new ManualResetEvent(false); + ManualResetEvent finishedEvent = new ManualResetEvent(false); + AutoResetEvent barrierEvent = new AutoResetEvent(false); + object monitor = new object(); + bool [] visited; + + private static void Yield() + { +#if SINGULARITY + Thread.Yield(); +#else + Thread.Sleep(0); +#endif + } + + public void WatchdogThreadMain() + { + TimeSpan delta = TimeSpan.FromSeconds(timeoutSeconds); + + int now = this.generation; + int last = 0; + do { + last = now; + if (finishedEvent.WaitOne(delta)) { + return; + } + Yield(); + now = this.generation; + if (last == now) + DebugStub.Break(); // break if we made no progress + } while (true); + } + + public void ControllerThreadMain() + { + const int iterations = 1000; + + const int YieldFudge = 50; + + // Signal worker threads to start + controllerReady.Set(); + Yield(); + + for (int i = 1; i <= iterations; i++) { + barrierEvent.WaitOne(); + // There is a potential + // race between the last thread reaching the barrier + // setting the barrierEvent and reaching the Monitor.Wait + // call. By yielding a few times we should give the + // thread enough time to get there. + for (int n = 0; n < YieldFudge; n++) + Yield(); + lock (this.monitor) { + // Reset check state + this.passedBarrier = 0; + for (int j = 0; j < visited.Length; j++) { + visited[j] = false; + } + + // Set stop flag if end is reached + this.stop = (i == iterations); + + // Wake up waiters + Monitor.PulseAll(this.monitor); + } + } + finishedEvent.Set(); + } + + public void WaiterThreadMain() + { + while (!stop) { + Monitor.Enter(this.monitor); + try { + Assert.True(waiterRunning == false, + "Two waiters have monitor."); + waiterRunning = true; + + Assert.False(this.visited[this.passedBarrier], + "Thread already reached barrier."); + this.visited[this.passedBarrier] = true; + + this.passedBarrier++; + this.generation++; + + Assert.LessOrEqual(this.passedBarrier, waiterCount, + "More waiters passed barrier than expected."); + + if (this.passedBarrier == this.waiterCount) { + barrierEvent.Set(); + } + + this.waiterRunning = false; + Monitor.Wait(this.monitor); + this.waiterRunning = true; + } + finally { + this.waiterRunning = false; + Monitor.Exit(this.monitor); + } + } + } + + public void RunTest() + { + Thread watchdog = + new Thread(new ThreadStart(WatchdogThreadMain)); + Thread controller = + new Thread(new ThreadStart(ControllerThreadMain)); + + watchdog.Start(); + controller.Start(); + + controllerReady.WaitOne(); + for (int i = 0; i < waiterCount; i++) { + Thread t = new Thread(new ThreadStart(WaiterThreadMain)); + t.Start(); + } + + while (!controller.Join(TimeSpan.FromMilliseconds(100))) { + Assert.False(this.timedOut, "Timed out!"); + } + watchdog.Join(); + } + + public PulseAllTest(int threadCount, int timeoutSeconds) + { + this.waiterCount = threadCount; + this.timeoutSeconds = timeoutSeconds; + this.visited = new bool [threadCount]; + } + + public static void FewThreadsTest() + { + (new PulseAllTest(8, 500)).RunTest(); + } + + public static void ManyThreadsTest() + { + (new PulseAllTest(50, 500)).RunTest(); + } + } +} diff --git a/base/Applications/Tests/MpStress/PulseTest.cs b/base/Applications/Tests/MpStress/PulseTest.cs new file mode 100644 index 0000000..27eaa46 --- /dev/null +++ b/base/Applications/Tests/MpStress/PulseTest.cs @@ -0,0 +1,210 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// + +using System; +using System.Threading; + +using Microsoft.Singularity.UnitTest; + +namespace Microsoft.Singularity.Applications +{ + /// + /// A class for testing Monitor.Pulse. + /// + /// This class creates a group of monitors (Node class) that + /// a collection of threads then enters, spins, pulses, and exits. + /// + /// + internal sealed class PulseTest + { + internal class Node + { + volatile bool inVisit = false; + + internal void BeginVisit() + { + Monitor.Enter(this); + Assert.False(inVisit, "Node is visited by two threads"); + inVisit = true; + } + + internal void EndVisit() + { + Assert.True(inVisit, "Unpaired visit ending"); + Monitor.Pulse(this); + inVisit = false; + Monitor.Exit(this); + } + } + + internal sealed class VisitPath + { + int [] path; + int remainLength; + int cycles; + + internal VisitPath(int pathLength) + { + this.path = new int[pathLength]; + this.remainLength = pathLength; + this.cycles = 0; + } + + internal void Initialize() + { + // This is a separate method so + // we can compile compatibly on Win32. SGC wants + // the Sing# specific NonDelayed attribute otherwise. + for (int i = 0; i < this.remainLength; i++) { + this.path[i] = i; + } + } + + internal int Pick(int randomNumber) + { + int n = randomNumber % this.remainLength; + int r = this.path[n]; + + this.remainLength--; + this.path[n] = this.path[this.remainLength]; + this.path[this.remainLength] = r; + + if (this.remainLength == 0) { + this.cycles++; + this.remainLength = this.path.Length; + } + return r; + } + + internal int Cycles { get { return this.cycles; } } + } + + private Node[] nodes; + private readonly int threadCount; + private readonly int maxCycles; + private volatile int startedCount; + private volatile int finishedCount; + private volatile int generation; + private ManualResetEvent finishedEvent; + + private static void Yield() + { +#if SINGULARITY + Thread.Yield(); +#else + Thread.Sleep(0); +#endif + } + + internal void VisitorThreadMain() + { + // Wait until all other threads have started + int threadNumber; + lock (this) { + threadNumber = startedCount++; + } + while (startedCount != this.threadCount) { + Yield(); + } + + Random rng = new Random(threadNumber); + VisitPath path = new VisitPath(this.nodes.Length); + path.Initialize(); + + while (path.Cycles != this.maxCycles) { + // Select a node previously unvisited in this cycle + Node/*!*/ n = /*^(!)^*/ nodes[path.Pick(rng.Next())]; + n.BeginVisit(); + + // Yield up to number of threads to give other + // threads a chance to run + int yieldCount = rng.Next(this.threadCount); + while (yieldCount-- > 0) + Yield(); + + n.EndVisit(); + // Console.Write("[{0}]", threadNumber); + this.generation++; + } + + lock (this) { + this.finishedCount++; + if (this.finishedCount == this.threadCount) { + this.finishedEvent.Set(); + } + } + } + + private void WatchdogThreadMain() + { + int last = this.generation; + for (;;) { + Thread.Sleep(TimeSpan.FromSeconds(500)); + if (this.finishedCount == this.threadCount) { + return; + } + int now = this.generation; + if (last == now) + DebugStub.Break(); + last = now; + } + } + + internal PulseTest(int numberOfNodes, int numberOfThreads, int iterations) + { + this.nodes = new Node[numberOfNodes]; + this.threadCount = numberOfThreads; + this.maxCycles = iterations; + this.startedCount = 0; + this.finishedCount = 0; + this.finishedEvent = new ManualResetEvent(false); + } + + private void Initialize() + { + for (int i = 0; i < nodes.Length; i++) { + nodes[i] = new Node(); + } + } + + internal void RunTest() + { + for (int i = 0; i < this.threadCount; i++) { + (new Thread(new ThreadStart(VisitorThreadMain))).Start(); + } + Thread watchdog = new Thread(new ThreadStart(WatchdogThreadMain)); + watchdog.Start(); + + finishedEvent.WaitOne(); + watchdog.Join(); + Assert.True(Assert.Failures == 0, "Failures within threads."); + } + + internal static void LowDensityTest() + { + PulseTest p = new PulseTest(128, 17, 20); + p.Initialize(); + p.RunTest(); + } + + internal static void MediumDensityTest() + { + PulseTest p = new PulseTest(32, 32, 20); + p.Initialize(); + p.RunTest(); + } + + internal static void HighDensityTest() + { + PulseTest p = new PulseTest(4, 128, 20); + p.Initialize(); + p.RunTest(); + } + } +} diff --git a/base/Applications/Tests/Ntlm/NtlmUnitTest.csproj b/base/Applications/Tests/Ntlm/NtlmUnitTest.csproj index 0340e5b..72664eb 100644 --- a/base/Applications/Tests/Ntlm/NtlmUnitTest.csproj +++ b/base/Applications/Tests/Ntlm/NtlmUnitTest.csproj @@ -1,26 +1,24 @@  - + {3F9845B1-E3A1-40DD-96E5-AFD900858FA4} Exe NtlmUnitTest 4 - true - + + @@ -28,8 +26,8 @@ - + - + diff --git a/base/Applications/Tests/Ntlm/NtlmUnitTest.sg b/base/Applications/Tests/Ntlm/NtlmUnitTest.sg index 8fec4a9..6fa2a56 100644 --- a/base/Applications/Tests/Ntlm/NtlmUnitTest.sg +++ b/base/Applications/Tests/Ntlm/NtlmUnitTest.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: NtlmSupplicant.cs -// // Note: // // This app is a unit test for the NTLM authentication library. It can @@ -113,7 +111,8 @@ internal class DefaultCommand if (failureCount == 0) { WriteLine("All computed responses match known good values. Test passes."); return 0; - } else { + } + else { WriteLine("Failures: " + failureCount); return -1; } @@ -281,8 +280,7 @@ class Util public static string! ByteArrayToString(byte[]! buffer, int index, int length) { StringBuilder sb = new StringBuilder(length * 2); - for (int i = 0; i < length; i++) - { + for (int i = 0; i < length; i++) { byte b = buffer[index + i]; sb.Append(HexDigits[b >> 4]); sb.Append(HexDigits[b & 0xf]); @@ -354,17 +352,18 @@ internal class RemoteAuthTestCommand internal int AppMain() { // Split the username into domain\username, if the user has specified domain name. - string! username = this.UserName; + string! username = (!)this.UserName; string! domain; int index = username.IndexOf('\\'); if (index != -1) { domain = username.Substring(0, index); username = username.Substring(index + 1); - } else { + } + else { domain = "."; } - string! password = this.Password; + string! password = (!)this.Password; // First, connect to BVT test server. // The sole purpose of the server is to test NTLMSSP messages. @@ -372,10 +371,10 @@ internal class RemoteAuthTestCommand try { // Bartok is failing on Dns.GetLocalHostAddresses - /* - IPHostEntry! he = (!)Dns.GetHostByName(this.ServerName); - IPAddress[]! addresses = (!)he.AddressList; - */ + // + //IPHostEntry! he = (!)Dns.GetHostByName(this.ServerName); + //IPAddress[]! addresses = (!)he.AddressList; + // IPv4 addr = IPv4.Parse(this.ServerName); IPAddress[]! addresses = { new IPAddress(addr) }; @@ -403,7 +402,8 @@ internal class RemoteAuthTestCommand socket.Connect(ep); Console.WriteLine("Connected."); connected = true; - } catch(Exception ex) { + } + catch (Exception ex) { Console.WriteLine("Connection failed."); Util.ShowException(ex); connected = false; @@ -429,8 +429,8 @@ internal class RemoteAuthTestCommand byte[]! negotiate = NtlmSupplicant.GetNegotiate( NtlmNegotiateFlags.None, - this.ClientMachineDomainName, - this.Workstation); + (!)this.ClientMachineDomainName, + (!)this.Workstation); NtlmUnitTestProtocol.SendMessage(socket, TestMessageType.Negotiate, negotiate); @@ -459,12 +459,14 @@ internal class RemoteAuthTestCommand if (result.Succeeded != 0) { Console.WriteLine(" Succeeded = TRUE"); - } else { + } + else { Console.WriteLine(" Succeeded = FALSE"); } Console.WriteLine(" Message: " + resulttext); - } catch(Exception ex) { + } + catch (Exception ex) { Console.WriteLine("Exception occurred during test."); Util.ShowException(ex); } @@ -473,7 +475,8 @@ internal class RemoteAuthTestCommand } return 0; - } catch(Exception ex) { + } + catch (Exception ex) { Console.WriteLine("Exception occurred during test."); Util.ShowException(ex); return -1; @@ -511,15 +514,15 @@ internal class RemoteAuthTestCredMgrCommand // First, connect to BVT test server. // The sole purpose of the server is to test NTLMSSP messages. - string! credentialsName = this.CredentialsName; + string! credentialsName = (!)this.CredentialsName; string! credentialsTag = this.CredentialsTag != null ? this.CredentialsTag : ""; try { // Bartok is failing on Dns.GetLocalHostAddresses - /* - IPHostEntry! he = (!)Dns.GetHostByName(this.ServerName); - IPAddress[]! addresses = (!)he.AddressList; - */ + // + //IPHostEntry! he = (!)Dns.GetHostByName(this.ServerName); + //IPAddress[]! addresses = (!)he.AddressList; + // IPv4 addr = IPv4.Parse(this.ServerName); IPAddress[]! addresses = { new IPAddress(addr) }; @@ -547,7 +550,8 @@ internal class RemoteAuthTestCredMgrCommand socket.Connect(ep); Console.WriteLine("Connected."); connected = true; - } catch(Exception ex) { + } + catch (Exception ex) { Console.WriteLine("Connection failed."); Util.ShowException(ex); connected = false; @@ -640,16 +644,19 @@ internal class RemoteAuthTestCredMgrCommand if (result.Succeeded != 0) { Console.WriteLine(" Succeeded = TRUE"); - } else { + } + else { Console.WriteLine(" Succeeded = FALSE"); } Console.WriteLine(" Message: " + resulttext); - } finally { + } + finally { delete supplicant; } - } catch(Exception ex) { + } + catch (Exception ex) { Console.WriteLine("Exception occurred during test."); Util.ShowException(ex); } @@ -658,7 +665,8 @@ internal class RemoteAuthTestCredMgrCommand } return 0; - } catch(Exception ex) { + } + catch (Exception ex) { Console.WriteLine("Exception occurred during test."); Util.ShowException(ex); return -1; diff --git a/base/Applications/Tests/Null/Null.cs b/base/Applications/Tests/Null/Null.cs index 66a5507..fde15cf 100644 --- a/base/Applications/Tests/Null/Null.cs +++ b/base/Applications/Tests/Null/Null.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Null.cs -// // Note: Simple Singularity test program. // using System; @@ -19,7 +17,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] diff --git a/base/Applications/Tests/RamDiskTest/RamDiskTest.cs b/base/Applications/Tests/RamDiskTest/RamDiskTest.cs new file mode 100644 index 0000000..a7b5b91 --- /dev/null +++ b/base/Applications/Tests/RamDiskTest/RamDiskTest.cs @@ -0,0 +1,414 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: Perform short test of some RamDisk and RamDiskClientManager operations. +// Used MemStress.cs as an application template. +// + +using System; +using System.Collections; +using System.Threading; + +using Microsoft.Singularity.UnitTest; +using Microsoft.Singularity.V1.Services; + +using Microsoft.Contracts; +using Microsoft.SingSharp; +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Io; + +using Microsoft.Singularity.Services.RamDisk; +using Microsoft.Singularity.Services.RamDisk.Contracts; + +[assembly: Transform(typeof(ApplicationResourceTransform))] + +namespace Microsoft.Singularity.Applications +{ + [ConsoleCategory(DefaultAction=true)] + internal class Parameters { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + reflective internal Parameters(); + + internal int AppMain() { + return RamDiskTest.AppMain(this); + } + } + + public class RamDiskTest + { + internal static int AppMain(Parameters! config) + { + const int diskBytes = 2000000 /*bytes*/; + + RamDiskControlContract.Imp managerImp = null; + DirectoryServiceContract.Imp dsImp = null; + DiskDeviceContract.Imp diskImp = null; + try { + managerImp = ConnectToRamDiskClientManager(); + + string! diskPath = CreateTest(managerImp, diskBytes); + dsImp = DirectoryService.NewClientEndpoint(); + diskImp = OpenDevice(dsImp, diskPath); + + int lastSector = (diskBytes - 1 + RamDisk.SECTOR_SIZE - 1)/RamDisk.SECTOR_SIZE; + BasicReadWriteTest(diskImp, 0/*sector*/, RamDisk.SECTOR_SIZE); + BasicReadWriteTest(diskImp, lastSector, RamDisk.SECTOR_SIZE); + BasicReadWriteTest(diskImp, lastSector/2, RamDisk.SECTOR_SIZE); + for (int size = RamDisk.SECTOR_SIZE; size < diskBytes; size *= 2) { + BasicReadWriteTest(diskImp, 0/*sector*/, size); + } + BasicReadWriteTest(diskImp, 0/*sector*/, diskBytes); + + BasicReadWriteErrorTest(diskImp, lastSector + 1, RamDisk.SECTOR_SIZE); + BasicReadWriteErrorTest(diskImp, lastSector, 2*RamDisk.SECTOR_SIZE); + BasicReadWriteErrorTest(diskImp, 0/*sector*/, diskBytes + RamDisk.SECTOR_SIZE); + BasicReadWriteErrorTest(diskImp, lastSector/2, (lastSector + 1 - lastSector/2 + 1) * RamDisk.SECTOR_SIZE); + + RandomReadWriteTest(diskImp, diskBytes); + + EnsureDestroyFailsWithInUse(managerImp, diskPath); + + delete diskImp; + diskImp = null; + + Destroy(managerImp, diskPath, false/*force*/); + EnsureDoesNotExist(dsImp, diskPath); + + OutOfMemoryTest(managerImp); + } + catch (Exception ex) { + Console.WriteLine("Error: " + ex.Message + "\n" + ex.StackTrace); + } + finally { + delete managerImp; + delete dsImp; + delete diskImp; + } + return 0; + } + + private static void BasicReadWriteTest(DiskDeviceContract.Imp:Ready! diskImp, + int sector, + int size) + { + byte[] in ExHeap data = null; + try { + data = new[ExHeap] byte[size]; + for (int i = 0; i < data.Length; i++) { + data[i] = (byte)i; + } + data = Write(diskImp, 0, data); + + delete data; + data = new[ExHeap] byte[size]; + + data = Read(diskImp, 0, data); + for (int i = 0; i < data.Length; i++) { + if (data[i] != (byte)i) { + throw new Exception(String.Format("Data did not match after write-read sequence: at byte {0} got {1}, expected {2}", + i, data[i], (byte)i)); + } + } + } + finally { + delete data; + } + } + + private static void BasicReadWriteErrorTest(DiskDeviceContract.Imp:Ready! imp, + int sector, + int size) + { + byte[] in ExHeap data = new[ExHeap] byte[size]; + imp.SendRead(data, 0, (ulong)size, (ulong)sector); + switch receive { + case imp.AckRead(outBuffer): + delete outBuffer; + throw new Exception("RamDisk read operation succeeded where expected to fail"); + case imp.NakRead(): + break; + case imp.ChannelClosed(): + throw new Exception("RamDisk closed channel prematurely"); + } + + data = new[ExHeap] byte[size]; + imp.SendWrite(data, 0, (ulong)size, (ulong)sector); + switch receive { + case imp.AckWrite(outBuffer): + throw new Exception("RamDisk write operation succeeded where expected to fail"); + case imp.NakWrite(): + break; + case imp.ChannelClosed(): + throw new Exception("RamDisk closed channel prematurely"); + } + } + + private static void RandomReadWriteTest(DiskDeviceContract.Imp:Ready! diskImp, + int diskBytes) + { + const int seed = 840799514; // Just an arbitrary value + const int sectorsToWrite = 1000; + int numSectors = (diskBytes - 1 + RamDisk.SECTOR_SIZE - 1)/RamDisk.SECTOR_SIZE + 1; + Random r = new Random(seed); + ArrayList! visitedSectors = new ArrayList(); + + byte[]! in ExHeap data = new[ExHeap] byte[RamDisk.SECTOR_SIZE]; + try { + byte[]! gcData = new byte[RamDisk.SECTOR_SIZE]; + for (int i = 0; i < sectorsToWrite; i++) { + int sectorNum = r.Next(numSectors); + if (visitedSectors.Contains(sectorNum)) { + continue; + } + else { + visitedSectors.Add(sectorNum); + } + + r.NextBytes(gcData); + + Bitter.FromByteArray(data, 0, RamDisk.SECTOR_SIZE, gcData, 0); + data = Write(diskImp, (ulong)sectorNum, data); + } + + visitedSectors.Clear(); + + r = new Random(seed); + for (int i = 0; i < sectorsToWrite; i++) { + int sectorNum = r.Next(numSectors); + if (visitedSectors.Contains(sectorNum)) { + continue; + } + else { + visitedSectors.Add(sectorNum); + } + + r.NextBytes(gcData); + + data = Read(diskImp, (ulong)sectorNum, data); + for (int byteNum = 0; byteNum < data.Length; byteNum++) { + if (data[byteNum] != gcData[byteNum]) { + throw new Exception( + String.Format("Data did not match after write-read sequence: at sector {0}, byte {1}, got {2}, expected {3}", + sectorNum, byteNum, data[byteNum], gcData[byteNum])); + } + } + } + } + finally { + delete data; + } + } + + private static RamDiskControlContract.Imp! ConnectToRamDiskClientManager() + { + DirectoryServiceContract.Imp! dsExp = + DirectoryService.NewClientEndpoint(); + + RamDiskControlContract.Imp! fccImp; + RamDiskControlContract.Exp! fccExp; + RamDiskControlContract.NewChannel(out fccImp, out fccExp); + try { + dsExp.SendBind( + Bitter.FromString2( + RamDiskControlContract.ManagerControlPath + ), + fccExp); + + switch receive { + case dsExp.AckBind(): + switch receive { + case fccImp.Success(): + return fccImp; + case fccImp.ChannelClosed(): + throw new Exception("RamDisk client manager closed channel prematurely"); + } + // unreached + case dsExp.NakBind(exp, errorCode): + delete exp; + delete fccImp; + throw new Exception("RamDisk client manager rejected bind"); + + case dsExp.ChannelClosed(): + delete fccImp; + throw new Exception("RamDisk client manager closed channel prematurely"); + } + } + finally { + delete dsExp; + } + } + + private static void OutOfMemoryTest(RamDiskControlContract.Imp! controller) + { + controller.SendCreate(1000000000000L); + + switch receive { + case controller.CreateSuccess(diskPath): + delete diskPath; + throw new Exception("Creation of very large disk succeeded where expected to fail with out of memory"); + case controller.Fail(error): + if (error != RamDiskContractErrorCode.OutOfMemory) { + throw new Exception("Creation of very large disk failed with unexpected error code " + error + ", expected to fail with out of memory"); + } + return; + case controller.ChannelClosed(): + throw new Exception("RamDisk client manager closed channel prematurely"); + } + } + + private static string! CreateTest(RamDiskControlContract.Imp! controller, + ulong diskSizeBytes) + { + controller.SendCreate(diskSizeBytes); + + switch receive { + case controller.CreateSuccess(diskPath): + string result = Bitter.ToString2(diskPath); + delete diskPath; + return result; + case controller.Fail(error): + throw new Exception("Create disk failed with error code " + error); + case controller.ChannelClosed(): + throw new Exception("RamDisk client manager closed channel prematurely"); + } + } + + private static DiskDeviceContract.Imp:Ready! + OpenDevice(DirectoryServiceContract.Imp:Ready! nsImp, + string! deviceName) + { + DiskDeviceContract.Exp! diskExp; + DiskDeviceContract.Imp! diskImp; + DiskDeviceContract.NewChannel(out diskImp, out diskExp); + + nsImp.SendBind(Bitter.FromString2(deviceName), diskExp); + switch receive { + case nsImp.AckBind(): + switch receive { + case diskImp.Success(): + return diskImp; + case diskImp.ContractNotSupported(): + delete diskImp; + throw new Exception("RamDisk does not support disk contract"); + case diskImp.ChannelClosed(): + delete diskImp; + throw new Exception("RamDisk closed channel prematurely"); + } + // unreached + case nsImp.NakBind(badExp, errorCode): + delete diskImp; + delete badExp; + throw new Exception("Failed to bind disk '" + deviceName + "' with error code " + errorCode); + case nsImp.ChannelClosed(): + delete diskImp; + throw new Exception("Directory Service closed channel prematurely"); + } + } + + private static void EnsureDoesNotExist(DirectoryServiceContract.Imp:Ready! nsImp, + string! fileName) + { + DiskDeviceContract.Exp! fileExp; + DiskDeviceContract.Imp! fileImp; + DiskDeviceContract.NewChannel(out fileImp, out fileExp); + + nsImp.SendBind(Bitter.FromString2(fileName), fileExp); + switch receive { + case nsImp.AckBind(): + throw new Exception("File expected to be missing exists"); + case nsImp.NakBind(badExp, errorCode): + delete fileImp; + delete badExp; + if (errorCode != ErrorCode.NotFound) { + throw new Exception("Failed to bind file '" + fileName + "' expected to be missing with unexpected error code " + errorCode); + } + return; + case nsImp.ChannelClosed(): + delete fileImp; + throw new Exception("Directory Service closed channel prematurely"); + } + } + + private static byte[]! in ExHeap Read(DiskDeviceContract.Imp:Ready! imp, + ulong sectorId, + [Claims] byte[]! in ExHeap data) + + { + ulong dataLength = (ulong)data.Length; + imp.SendRead(data, 0, dataLength, sectorId); + switch receive { + case imp.AckRead(outBuffer): + return outBuffer; + case imp.NakRead(): + throw new Exception("RamDisk rejected read operation"); + case imp.ChannelClosed(): + throw new Exception("RamDisk closed channel prematurely"); + } + } + + private static byte[]! in ExHeap Write(DiskDeviceContract.Imp:Ready! imp, + ulong sectorId, + [Claims] byte[]! in ExHeap data) + { + imp.SendWrite(data, 0, (ulong)data.Length, sectorId); + switch receive { + case imp.AckWrite(outBuffer): + return outBuffer; + case imp.NakWrite(): + throw new Exception("RamDisk rejected write operation"); + case imp.ChannelClosed(): + throw new Exception("RamDisk closed channel prematurely"); + } + } + + private static void EnsureDestroyFailsWithInUse(RamDiskControlContract.Imp! controller, + string! diskPath) + { + char[] in ExHeap diskPathExHeap = Bitter.FromString2(diskPath); + controller.SendDestroy(diskPathExHeap, false/*force*/); + + switch receive { + case controller.Success(): + throw new Exception("Destroy disk succeeded where expected to fail with in-use error"); + case controller.Fail(errorCode): + if (errorCode != RamDiskContractErrorCode.IsInUse) { + throw new Exception("Destroy disk failed with unexpected error code " + errorCode + ", expected in-use"); + } + return; + case controller.ChannelClosed(): + throw new Exception("RamDisk client manager closed channel prematurely"); + } + } + + private static void Destroy(RamDiskControlContract.Imp! controller, + string! diskPath, + bool force) + { + char[] in ExHeap diskPathExHeap = Bitter.FromString2(diskPath); + controller.SendDestroy(diskPathExHeap, force); + + switch receive { + case controller.Success(): + return; + case controller.Fail(error): + throw new Exception("Destroy disk failed with error code " + error); + case controller.ChannelClosed(): + throw new Exception("RamDisk client manager closed channel prematurely"); + } + } + } +} + + diff --git a/base/Applications/Tests/RamDiskTest/RamDiskTest.csproj b/base/Applications/Tests/RamDiskTest/RamDiskTest.csproj new file mode 100644 index 0000000..3cb3631 --- /dev/null +++ b/base/Applications/Tests/RamDiskTest/RamDiskTest.csproj @@ -0,0 +1,33 @@ + + + + + + + RamDiskTest + Exe + + true + true + + + + + + + + + + + + diff --git a/base/Applications/Tests/Recursion/Recursion.cs b/base/Applications/Tests/Recursion/Recursion.cs index a899560..ce8b466 100644 --- a/base/Applications/Tests/Recursion/Recursion.cs +++ b/base/Applications/Tests/Recursion/Recursion.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Recursion.cs -// // Note: Simple Singularity test program. // using System; @@ -19,7 +17,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] diff --git a/base/Applications/Tests/SDSTest/SDSTest.csproj b/base/Applications/Tests/SDSTest/SDSTest.csproj deleted file mode 100644 index 469edf6..0000000 --- a/base/Applications/Tests/SDSTest/SDSTest.csproj +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - SDSTest - Exe - true - true - - - - - - - - - - - - - diff --git a/base/Applications/Tests/SDSTest/SDSTest.sg b/base/Applications/Tests/SDSTest/SDSTest.sg index f9c0355..ac3061b 100644 --- a/base/Applications/Tests/SDSTest/SDSTest.sg +++ b/base/Applications/Tests/SDSTest/SDSTest.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: SDSTest.sg -// // Note: // @@ -73,7 +71,7 @@ namespace Microsoft.Singularity.Applications public int failCount; private bool nakLoaded; private bool dspLoaded; - private bool doDebug; + private bool doDebug; public DirectoryServiceTests (bool nakLoaded, bool dspLoaded, bool doDebug) { @@ -97,8 +95,8 @@ namespace Microsoft.Singularity.Applications SdsUtils.ErrorCodeToString(actual), ok ? "true" : "false" ); - Console.WriteLine(s); - if (doDebug) DebugStub.WriteLine(s); + Console.WriteLine(s); + if (doDebug) DebugStub.WriteLine(s); //DebugStub.Break(); } else { @@ -109,8 +107,8 @@ namespace Microsoft.Singularity.Applications SdsUtils.ErrorCodeToString(expected), ok ? "true" : "false" ); - Console.WriteLine(s); - if (doDebug) DebugStub.WriteLine(s); + Console.WriteLine(s); + if (doDebug) DebugStub.WriteLine(s); } } @@ -168,7 +166,7 @@ namespace Microsoft.Singularity.Applications bool ok = SdsUtils.GetLinkValue(path, ds, out linkValue, out errorOut); CheckError("GetLinkValue", path, expected, errorOut, ok); if (ok) { - if (expectedValue != linkValue){ + if (expectedValue != linkValue) { Console.WriteLine(" expected({0}) got({1})", expectedValue, linkValue); } } @@ -239,6 +237,8 @@ namespace Microsoft.Singularity.Applications CheckError("GetAttributes", path, expected, errorOut, ok); } + private readonly string testpeExecutableName = "/init/testpe/testpe" + DirectoryService.ExecutableExtension; + public void AttributesTests(DirectoryServiceContract.Imp! ds) { DoCreateDirectory(ds, "/d1", ErrorCode.NoError); @@ -248,8 +248,8 @@ namespace Microsoft.Singularity.Applications DoGetAttributes(ds, "/d1", ErrorCode.NoError, NodeType.Directory); DoGetAttributes(ds, "/d1/Link1", ErrorCode.NoError, NodeType.SymLink); - DoGetAttributes(ds, "/init/testpe/testpe.x86", ErrorCode.NoError, NodeType.IoMemory); - DoGetAttributes(ds, "/stress", ErrorCode.NoError, NodeType.ServiceProvider); + DoGetAttributes(ds, testpeExecutableName, ErrorCode.NoError, NodeType.IoMemory); + DoGetAttributes(ds, "/service/stress", ErrorCode.NoError, NodeType.ServiceProvider); DoGetAttributes(ds, "/d1/Link1/d2", ErrorCode.NoError, NodeType.Directory); DoGetAttributes(ds, "/d1/garbage", ErrorCode.NotFound, NodeType.BadNode); @@ -339,7 +339,7 @@ namespace Microsoft.Singularity.Applications // Attempt to delete non directories //service provider - DoDeleteDirectory(ds, "/stress", ErrorCode.NotDirectory); + DoDeleteDirectory(ds, "/service/stress", ErrorCode.NotDirectory); //link DoDeleteDirectory(ds, "/d2/Link1", ErrorCode.NotDirectory); //IoMemory @@ -404,20 +404,21 @@ namespace Microsoft.Singularity.Applications Dummy.Exp! dS; FileContract.NewChannel(out fc, out fs); - DoBind(ds,"/init/testpe/testpe.x86",fs,ErrorCode.NoError); + DoBind(ds,testpeExecutableName,fs,ErrorCode.NoError); delete fc; Dummy.NewChannel(out dC, out dS); - DoBind(ds,"/init/testpe/testpe.x86",dS,ErrorCode.ContractNotSupported); + DoBind(ds,testpeExecutableName,dS,ErrorCode.ContractNotSupported); delete dC; DirectoryServiceContract.NewChannel(out imp , out exp); - DoBind(ds,"/init/testpe/testpe.x86",exp,ErrorCode.ContractNotSupported); + DoBind(ds,testpeExecutableName,exp,ErrorCode.ContractNotSupported); delete imp; DoCreateLink(ds, "/link", "/init", ErrorCode.NoError); FileContract.NewChannel(out fc, out fs); - DoBind(ds,"/link/testpe/testpe.x86",fs,ErrorCode.NoError); + DoBind(ds,"/link/testpe/testpe"+DirectoryService.ExecutableExtension, + fs,ErrorCode.NoError); delete fc; DoDeleteLink(ds, "/link", ErrorCode.NoError); @@ -428,7 +429,7 @@ namespace Microsoft.Singularity.Applications FileContract.NewChannel(out fc, out fs); //bind to ServiceProvider with File - DoBind(ds,"/FsCtrl",fs,ErrorCode.ContractNotSupported); + DoBind(ds,"/service/fs",fs,ErrorCode.ContractNotSupported); delete fc; FileContract.NewChannel(out fc, out fs); @@ -495,10 +496,10 @@ namespace Microsoft.Singularity.Applications Process dsp = null; ErrorCode errorOut; - if (config.nsRef == null) { - throw new Exception("Unable to acquire handle to the Directory Service root"); - } - DirectoryServiceContract.Imp ds = config.nsRef.Acquire(); + if (config.nsRef == null) { + throw new Exception("Unable to acquire handle to the Directory Service root"); + } + DirectoryServiceContract.Imp ds = config.nsRef.Acquire(); ds.RecvSuccess(); @@ -507,7 +508,7 @@ namespace Microsoft.Singularity.Applications bool ok; /// - /// FIXFIX SdsUtils FlushCache needs to be re-implemented + /// TODO: FIXFIX SdsUtils FlushCache needs to be re-implemented /// in order for us to be able to wait for NakService termination /// @@ -515,16 +516,16 @@ namespace Microsoft.Singularity.Applications DirectoryServiceContract.Exp! dspExp; DirectoryServiceContract.NewChannel(out dspImp, out dspExp); - string dspMountPoint = config.mountPoint; + string dspMountPoint = (!)config.mountPoint; ok = SdsUtils.Bind(dspMountPoint, ds, dspExp, out errorOut); delete dspImp; bool dspLoaded = false; if (!ok) { - Console.WriteLine(" unable to bind to {0}. Reason={1}", + Console.WriteLine(" unable to bind to {0}. Reason={1}", dspMountPoint, SdsUtils.ErrorCodeToString(errorOut)); delete ds; - return 1; + return 1; } else dspLoaded = true; diff --git a/base/Applications/Tests/Scheduler/RMAPI/RMAPI.cs b/base/Applications/Tests/Scheduler/RMAPI/RMAPI.cs new file mode 100644 index 0000000..d8f87da --- /dev/null +++ b/base/Applications/Tests/Scheduler/RMAPI/RMAPI.cs @@ -0,0 +1,123 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Threading; + +using Microsoft.Contracts; +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.UnitTest; +using Microsoft.Singularity.V1.Services; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Configuration; +[assembly: Transform(typeof(ApplicationResourceTransform))] + +namespace Microsoft.Singularity.Applications +{ + [ConsoleCategory(HelpMessage="Utility to test Resource Management APIs. Run selected tests.", + DefaultAction=true)] + internal class Parameters { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [Endpoint] + public readonly TRef nsRef; + + [BoolParameter("ProcessWithOutput", Default = false, + HelpMessage="Run process creation test with children output to console.")] + public bool runProcessWithOutput; + + [BoolParameter("ProcessNoOutputLoop", Default = false, + HelpMessage="Run process creation test where children don't output to console. Run in infinit loop")] + public bool runProcessWithOutputLoop; + + [BoolParameter("ProcessNoOutput", Default = false, + HelpMessage="Run process creation test where children don't output to console.")] + public bool runProcessNoOutput; + + reflective internal Parameters(); + + internal int AppMain() { + TestsToRun testsToRun = TestsToRun.None; + if (runProcessWithOutput) { + testsToRun |= TestsToRun.ProcessWithOutput; + } + if (runProcessNoOutput) { + testsToRun |= TestsToRun.ProcessNoOutput; + } + if (runProcessWithOutputLoop) { + testsToRun |= TestsToRun.ProcessNoOutputLoop; + } + return RMAPI.AppMain(testsToRun); + } + } + + [ConsoleCategory(HelpMessage="Utility to test Resource Management APIs. Run all tests.", + Action="all")] + internal class AllTestsParameters { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [Endpoint] + public readonly TRef nsRef; + + reflective internal AllTestsParameters(); + + internal int AppMain() { + return RMAPI.AppMain(TestsToRun.All); + } + } + + [Flags] + internal enum TestsToRun + { + None = 0x0, + ProcessWithOutput = 0x1, + ProcessNoOutput = 0x2, + ProcessNoOutputLoop = 0x4, + All = 0xF, + } + + internal sealed class RMAPI + { + internal static int AppMain(TestsToRun testsToRun) + { + // Run test cases where the child processes don't output to the console + if ((testsToRun & TestsToRun.ProcessNoOutput) != 0) { + TestProcessCreation.RunWithNoChildOutput(); + } + + // Run test cases where the child processes output to the console + if ((testsToRun & TestsToRun.ProcessWithOutput) != 0) { + TestProcessCreation.RunWithChildOutput(); + } + + // Run test cases where the child processes don't output to the console + if ((testsToRun & TestsToRun.ProcessNoOutputLoop) != 0) { + for (int i = 0; ; i++) { + Console.Write(i); + Console.Write('.'); + TestProcessCreation.RunWithNoChildOutput(); + Thread.Sleep(1000); + } + } + + return 0; + } + } +} + + diff --git a/base/Applications/Tests/Scheduler/RMAPI/RMAPI.csproj b/base/Applications/Tests/Scheduler/RMAPI/RMAPI.csproj new file mode 100644 index 0000000..1ec6353 --- /dev/null +++ b/base/Applications/Tests/Scheduler/RMAPI/RMAPI.csproj @@ -0,0 +1,40 @@ + + + + + + + RMAPITest + Exe + true + true + + + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/Tests/Scheduler/RMAPI/TestProcessCreation.cs b/base/Applications/Tests/Scheduler/RMAPI/TestProcessCreation.cs new file mode 100644 index 0000000..8301bb4 --- /dev/null +++ b/base/Applications/Tests/Scheduler/RMAPI/TestProcessCreation.cs @@ -0,0 +1,366 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Diagnostics; +using System.Collections; +using System.Threading; +using Microsoft.SingSharp; +using Microsoft.Singularity.UnitTest; +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Channels; + +namespace Microsoft.Singularity.Applications +{ + /// + /// Test cases for creating processes in parallel. + /// + internal sealed class TestProcessCreation + { + private const int numThreads = 16; + private const int iterations = 16; + private const string commandLine = "fib"; + private static int idGenerator; + private static ManualResetEvent evtGate; + + /// + /// Run test to create processes. The child processes don't output + /// to the console. Instead, we swallow their output with a null pipe. + /// + internal static void RunWithNoChildOutput() + { + Console.Write(" test parallel process creation with no child output... "); + + evtGate = new ManualResetEvent(false); + + // Start the worker threads + Thread[] threads = new Thread[numThreads]; + for (int i = 0; i < threads.Length; i++) { + threads[i] = new Thread(new ThreadStart(ProcessCreationMainWithNoOutput)); + ((!)threads[i]).Start(); + Thread.Yield(); + } + + // Give the worker threads a bit time to initialize + // then signal the start + Thread.Sleep(1000); + evtGate.Set(); + + // Wait for all worker threads to finish + for (int i = 0; i < threads.Length; i++) { + ((!)threads[i]).Join(); + } + + // If we didn't crash, then this test is successful. + Console.WriteLine("OK"); + } + + /// + /// Run test to create processes in parallel. The child + /// processes output to the console through PipeMultiplexer. + /// + internal static void RunWithChildOutput() + { + Console.Write(" test parallel process creation with child output... "); + + evtGate = new ManualResetEvent(false); + + // Start the worker threads + WorkerThread[] threads = new WorkerThread[numThreads]; + for (int i = 0; i < threads.Length; i++) { + PipeMultiplexer! mux = CreatePipeMultiplexer(); + WorkerThread worker = new WorkerThread(mux); + worker.thread = new Thread(worker.ThreadRoutine); + threads[i] = worker; + } + + // We want to create all of the muxes first, then create + // all of the threads, then start them. + + foreach (WorkerThread! worker in threads) { + worker.thread.Start(); + Thread.Yield(); + } + + // Give the worker threads a bit time to initialize + // then signal the start + Thread.Sleep(1000); + evtGate.Set(); + + // Wait for all worker threads to finish + foreach (WorkerThread! worker in threads) { + worker.thread.Join(); + } + + foreach (WorkerThread! worker in threads) { + PipeMultiplexer! mux = worker.muxRef.Acquire(); + delete mux; + } + + // If we didn't crash, then this test is successful. + Console.WriteLine(); + Console.WriteLine("OK"); + } + + /// + /// Thread main function that creates processes. The child + /// processes don't output to the console. Instead, we swallow + /// their output with a null pipe. + /// + private static void ProcessCreationMainWithNoOutput() + { + int id = Interlocked.Increment(ref idGenerator) - 1; + + NullPipeControl.Imp:START! control = StartNullPipe(); + DirectoryServiceContract.Imp! directoryService = + DirectoryService.NewClientEndpoint(); + + // Wait for the signal + evtGate.WaitOne(); + + // Create many processes + Process[] children = new Process[iterations]; + try { + for (int i = 0; i < iterations; i++) { + UnicodePipeContract.Imp! childStdInImp; + UnicodePipeContract.Exp! childStdInExp; + UnicodePipeContract.NewChannel(out childStdInImp, out childStdInExp); + + UnicodePipeContract.Imp! childStdOutImp = CreateNullPipeClient(control); + + //Manifest manifest; + children[i] = Binder.CreateProcess(directoryService, + new string[1]{commandLine}, childStdInExp, childStdOutImp); + delete childStdInImp; + if (children[i] == null) { + throw new ProcessCreateException("Failed to create process"); + } + + ((!)children[i]).Start(); + if (id == 0) { + Console.Write('.'); + } + } + } + finally { + delete directoryService; + delete control; + } + + // Wait for all children to finish + for (int i = 0; i < children.Length; i++) { + ((!)children[i]).Join(); + } + } + + private contract NullPipeControl + { + in message NewInput(UnicodePipeContract.Exp:EXPMOVABLE! client); + out message Ack(); + + state START: NewInput? -> Ack! -> START; + } + + private static UnicodePipeContract.Imp:READY! CreateNullPipeClient( + NullPipeControl.Imp:START! control) + { + UnicodePipeContract.Imp! client; + UnicodePipeContract.Exp! exp; + UnicodePipeContract.NewChannel(out client, out exp, + UnicodePipeContract.EXPMOVABLE.Value); + control.SendNewInput(exp); + switch receive { + case control.Ack(): + switch receive { + case client.Moved(): + return client; + + case client.ChannelClosed(): + throw new ProcessCreateException( + "Failed to create null pipe: client receve"); + } + + case control.ChannelClosed(): + throw new ProcessCreateException( + "Failed to create null pipe: control receive"); + } + } + + private static NullPipeControl.Imp:START! StartNullPipe() + { + NullPipeControl.Imp! controlImp; + NullPipeControl.Exp! controlExp; + NullPipeControl.NewChannel(out controlImp, out controlExp); + + NullPipeThread pipeThread = new NullPipeThread(controlExp); + + Thread t = new Thread(new ThreadStart(pipeThread.PumpMessage)); + t.Start(); + return controlImp; + } + + private class NullPipeThread + { + TRef! controlExp; + + internal NullPipeThread([Claims] NullPipeControl.Exp:START! control) + { + this.controlExp = new TRef(control); + } + + internal void PumpMessage() + { + NullPipeControl.Exp:START! control = this.controlExp.Acquire(); + ESet clients = + new ESet(); + + bool done = false; + + try { + while (!done) { + switch receive { + + case control.NewInput(client): + client.SendMoved(); + clients.Add(client); + control.SendAck(); + break; + + case control.ChannelClosed(): + done = true; + break; + + case client.Write(buffer, index, count) in clients: + client.SendAckWrite(buffer); + clients.Add(client); + continue; + + case client.ChannelClosed() in clients: + delete client; + continue; + } + } + } + finally { + clients.Dispose(); + delete control; + } + } + } + + private class WorkerThread + { + public WorkerThread([Claims]PipeMultiplexer! mux) + { + this.muxRef = new TContainer(mux); + } + + public TContainer! muxRef; + + public Thread thread; + + public void ThreadRoutine() + { + int totalProcessesCreated = 0; + int goalCountProcessesCreated = 0x100; + int maxActiveProcesses = 0x10; + + ArrayList processes = new ArrayList(); + + PipeMultiplexer! mux = muxRef.Acquire(); + + DirectoryServiceContract.Imp! rootdir = DirectoryService.NewClientEndpoint(); + + try { + + for (;;) { + Thread.Yield(); + + // scan child processes and remove dead ones + int i = 0; + while (i < processes.Count) { + Process! process = (Process!)processes[i]; + if (process.Join(TimeSpan.Zero)) { + // oooo, it's dead. + // Debug.WriteLine(String.Format("yay, process {0} died", process.Id)); + process.Dispose(true); + processes.RemoveAt(i); + } + else { + // process not dead yet + i++; + } + } + + + bool canCreateProcess = (processes.Count < maxActiveProcesses + && totalProcessesCreated < goalCountProcessesCreated); + + if (canCreateProcess) { + string[] childArgs = new string[1]{ commandLine }; + Process process = Binder.CreateProcess(rootdir, childArgs, mux); + if (process == null) { + Debug.WriteLine("FAILED to create child process."); + Debugger.Break(); + break; + } + processes.Add(process); + process.Start(); + // Debug.WriteLine(String.Format("created child process (id {0}), this is {1}/{2}", process.Id, totalProcessesCreated, goalCountProcessesCreated)); + totalProcessesCreated++; + } + + if (!canCreateProcess) { + // Debug.WriteLine("snore"); + Thread.Sleep(250); + } + + if (processes.Count == 0 && totalProcessesCreated == goalCountProcessesCreated) { + Debug.WriteLine("goal has been created. worker thread terminating."); + break; + } + } + + foreach (Process! process in processes) { + Debug.WriteLine("Terminating child process: " + process.Id); + process.Stop(); + } + foreach (Process! process in processes) { + Debug.WriteLine("Waiting for child process: " + process.Id); + process.Join(); + process.Dispose(true); + } + processes.Clear(); + } + finally { + delete rootdir; + muxRef.Release(mux); + } + } + } + + // Redirect our standard output into a multiplexer so we can interleave + // output from child processes + private static PipeMultiplexer! CreatePipeMultiplexer() + { + // Swap our real stdOut with a newly created one + UnicodePipeContract.Exp! newOutputExp; + UnicodePipeContract.Imp! newOutputImp; + UnicodePipeContract.NewChannel(out newOutputImp, out newOutputExp); + UnicodePipeContract.Imp stdOut = ConsoleOutput.Swap(newOutputImp); + if (stdOut == null) { + throw new Exception("test expects a STDOUT pipe"); + } + // Use a mux to splice our own output together with the child + // processes we will run. + return PipeMultiplexer.Start(stdOut, newOutputExp); + } + + } +} diff --git a/base/Applications/Tests/Scheduler/Threading/TestAutoResetEvent.cs b/base/Applications/Tests/Scheduler/Threading/TestAutoResetEvent.cs new file mode 100644 index 0000000..3d0d18b --- /dev/null +++ b/base/Applications/Tests/Scheduler/Threading/TestAutoResetEvent.cs @@ -0,0 +1,504 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Threading; +using Microsoft.Singularity.UnitTest; + +namespace Microsoft.Singularity.Applications +{ + /// + /// Test cases for auto reset event class + /// + internal sealed class TestAutoResetEvent + { + internal static TestLog Expect; + private const int MaxWaiters = 16; + private static AutoResetEvent evt, evt1, evt2, evt3, evt4; + private static ManualResetEvent evtGate; + private static readonly TimeSpan trivialTestWaitTime = TimeSpan.FromMilliseconds(100); + private static int counter; + private static int readyCount; + + internal static void Run() + { + TestTrivialReset(); + TestTrivialSet(); + TestThreadSet(); + TestThreadReset(); + TestMultipleWaitOne(); + TestSetAll(); + TestDeadlock(); + + ///TODO: harden kernel code so it doesn't crash on this test, then enable the test + //TestDispose(); + } + + /// + /// After the event is reset, test to make sure WaitOne timeout in the same thread + /// + private static void TestTrivialReset() + { + Console.Write(" test trivial reset "); + + // + // reset by constructor + // + evt = new AutoResetEvent(false); + Console.Write('.'); + bool ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout"); + return; + } + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout again"); + return; + } + evt.Close(); + + // + // reset by constructor and Reset() + // + evt = new AutoResetEvent(false); + evt.Reset(); + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout"); + return; + } + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout again"); + return; + } + evt.Close(); + + // + // set by constructor and then explicitly Reset() + // + evt = new AutoResetEvent(true); + evt.Reset(); + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout"); + return; + } + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout again"); + return; + } + evt.Close(); + + // + // Set() and Reset() several times + // + evt = new AutoResetEvent(true); + evt.Set(); + evt.Reset(); + evt.Set(); + evt.Reset(); + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout"); + return; + } + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout again"); + return; + } + evt.Close(); + + evt = null; + Console.WriteLine("OK"); + } + + /// + /// After the event is set, test to make sure WaitOne succeed in the same thread + /// + private static void TestTrivialSet() + { + Console.Write(" test Trivial set "); + + // + // set by constructor + // + evt = new AutoResetEvent(true); + Console.Write('.'); + bool ret = evt.WaitOne(trivialTestWaitTime); + if (!ret) { + Expect.Fail("wait should succeed"); + return; + } + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout"); + return; + } + evt.Close(); + + // + // set by constructor then explicitly Set() + // + evt = new AutoResetEvent(true); + evt.Set(); + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (!ret) { + Expect.Fail("wait should succeed"); + return; + } + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout"); + return; + } + evt.Close(); + + // + // reset by constructor then explicitly Set() + // + evt = new AutoResetEvent(false); + evt.Set(); + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (!ret) { + Expect.Fail("wait should succeed"); + return; + } + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout"); + return; + } + evt.Close(); + + // + // reset and set several times + // + evt = new AutoResetEvent(false); + evt.Set(); + evt.Reset(); + evt.Set(); + evt.Reset(); + evt.Set(); + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (!ret) { + Expect.Fail("wait should succeed"); + return; + } + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout"); + return; + } + evt.Close(); + + evt = null; + Console.WriteLine("OK"); + } + + /// + /// Set the event, and then WaitOne from different thread. The wait should succeed. + /// + private static void TestThreadSet() + { + Console.Write(" test thread set "); + evt = new AutoResetEvent(true); + + Thread t = new Thread(new ThreadStart(WaiterMain)); + t.Start(); + t.Join(); + + Console.Write('.'); + bool ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout"); + return; + } + + evt.Close(); + evt = null; + Console.WriteLine("OK"); + } + + /// + /// Reset the event and then WaitOne from different thread. The wait should timeout + /// + private static void TestThreadReset() + { + Console.Write(" test thread reset "); + evt = new AutoResetEvent(false); + counter = 0; + + Thread t = new Thread(new ThreadStart(NoSignalWaiterMain)); + t.Start(); + Console.Write('.'); + t.Join(); + + // wait should timeout and therefore counter should be 1 + if (Thread.VolatileRead(ref counter) != 1) { + Expect.Fail("wait should timeout"); + return; + } + + Console.Write('.'); + evt.Set(); + evt.WaitOne(); + + evt.Close(); + evt = null; + Console.WriteLine("OK"); + } + + /// + /// multiple threads WaitOne on a reset auto reset event and master thread + /// calls SetAll. all waiters should be unblocked by it. + /// + private static void TestSetAll() + { + Console.Write(" test set all "); + readyCount = 0; + counter = 0; + + // + // multiple threads WaitOne on a reset auto reset event and master thread + // calls SetAll. all waiters should be unblocked by it. + // + Console.Write('.'); + evt = new AutoResetEvent(false); + Thread[] threads = new Thread[MaxWaiters]; + for (int i = 0; i < threads.Length; i++) { + threads[i] = new Thread(new ThreadStart(WaiterMain)); + ((!)threads[i]).Start(); + Thread.Yield(); + } + + // wait for all threads to be ready + for (int t = 0; t < 50; t++) { + if (Thread.VolatileRead(ref readyCount) == MaxWaiters) { + break; + } + Thread.Sleep(100); + } + evt.SetAll(); + + for (int i = 0; i < threads.Length; i++) { + ((!)threads[i]).Join(); + } + if (Thread.VolatileRead(ref counter) != MaxWaiters) { + Expect.Fail("unexpected counter"); + return; + } + evt.Close(); + + // + // now multiple threads wait on the same event after SetAll, they should + // all timeout except one + // + Console.Write('.'); + counter = 0; + evt = new AutoResetEvent(false); + evt.SetAll(); + threads = new Thread[MaxWaiters]; + for (int i = 0; i < threads.Length; i++) { + threads[i] = new Thread(new ThreadStart(NoSignalWaiterMain)); + ((!)threads[i]).Start(); + Thread.Yield(); + } + for (int i = 0; i < threads.Length; i++) { + ((!)threads[i]).Join(); + } + + // all waiter except one should timeout, therefore counter should be + // number of waiters minus one + if (Thread.VolatileRead(ref counter) != (MaxWaiters - 1)) { + Expect.Fail("unexpected counter"); + return; + } + evt.Close(); + + Console.WriteLine("OK"); + } + + /// + /// test multiple threads WaitOne() on the same event while the master + /// thread call Set one at a time. + /// + private static void TestMultipleWaitOne() + { + Console.Write(" test multiple wait one "); + evt = new AutoResetEvent(true); + + for (int n = 1; n < MaxWaiters; n++) { + // reset counter + counter = 0; + int previousCounter = 0; + int numSet = 0; + bool timeout = true; + evt.Set(); + + for (int i = 0; i < n; i++) { + new Thread(new ThreadStart(WaiterMain)).Start(); + } + + // Call Set() one at a time after observing a change in the counter + for (int t = 0; t < 50; t++) { + int currentCounter = Thread.VolatileRead(ref counter); + if (currentCounter == n) { + if (numSet != n - 1) { + Expect.Fail(string.Format( + "unexpected number of Set() with {0} threads", n)); + return; + } + timeout = false; + Console.Write('.'); + break; + } + else if (previousCounter != currentCounter) { + previousCounter = currentCounter; + evt.Set(); + numSet++; + Thread.Yield(); + } + else { + Thread.Sleep(100); + } + } + + if (timeout) { + Expect.Fail(string.Format("timed out with {0} threads", n)); + return; + } + } + + evt.Close(); + evt = null; + Console.WriteLine("OK"); + } + + /// + /// Test dead lock situation where two threads each waits the other to set an event + /// + private static void TestDeadlock() + { + Console.Write(" test deadlock ..."); + evtGate = new ManualResetEvent(false); + evt3 = new AutoResetEvent(false); + evt4 = new AutoResetEvent(false); + counter = 0; + Thread t1 = new Thread(new ThreadStart(DeadlockMain1)); + Thread t2 = new Thread(new ThreadStart(DeadlockMain2)); + t1.Start(); + t2.Start(); + + evt3.WaitOne(); + evt4.WaitOne(); + evtGate.Set(); + + t1.Join(); + t2.Join(); + + // wait in both thread should timeout, therefore the counter should be 2 + if (Thread.VolatileRead(ref counter) != 2) { + Expect.Fail("expecting deadlock"); + return; + } + + Console.WriteLine("OK"); + } + + /// + /// Test the scenario where we dispose an event object then wait on it. + /// + private static void TestDispose() + { + Console.Write(" test dispose "); + evt = new AutoResetEvent(false); + evt.Close(); + try { + evt.WaitOne(); + } + catch (Exception ex) { + Console.WriteLine(ex.ToString()); + return; + } + Expect.Fail("expecting exception on disposed object"); + } + + /// + /// Thread entry point to test waiting on an event. + /// increment the counter only if the wait timeout + /// + private static void NoSignalWaiterMain() + { + bool ret = evt.WaitOne(trivialTestWaitTime); + if (!ret) { + // increment counter if the wait timeout + Interlocked.Increment(ref counter); + } + } + + /// + /// Thread entry point to test waiting on an event. + /// increment readyCount before wait begins and increment counter after wait succeeds + /// + private static void WaiterMain() + { + Interlocked.Increment(ref readyCount); + evt.WaitOne(); + Interlocked.Increment(ref counter); + } + + /// + /// Thread entry point to test dead lock + /// increment counter if wait timeout + /// + private static void DeadlockMain1() + { + evt1 = new AutoResetEvent(false); + evt3.Set(); + evtGate.WaitOne(); + bool ret = evt2.WaitOne(TimeSpan.FromSeconds(1)); + if (!ret) { + // increment counter if the wait timeout + Interlocked.Increment(ref counter); + } + } + + /// + /// Thread entry point to test dead lock + /// increment counter if wait timeout + /// + private static void DeadlockMain2() + { + evt2 = new AutoResetEvent(false); + evt4.Set(); + evtGate.WaitOne(); + bool ret = evt1.WaitOne(TimeSpan.FromSeconds(1)); + if (!ret) { + // increment counter if the wait timeout + Interlocked.Increment(ref counter); + } + } + } +} diff --git a/base/Applications/Tests/Scheduler/Threading/TestManualResetEvent.cs b/base/Applications/Tests/Scheduler/Threading/TestManualResetEvent.cs new file mode 100644 index 0000000..8792f9a --- /dev/null +++ b/base/Applications/Tests/Scheduler/Threading/TestManualResetEvent.cs @@ -0,0 +1,455 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Threading; +using Microsoft.Singularity.UnitTest; + +namespace Microsoft.Singularity.Applications +{ + /// + /// Test cases for manual reset event class + /// + internal sealed class TestManualResetEvent + { + internal static TestLog Expect; + private const int MaxWaiters = 16; + private static ManualResetEvent evt, evt1, evt2, evt3, evt4; + private static readonly TimeSpan trivialTestWaitTime = TimeSpan.FromMilliseconds(100); + private static int counter; + private static int readyCount; + + internal static void Run() + { + TestTrivialReset(); + TestTrivialSet(); + TestThreadSet(); + TestThreadReset(); + TestMultipleWaitOneWithInitialSet(); + TestMultipleWaitOneWithInitialReset(); + TestDeadlock(); + + ///TODO: harden kernel code so it doesn't crash on this test, then enable the test + //TestDispose(); + } + + /// + /// After the event is reset, test to make sure WaitOne timeout in the same thread + /// + private static void TestTrivialReset() + { + Console.Write(" test trivial reset"); + + // + // reset by constructor + // + evt = new ManualResetEvent(false); + Console.Write('.'); + bool ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout"); + return; + } + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout again"); + return; + } + evt.Close(); + + // + // reset by constructor and Reset() + // + evt = new ManualResetEvent(false); + evt.Reset(); + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout"); + return; + } + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout again"); + return; + } + evt.Close(); + + // + // set by constructor and then explicitly Reset() + // + evt = new ManualResetEvent(true); + evt.Reset(); + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout"); + return; + } + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout again"); + return; + } + evt.Close(); + + // + // Set() and Reset() several times + // + evt = new ManualResetEvent(true); + evt.Set(); + evt.Reset(); + evt.Set(); + evt.Reset(); + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout"); + return; + } + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout again"); + return; + } + evt.Close(); + + evt = null; + Console.WriteLine("OK"); + } + + /// + /// After the event is set, test to make sure WaitOne succeed in the same thread + /// + private static void TestTrivialSet() + { + Console.Write(" test Trivial set "); + + // + // set by constructor + // + evt = new ManualResetEvent(true); + Console.Write('.'); + bool ret = evt.WaitOne(trivialTestWaitTime); + if (!ret) { + Expect.Fail("wait should succeed"); + return; + } + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (!ret) { + Expect.Fail("wait should succeed again"); + return; + } + evt.Close(); + + // + // set by constructor then explicitly Set() + // + evt = new ManualResetEvent(true); + evt.Set(); + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (!ret) { + Expect.Fail("wait should succeed"); + return; + } + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (!ret) { + Expect.Fail("wait should succeed again"); + return; + } + evt.Close(); + + // + // reset by constructor then explicitly Set() + // + evt = new ManualResetEvent(false); + evt.Set(); + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (!ret) { + Expect.Fail("wait should succeed"); + return; + } + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (!ret) { + Expect.Fail("wait should succeed again"); + return; + } + evt.Close(); + + // + // reset and set several times + // + evt = new ManualResetEvent(false); + evt.Set(); + evt.Reset(); + evt.Set(); + evt.Reset(); + evt.Set(); + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (!ret) { + Expect.Fail("wait should succeed"); + return; + } + Console.Write('.'); + ret = evt.WaitOne(trivialTestWaitTime); + if (!ret) { + Expect.Fail("wait should succeed again"); + return; + } + evt.Close(); + + evt = null; + Console.WriteLine("OK"); + } + + /// + /// Set the event, and then WaitOne from different thread. The wait should succeed. + /// + private static void TestThreadSet() + { + Console.Write(" test thread set "); + evt = new ManualResetEvent(true); + + Thread t = new Thread(new ThreadStart(WaiterMain)); + t.Start(); + t.Join(); + + Console.Write('.'); + evt.WaitOne(); + evt.Reset(); + + Console.Write('.'); + bool ret = evt.WaitOne(trivialTestWaitTime); + if (ret) { + Expect.Fail("wait should timeout"); + return; + } + + evt.Close(); + evt = null; + Console.WriteLine("OK"); + } + + /// + /// Reset the event and then WaitOne from different thread. The wait should timeout + /// + private static void TestThreadReset() + { + Console.Write(" test thread reset "); + evt = new ManualResetEvent(false); + counter = 0; + + Thread t = new Thread(new ThreadStart(NoSignalWaiterMain)); + t.Start(); + Console.Write('.'); + t.Join(); + + // wait should timeout and therefore the counter should be 1 + if (Thread.VolatileRead(ref counter) != 1) { + Expect.Fail("wait should timeout"); + return; + } + + Console.Write('.'); + evt.Set(); + evt.WaitOne(); + + evt.Close(); + evt = null; + Console.WriteLine("OK"); + } + + /// + /// test multiple threads WaitOne() on the same event which is initially set + /// + private static void TestMultipleWaitOneWithInitialSet() + { + Console.Write(" test multiple wait one with initial set "); + evt = new ManualResetEvent(true); + + for (int n = 1; n < MaxWaiters; n++) { + Console.Write('.'); + Thread[] threads = new Thread[n]; + for (int i = 0; i < n; i++) { + threads[i] = new Thread(new ThreadStart(WaiterMain)); + ((!)threads[i]).Start(); + } + for (int i = 0; i < n; i++) { + ((!)threads[i]).Join(); + } + } + + evt.Close(); + evt = null; + Console.WriteLine("OK"); + } + + /// + /// test multiple threads WaitOne() on the same event which is initially reset + /// the master thread then + /// + private static void TestMultipleWaitOneWithInitialReset() + { + Console.Write(" test multiple wait one with initial reset "); + evt = new ManualResetEvent(false); + + for (int n = 1; n < MaxWaiters; n++) { + // reset counter + counter = 0; + readyCount = 0; + // reset event + evt.Reset(); + + Console.Write('.'); + Thread[] threads = new Thread[n]; + for (int i = 0; i < n; i++) { + threads[i] = new Thread(new ThreadStart(WaiterMain)); + ((!)threads[i]).Start(); + Thread.Yield(); + } + + // wait for all threads to be ready + while (true) { + if (Thread.VolatileRead(ref readyCount) == n) { + break; + } + Thread.Yield(); + } + + // make sure no wait leaking + Expect.Equal(Thread.VolatileRead(ref counter), 0, + "Detected event wait leaking!"); + + evt.Set(); + for (int i = 0; i < n; i++) { + ((!)threads[i]).Join(); + } + } + + evt.Close(); + evt = null; + Console.WriteLine("OK"); + } + + /// + /// Test dead lock situation where two threads each waits the other to set an event + /// + private static void TestDeadlock() + { + Console.Write(" test deadlock ..."); + evt = new ManualResetEvent(false); + evt3 = new ManualResetEvent(false); + evt4 = new ManualResetEvent(false); + counter = 0; + Thread t1 = new Thread(new ThreadStart(DeadlockMain1)); + Thread t2 = new Thread(new ThreadStart(DeadlockMain2)); + t1.Start(); + t2.Start(); + + evt3.WaitOne(); + evt4.WaitOne(); + evt.Set(); + + t1.Join(); + t2.Join(); + + // wait in both thread should timeout, therefore the counter should be 2 + if (Thread.VolatileRead(ref counter) != 2) { + Expect.Fail("expecting deadlock"); + return; + } + + evt.Close(); + evt = null; + Console.WriteLine("OK"); + } + + /// + /// Test the scenario where we dispose an event object then wait on it. + /// + private static void TestDispose() + { + Console.Write(" test dispose "); + evt = new ManualResetEvent(false); + evt.Close(); + try { + evt.WaitOne(); + } + catch (Exception ex) { + Console.WriteLine(ex.ToString()); + return; + } + Expect.Fail("expecting exception on disposed object"); + } + + /// + /// Thread entry point to test waiting on an event. + /// increment the counter only if the wait timeout + /// + private static void NoSignalWaiterMain() + { + bool ret = evt.WaitOne(trivialTestWaitTime); + if (!ret) { + // increment counter if the wait timeout + Interlocked.Increment(ref counter); + } + } + + /// + /// Thread entry point to test waiting on an event. + /// increment readyCount before wait begins and increment counter after wait succeeds + /// + private static void WaiterMain() + { + Interlocked.Increment(ref readyCount); + evt.WaitOne(); + Interlocked.Increment(ref counter); + } + + /// + /// Thread entry point to test dead lock + /// increment counter if wait timeout + /// + private static void DeadlockMain1() + { + evt1 = new ManualResetEvent(false); + evt3.Set(); + evt.WaitOne(); + bool ret = evt2.WaitOne(TimeSpan.FromSeconds(1)); + if (!ret) { + // increment counter if the wait timeout + Interlocked.Increment(ref counter); + } + } + + /// + /// Thread entry point to test dead lock + /// increment counter if wait timeout + /// + private static void DeadlockMain2() + { + evt2 = new ManualResetEvent(false); + evt4.Set(); + evt.WaitOne(); + bool ret = evt1.WaitOne(TimeSpan.FromSeconds(1)); + if (!ret) { + // increment counter if the wait timeout + Interlocked.Increment(ref counter); + } + } + } +} diff --git a/base/Applications/Tests/Scheduler/Threading/TestMutex.cs b/base/Applications/Tests/Scheduler/Threading/TestMutex.cs new file mode 100644 index 0000000..910770a --- /dev/null +++ b/base/Applications/Tests/Scheduler/Threading/TestMutex.cs @@ -0,0 +1,300 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Threading; +using Microsoft.Singularity.UnitTest; + +namespace Microsoft.Singularity.Applications +{ + internal sealed class TestMutex + { + internal static TestLog Expect; + private static readonly TimeSpan waitTimeout = TimeSpan.FromMilliseconds(100); + private static Mutex mutex; + private static ManualResetEvent evtGate; + private const int numRoundsInContention = 65536; + private static int counter; + private static volatile int sum; + + internal static void Run() + { + ///TODO: harden kernel code so it doesn't crash on this then enable the test + //TestIncorrectRelease(); + + TestCtor(); + + ///TODO: currently mutex supports recursion in kernel but not in applications, + /// add support then enable this test case + //TestRecursion(); + + TestContention(); + + // there are other mutex related test cases e.g. Tests\MonitorTest that provide more + // test coverage. + } + + /// + /// Test two threads both running busy loops grabbing and releasing the same mutex + /// + private static void TestContention() + { + Console.Write(" test contention "); + + mutex = new Mutex(); + evtGate = new ManualResetEvent(false); + Thread t1 = new Thread(new ThreadStart(ContentionThreadMain)); + Thread t2 = new Thread(new ThreadStart(ContentionThreadMain)); + t1.Start(); + Thread.Yield(); + t2.Start(); + Thread.Yield(); + + evtGate.Set(); + t1.Join(); + t2.Join(); + + Cleanup(); + Console.WriteLine("OK"); + } + + /// + /// Thread entry point for contention test. Run a tight loop which acquires the mutex, + /// does a little work, then releases it + /// + private static void ContentionThreadMain() + { + evtGate.WaitOne(); + for (int i = 0; i < numRoundsInContention; i++) { + mutex.AcquireMutex(); + Thread.Yield(); + for (int j = 0; j < 1000; j++) { + // sum is volatile so this won't be optimized away + sum = unchecked(sum + i * j * j / 23); + } + if (i % 10000 == 0) { + Console.Write('.'); + } + Thread.Yield(); + mutex.ReleaseMutex(); + Thread.Yield(); + } + } + + /// + /// Test the situation where we release the mutex without acquiring it first + /// + private static void TestIncorrectRelease() + { + Console.Write(" test incorrect release "); + mutex = new Mutex(); + try { + mutex.ReleaseMutex(); + } + catch (InvalidOperationException) { + Cleanup(); + Console.WriteLine("OK"); + return; + } + Expect.Fail("expecting exception"); + } + + /// + /// Test the constructor of Mutex + /// + private static void TestCtor() + { + Console.Write(" test constructor "); + + // + // default ctor: initially owned = false + // + Cleanup(); + Console.Write('.'); + mutex = new Mutex(); + Thread thread = new Thread(new ThreadStart(TimeoutWaitMain)); + thread.Start(); + thread.Join(); + if (Thread.VolatileRead(ref counter) != 1) { + Expect.Fail("expecting wait to succeed"); + return; + } + + // + // explicit ctor: initially owned = false + // + Cleanup(); + Console.Write('.'); + mutex = new Mutex(false); + thread = new Thread(new ThreadStart(TimeoutWaitMain)); + thread.Start(); + thread.Join(); + if (Thread.VolatileRead(ref counter) != 1) { + Expect.Fail("expecting wait to succeed"); + return; + } + + // + // explicit ctor: initially owned = true + // + Cleanup(); + Console.Write('.'); + mutex = new Mutex(true); + thread = new Thread(new ThreadStart(TimeoutWaitMain)); + thread.Start(); + thread.Join(); + if (Thread.VolatileRead(ref counter) != 0) { + Expect.Fail("expecting wait to timeout"); + return; + } + + // + // explicit ctor: initially owned = true, then explicitly release + // + Cleanup(); + Console.Write('.'); + mutex = new Mutex(true); + mutex.ReleaseMutex(); + thread = new Thread(new ThreadStart(TimeoutWaitMain)); + thread.Start(); + thread.Join(); + if (Thread.VolatileRead(ref counter) != 1) { + Expect.Fail("expecting wait to succeed"); + return; + } + + // + // explicit ctor: initially owned = false, then explicitly acquire + // + Cleanup(); + Console.Write('.'); + mutex = new Mutex(false); + mutex.WaitOne(); + thread = new Thread(new ThreadStart(TimeoutWaitMain)); + thread.Start(); + thread.Join(); + if (Thread.VolatileRead(ref counter) != 0) { + Expect.Fail("expecting wait to timeout"); + return; + } + + Cleanup(); + Console.WriteLine("OK"); + } + + /// + /// Mutex supports recursion: the same thread can Acquire the same mutex + /// multiple times successfully + /// + private static void TestRecursion() + { + Console.Write(" test recursion "); + + // + // same number of acquire and release + // + Cleanup(); + Console.Write('.'); + mutex = new Mutex(); + mutex.AcquireMutex(); + mutex.AcquireMutex(); + mutex.AcquireMutex(); + mutex.ReleaseMutex(); + mutex.ReleaseMutex(); + mutex.ReleaseMutex(); + Thread thread = new Thread(new ThreadStart(TimeoutWaitMain)); + thread.Start(); + thread.Join(); + if (Thread.VolatileRead(ref counter) != 1) { + Expect.Fail("expecting wait to succeed"); + return; + } + + // + // same number of acquire and release, with the explicit ctor count as 1 + // + Cleanup(); + Console.Write('.'); + mutex = new Mutex(true); + mutex.AcquireMutex(); + mutex.AcquireMutex(); + mutex.ReleaseMutex(); + mutex.ReleaseMutex(); + mutex.ReleaseMutex(); + thread = new Thread(new ThreadStart(TimeoutWaitMain)); + thread.Start(); + thread.Join(); + if (Thread.VolatileRead(ref counter) != 1) { + Expect.Fail("expecting wait to succeed"); + return; + } + + // + // acquire without release, the other thread will wait and timeout + // + Cleanup(); + Console.Write('.'); + mutex = new Mutex(); + mutex.AcquireMutex(); + mutex.AcquireMutex(); + mutex.AcquireMutex(); + thread = new Thread(new ThreadStart(TimeoutWaitMain)); + thread.Start(); + thread.Join(); + if (Thread.VolatileRead(ref counter) != 0) { + Expect.Fail("expecting wait to timeout"); + return; + } + + // + // less release than acquire, the other thread will wait and timeout + // + Cleanup(); + Console.Write('.'); + mutex = new Mutex(); + mutex.AcquireMutex(); + mutex.AcquireMutex(); + mutex.AcquireMutex(); + mutex.ReleaseMutex(); + mutex.ReleaseMutex(); + thread = new Thread(new ThreadStart(TimeoutWaitMain)); + thread.Start(); + thread.Join(); + if (Thread.VolatileRead(ref counter) != 0) { + Expect.Fail("expecting wait to timeout"); + return; + } + + Cleanup(); + Console.WriteLine("OK"); + } + + /// + /// The thread entry point for the waiter. + /// increment counter only if the wait is successful + /// + private static void TimeoutWaitMain() + { + bool ret = mutex.WaitOne(waitTimeout); + if (ret) { + Interlocked.Increment(ref counter); + } + } + + /// + /// Dispose the mutex, reset the counter + /// + private static void Cleanup() + { + if (mutex != null) { + mutex.Close(); + mutex = null; + } + counter = 0; + } + } +} diff --git a/base/Applications/Tests/Scheduler/Threading/TestSynchronize.cs b/base/Applications/Tests/Scheduler/Threading/TestSynchronize.cs new file mode 100644 index 0000000..7fcebb3 --- /dev/null +++ b/base/Applications/Tests/Scheduler/Threading/TestSynchronize.cs @@ -0,0 +1,126 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Threading; +using Microsoft.Singularity.UnitTest; + +namespace Microsoft.Singularity.Applications +{ + internal sealed class TestSynchronize + { + private static Mutex! mutex; + private static AutoResetEvent! autoResetEvent; + private static ManualResetEvent! manualResetEvent; + private static AutoResetEvent! syncEvent; + private static Mutex! consoleLock; + + private static void SyncThread() + { + Write("[2] Acquiring mutex..."); + mutex.WaitOne(); + Write("[2] Mutex acquired!"); + mutex.ReleaseMutex(); + Write("[2] Mutex released."); + + Thread.Yield(); + Thread.Yield(); + Thread.Yield(); + Thread.Yield(); + Thread.Yield(); + + Write("[2] Setting event."); + autoResetEvent.Set(); + + Thread.Yield(); + Thread.Yield(); + Thread.Yield(); + Thread.Yield(); + Thread.Yield(); + + Write("[2] Waiting for notification..."); + syncEvent.WaitOne(); + + Write("[2] Setting event."); + autoResetEvent.Set(); + + Write("[2] Waiting for notification..."); + syncEvent.WaitOne(); + + Write("[2] Setting manual event."); + manualResetEvent.Set(); + } + + internal static void Run() + { + mutex = new Mutex(); + autoResetEvent = new AutoResetEvent(false); + manualResetEvent = new ManualResetEvent(true); + syncEvent = new AutoResetEvent(false); + consoleLock = new Mutex(); + + Thread t1 = new Thread(new ThreadStart(SyncThread)); + t1.Start(); + + Write("[1] Acquiring mutex..."); + mutex.WaitOne(); + Write("[1] Mutex acquired!"); + Write("[1] Releasing mutex..."); + mutex.ReleaseMutex(); + Write("[1] Mutex released."); + + Write("[1] Acquiring mutex..."); + mutex.WaitOne(); + Write("[1] Mutex acquired!"); + Write("[1] Yielding (1/3)."); + Thread.Yield(); + Write("[1] Yielding (2/3)."); + Thread.Yield(); + Write("[1] Yielding (3/3)."); + Thread.Yield(); + Write("[1] Releasing mutex..."); + mutex.ReleaseMutex(); + Write("[1] Mutex released."); + + Write("[1] Waiting on event."); + autoResetEvent.WaitOne(); + Write("[1] Event set!"); + + Write("[1] Setting sync event."); + syncEvent.Set(); + + Write("[1] Waiting on event."); + autoResetEvent.WaitOne(); + Write("[1] Event set!"); + + Write("[1] Waiting on manual event."); + manualResetEvent.WaitOne(); + Write("[1] Event set!"); + + Write("[1] Resetting manual event."); + manualResetEvent.Reset(); + + Write("[1] Setting sync event."); + syncEvent.Set(); + + Write("[1] Waiting on manual event."); + manualResetEvent.WaitOne(); + Write("[1] Event set!"); + + Write("[1] Waiting on manual event."); + manualResetEvent.WaitOne(); + Write("[1] Event set!"); + } + + private static void Write(string text) + { + consoleLock.WaitOne(); + Console.Write(" " + text); + consoleLock.ReleaseMutex(); + } + } +} diff --git a/base/Applications/Tests/Scheduler/Threading/TestWaitAny.cs b/base/Applications/Tests/Scheduler/Threading/TestWaitAny.cs new file mode 100644 index 0000000..0b8e77d --- /dev/null +++ b/base/Applications/Tests/Scheduler/Threading/TestWaitAny.cs @@ -0,0 +1,614 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Threading; +using Microsoft.Singularity.UnitTest; + +namespace Microsoft.Singularity.Applications +{ + /// + /// Test cases for WaitHandle.WaitAny() + /// + internal sealed class TestWaitAny + { + internal static TestLog Expect; + private const int numHandles = 7; + private const int numHandlesStress = 72; + private static readonly TimeSpan waitTimeout = TimeSpan.FromMilliseconds(100); + private static WaitHandle[] handles; + private static ManualResetEvent evtGate; + private static int counter; + private static int readyCount; + + internal static void Run() + { + ///TODO: harden code path and then enable this test case + //TestNullArray(); + + TestTrivialTimeout(); + TestTrivialSameHandleArray(); + TestTrivialEachPosition(); + TestThreadTimeout(); + TestThreadEachPosition(); + TestThreadMultipleWaitAny(); + TestThreadMixtureSyncObjects(); + } + + /// + /// WaitAny on a mixture of AutoResetEvent, ManualResetEvent, and Mutex + /// + private static void TestThreadMixtureSyncObjects() + { + Console.Write(" test thread mixture of sync objects "); + + Cleanup(); + Console.Write('.'); + evtGate = new ManualResetEvent(false); + handles = new WaitHandle[numHandlesStress]; + for (int i = 0; i < handles.Length; i++) { + switch(i % 3) { + case 0: + // auto reset events, half set and half reset + handles[i] = new AutoResetEvent(i % 6 == 0); + break; + case 1: + // mutexes, half initially owned and half not initially owned + handles[i] = new Mutex(i % 6 != 1); + break; + case 2: + // manual reset events, all reset + handles[i] = new ManualResetEvent(false); + break; + } + } + + Thread[] threads = new Thread[numHandlesStress]; + for (int i = 0; i < threads.Length; i++) { + threads[i] = new Thread(new ThreadStart(TimeoutWaitMain)); + ((!)threads[i]).Start(); + Thread.Yield(); + } + + // wait for all waiter threads to be ready + while (true) { + if (Thread.VolatileRead(ref readyCount) == numHandlesStress) { + break; + } + Thread.Yield(); + } + + // let the waiter threads go + evtGate.Set(); + for (int i = 0; i < threads.Length; i++) { + ((!)threads[i]).Join(); + } + + // half of the mutexes, half of the auto reset events, and all manual reset events + // should timeout. therefore the counter is + // numHandlesStress - (numHandlesStress + 3) / 6 * 2 + if (Thread.VolatileRead(ref counter) != + numHandlesStress - (numHandlesStress + 3) / 6 * 2) { + Expect.Fail(string.Format("unexpected counter {0}", counter)); + return; + } + Cleanup(); + + Console.WriteLine("OK"); + } + + /// + /// Multiple threads call WaitAny() on an array of wait handles of the same type + /// + private static void TestThreadMultipleWaitAny() + { + Console.Write(" test thread multiple wait any "); + + // + // wait on multiple mutexes + // + Cleanup(); + Console.Write('.'); + evtGate = new ManualResetEvent(false); + handles = new WaitHandle[numHandlesStress]; + for (int i = 0; i < handles.Length; i++) { + handles[i] = new Mutex(); + } + Thread[] threads = new Thread[numHandlesStress]; + for (int i = 0; i < threads.Length; i++) { + threads[i] = new Thread(new ThreadStart(TimeoutWaitMain)); + ((!)threads[i]).Start(); + Thread.Yield(); + } + // wait for all waiter threads to be ready + while (true) { + if (Thread.VolatileRead(ref readyCount) == numHandlesStress) { + break; + } + Thread.Yield(); + } + evtGate.Set(); + for (int i = 0; i < threads.Length; i++) { + ((!)threads[i]).Join(); + } + + if (Thread.VolatileRead(ref counter) != 0) { + Expect.Fail("unexpected counter"); + return; + } + + // + // wait on multiple auto reset events + // + Cleanup(); + Console.Write('.'); + evtGate.Reset(); + handles = new WaitHandle[numHandlesStress]; + for (int i = 0; i < handles.Length; i++) { + handles[i] = new AutoResetEvent(false); + } + threads = new Thread[numHandlesStress]; + for (int i = 0; i < threads.Length; i++) { + threads[i] = new Thread(new ThreadStart(TimeoutWaitMain)); + ((!)threads[i]).Start(); + Thread.Yield(); + } + // set half of the events + for (int i = 0; i < handles.Length; i += 2) { + ((AutoResetEvent!)handles[i]).Set(); + } + + // wait for all waiter threads to be ready + while (true) { + if (Thread.VolatileRead(ref readyCount) == numHandlesStress) { + break; + } + Thread.Yield(); + } + evtGate.Set(); + for (int i = 0; i < threads.Length; i++) { + ((!)threads[i]).Join(); + } + + // half of the events are set, the rest should timeout, therefore the counter + // should be numHandlesStress - numHandlesStress / 2 + if (Thread.VolatileRead(ref counter) != numHandlesStress - numHandlesStress / 2) { + Expect.Fail("unexpected counter"); + return; + } + Cleanup(); + + Console.WriteLine("OK"); + } + + /// + /// poke each position of WaitAny handles to make sure we don't have blind spot. + /// + private static void TestTrivialEachPosition() + { + Console.Write(" test trivial each position "); + int ret; + + // + // initialize an array of auto reset events, all set, and call WaitAny + // multiple times, it should cycle through the array, after the array is + // thoroughly walked, the next WaitAny should timeout + // + handles = new WaitHandle[numHandles]; + for (int i = 0; i < handles.Length; i++) { + handles[i] = new AutoResetEvent(true); + } + for (int i = 0; i < handles.Length; i++) { + Console.Write('.'); + ret = WaitHandle.WaitAny(handles); + if (ret != i) { + Expect.Fail(string.Format("expect wait succeed on handles[{0}]", i)); + return; + } + } + Console.Write('.'); + ret = WaitHandle.WaitAny(handles, waitTimeout); + if (ret != -1) { + Expect.Fail("wait should timeout"); + return; + } + Cleanup(); + + // + // now start with an array of auto reset events all reset, + // then poke each position at a time, WaitAny should success on that position + // + handles = new WaitHandle[numHandles]; + for (int i = 0; i < handles.Length; i++) { + handles[i] = new AutoResetEvent(false); + } + for (int i = handles.Length-1; i >= 0; i--) { + ((AutoResetEvent!)handles[i]).Set(); + Console.Write('.'); + ret = WaitHandle.WaitAny(handles); + if (ret != i) { + Expect.Fail(string.Format("expect wait succeed on handles[{0}]", i)); + return; + } + } + Console.Write('.'); + ret = WaitHandle.WaitAny(handles, waitTimeout); + if (ret != -1) { + Expect.Fail("wait should timeout"); + return; + } + Cleanup(); + + Console.WriteLine("OK"); + } + + /// + /// poke each position of WaitAny handles and call WaitAny on a different thread + /// + private static void TestThreadEachPosition() + { + Console.Write(" test thread each position "); + Cleanup(); + + // + // initialize an array of auto reset events, all set, and call WaitAny + // on multiple threads, they should cycle through the array, after the array is + // thoroughly walked, the next WaitAny should timeout + // + Console.Write('.'); + evtGate = new ManualResetEvent(false); + handles = new WaitHandle[numHandles]; + for (int i = 0; i < handles.Length; i++) { + handles[i] = new AutoResetEvent(true); + } + Thread[] threads = new Thread[numHandles]; + for (int i = 0; i < threads.Length; i++) { + threads[i] = new Thread(new ThreadStart(WaitMain)); + ((!)threads[i]).Start(); + } + evtGate.Set(); + for (int i = 0; i < threads.Length; i++) { + ((!)threads[i]).Join(); + } + + // all wait should be successful, therefore counter is 1+2+...+numHandles + if (Thread.VolatileRead(ref counter) != numHandles * (numHandles + 1) / 2) { + Expect.Fail("unexpected counter"); + return; + } + Console.Write('.'); + int ret = WaitHandle.WaitAny(handles, waitTimeout); + if (ret != -1) { + Expect.Fail("wait should timeout"); + return; + } + Cleanup(); + + // + // now start with an array of auto reset events all reset, + // then poke each position at a time, WaitAny should success on that position + // + Console.Write('.'); + handles = new WaitHandle[numHandles]; + for (int i = 0; i < handles.Length; i++) { + handles[i] = new AutoResetEvent(false); + } + threads = new Thread[numHandles]; + for (int i = 0; i < threads.Length; i++) { + threads[i] = new Thread(new ThreadStart(WaitMain)); + ((!)threads[i]).Start(); + } + evtGate.Set(); + + for (int i = handles.Length-1; i >= 0; i--) { + ((AutoResetEvent!)handles[i]).Set(); + Thread.Yield(); + } + for (int i = 0; i < threads.Length; i++) { + ((!)threads[i]).Join(); + } + // all wait should be successful, therefore counter is 1+2+...+numHandles + if (Thread.VolatileRead(ref counter) != numHandles * (numHandles + 1) / 2) { + Expect.Fail("unexpected counter"); + return; + } + + Console.Write('.'); + ret = WaitHandle.WaitAny(handles, waitTimeout); + if (ret != -1) { + Expect.Fail("wait should timeout"); + return; + } + Cleanup(); + + Console.WriteLine("OK"); + } + + /// + /// Thread entry point of the waiter thread. + /// Counter is incremented by the successful wait position plus one + /// + private static void WaitMain() + { + evtGate.WaitOne(); + int ret = WaitHandle.WaitAny(handles); + + // there is no Interlocked.Add on int :( Do it the hard way + int oldValue, newValue; + do { + oldValue = Thread.VolatileRead(ref counter); + newValue = oldValue + (ret + 1); + } + while (oldValue != Interlocked.CompareExchange(ref counter, newValue, oldValue)); + } + + /// + /// WaitAny on an array of handles where all elements are referening the same instance + /// + private static void TestTrivialSameHandleArray() + { + Console.Write(" test trivial same handle array "); + + // + // same instance of manual reset event + // + Console.Write('.'); + ManualResetEvent mevt = new ManualResetEvent(true); + handles = new WaitHandle[numHandles]; + for (int i = 0; i < handles.Length; i++) { + handles[i] = mevt; + } + int ret = WaitHandle.WaitAny(handles); + if (ret != 0) { + Expect.Fail("expect wait succeed on handles[0]"); + return; + } + ret = WaitHandle.WaitAny(handles); + if (ret != 0) { + Expect.Fail("expect wait succeed on handles[0] again"); + return; + } + Cleanup(); + + // + // same instance of auto reset event + // + Console.Write('.'); + AutoResetEvent aevt = new AutoResetEvent(true); + handles = new WaitHandle[numHandles]; + for (int i = 0; i < handles.Length; i++) { + handles[i] = aevt; + } + ret = WaitHandle.WaitAny(handles); + if (ret != 0) { + Expect.Fail("expect wait succeed on handles[0]"); + return; + } + ret = WaitHandle.WaitAny(handles, waitTimeout); + if (ret != -1) { + Expect.Fail("wait should timeout"); + return; + } + Cleanup(); + + + Console.WriteLine("OK"); + } + + /// + /// Test WaitAny on an array with is null or contains null elements + /// + private static void TestNullArray() + { + Console.Write(" test null array "); + + Console.Write('.'); + handles = null; + bool exception = false; + try { + WaitHandle.WaitAny(handles); + } + catch (ArgumentNullException) { + exception = true; + } + if (!exception) { + Expect.Fail("expecting exception"); + return; + } + + Console.Write('.'); + handles = new WaitHandle[3] { null, null, null }; + exception = false; + try { + WaitHandle.WaitAny(handles); + } + catch (ArgumentNullException) { + exception = true; + } + if (!exception) { + Expect.Fail("expecting exception"); + return; + } + + Console.WriteLine("OK"); + } + + /// + /// Test WaitAny() on an array of unsignaled handles and timeout + /// + private static void TestThreadTimeout() + { + Console.Write(" test thread timeout "); + + // + // timeout on unsignaled auto reset events + // + Cleanup(); + Console.Write('.'); + evtGate = new ManualResetEvent(false); + handles = new WaitHandle[numHandles]; + for (int i = 0; i < handles.Length; i++) { + handles[i] = new AutoResetEvent(false); + } + TestThreadTimeoutWorker(); + + // + // timeout on unsignaled manual reset events + // + Cleanup(); + Console.Write('.'); + evtGate = new ManualResetEvent(false); + handles = new WaitHandle[numHandles]; + for (int i = 0; i < handles.Length; i++) { + handles[i] = new ManualResetEvent(false); + } + TestThreadTimeoutWorker(); + + // + // timeout on owned mutexes + // + Cleanup(); + Console.Write('.'); + evtGate.Reset(); + handles = new WaitHandle[numHandles]; + for (int i = 0; i < handles.Length; i++) { + handles[i] = new Mutex(); + ((!)handles[i]).WaitOne(); + } + TestThreadTimeoutWorker(); + + // + // timeout on owned + // + Cleanup(); + Console.Write('.'); + evtGate.Reset(); + handles = new WaitHandle[numHandles]; + for (int i = 0; i < handles.Length; i++) { + handles[i] = new Mutex(true); + } + TestThreadTimeoutWorker(); + + Cleanup(); + Console.WriteLine("OK"); + } + + /// + /// create threads WaitAny on an array of unsignaled handles, + /// they should all timeout + /// + private static void TestThreadTimeoutWorker() + { + Thread[] threads = new Thread[numHandles]; + for (int i = 0; i < threads.Length; i++) { + threads[i] = new Thread(new ThreadStart(TimeoutWaitMain)); + ((!)threads[i]).Start(); + } + evtGate.Set(); + + for (int i = 0; i < threads.Length; i++) { + ((!)threads[i]).Join(); + } + // all wait should timeout, therefore counter == numHandles + if (Thread.VolatileRead(ref counter) != numHandles) { + Expect.Fail("unexpected number of timeouts"); + } + } + + /// + /// Thread entry point for waiter. + /// Increment readyCount before waiting on the gate, then performs + /// WaitAny, and increment the counter if wait timeout + /// + private static void TimeoutWaitMain() + { + Interlocked.Increment(ref readyCount); + evtGate.WaitOne(); + int ret = WaitHandle.WaitAny(handles, waitTimeout); + if (ret == -1) { + // increment counter only if wait timeout + Interlocked.Increment(ref counter); + } + } + + /// + /// Test WaitAny on an array of unsignaled handles and the wait should timeout + /// + private static void TestTrivialTimeout() + { + Console.Write(" test trivial timeout "); + + // + // wait on unsignaled manual reset event + // + Console.Write('.'); + handles = new WaitHandle[3] { + new ManualResetEvent(false), + new ManualResetEvent(false), + new ManualResetEvent(false) + }; + int ret = WaitHandle.WaitAny(handles, waitTimeout); + if (ret != -1) { + Expect.Fail("wait should timeout"); + return; + } + Cleanup(); + + // + // wait on unsignaled auto reset event + // + Console.Write('.'); + handles = new WaitHandle[3] { + new AutoResetEvent(false), + new AutoResetEvent(false), + new AutoResetEvent(false) + }; + ret = WaitHandle.WaitAny(handles, waitTimeout); + if (ret != -1) { + Expect.Fail("wait should timeout"); + return; + } + Cleanup(); + + // + // wait on mixture of unsignaled events + // + Console.Write('.'); + handles = new WaitHandle[4] { + new AutoResetEvent(false), + new ManualResetEvent(false), + new AutoResetEvent(false), + new ManualResetEvent(false) + }; + ret = WaitHandle.WaitAny(handles, waitTimeout); + if (ret != -1) { + Expect.Fail("wait should timeout"); + return; + } + Cleanup(); + + Console.WriteLine("OK"); + } + + /// + /// Release all wait handles, reset counters + /// + private static void Cleanup() + { + if (handles != null) { + for (int i = 0; i < handles.Length; i++) { + if (handles[i] != null) { + ((!)handles[i]).Close(); + } + } + handles = null; + } + readyCount = 0; + counter = 0; + } + } +} diff --git a/base/Applications/Tests/Scheduler/Threading/Threading.csproj b/base/Applications/Tests/Scheduler/Threading/Threading.csproj new file mode 100644 index 0000000..0e62284 --- /dev/null +++ b/base/Applications/Tests/Scheduler/Threading/Threading.csproj @@ -0,0 +1,33 @@ + + + + + + + Threading + Exe + true + true + + + + + + + + + + + + + diff --git a/base/Applications/Tests/Scheduler/Threading/ThreadingTest.cs b/base/Applications/Tests/Scheduler/Threading/ThreadingTest.cs new file mode 100644 index 0000000..1f2990a --- /dev/null +++ b/base/Applications/Tests/Scheduler/Threading/ThreadingTest.cs @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Threading; +using Microsoft.Singularity.UnitTest; + +namespace Microsoft.Singularity.Applications +{ + /// + /// Test synchronization objects. + /// + [TestClass] + public class ThreadingTest : TestClass + { + [TestMethod] + public void ManualResetEventTest() + { + TestManualResetEvent.Expect = Expect; + TestManualResetEvent.Run(); + } + + [TestMethod] + public void AutoResetEventTest() + { + TestAutoResetEvent.Expect = Expect; + TestAutoResetEvent.Run(); + } + + [TestMethod] + public void MutexTest() + { + TestMutex.Expect = Expect; + TestMutex.Run(); + } + + [TestMethod] + public void WaitAnyTest() + { + TestWaitAny.Expect = Expect; + TestWaitAny.Run(); + } + + [TestMethod] + public void SyncTest() + { + Console.WriteLine(" synchronization between 2 threads..."); + TestSynchronize.Run(); + Console.WriteLine(" ...OK"); + } + } +} diff --git a/base/Applications/Tests/SdsTiming/SdsTiming.sg b/base/Applications/Tests/SdsTiming/SdsTiming.sg index a4784af..bf008eb 100644 --- a/base/Applications/Tests/SdsTiming/SdsTiming.sg +++ b/base/Applications/Tests/SdsTiming/SdsTiming.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: SDSTest.sg -// // Note: // @@ -130,7 +128,7 @@ namespace Microsoft.Singularity.Applications bool ok = SdsUtils.GetLinkValue(path, ds, out linkValue, out errorOut); CheckError("GetLinkValue", path, expected, errorOut, ok); if (ok) { - if (expectedValue != linkValue){ + if (expectedValue != linkValue) { Console.WriteLine(" expected({0}) got({1})", expectedValue, linkValue); } } @@ -193,10 +191,12 @@ namespace Microsoft.Singularity.Applications NodeType expectedNodeType) { ErrorCode errorOut; - long length; NodeType nodeType; + FileAttributesRecord attributes; + + bool ok = SdsUtils.GetAttributes(path, ds, out attributes, out errorOut); + nodeType = attributes.Type; - bool ok = SdsUtils.GetAttributes(path, ds, out length, out nodeType, out errorOut); CheckError("GetAttributes", path, expectedNodeType, nodeType, ok); CheckError("GetAttributes", path, expected, errorOut, ok); } @@ -229,7 +229,7 @@ namespace Microsoft.Singularity.Applications DebugStub.WriteLine("\nstart get DirectoryService endpoint"); start = ProcessService.GetUpTime(); - for ( int i = 0; i < ITERATIONS; i++) { + for (int i = 0; i < ITERATIONS; i++) { DirectoryServiceContract.Imp! d = DirectoryService.NewClientEndpoint(); delete d; } @@ -240,23 +240,23 @@ namespace Microsoft.Singularity.Applications /// - DebugStub.WriteLine("\nstart bind to /init/testpe/testpe.x86"); + DebugStub.WriteLine("\nstart bind to /init/testpe/testpe"); start = ProcessService.GetUpTime(); - for ( int i = 0; i < ITERATIONS; i++) { + for (int i = 0; i < ITERATIONS; i++) { FileContract.NewChannel(out fc, out fs); - DoBind(ds,"/init/testpe/testpe.x86",fs,ErrorCode.NoError); + DoBind(ds,"/init/testpe/testpe",fs,ErrorCode.NoError); delete fc; } end = ProcessService.GetUpTime(); elapsed = end - start; - Console.WriteLine("\nBind /init/testpe/testpe.x86 count={0} time={1}", + Console.WriteLine("\nBind /init/testpe/testpe count={0} time={1}", ITERATIONS, elapsed); /// if (dspLoaded) { DebugStub.WriteLine("\nstart bind to /dsp"); start = ProcessService.GetUpTime(); - for ( int i = 0; i < ITERATIONS; i++) { + for (int i = 0; i < ITERATIONS; i++) { DirectoryServiceContract.NewChannel(out dspImp, out dspExp); DoBind(ds,"/dsp",dspExp,ErrorCode.NoError); delete dspImp; @@ -274,7 +274,7 @@ namespace Microsoft.Singularity.Applications if (haveDir && haveDsp) { DebugStub.WriteLine("\nstart bind to /dsp/bindtime"); start = ProcessService.GetUpTime(); - for ( int i = 0; i < ITERATIONS; i++) { + for (int i = 0; i < ITERATIONS; i++) { DirectoryServiceContract.NewChannel(out dspDirImp, out dspDirExp); DoBind(ds,"/dsp/bindtime",dspDirExp,ErrorCode.NoError); delete dspDirImp; @@ -289,7 +289,7 @@ namespace Microsoft.Singularity.Applications DebugStub.WriteLine("\nstart bind to /dsp/bindtime via mountpoint"); dspImp.RecvSuccess(); start = ProcessService.GetUpTime(); - for ( int i = 0; i < ITERATIONS; i++) { + for (int i = 0; i < ITERATIONS; i++) { DirectoryServiceContract.NewChannel(out dspDirImp, out dspDirExp); DoBind(dspImp,"/bindtime",dspDirExp,ErrorCode.NoError); delete dspDirImp; @@ -303,21 +303,21 @@ namespace Microsoft.Singularity.Applications delete dspImp; } - DebugStub.WriteLine("start bind to /init/testpe/testpe.x86 with NoAllocateAttribute ds"); + DebugStub.WriteLine("start bind to /init/testpe/testpe with NoAllocateAttribute ds"); TimeSpan s2 = ProcessService.GetUpTime(); - for ( int i = 0; i < ITERATIONS; i++) { + for (int i = 0; i < ITERATIONS; i++) { FileContract.NewChannel(out fc, out fs); - DoBind("/init/testpe/testpe.x86",fs,ErrorCode.NoError); + DoBind("/init/testpe/testpe",fs,ErrorCode.NoError); delete fc; } TimeSpan e2 = ProcessService.GetUpTime(); TimeSpan elapsed2 = e2 -s2; - Console.WriteLine("Bind testpe.x86 count={0} time={1}", + Console.WriteLine("Bind testpe count={0} time={1}", ITERATIONS, elapsed2); DebugStub.WriteLine("start directory to /init/testpe"); TimeSpan s3 = ProcessService.GetUpTime(); - for ( int i = 0; i < ITERATIONS; i++) { + for (int i = 0; i < ITERATIONS; i++) { DirectoryServiceContract.NewChannel(out imp, out exp); DoBind("/init/testpe",exp,ErrorCode.NoError); delete imp; @@ -357,7 +357,7 @@ namespace Microsoft.Singularity.Applications Process dsp = null; ErrorCode errorOut; - string dspName = "TestDSP.x86"; + string dspName = "TestDSP"; string dspMountPoint = "/dsp"; DirectoryServiceContract.Imp! ds = DirectoryService.NewClientEndpoint(); diff --git a/base/Applications/Tests/Select/Select.csproj b/base/Applications/Tests/Select/Select.csproj index 2352fe8..d4fb2d0 100644 --- a/base/Applications/Tests/Select/Select.csproj +++ b/base/Applications/Tests/Select/Select.csproj @@ -5,8 +5,6 @@ Microsoft Research Singularity Copyright (c) Microsoft Corporation. All rights reserved. -File: Applications\Tests\Select\Select.csproj - Note: Tests "select receive". ############################################################################## diff --git a/base/Applications/Tests/Select/Select.sg b/base/Applications/Tests/Select/Select.sg index a0a7fdf..7710d01 100644 --- a/base/Applications/Tests/Select/Select.sg +++ b/base/Applications/Tests/Select/Select.sg @@ -4,10 +4,9 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Select.cs -// // Note: Simple Singularity test program. // + using System; using System.Threading; using Microsoft.Singularity.Channels; @@ -19,7 +18,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] @@ -258,203 +258,203 @@ namespace Microsoft.Singularity.Applications { } } - /* - //[ShellCommand("select", "Run select test")] - public static int DoSelect() - { - SelectTest.Exp[] expEndpoints = new SelectTest.Exp [3]; - impEndpoints = new TRef [3]; - - for (int i = 0; i < 3; i++) { - SelectTest.Imp! imp; - SelectTest.Exp! exp; - SelectTest.NewChannel(out imp, out exp); - expEndpoints[i] = exp; - impEndpoints[i] = new TRef(imp); - } - - Thread t1 = new Thread(new ThreadStart(SelectThread)); - t1.Start(); - - int[][] patterns = new int[3][]; - patterns[0] = new int [] { 0, 0, SelectTest.Tags.A }; - patterns[1] = new int [] { SelectTest.Tags.B, SelectTest.Tags.C, 0 }; - patterns[2] = new int [] { SelectTest.Tags.A, SelectTest.Tags.C, 0 }; - - ISelectable[] selects = new ISelectable[3]; - for (int i = 0; i<3; i++) { - selects[i] = expEndpoints[i]; - } - - object setMatch; - int i1 = Endpoint.Select(patterns, selects, out setMatch); - Console.WriteLine("[1] Got {0}! (Expected 0.)", i1); - - expEndpoints[2].RecvA(); - expEndpoints[2].SendAck(); - - int i2 = Endpoint.Select(patterns, selects, out setMatch); - Console.WriteLine("[1] Got {0}! (Expected 0.)", i2); - - expEndpoints[2].RecvA(); - expEndpoints[2].SendAck(); - - int i3 = Endpoint.Select(patterns, selects, out setMatch); - Console.WriteLine("[1] Got {0}! (Expected 0.)", i3); - - expEndpoints[2].RecvA(); - expEndpoints[2].SendAck(); - - int i4 = Endpoint.Select(patterns, selects, out setMatch); - Console.WriteLine("[1] Got {0}! (Expected 1.)", i4); - - expEndpoints[0].RecvB(); - expEndpoints[1].RecvC(); - expEndpoints[0].SendAck(); - expEndpoints[1].SendAck(); - - int i5 = Endpoint.Select(patterns, selects, out setMatch); - Console.WriteLine("[1] Got {0}! (Expected 2.)", i5); - - expEndpoints[0].RecvA(); - expEndpoints[1].RecvC(); - expEndpoints[0].SendAck(); - expEndpoints[1].SendAck(); - - int i6 = Endpoint.Select(patterns, selects, out setMatch); - Console.WriteLine("[1] Got {0}! (Expected 2.)", i6); - - expEndpoints[0].RecvA(); - expEndpoints[1].RecvC(); - expEndpoints[0].SendAck(); - expEndpoints[1].SendAck(); - - int i7 = Endpoint.Select(patterns, selects, out setMatch); - Console.WriteLine("[1] Got {0}! (Expected -1.)", i7); - - Console.WriteLine("[1] Done."); - return 0; - } - - - //[ShellCommand("selectset", "Run select set test")] - public static int DoSelectSet() - { - SelectTest.Exp[] expEndpoints = new SelectTest.Exp [3]; - impEndpoints = new TRef [3]; - - for (int i = 0; i < 3; i++) { - SelectTest.Imp! imp; - SelectTest.Exp! exp; - SelectTest.NewChannel(out imp, out exp); - expEndpoints[i] = exp; - impEndpoints[i] = new TRef(imp); - } - - Thread t1 = new Thread(new ThreadStart(SelectThread)); - t1.Start(); - - int[][] patterns = new int[3][]; - patterns[0] = new int [] { SelectTest.Tags.A }; - patterns[1] = new int [] { SelectTest.Tags.B }; - patterns[2] = new int [] { SelectTest.Tags.C }; - - ESet eset = new ESet(); - eset.Add(expEndpoints[0]); - eset.Add(expEndpoints[1]); - eset.Add(expEndpoints[2]); - - ISelectable[] selectEPs = new ISelectable[1]{eset}; - - object setMatch; - int i1 = Endpoint.Select(patterns, selectEPs, out setMatch); - Console.WriteLine("[1] Got {0}! (Expected 0.)", i1); - - SetRecvA(setMatch, eset); - - int i2 = Endpoint.Select(patterns, selectEPs, out setMatch); - Console.WriteLine("[1] Got {0}! (Expected 0.)", i2); - - SetRecvA(setMatch, eset); - - int i3 = Endpoint.Select(patterns, selectEPs, out setMatch); - Console.WriteLine("[1] Got {0}! (Expected 0.)", i3); - - SetRecvA(setMatch, eset); - - int i4 = Endpoint.Select(patterns, selectEPs, out setMatch); - Console.WriteLine("[1] Got {0}! (Expected 1.)", i4); - - SetRecvB(setMatch, eset); - - int i5 = Endpoint.Select(patterns, selectEPs, out setMatch); - Console.WriteLine("[1] Got {0}! (Expected 2.)", i5); - - SetRecvC(setMatch, eset); - - int i6 = Endpoint.Select(patterns, selectEPs, out setMatch); - Console.WriteLine("[1] Got {0}! (Expected 0.)", i6); - - SetRecvA(setMatch, eset); - - int i7 = Endpoint.Select(patterns, selectEPs, out setMatch); - Console.WriteLine("[1] Got {0}! (Expected 2.)", i7); - - SetRecvC(setMatch, eset); - - int i8 = Endpoint.Select(patterns, selectEPs, out setMatch); - Console.WriteLine("[1] Got {0}! (Expected 0.)", i8); - - SetRecvA(setMatch, eset); - - int i9 = Endpoint.Select(patterns, selectEPs, out setMatch); - Console.WriteLine("[1] Got {0}! (Expected 2.)", i9); - - SetRecvC(setMatch, eset); - - int i10 = Endpoint.Select(patterns, selectEPs, out setMatch); - Console.WriteLine("[1] Got {0}! (Expected 0.)", i10); - - SetRecvA(setMatch, eset); - - int i11 = Endpoint.Select(patterns, selectEPs, out setMatch); - Console.WriteLine("[1] Got {0}! (Expected 1.)", i11); - - SetRecvB(setMatch, eset); - - int i12 = Endpoint.Select(patterns, selectEPs, out setMatch); - Console.WriteLine("[1] Got {0}! (Expected 2.)", i12); - - SetRecvC(setMatch, eset); - - int i13 = Endpoint.Select(patterns, selectEPs, out setMatch); - Console.WriteLine("[1] Got {0}! (Expected -1.)", i13); - - Console.WriteLine("[1] Done."); - return 0; - } - - private static void SetRecvA(object match, ESet eset) { - SelectTest.Exp exp = eset.Extract(match); - exp.RecvA(); - exp.SendAck(); - eset.Add(exp); - } - - private static void SetRecvB(object match, ESet eset) { - SelectTest.Exp exp = eset.Extract(match); - exp.RecvB(); - exp.SendAck(); - eset.Add(exp); - } - - private static void SetRecvC(object match, ESet eset) { - SelectTest.Exp exp = eset.Extract(match); - exp.RecvC(); - exp.SendAck(); - eset.Add(exp); - } - */ + // + ////[ShellCommand("select", "Run select test")] + //public static int DoSelect() + //{ + // SelectTest.Exp[] expEndpoints = new SelectTest.Exp [3]; + // impEndpoints = new TRef [3]; +// + // for (int i = 0; i < 3; i++) { + // SelectTest.Imp! imp; + // SelectTest.Exp! exp; + // SelectTest.NewChannel(out imp, out exp); + // expEndpoints[i] = exp; + // impEndpoints[i] = new TRef(imp); + // } +// + // Thread t1 = new Thread(new ThreadStart(SelectThread)); + // t1.Start(); +// + // int[][] patterns = new int[3][]; + // patterns[0] = new int [] { 0, 0, SelectTest.Tags.A }; + // patterns[1] = new int [] { SelectTest.Tags.B, SelectTest.Tags.C, 0 }; + // patterns[2] = new int [] { SelectTest.Tags.A, SelectTest.Tags.C, 0 }; +// + // ISelectable[] selects = new ISelectable[3]; + // for (int i = 0; i < 3; i++) { + // selects[i] = expEndpoints[i]; + // } +// + // object setMatch; + // int i1 = Endpoint.Select(patterns, selects, out setMatch); + // Console.WriteLine("[1] Got {0}! (Expected 0.)", i1); +// + // expEndpoints[2].RecvA(); + // expEndpoints[2].SendAck(); +// + // int i2 = Endpoint.Select(patterns, selects, out setMatch); + // Console.WriteLine("[1] Got {0}! (Expected 0.)", i2); +// + // expEndpoints[2].RecvA(); + // expEndpoints[2].SendAck(); +// + // int i3 = Endpoint.Select(patterns, selects, out setMatch); + // Console.WriteLine("[1] Got {0}! (Expected 0.)", i3); +// + // expEndpoints[2].RecvA(); + // expEndpoints[2].SendAck(); +// + // int i4 = Endpoint.Select(patterns, selects, out setMatch); + // Console.WriteLine("[1] Got {0}! (Expected 1.)", i4); +// + // expEndpoints[0].RecvB(); + // expEndpoints[1].RecvC(); + // expEndpoints[0].SendAck(); + // expEndpoints[1].SendAck(); +// + // int i5 = Endpoint.Select(patterns, selects, out setMatch); + // Console.WriteLine("[1] Got {0}! (Expected 2.)", i5); +// + // expEndpoints[0].RecvA(); + // expEndpoints[1].RecvC(); + // expEndpoints[0].SendAck(); + // expEndpoints[1].SendAck(); +// + // int i6 = Endpoint.Select(patterns, selects, out setMatch); + // Console.WriteLine("[1] Got {0}! (Expected 2.)", i6); +// + // expEndpoints[0].RecvA(); + // expEndpoints[1].RecvC(); + // expEndpoints[0].SendAck(); + // expEndpoints[1].SendAck(); +// + // int i7 = Endpoint.Select(patterns, selects, out setMatch); + // Console.WriteLine("[1] Got {0}! (Expected -1.)", i7); +// + // Console.WriteLine("[1] Done."); + // return 0; + //} +// +// + ////[ShellCommand("selectset", "Run select set test")] + //public static int DoSelectSet() + //{ + // SelectTest.Exp[] expEndpoints = new SelectTest.Exp [3]; + // impEndpoints = new TRef [3]; +// + // for (int i = 0; i < 3; i++) { + // SelectTest.Imp! imp; + // SelectTest.Exp! exp; + // SelectTest.NewChannel(out imp, out exp); + // expEndpoints[i] = exp; + // impEndpoints[i] = new TRef(imp); + // } +// + // Thread t1 = new Thread(new ThreadStart(SelectThread)); + // t1.Start(); +// + // int[][] patterns = new int[3][]; + // patterns[0] = new int [] { SelectTest.Tags.A }; + // patterns[1] = new int [] { SelectTest.Tags.B }; + // patterns[2] = new int [] { SelectTest.Tags.C }; +// + // ESet eset = new ESet(); + // eset.Add(expEndpoints[0]); + // eset.Add(expEndpoints[1]); + // eset.Add(expEndpoints[2]); +// + // ISelectable[] selectEPs = new ISelectable[1]{eset}; +// + // object setMatch; + // int i1 = Endpoint.Select(patterns, selectEPs, out setMatch); + // Console.WriteLine("[1] Got {0}! (Expected 0.)", i1); +// + // SetRecvA(setMatch, eset); +// + // int i2 = Endpoint.Select(patterns, selectEPs, out setMatch); + // Console.WriteLine("[1] Got {0}! (Expected 0.)", i2); +// + // SetRecvA(setMatch, eset); +// + // int i3 = Endpoint.Select(patterns, selectEPs, out setMatch); + // Console.WriteLine("[1] Got {0}! (Expected 0.)", i3); +// + // SetRecvA(setMatch, eset); +// + // int i4 = Endpoint.Select(patterns, selectEPs, out setMatch); + // Console.WriteLine("[1] Got {0}! (Expected 1.)", i4); +// + // SetRecvB(setMatch, eset); +// + // int i5 = Endpoint.Select(patterns, selectEPs, out setMatch); + // Console.WriteLine("[1] Got {0}! (Expected 2.)", i5); +// + // SetRecvC(setMatch, eset); +// + // int i6 = Endpoint.Select(patterns, selectEPs, out setMatch); + // Console.WriteLine("[1] Got {0}! (Expected 0.)", i6); +// + // SetRecvA(setMatch, eset); +// + // int i7 = Endpoint.Select(patterns, selectEPs, out setMatch); + // Console.WriteLine("[1] Got {0}! (Expected 2.)", i7); +// + // SetRecvC(setMatch, eset); +// + // int i8 = Endpoint.Select(patterns, selectEPs, out setMatch); + // Console.WriteLine("[1] Got {0}! (Expected 0.)", i8); +// + // SetRecvA(setMatch, eset); +// + // int i9 = Endpoint.Select(patterns, selectEPs, out setMatch); + // Console.WriteLine("[1] Got {0}! (Expected 2.)", i9); +// + // SetRecvC(setMatch, eset); +// + // int i10 = Endpoint.Select(patterns, selectEPs, out setMatch); + // Console.WriteLine("[1] Got {0}! (Expected 0.)", i10); +// + // SetRecvA(setMatch, eset); +// + // int i11 = Endpoint.Select(patterns, selectEPs, out setMatch); + // Console.WriteLine("[1] Got {0}! (Expected 1.)", i11); +// + // SetRecvB(setMatch, eset); +// + // int i12 = Endpoint.Select(patterns, selectEPs, out setMatch); + // Console.WriteLine("[1] Got {0}! (Expected 2.)", i12); +// + // SetRecvC(setMatch, eset); +// + // int i13 = Endpoint.Select(patterns, selectEPs, out setMatch); + // Console.WriteLine("[1] Got {0}! (Expected -1.)", i13); +// + // Console.WriteLine("[1] Done."); + // return 0; + //} +// + //private static void SetRecvA(object match, ESet eset) { + // SelectTest.Exp exp = eset.Extract(match); + // exp.RecvA(); + // exp.SendAck(); + // eset.Add(exp); + //} +// + //private static void SetRecvB(object match, ESet eset) { + // SelectTest.Exp exp = eset.Extract(match); + // exp.RecvB(); + // exp.SendAck(); + // eset.Add(exp); + //} +// + //private static void SetRecvC(object match, ESet eset) { + // SelectTest.Exp exp = eset.Extract(match); + // exp.RecvC(); + // exp.SendAck(); + // eset.Add(exp); + //} + // internal static int AppMain(Parameters! config) { diff --git a/base/Applications/Tests/SharedHeapTest/SharedHeapTest.cs b/base/Applications/Tests/SharedHeapTest/SharedHeapTest.cs index b58b396..e81e26c 100644 --- a/base/Applications/Tests/SharedHeapTest/SharedHeapTest.cs +++ b/base/Applications/Tests/SharedHeapTest/SharedHeapTest.cs @@ -4,10 +4,9 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: SharedHeapTest.cs -// // Note: User-mode test program for calling shared heap through the ABI. // + using Microsoft.Singularity.V1.Services; using System; using System.Runtime.CompilerServices; @@ -21,7 +20,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] @@ -87,7 +87,8 @@ namespace Microsoft.Singularity.Applications { AllocRandomSizedThingy(loop); if (CheckForValue(loop, 0) == true) { Console.WriteLine("Passed zero-fill test.\n"); - } else { + } + else { Console.WriteLine("Failed zero-fill test.\n"); } FillWithValue(loop, (byte)(1 << (int)loop)); @@ -137,7 +138,7 @@ namespace Microsoft.Singularity.Applications { // // Play around. // - switch(action) { + switch (action) { case 0: // // Allocate something new. @@ -146,7 +147,8 @@ namespace Microsoft.Singularity.Applications { AllocRandomSizedThingy(which); if (CheckForValue(which, 0) == true) { Console.WriteLine("Passed zero-fill test.\n"); - } else { + } + else { Console.WriteLine("Failed zero-fill test.\n"); } diff --git a/base/Applications/Tests/SmpTest/ForkTest.sg b/base/Applications/Tests/SmpTest/ForkTest.sg index 773bc11..606cebf 100644 --- a/base/Applications/Tests/SmpTest/ForkTest.sg +++ b/base/Applications/Tests/SmpTest/ForkTest.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: HeapTest.cs -// // Note: Allocate strings and encourage GCs // @@ -19,7 +17,7 @@ namespace Microsoft.Singularity.Applications { public class ForkTest { - static readonly string []! ProcessArgs = new string[] { "testpe.x86" }; + static readonly string []! ProcessArgs = new string[] { "testpe" }; static readonly TimeSpan ProcessTimeout = TimeSpan.FromSeconds(10); const int IterationsPerThread = 64; diff --git a/base/Applications/Tests/SmpTest/HeapTest.sg b/base/Applications/Tests/SmpTest/HeapTest.sg index 0bf3177..ac001c4 100644 --- a/base/Applications/Tests/SmpTest/HeapTest.sg +++ b/base/Applications/Tests/SmpTest/HeapTest.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: HeapTest.sg -// // Note: // diff --git a/base/Applications/Tests/SmpTest/Settings.sg b/base/Applications/Tests/SmpTest/Settings.sg index 183ec15..3f25812 100644 --- a/base/Applications/Tests/SmpTest/Settings.sg +++ b/base/Applications/Tests/SmpTest/Settings.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Settings.sg -// // Note: // diff --git a/base/Applications/Tests/SmpTest/SmpTest.csproj b/base/Applications/Tests/SmpTest/SmpTest.csproj index e505a9a..2a5e53e 100644 --- a/base/Applications/Tests/SmpTest/SmpTest.csproj +++ b/base/Applications/Tests/SmpTest/SmpTest.csproj @@ -24,6 +24,7 @@ + diff --git a/base/Applications/Tests/SmpTest/SmpTest.sg b/base/Applications/Tests/SmpTest/SmpTest.sg index fd35956..fba62e9 100644 --- a/base/Applications/Tests/SmpTest/SmpTest.sg +++ b/base/Applications/Tests/SmpTest/SmpTest.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: SmpTest.cs -// // Note: Some basic tests of system code on SMP. // diff --git a/base/Applications/Tests/SpecTest/SpecTest.csproj b/base/Applications/Tests/SpecTest/SpecTest.csproj new file mode 100644 index 0000000..231efc6 --- /dev/null +++ b/base/Applications/Tests/SpecTest/SpecTest.csproj @@ -0,0 +1,44 @@ + + + + + + + SpecTest + Exe + "Spec#\ Program\ Verifier\ finished\ with\ [0-9]*\ verified,\ 0\ errors" + + + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/Tests/SpecTest/SpecTest.sg b/base/Applications/Tests/SpecTest/SpecTest.sg new file mode 100644 index 0000000..ac6d097 --- /dev/null +++ b/base/Applications/Tests/SpecTest/SpecTest.sg @@ -0,0 +1,144 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: Test of various Spec# and Boogie features. +// +using System; + +using Microsoft.Singularity; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Io; +using Microsoft.SingSharp; +using Microsoft.SingSharp.Reflection; +using Microsoft.Contracts; +using Microsoft.Singularity.Applications; +[assembly: Transform(typeof(ApplicationResourceTransform))] + +namespace Microsoft.Singularity.Applications +{ + [ConsoleCategory(HelpMessage="SpecTest [options] Test of Spec# and Boogie", + DefaultAction=true)] + [Verify(false)] + internal sealed class Parameters + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + reflective internal Parameters(); + + internal int AppMain() + { + return new SpecTest().Run(); + } + } + + [Verify(false)] + internal contract Con1 + { + out message M1(); + in message M2(); + state S1 : one + { + M1! -> M2? -> S1; + } + } + + public class SpecTest + { + public int Run() + { + Console.WriteLine(); + PrintHello(); + int[]! arr = new int[] {3, 4, 5}; + bool b1 = bsearch(4, arr); + assert arr[1] == 4; + assert b1; + bool b2 = bsearch(6, arr); + assert !b2; + Console.WriteLine(b1); + Console.WriteLine(b2); + MakeCon(); + G(); + return 0; + } + + void T1(Con1.Imp:S1! imp, Con1.Exp:S1! exp) + { + } + + void F(int i) + requires i == 0; + { + } + + void G() + { + F(0); + } + + bool bsearch(int x, int[]! arr) + requires forall { int i in ( 0 : arr.Length - 1); arr[i] <= arr[i+1] }; + ensures forall { int i in ( 0 : arr.Length - 1); arr[i] <= arr[i+1] }; + ensures forall { int i in ( 0 : arr.Length); old(arr)[i] == arr[i]}; + ensures result == exists { int i in (0 : arr.Length); arr[i] == x}; + { + return bsearchr(x, arr, 0, arr.Length); + } + + bool bsearchr(int x, int[]! arr, int left, int right) + requires 0 <= left; + requires left <= right; + requires right <= arr.Length; + requires forall { int i in ( 0 : arr.Length - 1); arr[i] <= arr[i+1] }; + ensures forall { int i in ( 0 : arr.Length - 1); arr[i] <= arr[i+1]}; + ensures forall { int i in ( 0 : arr.Length); old(arr)[i] == arr[i]}; + ensures result == exists { int i in (left : right); arr[i] == x}; + { + if (right == left) return false; + else if (right == left + 1) return arr[left] == x; + else { + int mid = left / 2 + (right + 1) / 2; + return bsearchr(x, arr, left, mid) || bsearchr(x, arr, mid, right); + } + } + + + [Verify(false)] + void PrintHello() + { + Console.WriteLine("This is SpecTest"); + } + + [Verify(false)] + void T2(Con1.Imp:S1! imp, Con1.Exp:S1! exp) + { + exp.SendM1(); + imp.RecvM1(); + imp.SendM2(); + exp.RecvM2(); + } + + [Verify(false)] + void MakeCon() + { + Con1.Imp !imp; + Con1.Exp !exp; + Con1.NewChannel(out imp, out exp); + T1(imp, exp); + T2(imp, exp); + exp.SendM1(); + imp.RecvM1(); + imp.SendM2(); + exp.RecvM2(); + delete imp; + delete exp; + } + } +} diff --git a/base/Applications/Tests/Sync/Sync.cs b/base/Applications/Tests/Sync/Sync.cs index e08b986..3fc1dc8 100644 --- a/base/Applications/Tests/Sync/Sync.cs +++ b/base/Applications/Tests/Sync/Sync.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Sync.cs -// // Note: Simple Singularity test program. // using System; @@ -19,7 +17,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] diff --git a/base/Applications/Tests/TesterTest/SampleTest.sg b/base/Applications/Tests/TesterTest/SampleTest.sg new file mode 100644 index 0000000..fdf89d0 --- /dev/null +++ b/base/Applications/Tests/TesterTest/SampleTest.sg @@ -0,0 +1,221 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +using System.Diagnostics; + +// needed for DebugStub +using Microsoft.Singularity; +// needed for stdin and stdout +using Microsoft.Singularity.Io; + +// needed for the transform +using Microsoft.SingSharp.Reflection; + +// needed for the Thread.sleep() +using System.Threading; + +// needed for tests +using Microsoft.Singularity.UnitTest; + +// needed for the generated infrastructure +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Test.Contracts; + +[assembly: Transform(typeof(ApplicationResourceTransform))] +//[assembly: Transform(typeof(TestTransform))] +namespace Microsoft.Singularity +{ + + /// Classes with the [TestClass] attribute are classes containing test + /// methods. + /// Methods with the [TestMethod] attribute are unit test methods. + + [TestClass] + public class SampleTest : TestClass + { + int data = 0; + + [ClassInitialize] + public void Init() + { + data = 2; + Expect.Info("Initializing Sample"); + } + + [TestMethod] + public void HelloTest() + { + //TODO add something + Expect.Info("Hello test"); + Expect.True(true, "Got here"); + } + + [TestMethod] + public void ClassInitTest() + { + Expect.Equal(2, data, "data was setup correctly in ClassInitialize phase"); + } + + [TestMethod] + public void EqualTest() + { + Expect.Equal("","", "Empty strings are equal"); + Expect.NotEqual("","a", "Empty is not equal to a nonempty string"); + } + } + + [TestClass] + public class FailTest : TestClass + { + [ClassInitialize] + public void Init() + { + } + + [TestMethod] + public void SkipTest() + { + Expect.Continue(false, "This test should be skipped"); + Expect.Fail("This test should have been skipped"); + } + + [TestMethod] + public void TimeoutTest() + { + for (int i = 0; i < 1000; i++) { + Thread.Sleep(50); + } + Expect.Fail("This test should be aborted before now via timeout"); + } + } + + // EXAMPLE OF CODE THAT WOULD BE GENERATED ///////////////////// + internal class SampleTest_Jig : SuiteJig + { + private SampleTest! m_test; + + public SampleTest_Jig(TestLog! log) { + SampleTest t = new SampleTest(); + t.SetLog(log); + m_test = t; + } + + override public void Initialize() + { + m_test.Init(); + } + + override public void DoTest(string! test) + { + switch (test) { + case "HelloTest": + m_test.HelloTest(); + break; + case "EqualTest": + m_test.EqualTest(); + break; + case "ClassInitTest": + m_test.ClassInitTest(); + break; + default: + base.DoTest(test); + break; + } + } + } + + internal class FailTest_Jig : SuiteJig + { + private FailTest! m_test; + + public FailTest_Jig(TestLog! log) { + FailTest t = new FailTest(); + t.SetLog(log); + m_test = t; + } + + override public void Initialize() + { + m_test.Init(); + } + + override public void DoTest(string! test) + { + switch (test) { + case "SkipTest": + m_test.SkipTest(); + break; + case "TimeoutTest": + m_test.TimeoutTest(); + break; + default: + base.DoTest(test); + break; + } + } + } + + public class Sample_ModuleJig : ModuleJig + { + override public SuiteJig GetSuite(string! name, TestLog! log) + { + switch (name) { + case "SampleTest": + return new SampleTest_Jig(log); + case "FailTest": + return new FailTest_Jig(log); + case "Shell": + return new ShellTest_Jig(log); + default: + return base.GetSuite(name, log); + } + } + } + + // Currently required to get process launch code generated. + [ConsoleCategory(HelpMessage="Run using the test framework", DefaultAction=true)] + internal class ModuleConsole { + [InputEndpoint("data")] + public readonly TRef Stdin; + [OutputEndpoint("data")] + public readonly TRef Stdout; + + reflective internal ModuleConsole(); + + internal int AppMain() { + Console.WriteLine("This is a test application and can only be run from the tester."); + return -1; + } + } + + [ConsoleCategory(HelpMessage="ModuleTester", Action="test")] + internal class ModuleTest { + [InputEndpoint("data")] + public readonly TRef Stdin; + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [CustomEndpoint] + public readonly TRef testerRef; + + reflective internal ModuleTest(); + + internal int AppMain() { + ModuleTesterContract.Exp tester = ((!) testerRef).Acquire(); + if (tester == null) { + DebugStub.WriteLine("TEST unable to acquite handle to test driver"); + throw new Exception("Unable to acquire handle to the test driver"); + } + + ModuleJig jig = new Sample_ModuleJig(); + ModuleTester.RunTests(tester, jig); + return 0; + } + } +} diff --git a/base/Applications/Tests/TesterTest/ShellTest.sg b/base/Applications/Tests/TesterTest/ShellTest.sg new file mode 100644 index 0000000..d24cecd --- /dev/null +++ b/base/Applications/Tests/TesterTest/ShellTest.sg @@ -0,0 +1,98 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +using System.Diagnostics; +using System.Threading; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +using Microsoft.Bartok.Options; +using Microsoft.Bartok.Runtime; + +using Microsoft.Singularity.Directory; + +using Microsoft.Singularity; +using Microsoft.Singularity.Io; + +using Microsoft.Singularity.V1.Services; + +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.Test; +using Microsoft.Singularity.UnitTest; + +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Applications; +using Microsoft.Contracts; +using Microsoft.Singularity.Test.Contracts; + +//[assembly: Transform(typeof(TestTransform))] +namespace Microsoft.Singularity +{ + + internal class ShellTest_Jig : SuiteJig + { + TContainer m_outputMux; + private TestLog! Expect; + + public ShellTest_Jig(TestLog! log) { + Expect = log; + } + + override public void Initialize() + { + m_outputMux = new TContainer(MuxOut()); + Expect.NotNull(m_outputMux, "Got output multiplexer"); + } + + override public void Cleanup() + { + PipeMultiplexer outputMux = ((!) m_outputMux).Acquire(); + outputMux.Dispose(); + m_outputMux = null; + } + + // Special test implementation: + // take any test requested an dispatch a shell to execute it + // as a command + override public void DoTest(string! test) + { + string[] args = new string[] {"shell", "@single", test}; + PipeMultiplexer outputMux = ((!) m_outputMux).Acquire(); + DirectoryServiceContract.Imp ds = DirectoryService.NewClientEndpoint(); + try { + Manifest manifest; + Process child = Binder.CreateProcess(ds, args, outputMux, out manifest); + Expect.NotNull(child, "Spawned shell for requested test"); + Process! proc = (!) child; + proc.Start(); + proc.Join(); + Expect.Equal(proc.ExitCode, 0, "Spawned shell exited without errors"); + } + finally { + delete ds; + m_outputMux.Release(outputMux); + } + } + + // Redirect our standard output into a multiplexer so we can interleave + // output from child processes + private PipeMultiplexer! MuxOut() + { + // Swap our real stdOut with a newly created one + UnicodePipeContract.Exp! newOutputExp; + UnicodePipeContract.Imp! newOutputImp; + UnicodePipeContract.NewChannel(out newOutputImp, out newOutputExp); + UnicodePipeContract.Imp stdOut = ConsoleOutput.Swap(newOutputImp); + Expect.True(stdOut != null, "Got stdout pipe"); + // Use a mux to splice our own output together with the child + // processes we will run. + return PipeMultiplexer.Start((!)stdOut, newOutputExp); + } + } + +} diff --git a/base/Applications/Tests/TesterTest/shellbvt.tst b/base/Applications/Tests/TesterTest/shellbvt.tst new file mode 100644 index 0000000..c4db52a --- /dev/null +++ b/base/Applications/Tests/TesterTest/shellbvt.tst @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/base/Applications/Tests/TesterTest/shellfail.tst b/base/Applications/Tests/TesterTest/shellfail.tst new file mode 100644 index 0000000..0ad7294 --- /dev/null +++ b/base/Applications/Tests/TesterTest/shellfail.tst @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/base/Applications/Tests/TesterTest/testdriver.csproj b/base/Applications/Tests/TesterTest/testdriver.csproj new file mode 100644 index 0000000..6007e20 --- /dev/null +++ b/base/Applications/Tests/TesterTest/testdriver.csproj @@ -0,0 +1,50 @@ + + + + + + + TestDriver + Exe + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/Tests/TesterTest/tester.tst b/base/Applications/Tests/TesterTest/tester.tst new file mode 100644 index 0000000..d20d0c2 --- /dev/null +++ b/base/Applications/Tests/TesterTest/tester.tst @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/base/Applications/Tests/ThreadTest/ThreadTest.cs b/base/Applications/Tests/ThreadTest/ThreadTest.cs index 6bc837a..055419b 100644 --- a/base/Applications/Tests/ThreadTest/ThreadTest.cs +++ b/base/Applications/Tests/ThreadTest/ThreadTest.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: ThreadTest.cs -// // Note: Simple Singularity test program. // using System; @@ -20,7 +18,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] @@ -57,8 +56,7 @@ namespace Microsoft.Singularity.Applications { Console.WriteLine("Second thread!"); DebugStub.Print("Second thread!\n"); - for (int i = 0; i < 10; i++) - { + for (int i = 0; i < 10; i++) { Console.WriteLine(" ... [1]"); Thread.Yield(); } @@ -80,8 +78,7 @@ namespace Microsoft.Singularity.Applications { t2.Start(); Console.WriteLine("Started second thread."); - for (int i = 0; i < 30; i++) - { + for (int i = 0; i < 30; i++) { Thread.Yield(); } return 0; diff --git a/base/Applications/Tests/ThreadTest/ThreadTest.csproj b/base/Applications/Tests/ThreadTest/ThreadTest.csproj index 72547ff..545d57a 100644 --- a/base/Applications/Tests/ThreadTest/ThreadTest.csproj +++ b/base/Applications/Tests/ThreadTest/ThreadTest.csproj @@ -5,8 +5,6 @@ Microsoft Research Singularity Copyright (c) Microsoft Corporation. All rights reserved. -File: Applications\Tests\ThreadTest\ThreadTest.csproj - Note: ############################################################################## diff --git a/base/Applications/Tests/Throw/Throw.cs b/base/Applications/Tests/Throw/Throw.cs index 8b12881..1b79df1 100644 --- a/base/Applications/Tests/Throw/Throw.cs +++ b/base/Applications/Tests/Throw/Throw.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Throw.cs -// // Note: Simple Singularity test program. // using System; @@ -19,7 +17,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] @@ -42,17 +41,14 @@ namespace Microsoft.Singularity.Applications { { string args = "something"; - try - { + try { DebugStub.Print("About to throw exception\n"); - if (args == null) - { + if (args == null) { throw new ArgumentNullException("ArgNullException"); } throw new ApplicationException("AppException"); } - catch (Exception e) - { + catch (Exception e) { Console.WriteLine("Caught exception {0}", e); } return 0; diff --git a/base/Applications/Tests/Throw/Throw.csproj b/base/Applications/Tests/Throw/Throw.csproj index 35a18eb..e1fe451 100644 --- a/base/Applications/Tests/Throw/Throw.csproj +++ b/base/Applications/Tests/Throw/Throw.csproj @@ -5,8 +5,6 @@ Microsoft Research Singularity Copyright (c) Microsoft Corporation. All rights reserved. -File: Applications\Tests\Throw\Throw.csproj - Note: ############################################################################## diff --git a/base/Applications/Tests/ThrowWithLinkStack/ThrowWithLinkStack.cs b/base/Applications/Tests/ThrowWithLinkStack/ThrowWithLinkStack.cs index 65d7281..797dedf 100644 --- a/base/Applications/Tests/ThrowWithLinkStack/ThrowWithLinkStack.cs +++ b/base/Applications/Tests/ThrowWithLinkStack/ThrowWithLinkStack.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: ThrowWithLinkStack.cs -// // Note: Simple Singularity test program. // using System; @@ -20,7 +18,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] @@ -41,13 +40,11 @@ namespace Microsoft.Singularity.Applications { //[ShellCommand("throwWithLinkStack", "Throw an exception with link stack")] internal static int AppMain(Parameters! config) { - try - { + try { DebugStub.Print("About to throw exception\n"); Throw(); } - catch (Exception e) - { + catch (Exception e) { Console.WriteLine("Throw with Link Stack Caught exception {0}", e); } return 0; diff --git a/base/Applications/Tests/ThrowWithLinkStack/ThrowWithLinkStack.csproj b/base/Applications/Tests/ThrowWithLinkStack/ThrowWithLinkStack.csproj index 636987f..5f12af6 100644 --- a/base/Applications/Tests/ThrowWithLinkStack/ThrowWithLinkStack.csproj +++ b/base/Applications/Tests/ThrowWithLinkStack/ThrowWithLinkStack.csproj @@ -5,8 +5,6 @@ Microsoft Research Singularity Copyright (c) Microsoft Corporation. All rights reserved. -File: Applications\Tests\ThrowWithLinkStack\ThrowWithLinkStack.csproj - Note: ############################################################################## diff --git a/base/Applications/Tests/UnitTestTest/UnitTestTest.cs b/base/Applications/Tests/UnitTestTest/UnitTestTest.cs index 70a0651..d72a949 100644 --- a/base/Applications/Tests/UnitTestTest/UnitTestTest.cs +++ b/base/Applications/Tests/UnitTestTest/UnitTestTest.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: UnitTestTest.cs -// // Note: A test of the Unit testing code. // @@ -22,7 +20,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] diff --git a/base/Applications/Tests/UnitTestTest/UnitTestTest.csproj b/base/Applications/Tests/UnitTestTest/UnitTestTest.csproj deleted file mode 100644 index 70b8670..0000000 --- a/base/Applications/Tests/UnitTestTest/UnitTestTest.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - UnitTestTest - Exe - true - true - - - - - - - - - - - - diff --git a/base/Applications/Tests/VQTest/VQTest.sg b/base/Applications/Tests/VQTest/VQTest.sg index cb529ab..44efafd 100644 --- a/base/Applications/Tests/VQTest/VQTest.sg +++ b/base/Applications/Tests/VQTest/VQTest.sg @@ -1,11 +1,11 @@ -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // +// ---------------------------------------------------------------------------- using System; using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Channels; using Microsoft.Contracts; using Microsoft.SingSharp.Reflection; using Microsoft.Singularity.Applications; @@ -13,7 +13,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] @@ -30,7 +31,8 @@ namespace Microsoft.Singularity.Applications { } } - class Test { + class Test + { const int NUM = 21; @@ -39,7 +41,7 @@ namespace Microsoft.Singularity.Applications { VectorQueue vq1 = new VectorQueue(); VectorQueue vq2 = new VectorQueue(); - for (int i=0; i<=Test.NUM; i++) { + for (int i = 0; i <= Test.NUM; i++) { int[] in ExHeap v = new[ExHeap] int[i]; vq1.AddTail(v); } diff --git a/base/Applications/Tests/Varargs/Varargs.sg b/base/Applications/Tests/Varargs/Varargs.sg index d7f566a..a3a0880 100644 --- a/base/Applications/Tests/Varargs/Varargs.sg +++ b/base/Applications/Tests/Varargs/Varargs.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Varargs.sg -// // Note: Test __arglist parameters // using System; @@ -17,7 +15,7 @@ namespace Microsoft.Singularity.Applications { public static int Main(String[]! args) { - string name = "VarArgs.x86"; + string name = "VarArgs"; int len = 1; int code = Printf("Invoked command with name '%s' and %d arguments", __arglist(name, len)); Console.WriteLine("Varargs exiting."); diff --git a/base/Applications/Tests/Verify/Verify.cs b/base/Applications/Tests/Verify/Verify.cs index d676e45..e6a4882 100644 --- a/base/Applications/Tests/Verify/Verify.cs +++ b/base/Applications/Tests/Verify/Verify.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Verify.cs -// // Note: Simple Singularity test program. // using System; @@ -18,7 +16,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] diff --git a/base/Applications/Tests/Wait/Wait.cs b/base/Applications/Tests/Wait/Wait.cs index c7ecb18..0da2400 100644 --- a/base/Applications/Tests/Wait/Wait.cs +++ b/base/Applications/Tests/Wait/Wait.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Wait.cs -// // Note: Simple Singularity test program. // using System; @@ -19,7 +17,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] diff --git a/base/Applications/Tests/WaitTest/WaitTest.cs b/base/Applications/Tests/WaitTest/WaitTest.cs index 4045917..f3d8b29 100644 --- a/base/Applications/Tests/WaitTest/WaitTest.cs +++ b/base/Applications/Tests/WaitTest/WaitTest.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: WaitTest.cs -// // Note: Simple Singularity test program. // using System; @@ -20,7 +18,8 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.Singularity.Applications { +namespace Microsoft.Singularity.Applications +{ [ConsoleCategory(DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] @@ -103,7 +102,7 @@ namespace Microsoft.Singularity.Applications { Console.WriteLine("Queue: {0}\n", q); #endif - for(int i=0; i < 5; i++) { + for (int i = 0; i < 5; i++) { Console.WriteLine("Thread One About To Sleep " + ProcessService.GetUpTime().Ticks); Thread.Sleep(TimeSpan.FromMilliseconds(1000)); Console.WriteLine("Thread One Waking Up " + ProcessService.GetUpTime().Ticks); @@ -113,7 +112,7 @@ namespace Microsoft.Singularity.Applications { } public static void WaitThreadTwo() { Console.WriteLine("Starting Wait Thread Two"); - for(int i=0; i < 10; i++) { + for (int i = 0; i < 10; i++) { Console.WriteLine("Thread Two About To Sleep " + ProcessService.GetUpTime().Ticks); Thread.Sleep(TimeSpan.FromMilliseconds(30)); Console.WriteLine("Thread Two Waking Up " + ProcessService.GetUpTime().Ticks); diff --git a/base/Applications/TftpClient/tftp.cs b/base/Applications/TftpClient/tftp.cs index 80d7210..0b433a6 100644 --- a/base/Applications/TftpClient/tftp.cs +++ b/base/Applications/TftpClient/tftp.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: TftpClient.cs -// // Note: // @@ -99,13 +97,12 @@ namespace Microsoft.Singularity.Applications int len; int rc; - if (null != localName) - { + if (null != localName) { Console.WriteLine ("Creating file "+localName); success = FileUtils.CreateFile(localName); if (0 == success) { persistHandle = FileUtils.OpenFile(localName); - if (null == persistHandle){ + if (null == persistHandle) { Console.WriteLine("unable to open file " + localName); return ""; } @@ -128,43 +125,34 @@ namespace Microsoft.Singularity.Applications //string str = null; int count = 0; int lastBlock = -1; - while (!done) - { + while (!done) { //Socket.Select(listenList, null, null, 1000); - if (s.Poll(1000000, SelectMode.SelectRead)) - { + if (s.Poll(1000000, SelectMode.SelectRead)) { rc = s.Receive(reply); if (verboseMode) Console.WriteLine("Receive rc={0}", rc); - if (rc >= 4) - { + if (rc >= 4) { TftpHdr rh = new TftpHdr(reply, 0); //Console.WriteLine("Opcode {0} Arg {1} rc={2} curPos={3}", rh.Opcode, rh.Arg, rc, curPos); if (rh.Arg == lastBlock) Console.WriteLine(" duplicate block? block ="+rh.Arg); lastBlock = rh.Arg; - if (rh.Opcode == TftpOpcode.DATA) - { - if (!verboseMode) - { + if (rh.Opcode == TftpOpcode.DATA) { + if (!verboseMode) { Console.Write("."); - if (count++ > 50) - { + if (count++ > 50) { count = 0; Console.Write("\n"); } } - else - { + else { //str = Encoding.ASCII.GetString(reply, 4, rc-4); //Console.WriteLine("pos="+curPos+" str="+str); //str = null; } - if (persistHandle != null) - { + if (persistHandle != null) { Bitter.FromByteArray(buf,0,rc-4,reply,4); persistHandle.SendWrite(buf,0,curPos,rc-4); - switch receive - { + switch receive { case persistHandle.AckWrite( _buf, bytesWritten, error) : buf = _buf; curPos = rh.Arg * 512; @@ -188,10 +176,9 @@ namespace Microsoft.Singularity.Applications hdr.Marshal(request, 0); len = 4; } - if (rc < 512+4) done = true; + if (rc < 512 + 4) done = true; } - else - { + else { //Console.WriteLine("poll for data failed!"); //return null; } @@ -199,8 +186,7 @@ namespace Microsoft.Singularity.Applications if (verboseMode)Console.WriteLine("Sent {0} {1} rc {2}", hdr.Opcode, hdr.Arg, rc); //Thread.Sleep(1000); } - if (persistHandle != null) - { + if (persistHandle != null) { Console.WriteLine("Closing EP handle via delete"); delete persistHandle; } @@ -218,27 +204,26 @@ namespace Microsoft.Singularity.Applications int len; int rc; - long fileLength =0; - NodeType nodeType; + long fileLength = 0; ErrorCode errorOut; - if (null != localName) - { + if (null != localName) { DirectoryServiceContract.Imp! rootNS = DirectoryService.NewClientEndpoint(); - bool ok = FileUtils.GetAttributes(localName, rootNS, out fileLength, out nodeType, out errorOut); + FileAttributesRecord record; + bool ok = FileUtils.GetAttributes(localName, rootNS, out record, out errorOut); delete rootNS; - if (!ok) - { + if (!ok) { Console.WriteLine("Unable to open file ({0}) for send. reason:{1}", localName, SdsUtils.ErrorCodeToString(errorOut) ); return null; } + fileLength = record.FileSize; + fileHandle = FileUtils.OpenFile(localName); - if (null == fileHandle) - { + if (null == fileHandle) { Console.WriteLine("Unable to open file "+localName+" for send"); return null; } @@ -256,32 +241,26 @@ namespace Microsoft.Singularity.Applications int threshold = 20; int waitCount = 0; Console.WriteLine(" file size =" + fileLength); - while (!done) - { + while (!done) { //Socket.Select(listenList, null, null, 1000); - if (s.Poll(100000, SelectMode.SelectRead)) - { + if (s.Poll(100000, SelectMode.SelectRead)) { rc = s.Receive(reply); Console.WriteLine("Receive rc={0}", rc); - if (rc >= 4) - { + if (rc >= 4) { TftpHdr rh = new TftpHdr(reply, 0); Console.WriteLine("PUT: Opcode {0} Arg {1}", rh.Opcode, rh.Arg); - if (rh.Opcode == TftpOpcode.ACK) - { + if (rh.Opcode == TftpOpcode.ACK) { int datalen; int error; long bytesRead; Console.WriteLine("ACK for block {0}, pos={1}", rh.Arg,pos); waitCount = 0; - if (pos >= fileLength) - { + if (pos >= fileLength) { Console.WriteLine(" here"); bytesRead = 0; } - else - { + else { if (fileHandle == null) throw new Exception("fileHandle is null when I want to SendRead"); fileHandle.SendRead(buf, 0, pos, 512); @@ -302,8 +281,7 @@ namespace Microsoft.Singularity.Applications Bitter.ToByteArray(buf,0,datalen,request,4); len = 4 + datalen; } - else - { + else { Console.WriteLine("NOT AN ACK!\n"); DebugStub.Break(); } @@ -311,12 +289,10 @@ namespace Microsoft.Singularity.Applications rc = s.Send(request, len, SocketFlags.None); Console.WriteLine("Sent {0} {1} rc {2}", hdr.Opcode, hdr.Arg, rc); } - else - { + else { Console.WriteLine("Waiting."); waitCount++; - if (waitCount > threshold) - { + if (waitCount > threshold) { Console.WriteLine (" exceeded timeout threshold"); done = true; break; @@ -333,19 +309,18 @@ namespace Microsoft.Singularity.Applications private static IPAddress GetServerAddress(string server) { - try - { + try { return IPAddress.Parse(server); } - catch - {} + catch { + } try { IPHostEntry he = Dns.GetHostByName(server); return he.AddressList[0]; } - catch - {} + catch { + } return null; } @@ -361,29 +336,25 @@ namespace Microsoft.Singularity.Applications { bool verboseMode = false; String localName, remoteName; - if (args.Length < 4 || args.Length > 6) - { + if (args.Length < 4 || args.Length > 6) { Usage(); return 1; } IPAddress server = GetServerAddress(args[1]); - if (server == null) - { + if (server == null) { Console.WriteLine("Could not find server : {0}", args[1]); return 2; } int pos =2; - if (args[pos] == "-v") - { + if (args[pos] == "-v") { verboseMode = true; pos++; } bool doGet; - switch (args[pos]) - { + switch (args[pos]) { case "get": doGet = true; break; @@ -398,23 +369,19 @@ namespace Microsoft.Singularity.Applications remoteName = (!)args[pos]; pos++; localName = null; - if (pos >= args.Length) - { + if (pos >= args.Length) { Console.WriteLine("no destination given"); } - else - { + else { localName = args[pos]; } Console.WriteLine("doGet="+doGet+" src="+remoteName+" dest="+localName); TftpClient tftpClient = new TftpClient(server); - if (doGet) - { + if (doGet) { string contents = tftpClient.Get(remoteName,localName,verboseMode); if (verboseMode) Console.WriteLine("Contents:\n{0}", contents); } - else - { + else { //put tftpClient.Put(remoteName,localName); } diff --git a/base/Applications/Transforms/AppTransforms.csproj b/base/Applications/Transforms/AppTransforms.csproj index be09bfa..486fad6 100644 --- a/base/Applications/Transforms/AppTransforms.csproj +++ b/base/Applications/Transforms/AppTransforms.csproj @@ -1,8 +1,6 @@  + + + + + + Exe + MapDemoWebApp + true + true + + + + + + + + + + + + + + + + + + + + + + + + + Microsoft.Singularity.WebApps + MainMapPage + HTMLData + + + + Microsoft.Singularity.WebApps + SeattleAllMapPage + HTMLData + + + + Microsoft.Singularity.WebApps + SeattleStarMapPage + HTMLData + + + + Microsoft.Singularity.WebApps + SeattleSBCMapPage + HTMLData + + + + Microsoft.Singularity.WebApps + SeattleTullysMapPage + HTMLData + + + + + Microsoft.Singularity.WebApps + RedmondAllMapPage + HTMLData + + + + Microsoft.Singularity.WebApps + RedmondStarMapPage + HTMLData + + + + Microsoft.Singularity.WebApps + RedmondSBCMapPage + HTMLData + + + + Microsoft.Singularity.WebApps + RedmondTullysMapPage + HTMLData + + + + Microsoft.Singularity.WebApps + BlueIcon + ImageData + + + + Microsoft.Singularity.WebApps + RedIcon + ImageData + + + + Microsoft.Singularity.WebApps + GreenIcon + ImageData + + + + Microsoft.Singularity.WebApps + TowerIcon + ImageData + + + + Microsoft.Singularity.WebApps + VePage1 + HTMLData + + + + Microsoft.Singularity.WebApps + VePage2 + HTMLData + + + + Microsoft.Singularity.WebApps + VePage3 + HTMLData + + + + Microsoft.Singularity.WebApps + VePage + HTMLData + + + + Microsoft.Singularity.WebApps + MapControl + HTMLData + + + + + + + diff --git a/base/Applications/WebApps/MapDemo/blue.bmp b/base/Applications/WebApps/MapDemo/blue.bmp new file mode 100644 index 0000000..6d256e5 Binary files /dev/null and b/base/Applications/WebApps/MapDemo/blue.bmp differ diff --git a/base/Applications/WebApps/MapDemo/green.bmp b/base/Applications/WebApps/MapDemo/green.bmp new file mode 100644 index 0000000..ce07fe3 Binary files /dev/null and b/base/Applications/WebApps/MapDemo/green.bmp differ diff --git a/base/Applications/WebApps/MapDemo/mainpage.htm b/base/Applications/WebApps/MapDemo/mainpage.htm new file mode 100644 index 0000000..39a413b --- /dev/null +++ b/base/Applications/WebApps/MapDemo/mainpage.htm @@ -0,0 +1,16 @@ + + +Caffeinate me! + + + +

In Search of Caffeine

+ + +

Caffeinate me, I'm:

+
    +
  • At work at Microsoft Research
  • +
  • Napping on the couch in Seattle
  • + + + \ No newline at end of file diff --git a/base/Applications/WebApps/MapDemo/red.bmp b/base/Applications/WebApps/MapDemo/red.bmp new file mode 100644 index 0000000..57ffeeb Binary files /dev/null and b/base/Applications/WebApps/MapDemo/red.bmp differ diff --git a/base/Applications/WebApps/MapDemo/redmond_all.htm b/base/Applications/WebApps/MapDemo/redmond_all.htm new file mode 100644 index 0000000..b92621e --- /dev/null +++ b/base/Applications/WebApps/MapDemo/redmond_all.htm @@ -0,0 +1,42 @@ + + +Caffeinate me! + + + + + + +

    + + + + + + + +
    +

    In Search of Caffeine

    + + +

    Looks like a drive to Redmond is in your future... + +

    +

    + + + + \ No newline at end of file diff --git a/base/Applications/WebApps/MapDemo/redmond_sbc.htm b/base/Applications/WebApps/MapDemo/redmond_sbc.htm new file mode 100644 index 0000000..39cc958 --- /dev/null +++ b/base/Applications/WebApps/MapDemo/redmond_sbc.htm @@ -0,0 +1,41 @@ + + +Caffeinate me! + + + + + + +

    + + + + + + + +
    +

    In Search of Caffeine

    + + +

    Looks like a drive to Redmond is in your future... + +

    + Seattle's Best Coffee +
    Show all chains +
    Take me to Seattle instead +
    +

    + + + + \ No newline at end of file diff --git a/base/Applications/WebApps/MapDemo/redmond_star.htm b/base/Applications/WebApps/MapDemo/redmond_star.htm new file mode 100644 index 0000000..0243720 --- /dev/null +++ b/base/Applications/WebApps/MapDemo/redmond_star.htm @@ -0,0 +1,41 @@ + + +Caffeinate me! + + + + + + +

    + + + + + + + +
    +

    In Search of Caffeine

    + + +

    Looks like a drive to Redmond is in your future... + +

    +

    + + + + \ No newline at end of file diff --git a/base/Applications/WebApps/MapDemo/redmond_tullys.htm b/base/Applications/WebApps/MapDemo/redmond_tullys.htm new file mode 100644 index 0000000..493c531 --- /dev/null +++ b/base/Applications/WebApps/MapDemo/redmond_tullys.htm @@ -0,0 +1,41 @@ + + +Caffeinate me! + + + + + + +

    + + + + + + + +
    +

    In Search of Caffeine

    + + +

    Looks like a drive to Redmond is in your future... + +

    +

    + + + + \ No newline at end of file diff --git a/base/Applications/WebApps/MapDemo/seattle_all.htm b/base/Applications/WebApps/MapDemo/seattle_all.htm new file mode 100644 index 0000000..dc3d961 --- /dev/null +++ b/base/Applications/WebApps/MapDemo/seattle_all.htm @@ -0,0 +1,43 @@ + + +Caffeinate me! + + + + + + +

    + + + + + + + +
    +

    In Search of Caffeine

    + + +

    Looks like you have quite a few choices... +
    + +

    +

    + + + \ No newline at end of file diff --git a/base/Applications/WebApps/MapDemo/seattle_sbc.htm b/base/Applications/WebApps/MapDemo/seattle_sbc.htm new file mode 100644 index 0000000..780e7eb --- /dev/null +++ b/base/Applications/WebApps/MapDemo/seattle_sbc.htm @@ -0,0 +1,41 @@ + + +Caffeinate me! + + + + + + +

    + + + + + + + +
    +

    In Search of Caffeine

    + + +

    Is Seattle's Best Coffee either from Seattle, or its best? Discuss. +
    + +

    + Seattle's Best Coffee +
    Show all chains +
    Take me to Redmond instead +
    +

    + + + \ No newline at end of file diff --git a/base/Applications/WebApps/MapDemo/seattle_star.htm b/base/Applications/WebApps/MapDemo/seattle_star.htm new file mode 100644 index 0000000..486cafe --- /dev/null +++ b/base/Applications/WebApps/MapDemo/seattle_star.htm @@ -0,0 +1,41 @@ + + +Caffeinate me! + + + + + + +

    + + + + + + + +
    +

    In Search of Caffeine

    + + +

    The original Starbucks location is in Pike Place Market +
    + +

    +

    + + + \ No newline at end of file diff --git a/base/Applications/WebApps/MapDemo/seattle_tullys.htm b/base/Applications/WebApps/MapDemo/seattle_tullys.htm new file mode 100644 index 0000000..0ea6a9a --- /dev/null +++ b/base/Applications/WebApps/MapDemo/seattle_tullys.htm @@ -0,0 +1,42 @@ + + +Caffeinate me! + + + + + + +

    + + + + + + + +
    +

    In Search of Caffeine

    + + +

    Looks like you have quite a few choices... +
    + +

    + Tully's +
    Tully's with wireless +
    Show all chains +
    Take me to Redmond instead +
    +

    + + + \ No newline at end of file diff --git a/base/Applications/WebApps/MapDemo/tower.bmp b/base/Applications/WebApps/MapDemo/tower.bmp new file mode 100644 index 0000000..70ce45f Binary files /dev/null and b/base/Applications/WebApps/MapDemo/tower.bmp differ diff --git a/base/Applications/WebApps/MapDemo/ve.aspx b/base/Applications/WebApps/MapDemo/ve.aspx new file mode 100644 index 0000000..4ac9650 --- /dev/null +++ b/base/Applications/WebApps/MapDemo/ve.aspx @@ -0,0 +1,136 @@ + + + + + + +Caffeinate me! + + + + + + + + + + + + + + +

    In Search of Caffeine

    + + + + + +
    +
    + Locations: +
    >">Redmond / Microsoft +
    >">Seattle / Home +

    + Coffee: +
    &search=starbucks">Starbucks +
    &search=tullys">Tully's +
    &search=all">All chains +

    + Traffic: +
    Heavy
    +
    Light
    +

    +
    +
    +
    +
    + + diff --git a/base/Applications/WebApps/MapDemo/ve1.aspx b/base/Applications/WebApps/MapDemo/ve1.aspx new file mode 100644 index 0000000..7084647 --- /dev/null +++ b/base/Applications/WebApps/MapDemo/ve1.aspx @@ -0,0 +1,92 @@ + + + + + + +VE Demo 1 + + + + + + + + + + + +

    VE Demo 1 - Virtual Earth Hosting

    + + + + + +
    +
    + Locations: +
    > Redmond / Microsoft +
    > Seattle / Home +

    +
    Next demo +
    +
    +
    +
    + + diff --git a/base/Applications/WebApps/MapDemo/ve2.aspx b/base/Applications/WebApps/MapDemo/ve2.aspx new file mode 100644 index 0000000..e9af801 --- /dev/null +++ b/base/Applications/WebApps/MapDemo/ve2.aspx @@ -0,0 +1,107 @@ + + + + + + +VE Demo 2 + + + + + + + + + + + + + +

    VE Demo 2 - Local Search on Virtual Earth

    + + + + + +
    + + +
    +
    + + diff --git a/base/Applications/WebApps/MapDemo/ve3.aspx b/base/Applications/WebApps/MapDemo/ve3.aspx new file mode 100644 index 0000000..4b07705 --- /dev/null +++ b/base/Applications/WebApps/MapDemo/ve3.aspx @@ -0,0 +1,138 @@ + + + + + + +VE Demo 3 + + + + + + + + + + + + + + + +

    VE Demo 3 - Traffic and Search on Virtual Earth

    + + + + + +
    +
    + Locations: +
    >">Redmond / Microsoft +
    >">Seattle / Home +

    + Coffee: +
    &search=starbucks">Starbucks +
    &search=tullys">Tully's +
    &search=all">All chains +

    + Traffic: +
    Heavy
    +
    Light
    +

    +
    +
    +
    +
    + + diff --git a/base/Applications/WebApps/SPECweb99/CachedFile.sg b/base/Applications/WebApps/SPECweb99/CachedFile.sg index d25ab1c..c9db26e 100644 --- a/base/Applications/WebApps/SPECweb99/CachedFile.sg +++ b/base/Applications/WebApps/SPECweb99/CachedFile.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: CachedFile.sg -// // Note: CachedFile is intended to hold copies of Custom.Ads // and User.Profiles in memory. // diff --git a/base/Applications/WebApps/SPECweb99/README.txt b/base/Applications/WebApps/SPECweb99/README.txt index be4295e..78836fa 100644 --- a/base/Applications/WebApps/SPECweb99/README.txt +++ b/base/Applications/WebApps/SPECweb99/README.txt @@ -1,8 +1,3 @@ -From: Mark Aiken -Sent: Sunday, October 16, 2005 4:19 PM -To: Chris Hawblitzel -Subject: Running SPECweb - Here is how to run SPECweb: Build: @@ -13,7 +8,7 @@ Build: - Distro On Windows: -Run \\maiken\pub\SPECweb99.exe. Install as "prime client" +Run SPECweb99.exe. Install as "prime client" Copy the rc.sing file into "c:\SPECweb99" Edit the attached file to point to your Singularity machine (you will need to get a DHCP lease as described below, first. diff --git a/base/Applications/WebApps/SPECweb99/SPECWeb99.csproj b/base/Applications/WebApps/SPECweb99/SPECWeb99.csproj index 0198007..972e9c0 100644 --- a/base/Applications/WebApps/SPECweb99/SPECWeb99.csproj +++ b/base/Applications/WebApps/SPECweb99/SPECWeb99.csproj @@ -1,8 +1,6 @@  + + + + + + Exe + SPECWeb99WebAppIN + true + true + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/WebApps/SPECweb99in/SPECWeb99in_app.csproj b/base/Applications/WebApps/SPECweb99in/SPECWeb99in_app.csproj new file mode 100644 index 0000000..808568f --- /dev/null +++ b/base/Applications/WebApps/SPECweb99in/SPECWeb99in_app.csproj @@ -0,0 +1,37 @@ + + + + + + + Exe + SPECWeb99WebAppIN + true + true + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/WebApps/ServiceManager/ServiceManagerWebApp.csproj b/base/Applications/WebApps/ServiceManager/ServiceManagerWebApp.csproj new file mode 100644 index 0000000..daf6bb5 --- /dev/null +++ b/base/Applications/WebApps/ServiceManager/ServiceManagerWebApp.csproj @@ -0,0 +1,35 @@ + + + + + + Exe + ServiceManagerWebApp + true + {6FF50C76-A677-44BD-A178-B9F556686BBD} + + + + + + + + + + + + + + + + + + + + diff --git a/base/Applications/WebApps/ServiceManager/ServiceManagerWebApp.sg b/base/Applications/WebApps/ServiceManager/ServiceManagerWebApp.sg new file mode 100644 index 0000000..d338fb8 --- /dev/null +++ b/base/Applications/WebApps/ServiceManager/ServiceManagerWebApp.sg @@ -0,0 +1,790 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Text; +using System.Web; + +using Microsoft.SingSharp; +using Microsoft.SingSharp.Runtime; +using Microsoft.Singularity.Diagnostics.Contracts; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.WebApps; +using Microsoft.Singularity.WebApps.Contracts; +using Microsoft.Singularity.PingPong.Contracts; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.ServiceManager; + +[assembly: Microsoft.SingSharp.Reflection.Transform(typeof(WebAppResourceTransform))] + +namespace Microsoft.Singularity.WebApps +{ + [Category("WebApp")] + internal sealed class Parameters + { + [Endpoint] + public readonly TRef webAppRef; + + + reflective private Parameters(); + } + + class ServiceManagerWebApp : IWebApp, IDisposable + { + const string dbgprefix = "ServiceManagerWebApp: "; + + readonly TRef! rootdsRef; + readonly TRef! svmanagerRef; + readonly Hashtable/**/! urlHandlers = new Hashtable(); + readonly Hashtable/**/! fileHandlers = new Hashtable(); + + + readonly Encoding! encoding; + readonly byte[]! newlineBytes; + + [Microsoft.Contracts.NotDelayed] + ServiceManagerWebApp() + { + DirectoryServiceContract.Imp! rootds = DirectoryService.NewClientEndpoint(); + + // First, connect to SCM. + + ServiceManagerContract.Imp! svmanager = ConnectServiceManager(rootds); + + this.rootdsRef = new TRef(rootds); + this.svmanagerRef = new TRef(svmanager); + + Encoding encoding = Encoding.UTF8; + this.encoding = encoding; + this.newlineBytes = encoding.GetBytes("\r\n"); + + base(); + + this.urlHandlers["/"] = new UrlHandler(this.RootPageHandler); + this.fileHandlers["/style.css"] = new FileEntry("/init/sv_style.css", "text/css"); + } + + + void Run(Parameters! config) + { + Dbg("Calling Driver.ServiceChannel"); + WebAppContract.Exp! webappChannel = config.webAppRef.Acquire(); + webappChannel.SendWebAppReady(); + Driver.ServiceChannel(this, webappChannel); + } + + internal static int AppMain(Parameters! config) + { + Dbg("Starting"); + + using (ServiceManagerWebApp app = new ServiceManagerWebApp()) { + app.Run(config); + } + + return 0; + } + + public void Dispose() + { + + } + + static ServiceManagerContract.Imp! ConnectServiceManager(DirectoryServiceContract.Imp! rootds) + { + ServiceManagerContract.Imp! svmanager_imp; + ServiceManagerContract.Exp! svmanager_exp; + ServiceManagerContract.NewChannel(out svmanager_imp, out svmanager_exp); + + ErrorCode error; + if (!SdsUtils.Bind("/service/services", rootds, svmanager_exp, out error)) { + delete svmanager_imp; + throw new Exception("Failed to connect to Service Manager."); + } + + svmanager_imp.RecvSuccess(); + + return svmanager_imp; + } + + public void ProcessRequest(IHttpRequest! request) + { + try { + string! path = request.GetUriPath(); + string verb = request.GetVerb(); + + if (verb == null) + throw new InvalidRequestException("Invalid verb"); + + UrlHandler handler = (UrlHandler)urlHandlers[path]; + if (handler != null) { + Dbg("Received ProcessRequest - " + path); + handler(request); + return; + } + + FileEntry file_entry = (FileEntry)fileHandlers[path]; + if (file_entry != null) { + Dbg("Received ProcessRequest - " + path + " --> file " + file_entry.LocalPath); + + if (verb != "GET") { + throw new InvalidRequestException("Invalid verb"); + } + SendFile(request, file_entry.LocalPath, file_entry.ContentType); + return; + } + + Dbg("No handler for path '{0}'.", path); + request.SendStatus(404, "Not Found"); + + } + catch (InvalidRequestException ex) { + Dbg("Invalid request: " + ex.Message); + request.SendStatus(500, ex.Message); + } + catch (Exception ex) { + Dbg("Exception: " + ex.GetType().FullName + ": " + ex.Message); + DebugStub.Break(); + } + finally { + request.Done(); + } + } + + ServiceManagerContract.Imp! AcquireServiceManager() + { + return this.svmanagerRef.Acquire(); + } + + void ReleaseServiceManager([Claims]ServiceManagerContract.Imp! svmanager) + { + this.svmanagerRef.Release(svmanager); + } + + class RequestFormData + { + public readonly StringDictionary Values = new StringDictionary(); + + public string! this[string! name] + { + get { + string value = this.Values[name]; + if (value == null) + return ""; + else + return value; + } + } + + public bool IsSet(string! name) + { + string value = this.Values[name]; + return value != null && value.Length != 0; + } + } + + const string ContentTypeHeaderName = "Content-Type"; + const string UrlEncodedContentType = "application/x-www-form-urlencoded"; + + RequestFormData! GetFormData(IHttpRequest! request) + { + Dbg("Parsing form data..."); + + RequestFormData data = new RequestFormData(); + + string contentType = request.GetHeader(ContentTypeHeaderName); + if (contentType == null) { + Dbg("No content type. Assuming form data is not present."); + return data; + } + + if (String.Compare(contentType, UrlEncodedContentType, true) != 0) { + Dbg("Content type is not '{0}'; it's '{1}', which is not recognized.", UrlEncodedContentType, contentType); + return data; + } + + Dbg("Content type is correct."); + + byte[]! body_data = request.GetBodyData(); + + string! urlEncodedText = Encoding.UTF8.GetString(body_data); + Dbg("Decoding: " + urlEncodedText); + + string[]! fields = urlEncodedText.Split('&'); + foreach (string field in fields) { + if (field == null) + continue; + int equals_index = field.IndexOf('='); + if (equals_index == -1) { + Dbg("field is invalid: " + field); + continue; + } + + string! name_encoded = field.Substring(0, equals_index); + string! value_encoded = field.Substring(equals_index + 1); + string! name_decoded = (!)HttpUtility.UrlDecode(name_encoded); + string! value_decoded = (!)HttpUtility.UrlDecode(value_encoded); + data.Values[name_decoded] = value_decoded; + Dbg("found field '{0}' value '{1}'", name_decoded, value_decoded); + } + + return data; + } + + bool SelectService(ServiceManagerContract.Imp! svmanager, string! serviceName) + { + svmanager.SendSelectService(Bitter.FromString2(serviceName)); + + switch receive { + case svmanager.Ok(): + return true; + + case svmanager.RequestFailed(error): + Dbg("Failed to select service '{0}': {1}", serviceName, ServiceEnums.ToString(error)); + return false; + + case svmanager.ChannelClosed(): + Dbg("Service Manager closed channel!"); + return false; + } + } + + void StartService(string! serviceName) + { + Dbg("Starting service '{0}'...", serviceName); + + ServiceManagerContract.Imp! svmanager = AcquireServiceManager(); + + if (!SelectService(svmanager, serviceName)) + return; + + svmanager.SendStartServiceNoWait(); + switch receive { + case svmanager.ServiceStarting(): + break; + + case svmanager.RequestFailed(error): + break; + } + + svmanager.SendUnselectService(); + + ReleaseServiceManager(svmanager); + } + + void StopService(string! serviceName) + { + Dbg("Stopping service '{0}'...", serviceName); + + ServiceManagerContract.Imp! svmanager = AcquireServiceManager(); + + if (!SelectService(svmanager, serviceName)) + return; + + svmanager.SendStopServiceNoWait(); + switch receive { + case svmanager.ServiceStopping(): + break; + + case svmanager.RequestFailed(error): + break; + } + + svmanager.SendUnselectService(); + + ReleaseServiceManager(svmanager); + } + + void EnableService(string! serviceName, bool enabled) + { + Dbg("Setting service enabled to {0} for service '{1}'...", enabled, serviceName); + + ServiceManagerContract.Imp! svmanager = AcquireServiceManager(); + + if (!SelectService(svmanager, serviceName)) + return; + + svmanager.SendEnableService(enabled); + switch receive { + case svmanager.Ok(): + Dbg("Service Manager accepted request to enable/disable service."); + break; + + case svmanager.RequestFailed(error): + Dbg("Service Manager FAILED request to enable/disable service: " + ServiceEnums.ToString(error)); + break; + } + + svmanager.SendUnselectService(); + + ReleaseServiceManager(svmanager); + } + + void RootPageHandler(IHttpRequest! request) + { + Dbg("RootPageHandler running"); + + // + // If the user clicked one of the state-control buttons, then check to see which one. + // Perform the requested action, then redirect to this page. This prevents the + // browser from interpreting "refresh" as "post the data again". + // + string! verb = request.GetVerb(); + if (verb == "POST") { + // User clicked one of our buttons. + Dbg("Verb is POST, user probably clicked a control button"); + RequestFormData! data = GetFormData(request); + + string! serviceName = data["ServiceName"]; + Dbg("Service name from form: " + serviceName); + + if (serviceName == "") { + Dbg("Service name is empty!"); + } + + if (data.IsSet("StartService")) { + StartService(serviceName); + } + else if (data.IsSet("StopService")) { + StopService(serviceName); + } + else if (data.IsSet("DisableService")) { + EnableService(serviceName, false); + } + else if (data.IsSet("EnableService")) { + EnableService(serviceName, false); + } + else { + Dbg("Request did not contain a recognized control action."); + } + + Dbg("Redirecting client"); + request.SendStatus(303, "See Other"); + request.SendHeader(HttpHeader.RedirectLocation, "/"); + return; + } + + request.SendStatus(200, "OK"); + request.SendHeader(HttpHeader.Refresh, "5"); // refresh every N seconds + + + ResponseWrite(request, +@" + +Singularity Service Manager + + + +"); + + ResponseWrite(request, "

    Services

    \r\n"); + + ServiceManagerContract.Imp! svmanager = AcquireServiceManager(); + + try { + + ServiceInfo[]! in ExHeap first_infos = new[ExHeap] ServiceInfo[40]; + + svmanager.SendEnumerateServices(first_infos); + + string![]! columns = { + "Service Name", // 0 + // "Display Name", // + // "Executable", // + "Activation Mode", // 1 + "State", // 2 + "Process ID", // 3 + "Actions" // 4 + }; + + string[] cells = new string[columns.Length]; + + ResponseWriteLine(request, "\r\n"); + + for (;;) { + + ServiceInfo[]! in ExHeap infos; + bool more; + int count; + + switch receive { + case svmanager.NextServiceInfo(returned_infos, c): + infos = returned_infos; + count = c; + more = true; + break; + + case svmanager.EnumerationTerminated(returned_infos, c): + infos = returned_infos; + more = false; + count = c; + break; + + case timeout(TimeSpan.FromSeconds(30)): + ResponseWriteLine(request, "

    Error: Timeout occurred while waiting to receive data from Service Manager.

    "); + goto done; + } + + if (count > 0) { + + for (int i = 0; i < count; i++) { + expose(infos[i]) { + string! serviceName = Bitter.ToString2(infos[i].Config.ServiceName); + //string! displayName = Bitter.ToString2(infos[i].Config.DisplayName); + //string! executableName = Bitter.ToString2(infos[i].Config.ExecutableName); + string! activationMode = ServiceEnums.ToString(infos[i].Config.ActivationMode); + + int c = 0; + cells[c++] = serviceName; + // cells[c++] = displayName; + // cells[c++] = executableName; + cells[c++] = activationMode; + cells[c++] = ServiceEnums.ToString(infos[i].Status.State); + cells[c++] = infos[i].Status.ProcessId.ToString(); + + StringBuilder! actions = new StringBuilder(); + + bool start_stop_applies = + infos[i].Config.ActivationMode == ServiceActivationMode.Manual + || infos[i].Config.ActivationMode == ServiceActivationMode.Demand; + ServiceState svstate = infos[i].Status.State; + + bool disabled = infos[i].Config.IsAdministrativelyDisabled; + bool can_start = start_stop_applies && svstate == ServiceState.Stopped && !disabled; + bool can_stop = start_stop_applies && (svstate == ServiceState.Running || svstate == ServiceState.Starting) && !disabled; + + actions.Append("\r\n"); + actions.AppendFormat("\r\n", serviceName); + actions.Append(MakeServiceControlButton("StartService", "Start", can_start)); + actions.Append(MakeServiceControlButton("StopService", "Stop", can_stop)); + actions.Append(MakeServiceControlButton("DisableService", "Disable", !disabled)); + actions.Append(MakeServiceControlButton("EnableService", "Enable", disabled)); + actions.Append("\r\n"); + + cells[c++] = actions.ToString(); + } + + ResponseWrite(request, ""); + } + } + + if (more) { + svmanager.SendEnumerateServices(infos); + } + else { + delete infos; + break; + } + } + + done: + ResponseWriteLine(request, "
    "); + for (int i = 0; i < columns.Length; i++) { + string! column = columns[i]; + ResponseWrite(request, column); + if (i + 1 < columns.Length) + ResponseWrite(request, ""); + } + ResponseWriteLine(request, "
    "); + for (int j = 0; j < cells.Length; j++) { + string cell = cells[j]; + if (cell == null) + cell = ""; + ResponseWrite(request, cell); + + if (j + 1 < cells.Length) + ResponseWrite(request, ""); + } + + ResponseWriteLine(request, "
    "); + + } + finally { + ReleaseServiceManager(svmanager); + } + } + + static string! MakeServiceControlButton(string! action, string! label, bool enabled) + { + return String.Format("\r\n", action, label, + enabled ? "" : " disabled"); + + } + + DirectoryServiceContract.Imp! AcquireDirectory() + { + return rootdsRef.Acquire(); + } + + void ReleaseDirectory([Claims]DirectoryServiceContract.Imp! rootds) + { + rootdsRef.Release(rootds); + } + + void SendFile(IHttpRequest! request, string! path, string! contentType) + { + FileContract.Imp! file; + FileContract.Exp! file_exp; + FileContract.NewChannel(out file, out file_exp); + + DirectoryServiceContract.Imp! rootds = AcquireDirectory(); + ErrorCode error; + if (!SdsUtils.Bind(path, rootds, file_exp, out error)) { + delete file; + + ReleaseDirectory(rootds); + + string! errorText = SdsUtils.ErrorCodeToString(error); + request.SendStatus(400, errorText); + request.SendHeader(HttpHeader.ContentType, ContentType.TextHtmlUtf8); + + StringBuilder! html = new StringBuilder(); + html.Append("Error - "); + html.Append(errorText); + html.Append("

    Error

    "); + html.AppendFormat("

    The file '{0}' could not be opened.

    ", path); + html.Append("

    Error: "); + html.Append(errorText); + html.Append("

    "); + html.Append(""); + return; + } + + file.RecvSuccess(); + + ReleaseDirectory(rootds); + + request.SendStatus(200, "OK"); + request.SendHeader("Content-Type", contentType); + + const int buffer_length = 4096; + byte[]! in ExHeap exbuf = new[ExHeap] byte[buffer_length]; + byte[]! localbuf = new byte[buffer_length]; + long file_offset = 0; + file.SendRead(exbuf, 0, file_offset, exbuf.Length); + + bool done = false; + + while (!done) { + + switch receive { + case file.AckRead(returned_exbuf, bytes_read, read_error): + if (error != 0) { + Dbg("A request to read on file '{0}' failed, error = {1}", path, read_error); + delete returned_exbuf; + done = true; + break; + } + + if (bytes_read < 0 || bytes_read > buffer_length) { + Dbg("Filesystem returned a ridiculous number of bytes transferred."); + delete returned_exbuf; + done = true; + break; + } + + if (returned_exbuf.Length != buffer_length) { + Dbg("Filesystem returned a buffer with different length?!"); + delete returned_exbuf; + done = true; + break; + } + + if (bytes_read == 0) { + Dbg("Received EOF from filesystem"); + delete returned_exbuf; + done = true; + break; + } + + // We know this will not overflow; we just checked it against buffer_length. + int bytes_read32 = (int)bytes_read; + + Dbg("Received {0} bytes from filesystem", bytes_read); + + // Grrrrrr. IHttpRequest needs to be improved. It does not take + // a length within a buffer; it just blats out an entire byte[] buffer. + if (bytes_read < buffer_length) { + byte[]! partial_buffer = new byte[bytes_read32]; + Bitter.ToByteArray(returned_exbuf, 0, bytes_read32, partial_buffer, 0); + request.SendBodyData(partial_buffer); + done = true; + } + else { + Bitter.ToByteArray(returned_exbuf, 0, bytes_read32, localbuf, 0); + request.SendBodyData(localbuf); + } + + file_offset += bytes_read; + file.SendRead(returned_exbuf, 0, file_offset, exbuf.Length); + break; + + case file.ChannelClosed(): + Dbg("Filesystem closed channel!"); + done = true; + break; + } + } + + Dbg("Done sending file."); + delete file; + } + + void SendOkHtmlHeader(IHttpRequest! request) + { + request.SendStatus(200, "OK"); + request.SendHeader("Content-Type", "text/html;charset=utf-8"); + } + + void ResponseWriteLine(IHttpRequest! request, string! format, params object[]! args) + { + ResponseWriteLine(request, String.Format(format, args)); + } + + void ResponseWriteLine(IHttpRequest! request, string! line) + { + if (line.Length != 0) { + byte[]! bytes = encoding.GetBytes(line); + request.SendBodyData(bytes); + } + request.SendBodyData(newlineBytes); + } + + void ResponseWrite(IHttpRequest! request, string! text) + { + if (text.Length != 0) { + byte[]! bytes = encoding.GetBytes(text); + request.SendBodyData(bytes); + } + } + + internal static void Dbg(string! line) + { + DebugStub.WriteLine(dbgprefix + line); + } + + internal static void Dbg(string! format, params object[]! args) + { + Dbg(String.Format(format, args)); + } + } + + delegate void UrlHandler(IHttpRequest! request); + + class FileEntry + { + public FileEntry(string! localPath, string! contentType) + { + this.LocalPath = localPath; + this.ContentType = contentType; + } + + public string! LocalPath; + public string! ContentType; + } + + class InvalidRequestException : Exception + { + public InvalidRequestException(string! msg) + : base(msg) + { + } + + public InvalidRequestException() + : base("The HTTP request is invalid; details are not available.") + { + } + } + + static class ContentType + { + public const string TextHtml = "text/html"; + public const string TextHtmlUtf8 = "text/html;charset=utf-8"; + } + + static class HttpHeader + { + public const string ContentType = "Content-Type"; + public const string RedirectLocation = "Location"; + public const string Refresh = "Refresh"; + } + +#if false + static class HttpUtility + { + public static string! UrlDecode(string! text) + { + int pos = 0; + while (true) { + if (pos == text.Length) { + // no escapes found, just return same string + return text; + } + + if (pos == '+' || pos == '%') { + // found an escape; must break this loop and process for real + break; + } + + // keep looking + pos++; + } + + StringBuilder! result = new StringBuilder(); + result.Append(text, 0, pos); + + while (pos < text.Length) { + char c = text[pos]; + pos++; + + if (c == '+') { + result.Append(' '); + } + else if (c == '%') { + if (pos + 2 <= text.Length) { + int high = GetHexValue(text[pos]); + int low = GetHexValue(text[pos + 1]); + + if (high != -1 && low != -1) { + pos += 2; + char hexc = (char)(high << 4 | low); + result.Append(hexc); + } + else { + // bogus escape sequence! + result.Append('%'); + } + } + else { + // too short for proper escape sequence! + result.Append('%'); + continue; + } + } + else { + result.Append(c); + } + } + } + + int GetHexValue(char c) + { + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return -1; + } + } +#endif + +} diff --git a/base/Applications/WebApps/ServiceManager/sv_style.css b/base/Applications/WebApps/ServiceManager/sv_style.css new file mode 100644 index 0000000..d243d09 --- /dev/null +++ b/base/Applications/WebApps/ServiceManager/sv_style.css @@ -0,0 +1,22 @@ +body +{ + font-family: "Verdana"; +} + +table +{ + border-collapse: collapse; +} + +th +{ + border: solid 1px black; + background-color: #c0e0e0; + padding: 3px; + margin: 0px; +} + +td +{ + border: solid 1px black ; +} \ No newline at end of file diff --git a/base/Applications/WebApps/Spew/Spew.csproj b/base/Applications/WebApps/Spew/Spew.csproj index 311f446..daecd2d 100644 --- a/base/Applications/WebApps/Spew/Spew.csproj +++ b/base/Applications/WebApps/Spew/Spew.csproj @@ -1,8 +1,6 @@  + diff --git a/base/Applications/WebApps/WebShell/Breaker.cs b/base/Applications/WebApps/WebShell/Breaker.cs new file mode 100644 index 0000000..ff03bd9 --- /dev/null +++ b/base/Applications/WebApps/WebShell/Breaker.cs @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// + +using System; +using System.Text; +using System.Runtime.CompilerServices; +using Microsoft.Singularity; + +namespace Microsoft.Singularity.Applications +{ + public class Breaker + { + private static Object ToObject(StringBuilder sb) + { + return (Object)sb; + } + + public static void Break(int how) + { + if (how == 2) { + Console.WriteLine("Breaking into kernel debugger with write to page zero."); + DebugStub.WriteLine("About to write to page zero."); + StringBuilder sb = new StringBuilder(128); + Object o = ToObject(sb); + unsafe { + byte *buffer = null; + *buffer = 0xff; + } + sb = new StringBuilder(256); + DebugStub.WriteLine("sb={0}, o={1}", __arglist(sb, o)); + } + else if (how == 1) { + Console.WriteLine("Breaking into kernel debugger with read from page zero."); + DebugStub.WriteLine("About to read from page zero."); + StringBuilder sb = new StringBuilder(128); + Object o = ToObject(sb); + byte b; + unsafe { + byte *buffer = null; + b = *buffer; + } + sb = new StringBuilder(256); + DebugStub.WriteLine("sb={0}, o={1}, b={2}", __arglist(sb, o, b)); + } + else if (how == 2) { + Console.WriteLine("Breaking into kernel debugger."); + DebugStub.WriteLine("About to break into kernel debugger"); + StringBuilder sb = new StringBuilder(128); + Object o = ToObject(sb); + + DebugStub.Break(); + sb = new StringBuilder(256); + DebugStub.WriteLine("sb={0}, o={1}", __arglist(sb, o)); + } + } + } +} diff --git a/base/Applications/WebHost/WebHost.csproj b/base/Applications/WebHost/WebHost.csproj new file mode 100644 index 0000000..82984d1 --- /dev/null +++ b/base/Applications/WebHost/WebHost.csproj @@ -0,0 +1,27 @@ + + + + + + + + WebHost + Exe + + 2614,2637,2638,2613,2639 + + + + + + + + + + + diff --git a/base/Applications/WebHost/WebHost.sg b/base/Applications/WebHost/WebHost.sg new file mode 100644 index 0000000..2f42666 --- /dev/null +++ b/base/Applications/WebHost/WebHost.sg @@ -0,0 +1,175 @@ +//------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All Rights Reserved. +//------------------------------------------------------------------------------ + + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Reflection; + +using Microsoft.Contracts; +using Microsoft.SingSharp.Reflection; +using Microsoft.SingSharp.Runtime; +using Microsoft.SingSharp; +using Microsoft.Singularity; +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Security; +using Microsoft.Singularity.WebApps.Contracts; +using Microsoft.Singularity.WebHost; + +[assembly: Transform(typeof(ApplicationResourceTransform))] +[assembly: ApplicationPublisherAttribute("singularity.microsoft.com")] +[assembly: AssertPrivilegeAttribute("$register-privilege.localhost")] + +[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.WebHost +{ + [ConsoleCategory(HelpMessage="webhost [options] A web server", + DefaultAction=true)] + internal sealed class Parameters + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [Endpoint] + public readonly TRef nsRef; + + [StringParameter("apparg", Default="", + HelpMessage="Arg to pass to client app")] + internal string appArg; + + [StringParameter("app", Mandatory=true, Default="", + HelpMessage="Name of the app to run")] + internal string app; + + [StringParameter("nspath", Mandatory=false, Default="/service/webapp", + HelpMessage="NS path to server")] + internal string nsPath; + + reflective internal Parameters(); + + internal int AppMain() { + return WebHost.AppMain(this); + } + } + + public sealed class WebHost + { + private static string[] m_childArgs; + + /////////////////////////////////////////////////////////////////////// + // + // The code below gets used when this webapp is compiled + // to a stand-alone executable + // + internal static int AppMain(Parameters! config) + { + DebugStub.WriteLine("WebHost.AppMain"); + + // Start up the webapp so it's ready to respond to requests + string[] args; + + if (config.appArg != null) { + args = new string[2]; + args[0] = config.app; + args[1] = config.appArg; + } + else { + args = new string[1]; + args[0] = config.app; + } + m_childArgs = args; + + if (config.nsPath != null) { + // Publish out connection endpoint. + DirectoryServiceContract.Imp ds = (config.nsRef).Acquire(); + if (ds == null) { + throw new Exception("WebHost: Unable to acquire handle to Directory Service."); + } + ds.RecvSuccess(); + + ServiceProviderContract.Imp! nsImp; + ServiceProviderContract.Exp! nsExp; + ServiceProviderContract.NewChannel(out nsImp, out nsExp); + + try { + ds.SendRegister(Bitter.FromString2(config.nsPath), nsImp); + + switch receive { + case ds.AckRegister() : + // All is well. + break; + + case ds.NakRegister(ServiceProviderContract.Imp:Start rejectedEP, error) : + // All is very much not well; abort. + Console.WriteLine("WebHost: Failed to register endpoint as {0}: error {1}.", + config.nsPath, error); + delete nsExp; + delete rejectedEP; + return -1; + + case ds.ChannelClosed(): + Console.WriteLine("WebHost: ds channel closed"); + delete nsExp; + return -1; + } + } + finally { + delete ds; + } + + Console.WriteLine("WebHost: Ready at {0}", config.nsPath); + + // Wait for the web server to bind to us. + for (;;) { + switch receive { + // ------------------------------- Requests for new connections + + case nsExp.Connect(ServiceContract.Exp:Start! newEp): + // We expect people to give us WebAppContract.Exp instances + WebAppContract.Exp appConnExp = newEp as WebAppContract.Exp; + if (appConnExp == null) { + // Invalid contract type. Fail. + nsExp.SendNackConnect(newEp); + } + else { + // Signal ready and start servicing this contract + nsExp.SendAckConnect(); + + Console.WriteLine("WebHost: Starting child."); + StartChild(appConnExp); + } + break; + + case nsExp.ChannelClosed(): + // The namespace channel is closed so quit. + delete nsExp; + return -1; + } + } + Console.WriteLine("WebHost: Server connected."); + delete nsExp; + } + return 0; + } + + public static void StartChild([Claims] WebAppContract.Exp:ProcessingState appConnExp) + { + Process child = new Process(m_childArgs, (Endpoint * in ExHeap)appConnExp); + child.Start(); + } + } +} diff --git a/base/Applications/WebServer/Application.sg b/base/Applications/WebServer/Application.sg index 58072a6..66aa62d 100644 --- a/base/Applications/WebServer/Application.sg +++ b/base/Applications/WebServer/Application.sg @@ -1,70 +1,87 @@ -////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Dispatcher.sg -// // Note: -// This file holds the Singularity-specific web app dispatcher that knows -// how to invoke web apps under Singularity. +// This file holds the Singularity-specific web application dispatcher +// logic that knows how to invoke web applications under Singularity. // using System; using System.Collections; -using Microsoft.SingSharp; -using Microsoft.SingSharp.Runtime; -using Microsoft.Singularity; +using System.Web; + using Microsoft.Singularity.Channels; -using Microsoft.Singularity.V1.Services; using Microsoft.Singularity.WebApps; using Microsoft.Singularity.WebApps.Contracts; -using System.Diagnostics; -using System.Web; namespace Microsoft.Singularity.WebServer { + /// + /// WEB Application class with static lookup/create method and request processor. + /// internal sealed class Application { + /// + /// Name to Application Cache, including failed-to-start appliccations. + /// private static Hashtable applications = new Hashtable(); + /// + /// Map an Request to an Application Name. + /// + /// + /// Currently, this method simply takes the FilePath after the leading slash. + /// + /// Eventually this should be based on manifests and + /// the mapping configuration of the WebServer which + /// are passed to the WebServer at startup. + /// + /// + private static string ApplicationNameFromRequest(Request! request) + { + return request.GetFilePath().Substring(1); + } + + /// + /// Return an Application or null given a Request. + /// + /// + /// Request, used here to determine particular Application. + /// + /// + /// Application (normally) or null (if Application not found or failed to start). + /// + /// + /// Keeps a cache of Applications (including non-starters). + /// public static Application FindOrCreateApplication(Request! request) { - // For now, we have an extremely lame way of mapping from a request - // to the appropriate application. We simply take the FilePath - // after the leading slash. That better be the name of a launchable - // application. - // - // TODO: Eventually this will be based on manifests and the mapping - // configuration of the WebServer, passed to the WebServer at - // startup. + string applicationName = Application.ApplicationNameFromRequest(request); + + Application application = (Application)applications[applicationName]; - string filePath = request.GetFilePath(); - string appName = filePath.Substring(1); - - Application app = (Application) applications[appName]; - - if (app == null) { - // No races around inserting unstarted applications into the hashtable. + if (application == null) { + // Assure no races during insertion of applications into the hashtable. lock (applications) { - // Perhaps it's there now. - app = (Application) applications[appName]; - if (app == null) { - app = new Application(appName); - - // Regardless of whether the app starts successfully, - // record it in our set of applications. - app.Start(); - applications[appName] = app; + // Retrieve again under lock in case another + // thread just added the same application. + application = (Application)applications[applicationName]; + if (application == null) { + // Create and record the application in the set of applications + // and then try to start it. This avoids repeated create/start + // attempts on every client request when Start() fails. + application = new Application(applicationName); + applications[applicationName] = application; + application.Start(); } } } - // If an app failed to start, we keep it in the hashtable so we - // don't keep re-trying on every client request. - return ((app != null && app.started) - ? app - : null); + + // return only sucessfully started applications (see above). + return (application.started) ? application : null; } private TRef wrapAppConn; @@ -72,16 +89,23 @@ namespace Microsoft.Singularity.WebServer private bool started; // statistics - private ulong totalServed = 0; - private ulong totalTime = 0; + private ulong totalServed = 0ul; + private ulong totalTime = 0ul; - public Application(string! name) + private Application(string! name) { this.name = name; this.started = false; base(); } + /// + /// Process given a Request. + /// + /// + /// Request, used here to determine particular Application. + /// Passed to the application for further analysis. + /// public void Process(Request! request) { ulong startTime = Processor.CycleCount; @@ -116,37 +140,37 @@ namespace Microsoft.Singularity.WebServer totalTime += Processor.CycleCount - startTime; } - public void Start() + private void Start() { - string[] args = new string[1]; - - args[0] = name; - WebAppContract.Imp! appConnImp; WebAppContract.Exp! appConnExp; WebAppContract.NewChannel(out appConnImp, out appConnExp); try { - Process child = new Process(args, (Endpoint * in ExHeap) appConnExp); + string[] processArguments = new string[] {name}; + Process child = new Process(processArguments, (Endpoint * in ExHeap) appConnExp); child.Start(); appConnImp.RecvWebAppReady(); wrapAppConn = new TRef(appConnImp); + started = true; } catch (Exception) { - // TODO: The exception story of Singularity needs work. For now, - // just record the fact that this app won't start. - return; + // TODO: The exception story of Singularity needs work. + // Since the member 'started' is initialized to 'false' + // do nothing here. Eventually log the failure w/ ETS. } - started = true; } + // TODO: Why is this subclass here when it only contains a single + // static method? Should it be merged with the Application class? internal class RequestExporter { - public static void ServiceRequestChannel(IHttpRequest! request, HttpRequestContract.Exp! conn) + public static void ServiceRequestChannel(IHttpRequest! request, + HttpRequestContract.Exp! conn) { bool done = false; - while(!done) { + while (!done) { switch receive { case conn.GetUriPath() : conn.SendUriPath(Bitter.FromString2(request.GetUriPath())); @@ -166,7 +190,8 @@ namespace Microsoft.Singularity.WebServer string headerVal = request.GetHeader(headerName); if (headerVal != null) { conn.SendHeaderValue(Bitter.FromString(headerVal)); - } else { + } + else { conn.SendHeaderValue(null); } break; @@ -176,7 +201,8 @@ namespace Microsoft.Singularity.WebServer if (bodyData != null) { conn.SendBodyData(Bitter.FromByteArray(bodyData)); - } else { + } + else { conn.SendBodyData(null); } break; @@ -194,7 +220,8 @@ namespace Microsoft.Singularity.WebServer } break; - case conn.SendHeader(char[]! in ExHeap nameChars, char[]! in ExHeap valChars) : + case conn.SendHeader(char[]! in ExHeap nameChars, + char[]! in ExHeap valChars) : { string name = Bitter.ToString(nameChars); delete nameChars; diff --git a/base/Applications/WebServer/ByteParser.sg b/base/Applications/WebServer/ByteParser.sg index 1a524a1..aebd017 100644 --- a/base/Applications/WebServer/ByteParser.sg +++ b/base/Applications/WebServer/ByteParser.sg @@ -1,8 +1,5 @@ //------------------------------------------------------------------------------ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Information Contained Herein is Proprietary and Confidential. -// +// Copyright (c) Microsoft Corporation. All Rights Reserved. //------------------------------------------------------------------------------ namespace Microsoft.Singularity.WebServer @@ -36,7 +33,7 @@ namespace Microsoft.Singularity.WebServer for (int i = pos; i < bytes.Length; i++) { if (bytes[i] == (byte)'\n') { int len = i-pos; - if (len > 0 && bytes[i-1] == (byte)'\r') { + if (len > 0 && bytes[i - 1] == (byte)'\r') { len--; } diff --git a/base/Applications/WebServer/ByteString.sg b/base/Applications/WebServer/ByteString.sg index d1cab9d..96bcf64 100644 --- a/base/Applications/WebServer/ByteString.sg +++ b/base/Applications/WebServer/ByteString.sg @@ -1,8 +1,5 @@ //------------------------------------------------------------------------------ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Information Contained Herein is Proprietary and Confidential. -// +// Copyright (c) Microsoft Corporation. All Rights Reserved. //------------------------------------------------------------------------------ namespace Microsoft.Singularity.WebServer diff --git a/base/Applications/WebServer/CommandLine.sg b/base/Applications/WebServer/CommandLine.sg index 05152e6..c0a508f 100644 --- a/base/Applications/WebServer/CommandLine.sg +++ b/base/Applications/WebServer/CommandLine.sg @@ -1,8 +1,5 @@ //------------------------------------------------------------------------------ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Information Contained Herein is Proprietary and Confidential. -// +// Copyright (c) Microsoft Corporation. All Rights Reserved. //------------------------------------------------------------------------------ namespace Microsoft.Singularity.WebServer diff --git a/base/Applications/WebServer/Connection.sg b/base/Applications/WebServer/Connection.sg index 30757ed..c4fc9a4 100644 --- a/base/Applications/WebServer/Connection.sg +++ b/base/Applications/WebServer/Connection.sg @@ -1,8 +1,5 @@ //------------------------------------------------------------------------------ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Information Contained Herein is Proprietary and Confidential. -// +// Copyright (c) Microsoft Corporation. All Rights Reserved. //------------------------------------------------------------------------------ namespace Microsoft.Singularity.WebServer diff --git a/base/Applications/WebServer/Listener.sg b/base/Applications/WebServer/Listener.sg index 9abb8a6..77b3c69 100644 --- a/base/Applications/WebServer/Listener.sg +++ b/base/Applications/WebServer/Listener.sg @@ -1,8 +1,5 @@ //------------------------------------------------------------------------------ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Information Contained Herein is Proprietary and Confidential. -// +// Copyright (c) Microsoft Corporation. All Rights Reserved. //------------------------------------------------------------------------------ namespace Microsoft.Singularity.WebServer @@ -19,13 +16,17 @@ namespace Microsoft.Singularity.WebServer internal sealed class Listener { - /* ======================= - The original version of Cassini uses the .NET thread pool to run - certain methods. There is currently no thread pool available on Singularity, - so I adapted Cassini to spin up threads on demand instead. These helper - objects assist with this; we could go back and remove them when - thread pools become available, if we wanted. - ======================= */ + // New-Connection to GC Ratio (Default). + // BUGBUG: When 992 and 1024 fixed, set to UInt64.Max or remove. + private const ulong DefaultConnectionToGCRatio = 256; + + // ======================= + // The original version of Cassini uses the .NET thread pool to run + // certain methods. There is currently no thread pool available on Singularity, + // so I adapted Cassini to spin up threads on demand instead. These helper + // objects assist with this; we could go back and remove them when + // thread pools become available, if we wanted. + // ======================= internal abstract class ThreadObject { @@ -73,22 +74,31 @@ namespace Microsoft.Singularity.WebServer } } - /* ======================= - end helper objects - ======================= */ + // ======================= + // end helper objects + // ======================= private int port; private string! clientIP; + private ulong connectionCount; + private ulong connectionToGCRatio; private bool shutdownInProgress; private Socket socket; - public Listener(int port, string! clientIP) + private bool silentMode, debugMode; + + public Listener(int port, string! clientIP, bool doSilent, bool doDebug) { this.port = port; this.clientIP = clientIP; + this.connectionCount = 0; + this.connectionToGCRatio = Listener.DefaultConnectionToGCRatio; + this.silentMode = doSilent; + this.debugMode = doDebug; + base(); } @@ -160,15 +170,18 @@ namespace Microsoft.Singularity.WebServer return; } - // Suck the request off the connection and process it. - Request request = new Request(conn); + // Retrieve the request from the connection and process it. + Request request = new Request(conn, silentMode, debugMode); request.Process(); - -#if GCPROFILE - // This isn't necessary. But it's very convenient if a GC follows processing - // each (single-threaded) request. - GC.Collect(); -#endif + + // Run the CG every 'N' connections to avoid Bugs 992 and 1024. + // Can be done at every connection if searching for memory leaks + connectionCount++; + if ((connectionCount % connectionToGCRatio) == 0ul) { + GC.Collect(); // Free memory and fill finalizer Q. + GC.WaitForPendingFinalizers(); // Dispose non-managed stuff + GC.Collect(); // Free memory release by finalizing + } } } @@ -179,7 +192,7 @@ namespace Microsoft.Singularity.WebServer if (socket != null) // ROTORTODO { Socket requestingSocket = socket.Accept(); - //ThreadPool.QueueUserWorkItem(_onSocketAccept, socket); + // ThreadPool.QueueUserWorkItem(_onSocketAccept, socket); (new AcceptThread(this, requestingSocket)).Start(); } } diff --git a/base/Applications/WebServer/LocalHttpRequest.sg b/base/Applications/WebServer/LocalHttpRequest.sg index 20d4068..cadd2f2 100644 --- a/base/Applications/WebServer/LocalHttpRequest.sg +++ b/base/Applications/WebServer/LocalHttpRequest.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: RemoteHttpRequest.sg -// // Note: // This file wraps a WebServer's Request object so it is callable through // the Microsoft.Singularity.WebApps.IHttpRequest interface that web @@ -48,7 +46,8 @@ namespace Microsoft.Singularity.WebServer if (knownIndex > 0) { return request.GetKnownRequestHeader(knownIndex); - } else { + } + else { return request.GetUnknownRequestHeader(headerName); } } @@ -59,7 +58,7 @@ namespace Microsoft.Singularity.WebServer // Caller should not modify this buffer! public byte[] GetBodyData() { - // NOTE We don't currently deal with the + // NOTE: We don't currently deal with the // possibility that there is more body data not // yet read in. assert request.IsEntireEntityBodyIsPreloaded(); diff --git a/base/Applications/WebServer/Main.sg b/base/Applications/WebServer/Main.sg index 8481753..7d9ba45 100644 --- a/base/Applications/WebServer/Main.sg +++ b/base/Applications/WebServer/Main.sg @@ -1,8 +1,5 @@ //------------------------------------------------------------------------------ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Information Contained Herein is Proprietary and Confidential. -// +// Copyright (c) Microsoft Corporation. All Rights Reserved. //------------------------------------------------------------------------------ namespace Microsoft.Singularity.WebServer @@ -20,6 +17,10 @@ namespace Microsoft.Singularity.WebServer public sealed class HttpServer { + // using debugMode allows status to be watched in debug window while + // webserver is running in console background, doesn't pollute console + private static bool silentMode, debugMode; + public static int Main(string[]! args) { #if GCPROFILE @@ -31,9 +32,10 @@ namespace Microsoft.Singularity.WebServer #endif CommandLine commandLine = new CommandLine(args); - bool silent = (commandLine.Options["silent"] != null); + silentMode = (commandLine.Options["silent"] != null); + debugMode = (commandLine.Options["debug"] != null); - if (!silent && commandLine.ShowHelp) { + if (!silentMode && commandLine.ShowHelp) { ShowUsage(); return 0; } @@ -64,14 +66,14 @@ namespace Microsoft.Singularity.WebServer try { port = Int32.Parse(portText); if ((port < 1) || (port > 65535)) { - if (!silent) { + if (!silentMode) { ShowUsage(); } return -1; } } catch { - if (!silent) { + if (!silentMode) { ShowMessage("Invalid port '" + portText + "'"); } return -3; @@ -97,16 +99,16 @@ namespace Microsoft.Singularity.WebServer // =================== try { - Listener listener = new Listener(port, clientIP); + Listener listener = new Listener(port, clientIP, silentMode, debugMode); listener.Start(); String s1 = String.Format(Environment.NewLine + - "Running Web Server on port {0}." + Environment.NewLine, - port); - Console.WriteLine(s1); + "Running Web Server on port {0}, silent={1},debug={2}." + Environment.NewLine, + port, silentMode, debugMode); + ShowMessage(s1); } catch (Exception ex) { - if (!silent) { + if (!silentMode) { ShowMessage("Error opening port " + port + ": " + ex.Message); } return -5; @@ -133,6 +135,7 @@ namespace Microsoft.Singularity.WebServer usageString += " [-silent]\n"; usageString += " [-mapping:]\n"; usageString += " [-client:]\n"; + usageString += " [-debug]\n"; ShowMessage(usageString); } diff --git a/base/Applications/WebServer/Messages.sg b/base/Applications/WebServer/Messages.sg index a89060c..1fa1dc4 100644 --- a/base/Applications/WebServer/Messages.sg +++ b/base/Applications/WebServer/Messages.sg @@ -1,11 +1,9 @@ //------------------------------------------------------------------------------ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Information Contained Herein is Proprietary and Confidential. -// +// Copyright (c) Microsoft Corporation. All Rights Reserved. //------------------------------------------------------------------------------ -namespace Microsoft.Singularity.WebServer { +namespace Microsoft.Singularity.WebServer +{ using System; using System.Collections; using System.Globalization; diff --git a/base/Applications/WebServer/Request.sg b/base/Applications/WebServer/Request.sg index 7b2dab1..d49a454 100644 --- a/base/Applications/WebServer/Request.sg +++ b/base/Applications/WebServer/Request.sg @@ -1,8 +1,5 @@ //------------------------------------------------------------------------------ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Information Contained Herein is Proprietary and Confidential. -// +// Copyright (c) Microsoft Corporation. All Rights Reserved. //------------------------------------------------------------------------------ namespace Microsoft.Singularity.WebServer @@ -10,24 +7,18 @@ namespace Microsoft.Singularity.WebServer using System; using System.Collections; using System.Diagnostics; - using System.Net; + using System.Globalization; using System.Net.Sockets; using System.Security; using System.Text; - using System.Threading; - using System.Globalization; using System.Web; - // Singularity + // SingSharp, Singularity using Microsoft.SingSharp; using Microsoft.SingSharp.Runtime; using Microsoft.Singularity.Channels; - using Microsoft.Singularity; - using Microsoft.Singularity.WebApps; - using Microsoft.Contracts; - // - /* Event types local to this provider */ + // Event types local to this provider internal enum WebServerEvent : ushort { ProcessRequest = 1 @@ -39,9 +30,10 @@ namespace Microsoft.Singularity.WebServer private static ulong totalServed = 0; private static ulong totalTime = 0; + private const ulong ReportRatio = 1ul; private const int MaxChunkLength = 64 * 1024; + private Connection! connection; - private Application application; // raw request data @@ -75,11 +67,15 @@ namespace Microsoft.Singularity.WebServer private bool headersSent; private int responseStatus; private StringBuilder responseHeadersBuilder; - private ArrayList responseBodyBytes; + private ArrayList responseBodyBytes; // Contains byte[] or VContainer elements. - internal Request(Connection! connection) + private bool silentMode, debugMode; + + internal Request(Connection! connection, bool doSilent, bool doDebug) { this.connection = connection; + this.silentMode = doSilent; + this.debugMode = doDebug; base(); } @@ -126,12 +122,26 @@ namespace Microsoft.Singularity.WebServer if (application == null) { connection.WriteErrorAndClose(404); - } else { + } + else { application.Process(this); } + // Calculate Elapsed Time, but Keep Positive ulong elapsed = Processor.CycleCount - startTime; - Console.WriteLine("served \"" + GetFilePath() + "\" in " + elapsed + " cycles"); + if (elapsed > (ulong.MaxValue / 2)) { + elapsed = 1; + } + + // Log a fraction of the Requests as 'xml' elements. + if (!silentMode && ((totalServed % Request.ReportRatio) == 0ul)) { + Console.Write("{3}", + totalServed, elapsed, GetFilePath(), Environment.NewLine); + } + if (debugMode) { + DebugStub.WriteLine("{3}", + __arglist(totalServed, elapsed, GetFilePath(), Environment.NewLine)); + } totalServed++; totalTime += elapsed; @@ -606,16 +616,18 @@ namespace Microsoft.Singularity.WebServer if (bytes != null) { connection.WriteBody(bytes, 0, bytes.Length); - } else if (container != null) { + } + else if (container != null) { byte[]! in ExHeap exBytes = container.Acquire(); responseBodyBytes[i] = null; connection.WriteBody(exBytes); - } else { + } + else { Debug.Assert(false); } } } - catch(SocketException) { + catch (SocketException) { // If the socket throws an exception, abort trying to write the // rest of the body. } diff --git a/base/Applications/cassini/AssemblyInfo.sg b/base/Applications/cassini/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Applications/cassini/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Applications/cassini/ByteParser.cs b/base/Applications/cassini/ByteParser.cs index bbbf7e1..9dc5662 100644 --- a/base/Applications/cassini/ByteParser.cs +++ b/base/Applications/cassini/ByteParser.cs @@ -1,11 +1,9 @@ //------------------------------------------------------------------------------ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Information Contained Herein is Proprietary and Confidential. -// +// Copyright (c) Microsoft Corporation. All Rights Reserved. //------------------------------------------------------------------------------ -namespace Microsoft.VisualStudio.WebHost { +namespace Microsoft.VisualStudio.WebHost +{ using System; using System.Collections; using System.Text; @@ -31,7 +29,7 @@ namespace Microsoft.VisualStudio.WebHost { for (int i = _pos; i < _bytes.Length; i++) { if (_bytes[i] == (byte)'\n') { int len = i-_pos; - if (len > 0 && _bytes[i-1] == (byte)'\r') { + if (len > 0 && _bytes[i - 1] == (byte)'\r') { len--; } diff --git a/base/Applications/cassini/ByteString.cs b/base/Applications/cassini/ByteString.cs index cd53543..77277a9 100644 --- a/base/Applications/cassini/ByteString.cs +++ b/base/Applications/cassini/ByteString.cs @@ -1,11 +1,9 @@ //------------------------------------------------------------------------------ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Information Contained Herein is Proprietary and Confidential. -// +// Copyright (c) Microsoft Corporation. All Rights Reserved. //------------------------------------------------------------------------------ -namespace Microsoft.VisualStudio.WebHost { +namespace Microsoft.VisualStudio.WebHost +{ using System; using System.Collections; using System.Text; diff --git a/base/Applications/cassini/Cassini.csproj b/base/Applications/cassini/Cassini.csproj index 33687f3..2d9a978 100644 --- a/base/Applications/cassini/Cassini.csproj +++ b/base/Applications/cassini/Cassini.csproj @@ -1,7 +1,7 @@ - @@ -17,9 +17,9 @@ + - @@ -28,9 +28,10 @@ - + + diff --git a/base/Applications/cassini/CommandLine.cs b/base/Applications/cassini/CommandLine.cs deleted file mode 100644 index 5fafd0b..0000000 --- a/base/Applications/cassini/CommandLine.cs +++ /dev/null @@ -1,73 +0,0 @@ -//------------------------------------------------------------------------------ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Information Contained Herein is Proprietary and Confidential. -// -//------------------------------------------------------------------------------ - -namespace Microsoft.VisualStudio.WebServer { - using System; - using System.Collections; - using System.Globalization; - using Microsoft.Contracts; - - /// - /// - public sealed class CommandLine { - - private string[] _arguments; - private IDictionary _options; - private bool _showHelp; - - [NotDelayed] - public CommandLine(string[]! args) { - ArrayList argList = new ArrayList(); - - for (int i = 0; i < args.Length; i++) { - string! args_i = (!)args[i]; - char c = args_i[0]; - if ((c != '/') && (c != '-')) { - argList.Add(args_i); - } - else { - int index = args_i.IndexOf(':'); - if (index == -1) { - string option = args_i.Substring(1); - if ((String.Compare(option, "help", true) == 0) || - option.Equals("?")) { - _showHelp = true; - } - else { - Options[option] = String.Empty; - } - } - else { - Options[args_i.Substring(1, index - 1)] = args_i.Substring(index + 1); - } - } - } - _arguments = (string[])argList.ToArray(typeof(string)); - } - - public string[] Arguments { - get { - return _arguments; - } - } - - public IDictionary/*!*/ Options { - get { - if (_options == null) { - _options = new Hashtable(); - } - return _options; - } - } - - public bool ShowHelp { - get { - return _showHelp; - } - } - } -} diff --git a/base/Applications/cassini/Connection.cs b/base/Applications/cassini/Connection.cs index d5567d8..2b4d26c 100644 --- a/base/Applications/cassini/Connection.cs +++ b/base/Applications/cassini/Connection.cs @@ -1,11 +1,9 @@ //------------------------------------------------------------------------------ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Information Contained Herein is Proprietary and Confidential. -// +// Copyright (c) Microsoft Corporation. All Rights Reserved. //------------------------------------------------------------------------------ -namespace Microsoft.VisualStudio.WebHost { +namespace Microsoft.VisualStudio.WebHost +{ using System; using System.Collections; using System.Globalization; diff --git a/base/Applications/cassini/Host.cs b/base/Applications/cassini/Host.cs index 07ded66..06def5d 100644 --- a/base/Applications/cassini/Host.cs +++ b/base/Applications/cassini/Host.cs @@ -1,11 +1,9 @@ //------------------------------------------------------------------------------ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Information Contained Herein is Proprietary and Confidential. -// +// Copyright (c) Microsoft Corporation. All Rights Reserved. //------------------------------------------------------------------------------ -namespace Microsoft.VisualStudio.WebHost { +namespace Microsoft.VisualStudio.WebHost +{ using System; using System.Collections; using System.Diagnostics; diff --git a/base/Applications/cassini/Main.cs b/base/Applications/cassini/Main.cs index ffcf6a6..d4c087c 100644 --- a/base/Applications/cassini/Main.cs +++ b/base/Applications/cassini/Main.cs @@ -1,8 +1,5 @@ //------------------------------------------------------------------------------ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Information Contained Herein is Proprietary and Confidential. -// +// Copyright (c) Microsoft Corporation. All Rights Reserved. //------------------------------------------------------------------------------ @@ -15,14 +12,15 @@ using Microsoft.Singularity; using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.WebApps.Contracts; using Microsoft.Contracts; using Microsoft.SingSharp.Reflection; using Microsoft.Singularity.Applications; [assembly: Transform(typeof(ApplicationResourceTransform))] -namespace Microsoft.VisualStudio.WebServer { - - +namespace Microsoft.VisualStudio.WebServer +{ [ConsoleCategory(HelpMessage="cassini [options] A web server", DefaultAction=true)] internal sealed class Parameters @@ -33,30 +31,39 @@ namespace Microsoft.VisualStudio.WebServer { [OutputEndpoint("data")] public readonly TRef Stdout; - [BoolParameter( "v", Default=false , HelpMessage="Verbose")] + [Endpoint] + public readonly TRef nsRef; + + [BoolParameter("v", Default=false, HelpMessage="Verbose")] internal bool verbose; - [BoolParameter( "s", Default=false , HelpMessage="Silent")] + [BoolParameter("s", Default=false, HelpMessage="Silent")] internal bool silent; - [BoolParameter( "quitURL", Default=false , HelpMessage="Allow special URL to terminate")] + [BoolParameter("quitURL", Default=false, HelpMessage="Allow special URL to terminate")] internal bool quitURL; - [StringParameter( "port", Default=null , HelpMessage="Port ID to listen to")] + [StringParameter("port", Default=null, HelpMessage="Port ID to listen to")] internal string portString; - [StringParameter( "vpath", Default="", HelpMessage="Root of Virtual Path")] + [StringParameter("vpath", Default="", HelpMessage="Root of Virtual Path")] internal string virtualPath; - [StringParameter( "client", Default="", HelpMessage="Client IP")] + [StringParameter("client", Default="", HelpMessage="Client IP")] internal string client; - [StringParameter( "apparg", Default="", HelpMessage="Arg to pass to client app")] + [StringParameter("apparg", Default="", HelpMessage="Arg to pass to client app")] internal string appArg; - [StringParameter( "app", Mandatory=true, Default="", HelpMessage="Name of the app to run")] + [StringParameter("app", Mandatory=true, Default="", HelpMessage="Name of the app to run")] internal string app; + [StringParameter("nspath", Mandatory=false, Default=null, HelpMessage="NS path to app to run")] + internal string nsPath; + + [LongParameter("load", Mandatory=false, Default=1000, HelpMessage="Overload timeout threshold (in ms)")] + internal long load; + reflective internal Parameters(); internal int AppMain() { @@ -87,32 +94,32 @@ namespace Microsoft.VisualStudio.WebServer { } string physicalPath = "\\"; - /* - HACKHACK FIXME: the file system is not wired up - in Singularity; avoid using it! - - string physicalPath = (string)commandLine.Options["path"]; - if (physicalPath != null) { - physicalPath = physicalPath.Trim(); - } - if ((physicalPath == null) || (physicalPath.Length == 0)) { - if (!silent) { - ShowUsage(); - } - return -1; - } - else { - if (Directory.Exists(physicalPath) == false) { - if (!silent) { - ShowMessage("The physical path '"+ physicalPath + "' does not exist!"); - } - return -2; - } - - // added this to resolve paths like "." - physicalPath = Path.GetFullPath(physicalPath); - } - */ + // + //TODO: HACKHACK FIXME: the file system is not wired up + //in Singularity; avoid using it! +// + //string physicalPath = (string)commandLine.Options["path"]; + //if (physicalPath != null) { + // physicalPath = physicalPath.Trim(); + //} + //if ((physicalPath == null) || (physicalPath.Length == 0)) { + // if (!silent) { + // ShowUsage(); + // } + // return -1; + //} + //else { + // if (Directory.Exists(physicalPath) == false) { + // if (!silent) { + // ShowMessage("The physical path '"+ physicalPath + "' does not exist!"); + // } + // return -2; + // } +// + // // added this to resolve paths like "." + // physicalPath = Path.GetFullPath(physicalPath); + //} + // int port = 0; string portText = config.portString; @@ -166,13 +173,39 @@ namespace Microsoft.VisualStudio.WebServer { } if ((webApp == null) || (webApp.Length == 0)) { - webApp = "HelloWebApp.x86"; + webApp = "HelloWebApp"; } webApp.Trim(); - if (!Dispatcher.Initialize(webApp, appArgs, verbose, quitURL)) - { + // If a nsPath was provide, then connect to the client. + if (config.nsPath != null) { + Console.WriteLine("Cassini: Connecting to {0}.", config.nsPath); + DirectoryServiceContract.Imp! dsImp = config.nsRef.Acquire(); + dsImp.RecvSuccess(); + + WebAppContract.Imp! waImp; + WebAppContract.Exp! waExp; + WebAppContract.NewChannel(out waImp, out waExp); + + ErrorCode error; + if (!SdsUtils.Bind(config.nsPath, dsImp, waExp, out error)) { + DebugStub.WriteLine("Failed to bind to webapp store...error {0}\n", + __arglist(SdsUtils.ErrorCodeToString(error))); + Console.WriteLine("Failed to bind to webapp store...error {0}\n", + SdsUtils.ErrorCodeToString(error)); + + delete dsImp; + delete waImp; + return -1; + } + delete dsImp; + + waImp.RecvWebAppReady(); + Dispatcher.BalanceConnection(waImp, config.load); + } + + if (!Dispatcher.Initialize(webApp, appArgs, verbose, quitURL)) { if (!silent) { ShowMessage("Invalid web application name \"" + webApp + "\""); return -5; @@ -187,15 +220,10 @@ namespace Microsoft.VisualStudio.WebServer { Server server = new Server(port, virtualPath, physicalPath, clientIP); server.Start(); - String s1 = String.Format( Environment.NewLine + - "Running Web Server on port {0}." + Environment.NewLine + Environment.NewLine + - "Application '{1}' is mapped to '{2}'." + Environment.NewLine + Environment.NewLine, - port, virtualPath, physicalPath); - String s2 = String.Format( "http://localhost:{0}{1}", - port, virtualPath); - if (!s2.EndsWith("/")) - s2 += "/"; - Console.WriteLine(s1+s2+Environment.NewLine); + Console.WriteLine(); + Console.WriteLine("Running Web Server on port {0}.", port); + Console.WriteLine("Application '{0}' is mapped to '{1}'.", virtualPath, physicalPath); + Console.WriteLine("http://localhost:{0}{1}", port, virtualPath); // Currently no console-read support in Singularity. Run forever! //Console.WriteLine("Hit Enter to stop the server"); diff --git a/base/Applications/cassini/Messages.cs b/base/Applications/cassini/Messages.cs index bc81004..ca5278c 100644 --- a/base/Applications/cassini/Messages.cs +++ b/base/Applications/cassini/Messages.cs @@ -1,11 +1,9 @@ //------------------------------------------------------------------------------ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Information Contained Herein is Proprietary and Confidential. -// +// Copyright (c) Microsoft Corporation. All Rights Reserved. //------------------------------------------------------------------------------ -namespace Microsoft.VisualStudio.WebHost { +namespace Microsoft.VisualStudio.WebHost +{ using System; using System.Collections; using System.Globalization; diff --git a/base/Applications/cassini/Request.cs b/base/Applications/cassini/Request.cs index 0cbbe13..ffc6644 100644 --- a/base/Applications/cassini/Request.cs +++ b/base/Applications/cassini/Request.cs @@ -1,11 +1,9 @@ //------------------------------------------------------------------------------ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Information Contained Herein is Proprietary and Confidential. -// +// Copyright (c) Microsoft Corporation. All Rights Reserved. //------------------------------------------------------------------------------ -namespace Microsoft.VisualStudio.WebHost { +namespace Microsoft.VisualStudio.WebHost +{ using System; using System.Collections; using System.Diagnostics; @@ -26,7 +24,7 @@ namespace Microsoft.VisualStudio.WebHost { using Microsoft.Singularity; // - /* Event types local to this provider */ + // Event types local to this provider public enum CassiniEvent : ushort { ProcessRequest = 1 @@ -617,16 +615,18 @@ namespace Microsoft.VisualStudio.WebHost { if (bytes != null) { _connection.WriteBody(bytes, 0, bytes.Length); - } else if (container != null) { + } + else if (container != null) { byte[]! in ExHeap exBytes = container.Acquire(); _responseBodyBytes[i] = null; _connection.WriteBody(exBytes); - } else { + } + else { Debug.Assert(false); } } } - catch(SocketException) { + catch (SocketException) { // If the socket throws an exception, abort trying to write the // rest of the body. } diff --git a/base/Applications/cassini/Server.cs b/base/Applications/cassini/Server.cs index fcddc75..c1519ba 100644 --- a/base/Applications/cassini/Server.cs +++ b/base/Applications/cassini/Server.cs @@ -1,11 +1,9 @@ //------------------------------------------------------------------------------ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Information Contained Herein is Proprietary and Confidential. -// +// Copyright (c) Microsoft Corporation. All Rights Reserved. //------------------------------------------------------------------------------ -namespace Microsoft.VisualStudio.WebHost { +namespace Microsoft.VisualStudio.WebHost +{ using System; using System.Collections; using System.Globalization; @@ -18,13 +16,13 @@ namespace Microsoft.VisualStudio.WebHost { public class Server : SimpleApplicationHost { - /* ======================= - The original version of Cassini uses the .NET thread pool to run - certain methods. There is currently no thread pool available on Singularity, - so I adapted Cassini to spin up threads on demand instead. These helper - objects assist with this; we could go back and remove them when - thread pools become available, if we wanted. - ======================= */ + // ======================= + // The original version of Cassini uses the .NET thread pool to run + // certain methods. There is currently no thread pool available on Singularity, + // so I adapted Cassini to spin up threads on demand instead. These helper + // objects assist with this; we could go back and remove them when + // thread pools become available, if we wanted. + // ======================= internal abstract class ThreadObject { @@ -75,9 +73,9 @@ namespace Microsoft.VisualStudio.WebHost { } } - /* ======================= - end helper objects - ======================= */ + // ======================= + // end helper objects + // ======================= private int _port; @@ -102,7 +100,9 @@ namespace Microsoft.VisualStudio.WebHost { private const int maxWorkItems = workerThreads * 128; #endif - public Server(int port, string virtualPath, string physicalPath, string clientIP) : base(virtualPath, physicalPath) { + public Server(int port, string virtualPath, string physicalPath, string clientIP) + : base(virtualPath, physicalPath) + { _port = port; _virtualPath = virtualPath; _clientIP = clientIP; @@ -116,13 +116,13 @@ namespace Microsoft.VisualStudio.WebHost { #endif } - /* - // MarshalByRefObject override - public override object InitializeLifetimeService() { - // never expire the license - return null; - } - */ + // + //// MarshalByRefObject override + //public override object InitializeLifetimeService() { + // // never expire the license + // return null; + //} + // public int Port { get { @@ -205,6 +205,7 @@ namespace Microsoft.VisualStudio.WebHost { protected void OnSocketAccept(object acceptedSocket) { if (!_shutdownInProgress) { Connection conn = new Connection(this, (Socket)acceptedSocket, _clientIP); + //Console.WriteLine("- Accept at {0} from {1}", conn.LocalIP, conn.RemoteIP); // wait for at least some input if (conn.WaitForRequestBytes() == 0) { @@ -228,7 +229,8 @@ namespace Microsoft.VisualStudio.WebHost { } } - protected void OnStart() { + protected void OnStart() + { while (!_shutdownInProgress) { try { if (_socket != null) // ROTORTODO @@ -250,9 +252,11 @@ namespace Microsoft.VisualStudio.WebHost { } } - private Host GetHost() { - if (_shutdownInProgress) + private Host GetHost() + { + if (_shutdownInProgress) { return null; + } Host host = _host; diff --git a/base/Applications/cassini/Singularity/CassiniThreadPool.sg b/base/Applications/cassini/Singularity/CassiniThreadPool.sg index 973f4d5..4234388 100644 --- a/base/Applications/cassini/Singularity/CassiniThreadPool.sg +++ b/base/Applications/cassini/Singularity/CassiniThreadPool.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: CassiniThreadPool.sg -// // Note: adapted from FatThreadPool.sg // diff --git a/base/Applications/cassini/Singularity/Dispatcher.sg b/base/Applications/cassini/Singularity/Dispatcher.sg index 7488757..06966f7 100644 --- a/base/Applications/cassini/Singularity/Dispatcher.sg +++ b/base/Applications/cassini/Singularity/Dispatcher.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Dispatcher.sg -// // Note: // This file holds the Singularity-specific web app dispatcher that knows // how to invoke web apps under Singularity. @@ -20,20 +18,35 @@ using Microsoft.Singularity.V1.Services; using Microsoft.Singularity.WebApps; using Microsoft.Singularity.WebApps.Contracts; using System.Diagnostics; +using System.Threading; using System.Web; namespace Microsoft.VisualStudio.WebHost { internal class Dispatcher { + // Parameters for load balancing. + private static TRef m_AppConnBalance; + private static long m_loadTimeout; // If longer than this then, offload more requests. + private static int m_loadBase = 0; // # of requests to offload before handling one locally. + private static int m_loadCount = 0; // # of requests currently offloaded. + private static int m_maxLoadBase = 5; + + // When m_loadBase > 0 then we count it page request with m_loadCount. + // If m_loadCount != m_loadBase, we use the balancing WebApp. + // When m_loadCount >= m_loadBase, we use the child WebApp and reset + // m_loadCount to 0. + // We increment m_loadBase (to a max of m_maxLoadBase) whenever + // the time servicing a request (on the child WebApp) exceeds + // m_loadTimeout. + + // Standard parameters private static TRef m_AppConn; - private static bool m_quitUrl; - private static bool m_verbose; + private static bool m_quitUrl; + private static bool m_verbose; private static string[] m_childArgs; - private static ulong m_TotalLocalTime = 0; private static ulong m_TotalRemoteTime = 0; - private static ulong m_TotalLocalHits = 0; private static ulong m_TotalRemoteHits = 0; private const string QUIT_URI = "quit"; @@ -51,34 +64,81 @@ namespace Microsoft.VisualStudio.WebHost // Make the appropriate request object IHttpRequest localRequest = new LocalHttpRequest(request); - ulong started = Processor.CycleCount; - // Call out-of-proc (usual case) HttpRequestContract.Imp! reqConnImp; HttpRequestContract.Exp! reqConnExp; HttpRequestContract.NewChannel(out reqConnImp, out reqConnExp); - WebAppContract.Imp appConn = m_AppConn.Acquire(); + // [!!!] We should decide here which connection to use. + // [!!!] So we probably need some idea of load. + bool useBalance = false; - try - { - // Start processing this request - appConn.SendProcess(reqConnImp); + if (m_AppConnBalance != null) { +#if true + int count = Interlocked.Increment(ref m_loadCount); + //Console.WriteLine("- count={0}, base={1}.", count, m_loadBase); + if (m_loadBase > 0) { + if (count >= m_loadBase) { + //Console.WriteLine("+ count={0}, base={1}.", count, m_loadBase); + m_loadCount = 0; + useBalance = false; + } + else { + useBalance = true; + } + } +#endif +#if false + if (useBalance) { + Console.WriteLine("--- Using balance connection."); + } +#endif + } - switch receive { - case appConn.OK(): - break; + ulong started = Processor.CycleCount; + DateTime beg = new DateTime(); - case appConn.ChannelClosed(): - Console.WriteLine("Child died, attempting to restart."); - delete appConn; - appConn = StartChild(); - break; + if (useBalance) { + WebAppContract.Imp balConn = m_AppConnBalance.Acquire(); + try { + // Start processing this request + balConn.SendProcess(reqConnImp); + + switch receive { + case balConn.OK(): + break; + + case balConn.ChannelClosed(): + Console.WriteLine("Balance child died, no restart."); + break; + } + } + finally { + m_AppConnBalance.Release(balConn); } } - finally - { - m_AppConn.Release(appConn); + else { + WebAppContract.Imp appConn = m_AppConn.Acquire(); + + try { + // Start processing this request + + appConn.SendProcess(reqConnImp); + + switch receive { + case appConn.OK(): + break; + + case appConn.ChannelClosed(): + Console.WriteLine("Child died, attempting to restart."); + delete appConn; + appConn = StartChild(); + break; + } + } + finally { + m_AppConn.Release(appConn); + } } // Start servicing the request contract @@ -87,18 +147,29 @@ namespace Microsoft.VisualStudio.WebHost // Clean up delete reqConnExp; + DateTime end = DateTime.UtcNow; + TimeSpan span = end - beg; + + Console.WriteLine("- {0,8} ms : vs {1}", span.Milliseconds, m_loadTimeout); + if (!useBalance) { + if (span.Milliseconds > m_loadTimeout && m_loadBase < m_maxLoadBase) { + Interlocked.Increment(ref m_loadBase); + Console.WriteLine("- Incremented m_loadBase to {0}", m_loadBase); + } + } + ulong elapsed = Processor.CycleCount - started; - if (m_verbose) Console.WriteLine("served \"" + request.GetUriPath() + "\" in " + elapsed + " cycles"); + if (m_verbose) { + Console.WriteLine("served \"" + request.GetUriPath() + "\" in " + elapsed + " cycles"); + } ++m_TotalRemoteHits; - if (m_TotalRemoteHits > 1) - { + if (m_TotalRemoteHits > 1) { m_TotalRemoteTime += elapsed; //Console.WriteLine("Average remote time: " + m_TotalRemoteTime / (m_TotalRemoteHits - 1)); } - else - { + else { //Console.WriteLine("First remote hit (cold cache)"); } @@ -111,9 +182,9 @@ namespace Microsoft.VisualStudio.WebHost WebAppContract.Imp! appConnImp; WebAppContract.Exp! appConnExp; WebAppContract.NewChannel(out appConnImp, out appConnExp); - - // REVIEW. should really inspect the manifest and determine what - // endpoints are needed by the child process. For example,specweb99 + + // REVIEW. should really inspect the manifest and determine what + // endpoints are needed by the child process. For example, specweb99 // now needs both the WebAppContract and a directory service contract Process child = new Process(m_childArgs, (Endpoint * in ExHeap)appConnExp); child.Start(); @@ -140,7 +211,8 @@ namespace Microsoft.VisualStudio.WebHost for (int i = 0; i < appArgs.Length; ++i) { args[i + 1] = appArgs[i]; } - } else { + } + else { args = new string[1]; args[0] = appName; } @@ -148,7 +220,7 @@ namespace Microsoft.VisualStudio.WebHost WebAppContract.Imp! appConnImp = StartChild(); if (appConnImp == null) { - throw new Exception(String.Format("Could not create process {0}",args[0])); + throw new Exception(String.Format("Could not create process {0}",args[0])); } m_AppConn = new TRef(appConnImp); @@ -156,6 +228,15 @@ namespace Microsoft.VisualStudio.WebHost return true; } + public static bool BalanceConnection([Claims] WebAppContract.Imp:ProcessingState appImp, + long loadTimeout) + { + m_loadTimeout = loadTimeout; + Console.WriteLine(" - {0} ms is loadTimeout", m_loadTimeout); + m_AppConnBalance = new TRef(appImp); + return true; + } + internal class RequestExporter { public static void ServiceRequestChannel(IHttpRequest! request, @@ -164,10 +245,8 @@ namespace Microsoft.VisualStudio.WebHost { bool done = false; - while(!done) - { - switch receive - { + while (!done) { + switch receive { case conn.GetUriPath() : conn.SendUriPath(Bitter.FromString2(request.GetUriPath())); break; @@ -186,7 +265,8 @@ namespace Microsoft.VisualStudio.WebHost string headerVal = request.GetHeader(headerName); if (headerVal != null) { conn.SendHeaderValue(Bitter.FromString(headerVal)); - } else { + } + else { conn.SendHeaderValue(null); } break; @@ -196,7 +276,8 @@ namespace Microsoft.VisualStudio.WebHost if (bodyData != null) { conn.SendBodyData(Bitter.FromByteArray(bodyData)); - } else { + } + else { conn.SendBodyData(null); } break; diff --git a/base/Applications/cassini/Singularity/DummyMarshalHttpRequest.cs b/base/Applications/cassini/Singularity/DummyMarshalHttpRequest.cs index e5f8a4a..bf0678e 100644 --- a/base/Applications/cassini/Singularity/DummyMarshalHttpRequest.cs +++ b/base/Applications/cassini/Singularity/DummyMarshalHttpRequest.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: RemoteHttpRequest.sg -// // Note: // This file wraps a cassini Request object so it is callable through // the Microsoft.Singularity.WebApps.IHttpRequest interface that web @@ -54,8 +52,9 @@ namespace Microsoft.VisualStudio.WebHost start = Processor.CycleCount; int length = str.Length; - for (int i = 0; i < length; ++i) - { marshalled[i] = str[i]; } + for (int i = 0; i < length; ++i) { + marshalled[i] = str[i]; + } m_TotalSharedCharCopyTime += Processor.CycleCount - start; start = Processor.CycleCount; @@ -77,8 +76,9 @@ namespace Microsoft.VisualStudio.WebHost start = Processor.CycleCount; int length = bytes.Length; - for (int i = 0; i < length; ++i) - { marshalled[i] = bytes[i]; } + for (int i = 0; i < length; ++i) { + marshalled[i] = bytes[i]; + } m_TotalSharedByteCopyTime += Processor.CycleCount - start; start = Processor.CycleCount; @@ -113,7 +113,8 @@ namespace Microsoft.VisualStudio.WebHost if (knownIndex > 0) { return MarshallString(m_Req.GetKnownRequestHeader(knownIndex)); - } else { + } + else { return MarshallString(m_Req.GetUnknownRequestHeader(headerName)); } } @@ -128,12 +129,10 @@ namespace Microsoft.VisualStudio.WebHost name = MarshallString(name); value = MarshallString(value); - if (index == -1) - { + if (index == -1) { m_Req.SendUnknownResponseHeader(name, value); } - else - { + else { m_Req.SendKnownResponseHeader(index, value); } } diff --git a/base/Applications/cassini/Singularity/LocalHttpRequest.cs b/base/Applications/cassini/Singularity/LocalHttpRequest.cs index 6096ebb..432ae0a 100644 --- a/base/Applications/cassini/Singularity/LocalHttpRequest.cs +++ b/base/Applications/cassini/Singularity/LocalHttpRequest.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: RemoteHttpRequest.sg -// // Note: // This file wraps a cassini Request object so it is callable through // the Microsoft.Singularity.WebApps.IHttpRequest interface that web @@ -50,7 +48,8 @@ namespace Microsoft.VisualStudio.WebHost if (knownIndex > 0) { return m_Req.GetKnownRequestHeader(knownIndex); - } else { + } + else { return m_Req.GetUnknownRequestHeader(headerName); } } @@ -65,7 +64,8 @@ namespace Microsoft.VisualStudio.WebHost // Return a copy of the preloaded body and trust the // caller not to monkey with it. return m_Req.GetPreloadedEntityBody(); - } else { + } + else { int totalBodyLength = m_Req.GetTotalEntityBodyLength(); @@ -92,7 +92,8 @@ namespace Microsoft.VisualStudio.WebHost // First copy the cached portion Buffer.BlockCopy(preloadedBody, 0, body, 0, preloadedLength); - } else { + } + else { assert ((preloadedBody == null) && (preloadedLength == 0)); } @@ -113,7 +114,8 @@ namespace Microsoft.VisualStudio.WebHost Array.Copy(body, 0, shortBody, 0, totalBodyLength - remainder); body = shortBody; remainder = 0; // No more data will be forthcoming - } else { + } + else { remainder -= readBytes; } } @@ -138,12 +140,10 @@ namespace Microsoft.VisualStudio.WebHost { int index = HttpWorkerRequest.GetKnownResponseHeaderIndex(name); - if (index == -1) - { + if (index == -1) { m_Req.SendUnknownResponseHeader(name, value); } - else - { + else { m_Req.SendKnownResponseHeader(index, value); } } diff --git a/base/Applications/corlib_assembly_ref.il b/base/Applications/corlib_assembly_ref.il new file mode 100644 index 0000000..3ba1e27 --- /dev/null +++ b/base/Applications/corlib_assembly_ref.il @@ -0,0 +1,5 @@ +.assembly extern 'Corlib' as 'corlib' { +.ver 1:0:0:0 +.publickeytoken = ( 736440c9b414ea16 ) +} + diff --git a/base/Applications/echo/echo.csproj b/base/Applications/echo/echo.csproj index c478117..3a1df01 100644 --- a/base/Applications/echo/echo.csproj +++ b/base/Applications/echo/echo.csproj @@ -1,8 +1,6 @@  + diff --git a/base/Applications/iso9660/cddump/cddump.csproj b/base/Applications/iso9660/cddump/cddump.csproj index 712e378..c0e560b 100644 --- a/base/Applications/iso9660/cddump/cddump.csproj +++ b/base/Applications/iso9660/cddump/cddump.csproj @@ -1,8 +1,6 @@  + + + + + Exe + nib + true + true + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/base/Applications/perf/Perf.csproj b/base/Applications/perf/Perf.csproj index 51c7b3b..2608f1a 100644 --- a/base/Applications/perf/Perf.csproj +++ b/base/Applications/perf/Perf.csproj @@ -1,8 +1,6 @@  - - - - - - Exe - ProcMemInfo - true - - - - - - - - - - - - - - diff --git a/base/Applications/procmeminfo/ProcMemInfo.sg b/base/Applications/procmeminfo/ProcMemInfo.sg deleted file mode 100644 index 16532c6..0000000 --- a/base/Applications/procmeminfo/ProcMemInfo.sg +++ /dev/null @@ -1,650 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: ProcMemInfo.cs -// -// -// Note: Simple Windows XP-like ProcMemInfo program. -// -using System; -using System.Collections; -using System.Runtime.CompilerServices; -using System.Threading; - -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Diagnostics.Contracts; -using Microsoft.Singularity.Configuration; -using Microsoft.Singularity.Io; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.V1.Services; -using Microsoft.SingSharp; - -using Microsoft.Contracts; -using Microsoft.SingSharp.Reflection; -using Microsoft.Singularity.Applications; -[assembly: Transform(typeof(ApplicationResourceTransform))] - - -namespace Microsoft.Singularity.Applications -{ - [ConsoleCategory(HelpMessage= - "ProcMemInfo [options] Display Processor Cache info", - DefaultAction=true)] - internal sealed class Parameters - { - [Endpoint] - public readonly TRef Stdin; - - [Endpoint] - public readonly TRef Stdout; - - [Endpoint] - public readonly TRef pmInfoRef; - - [BoolParameter( "d", Default=true , HelpMessage= - "Dumps output and PageTable to Debugger.")] - internal bool dumpTable; - - reflective internal Parameters(); - - internal int AppMain() { - return ProcMemInfo.AppMain(this); - } - } - - public class ProcMemInfo - { - // Check if processor is Intel/AMD - internal static bool isIntel(uint ebx, uint ecx, uint edx) - { - if (ebx == 0x756e6547 && ecx == 0x6c65746e && edx == 0x49656e69) { - return true; - } - else { - return false; - } - } - - // Check if processor is Intel/AMD - internal static bool isAMD(uint ebx, uint ecx, uint edx) - { - if (ebx == 0x68747541 && ecx == 0x444d4163 && edx == 0x69746e65) { - return true; - } - else { - return false; - } - } - - // Call CPUID with all possible inputs (EAX=1-5,8000_0001-8000_000A) - internal static void DoTestAllCpuId(Parameters! config) - { - bool dumpTable = config.dumpTable; - uint v0, v1, v2, v3; - - WriteLine(dumpTable, "\n"); - WriteLine(dumpTable, "----------------------------------------"); - WriteLine(dumpTable, "TESTING ALL CPUID "); - WriteLine(dumpTable, "----------------------------------------\n"); - - for (uint i = 0; i < 6; i++) { - Processor.ReadCpuid(i, out v0, out v1, out v2, out v3); - WriteLine(dumpTable, "{0:x8} : {1:x8}.{2:x8}.{3:x8}.{4:x8}", - i, v0, v1, v2, v3); - } - for (uint i = 0x80000000; i < 0x80000010; i++) { - Processor.ReadCpuid(i, out v0, out v1, out v2, out v3); - WriteLine(dumpTable, "{0:x8} : {1:x8}.{2:x8}.{3:x8}.{4:x8}", - i, v0, v1, v2, v3); - } - WriteLine(dumpTable, "\n"); - } - - // Get AMD Processor Cache Information - internal static void GetAmdInfo(Parameters! config) - { - bool dumpTable = config.dumpTable; - uint v0, v1, v2, v3; - - WriteLine(dumpTable, "\n"); - WriteLine(dumpTable, "----------------------------------------"); - WriteLine(dumpTable, "PROCESSOR CACHE INFORMATION (from CPUID)"); - WriteLine(dumpTable, "----------------------------------------\n"); - - // Call CPUID with Input EAX=1, 8000_0005H and 8000_0006H - GetAmdInput1(config); - GetAmdInput85(config); - GetAmdInput86(config); - } - - // EAX = 1 - // a : Processor Signature - // b : Initial APIC ID, CLFLUSH Size, Brand ID - // c : Reserved - // d : Standard Feature support - internal static void GetAmdInput1(Parameters! config) - { - uint a, b, c, d; - uint eax = 1; - bool dumpTable = config.dumpTable; - - Processor.ReadCpuid(eax, out a, out b, out c, out d); - - // -------------------------------------- output EAX - uint stepping = (a & 0x0000000f); - uint model = (a & 0x000000f0) >> 4; - uint family = (a & 0x00000f00) >> 8; - uint xModel = (a & 0x000f0000) >> 16; - uint xFamily = (a & 0x0ff00000) >> 20; - - // from AMD, CPUID manual: - // computing effective family/model computation - uint effFamily = family; - if (family == 0x0f) { - effFamily = (xFamily + family); - } - uint effModel = model; - if (family == 0x0f) { - effModel = (xModel << 4) + model; - } - - /// -------------------------------------- output EBX - uint brandId = (b & 0x000000ff); - uint clflush = (b & 0x0000ff00) >> 8; - uint apicId = (b & 0xff000000) >> 24; - - Write(dumpTable, "\nInput EAX={0:x8} ", eax); - Write(dumpTable,"output:{1:x8}.{2:x8}.{3:x8}.{4:x8}\n\n", - eax, a, b, c, d); - - if (a == 0 && b == 0 && c == 0 && d == 0) { - return; - } - - WriteLine(dumpTable, " Output EAX:"); - WriteLine(dumpTable, " Stepping {0}", stepping); - WriteLine(dumpTable, " Model {0}", model); - WriteLine(dumpTable, " Family {0}", family); - WriteLine(dumpTable, " xModel {0}", xModel); - WriteLine(dumpTable, " xFamily {0}", xFamily); - WriteLine(dumpTable, " effFamily {0}", effFamily); - WriteLine(dumpTable, " effModel {0}", effModel); - WriteLine(dumpTable, " Output EBX:"); - WriteLine(dumpTable, " Brand Id {0}", brandId); - WriteLine(dumpTable, " CLFLUSH Size {0}", clflush); - WriteLine(dumpTable, " APIC ID {0}", apicId); - - // -------------------------------------- output EDX - WriteLine(dumpTable, " Output EDX:"); - Write(dumpTable, " "); - if ((d & 0x1) > 0) { Write(dumpTable, "x87 "); } - if ((d & 0x2) > 0) { Write(dumpTable, "VME "); } - if ((d & 0x4) > 0) { Write(dumpTable, "DBG "); } - if ((d & 0x8) > 0) { Write(dumpTable, "PSE "); } - if ((d & 0x10) > 0) { Write(dumpTable, "TSC "); } - if ((d & 0x20) > 0) { Write(dumpTable, "MSR "); } - if ((d & 0x40) > 0) { Write(dumpTable, "PAE "); } - if ((d & 0x80) > 0) { Write(dumpTable, "MCE "); } - Write(dumpTable, "\n "); - if ((d & 0x100) > 0) { Write(dumpTable, "CX8 "); } - if ((d & 0x200) > 0) { Write(dumpTable, "APIC "); } - if ((d & 0x200) > 0) { Write(dumpTable, "R0 "); } - if ((d & 0x800) > 0) { Write(dumpTable, "SEP "); } - if ((d & 0x1000) > 0) { Write(dumpTable, "MTRR "); } - if ((d & 0x2000) > 0) { Write(dumpTable, "PGE "); } - if ((d & 0x4000) > 0) { Write(dumpTable, "MCA "); } - if ((d & 0x8000) > 0) { Write(dumpTable, "CMOV "); } - Write(dumpTable, "\n "); - if ((d & 0x10000) > 0) { Write(dumpTable, "PAT "); } - if ((d & 0x20000) > 0) { Write(dumpTable, "P36 "); } - if ((d & 0x40000) > 0) { Write(dumpTable, "R1 "); } - if ((d & 0x80000) > 0) { Write(dumpTable, "CFL "); } - if ((d & 0x100000) > 0) { Write(dumpTable, "R2 "); } - if ((d & 0x200000) > 0) { Write(dumpTable, "R3 "); } - if ((d & 0x400000) > 0) { Write(dumpTable, "R4 "); } - if ((d & 0x800000) > 0) { Write(dumpTable, "MMX "); } - Write(dumpTable, "\n "); - if ((d & 0x1000000) > 0) { Write(dumpTable, "FXSR "); } - if ((d & 0x2000000) > 0) { Write(dumpTable, "SSE "); } - if ((d & 0x4000000) > 0) { Write(dumpTable, "SSE2 "); } - if ((d & 0x8000000) > 0) { Write(dumpTable, "R5 "); } - if ((d & 0x10000000) > 0){ Write(dumpTable, "R6 "); } - if ((d & 0x20000000) > 0){ Write(dumpTable, "R7 "); } - if ((d & 0x40000000) > 0){ Write(dumpTable, "R8 "); } - if ((d & 0x80000000) > 0){ Write(dumpTable, "R9 "); } - Write(dumpTable, "\n"); - } - - // EAX = 8000_0005 - // a : TLB Bits for 2 MB and 4MB - // b : TLB Bits for 4 KB - // c : L1 Data Cache Bits - // d : L1 Instruction Cache Bits - internal static void GetAmdInput85(Parameters! config) - { - uint a, b, c, d; - uint eax = 0x80000005; - bool dumpTable = config.dumpTable; - - Processor.ReadCpuid(eax, out a, out b, out c, out d); - - Write(dumpTable, "\nInput EAX={0:x8} ", eax); - Write(dumpTable,"output:{1:x8}.{2:x8}.{3:x8}.{4:x8}\n\n", - eax, a, b, c, d); - - if (a == 0 && b == 0 && c == 0 && d == 0) { - return; - } - - uint a1 = (a & 0x000000ff) >> 0; - uint a2 = (a & 0x0000ff00) >> 8; - uint a3 = (a & 0x00ff0000) >> 16; - uint a4 = (a & 0xff000000) >> 24; - - uint b1 = (b & 0x000000ff) >> 0; - uint b2 = (b & 0x0000ff00) >> 8; - uint b3 = (b & 0x00ff0000) >> 16; - uint b4 = (b & 0xff000000) >> 24; - - uint c1 = (c & 0x000000ff) >> 0; - uint c2 = (c & 0x0000ff00) >> 8; - uint c3 = (c & 0x00ff0000) >> 16; - uint c4 = (c & 0xff000000) >> 24; - - uint d1 = (d & 0x000000ff) >> 0; - uint d2 = (d & 0x0000ff00) >> 8; - uint d3 = (d & 0x00ff0000) >> 16; - uint d4 = (d & 0xff000000) >> 24; - - - // -------------------------------------- output EAX - WriteLine(dumpTable, " Output EAX:"); - WriteLine(dumpTable, " 2MB Inst TLB, Entries {0}", a1); - WriteLine(dumpTable, " 2MB Inst TLB, Assctvy {0}", Assoc(a2)); - WriteLine(dumpTable, " 2MB Data TLB, Entries {0}", a3); - WriteLine(dumpTable, " 2MB Data TLB, Assctvy {0}", Assoc(a4)); - - // -------------------------------------- output EBX - WriteLine(dumpTable, " Output EBX:"); - WriteLine(dumpTable, " 4KB Inst TLB, Entries {0}", b1); - WriteLine(dumpTable, " 4KB Inst TLB, Assctvy {0}", Assoc(b2)); - WriteLine(dumpTable, " 4KB Data TLB, Entries {0}", b3); - WriteLine(dumpTable, " 4KB Data TLB, Assctvy {0}", Assoc(b4)); - - // -------------------------------------- output ECX - WriteLine(dumpTable, " Output ECX:"); - WriteLine(dumpTable, " L1 Data, Line Size {0}", c1); - WriteLine(dumpTable, " L1 Data, Lines/Tag {0}", c2); - WriteLine(dumpTable, " L1 Data, Assctvy {0}", Assoc(c3)); - WriteLine(dumpTable, " L1 Data, Size (KB) {0}", c4); - - // -------------------------------------- output EDX - WriteLine(dumpTable, " Output EDX:"); - WriteLine(dumpTable, " L1 Inst , Line Size {0}", d1); - WriteLine(dumpTable, " L1 Inst , Lines/Tag {0}", d2); - WriteLine(dumpTable, " L1 Inst , Assctvy {0}", Assoc(d3)); - WriteLine(dumpTable, " L1 Inst , Size (KB) {0}", d4); - } - - // Convert associativity code to associativity in string - internal static string Assoc(uint associativity) - { - switch (associativity) { - case 0: return "Reserved"; break; - case 1: return "Direct Mapped"; break; - case 0xff: return "Fully Assoc"; break; - default : - return string.Format("{0}-way", associativity); - break; - } - } - - // Similar to Assoc() but specific for L2 cache - internal static string AssocL2(uint associativity) - { - switch (associativity) { - case 0: return "Off/Disabled"; break; - case 1: return "Direct Mapped"; break; - case 0x02: return "2-way"; break; - case 0x04: return "4-way"; break; - case 0x06: return "8-way"; break; - case 0x08: return "16-way"; break; - case 0x0f: return "Fully Assoc"; break; - default : - return "Reserved (disabled?)"; - break; - } - } - - // Check if L2 is reserved according to the associativity code - internal static bool IsL2Reserved(uint associativity) - { - switch (associativity) - { - case 0: - case 1: - case 0x02: - case 0x04: - case 0x06: - case 0x08: - case 0x0f: - return false; - break; - default : - return true; - break; - } - } - - // EAX = 8000_0006 - // a : L2 TLB Bits for 2 MB / 4 MB pages - // b : L2 TLB Bits for 4 KB - // c : L2 Cache Bits - // d : reserved - internal static void GetAmdInput86(Parameters! config) - { - uint a, b, c, d; - uint eax = 0x80000006; - - bool dumpTable = config.dumpTable; - - Processor.ReadCpuid(eax, out a, out b, out c, out d); - - uint a1 = (a & 0x000000ff) >> 0; - uint a2 = (a & 0x0000ff00) >> 8; - uint a3 = (a & 0x00ff0000) >> 16; - uint a4 = (a & 0xff000000) >> 24; - - uint b1 = (b & 0x000000ff) >> 0; - uint b2 = (b & 0x0000ff00) >> 8; - uint b3 = (b & 0x00ff0000) >> 16; - uint b4 = (b & 0xff000000) >> 24; - - uint c1 = (c & 0x000000ff) >> 0; - uint c2 = (c & 0x0000ff00) >> 8; - uint c3 = (c & 0x00ff0000) >> 16; - uint c4 = (c & 0xff000000) >> 24; - - Write(dumpTable, "\nInput EAX={0:x8} ", eax); - Write(dumpTable,"output:{1:x8}.{2:x8}.{3:x8}.{4:x8}\n\n", - eax, a, b, c, d); - - if (a == 0 && b == 0 && c == 0 && d == 0) { - return; - } - - // -------------------------------------- output EAX - WriteLine(dumpTable, " Output EAX:"); - if (a2 == 0) { - WriteLine(dumpTable, " 2MB L2 Inst TLB is disabled"); - } - else if (IsL2Reserved(a2)) { - WriteLine(dumpTable, " 2MB L2 Inst TLB is reserved"); - } - else { - WriteLine(dumpTable, " 2MB L2 Inst TLB, Entries {0}", a1); - WriteLine(dumpTable, " 2MB L2 Inst TLB, Assctvy {0}", - AssocL2(a2)); - } - if (a4 == 0) { - WriteLine(dumpTable, " 2MB L2 Data TLB is disabled"); - } - else if (IsL2Reserved(a4)) { - WriteLine(dumpTable, " 2MB L2 Inst TLB is reserved"); - } - else { - WriteLine(dumpTable, " 2MB L2 Data TLB, Entries {0}", a3); - WriteLine(dumpTable, " 2MB L2 Data TLB, Assctvy {0}", - AssocL2(a4)); - } - - // -------------------------------------- output EBX - WriteLine(dumpTable, " Output EBX:"); - if (b2 == 0) { - WriteLine(dumpTable, " 4KB L2 Inst TLB is disabled"); - } - else if (IsL2Reserved(b2)) { - WriteLine(dumpTable, " 4KB L2 Inst TLB is reserved"); - } - else { - WriteLine(dumpTable, " 4KB L2 Inst TLB, Entries {0}", b1); - WriteLine(dumpTable, " 4KB L2 Inst TLB, Assctvy {0}", - AssocL2(b2)); - } - if (b4 == 0) { - WriteLine(dumpTable, " 4KB L2 Data TLB is disabled"); - } - else if (IsL2Reserved(b4)) - { - WriteLine(dumpTable, " 4KB L2 Data TLB is reserved"); - } - else - { - WriteLine(dumpTable, " 4KB L2 Data TLB, Entries {0}", b3); - WriteLine(dumpTable, " 4KB L2 Data TLB, Assctvy {0}", - AssocL2(b4)); - } - - // -------------------------------------- output ECX - WriteLine(dumpTable, " Output ECX:"); - if (c3 == 0) - { - WriteLine(dumpTable, " L2 Cache is disabled"); - } - else if (IsL2Reserved(c3)) - { - WriteLine(dumpTable, " L2 Cache is reserved"); - } - else - { - WriteLine(dumpTable, " L2, Line Size {0}", c1); - WriteLine(dumpTable, " L2, Lines Per Tag {0}", c2); - WriteLine(dumpTable, " L2, Associativity {0}", c3); - WriteLine(dumpTable, " L2, Size (KB) {0}", c4); - } - } - - // Call GetCPUID - internal static void DoTestGetCpuId(Parameters! config) - { - bool intel = false, amd = false; - uint a, b, c, d; - - Processor.ReadCpuid(0, out a, out b, out c, out d); - intel = isIntel(b, c, d); - amd = isAMD(b, c, d); - - if (intel) { - // haryadi: I haven't implemented Intel config yet - // GetIntelInfo(config); - } - else if (amd) { - GetAmdInfo(config); - } - } - - // Get Processor and Memory Affinity - internal static void DoTestGetAffinity(Parameters! config) - { - bool dumpTable = config.dumpTable; - ProcMemInfoContract.ProcessorAffinity[] in ExHeap processors; - ProcMemInfoContract.MemoryAffinity[] in ExHeap memories; - - ProcMemInfoContract.Imp imp = config.pmInfoRef.Acquire(); - if (imp == null) { - throw new ApplicationException("Error: Unable to bind to " + - ProcMemInfoContract.ModuleName); - } - - imp.SendGetProcessorAffinity(); - imp.RecvResultProcessorAffinity(out processors); - - imp.SendGetMemoryAffinity(); - imp.RecvResultMemoryAffinity(out memories); - - WriteLine(dumpTable, "\n"); - WriteLine(dumpTable, "----------------------------------------"); - WriteLine(dumpTable, "PROCESSOR AFFINITY INFORMATION"); - WriteLine(dumpTable, "----------------------------------------\n"); - - if (processors == null) { - WriteLine(dumpTable, "There is no Processor Affinity info!"); - } - else { - WriteLine(dumpTable, "Found {0} processors\n", - processors.Length); - - for (int i = 0; i < processors.Length; i++) { - WriteLine(dumpTable, "Processor #{0}", i); - WriteLine(dumpTable, " domain : {0}", - processors[i].domain); - WriteLine(dumpTable, " apicId : {0}", - processors[i].apicId); - WriteLine(dumpTable, " flagIgnore : {0}\n", - processors[i].flagIgnore); - } - } - - WriteLine(dumpTable, "\n"); - WriteLine(dumpTable, "----------------------------------------"); - WriteLine(dumpTable, "MEMORY AFFINITY INFORMATION"); - WriteLine(dumpTable, "----------------------------------------\n"); - - if (memories == null) { - WriteLine(dumpTable, "There is no Memory Affinity info!"); - } - else { - WriteLine(dumpTable, "Found {0} memory\n", memories.Length); - - for (int i = 0; i < memories.Length; i++) { - WriteLine(dumpTable, "Memory #{0}", i); - WriteLine(dumpTable, " domain : {0}", - memories[i].domain); - WriteLine(dumpTable, " baseAddress : {0} {1}", - GetBigAddr(memories[i].baseAddress), - GetBigAddrStr(memories[i].baseAddress)); - WriteLine(dumpTable, " endAddress : {0} {1}", - GetBigAddr(memories[i].endAddress), - GetBigAddrStr(memories[i].endAddress)); - WriteLine(dumpTable, " memorySize : {0} {1}", - GetBigAddr(memories[i].memorySize), - GetBigAddrStr(memories[i].memorySize)); - WriteLine(dumpTable, " flagIgnore : {0}", - memories[i].flagIgnore); - WriteLine(dumpTable, " flagHotPlug : {0}", - memories[i].flagHotPluggable); - WriteLine(dumpTable, " flagNonVoltl: {0}\n", - memories[i].flagNonVolatile); - } - } - delete processors; - delete memories; - config.pmInfoRef.Release(imp); - return; - } - - internal static ulong GetBigAddr(ulong address) - { - ulong KB = 1024; - ulong MB = KB * KB; - ulong GB = KB * KB * KB; - - if (address > GB) { - return address / GB; - } - else if (address > MB) { - return address / MB; - } - else if (address > KB) { - return address / KB; - } - else { - return address; - } - } - - internal static string GetBigAddrStr(ulong address) - { - ulong KB = 1024; - ulong MB = KB * KB; - ulong GB = KB * KB * KB; - - if (address > GB) { - return "GB"; - } - else if (address > MB) { - return "MB"; - } - else if (address > KB) { - return "KB"; - } - else { - return "B"; - } - } - - // Main - internal static int AppMain(Parameters! config) - { - // Set the default options - bool dumpTable = config.dumpTable; - - WriteLine(dumpTable, "\nWelcome to ProcMemInfo ... \n\n"); - - // Test if ProcMemInfoContract is okay - ProcMemInfoContract.Imp imp3 = config.pmInfoRef.Acquire(); - imp3.RecvReady(); - config.pmInfoRef.Release(imp3); - - try { - DoTestGetAffinity(config); - DoTestGetCpuId (config); - DoTestAllCpuId (config); - } catch (Exception ex) { - Console.WriteLine("Error ."); - Console.WriteLine(ex.ToString()); - return 1; - } - - return 0; - } - - - /// - /// Use this method to write output to both the screen and the - /// debugger if toDebugger is true. - /// - public static void WriteLine(bool toDebugger, string format, - params object[] args) { - string s = String.Format(format, args); - Console.WriteLine(s); - if (toDebugger) { - DebugStub.WriteLine(s); - } - } - - public static void Write(bool toDebugger, string format, - params object[] args) { - string s = String.Format(format, args); - Console.Write(s); - if (toDebugger) { - DebugStub.Write(s); - } - } - - public static void WriteLine(bool toDebugger) { - Console.WriteLine(); - if (toDebugger) { - DebugStub.WriteLine(); - } - } - - } // end of ProcMemInfo class -} // end of Singularity.Applications - - - diff --git a/base/Applications/ptest/ptest.csproj b/base/Applications/ptest/ptest.csproj index 19d0e0a..d56d2df 100644 --- a/base/Applications/ptest/ptest.csproj +++ b/base/Applications/ptest/ptest.csproj @@ -5,8 +5,6 @@ Microsoft Research Singularity Copyright (c) Microsoft Corporation. All rights reserved. -File: Applications\ptest\ptest.csproj - Note: Test parameter matching code ############################################################################## @@ -14,11 +12,10 @@ Note: Test parameter matching code - + ptest Exe - true diff --git a/base/Applications/ptest/ptest.sg b/base/Applications/ptest/ptest.sg index 681a642..31b1338 100644 --- a/base/Applications/ptest/ptest.sg +++ b/base/Applications/ptest/ptest.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: ptest.sg -// // Note: test parameter matching code // using System; @@ -28,8 +26,7 @@ using Microsoft.Singularity.Applications; namespace app { - [ConsoleCategory(HelpMessage="Parameter test program.", - DefaultAction=true)] + [ConsoleCategory(Action="action1", HelpMessage="Test action1.")] internal class PlayConfiguration { private const string help1 = "this is a help message"; @@ -82,8 +79,8 @@ namespace app } } - - [ConsoleCategory(Action="action1", HelpMessage="Test action1.")] + [ConsoleCategory(HelpMessage="Parameter test program.", + DefaultAction=true)] internal class Action1 { [InputEndpoint("data")] @@ -105,19 +102,16 @@ namespace app } - // End of [TODO] marker - public class Play { private static SoundDeviceContract.Imp:Ready OpenSoundDevice(PlayConfiguration! config) { // extract imp handed in as part of process creation - SoundDeviceContract.Imp! imp = (config.ImpRef).Acquire(); + SoundDeviceContract.Imp! imp = ((!)config.ImpRef).Acquire(); // get it into the ready state Console.WriteLine("[{0}] Waiting for SB channel.", Thread.CurrentThread.GetThreadId()); - switch receive - { + switch receive { case imp.Success(): Console.WriteLine("[{0}] Got SB channel.", Thread.CurrentThread.GetThreadId()); break; @@ -143,7 +137,7 @@ namespace app string[] s = config.Args; if (s != null) { - for (int i=0; i< s.Length; i++) { + for (int i = 0; i < s.Length; i++) { Console.WriteLine("EverythingElse[{0}]={1}",i,s[i]); } } @@ -156,8 +150,8 @@ namespace app return 1; } - if ( config.numThreads > 0 ) { - for (int i=0; i < config.numThreads; i++) { + if (config.numThreads > 0) { + for (int i = 0; i < config.numThreads; i++) { Thread t = new Thread(new ThreadStart(new Play(config).DoPlayThread)); t.Start(); } @@ -201,8 +195,7 @@ namespace app // Copy contents into buffer WavAudio.Content[i] audio.SendPlayWav(buffer); - switch receive - { + switch receive { case audio.RecvAckPlayWav(oldbuffer): Console.WriteLine("[{1}] Done playing WAV audio {0}.", i, threadId); diff --git a/base/Applications/tty/tty.csproj b/base/Applications/tty/tty.csproj index 6e3f7fd..8ed0581 100644 --- a/base/Applications/tty/tty.csproj +++ b/base/Applications/tty/tty.csproj @@ -1,8 +1,6 @@  + diff --git a/base/Applications/utilities/attr/attr.csproj b/base/Applications/utilities/attr/attr.csproj index 521e8eb..7d49336 100644 --- a/base/Applications/utilities/attr/attr.csproj +++ b/base/Applications/utilities/attr/attr.csproj @@ -1,8 +1,6 @@  + + + + + + Exe + DeleteTree + + + + + + + + + + + + + + diff --git a/base/Applications/utilities/deleteTree/deleteTree.sg b/base/Applications/utilities/deleteTree/deleteTree.sg new file mode 100644 index 0000000..80d0712 --- /dev/null +++ b/base/Applications/utilities/deleteTree/deleteTree.sg @@ -0,0 +1,209 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// + +using FileSystem.Utils; +using System; +using System.Text; +using System.Threading; +using Microsoft.Singularity; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.FileSystem; +using Microsoft.Singularity.V1.Services; + +using Microsoft.Contracts; +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Configuration; +[assembly: Transform(typeof(ApplicationResourceTransform))] + +namespace Microsoft.Singularity.Applications +{ + [ConsoleCategory(HelpMessage="delete entire tree **warning** this deletes an entire tree! Only deletes directories and files", DefaultAction=true)] + internal class Parameters + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [Endpoint] + public readonly TRef nsRef; + + [StringParameter( "filename", Mandatory=true, Position=0, HelpMessage="Name of directory at top of tree.")] + internal string fileName; + + reflective internal Parameters(); + + internal int AppMain() { + return FsDeleteTree.AppMain(this); + } + } + + public class FsDeleteTree + { + internal static bool DeleteFile(string! fileName, DirectoryServiceContract.Imp! ds) + { + ErrorCode error; + FileUtils.DeleteFile(fileName, ds, out error); + bool ok = (error == ErrorCode.NoError); + if (!ok) { + Console.WriteLine(" File ({0}) delete failed. reason:{1}", + fileName, SdsUtils.ErrorCodeToString(error) ); + } + return ok; + } + + internal static bool DeleteLink(string! fileName, DirectoryServiceContract.Imp! ds) + { + ErrorCode error; + bool ok = SdsUtils.DeleteLink(fileName, ds, out error); + if (!ok) { + Console.WriteLine(" Symbolic Link ({0}) delete failed. reason:{1}", + fileName, SdsUtils.ErrorCodeToString(error) ); + } +#if DEBUG + else { + DebugStub.Break(); + } +#endif + return ok; + } + + //Sometimes Sing# has to catch and when we delete a direcotry immiediately after + //we close the channel, the channel will still be open on the other side + //and the directory service will think that the directory is still open. + internal static bool DeleteDirectory(string! fileName, DirectoryServiceContract.Imp! ds) + { + ErrorCode error; + bool ok = false; + int retry = 0; + while ((ok == false) && (retry < 3)) { + ok = SdsUtils.DeleteDirectory(fileName, ds, out error); + if (!ok) { + Console.WriteLine("Delete of directory ({0}) failed. reason:{1}", + fileName, SdsUtils.ErrorCodeToString(error) ); + //sleep for a second + Thread.Sleep(TimeSpan.FromSeconds(1)); + } + retry++; + } + if (ok == false) { + Console.WriteLine("Delete failed even after wait and retry cycle\n"); + } + return ok; + } + + internal static void RecursiveDelete(string parentName, DirectoryServiceContract.Imp!:Ready dirClient) + { + + EnumerationRecords[] in ExHeap responses = null; + ErrorCode errorOut; + bool isDir; + responses = SdsUtils.EnumerateDirectory(dirClient, out errorOut); + + if (null == responses) { + Console.WriteLine("RecursiveDelete: Enumerate directory failed. Error {0}\n", + SdsUtils.ErrorCodeToString(errorOut)); + return; + } + else { + for (int i = 0; i < responses.Length; i++) { + string displayName; + string name; + + expose (responses[i]) { + name = Bitter.ToString2(responses[i].Path); + displayName = parentName + name; + + //Mirror the dirs from the kernel + if (responses[i].Type == NodeType.Directory) { + displayName = displayName + "/"; + ErrorCode error; + + + DirectoryServiceContract.Imp! subDirClient; + DirectoryServiceContract.Exp! subDirServer; + DirectoryServiceContract.NewChannel(out subDirClient, out subDirServer); + + if(!SdsUtils.Bind(name, dirClient, subDirServer, out error)) { + DebugStub.WriteLine("Bind to '{0}' failed. reason: {1}", + __arglist(dirClient, SdsUtils.ErrorCodeToString(error))); + delete subDirClient; + break; + } + subDirClient.RecvSuccess(); + RecursiveDelete(displayName, subDirClient); + delete subDirClient; + Console.WriteLine("Deleting directory {0}\n", displayName); + DeleteDirectory(name, dirClient); + } + else if (responses[i].Type == NodeType.File) { + Console.WriteLine("Deleting file {0}\n", displayName); + DeleteFile(name, dirClient); + } + } + } + delete responses; + } + } + + internal static int AppMain(Parameters! config) + { + DirectoryServiceContract.Imp ds = ((!)config.nsRef).Acquire(); + if (ds == null) { + throw new Exception("Unable to acquire handle to the Directory Service root"); + } + + ds.RecvSuccess(); + + // see what kind of entity the path points to. + ErrorCode error; + FileAttributesRecord fileAttributes; + bool ok = SdsUtils.GetAttributes((!)config.fileName, ds, out fileAttributes, out error); + + if (!ok) { + Console.WriteLine(" File ({0}) not found. reason:{1}", + config.fileName, SdsUtils.ErrorCodeToString(error) ); +#if DEBUG + DebugStub.Break(); +#endif + delete ds; + return -1; + } + + if (fileAttributes.Type != NodeType.Directory) { + Console.WriteLine("Can only use delete tree with a root directory\n"); + delete ds; + return -1; + } + + Console.WriteLine("Deleting tree beginning at {0}\n", config.fileName); + DirectoryServiceContract.Imp! subDirClient; + DirectoryServiceContract.Exp! subDirServer; + DirectoryServiceContract.NewChannel(out subDirClient, out subDirServer); + + if(!SdsUtils.Bind(config.fileName, ds, subDirServer, out error)) { + DebugStub.WriteLine("Bind to '{0}' failed. reason: {1}", + __arglist(config.fileName, SdsUtils.ErrorCodeToString(error))); + delete subDirClient; + delete ds; + return -1; + } + subDirClient.RecvSuccess(); + RecursiveDelete(config.fileName, subDirClient); + + delete subDirClient; + delete ds; + return 0; + } + } +} diff --git a/base/Applications/utilities/deregister/Deregister.csproj b/base/Applications/utilities/deregister/Deregister.csproj index c435026..9936306 100644 --- a/base/Applications/utilities/deregister/Deregister.csproj +++ b/base/Applications/utilities/deregister/Deregister.csproj @@ -1,8 +1,6 @@  + @@ -8,11 +16,11 @@ - - - + + + diff --git a/base/Contracts/Contracts.Kernel.proj b/base/Contracts/Contracts.Kernel.proj index 7b5e334..1064895 100644 --- a/base/Contracts/Contracts.Kernel.proj +++ b/base/Contracts/Contracts.Kernel.proj @@ -1,3 +1,11 @@ + + @@ -7,10 +15,10 @@ - - + + diff --git a/base/Contracts/CredentialsManager.Contracts/CredentialsManagerContract.sg b/base/Contracts/CredentialsManager.Contracts/CredentialsManagerContract.sg index e98a18d..1facd24 100644 --- a/base/Contracts/CredentialsManager.Contracts/CredentialsManagerContract.sg +++ b/base/Contracts/CredentialsManager.Contracts/CredentialsManagerContract.sg @@ -4,7 +4,7 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: +// File: // // Note: // @@ -18,193 +18,194 @@ using Microsoft.SingSharp; namespace Microsoft.Singularity.Security { - // This structure describes a "protocol context" or a "protocol tuple". - // This captures (roughly) the parameters that are relevant to security - // that a client application uses. - // - public rep struct ProtocolTuple : ITracked - { - // The protocol that is specific to the application in question. - // For example, "smb" or "http" or "ftp". - public char[]! in ExHeap ApplicationProtocol; - - // The address of the remote service or application, who will be - // authenticating the client application. In this context, "address" - // can be any protocol-specific identifier of a remote peer. - // This includes addresses such as DNS names, IPv4 and IPv6 addresses, - // converted to text. - // For example, "products.redmond.corp.microsoft.com" or "192.168.1.1" - // or any other protocol-specific identifier of a remote principal. - public char[]! in ExHeap ServiceAddress; - - // The security protocol that the client wishes to use. - // For example, "ntlm" or "kerberos" or "spnego". - public char[]! in ExHeap AuthenticationProtocol; - - // The name of the remote security realm. For NTLM, this is a domain name, - // e.g. "redmond". For Kerberos, this is a domain name, e.g. - // "redmond.corp.microsoft.com". - public char[]! in ExHeap Realm; - } - - public rep struct CredentialsId : ITracked - { - public char[]! in ExHeap CredentialsName; - public char[]! in ExHeap Tag; - } + // This structure describes a "protocol context" or a "protocol tuple". + // This captures (roughly) the parameters that are relevant to security + // that a client application uses. + // + public rep struct ProtocolTuple : ITracked + { + // The protocol that is specific to the application in question. + // For example, "smb" or "http" or "ftp". + public char[]! in ExHeap ApplicationProtocol; - public rep struct ProtocolMapping : ITracked - { - public ProtocolTuple ProtocolTuple; - public CredentialsId CredentialsId; - } + // The address of the remote service or application, who will be + // authenticating the client application. In this context, "address" + // can be any protocol-specific identifier of a remote peer. + // This includes addresses such as DNS names, IPv4 and IPv6 addresses, + // converted to text. + // For example, "products.redmond.corp.microsoft.com" or "192.168.1.1" + // or any other protocol-specific identifier of a remote principal. + public char[]! in ExHeap ServiceAddress; - public contract CredentialsManagerContract : ServiceContract - { - public const string ChannelPath = "/dev/credentials-manager"; + // The security protocol that the client wishes to use. + // For example, "ntlm" or "kerberos" or "spnego". + public char[]! in ExHeap AuthenticationProtocol; - public const string ProtocolFieldWildcard = "*"; - - // - // Adds the specified credentials to the credentials store. - // - // The tag value can be used to disambiguate different instances of the same credentials. - // For example, the username ".\Administrator" might be used at three different machines, - // each of which have different passwords. If the tag value is null, it is treated as - // an empty string. - // - in message AddCredentials(CredentialsId id, char[]! in ExHeap password, bool replace); + // The name of the remote security realm. For NTLM, this is a domain name, + // e.g. "redmond". For Kerberos, this is a domain name, e.g. + // "redmond.corp.microsoft.com". + public char[]! in ExHeap Realm; + } - // - // Removes the specified credentials from the credentials store. The username and tag - // value must match exactly the values provided previously in a call to AddCredentials. - // If the tag value is null, it is treated as an empty string. - // - in message DeleteCredentials(CredentialsId id); - - // - // Deletes all credentials in the credentials store. - // - in message DeleteAllCredentials(); + public rep struct CredentialsId : ITracked + { + public char[]! in ExHeap CredentialsName; + public char[]! in ExHeap Tag; + } - // - // Enumerates the list of all credentials in the credentials store. - // - in message EnumerateCredentials(); - out message CredentialsList(CredentialsId[]! in ExHeap list); + public rep struct ProtocolMapping : ITracked + { + public ProtocolTuple ProtocolTuple; + public CredentialsId CredentialsId; + } - // - // Adds a new mapping of a protocol tuple to credentials. - // - // Some of the fields of the protocol mapping ID support wildcards. - // To do so, the field should be set to "*". - // This allows the protocol mapping to apply to all contexts that match. - // - in message AddProtocolMapping(ProtocolTuple tuple, CredentialsId credentials, bool replace); - - // - // Deletes a specific protocol mapping. The protocol mapping ID must match - // exactly an entry previously added using the AddProtocolMapping request. - // Wildcards (fields using "*") are not interpreted. - // - in message DeleteProtocolMapping(ProtocolTuple tuple); + public contract CredentialsManagerContract : ServiceContract + { + public const string ServiceName = "CredentialsManager"; + public const string ChannelPath = "/service/credentials"; - // - // Deletes all protocol mappings. - // - in message DeleteAllProtocolMappings(); + public const string ProtocolFieldWildcard = "*"; - // - // Searches the set of all protocol mappings for an entry that best matches - // a specific protocol mapping. - // - in message FindMatchingProtocolMapping(ProtocolTuple tuple, bool useWildcards); - out message NoMatchingProtocolMapping(); - out message MatchingProtocolMapping(CredentialsId credentials); + // + // Adds the specified credentials to the credentials store. + // + // The tag value can be used to disambiguate different instances of the same credentials. + // For example, the username ".\Administrator" might be used at three different machines, + // each of which have different passwords. If the tag value is null, it is treated as + // an empty string. + // + in message AddCredentials(CredentialsId id, char[]! in ExHeap password, bool replace); - // - // Queries the list of all protocol mappings. - // - in message EnumerateProtocolMappings(); - out message ProtocolMappings(ProtocolMapping[]! in ExHeap mappings); - - // This message requests that the credentials manager create an instance of a security - // protocol (identified by name), using the endpoint provided by the import holder. - // This allows the import holder to choose the channel contract, which allows a - // single protocol implementation to support more than one channel contract. - // In response to this request, the credentials manager creates an instance of the - // protocol (if possible). - in message CreateSupplicant( - char[]! in ExHeap authenticationProtocol, - CredentialsId credentials, - ServiceContract.Exp:Start! exp); - - // This request is similar to CreateSupplicant, but it also performs a protocol - // tuple look-up, since this is anticipated to be a common usage of the CM. - in message CreateSupplicantForProtocol( - ProtocolTuple protocol, - ServiceContract.Exp:Start! exp); - out message AckCreateSupplicantForProtocol(CredentialsId credentialsSelected); + // + // Removes the specified credentials from the credentials store. The username and tag + // value must match exactly the values provided previously in a call to AddCredentials. + // If the tag value is null, it is treated as an empty string. + // + in message DeleteCredentials(CredentialsId id); - // Generic response messages. - // These are used when there is no request-specific data to return. - out message Ok(); - out message RequestFailed(CredError error); + // + // Deletes all credentials in the credentials store. + // + in message DeleteAllCredentials(); + + // + // Enumerates the list of all credentials in the credentials store. + // + in message EnumerateCredentials(); + out message CredentialsList(CredentialsId[]! in ExHeap list); + + // + // Adds a new mapping of a protocol tuple to credentials. + // + // Some of the fields of the protocol mapping ID support wildcards. + // To do so, the field should be set to "*". + // This allows the protocol mapping to apply to all contexts that match. + // + in message AddProtocolMapping(ProtocolTuple tuple, CredentialsId credentials, bool replace); + + // + // Deletes a specific protocol mapping. The protocol mapping ID must match + // exactly an entry previously added using the AddProtocolMapping request. + // Wildcards (fields using "*") are not interpreted. + // + in message DeleteProtocolMapping(ProtocolTuple tuple); + + // + // Deletes all protocol mappings. + // + in message DeleteAllProtocolMappings(); + + // + // Searches the set of all protocol mappings for an entry that best matches + // a specific protocol mapping. + // + in message FindMatchingProtocolMapping(ProtocolTuple tuple, bool useWildcards); + out message NoMatchingProtocolMapping(); + out message MatchingProtocolMapping(CredentialsId credentials); + + // + // Queries the list of all protocol mappings. + // + in message EnumerateProtocolMappings(); + out message ProtocolMappings(ProtocolMapping[]! in ExHeap mappings); + + // This message requests that the credentials manager create an instance of a security + // protocol (identified by name), using the endpoint provided by the import holder. + // This allows the import holder to choose the channel contract, which allows a + // single protocol implementation to support more than one channel contract. + // In response to this request, the credentials manager creates an instance of the + // protocol (if possible). + in message CreateSupplicant( + char[]! in ExHeap authenticationProtocol, + CredentialsId credentials, + ServiceContract.Exp:Start! exp); + + // This request is similar to CreateSupplicant, but it also performs a protocol + // tuple look-up, since this is anticipated to be a common usage of the CM. + in message CreateSupplicantForProtocol( + ProtocolTuple protocol, + ServiceContract.Exp:Start! exp); + out message AckCreateSupplicantForProtocol(CredentialsId credentialsSelected); + + // Generic response messages. + // These are used when there is no request-specific data to return. + out message Ok(); + out message RequestFailed(CredError error); - out message Success(); + out message Success(); - override state Start : one - { - Success! -> Ready; - } - - state Ready : one - { - AddCredentials? -> (Ok! or RequestFailed!) -> Ready; - DeleteCredentials? -> (Ok! or RequestFailed!) -> Ready; - DeleteAllCredentials? -> (Ok! or RequestFailed!) -> Ready; - - EnumerateCredentials? -> (CredentialsList! or RequestFailed!) -> Ready; - EnumerateProtocolMappings? -> (ProtocolMappings! or RequestFailed!) -> Ready; - - FindMatchingProtocolMapping? -> (MatchingProtocolMapping! or RequestFailed!) -> Ready; + override state Start : one + { + Success! -> Ready; + } - AddProtocolMapping? -> (Ok! or RequestFailed!) -> Ready; - DeleteProtocolMapping? -> (Ok! or RequestFailed!) -> Ready; - DeleteAllProtocolMappings? -> (Ok! or RequestFailed!) -> Ready; - - CreateSupplicant? -> (Ok! or RequestFailed!) -> Ready; - CreateSupplicantForProtocol? -> (AckCreateSupplicantForProtocol! or RequestFailed!) -> Ready; - } - } - - /// Strings that are appropriate for passing to CreateSupplicant. - public sealed /* static */ class AuthenticationProtocolNames - { - public const string Ntlm = "ntlm"; - - // not yet implemented - public const string Kerberos = "kerberos"; - - // not yet implemented - public const string HttpMD5 = "http-md5"; - } + state Ready : one + { + AddCredentials? -> (Ok! or RequestFailed!) -> Ready; + DeleteCredentials? -> (Ok! or RequestFailed!) -> Ready; + DeleteAllCredentials? -> (Ok! or RequestFailed!) -> Ready; + + EnumerateCredentials? -> (CredentialsList! or RequestFailed!) -> Ready; + EnumerateProtocolMappings? -> (ProtocolMappings! or RequestFailed!) -> Ready; + + FindMatchingProtocolMapping? -> (MatchingProtocolMapping! or RequestFailed!) -> Ready; + + AddProtocolMapping? -> (Ok! or RequestFailed!) -> Ready; + DeleteProtocolMapping? -> (Ok! or RequestFailed!) -> Ready; + DeleteAllProtocolMappings? -> (Ok! or RequestFailed!) -> Ready; + + CreateSupplicant? -> (Ok! or RequestFailed!) -> Ready; + CreateSupplicantForProtocol? -> (AckCreateSupplicantForProtocol! or RequestFailed!) -> Ready; + } + } + + /// Strings that are appropriate for passing to CreateSupplicant. + public sealed /* static */ class AuthenticationProtocolNames + { + public const string Ntlm = "ntlm"; + + // not yet implemented + public const string Kerberos = "kerberos"; + + // not yet implemented + public const string HttpMD5 = "http-md5"; + } - /// Error codes for the requests of the CredentialsManagerContract. - public enum CredError - { - NoError = 0, - InternalError, - NoMatchingCredentials, - NoMatchingAuthenticationProtocol, - NoEntryFound, - ContractNotSupported, - MatchingEntryExists, - InvalidArguments, - EvidenceTypeNotSupported, - } + /// Error codes for the requests of the CredentialsManagerContract. + public enum CredError + { + NoError = 0, + InternalError, + NoMatchingCredentials, + NoMatchingAuthenticationProtocol, + NoEntryFound, + ContractNotSupported, + MatchingEntryExists, + InvalidArguments, + EvidenceTypeNotSupported, + } } diff --git a/base/Contracts/CredentialsManager.Contracts/GssSupplicantContract.sg b/base/Contracts/CredentialsManager.Contracts/GssSupplicantContract.sg index 219bf5b..031c721 100644 --- a/base/Contracts/CredentialsManager.Contracts/GssSupplicantContract.sg +++ b/base/Contracts/CredentialsManager.Contracts/GssSupplicantContract.sg @@ -20,53 +20,53 @@ using Microsoft.SingSharp; namespace Microsoft.Singularity.Security { - /** - - - This contract represents an active authentication session. It is modeled after - the GSS API, which is a standard API for performing authentication exchanges. - (See RFC 2078.) However, this contract models only the behavior of a supplicant - (a client who wishes to prove its identity), not that of an authenticator. - - - - This contract is analogous to an SSPI session in Win32. See InitializeSecurityContext - and friends. - - - - In general, applications hold the import endpoint of this contract, while - implementations of authentication protocols, implemented in the Credentials - Manager Service, hold the export side. The channel is bound using the - CreateSupplicant or CreateSupplicantForProtocol requests of the - CredentialsManagerContract. Applications ferry opaque authentication tokens - between the local supplicant and the remote network service until both sides - decide that authentication has succeeded, or one side decides that authentication - has failed. Applications should not interpret or modify the contents of the tokens. - - - - Authentication protocols vary in the number of messages sent, in which side sends - the first message, and in which side sends the last message. This contract supports - all message patterns. The supplicant sends the first message to the client application, - and that first message indicates whether the supplicant sends the first token, - (supplicant sends FirstToken to application), or expects the network service to send - the first token (supplicant sends NeedFirstToken). - - - - Currently, the only implementor of this contract is the NTLM authentication - protocol. However, it is intended that SPNEGO, Kerberos, etc. will eventually - implement this contract as well. - - - - This contract should be expanded to allow applications to negotiate standard - GSS options, such as integrity / confidentiality services, etc. For now, - it models only authentication (identification). - - - */ + /// + // + // + // This contract represents an active authentication session. It is modeled after + // the GSS API, which is a standard API for performing authentication exchanges. + // (See RFC 2078.) However, this contract models only the behavior of a supplicant + // (a client who wishes to prove its identity), not that of an authenticator. + // +// + // + // This contract is analogous to an SSPI session in Win32. See InitializeSecurityContext + // and friends. + // + // + // + // In general, applications hold the import endpoint of this contract, while + // implementations of authentication protocols, implemented in the Credentials + // Manager Service, hold the export side. The channel is bound using the + // CreateSupplicant or CreateSupplicantForProtocol requests of the + // CredentialsManagerContract. Applications ferry opaque authentication tokens + // between the local supplicant and the remote network service until both sides + // decide that authentication has succeeded, or one side decides that authentication + // has failed. Applications should not interpret or modify the contents of the tokens. + // + // + // + // Authentication protocols vary in the number of messages sent, in which side sends + // the first message, and in which side sends the last message. This contract supports + // all message patterns. The supplicant sends the first message to the client application, + // and that first message indicates whether the supplicant sends the first token, + // (supplicant sends FirstToken to application), or expects the network service to send + // the first token (supplicant sends NeedFirstToken). + // + // + // + // Currently, the only implementor of this contract is the NTLM authentication + // protocol. However, it is intended that SPNEGO, Kerberos, etc. will eventually + // implement this contract as well. + // + // + // + // This contract should be expanded to allow applications to negotiate standard + // GSS options, such as integrity / confidentiality services, etc. For now, + // it models only authentication (identification). + // + // + // public contract GssSupplicantContract : ServiceContract { /// diff --git a/base/Contracts/Diagnostics.Contracts/AssemblyInfo.sg b/base/Contracts/Diagnostics.Contracts/AssemblyInfo.sg new file mode 100644 index 0000000..7e8f7f9 --- /dev/null +++ b/base/Contracts/Diagnostics.Contracts/AssemblyInfo.sg @@ -0,0 +1,8 @@ +using System.Reflection; + +[assembly: AssemblyTitle("Microsoft.Singularity.Diagnostics.Contracts")] +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Contracts/Diagnostics.Contracts/ChannelContract.sg b/base/Contracts/Diagnostics.Contracts/ChannelContract.sg index 28d396e..3b11183 100644 --- a/base/Contracts/Diagnostics.Contracts/ChannelContract.sg +++ b/base/Contracts/Diagnostics.Contracts/ChannelContract.sg @@ -4,7 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: ChannelContract.sg // Note: Contract definition for the Singularity Channel diagnostics service // @@ -23,7 +22,7 @@ namespace Microsoft.Singularity.Diagnostics.Contracts public contract ChannelContract : ServiceContract { - public const string ModuleName = "/channel-diagnostics"; + public const string ModuleName = "/status/channels"; // Signal our identity out message Ready(); diff --git a/base/Contracts/Diagnostics.Contracts/Diagnostics.Contracts.Kernel.csproj b/base/Contracts/Diagnostics.Contracts/Diagnostics.Contracts.Kernel.csproj index bf89087..6da594d 100644 --- a/base/Contracts/Diagnostics.Contracts/Diagnostics.Contracts.Kernel.csproj +++ b/base/Contracts/Diagnostics.Contracts/Diagnostics.Contracts.Kernel.csproj @@ -1,12 +1,10 @@ - - - - - - - FileSystem.Contracts - Library - true - - - - - - - - - diff --git a/base/Contracts/FileSystem.Contracts/FileSystem.Contracts.csproj b/base/Contracts/FileSystem.Contracts/FileSystem.Contracts.csproj index ead38e7..ad35800 100644 --- a/base/Contracts/FileSystem.Contracts/FileSystem.Contracts.csproj +++ b/base/Contracts/FileSystem.Contracts/FileSystem.Contracts.csproj @@ -23,8 +23,10 @@ + + diff --git a/base/Contracts/FileSystem.Contracts/FileSystemControlContract.sg b/base/Contracts/FileSystem.Contracts/FileSystemControlContract.sg new file mode 100644 index 0000000..153ea74 --- /dev/null +++ b/base/Contracts/FileSystem.Contracts/FileSystemControlContract.sg @@ -0,0 +1,55 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +using Microsoft.Singularity; +using Microsoft.Singularity.Channels; + +namespace Iso9660 +{ + // + //This is a contract between a filesystem service (such as FatService or Iso9660Service) + //and a worker process that actually implements the filesystem (such as FatFs or Iso9660Fs). + // + public contract FileSystemControlContract + { + state Start : one { + Success! -> Ready; + } + out message Success(); + + state Ready : one { + MountVolume? -> Mounting; + Stop? -> Stopped; + } + in message MountVolume(char[]! in ExHeap device, char[]! in ExHeap mountPoint); + + state Mounting : one { + AckMountVolume! -> Running; + NakMountVolume! -> Ready; + } + out message AckMountVolume(); + out message NakMountVolume(); + + state Running : one { + DismountVolume? -> Dismounting; + } + in message DismountVolume(bool force); + + state Dismounting : one { + AckDismountVolume! -> Ready; + CannotDismount! -> Running; + } + out message AckDismountVolume(); + out message CannotDismount(); + + in message Stop(); + + state Stopped : one { + } + } +} + diff --git a/base/Contracts/FileSystem.Contracts/Iso9660ServiceControlContract.sg b/base/Contracts/FileSystem.Contracts/Iso9660ServiceControlContract.sg index 890bce9..b812d43 100644 --- a/base/Contracts/FileSystem.Contracts/Iso9660ServiceControlContract.sg +++ b/base/Contracts/FileSystem.Contracts/Iso9660ServiceControlContract.sg @@ -1,7 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // +// ---------------------------------------------------------------------------- using System; using System.Collections; @@ -10,20 +11,24 @@ using Microsoft.Singularity; using Microsoft.Singularity.Directory; using Microsoft.Singularity.Channels; -namespace Microsoft.Singularity.FileSystem { - public contract Iso9660ServiceControlContract : ServiceContract { - - in message Initialize(char[] in ExHeap device); - out message AckInitialize(); - out message NackInitialize(); +namespace Microsoft.Singularity.FileSystem +{ - in message Mount(char[] in ExHeap location); + /// + /// This contract is exported by services that are filesystem controllers. + /// + public contract FileSystemControllerContract : ServiceContract + { + in message Mount(char[]! in ExHeap device, char[]! in ExHeap mountPath); out message AckMount(); - out message NackMount(); + out message NakMount(); - in message Unmount(); + in message Unmount(char[]! in ExHeap mountPath); out message AckUnmount(); - out message NackUnmount(); + out message NakUnmount(); + + out message Ok(); + out message RequestFailed(ErrorCode error); out message Success(); @@ -32,9 +37,18 @@ namespace Microsoft.Singularity.FileSystem { } state Ready : one { - Initialize? -> (AckInitialize! or NackInitialize!) -> Ready; - Mount? -> (AckMount! or NackMount!) -> Ready; - Unmount? -> (AckUnmount! or NackUnmount!) -> Ready; + Mount? -> Mounting; + Unmount? -> Unmounting; + } + + state Mounting : one { + Ok! -> Ready; + RequestFailed! -> Ready; + } + + state Unmounting : one { + Ok! -> Ready; + RequestFailed! -> Ready; } } } diff --git a/base/Contracts/FileSystem.Contracts/ThreadPoolControlContract.sg b/base/Contracts/FileSystem.Contracts/ThreadPoolControlContract.sg index f1ac641..ab56325 100644 --- a/base/Contracts/FileSystem.Contracts/ThreadPoolControlContract.sg +++ b/base/Contracts/FileSystem.Contracts/ThreadPoolControlContract.sg @@ -1,13 +1,15 @@ -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // +// ---------------------------------------------------------------------------- using System; using Microsoft.Singularity.Directory; -namespace Microsoft.Singularity.FileSystem { +namespace Microsoft.Singularity.FileSystem +{ public contract ThreadPoolControlContract { in message TrackEndpoint(); out message AckTrackEndpoint(); diff --git a/base/Contracts/InstallerService.Contracts/AssemblyInfo.sg b/base/Contracts/InstallerService.Contracts/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Contracts/InstallerService.Contracts/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Contracts/InstallerService.Contracts/InstallerService.Contracts.csproj b/base/Contracts/InstallerService.Contracts/InstallerService.Contracts.csproj new file mode 100644 index 0000000..86a1cc1 --- /dev/null +++ b/base/Contracts/InstallerService.Contracts/InstallerService.Contracts.csproj @@ -0,0 +1,31 @@ + + + + + + + + Library + InstallerService.Contracts + true + + + + + + + + + diff --git a/base/Contracts/InstallerService.Contracts/InstallerServiceContract.sg b/base/Contracts/InstallerService.Contracts/InstallerServiceContract.sg new file mode 100644 index 0000000..82116c1 --- /dev/null +++ b/base/Contracts/InstallerService.Contracts/InstallerServiceContract.sg @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Contracts\InstallerService.Contracts\InstallerContract.sg +// +// Note: +// +using System; +using Microsoft.Singularity; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.ServiceManager; + +namespace Microsoft.Singularity.Services +{ + public enum InstallerCode : uint + { + NoError = 0, + NotFound = 1, + AlreadyExists = 2, + ManifestNotFound = 3, + MissingAssemblies = 4, + NoAssembliesInManifest = 5, + CouldNotOpenDirectory = 6, + EmptyDirectory = 7, + UnableToCreatePackage = 8, + LowMemory = 9, + CannotWriteManifest = 10, + UnsignedAssembly = 11, + Unknown = 99, + } + + contract InstallerServiceContract : ServiceContract + { + public const string ModuleName = "/service/installer"; + + out message Success(); + + in message Add(char[]! in ExHeap path); + out message AckAdd(); + out message NakAdd(InstallerCode errorCode); + + in message Delete(char[]! in ExHeap path); + out message AckDelete(); + out message NakDelete(InstallerCode errorCode); + + override state Start : one { + Success! -> Ready; + } + + state Ready : one { + Add? -> (AckAdd! + or NakAdd! + ) -> Ready; + + Delete? -> (AckDelete! + or NakDelete! + ) -> Ready; + + } + } +} diff --git a/base/Contracts/Io.Contracts/AssemblyInfo.sg b/base/Contracts/Io.Contracts/AssemblyInfo.sg new file mode 100644 index 0000000..7459ec5 --- /dev/null +++ b/base/Contracts/Io.Contracts/AssemblyInfo.sg @@ -0,0 +1,8 @@ +using System.Reflection; + +[assembly: AssemblyTitle("Microsoft.Singularity.Io")] +[assembly: AssemblyProduct("Microsoft Research Singularity IO Contracts")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Contracts/Io.Contracts/ChannelDeliveryContract.sg b/base/Contracts/Io.Contracts/ChannelDeliveryContract.sg new file mode 100644 index 0000000..b2edbec --- /dev/null +++ b/base/Contracts/Io.Contracts/ChannelDeliveryContract.sg @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ChannelDeliveryContract.sg +// Note: Contract definition for Channel Delivery mechansims +// + +using System; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Channels; + +namespace Microsoft.Singularity.Io +{ + public contract ChannelDeliveryContract : ServiceContract + { + public const string ModuleName = "/service/channel-delivery"; + + public rep struct MemoryRegion + { + public UIntPtr Base; + public UIntPtr Length; + + public void Initialize(UIntPtr baseAddress, UIntPtr length) + { + this.Base = baseAddress; + this.Length = length; + } + } + + // Signal our identity + out message Ready(); + + override state Start : one + { + Ready! -> ReadyState; + } + + state ReadyState : one + { + } + } +} diff --git a/base/Contracts/Io.Contracts/DiskContract.sg b/base/Contracts/Io.Contracts/DiskContract.sg index 2730356..cc22dee 100644 --- a/base/Contracts/Io.Contracts/DiskContract.sg +++ b/base/Contracts/Io.Contracts/DiskContract.sg @@ -26,6 +26,9 @@ namespace Microsoft.Singularity.Io public contract DiskDeviceContract : DeviceContract { + /// + /// Retrieve the device name. + /// in message GetDeviceName(); out message AckGetDeviceName(char* opt(ExHeap[])data); diff --git a/base/Contracts/Io.Contracts/Io.Contracts.Kernel.csproj b/base/Contracts/Io.Contracts/Io.Contracts.Kernel.csproj index 65aa4f6..8d9c00e 100644 --- a/base/Contracts/Io.Contracts/Io.Contracts.Kernel.csproj +++ b/base/Contracts/Io.Contracts/Io.Contracts.Kernel.csproj @@ -33,12 +33,14 @@ + + + - true - + diff --git a/base/Contracts/Io.Contracts/Io.Contracts.csproj b/base/Contracts/Io.Contracts/Io.Contracts.csproj index 5769564..a4014fe 100644 --- a/base/Contracts/Io.Contracts/Io.Contracts.csproj +++ b/base/Contracts/Io.Contracts/Io.Contracts.csproj @@ -1,5 +1,4 @@ - - + + + + + + Library + Io.Net.Contracts + true + true + true + true + + + + + + + + + + + + + + + + + + + + diff --git a/base/Contracts/Io.Net.Contracts/Io.Net.Contracts.csproj b/base/Contracts/Io.Net.Contracts/Io.Net.Contracts.csproj index 4780576..09339fb 100644 --- a/base/Contracts/Io.Net.Contracts/Io.Net.Contracts.csproj +++ b/base/Contracts/Io.Net.Contracts/Io.Net.Contracts.csproj @@ -5,7 +5,7 @@ # # Copyright (c) Microsoft Corporation. All rights reserved. # -# File: Contracts\Io.Net.Contracts\Io.Net.Contracts.csproj +# File: Contracts\Io.Net.Contracts\Io.Net.Contracts.App.csproj # # Note: # @@ -25,16 +25,17 @@ - + + - + - + diff --git a/base/Contracts/Io.Net.Contracts/PacketFragment.sg b/base/Contracts/Io.Net.Contracts/PacketFragment.sg index d980cf8..4e96c04 100644 --- a/base/Contracts/Io.Net.Contracts/PacketFragment.sg +++ b/base/Contracts/Io.Net.Contracts/PacketFragment.sg @@ -47,19 +47,12 @@ namespace Microsoft.Singularity.Io.Net requires bufferLength >= 0; { expose (this) { - if (this.data == null || bufferLength >= this.data.Length) { + if (this.data != null) { delete this.data; - this.data = buffer; - this.start = bufferStart; - this.length = bufferLength; - } - else { - Bitter.Copy(this.data, 0, bufferLength, - buffer, bufferStart); - this.start = 0; - this.length = bufferLength; - delete buffer; } + this.data = buffer; + this.start = bufferStart; + this.length = bufferLength; } } diff --git a/base/Contracts/MapPointProxy.Contracts/AssemblyInfo.sg b/base/Contracts/MapPointProxy.Contracts/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Contracts/MapPointProxy.Contracts/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Contracts/MapPointProxy.Contracts/MapPointProxy.Contracts.csproj b/base/Contracts/MapPointProxy.Contracts/MapPointProxy.Contracts.csproj index 575962d..2824d36 100644 --- a/base/Contracts/MapPointProxy.Contracts/MapPointProxy.Contracts.csproj +++ b/base/Contracts/MapPointProxy.Contracts/MapPointProxy.Contracts.csproj @@ -24,6 +24,7 @@ + diff --git a/base/Contracts/MapPointProxy.Contracts/MapPointProxyContract.sg b/base/Contracts/MapPointProxy.Contracts/MapPointProxyContract.sg index a95b38b..f95cdeb 100644 --- a/base/Contracts/MapPointProxy.Contracts/MapPointProxyContract.sg +++ b/base/Contracts/MapPointProxy.Contracts/MapPointProxyContract.sg @@ -4,7 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: MapPointProxyContract.sg // Note: Contract for a ChildMapPointProxy process // @@ -27,7 +26,7 @@ namespace Microsoft.Singularity.MapPointProxy.Contracts public char[] in ExHeap label; } - public const string ModuleName = "/dev/MapPointProxy"; + public const string ModuleName = "/service/mappoint"; in message GetMap(char[]! in ExHeap centerLat, char[]! in ExHeap centerLong, int zoom, PushPin[] in ExHeap pushPins); diff --git a/base/Contracts/NameSpace.Contracts/Directory.sg b/base/Contracts/NameSpace.Contracts/Directory.sg deleted file mode 100644 index 7333f1d..0000000 --- a/base/Contracts/NameSpace.Contracts/Directory.sg +++ /dev/null @@ -1,122 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -using System; -using Microsoft.SingSharp; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; - -namespace Microsoft.Singularity.Directory { - contract DirectoryServiceContract : ServiceContract { - - // Flag values (used in AckAttributes) - public enum FileFlags - { - File = 128, - SymLink = 64, - Directory = 16, - Hidden = 2, //will we ever support this? - ReadOnly = 1, //will we ever support this? - } - - out message Success(); - - //////////////////////////////////////////////////////// - // Directory-related messages - //////////////////////////////////////////////////////// - in message CreateDirectory(char []! in ExHeap dirName); - out message AckCreateDirectory(); - out message NakCreateDirectory(ErrorCode code); - - in message CreateAndBindDirectory(char []! in ExHeap dirName, DirectoryServiceContract.Imp:Ready! imp); - out message AckCreateAndBindDirectory(); - out message NakCreateAndBindDirectory(ErrorCode code,DirectoryServiceContract.Imp:Ready imp); - - in message BindDirectory(char []! in ExHeap dirName, DirectoryServiceContract.Imp:Ready! imp); - out message AckBindDirectory(); - out message NakBindDirectory(ErrorCode code,DirectoryServiceContract.Imp:Ready imp); - - in message DeleteDirectory(char []! in ExHeap dirName); - out message AckDeleteDirectory(); - out message NakDeleteDirectory(ErrorCode code); - - //////////////////////////////////////////////////////// - // File-related messages - //////////////////////////////////////////////////////// - in message CreateFile(char []! in ExHeap fileName); - out message AckCreateFile(); - out message NakCreateFile(ErrorCode code); - - in message CreateAndBindFile(char []! in ExHeap fileName, FileContract.Imp:Ready! imp); - out message AckCreateAndBindFile(); - out message NakCreateAndBindFile(ErrorCode code,FileContract.Imp:Ready imp); - - in message BindFile(char []! in ExHeap fileName, FileContract.Imp:Ready! imp); - out message AckBindFile(); - out message NakBindFile(ErrorCode code,FileContract.Imp:Ready imp); - - in message DeleteFile(char []! in ExHeap fileName); - out message AckDeleteFile(); - out message NakDeleteFile(ErrorCode code); - - in message Attributes(char []! in ExHeap fileName); - out message AckAttributes(long size, uint flags, long linkFlags); - out message NakFileAttributes(ErrorCode code); - - //////////////////////////////////////////////////////// - // Security-related messages - //////////////////////////////////////////////////////// - in message QueryACL(char []! in ExHeap fileName, - byte[]! in ExHeap permission); - out message AckQueryACL(byte[]! in ExHeap acl); - out message NakQueryACL(ErrorCode code); - - in message StoreACL(char []! in ExHeap fileName, - byte[]! in ExHeap permission, - byte[]! in ExHeap acl); - out message AckStoreACL(); - out message NakStoreACL(ErrorCode code); - - //////////////////////////////////////////////////////// - // SymbolicLink-related messages - //////////////////////////////////////////////////////// - in message CreateLink(char []! in ExHeap linkPath, char []! in ExHeap linkValue ); - out message AckCreateLink(); - out message NakCreateLink(ErrorCode code); - - in message DeleteLink(char []! in ExHeap linkPath); - out message AckDeleteLink(); - out message NakDeleteLink(ErrorCode code); - - in message Close(); - out message AckClose(); - - - override state Start: one { - Success! -> Ready; - } - - state Ready: one { - CreateDirectory? -> (AckCreateDirectory! or NakCreateDirectory!) -> Ready; - CreateAndBindDirectory? -> (AckCreateAndBindDirectory! or NakCreateAndBindDirectory!) -> Ready; - BindDirectory? -> (AckBindDirectory! or NakBindDirectory!) -> Ready; - DeleteDirectory? -> (AckDeleteDirectory! or NakDeleteDirectory!) -> Ready; - - CreateFile? -> (AckCreateFile! or NakCreateFile!) -> Ready; - CreateAndBindFile? -> (AckCreateAndBindFile! or NakCreateAndBindFile!) -> Ready; - BindFile? -> (AckBindFile! or NakBindFile!) -> Ready; - DeleteFile? -> (AckDeleteFile! or NakDeleteFile!) -> Ready; - Attributes? -> (AckAttributes! or NakFileAttributes!) -> Ready; - - QueryACL? -> (AckQueryACL! or NakQueryACL!) -> Ready; - StoreACL? -> (AckStoreACL! or NakStoreACL!) -> Ready; - - CreateLink? -> (AckCreateLink! or NakCreateLink!) -> Ready; - DeleteLink? -> (AckDeleteLink! or NakDeleteLink!) -> Ready; - - Close? -> AckClose! -> Ready; - } - } -} diff --git a/base/Contracts/NameSpace.Contracts/DirectoryContract.sg b/base/Contracts/NameSpace.Contracts/DirectoryContract.sg deleted file mode 100644 index 7e8b846..0000000 --- a/base/Contracts/NameSpace.Contracts/DirectoryContract.sg +++ /dev/null @@ -1,262 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: DirectoryServiceContract.sg -// - -using Microsoft.SingSharp; -using Microsoft.Singularity.Channels; - -namespace Microsoft.Singularity.Directory -{ - // enumeration of possible errors reported by any operation - public enum ErrorCode : uint - { - AccessDenied, - AlreadyExists, - BadArguments, - ContractNotSupported, - DirectoryNotEmpty, - DoesNotExist, - NotFound, - NotImplemented, - } - - // enumeration of the existing node types found in the namespace - // implementation and the filesystem - public enum NodeType - { - Dir, - File, - IoMemory, - ServiceProvider, - SymLink, - } - - // a REP struct used to return responses to find or notify. - public rep struct FindResponse : ITracked - { - public char[]! in ExHeap Path; // short name within a directory - public NodeType Type; // its type - } - - public contract DirectoryServiceContract : ServiceContract - { - - // attempt to bind to a service exp endpoint - in message Bind(char[]! in ExHeap path, - ServiceContract.Exp:Start! exp); - out message AckBind(); - // attempt to return unused endpoint if possible - out message NakBind(ErrorCode code, - ServiceContract.Exp:Start! exp ); - - // Find path and notify caller of any changes matching pattern over - // the imp endpoint supplied. - in message Notify(char[]! in ExHeap path,char[]! in ExHeap pattern, - NotifyContract.Imp:Start! imp); - out message AckNotify(); - out message NakNotify(NotifyContract.Imp:Start! imp, - ErrorCode error); - - // Find path and return all elements matching pattern - in message Find(char[]! in ExHeap path,char[]! in ExHeap pattern); - out message AckFind (FindResponse[]! in ExHeap results); - out message NakFind(ErrorCode error); - - // Find path and return all elements matching pattern A - // AND notify of all changes over the imp endpoint supplied. - // this should solve any potential race conditions if - in message FindAndNotify(char[]! in ExHeap path, - char[]! in ExHeap pattern, - NotifyContract.Imp:Start! imp); - out message AckFindAndNotify (FindResponse[]! in ExHeap results); - out message NakFindAndNotify(NotifyContract.Imp:Start! imp, - ErrorCode error); - - // given a path return its nodetype and if meaningful its length - in message Attributes(char []! in ExHeap path); - out message AckAttributes(NodeType type, long size); - out message NakAttributes(ErrorCode code); - - // given a path return the associated ACL if present - in message QueryACL(char []! in ExHeap path, - byte[]! in ExHeap permission); - out message AckQueryACL(byte[]! in ExHeap acl); - out message NakQueryACL(ErrorCode code); - - - // Greater permissions needed for ops below - - // register a service's name serviceProvider endpoint with the SDS - in message Register(char[]! in ExHeap path, - ServiceProviderContract.Imp:Start! imp); - out message AckRegister(); - out message NakRegister(ServiceProviderContract.Imp:Start imp, - ErrorCode error); - - // create dirName within the current directory - // the current directory is the one bound on this channel - in message CreateDirectory(char []! in ExHeap dirName); - out message AckCreateDirectory(); - out message NakCreateDirectory(ErrorCode code); - - // delete dirName within the current directory - // the current directory is the one bound on this channel - in message DeleteDirectory(char []! in ExHeap dirName); - out message AckDeleteDirectory(); - out message NakDeleteDirectory(ErrorCode code); - - // create file within the current directory - // the current directory is the one bound on this channel - // file creation only applies to the FS - in message CreateFile(char []! in ExHeap fileName); - out message AckCreateFile(); - out message NakCreateFile(ErrorCode code); - - // create and "open" or bind to fileName within the current directory - // the current directory is the one bound on this channel - // this is common enough pattern to supply an optimization - in message CreateAndBindFile(char []! in ExHeap fileName, - FileContract.Imp:Ready! imp); - out message AckCreateAndBindFile(); - out message NakCreateAndBindFile(ErrorCode code, - FileContract.Imp:Ready imp); - - // delete fileName within the current directory - // the current directory is the one bound on this channel - in message DeleteFile(char []! in ExHeap fileName); - out message AckDeleteFile(); - out message NakDeleteFile(ErrorCode code); - - // for the given path and permission, return the associated ACL if present - - in message StoreACL(char []! in ExHeap fileName, - byte[]! in ExHeap permission, - byte[]! in ExHeap acl); - out message AckStoreACL(); - out message NakStoreACL(ErrorCode code); - - // create a symlink node at linkPath with value linkValue - // Upon traversal linkPath will be returned to client - // it is up to the client to interpret and re-submit bind - // see reparse messages below. - - in message CreateLink(char []! in ExHeap linkPath, - char []! in ExHeap linkValue ); - out message AckCreateLink(); - out message NakCreateLink(ErrorCode code); - - // delete symlink node at linkPath - in message DeleteLink(char []! in ExHeap linkPath); - out message AckDeleteLink(); - out message NakDeleteLink(ErrorCode code); - - // REPARSE MESSAGES - // used for 2 different scenarios: DirectoryProvider and SymLink traversal - // DirectoryProvider: - // path=new directoryProvider to bind to to continue traversal - // rest=suffix(P): the remainder of the initial path not yet parsed. - // SymLink - // path=contents of the symbolic link - // rest=suffix(P): the remainder of the initial path not yet parsed. - out message NakBindReparse(char[]! in ExHeap path, - char[]! in ExHeap rest, - bool linkFound, - ServiceContract.Exp:Start! exp); - - out message NakNotifyReparse(char[]! in ExHeap path, - char[]! in ExHeap rest, - bool linkFound, - NotifyContract.Imp:Start imp); - - out message NakFindAndNotifyReparse(char[]! in ExHeap path, - char[]! in ExHeap rest, - bool linkFound, - NotifyContract.Imp:Start imp); - - out message NakFindReparse(char[]! in ExHeap path, - char[]! in ExHeap rest, - bool linkFound); - - out message NakRegisterReparse(char[]! in ExHeap path, - char[]! in ExHeap rest, - ServiceProviderContract.Imp:Start imp); - - out message Success(); - - //////////////////////////////////////////////////////// - // State Machine - //////////////////////////////////////////////////////// - - override state Start: one { - Success! -> Ready; - } - state Ready: one - { - Bind? -> (AckBind! - or NakBind! - or NakBindReparse! - ) -> Ready; - - Find? -> (AckFind! - or NakFind! - or NakFindReparse! - ) -> Ready; - - FindAndNotify? -> (AckFindAndNotify! - or NakFindAndNotify! - or NakFindAndNotifyReparse! - ) -> Ready; - - - Notify? -> (AckNotify! - or NakNotify! - or NakNotifyReparse! - ) -> Ready; - - Register? -> (AckRegister! - or NakRegister! - or NakRegisterReparse! - ) -> Ready; - - CreateDirectory? -> (AckCreateDirectory! - or NakCreateDirectory! - ) -> Ready; - - DeleteDirectory? -> (AckDeleteDirectory! - or NakDeleteDirectory! - ) -> Ready; - - CreateFile? -> (AckCreateFile! - or NakCreateFile! - ) -> Ready; - - CreateAndBindFile? -> (AckCreateAndBindFile! - or NakCreateAndBindFile! - ) -> Ready; - - DeleteFile? -> (AckDeleteFile! - or NakDeleteFile! - ) -> Ready; - - Attributes? -> (AckAttributes! - or NakAttributes! - ) -> Ready; - - QueryACL? -> (AckQueryACL! or NakQueryACL!) -> Ready; - StoreACL? -> (AckStoreACL! or NakStoreACL!) -> Ready; - - CreateLink? -> (AckCreateLink! - or NakCreateLink! - ) -> Ready; - - DeleteLink? -> (AckDeleteLink! - or NakDeleteLink! - ) -> Ready; - } - } -} diff --git a/base/Contracts/NameSpace.Contracts/ExtensionContract.sg b/base/Contracts/NameSpace.Contracts/ExtensionContract.sg deleted file mode 100644 index ba1c044..0000000 --- a/base/Contracts/NameSpace.Contracts/ExtensionContract.sg +++ /dev/null @@ -1,35 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: ExtensionContract.sg -// - -// This is the base contract to be used, and overridden by, every -// system extension loaded as a child process. - -using Microsoft.Singularity.Directory; - -namespace Microsoft.Singularity.Extending -{ - public contract ExtensionContract : ServiceContract - { - in message Shutdown(); // Shutdown the extension gracefully. - out message AckShutdown(); - out message NakShutdown(); - - out message Success(); - - override state Start: one { - Success! -> Ready; - } - - state Ready: one - { - Shutdown? -> (AckShutdown! or NakShutdown!) -> Ready; - // AckShutdown should really go to done. - } - } -} diff --git a/base/Contracts/NameSpace.Contracts/FSDirectoryServiceContract.sg b/base/Contracts/NameSpace.Contracts/FSDirectoryServiceContract.sg deleted file mode 100644 index 5306c6d..0000000 --- a/base/Contracts/NameSpace.Contracts/FSDirectoryServiceContract.sg +++ /dev/null @@ -1,94 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -using System; -using Microsoft.SingSharp; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; - -namespace Microsoft.Singularity.Directory { - contract DirectoryServiceContract : ServiceContract { - // Flag values (used in AckAttributes) - public enum FileFlags - { - Normal = 128, - Directory = 16, - Hidden = 2, - ReadOnly = 1, - } - - out message Success(); - - in message Bind(char[]! in ExHeap path, ServiceContract.Exp:Start! exp); - out message AckBind(); - out message NakBind(ServiceContract.Exp:Start exp); // return unconnected endpoint - - in message CreateDirectory(char []! in ExHeap dirName); - out message AckCreateDirectory(); - out message NakCreateDirectory(); - - in message DeleteDirectory(char []! in ExHeap dirName); - out message AckDeleteDirectory(); - out message NakDeleteDirectory(); - - in message CreateFile(char []! in ExHeap fileName); - out message AckCreateFile(); - out message NakCreateFile(); - - in message DeleteFile(char []! in ExHeap fileName); - out message AckDeleteFile(); - out message NakDeleteFile(); - - in message Attributes(char []! in ExHeap fileName); - out message AckAttributes(long size, uint flags, long linkFlags); - out message NakAttributes(); - - in message QueryACL(char []! in ExHeap fileName, - byte[]! in ExHeap permission); - out message AckQueryACL(byte[]! in ExHeap acl); - out message NakQueryACL(); - - in message StoreACL(char []! in ExHeap fileName, - byte[]! in ExHeap permission, - byte[]! in ExHeap acl); - out message AckStoreACL(); - out message NakStoreACL(); - - in message CreateLink(char []! in ExHeap linkPath, char []! in ExHeap linkValue ); - out message AckCreateLink(); - out message NakCreateLink(); - - in message DeleteLink(char []! in ExHeap linkPath); - out message AckDeleteLink(); - out message NakDeleteLink(); - - in message Close(); - out message AckClose(); - - override state Start: one { - Success! -> Ready; - } - - state Ready: one { - Bind? -> ( AckBind! or NakBind!) -> Ready; - - CreateDirectory? -> ( AckCreateDirectory! or NakCreateDirectory! ) -> Ready; - DeleteDirectory? -> ( AckDeleteDirectory! or NakDeleteDirectory! ) -> Ready; - - CreateFile? -> (AckCreateFile! or NakCreateFile!) -> Ready; - DeleteFile? -> ( AckDeleteFile! or NakDeleteFile!) -> Ready; - - Attributes? -> (AckAttributes! or NakAttributes!) -> Ready; - - QueryACL? -> (AckQueryACL! or NakQueryACL!) -> Ready; - StoreACL? -> (AckStoreACL! or NakStoreACL!) -> Ready; - - CreateLink? -> ( AckCreateLink! or NakCreateLink! ) -> Ready; - DeleteLink? -> ( AckDeleteLink! or NakDeleteLink! ) -> Ready; - - Close? -> AckClose! -> Ready; - } - } -} diff --git a/base/Contracts/NameSpace.Contracts/FileContract.sg b/base/Contracts/NameSpace.Contracts/FileContract.sg deleted file mode 100644 index cde5e70..0000000 --- a/base/Contracts/NameSpace.Contracts/FileContract.sg +++ /dev/null @@ -1,37 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -using System; -using Microsoft.SingSharp; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; - -namespace Microsoft.Singularity.Directory { - contract FileContract : ServiceContract { - out message Success(); - - in message Read(byte []! in ExHeap buf, long bufOffset, - long fileOffset, long maxLength); - out message AckRead(byte []! in ExHeap buf, long bytesRead, int error); - - in message Write(byte []! in ExHeap buf, long bufOffset, - long fileOffset, long maxLength); - out message AckWrite(byte []! in ExHeap buf, long bytesWritten, - int error); - - in message Close(); - out message AckClose(); - - override state Start: one { - Success! -> Ready; - } - - state Ready: one { - Read? -> AckRead! -> Ready; - Write? -> AckWrite! -> Ready; - Close? -> AckClose! -> Ready; - } - } -} diff --git a/base/Contracts/NameSpace.Contracts/FsFileServiceContract.sg b/base/Contracts/NameSpace.Contracts/FsFileServiceContract.sg deleted file mode 100644 index 06eeb67..0000000 --- a/base/Contracts/NameSpace.Contracts/FsFileServiceContract.sg +++ /dev/null @@ -1,37 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -using System; -using Microsoft.SingSharp; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; - -namespace Microsoft.Singularity.Directory { - contract FileContract : ServiceContract { - out message Success(); - - in message Read(byte []! in ExHeap buf, long bufOffset, - long fileOffset, long maxLength); - out message AckRead(byte []! in ExHeap buf, long bytesRead, int error); - - in message Write(byte []! in ExHeap buf, long bufOffset, - long fileOffset, long maxLength); - out message AckWrite(byte []! in ExHeap buf, long bytesWritten, - int error); - - in message Close(); - out message AckClose(); - - override state Start: one { - Success! -> Ready; - } - - state Ready: one { - Read? -> AckRead! -> Ready; - Write? -> AckWrite! -> Ready; - Close? -> AckClose! -> Ready; - } - } -} diff --git a/base/Contracts/NameSpace.Contracts/NameSpace.Contracts.csproj b/base/Contracts/NameSpace.Contracts/NameSpace.Contracts.csproj deleted file mode 100644 index cd81a7c..0000000 --- a/base/Contracts/NameSpace.Contracts/NameSpace.Contracts.csproj +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - Library - Directory.Contracts - true - true - true - - - - - - - - - - - - - - - - - - diff --git a/base/Contracts/NameSpace.Contracts/NameSpaceContract.sg b/base/Contracts/NameSpace.Contracts/NameSpaceContract.sg deleted file mode 100644 index 61584dc..0000000 --- a/base/Contracts/NameSpace.Contracts/NameSpaceContract.sg +++ /dev/null @@ -1,59 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: DirectoryServiceContract.sg -// - -using Microsoft.SingSharp; -using Microsoft.Singularity.Channels; - -namespace Microsoft.Singularity.Directory -{ - public enum NodeType - { - Inner = 0, - Dir = 1, - Provider = 2, - SymLink = 3, - File = 4, - IoMemory = 5, - } - - public rep struct FindResponse : ITracked - { - public char[]! in ExHeap Path; - public NodeType Type; - } - - public contract DirectoryServiceContract : ServiceContract - { - // ReadOnly part - - in message Bind(char[]! in ExHeap path, ServiceContract.Exp:Start! exp); - out message AckBind(); - out message NakBind(ServiceContract.Exp:Start exp); // return unconnected endpoint - - in message Notify(char[]! in ExHeap pathSpec, NotifyContract.Imp:Start! imp); - out message AckNotify(); - out message NakNotify(NotifyContract.Imp:Start! imp); // return unconnected endpoint - - in message Find(char[]! in ExHeap pathSpec, char[]! in ExHeap pattern); - out message AckFind(FindResponse[]! in ExHeap results); - out message NakFind(); - - out message Success(); - - override state Start: one { - Success! -> Ready; - } - state Ready: one - { - Bind? -> ( AckBind! or NakBind! ) -> Ready; - Find? -> ( AckFind! or NakFind! ) -> Ready; - Notify? -> ( AckNotify! or NakNotify! ) -> Ready; - } - } -} diff --git a/base/Contracts/NameSpace.Contracts/NameSpaceWriteContract.sg b/base/Contracts/NameSpace.Contracts/NameSpaceWriteContract.sg deleted file mode 100644 index 32ad06a..0000000 --- a/base/Contracts/NameSpace.Contracts/NameSpaceWriteContract.sg +++ /dev/null @@ -1,87 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: DirectoryServiceContract.sg -// - -using Microsoft.SingSharp; -using Microsoft.Singularity.Channels; - -namespace Microsoft.Singularity.Directory -{ - public contract DirectoryServiceContract : ServiceContract - { - // ReadOnly part - - in message Bind(char[]! in ExHeap path, ServiceContract.Exp:Start! exp); - out message AckBind(); - out message NakBind(ServiceContract.Exp:Start exp); // return unconnected endpoint - out message NakBindReparse( char[]! in ExHeap linkValue, ServiceContract.Exp:Start! exp); - - in message Notify(char[]! in ExHeap pathSpec, NotifyContract.Imp:Start! imp); - out message AckNotify(); - out message NakNotify(NotifyContract.Imp:Start! imp); // return unconnected endpoint - out message NakNotifyReparse(char []! in ExHeap symlink, NotifyContract.Imp:Start! imp); // return unconnected endpoint - - in message Find(char[]! in ExHeap path, char[]! in ExHeap pattern); - out message AckFind (FindResponse[]! in ExHeap results); - out message NakFind(); - - - // ReadWrite part - - in message Register(char[]! in ExHeap path, ServiceProviderContract.Imp:Start! imp); - out message AckRegister(); - out message NakRegister(ServiceProviderContract.Imp:Start imp); // return unconnected endpoint - - in message Deregister(char[]! in ExHeap path); - out message AckDeregister(ServiceProviderContract.Imp:Start! imp); // return deregistered endpoint - out message NakDeregister(); - - in message RegisterNameProvider(char[]! in ExHeap path, DirectoryServiceContract.Imp:Ready! imp); - out message AckRegisterNameProvider(); - out message NackRegisterNameProvider(DirectoryServiceContract.Imp:Ready! imp); // return unconnected endpoint - - in message DeregisterNameProvider(char[]! in ExHeap path); - out message AckDeregisterNameProvider(DirectoryServiceContract.Imp:Ready! imp); // return deregistered endpoint - out message NackDeregisterNameProvider(); - - // - // FindFirst/FindNext: code= 0 more entries, 1 no more entries - // - in message FindFirst(char[]! in ExHeap prefix); - out message AckFindFirst(char[] in ExHeap path, int code); - out message NakFindFirst(); - - in message FindNext(char[]! in ExHeap prefix, - char[]! in ExHeap previous); - - out message AckFindNext(char[] in ExHeap path, - int code); - - out message NakFindNext(); - - out message Success(); - - override state Start: one { - Success! -> Ready; - } - state Ready: one - { - Bind? -> ( AckBind! or NakBind! or NakBindReparse!) -> Ready; - Find? -> ( AckFind! or NakFind! ) -> Ready; - Notify? -> ( AckNotify! or NakNotify! or NakNotifyReparse! ) -> Ready; - - FindNext? -> ( AckFindNext! or NakFindNext! ) -> Ready; - FindFirst? -> ( AckFindFirst! or NakFindFirst!) -> Ready; - - Register? -> ( AckRegister! or NakRegister! ) -> Ready; - Deregister? -> ( AckDeregister! or NakDeregister! ) -> Ready; - RegisterNameProvider? -> ( AckRegisterNameProvider! or NackRegisterNameProvider! ) -> Ready; - DeregisterNameProvider? -> ( AckDeregisterNameProvider! or NackDeregisterNameProvider! ) -> Ready; - } - } -} diff --git a/base/Contracts/NameSpace.Contracts/NotifyContract.sg b/base/Contracts/NameSpace.Contracts/NotifyContract.sg deleted file mode 100644 index 1c3dbd3..0000000 --- a/base/Contracts/NameSpace.Contracts/NotifyContract.sg +++ /dev/null @@ -1,34 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: NotifyContract.sg -// - -using Microsoft.SingSharp; -using Microsoft.Singularity.Channels; - -namespace Microsoft.Singularity.Directory -{ - public enum NotifyType - { - Creation, - Deletion, - Modification - } - - public contract NotifyContract - { - in message Begin(); - - in message ChangeNotification(char []! in ExHeap path, NotifyType type); - out message AckChangeNotification(); - - state Start : Begin? -> Notify; - - state Notify : ChangeNotification? -> NotifyAck; - state NotifyAck: AckChangeNotification! -> Notify; - } -} diff --git a/base/Contracts/NameSpace.Contracts/ServiceContract.sg b/base/Contracts/NameSpace.Contracts/ServiceContract.sg deleted file mode 100644 index 5c1b665..0000000 --- a/base/Contracts/NameSpace.Contracts/ServiceContract.sg +++ /dev/null @@ -1,26 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: ServiceContract.sg -// - -// This is the base contract to be used, and overridden by, every Service -// participating in the Directory - -namespace Microsoft.Singularity.Directory -{ - public contract ServiceContract - { - out message ContractNotSupported(); - - state Start: one - { - ContractNotSupported! -> Done; - } - state Done: one {} - } - -} diff --git a/base/Contracts/NameSpace.Contracts/ServiceProviderContract.sg b/base/Contracts/NameSpace.Contracts/ServiceProviderContract.sg deleted file mode 100644 index 0297d7a..0000000 --- a/base/Contracts/NameSpace.Contracts/ServiceProviderContract.sg +++ /dev/null @@ -1,32 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: ServiceProviderContract.sg -// - -// -// Every leaf in the Directory communicates to a service by connecting through this contract -// - -namespace Microsoft.Singularity.Directory -{ - contract ServiceProviderContract - { - in message Connect(ServiceContract.Exp:Start! exp); - out message AckConnect(); - out message NackConnect(ServiceContract.Exp:Start exp); - // return the unconnected endpoint if possible. - - state Start: Connect? -> Ack; - - state Ack: one - { - AckConnect! -> Start; - NackConnect! -> Start; - } - } - } - diff --git a/base/Contracts/NetStack.Contracts/AssemblyInfo.sg b/base/Contracts/NetStack.Contracts/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Contracts/NetStack.Contracts/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Contracts/NetStack.Contracts/DNSContract.sg b/base/Contracts/NetStack.Contracts/DNSContract.sg index 62f6063..4d77fcb 100644 --- a/base/Contracts/NetStack.Contracts/DNSContract.sg +++ b/base/Contracts/NetStack.Contracts/DNSContract.sg @@ -4,11 +4,10 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: DNSContract.gs // Note: Contract definition for interacting with the DNS Module // -// note This is all IPv4 flavored for now. +// NOTE: This is all IPv4 flavored for now. using Microsoft.Singularity.Channels; using Microsoft.Singularity.Directory; @@ -19,7 +18,7 @@ namespace NetStack.Contracts { // This is the name under which the DNS module registers with the // name server - public const string ModuleName = "/dev/dns"; + public const string ModuleName = "/service/dns"; // Response messages out message NotFound(); diff --git a/base/Contracts/NetStack.Contracts/IPContract.sg b/base/Contracts/NetStack.Contracts/IPContract.sg index 91df741..bc9b84a 100644 --- a/base/Contracts/NetStack.Contracts/IPContract.sg +++ b/base/Contracts/NetStack.Contracts/IPContract.sg @@ -31,8 +31,7 @@ namespace NetStack.Contracts public void Set(byte[]! bytes) { - if (bytes.Length != 6) - { + if (bytes.Length != 6) { throw new ArgumentException("Invalid array length in HardwareAddress()"); } @@ -68,7 +67,7 @@ namespace NetStack.Contracts { // This is the name under which the IP module registers itself with // the name server - public const string ModuleName = "/dev/ip"; + public const string ModuleName = "/service/ip"; // Response messages out message InterfaceList(char[][]! in ExHeap names); diff --git a/base/Contracts/NetStack.Contracts/NetStack.Contracts.csproj b/base/Contracts/NetStack.Contracts/NetStack.Contracts.csproj index 74f9453..0ee0947 100644 --- a/base/Contracts/NetStack.Contracts/NetStack.Contracts.csproj +++ b/base/Contracts/NetStack.Contracts/NetStack.Contracts.csproj @@ -23,6 +23,7 @@ + diff --git a/base/Contracts/NetStack.Contracts/RoutingContract.sg b/base/Contracts/NetStack.Contracts/RoutingContract.sg index 0dbc12b..a233580 100644 --- a/base/Contracts/NetStack.Contracts/RoutingContract.sg +++ b/base/Contracts/NetStack.Contracts/RoutingContract.sg @@ -5,6 +5,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // File: RoutingContract.gs +// // Note: Contract definition for interacting with the IP Routing module // @@ -32,7 +33,7 @@ namespace NetStack.Contracts { // This is the name under which the routing module // registers itself with the name server. - public const string ModuleName = "/dev/routing"; + public const string ModuleName = "/service/routing"; // Response messages out message RoutingTable(RouteEntry[] in ExHeap routes); diff --git a/base/Contracts/NetStack.Contracts/TCPConnectionContract.sg b/base/Contracts/NetStack.Contracts/TCPConnectionContract.sg index 7bbd077..4829734 100644 --- a/base/Contracts/NetStack.Contracts/TCPConnectionContract.sg +++ b/base/Contracts/NetStack.Contracts/TCPConnectionContract.sg @@ -4,7 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: TcpConnectionContract.sg // Note: Contract definition for TCP channels // @@ -13,22 +12,20 @@ using System; namespace NetStack.Contracts { - public enum TcpError - { - Unknown = 1, - AlreadyConnected, // this connection is already in use - Refused, // the receiving host actively refused the connection - Reset, // the connection was reset - Timeout, // no response was received - ProtocolViolation, // we received a TCP segment that is not acceptable in the current state - ResourcesExhausted, // out of memory, etc. - Closed, // remote peer has closed socket - } - + public enum TcpError + { + Unknown = 1, + AlreadyConnected, // this connection is already in use + Refused, // the receiving host actively refused the connection + Reset, // the connection was reset + Timeout, // no response was received + ProtocolViolation, // we received a TCP segment that is not acceptable in the current state + ResourcesExhausted, // out of memory, etc. + Closed, // remote peer has closed socket + } + public contract TcpConnectionContract { - - // Requests in message Connect(uint dstIP, ushort dstPort); in message BindLocalEndPoint(uint dstIP, ushort dstPort); diff --git a/base/Contracts/NetStack.Contracts/TCPContract.sg b/base/Contracts/NetStack.Contracts/TCPContract.sg index 228017f..aa06a01 100644 --- a/base/Contracts/NetStack.Contracts/TCPContract.sg +++ b/base/Contracts/NetStack.Contracts/TCPContract.sg @@ -5,6 +5,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // File: TcpContract.sg +// // Note: Contract definition for TCP channels // @@ -15,7 +16,7 @@ namespace NetStack.Contracts public contract TcpContract : ServiceContract { // The name you should use to look up this module using the NameServer. - public const string ModuleName = "/dev/tcp"; + public const string ModuleName = "/service/tcp"; // Signal our identity out message Ready(); diff --git a/base/Contracts/NetStack.Contracts/UDPConnectionContract.sg b/base/Contracts/NetStack.Contracts/UDPConnectionContract.sg index 5cd1cc8..50b4e34 100644 --- a/base/Contracts/NetStack.Contracts/UDPConnectionContract.sg +++ b/base/Contracts/NetStack.Contracts/UDPConnectionContract.sg @@ -4,9 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: UdpConnectionContract.sg -// -// // Note: Contract definition for UDP channels // // UDP is ugly because of WriteTo and arbitrarily timed binds. diff --git a/base/Contracts/NetStack.Contracts/UDPContract.sg b/base/Contracts/NetStack.Contracts/UDPContract.sg index d4aaa81..a77a02f 100644 --- a/base/Contracts/NetStack.Contracts/UDPContract.sg +++ b/base/Contracts/NetStack.Contracts/UDPContract.sg @@ -16,7 +16,7 @@ namespace NetStack.Contracts public contract UdpContract : ServiceContract { // The name you should use to look up this module using the NameServer. - public const string ModuleName = "/dev/udp"; + public const string ModuleName = "/service/udp"; // Signal our identity out message Ready(); diff --git a/base/Contracts/PingPong.Contracts/AssemblyInfo.sg b/base/Contracts/PingPong.Contracts/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Contracts/PingPong.Contracts/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Contracts/PingPong.Contracts/PingContract.sg b/base/Contracts/PingPong.Contracts/PingContract.sg index e414d9b..24c0758 100644 --- a/base/Contracts/PingPong.Contracts/PingContract.sg +++ b/base/Contracts/PingPong.Contracts/PingContract.sg @@ -4,7 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: PingContract.sg // Note: Contract for a ChildPing process // diff --git a/base/Contracts/PingPong.Contracts/PingPong.Contracts.csproj b/base/Contracts/PingPong.Contracts/PingPong.Contracts.csproj index fefe840..70fceef 100644 --- a/base/Contracts/PingPong.Contracts/PingPong.Contracts.csproj +++ b/base/Contracts/PingPong.Contracts/PingPong.Contracts.csproj @@ -24,6 +24,7 @@ + diff --git a/base/Contracts/PingPong.Contracts/PongContract.sg b/base/Contracts/PingPong.Contracts/PongContract.sg index 3b8c34f..2d61982 100644 --- a/base/Contracts/PingPong.Contracts/PongContract.sg +++ b/base/Contracts/PingPong.Contracts/PongContract.sg @@ -4,7 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: PongContract.sg // Note: Contract definition for the ChildPong process // diff --git a/base/Contracts/SeattleTrafficProxy.Contracts/AssemblyInfo.sg b/base/Contracts/SeattleTrafficProxy.Contracts/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Contracts/SeattleTrafficProxy.Contracts/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Contracts/SeattleTrafficProxy.Contracts/SeattleTrafficProxy.Contracts.csproj b/base/Contracts/SeattleTrafficProxy.Contracts/SeattleTrafficProxy.Contracts.csproj index 9605561..2fdb7e7 100644 --- a/base/Contracts/SeattleTrafficProxy.Contracts/SeattleTrafficProxy.Contracts.csproj +++ b/base/Contracts/SeattleTrafficProxy.Contracts/SeattleTrafficProxy.Contracts.csproj @@ -23,6 +23,7 @@ + diff --git a/base/Contracts/SeattleTrafficProxy.Contracts/SeattleTrafficProxyContract.sg b/base/Contracts/SeattleTrafficProxy.Contracts/SeattleTrafficProxyContract.sg index e2e68ca..deb0e13 100644 --- a/base/Contracts/SeattleTrafficProxy.Contracts/SeattleTrafficProxyContract.sg +++ b/base/Contracts/SeattleTrafficProxy.Contracts/SeattleTrafficProxyContract.sg @@ -23,7 +23,7 @@ namespace Microsoft.Singularity.SeattleTrafficProxy.Contracts public contract SeattleTrafficProxyContract : ServiceContract { - public const string ModuleName = "/dev/SeattleTrafficProxy"; + public const string ModuleName = "/service/traffic"; // Signal our identity out message Ready(); diff --git a/base/Contracts/Security.Contracts/AssemblyInfo.sg b/base/Contracts/Security.Contracts/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Contracts/Security.Contracts/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Contracts/Security.Contracts/Security.Contracts.csproj b/base/Contracts/Security.Contracts/Security.Contracts.csproj index cdfa18e..cfcb5ce 100644 --- a/base/Contracts/Security.Contracts/Security.Contracts.csproj +++ b/base/Contracts/Security.Contracts/Security.Contracts.csproj @@ -23,6 +23,7 @@ + diff --git a/base/Contracts/Security.Contracts/SecurityDiagnosticsContract.sg b/base/Contracts/Security.Contracts/SecurityDiagnosticsContract.sg index 8f9f406..9dea06e 100644 --- a/base/Contracts/Security.Contracts/SecurityDiagnosticsContract.sg +++ b/base/Contracts/Security.Contracts/SecurityDiagnosticsContract.sg @@ -1,7 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // +// ---------------------------------------------------------------------------- namespace Microsoft.Singularity.Security { @@ -12,7 +13,7 @@ namespace Microsoft.Singularity.Security public contract SecurityDiagnosticsContract : ServiceContract { - public const string ModuleName="/securitydiag"; + public const string ModuleName="/status/security"; /// /// Tell the client the channel is ready. diff --git a/base/Contracts/ServiceManager.Contracts/BenchmarkContract.sg b/base/Contracts/ServiceManager.Contracts/BenchmarkContract.sg index 2826ba3..6a91b68 100644 --- a/base/Contracts/ServiceManager.Contracts/BenchmarkContract.sg +++ b/base/Contracts/ServiceManager.Contracts/BenchmarkContract.sg @@ -1,8 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // - +// ---------------------------------------------------------------------------- using System; using Microsoft.Singularity; @@ -12,8 +12,8 @@ namespace Microsoft.Singularity.Services { contract BenchmarkContract : ServiceContract { - public const string ModuleName = "/RBench"; - public const string ModuleNameProxy = "/RBenchProxy"; + public const string ModuleName = "/service/rbench"; + public const string ModuleNameProxy = "/service/rbenchproxy"; out message Success(); diff --git a/base/Contracts/ServiceManager.Contracts/Enums.sg b/base/Contracts/ServiceManager.Contracts/Enums.sg new file mode 100644 index 0000000..0c63d08 --- /dev/null +++ b/base/Contracts/ServiceManager.Contracts/Enums.sg @@ -0,0 +1,136 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; + +namespace Microsoft.Singularity.ServiceManager +{ + public enum ServiceError : uint + { + None = 0, + + ChannelClosed, + ContractNotSupported, + ServiceNotFound, + PermissionDenied, + InvalidArguments, + ServiceExists, + ErrorLoadingManifest, + ServiceHasInvalidManifest, + CreateProcessFailed, + FailedSetChannels, + StartProcessFailed, + ServiceIsAlreadyRunning, + ServiceIsStopping, + ServiceIsNotRunning, + ServiceIsStarting, + InternalError, + ServiceIsAdministrativelyDisabled, + ServiceIsDeleted, + ServiceIsMarkedForDeletion, + ServiceIsDefective, + ServiceIsAlreadyStarting, + CannotStopService, + + + Unknown = 0xFFFFFFFF + } + + // There are better places for this (which don't exist yet), + // and there are also much worse places for this. + public static class ServiceEnums + { + public static string! ToString(ServiceState value) + { + switch (value) { + case ServiceState.Stopped: return "Stopped"; + case ServiceState.Starting: return "Starting"; + case ServiceState.Running: return "Running"; + case ServiceState.Stopping: return "Stopping"; + default: + return ((int)value).ToString(); + } + } + + public static string! ToString(ServiceProcessState value) + { + switch (value) { + case ServiceProcessState.Starting: return "Starting"; + case ServiceProcessState.Running: return "Running"; + case ServiceProcessState.Stopping: return "Stopping"; + default: + return ((int)value).ToString(); + } + } + + public static string! ToString(ServiceActivationMode value) + { + switch (value) { + case ServiceActivationMode.AlwaysActive: return "AlwaysActive"; + case ServiceActivationMode.Manual: return "Running"; + case ServiceActivationMode.Demand: return "Demand"; + default: + return ((int)value).ToString(); + } + } + + public static string! ToString(ServiceError value) + { + switch (value) { + case ServiceError.None: return "None"; + case ServiceError.ChannelClosed: return "ChannelClosed"; + case ServiceError.ContractNotSupported: return "ContractNotSupported"; + case ServiceError.ServiceNotFound: return "ServiceNotFound"; + case ServiceError.PermissionDenied: return "PermissionDenied"; + case ServiceError.InvalidArguments: return "InvalidArguments"; + case ServiceError.ServiceExists: return "ServiceExists"; + case ServiceError.ErrorLoadingManifest: return "ErrorLoadingManifest"; + case ServiceError.ServiceHasInvalidManifest: return "ServiceHasInvalidManifest"; + case ServiceError.CreateProcessFailed: return "CreateProcessFailed"; + case ServiceError.FailedSetChannels: return "FailedSetChannels"; + case ServiceError.StartProcessFailed: return "StartProcessFailed"; + case ServiceError.ServiceIsAlreadyRunning: return "ServiceIsAlreadyRunning"; + case ServiceError.ServiceIsStopping: return "ServiceIsStopping"; + case ServiceError.InternalError: return "InternalError"; + case ServiceError.ServiceIsAdministrativelyDisabled: return "ServiceIsAdministrativelyDisabled"; + case ServiceError.ServiceIsNotRunning: return "ServiceIsNotRunning"; + case ServiceError.ServiceIsDeleted: return "ServiceIsDeleted"; + case ServiceError.ServiceIsMarkedForDeletion: return "ServiceIsMarkedForDeletion"; + case ServiceError.ServiceIsDefective: return "ServiceIsDefective"; + case ServiceError.ServiceIsAlreadyStarting: return "ServiceIsAlreadyStarting"; + case ServiceError.CannotStopService: return "CannotStopService"; + + default: + return ((int)value).ToString(); + } + } + + public static string! ToString(ServiceLoad value) + { + switch (value) { + case ServiceLoad.Unknown: return "Unknown"; + case ServiceLoad.Low: return "Low"; + case ServiceLoad.Moderate: return "Moderate"; + case ServiceLoad.High: return "High"; + case ServiceLoad.Overloaded: return "Overloaded"; + default: + return ((int)value).ToString(); + } + } + + public static string! ToString(ServiceHealth value) + { + switch (value) { + case ServiceHealth.Unknown: return "Unknown"; + case ServiceHealth.Normal: return "Normal"; + case ServiceHealth.UnrecoverableError: return "UnrecoverableError"; + case ServiceHealth.RecoverableError: return "RecoverableError"; + default: + return ((int)value).ToString(); + } + } + } +} diff --git a/base/Contracts/ServiceManager.Contracts/GameContract.sg b/base/Contracts/ServiceManager.Contracts/GameContract.sg index 5bbb03d..bac381a 100644 --- a/base/Contracts/ServiceManager.Contracts/GameContract.sg +++ b/base/Contracts/ServiceManager.Contracts/GameContract.sg @@ -16,7 +16,7 @@ namespace Microsoft.Singularity.ServiceManager { public contract GamePlayerContract : ServiceContract { - public const String ModuleName = "ReplaceGame"; + public const String ModuleName = "ReplaceGameService"; out message Success(); diff --git a/base/Contracts/ServiceManager.Contracts/ManagedServiceContract.sg b/base/Contracts/ServiceManager.Contracts/ManagedServiceContract.sg deleted file mode 100644 index 5063159..0000000 --- a/base/Contracts/ServiceManager.Contracts/ManagedServiceContract.sg +++ /dev/null @@ -1,85 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Contracts\ServiceManager.Contracts\ServiceContract.sg -// -// Note: For service providers managed by Service Manager -// -using Microsoft.SingSharp; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; - -namespace Microsoft.Singularity.ServiceManager -{ - public contract ManagedServiceContract - { - out message Success(); - out message Busy(); - - in message Knock(); - out message Alive(); - - in message StartService(); - out message AckStartService(); - out message NakStartService(); - - in message StopService(); - out message AckStopService(); - - in message RestartService(); - out message AckRestartService(); - - // Restart the service process - in message Restart(); - out message AckRestart(); - - // Stop the service process - in message Stop(); - out message AckStop(); - - state Start : one { - Success! -> Ready; - } - - state Ready : one { - Knock? -> Alive! -> Ready; - StartService? -> (AckStartService! or NakStartService!) -> Ready; - RestartService? -> (AckRestartService! or Busy!) -> Ready; - StopService? -> (AckStopService! or Busy!) -> Ready; - Restart? -> AckRestart! -> Ready; - - Stop? -> AckStop! -> Ready; - } - } - - public contract ManagedProxyContract - { - out message Success(); - - in message RequestRecovery(); - out message AckRecovery(DirectoryServiceContract.Exp:Start! directory, - ServiceProxyContract.Exp:Start! proxy); - out message NakRecovery(); - - in message RequestDSRecovery(); - out message AckDSRecovery(DirectoryServiceContract.Exp:Start! proxy); - out message NakDSRecovery(); - - // reports the end of recovery process to service manager - in message EndOfRecovery(); - out message AckEndOfRecovery(); - - state Start : one { - Success! -> Ready; - } - - state Ready : one { - RequestRecovery? -> (AckRecovery! or NakRecovery!) -> Ready; - RequestDSRecovery? -> (AckDSRecovery! or NakDSRecovery!) -> Ready; - EndOfRecovery? -> AckEndOfRecovery! -> Ready; - } - } -} diff --git a/base/Contracts/ServiceManager.Contracts/ServiceControlContract.sg b/base/Contracts/ServiceManager.Contracts/ServiceControlContract.sg index f1448cb..8f49e33 100644 --- a/base/Contracts/ServiceManager.Contracts/ServiceControlContract.sg +++ b/base/Contracts/ServiceManager.Contracts/ServiceControlContract.sg @@ -11,69 +11,3 @@ using Microsoft.Singsharp; using Microsoft.Singularity.Channels; using Microsoft.Singularity.Directory; - -namespace Microsoft.Singularity.ServiceManager -{ - public enum ServiceError : uint - { - None = 0, - - ChannelClosed, - NotFound, - PermissionDenied, - TryAgain, - - Unknown = 0xFFFFFFFF - } - - public contract ServiceControlContract - { - out message Success(); - - in message StartService(); - out message AckStartService(); - out message NakStartService(ServiceError err); - - in message StopService(); - out message AckStopService(); - out message NakStopService(ServiceError err); - - in message RestartService(); - out message AckRestartService(); - out message NakRestartService(ServiceError err); - - in message StartPoll(int interval); - out message AckStartPoll(); - out message NakStartPoll(ServiceError err); - - in message StopPoll(); - out message AckStopPoll(); - out message NakStopPoll(ServiceError err); - - state Start : one { - Success! -> Ready; - } - - state Ready : one { - StartService? -> (AckStartService! - or NakStartService! - ) -> Ready; - - StopService? -> (AckStopService! - or NakStopService! - ) -> Ready; - - RestartService? -> (AckRestartService! - or NakRestartService! - ) -> Ready; - - StartPoll? -> (AckStartPoll! - or NakStartPoll! - ) -> Ready; - - StopPoll? -> (AckStopPoll! - or NakStopPoll! - ) -> Ready; - } - } -} diff --git a/base/Contracts/ServiceManager.Contracts/ServiceManagementContract.sg b/base/Contracts/ServiceManager.Contracts/ServiceManagementContract.sg index bb578f2..b55cbed 100644 --- a/base/Contracts/ServiceManager.Contracts/ServiceManagementContract.sg +++ b/base/Contracts/ServiceManager.Contracts/ServiceManagementContract.sg @@ -8,119 +8,416 @@ // // Note: SMS-clients contract // +using System; using Microsoft.SingSharp; using Microsoft.Singularity.Channels; using Microsoft.Singularity.Directory; namespace Microsoft.Singularity.ServiceManager { + + public rep struct ExSchedulerTime + { + public long Ticks; + } + + /// + /// This enumeration defines the activation mode for services, which controls when + /// the Service Manager starts services. + /// + public enum ServiceActivationMode + { + /// + /// In this mode, the Service Manager will start the service, if necessary, when clients + /// attempt to connect to the service. When the service is created, the Service Manager + /// will not attempt to start the service. + /// + Demand = 1, + + /// + /// In this mode, the Service Manager will always attempt to keep the service running. + /// The StartService and StopService control messages cannot be used to control these + /// services. The only way to stop a service that uses this activation mode is to + /// disable the service (set IsAdministrativelyDisabled to true), or to delete the service. + /// If a service process terminates unexpectedly, the Service Manager may attempt to restart + /// the service. + /// + AlwaysActive = 2, + + /// + /// In this mode, management clients control the state of the service. When the service + /// is created, the Service Manager leaves the service in the Stopped state. + /// + Manual = 3, + } + + /// + /// This structure describes all of the configuration parameters of a service. + /// + public rep struct ServiceConfig : ITracked + { + /// + /// Specifies the name of the service. This field is arbitrary; the Service Manager does not + /// impose any interpretation on the service name. It only serves to distinguish services. + /// The field is case-sensitive. + /// + public char[]! in ExHeap ServiceName; + + /// + /// Specifies the name to display to the user for this service. + /// + public char[]! in ExHeap DisplayName; + + /// + /// Specifies the name of the executable (a manifest name). + /// + public char[]! in ExHeap ExecutableName; + + + /// + /// Controls when the Service Manager starts the service. + /// + public ServiceActivationMode ActivationMode; + + /// + /// If this field is set to true, then this service has been administratively disabled. + /// This allows administrators to disable a service, without affecting any other aspect + /// of its configuration. + /// + public bool IsAdministrativelyDisabled; + + /// + /// Specifies the minimum number of processes that the Service Manager should create for this + /// service. If this field is 0, then the Service Manager will not create any processes for + /// the service, until the Service Manager receives a Connect request. Later, when demand + /// for the service decreases, the Service Manager may stop the process (or processes) that + /// it created. + /// + public int MinProcesses; + + /// + /// Specifies the maximum number of processes that the Service Manager should create for + /// this service. If this field is 1, then the service is a singleton. If this field is + /// UnlimitedProcesses (-1), then the Service Manager will not limit the number of processes. + /// + public int MaxProcesses; + + /// + /// Specifies the maximum number of clients that each service process should handle. If this + /// field is 1, then there is a 1:1 relationship between service processes and clients. + /// If this field is UnlimitedClientsPerProcess, then the Service Manager will not create more + /// than one service process (per service). + /// + public int MaxClientsPerProcess; + + /// + /// The maximum age, in seconds, of each service process. The Service Manager records the + /// time when it created each service process. When a process reaches the age specified by + /// this field, the Service Manager will no longer route requests to that process. Instead, + /// it will route requests to an existing process (whose age limit has not yet been reached), + /// or will create a new process, if needed. + /// + /// If this field is set to UnlimitedProcessAge, then the Service Manager will not perform + /// service process aging. + /// + public int MaxProcessAgeInSeconds; + + public const int UnlimitedProcesses = -1; + public const int UnlimitedClientsPerProcess = -1; + public const int UnlimitedProcessAge = -1; + } + + + public enum ServiceProcessState + { + /// + /// In this state, the Service Manager has created a new service process, + /// and is waiting for the process to indicate that it has finished starting. + /// The process must either indicate that it has finished starting by sending + /// a StartSucceeded message, indicate that it failed to start by sending a + /// StartFailed message. If the service process does not send either of these + /// messages within a reasonable period of time, then the Service Manager will + /// mark the process as Defective. + /// + Starting = 1, + + /// + /// In this state, the service process is ready for use; the Service Manager + /// will route client connection requests to the process. + /// + Running = 2, + + /// + /// In this state, the Service Manager is stopping the service process. + /// There may be important substates to consider, but this is the overall + /// state of the service. + /// + Stopping = 3, + + /// + /// In this state, the Service Manager considers this process to be defective. + /// The Service Manager has (or will) close all channels to this process, and + /// will not route any new client connections to the service. + /// + Defective = 4, + } + + + public enum ServiceState + { + Stopped = 1, + Starting, + Running, + Stopping, + } + + /// + /// This structure describes the operational status of a service. + /// + public rep struct ServiceStatus + { + public ServiceState State; + + /// + /// The total number of clients that are connect to the service. + /// + public int TotalActiveClients; + + /// + /// The total number of processes that the Service Manager has created for this service. + /// + public int TotalActiveProcesses; + + public int ConnectQueueLength; + + public long ProcessId; + + public bool LastStartFailed; + public ServiceError LastStartError; + } + + public rep struct ServiceProcessStatus + { + public long ProcessId; + + public ServiceProcessState State; + + /// + /// The total number of client connections that have ever been routed to this + /// service process. + /// + public int TotalConnects; + + /// + /// The system time (actually, scheduler time) when the process was created. + /// + public ExSchedulerTime TimeCreated; + + /// + /// The number of clients that the Service Manager believes are being + /// serviced by this process. + /// + public int ActiveClientCount; + + public ServiceHealth Health; + + public ServiceLoad Load; + } + public rep struct ServiceInfo : ITracked { - private char[]! in ExHeap name; - private char[]! in ExHeap binary; - public int Id; - public ServiceType Type; - - public ServiceInfo(int id, string! name, string! binary, - ServiceType type) - { - Id = id; - this.name = Bitter.FromString2(name); - this.binary = Bitter.FromString2(binary); - this.Type = type; - } - - public string! Name - { - get - { - expose (this) { - return Bitter.ToString2(name); - } - } - private set {} - } - - public string! Binary - { - get - { - expose (this) { - return Bitter.ToString2(binary); - } - } - private set {} - } + public ServiceConfig Config; + public ServiceStatus Status; } - public enum ServiceType : uint + [Flags] + public enum ServiceManagerEventMask { - Unknown, - Default, - Resilient, + AnyServiceConfig = 1, + AnyServiceStatus = 2, } - public contract ServiceManagementContract : ServiceContract + // +// + //This contract is implemented (exported) by the Service Manager, and is published at + //the well-known path "/service/services". This contract allows management clients (such as + //svconf) to connect to the Service Manager, and: +// + // * query the status of the Service Manager, + // * enumerate services, + // * query the status of services, + // * subscribe to the status of a service or services, + // * start and stop services, + // * create, modify, and delete services, +// + // + public contract ServiceManagerContract : ServiceContract { - public const string ModuleName = "/services"; + public const string ModuleName = "/service/services"; out message Success(); - - out message NotFound(ServiceControlContract.Exp:Start! ep); - out message PermissionDenied(ServiceControlContract.Exp:Start! ep); - out message TryAgain(ServiceControlContract.Exp:Start! ep); - - in message Bind(ServiceInfo*! in ExHeap info, ServiceControlContract.Exp:Start! ep); - out message AckBind(); - - in message GetControl(int id, ServiceControlContract.Exp:Start! ep); - out message AckGetControl(); - - in message Unbind(int id); - out message AckUnbind(ServiceControlContract.Exp:Start! ep); - out message ControlNotFound(); - out message ServiceNotFound(); - out message ControlPermissionDenied(); - - in message BeginEnumeration(); - in message MoveNext(); - in message EndEnumeration(); - out message EnumerationTerminated(); - out message Current(ServiceInfo*! in ExHeap info); - override state Start : one { Success! -> Ready; } - state Enumerate : one { + // + // Enumeration of services + // + in message EndEnumeration(); + out message EnumerationTerminated(ServiceInfo[]! in ExHeap infos, int count); + out message NextServiceInfo(ServiceInfo[]! in ExHeap infos, int count); + + state EnumeratingServices : one { + NextServiceInfo! -> EnumerateAck; EnumerationTerminated! -> Ready; - Current! -> EnumerateAck; } state EnumerateAck : one { - MoveNext? -> Enumerate; + EnumerateServices? -> EnumeratingServices; EndEnumeration? -> Ready; } + // + // Main request state. + // + state Ready : one { - Bind? -> (AckBind! - or NotFound! - or PermissionDenied! - ) -> Ready; - - GetControl? -> (AckGetControl! - or NotFound! - or PermissionDenied! - or TryAgain! - ) -> Ready; - - Unbind? -> (AckUnbind! - or ControlNotFound! - or ServiceNotFound! - or ControlPermissionDenied! - ) -> Ready; - - BeginEnumeration? -> Enumerate; + EnumerateServices? -> EnumeratingServices; + SelectService? -> SelectingService; + CreateService? -> (Ok! or RequestFailed!); + WatchServiceManager? -> RequestingWatchServiceManager; } + + in message WatchServiceManager(ServiceManagerEventMask mask); + in message EnumerateServices(ServiceInfo[]! in ExHeap infos); + in message CreateService(ServiceConfig config); + in message SelectService(char[]! in ExHeap serviceName); + + state SelectingService : one { + Ok! -> ServiceSelected; + RequestFailed! -> Ready; + } + + state RequestingWatchServiceManager : one { + Ok! -> ReadyWatchServiceManager; + RequestFailed! -> Ready; + } + + // + // ReadyWatchServiceManager - In this state, a client is watching the overall status + // of the Service Manager, and uses WaitNextChange() to be notified of the next change + // in the configuration or in status of any service. The client and SM then + // oscillate between ReadyWatchAll <--> WaitingNext + // + + state ReadyWatchServiceManager : one { + WaitNextServiceManagerChange? -> WaitingServiceManagerChange; + StopWatchingServiceManager? -> Ready; + } + + state WaitingServiceManagerChange : one { + ServiceManagerChanged! -> ReadyWatchServiceManager; + } + + in message StopWatchingServiceManager(); + in message WaitNextServiceManagerChange(); + out message ServiceManagerChanged(ServiceManagerEventMask mask); + + // + // In this state, the client has chosen a specific service to act on, using the + // SelectService request. All of the requests in this state act on that server, + // except for UnselectService, which returns the client to the Ready state. + // + + state ServiceSelected : one { + WatchServiceStatus? -> RequestingWatchServiceStatus; + StartServiceWait? -> StartingServiceWait; + StartServiceNoWait? -> StartingServiceNoWait; + StopServiceWait? -> StoppingServiceWait; + StopServiceNoWait? -> StoppingServiceNoWait; + QueryServiceStatus? -> (CurrentServiceStatus! or RequestFailed!) -> ServiceSelected; + QueryServiceConfig? -> (CurrentServiceConfig! or RequestFailed!) -> ServiceSelected; + DeleteService? -> (Ok! or RequestFailed!); + UnselectService? -> Ready; + EnableService? -> (Ok! or RequestFailed!); + TerminateServiceAllProcesses? -> (Ok! or RequestFailed!); + TerminateServiceProcess? -> (Ok! or RequestFailed!); + } + + in message StartServiceNoWait(); + in message StartServiceWait(); + in message StopServiceWait(); + in message StopServiceNoWait(); + in message DeleteService(); + in message UnselectService(); + in message QueryServiceConfig(); + in message QueryServiceStatus(); + in message WatchServiceStatus(); + in message EnableService(bool enable); + in message TerminateServiceAllProcesses(); + in message TerminateServiceProcess(int processId); + + state RequestingWatchServiceStatus : one { + Ok! -> ReadyWatchService; + RequestFailed! -> Ready; + } + + state StartingServiceWait : one { + ServiceStarted! -> ServiceSelected; // service has successfully started + RequestFailed! -> ServiceSelected; + } + + state StartingServiceNoWait : one { + ServiceStarting! -> ServiceSelected; // service is starting, but may not be finished. + RequestFailed! -> ServiceSelected; + } + + state StoppingServiceWait : one { + ServiceStopped! -> ServiceSelected; // service has stopped. + RequestFailed! -> ServiceSelected; + } + + state StoppingServiceNoWait : one { + ServiceStopping! -> ServiceSelected; // service is stopping, but may not be finished. + RequestFailed! -> ServiceSelected; + } + + out message CurrentServiceConfig(ServiceConfig config); + out message CurrentServiceStatus(ServiceStatus status); + + out message ServiceStarting(); + out message ServiceStarted(); + out message ServiceStopping(); + out message ServiceStopped(); + + + // + // ReadyWatchService - In this state, the client has subscribed to the status of a + // service (the Service Manager has already approved the subscription), and the client + // is the next message sender. The client can now wait for the next notification, + // or can end the subscription. + // + + state ReadyWatchService : one { + WaitServiceChange? -> WaitingServiceChange; + StopWatchingService? -> Ready; + } + + state WaitingServiceChange : one { + ServiceStatusChanged! -> ReadyWatchService; + RequestFailed! -> Ready; + } + + in message WaitServiceChange(); + in message StopWatchingService(); + out message ServiceStatusChanged(ServiceStatus status, bool missedChanges); + + + // + // Generic response codes. + // + + out message Ok(); + out message RequestFailed(ServiceError err); } } diff --git a/base/Contracts/ServiceManager.Contracts/ServiceManager.Contracts.csproj b/base/Contracts/ServiceManager.Contracts/ServiceManager.Contracts.csproj index cc3820f..bb2f2b9 100644 --- a/base/Contracts/ServiceManager.Contracts/ServiceManager.Contracts.csproj +++ b/base/Contracts/ServiceManager.Contracts/ServiceManager.Contracts.csproj @@ -11,30 +11,26 @@ # ############################################################################## --> - - - Library ServiceManager.Contracts true + {3651F014-C6EA-42D7-BDCD-E2C6C2A31724} - - - - - - - - - + + {19BB002E-2B79-40ED-81A8-D2070C7A689A} + Directory.Contracts + + + + + - - + \ No newline at end of file diff --git a/base/Contracts/ServiceManager.Contracts/ServiceProcessContract.sg b/base/Contracts/ServiceManager.Contracts/ServiceProcessContract.sg new file mode 100644 index 0000000..66a330c --- /dev/null +++ b/base/Contracts/ServiceManager.Contracts/ServiceProcessContract.sg @@ -0,0 +1,134 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Contracts\ServiceManager.Contracts\ServiceContract.sg +// +// Note: For service providers managed by Service Manager +// +using Microsoft.SingSharp; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Directory; + +namespace Microsoft.Singularity.ServiceManager +{ + /// + // + //This contract describes communication between the Service Manager and the processes that + //it creates. The Service Manager holds the import endpoint, the service holds the export. + // + // + // + // + //When the Service Manager creates a process, it searches the manifest of the executable + //for an endpoint declaration matchign ServiceProcessContract. All service executables + //must declare such an endpoint; if they do not, then the Service Manager will not start + //the process. + // + // + // + //When the Service Manager creates a channel using this contract, the initial state is + //"Start", and in this state, the service is the next sender. The first message is + //either ServiceStarted, which indicates that the service has successfully initialized + //and is ready for clients, or ServiceFailedStart, indicating that the service failed + //to initialize. + // + // + // + //Once the service is running, the Service Manager will route client connect requests + //to the service, using the Connect message. + // +// + // + // + + public contract ServiceProcessContract + { + out message StartSucceeded(); + out message StartFailed(ServiceError error); + out message Busy(); + + in message Knock(); + out message Alive(); + + in message Stop(); + out message AckStop(); + + in message Connect(char[] in ExHeap path, ServiceContract.Exp:Start! exp); + out message AckConnect(); + out message NakConnect(ErrorCode error, ServiceContract.Exp:Start exp); + + state Starting : one { + StartSucceeded! -> Running; + StartFailed! -> Stopped; + } + + state Running : one { + Knock? -> Knocking; + Stop? -> Stopping; + Connect? -> Connecting; + } + + state Knocking : one { + Alive! -> Running; + } + + state Stopping : one { + AckStop! -> Stopped; + Busy! -> Running; + } + + state Stopped : one { + } + + state Connecting : one { + NakConnect! -> Running; + AckConnect! -> Running; + } + } + + public enum ServiceHealth + { + Unknown = 1, + Normal, + UnrecoverableError, + RecoverableError, + } + + public enum ServiceLoad + { + Unknown = 1, + Low, + Moderate, + High, + Overloaded, + } + + /// + /// The Service Manager and service processes communicate using this contract. + /// Service Manager holds the import side, the service the export side. + /// The service sends notification events to the SM. + /// + public contract ServiceEventContract + { + in message HealthChanged(ServiceHealth health); + in message LoadChanged(ServiceLoad load); + + out message Ack(); + + state Ready : one { + HealthChanged? -> HealthChanging; + LoadChanged? -> LoadChanging; + } + + state HealthChanging : one { + Ack! -> Ready; + } + + state LoadChanging : one { + Ack! -> Ready; + } + } +} diff --git a/base/Contracts/ServiceManager.Contracts/ServiceProxyContract.sg b/base/Contracts/ServiceManager.Contracts/ServiceProxyContract.sg deleted file mode 100644 index 9985972..0000000 --- a/base/Contracts/ServiceManager.Contracts/ServiceProxyContract.sg +++ /dev/null @@ -1,203 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Contracts\ServiceManager.Contracts\ProxyContract.sg -// -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; - -namespace Microsoft.Singularity.ServiceManager -{ - public rep struct Persistent : ITracked { - } - - public contract ServiceProxyContract - { - out message Success(); - - //--------------------------------------------------------------------- - // Directory Service Imitation - //--------------------------------------------------------------------- - - /// - /// Registers the path to the root directory service. - /// - in message Register(char[]! in ExHeap path, - ServiceProviderContract.Imp:Start! imp); - out message AckRegister(); - out message NakRegister(ServiceProviderContract.Imp:Start imp, - ErrorCode error); - - /// - /// De-registers the path from the root directory service. - /// - in message Deregister(char[]! in ExHeap path); - out message AckDeregister(ServiceProviderContract.Imp:Start! imp); - out message NakDeregister(ErrorCode error); - - //--------------------------------------------------------------------- - // Persistent Memory - //--------------------------------------------------------------------- - - in message Allocate(Persistent in ExHeap obj); - out message AckAllocate(ulong objectId); - out message NakAllocate(Persistent in ExHeap obj); - in message Deallocate(ulong objectId); - out message AckDeallocate(); - out message NakDeallocate(); - - in message AcquireObject(ulong objectId); - out message AckAcquireObject(Persistent in ExHeap obj); - out message NakAcquireObject(); - in message ReleaseObject(Persistent in ExHeap obj); - out message AckReleaseObject(); - out message NakReleaseObject(); - - in message Upload(byte[]! in ExHeap data); - out message AckUpload(); - in message Download(); - out message AckDownload(byte[]! in ExHeap data); - out message NakDownload(); - - //--------------------------------------------------------------------- - // Snapshot facility - //--------------------------------------------------------------------- - - /// - /// Synchronizes journalets to the nearest checkpoints. - /// - in message Suspend(); - //in message Suspend(char[]! in ExHeap path); - out message AckSuspend(int generation); - - /// - /// Resumes journalets - /// - in message Resume(int generation); - //in message Resume(char[]! in ExHeap path); - out message AckResume(); - - /// - /// Updates the master copy - /// - in message Update(int generation); - out message AckUpdate(); - - state Start : one { - Success! -> Ready; - } - - state Ready : one { - Register? -> (AckRegister! or NakRegister!) -> Ready; - Deregister? -> (AckDeregister! or NakDeregister!) -> Ready; - - Allocate? -> (AckAllocate! or NakAllocate!) -> Ready; - Deallocate? -> (AckDeallocate! or NakDeallocate!) -> Ready; - AcquireObject? -> (AckAcquireObject! or NakAcquireObject!) -> Ready; - ReleaseObject? -> (AckReleaseObject! or NakReleaseObject!) -> Ready; - - Upload? -> AckUpload! -> Ready; - Download? -> (AckDownload! or NakDownload!) -> Ready; - - Suspend? -> AckSuspend! -> Ready; - Resume? -> AckResume! -> Ready; - Update? -> AckUpdate! -> Ready; - } - } - - public contract CPTestContract - { - out message Success(); - - in message Read1(); - in message Read2(); - in message Write1(char[] in ExHeap message); - in message Write2(char[] in ExHeap message); - in message Write3(char[] in ExHeap message); - in message Write4(char[] in ExHeap message); - out message AckRead1(char[] in ExHeap message); - out message AckRead2(char[] in ExHeap message); - out message AckWrite1(); - out message AckWrite2(); - out message AckWrite3(); - - state Start : one { - Success! -> CP1; - } - - state CP1 : one { - Read1? -> AckRead1! -> Write1? -> AckWrite1! -> CP2; - } - - state CP2 : one { - Write2? -> Write3? -> AckWrite2! -> CP3; - } - - state CP3 : one { - Read2? -> AckRead2! -> Write4? -> AckWrite3! -> CP1; - } - } - - /* - public sealed class PRef where T : unmanaged struct, ITracked - { - unsafe T* ExHeap obj; - Mutex mutex; - - public PRef([Claims] T* ExHeap! i_obj) - { - if (i_obj == null) { - throw new ArgumentNullException("PRef must be initialized" + - " with a non-null value!"); - } - ep = master.Acquire(); - ep.SendAllocate(i_obj); - ep.RecvAckAllocate(p_obj); - i_obj->Release(); - obj = i_obj; - this.mutex = new Mutex(); - } - - public T* ExHeap! Acquire() - { - this.mutex.WaitOne(); - T* ExHeap elem = this.obj; - if (elem == null) { - throw new ApplicaionException("thread already holds TRef!"); - } - ep.SendAcquire(this); - ep.RecvAckAcquire(elem); - this.obj = null; - return elem; - } - - public void Release([Claims] T* ExHeap! newObj) - { - if (newObj == null) { - throw new ArgumentNullException("TRef must be released" + - " with a non-null value!"); - } - newObj->Release(); - obj = newObj; - this.mutex.ReleaseMutex(); - } - - ~PRef() - { - T* ExHeap toDelete = this.obj; - if (toDelete != null) { - this.obj = null; - unsafe { - toDelete->Dispose(); - } - delete toDelete; - } - } - } - */ -} diff --git a/base/Contracts/ServiceManager.Contracts/ThreadTerminationContract.sg b/base/Contracts/ServiceManager.Contracts/ThreadTerminationContract.sg index 30011cf..a098000 100644 --- a/base/Contracts/ServiceManager.Contracts/ThreadTerminationContract.sg +++ b/base/Contracts/ServiceManager.Contracts/ThreadTerminationContract.sg @@ -1,7 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // +// ---------------------------------------------------------------------------- namespace Microsoft.Singularity.Services { diff --git a/base/Contracts/Stress.Contracts/StressContract.sg b/base/Contracts/Stress.Contracts/StressContract.sg index fce941e..e378f26 100644 --- a/base/Contracts/Stress.Contracts/StressContract.sg +++ b/base/Contracts/Stress.Contracts/StressContract.sg @@ -29,7 +29,7 @@ namespace Microsoft.Singularity.Stress.Contracts public contract StressContract : ServiceContract { // The name you should use to look up this module using the NameServer. - public const string ModuleName = "/stress"; + public const string ModuleName = "/service/stress"; // Signal our identity out message Ready(); diff --git a/base/Contracts/Test.Contracts/ModuleTesterContract.sg b/base/Contracts/Test.Contracts/ModuleTesterContract.sg new file mode 100644 index 0000000..f347c72 --- /dev/null +++ b/base/Contracts/Test.Contracts/ModuleTesterContract.sg @@ -0,0 +1,121 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +using System.Threading; +using Microsoft.SingSharp; +using Microsoft.Singularity; +using Microsoft.Singularity.Channels; + +namespace Microsoft.Singularity.Test.Contracts +{ + public contract LogContract + { + out message Log(char[]! in ExHeap msg, long atCycle, long atTime); + in message OK(); + + state START : one { + Log! -> OK? -> START; + } + } + + public contract ModuleTesterContract + { + in message Logger(LogContract.Exp:START! log, bool logSuccess); + out message GetLogger(); + + // manage the applications tester + in message CleanupModule(); + + // manage a specific suite (test class) in the tester + in message InitSuite(char[]! in ExHeap suite); + in message CleanupSuite(); + + // Run a specific test (which will pass or fail + in message RunTest(char[]! in ExHeap test); + + out message Passed(long cycles, long duration); + out message Failed(char[]! in ExHeap error, long cycles, long duration); + out message Skipped(char[]! in ExHeap why); + + state START : one { + GetLogger! -> Logger? -> GO; + } + state GO : one { + Passed! -> DO_MODULE; + Failed! -> END; + Skipped! -> END; + } + state DO_MODULE : one { + InitSuite? -> TRY_SUITE; + CleanupModule? -> RUNNING -> END; + } + state TRY_SUITE : one { + Passed! -> DO_SUITE; + Failed! -> DO_MODULE; + Skipped! -> DO_MODULE; + } + state DO_SUITE : one { + RunTest? -> RUNNING -> DO_SUITE; + CleanupSuite? -> RUNNING -> DO_MODULE; + } + state RUNNING : one { + Passed! ; + Failed! ; + Skipped! ; + } + state END: one {} + } +} + +#if false + public contract ModuleTesterContract + { + // manage the applications tester + in message CleanupModule(); + + // manage a specific suite (test class) in the tester + in message InitSuite(char[]! in ExHeap suite); + in message CleanupSuite(); + + // Run a specific test (which will pass or fail + in message RunTest(char[]! in ExHeap test, bool reportAssertions); + + out message Passed(long cycles, long duration); + out message Failed(char[]! in ExHeap error, long cycles, long duration); + out message Skipped(char[]! in ExHeap why); + + state START : one { + Passed! -> DO_MODULE; + Failed! -> END; + Skipped! -> END; + } + state DO_MODULE : one { + InitSuite? -> TRY_SUITE; + CleanupModule? -> ENDING -> END; + } + state TRY_SUITE : one { + Passed! -> DO_SUITE; + Failed! -> DO_MODULE; + Skipped! -> DO_MODULE; + } + state DO_SUITE : one { + RunTest? -> RUNNING -> DO_SUITE; + CleanupSuite? -> ENDING -> DO_MODULE; + } + state RUNNING : one { + Passed! ; + Failed! ; + Skipped! ; + } + state ENDING : one { + Passed! ; + Failed! ; + } + state END: one {} + } +#endif + diff --git a/base/Contracts/Test.Contracts/app.csproj b/base/Contracts/Test.Contracts/app.csproj new file mode 100644 index 0000000..09f8dd5 --- /dev/null +++ b/base/Contracts/Test.Contracts/app.csproj @@ -0,0 +1,28 @@ + + + + + + + + Library + Test.Contracts + true + + + + + + + + diff --git a/base/Contracts/WebApps.Contracts/AssemblyInfo.sg b/base/Contracts/WebApps.Contracts/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Contracts/WebApps.Contracts/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Contracts/WebApps.Contracts/HttpRequestContract.sg b/base/Contracts/WebApps.Contracts/HttpRequestContract.sg index 17dd60e..58ba5fe 100644 --- a/base/Contracts/WebApps.Contracts/HttpRequestContract.sg +++ b/base/Contracts/WebApps.Contracts/HttpRequestContract.sg @@ -4,7 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: HttpRequestContract.sg // Note: Contract definition for HTTP requests processed by web apps // diff --git a/base/Contracts/WebApps.Contracts/WebAppContract.sg b/base/Contracts/WebApps.Contracts/WebAppContract.sg index e4a745c..3186d82 100644 --- a/base/Contracts/WebApps.Contracts/WebAppContract.sg +++ b/base/Contracts/WebApps.Contracts/WebAppContract.sg @@ -4,7 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: WebAppContract.sg // Note: Contract definition for web applications // diff --git a/base/Contracts/WebApps.Contracts/WebApps.Contracts.csproj b/base/Contracts/WebApps.Contracts/WebApps.Contracts.csproj index 9363247..fac514d 100644 --- a/base/Contracts/WebApps.Contracts/WebApps.Contracts.csproj +++ b/base/Contracts/WebApps.Contracts/WebApps.Contracts.csproj @@ -23,6 +23,7 @@ + diff --git a/base/Distro/Async.proj b/base/Distro/Async.proj new file mode 100644 index 0000000..39d9722 --- /dev/null +++ b/base/Distro/Async.proj @@ -0,0 +1,65 @@ + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Distro/BVT.proj b/base/Distro/BVT.proj index 8c59274..3283937 100644 --- a/base/Distro/BVT.proj +++ b/base/Distro/BVT.proj @@ -1,44 +1,93 @@ + - - - BVT - + + - + + + + + + + + + + + + + + + + + + + + + + - + + - - + - + + + + + + + + + + + + + + + + + diff --git a/base/Distro/BVT_Drivers.proj b/base/Distro/BVT_Drivers.proj new file mode 100644 index 0000000..394104a --- /dev/null +++ b/base/Distro/BVT_Drivers.proj @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Distro/ExcludeTests.targets b/base/Distro/ExcludeTests.targets new file mode 100644 index 0000000..322ef06 --- /dev/null +++ b/base/Distro/ExcludeTests.targets @@ -0,0 +1,8 @@ + + + + $(SINGULARITY_ROOT)\Applications\tests\bartok\tree\tree.csproj + $(SINGULARITY_ROOT)\Applications\tests\bartok\tree\tree.csproj + + + diff --git a/base/Distro/Files/BVT.tst b/base/Distro/Files/BVT.tst new file mode 100644 index 0000000..9ddadd4 --- /dev/null +++ b/base/Distro/Files/BVT.tst @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/base/Distro/Files/Threading.tst b/base/Distro/Files/Threading.tst new file mode 100644 index 0000000..4cd7dc6 --- /dev/null +++ b/base/Distro/Files/Threading.tst @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/base/Distro/Files/accounts b/base/Distro/Files/accounts new file mode 100644 index 0000000..c070f27 --- /dev/null +++ b/base/Distro/Files/accounts @@ -0,0 +1,136 @@ +alewis:andrew.lewis@enron.com +allen:k..allen@enron.com +arnold:john.arnold@enron.com +arora:harry.arora@enron.com +badeer:robert.badeer@enron.com +bagwell:jennifer.bagwell@enron.com +bailey:susan.bailey@enron.com +bass:eric.bass@enron.com +baughman:don.baughman@enron.com +beck:sally.beck@enron.com +benson:robert.benson@enron.com +blair:lynn.blair@enron.com +brawner:f..brawner@enron.com +brawners:sandra.brawner@enron.com +buy:rick.buy@enron.com +campbell:f..campbell@enron.com +carson:mike.carson@enron.com +cash:michelle.cash@enron.com +causholli:monika.causholli@enron.com +corman:shelley.corman@enron.com +crandall:sean.crandall@enron.com +cuilla:martin.cuilla@enron.com +dasovich:jeff.dasovich@enron.com +davis:dana.davis@enron.com +dean:craig.dean@enron.com +delainey:w..delainey@enron.com +derrick:james.derrick@enron.com +donoho:lindy.donoho@enron.com +donohoe:tom.donohoe@enron.com +dorland:chris.dorland@enron.com +ermis:frank.ermis@enron.com +evans:casey.evans@enron.com +farmer:j..farmer@enron.com +fischer:mary.fischer@enron.com +forney:m..forney@enron.com +gang:lisa.gang@enron.com +gay:l..gay@enron.com +geaccone:tracy.geaccone@enron.com +germany:chris.germany@enron.com +gilbertsmith:doug.gilbert-smith@enron.com +giron:c..giron@enron.com +griffith:john.griffith@enron.com +grigsby:mike.grigsby@enron.com +haedicke:e..haedicke@enron.com +hayslett:rod.hayslett@enron.com +heard:marie.heard@enron.com +hendrickson:scott.hendrickson@enron.com +hernandez:juan.hernandez@enron.com +hlewis:h..lewis@enron.com +hodge:john.hodge@enron.com +holst:keith.holst@enron.com +horton:stanley.horton@enron.com +hyatt:kevin.hyatt@enron.com +jones:tana.jones@enron.com +kaminski:j.kaminski@enron.com +kean:j..kean@enron.com +keavey:peter.keavey@enron.com +keaveyf:f..keavey@enron.com +keiser:kam.keiser@enron.com +king:jeff.king@enron.com +kitchen:louise.kitchen@enron.com +kuykendall:tori.kuykendall@enron.com +lavorato:lavorato@enron.com +lay:kenneth.lay@enron.com +lenhart:matthew.lenhart@enron.com +lokay:michelle.lokay@enron.com +lokey:teb.lokey@enron.com +love:m..love@enron.com +lucci:t..lucci@enron.com +maggi:mike.maggi@enron.com +mann:kay.mann@enron.com +martin:a..martin@enron.com +may:larry.may@enron.com +mccarty:danny.mccarty@enron.com +mcconnell:mark.mcconnell@enron.com +mckayb:brad.mckay@enron.com +mckayj:jonathan.mckay@enron.com +mclaughlin:errol.mclaughlin@enron.com +meyers:albert.meyers@enron.com +mims:l..mims@enron.com +motley:matt.motley@enron.com +neal:scott.neal@enron.com +nemec:gerald.nemec@enron.com +panus:stephanie.panus@enron.com +parks:joe.parks@enron.com +pereira:w..pereira@enron.com +perlingiere:debra.perlingiere@enron.com +pimenov:vladi.pimenov@enron.com +platter:phillip.platter@enron.com +presto:m..presto@enron.com +quenet:joe.quenet@enron.com +quigley:dutch.quigley@enron.com +rapp:bill.rapp@enron.com +reitmeyer:jay.reitmeyer@enron.com +richey:cooper.richey@enron.com +ringa:andrea.ring@enron.com +ringr:richard.ring@enron.com +rogers:benjamin.rogers@enron.com +ruscitti:kevin.ruscitti@enron.com +sager:elizabeth.sager@enron.com +saibi:eric.saibi@enron.com +salisbury:holden.salisbury@enron.com +sanchez:monique.sanchez@enron.com +sanders:b..sanders@enron.com +scholtes:diana.scholtes@enron.com +schoolcraft:darrell.schoolcraft@enron.com +schwieger:jim.schwieger@enron.com +scott:susan.scott@enron.com +semperger:cara.semperger@enron.com +shackleton:sara.shackleton@enron.com +shankman:a..shankman@enron.com +shankmanj:jeffrey.a.shankman@enron.com +shapiro:richard.shapiro@enron.com +shively:s..shively@enron.com +skilling:jeff.skilling@enron.com +slinger:ryan.slinger@enron.com +smith:matt.smith@enron.com +solberg:geir.solberg@enron.com +staab:theresa.staab@enron.com +steffes:d..steffes@enron.com +stepenovitch:joe.stepenovitch@enron.com +storey:geoff.storey@enron.com +sturm:j..sturm@enron.com +swerzbin:mike.swerzbin@enron.com +tholt:jane.tholt@enron.com +thomas:d..thomas@enron.com +townsend:judy.townsend@enron.com +tycholiz:barry.tycholiz@enron.com +ward:kim.ward@enron.com +watson:kimberly.watson@enron.com +weldon:charles.weldon@enron.com +whalley:greg.whalley@enron.com +white:w..white@enron.com +whitt:mark.whitt@enron.com +williams:jason.williams@enron.com +forwarding:mail_forward@enron.com diff --git a/base/Distro/Files/babysitter.tst b/base/Distro/Files/babysitter.tst new file mode 100644 index 0000000..0632edc --- /dev/null +++ b/base/Distro/Files/babysitter.tst @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/base/Distro/Files/daily.tst b/base/Distro/Files/daily.tst new file mode 100644 index 0000000..ead05d5 --- /dev/null +++ b/base/Distro/Files/daily.tst @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/base/Distro/Files/installer.options b/base/Distro/Files/installer.options new file mode 100644 index 0000000..74fef71 --- /dev/null +++ b/base/Distro/Files/installer.options @@ -0,0 +1,40 @@ + + + "x86" + "/fs" + "installer" + "/init/nib.options" + "/init/native/lib/" + "configuration/installedapps" + "assemblies" + "manifest" + "excutable" + "temp" + + + "/init/assemblies/Corlib.dll" + "/init/assemblies/Corlibsg.dll" + "/init/assemblies/Corlibsg.Vxx.dll" + "/init/assemblies/Directory.Contracts.dll" + "/init/assemblies/Io.Contracts.dll" + "/init/assemblies/Microsoft.SingSharp.Runtime.dll" + "/init/assemblies/Singularity.V1.dll" + "/init/assemblies/System.Compiler.Runtime.dll" + "/init/assemblies/NetStack.Channels.Public.dll" + "/init/assemblies/Diagnostics.Contracts.dll" + "/init/assemblies/MapPointProxy.Contracts.dll" + "/init/assemblies/SeattleTrafficProxy.Contracts.dll" + "/init/assemblies/PingPong.Contracts.dll" + "/init/assemblies/WebApps.Contracts.dll" + "/init/assemblies/NetStack.Contracts.dll" + "/init/assemblies/DirectoryService.Utils.dll" + "/init/assemblies/FileSystem.Utils.dll" + "/init/assemblies/System.Console.dll" + "/init/assemblies/System.IO.dll" + "/init/assemblies/System.Net.dll" + "/init/assemblies/System.Net.IP.dll" + "/init/assemblies/System.Web.dll" + "/init/assemblies/WebApps.dll" + "/init/assemblies/WebAppDriver.dll" + + diff --git a/base/Distro/Files/nib.options b/base/Distro/Files/nib.options new file mode 100644 index 0000000..5a37d82 --- /dev/null +++ b/base/Distro/Files/nib.options @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Distro/Files/stress.tst b/base/Distro/Files/stress.tst new file mode 100644 index 0000000..14de860 --- /dev/null +++ b/base/Distro/Files/stress.tst @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/base/Distro/Files/unit.tst b/base/Distro/Files/unit.tst new file mode 100644 index 0000000..fc51953 --- /dev/null +++ b/base/Distro/Files/unit.tst @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/base/Distro/LegacyPCDistro.xml b/base/Distro/LegacyPCDistro.xml index 21f4a7e..01d6766 100644 --- a/base/Distro/LegacyPCDistro.xml +++ b/base/Distro/LegacyPCDistro.xml @@ -12,24 +12,32 @@ + + - + + + + - - - - - - - - - - + + + + + + + + + + + + + @@ -41,15 +49,22 @@ + + - + + + + + + @@ -66,14 +81,6 @@ - - - - - - - - diff --git a/base/Distro/Omap3430Tiny.proj b/base/Distro/Omap3430Tiny.proj new file mode 100644 index 0000000..5452691 --- /dev/null +++ b/base/Distro/Omap3430Tiny.proj @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Distro/SPECweb99.proj b/base/Distro/SPECweb99.proj index be534b7..385acc3 100644 --- a/base/Distro/SPECweb99.proj +++ b/base/Distro/SPECweb99.proj @@ -1,57 +1,68 @@ + - - SPECweb99 - - - - + + - + + + - - - - - - - + + + + + + + + + + + + + diff --git a/base/Distro/Scripts/MapDemo.script b/base/Distro/Scripts/MapDemo.script new file mode 100644 index 0000000..1040b51 --- /dev/null +++ b/base/Distro/Scripts/MapDemo.script @@ -0,0 +1,20 @@ +echo 'ipconfig @dhcp /dev/nic0 start' +ipconfig '@dhcp' '/dev/nic0' 'start' +echo 'ipconfig @show' +ipconfig '@show' +echo 'starting map-point with proxy 157.54.118.18' +mappoint '157.54.118.18' & +echo 'starting SeattleTraffic' +seattletraffic & +echo 'starting cassini web application shell' +cassini '-app=MapDemoWebApp' & + +sleep 5 +echo ' ' +echo ' ' +echo 'From your desktop go to http:///' +echo 'You should see' +echo ' Caffeinate me' +echo 'Im At work at Microsoft Research' +echo 'or Napping on the couch in Seattle' + diff --git a/base/Distro/Scripts/bb.script b/base/Distro/Scripts/bb.script index 5aebf67..f5018bf 100644 --- a/base/Distro/Scripts/bb.script +++ b/base/Distro/Scripts/bb.script @@ -33,7 +33,7 @@ while ($i < 10) { exit } - bartok '/Singularity' '/verbosity:silence' '/LinkedStacksRequireExternalBound=true' '/LinkedStacksDumpBounds=true' '/BackEndComments=true' '/GCInlineAllocations=false' '/GCInlineWriteBarrier=false' '/LinkedStacks=true' '/UseSegmentRegister=true' '/OmitFramePointer=false' '/SymbolicDebug=true' '/DebugInline=true' '/UnnameTracedPtrs=true' '/Warnings=true' '/WholeProgram=true' '/GenCoffLineNumber=false' '/MarkSweepGC' '/minopt' '/IrSimpleInliner=false' '/DumpMethodSizes=false' '/LinkedStacksTrace=false' '/LinkedStacksDumpBounds=false' '/LinkedStacksDumpEntryPointBounds=false' '/out:' '\blackhole\kernel.obj' '/outdir:' '\blackhole' '/lib:' '\init' '\init\kernel.exe' '\init\Diagnostics.Contracts.dll' '\init\Diagnostics.dll' '\init\Directory.Contracts.dll' '\init\Directory.dll' '\init\Drivers.dll' '\init\FileSystem.Contracts.dll' '\init\Hal.LegacyPC.dll' '\init\ILHelpers.dll' '\init\Io.Contracts.dll' '\init\IoSystem.dll' '\init\Loader.dll' '\init\Microsoft.SingSharp.Runtime.dll' '\init\Security.Contracts.dll' '\init\SecurityService.dll' '\init\Security.dll' '\init\Stress.Contracts.dll' '\init\Stress.dll' '\init\System.Compiler.Runtime.dll' + bartok '/Singularity' '/verbosity:silence' '/LinkedStacksRequireExternalBound=true' '/LinkedStacksDumpBounds=true' '/BackEndComments=true' '/GCInlineArrayAllocations=false' '/GCInlineFixedAllocations=false' '/GCIntrinsicFixedAllocations=false' '/GCInlineWriteBarrier=false' '/LinkedStacks=true' '/UseSegmentRegister=true' '/OmitFramePointer=false' '/SymbolicDebug=true' '/DebugInline=true' '/UnnameTracedPtrs=true' '/Warnings=true' '/WholeProgram=true' '/GenCoffLineNumber=false' '/MarkSweepGC' '/minopt' '/IrSimpleInliner=false' '/DumpMethodSizes=false' '/LinkedStacksTrace=false' '/LinkedStacksDumpBounds=false' '/LinkedStacksDumpEntryPointBounds=false' '/out:' '\blackhole\kernel.obj' '/outdir:' '\blackhole' '/lib:' '\init' '\init\kernel.exe' '\init\Diagnostics.Contracts.dll' '\init\Diagnostics.dll' '\init\Directory.Contracts.dll' '\init\Directory.dll' '\init\Drivers.dll' '\init\FileSystem.Contracts.dll' '\init\Hal.LegacyPC.dll' '\init\Hypercall.Contracts.dll' '\init\Hypercall.dll' '\init\ILHelpers.dll' '\init\Io.Contracts.dll' '\init\IoSystem.dll' '\init\Loader.dll' '\init\Microsoft.SingSharp.Runtime.dll' '\init\Security.Contracts.dll' '\init\SecurityService.dll' '\init\Security.dll' '\init\Stress.Contracts.dll' '\init\Stress.dll' '\init\System.Compiler.Runtime.dll' if (!($? == 0)) { echo 'bb: bartok failed.' decho 'bb: bartok failed.' diff --git a/base/Distro/Scripts/bst.script b/base/Distro/Scripts/bst.script new file mode 100644 index 0000000..0245b00 --- /dev/null +++ b/base/Distro/Scripts/bst.script @@ -0,0 +1,37 @@ +bootCount +boots = $? + +msg = "Starting Tests pass ${boots}..." +echo $msg +decho $msg + +singunit '-Timings' 'bvt' "-Pass:${boots}" + +if ($boots == 0) { + msg = 'Running suspend/stop test.' + echo $msg + decho $msg + arg1 = '(& p2 (* 9999999 print 2 delay 2 ) wall )' + arg2 = ' (* 5 print 1 delay 2 ) suspend p2' + arg3 = ' (* 5 print 1 delay 2 ) resume p2' + arg4 = ' (* 5 print 1 delay 2 ) stop p2' + arg5 = ' (* 5 print 1 delay 2 )' + arg = $arg1 . $arg2 . $arg3 . $arg4 . $arg5 + kptest $arg + msg = 'Completed suspend/stop test.' + echo $msg + decho $msg +} + +if ($boots < 2) { + msg = "Complete tests pass ${boots}...rebooting" + echo $msg + decho $msg + warmboot +} +else { + msg = 'Tests Finished. Shutting down...' + echo $msg + decho $msg + shutdown +} diff --git a/base/Distro/Scripts/bvt.0.script b/base/Distro/Scripts/bvt.0.script deleted file mode 100644 index dc23f5b..0000000 --- a/base/Distro/Scripts/bvt.0.script +++ /dev/null @@ -1,96 +0,0 @@ -msg = '[BVT 0.0] Running page table test.' -echo $msg -decho $msg -dumppages - -msg = '[BVT 0.1] Running Pnp test.' -echo $msg -decho $msg -if (false) { - pnp - pnp - pnp -} -dumppages - -msg = '[BVT 0.2] Running channel test.' -echo $msg -decho $msg -channeldemo -channeldemo -channeldemo -dumppages - -msg = '[BVT 0.3] Running ram disk contents test.' -echo $msg -decho $msg -if (false) { - disk - disk - disk -} -dumppages - -msg = '[BVT 0.4] Running sound driver test.' -echo $msg -decho $msg -play -play -play -dumppages - -msg = '[BVT 0.5] Running select test.' -echo $msg -decho $msg -select -select -select -dumppages - -msg = '[BVT 0.6] Running thread test.' -echo $msg -decho $msg -threadtest -threadtest -threadtest -dumppages - -msg = '[BVT 0.7] Running mptxofy test.' -echo $msg -decho $msg -if (false) { - mptxofy -} - -msg = '[BVT 0.8] Running tasklist.' -echo $msg -decho $msg -tasklist -dumppages - -msg = '[BVT 0.9] Running exception test.' -echo $msg -decho $msg -throw -throwwithlinkstack - -msg = '[BVT .10] Running suspend/stop test.' -echo $msg -decho $msg -arg1 = '(& p2 (* 9999999 print 2 delay 2 ) wall )' -arg2 = ' (* 5 print 1 delay 2 ) suspend p2' -arg3 = ' (* 5 print 1 delay 2 ) resume p2' -arg4 = ' (* 5 print 1 delay 2 ) stop p2' -arg5 = ' (* 5 print 1 delay 2 )' -arg = $arg1 . $arg2 . $arg3 . $arg4 . $arg5 -kptest $arg - -msg = '[BVT 0.11] Running singbench test.' -echo $msg -decho $msg -singbench - -msg = '[BVT 0.12] Running warmboot test.' -echo $msg -decho $msg -warmboot diff --git a/base/Distro/Scripts/bvt.1.script b/base/Distro/Scripts/bvt.1.script deleted file mode 100644 index f601c9a..0000000 --- a/base/Distro/Scripts/bvt.1.script +++ /dev/null @@ -1,29 +0,0 @@ -msg = '[BVT 1.0] Running page table test.' -echo $msg -decho $msg -dumppages - -msg = '[BVT 1.1] Running ram disk contents test.' -echo $msg -decho $msg -if (false) { - disk -} -dumppages - -msg = '[BVT 1.2] Running sound driver test.' -echo $msg -decho $msg -play -dumppages - -msg = '[BVT 0.8] Running tasklist.' -echo $msg -decho $msg -tasklist -dumppages - -msg = '[BVT 1.3] Running warmboot test.' -echo $msg -decho $msg -warmboot diff --git a/base/Distro/Scripts/bvt.2.script b/base/Distro/Scripts/bvt.2.script deleted file mode 100644 index b5f5884..0000000 --- a/base/Distro/Scripts/bvt.2.script +++ /dev/null @@ -1,9 +0,0 @@ -msg = '[BVT 2.0] Running page table test.' -echo $msg -decho $msg -dumppages - -msg = '[BVT 2.1] Running shutdown test.' -echo $msg -decho $msg -shutdown diff --git a/base/Distro/Scripts/bvt.mandelbrot.script b/base/Distro/Scripts/bvt.mandelbrot.script new file mode 100644 index 0000000..a9b6545 --- /dev/null +++ b/base/Distro/Scripts/bvt.mandelbrot.script @@ -0,0 +1,9 @@ +msg = '[BVT 2.0] Running mandelbrot test.' +echo $msg +decho $msg +mandelbrot '-bvt' '-time' '10' '-trace' + +# msg = '[BVT 2.1] Running shutdown test.' +# echo $msg +# decho $msg +# shutdown diff --git a/base/Distro/Scripts/bvt.script b/base/Distro/Scripts/bvt.script new file mode 100644 index 0000000..0245b00 --- /dev/null +++ b/base/Distro/Scripts/bvt.script @@ -0,0 +1,37 @@ +bootCount +boots = $? + +msg = "Starting Tests pass ${boots}..." +echo $msg +decho $msg + +singunit '-Timings' 'bvt' "-Pass:${boots}" + +if ($boots == 0) { + msg = 'Running suspend/stop test.' + echo $msg + decho $msg + arg1 = '(& p2 (* 9999999 print 2 delay 2 ) wall )' + arg2 = ' (* 5 print 1 delay 2 ) suspend p2' + arg3 = ' (* 5 print 1 delay 2 ) resume p2' + arg4 = ' (* 5 print 1 delay 2 ) stop p2' + arg5 = ' (* 5 print 1 delay 2 )' + arg = $arg1 . $arg2 . $arg3 . $arg4 . $arg5 + kptest $arg + msg = 'Completed suspend/stop test.' + echo $msg + decho $msg +} + +if ($boots < 2) { + msg = "Complete tests pass ${boots}...rebooting" + echo $msg + decho $msg + warmboot +} +else { + msg = 'Tests Finished. Shutting down...' + echo $msg + decho $msg + shutdown +} diff --git a/base/Distro/Scripts/evtst.script b/base/Distro/Scripts/evtst.script new file mode 100644 index 0000000..5c52178 --- /dev/null +++ b/base/Distro/Scripts/evtst.script @@ -0,0 +1,5 @@ +singunit '@manual' EventTest EventTest TestAll '-TestTimeout=200000000' +#singunit '@manual' EvStress EvStress TestAll '-TestTimeout=200000' +#singunit '@manual' EventActive EventActive TestAll '-TestTimeout=200000' + + diff --git a/base/Distro/Scripts/fibbench.script b/base/Distro/Scripts/fibbench.script new file mode 100644 index 0000000..ba1af7b --- /dev/null +++ b/base/Distro/Scripts/fibbench.script @@ -0,0 +1,18 @@ +# +# A script for measuring chores using Fibonacci with delays +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# + + +n = 30 + +echo 'Beginning Chores benchmark: Fib '$n + +echo '' + +fib '-n:30' '-reps:3' '-ShowDelay' '-ShowSequence' '-ShowPromise' '-ShowChore' '0' '28' '80' '160' '375' '750' '1200' '8000' + +echo '' + +echo 'Finished Chores benchmark' diff --git a/base/Distro/Scripts/netstart.script b/base/Distro/Scripts/netstart.script new file mode 100644 index 0000000..b15a213 --- /dev/null +++ b/base/Distro/Scripts/netstart.script @@ -0,0 +1,7 @@ +# Configure the network defaults +ipconfig '@dhcp' '/dev/nic0' 'start' + +# Show our address +ipconfig '@show' + + diff --git a/base/Distro/Scripts/ramfat.script b/base/Distro/Scripts/ramfat.script new file mode 100644 index 0000000..e1efa24 --- /dev/null +++ b/base/Distro/Scripts/ramfat.script @@ -0,0 +1,4 @@ + +ramdiskcontrol '@create' '-s=50M' +fatcontrol '@format' '/dev/ramdisk0' +fatcontrol '@mount' '/dev/ramdisk0' '/fs' diff --git a/base/Distro/Scripts/smtpreset.script b/base/Distro/Scripts/smtpreset.script new file mode 100644 index 0000000..6ad2111 --- /dev/null +++ b/base/Distro/Scripts/smtpreset.script @@ -0,0 +1,20 @@ +#kill smtp +kill 'smtpagent' + +#kill mailstore +kill 'mailstore' + +#unmount fs +fatcontrol '@unmount' '/fs' + +#reformat with 20 GB partiton +fatcontrol '@format' '-m=41943040' '/dev/disk0' + +#mount +fatcontrol '@mount' 'dev/disk0' '/fs' + +#restart mailstore' +mailstore & + + + diff --git a/base/Distro/Scripts/smtptest.script b/base/Distro/Scripts/smtptest.script new file mode 100644 index 0000000..67a017f --- /dev/null +++ b/base/Distro/Scripts/smtptest.script @@ -0,0 +1,11 @@ +# Configure the network defaults +ipconfig '@dhcp' '/dev/nic0' 'start' + +# Show our address +ipconfig '@show' + +#mount file system +fatcontrol '@mount' '/dev/disk0' '/fs' + + + diff --git a/base/Distro/Scripts/specweb2.script b/base/Distro/Scripts/specweb2.script new file mode 100644 index 0000000..bcb5f1f --- /dev/null +++ b/base/Distro/Scripts/specweb2.script @@ -0,0 +1,6 @@ +ipconfig '@dhcp' '/dev/nic0' start +fatcontrol '@mount' '/dev/vol0.0' '/fs' +ipconfig '@show' +testutil +cassini '-quitURL' '-app:SPECWeb99WebApp' +exit 8188 diff --git a/base/Distro/Scripts/specweb99.script b/base/Distro/Scripts/specweb99.script index e9ffd24..9632aa0 100644 --- a/base/Distro/Scripts/specweb99.script +++ b/base/Distro/Scripts/specweb99.script @@ -1,10 +1,9 @@ # # A script for automated specweb99 testing. # -# Copyright (c) Microsoft Corporation. All rights reserved. +# Copyright (c) Microsoft Corporation. All rights reserved. # -# # Wait just to be sure netstack has spun up. This script is most likely # run from boot. sleep 5 diff --git a/base/Distro/Scripts/specweball.script b/base/Distro/Scripts/specweball.script new file mode 100644 index 0000000..a3f4499 --- /dev/null +++ b/base/Distro/Scripts/specweball.script @@ -0,0 +1,9 @@ +fatcontrol '@format' '/dev/vol0.0' +fatcontrol '@mount' '/dev/vol0.0' '/fs' +mkdir '/fs/specweb' +wafgen99 '-c=2000' '/fs/specweb' +ipconfig '@dhcp' '/dev/nic0' start +ipconfig '@show' +testutil +cassini '-quitURL' '-app:SPECWeb99WebApp' +exit 8188 diff --git a/base/Distro/Scripts/startup.script b/base/Distro/Scripts/startup.script index e33d70b..c1c68ef 100644 --- a/base/Distro/Scripts/startup.script +++ b/base/Distro/Scripts/startup.script @@ -9,3 +9,21 @@ # net '@mount' '/distro' '\\192.168.0.12\distro' echo 'Welcome to Singularity' + +# Uncomment these lines to configure and start the SMTP server: +# +# echo 'Configure IP to 10.99.99.2' +# ipconfig '/dev/nic0' '10.99.99.2' '255.255.255.0' '10.99.99.1' +# echo 'Starting SMTPD' +# smtpd '/s:10.99.99.2' '/a:/init/accounts' +# + +# Uncomment these lines to configure and start the WebServer with WebHost server: +# +# echo 'Configure IP to 10.99.99.2' +# ipconfig '/dev/nic0' '10.99.99.2' '255.255.255.0' '10.99.99.1' +# echo start webhost '-app:diagnosticswebapp' +# start webhost '-app:diagnosticswebapp' +# echo start cassini '-app:diagnosticswebapp' '-nspath:/service/webapp' +# start cassini '-app:diagnosticswebapp' '-nspath:/service/webapp' '-load:500' + diff --git a/base/Distro/Scripts/tblast.script b/base/Distro/Scripts/tblast.script new file mode 100644 index 0000000..c5c9ea0 --- /dev/null +++ b/base/Distro/Scripts/tblast.script @@ -0,0 +1,3 @@ +tcpblast '157.55.97.48' '9' '10000' '256' + + diff --git a/base/Distro/Scripts/tgulp.script b/base/Distro/Scripts/tgulp.script new file mode 100644 index 0000000..45d7156 --- /dev/null +++ b/base/Distro/Scripts/tgulp.script @@ -0,0 +1,3 @@ +tcpgulp '157.55.97.48' '9' + + diff --git a/base/Distro/Scripts/webstress.script b/base/Distro/Scripts/webstress.script new file mode 100644 index 0000000..80a08fa --- /dev/null +++ b/base/Distro/Scripts/webstress.script @@ -0,0 +1,14 @@ +echo 'Starting DHCP' +sleep '5' +ipconfig '@dhcp' '/dev/nic0' start + +# Waste some time, for the dhcp request to complete +sleep '20' +ipconfig '@show' + +# cause the IP address to appear in the debugger +testutil + +echo 'Starting WebStress' +webserver & + diff --git a/base/Distro/Scripts/xbench.init.script b/base/Distro/Scripts/xbench.init.script new file mode 100644 index 0000000..41870b2 --- /dev/null +++ b/base/Distro/Scripts/xbench.init.script @@ -0,0 +1,7 @@ +# This script mounts a filesystem and creates the file set for +# running the xbench script. + +fatcontrol '@format' '/dev/vol0.0' +fatcontrol '@mount' '/dev/vol0.0' '/fs' +wafgen99 '-v' '/fs' +fatcontrol '@unmount' '/fs' diff --git a/base/Distro/Scripts/xbench.script b/base/Distro/Scripts/xbench.script index ccd8471..b2a845a 100644 --- a/base/Distro/Scripts/xbench.script +++ b/base/Distro/Scripts/xbench.script @@ -4,6 +4,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # + i = 0 while ($i < 10) { clear @@ -26,7 +27,7 @@ while ($i < 10) { echo 'xb: bartok-'$i decho 'xb: bartok-'$i perfcnt '-g' - bartok '/Singularity' '/verbosity:PerPhase' '/LinkedStacksRequireExternalBound=true' '/LinkedStacksDumpBounds=true' '/BackEndComments=true' '/GCInlineAllocations=false' '/GCInlineWriteBarrier=false' '/LinkedStacks=true' '/UseSegmentRegister=true' '/OmitFramePointer=false' '/SymbolicDebug=true' '/DebugInline=true' '/UnnameTracedPtrs=true' '/Warnings=true' '/WholeProgram=true' '/GenCoffLineNumber=false' '/MarkSweepGC' '/minopt' '/IrSimpleInliner=false' '/DumpMethodSizes=false' '/LinkedStacksTrace=false' '/LinkedStacksDumpBounds=false' '/LinkedStacksDumpEntryPointBounds=false' '/out:' '\init\kernel.obj' '/outdir:' '\init' '\init\kernel.exe' '\init\Diagnostics.dll' '\init\Loader.dll' '\init\Directory.dll' '\init\Stress.dll' '\init\IoSystem.dll' '\init\Hal.LegacyPC.dll' '\init\System.Compiler.Runtime.dll' '\init\ILHelpers.dll' '\init\Microsoft.SingSharp.Runtime.dll' '\init\Diagnostics.Contracts.dll' '\init\Directory.Contracts.dll' '\init\FileSystem.Contracts.dll' '\init\Io.Contracts.dll' '\init\Stress.Contracts.dll' '\init\drivers.dll' '\init\Security.Contracts.dll' '\init\Security.dll' '\init\Security.Service.dll' + bartok '/Singularity' '/verbosity:PerPhase' '/LinkedStacksRequireExternalBound=true' '/LinkedStacksDumpBounds=true' '/BackEndComments=true' '/GCInlineArrayAllocations=false' '/GCInlineFixedAllocations=false' '/GCIntrinsicFixedAllocations=false' '/GCInlineWriteBarrier=false' '/LinkedStacks=true' '/UseSegmentRegister=true' '/OmitFramePointer=false' '/SymbolicDebug=true' '/DebugInline=true' '/UnnameTracedPtrs=true' '/Warnings=true' '/WholeProgram=true' '/GenCoffLineNumber=false' '/MarkSweepGC' '/minopt' '/IrSimpleInliner=false' '/DumpMethodSizes=false' '/LinkedStacksTrace=false' '/LinkedStacksDumpBounds=false' '/LinkedStacksDumpEntryPointBounds=false' '/out:' '\init\kernel.obj' '/outdir:' '\init' '\init\kernel.exe' '\init\Diagnostics.dll' '\init\Loader.dll' '\init\Directory.dll' '\init\Stress.dll' '\init\IoSystem.dll' '\init\Hal.LegacyPC.dll' '\init\System.Compiler.Runtime.dll' '\init\ILHelpers.dll' '\init\Microsoft.SingSharp.Runtime.dll' '\init\Diagnostics.Contracts.dll' '\init\Directory.Contracts.dll' '\init\FileSystem.Contracts.dll' '\init\Io.Contracts.dll' '\init\Stress.Contracts.dll' '\init\drivers.dll' '\init\Security.Contracts.dll' '\init\Security.dll' '\init\Security.Service.dll' perfcnt i = $i + 1 } @@ -35,19 +36,20 @@ echo 'xb: finished with bartok' decho 'xb: finished with bartok' clear -echo 'Mounting /dev/vol0.2' -fsmount '/dev/vol0.2' '/fs' +echo 'Mounting /dev/vol0.0' +fatcontrol '@mount' '-r' '/dev/vol0.0' '/fs' i = 0 while ($i < 10) { clear echo 'xb: webfiles-'$i decho 'xb: webfiles-'$i perfcnt '-g' - webfiles '-f:50000' + webfiles '-f=50000' '/fs' perfcnt i = $i + 1 } +fatcontrol '@unmount' '/fs' clear echo 'xb: finished with webfiles' diff --git a/base/Distro/SlideDistro.xml b/base/Distro/SlideDistro.xml index 147e798..d5f481e 100644 --- a/base/Distro/SlideDistro.xml +++ b/base/Distro/SlideDistro.xml @@ -1,21 +1,6 @@ - - - - - - - - - - - - - - - @@ -24,7 +9,9 @@ - + + + @@ -32,39 +19,66 @@ - - - - - + + + + + + + + + + + + - - - - - - - + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + - - - + - + diff --git a/base/Distro/Slides.proj b/base/Distro/Slides.proj new file mode 100644 index 0000000..fc5cb65 --- /dev/null +++ b/base/Distro/Slides.proj @@ -0,0 +1,37 @@ + + + + + + + + $(SINGULARITY_ROOT)\Distro\SlideDistro.xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Distro/Small.proj b/base/Distro/Small.proj index 249d069..83b5787 100644 --- a/base/Distro/Small.proj +++ b/base/Distro/Small.proj @@ -1,12 +1,15 @@ + - - Small - - @@ -15,15 +18,20 @@ - - - + + + + + + + diff --git a/base/Distro/Smtp.proj b/base/Distro/Smtp.proj new file mode 100644 index 0000000..51591b2 --- /dev/null +++ b/base/Distro/Smtp.proj @@ -0,0 +1,48 @@ + + + + + + + Smtp + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Distro/Tiny.proj b/base/Distro/Tiny.proj index 75542db..c621ca6 100644 --- a/base/Distro/Tiny.proj +++ b/base/Distro/Tiny.proj @@ -1,20 +1,26 @@ + - - Tiny - - - + - + + + diff --git a/base/Distro/WebApps.proj b/base/Distro/WebApps.proj new file mode 100644 index 0000000..cae0ae7 --- /dev/null +++ b/base/Distro/WebApps.proj @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Distro/World.proj b/base/Distro/World.proj index 9422775..759ddc4 100644 --- a/base/Distro/World.proj +++ b/base/Distro/World.proj @@ -1,19 +1,39 @@ + - - - World - + - - - + Exclude="$(SINGULARITY_ROOT)\Applications\Namespace\AtomicTestDSP\AtomicTestDSP.csproj; + $(SINGULARITY_ROOT)\Applications\CHello2\CHello2.csproj; + $(SINGULARITY_ROOT)\Applications\Benchmarks\bartokh\**\*.csproj; + $(SINGULARITY_ROOT)\Applications\Tests\InsightTests\WpfObserverTest\WpfObserverTest.csproj; + $(SINGULARITY_ROOT)\Applications\Tests\InsightTests\InsightTestsVs.csproj; + $(SINGULARITY_ROOT)\Applications\**\*.Win.*proj; + $(ExcludePhoenixTestApp)" + /> + + + + + + + + diff --git a/base/Distro/Xbench.proj b/base/Distro/Xbench.proj new file mode 100644 index 0000000..7190124 --- /dev/null +++ b/base/Distro/Xbench.proj @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Distro/virus.db b/base/Distro/virus.db new file mode 100644 index 0000000..6ef9fd6 --- /dev/null +++ b/base/Distro/virus.db @@ -0,0 +1 @@ +singularity_fake=fda3734aa2e54daa94f8c584478348d753427a01583b4562998fc10e95be8d41afbef9cce4b445c7809392b7555c22593caba92e68d44c3491e4bc5be74716e9a608033c285a4b42ac717ef8268ab86586cacb39646c4f32a3dbe04db5fa537e diff --git a/base/Distro/virus.hdb b/base/Distro/virus.hdb new file mode 100644 index 0000000..e6fb98c --- /dev/null +++ b/base/Distro/virus.hdb @@ -0,0 +1 @@ +fda3734aa2e54daa94f8c584478348d7:72737:SingularityFake diff --git a/base/Distro/virus.ndb b/base/Distro/virus.ndb new file mode 100644 index 0000000..ea61295 --- /dev/null +++ b/base/Distro/virus.ndb @@ -0,0 +1 @@ +Singularity.HTML.Fake:3:*:fda3734aa2e54daa94f8c584478348d753427a01583b4562998fc10e95be8d41afbef9cce4b445c7809392b7555c22593caba92e68d44c3491e4bc5be74716e9a608033c285a4b42ac717ef8268ab86586cacb39646c4f32a3dbe04db5fa537e diff --git a/base/Drivers/Disk/BusMasterDma.cs b/base/Drivers/Disk/BusMasterDma.cs index db95623..e9dd981 100644 --- a/base/Drivers/Disk/BusMasterDma.cs +++ b/base/Drivers/Disk/BusMasterDma.cs @@ -9,18 +9,18 @@ // See "Programming Interface for Bus Master IDE Controller" // or www.t13.org 1510D Draft "ATA/ATAPI Host Adapters Standard(ATA - Adapter)" -/* - 1. Software prepares a PRD Table in host memory. - 2. Software provides the starting address of the PRD Table by loading the PRD Table - Pointer Register. - Setting of the Read/Write Control bit specifies the direction of the data transfer. - Clearing the Interrupt and Error bits in the ATA Bus Master Status register - to zero readies the adapter for a data transfer. - 3. Software issues the appropriate DMA transfer command to the device. - 4. Software initiates the bus master function by writing a one to the Start bit - in the ATA Bus Master Command Register for the appropriate channel. - 5. The adapter transfers data to/from host memory responding to DMA requests from the ATA device.` -*/ +// +// 1. Software prepares a PRD Table in host memory. +// 2. Software provides the starting address of the PRD Table by loading the PRD Table +// Pointer Register. +// Setting of the Read/Write Control bit specifies the direction of the data transfer. +// Clearing the Interrupt and Error bits in the ATA Bus Master Status register +// to zero readies the adapter for a data transfer. +// 3. Software issues the appropriate DMA transfer command to the device. +// 4. Software initiates the bus master function by writing a one to the Start bit +// in the ATA Bus Master Command Register for the appropriate channel. +// 5. The adapter transfers data to/from host memory responding to DMA requests from the ATA device.` +// //#define DEBUG_BUS_MASTER_DMA @@ -143,7 +143,7 @@ namespace Microsoft.Singularity.Drivers.IDE { //probe the status register to see if we have been interrupted byte status = statusPort.Read8(); - if ( (status & (byte) BUSMASTER_STATUS_MASK_INTERRUPT) == 0 ) { + if ((status & (byte) BUSMASTER_STATUS_MASK_INTERRUPT) == 0) { return false; } return true; @@ -160,14 +160,16 @@ namespace Microsoft.Singularity.Drivers.IDE commandPort.Write8(BUSMASTER_CONTROL_MASK_STOP); // disable BM //Tracing.Log(Tracing.Debug," stop: fullstatus ={0:x}\n",(UIntPtr)ideConfigHandle.IdeController.ReadFullStatus()); byte status = GetStatus(); - if ( (status & (byte)BUSMASTER_STATUS_MASK_INTERRUPT) == 0 ) { + if ((status & (byte)BUSMASTER_STATUS_MASK_INTERRUPT) == 0) { Tracing.Log(Tracing.Debug,"BusMaster.Disarm: interrupt line not set {0}!\n",(UIntPtr) status); - DebugStub.Break(); + DebugStub.WriteLine("BusMaster.Disarm: interrupt line not set {0}!\n",__arglist(status)); + //DebugStub.Break(); } - if ( (status & (byte)BUSMASTER_STATUS_MASK_ERROR) > 0 ) { + if ((status & (byte)BUSMASTER_STATUS_MASK_ERROR) > 0) { Tracing.Log(Tracing.Debug,"BusMaster.Disarm: error!!!!\n",(UIntPtr) status); - DebugStub.Break(); + DebugStub.WriteLine("BusMaster.Disarm: error!!!!\n",__arglist(status)); + //DebugStub.Break(); } status = (byte) (BUSMASTER_STATUS_MASK_INTERRUPT | BUSMASTER_STATUS_MASK_ERROR); @@ -180,7 +182,7 @@ namespace Microsoft.Singularity.Drivers.IDE [ System.Diagnostics.Conditional("DEBUG_BUS_MASTER_DMA") ] public void DumpPrd() { - for (int i=0; i < PRD_MAX_ENTRIES; i++) { + for (int i = 0; i < PRD_MAX_ENTRIES; i++) { uint address; uint length; bool eot; diff --git a/base/Drivers/Disk/Disk.csproj b/base/Drivers/Disk/Disk.csproj index 4697eae..444cf87 100644 --- a/base/Drivers/Disk/Disk.csproj +++ b/base/Drivers/Disk/Disk.csproj @@ -1,8 +1,6 @@  + + @@ -6,7 +14,7 @@ - + diff --git a/base/Drivers/LegacyKeyboard/LegacyKeyboard.csproj b/base/Drivers/LegacyKeyboard/LegacyKeyboard.csproj index 715db5a..a70a416 100644 --- a/base/Drivers/LegacyKeyboard/LegacyKeyboard.csproj +++ b/base/Drivers/LegacyKeyboard/LegacyKeyboard.csproj @@ -1,8 +1,6 @@  + + + + + + Exe + IntelGigEthernet + false + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Drivers/Network/Intel/Intel.sg b/base/Drivers/Network/Intel/Intel.sg new file mode 100644 index 0000000..1a75d14 --- /dev/null +++ b/base/Drivers/Network/Intel/Intel.sg @@ -0,0 +1,919 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Notes: +// +// Simple Driver for Intel 8254x PCI Ethernet Cards. +// +// Useful reference URLs: +// http://download.intel.com/design/network/manuals/8254x_GBe_SDM.pdf +// +// We use standard IP/TCP checksum offloading. +// +// The driver currently runs in interrupt driven mode using hardware based +// interrupt throttling. +// +// Phy handling is automatic and relies on attached phy supporting +// auto-negotiation. The phy update interrupt is used to keep track +// of phy state. +// +// TODO: +// +// - Flow Control Support +// - Support for packet fragments (all packets must have a single fragment +// currently) +// - Jumbo Packets +// - Transmit TCP/IP checksum offloading +// + +//#define DEBUG_INTEL + +using System; +using System.Threading; +using System.Diagnostics; +using System.Collections; + +using Microsoft.Contracts; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Io.Net; +using Microsoft.Singularity.V1.Services; +using Microsoft.Singularity.Drivers; +using Microsoft.SingSharp; + +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Extending; + +using Drivers.Net; + +namespace Microsoft.Singularity.Drivers.Network.Intel +{ + [CLSCompliant(false)] + public class Intel + { + internal const uint MaxTxFragmentsPerPacket = 1; // Todo: allow frags + internal const uint MaxRxFragmentsPerPacket = 1; // Todo: allow frags + + internal const uint MaxRxPackets = 1023; // one less than full buffer + internal const uint MaxTxPackets = 2047; + + // Fragments must be power of two. + internal const uint MaxTxFragmentsInRing = ((MaxTxPackets + 1) * + MaxTxFragmentsPerPacket) ; + internal const uint MaxRxFragmentsInRing = ((MaxRxPackets + 1) * + MaxRxFragmentsPerPacket); + + internal const ChecksumSupport ChecksumSupport = + ChecksumSupport.AllIp4Recieve | ChecksumSupport.AllIp6Recieve; + + internal const uint IEEE8023FrameBytes = 1518; + internal const uint MtuBytes = 1514; + internal const uint PhyAddress = 1; + + private string! cardName; + private CardType cardType; + private PciDeviceConfig! pciConfig; + private IoMemory! ioMemory; + private IoIrq! irq; + private Thread irqWorkerThread; + private bool irqWorkerStop; + private bool ioRunning; + + private IntelEventRelay eventRelay; + + private EerdRegister! eerdReg; + private EthernetAddress macAddress; + + private IntelRxRingBuffer! rxRingBuffer; + private IntelTxRingBuffer! txRingBuffer; + + [NotDelayed] + internal Intel(IntelResources! res) + { + IoMemoryRange imr = (!)res.imr; + ioMemory = (!) imr.MemoryAtOffset(0, 0x20000, Access.ReadWrite); + + IoIrqRange! iir = (!)res.irq; + irq = (!) iir.IrqAtOffset(0); + + rxRingBuffer = new IntelRxRingBuffer(MaxRxFragmentsInRing); + txRingBuffer = new IntelTxRingBuffer(MaxTxFragmentsInRing); + + this.cardName = res.CardName; + this.cardType = res.CardType; + + PciDeviceConfig! config = (PciDeviceConfig!)IoConfig.GetConfig(); + this.pciConfig = config; + + DebugWriteLine("PCI Control {0:x8} Status {1:x8}", + __arglist(config.Control, config.Status)); + + byte cap = config.Capabilities; + while (cap != 0 && cap < 0xff) { + DebugWriteLine("Capability at: {0:x2}", __arglist(cap)); + byte id = config.Read8(cap); + if (id == 7) { + DebugWriteLine("PCI-X Command {0:x4} Status {1:x8}", + __arglist( + config.Read16(cap + 2) & 0x3f, + config.Read32(cap + 4) + ) + ); + } + else if (id == 5) { + // MSI capability - only reached if enabled, + // but usually present. + DebugWriteLine("MSI Control {0:x4} Address {1:x8}.{2:x8} Data {3:x4}", + __arglist(config.Read16(cap + 2), + config.Read32(cap + 8), + config.Read32(cap + 4), + config.Read16(cap + 10) + ) + ); + } + else { + DebugWriteLine("Unknown capability {0:x2}", __arglist(id)); + } + + cap = config.Read8(cap + 1); + } + + eerdReg = new EerdRegister(config.DeviceId); + + base(); + + DebugWriteLine("Irq {0}", __arglist(irq.Irq)); + + Debug.Assert((config.Control & PciConfig.PCI_ENABLE_BUS_MASTER) != 0); + Debug.Assert((config.Control & PciConfig.PCI_ENABLE_MEMORY_SPACE) != 0); + DebugStub.Assert(config.InterruptsEnabled); + } + + ~Intel() + { + ReleaseResources(); + } + + /////////////////////////////////////////////////////////////////////// + // + // Initialisation and finalisation code + // + internal void Initialize() + { + DebugPrint("Initialising " + DriverName + " Version " + DriverVersion); + + if (!irq.RegisterInterrupt()) { + DebugStub.Break(); + } + + ResetDevice(); + SetupPhys(); + SetupMac(); + } + + void ReleaseResources() + { + DisableInterrupts(); + StopIo(); + irq.ReleaseInterrupt(); + irqWorkerThread = null; + } + + internal void Shutdown() + { + if (irqWorkerThread != null) { + // XXX This is not the right way to do this. + DisableInterrupts(); + StopIo(); + } + } + + internal void StartIo() + { + irqWorkerStop = false; + irqWorkerThread = new Thread(new ThreadStart(this.IrqWorkerMain)); + irqWorkerThread.Start(); + + SetupMac(); + + EnableInterrupts(); + + StartReceiver(); + StartTransmitter(); + this.ioRunning = true; + } + + internal void StopIo() + { + StopTransmitter(); + StopReceiver(); + if (irqWorkerThread != null) { + // Set stop flag, wake-up irqWorker thread, then wait. + irqWorkerStop = true; + irq.Pulse(); + irqWorkerThread.Join(); + irqWorkerThread = null; + } + this.ioRunning = false; + } + + internal void SetEventRelay(IntelEventRelay intelEventRelay) + { + eventRelay = intelEventRelay; + } + + /////////////////////////////////////////////////////////////////////// + // + // Setup functions + // + private void ResetDevice() + { + // Disable all interrupts + DisableInterrupts(); + + DebugWriteLine("CTRL pre-device-reset : {0:x8}\n", + __arglist(Read32(Register.CTRL))); + + Write32(Register.RECV_CTRL, 0); + Write32(Register.TSMT_CTRL, TsmtCtrlBits.PAD_SHORT_PACKETS); + Read32(Register.STATUS); + + // Allow pending PCI transactions to complete + Delay(10); + + // Reset the device + RegSetBits(Register.CTRL, CtrlBits.RST | CtrlBits.PHY_RST); + + // Wait for 3us before board is really reset + Delay(3); + + Delay(100); + + Read32(Register.CTRL); + + // Set the control register to the proper initial values, + // clearing RST and PHY_RST as a side-effect. + Write32(Register.CTRL, CtrlBits.FD | CtrlBits.ASDE | CtrlBits.SLU); + + while ((Read32(Register.CTRL) & CtrlBits.RST) != 0) { + DebugWriteLine("."); + } + DebugWriteLine("Autonegotiation complete"); + } + + private void SetupPhys() + { + if (this.cardType == CardType.I82545GM ) { + Write32(Register.MDIC, Mdic.MdiRead); + while ((Read32(Register.MDIC) & Mdic.Ready) == 0); + uint mdic = Read32(Register.MDIC) & Mdic.DataMask; + mdic |= Mdic.MdiWrite; + mdic &= Mdic.PowerMask; + Write32(Register.MDIC, mdic); + while ((Read32(Register.MDIC) & Mdic.Ready) == 0); + + DumpPhy(); + + uint ctrl = Read32(Register.CTRL); + ctrl |= CtrlBits.SLU | CtrlBits.ASDE; + ctrl &= ~(CtrlBits.ILOS | CtrlBits.FRCSPD | CtrlBits.FRCDPLX); + Write32(Register.CTRL, ctrl); + Delay(20); + + // Wait for link to come up + int attempts = 5000; + while ((MiiRead(PhyAddress, 1) & 0x4) == 0) { + Delay(1000); + if (0 == attempts--) { + DumpPhy(); + DebugStub.Break(); + } + } + + DumpPhy(); + + uint phyCtrl = MiiRead(PhyAddress, 0); + phyCtrl |= 0x1200; // Enable-AutoNeg + Restart-AutoNeg + MiiWrite(PhyAddress, 0, phyCtrl); + + uint phyStat; + attempts = 5000; + do { + Delay(1000); + phyStat = MiiRead(PhyAddress, 1); + phyStat = MiiRead(PhyAddress, 1); + } while ((phyStat & 0x20) == 0 && --attempts > 0); // wait for autoneg complete + + DebugStub.Assert((phyStat & 0x20) != 0); + + // Transfer settings from phy to ctrl + uint phyPssr = MiiRead(PhyAddress, 17); + ctrl = Read32(Register.CTRL); + ctrl &= ~(CtrlBits.SPEED | CtrlBits.FD); + if ((phyPssr & (1 << 13)) != 0) { + ctrl |= CtrlBits.FD; + } + + ctrl |= ((phyPssr >> 14) & 3) << 8; + ctrl |= CtrlBits.FRCSPD | CtrlBits.FRCDPLX; + Write32(Register.CTRL, ctrl); + + Delay(1000000); + + DumpPhy(); + } + else if (this.cardType == CardType.I82541PI) { + // Use Internal Phys mode (SerDes is not possible for some 8254x cards) + Write32BitRange(Register.CTRL_EXT, + CtrlExtBits.LINK_MODE_PHYS, + CtrlExtBits.LINK_MODE_LO_BIT, + CtrlExtBits.LINK_MODE_HI_BIT); + } + + // We don't want flow control + Write32(Register.FCAL, 0x0); + Write32(Register.FCAH, 0x0); + Write32(Register.FCT, 0x0); + Write32(Register.FCTTV, 0x0); + } + + private void SetupMac() + { + uint ral, rah; + + // Setup our Ethernet address + macAddress = GetMacFromEeprom(); + + DebugPrint("Setting Ethernet Mac Address to {0:s}\n", + __arglist(macAddress.ToString())); + + GetMacHiLow(out ral, out rah, macAddress); + rah = rah | RahRegister.ADDRESS_VALID; + Write32(Register.RAL0, ral); + Write32(Register.RAH0, rah); + + // Clear the mutlicast table array + for (uint i = 0; i < MtaRegister.MTA_LENGTH; i++) + { + Write32(Register.MTA_START + (4*i), 0); + } + + // Setup Descriptor buffers for rx + ResetRxRingBuffer(); + + // Setup Receiever Control flags + Write32(Register.RECV_CTRL, (RecvCtrlBits.BROADCAST_ACCEPT | + RecvCtrlBits.STRIP_CRC | + RecvCtrlBits.LOOPBACK_MODE_DISABLE | + RecvCtrlBits.MULTICAST_OFFSET_47_36 | + RecvCtrlBits.BUFFER_SIZE_2KB | + RecvCtrlBits.RECV_DESC_THRESHOLD_QUARTER)); + // Note: If MTU ever changes (e.g. for jumbo frames), the + // recv buffer size will need to be increased. + + // Setup the rx interrupt delay + Write32(Register.RECV_DELAY_TIMER, RxDelayTimers.RECV_DELAY_TIMER); + Write32(Register.RECV_INT_ABS_TIMER, RxDelayTimers.RECV_ABSOLUTE_TIMER); + + // Enable IP and TCP checksum calculation offloading + Write32(Register.RECV_CHECKSUM, (RecvChecksumBits.IP_CHECKSUM_ENABLE | + RecvChecksumBits.TCP_CHECKSUM_ENABLE | + RecvChecksumBits.IP6_CHECKSUM_ENABLE)); + + // Setup Descriptor buffers for tx + ResetTxRingBuffer(); + + // Setup Transmit Control flags + Write32(Register.TSMT_CTRL, + TsmtCtrlBits.PAD_SHORT_PACKETS | + TsmtCtrlBits.COLL_THRESHOLD_DEFAULT | + TsmtCtrlBits.COLL_DISTANCE_DEFAULT); + + // Setup Transmit Inter Frame Gap + Write32(Register.TSMT_IPG, TsmtIpg.DEFAULT_IPG); + + // TODO enable transmit checksum offloading + } + + /////////////////////////////////////////////////////////////////////// + // + // Helper functions + // + internal void GetMacHiLow(out uint addr_low, + out uint addr_hi, + EthernetAddress mac) + { + byte[]! macBytes = mac.GetAddressBytes(); + addr_hi = (uint) ((macBytes[5] << 8) | + (macBytes[4])); + addr_low = (uint) ((macBytes[3] << 24) | + (macBytes[2] << 16) | + (macBytes[1] << 8) | + (macBytes[0])); + } + + internal EthernetAddress GetMacFromEeprom() + { + ushort eepromData; + + byte[] macBytes = new byte[6]; + + // Mac address is in reverse byte order in the EEPROM + eepromData = ReadEepromWord(0); + macBytes[0] = (byte) (eepromData & 0xff); + macBytes[1] = (byte) (eepromData >> 8); + + eepromData = ReadEepromWord(1); + macBytes[2] = (byte) (eepromData & 0xff); + macBytes[3] = (byte) (eepromData >> 8); + + eepromData = ReadEepromWord(2); + macBytes[4] = (byte) (eepromData & 0xff); + macBytes[5] = (byte) (eepromData >> 8); + + return new EthernetAddress(macBytes); + } + + private ushort ReadEepromWord(ushort eepromAddress) + { + uint eepromRead; + + // Write address required + uint writeVal = (EerdRegister.Start | + ((uint) eepromAddress << eerdReg.AddressShift)); + + Write32(Register.EERD, writeVal); + + // wait until read has completed + do { + eepromRead = Read32(Register.EERD); + } while ((eepromRead & eerdReg.Done) == 0); + + // return data value + return (ushort) (eepromRead >> EerdRegister.DataShift); + } + + /////////////////////////////////////////////////////////////////////// + // + // Interrupt Handling + // + internal void EnableInterrupts() + { + // Clear existing interrupts + Write32(Register.IMC, 0xffffffff); + Read32(Register.ICR); + + // Set interrupts we are interested in + RegSetBits(Register.IMS, (InterruptMasks.RXT0 | InterruptMasks.RXO | + InterruptMasks.RXDMT0 | + InterruptMasks.LSC | InterruptMasks.TXQE | + InterruptMasks.TXDW)); + } + + internal void DisableInterrupts() + { + // Clear existing interrupts + Write32(Register.IMC, 0xffffffff); + Read32(Register.ICR); + } + + private bool HandleInterrupts(uint intrCause) + { + if (intrCause == 0) { + return false; + } + + NicEventType ev = NicEventType.NoEvent; + + if ((intrCause & InterruptMasks.LSC) != 0) { + DebugPrint("Link Status Change\n"); + ev |= NicEventType.LinkEvent; + } + + if ((intrCause & InterruptMasks.RXSEQ) != 0) { + DebugPrint("Sequence Error\n"); + ev |= NicEventType.LinkEvent; + } + + if (((InterruptMasks.RXT0 | + InterruptMasks.RXO | + InterruptMasks.RXDMT0) & intrCause) != 0) { + ev |= NicEventType.ReceiveEvent; + } + + // no transmit interupts are set, check rxbuffer to see + // if any packets have been sent since last time + if (txRingBuffer.NewTransmitEvent()) { + ev |= NicEventType.TransmitEvent; + } + + if (eventRelay != null) { + eventRelay.ForwardEvent(ev); + } + + return true; + } + + private void IrqWorkerMain() + { + DebugPrint( + "Intel {0} Ethernet Driver irq worker thread started.\n", + __arglist(this.cardName) + ); + + while (irqWorkerStop == false) { + irq.WaitForInterrupt(); + uint icr = Read32(Register.ICR); + HandleInterrupts(icr); + irq.AckInterrupt(); + } + + DisableInterrupts(); + DebugPrint( + "Intel {0} Ethernet Driver irq worker thread stopped.\n", + __arglist(this.cardName) + ); + } + + /////////////////////////////////////////////////////////////////////// + // + // Rx buffer operations + // + + internal void PopulateRecvBuffer(PacketFifo*! in ExHeap fromUser) + { + lock (rxRingBuffer) { + while (fromUser->Count > 0) { + rxRingBuffer.LockedPushRecvBuffer(fromUser->Pop()); + } + Write32(Register.RECV_DESC_TAIL, rxRingBuffer.Head); + } + } + + internal void DrainRecvBuffer(PacketFifo*! in ExHeap toUser) + { + lock (rxRingBuffer) { + rxRingBuffer.LockedDrainRecvBuffer(toUser); + } + } + + private void ResetRxRingBuffer() + { + ulong descBase; + uint descBaseLo, descBaseHi; + + rxRingBuffer.Reset(); + descBase = rxRingBuffer.BaseAddress.ToUInt64(); + descBaseLo = (uint)(0xffffffff & descBase); + descBaseHi = (uint) (0xffffffff & (descBase >> 32)); + Write32(Register.RECV_DESC_BASE_LO, ByteOrder.HostToLittleEndian(descBaseLo)); + Write32(Register.RECV_DESC_BASE_HI, ByteOrder.HostToLittleEndian(descBaseHi)); + Write32(Register.RECV_DESC_LENGTH, ByteOrder.HostToLittleEndian(rxRingBuffer.DescLength)); + Write32(Register.RECV_DESC_HEAD, ByteOrder.HostToLittleEndian(rxRingBuffer.Head)); + Write32(Register.RECV_DESC_TAIL, ByteOrder.HostToLittleEndian(rxRingBuffer.Tail)); + } + + private void StartReceiver() + { + ResetRxRingBuffer(); + RegSetBits(Register.RECV_CTRL, RecvCtrlBits.RECV_ENABLE); + + DebugPrint("Receiver Enabled.\n"); + } + + private void StopReceiver() + { + RegClrBits(Register.RECV_CTRL, RecvCtrlBits.RECV_ENABLE); + } + + /////////////////////////////////////////////////////////////////////// + // + // Tx buffer operations + // + + internal void PopulateTsmtBuffer(PacketFifo*! in ExHeap fromUser) + { + DebugStub.Assert(this.ioRunning); + + // since no transmit interrupts are sent, we must check for + // transmission events here + if (txRingBuffer.NewTransmitEvent()) { + NicEventType ev = NicEventType.TransmitEvent; + if (eventRelay != null) { + eventRelay.ForwardEvent(ev); + } + } + + lock (txRingBuffer) { + while (fromUser->Count > 0) { + Packet*! in ExHeap packet = fromUser->Pop(); + txRingBuffer.LockedPushTsmtBuffer(packet); + } + // update hardware tail pointer + // so that hardware knows it has new packets to transmit + Write32(Register.TSMT_DESC_TAIL, txRingBuffer.Head); // sw head is hw tail + } + } + + internal void DrainTsmtBuffer(PacketFifo*! in ExHeap toUser) + { + lock (txRingBuffer) { + txRingBuffer.LockedDrainTsmtBuffer(toUser); + } + } + + private void ResetTxRingBuffer() + { + ulong descBase; + uint descBaseLo, descBaseHi; + + txRingBuffer.Reset(); + descBase = txRingBuffer.BaseAddress.ToUInt64(); + descBaseLo = (uint)(0xffffffff & descBase); + descBaseHi = (uint) (0xffffffff & (descBase >> 32)); + Write32(Register.TSMT_DESC_BASE_LO, ByteOrder.HostToLittleEndian(descBaseLo)); + Write32(Register.TSMT_DESC_BASE_HI, ByteOrder.HostToLittleEndian(descBaseHi)); + Write32(Register.TSMT_DESC_LENGTH, ByteOrder.HostToLittleEndian(txRingBuffer.DescLength)); + Write32(Register.TSMT_DESC_HEAD, ByteOrder.HostToLittleEndian(txRingBuffer.Head)); + Write32(Register.TSMT_DESC_TAIL, ByteOrder.HostToLittleEndian(txRingBuffer.Tail)); + } + + private void StartTransmitter() + { + ResetTxRingBuffer(); + RegSetBits(Register.TSMT_CTRL, TsmtCtrlBits.TSMT_ENABLE); + + DebugPrint("Transmitter Enabled.\n"); + } + + private void StopTransmitter() + { + RegClrBits(Register.TSMT_CTRL, TsmtCtrlBits.TSMT_ENABLE); + } + + /////////////////////////////////////////////////////////////////////// + // + // Driver Details + // + + internal string! DriverName + { + get { return string.Format("Intel {0} Ethernet Driver", this.cardName); } + } + + internal string! DriverVersion + { + get { return "0.1"; } + } + + internal EthernetAddress getMacAddress + { + get { return macAddress; } + } + + /////////////////////////////////////////////////////////////////////// + // + // MII + // + + private uint MiiRead(uint phy, uint register) + { + uint mdic = (((phy & Mdic.PhyMask) << Mdic.PhyRoll) | + ((register & Mdic.RegMask) << Mdic.RegRoll) | + Mdic.MdiRead); + + Write32(Register.MDIC, mdic); + do { + mdic = Read32(Register.MDIC); + DebugStub.Assert((mdic & Mdic.Error) == 0); + } while ((mdic & Mdic.Ready) == 0); + + return mdic & Mdic.DataMask; + } + + private uint MiiWrite(uint phy, uint register, uint value) + { + DebugStub.Assert((value & ~Mdic.DataMask) == 0); + + uint mdic = (((phy & Mdic.PhyMask) << Mdic.PhyRoll) | + ((register & Mdic.RegMask) << Mdic.RegRoll) | + Mdic.MdiWrite); + + mdic |= (uint)value; + + Write32(Register.MDIC, mdic); + do { + mdic = Read32(Register.MDIC); + DebugStub.Assert((mdic & Mdic.Error) == 0); + } while ((mdic & Mdic.Ready) == 0); + + return mdic & Mdic.DataMask; + } + + /////////////////////////////////////////////////////////////////////// + // + // Register accessors / modifiers / utilities + // + + private uint Read32(uint offset) + { + return ioMemory.Read32((int) offset); + } + + private void Write32(uint offset, uint value) + { + ioMemory.Write32((int) offset, value); + } + + private void Write32BitRange(uint offset, + uint new_val, + int hiBit, + int loBit) + { + uint write_val = SetValueBits(Read32(offset), new_val, hiBit, loBit); + Write32(offset, write_val); + } + + private void RegSetBits(int offset, uint bits) + { + ioMemory.Write32(offset, ioMemory.Read32(offset) | bits); + } + + private void RegClrBits(int offset, uint bits) + { + ioMemory.Write32(offset, ioMemory.Read32(offset) & ~bits); + } + + private uint SetValueBits(uint value, + uint bits, + int hiBit, + int loBit) + { + int width = (hiBit - loBit) + 1; + uint mask = (1u << width) - 1u; + bits = (bits & mask); + value &= ~(mask << loBit); + value |= bits << loBit; + return value; + } + + private void SetBit(ref uint value, int bit) + { + value = value | (1u << bit); + } + + private void ClearBit(ref uint value, int bit) + { + value = value & ~(1u << bit); + } + + private static void Delay(int us) + { + long expiry = ProcessService.GetUpTime().Ticks + (us * 10); + while (ProcessService.GetUpTime().Ticks < expiry); + } + + /////////////////////////////////////////////////////////////////////// + // + // Debug Helper Functions + // + + [Conditional("DEBUG_INTEL")] + internal static void DebugPrint(string format, __arglist) + { + DebugStub.Print(format, new ArgIterator(__arglist)); + } + + [Conditional("DEBUG_INTEL")] + internal static void DebugPrint(string format) + { + DebugStub.Print(format); + } + + [Conditional("DEBUG_INTEL")] + internal static void DebugWriteLine(string format, __arglist) + { + DebugStub.WriteLine(format, new ArgIterator(__arglist)); + } + + [Conditional("DEBUG_INTEL")] + internal static void DebugWriteLine(string format) + { + DebugStub.WriteLine(format); + } + + /////////////////////////////////////////////////////////////////////// + // + // Debugging methods + // + [Conditional("DEBUG_INTEL")] + private void DumpBufferDebugRegisters() + { + // TODO, uses Tracing log + DebugPrint("Device Control {0:x8} Device Status {1:x8}\n", + __arglist(Read32(Register.CTRL), + Read32(Register.STATUS))); + + DebugPrint("PCI Status {0:x4}", __arglist(this.pciConfig.Status)); + + DebugPrint("Recv Control {0:x8} Tsmt Control {1:x8}\n", + __arglist(Read32(Register.RECV_CTRL), + Read32(Register.TSMT_CTRL))); + + DebugPrint("RDTR {0:x8} RADV {1:x8}\n", + __arglist(Read32(0x2820), Read32(0x282c))); + + DebugPrint("Total Transmit {0:x8} Total Received {1:x8}\n", + __arglist(Read32(Register.TOTAL_TSMT_PACKETS), + Read32(Register.TOTAL_RECV_PACKETS))); + + DebugPrint("Interrupt Mask {0:x8} Rx Error Count {1:x8}\n", + __arglist(Read32(Register.IMS), + Read32(Register.RX_ERR_COUNT))); + + DebugPrint("Recv Addr High {0:x8} Recv Addr Low {1:x8}\n", + __arglist(Read32(Register.RAH0), + Read32(Register.RAL0))); + + DebugPrint("Recv Desc Head {0:x8} Recv Desc Tail {1:x8}\n", + __arglist(Read32(Register.RECV_DESC_HEAD), + Read32(Register.RECV_DESC_TAIL))); + + DebugPrint("Tsmt Desc Head {0:x8} Tsmt Desc Tail {1:x8}\n", + __arglist(Read32(Register.TSMT_DESC_HEAD), + Read32(Register.TSMT_DESC_TAIL))); + } + + [Conditional("DEBUG_INTEL")] + private void DumpPhy() + { + for (uint i = 0; i < 32; i += 8) { + DebugWriteLine("PHY {0:x4} : {1:x4} {2:x4} {3:x4} {4:x4} {5:x4} {6:x4} {7:x4} {8:x4}", + __arglist(i, + MiiRead(PhyAddress, i + 0), + MiiRead(PhyAddress, i + 1), + MiiRead(PhyAddress, i + 2), + MiiRead(PhyAddress, i + 3), + MiiRead(PhyAddress, i + 4), + MiiRead(PhyAddress, i + 5), + MiiRead(PhyAddress, i + 6), + MiiRead(PhyAddress, i + 7)) + ); + } + } + +#if DEBUG_INTEL + // Occasionally helpful when debugging. + + Thread statsThread = null; + + internal void StartStatistics() + { + DebugStub.Assert(statsThread == null); + statsThread = new Thread(new ThreadStart(this.StatisticsMain)); + statsThread.Start(); + } + + internal void ReportChanges(uint[]! now) + { + DebugWriteLine("Changes."); + for (int i = 0; i < now.Length; i++) { + if (now[i] != 0) { + DebugWriteLine("{0} [0x40{1:x1}] -> {2}", + __arglist(i, i * 4, now[i])); + } + } + + rxRingBuffer.Dump(); + DebugWriteLine("Rx head {0:x8} tail {1:x8}", + __arglist(Read32(Register.RECV_DESC_HEAD), + Read32(Register.RECV_DESC_TAIL))); + DebugWriteLine("ICS = {0:x8} ICR = {1:x8}", + __arglist(Read32(Register.ICS), + Read32(Register.ICR))); + } + + internal void StatisticsMain() + { + uint []! counters1 = new uint[64]; + + for (;;) { + Thread.Sleep(TimeSpan.FromSeconds(10)); + GetStatistics(counters1); + ReportChanges(counters1); + } + } + + internal void GetStatistics(uint []! counters) + { + for (uint i = 0; i < counters.Length; i++) { + counters[i] = Read32(0x4000 + i * 4); + } + } +#endif + } +} diff --git a/base/Drivers/Network/Intel/IntelConstants.cs b/base/Drivers/Network/Intel/IntelConstants.cs new file mode 100644 index 0000000..c2512ec --- /dev/null +++ b/base/Drivers/Network/Intel/IntelConstants.cs @@ -0,0 +1,384 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +namespace Microsoft.Singularity.Drivers.Network.Intel +{ + // + // Memory Mapped Register offsets + // + internal struct Register + { + // control / status + internal const uint CTRL = 0x0000; + internal const uint STATUS = 0x0008; + + // EEPROM access + internal const uint EECD = 0x0010; + internal const uint EERD = 0x0014; + internal const uint FLA = 0x001c; + + // Device controls + internal const uint CTRL_EXT = 0x0018; + internal const uint MDIC = 0x0020; + internal const uint FCAL = 0x0028; + internal const uint FCAH = 0x002C; + internal const uint FCT = 0x0030; + internal const uint VET = 0x0038; + internal const uint FCTTV = 0x0170; + internal const uint TXCW = 0x0178; + internal const uint RXCW = 0x0180; + internal const uint LED_CTRL = 0x0e00; + + // DMA + internal const uint PBA = 0x1000; + + // Interrupts + internal const uint ICR = 0x00c0; + internal const uint ITR = 0x00c4; + internal const uint ICS = 0x00c8; + internal const uint IMS = 0x00d0; + internal const uint IMC = 0x00d8; + + // Receive + internal const uint RECV_CTRL = 0x0100; + + internal const uint FLOW_CTRL_RECV_LO = 0x2160; + internal const uint FLOW_CTRL_RECV_HI = 0x2168; + internal const uint RECV_DESC_BASE_LO = 0x2800; + internal const uint RECV_DESC_BASE_HI = 0x2804; + internal const uint RECV_DESC_LENGTH = 0x2808; + internal const uint RECV_DESC_HEAD = 0x2810; + internal const uint RECV_DESC_TAIL = 0x2818; + internal const uint RECV_DELAY_TIMER = 0x2820; + internal const uint RECV_INT_ABS_TIMER = 0x282c; + internal const uint RECV_SML_PKT_INT = 0x2c00; + + internal const uint RECV_CHECKSUM = 0x5000; + + internal const uint MTA_START = 0x5200; + + internal const uint RAL0 = 0x5400; + internal const uint RAH0 = 0x5404; + + // Transmit + internal const uint TSMT_CTRL = 0x0400; + internal const uint TSMT_IPG = 0x0410; + internal const uint TSMT_IFS_THROTTLE = 0x0458; + internal const uint TSMT_DESC_BASE_LO = 0x3800; + internal const uint TSMT_DESC_BASE_HI = 0x3804; + internal const uint TSMT_DESC_LENGTH = 0x3808; + internal const uint TSMT_DESC_HEAD = 0x3810; + internal const uint TSMT_DESC_TAIL = 0x3818; + internal const uint TSMT_INT_DELAY = 0x3820; + + // stats + internal const uint RX_ERR_COUNT = 0x400c; + internal const uint TOTAL_RECV_PACKETS = 0x40d0; + internal const uint TOTAL_TSMT_PACKETS = 0x40d4; + } + + // Bits for control reg + internal struct CtrlBits + { + internal const uint FD = 1u << 0; + internal const uint LRST = 1u << 3; + internal const uint ASDE = 1u << 5; + internal const uint SLU = 1u << 6; + internal const uint ILOS = 1u << 7; + internal const uint FRCSPD = 1u << 11; + internal const uint FRCDPLX = 1u << 12; + internal const uint SDP0_DATA = 1u << 18; + internal const uint SDP1_DATA = 1u << 19; + internal const uint ADVD3WUC = 1u << 20; + internal const uint EN_PHYS_PWR_MGMT = 1u << 21; + internal const uint SDP0_IODIR = 1u << 22; + internal const uint SDP1_IODIR = 1u << 23; + internal const uint RST = 1u << 26; + internal const uint RFCE = 1u << 27; + internal const uint TFCE = 1u << 28; + internal const uint VME = 1u << 30; + internal const uint PHY_RST = 1u << 31; + + internal const uint SPEED_10Mb = 0u; + internal const uint SPEED_100Mb = 1u << 8; + internal const uint SPEED_1000Mb = 2u << 8; + internal const uint SPEED = 3u << 8; + } + + // Bits for setting the interrupt mask + + internal struct InterruptMasks + { + internal const uint TXDW = 1u << 0; + internal const uint TXQE = 1u << 1; + internal const uint LSC = 1u << 2; + internal const uint RXSEQ = 1u << 3; + internal const uint RXDMT0 = 1u << 4; + internal const uint RXO = 1u << 6; + internal const uint RXT0 = 1u << 7; + internal const uint MDAC = 1u << 9; + internal const uint RXCFG = 1u << 10; + internal const uint PHYINT = 1u << 12; + internal const uint TXD_LOW = 1u << 15; + internal const uint SRPD = 1u << 16; + } + + // Bits for extended control reg + internal struct CtrlExtBits + { + internal const uint PHY_INTERUPT = 1u << 5; + internal const uint ASD_CHECK = 1u << 12; + internal const uint EE_RST = 1u << 13; + internal const uint SPD_BYPASS = 1u << 15; + internal const uint RO_DIS = 1u << 17; + internal const uint VREG_POWER_DOWN = 1u << 21; + + internal const int LINK_MODE_LO_BIT = 22u; + internal const int LINK_MODE_HI_BIT = 23u; + internal const uint LINK_MODE_PHYS = 0u; + internal const uint LINK_MODE_SERDES = 2u; + internal const uint LINK_MODE_EXT_TBI = 3u; + + } + + internal struct RecvCtrlBits + { + internal const uint RECV_ENABLE = 1u << 1; + internal const uint STORE_BAD_PKTS = 1u << 2; + internal const uint UNICAST_PROMISCUOUS = 1u << 3; + internal const uint MULTICAST_PROMISCUOUS = 1u << 4; + internal const uint LONG_PKT_ENABLE = 1u << 5; + internal const uint BROADCAST_ACCEPT = 1u << 15; + internal const uint VLAN_FILTER_ENABLE = 1u << 18; + internal const uint CANONICAL_FORM_ENABLE = 1u << 19; + internal const uint DISCARD_PAUSE_FRAMES = 1u << 22; + internal const uint PASS_MAC_CTRL_FRAMES = 1u << 23; + internal const uint STRIP_CRC = 1u << 26; + + internal const uint LOOPBACK_MODE_DISABLE = 0u << 6; + internal const uint LOOPBACK_MODE_ENABLE = 3u << 6; + + internal const uint RECV_DESC_THRESHOLD_HALF = 0u << 8; + internal const uint RECV_DESC_THRESHOLD_QUARTER= 1u << 8; + internal const uint RECV_DESC_THRESHOLD_EIGHTH = 2u << 8; + + internal const int MULTICAST_OFFSET_LO_BIT = 12u; + internal const int MULTICAST_OFFSET_HI_BIT = 13u; + internal const uint MULTICAST_OFFSET_47_36 = 0u; + internal const uint MULTICAST_OFFSET_46_35 = 1u; + internal const uint MULTICAST_OFFSET_45_34 = 2u; + internal const uint MULTICAST_OFFSET_43_32 = 3u; + + internal const uint BUFFER_SIZE_MASK = 0x02030000; + internal const uint BUFFER_SIZE_256B = 0x00030000; + internal const uint BUFFER_SIZE_512B = 0x00020000; + internal const uint BUFFER_SIZE_1KB = 0x00010000; + internal const uint BUFFER_SIZE_2KB = 0x00000000; + internal const uint BUFFER_SIZE_4KB = 0x02030000; + internal const uint BUFFER_SIZE_8KB = 0x02020000; + internal const uint BUFFER_SIZE_16KB = 0x02010000; + } + + internal struct TsmtCtrlBits + { + internal const uint TSMT_ENABLE = 1u << 1; + internal const uint PAD_SHORT_PACKETS = 1u << 3; + internal const uint SOFTWARE_XOFF_TRANS = 1u << 22; + internal const uint RE_TSMT_LATE_COLL = 1u << 24; + internal const uint NO_RE_TSMT_ON_UNDERRUN = 1u << 25; + + internal const uint COLL_THRESHOLD_DEFAULT = 0x0fu << 4; + internal const uint COLL_DISTANCE_DEFAULT = 0x40u << 12; + } + + internal struct TsmtIpg + { + internal const uint DEFAULT_IPG_T = 10u << 0; + internal const uint DEFAULT_IPG_R1 = 10u << 10; + internal const uint DEFAULT_IPG_R2 = 10u << 20; + + internal const uint DEFAULT_IPG = (DEFAULT_IPG_T | + DEFAULT_IPG_R1 | + DEFAULT_IPG_R2); + } + + internal struct RecvChecksumBits + { + internal const uint IP_CHECKSUM_ENABLE = 1u << 8; + internal const uint TCP_CHECKSUM_ENABLE = 1u << 9; + internal const uint IP6_CHECKSUM_ENABLE = 1u << 10; + } + + // + // The defaults for the rx interrupt delay timers + // + internal struct RxDelayTimers + { + internal const uint RECV_DELAY_TIMER = 100u; // ~100 us + internal const uint RECV_ABSOLUTE_TIMER = 1000u; // ~1000 us + } + + // + // Recieve address High bits + // + internal struct RahRegister + { + internal const uint ADDRESS_VALID = 1u << 31; + } + + // + // MultiCast Table Array + // + internal struct MtaRegister + { + internal const uint MTA_LENGTH = 128; + } + + // + // bits for EEPROM Read register + // + internal class EerdRegister + { + uint done; + int shift; + + internal EerdRegister (ushort devIdArg) + { + if (devIdArg == 0x1019 || // 82547 EI/GI + devIdArg == 0x1013 || // 82541 EI + devIdArg == 0x1018 || // 82541 EI Mobile + devIdArg == 0x1076 || // 82541 GI/PI + devIdArg == 0x1077 || // 82541 GI Mobile + devIdArg == 0x1078 || // 82541 ER Copper + devIdArg == 0x107c) { // 82541 PI + + done = 0x02; + shift = 2; + } + else { + done = 0x10; + shift = 8; + } + } + + internal uint Done { get { return done; } } + internal int AddressShift { get { return shift; } } + + internal const uint Start = 0x01; + internal const int DataShift = 16; + } + + internal struct Mdic + { + internal const uint DataMask = 0x0000ffff; + internal const uint MdiWrite = 0x04000000; + internal const uint MdiRead = 0x08000000; + internal const uint Ready = 0x10000000; + internal const uint InterruptEnable = 0x20000000; + internal const uint Error = 0x40000000; + internal const uint PowerMask = 0xfffff7ff; + + internal const uint RegMask = 0x1f; + internal const int RegRoll = 16; + internal const uint PhyMask = 0x1f; + internal const int PhyRoll = 21; + } + + // + // Bit fields common to both rx and tx descriptors (relative to ulong + // control words + // + internal struct Descriptor + { + internal const ulong DESCRIPTOR_DONE = 0x100000000ul; + } + + // + // Bit fields common to both rx and tx descriptors (relative to uint + // stat part of control words + // + internal struct DescriptorStat + { + internal const ulong DESCRIPTOR_DONE = 0x1u; + internal const ulong DESCRIPTOR_EXCESS_COLLISIONS = 0x2u; + internal const ulong DESCRIPTOR_LATE_COLLISION = 0x4u; + internal const ulong DESCRIPTOR_TRANSMIT_OVERRUN = 0x8u; + } + + // + // The parts within a recieve descriptor + // + internal struct RxDescriptor + { + internal const ulong LENGTH_MASK = 0xfffful; + internal const int LENGTH_SHIFT = 0u; + internal const ulong ERR_STAT_MASK = 0xffff00000000ul; + internal const int ERR_STAT_SHIFT = 32u; + } + + // + // Fields within the Errors and Stats parts of a recieve descriptor + // + internal struct RxErrStatFields + { + // status + internal const uint STATUS_MASK = 0x00ff; + + internal const uint DESCRIPTOR_DONE = 1u << 0; + internal const uint END_OF_PACKET = 1u << 1; + internal const uint IGNORE_CHECKSUM = 1u << 2; + internal const uint VLAN_PACKET = 1u << 3; + internal const uint TCP_CHECKSUM_CALC = 1u << 5; + internal const uint IP_CHECKSUM_CALC = 1u << 6; + internal const uint PASSED_IN_EXACT = 1u << 7; + + // errors + internal const uint ERR_MASK = 0xff00; + + internal const uint CRC_ERROR = 1u << 8; + internal const uint SYMBOL_ERROR = 1u << 9; + internal const uint SEQUENCE_ERROR = 1u << 10; + internal const uint CARRIER_EXT_ERROR = 1u << 12; + internal const uint TCP_CHECKSUM_ERROR = 1u << 13; + internal const uint IP_CHECKSUM_ERROR = 1u << 14; + internal const uint RX_DATA_ERROR = 1u << 15; + } + + // + // The parts within a recieve descriptor + // + internal struct TxDescriptor + { + internal const ulong LENGTH_MASK = 0xfffful; + internal const int LENGTH_SHIFT = 0u; + internal const ulong ERR_STAT_MASK = 0xf00000000ul; + internal const int ERR_STAT_SHIFT = 32u; + } + + internal struct TxCmdFields + { + // status + internal const uint END_OF_PACKET = 1u << 24; + internal const uint INSERT_FCS = 1u << 25; + internal const uint INSERT_CHECKSUM = 1u << 26; + internal const uint REPORT_STATUS = 1u << 27; + internal const uint REPORT_PACKET_SENT = 1u << 28; + internal const uint EXTENTION_MODE = 1u << 29; + internal const uint VLAN_PACKET_ENABLE = 1u << 30; + internal const uint INT_DELAY_ENABLE = 1u << 31; + } + + + internal struct TxStatErrFields + { + // status + internal const uint DESCRIPTOR_DONE = 1u << 0; + internal const uint LATE_COLLISION = 1u << 1; + internal const uint EXCESS_COLLISIONS = 1u << 2; + internal const uint TRANSMIT_UNDERRUN = 1u << 3; + } +} diff --git a/base/Drivers/Network/Intel/IntelController.sg b/base/Drivers/Network/Intel/IntelController.sg new file mode 100644 index 0000000..613fe92 --- /dev/null +++ b/base/Drivers/Network/Intel/IntelController.sg @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Io.Net; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Extending; +using Microsoft.Singularity.V1.Services; + +using System; +using System.Threading; + +namespace Microsoft.Singularity.Drivers.Network.Intel +{ + internal class IntelController + { + public static int DriverMain(IntelResources! resources) + { + ExtensionContract.Exp! ep = ((!)resources.ec).Acquire(); + ServiceProviderContract.Exp! sp = ((!)resources.nicsp).Acquire(); + + Intel! device = new Intel(resources); + + device.Initialize(); + + ep.SendSuccess(); + + try { + for (bool run = true; run;) { + switch receive { + case sp.Connect(ServiceContract.Exp:Start! exp): + NicDeviceContract.Exp nd = exp as NicDeviceContract.Exp; + if (nd != null) { + Tracing.Log(Tracing.Debug, "Connect success."); + sp.SendAckConnect(); + IntelDeviceChannel.CreateThread(device, nd); + } + else { + Tracing.Log(Tracing.Error, "Connect failed."); + sp.SendNackConnect(exp); + } + break; + + case sp.ChannelClosed(): + device.Shutdown(); + run = false; + break; + + case ep.Shutdown(): + device.Shutdown(); + ep.SendAckShutdown(); + break; + + case ep.ChannelClosed(): + device.Shutdown(); + run = false; + break; + + case unsatisfiable: + DebugStub.Break(); + break; + } + } + } + finally { + delete ep; + delete sp; + } + return 0; + } + } +} diff --git a/base/Drivers/Network/Intel/IntelDeviceChannel.sg b/base/Drivers/Network/Intel/IntelDeviceChannel.sg new file mode 100644 index 0000000..f119f93 --- /dev/null +++ b/base/Drivers/Network/Intel/IntelDeviceChannel.sg @@ -0,0 +1,184 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: IntelDeviceChannel.sg +// +#define DEBUG_INTEL + +using Microsoft.SingSharp; + +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Io.Net; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.V1.Services; + +using System; +using System.Threading; + +namespace Microsoft.Singularity.Drivers.Network.Intel +{ + internal class IntelDeviceChannel + { + Intel! device; + TRef channel; + + private IntelDeviceChannel( + Intel! theDevice, + [Claims] NicDeviceContract.Exp:Start! channel + ) + ensures this.channel != null; + { + this.device = theDevice; + this.channel = new TRef(channel); + base(); + } + + private bool IoRunningMessageLoop(NicDeviceContract.Exp! ep) + { + Intel.DebugPrint("Intel 8254x entered {0} state", + __arglist(ep.CurrentState())); + + device.StartIo(); + + for (;;) { + switch receive { + case ep.GiveTxPacketsToDevice(txFifo): + device.PopulateTsmtBuffer(txFifo); + device.DrainTsmtBuffer(txFifo); + ep.SendTakeTxPacketsFromDevice(txFifo); + break; + + case ep.GiveRxPacketsToDevice(rxFifo): + device.PopulateRecvBuffer(rxFifo); + device.DrainRecvBuffer(rxFifo); + ep.SendTakeRxPacketsFromDevice(rxFifo); + break; + + case ep.StopIO(): + device.StopIo(); + return true; + + case ep.ChannelClosed(): + device.StopIo(); + return false; + } + } + return false; + } + + private bool IoConfigureMessagePump(NicDeviceContract.Exp! ep) + { + Intel.DebugPrint("Intel 8254x entered {0} state", + __arglist(ep.CurrentState())); + + for (;;) { + switch receive { + case ep.RegisterForEvents(eventExp): + ep.SendSuccess(); + eventExp.SendSuccess(); + device.SetEventRelay(new IntelEventRelay(eventExp)); + break; + + case ep.SetChecksumProperties(checksum): + if ((checksum & ~Intel.ChecksumSupport) == 0) { + // TODO: Running with checksumming on anyway... + // but should be responsive to this request. + ep.SendSuccess(); + } + else { + ep.SendUnsupportedChecksumProperties(); + } + break; + + case ep.StartIO(): + ep.SendAckStartIO(); + return IoRunningMessageLoop(ep); + + case ep.ChannelClosed(): + return false; + + case unsatisfiable: + DebugStub.Break(); + break; + } + } + + return false; + } + + private void ReadyMessagePump() + requires this.channel != null; + { + NicDeviceContract.Exp! ep = channel.Acquire(); + this.channel = null; + + assert ep.InState(NicDeviceContract.Start.Value); + ep.SendSuccess(); + assert ep.InState(NicDeviceContract.READY.Value); + + try { + for (bool run = true; run == true;) { + switch receive { + case ep.GetDeviceProperties(dp): + GetDeviceProperties(dp); + ep.SendDeviceProperties(dp); + break; + + case ep.ConfigureIO(): + ep.SendAckConfigureIO(); + run = IoConfigureMessagePump(ep); + break; + + case ep.ChannelClosed(): + run = false; + break; + + case unsatisfiable: + DebugStub.Break(); + run = false; + break; + } + } + } + finally { + delete ep; + } + } + + private void GetDeviceProperties(NicDeviceProperties*! in ExHeap dp) + { + expose (dp) { + delete dp->DriverName; + dp->DriverName = Bitter.FromString2(device.DriverName); + + delete dp->DriverVersion; + dp->DriverVersion = Bitter.FromString2(device.DriverVersion); + + dp->MacType = MacType.Ethernet; + delete dp->MacAddress; + dp->MacAddress = + Bitter.FromByteArray(device.getMacAddress.GetAddressBytes()); + + dp->ChecksumSupport = Intel.ChecksumSupport; + dp->MtuBytes = Intel.MtuBytes; + dp->MaxRxFragmentsPerPacket = Intel.MaxRxFragmentsPerPacket; + dp->MaxTxFragmentsPerPacket = Intel.MaxTxFragmentsPerPacket; + dp->MaxRxPacketsInDevice = Intel.MaxRxPackets; + dp->MaxTxPacketsInDevice = Intel.MaxTxPackets; + } + } + + internal static bool + CreateThread(Intel! device, [Claims] NicDeviceContract.Exp:Start! ep) + { + IntelDeviceChannel idc = new IntelDeviceChannel(device, ep); + Thread thread = new Thread(new ThreadStart(idc.ReadyMessagePump)); + thread.Start(); + return true; + } + } +} diff --git a/base/Drivers/Network/Intel/IntelEventRelay.sg b/base/Drivers/Network/Intel/IntelEventRelay.sg new file mode 100644 index 0000000..7c3866c --- /dev/null +++ b/base/Drivers/Network/Intel/IntelEventRelay.sg @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: Right now this interface waits for acks from the receiver. +// This adds an unnecessary context switch. At some future point +// we may be able to have something more akin to a one-way single +// buffer-slot notification system, but for now we ack. + +using Microsoft.SingSharp; +using Microsoft.Singularity; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Extending; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Io.Net; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.V1.Services; + +using System; +using System.Threading; + +namespace Microsoft.Singularity.Drivers.Network.Intel +{ + internal class IntelEventRelay + { + TRef! channel; + bool channelClosed; + + internal IntelEventRelay([Claims] + NicDeviceEventContract.Exp:READY! ep) + { + channel = new TRef(ep); + channelClosed = false; + } + + internal void ForwardEvent(NicEventType theEvent) + { + if (channelClosed) { + return; + } + + NicDeviceEventContract.Exp! exp = channel.Acquire(); + exp.SendNicDeviceEvent(theEvent); + try { + exp.RecvAckNicDeviceEvent(); + } + catch (ChannelClosedException) { + channelClosed = true; + } + finally { + channel.Release(exp); + } + } + } +} diff --git a/base/Drivers/Network/Intel/IntelResources.sg b/base/Drivers/Network/Intel/IntelResources.sg new file mode 100644 index 0000000..36c5d4e --- /dev/null +++ b/base/Drivers/Network/Intel/IntelResources.sg @@ -0,0 +1,205 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: IntelResources.cs +// +// #define TYAN_MOTHERBOARD_HACK +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Extending; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Io.Net; +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.Drivers; + + +[assembly: Transform(typeof(DriverResourceTransform))] +namespace Microsoft.Singularity.Drivers.Network.Intel +{ + internal enum CardType : int + { + I82541PI, + I82545GM + } + + // Common interface for all intel resource classes + internal interface IntelResources + { + IoMemoryRange! imr { + get; + } + + IoIrqRange! irq { + get; + } + + string! CardName { + get; + } + + CardType CardType { + get; + } + + TRef! ec { + get; + } + + TRef! nicsp { + get; + } + } + + // Intel Pro/1000 GT - 82541 PI + [DriverCategory] + [Signature("pci/ven_8086&dev_107c&cc_0200")] + internal class Intel82541piResources : DriverCategoryDeclaration, IntelResources + { +#if !TYAN_MOTHERBOARD_HACK + [IoMemoryRange(0, Default = 0xfebe0000, Length = 0x20000)] + internal IoMemoryRange imrField; + + [IoMemoryRange(1, Default = 0xfebe0000, Length = 0x20000)] + internal IoMemoryRange flashField; // this is unused, but we must declare itres + + [IoPortRange(2, Default = 0xec00, Length = 0x40)] + internal IoPortRange ioPortCsrField; + + [IoIrqRange(6, Default = 0x05, Shared = true)] + internal IoIrqRange irqField; +#else + [IoMemoryRange(0, Default = 0xb0320000, Length = 0x20000)] + internal IoMemoryRange imrField; + + [IoMemoryRange(1, Default = 0xb0300000, Length = 0x20000)] + internal IoMemoryRange flashField; // this is unused, but we must declare itres + + [IoPortRange(2, Default = 0x3000, Length = 0x40)] + internal IoPortRange ioPortCsrField; + + [IoIrqRange(6, Default = 0x05, Shared = true)] + internal IoIrqRange irqField; +#endif + [ExtensionEndpoint] + internal TRef ecField; + + [ServiceEndpoint(typeof(NicDeviceContract))] + internal TRef nicspField; + + // proerties + public IoMemoryRange! imr { + get { + return (!) imrField; + } + } + + public IoIrqRange! irq { + get{ + return (!) irqField; + } + } + + public TRef! ec { + get{ + return (!) ecField; + } + } + + public TRef! nicsp { + get{ + return (!) nicspField; + } + } + + public string! CardName + { + get { + return "82541 PI"; + } + } + + public CardType CardType + { + get { + return CardType.I82541PI; + } + } + + internal int DriverMain(string instance) + { + return IntelController.DriverMain(this); + } + } + + // Intel Pro/1000 GT 82545 GM + [DriverCategory] + [Signature("pci/ven_8086&dev_1026&cc_0200")] + internal class Intel82545gmResources : DriverCategoryDeclaration, IntelResources + { + [IoMemoryRange(0, Default = 0xfebe0000, Length = 0x20000)] + internal readonly IoMemoryRange imrField; + + [IoMemoryRange(1, Default = 0xfebe0000, Length = 0x10000)] + internal readonly IoMemoryRange flashField; // this is unused, but we must declare itres + + [IoPortRange(2, Default = 0xec00, Length = 0x40)] + internal readonly IoPortRange ioPortCsrField; + + [IoIrqRange(6, Default = 0x05, Shared = true)] + internal readonly IoIrqRange irqField; + + [ExtensionEndpoint] + internal TRef ecField; + + [ServiceEndpoint(typeof(NicDeviceContract))] + internal TRef nicspField; + + // proerties + public IoMemoryRange! imr { + get { + return (!) imrField; + } + } + + public IoIrqRange! irq { + get{ + return (!) irqField; + } + } + + public TRef! ec { + get{ + return (!) ecField; + } + } + + public TRef! nicsp { + get{ + return (!) nicspField; + } + } + + public string! CardName + { + get { + return "82545 GM"; + } + } + + public CardType CardType + { + get { + return CardType.I82545GM; + } + } + + internal int DriverMain(string instance) + { + return IntelController.DriverMain(this); + } + } +} diff --git a/base/Drivers/Network/Intel/IntelRingBuffer.sg b/base/Drivers/Network/Intel/IntelRingBuffer.sg new file mode 100644 index 0000000..e18eccc --- /dev/null +++ b/base/Drivers/Network/Intel/IntelRingBuffer.sg @@ -0,0 +1,237 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: IntelRingBuffer.cs +// +// Notes: +// +// The underlying implementation of the ring buffer code. IntelRxRingBuffer or +// IntelTxRingBuffer should be used for actual implementation of these buffers. + +using Microsoft.Contracts; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.V1.Services; +using System; + +namespace Microsoft.Singularity.Drivers.Network.Intel +{ + internal class IntelRingBuffer + { + /// + /// Structure used to hold Virtual-To-Physical addresses for + /// data blocks passed into an NvMacRingBuffer instance. + /// + internal struct MapEntry { + internal UIntPtr VirtualAddress; + internal UIntPtr PhysicalAddress; + + internal void Initialize(UIntPtr va, UIntPtr pa) + { + this.VirtualAddress = va; + this.PhysicalAddress = pa; + } + } + + private const uint DescriptorBytes = 16; + private const uint AlignmentBytes = 64; // cache line alignment + + private IoMemory! region; + private MapEntry []! mapEntries; + uint capacity; + uint head; + uint tail; + uint count; + + internal IntelRingBuffer(uint capacity) + requires capacity > 0 && IsPowerOf2(capacity); + { + UIntPtr length = new UIntPtr(capacity * DescriptorBytes); + IoMemory! region = (!)IoMemory.AllocatePhysical(length, + AlignmentBytes); + + PlatformService.SetCacheAttributes(region.VirtualAddress, + (UIntPtr)region.Length, + false, + false); + + this.region = region; + this.mapEntries = new MapEntry [capacity]; + this.capacity = capacity; + + this.head = 0; + this.tail = 0; + this.count = 0; + + // Clear out ring buffer + for (int i = 0; i < region.Length; i += 4) { + region.Write32(i, 0); + } + } + + internal void Reset() + requires this.Count == 0; + { + this.head = 0; + this.tail = 0; + + for (int i = 0; i < this.region.Length; i += 4) { + this.region.Write32(i, 0); + } + } + + internal void Push(UIntPtr fragmentVirtualAddress, ulong controlBits) + requires this.Count < (this.Capacity - 1); // must keep one slot empty to prevent + // head==tail which would signal an + // empty buffer. + ensures this.Count == old(this.Count) + 1; + { + UIntPtr pa = GetPhysicalAddress(fragmentVirtualAddress); + + this.mapEntries[this.head].Initialize(fragmentVirtualAddress, pa); + WriteDescriptor(this.head, pa, controlBits); + + this.head = (this.head + 1) & (this.Capacity - 1); + this.count++; + } + + internal void Pop() + requires this.Count > 0; + ensures this.Count == old(this.count) - 1; + { + this.tail = (this.tail + 1) & (this.Capacity - 1); + this.count--; + } + + // Returns true if hardware is done with this descriptor. + internal bool Peek() { + if (this.count > 0) { + uint cb = this.region.Read32((int) (this.tail * DescriptorBytes + 12)); + return (cb & DescriptorStat.DESCRIPTOR_DONE) != 0; + } else { + return false; + } + } + + // Returns true if hardware is done with this descriptor. + internal bool Peek(out UIntPtr fragmentVirtualAddress, + out ulong controlBits) + { + if (this.count > 0) { + UIntPtr pa; + ReadDescriptor(this.tail, out pa, out controlBits); + DebugStub.Assert(pa == this.mapEntries[tail].PhysicalAddress); + fragmentVirtualAddress = this.mapEntries[tail].VirtualAddress; + return ((controlBits & Descriptor.DESCRIPTOR_DONE) != 0); + } else { + fragmentVirtualAddress = 0; + controlBits = 0; + return false; + } + } + + private void WriteDescriptor(uint index, + UIntPtr physicalAddr, + ulong controlBits) + requires (index >= 0 && index <= this.Capacity); + { + ulong addrPtr = physicalAddr.ToUInt64(); + int descriptorOffset = (int)(index * DescriptorBytes); + + this.region.Write64(descriptorOffset, addrPtr); + this.region.Write64(descriptorOffset + 8, controlBits); + +#if false + PlatformService.CleanAndInvalidateDCache( + ((UIntPtr)this.region.PhysicalAddress.Value) + descriptorOffset, + DescriptorBytes); +#endif // false + } + + private void ReadDescriptor(uint index, + out UIntPtr physicalAddr, + out ulong controlBits) + { + int descriptorOffset = (int)(index * DescriptorBytes); + +#if false + PlatformService.InvalidateDCache( + ((UIntPtr)this.region.PhysicalAddress.Value) + descriptorOffset, + DescriptorBytes); +#endif // false + + ulong addrPtr = this.region.Read64(descriptorOffset); + controlBits = this.region.Read64(descriptorOffset + 8); + + physicalAddr = new UIntPtr(addrPtr); + } + + internal static UIntPtr GetPhysicalAddress(UIntPtr va) + { + UIntPtr pa; // Physical address + UIntPtr paLeft; // Bytes remaining on physical page + if (!DeviceService.GetDmaPhysicalAddress(va, out pa, out paLeft) || + pa == UIntPtr.Zero || + paLeft < Intel.IEEE8023FrameBytes) { + throw new ApplicationException("Bad DMA pointer"); + } + return pa; + } + + internal void Dump(string! preamble, uint count) + { + if (count > this.capacity) { + count = this.capacity; + } + Intel.DebugWriteLine("Head {0} Tail {1}\n", + __arglist(this.Head, this.Tail)); + for (uint i = 0; i < count; i++) { + ulong address = this.region.Read64((int)(i * 16)); + ulong fields = this.region.Read64((int)(i * 16 + 8)); + Intel.DebugWriteLine("{0}: [{1}] Address {2:x16} Sp={3:x4} Err={4:x1} Sta={5:x2} Checksum {6:x4} Length {7:x4}", + __arglist(preamble, i, address, + (fields >> 48) & 0xffff, + (fields >> 40) & 0xff, + (fields >> 32) & 0xff, + (fields >> 16) & 0xffff, + fields & 0xffff)); + } + } + + internal UIntPtr BaseAddress + { + get { return this.region.PhysicalAddress.Value; } + } + + [Pure] + internal uint Capacity { get { return this.capacity; } } + + // Note, saves one descriptor so that ring never gets completely full, which would + // lead to head == tail, which the hardware takes as meaning the ring is empty. + [Pure] + internal uint Free { get { return (this.capacity - 1) - this.count; } } + + [Pure] + internal bool IsFull { get { return (this.Free == 0); } } + + [Pure] + internal uint Count { get { return this.count; } } + + [Pure] + internal uint Tail { get { return this.tail; } } + + [Pure] + internal uint Head { get { return this.head; } } + + [Pure] + internal uint DescLength { get { return (this.Capacity * DescriptorBytes); } } + + [Pure] + internal static bool IsPowerOf2(uint n) + { + return (n > 0) && ((n & (n - 1)) == 0); + } + } +} diff --git a/base/Drivers/Network/Intel/IntelRxRingBuffer.sg b/base/Drivers/Network/Intel/IntelRxRingBuffer.sg new file mode 100644 index 0000000..380df1f --- /dev/null +++ b/base/Drivers/Network/Intel/IntelRxRingBuffer.sg @@ -0,0 +1,189 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: IntelRxRingBuffer.cs +// +// Notes: +// +// Adaptor class for the Rx ring buffer. +// + +using Microsoft.Contracts; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Io.Net; +using Microsoft.Singularity.V1.Services; +using Microsoft.SingSharp; + +using System; + +namespace Microsoft.Singularity.Drivers.Network.Intel +{ + internal class IntelRxRingBuffer + { + IntelRingBuffer! rxRingBuffer; + ExRef! rxPacketsInDevice; + + internal IntelRxRingBuffer(uint capacity) + requires capacity > 0 && IntelRingBuffer.IsPowerOf2(capacity); + { + this.rxRingBuffer = new IntelRingBuffer(capacity); + this.rxPacketsInDevice = new ExRef( + new [ExHeap] PacketFifo((int) capacity), + false + ); + } + + internal void Reset() + { + rxRingBuffer.Reset(); + } + + /////////////////////////////////////////////////////////////////////// + // + // Buffer Operations, should only be called when a lock is held + // this ring buffer. + // + private void LockedPushRecvBuffer(UIntPtr packetVirtAddr, int length) + { + PlatformService.InvalidateDCache(packetVirtAddr, (UIntPtr)length); + + ulong controlBits = (ulong) ByteOrder.HostToLittleEndian(length); + + rxRingBuffer.Push(packetVirtAddr, controlBits); + } + + static int pass = 0; + + internal void LockedPushRecvBuffer([Claims]Packet*! in ExHeap packet) + { + DebugStub.Assert(packet->FragmentCount == 1); + DebugStub.Assert(!rxRingBuffer.IsFull); + + UIntPtr address; + int length; + + packet->GetFragmentRange(0, out address, out length); + + this.LockedPushRecvBuffer(address, length); + PacketFifo*! in ExHeap liveFifo = (!)this.rxPacketsInDevice.Acquire(); + liveFifo->Push(packet); + rxPacketsInDevice.Release(liveFifo); + } + + internal void Dump() + { + rxRingBuffer.Dump("rx", 8); + } + + private FromDeviceFlags GetRecvPktFlags(uint stat_err_flags) + { + FromDeviceFlags fromDevFlags = 0; + + if (((RxErrStatFields.CRC_ERROR | RxErrStatFields.SYMBOL_ERROR | + RxErrStatFields.SEQUENCE_ERROR | RxErrStatFields.CARRIER_EXT_ERROR | + RxErrStatFields.RX_DATA_ERROR) & stat_err_flags) != 0) { + Intel.DebugPrint("Packet Rsv Error\n"); + fromDevFlags |= FromDeviceFlags.ReceiveError; + } else { + fromDevFlags |= FromDeviceFlags.ReceiveSuccess; + } + + if (((stat_err_flags & RxErrStatFields.IGNORE_CHECKSUM) == 0) && + ((stat_err_flags & RxErrStatFields.IP_CHECKSUM_CALC) != 0)) { + if ((stat_err_flags & RxErrStatFields.IP_CHECKSUM_ERROR) != 0) { + Intel.DebugPrint("Bad Ip Checksum\n"); + fromDevFlags |= FromDeviceFlags.BadIp4Checksum; + } else { + // Good IP checksum flag??? + } + } + + if (((stat_err_flags & RxErrStatFields.IGNORE_CHECKSUM) == 0) && + ((stat_err_flags & RxErrStatFields.TCP_CHECKSUM_CALC)!= 0)) { + if ((stat_err_flags & RxErrStatFields.TCP_CHECKSUM_ERROR) != 0) { + fromDevFlags |= (FromDeviceFlags.BadTcp4Checksum | + FromDeviceFlags.BadUdp4Checksum); + // don't know if UDP or TCP + Intel.DebugPrint("Bad TCP/UDP Checksum\n"); + } else { + fromDevFlags |= (FromDeviceFlags.GoodTcp4Checksum | + FromDeviceFlags.GoodUdp4Checksum); + } + } + + DebugStub.Assert((fromDevFlags == (FromDeviceFlags.ReceiveSuccess)) || (fromDevFlags == (FromDeviceFlags.ReceiveSuccess | FromDeviceFlags.GoodTcp4Checksum | FromDeviceFlags.GoodUdp4Checksum))); + + return fromDevFlags; + } + + private Packet*! in ExHeap MakePacketFromDescriptor(UIntPtr fragmentVirtAddr, + ulong controlBits) + { + PacketFifo*! in ExHeap inDevPkts = rxPacketsInDevice.Acquire(); + Packet*! in ExHeap packet = inDevPkts->Pop(); + + int length = (int) ((controlBits & RxDescriptor.LENGTH_MASK) + >> RxDescriptor.LENGTH_SHIFT); + uint stat_err = (uint) ((controlBits & RxDescriptor.ERR_STAT_MASK) + >> RxDescriptor.ERR_STAT_SHIFT); + + // can't deal with fragments yet + DebugStub.Assert((stat_err & RxErrStatFields.END_OF_PACKET) != 0); + DebugStub.Assert(packet->GetFragmentVirtualAddress(0) == fragmentVirtAddr); + + packet->FromDeviceFlags = GetRecvPktFlags(stat_err); + packet->SetFragmentLength(0, length); + + rxPacketsInDevice.Release(inDevPkts); + + return packet; + } + + internal void LockedDrainRecvBuffer(PacketFifo*! in ExHeap toUser) + { + UIntPtr fragmentVirtAddr; + ulong controlBits; + + while (rxRingBuffer.Peek(out fragmentVirtAddr, out controlBits)) { + Packet*! in ExHeap packet = MakePacketFromDescriptor(fragmentVirtAddr, + controlBits); + toUser->Push(packet); + rxRingBuffer.Pop(); + } + } + + /////////////////////////////////////////////////////////////////////// + // + // Ring buffer properties + // + internal UIntPtr BaseAddress + { + get { return rxRingBuffer.BaseAddress; } + } + + [Pure] + internal uint Capacity { get { return rxRingBuffer.Capacity; } } + + [Pure] + internal uint Free { get { return rxRingBuffer.Free; } } + + [Pure] + internal bool IsFull { get { return rxRingBuffer.IsFull; } } + + [Pure] + internal uint Count { get { return rxRingBuffer.Count; } } + + [Pure] + internal uint Tail { get { return rxRingBuffer.Tail; } } + + [Pure] + internal uint Head { get { return rxRingBuffer.Head; } } + + [Pure] + internal uint DescLength { get { return rxRingBuffer.DescLength; } } + } +} diff --git a/base/Drivers/Network/Intel/IntelTxRingBuffer.sg b/base/Drivers/Network/Intel/IntelTxRingBuffer.sg new file mode 100644 index 0000000..bba9953 --- /dev/null +++ b/base/Drivers/Network/Intel/IntelTxRingBuffer.sg @@ -0,0 +1,154 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: IntelTxRingBuffer.cs +// +// Notes: +// +// Adaptor class for the Tx ring buffer. +// + +using Microsoft.Contracts; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Io.Net; +using Microsoft.Singularity.V1.Services; +using Microsoft.SingSharp; + +using System; + +namespace Microsoft.Singularity.Drivers.Network.Intel +{ + internal class IntelTxRingBuffer + { + IntelRingBuffer! txRingBuffer; + ExRef! txPacketsInDevice; + + internal IntelTxRingBuffer(uint capacity) + requires capacity > 0 && IntelRingBuffer.IsPowerOf2(capacity); + { + this.txRingBuffer = new IntelRingBuffer(capacity); + this.txPacketsInDevice = new ExRef( + new [ExHeap] PacketFifo((int) capacity), + false + ); + } + + internal void Reset() + { + txRingBuffer.Reset(); + } + + /////////////////////////////////////////////////////////////////////// + // + // Buffer Operations, should only be called when a lock is held + // this ring buffer. + // + private void LockedPushTsmtBuffer(UIntPtr packetVirtAddr, int length) + { + PlatformService.CleanAndInvalidateDCache(packetVirtAddr, (UIntPtr)length); + + ulong controlBits = (ulong) ByteOrder.HostToLittleEndian(length); + + // set necessary command fields + controlBits |= (TxCmdFields.END_OF_PACKET | TxCmdFields.INSERT_FCS | + TxCmdFields.REPORT_STATUS); + txRingBuffer.Push(packetVirtAddr, controlBits); + } + + internal void LockedPushTsmtBuffer([Claims]Packet*! in ExHeap packet) + { + DebugStub.Assert(packet->FragmentCount == 1); + DebugStub.Assert(!txRingBuffer.IsFull); + + UIntPtr address; + int length; + + packet->GetFragmentRange(0, out address, out length); + + this.LockedPushTsmtBuffer(address, length); + + PacketFifo*! in ExHeap liveFifo = (!)this.txPacketsInDevice.Acquire(); + liveFifo->Push(packet); + txPacketsInDevice.Release(liveFifo); + } + + private Packet*! in ExHeap MakePacketFromDescriptor(UIntPtr fragmentVirtAddr, + ulong controlBits) + { + PacketFifo*! in ExHeap inDevPkts = txPacketsInDevice.Acquire(); + Packet*! in ExHeap packet = inDevPkts->Pop(); + + // int length = (int) ((controlBits & TxDescriptor.LENGTH_MASK) + // >> TxDescriptor.LENGTH_SHIFT); + int stat_err = (int) ((controlBits & TxDescriptor.ERR_STAT_MASK) + >> TxDescriptor.ERR_STAT_SHIFT); + + + DebugStub.Assert(packet->GetFragmentVirtualAddress(0) == fragmentVirtAddr); + + if (((TxStatErrFields.LATE_COLLISION | + TxStatErrFields.EXCESS_COLLISIONS | + TxStatErrFields.TRANSMIT_UNDERRUN) & stat_err) == 0) { + packet->FromDeviceFlags = FromDeviceFlags.TransmitSuccess; + } else { + packet->FromDeviceFlags = FromDeviceFlags.TransmitError; + } + + txPacketsInDevice.Release(inDevPkts); + + return packet; + } + + internal void LockedDrainTsmtBuffer (PacketFifo*! in ExHeap toUser) + { + UIntPtr fragmentVirtAddr; + ulong controlBits; + + while (txRingBuffer.Peek(out fragmentVirtAddr, out controlBits)) { + Packet*! in ExHeap packet = MakePacketFromDescriptor(fragmentVirtAddr, + controlBits); + toUser->Push(packet); + txRingBuffer.Pop(); + } + } + + + /////////////////////////////////////////////////////////////////////// + // + // Ring buffer properties + // + internal UIntPtr BaseAddress + { + get { return txRingBuffer.BaseAddress; } + } + + internal bool NewTransmitEvent() { + return txRingBuffer.Peek(); + } + + [Pure] + internal uint Capacity { get { return txRingBuffer.Capacity; } } + + [Pure] + internal uint Free { get { return txRingBuffer.Free; } } + + [Pure] + internal bool IsFull { get { return txRingBuffer.IsFull; } } + + [Pure] + internal uint Count { get { return txRingBuffer.Count; } } + + [Pure] + internal uint Tail { get { return txRingBuffer.Tail; } } + + [Pure] + internal uint Head { get { return txRingBuffer.Head; } } + + [Pure] + internal uint DescLength { get { return txRingBuffer.DescLength; } } + } +} diff --git a/base/Drivers/Network/Tulip/Tulip.csproj b/base/Drivers/Network/Tulip/Tulip.csproj index 75307df..aa3e95a 100644 --- a/base/Drivers/Network/Tulip/Tulip.csproj +++ b/base/Drivers/Network/Tulip/Tulip.csproj @@ -16,12 +16,12 @@ Tulip true - + - + - + @@ -30,9 +30,9 @@ - + - + diff --git a/base/Drivers/Network/Tulip/Tulip.sg b/base/Drivers/Network/Tulip/Tulip.sg index 79cdbc1..3ac5a7a 100644 --- a/base/Drivers/Network/Tulip/Tulip.sg +++ b/base/Drivers/Network/Tulip/Tulip.sg @@ -92,30 +92,29 @@ namespace Microsoft.Singularity.Drivers.Network.Tulip private volatile bool isShutdown = false; // Constructor - internal Tulip(TulipResources! res) + internal Tulip(IoPortRange! csrRange, + IoMemoryRange! memRange, + IoIrqRange! irqRange) { TulipDebug.Print("Tulip: Initialize() called\n"); - assume res.csr != null; - assume res.irq != null; + csr0 = (!)csrRange.PortAtOffset(0x00, 4, Access.ReadWrite); + csr1 = (!)csrRange.PortAtOffset(0x08, 4, Access.Write); + csr2 = (!)csrRange.PortAtOffset(0x10, 4, Access.Write); + csr3 = (!)csrRange.PortAtOffset(0x18, 4, Access.ReadWrite); + csr4 = (!)csrRange.PortAtOffset(0x20, 4, Access.ReadWrite); + csr5 = (!)csrRange.PortAtOffset(0x28, 4, Access.ReadWrite); + csr6 = (!)csrRange.PortAtOffset(0x30, 4, Access.ReadWrite); + csr7 = (!)csrRange.PortAtOffset(0x38, 4, Access.ReadWrite); + csr8 = (!)csrRange.PortAtOffset(0x40, 4, Access.Read); + csr9 = (!)csrRange.PortAtOffset(0x48, 4, Access.ReadWrite); + csr10 = (!)csrRange.PortAtOffset(0x50, 4, Access.Read); + csr11 = (!)csrRange.PortAtOffset(0x58, 4, Access.ReadWrite); + csr12 = (!)csrRange.PortAtOffset(0x60, 4, Access.ReadWrite); + csr15 = (!)csrRange.PortAtOffset(0x78, 4, Access.ReadWrite); - csr0 = (!)res.csr.PortAtOffset(0x00, 4, Access.ReadWrite); - csr1 = (!)res.csr.PortAtOffset(0x08, 4, Access.Write); - csr2 = (!)res.csr.PortAtOffset(0x10, 4, Access.Write); - csr3 = (!)res.csr.PortAtOffset(0x18, 4, Access.ReadWrite); - csr4 = (!)res.csr.PortAtOffset(0x20, 4, Access.ReadWrite); - csr5 = (!)res.csr.PortAtOffset(0x28, 4, Access.ReadWrite); - csr6 = (!)res.csr.PortAtOffset(0x30, 4, Access.ReadWrite); - csr7 = (!)res.csr.PortAtOffset(0x38, 4, Access.ReadWrite); - csr8 = (!)res.csr.PortAtOffset(0x40, 4, Access.Read); - csr9 = (!)res.csr.PortAtOffset(0x48, 4, Access.ReadWrite); - csr10 = (!)res.csr.PortAtOffset(0x50, 4, Access.Read); - csr11 = (!)res.csr.PortAtOffset(0x58, 4, Access.ReadWrite); - csr12 = (!)res.csr.PortAtOffset(0x60, 4, Access.ReadWrite); - csr15 = (!)res.csr.PortAtOffset(0x78, 4, Access.ReadWrite); - - irq = (!)res.irq.IrqAtOffset(0); - TulipDebug.Print("Tulip IRQ {0}\n", __arglist(res.irq.ToString())); + irq = (!)irqRange.IrqAtOffset(0); + TulipDebug.Print("Tulip IRQ {0}\n", __arglist(irqRange.ToString())); PciDeviceConfig! config = this.config = (PciDeviceConfig!)IoConfig.GetConfig(); Debug.Assert((config.InterruptsEnabled)); @@ -238,21 +237,34 @@ namespace Microsoft.Singularity.Drivers.Network.Tulip DebugStub.Break(); } - // Figure out PCI cache bits for csr0 - // Not sure this is sane. - uint cls = config.CacheLineSize; + // Configure bus mode register + uint cls = config.CacheLineSize; // # of DWORDS uint cacheBits = CSR0.WIE | CSR0.RLE | CSR0.RME; - if (cls < 8 || cls > 32) { - cls = 0; - cacheBits = 0; + uint cal = 0; // cache alignment + uint pbl = 32; // # of DWORDS to burst (>= cal) + switch (cls) { + case 8: + cal = 1; + break; + case 16: + cal = 2; + break; + case 32: + cal = 3; + break; + default: + cacheBits = 0; + cal = 0; + pbl = 0; + break; } InitChains(); // Write CSR0 to set global host bus operating parameters csr0.Write32(cacheBits | CSR0.BAR | - ((cls / 8u) << CSR0.CAL_ROLL) | - (cls << CSR0.PBL_ROLL)); + (cal << CSR0.CAL_ROLL) | + (pbl << CSR0.PBL_ROLL)); // This is a debug helper and could be used to switch to // polling mode. @@ -620,8 +632,7 @@ namespace Microsoft.Singularity.Drivers.Network.Tulip uint crc = 0xffffffff; - for (uint index = 0; index < DATA_LEN; index++) - { + for (uint index = 0; index < DATA_LEN; index++) { byte currentByte = srom[index]; for (uint bit = 0; bit < 8; bit++) { uint Msb = (crc >> 31) & 1; @@ -735,8 +746,7 @@ namespace Microsoft.Singularity.Drivers.Network.Tulip // Starting byte address to starting word address addr >>= 1; - foreach (byte b in SRomReadPreamble) - { + foreach (byte b in SRomReadPreamble) { csr9.Write32(srom_base_cmd | (uint)(b >> 4)); Delay(3); csr9.Write32(srom_base_cmd | (uint)(b & 0x0f)); Delay(3); } diff --git a/base/Drivers/Network/Tulip/TulipConstants.cs b/base/Drivers/Network/Tulip/TulipConstants.sg similarity index 100% rename from base/Drivers/Network/Tulip/TulipConstants.cs rename to base/Drivers/Network/Tulip/TulipConstants.sg diff --git a/base/Drivers/Network/Tulip/TulipController.sg b/base/Drivers/Network/Tulip/TulipController.sg index 143613c..61494bd 100644 --- a/base/Drivers/Network/Tulip/TulipController.sg +++ b/base/Drivers/Network/Tulip/TulipController.sg @@ -22,62 +22,78 @@ namespace Microsoft.Singularity.Drivers.Network.Tulip { internal class TulipController { - public static int DriverMain(TulipResources! resources) + public static int DriverMain(PnicResources! resources) { ExtensionContract.Exp! ep = (resources.ec).Acquire(); ServiceProviderContract.Exp! sp = (resources.nicsp).Acquire(); + Tulip! device = new Tulip(resources.csr, resources.mem, resources.irq); - Tulip! device = new Tulip(resources); - device.Initialize(); - - ep.SendSuccess(); try { - for (bool run = true; run;) { - switch receive { - case sp.Connect(ServiceContract.Exp:Start! exp): - NicDeviceContract.Exp nd = exp as NicDeviceContract.Exp; - if (nd != null) { - Tracing.Log(Tracing.Debug, "Connect success."); - sp.SendAckConnect(); - TulipDeviceChannel.CreateThread(device, nd); - } - else { - Tracing.Log(Tracing.Error, "Connect failed."); - sp.SendNackConnect(exp); - } - break; - - case sp.ChannelClosed(): - device.Shutdown(); - run = false; - break; - - case ep.Shutdown(): - device.Shutdown(); - ep.SendAckShutdown(); - break; - - case ep.ChannelClosed(): - device.Shutdown(); - run = false; - break; - - case unsatisfiable: - DebugStub.Break(); - break; - } - } + return SharedMain(ep, sp, device); } finally { delete ep; delete sp; } - return 0; } - public static int DriverMain(PnicResources! resources) + public static int DriverMain(TulipResources! resources) { - DebugStub.Break(); + ExtensionContract.Exp! ep = (resources.ec).Acquire(); + ServiceProviderContract.Exp! sp = (resources.nicsp).Acquire(); + Tulip! device = new Tulip(resources.csr, resources.mem, resources.irq); + + try { + return SharedMain(ep, sp, device); + } + finally { + delete ep; + delete sp; + } + } + + public static int SharedMain(ExtensionContract.Exp! ep, + ServiceProviderContract.Exp! sp, + Tulip! device) + { + device.Initialize(); + + ep.SendSuccess(); + for (bool run = true; run;) { + switch receive { + case sp.Connect(ServiceContract.Exp:Start! exp): + NicDeviceContract.Exp nd = exp as NicDeviceContract.Exp; + if (nd != null) { + Tracing.Log(Tracing.Debug, "Connect success."); + sp.SendAckConnect(); + TulipDeviceChannel.CreateThread(device, nd); + } + else { + Tracing.Log(Tracing.Error, "Connect failed."); + sp.SendNackConnect(exp); + } + break; + + case sp.ChannelClosed(): + device.Shutdown(); + run = false; + break; + + case ep.Shutdown(): + device.Shutdown(); + ep.SendAckShutdown(); + break; + + case ep.ChannelClosed(): + device.Shutdown(); + run = false; + break; + + case unsatisfiable: + DebugStub.Break(); + break; + } + } return 0; } } diff --git a/base/Drivers/Network/Tulip/TulipDebug.cs b/base/Drivers/Network/Tulip/TulipDebug.cs deleted file mode 100644 index 1bc6dc3..0000000 --- a/base/Drivers/Network/Tulip/TulipDebug.cs +++ /dev/null @@ -1,98 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: TulipDebug.cs -// -// -// Simple Driver for DEC 21140a PCI Ethernet card. (as used in Virtual PC) -// -// Useful reference URLs: -// http://www.intel.com/design/network/manuals/21140ahm.pdf - -using Microsoft.Singularity.Io; -using Microsoft.Singularity.V1.Services; - -using System; -using System.Diagnostics; - -namespace Microsoft.Singularity.Drivers.Network.Tulip -{ - class TulipDebug - { - [Conditional("DEBUG_TULIP")] - internal static void - Print(string! format) - { - DebugStub.Print(format); - } - - [Conditional("DEBUG_TULIP")] - internal static void - Print(string! format, __arglist) - { - DebugStub.Print(format, new ArgIterator(__arglist)); - } - - [Conditional("DEBUG_TULIP")] - private static void - PrintByteRow(byte[]! data, int start, int end) - { - Print("{0:x8} ", __arglist(start)); - for (int j = start; j < end; j++) { - Print("{0:x2} ", __arglist(data[j])); - } - Print("\n"); - } - - [Conditional("DEBUG_TULIP")] - internal static void - PrintBytes(string! title, byte[]! data, int start, int length) - { - Print("{0}\n", __arglist(title)); - const int bpl = 16; - - int m = start + length - (length % bpl); - int n = start + length; - - for (int i = start; i < m; i += bpl) { - PrintByteRow(data, i, i + bpl); - } - if (n != m) { - PrintByteRow(data, m, n); - } - } - - [Conditional("DEBUG_TULIP")] - internal static void - PrintBytes(string! title, byte[]! data) - { - PrintBytes(title, data, 0, data.Length); - } - - [Conditional("DEBUG_TULIP")] - internal static void - PrintCsrs(IoPort! csr0, IoPort! csr1, IoPort! csr2, IoPort! csr3, - IoPort! csr4, IoPort! csr5, IoPort! csr6, IoPort! csr7, - IoPort! csr8, IoPort! csr9, IoPort! csr10, IoPort! csr11, - IoPort! csr12, IoPort! csr15) - { - Print("CSR0 {0:x8} CSR3 {1:x8} CSR4 {2:x8}\n", - __arglist(csr0.Read32(), csr3.Read32(), csr4.Read32())); - - uint status = csr5.Read32(); - Print("CSR5 {0:x} Bus Error {0:x} TX {1:x} RX {2:x}\n", - __arglist(status, - (status >> CSR5.EB_ROLL) & CSR5.EB_MASK, - (status >> CSR5.TS_ROLL) & CSR5.TS_MASK, - (status >> CSR5.RS_ROLL) & CSR5.RS_MASK - )); - Print("CSR6 {0:x8} CSR7 {1:x8} CSR8 {2:x8}\n", - __arglist(csr6.Read32(), csr7.Read32(), csr8.Read32())); - Print("CSR11 {0:x8} CSR12 {1:x8} CSR15 {2:x8}\n", - __arglist(csr11.Read32(), csr12.Read32(), csr15.Read32())); - } - } -} diff --git a/base/Drivers/Network/Tulip/TulipDebug.sg b/base/Drivers/Network/Tulip/TulipDebug.sg new file mode 100644 index 0000000..4304e9a --- /dev/null +++ b/base/Drivers/Network/Tulip/TulipDebug.sg @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: TulipDebug.cs +// +// Simple Driver for DEC 21140a PCI Ethernet card. (as used in Virtual PC) +// +// Useful reference URLs: +// http://www.intel.com/design/network/manuals/21140ahm.pdf + +using Microsoft.Singularity.Io; +using Microsoft.Singularity.V1.Services; + +using System; +using System.Diagnostics; + +namespace Microsoft.Singularity.Drivers.Network.Tulip +{ + class TulipDebug + { + [Conditional("DEBUG_TULIP")] + internal static void + Print(string! format) + { + DebugStub.Print(format); + } + + [Conditional("DEBUG_TULIP")] + internal static void + Print(string! format, __arglist) + { + DebugStub.Print(format, new ArgIterator(__arglist)); + } + + [Conditional("DEBUG_TULIP")] + private static void + PrintByteRow(byte[]! data, int start, int end) + { + Print("{0:x8} ", __arglist(start)); + for (int j = start; j < end; j++) { + Print("{0:x2} ", __arglist(data[j])); + } + Print("\n"); + } + + [Conditional("DEBUG_TULIP")] + internal static void + PrintBytes(string! title, byte[]! data, int start, int length) + { + Print("{0}\n", __arglist(title)); + const int bpl = 16; + + int m = start + length - (length % bpl); + int n = start + length; + + for (int i = start; i < m; i += bpl) { + PrintByteRow(data, i, i + bpl); + } + if (n != m) { + PrintByteRow(data, m, n); + } + } + + [Conditional("DEBUG_TULIP")] + internal static void + PrintBytes(string! title, byte[]! data) + { + PrintBytes(title, data, 0, data.Length); + } + + [Conditional("DEBUG_TULIP")] + internal static void + PrintCsrs(IoPort! csr0, IoPort! csr1, IoPort! csr2, IoPort! csr3, + IoPort! csr4, IoPort! csr5, IoPort! csr6, IoPort! csr7, + IoPort! csr8, IoPort! csr9, IoPort! csr10, IoPort! csr11, + IoPort! csr12, IoPort! csr15) + { + Print("CSR0 {0:x8} CSR3 {1:x8} CSR4 {2:x8}\n", + __arglist(csr0.Read32(), csr3.Read32(), csr4.Read32())); + + uint status = csr5.Read32(); + Print("CSR5 {0:x} Bus Error {0:x} TX {1:x} RX {2:x}\n", + __arglist(status, + (status >> CSR5.EB_ROLL) & CSR5.EB_MASK, + (status >> CSR5.TS_ROLL) & CSR5.TS_MASK, + (status >> CSR5.RS_ROLL) & CSR5.RS_MASK + )); + Print("CSR6 {0:x8} CSR7 {1:x8} CSR8 {2:x8}\n", + __arglist(csr6.Read32(), csr7.Read32(), csr8.Read32())); + Print("CSR11 {0:x8} CSR12 {1:x8} CSR15 {2:x8}\n", + __arglist(csr11.Read32(), csr12.Read32(), csr15.Read32())); + } + } +} diff --git a/base/Drivers/Network/Tulip/TulipResources.sg b/base/Drivers/Network/Tulip/TulipResources.sg index 5a16d74..c665662 100644 --- a/base/Drivers/Network/Tulip/TulipResources.sg +++ b/base/Drivers/Network/Tulip/TulipResources.sg @@ -25,13 +25,13 @@ namespace Microsoft.Singularity.Drivers.Network.Tulip internal class TulipResources : DriverCategoryDeclaration { [IoPortRange(0, Default = 0xec00, Length = 0x80)] - internal IoPortRange csr; + internal readonly IoPortRange csr; [IoMemoryRange(1, Default = 0xfebff000, Length = 0x1000)] - internal IoMemoryRange mem; // this is unused, but we must declare it + internal readonly IoMemoryRange mem; // this is unused, but we must declare it [IoIrqRange(6, Default = 0x0b, Shared = true)] - internal IoIrqRange irq; + internal readonly IoIrqRange irq; [ExtensionEndpoint] internal TRef ec; @@ -39,7 +39,7 @@ namespace Microsoft.Singularity.Drivers.Network.Tulip [ServiceEndpoint(typeof(NicDeviceContract))] internal TRef nicsp; - internal int DriverMain() + internal int DriverMain(string instance) { return TulipController.DriverMain(this); } @@ -51,16 +51,16 @@ namespace Microsoft.Singularity.Drivers.Network.Tulip // largely the case with the PNIC and Tulip). [DriverCategory] [Signature("pci/ven_11ad&dev_0002&cc_0200")] - internal class PnicResources // : DriverCategoryDeclaration HACK!HACK!HACK! + internal class PnicResources : DriverCategoryDeclaration { [IoPortRange(0, Default = 0xec00, Length = 0x100)] - internal IoPortRange csr; + internal readonly IoPortRange csr; [IoMemoryRange(1, Default = 0xfebff000, Length = 0x100)] - internal IoMemoryRange mem; + internal readonly IoMemoryRange mem; [IoIrqRange(6, Default = 0x0b, Shared = true)] - internal IoIrqRange irq; + internal readonly IoIrqRange irq; [ExtensionEndpoint] internal TRef ec; @@ -68,11 +68,9 @@ namespace Microsoft.Singularity.Drivers.Network.Tulip [ServiceEndpoint(typeof(NicDeviceContract))] internal TRef nicsp; -#if NOTYET - internal int DriverMain() + internal int DriverMain(string instance) { return TulipController.DriverMain(this); } -#endif // NOTYET } } diff --git a/base/Drivers/Network/Tulip/TulipTxRingBuffer.sg b/base/Drivers/Network/Tulip/TulipTxRingBuffer.sg index 1664a60..9a40c22 100644 --- a/base/Drivers/Network/Tulip/TulipTxRingBuffer.sg +++ b/base/Drivers/Network/Tulip/TulipTxRingBuffer.sg @@ -191,7 +191,8 @@ namespace Microsoft.Singularity.Drivers.Network.Tulip uint tdes0 = this.region.Read32(this.Tail * BytesPerDescriptor); if ((tdes0 & TDES0.OWN) == TDES0.OWN) { break; - } else if ((tdes0 & TDES0.ES) == TDES0.ES) { + } + else if ((tdes0 & TDES0.ES) == TDES0.ES) { // Found an error, clear error for // retry. this.region.Write32(this.Tail * BytesPerDescriptor, @@ -283,8 +284,10 @@ namespace Microsoft.Singularity.Drivers.Network.Tulip UIntPtr pa; // Physical address UIntPtr paLeft; // Bytes remaining on physical page if (!DeviceService.GetDmaPhysicalAddress(va, out pa, out paLeft) || - pa == UIntPtr.Zero || - paLeft < Tulip.MtuBytes) { + pa == UIntPtr.Zero ) { + //XXX commenting this out for new netstack. + //paLeft < Tulip.MtuBytes) { + DebugStub.Break(); throw new ApplicationException("Bad DMA pointer"); } return pa; diff --git a/base/Drivers/Network/nForce4_MSIL/nForce4_MSIL.csproj b/base/Drivers/Network/nForce4_MSIL/nForce4_MSIL.csproj deleted file mode 100644 index 44d9ef8..0000000 --- a/base/Drivers/Network/nForce4_MSIL/nForce4_MSIL.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - Exe - nvmac - true - $(PRECOMPILED_NVIDIA_DIR) - - - - - - - - - - diff --git a/base/Drivers/Omap3430Keyboard/Omap3430Keyboard.csproj b/base/Drivers/Omap3430Keyboard/Omap3430Keyboard.csproj new file mode 100644 index 0000000..fec3672 --- /dev/null +++ b/base/Drivers/Omap3430Keyboard/Omap3430Keyboard.csproj @@ -0,0 +1,24 @@ + + + + + + + Exe + Omap3430Keyboard + true + + + + + + + + + diff --git a/base/Drivers/Omap3430Keyboard/Omap3430Keyboard.sg b/base/Drivers/Omap3430Keyboard/Omap3430Keyboard.sg new file mode 100644 index 0000000..e85dd3a --- /dev/null +++ b/base/Drivers/Omap3430Keyboard/Omap3430Keyboard.sg @@ -0,0 +1,209 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Omap3430Keyboard.cs +// + +//#define DEBUG_DISPATCH_IO +//#define DEBUG_IO + +using System; +using System.Text; +using System.Threading; +using System.Runtime.CompilerServices; //StructAlign attribute +using System.Runtime.InteropServices; //structLayout attribute +using System.GCs; + +using Microsoft.SingSharp; +using Microsoft.Singularity; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Extending; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Io.Keyboard; +using Microsoft.Singularity.Configuration; + +using Microsoft.Singularity.V1.Services; +using Microsoft.Singularity.V1.Threads; + +namespace Microsoft.Singularity.Drivers.Omap3430Keyboard +{ + // Should also support PNP0F13: PS/2 Mouse + // [Signature("/pnp/PNP0F13")] + // [IoIrqRange(0, Default = 0x0c)] + // [Follows("/pnp/PNP0303")] + + // create the resource object for CTR to fill in + [DriverCategory] + [Signature("/arm/ti/3430/FAC")] + internal class KeyboardResources : DriverCategoryDeclaration + { + [IoIrqRange(0, Default = 0x01)] + internal IoIrqRange irq; + + [ExtensionEndpoint] + internal TRef ec; + + [ServiceEndpoint(typeof(KeyboardDeviceContract))] + internal TRef kbdsp; + + // This should have a custom attribute. + internal static KeyboardResources Values; + + // CTR will create the rest of this class: + + // CTR creates a private constructor so that the app writer can't + // instantiate objects of this class + private KeyboardResources() + { + // endpoint initialization + ec = new TRef + ((!)(Process.GetStartupEndpoint(0) as ExtensionContract.Exp)); + kbdsp = new TRef + ((!)(Process.GetStartupEndpoint(1) as ServiceProviderContract.Exp)); + + // Io Resource initialization + IoConfig config = (!)IoConfig.GetConfig(); + + // dynamic resources + irq = (IoIrqRange)config.DynamicRanges[0]; + + base(); + } + + static KeyboardResources() + { + Values = new KeyboardResources(); + } + } + + public class KeyboardControl + { + private static AutoResetEvent stall; + + public static int Main(String[] args) + { +#if VERBOSE + DebugStub.WriteLine("Omap3430Keyboard driver started."); + for (int i = 0; i < args.Length; i++) { + DebugStub.WriteLine(" {0}. [{1}]", __arglist(i, args[i])); + } +#endif + + stall = new AutoResetEvent(false); + + // get the endpoints and set up the main switch receive + ExtensionContract.Exp ec = KeyboardResources.Values.ec.Acquire(); + ServiceProviderContract.Exp sp = KeyboardResources.Values.kbdsp.Acquire(); + + Tracing.Log(Tracing.Audit, "Registered"); + + ec.SendSuccess(); + + try { + for (bool run = true; run;) { + switch receive { + // Listen for new connections + case sp.Connect(candidate): + KeyboardDeviceContract.Exp newClient = candidate as KeyboardDeviceContract.Exp; + if (newClient != null) { + KeyboardChannel.CreateThread(stall, newClient); + sp.SendAckConnect(); + } + else { + sp.SendNackConnect(candidate); + } + break; + + // Listen for extension parent + case ec.Shutdown(): + ec.SendAckShutdown(); + run = false; + break; + + case sp.ChannelClosed(): + Tracing.Log(Tracing.Debug, "Keyboard driver no longer needed."); + run = false; + break; + } + } + } + finally { + delete sp; + Tracing.Log(Tracing.Debug, "Keyboard finished message pump."); + } + + // Close the device + stall.Set(); + + Tracing.Log(Tracing.Audit, "Shutdown"); + delete ec; + + return 0; + } + } + + ////////////////////////////////////////////////////////////////////////// + // + // This worker thread processes incoming play requests. + // + public class KeyboardChannel + { + private AutoResetEvent stall; + private TRef epStart; + + public static void CreateThread(AutoResetEvent stall, + [Claims] KeyboardDeviceContract.Exp:Start! ep) + { + KeyboardChannel! channel = new KeyboardChannel(stall, ep); + Thread! thread = new Thread(new ThreadStart(channel.MessagePump)); + Tracing.Log(Tracing.Audit, "KeyboardChannel starting thread {0:x}", + AppRuntime.AddressOf(thread)); + thread.Start(); + } + + private KeyboardChannel(AutoResetEvent stall, + [Claims] KeyboardDeviceContract.Exp:Start! ep) + { + this.stall = stall; + this.epStart = new TRef(ep); + base(); + } + + private void MessagePump() + { + Tracing.Log(Tracing.Debug, "KeyboardChannel.Run entered."); + KeyboardDeviceContract.Exp! ep = epStart.Acquire(); + epStart = null; + + try { + ep.SendSuccess(); + + for (bool run = true; run;) { + uint key; + + switch receive { + case ep.GetKey(): + Tracing.Log(Tracing.Debug, "GetKey()"); + stall.WaitOne(); + Tracing.Log(Tracing.Debug, "GetKey() => {0:x8}", 0); + ep.SendAckKey(0); + break; + + case ep.ChannelClosed(): + Tracing.Log(Tracing.Debug, "peer closed channel."); + run = false; + break; + } + } + } + finally { + delete ep; + } + Tracing.Log(Tracing.Debug, "KeyboardChannel exiting."); + } + } +} diff --git a/base/Drivers/Omap3430Video/Omap3430Video.csproj b/base/Drivers/Omap3430Video/Omap3430Video.csproj new file mode 100644 index 0000000..e23ce12 --- /dev/null +++ b/base/Drivers/Omap3430Video/Omap3430Video.csproj @@ -0,0 +1,28 @@ + + + + + + + Exe + Omap3430Video + true + + + + + + + + + + + + + diff --git a/base/Drivers/Omap3430Video/Omap3430Video.sg b/base/Drivers/Omap3430Video/Omap3430Video.sg new file mode 100644 index 0000000..ac4302d --- /dev/null +++ b/base/Drivers/Omap3430Video/Omap3430Video.sg @@ -0,0 +1,924 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Omap3430Video.sg +// +// Note: +// + +using System; +using System.Collections; +using System.Configuration.Assemblies; +using System.Runtime.InteropServices; +using System.Runtime.Remoting; +using System.Text; +using System.Threading; + +using Microsoft.SingSharp; +using Microsoft.Contracts; +using Microsoft.Singularity; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Extending; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Configuration; + +using Microsoft.Singularity.V1.Services; +using Microsoft.Singularity.V1.Threads; +using Allocation = Microsoft.Singularity.V1.Services.SharedHeapService.Allocation; + +namespace Microsoft.Singularity.Drivers.Omap3430Video +{ + // create the resource object for CTR to fill in + [DriverCategory] + [Signature("/arm/ti/3430/GFX")] + internal class VideoResources : DriverCategoryDeclaration + { + [IoMemoryRange(0, Default = 0x80010000, Length = 0x25800)] + internal IoMemoryRange frameBuffer; + + [IoMemoryRange(1, Default = 0x48050400, Length = 0x400)] + internal IoMemoryRange controls; + + [IoIrqRange(2, Default = 21, Length = 1)] + internal IoIrqRange gfxInterrupt; + + [IoIrqRange(3, Default = 25, Length = 1)] + internal IoIrqRange displayInterrupt; + + [ExtensionEndpoint] + internal TRef ec; + + [ServiceEndpoint(typeof(VideoDeviceContract))] + internal TRef video; + + [ServiceEndpoint(typeof(ConsoleDeviceContract))] + internal TRef console; + + // This should have a custom attribute. + internal static VideoResources Values; + + // LTR will create the rest of this class: + + // LTR creates a private constructor so that the app writer can't + // instantiate objects of this class + private VideoResources() + { + // endpoint initialization + ec = new TRef + ((!)(Process.GetStartupEndpoint(0) as ExtensionContract.Exp)); + video = new TRef + ((!)(Process.GetStartupEndpoint(1) as ServiceProviderContract.Exp)); + console = new TRef + ((!)(Process.GetStartupEndpoint(2) as ServiceProviderContract.Exp)); + + // Io Resource initialization + IoConfig config = (IoConfig!)IoConfig.GetConfig(); + + // dynamic resources + frameBuffer = (IoMemoryRange)config.DynamicRanges[0]; + controls = (IoMemoryRange)config.DynamicRanges[1]; + gfxInterrupt = (IoIrqRange)config.DynamicRanges[2]; + displayInterrupt = (IoIrqRange)config.DynamicRanges[3]; + + base(); + } + + static VideoResources() + { + Values = new VideoResources(); + } + } + + public class VideoControl + { + private static VideoDevice device; + + public static int Main(String[] args) + { + ExtensionContract.Exp! ec = VideoResources.Values.ec.Acquire(); + ServiceProviderContract.Exp! ve = VideoResources.Values.video.Acquire(); + ServiceProviderContract.Exp! te = VideoResources.Values.console.Acquire(); + + // Create the device + device = new VideoDevice(VideoResources.Values); + device.Initialize(); + + // Signal I/O system that we are initialized. + ec.SendSuccess(); + + // create a set of all client endpoints connected to the video + // interface. + ESet vs + = new ESet(); + // create a set of all client endpoints connected to the text + // interface. + ESet ts + = new ESet(); + + try { + for (bool run = true; run;) { + switch receive { + ///////////////////////////////////// I/O System Messages. + case ec.Shutdown(): + ec.SendAckShutdown(); + run = false; + break; + + /////////////////////////////// Service Provider Messages. + case ve.Connect(candidate): + VideoDeviceContract.Exp newClient + = candidate as VideoDeviceContract.Exp; + if (newClient != null) { + newClient.SendSuccess(); + vs.Add(newClient); + ve.SendAckConnect(); + } + else { + ve.SendNackConnect(candidate); + } + break; + + case te.Connect(candidate): + ConsoleDeviceContract.Exp newClient + = candidate as ConsoleDeviceContract.Exp; + if (newClient != null) { + newClient.SendSuccess(); + ts.Add(newClient); + te.SendAckConnect(); + } + else { + te.SendNackConnect(candidate); + } + break; + + ////////////////////////////////// Video Service Messages. + case ep.Plot(x, y, color32) in vs: + device.Plot(x, y, new RGB(color32)); + ep.SendAckPlot(); + vs.Add(ep); + break; + + case ep.Fill(x1, y1, x2, y2, color32) in vs: + device.Fill(x1, y1, x2, y2, new RGB(color32)); + ep.SendAckFill(); + vs.Add(ep); + break; + + case ep.BitBltBmp(x, y, buffer) in vs: + device.BitBltBmp(x, y, (!)buffer); + ep.SendAckBitBltBmp(buffer); + vs.Add(ep); + break; + + case ep.BitBltPng(x, y, buffer) in vs: + device.BitBltPng(x, y, (!)buffer); + ep.SendAckBitBltPng(buffer); + vs.Add(ep); + break; + + case ep.Scroll(x1, y1, x2, y2, dy) in vs: + device.Scroll(x1, y1, x2, y2, dy); + ep.SendAckScroll(); + vs.Add(ep); + break; + + case ep.ChannelClosed() in vs: + Tracing.Log(Tracing.Debug, "Client channel closes."); + delete ep; + break; + + //////////////////////////////// Console Service Messages. + case ep.Clear() in ts: + device.Window.Clear(); + ep.SendAckClear(); + ts.Add(ep); + break; + + case ep.ClearCursorToEndOfLine() in ts: + device.Window.ClearCursorToEndOfLine(); + ep.SendAckClearCursorToEndOfLine(); + ts.Add(ep); + break; + + case ep.Write(buffer, offset, count) in ts: + if (offset + count <= buffer.Length) { + device.Window.Write(buffer, offset, count); + ep.SendAckWrite(buffer); + } + else { + ep.SendNakWrite(buffer); + } + ts.Add(ep); + break; + + case ep.PutChar(c) in ts: + device.Window.PutChar(c); + ep.SendAckPutChar(); + ts.Add(ep); + break; + + case ep.PutCharAt(c, column, row) in ts: + if (device.Window.PutCharAt(c, column, row)) { + ep.SendAckPutCharAt(); + } + else { + ep.SendInvalidPosition(); + } + ts.Add(ep); + break; + + case ep.GetDisplayDimensions() in ts: + ep.SendDisplayDimensions(device.Window.TextColumns, + device.Window.TextRows); + ts.Add(ep); + break; + + case ep.GetCursorPosition() in ts: + ep.SendCursorPosition(device.Window.TextColumn, + device.Window.TextRow); + ts.Add(ep); + break; + + case ep.SetCursorPosition(int column, int row) in ts: + if (device.Window.SetTextCursor(column, row)) { + ep.SendAckSetCursorPosition(); + } + else { + ep.SendInvalidPosition(); + } + ts.Add(ep); + break; + + case ep.SetCursorSize(cursorSize) in ts: + ep.SendNotSupported(); + ts.Add(ep); + break; + + case ep.CursorFlash() in ts: + device.Window.CursorFlash(); + ep.SendAckCursorFlash(); + ts.Add(ep); // add client endpoint back into set. + break; + + case ep.CursorHide() in ts: + device.Window.CursorHide(); + ep.SendAckCursorHide(); + ts.Add(ep); // add client endpoint back into set. + break; + + case ep.CursorShow() in ts: + device.Window.CursorShow(); + ep.SendAckCursorShow(); + ts.Add(ep); // add client endpoint back into set. + break; + + case ep.ChannelClosed() in ts: + Tracing.Log(Tracing.Debug, "Client channel closes."); + delete ep; + break; + + case unsatisfiable: + Tracing.Log(Tracing.Debug, "Unsatisfiable."); + run = false; + break; + } + } + } + finally { + Tracing.Log(Tracing.Debug, "Omap3430Video finished message pump."); + } + + // Close the device + device.Finalize(); + + Tracing.Log(Tracing.Audit, "Shutdown"); + delete ec; + delete te; + delete ve; + vs.Dispose(); + ts.Dispose(); + + return 0; + } + } + + public class VideoDevice + { + private SvgaWindow console; + private bool Ready; + + public const int BytesPerPixel = 2; + public const int ScreenWidth = 240; + public const int ScreenStride = BytesPerPixel * ScreenWidth; + public const int ScreenHeight = 320; + private ushort[]! lineBuffer; + private IoMemory! screenBuffer; // Screen buffer within PCI-mapped memory. + + internal VideoDevice(VideoResources! res) + { + screenBuffer = (!)res.frameBuffer.MemoryAtOffset(0, 0x25800, Access.ReadWrite); + +#if DONT + DebugStub.WriteLine("Omap3430Video linear buffer at {0:x}", + __arglist((uint)screenBuffer.PhysicalAddress.Value)); +#endif + + lineBuffer = new ushort [2048]; + + base(); + + Ready = true; + } + + public void Initialize() + { +#if DONT + DebugStub.Print("Initializing Video Display\n"); +#endif + + Fill(0, 0, ScreenWidth - 1, ScreenHeight - 1, RGB.Black); + + console = new SvgaWindow(this, 0, 0, ScreenWidth - 1, ScreenHeight - 1); + + console.Write("Singularity 3430 Video Driver\n"); + console.Write(""); + console.Write(""); + console.Write(""); + } + + public SvgaWindow! Window + { + get { return console; } + } + + public void Finalize() + { + Ready = false; + } + + public void Plot(int x, int y, RGB color) + { + if (!Ready) { + return; + } + + int offset = y * ScreenStride + x * BytesPerPixel; + + // The read triggers the latch register. + screenBuffer.Write16(offset, (ushort)color); + + } + + public void Fill(int x1, int y1, int x2, int y2, RGB color) + { + if (!Ready) { + return; + } + + if (x1 < 0 || x1 >= ScreenWidth || + x2 < 0 || x2 >= ScreenWidth || + y1 < 0 || y1 >= ScreenHeight || + y2 < 0 || y2 >= ScreenHeight) + { + + throw new OverflowException("Draw bounds invalid."); + } + + ushort clr = (ushort)color; + int pDst = y1 * ScreenStride + x1 * BytesPerPixel; + for (int y = y1; y <= y2; y++) { + screenBuffer.Write16(pDst, clr, x2 - x1 + 1); + pDst += ScreenStride; + } + } + + public void BitBltChr(int x, int y, int width, int height, + byte[]! buffer, int offset, int ScanWidth, + RGB color, RGB background) + { + if (!Ready) { + return; + } + + ushort clr = (ushort)color; + ushort bkg = (ushort)background; + + int pSrc = offset; + // int pDst = (y + height - 1) * ScreenStride + x * BytesPerPixel; + int pDst = y * ScreenStride + x * BytesPerPixel; + + for (int j = 0; j < height; j++) { + int pSrcTemp = pSrc; + + for (int i = 0; i < width;) { + byte b = buffer[pSrcTemp++]; + + lineBuffer[i++] = ((b & 0x80) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x40) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x20) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x10) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x08) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x04) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x02) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x01) != 0) ? clr : bkg; + } + screenBuffer.Write16(pDst, lineBuffer, 0, width); + + pSrc += ScanWidth; + // pDst -= ScreenStride; + pDst += ScreenStride; + } + } + private void BitBlt24(int x, int y, int width, int height, + byte[]! in ExHeap buffer, int offset, int ScanWidth) + + { + int pDst = (y + height - 1) * ScreenStride + x * BytesPerPixel; + int pSrc = offset; + byte[] raw = new byte[ScanWidth]; + + for (int j = 0; j < height; j++) { + int pRaw = 0; + + // Decompress first + while (pRaw < ScanWidth) { + ushort len = (ushort)(buffer[pSrc] + ((ushort)buffer[pSrc+1] << 8)); + pSrc += 2; + + short val = unchecked((short)len); + if (val < 0) { + for (int i = 0; i > val; i--) { + raw[pRaw++] = buffer[pSrc+0]; + raw[pRaw++] = buffer[pSrc+1]; + raw[pRaw++] = buffer[pSrc+2]; + } + pSrc += 3; + } + else { + for (int i = 0; i < val; i++) { + raw[pRaw++] = buffer[pSrc++]; + } + } + } + + // Now draw the decompressed bits. + pRaw = 0; + for (int i = 0; i < width;) { + lineBuffer[i++] = RGB.Compute16(raw[pRaw+2], + raw[pRaw+1], + raw[pRaw+0]); + pRaw += 3; + } + + screenBuffer.Write16(pDst, lineBuffer, 0, width); + + pDst -= ScreenStride; + } + } + + private void BitBlt8(int x, int y, int width, int height, + byte[]! in ExHeap buffer, int offset, int ScanWidth, + ushort[]! palette) + { + int pDst = (y + height - 1) * ScreenStride + x * BytesPerPixel; + int pSrc = offset; + + for (int j = 0; j < height; j++) { + int pSrcTemp = pSrc; + + for (int i = 0; i < width;) { + lineBuffer[i++] = palette[buffer[pSrcTemp++]]; + } + screenBuffer.Write16(pDst, lineBuffer, 0, width); + + pSrc += ScanWidth; + pDst -= ScreenStride; + } + } + + private void BitBlt4(int x, int y, int width, int height, + byte[]! in ExHeap buffer, int offset, int ScanWidth, + ushort[]! palette) + { + int pDst = (y + height - 1) * ScreenStride + x * BytesPerPixel; + int pSrc = offset; + + for (int j = 0; j < height; j++) { + int pSrcTemp = pSrc; + + for (int i = 0; i < width;) { + byte b = buffer[pSrcTemp++]; + + lineBuffer[i++] = palette[b >> 4]; + lineBuffer[i++] = palette[b & 0xf]; + } + screenBuffer.Write16(pDst, lineBuffer, 0, width); + + pSrc += ScanWidth; + pDst -= ScreenStride; + } + } + + public void BitBlt1(int x, int y, int width, int height, + byte[]! in ExHeap buffer, int offset, int ScanWidth, + RGB color, RGB background) + { + if (!Ready) { + return; + } + + ushort clr = (ushort)color; + ushort bkg = (ushort)background; + + int pSrc = offset; + int pDst = (y + height - 1) * ScreenStride + x * BytesPerPixel; + + for (int j = 0; j < height; j++) { + int pSrcTemp = pSrc; + + for (int i = 0; i < width;) { + byte b = buffer[pSrcTemp++]; + + lineBuffer[i++] = ((b & 0x80) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x40) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x20) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x10) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x08) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x04) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x02) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x01) != 0) ? clr : bkg; + } + screenBuffer.Write16(pDst, lineBuffer, 0, width); + + pSrc += ScanWidth; + pDst -= ScreenStride; + } + } + + public void BitBltBmp(int x, int y, byte[]! in ExHeap buffer) + { + if (!Ready) { + return; + } + + try { + BITMAPFILEHEADER bfh; + BITMAPINFOHEADER bih; + int lDelta; + int cbScanLine; + int used; + int offset = 0; + + bfh = BITMAPFILEHEADER.Read(buffer, offset, out used); + bih = BITMAPINFOHEADER.Read(buffer, used, out used); + RGB[] Palette = bih.ReadPalette(buffer, used, out used); + + if (bih.biWidth == 0 || bih.biHeight == 0) { + return; + } + + if (x < 0) { + x = ScreenWidth + x - bih.biWidth; + } + if (y < 0) { + y = ScreenHeight + y - bih.biHeight; + } + + if (x < 0 || x + bih.biWidth > ScreenWidth || + y < 0 || y + bih.biHeight > ScreenHeight) + { + + throw new OverflowException("Draw bounds invalid."); + } + + offset = offset + bfh.bfOffBits; + used = offset; + + ushort[] palette = null; + + if (Palette != null) { + palette = new ushort [Palette.Length]; + for (int i = 0; i < Palette.Length; i++) { + palette[i] = (ushort)Palette[i]; + } + } + + // + // Make sure this is a 1bpp, 4bpp, or 8bpp bitmap. + // + + if (bih.biPlanes != 1) { + DebugStub.Print("biPlanes != 1"); + return; + } + + cbScanLine = (((bih.biWidth * bih.biBitCount) + 31) & ~31) / 8; + + if (bih.biHeight < 0) { + DebugStub.Print("bih.biHeight = {0} < 0\n", + __arglist(bih.biHeight)); + return; + } + +#if DONT + if (used + cbScanLine * bih.biHeight > buffer.Length) { + DebugStub.Print("{0} + {1} * {2} = {3} > {4}\n", + __arglist(used, + cbScanLine, + bih.biHeight, + used + cbScanLine * bih.biHeight, + buffer.Length)); + + throw new OverflowException("Bitmap invalid."); + } +#endif + + if (bih.biBitCount == 1) { + BitBlt1(x, y, bih.biWidth, bih.biHeight, buffer, offset, cbScanLine, + RGB.White, RGB.Black); + } + else if (bih.biBitCount == 4) { + assert palette != null; + BitBlt4(x, y, bih.biWidth, bih.biHeight, buffer, offset, cbScanLine, + palette); + } + else if (bih.biBitCount == 8) { + assert palette != null; + BitBlt8(x, y, bih.biWidth, bih.biHeight, buffer, offset, cbScanLine, + palette); + } + else if (bih.biBitCount == 24) { + BitBlt24(x, y, bih.biWidth, bih.biHeight, buffer, offset, cbScanLine); + } + else { + // + // We don't support this type of bitmap. + // + DebugStub.Print("((bih.biBitCount * bih.biPlanes) <= 4"); + } + } + catch (Exception e) { + DebugStub.WriteLine("Exception in BitBltBmp: {0}", __arglist(e.ToString())); + } + } + + public void BitBltPng(int x, int y, byte[]! in ExHeap buffer) + { + DebugStub.Print("BitBltPng not supported currently.\n"); + } + + public void Scroll(int x1, int y1, int x2, int y2, int CharHeight) + { + if (!Ready) { + return; + } + + if (x1 < 0 || x1 >= ScreenWidth || + x2 < 0 || x2 >= ScreenWidth || + y1 < 0 || y1 >= ScreenHeight || + y2 < 0 || y2 >= ScreenHeight || + y2 - y1 < CharHeight) + { + + throw new OverflowException("Draw bounds invalid."); + } + + int width = x2 - x1 + 1; + int pDst = y1 * ScreenStride + x1 * BytesPerPixel; + int pSrc = pDst + ScreenStride * CharHeight; + + for (int i = y1; i <= y2 - CharHeight; i++) { + screenBuffer.Copy16(pSrc, pDst, width); + + pDst += ScreenStride; + pSrc += ScreenStride; + } + } + } + + public class SvgaWindow + { + private readonly VideoDevice screen; + private readonly int regionLeft; + private readonly int regionTop; + private readonly int regionRight; + private readonly int regionBottom; + + private int cursorX = 0; + private int cursorY = 0; + private bool cursorVisible = true; + private bool cursorView = true; + private RGB foreColor = RGB.White; + private RGB backColor = RGB.Black; + + public const int FONT_HEIGHT = 8; + public const int FONT_WIDTH = 8; + + [NotDelayed] + public SvgaWindow(VideoDevice! screen, int x1, int y1, int x2, int y2) + requires x1 < x2 && y1 < y2; + { + this.screen = screen; + + regionLeft = x1; + regionTop = y1; + regionRight = x2; + regionBottom = y2; + + cursorX = regionLeft; + cursorY = regionTop; + + screen.Fill(regionLeft, regionTop, regionRight, regionBottom, backColor); + } + + public void Initialize() + { + } + + public void Finalize() + { + } + + public void SetTextColor(RGB color, RGB background) + { + foreColor = color; + backColor = background; + } + + public int TextColumn + { + get { return (cursorX - regionLeft) / FONT_WIDTH; } + } + + public int TextRow + { + get { return (cursorY - regionTop) / FONT_HEIGHT; } + } + + public bool SetTextCursor(int column, int row) + { + if (column >= 0 && column < TextColumns && + row >= 0 && row < TextRows) { + bool wasVisible = cursorVisible; + CursorHide(); + cursorX = regionLeft + column * FONT_WIDTH; + cursorY = regionTop + row * FONT_HEIGHT; + if (wasVisible) { + CursorShow(); + } + return true; + } + return false; + } + + public int TextColumns + { + get { return (regionRight - regionLeft) / FONT_WIDTH; } + } + + public int TextRows + { + get { return (regionBottom - regionTop) / FONT_HEIGHT; } + } + + private void DrawCursor() + { + cursorView = true; + CursorFlash(); + } + + public void CursorHide() + { + cursorView = false; + CursorFlash(); + cursorVisible = false; + } + + public void CursorShow() + { + cursorVisible = true; + DrawCursor(); + } + + public void CursorFlash() + { + if (cursorVisible) { + int x = cursorX; + int y = cursorY; + + if (x + FONT_WIDTH > regionRight) { + x -= FONT_WIDTH; + } + + if (cursorView) { + screen.BitBltChr(x, y, FONT_WIDTH, FONT_HEIGHT, + Fonts.Font8, 256 * FONT_HEIGHT, 1, foreColor, backColor); + } + else { + screen.BitBltChr(x, y, FONT_WIDTH, FONT_HEIGHT, + Fonts.Font8, 32 * FONT_HEIGHT, 1, foreColor, backColor); + } + cursorView = !cursorView; + } + } + + public void Clear() + { + screen.Fill(regionLeft, regionTop, regionRight, regionBottom, backColor); + cursorX = regionLeft; + cursorY = regionTop; + DrawCursor(); + } + + public void ClearCursorToEndOfLine() + { + screen.Fill(cursorX, cursorY, + regionRight, cursorY + FONT_HEIGHT, + backColor); + } + + public void Write(String! s) + { + for (int i = 0; i < s.Length; i++) { + Write((byte)s[i]); + } + DrawCursor(); + } + + public void Write(char[]! in ExHeap buffer, int offset, int count) + { + while (count-- > 0) { + Write((byte)buffer[offset++]); + } + DrawCursor(); + } + + public void PutChar(char c) + { + Write((byte)c); + DrawCursor(); + } + + public bool PutCharAt(char c, int column, int row) + { + if (column >= 0 && column < TextColumns && + row >= 0 && row < TextRows) { + screen.BitBltChr(regionLeft + column * FONT_WIDTH, + regionTop + row * FONT_HEIGHT, + FONT_WIDTH, FONT_HEIGHT, + Fonts.Font8, c * FONT_HEIGHT, 1, + foreColor, backColor); + return true; + } + return false; + } + + private void Write(byte c) + { + switch (c) { + + case (byte)'\n': + cursorY += FONT_HEIGHT; + cursorX = regionLeft; + break; + + case (byte)'\r': + screen.Fill(cursorX, cursorY, regionRight, + cursorY + FONT_HEIGHT, + backColor); + cursorX = regionLeft; + break; + + case (byte)'\b': + CursorHide(); + cursorX = cursorX - FONT_WIDTH; + CursorShow(); + break; + + default: + screen.BitBltChr(cursorX, cursorY, FONT_WIDTH, FONT_HEIGHT, + Fonts.Font8, c * FONT_HEIGHT, 1, foreColor, backColor); + + cursorX += FONT_WIDTH; + break; + } + + if (cursorX + FONT_WIDTH > regionRight) { + cursorY += FONT_HEIGHT; + cursorX = regionLeft; + } + + if (cursorY + FONT_HEIGHT > regionBottom) { + screen.Scroll(regionLeft, regionTop, regionRight, regionBottom, + FONT_HEIGHT); + cursorY = cursorY - FONT_HEIGHT; + screen.Fill(regionLeft, cursorY, regionRight, regionBottom, backColor); + } + } + + } +} diff --git a/base/Drivers/S3Trio64/Fonts.sg b/base/Drivers/S3Trio64/Fonts.sg index a9938f4..ac7a2a0 100644 --- a/base/Drivers/S3Trio64/Fonts.sg +++ b/base/Drivers/S3Trio64/Fonts.sg @@ -9,7 +9,7 @@ // Note: // -namespace Microsoft.Singularity.Drivers.S3Trio64 +namespace Microsoft.Singularity.Io { public sealed class Fonts { @@ -270,6 +270,7 @@ namespace Microsoft.Singularity.Drivers.S3Trio64 0x00,0x70,0xd8,0x30,0x60,0xc8,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x7c,0x7c,0x7c,0x7c,0x7c,0x7c,0x7c,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00 // 256 Cursor }; public static byte[] Font14 = { @@ -529,6 +530,7 @@ namespace Microsoft.Singularity.Drivers.S3Trio64 0x00,0x70,0xd8,0x30,0x60,0xc8,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x7c,0x7c,0x7c,0x7c,0x7c,0x7c,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00 // 256 Cursor }; public static byte[] Font8 = { @@ -788,6 +790,7 @@ namespace Microsoft.Singularity.Drivers.S3Trio64 0x70,0x18,0x30,0x60,0x78,0x00,0x00,0x00, 0x00,0x00,0x3c,0x3c,0x3c,0x3c,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff // 256 Cursor }; public static byte[] FontData = { @@ -1048,7 +1051,7 @@ namespace Microsoft.Singularity.Drivers.S3Trio64 0x00,0x00,0x00,0x00,0x00,0x00,0x7c,0x30,0x18,0x0c,0x78,0x00, // 253 0x00,0x00,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x00,0x00, // 254 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 255 - 0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 254 Cursor + 0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 256 Cursor }; } } diff --git a/base/Drivers/S3Trio64/S3Trio64.csproj b/base/Drivers/S3Trio64/S3Trio64.csproj index 992301e..7174404 100644 --- a/base/Drivers/S3Trio64/S3Trio64.csproj +++ b/base/Drivers/S3Trio64/S3Trio64.csproj @@ -1,8 +1,6 @@  + + + + + + Exe + Vesa + true + + + + + + + + + + + + + diff --git a/base/Drivers/Vesa/Vesa.sg b/base/Drivers/Vesa/Vesa.sg new file mode 100644 index 0000000..5e70545 --- /dev/null +++ b/base/Drivers/Vesa/Vesa.sg @@ -0,0 +1,911 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Vesa.sg +// +// Note: +// + +using System; +using System.Collections; +using System.Configuration.Assemblies; +using System.Runtime.InteropServices; +using System.Runtime.Remoting; +using System.Text; +using System.Threading; + +using Microsoft.SingSharp; +using Microsoft.Contracts; +using Microsoft.Singularity; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Extending; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Configuration; + +using Microsoft.Singularity.V1.Services; +using Microsoft.Singularity.V1.Threads; +using Allocation = Microsoft.Singularity.V1.Services.SharedHeapService.Allocation; + +namespace Microsoft.Singularity.Drivers.Vesa +{ + // create the resource object for CTR to fill in + [DriverCategory] + [Signature("/pnp/vesa")] + internal class VesaResources : DriverCategoryDeclaration + { + [IoMemoryRange(0, Default = 0xf8000000, Length = 0x300000)] + internal IoMemoryRange frameBuffer; + + [ExtensionEndpoint] + internal TRef ec; + + [ServiceEndpoint(typeof(VideoDeviceContract))] + internal TRef video; + + [ServiceEndpoint(typeof(ConsoleDeviceContract))] + internal TRef console; + + // This should have a custom attribute. + internal static VesaResources Values; + + // LTR will create the rest of this class: + + // LTR creates a private constructor so that the app writer can't + // instantiate objects of this class + private VesaResources() + { + // endpoint initialization + ec = new TRef + ((!)(Process.GetStartupEndpoint(0) as ExtensionContract.Exp)); + video = new TRef + ((!)(Process.GetStartupEndpoint(1) as ServiceProviderContract.Exp)); + console = new TRef + ((!)(Process.GetStartupEndpoint(2) as ServiceProviderContract.Exp)); + + // Io Resource initialization + IoConfig config = (IoConfig!)IoConfig.GetConfig(); + + // dynamic resources + frameBuffer = (IoMemoryRange)config.DynamicRanges[0]; + + base(); + } + + static VesaResources() + { + Values = new VesaResources(); + } + } + + public class S3Control + { + private static VesaDevice device; + + public static int Main(String[] args) + { + ExtensionContract.Exp! ec = VesaResources.Values.ec.Acquire(); + ServiceProviderContract.Exp! ve = VesaResources.Values.video.Acquire(); + ServiceProviderContract.Exp! te = VesaResources.Values.console.Acquire(); + + // Create the device + device = new VesaDevice(VesaResources.Values); + device.Initialize(); + + // Signal I/O system that we are initialized. + ec.SendSuccess(); + + // create a set of all client endpoints connected to the video + // interface. + ESet vs + = new ESet(); + // create a set of all client endpoints connected to the text + // interface. + ESet ts + = new ESet(); + + try { + for (bool run = true; run;) { + switch receive { + ///////////////////////////////////// I/O System Messages. + case ec.Shutdown(): + ec.SendAckShutdown(); + run = false; + break; + + /////////////////////////////// Service Provider Messages. + case ve.Connect(candidate): + VideoDeviceContract.Exp newClient + = candidate as VideoDeviceContract.Exp; + if (newClient != null) { + newClient.SendSuccess(); + vs.Add(newClient); + ve.SendAckConnect(); + } + else { + ve.SendNackConnect(candidate); + } + break; + + case te.Connect(candidate): + ConsoleDeviceContract.Exp newClient + = candidate as ConsoleDeviceContract.Exp; + if (newClient != null) { + newClient.SendSuccess(); + ts.Add(newClient); + te.SendAckConnect(); + } + else { + te.SendNackConnect(candidate); + } + break; + + ////////////////////////////////// Video Service Messages. + case ep.Plot(x, y, color32) in vs: + device.Plot(x, y, new RGB(color32)); + ep.SendAckPlot(); + vs.Add(ep); + break; + + case ep.Fill(x1, y1, x2, y2, color32) in vs: + device.Fill(x1, y1, x2, y2, new RGB(color32)); + ep.SendAckFill(); + vs.Add(ep); + break; + + case ep.BitBltBmp(x, y, buffer) in vs: + device.BitBltBmp(x, y, (!)buffer); + ep.SendAckBitBltBmp(buffer); + vs.Add(ep); + break; + + case ep.BitBltPng(x, y, buffer) in vs: + device.BitBltPng(x, y, (!)buffer); + ep.SendAckBitBltPng(buffer); + vs.Add(ep); + break; + + case ep.Scroll(x1, y1, x2, y2, dy) in vs: + device.Scroll(x1, y1, x2, y2, dy); + ep.SendAckScroll(); + vs.Add(ep); + break; + + case ep.ChannelClosed() in vs: + Tracing.Log(Tracing.Debug, "Client channel closes."); + delete ep; + break; + + //////////////////////////////// Console Service Messages. + case ep.Clear() in ts: + device.Window.Clear(); + ep.SendAckClear(); + ts.Add(ep); + break; + + case ep.ClearCursorToEndOfLine() in ts: + device.Window.ClearCursorToEndOfLine(); + ep.SendAckClearCursorToEndOfLine(); + ts.Add(ep); + break; + + case ep.Write(buffer, offset, count) in ts: + if (offset + count <= buffer.Length) { + device.Window.Write(buffer, offset, count); + ep.SendAckWrite(buffer); + } + else { + ep.SendNakWrite(buffer); + } + ts.Add(ep); + break; + + case ep.PutChar(c) in ts: + device.Window.PutChar(c); + ep.SendAckPutChar(); + ts.Add(ep); + break; + + case ep.PutCharAt(c, column, row) in ts: + if (device.Window.PutCharAt(c, column, row)) { + ep.SendAckPutCharAt(); + } + else { + ep.SendInvalidPosition(); + } + ts.Add(ep); + break; + + case ep.GetDisplayDimensions() in ts: + ep.SendDisplayDimensions(device.Window.TextColumns, + device.Window.TextRows); + ts.Add(ep); + break; + + case ep.GetCursorPosition() in ts: + ep.SendCursorPosition(device.Window.TextColumn, + device.Window.TextRow); + ts.Add(ep); + break; + + case ep.SetCursorPosition(int column, int row) in ts: + if (device.Window.SetTextCursor(column, row)) { + ep.SendAckSetCursorPosition(); + } + else { + ep.SendInvalidPosition(); + } + ts.Add(ep); + break; + + case ep.SetCursorSize(cursorSize) in ts: + ep.SendNotSupported(); + ts.Add(ep); + break; + + case ep.CursorFlash() in ts: + device.Window.CursorFlash(); + ep.SendAckCursorFlash(); + ts.Add(ep); // add client endpoint back into set. + break; + + case ep.CursorHide() in ts: + device.Window.CursorHide(); + ep.SendAckCursorHide(); + ts.Add(ep); // add client endpoint back into set. + break; + + case ep.CursorShow() in ts: + device.Window.CursorShow(); + ep.SendAckCursorShow(); + ts.Add(ep); // add client endpoint back into set. + break; + + case ep.ChannelClosed() in ts: + Tracing.Log(Tracing.Debug, "Client channel closes."); + delete ep; + break; + + case unsatisfiable: + Tracing.Log(Tracing.Debug, "Unsatisfiable."); + run = false; + break; + } + } + } + finally { + Tracing.Log(Tracing.Debug, "Vesa finished message pump."); + } + + // Close the device + device.Finalize(); + + Tracing.Log(Tracing.Audit, "Shutdown"); + delete ec; + delete te; + delete ve; + vs.Dispose(); + ts.Dispose(); + + return 0; + } + } + + public class VesaDevice + { + private SvgaWindow console; + private bool Ready; + + public const int BytesPerPixel = 2; + public const int ScreenWidth = 1024; + public const int ScreenStride = BytesPerPixel * ScreenWidth; + public const int ScreenHeight = 768; + private ushort[]! lineBuffer; + private IoMemory! screenBuffer; // Screen buffer within PCI-mapped memory. + + internal VesaDevice(VesaResources! res) + { + // Allow access to first 2MB for device's memory. + screenBuffer = (!)res.frameBuffer.MemoryAtOffset(0, 0x180000, Access.ReadWrite); + +#if DONT + DebugStub.WriteLine("Vesa linear buffer at {0:x}", + __arglist((uint)screenBuffer.PhysicalAddress.Value)); +#endif + + lineBuffer = new ushort [2048]; + + base(); + + Ready = true; + } + + public void Initialize() + { +#if DONT + DebugStub.Print("Initializing S3 Display\n"); +#endif + + Fill(0, 0, ScreenWidth - 1, ScreenHeight - 1, RGB.Black); + + console = new SvgaWindow(this, 12, 12, ScreenWidth - 13, ScreenHeight - 13); + + console.Write("Singularity VESA Driver\n"); + console.Write(""); + console.Write(""); + console.Write(""); + } + + public SvgaWindow! Window + { + get { return console; } + } + + public void Finalize() + { + Ready = false; + } + + public void Plot(int x, int y, RGB color) + { + if (!Ready) { + return; + } + + int offset = y * ScreenStride + x * BytesPerPixel; + + // The read triggers the latch register. + screenBuffer.Write16(offset, (ushort)color); + + } + + public void Fill(int x1, int y1, int x2, int y2, RGB color) + { + if (!Ready) { + return; + } + + if (x1 < 0 || x1 >= ScreenWidth || + x2 < 0 || x2 >= ScreenWidth || + y1 < 0 || y1 >= ScreenHeight || + y2 < 0 || y2 >= ScreenHeight) + { + + throw new OverflowException("Draw bounds invalid."); + } + + ushort clr = (ushort)color; + int pDst = y1 * ScreenStride + x1 * BytesPerPixel; + for (int y = y1; y <= y2; y++) { + screenBuffer.Write16(pDst, clr, x2 - x1 + 1); + pDst += ScreenStride; + } + } + + public void BitBltChr(int x, int y, int width, int height, + byte[]! buffer, int offset, int ScanWidth, + RGB color, RGB background) + { + if (!Ready) { + return; + } + + ushort clr = (ushort)color; + ushort bkg = (ushort)background; + + int pSrc = offset; + int pDst = (y + height - 1) * ScreenStride + x * BytesPerPixel; + + for (int j = 0; j < height; j++) { + int pSrcTemp = pSrc; + + for (int i = 0; i < width;) { + byte b = buffer[pSrcTemp++]; + + lineBuffer[i++] = ((b & 0x80) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x40) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x20) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x10) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x08) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x04) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x02) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x01) != 0) ? clr : bkg; + } + screenBuffer.Write16(pDst, lineBuffer, 0, width); + + pSrc += ScanWidth; + pDst -= ScreenStride; + } + } + private void BitBlt24(int x, int y, int width, int height, + byte[]! in ExHeap buffer, int offset, int ScanWidth) + + { + int pDst = (y + height - 1) * ScreenStride + x * BytesPerPixel; + int pSrc = offset; + byte[] raw = new byte[ScanWidth]; + + for (int j = 0; j < height; j++) { + int pRaw = 0; + + // Decompress first + while (pRaw < ScanWidth) { + ushort len = (ushort)(buffer[pSrc] + ((ushort)buffer[pSrc+1] << 8)); + pSrc += 2; + + short val = unchecked((short)len); + if (val < 0) { + for (int i = 0; i > val; i--) { + raw[pRaw++] = buffer[pSrc+0]; + raw[pRaw++] = buffer[pSrc+1]; + raw[pRaw++] = buffer[pSrc+2]; + } + pSrc += 3; + } + else { + for (int i = 0; i < val; i++) { + raw[pRaw++] = buffer[pSrc++]; + } + } + } + + // Now draw the decompressed bits. + pRaw = 0; + for (int i = 0; i < width;) { + lineBuffer[i++] = RGB.Compute16(raw[pRaw+2], + raw[pRaw+1], + raw[pRaw+0]); + pRaw += 3; + } + + screenBuffer.Write16(pDst, lineBuffer, 0, width); + + pDst -= ScreenStride; + } + } + + private void BitBlt8(int x, int y, int width, int height, + byte[]! in ExHeap buffer, int offset, int ScanWidth, + ushort[]! palette) + { + int pDst = (y + height - 1) * ScreenStride + x * BytesPerPixel; + int pSrc = offset; + + for (int j = 0; j < height; j++) { + int pSrcTemp = pSrc; + + for (int i = 0; i < width;) { + lineBuffer[i++] = palette[buffer[pSrcTemp++]]; + } + screenBuffer.Write16(pDst, lineBuffer, 0, width); + + pSrc += ScanWidth; + pDst -= ScreenStride; + } + } + + private void BitBlt4(int x, int y, int width, int height, + byte[]! in ExHeap buffer, int offset, int ScanWidth, + ushort[]! palette) + { + int pDst = (y + height - 1) * ScreenStride + x * BytesPerPixel; + int pSrc = offset; + + for (int j = 0; j < height; j++) { + int pSrcTemp = pSrc; + + for (int i = 0; i < width;) { + byte b = buffer[pSrcTemp++]; + + lineBuffer[i++] = palette[b >> 4]; + lineBuffer[i++] = palette[b & 0xf]; + } + screenBuffer.Write16(pDst, lineBuffer, 0, width); + + pSrc += ScanWidth; + pDst -= ScreenStride; + } + } + + public void BitBlt1(int x, int y, int width, int height, + byte[]! in ExHeap buffer, int offset, int ScanWidth, + RGB color, RGB background) + { + if (!Ready) { + return; + } + + ushort clr = (ushort)color; + ushort bkg = (ushort)background; + + int pSrc = offset; + int pDst = (y + height - 1) * ScreenStride + x * BytesPerPixel; + + for (int j = 0; j < height; j++) { + int pSrcTemp = pSrc; + + for (int i = 0; i < width;) { + byte b = buffer[pSrcTemp++]; + + lineBuffer[i++] = ((b & 0x80) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x40) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x20) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x10) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x08) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x04) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x02) != 0) ? clr : bkg; + lineBuffer[i++] = ((b & 0x01) != 0) ? clr : bkg; + } + screenBuffer.Write16(pDst, lineBuffer, 0, width); + + pSrc += ScanWidth; + pDst -= ScreenStride; + } + } + + public void BitBltBmp(int x, int y, byte[]! in ExHeap buffer) + { + if (!Ready) { + return; + } + + try { + BITMAPFILEHEADER bfh; + BITMAPINFOHEADER bih; + int lDelta; + int cbScanLine; + int used; + int offset = 0; + + bfh = BITMAPFILEHEADER.Read(buffer, offset, out used); + bih = BITMAPINFOHEADER.Read(buffer, used, out used); + RGB[] Palette = bih.ReadPalette(buffer, used, out used); + + if (bih.biWidth == 0 || bih.biHeight == 0) { + return; + } + + if (x < 0) { + x = ScreenWidth + x - bih.biWidth; + } + if (y < 0) { + y = ScreenHeight + y - bih.biHeight; + } + + if (x < 0 || x + bih.biWidth > ScreenWidth || + y < 0 || y + bih.biHeight > ScreenHeight) + { + + throw new OverflowException("Draw bounds invalid."); + } + + offset = offset + bfh.bfOffBits; + used = offset; + + ushort[] palette = null; + + if (Palette != null) { + palette = new ushort [Palette.Length]; + for (int i = 0; i < Palette.Length; i++) { + palette[i] = (ushort)Palette[i]; + } + } + + // + // Make sure this is a 1bpp, 4bpp, or 8bpp bitmap. + // + + if (bih.biPlanes != 1) { + DebugStub.Print("biPlanes != 1"); + return; + } + + cbScanLine = (((bih.biWidth * bih.biBitCount) + 31) & ~31) / 8; + + if (bih.biHeight < 0) { + DebugStub.Print("bih.biHeight = {0} < 0\n", + __arglist(bih.biHeight)); + return; + } + +#if DONT + if (used + cbScanLine * bih.biHeight > buffer.Length) { + DebugStub.Print("{0} + {1} * {2} = {3} > {4}\n", + __arglist(used, + cbScanLine, + bih.biHeight, + used + cbScanLine * bih.biHeight, + buffer.Length)); + + throw new OverflowException("Bitmap invalid."); + } +#endif + + if (bih.biBitCount == 1) { + BitBlt1(x, y, bih.biWidth, bih.biHeight, buffer, offset, cbScanLine, + RGB.White, RGB.Black); + } + else if (bih.biBitCount == 4) { + assert palette != null; + BitBlt4(x, y, bih.biWidth, bih.biHeight, buffer, offset, cbScanLine, + palette); + } + else if (bih.biBitCount == 8) { + assert palette != null; + BitBlt8(x, y, bih.biWidth, bih.biHeight, buffer, offset, cbScanLine, + palette); + } + else if (bih.biBitCount == 24) { + BitBlt24(x, y, bih.biWidth, bih.biHeight, buffer, offset, cbScanLine); + } + else { + // + // We don't support this type of bitmap. + // + DebugStub.Print("((bih.biBitCount * bih.biPlanes) <= 4"); + } + } + catch (Exception e) { + DebugStub.WriteLine("Exception in BitBltBmp: {0}", __arglist(e.ToString())); + } + } + + public void BitBltPng(int x, int y, byte[]! in ExHeap buffer) + { + DebugStub.Print("BitBltPng not supported currently.\n"); + } + + public void Scroll(int x1, int y1, int x2, int y2, int CharHeight) + { + if (!Ready) { + return; + } + + if (x1 < 0 || x1 >= ScreenWidth || + x2 < 0 || x2 >= ScreenWidth || + y1 < 0 || y1 >= ScreenHeight || + y2 < 0 || y2 >= ScreenHeight || + y2 - y1 < CharHeight) + { + + throw new OverflowException("Draw bounds invalid."); + } + + int width = x2 - x1 + 1; + int pDst = y1 * ScreenStride + x1 * BytesPerPixel; + int pSrc = pDst + ScreenStride * CharHeight; + + for (int i = y1; i <= y2 - CharHeight; i++) { + screenBuffer.Copy16(pSrc, pDst, width); + + pDst += ScreenStride; + pSrc += ScreenStride; + } + } + } + + public class SvgaWindow + { + private readonly VesaDevice screen; + private readonly int regionLeft; + private readonly int regionTop; + private readonly int regionRight; + private readonly int regionBottom; + + private int cursorX = 0; + private int cursorY = 0; + private bool cursorVisible = true; + private bool cursorView = true; + private RGB foreColor = RGB.White; + private RGB backColor = RGB.Black; + + public const int FONT_HEIGHT = 12; + public const int FONT_WIDTH = 8; + + [NotDelayed] + public SvgaWindow(VesaDevice! screen, int x1, int y1, int x2, int y2) + requires x1 < x2 && y1 < y2; + { + this.screen = screen; + + regionLeft = x1; + regionTop = y1; + regionRight = x2; + regionBottom = y2; + + cursorX = regionLeft; + cursorY = regionTop; + + screen.Fill(regionLeft, regionTop, regionRight, regionBottom, backColor); + } + + public void Initialize() + { + } + + public void Finalize() + { + } + + public void SetTextColor(RGB color, RGB background) + { + foreColor = color; + backColor = background; + } + + public int TextColumn + { + get { return (cursorX - regionLeft) / FONT_WIDTH; } + } + + public int TextRow + { + get { return (cursorY - regionTop) / FONT_HEIGHT; } + } + + public bool SetTextCursor(int column, int row) + { + if (column >= 0 && column < TextColumns && + row >= 0 && row < TextRows) { + bool wasVisible = cursorVisible; + CursorHide(); + cursorX = regionLeft + column * FONT_WIDTH; + cursorY = regionTop + row * FONT_HEIGHT; + if (wasVisible) { + CursorShow(); + } + return true; + } + return false; + } + + public int TextColumns + { + get { return (regionRight - regionLeft) / FONT_WIDTH; } + } + + public int TextRows + { + get { return (regionBottom - regionTop) / FONT_HEIGHT; } + } + + private void DrawCursor() + { + cursorView = true; + CursorFlash(); + } + + public void CursorHide() + { + cursorView = false; + CursorFlash(); + cursorVisible = false; + } + + public void CursorShow() + { + cursorVisible = true; + DrawCursor(); + } + + public void CursorFlash() + { + if (cursorVisible) { + int x = cursorX; + int y = cursorY; + + if (x + FONT_WIDTH > regionRight) { + x -= FONT_WIDTH; + } + + if (cursorView) { + screen.BitBltChr(x, y, FONT_WIDTH, FONT_HEIGHT, + Fonts.FontData, 256 * FONT_HEIGHT, 1, foreColor, backColor); + } + else { + screen.BitBltChr(x, y, FONT_WIDTH, FONT_HEIGHT, + Fonts.FontData, 32 * FONT_HEIGHT, 1, foreColor, backColor); + } + cursorView = !cursorView; + } + } + + public void Clear() + { + screen.Fill(regionLeft, regionTop, regionRight, regionBottom, backColor); + cursorX = regionLeft; + cursorY = regionTop; + DrawCursor(); + } + + public void ClearCursorToEndOfLine() + { + screen.Fill(cursorX, cursorY, + regionRight, cursorY + FONT_HEIGHT, + backColor); + } + + public void Write(String! s) + { + for (int i = 0; i < s.Length; i++) { + Write((byte)s[i]); + } + DrawCursor(); + } + + public void Write(char[]! in ExHeap buffer, int offset, int count) + { + while (count-- > 0) { + Write((byte)buffer[offset++]); + } + DrawCursor(); + } + + public void PutChar(char c) + { + Write((byte)c); + DrawCursor(); + } + + public bool PutCharAt(char c, int column, int row) + { + if (column >= 0 && column < TextColumns && + row >= 0 && row < TextRows) { + screen.BitBltChr(regionLeft + column * FONT_WIDTH, + regionTop + row * FONT_HEIGHT, + FONT_WIDTH, FONT_HEIGHT, + Fonts.FontData, c * FONT_HEIGHT, 1, + foreColor, backColor); + return true; + } + return false; + } + + private void Write(byte c) + { + switch (c) { + + case (byte)'\n': + cursorY += FONT_HEIGHT; + cursorX = regionLeft; + break; + + case (byte)'\r': + screen.Fill(cursorX, cursorY, regionRight, + cursorY + FONT_HEIGHT, + backColor); + cursorX = regionLeft; + break; + + case (byte)'\b': + CursorHide(); + cursorX = cursorX - FONT_WIDTH; + CursorShow(); + break; + + default: + screen.BitBltChr(cursorX, cursorY, FONT_WIDTH, FONT_HEIGHT, + Fonts.FontData, c * FONT_HEIGHT, 1, foreColor, backColor); + + cursorX += FONT_WIDTH; + break; + } + + if (cursorX + FONT_WIDTH > regionRight) { + cursorY += FONT_HEIGHT; + cursorX = regionLeft; + } + + if (cursorY + FONT_HEIGHT > regionBottom) { + screen.Scroll(regionLeft, regionTop, regionRight, regionBottom, + FONT_HEIGHT); + cursorY = cursorY - FONT_HEIGHT; + screen.Fill(regionLeft, cursorY, regionRight, regionBottom, backColor); + } + } + + } +} diff --git a/base/Drivers/VolumeManager/VolumeManager.csproj b/base/Drivers/VolumeManager/VolumeManager.csproj index f6517c3..e2e591c 100644 --- a/base/Drivers/VolumeManager/VolumeManager.csproj +++ b/base/Drivers/VolumeManager/VolumeManager.csproj @@ -1,8 +1,6 @@  + + + + + + + + + + diff --git a/base/Imported/Bartok/library/singularity/System/Runtime/CompilerServices/Attributes.cs b/base/Imported/Bartok/library/singularity/System/Runtime/CompilerServices/Attributes.cs new file mode 100644 index 0000000..b55a66f --- /dev/null +++ b/base/Imported/Bartok/library/singularity/System/Runtime/CompilerServices/Attributes.cs @@ -0,0 +1,55 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Runtime.CompilerServices; +using Microsoft.Singularity.Audit; + +namespace System.Runtime.CompilerServices { + + [AllCallees][NoAllocInstr] + public sealed partial class NoHeapAllocationAttribute : Attribute { + } + + [AllCallees] // must be recursive + public sealed partial class NoBarriersAttribute: Attribute { + } +} + +namespace System.Runtime.InteropServices +{ + public enum GCOption { + NONE = 0, + GCFRIEND = 1, + NOGC = 2, + NOSTGC = 3, + } + + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + [RequiredByBartok] + public sealed class GCAnnotationAttribute : Attribute { + internal GCOption _options; + public GCAnnotationAttribute(GCOption options ) { + _options = options; + } + } +} + + // special meta-attributes used to flag attributes which audit.exe looks at + +namespace Microsoft.Singularity.Audit +{ + public class AllCalleesAttribute : Attribute + { + public AllCalleesAttribute() {} + } + public class NoAllocInstrAttribute : Attribute + { + public NoAllocInstrAttribute() {} + } + public class LayerAttribute : Attribute + { + public LayerAttribute(int n) {} + } +} diff --git a/base/Imported/Bartok/library/singularity/System/RuntimeType.cs b/base/Imported/Bartok/library/singularity/System/RuntimeType.cs new file mode 100644 index 0000000..c7e3cc9 --- /dev/null +++ b/base/Imported/Bartok/library/singularity/System/RuntimeType.cs @@ -0,0 +1,193 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// __RuntimeType is the basic Type object representing classes as found in the +// system. This type is never creatable by users, only by the system itself. +// The internal structure is known about by the runtime. __RuntimeXXX classes +// are created only once per object in the system and support == comparisons. +// + +namespace System +{ + + using Microsoft.Bartok.Runtime; + using System; + using System.Reflection; + using System.Runtime.InteropServices; + using System.Runtime.CompilerServices; + using System.Text; + using Thread = System.Threading.Thread; + using SystemType = Microsoft.Singularity.V1.Types.SystemType; + + internal sealed partial class RuntimeType : Type + { + public override int GetHashCode() { + return ((int)this.classVtable.arrayOf << 8 + rank) + + (int)this.classVtable.structuralView; + } + + // Return the name of the class. The name does not contain the namespace. + public override String ToString(){ + return InternalGetProperlyQualifiedName(); + } + + private String InternalGetProperlyQualifiedName() { + // See also Lightning\Src\VM\COMClass.cpp::GetProperName + return FullName; + } + + // GetInterfacesInternal + // A version of GetInterfaces to be used by trusted code. It does not + // copy the array before returning it. + + protected Type[] GetInterfacesInternal() { + return this.interfaces; + } + + //////////////////////////////////////////////////////////////////////////////// + // + // Attributes + // + // The attributes are all treated as read-only properties on a class. Most of + // these boolean properties have flag values defined in this class and act like + // a bit mask of attributes. There are also a set of boolean properties that + // relate to the classes relationship to other classes and to the state of the + // class inside the runtime. + // + //////////////////////////////////////////////////////////////////////////////// + + [NoHeapAllocation] + protected override bool HasElementTypeImpl() + { + return (IsArray); + } + + // Return the underlying Type that represents the IReflect Object. For expando object, + // this is the (Object) IReflectInstance.GetType(). For Type object it is this. + public override Type UnderlyingSystemType { + [NoHeapAllocation] + get {return this;} + } + + [NoHeapAllocation] + internal override TypeCode GetTypeCodeInternal() + { + switch (classVtable.structuralView) { + case StructuralType.Bool: + return TypeCode.Boolean; + case StructuralType.Char: + return TypeCode.Object; + case StructuralType.Int8: + return TypeCode.SByte; + case StructuralType.Int16: + return TypeCode.Int16; + case StructuralType.Int32: + return TypeCode.Int32; + case StructuralType.Int64: + return TypeCode.Int64; + case StructuralType.UInt8: + return TypeCode.Byte; + case StructuralType.UInt16: + return TypeCode.UInt16; + case StructuralType.UInt32: + return TypeCode.UInt32; + case StructuralType.UInt64: + return TypeCode.UInt64; + case StructuralType.Float32: + return TypeCode.Single; + case StructuralType.Float64: + return TypeCode.Double; + default: + return TypeCode.Object; + } + } + + public override SystemType GetSystemType() { + if (SystemType.IsNull(this.systemType)) { + // initialize it + if (this.baseType == null) { + this.systemType = SystemType.RootSystemType(); + } + else { + SystemType baseSt = this.baseType.GetSystemType(); + long lower, upper; + string fullname = this.FullName; + // for now compute an MD5 over the full name + +#if SINGULARITY_PROCESS + unsafe { + byte[] nameArray = + RuntimeTypeHash.ComputeHashAndReturnName(fullname, + out lower, + out upper); + fixed(byte* dataptr = &nameArray[0]) { + char* name = (char*)dataptr; + this.systemType = SystemType.Register(name, + fullname.Length, + lower, upper, baseSt); + } + } +#else + RuntimeTypeHash.ComputeHash(fullname, out lower, out upper); + this.systemType = SystemType.Register(fullname, + lower, + upper, + baseSt); +#endif + } + } + return this.systemType; + } + + } + + + // Pulled these methods out of the main class in order to give + // the IoSystem access to the ComputeHash function when prebinding endpoints + public class RuntimeTypeHash + { + public static void ComputeHash(string fullname, + out long lower, + out long upper) + { + ComputeHashAndReturnName(fullname, out lower, out upper); + } + + unsafe internal static byte[] ComputeHashAndReturnName(string fullname, + out long lower, + out long upper) + { + byte[] data = new byte[fullname.Length*sizeof(char)]; + String.InternalCopy(fullname, data, fullname.Length); + ComputeHash(data, out lower, out upper); + return data; + } + /// + /// Needs to be replaced with real hash computed over the + /// signature of the type + /// + unsafe internal static void ComputeHash(byte[] fullname, + out long lower, + out long upper) + { + byte[] md5 = new Microsoft.Singularity.Crypto.MD5().Hash(fullname); + + lower = ConvertToLong(md5, 0); + upper = ConvertToLong(md5, 8); + } + + private static long ConvertToLong(byte[] data, int start) { + long result = data[start]; + for (int i = 1; i < 8; i++) { + result = result << 8; + result |= data[start+i]; + } + return result; + } + } +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/ARCSCollector.cs b/base/Imported/Bartok/runtime/shared/GCs/ARCSCollector.cs new file mode 100644 index 0000000..5db1a9b --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/ARCSCollector.cs @@ -0,0 +1,683 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + + +// #define DEBUG + +namespace System.GCs { + + using Microsoft.Bartok.Options; + using Microsoft.Bartok.Runtime; + using System.Runtime.InteropServices; + using System.Runtime.CompilerServices; + using System.Threading; + using System.Collections; + + [NoCCtor] + [RequiredByBartok] + internal class ARCSCollector : SingleThreadedRCCollector { + [PreInitRefCounts] + public new static void Initialize() { + SingleThreadedRCCollector.Initialize(); + + incrementer = + (RefCountIncrementer)BootstrapMemory. + Allocate(typeof(RefCountIncrementer)); + decrementer = + (RefCountDecrementer)BootstrapMemory. + Allocate(typeof(RefCountDecrementer)); + + instance = + (ARCSCollector)BootstrapMemory. + Allocate(typeof(ARCSCollector)); + } + + [NoInline] + [ManualRefCounts] + public override void VerifyHeap(bool beforeCollection) { + preVerifyHeap(); + + // Deallocate objects on the delayed deallocation list. + purgeDeallocationList(decrementer); + + postVerifyHeap(beforeCollection); + } + + + [MixinConditional("ARCSGC")] + [Mixin(typeof(Object))] + internal class ARCSGCObject : System.Object { + internal new TrialDeletion.PreHeader preHeader; + } + + + internal override void EnableHeap() { + } + + internal override void DestructHeap() { + if (VTable.enableGCVerify) { + VerifyHeap(true); + } + base.DestructHeap(); + if (VTable.enableGCProfiling) { + if (RCCollector.ProfilingMode) { + EmitRefCountsProfile(); + } + } + } + + [NoInline] + [InlineIntoOnce] + [ManualRefCounts] + [RequiredByBartok] + internal static unsafe void NonNullIncrementRefCount(Object obj) { + uint refState = BasicIncrementRefCount(obj); + + td.IncrementCycleProcessing(refState, obj); + } + + // Exclude the object from leaked cycle processing. + if ((refState & RSMasks.acyclicFlag) != 0) { +#if DEBUG + PLCLink* pLinkAddr = td.getPLCLink(obj); + VTable.Assert(pLinkAddr == null, + @"pLinkAddr == null"); +#endif // DEBUG + } else { + PLCLink* pLinkAddr = td.getPLCLink(obj); + // If the object hasn't already been removed from + // the PLC ("potentially leaked cycle") list, remove it. + if (pLinkAddr != null) { + // Reset the PLC link pointer in the object. + td.setPLCLink(obj, null); + td.removeFromPLCList(pLinkAddr); + } + } + } + + [Inline] + [InlineIntoOnce] + [ManualRefCounts] + [RequiredByBartok] + internal static void AcyclicIncrementRefCount(Object obj) { + if (obj == null) { + return; + } + + NonNullAcyclicIncrementRefCount(obj); + } + + [Inline] + [InlineIntoOnce] + [ManualRefCounts] + [RequiredByBartok] + internal static void NonNullAcyclicIncrementRefCount(Object obj) { + uint refState = BasicIncrementRefCount(obj); + + VTable.Assert((refState & RSMasks.acyclicFlag) != 0, + @"(refState & RSMasks.acyclicFlag) != 0"); + } + + [Inline] + [ManualRefCounts] + [RequiredByBartok] + internal static uint BasicIncrementRefCount(Object obj) { + VTable.Assert(obj != null && + (obj.REF_STATE & RSMasks.refCount) < + RSMasks.refCount, + @"obj != null && + (obj.REF_STATE & RSMasks.refCount) < + RSMasks.refCount"); + + uint refState = obj.REF_STATE; + if ((refState & countingONFlagMask) == 0) { + return; + } + VTable.Assert(refState != 0, + @"refState != 0"); + + obj.REF_STATE = refState+1; + + return refState; + } + + + [NoInline] + [ManualRefCounts] + [RequiredByBartok] + internal static unsafe void NonNullDecrementRefCount(Object obj) { + VTable.Assert(obj != null && + (obj.REF_STATE & RSMasks.refCount) > 0, + @"obj != null && + (obj.REF_STATE & RSMasks.refCount) > 0"); + + uint refState = obj.REF_STATE; + if ((refState & RSMasks.refCount) == 1) { + VTable.Assert((refState & RSMasks.countingFlag) != 0, + @"(refState & RSMasks.countingFlag) != 0"); + + MultiUseWord muw = MultiUseWord.GetForObject(obj); + if (muw.IsMonitorOrInflatedTag()) { + MultiUseWord.RefCountGCDeadObjHook(muw); + } + + // Set aside the object for a delayed deallocation. + deallocateLazily(obj); + } else if ((refState & RSMasks.countingFlag) == 0) { + return; + } + + obj.REF_STATE = refState-1; + } + + [NoInline] + [InlineIntoOnce] + [ManualRefCounts] + [RequiredByBartok] + internal static unsafe void DecrementRefCount(Object obj) { + if (obj == null) { + return; + } + + NonNullDecrementRefCount(obj); + } + + + [RequiredByBartok] + internal static void IncrementReferentRefCounts(UIntPtr objAddr, + VTable vtable) { + UpdateReferentRefCounts(objAddr, vtable, incrementer); + } + + [RequiredByBartok] + internal static void DecrementReferentRefCounts(UIntPtr objAddr, + VTable vtable) { + UpdateReferentRefCounts(objAddr, vtable, decrementer); + } + + [ManualRefCounts] + internal static unsafe void IncrementReferentRefCounts + (UIntPtr objAddr, + VTable vtable, + int start, + int span) { + UpdateReferentRefCounts(objAddr, vtable, start, span, + incrementer); + } + + [ManualRefCounts] + internal static unsafe void DecrementReferentRefCounts + (UIntPtr objAddr, + VTable vtable, + int start, + int span) { + UpdateReferentRefCounts(objAddr, vtable, start, span, + decrementer); + } + + + [Inline] + [ManualRefCounts] + protected static void deallocateObjects() { + deallocateObjects(decrementer); + } + + [NoInline] + [ManualRefCounts] + protected static void purgeDeallocationList() { + deallocateObjects(decrementer); + } + + + [ManualRefCounts] + private static UIntPtr getDfsFinishingTime(Object obj) { + return ((RCGCVerificationObject)obj).preHeader. + dfsFinishingTime; + } + + [ManualRefCounts] + private static void setDfsFinishingTime(Object obj, + UIntPtr time) { + ((RCGCVerificationObject)obj).preHeader.dfsFinishingTime = + time; + } + + + private class RefCountIncrementer : NonNullReferenceVisitor { + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + Object obj = Magic.fromAddress(objAddr); + IncrementRefCount(obj); + } + } + + private class RefCountDecrementer : NonNullReferenceVisitor { + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + Object obj = Magic.fromAddress(objAddr); + DecrementRefCount(obj); + } + } + + + private abstract class ObjectVisitor : + SegregatedFreeList.ObjectVisitor { + [ManualRefCounts] + internal override void VisitSmall(Object obj, + UIntPtr memAddr) { + this.Visit(obj); + } + + [ManualRefCounts] + internal override UIntPtr VisitLarge(Object obj) { + return this.Visit(obj); + } + + internal abstract override UIntPtr Visit(Object obj); + } + + + private class BackupInitializer : ObjectVisitor { + [ManualRefCounts] + internal override UIntPtr Visit(Object obj) { + setBackupRefcount(obj, UIntPtr.Zero); + + VTable vtable = obj.vtable; + return ObjectLayout. + ObjectSize(Magic.addressOf(obj), vtable); + } + } + + private class BackupRefCount : NonNullReferenceVisitor { + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr addr = *loc; + + UIntPtr page = PageTable.Page(addr); + if (!PageTable.IsGcPage(page)) { + PageType pageType = PageTable.Type(page); + VTable.Assert(pageType == PageType.NonGC || + pageType == PageType.Stack, + @"pageType == PageType.NonGC || + pageType == PageType.Stack"); + + return; + } + + UIntPtr objAddr = SegregatedFreeList.Find(addr); + incrementBackupRefCount.Traverse(objAddr); + } + } + + private class IncrementBackupRefCount : NonNullReferenceVisitor { + [ManualRefCounts] + internal void Traverse(UIntPtr objAddr) { + Object obj = Magic.fromAddress(objAddr); + UIntPtr count = getBackupRefcount(obj); + setBackupRefcount(obj, count+1); + if (obj.GcMark((UIntPtr)1)) { + this.VisitReferenceFields(obj); + } + while (!this.workList.IsEmpty) { + objAddr = this.workList.Read(); + obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + } + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr addr = *loc; + + UIntPtr page = PageTable.Page(addr); + if (!PageTable.IsGcPage(page)) { + PageType pageType = PageTable.Type(page); + VTable.Assert(pageType == PageType.NonGC || + pageType == PageType.Stack, + @"pageType == PageType.NonGC || + pageType == PageType.Stack"); + + return; + } + + UIntPtr objAddr = SegregatedFreeList.Find(addr); + Object obj = Magic.fromAddress(objAddr); + UIntPtr count = getBackupRefcount(obj); + setBackupRefcount(obj, count+1); + if (obj.GcMark((UIntPtr)1)) { + this.workList.Write(objAddr); + } + } + + private UIntPtrQueue workList; + } + + private class RootsScanner : NonNullReferenceVisitor { + internal void Initialize(NonNullReferenceVisitor v) { + this.visitor = v; + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr pageLoc = PageTable.Page((UIntPtr)loc); + PageType pageType = PageTable.Type(pageLoc); + if (pageType != PageType.NonGC && + pageType != PageType.Stack) { + VTable.Assert(PageTable.IsGcPage(pageLoc), + @"PageTable.IsGcPage(pageLoc)"); + + return; + } + + uint addr = (uint)*loc; + if (pageType == PageType.NonGC || (addr & 0x03) == 0) { + this.visitor.Visit(loc); + } + if (pageType == PageType.Stack) { + *loc = (UIntPtr)(addr | 0x01); + } + } + + NonNullReferenceVisitor visitor; + } + + private class ResetRoots : NonNullReferenceVisitor { + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr pageLoc = PageTable.Page((UIntPtr)loc); + PageType pageType = PageTable.Type(pageLoc); + if (pageType != PageType.NonGC && + pageType != PageType.Stack) { + VTable.Assert(PageTable.IsGcPage(pageLoc), + @"PageTable.IsGcPage(pageLoc)"); + + return; + } + + if (pageType == PageType.Stack) { + *loc = (UIntPtr)((uint)*loc & 0xfffffffc); + } + } + } + + private class LeakAccumulator : ObjectVisitor { + internal UIntPtr Size; + + internal void Initialize() { + this.Size = UIntPtr.Zero; + } + + [ManualRefCounts] + internal override unsafe UIntPtr Visit(Object obj) { + VTable vtable = obj.vtable; + UIntPtr size = + ObjectLayout.ObjectSize(Magic.addressOf(obj), vtable); + + uint refState = obj.REF_STATE; + UIntPtr refCount = (UIntPtr)(refState & RSMasks.refCount); + if ((refState & RSMasks.countingFlag) != 0 && + refCount > 0) { + // This object is considered live by the + // RC collector. + UIntPtr count = getBackupRefcount(obj); + if (count == 0) { + // But it is actually unreachable. + this.Size += size; + } + } + + return size; + } + } + + private class LeakedNodesDFS : ObjectVisitor { + [ManualRefCounts] + internal override unsafe UIntPtr Visit(Object obj) { + VTable vtable = obj.vtable; + UIntPtr size = + ObjectLayout.ObjectSize(Magic.addressOf(obj), vtable); + + uint refState = obj.REF_STATE; + UIntPtr refCount = (UIntPtr)(refState & RSMasks.refCount); + if ((refState & RSMasks.countingFlag) != 0 && + refCount > 0) { + UIntPtr count = getBackupRefcount(obj); + if (count == 0) { + UIntPtr objAddr = Magic.addressOf(obj); + dfsMarker.Visit(&objAddr); + } + } + + return size; + } + } + + private class DFSMarker : NonNullReferenceVisitor { + internal void Initialize() { + this.time = UIntPtr.Zero; + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + + UIntPtr page = PageTable.Page(objAddr); + if (!PageTable.IsGcPage(page)) { + PageType pageType = PageTable.Type(page); + VTable.Assert(pageType == PageType.NonGC || + pageType == PageType.Stack, + @"pageType == PageType.NonGC || + pageType == PageType.Stack"); + + return; + } + + Object obj = Magic.fromAddress(objAddr); + if (obj.GcMark((UIntPtr)1)) { + this.time = this.time+1; + setDfsDiscoveryTime(obj, this.time); + + UIntPtr vtableAddr = Magic.addressOf(obj.vtable); + this.Visit(&vtableAddr); + this.VisitReferenceFields(obj); + + this.time = this.time+1; + setDfsFinishingTime(obj, this.time); + } + } + + private UIntPtr time; + } + + private class LeakedCycleClosure : ObjectVisitor { + [ManualRefCounts] + internal override unsafe UIntPtr Visit(Object obj) { + VTable vtable = obj.vtable; + UIntPtr size = + ObjectLayout.ObjectSize(Magic.addressOf(obj), vtable); + + uint refState = obj.REF_STATE; + UIntPtr refCount = (UIntPtr)(refState & RSMasks.refCount); + if ((refState & RSMasks.countingFlag) != 0 && + refCount > 0) { + UIntPtr count = getBackupRefcount(obj); + if (count == 0) { + UIntPtr dTime = getDfsDiscoveryTime(obj); + UIntPtr fTime = getDfsFinishingTime(obj); + cycleClosure.Initialize(dTime, fTime); + cycleClosure.VisitReferenceFields(obj); + } + } + + return size; + } + } + + private class CycleClosure : NonNullReferenceVisitor { + internal void Initialize(UIntPtr dTime, UIntPtr fTime) { + this.predDiscoveryTime = dTime; + this.predFinishingTime = fTime; + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + + UIntPtr page = PageTable.Page(objAddr); + if (!PageTable.IsGcPage(page)) { + PageType pageType = PageTable.Type(page); + VTable.Assert(pageType == PageType.NonGC || + pageType == PageType.Stack, + @"pageType == PageType.NonGC || + pageType == PageType.Stack"); + + return; + } + + Object obj = Magic.fromAddress(objAddr); + UIntPtr dTime = getDfsDiscoveryTime(obj); + UIntPtr fTime = getDfsFinishingTime(obj); + VTable.Assert(this.predDiscoveryTime > UIntPtr.Zero && + this.predFinishingTime > UIntPtr.Zero && + dTime > UIntPtr.Zero && + fTime > UIntPtr.Zero, + @"this.predDiscoveryTime > UIntPtr.Zero && + this.predFinishingTime > UIntPtr.Zero && + dTime > UIntPtr.Zero && + fTime > UIntPtr.Zero"); + + if (dTime < this.predDiscoveryTime && + this.predDiscoveryTime < this.predFinishingTime && + this.predFinishingTime < fTime) { + // A back edge is incident on this node; + // therefore, the node is part of a cycle. + backupRefCount.Visit(&objAddr); + } + } + + private UIntPtr predDiscoveryTime; + private UIntPtr predFinishingTime; + } + + private class ResetTraversal : ObjectVisitor { + [ManualRefCounts] + internal override unsafe UIntPtr Visit(Object obj) { + obj.GcMark(UIntPtr.Zero); + VTable vtable = obj.vtable; + UIntPtr size = + ObjectLayout.ObjectSize(Magic.addressOf(obj), vtable); + + return size; + } + } + + private class BFSMarker : NonNullReferenceVisitor { + internal void Initialize(bool isVisited) { + this.isVisited = (UIntPtr)(isVisited ? 1 : 0); + } + + [ManualRefCounts] + internal void Traverse(Object obj) { + this.VisitReferenceFields(obj); + while (!this.workList.IsEmpty) { + UIntPtr objAddr = this.workList.Read(); + obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + } + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr addr = *loc; + + UIntPtr page = PageTable.Page(addr); + if (!PageTable.IsGcPage(page)) { + PageType pageType = PageTable.Type(page); + VTable.Assert(pageType == PageType.NonGC || + pageType == PageType.Stack, + @"pageType == PageType.NonGC || + pageType == PageType.Stack"); + + return; + } + + UIntPtr objAddr = SegregatedFreeList.Find(addr); + Object obj = Magic.fromAddress(objAddr); + if (obj.GcMark(this.isVisited)) { + this.workList.Write(objAddr); + } + } + + private UIntPtr isVisited; + private UIntPtrQueue workList; + } + + private class LeakedRoots : ObjectVisitor { + internal void Initialize() { + bfsMarker.Initialize(true); + } + + [ManualRefCounts] + internal override unsafe UIntPtr Visit(Object obj) { + VTable vtable = obj.vtable; + UIntPtr size = + ObjectLayout.ObjectSize(Magic.addressOf(obj), vtable); + + uint refState = obj.REF_STATE; + UIntPtr refCount = (UIntPtr)(refState & RSMasks.refCount); + if ((refState & RSMasks.countingFlag) != 0 && + refCount > 0) { + UIntPtr count = getBackupRefcount(obj); + if (count == 0 && obj.GcMark() == UIntPtr.Zero) { + bfsMarker.Traverse(obj); + } + } + + return size; + } + } + + private class LeakedRootsCounter : ObjectVisitor { + internal uint Total; + + internal void Initialize() { + this.Total = 0; + } + + [ManualRefCounts] + internal override unsafe UIntPtr Visit(Object obj) { + VTable vtable = obj.vtable; + UIntPtr size = + ObjectLayout.ObjectSize(Magic.addressOf(obj), + vtable); + + uint refState = obj.REF_STATE; + UIntPtr refCount = (UIntPtr)(refState & RSMasks.refCount); + if ((refState & RSMasks.countingFlag) != 0 && + refCount > 0) { + UIntPtr count = getBackupRefcount(obj); + if (count == 0 && obj.GcMark() == UIntPtr.Zero) { + this.Total++; + } + } + + return size; + } + } + } + private static NonNullReferenceVisitor incrementer; + private static NonNullReferenceVisitor decrementer; + +} + + diff --git a/base/Imported/Bartok/runtime/shared/GCs/AbortingCoCoBarrier.cs b/base/Imported/Bartok/runtime/shared/GCs/AbortingCoCoBarrier.cs new file mode 100644 index 0000000..4051021 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/AbortingCoCoBarrier.cs @@ -0,0 +1,498 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using Microsoft.Bartok.Runtime; + using Microsoft.Bartok.Options; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using System.Threading; + + internal unsafe class AbortingCoCoBarrier: CoCoBarrier { + + [MixinConditional("AbortingCoCo")] + [Mixin(typeof(PreHeader))] + internal struct CoCoPreHeader { + internal MultiUseWord muw; + [SelfPoint] + [NoBarriers] + internal UIntPtr CoCoWord; + } + + [MixinConditional("AbortingCoCo")] + [Mixin(typeof(Object))] + internal class CoCoObject: System.Object { + internal new CoCoPreHeader preHeader; + } + + internal static CoCoObject MixinObject(Object o) { + return (CoCoObject)o; + } + + internal static Thread mainThread; + + internal override void InitLateStub() + { + CoCoWordOffset = + (UIntPtr)Magic.toPointer(ref MixinObject(interlock).preHeader.CoCoWord) + - Magic.addressOf(interlock); + + mainThread = Thread.CurrentThread; + } + + internal AbortingCoCoBarrier() + { + } + + internal static new AbortingCoCoBarrier instance; + + [NoBarriers] + internal static new void Initialize() + { + CoCoBarrier.instance = AbortingCoCoBarrier.instance = + (AbortingCoCoBarrier) + BootstrapMemory.Allocate(typeof(AbortingCoCoBarrier)); + } + + /////////////////////////// forwarding word states + + // the CoCoWord contains the forwarding pointer, plus one bit: + // + // Copying: + // This is the lowest-order bit. If it is zero, then the object is + // not currently being copied. This is the default state. If it + // is one, it means that copying activity is on-going with this + // object as the source. To write to the object, the copying bit + // must first be cleared by the mutator. + // + // If the object gets forwarded (i.e. the forwarding pointer is not + // a self-pointer), the copying bit gets locked in with + // a value of 0 (indicating not-copying). Note + // that it is illegal to set the copying bit to 1 + // if the forwarding pointer is not a self-pointer. + + [ForceInline] + internal static UIntPtr ForwardPtr(UIntPtr CoCoWord) + { + return CoCoWord&~(UIntPtr)3; + } + + [ForceInline] + internal static bool IsCopying(UIntPtr CoCoWord) + { + return (CoCoWord&(UIntPtr)1) != 0; + } + + [ForceInline] + [NoBarriers] + internal static bool IsForwarded(UIntPtr CoCoWord, + Object o) + { + return ForwardPtr(CoCoWord) != Magic.addressOf(o); + } + + [ForceInline] + internal static UIntPtr WithForwardPtr(UIntPtr CoCoWord, + UIntPtr forward) + { + return (CoCoWord&(UIntPtr)1)|forward; + } + + [ForceInline] + internal static UIntPtr WithForward(UIntPtr forward) + { + return forward; + } + + [ForceInline] + [NoBarriers] + internal static UIntPtr WithNoForward(Object o, + bool copying) + { + return WithCopying(Magic.addressOf(o),copying); + } + + [ForceInline] + internal static UIntPtr WithNoForwardCopying(Object o) + { + return WithNoForward(o,true); + } + + [ForceInline] + internal static UIntPtr WithNoForwardNotCopying(Object o) + { + return WithNoForward(o,false); + } + + [ForceInline] + internal static UIntPtr WithCopying(UIntPtr CoCoWord, + bool copying) + { + if (copying) { + return CoCoWord|(UIntPtr)1; + } else { + return CoCoWord&~(UIntPtr)1; + } + } + + /////////////////////////// overriding CoCo and Barrier functionality + + [NoBarriers] + internal override bool ObjectIsNotCopied(Object o) + { + // BUGBUG: get rid of this method. it is meaninless. + return true; + } + + [NoBarriers] + [Inline] + internal override bool IsInToSpace(UIntPtr ptr) + { + Object o = Magic.fromAddress(ptr); + return !IsForwarded(MixinObject(o).preHeader.CoCoWord,o); + } + + [NoBarriers] + [Inline] + internal override UIntPtr ToSpaceImplBeforeReadyNonNull(Object o) + { + return ForwardPtr(MixinObject(o).preHeader.CoCoWord); + } + + [ForceInline] + [NoBarriers] + protected override void InitObjectImpl(Object o,VTable vtable) + { + MyInitObject(o, vtable); + } + + [ForceInline] + [NoBarriers] + internal static new void BootstrapInitObjectImpl(Object o, + VTable vtable) + { + MyInitObject(o, vtable); + } + + [ForceInline] + [NoBarriers] + private static void MyInitObject(Object o, VTable vtable) + { + MixinObject(o).preHeader.CoCoWord = WithNoForwardNotCopying(o); + o.vtable = vtable; + } + + [Inline] + internal override UIntPtr DoPin(UIntPtr address, + Pinner pinner) + { + UIntPtr baseAddr=FindObjectForInteriorPtr(address); + Object o=Magic.fromAddress(baseAddr); + UIntPtr offset=address-baseAddr; + o=Pin(o,pinner); + return Magic.addressOf(o)+offset; + } + + [NoBarriers] + [Inline] + internal override UIntPtr ToSpaceImplNonNull(Object o) + { + return ForwardPtr(MixinObject(o).preHeader.CoCoWord); + } + + [ForceInline] + [NoBarriers] + protected override Object ForwardImpl(Object o,int mask) + { + if ((mask & BarrierMask.Forward.Nullable)!=0 && o == null) { + return null; + } else { + UIntPtr CoCoWord = MixinObject(o).preHeader.CoCoWord; + if ((mask & BarrierMask.Forward.Writable)!=0 && IsCopying(CoCoWord)) { + return ForwardWritableSlow(o); + } else { + return Magic.fromAddress(ForwardPtr(CoCoWord)); + } + } + } + + [NoInline] + [CalledRarely] + internal static Object ForwardWritableSlow(Object o) + { + return Pin(o,Pinner.Barrier); + } + + [NoBarriers] + internal static bool EqImpl(Object a,Object b) + { + return a == b + || (ToSpaceBeforeReadyImpl(a) + == ToSpaceBeforeReadyImpl(b)); + } + + [NoBarriers] + [Inline] + protected override bool EqImpl(Object a,Object b, + int mask) + { + return EqImpl(a,b); + } + + [NoBarriers] + internal static Object Pin(Object o, + Pinner pinner) + { + if (fAbortVerboseDebug) { + VTable.DebugPrint("Aborter: requested pinning on "); + VTable.DebugPrint((ulong)Magic.addressOf(o)); + VTable.DebugPrint(" in thread "); + VTable.DebugPrint((ulong)Magic.addressOf(Thread.CurrentThread)); + VTable.DebugPrint(" with pinner = "); + VTable.DebugPrint((int)pinner); + VTable.DebugPrint("\n"); + } + UIntPtr oldCoCoWord= + CAS(ref MixinObject(o).preHeader.CoCoWord, + WithNoForwardNotCopying(o), + WithNoForwardCopying(o)); + if (!IsForwarded(oldCoCoWord,o)) { + // the object is not forwarded - nothing further to do. + // (we know that it must now be aborted, since if it + // was, then that couldn't have changed; and if it wasn't, + // then our CAS would have succeeded.) + if (fAbortVerboseDebug && IsCopying(oldCoCoWord)) { + VTable.DebugPrint("Aborter: aborted copying on "); + VTable.DebugPrint((ulong)Magic.addressOf(o)); + VTable.DebugPrint(" in thread "); + VTable.DebugPrint((ulong)Magic.addressOf(Thread.CurrentThread)); + VTable.DebugPrint("\n"); + } + if (fBreakOnAbort && + Thread.CurrentThread!=mainThread && + pinner == Pinner.Barrier) { + VTable.DebugBreak(); + } + return o; + } else { + VTable.Assert(pinner == Pinner.Barrier, + "Encountered a forwarded object in a pin "+ + "request that did not originate from the "+ + "barrier"); + if (fAbortVerboseDebug) { + VTable.DebugPrint("Aborter: encountered forwarded object "+ + "at "); + VTable.DebugPrint((ulong)Magic.addressOf(o)); + VTable.DebugPrint(" in thread "); + VTable.DebugPrint((ulong)Magic.addressOf(Thread.CurrentThread)); + VTable.DebugPrint("\n"); + } + return Magic.fromAddress(ForwardPtr(oldCoCoWord)); + } + } + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected override Object AtomicSwapImpl(ref Object reference, + Object value, + int mask) + { + TargetWithForwardAndSourceNoForwardBarrier(ref reference,value); + return Interlocked.Exchange(ref reference,value); + } + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected override Object + AtomicCompareAndSwapImpl(ref Object reference, + Object newValue, + Object comparand, + int mask) + { + TargetWithForwardAndSourceNoForwardBarrier(ref reference,newValue); + for (;;) { + Object oldValue = reference; + Object myNewValue; + if (EqImpl(oldValue, comparand)) { + myNewValue = newValue; + } else { + myNewValue = oldValue; + } + if (Interlocked.CompareExchange(ref reference, + myNewValue, oldValue) + == oldValue) { + return oldValue; + } + } + } + + [ForceInline] + [NoBarriers] + protected override void WriteImpl(UIntPtr *location, + Object value, + int mask) + { + UIntPtr valueBits=Magic.addressOf(value); + TargetWithForwardAndSourceNoForwardBarrier(location,valueBits); + *location=valueBits; + } + + [ForceInline] + [NoBarriers] + protected override void WriteImplByRef(ref Object location, + Object value, + int mask) + { + TargetWithForwardAndSourceNoForwardBarrier(ref location,value); + location=value; + } + + class TagNode { + internal Object from; + internal Object to; + internal TagNode next; + } + + static TagNode tagHead; + static ulong nTagged; + + internal override bool AnyTaggedForCopying + { + get { + return tagHead!=null; + } + } + + internal override bool TagObjectForCopy(Object from, + Object to, + out UIntPtr spaceOverhead) + { + TagNode tn=new TagNode(); + spaceOverhead=ObjectLayout.Sizeof(tn); + tn.next=tagHead; + tn.from=from; + tn.to=to; + tagHead=tn; + nTagged++; + return true; + } + + [NoBarriers] + internal override void PinningEnabledHook() + { + if (fAbortDebug) { + VTable.DebugPrint("Aborter: un-aborting "); + VTable.DebugPrint(nTagged); + VTable.DebugPrint(" objects.\n"); + } + for (TagNode n=tagHead; + n!=null; + n=n.next) { + if (fAbortVerboseDebug) { + VTable.DebugPrint("Aborter: un-aborting "); + VTable.DebugPrint((ulong)Magic.addressOf(n.from)); + VTable.DebugPrint("\n"); + } + UIntPtr oldCoCoWord= + CAS(ref MixinObject(n.from).preHeader.CoCoWord, + WithNoForwardCopying(n.from), + WithNoForwardNotCopying(n.from)); + VTable.Assert(!IsCopying(oldCoCoWord)); + VTable.Assert(!IsForwarded(oldCoCoWord,n.from)); + } + if (fAbortDebug) { + VTable.DebugPrint("Aborter: un-aborted "); + VTable.DebugPrint(nTagged); + VTable.DebugPrint(" objects.\n"); + } + } + + internal override bool NeedsPrepPhase + { + get { + return false; + } + } + + internal override bool PinOffsetPointers { + [Inline] + get { return true; } + } + + [NoBarriers] + internal override ulong Copy() + { + ulong cnt=0; + + TagNode myTagHead=tagHead; + ulong myNTagged=nTagged; + tagHead=null; + nTagged=0; + + if (fAbortDebug) { + VTable.DebugPrint("Aborter: copying "); + VTable.DebugPrint(myNTagged); + VTable.DebugPrint(" objects.\n"); + } + + // first do all of the copying + for (TagNode n=myTagHead; + n!=null; + n=n.next) { + Util.MemCopy(Magic.addressOf(n.to)-PreHeader.Size, + Magic.addressOf(n.from)-PreHeader.Size, + ObjectLayout.Sizeof(n.from)); + // fix the forwarding word in the to-space object (without + // this it'll point at from-space) + MixinObject(n.to).preHeader.CoCoWord=WithNoForwardNotCopying(n.to); + } + + if (fAbortDebug) { + VTable.DebugPrint("Aborter: copied "); + VTable.DebugPrint(myNTagged); + VTable.DebugPrint(" objects.\n"); + } + + // now attempt to forward all objects. + for (TagNode n=myTagHead; + n!=null; + n=n.next) { + UIntPtr oldCoCoWord= + CAS(ref MixinObject(n.from).preHeader.CoCoWord, + WithForward(Magic.addressOf(n.to)), + WithNoForwardCopying(n.from)); + VTable.Assert(!IsForwarded(MixinObject(n.to).preHeader.CoCoWord, + n.to)); + if (oldCoCoWord == WithNoForwardCopying(n.from)) { + // copy successful + cnt++; + } + } + + if (fAbortDebug) { + VTable.DebugPrint("Aborter: forwarded "); + VTable.DebugPrint(cnt); + VTable.DebugPrint(" objects.\n"); + } + + return cnt; + } + + private static bool fAbortDebug { get { return false; } } + private static bool fAbortVerboseDebug { get { return false; } } + private static bool fBreakOnAbort { get { return false; } } + } + +} + diff --git a/base/Imported/Bartok/runtime/shared/GCs/AdaptiveCopyingCollector.cs b/base/Imported/Bartok/runtime/shared/GCs/AdaptiveCopyingCollector.cs new file mode 100644 index 0000000..4ad126b --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/AdaptiveCopyingCollector.cs @@ -0,0 +1,68 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + + +namespace System.GCs { + + using System.Collections; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using System.Threading; + +#if SINGULARITY + using Microsoft.Singularity; +#endif + [NoCCtor] + internal class AdaptiveCopyingCollector: GenerationalCollector + { + + internal static AdaptiveCopyingCollector instance; + + private AdaptiveCopyingCollector() { + } + + public static new void Initialize() { + GenerationalCollector.Initialize(); + SemispaceCollector.Initialize(); + SlidingCollector.Initialize(); + // instance = new AdaptiveCopyingCollector(); + instance = (AdaptiveCopyingCollector ) + BootstrapMemory.Allocate(typeof(AdaptiveCopyingCollector)); + } + + internal override void TruncateOlderAllocationAreas(int generation) { + SemispaceCollector.instance.TruncateOlderAllocationAreas(generation); + SlidingCollector.instance.TruncateOlderAllocationAreas(generation); + } + + internal override void CollectGeneration(int generation, + UIntPtr generationPageCount) + { + UIntPtr availableMemory = (UIntPtr) + (MemoryManager.MemorySize - MemoryManager.OperatingSystemSize); + UIntPtr softPageCountLimit = PageTable.PageCount(availableMemory); + if (generation == (int)MAX_GENERATION && + (generationPageCount << 1) > softPageCountLimit) { + // Use sliding collector when fromSpace > 1/2 available memory + SlidingCollector.instance.CollectGeneration(generation, generationPageCount); + } else { + SemispaceCollector.instance.CollectGeneration(generation, generationPageCount); + } + } + + internal override void EnableHeap() { + SlidingCollector.instance.EnableHeap(); + SemispaceCollector.instance.EnableHeap(); + } + + } + +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/AllCardsWriteBarrier.cs b/base/Imported/Bartok/runtime/shared/GCs/AllCardsWriteBarrier.cs new file mode 100644 index 0000000..d7557ac --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/AllCardsWriteBarrier.cs @@ -0,0 +1,143 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + + +namespace System.GCs { + + using Microsoft.Bartok.Runtime; + using System.Runtime.CompilerServices; + using System.Threading; + + //[NoBarriers] + internal unsafe class AllCardsWriteBarrier : RefWriteBarrier + { + + internal static AllCardsWriteBarrier instance; + + [NoBarriers] + internal static new void Initialize() { + AllCardsWriteBarrier.instance = (AllCardsWriteBarrier) + BootstrapMemory.Allocate(typeof(AllCardsWriteBarrier)); + } + + [Inline] + [NoBarriers] + protected override void StoreStaticFieldImpl(ref Object staticField, + Object value, + int mask) + { + // No need to mark the card for a static field. + staticField = value; + } + + protected override void CopyStructImpl(Object srcObj, + Object dstObj, + VTable vtable, + UIntPtr srcPtr, + UIntPtr dstPtr) + { + CopyStructWithBarrier(vtable, srcPtr, dstPtr); + } + + [Inline] + protected override Object AtomicSwapImpl(ref Object reference, + Object value, + int mask) + { + UIntPtr resultAddr = + Interlocked.Exchange(Magic.toPointer(ref reference), + Magic.addressOf(value)); + RecordReference(Magic.toPointer(ref reference), value); + return Magic.fromAddress(resultAddr); + } + + [Inline] + protected override + Object AtomicCompareAndSwapImpl(ref Object reference, + Object newValue, + Object comparand, + int mask) + { + UIntPtr resultAddr = + Interlocked.CompareExchange(Magic.toPointer(ref reference), + Magic.addressOf(newValue), + Magic.addressOf(comparand)); + RecordReference(Magic.toPointer(ref reference), newValue); + return Magic.fromAddress(resultAddr); + } + + [Inline] + protected override void CloneImpl(Object srcObject, Object dstObject) + { + CloneNoBarrier(srcObject, dstObject); + RecordClone(dstObject); + } + + // 'offset' is not relative to the lower bound, but is a count + // of elements from the first element in the array. + protected override void ArrayZeroImpl(Array array, + int offset, + int length) + { + ArrayZeroNoBarrier(array, offset, length); + } + + // 'offset' is not relative to the lower bound, but is a count + // of elements from the first element in the array. + protected override void ArrayCopyImpl(Array srcArray, int srcOffset, + Array dstArray, int dstOffset, + int length) + { + if ((length > 1000) || ((length << 2) >= dstArray.Length)) { + ArrayCopyNoBarrier(srcArray, srcOffset, + dstArray, dstOffset, + length); + RecordClone(dstArray); + } else { + ArrayCopyWithBarrier(srcArray, srcOffset, + dstArray, dstOffset, + length); + } + } + + [Inline] + protected override void WriteImpl(UIntPtr *location, + Object value, + int mask) + { + *location = Magic.addressOf(value); + RecordReference(location, value); + } + + [Inline] + protected override void WriteImplByRef(ref Object location, + Object value, + int mask) + { + WriteImpl(Magic.toPointer(ref location), value, mask); + } + + [Inline] + private static void RecordClone(Object clone) { + GenerationalGCData.installedRemSet.RecordClonedObject(clone); + } + + [Inline] + private static void RecordReference(UIntPtr *location, + Object value) + { + GenerationalGCData. + installedRemSet.RecordReference(location, value); + } + + } + +} diff --git a/base/Kernel/Bartok/GCs/Allocator.cs b/base/Imported/Bartok/runtime/shared/GCs/Allocator.cs similarity index 80% rename from base/Kernel/Bartok/GCs/Allocator.cs rename to base/Imported/Bartok/runtime/shared/GCs/Allocator.cs index b3fef3a..a31930e 100644 --- a/base/Kernel/Bartok/GCs/Allocator.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/Allocator.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,9 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// namespace System.GCs { @@ -16,7 +17,7 @@ namespace System.GCs using System.Runtime.CompilerServices; [NoCCtor] - internal class Allocator + internal abstract class Allocator { [Inline] @@ -83,35 +84,44 @@ namespace System.GCs return startAddr; } - [NoHeapAllocation] - internal static unsafe UIntPtr * GetObjectVTableAddress - (UIntPtr potentialObject) { - UIntPtr vtableAddr = potentialObject + Magic.OffsetOfVTable; - return (UIntPtr *) vtableAddr; - } - - [NoHeapAllocation] - internal static unsafe UIntPtr GetObjectVTable - (UIntPtr potentialObject) { - return *GetObjectVTableAddress(potentialObject); - } - - [NoHeapAllocation] - internal static unsafe void SetObjectVTable(UIntPtr potentialObject, - UIntPtr vtable) { - *GetObjectVTableAddress(potentialObject) = vtable; - } - - [NoHeapAllocation] - internal static bool IsZeroVTable(UIntPtr addr) { - return GetObjectVTable(addr) == UIntPtr.Zero; - } - - private static UIntPtr ALIGNMENT_TOKEN{ + private static UIntPtr ALIGNMENT_TOKEN + { [Inline] [NoHeapAllocation] get { return ~((UIntPtr)3u); } } + + // REVIEW: Consider moving to BartokObject --Bjarne + [NoHeapAllocation] + internal static unsafe + UIntPtr * GetObjectVTableAddress(UIntPtr potentialObject) + { + UIntPtr vtableAddr = potentialObject + Magic.OffsetOfVTable; + return (UIntPtr *) vtableAddr; + } + + // REVIEW: Consider moving to BartokObject --Bjarne + [NoHeapAllocation] + internal static unsafe UIntPtr GetObjectVTable(UIntPtr potentialObject) + { + return *GetObjectVTableAddress(potentialObject); + } + + // REVIEW: Consider moving to BartokObject --Bjarne + [NoHeapAllocation] + internal static unsafe void SetObjectVTable(UIntPtr potentialObject, + UIntPtr vtable) + { + *GetObjectVTableAddress(potentialObject) = vtable; + } + + // REVIEW: Consider moving to BartokObject --Bjarne + [NoHeapAllocation] + internal static bool IsZeroVTable(UIntPtr addr) + { + return GetObjectVTable(addr) == UIntPtr.Zero; + } + } } diff --git a/base/Imported/Bartok/runtime/shared/GCs/Barrier.cs b/base/Imported/Bartok/runtime/shared/GCs/Barrier.cs new file mode 100644 index 0000000..e63e3c4 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/Barrier.cs @@ -0,0 +1,4563 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + + +namespace System.GCs +{ + using Microsoft.Bartok.Runtime; + using System.Runtime.CompilerServices; + using System.Threading; + + //[CCtorIsRunDuringStartup] + //[NoBarriers] + [NoCCtor] + internal abstract unsafe class Barrier + { + + [TrustedNonNull] + [NoBarriers] + protected internal static Barrier installedBarrier; + + [TrustedNonNull] + private static CopyFieldsVisitor copyFieldsVisitor; + + [TrustedNonNull] + private static ZeroFieldsVisitor zeroFieldsVisitor; + + [TrustedNonNull] + private static SlowCopyFieldsVisitor slowCopyFieldsVisitor; + + [TrustedNonNull] + private static SlowZeroFieldsVisitor slowZeroFieldsVisitor; + + [NoBarriers] + [NoHeapAllocation] + internal static void PreInitialize() + { +#if !SINGULARITY || CONCURRENT_MS_COLLECTOR + if (GC.wbType == WBType.CMS) { + // We need a write barrier even if we haven't set up enough of the memory + // system to support allocating from bootstrap memory yet. + installedBarrier = WriteBarrierCMS.MakeEarlyInstance(); + } +#endif + } + + [NoBarriers] + [PreInitRefCounts] + internal static void Initialize() + { + switch (GC.wbType) { +#if !SINGULARITY || MARK_SWEEP_COLLECTOR || NULL_COLLECTOR + case WBType.noWB: { + EmptyWriteBarrier.Initialize(); + installedBarrier = EmptyWriteBarrier.instance; + break; + } +#endif +#if !SINGULARITY || SEMISPACE_COLLECTOR || SLIDING_COLLECTOR || ADAPTIVE_COPYING_COLLECTOR + case WBType.Generational: { + GenerationalWriteBarrier.Initialize(); + installedBarrier = GenerationalWriteBarrier.instance; + break; + } +#endif +#if !SINGULARITY || CONCURRENT_MS_COLLECTOR + case WBType.CMS: { + WriteBarrierCMS.Initialize(); + installedBarrier = WriteBarrierCMS.instance; + break; + } +#endif +#if !SINGULARITY || ATOMIC_RC_COLLECTOR + case WBType.ARC: { + AtomicRCWriteBarrier.Initialize(); + installedBarrier = AtomicRCWriteBarrier.instance; + break; + } +#endif +#if !SINGULARITY || SEMISPACE_COLLECTOR || SLIDING_COLLECTOR || ADAPTIVE_COPYING_COLLECTOR + case WBType.AllCards: { + AllCardsWriteBarrier.Initialize(); + installedBarrier = AllCardsWriteBarrier.instance; + break; + } +#endif +#if !SINGULARITY + case WBType.ExpandingCoCo: { + ExpandingCoCoBarrier.Initialize(); + installedBarrier = ExpandingCoCoBarrier.instance; + break; + } + case WBType.ProbabilisticCoCo: { + ProbabilisticCoCoBarrier.Initialize(); + installedBarrier = ProbabilisticCoCoBarrier.instance; + break; + } + case WBType.AbortingCoCo: { + AbortingCoCoBarrier.Initialize(); + installedBarrier = AbortingCoCoBarrier.instance; + break; + } + case WBType.BrooksTest: { + BrooksBarrierTest.Initialize(); + installedBarrier = BrooksBarrierTest.instance; + break; + } + case WBType.BrooksCMSTest: { + BrooksCMSBarrierTest.Initialize(); + installedBarrier = BrooksCMSBarrierTest.instance; + break; + } +#endif + default: { + VTable.NotReached("Unknown write barrier type: "+GC.wbType); + break; + } + } + // copyFieldsVisitor = new CopyFieldsVisitor(); + Barrier.copyFieldsVisitor = (CopyFieldsVisitor) + BootstrapMemory.Allocate(typeof(CopyFieldsVisitor)); + // zeroFieldsVisitor = new ZeroFieldsVisitor(); + Barrier.zeroFieldsVisitor = (ZeroFieldsVisitor) + BootstrapMemory.Allocate(typeof(ZeroFieldsVisitor)); + // slowCopyFieldsVisitor = new SlowCopyFieldsVisitor(); + Barrier.slowCopyFieldsVisitor = (SlowCopyFieldsVisitor) + BootstrapMemory.Allocate(typeof(SlowCopyFieldsVisitor)); + // slowZeroFieldsVisitor = new SlowZeroFieldsVisitor(); + Barrier.slowZeroFieldsVisitor = (SlowZeroFieldsVisitor) + BootstrapMemory.Allocate(typeof(SlowZeroFieldsVisitor)); + } + + // NOTE: this code uses ForceInline instead of Inline to indicate that + // inlining should occur even if the caller is huge. In general, this + // attribute should be used with great care. DO NOT USE IT ELSEWHERE IN + // THE RUNTIME UNLESS YOU ARE WILLING TO DOCUMENT YOUR USE IN + // IrSimpleInliner.cs AND Attributes.cs! AND NEVER USE IT IN + // APPLICATION OR OS CODE! + + //////////////////////// StoreIndirectImpl ///////////////////// + + [ForceInline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreIndirectImplByRef(ref Object location, + Object value, + int mask) + { + this.WriteImplByRef(ref location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreIndirectImplByRef(ref float location, + float value, + int mask) + { + this.WriteImplByRef(ref location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreIndirectImplByRef(ref double location, + double value, + int mask) + { + this.WriteImplByRef(ref location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreIndirectImplByRef(ref byte location, + byte value, + int mask) + { + this.WriteImplByRef(ref location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreIndirectImplByRef(ref ushort location, + ushort value, + int mask) + { + this.WriteImplByRef(ref location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreIndirectImplByRef(ref uint location, + uint value, + int mask) + { + this.WriteImplByRef(ref location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreIndirectImplByRef(ref ulong location, + ulong value, + int mask) + { + this.WriteImplByRef(ref location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreIndirectImplByRef(ref UIntPtr location, + UIntPtr value, + int mask) + { + this.WriteImplByRef(ref location, value, mask); + } + + //////////////////////// StoreIndirectImpl to ptr ///////////////////// + + [ForceInline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreIndirectImpl(UIntPtr* location, + Object value, + int mask) + { + this.WriteImpl(location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreIndirectImpl(float* location, + float value, + int mask) + { + this.WriteImpl(location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreIndirectImpl(double* location, + double value, + int mask) + { + this.WriteImpl(location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreIndirectImpl(byte* location, + byte value, + int mask) + { + this.WriteImpl(location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreIndirectImpl(ushort* location, + ushort value, + int mask) + { + this.WriteImpl(location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreIndirectImpl(uint* location, + uint value, + int mask) + { + this.WriteImpl(location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreIndirectImpl(ulong* location, + ulong value, + int mask) + { + this.WriteImpl(location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreIndirectImpl(UIntPtr* location, + UIntPtr value, + int mask) + { + this.WriteImpl(location, value, mask); + } + + //////////////////////// LoadIndirectImpl from ptr ///////////////////// + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual Object LoadObjIndirectImpl(UIntPtr* location, + int mask) + { + return this.ReadObjImpl(location, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual float LoadIndirectImpl(float* location, + int mask) + { + return this.ReadImpl(location, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual double LoadIndirectImpl(double* location, + int mask) + { + return this.ReadImpl(location, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual byte LoadIndirectImpl(byte* location, + int mask) + { + return this.ReadImpl(location, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual ushort LoadIndirectImpl(ushort* location, + int mask) + { + return this.ReadImpl(location, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual uint LoadIndirectImpl(uint* location, + int mask) + { + return this.ReadImpl(location, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual ulong LoadIndirectImpl(ulong* location, + int mask) + { + return this.ReadImpl(location, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual UIntPtr LoadIndirectImpl(UIntPtr* location, + int mask) + { + return this.ReadImpl(location, mask); + } + + //////////////////////// LoadIndirectImpl ///////////////////// + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual Object LoadIndirectImplByRef(ref Object location, + int mask) + { + return this.ReadImplByRef(ref location, mask); + } + + [Inline] + [NoBarriers] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected virtual float LoadIndirectImplByRef(ref float location, + int mask) + { + return this.ReadImplByRef(ref location, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual double LoadIndirectImplByRef(ref double location, + int mask) + { + return this.ReadImplByRef(ref location, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual byte LoadIndirectImplByRef(ref byte location, + int mask) + { + return this.ReadImplByRef(ref location, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual ushort LoadIndirectImplByRef(ref ushort location, + int mask) + { + return this.ReadImplByRef(ref location, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual uint LoadIndirectImplByRef(ref uint location, + int mask) + { + return this.ReadImplByRef(ref location, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual ulong LoadIndirectImplByRef(ref ulong location, + int mask) + { + return this.ReadImplByRef(ref location, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual UIntPtr LoadIndirectImplByRef(ref UIntPtr location, + int mask) + { + return this.ReadImplByRef(ref location, mask); + } + + /////////////////////// StoreObjectFieldImpl /////////////////// + + [ForceInline] + [NoBarriers] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected virtual void StoreObjectFieldImpl(Object obj, + UIntPtr fieldOffset, + Object value, + int mask) + { + this.WriteImpl(obj, fieldOffset, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreObjectFieldImpl(Object obj, + UIntPtr fieldOffset, + float value, + int mask) + { + this.WriteImpl(obj, fieldOffset, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreObjectFieldImpl(Object obj, + UIntPtr fieldOffset, + double value, + int mask) + { + this.WriteImpl(obj, fieldOffset, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreObjectFieldImpl(Object obj, + UIntPtr fieldOffset, + byte value, + int mask) + { + this.WriteImpl(obj, fieldOffset, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreObjectFieldImpl(Object obj, + UIntPtr fieldOffset, + ushort value, + int mask) + { + this.WriteImpl(obj, fieldOffset, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreObjectFieldImpl(Object obj, + UIntPtr fieldOffset, + uint value, + int mask) + { + this.WriteImpl(obj, fieldOffset, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreObjectFieldImpl(Object obj, + UIntPtr fieldOffset, + ulong value, + int mask) + { + this.WriteImpl(obj, fieldOffset, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreObjectFieldImpl(Object obj, + UIntPtr fieldOffset, + UIntPtr value, + int mask) + { + this.WriteImpl(obj, fieldOffset, value, mask); + } + + /////////////////////// LoadObjectFieldImpl /////////////////// + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual Object LoadObjObjectFieldImpl(Object obj, + UIntPtr fieldOffset, + int mask) + { + return this.ReadObjImpl(obj, fieldOffset, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual float LoadFloatObjectFieldImpl(Object obj, + UIntPtr fieldOffset, + int mask) + { + return this.ReadFloatImpl(obj, fieldOffset, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual double LoadDoubleObjectFieldImpl(Object obj, + UIntPtr fieldOffset, + int mask) + { + return this.ReadDoubleImpl(obj, fieldOffset, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual byte LoadByteObjectFieldImpl(Object obj, + UIntPtr fieldOffset, + int mask) + { + return this.ReadByteImpl(obj, fieldOffset, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual ushort LoadUShortObjectFieldImpl(Object obj, + UIntPtr fieldOffset, + int mask) + { + return this.ReadUShortImpl(obj, fieldOffset, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual uint LoadUIntObjectFieldImpl(Object obj, + UIntPtr fieldOffset, + int mask) + { + return this.ReadUIntImpl(obj, fieldOffset, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual ulong LoadULongObjectFieldImpl(Object obj, + UIntPtr fieldOffset, + int mask) + { + return this.ReadULongImpl(obj, fieldOffset, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual + UIntPtr LoadUIntPtrObjectFieldImpl(Object obj, + UIntPtr fieldOffset, + int mask) + { + return this.ReadUIntPtrImpl(obj, fieldOffset, mask); + } + + ////////////////////// StoreStructFieldImpl //////////////////// + + [ForceInline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreStructFieldImpl(UIntPtr structPtr, + UIntPtr fieldOffset, + Object value, + int mask) + { + UIntPtr *fieldPtr = (UIntPtr *) (structPtr + fieldOffset); + this.WriteImpl(fieldPtr, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreStructFieldImpl(UIntPtr structPtr, + UIntPtr fieldOffset, + float value, + int mask) + { + float *fieldPtr = (float *) (structPtr + fieldOffset); + this.WriteImpl(fieldPtr, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreStructFieldImpl(UIntPtr structPtr, + UIntPtr fieldOffset, + double value, + int mask) + { + double *fieldPtr = (double *) (structPtr + fieldOffset); + this.WriteImpl(fieldPtr, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreStructFieldImpl(UIntPtr structPtr, + UIntPtr fieldOffset, + byte value, + int mask) + { + byte *fieldPtr = (byte *) (structPtr + fieldOffset); + this.WriteImpl(fieldPtr, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreStructFieldImpl(UIntPtr structPtr, + UIntPtr fieldOffset, + ushort value, + int mask) + { + ushort *fieldPtr = (ushort *) (structPtr + fieldOffset); + this.WriteImpl(fieldPtr, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreStructFieldImpl(UIntPtr structPtr, + UIntPtr fieldOffset, + uint value, + int mask) + { + uint *fieldPtr = (uint *) (structPtr + fieldOffset); + this.WriteImpl(fieldPtr, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreStructFieldImpl(UIntPtr structPtr, + UIntPtr fieldOffset, + ulong value, + int mask) + { + ulong *fieldPtr = (ulong *) (structPtr + fieldOffset); + this.WriteImpl(fieldPtr, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreStructFieldImpl(UIntPtr structPtr, + UIntPtr fieldOffset, + UIntPtr value, + int mask) + { + UIntPtr *fieldPtr = (UIntPtr *) (structPtr + fieldOffset); + this.WriteImpl(fieldPtr, value, mask); + } + + //////////////////// LoadStructFieldImpl //////////////////// + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual Object LoadObjStructFieldImpl(UIntPtr structPtr, + UIntPtr fieldOffset, + int mask) + { + UIntPtr *fieldPtr = (UIntPtr *) (structPtr + fieldOffset); + return this.ReadObjImpl(fieldPtr, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual float LoadFloatStructFieldImpl(UIntPtr structPtr, + UIntPtr fieldOffset, + int mask) + { + float *fieldPtr = (float *) (structPtr + fieldOffset); + return this.ReadImpl(fieldPtr, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual double LoadDoubleStructFieldImpl(UIntPtr structPtr, + UIntPtr fieldOffset, + int mask) + { + double *fieldPtr = (double *) (structPtr + fieldOffset); + return this.ReadImpl(fieldPtr, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual byte LoadByteStructFieldImpl(UIntPtr structPtr, + UIntPtr fieldOffset, + int mask) + { + byte *fieldPtr = (byte *) (structPtr + fieldOffset); + return this.ReadImpl(fieldPtr, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual ushort LoadUShortStructFieldImpl(UIntPtr structPtr, + UIntPtr fieldOffset, + int mask) + { + ushort *fieldPtr = (ushort *) (structPtr + fieldOffset); + return this.ReadImpl(fieldPtr, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual uint LoadUIntStructFieldImpl(UIntPtr structPtr, + UIntPtr fieldOffset, + int mask) + { + uint *fieldPtr = (uint *) (structPtr + fieldOffset); + return this.ReadImpl(fieldPtr, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual ulong LoadULongStructFieldImpl(UIntPtr structPtr, + UIntPtr fieldOffset, + int mask) + { + ulong *fieldPtr = (ulong *) (structPtr + fieldOffset); + return this.ReadImpl(fieldPtr, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual + UIntPtr LoadUIntPtrStructFieldImpl(UIntPtr structPtr, + UIntPtr fieldOffset, + int mask) + { + UIntPtr *fieldPtr = (UIntPtr *) (structPtr + fieldOffset); + return this.ReadImpl(fieldPtr, mask); + } + + //////////////////// StoreVectorElementImpl //////////////////// + + [ForceInline] + [NoBarriers] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected virtual void StoreVectorElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + Object value, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + this.WriteImpl(vector, offset, value, mask); + + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreVectorElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + float value, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + this.WriteImpl(vector, offset, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreVectorElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + double value, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + this.WriteImpl(vector, offset, value, mask); + } + + [Inline] + [NoBarriers] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected virtual void StoreVectorElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + byte value, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + this.WriteImpl(vector, offset, value, mask); + } + + [Inline] + [AssertDevirtualize] + [NoBarriers] + [NoStackLinkCheckTrans] + protected virtual void StoreVectorElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + ushort value, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + this.WriteImpl(vector, offset, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreVectorElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + uint value, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + this.WriteImpl(vector, offset, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreVectorElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + ulong value, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + this.WriteImpl(vector, offset, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreVectorElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + UIntPtr value, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + this.WriteImpl(vector, offset, value, mask); + } + + //////////////////// LoadVectorElementImpl //////////////////// + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual Object LoadObjVectorElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + return this.ReadObjImpl(vector, offset, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual + float LoadFloatVectorElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + return this.ReadFloatImpl(vector, offset, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual + double LoadDoubleVectorElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + return this.ReadDoubleImpl(vector, offset, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual + byte LoadByteVectorElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + return this.ReadByteImpl(vector, offset, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual + ushort LoadUShortVectorElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + return this.ReadUShortImpl(vector, offset, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual + uint LoadUIntVectorElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + return this.ReadUIntImpl(vector, offset, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual + ulong LoadULongVectorElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + return this.ReadULongImpl(vector, offset, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual + UIntPtr LoadUIntPtrVectorElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + return this.ReadUIntPtrImpl(vector, offset, mask); + } + + //////////////////// StoreArrayElementImpl //////////////////// + + [ForceInline] + [NoBarriers] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected virtual void StoreArrayElementImpl(Array array, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + Object value, + int mask) + { + UIntPtr offset = IndexedFieldOffset(array, index, + arrayElementSize, fieldOffset); + this.WriteImpl(array, offset, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreArrayElementImpl(Array array, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + float value, + int mask) + { + UIntPtr offset = IndexedFieldOffset(array, index, + arrayElementSize, fieldOffset); + this.WriteImpl(array, offset, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreArrayElementImpl(Array array, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + double value, + int mask) + { + UIntPtr offset = IndexedFieldOffset(array, index, + arrayElementSize, fieldOffset); + this.WriteImpl(array, offset, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreArrayElementImpl(Array array, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + byte value, + int mask) + { + UIntPtr offset = IndexedFieldOffset(array, index, + arrayElementSize, fieldOffset); + this.WriteImpl(array, offset, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreArrayElementImpl(Array array, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + ushort value, + int mask) + { + UIntPtr offset = IndexedFieldOffset(array, index, + arrayElementSize, fieldOffset); + this.WriteImpl(array, offset, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreArrayElementImpl(Array array, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + uint value, + int mask) + { + UIntPtr offset = IndexedFieldOffset(array, index, + arrayElementSize, fieldOffset); + this.WriteImpl(array, offset, value, mask); + } + + [Inline] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected virtual void StoreArrayElementImpl(Array array, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + ulong value, + int mask) + { + UIntPtr offset = IndexedFieldOffset(array, index, + arrayElementSize, fieldOffset); + this.WriteImpl(array, offset, value, mask); + } + + [Inline] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual void StoreArrayElementImpl(Array array, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + UIntPtr value, + int mask) + { + UIntPtr offset = IndexedFieldOffset(array, index, + arrayElementSize, fieldOffset); + this.WriteImpl(array, offset, value, mask); + } + + //////////////////// LoadArrayElementImpl //////////////////// + + [Inline] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected virtual Object LoadObjArrayElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + return this.ReadObjImpl(vector, offset, mask); + } + + [Inline] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected virtual + float LoadFloatArrayElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + return this.ReadFloatImpl(vector, offset, mask); + } + + [Inline] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual + double LoadDoubleArrayElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + return this.ReadDoubleImpl(vector, offset, mask); + } + + [Inline] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual + byte LoadByteArrayElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + return this.ReadByteImpl(vector, offset, mask); + } + + [Inline] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual + ushort LoadUShortArrayElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + return this.ReadUShortImpl(vector, offset, mask); + } + + [Inline] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual + uint LoadUIntArrayElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + return this.ReadUIntImpl(vector, offset, mask); + } + + [Inline] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected virtual + ulong LoadULongArrayElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + return this.ReadULongImpl(vector, offset, mask); + } + + [Inline] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected virtual + UIntPtr LoadUIntPtrArrayElementImpl(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + UIntPtr offset = IndexedFieldOffset(vector, index, + arrayElementSize, fieldOffset); + return this.ReadUIntPtrImpl(vector, offset, mask); + } + + ////////////////// StoreStaticFieldImpl /////////////////////// + + [ForceInline] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected virtual void StoreStaticFieldImpl(ref Object staticField, + Object value, + int mask) + { + this.WriteImplByRef(ref staticField, value, mask); + } + + ////////////////// LoadStaticFieldImpl /////////////////////// + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual Object LoadStaticFieldImpl(ref Object staticField, + int mask) + { + return this.ReadImplByRef(ref staticField, mask); + } + + ///////////////////////// AtomicSwapImpl /////////////////////// + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual Object AtomicSwapImpl(ref Object reference, + Object value, + int mask) + { + return Interlocked.Exchange(ref reference,value); + } + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual int AtomicSwapImpl(ref int reference, + int value, + int mask) + { + return Interlocked.Exchange(ref reference,value); + } + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual UIntPtr AtomicSwapImpl(ref UIntPtr reference, + UIntPtr value, + int mask) + { + return Interlocked.Exchange(ref reference,value); + } + + ////////////////////// AtomicCompareAndSwapImpl //////////////// + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual Object + AtomicCompareAndSwapImpl(ref Object reference, + Object newValue, + Object comparand, + int mask) + { + return Interlocked.CompareExchange(ref reference, + newValue,comparand); + } + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual int + AtomicCompareAndSwapImpl(ref int reference, + int newValue, + int comparand, + int mask) + { + return Interlocked.CompareExchange(ref reference, + newValue, comparand); + } + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual long + AtomicCompareAndSwapImpl(ref long reference, + long newValue, + long comparand, + int mask) + { + return Interlocked.CompareExchange(ref reference, + newValue, comparand); + } + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual UIntPtr + AtomicCompareAndSwapImpl(ref UIntPtr reference, + UIntPtr newValue, + UIntPtr comparand, + int mask) + { + return Interlocked.CompareExchange(ref reference, + newValue, comparand); + } + + /////////////////////////// ForwardImpl /////////////////////// + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual Object ForwardImpl(Object o,int mask) + { + return o; + } + + /////////////////////////// MemoryBarrierImpl /////////////////////// + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual void MemoryBarrierImpl(int mask) + { +#if !ARM && !ISA_ARM + // HACK: MemoryBarrier is unimplemented in ARM + // and causes compile-time failures when building + // mscorlib in sepcomp mode. This change will break + // CoCo if built with ARM. + Thread.MemoryBarrier(); +#endif + } + + //////////////////////// PinImpl ////////////////////////////// + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual UIntPtr PinImpl(UIntPtr address, + int mask) + { + return address; + } + + /////////////////////// InitObjectImpl //////////////////////// + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual void InitObjectImpl(Object o,VTable vtable) + { + MyInitObject(o, vtable); + } + + [ForceInline] + [NoBarriers] + protected static void BootstrapInitObjectImpl(Object o, VTable vtable) + { + MyInitObject(o, vtable); + } + + [ForceInline] + [NoBarriers] + private static void MyInitObject(Object o, VTable vtable) + { + o.vtable = vtable; + } + + ///////////////////////// Weak Ref //////////////////////////// + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual Object WeakRefReadImpl(UIntPtr addr, int mask) + { + return Magic.fromAddress(addr); + } + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual UIntPtr WeakRefWriteImpl(Object obj, int mask) + { + return Magic.addressOf(obj); + } + + //////////////////////////// EqImpl /////////////////////////// + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual bool EqImpl(Object a,Object b, int mask) + { + return a==b; + } + + ////////////////////////// fast path selection //////////////// + + [ForceInline] + [AssertDevirtualize] + protected virtual bool AllowFastPathImpl() + { + return false; + } + + ///////////////////////// CopyStructImpl /////////////////////// + + [AssertDevirtualize] + [Inline] + protected virtual void CopyStructImpl(Object srcObj, + Object dstObj, + VTable vtable, + UIntPtr srcPtr, + UIntPtr dstPtr) + { + CopyStructWithBarrier(vtable, srcPtr, dstPtr); + } + + ////////////////////////// CloneImpl ////////////////////////// + + [AssertDevirtualize] + [Inline] + protected virtual void CloneImpl(Object srcObject, Object dstObject) + { + CloneWithBarrier(srcObject, dstObject); + } + + /////////////////////// ArrayZeroImpl ///////////////////////// + + // 'offset' is not relative to the lower bound, but is a count + // of elements from the first element in the array. + [AssertDevirtualize] + [Inline] + protected virtual void ArrayZeroImpl(Array array, + int offset, + int length) + { + ArrayZeroWithBarrier(array, offset, length); + } + + /////////////////////// ArrayCopyImpl ///////////////////////// + + // 'offset' is not relative to the lower bound, but is a count + // of elements from the first element in the array. + [AssertDevirtualize] + [Inline] + protected virtual void ArrayCopyImpl(Array srcArray, int srcOffset, + Array dstArray, int dstOffset, + int length) + { + ArrayCopyWithBarrier(srcArray, srcOffset, + dstArray, dstOffset, + length); + } + + ///////////////////// WriteImpl to ptr ////////////////////// + + [AssertDevirtualize] + [ForceInline] + protected virtual void WriteImpl(UIntPtr *location, + Object value, + int mask) + { + *location = Magic.addressOf(value); + } + + [AssertDevirtualize] + [ForceInline] + protected virtual void WriteImpl(float *location, + float value, + int mask) + { + *location = value; + } + + [AssertDevirtualize] + [ForceInline] + protected virtual void WriteImpl(double *location, + double value, + int mask) + { + *location = value; + } + + [AssertDevirtualize] + [ForceInline] + protected virtual void WriteImpl(byte *location, + byte value, + int mask) + { + *location = value; + } + + [AssertDevirtualize] + [ForceInline] + protected virtual void WriteImpl(ushort *location, + ushort value, + int mask) + { + *location = value; + } + + [AssertDevirtualize] + [ForceInline] + protected virtual void WriteImpl(uint *location, + uint value, + int mask) + { + *location = value; + } + + [AssertDevirtualize] + [ForceInline] + protected virtual void WriteImpl(ulong *location, + ulong value, + int mask) + { + *location = value; + } + + [AssertDevirtualize] + [ForceInline] + protected virtual void WriteImpl(UIntPtr *location, + UIntPtr value, + int mask) + { + *location = value; + } + + ////////////////////// WriteImpl to Object, pointer ///////////////// + + [AssertDevirtualize] + [ForceInline] + protected virtual void WriteImpl(Object o, + UIntPtr *ptr, + Object value, + int mask) + { + this.WriteImpl(ptr, value, mask); + } + + [AssertDevirtualize] + [Inline] + protected virtual void WriteImpl(Object o, + float *ptr, + float value, + int mask) + { + this.WriteImpl(ptr, value, mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual void WriteImpl(Object o, + double *ptr, + double value, + int mask) + { + this.WriteImpl(ptr, value, mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual void WriteImpl(Object o, + byte *ptr, + byte value, + int mask) + { + this.WriteImpl(ptr, value, mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual void WriteImpl(Object o, + ushort *ptr, + ushort value, + int mask) + { + this.WriteImpl(ptr, value, mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual void WriteImpl(Object o, + uint *ptr, + uint value, + int mask) + { + this.WriteImpl(ptr, value, mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual void WriteImpl(Object o, + ulong *ptr, + ulong value, + int mask) + { + this.WriteImpl(ptr, value, mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual void WriteImpl(Object o, + UIntPtr *ptr, + UIntPtr value, + int mask) + { + this.WriteImpl(ptr, value, mask); + } + + ///////////////////// WriteImpl to Object+offset ////////////////////// + + [ForceInline] + [AssertDevirtualize] + protected virtual void WriteImpl(Object o, + UIntPtr offset, + Object value, + int mask) + { + this.WriteImpl(o, + (UIntPtr*)(Magic.addressOf(o)+offset), + value, + mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual void WriteImpl(Object o, + UIntPtr offset, + float value, + int mask) + { + this.WriteImpl(o, + (float*)(Magic.addressOf(o)+offset), + value, + mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual void WriteImpl(Object o, + UIntPtr offset, + double value, + int mask) + { + this.WriteImpl(o, + (double*)(Magic.addressOf(o)+offset), + value, + mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual void WriteImpl(Object o, + UIntPtr offset, + byte value, + int mask) + { + this.WriteImpl(o, + (byte*)(Magic.addressOf(o)+offset), + value, + mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual void WriteImpl(Object o, + UIntPtr offset, + ushort value, + int mask) + { + this.WriteImpl(o, + (ushort*)(Magic.addressOf(o)+offset), + value, + mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual void WriteImpl(Object o, + UIntPtr offset, + uint value, + int mask) + { + this.WriteImpl(o, + (uint*)(Magic.addressOf(o)+offset), + value, + mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual void WriteImpl(Object o, + UIntPtr offset, + ulong value, + int mask) + { + this.WriteImpl(o, + (ulong*)(Magic.addressOf(o)+offset), + value, + mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual void WriteImpl(Object o, + UIntPtr offset, + UIntPtr value, + int mask) + { + this.WriteImpl(o, + (UIntPtr*)(Magic.addressOf(o)+offset), + value, + mask); + } + + ///////////////////// WriteImpl to ref ////////////////////// + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual void WriteImplByRef(ref Object location, + Object value, + int mask) + { + location = value; + } + + [AssertDevirtualize] + [NoBarriers] + [ForceInline] + protected virtual void WriteImplByRef(ref float location, + float value, + int mask) + { + location = value; + } + + [AssertDevirtualize] + [NoBarriers] + [ForceInline] + protected virtual void WriteImplByRef(ref double location, + double value, + int mask) + { + location = value; + } + + [AssertDevirtualize] + [NoBarriers] + [ForceInline] + protected virtual void WriteImplByRef(ref byte location, + byte value, + int mask) + { + location = value; + } + + [AssertDevirtualize] + [NoBarriers] + [ForceInline] + protected virtual void WriteImplByRef(ref ushort location, + ushort value, + int mask) + { + location = value; + } + + [AssertDevirtualize] + [NoBarriers] + [ForceInline] + protected virtual void WriteImplByRef(ref uint location, + uint value, + int mask) + { + location = value; + } + + [AssertDevirtualize] + [NoBarriers] + [ForceInline] + protected virtual void WriteImplByRef(ref ulong location, + ulong value, + int mask) + { + location = value; + } + + [AssertDevirtualize] + [NoBarriers] + [ForceInline] + protected virtual void WriteImplByRef(ref UIntPtr location, + UIntPtr value, + int mask) + { + location = value; + } + + ///////////////////// ReadImpl from ptr ////////////////////// + + [AssertDevirtualize] + [ForceInline] + protected virtual Object ReadObjImpl(UIntPtr *location, + int mask) + { + return Magic.fromAddress(*location); + } + + [AssertDevirtualize] + [ForceInline] + protected virtual float ReadImpl(float *location, + int mask) + { + return *location; + } + + [AssertDevirtualize] + [ForceInline] + protected virtual double ReadImpl(double *location, + int mask) + { + return *location; + } + + [AssertDevirtualize] + [ForceInline] + protected virtual byte ReadImpl(byte *location, + int mask) + { + return *location; + } + + [AssertDevirtualize] + [ForceInline] + protected virtual ushort ReadImpl(ushort *location, + int mask) + { + return *location; + } + + [AssertDevirtualize] + [ForceInline] + protected virtual uint ReadImpl(uint *location, + int mask) + { + return *location; + } + + [AssertDevirtualize] + [ForceInline] + protected virtual ulong ReadImpl(ulong *location, + int mask) + { + return *location; + } + + [AssertDevirtualize] + [ForceInline] + protected virtual UIntPtr ReadImpl(UIntPtr *location, + int mask) + { + return *location; + } + + ///////////////////// ReadImpl from Object, ptr /////////////////// + + [Inline] + [AssertDevirtualize] + protected virtual Object ReadObjImpl(Object o, + UIntPtr *ptr, + int mask) + { + return this.ReadObjImpl(ptr, mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual float ReadImpl(Object o, + float *ptr, + int mask) + { + return this.ReadImpl(ptr, mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual double ReadImpl(Object o, + double *ptr, + int mask) + { + return this.ReadImpl(ptr, mask); + } + + [AssertDevirtualize] + [Inline] + protected virtual byte ReadImpl(Object o, + byte *ptr, + int mask) + { + return this.ReadImpl(ptr, mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual ushort ReadImpl(Object o, + ushort *ptr, + int mask) + { + return this.ReadImpl(ptr, mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual uint ReadImpl(Object o, + uint *ptr, + int mask) + { + return this.ReadImpl(ptr, mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual ulong ReadImpl(Object o, + ulong *ptr, + int mask) + { + return this.ReadImpl(ptr, mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual UIntPtr ReadImpl(Object o, + UIntPtr *ptr, + int mask) + { + return this.ReadImpl(ptr, mask); + } + + ///////////////////// ReadImpl from Object+off ////////////////////// + + [Inline] + [AssertDevirtualize] + protected virtual Object ReadObjImpl(Object o, + UIntPtr offset, + int mask) + { + return this.ReadObjImpl(o, + (UIntPtr*)(Magic.addressOf(o)+offset), + mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual float ReadFloatImpl(Object o, + UIntPtr offset, + int mask) + { + return this.ReadImpl(o, + (float*)(Magic.addressOf(o)+offset), + mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual double ReadDoubleImpl(Object o, + UIntPtr offset, + int mask) + { + return this.ReadImpl(o, + (double*)(Magic.addressOf(o)+offset), + mask); + } + + [AssertDevirtualize] + [Inline] + protected virtual byte ReadByteImpl(Object o, + UIntPtr offset, + int mask) + { + return this.ReadImpl(o, + (byte*)(Magic.addressOf(o)+offset), + mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual ushort ReadUShortImpl(Object o, + UIntPtr offset, + int mask) + { + return this.ReadImpl(o, + (ushort*)(Magic.addressOf(o)+offset), + mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual uint ReadUIntImpl(Object o, + UIntPtr offset, + int mask) + { + return this.ReadImpl(o, + (uint*)(Magic.addressOf(o)+offset), + mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual ulong ReadULongImpl(Object o, + UIntPtr offset, + int mask) + { + return this.ReadImpl(o, + (ulong*)(Magic.addressOf(o)+offset), + mask); + } + + [Inline] + [AssertDevirtualize] + protected virtual UIntPtr ReadUIntPtrImpl(Object o, + UIntPtr offset, + int mask) + { + return this.ReadImpl(o, + (UIntPtr*)(Magic.addressOf(o)+offset), + mask); + } + + ///////////////////// ReadImpl from ref ////////////////////// + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual Object ReadImplByRef(ref Object location, + int mask) + { + return location; + } + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual float ReadImplByRef(ref float location, + int mask) + { + return location; + } + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual double ReadImplByRef(ref double location, + int mask) + { + return location; + } + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual byte ReadImplByRef(ref byte location, + int mask) + { + return location; + } + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual ushort ReadImplByRef(ref ushort location, + int mask) + { + return location; + } + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual uint ReadImplByRef(ref uint location, + int mask) + { + return location; + } + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual ulong ReadImplByRef(ref ulong location, + int mask) + { + return location; + } + + [AssertDevirtualize] + [ForceInline] + [NoBarriers] + protected virtual UIntPtr ReadImplByRef(ref UIntPtr location, + int mask) + { + return location; + } + + /////////////////////// Helpers... ////////////////////////////// + + [ForceInline] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected static UIntPtr IndexedDataPtr(Array array) { + return (UIntPtr) (Magic.addressOf(array) + + (array.vtable.baseLength-(uint)PreHeader.Size)); + } + + [ForceInline] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected static UIntPtr IndexedFieldOffset(Array array, + int index, + int arrayElementSize, + UIntPtr fieldOffset) + { + return ((UIntPtr)(uint)index * (UIntPtr)(uint)arrayElementSize + + array.vtable.baseLength + + fieldOffset + - (UIntPtr)(uint)PreHeader.Size); + } + + [ForceInline] + [AssertDevirtualize] + protected static UIntPtr IndexedElementOffset(Array array, + int index) + { + return (UIntPtr)(uint)index * (UIntPtr)(uint)array.vtable.arrayElementSize + + array.vtable.baseLength + - (UIntPtr)(uint)PreHeader.Size; + } + + // copies a span of primitives from one object to another invoking + // word-size read and write barriers along the way. + // mostly useful as part of a greater implementation of struct, + // object, or array copy in a GC that requires primitive barriers. + // note that dstOff, srcOff, and nBytes must be word-aligned. + // call this with great care. + [Inline] + protected static void MemCopyBarrierSlow(Object dst, + UIntPtr dstOff, + Object src, + UIntPtr srcOff, + UIntPtr nBytes) { + for (;nBytes>0;nBytes-=sizeof(UIntPtr)) { + installedBarrier + .WriteImpl(dst,dstOff, + installedBarrier.ReadUIntPtrImpl(src,srcOff,0),0); + dstOff+=sizeof(UIntPtr); + srcOff+=sizeof(UIntPtr); + } + } + + // Same as above but without knowledge of the object base. + [Inline] + protected static void MemCopyBarrierSlow(UIntPtr *dstPtr, + UIntPtr *srcPtr, + UIntPtr nBytes) { + for (;nBytes>0;nBytes-=sizeof(UIntPtr)) { + installedBarrier + .WriteImpl(dstPtr,installedBarrier.ReadImpl(srcPtr,0),0); + dstPtr++; + srcPtr++; + } + } + + // Really slow way of zeroing memory while invoking write + // barriers. Assumes that everything is word-aligned. + [Inline] + protected static void MemZeroBarrierSlow(Object dst, + UIntPtr dstOff, + UIntPtr nBytes) { + for (;nBytes>0;nBytes-=sizeof(UIntPtr)) { + installedBarrier.WriteImpl(dst,dstOff,UIntPtr.Zero,0); + dstOff+=sizeof(UIntPtr); + } + } + + // Really slow way of zeroing memory while invoking write + // barriers. Assumes that everything is word-aligned. + [Inline] + protected static void MemZeroBarrierSlow(UIntPtr *dstPtr, + UIntPtr nBytes) { + for (;nBytes>0;nBytes-=sizeof(UIntPtr)) { + installedBarrier.WriteImpl(dstPtr,UIntPtr.Zero,0); + dstPtr++; + } + } + + [Inline] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected void CopyStructNoBarrier(VTable vtable, + UIntPtr srcPtr, + UIntPtr dstPtr) + { + int preHeaderSize = PreHeader.Size; + int postHeaderSize = PostHeader.Size; + int structSize = ((int) ObjectLayout.ObjectSize(vtable) - + (preHeaderSize + postHeaderSize)); + Buffer.MoveMemory((byte *) dstPtr, (byte *) srcPtr, structSize); + } + + [Inline] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected void CopyStructWithBarrier(VTable vtable, + UIntPtr srcPtr, + UIntPtr dstPtr) + { + copyFieldsVisitor.VisitReferenceFields(vtable, srcPtr, dstPtr); + } + + [Inline] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected void CopyStructWithSlowBarrier(Object srcObj, + Object dstObj, + VTable vtable, + UIntPtr srcOff, + UIntPtr dstOff) + { + slowCopyFieldsVisitor.VisitReferenceFields(vtable, + srcObj, dstObj, + srcOff, dstOff); + } + + [Inline] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected void CloneNoBarrier(Object srcObject, + Object dstObject) + { + UIntPtr objectSize = System.GCs.ObjectLayout.Sizeof(srcObject); + int preHeaderSize = PreHeader.Size; + int postHeaderSize = PostHeader.Size; + // We don't copy any of the header fields. + Util.MemCopy(Magic.addressOf(dstObject) + postHeaderSize, + Magic.addressOf(srcObject) + postHeaderSize, + objectSize - preHeaderSize - postHeaderSize); + } + + [Inline] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected void CloneWithBarrier(Object srcObject, + Object dstObject) + { + copyFieldsVisitor.VisitReferenceFields(srcObject, dstObject); + } + + [Inline] + [NoStackLinkCheckTrans] + [AssertDevirtualize] + protected void CloneWithSlowBarrier(Object srcObject, + Object dstObject) + { + slowCopyFieldsVisitor.VisitReferenceFields(srcObject, dstObject); + } + + // 'offset' is not relative to the lower bound, but is a count + // of elements from the first element in the array. + [Inline] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected void ArrayZeroNoBarrier(Array array, int offset, + int length) + { + UIntPtr dataPtr = IndexedDataPtr(array); + int elementSize = array.vtable.arrayElementSize; + Buffer.ZeroMemory((byte *)dataPtr + offset * elementSize, + length * elementSize); + } + + // 'offset' is not relative to the lower bound, but is a count + // of elements from the first element in the array. + [Inline] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected void ArrayZeroWithBarrier(Array array, int offset, + int length) + { + UIntPtr dataPtr = IndexedDataPtr(array); + int elementSize = array.vtable.arrayElementSize; + UIntPtr startAddr = dataPtr + offset * elementSize; + zeroFieldsVisitor.VisitReferenceFields(array.vtable, + startAddr, length); + } + + // 'offset' is not relative to the lower bound, but is a count + // of elements from the first element in the array. + [Inline] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected void ArrayZeroWithSlowBarrier(Array array, int offset, + int length) + { + slowZeroFieldsVisitor + .VisitReferenceFields(array, + IndexedElementOffset(array,offset), + length); + } + + // 'offset' is not relative to the lower bound, but is a count + // of elements from the first element in the array. + [Inline] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected void ArrayCopyNoBarrier(Array srcArray, int srcOffset, + Array dstArray, int dstOffset, + int length) + { + UIntPtr srcDataAddr = IndexedDataPtr(srcArray); + UIntPtr dstDataAddr = IndexedDataPtr(dstArray); + int elementSize = srcArray.vtable.arrayElementSize; + VTable.Assert(elementSize == + dstArray.vtable.arrayElementSize); + Buffer.MoveMemory((byte *) (dstDataAddr + dstOffset * elementSize), + (byte *) (srcDataAddr + srcOffset * elementSize), + length * elementSize); + } + + // 'offset' is not relative to the lower bound, but is a count + // of elements from the first element in the array. + [Inline] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected void ArrayCopyWithBarrier(Array srcArray, int srcOffset, + Array dstArray, int dstOffset, + int length) + { + UIntPtr srcDataAddr = IndexedDataPtr(srcArray); + UIntPtr dstDataAddr = IndexedDataPtr(dstArray); + int elementSize = srcArray.vtable.arrayElementSize; + VTable.Assert(elementSize == dstArray.vtable.arrayElementSize); + UIntPtr srcStartAddr = srcDataAddr + srcOffset * elementSize; + UIntPtr dstStartAddr = dstDataAddr + dstOffset * elementSize; + copyFieldsVisitor.VisitReferenceFields(srcArray.vtable, + srcStartAddr, + dstStartAddr, + length); + } + + // 'offset' is not relative to the lower bound, but is a count + // of elements from the first element in the array. + [Inline] + [AssertDevirtualize] + [NoStackLinkCheckTrans] + protected void ArrayCopyWithSlowBarrier(Array srcArray, int srcOffset, + Array dstArray, int dstOffset, + int length) + { + VTable.Assert(srcArray.vtable.arrayElementSize + == dstArray.vtable.arrayElementSize); + slowCopyFieldsVisitor + .VisitReferenceFields(srcArray.vtable, + srcArray, + dstArray, + IndexedElementOffset(srcArray, + srcOffset), + IndexedElementOffset(dstArray, + dstOffset), + length); + } + + ////////////////////// StoreIndirect to pointer /////////////////// + // this isn't called by the compiler + + [ForceInline] + [NoStackLinkCheckTrans] + internal static void StoreIndirect(UIntPtr *location, + Object value, + int mask) + { + installedBarrier.StoreIndirectImpl(location, value, mask); + } + + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreIndirect(float *location, + float value, + int mask) + { + installedBarrier.StoreIndirectImpl(location, value, mask); + } + + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreIndirect(double *location, + double value, + int mask) + { + installedBarrier.StoreIndirectImpl(location, value, mask); + } + + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreIndirect(byte *location, + byte value, + int mask) + { + installedBarrier.StoreIndirectImpl(location, value, mask); + } + + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreIndirect(ushort *location, + ushort value, + int mask) + { + installedBarrier.StoreIndirectImpl(location, value, mask); + } + + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreIndirect(uint *location, + uint value, + int mask) + { + installedBarrier.StoreIndirectImpl(location, value, mask); + } + + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreIndirect(ulong *location, + ulong value, + int mask) + { + installedBarrier.StoreIndirectImpl(location, value, mask); + } + + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreIndirect(UIntPtr *location, + UIntPtr value, + int mask) + { + installedBarrier.StoreIndirectImpl(location, value, mask); + } + + //////////////////// StoreIndirect to ref ///////////////////////// + + [RequiredByBartok] + [ForceInline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static void StoreIndirectByRef(ref Object reference, + Object value, + int mask) + { + installedBarrier.StoreIndirectImplByRef(ref reference, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreIndirectByRef(ref float reference, + float value, + int mask) + { + installedBarrier.StoreIndirectImplByRef(ref reference, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreIndirectByRef(ref double reference, + double value, + int mask) + { + installedBarrier.StoreIndirectImplByRef(ref reference, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreIndirectByRef(ref byte reference, + byte value, + int mask) + { + installedBarrier.StoreIndirectImplByRef(ref reference, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreIndirectByRef(ref ushort reference, + ushort value, + int mask) + { + installedBarrier.StoreIndirectImplByRef(ref reference, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreIndirectByRef(ref uint reference, + uint value, + int mask) + { + installedBarrier.StoreIndirectImplByRef(ref reference, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreIndirectByRef(ref ulong reference, + ulong value, + int mask) + { + installedBarrier.StoreIndirectImplByRef(ref reference, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreIndirectByRef(ref UIntPtr reference, + UIntPtr value, + int mask) + { + installedBarrier.StoreIndirectImplByRef(ref reference, + value, mask); + } + + //////////////////// LoadIndirect from ptr ///////////////////////// + + [Inline] + [NoStackLinkCheckTrans] + internal static Object LoadObjIndirect(UIntPtr *reference, + int mask) + { + return installedBarrier.LoadObjIndirectImpl(reference, mask); + } + + [Inline] + [NoStackLinkCheckTrans] + internal static float LoadIndirect(float* reference, + int mask) + { + return installedBarrier.LoadIndirectImpl(reference, mask); + } + + [Inline] + [NoStackLinkCheckTrans] + internal static double LoadIndirect(double* reference, + int mask) + { + return installedBarrier.LoadIndirectImpl(reference, mask); + } + + [Inline] + [NoStackLinkCheckTrans] + internal static byte LoadIndirect(byte* reference, + int mask) + { + return installedBarrier.LoadIndirectImpl(reference, mask); + } + + [Inline] + [NoStackLinkCheckTrans] + internal static ushort LoadIndirect(ushort* reference, + int mask) + { + return installedBarrier.LoadIndirectImpl(reference, mask); + } + + [Inline] + [NoStackLinkCheckTrans] + internal static uint LoadIndirect(uint* reference, + int mask) + { + return installedBarrier.LoadIndirectImpl(reference, mask); + } + + [Inline] + [NoStackLinkCheckTrans] + internal static ulong LoadIndirect(ulong* reference, + int mask) + { + return installedBarrier.LoadIndirectImpl(reference, mask); + } + + [Inline] + [NoStackLinkCheckTrans] + internal static UIntPtr LoadIndirect(UIntPtr* reference, + int mask) + { + return installedBarrier.LoadIndirectImpl(reference, mask); + } + + //////////////////// LoadIndirect from ref ///////////////////////// + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static Object LoadIndirectByRef(ref Object reference, + int mask) + { + return installedBarrier.LoadIndirectImplByRef(ref reference, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static float LoadIndirectByRef(ref float reference, + int mask) + { + return installedBarrier.LoadIndirectImplByRef(ref reference, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static double LoadIndirectByRef(ref double reference, + int mask) + { + return installedBarrier.LoadIndirectImplByRef(ref reference, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static byte LoadIndirectByRef(ref byte reference, + int mask) + { + return installedBarrier.LoadIndirectImplByRef(ref reference, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static ushort LoadIndirectByRef(ref ushort reference, + int mask) + { + return installedBarrier.LoadIndirectImplByRef(ref reference, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static uint LoadIndirectByRef(ref uint reference, + int mask) + { + return installedBarrier.LoadIndirectImplByRef(ref reference, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static ulong LoadIndirectByRef(ref ulong reference, + int mask) + { + return installedBarrier.LoadIndirectImplByRef(ref reference, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static UIntPtr LoadIndirectByRef(ref UIntPtr reference, + int mask) + { + return installedBarrier.LoadIndirectImplByRef(ref reference, mask); + } + + ///////////////////// StoreObjectField ///////////////////////// + + [RequiredByBartok] + [ForceInline] + [NoStackLinkCheckTrans] + internal static void StoreObjectField(Object obj, + UIntPtr fieldOffset, + Object value, + int mask) + { + installedBarrier.StoreObjectFieldImpl(obj, fieldOffset, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreObjectField(Object obj, + UIntPtr fieldOffset, + float value, + int mask) + { + installedBarrier.StoreObjectFieldImpl(obj, fieldOffset, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreObjectField(Object obj, + UIntPtr fieldOffset, + double value, + int mask) + { + installedBarrier.StoreObjectFieldImpl(obj, fieldOffset, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreObjectField(Object obj, + UIntPtr fieldOffset, + byte value, + int mask) + { + installedBarrier.StoreObjectFieldImpl(obj, fieldOffset, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreObjectField(Object obj, + UIntPtr fieldOffset, + ushort value, + int mask) + { + installedBarrier.StoreObjectFieldImpl(obj, fieldOffset, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreObjectField(Object obj, + UIntPtr fieldOffset, + uint value, + int mask) + { + installedBarrier.StoreObjectFieldImpl(obj, fieldOffset, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreObjectField(Object obj, + UIntPtr fieldOffset, + ulong value, + int mask) + { + installedBarrier.StoreObjectFieldImpl(obj, fieldOffset, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreObjectField(Object obj, + UIntPtr fieldOffset, + UIntPtr value, + int mask) + { + installedBarrier.StoreObjectFieldImpl(obj, fieldOffset, + value, mask); + } + + ///////////////////// LoadObjectField ///////////////////////// + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static Object LoadObjObjectField(Object obj, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadObjObjectFieldImpl(obj, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static float LoadFloatObjectField(Object obj, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadFloatObjectFieldImpl(obj, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static double LoadDoubleObjectField(Object obj, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadDoubleObjectFieldImpl(obj, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static byte LoadByteObjectField(Object obj, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadByteObjectFieldImpl(obj, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static ushort LoadUShortObjectField(Object obj, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadUShortObjectFieldImpl(obj, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static uint LoadUIntObjectField(Object obj, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadUIntObjectFieldImpl(obj, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static ulong LoadULongObjectField(Object obj, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadULongObjectFieldImpl(obj, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static UIntPtr LoadUIntPtrObjectField(Object obj, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadUIntPtrObjectFieldImpl(obj, + fieldOffset, + mask); + } + + ///////////////////// StoreStructField //////////////////////// + // what does this mean for CoCo? + // find base obj pointer. if there is none then + // ignore. if there is then delegate to StoreObjectField + + [RequiredByBartok] + [ForceInline] + [NoStackLinkCheckTrans] + internal static void StoreStructField(UIntPtr structPtr, + UIntPtr fieldOffset, + Object value, + int mask) + { + installedBarrier.StoreStructFieldImpl(structPtr, fieldOffset, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreStructField(UIntPtr structPtr, + UIntPtr fieldOffset, + float value, + int mask) + { + installedBarrier.StoreStructFieldImpl(structPtr, fieldOffset, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreStructField(UIntPtr structPtr, + UIntPtr fieldOffset, + double value, + int mask) + { + installedBarrier.StoreStructFieldImpl(structPtr, fieldOffset, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreStructField(UIntPtr structPtr, + UIntPtr fieldOffset, + byte value, + int mask) + { + installedBarrier.StoreStructFieldImpl(structPtr, fieldOffset, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreStructField(UIntPtr structPtr, + UIntPtr fieldOffset, + ushort value, + int mask) + { + installedBarrier.StoreStructFieldImpl(structPtr, fieldOffset, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreStructField(UIntPtr structPtr, + UIntPtr fieldOffset, + uint value, + int mask) + { + installedBarrier.StoreStructFieldImpl(structPtr, fieldOffset, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreStructField(UIntPtr structPtr, + UIntPtr fieldOffset, + ulong value, + int mask) + { + installedBarrier.StoreStructFieldImpl(structPtr, fieldOffset, + value, mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreStructField(UIntPtr structPtr, + UIntPtr fieldOffset, + UIntPtr value, + int mask) + { + installedBarrier.StoreStructFieldImpl(structPtr, fieldOffset, + value, mask); + } + + ///////////////////// LoadStructField //////////////////////// + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static Object LoadObjStructField(UIntPtr obj, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadObjStructFieldImpl(obj, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static float LoadFloatStructField(UIntPtr obj, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadFloatStructFieldImpl(obj, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static double LoadDoubleStructField(UIntPtr obj, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadDoubleStructFieldImpl(obj, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static byte LoadByteStructField(UIntPtr obj, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadByteStructFieldImpl(obj, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static ushort LoadUShortStructField(UIntPtr obj, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadUShortStructFieldImpl(obj, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static uint LoadUIntStructField(UIntPtr obj, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadUIntStructFieldImpl(obj, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static ulong LoadULongStructField(UIntPtr obj, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadULongStructFieldImpl(obj, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static UIntPtr LoadUIntPtrStructField(UIntPtr obj, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadUIntPtrStructFieldImpl(obj, + fieldOffset, + mask); + } + + ////////////////////// StoreVectorElement //////////////////// + + [RequiredByBartok] + [ForceInline] + [NoStackLinkCheckTrans] + internal static void StoreVectorElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + Object value, + int mask) + { + installedBarrier.StoreVectorElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + value, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreVectorElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + float value, + int mask) + { + installedBarrier.StoreVectorElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + value, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreVectorElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + double value, + int mask) + { + installedBarrier.StoreVectorElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + value, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreVectorElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + byte value, + int mask) + { + installedBarrier.StoreVectorElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + value, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreVectorElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + ushort value, + int mask) + { + installedBarrier.StoreVectorElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + value, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreVectorElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + uint value, + int mask) + { + installedBarrier.StoreVectorElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + value, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreVectorElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + ulong value, + int mask) + { + installedBarrier.StoreVectorElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + value, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreVectorElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + UIntPtr value, + int mask) + { + installedBarrier.StoreVectorElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + value, + mask); + } + + ////////////////////// LoadVectorElement //////////////////// + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static Object LoadObjVectorElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadObjVectorElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static float LoadFloatVectorElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier + .LoadFloatVectorElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static double LoadDoubleVectorElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier + .LoadDoubleVectorElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static byte LoadByteVectorElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadByteVectorElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static ushort LoadUShortVectorElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier + .LoadUShortVectorElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static uint LoadUIntVectorElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadUIntVectorElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static ulong LoadULongVectorElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier + .LoadULongVectorElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static UIntPtr LoadUIntPtrVectorElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier + .LoadUIntPtrVectorElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + mask); + } + + ///////////////////// StoreArrayElement ///////////////////// + + [RequiredByBartok] + [ForceInline] + [NoStackLinkCheckTrans] + internal static void StoreArrayElement(Array array, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + Object value, + int mask) + { + installedBarrier.StoreArrayElementImpl(array, + index, + arrayElementSize, + fieldOffset, + value, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreArrayElement(Array array, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + float value, + int mask) + { + installedBarrier.StoreArrayElementImpl(array, + index, + arrayElementSize, + fieldOffset, + value, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreArrayElement(Array array, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + double value, + int mask) + { + installedBarrier.StoreArrayElementImpl(array, + index, + arrayElementSize, + fieldOffset, + value, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreArrayElement(Array array, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + byte value, + int mask) + { + installedBarrier.StoreArrayElementImpl(array, + index, + arrayElementSize, + fieldOffset, + value, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreArrayElement(Array array, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + ushort value, + int mask) + { + installedBarrier.StoreArrayElementImpl(array, + index, + arrayElementSize, + fieldOffset, + value, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreArrayElement(Array array, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + uint value, + int mask) + { + installedBarrier.StoreArrayElementImpl(array, + index, + arrayElementSize, + fieldOffset, + value, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreArrayElement(Array array, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + ulong value, + int mask) + { + installedBarrier.StoreArrayElementImpl(array, + index, + arrayElementSize, + fieldOffset, + value, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static void StoreArrayElement(Array array, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + UIntPtr value, + int mask) + { + installedBarrier.StoreArrayElementImpl(array, + index, + arrayElementSize, + fieldOffset, + value, + mask); + } + + ////////////////////// LoadArrayElement //////////////////// + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static Object LoadObjArrayElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadObjArrayElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static float LoadFloatArrayElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadFloatArrayElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static double LoadDoubleArrayElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier + .LoadDoubleArrayElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static byte LoadByteArrayElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadByteArrayElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static ushort LoadUShortArrayElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier + .LoadUShortArrayElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static uint LoadUIntArrayElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadUIntArrayElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static ulong LoadULongArrayElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier.LoadULongArrayElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + mask); + } + + [RequiredByBartok] + [Inline] + [NoStackLinkCheckTrans] + internal static UIntPtr LoadUIntPtrArrayElement(Array vector, + int index, + int arrayElementSize, + UIntPtr fieldOffset, + int mask) + { + return installedBarrier + .LoadUIntPtrArrayElementImpl(vector, + index, + arrayElementSize, + fieldOffset, + mask); + } + + //////////////////// StoreStaticField ///////////////////////// + // only support ref wb for now, since nobody needs integral + // wb on statics + + [RequiredByBartok] + [ForceInline] + [NoStackLinkCheckTrans] + internal static void StoreStaticField(ref Object staticField, + Object value, + int mask) + { + installedBarrier.StoreStaticFieldImpl(ref staticField, + value, mask); + } + + //////////////////// LoadStaticField ///////////////////////// + // only support ref rb for now, since nobody needs integral + // rb on statics + + [RequiredByBartok] + [Inline] + internal static Object LoadStaticField(ref Object staticField, + int mask) + { + return installedBarrier.LoadStaticFieldImpl(ref staticField, + mask); + } + + ///////////////////////// Increment/Decrement ///////////////////// + // REVIEW: maybe optimize these. or not. + + [ForceInline] + [RequiredByBartok] + internal static int AtomicIncrement(ref int reference, + int maskmask) + { + for (;;) { + int oldVal = reference; + if (Interlocked.CompareExchange(ref reference, + oldVal+1, + oldVal) + == oldVal) { + return oldVal+1; + } + } + } + + [ForceInline] + [RequiredByBartok] + internal static int AtomicDecrement(ref int reference, + int mask) + { + for (;;) { + int oldVal = reference; + if (Interlocked.CompareExchange(ref reference, + oldVal-1, + oldVal) + == oldVal) { + return oldVal-1; + } + } + } + + /////////////////////////// AtomicSwap ///////////// + + [RequiredByBartok] + [ForceInline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static Object AtomicSwap(ref Object reference, + Object value, + int mask) + { + return installedBarrier.AtomicSwapImpl(ref reference, + value, + mask); + } + + [RequiredByBartok] + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static int AtomicSwap(ref int reference, + int value, + int mask) + { + return installedBarrier.AtomicSwapImpl(ref reference, + value, + mask); + } + + [RequiredByBartok] + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static UIntPtr AtomicSwap(ref UIntPtr reference, + UIntPtr value, + int mask) + { + return installedBarrier.AtomicSwapImpl(ref reference, + value, + mask); + } + + //////////////// AtomicCompareAndSwap ///////////////// + + [RequiredByBartok] + [ForceInline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static Object AtomicCompareAndSwap(ref Object reference, + Object newValue, + Object comparand, + int mask) + { + return + installedBarrier.AtomicCompareAndSwapImpl(ref reference, + newValue, + comparand, + mask); + } + + [RequiredByBartok] + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static int AtomicCompareAndSwap(ref int reference, + int newValue, + int comparand, + int mask) + { + return + installedBarrier.AtomicCompareAndSwapImpl(ref reference, + newValue, + comparand, + mask); + } + + [RequiredByBartok] + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static long AtomicCompareAndSwap(ref long reference, + long newValue, + long comparand, + int mask) + { + return + installedBarrier.AtomicCompareAndSwapImpl(ref reference, + newValue, + comparand, + mask); + } + + [RequiredByBartok] + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static UIntPtr AtomicCompareAndSwap(ref UIntPtr reference, + UIntPtr newValue, + UIntPtr comparand, + int mask) + { + return + installedBarrier.AtomicCompareAndSwapImpl(ref reference, + newValue, + comparand, + mask); + } + + /////////////////////// Forward //////////////////////// + + [RequiredByBartok] + [ForceInline] + internal static Object Forward(Object o,int mask) + { + return installedBarrier.ForwardImpl(o,mask); + } + + /////////////////////// MemmoryBarrier /////////////////////////// + + [RequiredByBartok] + [ForceInline] + internal static void MemoryBarrier(int mask) + { + installedBarrier.MemoryBarrierImpl(mask); + } + + //////////////////////// Pin /////////////////////////// + // + // takes an address at which you wish to pin and returns to + // you a suitable "pinned" address to use. may also cause + // you to wait for any arbitrary amount of time. but the + // invariants are: + // -> the address returned is pinned until the next safepoint, + // or until no pinned locals refer to it, whichever comes + // later. + // -> even though the address returned may be different from + // the address given, and even though non-pinned pointers + // to the object may use totally different addresses, you're + // guaranteed that the address returned is the "true" + // address of the object, and any modifications made to it + // will be seen by anyone else accessing the same object, even + // if they do it via a different address. + [RequiredByBartok] + [ForceInline] + internal static UIntPtr Pin(UIntPtr address, + int mask) + { + return installedBarrier.PinImpl(address, mask); + } + + ////////////////////////// Initialize Object ///////////////// + + [Inline] + internal static void InitObject(Object o,VTable vtable) + { + installedBarrier.InitObjectImpl(o,vtable); + } + + // TODO: Eliminate this hack when possible. That should be either + // when the compiler is smart enough to devirtualize the + // installedBarrier.InitObjectImpl call in Barrier.InitObject when + // doing sepcomp, or when we stop using a class hierarchy of barriers. + // + // This method is called only from BootstrapMemory and is required + // when the compiler cannot statically resolve which barrier to + // use and the installedBarrier field has not been initialized. + internal static void BootstrapInitObject(Object o, VTable vtable) + { + switch (GC.wbType) { +#if !SINGULARITY || MARK_SWEEP_COLLECTOR || NULL_COLLECTOR + case WBType.noWB: { + EmptyWriteBarrier.BootstrapInitObjectImpl(o, vtable); + break; + } +#endif +#if !SINGULARITY || SEMISPACE_COLLECTOR || SLIDING_COLLECTOR || ADAPTIVE_COPYING_COLLECTOR + case WBType.Generational: { + GenerationalWriteBarrier.BootstrapInitObjectImpl(o, vtable); + break; + } +#endif +#if !SINGULARITY || CONCURRENT_MS_COLLECTOR + case WBType.CMS: { + WriteBarrierCMS.BootstrapInitObjectImpl(o, vtable); + break; + } +#endif +#if !SINGULARITY || ATOMIC_RC_COLLECTOR + case WBType.ARC: { + AtomicRCWriteBarrier.BootstrapInitObjectImpl(o, vtable); + break; + } +#endif +#if !SINGULARITY || SEMISPACE_COLLECTOR || SLIDING_COLLECTOR || ADAPTIVE_COPYING_COLLECTOR + case WBType.AllCards: { + AllCardsWriteBarrier.BootstrapInitObjectImpl(o, vtable); + break; + } +#endif +#if !SINGULARITY + case WBType.ExpandingCoCo: { + ExpandingCoCoBarrier.BootstrapInitObjectImpl(o, vtable); + break; + } + case WBType.ProbabilisticCoCo: { + ProbabilisticCoCoBarrier.BootstrapInitObjectImpl(o, vtable); + break; + } + case WBType.AbortingCoCo: { + AbortingCoCoBarrier.BootstrapInitObjectImpl(o, vtable); + break; + } + case WBType.BrooksTest: { + BrooksBarrierTest.BootstrapInitObjectImpl(o, vtable); + break; + } + case WBType.BrooksCMSTest: { + BrooksCMSBarrierTest.BootstrapInitObjectImpl(o, vtable); + break; + } +#endif + default: { + VTable.NotReached("Unknown write barrier type: "+GC.wbType); + break; + } + } + } + + //////////////////////// Weak Ref //////////////////////////// + + [Inline] + internal static Object WeakRefRead(UIntPtr addr, + int mask) + { + return installedBarrier.WeakRefReadImpl(addr, mask); + } + + [Inline] + internal static UIntPtr WeakRefWrite(Object obj, + int mask) + { + return installedBarrier.WeakRefWriteImpl(obj, mask); + } + + ///////////////////////// Equality Barriers /////////////////// + + [RequiredByBartok] + [Inline] + internal static bool Eq(Object a, Object b, int mask) + { + return installedBarrier.EqImpl(a, b, mask); + } + + [RequiredByBartok] + [Inline] + internal static bool Neq(Object a, Object b, int mask) + { + return !Eq(a, b, mask); + } + + ////////////////////////// fast path selection //////////////// + + [RequiredByBartok] + [ForceInline] + internal static bool AllowFastPath() + { + return installedBarrier.AllowFastPathImpl(); + } + + /////////////////////// CopyStruct //////////////////////////// + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static void CopyStruct(Object srcObj, + Object dstObj, + VTable vtable, + UIntPtr srcPtr, + UIntPtr dstPtr) + { + installedBarrier.CopyStructImpl(srcObj, dstObj, + vtable, srcPtr, dstPtr); + } + + //////////////////////////// Clone ///////////////////////////// + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static void Clone(Object srcObject, Object dstObject) + { + installedBarrier.CloneImpl(srcObject, dstObject); + } + + //////////////////////// ArrayZero ////////////////////////////// + + // 'offset' is not relative to the lower bound, but is a count + // of elements from the first element in the array. + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static void ArrayZero(Array array, int offset, int length) + { + installedBarrier.ArrayZeroImpl(array, offset, length); + } + + ////////////////////////// ArrayCopy //////////////////////////// + + // 'offset' is not relative to the lower bound, but is a count + // of elements from the first element in the array. + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static void ArrayCopy(Array srcArray, int srcOffset, + Array dstArray, int dstOffset, + int length) + { + installedBarrier.ArrayCopyImpl(srcArray, srcOffset, + dstArray, dstOffset, + length); + } + + ////////////////////// Write //////////////////////////// + + [ForceInline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static void Write(UIntPtr *location, Object value, int mask) + { + installedBarrier.WriteImpl(location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static void Write(float *location, float value, int mask) + { + installedBarrier.WriteImpl(location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static void Write(double *location, double value, int mask) + { + installedBarrier.WriteImpl(location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static void Write(byte *location, byte value, int mask) + { + installedBarrier.WriteImpl(location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static void Write(ushort *location, ushort value, int mask) + { + installedBarrier.WriteImpl(location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static void Write(uint *location, uint value, int mask) + { + installedBarrier.WriteImpl(location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static void Write(ulong *location, ulong value, int mask) + { + installedBarrier.WriteImpl(location, value, mask); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static void Write(UIntPtr *location, UIntPtr value, int mask) + { + installedBarrier.WriteImpl(location, value, mask); + } + + ///////////////////////// Visitors... ///////////////////////////// + + private class CopyFieldsVisitor : OffsetReferenceVisitor + { + + // Struct copy + internal void VisitReferenceFields(VTable vtable, + UIntPtr srcPtr, + UIntPtr dstPtr) + { + int postHeaderSize = PostHeader.Size; + ObjectDescriptor objDesc = + new ObjectDescriptor(vtable, + srcPtr - postHeaderSize, + dstPtr - postHeaderSize, + (UIntPtr) postHeaderSize); + VisitReferenceFieldsTemplate(ref objDesc); + int preHeaderSize = PreHeader.Size; + UIntPtr limitSize = + ObjectLayout.ObjectSize(vtable) - preHeaderSize; + UIntPtr previouslyDone = objDesc.extra; + UIntPtr tailSize = limitSize - previouslyDone; + if (tailSize > UIntPtr.Zero) { + Util.MemCopy(objDesc.secondBase + previouslyDone, + objDesc.objectBase + previouslyDone, + tailSize); + } + } + + internal void VisitReferenceFields(Object srcObject, + Object dstObject) + { + int postHeaderSize = PostHeader.Size; + ObjectDescriptor objDesc = + new ObjectDescriptor(srcObject.vtable, + Magic.addressOf(srcObject), + Magic.addressOf(dstObject), + (UIntPtr) postHeaderSize); + UIntPtr objectSize = VisitReferenceFieldsTemplate(ref objDesc); + int preHeaderSize = PreHeader.Size; + UIntPtr limitSize = objectSize - preHeaderSize; + UIntPtr previouslyDone = objDesc.extra; + UIntPtr tailSize = limitSize - previouslyDone; + if (tailSize > UIntPtr.Zero) { + Util.MemCopy(objDesc.secondBase + previouslyDone, + objDesc.objectBase + previouslyDone, + tailSize); + } + } + + // Partial array copy + internal void VisitReferenceFields(VTable vtable, + UIntPtr srcElementPtr, + UIntPtr dstElementPtr, + int length) + { + ObjectDescriptor objDesc = + new ObjectDescriptor(vtable, srcElementPtr, dstElementPtr); + VisitReferenceFieldsTemplate(ref objDesc, length); + UIntPtr dataSize = + srcElementPtr + length * vtable.arrayElementSize; + UIntPtr srcLimitAddr = Util.UIntPtrPad(dataSize); + UIntPtr previouslyDone = objDesc.objectBase + objDesc.extra; + UIntPtr tailSize = srcLimitAddr - previouslyDone; + if (tailSize > UIntPtr.Zero) { + Util.MemCopy(objDesc.secondBase + objDesc.extra, + previouslyDone, tailSize); + } + } + + internal override void FieldOffset(UIntPtr offset, + ref ObjectDescriptor objDesc) + { + UIntPtr previouslyDone = objDesc.extra; + objDesc.extra = offset + UIntPtr.Size; + UIntPtr norefSize = offset - previouslyDone; + if (norefSize > UIntPtr.Zero) { + Util.MemCopy(objDesc.secondBase + previouslyDone, + objDesc.objectBase + previouslyDone, + norefSize); + } + UIntPtr *srcAddr = (UIntPtr *) (objDesc.objectBase + offset); + UIntPtr *dstAddr = (UIntPtr *) (objDesc.secondBase + offset); + Object fieldValue = installedBarrier.ReadObjImpl(srcAddr, 0); + fieldValue = + installedBarrier.ForwardImpl(fieldValue, + BarrierMask.Forward.Nullable); + installedBarrier.WriteImpl(dstAddr, fieldValue, 0); + } + + } + + private class SlowCopyFieldsVisitor : OffsetReferenceVisitor + { + + // Struct copy + internal void VisitReferenceFields(VTable vtable, + Object src, + Object dst, + UIntPtr srcOff, + UIntPtr dstOff) + { + int postHeaderSize = PostHeader.Size; + ObjectDescriptor objDesc = + new ObjectDescriptor(vtable, + Magic.addressOf(src) + + srcOff - postHeaderSize, + Magic.addressOf(dst) + + dstOff - postHeaderSize, + (UIntPtr) postHeaderSize, + src,dst); + VisitReferenceFieldsTemplate(ref objDesc); + int preHeaderSize = PreHeader.Size; + UIntPtr limitSize = + ObjectLayout.ObjectSize(vtable) - preHeaderSize; + UIntPtr previouslyDone = objDesc.extra; + UIntPtr tailSize = limitSize - previouslyDone; + MemCopyBarrierSlow(dst, + objDesc.secondBase + + previouslyDone + - Magic.addressOf(dst), + src, + objDesc.objectBase + + previouslyDone + - Magic.addressOf(src), + tailSize); + } + + // Partial array copy + internal void VisitReferenceFields(VTable vtable, + Object srcArr, + Object dstArr, + UIntPtr srcOff, + UIntPtr dstOff, + int length) + { + ObjectDescriptor objDesc = + new ObjectDescriptor(vtable, + Magic.addressOf(srcArr) + srcOff, + Magic.addressOf(dstArr) + dstOff, + UIntPtr.Zero, + srcArr, dstArr); + VisitReferenceFieldsTemplate(ref objDesc, length); + UIntPtr srcLimitAddr = + Magic.addressOf(srcArr) + srcOff + + length * vtable.arrayElementSize; + UIntPtr previouslyDone = objDesc.objectBase + objDesc.extra; + UIntPtr tailSize = srcLimitAddr - previouslyDone; + MemCopyBarrierSlow(dstArr, + objDesc.secondBase + objDesc.extra + - Magic.addressOf(dstArr), + srcArr, + previouslyDone - Magic.addressOf(srcArr), + tailSize); + } + + internal void VisitReferenceFields(Object srcObject, + Object dstObject) + { + int postHeaderSize = PostHeader.Size; + ObjectDescriptor objDesc = + new ObjectDescriptor(srcObject.vtable, + Magic.addressOf(srcObject), + Magic.addressOf(dstObject), + (UIntPtr) postHeaderSize, + srcObject, + dstObject); + UIntPtr objectSize = VisitReferenceFieldsTemplate(ref objDesc); + int preHeaderSize = PreHeader.Size; + UIntPtr limitSize = objectSize - preHeaderSize; + UIntPtr previouslyDone = objDesc.extra; + UIntPtr tailSize = limitSize - previouslyDone; + MemCopyBarrierSlow(dstObject,previouslyDone, + srcObject,previouslyDone, + tailSize); + } + + internal override void FieldOffset(UIntPtr offset, + ref ObjectDescriptor objDesc) + { + UIntPtr previouslyDone = objDesc.extra; + objDesc.extra = offset + UIntPtr.Size; + UIntPtr norefSize = offset - previouslyDone; + MemCopyBarrierSlow(objDesc.realSecondBase, + previouslyDone + objDesc.secondBase + - Magic.addressOf(objDesc.realSecondBase), + objDesc.realObjectBase, + previouslyDone + objDesc.objectBase + - Magic.addressOf(objDesc.realObjectBase), + norefSize); + Object fieldValue = + installedBarrier + .ReadObjImpl(objDesc.realObjectBase, + offset + objDesc.objectBase + - Magic.addressOf(objDesc.realObjectBase), + 0); + installedBarrier + .WriteImpl(objDesc.realSecondBase, + offset + objDesc.secondBase + - Magic.addressOf(objDesc.realSecondBase), + fieldValue, + 0); + } + + } + + private class ZeroFieldsVisitor : OffsetReferenceVisitor + { + + internal void VisitReferenceFields(VTable vtable, + UIntPtr elementAddr, + int length) + { + ObjectDescriptor objDesc = + new ObjectDescriptor(vtable, elementAddr); + VisitReferenceFieldsTemplate(ref objDesc, length); + UIntPtr limitAddr = + elementAddr + length * vtable.arrayElementSize; + UIntPtr previouslyDone = objDesc.objectBase + objDesc.extra; + UIntPtr tailSize = limitAddr - previouslyDone; + if (tailSize > UIntPtr.Zero) { + Buffer.ZeroMemory((byte *) previouslyDone, tailSize); + } + } + + internal override void FieldOffset(UIntPtr offset, + ref ObjectDescriptor objDesc) + { + UIntPtr previouslyDone = objDesc.extra; + objDesc.extra = offset + UIntPtr.Size; + UIntPtr norefSize = offset - previouslyDone; + if (norefSize > UIntPtr.Zero) { + Util.MemClear(objDesc.objectBase + previouslyDone, + norefSize); + } + UIntPtr *fieldAddr = (UIntPtr *) (objDesc.objectBase + offset); + installedBarrier.WriteImpl(fieldAddr, null, 0); + } + + } + + private class SlowZeroFieldsVisitor : OffsetReferenceVisitor + { + + internal void VisitReferenceFields(Object arr, + UIntPtr off, + int length) + { + ObjectDescriptor objDesc = + new ObjectDescriptor(arr.vtable, + Magic.addressOf(arr) + off, + off, + UIntPtr.Zero, + arr, + null); + VisitReferenceFieldsTemplate(ref objDesc, length); + UIntPtr limitAddr = + Magic.addressOf(arr) + off + length * arr.vtable.arrayElementSize; + UIntPtr previouslyDone = objDesc.objectBase + objDesc.extra; + UIntPtr tailSize = limitAddr - previouslyDone; + Barrier.MemZeroBarrierSlow(arr, + previouslyDone - Magic.addressOf(arr), + tailSize); + } + + internal override void FieldOffset(UIntPtr offset, + ref ObjectDescriptor objDesc) + { + UIntPtr previouslyDone = objDesc.extra; + objDesc.extra = offset + UIntPtr.Size; + UIntPtr norefSize = offset - previouslyDone; + Barrier + .MemZeroBarrierSlow(objDesc.realObjectBase, + objDesc.secondBase + previouslyDone, + norefSize); + installedBarrier + .WriteImpl(objDesc.realObjectBase, + objDesc.secondBase + offset, + null, + 0); + } + + } + + } + +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/BaseCollector.cs b/base/Imported/Bartok/runtime/shared/GCs/BaseCollector.cs new file mode 100644 index 0000000..4cbfa63 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/BaseCollector.cs @@ -0,0 +1,355 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + + +namespace System.GCs +{ + + using Microsoft.Bartok.Runtime; + + using System.Threading; + using System.Runtime.CompilerServices; + +#if SINGULARITY + using Microsoft.Singularity; +#endif + +#if SINGULARITY_KERNEL + using Microsoft.Singularity.Scheduling; +#endif + + [NoCCtor] + internal abstract class BaseCollector : Collector + { + + [Inline] + protected bool IsValidGeneration(int generation) + { + return ((generation >= MinGeneration) && + (generation <= MaxGeneration)); + } + +#region Heap Lifetime Events + + internal override void Shutdown() + { + } + + internal override void DestructHeap() + { + PrintGCTiming(); + PrintAllocations(); + CollectorStatistics.Event(GCEvent.DestroyHeap); + CollectorStatistics.Summary(); + } + +#endregion + +#region Notifications + + internal override void NewThreadNotification(Thread newThread, + bool initial) + { + Transitions.NewThreadNotification(newThread.threadIndex, initial); + } + + internal override void DeadThreadNotification(Thread deadThread) + { + } + + internal override void ThreadStartNotification(int currentThreadIndex) + { +#if !SINGULARITY + Thread currentThread = Thread.threadTable[currentThreadIndex]; + PageManager.MarkThreadStack(currentThread); +#endif + } + + internal override void ThreadEndNotification(Thread currentThread) + { + } + + internal override void ThreadDormantGCNotification(int threadIndex) + { + } + +#endregion + +#region Allocation + + [ManualRefCounts] + [Inline] + internal override Object AllocateObject(VTable vtable, + Thread currentThread) + { + return AllocateObject(vtable, + ObjectLayout.ObjectSize(vtable), + vtable.baseAlignment, + currentThread); + } + + [ManualRefCounts] + [Inline] + internal override Object AllocateObject(VTable vtable, + UIntPtr numBytes, + uint baseAlignment, + Thread currentThread) + { + UIntPtr objectAddr = + AllocateObjectMemory(numBytes, baseAlignment, + currentThread); + Object result = Magic.fromAddress(objectAddr); + this.CreateObject(result, vtable, currentThread); + return result; + } + + [ManualRefCounts] + internal override Array AllocateVector(VTable vtable, + int numElements, + Thread currentThread) + { + UIntPtr numBytes = + ObjectLayout.ArraySize(vtable, unchecked((uint)numElements)); + UIntPtr vectorAddr = + AllocateObjectMemory(numBytes, vtable.baseAlignment, + currentThread); + Array result = Magic.toArray(Magic.fromAddress(vectorAddr)); + CreateObject(result, vtable, currentThread); + result.InitializeVectorLength(numElements); + return result; + } + + [ManualRefCounts] + internal override Array AllocateArray(VTable vtable, + int rank, + int totalElements, + Thread currentThread) + { + UIntPtr numBytes = + ObjectLayout.ArraySize(vtable, unchecked((uint)totalElements)); + UIntPtr arrayAddr = + AllocateObjectMemory(numBytes, vtable.baseAlignment, + currentThread); + Array result = Magic.toArray(Magic.fromAddress(arrayAddr)); + CreateObject(result, vtable, currentThread); + result.InitializeArrayLength(rank, totalElements); + return result; + } + + [ManualRefCounts] + internal override String AllocateString(int stringLength, + Thread currentThread) + { + VTable vtable = + Magic.toRuntimeType(typeof(System.String)).classVtable; + UIntPtr numBytes = + ObjectLayout.StringSize(vtable, + unchecked((uint) (stringLength+1))); + UIntPtr stringAddr = + AllocateObjectMemory(numBytes, unchecked((uint) UIntPtr.Size), + currentThread); + String result = Magic.toString(Magic.fromAddress(stringAddr)); + CreateObject(result, vtable, currentThread); + result.InitializeStringLength(stringLength); + return result; + } + + [ManualRefCounts] + [AssertDevirtualize] + [Inline] + protected virtual void CreateObject(Object obj, VTable vtable, + Thread currentThread) + { + Barrier.InitObject(obj, vtable); + } + +#endregion + +#region Profiling + + internal override void SetProfiler(GCProfiler profiler) { + if (GcProfiler != null) { + throw new InvalidOperationException("Only one GCProfiler can be active in a process"); + } + ProfileRoots = new ProfileRootsDelegate(ProfileScanRoots); + ProfileObjects = new ProfileObjectsDelegate(ProfileScanObjects); + + GcProfiler = profiler; + } + + // A profiler can request a scan of all Roots, passing in a + // visitor for callback. + private void ProfileScanRoots(NonNullReferenceVisitor visitor) { + CallStack.ScanStacks(visitor, visitor); + Thread.VisitBootstrapData(visitor); +#if SINGULARITY_KERNEL + Kernel.VisitSpecialData(visitor); +#endif + MultiUseWord.VisitStrongRefs(visitor, + false /* Don't use shadows */); + StaticData.ScanStaticData(visitor); + } + + // A profiler can request a scan of all Objects in the heap, + // passing in a visitor for callback. + private + void ProfileScanObjects(SegregatedFreeList.ObjectVisitor visitor) + { +#if !SINGULARITY + VTable.Assert((System.GC.installedGC as MarkSweepCollector != null) + || (System.GC.installedGC as ConcurrentMSCollector != null) + || (System.GC.installedGC as ReferenceCountingCollector != null) + || (System.GC.installedGC as DeferredReferenceCountingCollector != null), + "ProfileScanObjects is only valid for MarkSweep, ConcurrentMS, " + + "ReferenceCounting, and DeferredReferenceCounting collectors"); +#endif + SegregatedFreeList.VisitAllObjects(visitor); + } + + internal override void ProfileAllocation(Object obj) + { + if (GC.IsProfiling && !HeapDamaged) { + UIntPtr size = ObjectLayout.Sizeof(obj); + GcProfiler.NotifyAllocation(Magic.addressOf(obj), + obj.GetType(), size); + } + } + + protected static GCProfiler GcProfiler; + protected static ProfileRootsDelegate ProfileRoots; + protected static ProfileObjectsDelegate ProfileObjects; + protected static bool HeapDamaged; + +#endregion + +#region Accounting + + private static UIntPtr newBytesSinceGC; + internal static long gcTotalBytes; + internal static int gcTotalCount; + internal static long gcTotalTime; + private static long maxPauseTime; + private static long pauseCount; + + // Abstraction violation. Use only for debugging! + internal static UIntPtr DebugNewBytesSinceGC { + get { return newBytesSinceGC; } + } + + internal static bool NewBytesSinceGCExceeds(UIntPtr limit) + { + return newBytesSinceGC >= limit; + } + + internal static void IncrementNewBytesSinceGC(UIntPtr increment) + { + newBytesSinceGC += increment; + } + + internal static void StartGCCycle() + { + gcTotalBytes += (long) newBytesSinceGC; + newBytesSinceGC = UIntPtr.Zero; + gcTotalCount++; + } + + internal static void RegisterPause(int pauseTicks) + { + gcTotalTime += pauseTicks; + if (maxPauseTime < pauseTicks) { + maxPauseTime = pauseTicks; + } + pauseCount++; + } + + internal virtual void RegisterHeapSize(ulong heapSize) + { + } + + internal virtual void RegisterNewObject(ulong objectSize) + { + } + + internal virtual void PrintGCTiming() + { + if (VTable.enableGCTiming || VTable.enableFinalGCTiming) { +#if SINGULARITY + DebugStub.WriteLine("Total GC Time (ms): {0}", + __arglist(gcTotalTime)); +#else + Console.Error.WriteLine("Total GC Time (ms): "+gcTotalTime); + Console.Error.WriteLine("Max. Pause Time (ms): "+maxPauseTime); + if (BaseCollector.pauseCount != 0) { + Console.Error.WriteLine("Avg. Pause Time (ms): "+ + gcTotalTime/pauseCount); + } else { + Console.Error.WriteLine("Avg. Pause Time (ms): 0"); + } +#endif + } + } + + internal virtual void PrintAllocations() + { + } + +#endregion + +#region Helper functions + + internal static void AllThreadRendezvous(int currentThreadIndex) + { + Transitions.MakeGCRequests(currentThreadIndex); + for (int i = 0; i < Thread.threadTable.Length; i++) { + if (Thread.threadTable[i] == null || + i == currentThreadIndex) { + continue; + } + CollectorStatistics.Event(GCEvent.StopThread, i); + while (!Transitions.TakeGCControl(i) && + !Transitions.UnderGCControl(i) && + Transitions.HasGCRequest(i) && + Thread.threadTable[i] != null) { + // NOTE: there is no code in this loop that could + // cause a signal on an event to be consumed. + Thread.WaitForGCEvent(currentThreadIndex); + } + } + } + + internal static void AllThreadRelease(int currentThreadIndex) + { + for (int i = 0; i < Thread.threadTable.Length; i++) { +#if SINGULARITY_KERNEL + if (Scheduler.IsIdleThread(i)) { + continue; + } +#endif + if (i == currentThreadIndex) { + if (Transitions.HasGCRequest(i)) { + Transitions.ClearGCRequest(i); + } + } else if (Transitions.UnderGCControl(i)) { + Transitions.ReleaseGCControl(i); + } + // Signal all threads to ensure that the GC process didn't + // accidentally "gobble" an event signal that was meant for + // something else. + if (Thread.threadTable[i] != null) { + Thread.SignalGCEvent(i); + } + } + } + +#endregion + } + +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/BootstrapMemory.cs b/base/Imported/Bartok/runtime/shared/GCs/BootstrapMemory.cs new file mode 100644 index 0000000..b77d5c7 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/BootstrapMemory.cs @@ -0,0 +1,168 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + + +namespace System.GCs { + + using Microsoft.Bartok.Runtime; + using System.Threading; + using System.Runtime.CompilerServices; + + internal unsafe class BootstrapMemory : Allocator + { + + // WARNING: don't initialize any static fields in this class + // without manually running the class constructor at startup! + + private static UIntPtr allocPtr; + private static UIntPtr limitPtr; + + [PreInitRefCounts] +#if !SINGULARITY // Needed before Bartok runtime is initialized + [NoStackLinkCheck] +#endif + [NoBarriers] + internal static void Initialize(UIntPtr systemMemorySize) { + allocPtr = MemoryManager.AllocateMemory(systemMemorySize); + limitPtr = allocPtr + systemMemorySize; + if(GC.gcType != GCType.NullCollector) { + PageManager.SetStaticDataPages(allocPtr, systemMemorySize); +#if !SINGULARITY + PageTable.SetProcess(PageTable.Page(allocPtr), + PageTable.PageCount(systemMemorySize)); +#endif + } + } + + [NoBarriers] + private static UIntPtr AllocateBlock(UIntPtr bytes, uint alignment) + { + UIntPtr startPtr = + Allocator.AlignedAllocationPtr(allocPtr, limitPtr, alignment); + allocPtr = startPtr + bytes; + if (allocPtr > limitPtr) { + VTable.DebugPrint("Out of BootstrapMemory"); + VTable.DebugBreak(); + } + return startPtr + PreHeader.Size; + } + + [NoBarriers] + [ManualRefCounts] +#if !SINGULARITY // Needed before Bartok runtime is initialized + [NoStackLinkCheckTrans] +#endif + internal static Object Allocate(VTable vtable) { + UIntPtr numBytes = ObjectLayout.ObjectSize(vtable); + UIntPtr objectAddr = AllocateBlock(numBytes, vtable.baseAlignment); + Object result = Magic.fromAddress(objectAddr); +#if REFERENCE_COUNTING_GC + uint refState = vtable.isAcyclicRefType ? + (ReferenceCountingCollector. + acyclicFlagMask | 2) : 2; + result.REF_STATE = refState & + ~ReferenceCountingCollector.countingONFlagMask; +#elif DEFERRED_REFERENCE_COUNTING_GC + uint refState = vtable.isAcyclicRefType ? + (DeferredReferenceCountingCollector. + acyclicFlagMask | + DeferredReferenceCountingCollector. + markFlagMask) : + DeferredReferenceCountingCollector. + markFlagMask; + result.REF_STATE = refState & + ~DeferredReferenceCountingCollector.countingONFlagMask; +#endif + Barrier.BootstrapInitObject(result, vtable); + return result; + } + + [NoBarriers] + [ManualRefCounts] +#if !SINGULARITY // Needed before Bartok runtime is initialized + [NoStackLinkCheckTrans] +#endif + internal static Object Allocate(VTable vtable, uint count) { + UIntPtr numBytes = ObjectLayout.ArraySize(vtable, count); + UIntPtr objectAddr = AllocateBlock(numBytes, vtable.baseAlignment); + Array result = Magic.toArray(Magic.fromAddress(objectAddr)); +#if REFERENCE_COUNTING_GC + uint refState = vtable.isAcyclicRefType ? + (ReferenceCountingCollector. + acyclicFlagMask | 2) : 2; + result.REF_STATE = refState & + ~ReferenceCountingCollector.countingONFlagMask; +#elif DEFERRED_REFERENCE_COUNTING_GC + uint refState = vtable.isAcyclicRefType ? + (DeferredReferenceCountingCollector. + acyclicFlagMask | + DeferredReferenceCountingCollector. + markFlagMask) : + DeferredReferenceCountingCollector. + markFlagMask; + result.REF_STATE = refState & + ~DeferredReferenceCountingCollector.countingONFlagMask; +#endif + Barrier.BootstrapInitObject(result, vtable); + result.InitializeVectorLength((int) count); + return result; + } + + [NoBarriers] + [PreInitRefCounts] +#if !SINGULARITY + [NoStackLinkCheckTrans] +#endif + internal static Object Allocate(Type t) { + return Allocate(Magic.toRuntimeType(t)); + } + + [NoBarriers] + [PreInitRefCounts] +#if !SINGULARITY + [NoStackLinkCheckTrans] +#endif + internal static Object Allocate(Type t, uint count) { + return Allocate(Magic.toRuntimeType(t), count); + } + + [NoBarriers] + [PreInitRefCounts] +#if !SINGULARITY + [NoStackLinkCheckTrans] +#endif + internal static Object Allocate(RuntimeType t) { + return Allocate(t.classVtable); + } + + [NoBarriers] + [PreInitRefCounts] +#if !SINGULARITY + [NoStackLinkCheckTrans] +#endif + internal static Object Allocate(RuntimeType t, uint count) { + return Allocate(t.classVtable, count); + } + + internal static void Truncate() { + UIntPtr allocLimit = PageTable.PagePad(allocPtr); + UIntPtr unusedSize = limitPtr - allocLimit; + if(GC.gcType != GCType.NullCollector) { + PageManager.ReleaseUnusedPages(PageTable.Page(allocLimit), + PageTable.PageCount(unusedSize), + true); + } + limitPtr = allocLimit; + } + + } + +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/BrooksBarrierTest.cs b/base/Imported/Bartok/runtime/shared/GCs/BrooksBarrierTest.cs new file mode 100644 index 0000000..52e7390 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/BrooksBarrierTest.cs @@ -0,0 +1,152 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using Microsoft.Bartok.Runtime; + using Microsoft.Bartok.Options; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using System.Threading; + + internal unsafe class BrooksBarrierTest: UniversalWriteBarrier { + internal static BrooksBarrierTest instance; + + internal static bool allowFastPath; + + [NoBarriers] + internal static new void Initialize() + { + BrooksBarrierTest.instance = + (BrooksBarrierTest) + BootstrapMemory.Allocate(typeof(BrooksBarrierTest)); + } + + [RequiredByBartok] + [MixinConditional("BrooksBarrierTest")] + [Mixin(typeof(PreHeader))] + internal struct BrooksPreHeader { + internal MultiUseWord muw; + [SelfPoint] + internal Object forward; + } + + [MixinConditional("BrooksBarrierTest")] + [Mixin(typeof(Object))] + internal class BrooksObject: System.Object { + internal new BrooksPreHeader preHeader; + } + + internal static BrooksObject MixinObject(Object o) { + return (BrooksObject)o; + } + + [NoBarriers] + internal static Object Forward(Object o) + { + Object result=MixinObject(o).preHeader.forward; + VTable.Assert(result == o); + return result; + } + + [NoBarriers] + internal static Object ForwardNullable(Object o) + { + if (o==null) { + return null; + } else { + return Forward(o); + } + } + + [NoBarriers] + [Inline] + protected override Object ForwardImpl(Object o,int mask) + { + if ((mask&BarrierMask.PathSpec.AllowFast)!=0) { + return o; + } else { + if ((mask&BarrierMask.Forward.Nullable)!=0 && o == null) { + return null; + } else { + return Forward(o); + } + } + } + + [ForceInline] + [NoBarriers] + protected override bool AllowFastPathImpl() + { + return allowFastPath; + } + + [ForceInline] + [NoBarriers] + protected override void InitObjectImpl(Object o, VTable vtable) + { + MyInitObject(o, vtable); + } + + [ForceInline] + [NoBarriers] + internal static new void BootstrapInitObjectImpl(Object o, + VTable vtable) + { + MyInitObject(o, vtable); + } + + [ForceInline] + [NoBarriers] + private static void MyInitObject(Object o, VTable vtable) + { + MixinObject(o).preHeader.forward = o; + o.vtable = vtable; + } + + [ForceInline] + [NoBarriers] + protected override Object AtomicCompareAndSwapImpl(ref Object loc, + Object newVal, + Object comp, + int mask) + { + for (;;) { + Object oldVal = loc; + Object commitVal; + if (oldVal == comp || + ForwardNullable(oldVal) == ForwardNullable(comp)) { + commitVal = newVal; + } else { + // still CAS but only to get a memory barrier + commitVal = oldVal; + } + if (Interlocked.CompareExchange(ref loc, + commitVal, oldVal) + == oldVal) { + return oldVal; + } + } + } + + [ForceInline] + [NoBarriers] + protected override bool EqImpl(Object a,Object b,int mask) + { + if ((mask&BarrierMask.PathSpec.AllowFast)!=0) { + return a == b; + } else { + return a == b || ForwardNullable(a) == ForwardNullable(b); + } + } + } +} + diff --git a/base/Imported/Bartok/runtime/shared/GCs/BrooksCMSBarrierTest.cs b/base/Imported/Bartok/runtime/shared/GCs/BrooksCMSBarrierTest.cs new file mode 100644 index 0000000..a605435 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/BrooksCMSBarrierTest.cs @@ -0,0 +1,187 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using Microsoft.Bartok.Runtime; + using Microsoft.Bartok.Options; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using System.Threading; + + internal unsafe class BrooksCMSBarrierTest: UniversalWriteBarrier { + internal static BrooksCMSBarrierTest instance; + + internal static bool allowFastPath; + + [NoBarriers] + internal static new void Initialize() + { + BrooksCMSBarrierTest.instance = + (BrooksCMSBarrierTest) + BootstrapMemory.Allocate(typeof(BrooksCMSBarrierTest)); + } + + [RequiredByBartok] + [MixinConditional("BrooksCMSBarrierTest")] + [Mixin(typeof(PreHeader))] + internal struct BrooksPreHeader { + internal MultiUseWord muw; + [SelfPoint] + internal Object forward; + } + + [MixinConditional("BrooksCMSBarrierTest")] + [Mixin(typeof(Object))] + internal class BrooksObject: System.Object { + internal new BrooksPreHeader preHeader; + } + + internal static BrooksObject MixinObject(Object o) { + return (BrooksObject)o; + } + + [NoBarriers] + internal static Object Forward(Object o) + { + Object result=MixinObject(o).preHeader.forward; + VTable.Assert(result == o); + return result; + } + + [NoBarriers] + internal static Object ForwardNullable(Object o) + { + if (o==null) { + return null; + } else { + return Forward(o); + } + } + + [NoBarriers] + [Inline] + protected override Object ForwardImpl(Object o, int mask) + { + if ((mask&BarrierMask.PathSpec.AllowFast)!=0) { + return o; + } else { + if ((mask&BarrierMask.Forward.Nullable)!=0 && o == null) { + return null; + } else { + return Forward(o); + } + } + } + + [ForceInline] + [NoBarriers] + protected override bool AllowFastPathImpl() + { + return allowFastPath; + } + + [ForceInline] + [NoBarriers] + protected override void InitObjectImpl(Object o, VTable vtable) + { + MyInitObject(o, vtable); + } + + internal static new void BootstrapInitObjectImpl(Object o, + VTable vtable) + { + MyInitObject(o, vtable); + } + + [ForceInline] + [NoBarriers] + private static void MyInitObject(Object o, VTable vtable) + { + MixinObject(o).preHeader.forward = o; + o.vtable = vtable; + } + + [ForceInline] + [NoBarriers] + protected override Object AtomicSwapImpl(ref Object reference, + Object value, + int mask) + { + CMSMarking.ReferenceCheck(ref reference, value, 0); + UIntPtr resultAddr = + Interlocked.Exchange(Magic.toPointer(ref reference), + Magic.addressOf(value)); + return Magic.fromAddress(resultAddr); + } + + [ForceInline] + [NoBarriers] + protected override Object AtomicCompareAndSwapImpl(ref Object loc, + Object newVal, + Object comp, + int mask) + { + CMSMarking.ReferenceCheck(ref loc, newVal, 0); + newVal = ForwardNullable(newVal); + for (;;) { + Object oldVal = loc; + Object commitVal; + if (oldVal == comp || + ForwardNullable(oldVal) == ForwardNullable(comp)) { + commitVal = newVal; + } else { + // still CAS but only to get a memory barrier + commitVal = oldVal; + } + if (Interlocked.CompareExchange(ref loc, + commitVal, oldVal) + == oldVal) { + return oldVal; + } + } + } + + [ForceInline] + [NoBarriers] + protected override bool EqImpl(Object a, Object b, int mask) + { + if ((mask&BarrierMask.PathSpec.AllowFast)!=0) { + return a == b; + } else { + return a == b || ForwardNullable(a) == ForwardNullable(b); + } + } + + [ForceInline] + [NoBarriers] + [NoStackLinkCheckTrans] + protected override void WriteImpl(UIntPtr *location, + Object value, + int mask) + { + CMSMarking.ReferenceCheck(location, value, 0); + *location = Magic.addressOf(value); + } + + [ForceInline] + [NoBarriers] + [NoStackLinkCheckTrans] + protected override void WriteImplByRef(ref Object location, + Object value, + int mask) + { + CMSMarking.ReferenceCheck(ref location, value, 0); + location = value; + } + } +} + diff --git a/base/Kernel/Bartok/GCs/BumpAllocator.cs b/base/Imported/Bartok/runtime/shared/GCs/BumpAllocator.cs similarity index 96% rename from base/Kernel/Bartok/GCs/BumpAllocator.cs rename to base/Imported/Bartok/runtime/shared/GCs/BumpAllocator.cs index e991566..75b717b 100644 --- a/base/Kernel/Bartok/GCs/BumpAllocator.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/BumpAllocator.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,9 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// // Conceptually, even if nothing is recorded in the offset table // during object allocation, the offset table itself should be @@ -53,7 +54,9 @@ namespace System.GCs { [MixinConditional("BumpAllocator")] [MixinConditional("AllThreadMixins")] [Mixin(typeof(Thread))] - private class BumpAllocatorThread : Object { + // Should be private, but mixin implementation will warn if visibility + // does not match that of Thread. + public sealed class BumpAllocatorThread : Object { // Thread-specific bump allocator [RequiredByBartok] internal BumpAllocator bumpAllocator; @@ -230,7 +233,7 @@ namespace System.GCs { { #if SINGULARITY_KERNEL #if ENSURE_ALLOCATION_ALLOWED - BumpAllocator.EnsureAllocationAllowed(); + // BumpAllocator.EnsureAllocationAllowed(); #endif #endif UIntPtr allocPtr = @@ -297,7 +300,7 @@ namespace System.GCs { fCleanPages); return UIntPtr.Zero; } - GC.newBytesSinceGC += paddedNeed; + BaseCollector.IncrementNewBytesSinceGC(paddedNeed); this.allocNew = this.reserveLimit; // Pad alignment space if necessary. NB: a prior call to // AllocateFast may have started generating alignment tokens, @@ -380,7 +383,7 @@ namespace System.GCs { this.Truncate(); UIntPtr paddedBytes = PageTable.PagePad(bytes + alignment - UIntPtr.Size); - GC.newBytesSinceGC += paddedBytes; + BaseCollector.IncrementNewBytesSinceGC(paddedBytes); UIntPtr pages = PageTable.PageCount(paddedBytes); bool fCleanPages = CLEAR_POOL_PAGES(); // We may eventually want to ask for specific pages diff --git a/base/Imported/Bartok/runtime/shared/GCs/CMSMarking.cs b/base/Imported/Bartok/runtime/shared/GCs/CMSMarking.cs new file mode 100644 index 0000000..60928f9 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/CMSMarking.cs @@ -0,0 +1,208 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + + +namespace System.GCs { + + using Microsoft.Bartok.Runtime; + using System.Threading; + using System.Runtime.CompilerServices; + + //[NoBarriers] + internal unsafe class CMSMarking + { + + internal enum MarkingPhase { + Dummy, // Not used! + Idle, // We are not doing anything at this point + Requested, // Someone has asked for a GC to be started + Preparing, // No marking, but will be soon + ComputingRoots, // Marking the roots + Tracing, // Tracing gray objects + StopTheWorld, // Stop all mutators to do a STW trace + } + + internal static int currentMarkingPhase; + + internal static UIntPtr markedColor; + internal static UIntPtr unmarkedColor; + + internal static MarkingPhase CurrentMarkingPhase { + [NoStackLinkCheck] + get { return (MarkingPhase) currentMarkingPhase; } + } + + // We use this negated variable because it is initialized by + // the compiler to be false. This allows the runtime system + // to use the fast code paths early in the bootstrap process. + private static bool referenceCheckIsSlow; + + internal static bool referenceCheckIsFast + { + [Inline] + get { return !referenceCheckIsSlow; } + [Inline] + set { referenceCheckIsSlow = !value; } + } + + internal static bool ReferenceCheckIsFast(int mask) + { + if ((mask & BarrierMask.PathSpec.UseMask) != 0) { + return (mask & BarrierMask.PathSpec.AllowFast) !=0; + } else { + return referenceCheckIsFast; + } + } + + [NoInline] + [CalledRarely] + [NoStackLinkCheckTrans] + internal static void ReferenceCheckSlow(UIntPtr *addr, Object value) + { +#if !SINGULARITY || CONCURRENT_MS_COLLECTOR + UIntPtr oldValue = *addr; + MarkIfNecessary(oldValue); + if (CurrentMarkingPhase == MarkingPhase.ComputingRoots) { + MarkIfNecessary(Magic.addressOf(value)); + } +#endif // CONCURRENT_MS_COLLECTOR + } + + /// + /// In the sliding views phase, where some threads may have + /// scanned their roots and others have not, we need to ensure + /// that both old and new values will be marked and scanned. + /// In the tracing phase we only need to ensure that the old + /// values are traced and marked, as the old values may be the + /// only references to a part of the snapshot reachable object + /// graph from the untraced part of the object graph. + /// + /// The memory location being modified + /// The reference value to be written into + /// the "addr" location + /// The barrier mode mask generated by the + /// compiler + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static void ReferenceCheck(UIntPtr *addr, Object value, + int mask) + { +#if !SINGULARITY || CONCURRENT_MS_COLLECTOR + if (!ReferenceCheckIsFast(mask)) { + ReferenceCheckSlow(addr, value); + } +#endif // CONCURRENT_MS_COLLECTOR + } + + [NoInline] + [CalledRarely] + [NoStackLinkCheckTrans] + internal static void ReferenceCheckSlow(ref Object reference, Object value) + { +#if !SINGULARITY || CONCURRENT_MS_COLLECTOR + UIntPtr oldValue = Magic.addressOf(reference); + MarkIfNecessary(oldValue); + if (CurrentMarkingPhase == MarkingPhase.ComputingRoots) { + MarkIfNecessary(Magic.addressOf(value)); + } +#endif // CONCURRENT_MS_COLLECTOR + } + + /// + /// In the sliding views phase, where some threads may have + /// scanned their roots and others have not, we need to ensure + /// that both old and new values will be marked and scanned. + /// In the tracing phase we only need to ensure that the old + /// values are traced and marked, as the old values may be the + /// only references to a part of the snapshot reachable object + /// graph from the untraced part of the object graph. + /// + /// The memory location being modified + /// The reference value to be written into + /// the "addr" location + /// The barrier mode mask generated by the + /// compiler + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + internal static void ReferenceCheck(ref Object reference, Object value, + int mask) + { +#if !SINGULARITY || CONCURRENT_MS_COLLECTOR + if (!ReferenceCheckIsFast(mask)) { + ReferenceCheckSlow(ref reference, value); + } +#endif // CONCURRENT_MS_COLLECTOR + } + + /// + /// Ensures that a reference value is going to be marked and + /// scanned. + /// + /// The reference value that may need to + /// be marked + [NoBarriers] + [NoStackLinkCheckTrans] + internal static bool MarkIfNecessary(UIntPtr value) + { +#if !SINGULARITY || CONCURRENT_MS_COLLECTOR + if (value == 0) { + return false; + } + UIntPtr marked = markedColor; + if (PageTable.IsGcPage(PageTable.Page(value)) && + ThreadHeaderQueue.GcMark(Magic.fromAddress(value)) != marked) { + VTable.Assert(PageTable.IsMyPage(PageTable.Page(value))); + Thread thread = Thread.CurrentThread; + UIntPtr unmarked = unmarkedColor; + ThreadHeaderQueue.Push(thread, value, marked, unmarked); + return true; + } +#endif // CONCURRENT_MS_COLLECTOR + return false; + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + [DisableNullChecks] + internal static void MarkIfNecessaryInline(UIntPtr value, + Thread thread) + { +#if !SINGULARITY || CONCURRENT_MS_COLLECTOR + UIntPtr marked = markedColor; + if (ThreadHeaderQueue.GcMark(Magic.fromAddress(value)) != marked) { + VTable.Assert(PageTable.IsMyPage(PageTable.Page(value))); + UIntPtr unmarked = unmarkedColor; + ThreadHeaderQueue.Push(thread, value, marked, unmarked); + } +#endif // CONCURRENT_MS_COLLECTOR + } + + /// + /// Ensures that an object is going to be marked and scanned. + /// + + // Change these to ///-style comments when the TODO is removed: + // + // The object that may need to be marked + // !TODO! + [Inline] + internal static void MarkObject(UIntPtr value, Thread t) { +#if !SINGULARITY || CONCURRENT_MS_COLLECTOR + MarkIfNecessaryInline(value, t); +#endif // CONCURRENT_MS_COLLECTOR + } + + } + +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/CallStack.cs b/base/Imported/Bartok/runtime/shared/GCs/CallStack.cs new file mode 100644 index 0000000..b081b9b --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/CallStack.cs @@ -0,0 +1,622 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using System.Threading; + + using Microsoft.Bartok.Options; + using Microsoft.Bartok.Runtime; + +#if SINGULARITY_KERNEL +#elif SINGULARITY_PROCESS + using Microsoft.Singularity.V1.Services; +#endif + + [NoCCtor] + [AccessedByRuntime("referenced from halexn.cpp")] + internal unsafe class CallStack + { + +#if SINGULARITY + // Static fields to be initialized to the addresses of labels + // surrounding the linked-stack assembly code that we must skip. + private static UIntPtr LinkStackFunctionsBegin; + private static UIntPtr LinkStackFunctionsLimit; + private static UIntPtr LinkStackBegin; + private static UIntPtr LinkStackLimit; + private static UIntPtr UnlinkStackBegin; + private static UIntPtr UnlinkStackLimit; + private static UIntPtr LinkStackStubsBegin; + private static UIntPtr LinkStackStubsLimit; +#endif + + internal static void Initialize() + { +#if SINGULARITY + // Linked stacks pose a special problem for stack scanning. + // The stack will contain "stuff" from the assembly code + // implementations of linked stacks, and the stack walking + // code needs to simply skip that "stuff". Since the code + // is defined in a single .asm file, we use labels around + // the code to help us identify the PC values that will need + // special processing. Initialize the above static fields + // to hold the addresses of the labels surrounding the code. + fixed (byte *regionBegin = + &Microsoft.Singularity.Memory.Stacks.LinkStackFunctionsBegin) { + LinkStackFunctionsBegin = (UIntPtr) regionBegin; + } + fixed (byte *regionLimit = + &Microsoft.Singularity.Memory.Stacks.LinkStackFunctionsLimit) { + LinkStackFunctionsLimit = (UIntPtr) regionLimit; + } + fixed (byte *linkStackBegin = + &Microsoft.Singularity.Memory.Stacks.LinkStackBegin) { + LinkStackBegin = (UIntPtr) linkStackBegin; + } + fixed (byte *linkStackLimit = + &Microsoft.Singularity.Memory.Stacks.LinkStackLimit) { + LinkStackLimit = (UIntPtr) linkStackLimit; + } + fixed (byte *unlinkStackBegin = + &Microsoft.Singularity.Memory.Stacks.UnlinkStackBegin) { + UnlinkStackBegin = (UIntPtr) unlinkStackBegin; + } + fixed (byte *unlinkStackLimit = + &Microsoft.Singularity.Memory.Stacks.UnlinkStackLimit) { + UnlinkStackLimit = (UIntPtr) unlinkStackLimit; + } + fixed (byte *linkStackStubsBegin = + &Microsoft.Singularity.Memory.Stacks.LinkStackStubsBegin) { + LinkStackStubsBegin = (UIntPtr) linkStackStubsBegin; + } + fixed (byte *linkStackStubsLimit = + &Microsoft.Singularity.Memory.Stacks.LinkStackStubsLimit) { + LinkStackStubsLimit = (UIntPtr)linkStackStubsLimit; + } +#endif + } + + [Mixin(typeof(Thread))] + // Should be private, but mixin implementation will warn if visibility + // does not match that of Thread. + public sealed class CallStackThread : Object { + [AccessedByRuntime("referenced in brtstack.asm")] + internal UIntPtr asmStackBase; // Limit of thread's stack + [AccessedByRuntime("referenced in brtstack.asm")] + internal UIntPtr asmStackLimit; + [AccessedByRuntime("referenced in brtforgc.asm")] + internal unsafe TransitionRecord *asmStackMarker; + } + + private static CallStackThread MixinThread(Thread t) { + return (CallStackThread) (Object) t; + } + + internal static UIntPtr StackBase(Thread t) { + return MixinThread(t).asmStackBase; + } + + internal static void SetStackBase(Thread t, UIntPtr value) { + MixinThread(t).asmStackBase = value; + } + + internal static UIntPtr StackLimit(Thread t) { + return MixinThread(t).asmStackLimit; + } + + internal static void SetStackLimit(Thread t, UIntPtr value) { + MixinThread(t).asmStackLimit = value; + } + + internal static TransitionRecord* StackMarker(Thread t) + { + return MixinThread(t).asmStackMarker; + } + + [AccessedByRuntime("referenced from halforgc.asm")] + [RequiredByBartok] + internal unsafe struct TransitionRecord { + [AccessedByRuntime("referenced from halforgc.asm")] + internal TransitionRecord *oldTransitionRecord; + [AccessedByRuntime("referenced from halforgc.asm")] + internal UIntPtr callAddr; + [AccessedByRuntime("referenced from halforgc.asm")] + internal UIntPtr stackBottom; + [AccessedByRuntime("referenced from halforgc.asm")] + internal CalleeSaveRegisters calleeSaveRegisters; + } + + internal static Thread threadBeingProcessed; + + [RequiredByBartok] + private static int callSiteTableCount; + [AccessedByRuntime("referenced from halexn.cpp")] + private static UIntPtr *codeBaseStartTable; + [RequiredByBartok] + private static UIntPtr **returnAddressToCallSiteSetNumbers; + [RequiredByBartok] + private static int **callSiteSetCount; + + private static int CallSiteTableNumber(UIntPtr returnAddr) + { + UIntPtr address = returnAddr; + for (int i = 0; i < callSiteTableCount; i++) { + UIntPtr baseAddress = codeBaseStartTable[i]; + if (address < baseAddress) { + continue; + } + UIntPtr relativeAddress = address - baseAddress; + UIntPtr *ptr = returnAddressToCallSiteSetNumbers[i]; + int callSiteCount = *(callSiteSetCount[i]); + if (relativeAddress >= ptr[0] && + relativeAddress <= ptr[callSiteCount]) { + return i; + } + } + return -1; + } + + private static int CallSiteSetNumber(UIntPtr returnAddr, int index) + { + UIntPtr codeBaseAddr = codeBaseStartTable[index]; + UIntPtr relativeAddr = returnAddr - codeBaseAddr; + UIntPtr *callSiteTable = returnAddressToCallSiteSetNumbers[index]; + int callSiteCount = *(callSiteSetCount[index]); + int left = 0; + int right = callSiteCount; + // Loop invariant: + // callSiteTable[left] <= returnAddress < callSiteTable[right] + while (left < right-1) { + int mid = (left + right)/2; + if (callSiteTable[mid] <= relativeAddr) { + left = mid; + } else { + right = mid; + } + } + return left; + } + + [StructLayout(LayoutKind.Sequential)] + private struct FullDescriptor { + internal UIntPtr mask; + internal int variableData; + } + + private const uint ESCAPE32_TAG = 0x0; + private const uint ESCAPE16_TAG = 0x1; + private const uint ESCAPE8_TAG = 0x2; + + // Check whether the specified stack frame contains the + // transition record: if so then we're done scanning this + // segment of the stack. NB: the caller must ensure that the + // framePointer has been recomputed in a + // framePointerOmitted-method. + private static bool FrameContainsTransitionRecord(UIntPtr *framePointer, + UIntPtr *stackPointer, + TransitionRecord *stopMarker) { + bool result = false; + if ((framePointer >= stopMarker) && (stackPointer < stopMarker)) { + result = true; + } + return result; + } + + private static + bool WalkStackFrame(ref UIntPtr *framePointer, + ref UIntPtr *stackPointer, + ref CalleeSaveLocations calleeSaves, + ref UIntPtr returnAddr, + NonNullReferenceVisitor threadReferenceVisitor, + NonNullReferenceVisitor pinnedReferenceVisitor, + CompressedFrameDescriptor smallFrameDescriptor, + TransitionRecord *stopMarker, + Thread thread) + // BUGBUG: Remove thread argument when ThreadContext is on stack! + { + FrameDescriptor frameDescriptor = + new FrameDescriptor(smallFrameDescriptor); + if (frameDescriptor.isFramePointerOmitted) { + framePointer = stackPointer + frameDescriptor.frameSize; + } + if (FrameContainsTransitionRecord(framePointer, stackPointer, + stopMarker)) { + return true; // true: done + } + switch (frameDescriptor.argumentTag) { + case FrameDescriptor.ESCAPE32_TAG: { + int *table = (int *) frameDescriptor.argumentMaskOrTable; + int count = table[0]; + int pinnedCount = table[1]; + int *offsets = &table[2]; + if (threadReferenceVisitor != null) { + for (int i = 0; i < count; i++) { + UIntPtr *loc = framePointer + offsets[i]; + if (*loc != UIntPtr.Zero) { + // BUGBUG: threadReferenceVisitor.Visit(loc); + VisitIfNotContext(thread, threadReferenceVisitor, loc); + } + } + } + if (frameDescriptor.hasPinnedVariables && + pinnedReferenceVisitor != null) { + offsets = &offsets[count]; + for (int i = 0; i < pinnedCount; i++) { + UIntPtr *loc = framePointer + offsets[i]; + if (*loc != UIntPtr.Zero) { + pinnedReferenceVisitor.Visit(loc); + } + } + } + break; + } + case FrameDescriptor.ESCAPE16_TAG: { + short *table = (short *) frameDescriptor.argumentMaskOrTable; + int count = table[0]; + int pinnedCount = table[1]; + short *offsets = &table[2]; + if (threadReferenceVisitor != null) { + for (int i = 0; i < count; i++) { + UIntPtr *loc = framePointer + offsets[i]; + if (*loc != UIntPtr.Zero) { + // BUGBUG: threadReferenceVisitor.Visit(loc); + VisitIfNotContext(thread, threadReferenceVisitor, loc); + } + } + } + if (frameDescriptor.hasPinnedVariables && + pinnedReferenceVisitor != null) { + offsets = &offsets[count]; + for (int i = 0; i < pinnedCount; i++) { + UIntPtr *loc = framePointer + offsets[i]; + if (*loc != UIntPtr.Zero) { + pinnedReferenceVisitor.Visit(loc); + } + } + } + break; + } + case FrameDescriptor.ESCAPE8_TAG: { + sbyte *table = (sbyte *) frameDescriptor.argumentMaskOrTable; + int count = table[0]; + int pinnedCount = table[1]; + sbyte *offsets = &table[2]; + if (threadReferenceVisitor != null) { + for (int i = 0; i < count; i++) { + UIntPtr *loc = framePointer + offsets[i]; + if (*loc != UIntPtr.Zero) { + // BUGBUG: threadReferenceVisitor.Visit(loc); + VisitIfNotContext(thread, threadReferenceVisitor, loc); + } + } + } + if (frameDescriptor.hasPinnedVariables && + pinnedReferenceVisitor != null) { + offsets = &offsets[count]; + for (int i = 0; i < pinnedCount; i++) { + UIntPtr *loc = framePointer + offsets[i]; + if (*loc != UIntPtr.Zero) { + pinnedReferenceVisitor.Visit(loc); + } + } + } + break; + } + case FrameDescriptor.COMPRESSED_MASK_TAG: { + // Process the arguments + int inBetweenSlotsAbove = + frameDescriptor.inBetweenSlotsAbove; + uint stackArgSize = frameDescriptor.argumentCount; + UIntPtr mask = frameDescriptor.argumentMaskOrTable; + if (threadReferenceVisitor != null && stackArgSize > 0) { + UIntPtr *argumentPointer = + framePointer + stackArgSize - 1 + inBetweenSlotsAbove; + for (int i = 0; mask != 0 && i < stackArgSize; i++) { + if ((mask & 0x1) != 0 && + *argumentPointer != UIntPtr.Zero) { + // BUGBUG: threadReferenceVisitor.Visit(p); + VisitIfNotContext(thread, threadReferenceVisitor, + argumentPointer); + } + mask >>= 1; + argumentPointer--; + } + } else { + for (int i = 0; mask != 0 && i < stackArgSize; i++) { + mask >>= 1; + } + } + // Process the local variables + if (threadReferenceVisitor != null) { + int transitionRecordSize = + frameDescriptor.hasTransitionRecord ? + sizeof(TransitionRecord) / sizeof (UIntPtr) : + 0; + int registerCount = + CountBits(frameDescriptor.calleeSaveMask); + int inBetweenSlotsBelow = + frameDescriptor.inBetweenSlotsBelow; + UIntPtr *variablePtr = + framePointer - inBetweenSlotsBelow - registerCount + - transitionRecordSize - 1; // -1 because FP is pointing in "above" region. + while (mask != UIntPtr.Zero) { + if ((mask & 0x1) != 0 && + *variablePtr != UIntPtr.Zero) { + // BUGBUG: threadReferenceVisitor.Visit(p); + VisitIfNotContext(thread, threadReferenceVisitor, + variablePtr); + } + mask >>= 1; + variablePtr--; + } + } + break; + } + default: { + VTable.NotReached("FrameDescriptor mask switch failed"); + break; + } + } + if (frameDescriptor.calleeSaveValueMask != 0 && + threadReferenceVisitor != null) { + calleeSaves.ScanLiveRegs(frameDescriptor.calleeSaveValueMask, + threadReferenceVisitor); + } + +#if X86 || AMD64 || ISA_IX86 || ISA_IX64 + UIntPtr nextReturnAddr; + if (frameDescriptor.isFramePointerOmitted) { + nextReturnAddr = *framePointer; + stackPointer = framePointer + 1; + } else { + nextReturnAddr = *(framePointer + 1); + stackPointer = framePointer + 2; + } +#elif ARM || ISA_ARM + // CompressedFrameDescriptor is the target specific portion. + // This will be refactored to FrameDescriptor via partial classes. + UIntPtr nextReturnAddr = CompressedFrameDescriptor.NextCallSite(frameDescriptor.isFramePointerOmitted, framePointer, out stackPointer); +#else +#error Unknown Architecture +#endif + + // In Singularity, the final return address of a thread is zero + if (nextReturnAddr != UIntPtr.Zero) { + calleeSaves.PopFrame(framePointer, + frameDescriptor.calleeSaveMask, + frameDescriptor.isFramePointerOmitted, + frameDescriptor.hasTransitionRecord); + } else { + calleeSaves.ClearFrame(frameDescriptor.calleeSaveMask, + frameDescriptor.isFramePointerOmitted); + } + UIntPtr *calcedfp = (UIntPtr *) calleeSaves.GetFramePointer(); + if (calcedfp != null) { + framePointer = calcedfp; + } + returnAddr = nextReturnAddr; + return false; // false: not done scanning: proceed to next frame + } + + private static int CountBits(UIntPtr bitMask) { + int result = 0; + while (bitMask != UIntPtr.Zero) { + if ((bitMask & (UIntPtr) 0x1) != UIntPtr.Zero) { + result++; + } + bitMask >>= 1; + } + return result; + } + +#if SINGULARITY_PROCESS + // TODO: BUGBUG: Get rid of this when ThreadContext is on stack! + [Inline] + private static + void VisitIfNotContext(Thread thread, + NonNullReferenceVisitor threadReferenceVisitor, + UIntPtr *p) + { + if (*p != (UIntPtr) thread.context && + (int *) *p != &thread.context->gcStates) { + threadReferenceVisitor.Visit(p); + } + } +#else + [Inline] + private static + void VisitIfNotContext(Thread thread, + NonNullReferenceVisitor threadReferenceVisitor, + UIntPtr *p) + { + threadReferenceVisitor.Visit(p); + } +#endif + + [RequiredByBartok] + private static ushort **callSetSiteNumberToIndex; + [RequiredByBartok] + private static CompressedFrameDescriptor **activationDescriptorTable; + + [PreInitRefCounts] + internal static + int ScanStacks(NonNullReferenceVisitor VisitThreadReference, + NonNullReferenceVisitor VisitPinnedReference) + { + int limit = Thread.threadTable.Length; + int countThreads = 0; + for (int i = 0; i < limit; i++) { + Thread t = Thread.threadTable[i]; + if (t != null) { + CollectorStatistics.Event(GCEvent.StackScanStart, i); + ScanStack(t, VisitThreadReference, VisitPinnedReference); + CollectorStatistics.Event(GCEvent.StackScanComplete, i); + countThreads++; + } + } + return countThreads; + } + + [PreInitRefCounts] + internal static + uint ScanStack(Thread thread, + NonNullReferenceVisitor VisitThreadReference, + NonNullReferenceVisitor VisitPinnedReference) + { + Trace.Log(Trace.Area.Stack, + "Scanning stack for thread {0:x}", + __arglist(thread.threadIndex)); + + uint numStackFrames = 0; + threadBeingProcessed = thread; + CalleeSaveLocations calleeSaves = new CalleeSaveLocations(); +#if SINGULARITY_KERNEL + TransitionRecord *marker = (TransitionRecord *) thread.context.stackMarkers; +#elif SINGULARITY_PROCESS + TransitionRecord *marker = null; + if (thread.context != null) { + marker = (TransitionRecord *) thread.context->stackMarkers; + } +#else + TransitionRecord *marker = (TransitionRecord *) + MixinThread(thread).asmStackMarker; +#endif + while (marker != null) { + Trace.Log(Trace.Area.Stack, + "Transition record: old={0:x}, callAddr={1:x}, stackBottom={2:x}", __arglist(marker->oldTransitionRecord, marker->callAddr, marker->stackBottom)); + marker->calleeSaveRegisters.DebugTrace(); + TransitionRecord *stopMarker = marker->oldTransitionRecord; + UIntPtr returnAddr = marker->callAddr; + UIntPtr *fp = (UIntPtr *) + marker->calleeSaveRegisters.GetFramePointer(); + UIntPtr *sp = (UIntPtr *) marker->stackBottom; + calleeSaves.SetCalleeSaves(&marker->calleeSaveRegisters); + numStackFrames += + ScanStackSegment(ref calleeSaves, returnAddr, fp, sp, + stopMarker, VisitThreadReference, + VisitPinnedReference, thread); + marker = marker->oldTransitionRecord; + } + threadBeingProcessed = null; + return numStackFrames; + } + + [PreInitRefCounts] + private static + uint ScanStackSegment(ref CalleeSaveLocations calleeSaves, + UIntPtr returnAddr, + UIntPtr *fp, + UIntPtr *sp, + TransitionRecord *stopMarker, + NonNullReferenceVisitor VisitThreadReference, + NonNullReferenceVisitor VisitPinnedReference, + Thread thread) + // BUGBUG: Remove thread argument when ThreadContext is on stack! + { + uint numStackFrames = 0; + while (true) { +#if SINGULARITY + if (returnAddr >= LinkStackFunctionsBegin && + returnAddr <= LinkStackFunctionsLimit) { + if (returnAddr >= LinkStackBegin && + returnAddr <= LinkStackLimit) { + returnAddr = SkipLinkStackFrame(ref fp, ref sp); + } else if (returnAddr >= UnlinkStackBegin && + returnAddr <= UnlinkStackLimit) { + returnAddr = SkipUnlinkStackFrame(ref fp, ref sp); + } else if (returnAddr >= LinkStackStubsBegin && + returnAddr <= LinkStackStubsLimit) { + returnAddr = SkipLinkStackStubFrame(ref fp, ref sp); + } else { + VTable.NotReached("Unexpected link stack function"); + } + } +#endif + + // Exit loop if we have reached the of the stack segment + if (fp >= stopMarker && sp < stopMarker) { + break; + } + int tableIndex = CallSiteTableNumber(returnAddr); + if (tableIndex < 0) { + break; + } + int callSiteSet = CallSiteSetNumber(returnAddr, tableIndex); + if (callSiteSet < 0) { + break; + } + ushort *callSiteToIndexTable = + callSetSiteNumberToIndex[tableIndex]; + int activationIndex = (int) callSiteToIndexTable[callSiteSet]; + VTable.Assert(activationIndex >= 0); + CompressedFrameDescriptor *descriptorTable = + activationDescriptorTable[tableIndex]; + CompressedFrameDescriptor frameDescriptor = + descriptorTable[activationIndex]; + bool done = WalkStackFrame(ref fp, ref sp, ref calleeSaves, + ref returnAddr, + VisitThreadReference, + VisitPinnedReference, + frameDescriptor, + stopMarker, thread); + if (done) { + break; + } + numStackFrames++; + } + calleeSaves.ClearCalleeSaves(); + return numStackFrames; + } + +#if SINGULARITY + private static UIntPtr SkipLinkStackFrame(ref UIntPtr *framePointer, + ref UIntPtr *stackPointer) + { + // The stack pointer and the frame pointer are in the same + // stack segment. We still need to skip over the frame + // set up for the function that called LinkStack hasn't been + // executed yet (the GC interaction occurred during allocation + // of the new stack segment). + stackPointer = framePointer + 2; + framePointer = (UIntPtr*) *framePointer; + return *(stackPointer - 1); + } + + private static UIntPtr SkipUnlinkStackFrame(ref UIntPtr *framePointer, + ref UIntPtr *stackPointer) + { + // The frame pointer is already set up. The stack pointer + // needs to be adjusted for the two PC values (LinkStackXX and + // UnlinkStack) and the two spilled values pushed onto the stack. + stackPointer = stackPointer + 4; + return *(stackPointer - 1); + } + + private static UIntPtr SkipLinkStackStubFrame(ref UIntPtr *framePointer, + ref UIntPtr *stackPointer) + { + // 'framePointer' points to the ebp in the old frame + // since at the end of WalkStackFrame, it sets return addr + // and pops up a frame. However, since we are using linked + // stacks, "pop up a frame" didn't actually set the frame + // to the caller's frame, it just goes from the new frame to the + // old frame, therefore, we need to get return addr and pop + // a frame again. + stackPointer = framePointer + 2; + framePointer = (UIntPtr*) *framePointer; + return *(stackPointer - 1); + } +#endif + } +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/CalleeSave.cs b/base/Imported/Bartok/runtime/shared/GCs/CalleeSave.cs new file mode 100644 index 0000000..f635bd0 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/CalleeSave.cs @@ -0,0 +1,183 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + + +namespace System.GCs { + + using System.Runtime.CompilerServices; +#if SINGULARITY + using Microsoft.Singularity; +#endif + + // REVIEW: This should probably only be on the mixin types to + // CalleeSaveRegisters, but it seems to be lost if put there. + [AccessedByRuntime("referenced from halforgc.asm")] + internal struct CalleeSaveRegisters + { + + internal void DebugTrace() + { + // Placeholder for an override method + } + + internal UIntPtr GetFramePointer() + { + // Placeholder for an override method + return UIntPtr.Zero; + } + + } + + internal unsafe struct CalleeSaveLocations { + +#if SINGULARITY_KERNEL + internal void SetCalleeSaves(ThreadContext *context) + { + // Placeholder for an override method + } +#endif + + internal void SetCalleeSaves(CalleeSaveRegisters *calleeSaveRegisters) + { + // Placeholder for an override method + } + + internal void ClearCalleeSaves() + { + // Placeholder for an override method + } + + [PreInitRefCounts] + internal void ScanLiveRegs(UIntPtr mask, + NonNullReferenceVisitor referenceVisitor) + { + // Placeholder for an override method + } + + internal void PopFrame(UIntPtr *framePointer, + UIntPtr calleeSaveMask, + bool framePointerOmitted, + bool hasTransitionRecord) + { + // Placeholder for an override method + } + + internal void ClearFrame(UIntPtr calleeSaveMask, + bool framePointerOmitted) + { + // Placeholder for an override method + } + + internal UIntPtr GetFramePointer() + { + // Placeholder for an override method + return UIntPtr.Zero; + } + + internal struct RegLocation + { + + [Inline] + internal void SetCalleeReg(UIntPtr *regField) + { + this.pending = false; + this.value = *regField; + this.head = regField; + *regField = UIntPtr.Zero; + } + + internal void ClearCalleeReg() + { + VTable.Deny(this.pending); + UIntPtr *scan = this.head; + while (scan != null) { + UIntPtr temp = *scan; + *scan = value; + scan = (UIntPtr *) temp; + } + this.head = null; + } + + [PreInitRefCounts] + internal void ScanLiveReg(uint kind, + NonNullReferenceVisitor visitor) + { + switch (kind) { + case 0: { + // Value is not a traceable heap pointer + break; + } + case 1: { + // Value is a pointer variable + VTable.Deny(this.head == null); + if (value != UIntPtr.Zero) { + fixed (UIntPtr *valueField = &this.value) { + visitor.Visit(valueField); + } + } + ClearCalleeReg(); + break; + } + case 2: { + // Value is unchanged since function entry + VTable.Deny(this.pending); + this.pending = true; + break; + } + case 3: + default: { + VTable.NotReached("ScanLiveReg 3 or default"); + break; + } + } + } + + internal void PopFrameReg(ref UIntPtr *calleeSaveStart) + { + if (this.head != null && !this.pending) { + ClearCalleeReg(); + } + if (this.head == null) { + this.value = *calleeSaveStart; + } else { + VTable.Assert(this.pending, "pending should be true"); + VTable.Assert(*calleeSaveStart == this.value, + "values are not equal"); + } + this.pending = false; + *calleeSaveStart = (UIntPtr) this.head; + this.head = calleeSaveStart; + calleeSaveStart--; + } + + internal void ClearFrameReg() { + UIntPtr *scan = this.head; + while (scan != null) { + UIntPtr temp = *scan; + *scan = value; + scan = (UIntPtr *) temp; + } + this.head = null; + this.pending = false; + this.value = UIntPtr.Zero; + } + + internal bool pending; + internal UIntPtr value; + internal UIntPtr *head; + + } + + CalleeSaveLocations.RegLocation FramePointer; + + } + +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/CalleeSaveARM.cs b/base/Imported/Bartok/runtime/shared/GCs/CalleeSaveARM.cs new file mode 100644 index 0000000..9df486c --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/CalleeSaveARM.cs @@ -0,0 +1,220 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + + +namespace System.GCs { + + using Microsoft.Bartok.Options; + + using System.Runtime.CompilerServices; + +#if SINGULARITY + using Microsoft.Singularity; + using Microsoft.Singularity.Isal; + using Microsoft.Singularity.Isal.Arm; +#endif + + [MixinConditional("ARM")] + [MixinConditional("ISA_ARM")] + [Mixin(typeof(System.GCs.CalleeSaveRegisters))] + [AccessedByRuntime("Referenced from halforgc.asm")] + internal struct CalleeSaveRegistersARM /* : CalleeSaveRegisters */ + { + + [MixinOverride] + internal void DebugTrace() { + Trace.Log(Trace.Area.Stack, + "Registers: R4={0:x}, R5={0:x}, R6={0:x}, R7={0:x},\n R8={0:x}, R9={0:x}, R10={0:x}, R11={0:x}", + __arglist(this.r4, this.r5, this.r6, this.r7, + this.r8, this.r9, this.r10, this.r11)); + } + + [MixinOverride] + internal UIntPtr GetFramePointer() + { + return this.r11; + } + + [AccessedByRuntime("Referenced from halforgc.asm")] + internal UIntPtr r4; + [AccessedByRuntime("Referenced from halforgc.asm")] + internal UIntPtr r5; + [AccessedByRuntime("Referenced from halforgc.asm")] + internal UIntPtr r6; + [AccessedByRuntime("Referenced from halforgc.asm")] + internal UIntPtr r7; + [AccessedByRuntime("Referenced from halforgc.asm")] + internal UIntPtr r8; + [AccessedByRuntime("Referenced from halforgc.asm")] + internal UIntPtr r9; + [AccessedByRuntime("Referenced from halforgc.asm")] + internal UIntPtr r10; + [AccessedByRuntime("Referenced from halforgc.asm")] + internal UIntPtr r11; + + } + + [MixinConditional("ARM")] + [MixinConditional("ISA_ARM")] + [Mixin(typeof(System.GCs.CalleeSaveLocations))] + internal unsafe struct CalleeSaveLocationsARM /* : CalleeSaveLocations */ + { + +#if SINGULARITY_KERNEL +#if false + [MixinOverride] + internal void SetCalleeSaves(SpillContext *context) { + r4.SetCalleeReg(&context->r4); + r5.SetCalleeReg(&context->r5); + r6.SetCalleeReg(&context->r6); + r7.SetCalleeReg(&context->r7); + r8.SetCalleeReg(&context->r8); + r9.SetCalleeReg(&context->r9); + r10.SetCalleeReg(&context->r10); + r11.SetCalleeReg(&context->r11); + } +#endif +#endif + + [MixinOverride] + internal + void SetCalleeSaves(CalleeSaveRegistersARM *calleeSaveRegisters) + { + r4.SetCalleeReg(&calleeSaveRegisters->r4); + r5.SetCalleeReg(&calleeSaveRegisters->r5); + r6.SetCalleeReg(&calleeSaveRegisters->r6); + r7.SetCalleeReg(&calleeSaveRegisters->r7); + r8.SetCalleeReg(&calleeSaveRegisters->r8); + r9.SetCalleeReg(&calleeSaveRegisters->r9); + r10.SetCalleeReg(&calleeSaveRegisters->r10); + r11.SetCalleeReg(&calleeSaveRegisters->r11); + } + + [MixinOverride] + internal void ClearCalleeSaves() { + r4.ClearCalleeReg(); + r5.ClearCalleeReg(); + r6.ClearCalleeReg(); + r7.ClearCalleeReg(); + r8.ClearCalleeReg(); + r9.ClearCalleeReg(); + r10.ClearCalleeReg(); + r11.ClearCalleeReg(); + } + + [MixinOverride] + internal void ScanLiveRegs(UIntPtr mask, + NonNullReferenceVisitor referenceVisitor) + { + r4.ScanLiveReg((mask >> 0) & 0x3, referenceVisitor); + r5.ScanLiveReg((mask >> 2) & 0x3, referenceVisitor); + r6.ScanLiveReg((mask >> 4) & 0x3, referenceVisitor); + r7.ScanLiveReg((mask >> 6) & 0x3, referenceVisitor); + r8.ScanLiveReg((mask >> 8) & 0x3, referenceVisitor); + r9.ScanLiveReg((mask >> 10) & 0x3, referenceVisitor); + r10.ScanLiveReg((mask >> 12) & 0x3, referenceVisitor); + r11.ScanLiveReg((mask >> 14) & 0x3, referenceVisitor); + } + + [MixinOverride] + internal void PopFrame(UIntPtr *framePointer, + UIntPtr calleeSaveMask, + bool framePointerOmitted, + bool hasTransitionRecord) + { + UIntPtr *calleeSaveStart; + calleeSaveStart = framePointer - 3; // Step over SP & LR + + if (hasTransitionRecord) { + calleeSaveStart -= + sizeof(CallStack.TransitionRecord) / sizeof(UIntPtr); + } + + // Note: the order in which these appear is important! + if (!framePointerOmitted || (calleeSaveMask & 0x80) != 0) { + r11.PopFrameReg(ref calleeSaveStart); + } + if ((calleeSaveMask & 0x40) != 0) { + r10.PopFrameReg(ref calleeSaveStart); + } + if ((calleeSaveMask & 0x20) != 0) { + r9.PopFrameReg(ref calleeSaveStart); + } + if ((calleeSaveMask & 0x10) != 0) { + r8.PopFrameReg(ref calleeSaveStart); + } + if ((calleeSaveMask & 0x8) != 0) { + r7.PopFrameReg(ref calleeSaveStart); + } + if ((calleeSaveMask & 0x4) != 0) { + r6.PopFrameReg(ref calleeSaveStart); + } + if ((calleeSaveMask & 0x2) != 0) { + r5.PopFrameReg(ref calleeSaveStart); + } + if ((calleeSaveMask & 0x1) != 0) { + r4.PopFrameReg(ref calleeSaveStart); + } + } + + [MixinOverride] + internal void ClearFrame(UIntPtr calleeSaveMask, + bool framePointerOmitted) + { + if (!framePointerOmitted) { + VTable.Assert((calleeSaveMask & 0x100) == 0, + "EBP should not be callee saved"); + r11.ClearFrameReg(); + } + if ((calleeSaveMask & 0x1) != 0) { + r4.ClearFrameReg(); + } + if ((calleeSaveMask & 0x2) != 0) { + r5.ClearFrameReg(); + } + if ((calleeSaveMask & 0x4) != 0) { + r6.ClearFrameReg(); + } + if ((calleeSaveMask & 0x8) != 0) { + r7.ClearFrameReg(); + } + if ((calleeSaveMask & 0x10) != 0) { + r8.ClearFrameReg(); + } + if ((calleeSaveMask & 0x20) != 0) { + r9.ClearFrameReg(); + } + if ((calleeSaveMask & 0x40) != 0) { + r10.ClearFrameReg(); + } + if ((calleeSaveMask & 0x80) != 0) { + r11.ClearFrameReg(); + } + } + + [MixinOverride] + internal UIntPtr GetFramePointer() + { + return r11.value; + } + + CalleeSaveLocations.RegLocation r4; + CalleeSaveLocations.RegLocation r5; + CalleeSaveLocations.RegLocation r6; + CalleeSaveLocations.RegLocation r7; + CalleeSaveLocations.RegLocation r8; + CalleeSaveLocations.RegLocation r9; + CalleeSaveLocations.RegLocation r10; + CalleeSaveLocations.RegLocation r11; + + } + +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/CalleeSaveX64.cs b/base/Imported/Bartok/runtime/shared/GCs/CalleeSaveX64.cs new file mode 100644 index 0000000..1aa2723 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/CalleeSaveX64.cs @@ -0,0 +1,225 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + + +namespace System.GCs { + + using Microsoft.Bartok.Options; + + using System.Runtime.CompilerServices; + +#if SINGULARITY + using Microsoft.Singularity; + using Microsoft.Singularity.Isal; +#endif + + + [MixinConditional("X64")] + [MixinConditional("ISA_IX64")] + [Mixin(typeof(System.GCs.CalleeSaveRegisters))] + [AccessedByRuntime("referenced from halforgc.asm")] + internal struct CalleeSaveRegistersX64 /* : CalleeSaveRegisters */ + { + + [MixinOverride] + internal void DebugTrace() { + Trace.Log(Trace.Area.Stack, + "Registers: EBX={0:x}, EDI={1:x}, ESI={2:x}, EBP={3:x}"+ + "\nR12={4:x}, R13={5:x}, R14={6:x}, R15={7:x}", + __arglist(this.EBX, this.EDI, this.ESI, this.EBP, + this.R12, this.R13, this.R14, this.R15)); + } + + [MixinOverride] + internal UIntPtr GetFramePointer() + { + return this.EBP; + } + + [AccessedByRuntime("referenced from halforgc.asm")] + internal UIntPtr EBX; + [AccessedByRuntime("referenced from halforgc.asm")] + internal UIntPtr EDI; + [AccessedByRuntime("referenced from halforgc.asm")] + internal UIntPtr ESI; + [AccessedByRuntime("referenced from halforgc.asm")] + internal UIntPtr EBP; + [AccessedByRuntime("referenced from halforgc.asm")] + internal UIntPtr R12; + [AccessedByRuntime("referenced from halforgc.asm")] + internal UIntPtr R13; + [AccessedByRuntime("referenced from halforgc.asm")] + internal UIntPtr R14; + [AccessedByRuntime("referenced from halforgc.asm")] + internal UIntPtr R15; + } + + [MixinConditional("X64")] + [MixinConditional("ISA_IX64")] + [Mixin(typeof(System.GCs.CalleeSaveLocations))] + internal unsafe struct CalleeSaveLocationsX64 /* : CalleeSaveLocations */ + { + +#if SINGULARITY_KERNEL + [MixinOverride] + internal void SetCalleeSaves(SpillContext *context) { + EBX.SetCalleeReg(&context->bx); + EDI.SetCalleeReg(&context->di); + ESI.SetCalleeReg(&context->si); + EBP.SetCalleeReg(&context->bp); + R12.SetCalleeReg(&context->r12); + R13.SetCalleeReg(&context->r13); + R14.SetCalleeReg(&context->r14); + R15.SetCalleeReg(&context->r15); + } +#endif + + [MixinOverride] + internal + void SetCalleeSaves(CalleeSaveRegistersX64 *calleeSaveRegisters) + { + EBX.SetCalleeReg(&calleeSaveRegisters->EBX); + EDI.SetCalleeReg(&calleeSaveRegisters->EDI); + ESI.SetCalleeReg(&calleeSaveRegisters->ESI); + EBP.SetCalleeReg(&calleeSaveRegisters->EBP); + R12.SetCalleeReg(&calleeSaveRegisters->R12); + R13.SetCalleeReg(&calleeSaveRegisters->R13); + R14.SetCalleeReg(&calleeSaveRegisters->R14); + R15.SetCalleeReg(&calleeSaveRegisters->R15); + } + + [MixinOverride] + internal void ClearCalleeSaves() { + EBX.ClearCalleeReg(); + ESI.ClearCalleeReg(); + EDI.ClearCalleeReg(); + EBP.ClearCalleeReg(); + R12.ClearCalleeReg(); + R13.ClearCalleeReg(); + R14.ClearCalleeReg(); + R15.ClearCalleeReg(); + } + + [MixinOverride] + internal void ScanLiveRegs(UIntPtr mask, + NonNullReferenceVisitor referenceVisitor) + { + EDI.ScanLiveReg((mask >> 2) & 0x3, referenceVisitor); + ESI.ScanLiveReg((mask >> 4) & 0x3, referenceVisitor); + EBX.ScanLiveReg((mask >> 0) & 0x3, referenceVisitor); + R12.ScanLiveReg((mask >> 6) & 0x3, referenceVisitor); + R13.ScanLiveReg((mask >> 8) & 0x3, referenceVisitor); + R14.ScanLiveReg((mask >> 10) & 0x3, referenceVisitor); + R15.ScanLiveReg((mask >> 12) & 0x3, referenceVisitor); + EBP.ScanLiveReg((mask >> 14) & 0x3, referenceVisitor); + } + + [MixinOverride] + internal void PopFrame(UIntPtr *framePointer, + UIntPtr calleeSaveMask, + bool framePointerOmitted, + bool hasTransitionRecord) + { + UIntPtr *calleeSaveStart; + if (framePointerOmitted) { + calleeSaveStart = framePointer - 1; + } else { + VTable.Assert((calleeSaveMask & 0x100) == 0, + "EBP should not be callee saved"); + calleeSaveStart = framePointer; + EBP.PopFrameReg(ref calleeSaveStart); + } + if (hasTransitionRecord) { + calleeSaveStart -= + sizeof(CallStack.TransitionRecord) / sizeof(UIntPtr); + } + + // Note: the order in which these appear is important! + if ((calleeSaveMask & 0x1) != 0) { + EBX.PopFrameReg(ref calleeSaveStart); + } + if ((calleeSaveMask & 0x80) != 0) { + EBP.PopFrameReg(ref calleeSaveStart); + } + if ((calleeSaveMask & 0x4) != 0) { + ESI.PopFrameReg(ref calleeSaveStart); + } + if ((calleeSaveMask & 0x2) != 0) { + EDI.PopFrameReg(ref calleeSaveStart); + } + if ((calleeSaveMask & 0x8) != 0) { + R12.PopFrameReg(ref calleeSaveStart); + } + if ((calleeSaveMask & 0x10) != 0) { + R13.PopFrameReg(ref calleeSaveStart); + } + if ((calleeSaveMask & 0x20) != 0) { + R14.PopFrameReg(ref calleeSaveStart); + } + if ((calleeSaveMask & 0x40) != 0) { + R15.PopFrameReg(ref calleeSaveStart); + } + } + + [MixinOverride] + internal void ClearFrame(UIntPtr calleeSaveMask, + bool framePointerOmitted) + { + if (!framePointerOmitted) { + VTable.Assert((calleeSaveMask & 0x100) == 0, + "EBP should not be callee saved"); + EBP.ClearFrameReg(); + } + if ((calleeSaveMask & 0x1) != 0) { + EBX.ClearFrameReg(); + } + if ((calleeSaveMask & 0x80) != 0) { + EBP.ClearFrameReg(); + } + if ((calleeSaveMask & 0x4) != 0) { + ESI.ClearFrameReg(); + } + if ((calleeSaveMask & 0x2) != 0) { + EDI.ClearFrameReg(); + } + if ((calleeSaveMask & 0x8) != 0) { + R12.ClearFrameReg(); + } + if ((calleeSaveMask & 0x10) != 0) { + R13.ClearFrameReg(); + } + if ((calleeSaveMask & 0x20) != 0) { + R14.ClearFrameReg(); + } + if ((calleeSaveMask & 0x40) != 0) { + R15.ClearFrameReg(); + } + + } + + [MixinOverride] + internal UIntPtr GetFramePointer() + { + return EBP.value; + } + + CalleeSaveLocations.RegLocation EBX; + CalleeSaveLocations.RegLocation EDI; + CalleeSaveLocations.RegLocation ESI; + CalleeSaveLocations.RegLocation EBP; + CalleeSaveLocations.RegLocation R12; + CalleeSaveLocations.RegLocation R13; + CalleeSaveLocations.RegLocation R14; + CalleeSaveLocations.RegLocation R15; + + } + +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/CalleeSaveX86.cs b/base/Imported/Bartok/runtime/shared/GCs/CalleeSaveX86.cs new file mode 100644 index 0000000..7a4743e --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/CalleeSaveX86.cs @@ -0,0 +1,169 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + + +namespace System.GCs { + + using Microsoft.Bartok.Options; + + using System.Runtime.CompilerServices; + +#if SINGULARITY + using Microsoft.Singularity; +#endif + + [MixinConditional("X86")] + [MixinConditional("ISA_IX86")] + [Mixin(typeof(System.GCs.CalleeSaveRegisters))] + [AccessedByRuntime("referenced from halforgc.asm")] + internal struct CalleeSaveRegistersX86 /* : CalleeSaveRegisters */ + { + + [MixinOverride] + internal void DebugTrace() { + Trace.Log(Trace.Area.Stack, + "Registers: EBX={0:x}, EDI={1:x}, ESI={2:x}, EBP={3:x}", + __arglist(this.EBX, this.EDI, this.ESI, this.EBP)); + } + + [MixinOverride] + internal UIntPtr GetFramePointer() + { + return this.EBP; + } + + [AccessedByRuntime("referenced from halforgc.asm")] + internal UIntPtr EBX; + [AccessedByRuntime("referenced from halforgc.asm")] + internal UIntPtr EDI; + [AccessedByRuntime("referenced from halforgc.asm")] + internal UIntPtr ESI; + [AccessedByRuntime("referenced from halforgc.asm")] + internal UIntPtr EBP; + } + + [MixinConditional("X86")] + [MixinConditional("ISA_IX86")] + [Mixin(typeof(System.GCs.CalleeSaveLocations))] + internal unsafe struct CalleeSaveLocationsX86 /* : CalleeSaveLocations */ + { + +#if SINGULARITY_KERNEL +#if false + [MixinOverride] + internal void SetCalleeSaves(SpillContext *context) { + EBX.SetCalleeReg(&context->ebx); + EDI.SetCalleeReg(&context->edi); + ESI.SetCalleeReg(&context->esi); + EBP.SetCalleeReg(&context->ebp); + } +#endif +#endif + + [MixinOverride] + internal + void SetCalleeSaves(CalleeSaveRegistersX86 *calleeSaveRegisters) + { + EBX.SetCalleeReg(&calleeSaveRegisters->EBX); + EDI.SetCalleeReg(&calleeSaveRegisters->EDI); + ESI.SetCalleeReg(&calleeSaveRegisters->ESI); + EBP.SetCalleeReg(&calleeSaveRegisters->EBP); + } + + [MixinOverride] + internal void ClearCalleeSaves() { + EBX.ClearCalleeReg(); + ESI.ClearCalleeReg(); + EDI.ClearCalleeReg(); + EBP.ClearCalleeReg(); + } + + [MixinOverride] + internal void ScanLiveRegs(UIntPtr mask, + NonNullReferenceVisitor referenceVisitor) + { + EDI.ScanLiveReg((mask >> 2) & 0x3, referenceVisitor); + ESI.ScanLiveReg((mask >> 4) & 0x3, referenceVisitor); + EBX.ScanLiveReg((mask >> 0) & 0x3, referenceVisitor); + EBP.ScanLiveReg((mask >> 6) & 0x3, referenceVisitor); + } + + [MixinOverride] + internal void PopFrame(UIntPtr *framePointer, + UIntPtr calleeSaveMask, + bool framePointerOmitted, + bool hasTransitionRecord) + { + UIntPtr *calleeSaveStart; + if (framePointerOmitted) { + calleeSaveStart = framePointer - 1; + } else { + VTable.Assert((calleeSaveMask & 0x10) == 0, + "EBP should not be callee saved"); + calleeSaveStart = framePointer; + EBP.PopFrameReg(ref calleeSaveStart); + } + if (hasTransitionRecord) { + calleeSaveStart -= + sizeof(CallStack.TransitionRecord) / sizeof(UIntPtr); + } + // Note: the order in which these appear is important! + if ((calleeSaveMask & 0x1) != 0) { + EBX.PopFrameReg(ref calleeSaveStart); + } + if ((calleeSaveMask & 0x8) != 0) { + EBP.PopFrameReg(ref calleeSaveStart); + } + if ((calleeSaveMask & 0x4) != 0) { + ESI.PopFrameReg(ref calleeSaveStart); + } + if ((calleeSaveMask & 0x2) != 0) { + EDI.PopFrameReg(ref calleeSaveStart); + } + } + + [MixinOverride] + internal void ClearFrame(UIntPtr calleeSaveMask, + bool framePointerOmitted) + { + if (!framePointerOmitted) { + VTable.Assert((calleeSaveMask & 0x10) == 0, + "EBP should not be callee saved"); + EBP.ClearFrameReg(); + } + if ((calleeSaveMask & 0x1) != 0) { + EBX.ClearFrameReg(); + } + if ((calleeSaveMask & 0x8) != 0) { + EBP.ClearFrameReg(); + } + if ((calleeSaveMask & 0x4) != 0) { + ESI.ClearFrameReg(); + } + if ((calleeSaveMask & 0x2) != 0) { + EDI.ClearFrameReg(); + } + } + + [MixinOverride] + internal UIntPtr GetFramePointer() + { + return EBP.value; + } + + CalleeSaveLocations.RegLocation EBX; + CalleeSaveLocations.RegLocation EDI; + CalleeSaveLocations.RegLocation ESI; + CalleeSaveLocations.RegLocation EBP; + + } + +} diff --git a/base/Kernel/Bartok/GCs/CardTable.cs b/base/Imported/Bartok/runtime/shared/GCs/CardTable.cs similarity index 96% rename from base/Kernel/Bartok/GCs/CardTable.cs rename to base/Imported/Bartok/runtime/shared/GCs/CardTable.cs index 0d2f904..e2dd144 100644 --- a/base/Kernel/Bartok/GCs/CardTable.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/CardTable.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,9 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// //#define DEBUG_CARDS diff --git a/base/Kernel/Bartok/GCs/CentralPT.cs b/base/Imported/Bartok/runtime/shared/GCs/CentralPT.cs similarity index 92% rename from base/Kernel/Bartok/GCs/CentralPT.cs rename to base/Imported/Bartok/runtime/shared/GCs/CentralPT.cs index b441701..ed7ab6b 100644 --- a/base/Kernel/Bartok/GCs/CentralPT.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/CentralPT.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,19 +9,14 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// namespace System.GCs { using System.Runtime.CompilerServices; using System.Threading; - [RequiredByBartok] internal class CentralPT: PageTable { - [PreInitRefCounts] [NoStackLinkCheck] new internal static unsafe void Initialize() { @@ -32,7 +31,6 @@ namespace System.GCs { SetProcess(UIntPtr.Zero, pageTableCount); } - [Inline] internal static unsafe uint PageTableEntryImpl(UIntPtr page) { @@ -40,7 +38,8 @@ namespace System.GCs { } [Inline] - internal static unsafe void SetPageTableEntryImpl(UIntPtr page, uint value) + internal static unsafe void SetPageTableEntryImpl(UIntPtr page, + uint value) { *(halPageDescriptor + page) = value; } diff --git a/base/Imported/Bartok/runtime/shared/GCs/CoCoBarrier.cs b/base/Imported/Bartok/runtime/shared/GCs/CoCoBarrier.cs new file mode 100644 index 0000000..75f0322 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/CoCoBarrier.cs @@ -0,0 +1,960 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using Microsoft.Bartok.Runtime; + using Microsoft.Bartok.Options; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using System.Threading; + + using MarkingPhase = CMSMarking.MarkingPhase; + + internal unsafe abstract class CoCoBarrier: Barrier { + + // hook-ins to the system. maybe this could be redone so that + // it is more orthogonal. + + internal bool BigEndian { + get { return false; /* BUGBUG */ } + } + + [NoBarriers] + internal static new void Initialize() + { + // preWriteFieldsVisitor = new PreWriteFieldsVisitor(); + LowAbortCoCoBarrier.preWriteFieldsVisitor = (PreWriteFieldsVisitor) + BootstrapMemory.Allocate(typeof(PreWriteFieldsVisitor)); + } + + internal static UIntPtr GCFieldOffset; + internal static UIntPtr vtableFieldOffset; + + private static MarkingPhase CurrentMarkingPhase { + [NoStackLinkCheck] + get { return CMSMarking.CurrentMarkingPhase; } + } + + // wrappers - used for profiling + internal static UIntPtr CAS(UIntPtr *loc, + UIntPtr newValue, + UIntPtr comparand) + { + UIntPtr result= + Interlocked.CompareExchange(loc, newValue, comparand); + if (fProfileCAS && result != comparand) { + numCASFailed++; + } + return result; + } + + [NoBarriers] + internal static UIntPtr CAS(ref UIntPtr loc, + UIntPtr newValue, + UIntPtr comparand) + { + UIntPtr result= + Interlocked.CompareExchange(ref loc, newValue, comparand); + if (fProfileCAS && result != comparand) { + numCASFailed++; + } + return result; + } + + [NoBarriers] + internal static int CAS(ref int loc, + int newValue, + int comparand) + { + int result= + Interlocked.CompareExchange(ref loc, newValue, comparand); + if (fProfileCAS && result != comparand) { + numCASFailed++; + } + return result; + } + + internal static int CAS(int *loc, + int newValue, + int comparand) + { + int result= + Interlocked.CompareExchange(loc, newValue, comparand); + if (fProfileCAS && result != comparand) { + numCASFailed++; + } + return result; + } + + internal static long CAS(long *loc, + long newValue, + long comparand) + { + long result=Interlocked.CompareExchange(loc, newValue, comparand); + if (fProfileCAS && result != comparand) { + numCASFailed++; + } + return result; + } + + [NoBarriers] + internal static Object CAS(ref Object loc, + Object newValue, + Object comparand) + { + Object result = + Interlocked.CompareExchange(ref loc, newValue, comparand); + if (fProfileCAS && result != comparand) { + numCASFailed++; + } + return result; + } + + // hacks + + [NoInline] + internal static float IntToFloatBits(uint x) { + return *(float*)Magic.toPointer(ref x); + } + + [NoInline] + internal static double LongToDoubleBits(ulong x) { + return *(double*)Magic.toPointer(ref x); + } + + [NoInline] + internal static uint FloatToIntBits(float x) { + return *(uint*)Magic.toPointer(ref x); + } + + [NoInline] + internal static ulong DoubleToLongBits(double x) { + return *(ulong*)Magic.toPointer(ref x); + } + + internal enum Phase { + Idle = 0, + Prep = 1, + Copy = 2, + Fixup = 3 + } + + internal static Phase phase; + internal static bool forwarding; + internal static bool pinning; + + // these force thingies are for profiling + internal static bool forceSlow; + internal static bool forceNotIdle; + internal static bool forceForwarding; + internal static bool forcePinning; + + internal static bool isNotIdle; + + // The value of this variable is + // !((phase == Phase.Idle && !forwarding && !pinning) && !forceSlow) + // The purposes of this variable is to take advantage of the default + // initialization of boolean variables to false. In this case, this + // allows us to use the fast-path version upon startup. + private static bool dontAllowFastPath; + + internal static bool allowFastPath { + get { return !dontAllowFastPath; } + set { dontAllowFastPath = !value; } + } + + [MixinConditional("CoCo")] + [MixinConditional("AllThreadMixins")] + [Mixin(typeof(Thread))] + public sealed class CoCoThread : Object { + internal bool readyForCoCo; + internal int acknowledgedPhase; + internal bool acknowledgedForwarding; + internal bool acknowledgedPinning; + internal bool pinnedOut; + internal ulong phaseVersion; // useful for detecting transitions + } + + internal static CoCoThread MixinThread(Thread t) { + return (CoCoThread) (Object) t; + } + + internal static ulong CurThreadPhaseVersion + { + get { + if (Thread.CanGetCurThread) { + return MixinThread(Thread.CurrentThread).phaseVersion; + } else { + return 0; + } + } + } + + internal static bool inited; + internal static Object interlock; + + // NOTE: this code uses ForceInline instead of Inline to indicate that + // inlining should occur even if the caller is huge. In general, this + // attribute should be used with great care. DO NOT USE IT ELSEWHERE IN + // THE RUNTIME UNLESS YOU ARE WILLING TO DOCUMENT YOUR USE IN + // IrSimpleInliner.cs AND Attributes.cs! AND NEVER USE IT IN + // APPLICATION OR OS CODE! + + internal static bool NeedsTargetBarrier + { + [ForceInline] + get { + return CurrentMarkingPhase >= MarkingPhase.ComputingRoots; + } + } + + internal static bool NeedsSourceBarrier + { + [ForceInline] + get { + return CurrentMarkingPhase == MarkingPhase.ComputingRoots; + } + } + + internal static bool NeedsTargetOrSourceBarrier + { + [ForceInline] + get { + return CurrentMarkingPhase >= MarkingPhase.ComputingRoots; + } + } + + [AssertDevirtualize] + internal abstract bool IsInToSpace(UIntPtr ptr); + + internal static void Mark(UIntPtr ptr) + { + if (CMSMarking.MarkIfNecessary(ptr) && + fVerifyToSpaceMark && + ptr!=UIntPtr.Zero && + !instance.IsInToSpace(ptr)) { + VTable.DebugBreak(); + } + } + + internal static void TargetBarrierWithForward(UIntPtr value) + { + if (NeedsTargetBarrier) { + Mark(ToSpaceAsPtr(value)); + } + } + + internal static void TargetBarrierNoForward(UIntPtr value) + { + if (NeedsTargetBarrier) { + Mark(value); + } + } + + // source barrier needs forwarding during the fixup phase, where + // the stacks will have from-space references but we want the collector + // to mark to-space references. + internal static void SourceBarrierWithForward(UIntPtr value) + { + if (NeedsSourceBarrier) { + Mark(ToSpaceAsPtr(value)); + } + } + + internal static void SourceBarrierNoForward(UIntPtr value) + { + if (NeedsSourceBarrier) { + Mark(value); + } + } + + [NoInline] + [CalledRarely] + internal static void + TargetAndSourceBarrierNoForwardSlow(UIntPtr *target, + UIntPtr source) + { + Mark(*target); + if (NeedsSourceBarrier) { + Mark(source); + } + } + + [ForceInline] + internal static void TargetAndSourceBarrierNoForward(UIntPtr *target, + UIntPtr source) + { + if (NeedsTargetOrSourceBarrier) { + TargetAndSourceBarrierNoForwardSlow(target, source); + } + } + + [CalledRarely] + internal static void + TargetWithForwardAndSourceNoForwardBarrierSlow(UIntPtr *target, + UIntPtr source) + { + Mark(ToSpaceAsPtr(*target)); + if (NeedsSourceBarrier) { + Mark(source); + } + } + + [ForceInline] + internal static void + TargetWithForwardAndSourceNoForwardBarrier(UIntPtr *target, + UIntPtr source) + { + if (NeedsTargetOrSourceBarrier) { + TargetWithForwardAndSourceNoForwardBarrierSlow(target, source); + } + } + + [CalledRarely] + [NoBarriers] + internal static void + TargetWithForwardAndSourceNoForwardBarrierSlow(ref Object target, + Object source) + { + Mark(ToSpaceAsPtr(target)); + if (NeedsSourceBarrier) { + Mark(Magic.addressOf(source)); + } + } + + [ForceInline] + internal static void + TargetWithForwardAndSourceNoForwardBarrier(ref Object target, + Object source) + { + if (NeedsTargetOrSourceBarrier) { + TargetWithForwardAndSourceNoForwardBarrierSlow(ref target, + source); + } + } + + internal static UIntPtr CoCoWordOffset; + + internal static bool IgnoreOffset(UIntPtr offset) + { + return !inited + || offset == GCFieldOffset + || offset == vtableFieldOffset + || offset == CoCoWordOffset; + } + + // for this object, does this offset represent an internally used, directly + // accessed (without barriers) immutable entity? + internal static bool InternalImmutableOffset(Object o, + UIntPtr offset) + { + if (o is Array) { + return (offset - vtableFieldOffset) < + (UIntPtr)(o.vtable.baseLength - PreHeader.Size); + } else { + return offset == vtableFieldOffset; + } + } + + internal abstract void InitLateStub(); + + // call when the heap is inited + internal static void InitLate() + { + if (fDebug) { + VTable.DebugPrint("CoCo: in InitLate\n"); + } + interlock=new Object(); + MultiUseWord.GetMonitor(interlock); + MixinThread(Thread.CurrentThread).readyForCoCo=true; + + // REVIEW: this is just offensive + GCFieldOffset = + (UIntPtr)Magic.toPointer(ref ThreadHeaderQueue.MixinObject(instance).preHeader.link) + - Magic.addressOf(instance); + vtableFieldOffset = + (UIntPtr)instance.VTableFieldAddr + - Magic.addressOf(instance); + instance.InitLateStub(); + + inited=true; + } + + internal static void ForceSlow() + { + VTable.DebugPrint("using FORCE SLOW\n"); + allowFastPath = false; + forceSlow = true; + } + + internal static void ForceNotIdle() + { + VTable.DebugPrint("using FORCE NOT IDLE\n"); + isNotIdle = true; + forceNotIdle = true; + } + + internal static void ForceForwarding() + { + VTable.DebugPrint("using FORCE FORWARDING\n"); + forwarding = true; + forceForwarding = true; + } + + internal static void ForcePinning() + { + VTable.DebugPrint("using FORCE PINNING\n"); + pinning = true; + forcePinning = true; + } + + internal static Thread debugThread; + + internal static bool DebugThread + { + get { + if (Thread.CanGetCurThread) { + if (debugThread==null) { + debugThread=Thread.CurrentThread; + } + return debugThread==Thread.CurrentThread; + } else { + return true; + } + } + } + + private static UIntPtr interlockAddr, forwardedInterlockAddr; + + internal static void ClientHandshake() { + if (inited) { + if (fVerbose) { + VTable.DebugPrint(" !! ClientHandshake: interlock at "); + VTable.DebugPrint((ulong)Magic.addressOf(interlock)); + VTable.DebugPrint("\n"); + } + if (fVerbose) { + if (Magic.addressOf(interlock)!=interlockAddr) { + VTable.DebugPrint(" !! ClientHandshake seeing interlock at new address: "); + VTable.DebugPrint((ulong)Magic.addressOf(interlock)); + VTable.DebugPrint("\n"); + } + if (ToSpaceAsPtr(interlock)!=forwardedInterlockAddr) { + VTable.DebugPrint(" !! ClientHandshake seeing interlock at new FORWARDED address: "); + VTable.DebugPrint((ulong)ToSpaceAsPtr(interlock)); + VTable.DebugPrint("\n"); + } + } + lock (interlock) { + if (fVerbose) { + interlockAddr=Magic.addressOf(interlock); + forwardedInterlockAddr=ToSpaceAsPtr(interlock); + } + CoCoThread t=MixinThread(Thread.CurrentThread); + if (phase!=(Phase)t.acknowledgedPhase || + forwarding!=t.acknowledgedForwarding || + pinning!=t.acknowledgedPinning) { + if (fDebug) { + VTable.DebugPrint(" !! thread "); + VTable.DebugPrint((ulong)Magic.addressOf(t)); + VTable.DebugPrint(" doing ack\n"); + } + t.acknowledgedPhase=(int)phase; + t.acknowledgedForwarding=forwarding; + t.acknowledgedPinning=pinning; + t.phaseVersion++; + Monitor.PulseAll(interlock); + } + } + } + } + + internal static bool ExchangeReadyForCoCo(bool value) + { + CoCoThread t=MixinThread(Thread.CurrentThread); + bool result=t.readyForCoCo; + t.readyForCoCo=value; + return result; + } + + internal static void ThreadStart(Thread t_) + { + if (inited) { + lock (interlock) { + CoCoThread t = MixinThread(t_); + t.readyForCoCo=true; + t.acknowledgedPhase=(int)phase; + t.acknowledgedForwarding=forwarding; + t.acknowledgedPinning=pinning; + } + } + } + + // must have some manner of handshake after this + internal static void EnablePinning() + { + if (fDebug) { + VTable.DebugPrint(" --> CoCo enabling pinning "); + } + pinning = true; + SetAllowFastPath(); + } + + internal static void SetAllowFastPath() + { + allowFastPath = + (phase == Phase.Idle && !forwarding && !pinning) + && !forceSlow; + } + + internal static void ChangePhase(Phase phase_, + bool forwarding_, + bool pinning_) + { + if (fDebug) { + VTable.DebugPrint(" --> CoCo going to "); + switch (phase_) { + case Phase.Idle: VTable.DebugPrint("Idle"); break; + case Phase.Prep: VTable.DebugPrint("Prep"); break; + case Phase.Copy: VTable.DebugPrint("Copy"); break; + case Phase.Fixup: VTable.DebugPrint("Fixup"); break; + default: VTable.NotReached(); break; + } + VTable.DebugPrint(" (with"); + if (!forwarding_) { + VTable.DebugPrint("out"); + } + VTable.DebugPrint(" forwarding, with"); + if (!pinning_) { + VTable.DebugPrint("out"); + } + VTable.DebugPrint(" pinning)\n"); + } + lock (interlock) { + phase = phase_; + forwarding = forwarding_ || forceForwarding; + pinning = pinning_ || forcePinning; + SetAllowFastPath(); + isNotIdle = phase != Phase.Idle || forceNotIdle; + CoCoThread t = MixinThread(Thread.CurrentThread); + t.acknowledgedPhase=(int)phase; + t.acknowledgedForwarding=forwarding; + t.acknowledgedPinning=pinning; + t.phaseVersion++; + Monitor.PulseAll(interlock); + for (;;) { + bool needToWait=false; + bool doPulseAll=false; + for (int i = 0; i < Thread.threadTable.Length; ++i) { + Thread t_=Thread.threadTable[i]; + if (t_==null) { + continue; + } + t = MixinThread(t_); + if (Transitions.InDormantState(i) || + !t.readyForCoCo || + t.pinnedOut) { + t.acknowledgedPhase = (int)phase; + t.acknowledgedForwarding = forwarding; + t.acknowledgedPinning = pinning; + } + if (t.pinnedOut && phase==Phase.Idle) { + t.pinnedOut=false; + doPulseAll=true; + } + if ((Phase)t.acknowledgedPhase != phase || + t.acknowledgedForwarding != forwarding || + t.acknowledgedPinning != pinning) { + if (fDebug) { + VTable.DebugPrint(" !! thread "); + VTable.DebugPrint((ulong)Magic.addressOf(t)); + VTable.DebugPrint(" not ack\n"); + } + needToWait = true; + } + } + if (doPulseAll) { + Monitor.PulseAll(interlock); + } + if (!needToWait) { + break; + } + // REVIEW: make the timeout less than 500 ms + Monitor.Wait(interlock, 500); + } + } + } + + internal static bool IsIdle + { + [ForceInline] + get { + return !isNotIdle; + } + } + + // Given a pointer to an object or into the PostHeader or payload + // parts of an object, return the address of the object. + internal static UIntPtr FindObjectForInteriorPtr(UIntPtr addr) + { + UIntPtr result; + if (SegregatedFreeList.IsGcPtr(addr)) { + result = GC.installedGC.FindObjectAddr(addr); + } else { + result = UIntPtr.Zero; + } + return result; + } + + // Given a pointer to an object or into the PreHeader, PostHeader, + // or payload parts, return the address of the object. A pointer + // past the last element of an object or array, which is considered + // a valid "user-level" interior pointer into the object, may be + // considered an interior pointer into the subsequent object in + // memory, so this method is only to be used to translate from + // real field addresses to object addresses. + internal static UIntPtr FindObjectForPreInteriorPtr(UIntPtr addr) + { + return FindObjectForInteriorPtr(addr + PreHeader.Size); + } + + [ForceInline] + internal static bool StrictlyAllowFastPath(int mask) + { + return (mask & BarrierMask.PathSpec.AllowFast)!=0; + } + + [ForceInline] + internal static bool AllowIdleFastPath(int mask) + { + if ((mask & BarrierMask.PathSpec.UseMask)!=0) { + return (mask & BarrierMask.PathSpec.AllowFast)!=0; + } else { + return IsIdle; + } + } + + [ForceInline] + internal static bool AllowFastPath(int mask) + { + if ((mask & BarrierMask.PathSpec.UseMask)!=0) { + return (mask & BarrierMask.PathSpec.AllowFast)!=0; + } else { + return allowFastPath; + } + } + + [ForceInline] + internal static bool AllowPinFastPath(int mask) + { + if ((mask & BarrierMask.PathSpec.UseMask)!=0) { + return (mask & BarrierMask.PathSpec.AllowFast)!=0; + } else { + return !pinning; + } + } + + [NoBarriers] + [TrustedNonNull] + internal static CoCoBarrier instance; + + internal CoCoBarrier() + { + } + + [ForceInline] + protected override bool AllowFastPathImpl() + { + return allowFastPath; + } + + internal abstract bool ObjectIsNotCopied(Object o); + + internal abstract UIntPtr ToSpaceImplBeforeReadyNonNull(Object o); + + internal enum Pinner { + StackScan, + Barrier + } + + [AssertDevirtualize] + internal abstract UIntPtr DoPin(UIntPtr address, + Pinner pinner); + + [Inline] + [NoBarriers] + internal static UIntPtr ToSpaceBeforeReadyImpl(Object o) + { + if (o==null) { + return UIntPtr.Zero; + } else { + return instance.ToSpaceImplBeforeReadyNonNull(o); + } + } + + [AssertDevirtualize] + internal abstract UIntPtr ToSpaceImplNonNull(Object o); + + [Inline] + [NoBarriers] + internal static UIntPtr ToSpaceImpl(Object o) + { + if (o==null) { + return UIntPtr.Zero; + } else { + return instance.ToSpaceImplNonNull(o); + } + } + + [NoInline] + [NoBarriers] + [CalledRarely] + internal static UIntPtr ToSpaceImplNoInline(Object o) + { + return ToSpaceImpl(o); + } + + [Inline] + internal static UIntPtr ToSpaceAsPtr(Object o) + { + return ToSpaceImpl(o); + } + + [Inline] + internal static UIntPtr ToSpaceAsPtr(UIntPtr addr) + { + return ToSpaceImpl(Magic.fromAddress(addr)); + } + + [Inline] + internal static Object ToSpaceAsObj(Object o) + { + return Magic.fromAddress(ToSpaceImpl(o)); + } + + [Inline] + internal static Object ToSpaceAsObj(UIntPtr addr) + { + return Magic.fromAddress(ToSpaceImpl(Magic.fromAddress(addr))); + } + + [Inline] + protected override Object WeakRefReadImpl(UIntPtr addr, + int mask) + { + addr=ToSpaceAsPtr(addr); + if (NeedsTargetBarrier) { + Mark(addr); + } + return Magic.fromAddress(addr); + } + + [Inline] + protected override UIntPtr WeakRefWriteImpl(Object obj, + int mask) + { + return ToSpaceAsPtr(obj); + } + + // returns true if any objects have been tagged for copying. + internal abstract bool AnyTaggedForCopying + { + get; + } + + // collector calls this method to indicate the intent to copy a given + // object. the to-space copy must already be allocated. no calls to + // this method are allowed after the pin stack scan. return false + // if tagging failed. spaceOverhead will hold the amount of space + // overhead induced by tagging (or failing to tag), not counting the + // to-space. + internal abstract bool TagObjectForCopy(Object from, + Object to, + out UIntPtr spaceOverhead); + + internal abstract void PinningEnabledHook(); + + // returns true if the prep phase is needed. (if false then go directly + // to copy.) + internal abstract bool NeedsPrepPhase { + get; + } + + // returns true if any offset pointers (i.e. managed pointers into + // objects) should result in pinning of the base object. + internal abstract bool PinOffsetPointers { + get; + } + + // collector calls this method to request copying. can only be called + // from the copy phase. returns number of objects actually copied. + internal abstract ulong Copy(); + + // this is _just_ a notification - it doesn't pin the object, it's just + // what we do if an object ends up being pinned. + internal static void NotifyPin(UIntPtr objAddr) + { + UIntPtr page=PageTable.Page(objAddr); + if (PageTable.Type(page)!=SegregatedFreeList.SMALL_OBJ_PAGE) { + return; + } + SegregatedFreeList.PageHeader *ph= + (SegregatedFreeList.PageHeader*)PageTable.PageAddr(page); + CoCoPageUserValue v=new CoCoPageUserValue(ph->userValue); + if (v.Marked) { + v.Pinned=true; + } + ph->userValue=v.Bits; + } + + internal static bool ShouldPin(UIntPtr objAddr) + { + UIntPtr page=PageTable.Page(objAddr); + if (PageTable.Type(page)!=SegregatedFreeList.SMALL_OBJ_PAGE) { + // in practice this won't be reached + return true; + } + SegregatedFreeList.PageHeader *ph= + (SegregatedFreeList.PageHeader*)PageTable.PageAddr(page); + return new CoCoPageUserValue(ph->userValue).Pinned; + } + + //////////////////// Pre write barriers //////////////////// + // REVIEW: this isn't general enough. Currently it is + // only used by CoCo, but it is probably of use to other + // collectors. + // + // value = the old value at a location being overwritten + [AssertDevirtualize] + [ForceInline] + protected virtual void TargetBarrierImpl(UIntPtr value) + { + Mark(value); + } + + [TrustedNonNull] + private static PreWriteFieldsVisitor preWriteFieldsVisitor; + + [Inline] + [AssertDevirtualize] + protected void PreWriteStruct(VTable vtable, + UIntPtr dstPtr) + { + preWriteFieldsVisitor.VisitReferenceFields(vtable, dstPtr); + } + + [Inline] + [AssertDevirtualize] + protected void PreWriteObject(Object dstObject) + { + preWriteFieldsVisitor.VisitReferenceFields(dstObject); + } + + [Inline] + [AssertDevirtualize] + protected void PreWriteArray(Array array, int offset, + int length) + { + preWriteFieldsVisitor + .VisitReferenceFields(array.vtable, + Magic.addressOf(array) + + IndexedElementOffset(array, offset), + length); + } + + private class PreWriteFieldsVisitor : OffsetReferenceVisitor + { + + // Pre struct copy + internal void VisitReferenceFields(VTable vtable, + UIntPtr dstPtr) + { + int postHeaderSize = PostHeader.Size; + ObjectDescriptor objDesc = + new ObjectDescriptor(vtable, + dstPtr - postHeaderSize); + VisitReferenceFieldsTemplate(ref objDesc); + } + + // Pre object copy (already provided) + //internal void VisitReferenceFields(Object dstObject) + + // Partial array copy or array zero + internal void VisitReferenceFields(VTable vtable, + UIntPtr dstElementPtr, + int length) + { + ObjectDescriptor objDesc = + new ObjectDescriptor(vtable, dstElementPtr); + VisitReferenceFieldsTemplate(ref objDesc, length); + } + + internal override void FieldOffset(UIntPtr offset, + ref ObjectDescriptor objDesc) + { + UIntPtr *dstAddr = (UIntPtr *) (objDesc.objectBase + offset); + ((LowAbortCoCoBarrier) Barrier.installedBarrier).TargetBarrierImpl(*dstAddr); + } + + } + + internal static bool fCount { get { return true; } } + internal static ulong numPins; + internal static ulong numWaitPins; + internal static ulong numPinWaits; + internal static ulong numAtomics; + internal static ulong numSlowCopyStructs; + internal static ulong numSlowClones; + internal static ulong numSlowArrayZeroes; + internal static ulong numSlowArrayCopies; + internal static ulong numTaintPins; + + internal static bool fProfileCAS { get { return true; } } + internal static ulong numCASFailed; + + internal static void PrintStat(string name, ulong n) + { + VTable.DebugPrint(name); + VTable.DebugPrint(" = "); + VTable.DebugPrint(n); + VTable.DebugPrint("\n"); + } + + internal static void PrintStats() + { + if (fCount) { + PrintStat("numPins", numPins); + PrintStat("numWaitPins", numWaitPins); + PrintStat("numPinWaits", numPinWaits); + PrintStat("numAtomics", numAtomics); + PrintStat("numSlowCopyStructs", numSlowCopyStructs); + PrintStat("numSlowClones", numSlowClones); + PrintStat("numSlowArrayZeroes", numSlowArrayZeroes); + PrintStat("numSlowArrayCopies", numSlowArrayCopies); + } + if (fProfileCAS) { + PrintStat("numCASFailed", numCASFailed); + } + } + + internal static bool fDebug { get { return false; } } + internal static bool fVerboseNull { get { return false; } } + internal static bool fVerboseCopy { get { return false; } } + internal static bool fGorierCopy { get { return false; } } + internal static bool fVerboseRead { get { return false; } } + internal static bool fVerbose { get { return false; } } + internal static bool fDebugFindObj { get { return false; } } + + internal static bool fVerifyToSpaceMark { get { return true; } } + } +} + diff --git a/base/Imported/Bartok/runtime/shared/GCs/CoCoMSCollector.cs b/base/Imported/Bartok/runtime/shared/GCs/CoCoMSCollector.cs new file mode 100644 index 0000000..fb61976 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/CoCoMSCollector.cs @@ -0,0 +1,823 @@ +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +namespace System.GCs { + + using Microsoft.Bartok.Runtime; + using System.Runtime.CompilerServices; + using System.Threading; + +#if SINGULARITY + using Microsoft.Singularity; +#if SINGULARITY_PROCESS + using Microsoft.Singularity.V1.Services; // Used for timing, only +#elif SINGULARITY_KERNEL + using Microsoft.Singularity.Scheduling; + using Microsoft.Singularity.X86; +#endif +#endif + + using Microsoft.Win32; + + [NoCCtor] + [RequiredByBartok] + internal class CoCoMSCollector: ConcurrentMSCollector + { + internal static new CoCoMSCollector instance; + + internal static Thread cocoThread; + + internal static bool didStartTrace; + internal static bool didEndTrace; + internal static bool wantCoCo; + internal static bool doingCoCo; + internal static int markingForCoCo; // actually a boolean + internal static bool inFixUp; + internal static bool die; + internal static ulong numCopied; + internal static int delayCnt; + internal static Object interlock; + + internal static int pinTime; + internal static int prepTime; + internal static int copyTime; + internal static int forwardTime; + + internal static int timingBefore; + internal static int timingAfterPin; + internal static int timingAfterPrep; + internal static int timingAfterCopy; + internal static int timingAfter; + + internal static UIntPtr maxSpaceOverhead; + + internal static int sizeFracLim; + internal static int sizeLim; + internal static int pageFragThres; + internal static int pinPenalty; + internal static int cocoDelay; + + internal static UIntPtr lastSmallPagesRecycle; + + internal static NonNullReferenceVisitor normalStackMarker; + internal static NonNullReferenceVisitor nopStackMarker; + internal static NonNullReferenceVisitor pinStackMarker; + + [NoBarriers] + [PreInitRefCounts] + public static new void Initialize() + { + ConcurrentMSCollector.InitializeAllButVisitors(); + // instance = new CoCoMSCollector(); + ConcurrentMSCollector.instance + = CoCoMSCollector.instance + = (CoCoMSCollector) + BootstrapMemory.Allocate(typeof(CoCoMSCollector)); + ConcurrentMSCollector.markReferenceVisitor = + (MarkReferenceVisitor) + BootstrapMemory.Allocate(typeof(MarkAndForwardReferenceVisitor)); + CoCoMSCollector.normalStackMarker = + (NonNullReferenceVisitor) + BootstrapMemory.Allocate(typeof(StackForwardReferenceVisitor)); + CoCoMSCollector.pinStackMarker = + (NonNullReferenceVisitor) + BootstrapMemory.Allocate(typeof(StackMarkPinnedReferenceVisitor)); + CoCoMSCollector.nopStackMarker = + (NonNullReferenceVisitor) + BootstrapMemory.Allocate(typeof(StackNopReferenceVisitor)); + ConcurrentMSCollector.stackMarkReferenceVisitor = + CoCoMSCollector.normalStackMarker; + ConcurrentMSCollector.stackMarkPinnedReferenceVisitor = + CoCoMSCollector.normalStackMarker; + ConcurrentMSCollector.updateReferenceVisitor = + (UpdateReferenceVisitor) + BootstrapMemory.Allocate(typeof(UpdateAndForwardReferenceVisitor)); + ConcurrentMSCollector.partialFreePageVisitor = + (SegregatedFreeList.PartialFreePageVisitor) + BootstrapMemory.Allocate(typeof(TaggingPartialFreePageVisitor)); + // sweepVisitor = new SweepVisitor(); + sweepVisitor = (SweepVisitor) + BootstrapMemory.Allocate(typeof(SweepVisitor)); + } + + internal static int EnvInt(int defVal, + string name) + { + string str=Environment.GetEnvironmentVariable(name); + if (str==null) { + return defVal; + } else { + return Int32.Parse(str); + } + } + + internal override void EnableHeap() + { + interlock=new Object(); + MultiUseWord.GetMonitor(interlock); + CoCoBarrier.InitLate(); + + // REVIEW: add some bartok args instead + + sizeFracLim = EnvInt( 10 , "COCO_SIZE_FRAC_LIM" ); + sizeLim = EnvInt( -1 , "COCO_SIZE_LIM" ); + pageFragThres = EnvInt( 2 , "COCO_PAGE_FRAG_THRES" ); + pinPenalty = EnvInt( 10 , "COCO_PIN_PENALTY" ); + cocoDelay = EnvInt( 8 , "COCO_COPY_DELAY" ); + + if (EnvInt(0,"COCO_FORCE_SLOW")!=0) { + CoCoBarrier.ForceSlow(); + } + if (EnvInt(0,"COCO_FORCE_NOT_IDLE")!=0) { + CoCoBarrier.ForceNotIdle(); + } + if (EnvInt(0,"COCO_FORCE_FORWARDING")!=0) { + CoCoBarrier.ForceForwarding(); + } + if (EnvInt(0,"COCO_FORCE_PINNING")!=0) { + CoCoBarrier.ForcePinning(); + } + + base.EnableHeap(); + cocoThread=new Thread(new ThreadStart(CoCoLoop)); + cocoThread.Start(); + } + + internal override void Shutdown() + { + base.Shutdown(); + lock (interlock) { + die = true; + Monitor.PulseAll(interlock); + } + cocoThread.Join(); + if (VTable.enableFinalGCTiming) { + VTable.DebugPrint("CoCo completed "); + VTable.DebugPrint(cycles); + VTable.DebugPrint(" cycles, started "); + VTable.DebugPrint(cyclesStarted); + VTable.DebugPrint(" cycles, and copied "); + VTable.DebugPrint(numCopied); + VTable.DebugPrint(" objects.\n"); + VTable.DebugPrint("CoCo took "); + VTable.DebugPrint((ulong)pinTime); + VTable.DebugPrint("+"); + VTable.DebugPrint((ulong)prepTime); + VTable.DebugPrint("+"); + VTable.DebugPrint((ulong)copyTime); + VTable.DebugPrint("+"); + VTable.DebugPrint((ulong)forwardTime); + VTable.DebugPrint("="); + VTable.DebugPrint((ulong)(pinTime+prepTime+copyTime+forwardTime)); + VTable.DebugPrint(" ms.\n"); + VTable.DebugPrint("max space overhead = "); + VTable.DebugPrint((ulong)maxSpaceOverhead); + VTable.DebugPrint("\n"); + CoCoBarrier.PrintStats(); + } + } + + internal override void ThreadStartNotification(int currentThreadIndex) + { + base.ThreadStartNotification(currentThreadIndex); + CoCoBarrier.ThreadStart(Thread.threadTable[currentThreadIndex]); + } + + internal override UIntPtr AllocateObjectMemorySlow(UIntPtr numBytes, + uint alignment, + Thread currentThread) + { + CoCoBarrier.ClientHandshake(); + return base.AllocateObjectMemorySlow(numBytes, + alignment, + currentThread); + } + + internal static void InterlockWithCopier(ref bool condition) + { + lock (interlock) { + if (fVerbose) { + VTable.DebugPrint(" ~~ acquired lock, setting condition\n"); + } + condition = true; + if (fVerbose) { + VTable.DebugPrint(" ~~ pulsing = "); + VTable.DebugPrint((ulong)Magic.addressOf(interlock)); + VTable.DebugPrint("\n"); + } + Monitor.PulseAll(interlock); + if (fVerbose) { + VTable.DebugPrint(" ~~ waiting\n"); + } + while (condition) { + Monitor.Wait(interlock); + } + } + } + + internal override void PreTraceHook() + { + if (fVerbose) { + VTable.DebugPrint(" ~~~~~ PreTraceHook ~ waiting\n"); + } + InterlockWithCopier(ref didStartTrace); + if (fVerbose) { + VTable.DebugPrint(" ~~~~~ PreTraceHook ~ awoken\n"); + } + } + + internal override void PostRootScanHook() + { + if (inFixUp) { + timingAfterCopy = Environment.TickCount; + CoCoBarrier.ChangePhase(CoCoBarrier.Phase.Idle,true,true); + } + } + + internal override void PostTraceHook() + { + if (fVerbose) { + VTable.DebugPrint(" ~~~~~ PostTraceHook ~ waiting\n"); + } + InterlockWithCopier(ref didEndTrace); + if (fVerbose) { + VTable.DebugPrint(" ~~~~~ PostTraceHook ~ awoken\n"); + } + } + + internal override void PostReclamationHook() + { + } + + internal override void PreSweepHook() + { + CoCoBarrier.ExchangeReadyForCoCo(false); + } + + internal override void PostSweepHook() + { + CoCoBarrier.ExchangeReadyForCoCo(true); + } + + internal static void CoCoLoop() + { + if (fDebug) { + VTable.DebugPrint("coco thread = "); + VTable.DebugPrint((ulong)Win32Native.GetCurrentThreadId()); + VTable.DebugPrint("\n"); + VTable.DebugPrint("CoCo at "); + VTable.DebugPrint((ulong)Magic.addressOf(Thread.CurrentThread)); + VTable.DebugPrint("\n"); + } + for (;;) { + lock (interlock) { + doingCoCo=false; + for (;;) { + if (die) { + return; + } else if (didStartTrace) { + didStartTrace=false; + Monitor.PulseAll(interlock); + } else if (didEndTrace) { + didEndTrace=false; + Monitor.PulseAll(interlock); + if (wantCoCo) { + break; + } + } + Monitor.Wait(interlock); + } + wantCoCo=false; + } + + // now further tracing is BLOCKED + + cyclesStarted++; + timingBefore = Environment.TickCount; + + if (fDebug) { + VTable.DebugPrint("+++++ Start Concurrent Copying\n"); + } + + CoCoBarrier.EnablePinning(); + doingCoCo=true; + ConcurrentMSCollector.stackMarkReferenceVisitor = + CoCoMSCollector.nopStackMarker; + ConcurrentMSCollector.stackMarkPinnedReferenceVisitor = + CoCoMSCollector.pinStackMarker; + // Perform a scan of all call stacks, including the call + // stack of the CoCo thread. + ConcurrentMSCollector.TrivialHandshake = false; + ConcurrentMSCollector.IncludeMUWInHandshake = false; + ConcurrentMSCollector.CollectorHandshake(cocoThread); + // In order to scan the call stack of the current thread, + // we need a TransitionRecord for the thread. At this + // point we don't have one, so we have to go through + // CollectBodyTransition to get one. + Transitions.MakeGCRequest(cocoThread.threadIndex); + GC.InvokeCollection(cocoThread); + ConcurrentMSCollector.TrivialHandshake = true; + ConcurrentMSCollector.IncludeMUWInHandshake = true; + ConcurrentMSCollector.stackMarkReferenceVisitor = + CoCoMSCollector.normalStackMarker; + ConcurrentMSCollector.stackMarkPinnedReferenceVisitor = + CoCoMSCollector.normalStackMarker; + + timingAfterPin = Environment.TickCount; + + if (fDebug) { + VTable.DebugPrint("+++++ Copying\n"); + } + + if (CoCoBarrier.instance.NeedsPrepPhase) { + CoCoBarrier.ChangePhase(CoCoBarrier.Phase.Prep,false,true); + } + + timingAfterPrep = Environment.TickCount; + + CoCoBarrier.ChangePhase(CoCoBarrier.Phase.Copy,true,true); + + numCopied+=CoCoBarrier.instance.Copy(); + + CoCoBarrier.ChangePhase(CoCoBarrier.Phase.Fixup,true,true); + + AddCollectionRequest(); + + // wait for a complete collector cycle. This is for fixup. + if (fDebug) { + VTable.DebugPrint("+++++ Fixup: Waiting to start tracing\n"); + } + lock (interlock) { + while (!didStartTrace && !die) { + Monitor.Wait(interlock); + } + if (die) { + return; + } + didStartTrace=false; + inFixUp=true; + Monitor.PulseAll(interlock); + } + + if (fDebug) { + VTable.DebugPrint("+++++ Fixup: Waiting to end tracing\n"); + } + lock (interlock) { + while (!didEndTrace && !die) { + Monitor.Wait(interlock); + } + if (die) { + return; + } + didEndTrace=false; + doingCoCo=false; + inFixUp=false; + Monitor.PulseAll(interlock); + } + + timingAfter = Environment.TickCount; + + CoCoBarrier.ChangePhase(CoCoBarrier.Phase.Idle,false,false); + + if (fDebug) { + VTable.DebugPrint("+++++ Finish Concurrent Copying\n"); + } + + pinTime+=(timingAfterPin-timingBefore); + prepTime+=(timingAfterPrep-timingAfterPin); + copyTime+=(timingAfterCopy-timingAfterPrep); + forwardTime+=(timingAfter-timingAfterCopy); + + cycles++; + } + } + + internal class MarkAndForwardReferenceVisitor : MarkReferenceVisitor + { + [NoBarriers] + ulong cnt; + [NoBarriers] + bool amMarkingForCoCo; + [NoBarriers] + UIntPtr spaceOverhead; + + internal override void Init() + { + base.Init(); + cnt=0; + amMarkingForCoCo=(markingForCoCo!=0); + spaceOverhead=UIntPtr.Zero; + if (fDebug && amMarkingForCoCo) { + VTable.DebugPrint("+++++ Marking For CoCo\n"); + } + } + + internal override void Cleanup() + { + base.Cleanup(); + if (spaceOverhead>maxSpaceOverhead) { + maxSpaceOverhead=spaceOverhead; + } + if (amMarkingForCoCo) { + markingForCoCo=0; +#if !ARM && !ISA_ARM + // HACK: MemoryBarrier is unimplemented in ARM + // and causes compile-time failures when building + // mscorlib in sepcomp mode. This change will break + // CoCo if built with ARM. + Thread.MemoryBarrier(); +#endif + } + if (fDebug) { + VTable.DebugPrint(" $$$ tagged "); + VTable.DebugPrint(cnt); + VTable.DebugPrint(" objects\n"); + } + } + + [NoInline] + internal unsafe void ForwardAndVisit(UIntPtr *loc) + { + UIntPtr addr=*loc; + if (addr==UIntPtr.Zero) { + return; + } + UIntPtr forward= + CoCoBarrier.instance.ToSpaceImplNonNull(Magic.fromAddress(addr)); + if (forward != addr) { + CoCoBarrier.CAS(loc,forward,addr); + // if this CAS fails it means that someone stored + // a different pointer into the field, but in that + // case the pointer stored would have already been + // forwarded thanks to the CoCo write barrier. + } + VisitValueNonNull(forward); + } + + [Inline] + protected override unsafe + void Filter(UIntPtr *location, ref ObjectDescriptor objDesc) + { + this.Visit(location); + } + + /// + /// Visit an object reference. + /// + [Inline] + internal unsafe override void Visit(UIntPtr *loc) + { + if (CoCoBarrier.forwarding) { + ForwardAndVisit(loc); + } else { + VisitValueMaybeNull(*loc); + } + } + + // This method simply forces the compiler to generate a copy + // of VisitReferenceFieldsTemplate in this class. + [ManualRefCounts] + [Inline] + internal override UIntPtr VisitReferenceFields(Object obj) + { + return this.VisitReferenceFields(Magic.addressOf(obj), + obj.vtable); + } + + // This method simply forces the compiler to generate a copy + // of VisitReferenceFieldsTemplate in this class. + [ManualRefCounts] + [Inline] + internal override + UIntPtr VisitReferenceFields(UIntPtr objectBase, VTable vtable) + { + ObjectDescriptor objDesc = + new ObjectDescriptor(vtable, objectBase); + return VisitReferenceFieldsTemplate(ref objDesc); + } + + [NoInline] + //[NoBarriers] + void ProcessObjectsSlow(ref ThreadHeaderQueue.LocalList workList) + { + while (!ConcurrentMSCollector.killCollectorThreads + && !workList.IsEmpty()) { + // Pop the next value + Object obj = workList.Pop(markedColor); + if (fVerbose) { + VTable.DebugPrint("cms popped: "); + VTable.DebugPrint((ulong)Magic.addressOf(obj)); + VTable.DebugPrint("\n"); + } + // let CoCo do some stuff + ScanHook(obj); + // Visit Fields + this.VisitReferenceFields(obj); + } + } + + //[NoBarriers] + internal override + void ProcessMyGrayObjects(ref ThreadHeaderQueue.LocalList workList) + { + if (amMarkingForCoCo) { + ProcessObjectsSlow(ref workList); + } else { + // hand-inlined from ConcurrentMSCollector. needed + // to ensure that the VisitReferenceFields call gets + // inlined. + while (!ConcurrentMSCollector.killCollectorThreads + && !workList.IsEmpty()) { + // Pop the next value + Object obj = workList.Pop(markedColor); + if (CoCoBarrier.fVerifyToSpaceMark && + !CoCoBarrier.instance.IsInToSpace(Magic.addressOf(obj))) { + VTable.DebugBreak(); + } + // Visit Fields + this.VisitReferenceFields(obj); + } + } + } + + internal unsafe void ScanHook(Object obj) + { + UIntPtr page=PageTable.Page(Magic.addressOf(obj)); + if (PageTable.Type(page)!=SegregatedFreeList.SMALL_OBJ_PAGE) { + //VTable.DebugPrint(" not tagging because this isn't a small object page"); + return; + } + SegregatedFreeList.PageHeader *ph= + (SegregatedFreeList.PageHeader*)PageTable.PageAddr(page); + if (!new CoCoPageUserValue(ph->userValue).Marked) { + //VTable.DebugPrint(" not tagging because the page isn't marked\n"); + return; + } + if (obj is EMU || + obj is Monitor || + obj is Thread || + obj is ThreadHeaderQueue) { + CoCoBarrier.NotifyPin(Magic.addressOf(obj)); + if (fVerbose) { + VTable.DebugPrint(" $$ not tagging object because it's a monitor or EMU\n"); + } + return; + } + if (doingCoCo) { + //VTable.DebugPrint(" not tagging object because doingCoCo\n"); + return; + } + if (!CoCoBarrier.instance.ObjectIsNotCopied(obj)) { + if (fVerbose) { + VTable.DebugPrint(" not tagging object because object is already in the process of being copied.\n"); + } + return; + } + + if (fVerbose && obj.GetType() != typeof(Object)) { + VTable.DebugPrint(" $$ tagging a non-System.Object; type is "); + VTable.DebugPrint(obj.GetType().Name); + VTable.DebugPrint("\n"); + } + + // REVIEW: I wish that there was an easier way of + // doing this. + Object copy; + if (obj is Array) { + Array a=(Array)obj; + if (a.IsVector) { + copy=GC.AllocateVector(a.vtable,a.Length); + } else { + copy=GC.AllocateArray(a.vtable,a.Rank,a.Length); + } + } else if (obj is String) { + String s=(String)obj; + // REVIEW: this is not nice. + copy=GC.AllocateString(s.ArrayLength-1); + } else { + copy=GC.AllocateObject(obj.vtable); + } + + VTable.Assert(ObjectLayout.Sizeof(copy) + == ObjectLayout.Sizeof(obj), + "Copy is not same size as original"); + + spaceOverhead+=ObjectLayout.Sizeof(copy); + + bool first=!CoCoBarrier.instance.AnyTaggedForCopying; + UIntPtr thisSpaceOverhead; + if (CoCoBarrier.instance.TagObjectForCopy(obj,copy, + out thisSpaceOverhead)) { + cnt++; + if (first) { + lock (interlock) { + if (!wantCoCo && !doingCoCo) { + wantCoCo=true; + } + } + } + } + + spaceOverhead+=thisSpaceOverhead; + } + } + + internal class UpdateAndForwardReferenceVisitor: UpdateReferenceVisitor + { + internal override UIntPtr ForwardIfNecessary(UIntPtr addr) + { + return CoCoBarrier.ToSpaceAsPtr(addr); + } + } + + internal class TaggingPartialFreePageVisitor: SegregatedFreeList.PartialFreePageVisitor + { + UIntPtr cnt; + UIntPtr unmarkedCnt; + UIntPtr delayedCnt; + ulong emptyCnt; + bool delay; + + internal override void Start() + { + cnt=UIntPtr.Zero; + unmarkedCnt=UIntPtr.Zero; + delayedCnt=UIntPtr.Zero; + emptyCnt=0; + if (fDebug) { + VTable.DebugPrint(" $$$ delayCnt = "); + VTable.DebugPrint((ulong)delayCnt); + VTable.DebugPrint(", markingForCoCo = "); + VTable.DebugPrint((ulong)markingForCoCo); + VTable.DebugPrint("\n"); + } + if (delayCnt==0) { + delay=(markingForCoCo!=0); // delay if there are still pages marked + } else { + delayCnt--; + delay=true; + } + if (fDebug) { + VTable.DebugPrint(" $$$ delayCnt = "); + VTable.DebugPrint((ulong)delayCnt); + VTable.DebugPrint(", markingForCoCo = "); + VTable.DebugPrint((ulong)markingForCoCo); + VTable.DebugPrint(", delay = "); + VTable.DebugPrint(delay?"yes":"no"); + VTable.DebugPrint("\n"); + } + } + + internal override void Finish() + { + lastSmallPagesRecycle=SegregatedFreeList.SmallPages; + if (fDebug) { + VTable.DebugPrint(" $$$ marked "); + VTable.DebugPrint((ulong)cnt); + VTable.DebugPrint(", unmarked "); + VTable.DebugPrint((ulong)unmarkedCnt); + VTable.DebugPrint(", delayed "); + VTable.DebugPrint((ulong)delayedCnt); + VTable.DebugPrint(", and freed "); + VTable.DebugPrint(emptyCnt); + VTable.DebugPrint(".\n"); + } + if (cnt!=0) { + delayCnt=cocoDelay; + markingForCoCo=1; +#if !ARM && !ISA_ARM + // HACK: MemoryBarrier is unimplemented in ARM + // and causes compile-time failures when building + // mscorlib in sepcomp mode. This change will break + // CoCo if built with ARM. + Thread.MemoryBarrier(); +#endif + } + } + + internal override void ObserveEmptyPage() + { + emptyCnt++; + } + + internal override unsafe SegregatedFreeList.PartialPageAction + VisitPage(UIntPtr page, + SegregatedFreeList.PageHeader *ph, + int cells) + { + if (fDebugPage) { + VTable.DebugPrint(" $$ visiting page.\n"); + } + CoCoPageUserValue v=new CoCoPageUserValue(ph->userValue); + if (inFixUp && v.Marked) { + unmarkedCnt++; + v.Marked=false; + if (v.Pinned) { + v.Pinned=false; + v.Version=pinPenalty; /* use the Version to delay + any future evacuation + attempts for this page */ + } + } else if (!doingCoCo && !delay) { + if (v.Version>=1) { + // this page has a non-zero Version - this means + // that any attempts to evacuate it should be delayed. + v.Version--; + delayedCnt++; + if (fDebugPage) { + VTable.DebugPrint(" $$ page had non-zero version.\n"); + } + } else { + if (fDebugPage) { + VTable.DebugPrint(" $$ cnt = "); + VTable.DebugPrint((ulong)cnt); + VTable.DebugPrint(", lastSmallPagesRecycle = "); + VTable.DebugPrint((ulong)lastSmallPagesRecycle); + VTable.DebugPrint(", sizeFracLim = "); + VTable.DebugPrint((ulong)sizeFracLim); + VTable.DebugPrint(", sizeLim = "); + VTable.DebugPrint((ulong)sizeLim); + VTable.DebugPrint(", PageSize = "); + VTable.DebugPrint((ulong)PageTable.PageSize); + VTable.DebugPrint(", freeCount = "); + VTable.DebugPrint((ulong)ph->freeCount); + VTable.DebugPrint(", pageFragThres = "); + VTable.DebugPrint((ulong)pageFragThres); + VTable.DebugPrint(", cells = "); + VTable.DebugPrint((ulong)cells); + VTable.DebugPrint("\n"); + } + if ((long)cnt < ((long)lastSmallPagesRecycle + /(long)sizeFracLim) + && (sizeLim<0 || + cnt*PageTable.PageSize < (UIntPtr)sizeLim) + && ph->freeCount*pageFragThres >= cells) { + cnt++; + v.Marked=true; + } + } + } else { + if (fDebugPage) { + VTable.DebugPrint(" $$ not doing anything about page.\n"); + } + } + ph->userValue=v.Bits; + if (v.Marked) { + return SegregatedFreeList.PartialPageAction.CommitFull; + } else { + return SegregatedFreeList.PartialPageAction.CommitFree; + } + } + } + + internal class StackForwardReferenceVisitor : ConcurrentMSCollector.StackMarkReferenceVisitor + { + internal override unsafe void ProcessObjectPtr(UIntPtr realPtr, + UIntPtr *loc, + UIntPtr addr) + { + UIntPtr forward=CoCoBarrier.ToSpaceAsPtr(realPtr); + if (forward!=realPtr) { + *loc=addr-realPtr+forward; + } + markReferenceVisitor.VisitValueAnyThreadMaybeNull(forward); + } + } + + internal class StackNopReferenceVisitor : NonNullReferenceVisitor + { + [Inline] + internal override unsafe void Visit(UIntPtr *location) + { + // do nothing + } + } + + internal class StackMarkPinnedReferenceVisitor : ConcurrentMSCollector.StackMarkReferenceVisitor + { + internal override unsafe void ProcessObjectPtr(UIntPtr realPtr, + UIntPtr *loc, + UIntPtr addr) + { + // FIXME: DoPin() already does FindObjectForInteriorPtr + CoCoBarrier.instance.DoPin(realPtr,CoCoBarrier.Pinner.StackScan); + } + } + + private static ulong cyclesStarted; + private static ulong cycles; + + private static bool fVerbose { get { return false; } } + private static bool fDebug { get { return false; } } + private static bool fDebugPage { get { return false; } } + } + +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/CoCoPageUserValue.cs b/base/Imported/Bartok/runtime/shared/GCs/CoCoPageUserValue.cs new file mode 100644 index 0000000..8820825 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/CoCoPageUserValue.cs @@ -0,0 +1,63 @@ +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +namespace System.GCs { + + internal struct CoCoPageUserValue + { + private ushort userValue; + + internal CoCoPageUserValue(ushort userValue) + { + this.userValue = userValue; + } + + internal ushort Bits + { + get { + return userValue; + } + } + + internal bool Marked + { + get { + return (userValue&1)!=0; + } + set { + userValue=(ushort)((userValue&~(ushort)1)|(value?(ushort)1:(ushort)0)); + } + } + + internal bool Pinned + { + get { + return (userValue&2)!=0; + } + set { + userValue=(ushort)((userValue&~(ushort)2)|(value?(ushort)2:(ushort)0)); + } + } + + internal int Version + { + get { + return (int)(userValue>>2); + } + set { + userValue=(ushort)((userValue&(ushort)3)|((ushort)(value<<2))); + } + } + + } + +} + diff --git a/base/Kernel/Bartok/GCs/Collector.cs b/base/Imported/Bartok/runtime/shared/GCs/Collector.cs similarity index 82% rename from base/Kernel/Bartok/GCs/Collector.cs rename to base/Imported/Bartok/runtime/shared/GCs/Collector.cs index 92e3efc..0637955 100644 --- a/base/Kernel/Bartok/GCs/Collector.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/Collector.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,10 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - namespace System.GCs { @@ -21,8 +21,9 @@ namespace System.GCs internal abstract class Collector { // Helper methods for GC.cs - internal abstract void Collect(int currentThreadIndex, - int generation); + internal abstract void Collect(Thread currentThread, int generation); + internal abstract void CollectStoppable(int currentThreadIndex, + int generation); internal abstract void CheckForNeededGCWork(Thread currentThread); internal abstract int CollectionGeneration(int gen); internal abstract int GetGeneration(Object obj); @@ -31,6 +32,7 @@ namespace System.GCs internal abstract long TotalMemory { get; } // Creation and destruction of heaps internal abstract void EnableHeap(); + internal abstract void Shutdown(); internal abstract void DestructHeap(); // Verification of the heap internal abstract void VerifyHeap(bool beforeCollection); @@ -51,11 +53,16 @@ namespace System.GCs internal virtual void SetProfiler(GCProfiler profiler) { } internal virtual void ProfileAllocation(Object obj) { } // Allocation of objects + [AssertDevirtualize] internal abstract UIntPtr AllocateObjectMemory(UIntPtr numBytes, uint alignment, Thread currentThread); internal abstract Object AllocateObject(VTable vtable, Thread currentThread); + internal abstract Object AllocateObject(VTable vtable, + UIntPtr numBytes, + uint baseAlignment, + Thread currentThread); internal abstract Array AllocateVector(VTable vtable, int numElements, Thread currentThread); diff --git a/base/Kernel/Bartok/GCs/CollectorStatistics.cs b/base/Imported/Bartok/runtime/shared/GCs/CollectorStatistics.cs similarity index 95% rename from base/Kernel/Bartok/GCs/CollectorStatistics.cs rename to base/Imported/Bartok/runtime/shared/GCs/CollectorStatistics.cs index 2bad0b2..89ffa05 100644 --- a/base/Kernel/Bartok/GCs/CollectorStatistics.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/CollectorStatistics.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,11 +9,8 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -#define ENABLED +//#define ENABLED namespace System.GCs { using System; @@ -234,7 +235,6 @@ namespace System.GCs { #endif } - private static SpinLock hack; private static void Log(LogEntry le) { #if SINGULARITY Tracing.Log(Tracing.Audit, diff --git a/base/Imported/Bartok/runtime/shared/GCs/ConcurrentMSCollector.cs b/base/Imported/Bartok/runtime/shared/GCs/ConcurrentMSCollector.cs new file mode 100644 index 0000000..4d31578 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/ConcurrentMSCollector.cs @@ -0,0 +1,1626 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using Microsoft.Bartok.Runtime; + using System.Runtime.CompilerServices; + using System.Threading; + +#if SINGULARITY + using Microsoft.Singularity; +#if SINGULARITY_PROCESS + using Microsoft.Singularity.V1.Services; // Used for timing, only +#elif SINGULARITY_KERNEL + using Microsoft.Singularity.Scheduling; +#endif +#else + using Microsoft.Win32; +#endif + + using MarkingPhase = CMSMarking.MarkingPhase; + + /// + /// This class implements a semi-concurrent version of MarkSweep. + /// + /// The goal for this collector is to perform as much of the processing + /// as possible during mutator time, and also have a fixed upper bound + /// on pause times. + /// + /// Additionally, the collector will be required in the future to + /// communicate with the scheduler to prevent real time applications + /// from competing with the GC. + /// + /// + /// TODO: BUGBUG: NEED TO REPLICATE THE ALLOCATION COLOR LOGIC TO THE + /// INLINED ALLOCATION SEQUENCE. + /// + + [NoCCtor] + internal class ConcurrentMSCollector: BaseCollector + { + + internal static bool UseAnyThrottle { get { return true; } } + internal static bool UseYieldThrottle { get { return UseAnyThrottle && false; } } + internal static bool UseSleepThrottle { get { return UseAnyThrottle && true; } } + internal static bool UseOverlappingPhases { get { return false; } } + internal static bool UseSTWTracingPhase { get { return GC.enableSTWRetrace; } } + + private static MarkingPhase CurrentMarkingPhase { + [NoStackLinkCheck] + get { return CMSMarking.CurrentMarkingPhase; } + } + + internal enum ReclamationPhase { + Dummy, // Not used! + Idle, // No reclamation is in progress + Reclaiming, // Reclamation is in progress + } + + internal static ReclamationPhase CurrentReclamationPhase; + +#if SINGULARITY + private static TimeSpan infiniteWait; + private static TimeSpan minimumWait; +#else + private static int infiniteWait; + private static int minimumWait; +#endif + + /// + /// Current mark state. This is flipped between collections. + /// + internal static UIntPtr markedColor { + get { return CMSMarking.markedColor; } + set { CMSMarking.markedColor = value; } + } + internal static UIntPtr unmarkedColor { + get { return CMSMarking.unmarkedColor; } + set { CMSMarking.unmarkedColor = value; } + } + internal static UIntPtr reclamationColor; + + private const uint markOne = 1U; + private const uint markTwo = 2U; + private const uint markThree = 3U; + +#if SINGULARITY + // + // Note: This is updated to allow a large SINGULARITY build to advance + // through starting all CPU's before triggering the first collection. + // + // If we enter the marking phase while starting the second processor + // it will crash while trying to setup its processor context to point + // to the current thread which must be valid for the marking list. + // + private const int minimumCollectionTrigger = (1 << 25); // 32 MB +#else + private const int minimumCollectionTrigger = (1 << 24); // 16 MB +#endif + private const int maximumCollectionTrigger = (1 << 26); // 64 MB + + /// + /// Amount of memory to allocate between collections. + /// BUGBUG: This heuristic is not very good for CMS. + /// + private static UIntPtr collectionTrigger; + + internal static ConcurrentMSCollector instance; + + // Pointer visitors used for marking, etc + internal static MarkReferenceVisitor markReferenceVisitor; + internal static UpdateReferenceVisitor updateReferenceVisitor; + internal static NonNullReferenceVisitor stackMarkReferenceVisitor; + internal static NonNullReferenceVisitor stackMarkPinnedReferenceVisitor; + internal static UnmarkVisitor unmarkVisitor; + + // Object visitors used for sweeping, etc + internal static SweepVisitor sweepVisitor; + + // What to do when we encounter a partially free page + internal static + SegregatedFreeList.PartialFreePageVisitor partialFreePageVisitor; + + // Which threads are in what marking stage? + private static UIntPtr[] threadColor; + + // Threads waiting for a particular collection to finish + private static int[] waitingThreads; + private static int firstWaitingThread; + private const int waitingThreadsNullValue = -1; + private const int waitingThreadsUnusedValue = -2; + + private const uint noColor = 0xffffffff; + + // The threads that performs the collection work. MUST MAKE + // SURE THAT THESE ARE PINNED IN COCO. Currently that's OK + // because all threads are pinned, for other reasons. + [NoBarriers] + private static Thread markThread; + [NoBarriers] + private static Thread workerThread1; + [NoBarriers] + private static Thread workerThread2; + + private static ulong totalTraceTime; + private static ulong totalSTWTraceTime; + private static ulong totalSweepTime; + private static ulong numTraces; + private static ulong numSTWTraces; + private static ulong numSweeps; + + private static AutoResetEvent sweepPhaseMutex; + + // A trivial handshake executes no code other than clearing the + // GCRequest. A trivial handshake's only purpose is to indicicate + // that the mutator is not in the middle of a "complex" GC operation + // such as a barrier operation or unlinking an element from a list. + internal static bool TrivialHandshake; + + internal static bool IncludeMUWInHandshake; + + /// + /// Does this collector compute a root set with threads running? + /// + internal override bool IsOnTheFlyCollector { + get { + return true; + } + } + + [NoBarriers] + [PreInitRefCounts] + internal static void InitializeAllButVisitors() + { + SegregatedFreeList.Initialize(); + unmarkedColor = (UIntPtr) markOne; + markedColor = (UIntPtr) markTwo; + reclamationColor = (UIntPtr) markThree; + // threadColor = new UIntPtr[Thread.maxThreads]; + threadColor = (UIntPtr[]) + BootstrapMemory.Allocate(typeof(UIntPtr[]), Thread.maxThreads); + for (int i = 0; i < threadColor.Length; i++) { + threadColor[i] = (UIntPtr) noColor; + } + TrivialHandshake = true; + collectionTrigger = (UIntPtr) minimumCollectionTrigger; + IncludeMUWInHandshake = true; + CMSMarking.currentMarkingPhase = (int) MarkingPhase.Idle; + CurrentReclamationPhase = ReclamationPhase.Idle; +#if SINGULARITY + infiniteWait = TimeSpan.Infinite; + minimumWait = TimeSpan.FromMilliseconds(1); +#else + infiniteWait = Timeout.Infinite; + minimumWait = 1; +#endif + } + + /// + /// Initialise the collector and allow allocations to commence. + /// + [PreInitRefCounts] + public static void Initialize() { + InitializeAllButVisitors(); + // instance = new ConcurrentMSCollector(); + ConcurrentMSCollector.instance = (ConcurrentMSCollector) + BootstrapMemory.Allocate(typeof(ConcurrentMSCollector)); + // markReferenceVisitor = new MarkReferenceVisitor(); + markReferenceVisitor = (MarkReferenceVisitor) + BootstrapMemory.Allocate(typeof(MarkReferenceVisitor)); + // updateReferenceVisitor = new UpdateReferenceVisitor(); + updateReferenceVisitor = (UpdateReferenceVisitor) + BootstrapMemory.Allocate(typeof(UpdateReferenceVisitor)); + // stackMarkReferenceVisitor = new StackMarkReferenceVisitor(); + stackMarkReferenceVisitor = (StackMarkReferenceVisitor) + BootstrapMemory.Allocate(typeof(StackMarkReferenceVisitor)); + stackMarkPinnedReferenceVisitor = stackMarkReferenceVisitor; + unmarkVisitor = (UnmarkVisitor) + BootstrapMemory.Allocate(typeof(UnmarkVisitor)); + // sweepVisitor = new SweepVisitor(); + sweepVisitor = (SweepVisitor) + BootstrapMemory.Allocate(typeof(SweepVisitor)); + // set the null partial free page visitor + partialFreePageVisitor = + SegregatedFreeList.nullPartialFreePageVisitor; + } + + internal override void Collect(Thread currentThread, int generation) + { + try { + if (!GC.IsUserCollectionRequest(generation) && + Transitions.HasGCRequest(currentThread.threadIndex) && + TrivialHandshake) { + // Someone asked for a handshake, and it is a trivial + // handshake, so there is no need to go through + // the overhead of saving and restoring the callee-save + // registers in CollectBodyTransition. + Transitions.ClearGCRequest(currentThread.threadIndex); + // BUGBUG: consider restoring the following + // markThread.SignalEvent(); + } else { + GC.CollectTransition(currentThread, generation); + } + } catch (Exception e) { + VTable.DebugPrint("Garbage collection failed with exception"); + VTable.DebugPrint(e.GetType().Name); + VTable.DebugBreak(); + } + } + + /// + /// Perform a collection. Depending on the current phase of collection + /// this method will either: + /// + /// 1. Start a new collection and schedule the mark thread + /// 2. Notice that a collection is underway and exit + /// 3. Clean up after a collection + /// + /// BUGBUG: The interaction between the collector needs work! + /// + internal override void CollectStoppable(int currentThreadIndex, + int generation) + { + if (!GC.IsUserCollectionRequest(generation) && + Transitions.HasGCRequest(currentThreadIndex)) { + // We are GC safe, so we may do this + Transitions.TakeDormantControlNoGC(currentThreadIndex); + if (Transitions.TakeGCControl(currentThreadIndex)) { + if (UseSTWTracingPhase && + CurrentMarkingPhase == MarkingPhase.StopTheWorld) { + Thread.SignalGCEvent(markThread.threadIndex); + } else { + MutatorHandshake(currentThreadIndex); + Transitions.ReleaseGCControl(currentThreadIndex); + // BUGBUG: consider restoring the following + // markThread.SignalEvent(); + } + } + Transitions.TakeMutatorControlNoGC(currentThreadIndex); + } else if (GC.IsRealCollectionRequest(generation)) { + if (generation >= 0) { + AddThreadToWaitList(currentThreadIndex); + } + AddCollectionRequest(); + if (generation >= 0) { + Thread currentThread = + Thread.threadTable[currentThreadIndex]; + while (ThreadIsWaiting(currentThreadIndex)) { + // NOTE: if there was a GC request and this was a + // user-requested collection, then the stack scanning + // will happen in here. + // NOTE: there is no code in this loop that could + // cause a signal on an event to be consumed. + currentThread.WaitForEvent(infiniteWait); + } + } + } + } + + internal override void CheckForNeededGCWork(Thread currentThread) { + if (Transitions.HasGCRequest(currentThread.threadIndex)) { + GC.InvokeCollection(currentThread); + } + } + + internal static void CollectorHandshake(Thread collectorThread) + { + Transitions.MakeGCRequests(collectorThread.threadIndex); + // Handshake with all the (other) threads. + for(int i=0; i < Thread.threadTable.Length; i++) { +#if SINGULARITY_KERNEL + if (Scheduler.IsIdleThread(i)) { + continue; + } +#endif + // Is there an unscanned thread here? + while (Transitions.HasGCRequest(i)) { + if (Transitions.TakeGCControl(i)) { + MutatorHandshake(i); + Transitions.ReleaseGCControl(i); + } else if (Thread.threadTable[i] == null) { + // The thread must have terminated but the + // state hasn't yet changed to DormantState. + break; + } else { + // NOTE: there is no code in this loop that could + // cause a signal on an event to be consumed without + // being putting back. + collectorThread.WaitForEvent(minimumWait); + } + } + } + + if (ConcurrentMSCollector.IncludeMUWInHandshake && + !ConcurrentMSCollector.TrivialHandshake && + !UseSTWTracingPhase) { + MultiUseWord.CollectFromPreviousCollections(false); + } + } + + private static void MutatorHandshake(int threadIndex) + { +#if SINGULARITY_KERNEL + Microsoft.Singularity.Processor currentProcessor = + Microsoft.Singularity.Processor.CurrentProcessor; + if (currentProcessor.InInterruptContext) { + Tracing.Log(Tracing.Fatal, "Attempt to perform a collector handshake from an interrupt context!"); + VTable.DebugPrint("Attempt to perform a collector handshake from withing an interrupt context"); + VTable.DebugBreak(); + } +#endif + if (!ConcurrentMSCollector.TrivialHandshake) { + Thread thread = Thread.threadTable[threadIndex]; + if (thread != null) { + ScanThreadRoots(thread); + if (IncludeMUWInHandshake && !UseSTWTracingPhase) { + MultiUseWord.CollectFromThread(thread); + } + } + } + } + + private static void ScanThreadRoots(Thread thread) { + long start = CollectorStatistics.PerformanceCounter; + CollectorStatistics.Event(GCEvent.StackScanStart, + thread.threadIndex); + CallStack.ScanStack(thread, stackMarkReferenceVisitor, + stackMarkPinnedReferenceVisitor); + threadColor[thread.threadIndex] = markedColor; + // Report the pause + long pause = + CollectorStatistics.PerformanceCounter - start; + CollectorStatistics.Event(GCEvent.StackScanComplete, + pause); + } + + internal static bool terminateCollectorThreads; + + // REVIEW: merge this with terminateCollectorThreads, maybe + internal static bool killCollectorThreads; /* non-Singularity thread killage */ + + internal static void TraceThreadNotification() { + terminateCollectorThreads = true; + GC.Collect(); + } + + /// + /// This method is run by the collector threads. + /// + private static void CollectionTask() { + try { + CollectionLoop(); + } catch (Exception e) { + VTable.DebugPrint("Collection task failed with exception "); + VTable.DebugPrint(e.GetType().Name); + VTable.DebugBreak(); + } + } + + private static void CollectionLoop() { +#if !SINGULARITY + if (fDebug) { + VTable.DebugPrint("cms thread = "); + VTable.DebugPrint((ulong)Win32Native.GetCurrentThreadId()); + VTable.DebugPrint("\n"); + } +#endif + + Thread currentThread = Thread.CurrentThread; +#if SINGULARITY_PROCESS + currentThread.MakeServiceThread(new Thread.StopServiceNotice(TraceThreadNotification)); +#endif + while (!terminateCollectorThreads && !killCollectorThreads) { + // Wait to be told to start working. + while (!TakeChargeOfTraceRequest() && !killCollectorThreads) { + // NOTE: there is no code in this loop that could + // cause a signal on an event to be consumed. + currentThread.WaitForEvent(infiniteWait); + } + if (killCollectorThreads) { + break; + } + StartGCCycle(); + instance.PreTraceHook(); +#if SINGULARITY + DebugStub.WriteLine("~~~~~ Start Concurrent Marking [data={0:x8}, pid={1:x3}]", + __arglist(SegregatedFreeList.TotalBytes, + PageTable.processTag >> 16)); +#endif + if (fDebug) { + VTable.DebugPrint("~~~~~ Start Concurrent Marking\n"); + } + int startTicks = Environment.TickCount; + if (GC.IsProfiling) { + GcProfiler.NotifyPreGC(instance.MinGeneration); + } + markThread = currentThread; + advanceMarkColors(); + MarkReferenceVisitor.markThread = currentThread; + // Construct the root set. + CollectorStatistics.Event(GCEvent.ComputeRootSet); + StartRootMarkingPhase(); + // Start the process of recycling pages + partialFreePageVisitor.Start(); + SegregatedFreeList + .RecycleGlobalPagesPhase1(partialFreePageVisitor); + // One handshake to ensure that everyone starts snooping + CollectorHandshake(currentThread); + // Complete the process of recycling pages + SegregatedFreeList + .RecycleGlobalPagesPhase2(partialFreePageVisitor); + partialFreePageVisitor.Finish(); + // Another handshake to ensure that all updates started by + // other threads prior to their handshake are done and + // snooping will affect all new updates. + CollectorHandshake(currentThread); + // A third handshake to get the threads to process their + // own roots. The collector thread needs to scan its + // roots, too. + ConcurrentMSCollector.TrivialHandshake = false; + CollectorHandshake(currentThread); + // In order to scan the call stack of the current thread, + // we need a TransitionRecord for the thread. At this + // point we don't have one, so we have to go through + // CollectBodyTransition to get one. + Transitions.MakeGCRequest(currentThread.threadIndex); + GC.InvokeCollection(currentThread); + ConcurrentMSCollector.TrivialHandshake = true; + Finalizer.PrepareCollectFinalizers(); + Thread.VisitBootstrapData(markReferenceVisitor); +#if SINGULARITY_KERNEL + Kernel.VisitSpecialData(markReferenceVisitor); +#endif + MultiUseWord.VisitStrongRefs(markReferenceVisitor, + false /* Don't use shadows */); +#if !VC + TryAllManager.VisitStrongRefs(markReferenceVisitor); +#endif + instance.PostRootScanHook(); + StaticData.ScanStaticData(markReferenceVisitor); + CollectorStatistics.Event(GCEvent.RootSetComputed); + int waitingThreadList = StealWaitingThreadsList(); + // We are now in the concurrent tracing phase. + ResetCollectorRequests(); + StartTracingPhase(); + markReferenceVisitor.Init(); + CollectorStatistics.Event(GCEvent.TraceStart, + ReservedMemory); + // Trace live objects from the root set. + markReferenceVisitor.ProcessGrayObjects(); + if (killCollectorThreads) { + break; + } + // If we want to use a STW phase to redo the tracing (for + // measurement purposes, most likely), now is the time to + // do so. + if (UseSTWTracingPhase) { + int preSTWTrace = Environment.TickCount; + StartSTWPhase(); + bool oldTrivialHandshake = TrivialHandshake; + TrivialHandshake = false; + BaseCollector.AllThreadRendezvous(markThread.threadIndex); + TrivialHandshake = oldTrivialHandshake; + STWInitialize(); + STWScanStacks(stackMarkReferenceVisitor, + stackMarkPinnedReferenceVisitor); + Finalizer.PrepareCollectFinalizers(); + Thread.VisitBootstrapData(markReferenceVisitor); +#if SINGULARITY_KERNEL + Kernel.VisitSpecialData(markReferenceVisitor); +#endif + MultiUseWord.VisitStrongRefs(markReferenceVisitor, + false /* Don't use shadows */); +#if !VC + TryAllManager.VisitStrongRefs(markReferenceVisitor); +#endif + StaticData.ScanStaticData(markReferenceVisitor); + markReferenceVisitor.Init(); + markReferenceVisitor.ProcessGrayObjects(); + BaseCollector.AllThreadRelease(markThread.threadIndex); + FinishSTWPhase(); + int postSTWTrace = Environment.TickCount; + totalSTWTraceTime += (ulong)(postSTWTrace-preSTWTrace); + numSTWTraces ++; + } + CollectorStatistics.Event(GCEvent.TraceSpecial); + // Mark weak references that do not track resurrection as dead. + WeakReference.Process(updateReferenceVisitor, true, true); + // Resurrect any finalization candidates. + Finalizer.ResurrectCandidates(updateReferenceVisitor, + markReferenceVisitor, true); + // Complete closure from finalized objects. + markReferenceVisitor.ProcessGrayObjects(); + if (killCollectorThreads) { + break; + } + // Mark appropriate weak references as dead + WeakReference.Process(updateReferenceVisitor, true, false); + MultiUseWord.VisitWeakRefs(updateReferenceVisitor, + false /* Don't use shadows */); +#if !VC + TryAllManager.VisitWeakRefs(updateReferenceVisitor); +#endif + MultiUseWord.PostGCHook(); + // Reset thread queues. They should all be empty. + markReferenceVisitor.Cleanup(); + int middleTicks = Environment.TickCount; + totalTraceTime += (ulong)(middleTicks - startTicks); + numTraces ++; +#if SINGULARITY + DebugStub.WriteLine("~~~~~ Finish Concurrent Marking [data={0:x8}, pid={1:x3} ms={2:d6}]", + __arglist(SegregatedFreeList.TotalBytes, + PageTable.processTag >> 16, + middleTicks - startTicks)); +#endif + if (fDebug) { + VTable.DebugPrint("~~~~~ Finish Concurrent Marking\n"); + } + instance.PostTraceHook(); + markThread = nextWorkerThread(currentThread); + sweepPhaseMutex.WaitOne(); + try { + reclamationColor = unmarkedColor; + FinishTracingPhase(); + SatisfyCollectorRequest(); // May start another trace phase + // Sweep garbage objects + StartReclamationPhase(); +#if SINGULARITY + DebugStub.WriteLine("~~~~~ Start Concurrent Reclamation [data={0:x8}, pid={1:x3}]", + __arglist(SegregatedFreeList.TotalBytes, + PageTable.processTag >> 16)); +#endif + if (fDebug) { + VTable.DebugPrint("~~~~~ Start Concurrent Reclamation\n"); + } + CollectorStatistics.Event(GCEvent.SweepStart, + ReservedMemory); + instance.PreSweepHook(); + Sweep(); + if (killCollectorThreads) { + break; + } + instance.PostSweepHook(); + // Clean up after the collection + CollectorStatistics.Event(GCEvent.SweepSpecial, + ReservedMemory); + Finalizer.ReleaseCollectFinalizers(); + if (GC.IsProfiling) { + // Allocations may occur inside the PostGCHook. Hopefully a + // sufficiently limited quantity that we don't recursively + // trigger a GC. + GcProfiler.NotifyPostGC(ProfileRoots, ProfileObjects); + } + CollectorStatistics.Event(GCEvent.SweepPreCommit, + ReservedMemory); + // Commit accounting changes + CommitSweep(); + CollectorStatistics.Event(GCEvent.CollectionComplete, + ReservedMemory); + // Determine a new collection trigger + UIntPtr testTrigger = (UIntPtr) ReservedMemory >> 2; + UIntPtr minTrigger = (UIntPtr) minimumCollectionTrigger; + UIntPtr maxTrigger = (UIntPtr) maximumCollectionTrigger; + collectionTrigger = + (testTrigger > minTrigger) ? + (testTrigger < maxTrigger ? + testTrigger : maxTrigger) : minTrigger; + // Finish up and wake up any GC requesting threads. + SignalWaitingThreads(waitingThreadList); + FinishReclamationPhase(); + } finally { + sweepPhaseMutex.Set(); + } + instance.PostReclamationHook(); + int endTicks = Environment.TickCount; + totalSweepTime += (ulong)(endTicks - middleTicks); + numSweeps ++; +#if SINGULARITY + DebugStub.WriteLine("~~~~~ Finish Concurrent Reclamation [data={0:x8}, pid={1:x3} ms={2:d6}]", + __arglist(ReservedMemory, + PageTable.processTag >> 16, + endTicks - middleTicks)); +#endif + if (fDebug) { + VTable.DebugPrint("~~~~~ Finish Concurrent Reclamation\n"); + } + if (VTable.enableDumpMemStats) { + VTable.DebugPrint("Heap mem usage: {0:x8}\n", + __arglist(ReservedMemory)); + } + } + } + + private static void STWInitialize() + { + for (int i = 0; i < Thread.threadTable.Length; i++) { + Thread t = Thread.threadTable[i]; + if (t != null) { + ThreadHeaderQueue.Reset(t); + } + } + SegregatedFreeList.VisitAllObjects(unmarkVisitor); + } + + private static + void STWScanStacks(NonNullReferenceVisitor VisitThreadReference, + NonNullReferenceVisitor VisitPinnedReference) + { + for (int i = 0; i < Thread.threadTable.Length; i++) { + Thread t = Thread.threadTable[i]; + if (t != null) { + CallStack.ScanStack(t, VisitThreadReference, + VisitPinnedReference); + if (IncludeMUWInHandshake) { + MultiUseWord.CollectFromThread(t); + } + } + } + if (IncludeMUWInHandshake) { + MultiUseWord.CollectFromPreviousCollections(false); + } + } + + internal virtual void PreTraceHook() + { + } + + internal virtual void PostRootScanHook() + { + } + + internal virtual void PostTraceHook() + { + } + + internal virtual void PostReclamationHook() + { + } + + internal virtual void PreSweepHook() + { + } + + internal virtual void PostSweepHook() + { + } + + /// + /// Allocate memory for a new object, potentially triggering a + /// collection. + /// + [Inline] + internal override UIntPtr AllocateObjectMemory(UIntPtr numBytes, + uint alignment, + Thread currentThread) + { + UIntPtr resultAddr = + SegregatedFreeList.AllocateFast(currentThread, + numBytes, alignment); + if (resultAddr == UIntPtr.Zero) { + resultAddr = AllocateObjectMemorySlow(numBytes, alignment, + currentThread); + } + return resultAddr; + } + + [NoInline] + internal virtual UIntPtr AllocateObjectMemorySlow(UIntPtr numBytes, + uint alignment, + Thread currentThread) + { + if (Transitions.HasGCRequest(currentThread.threadIndex)) { + GC.InvokeCollection(currentThread); + } else if (NewBytesSinceGCExceeds(collectionTrigger) && + GC.allocationGCInhibitCount == 0) { + if (CurrentMarkingPhase == MarkingPhase.Idle) { + GC.InvokeCollection(currentThread); + } else if (currentThread!=workerThread1 && + currentThread!=workerThread2 && + NewBytesSinceGCExceeds(collectionTrigger<<1)) { + // Slow down the allocating thread a bit + if (UseSleepThrottle && + (NewBytesSinceGCExceeds((collectionTrigger<<3)+ + (collectionTrigger<<2)) || + TotalMemory > (1<<30)+(1<<29))) { + // Context switches didn't help, so let's try + // suspending ourselves for a bit. + Thread.Sleep(1); + } else if (UseYieldThrottle) { + // If there are more threads than processors, + // forcing a context switch should slow down + // the mutator thread. + Thread.Yield(); + } + } + } + return SegregatedFreeList.AllocateSlow(currentThread, + numBytes, alignment); + } + + [NoInline] + [CalledRarely] + protected void MarkOnAlloc(Thread currentThread, + Object obj) + { + ThreadHeaderQueue.PushPrivateObject(currentThread, + obj, markedColor); + } + + [Inline] + protected override void CreateObject(Object obj, VTable vtable, + Thread currentThread) + { + // We expect the color to be assigned before the vtable field + // is initialized. This ensures that every real object has a + // valid color. + UIntPtr markBits = threadColor[currentThread.threadIndex]; + ThreadHeaderQueue.SetGcMark(obj, markBits); + // The vtable field must be initialized before the object is + // inserted into a list of objects to be scanned. + base.CreateObject(obj, vtable, currentThread); + // If necessary, mark the object for future scanning + if (CurrentMarkingPhase == MarkingPhase.ComputingRoots) { + MarkOnAlloc(currentThread, obj); + } + + ProfileAllocation(obj); + } + + /// + /// Return the generation for an object. We only have one + /// generation, so we always return generation zero. + /// + internal override int GetGeneration(Object obj) { + Verifier.genericObjectVisitor.Visit(obj); + return MinGeneration; + } + + /// + /// The maximum generation. For MarkSweep this is generation zero. + /// + internal override int MaxGeneration { + get { return (int)PageType.Owner0; } + } + + /// + /// The minimum generation. For MarkSweep this is generation zero. + /// + internal override int MinGeneration { + get { return (int)PageType.Owner0; } + } + + /// + /// This returns the total amount of memory that is allocated within + /// the collected heap. + /// + internal override long TotalMemory { + get { + return ReservedMemory; + } + } + + private static long ReservedMemory { + get { + return (long)(SegregatedFreeList.TotalPages*PageTable.PageSize); + } + } + + internal static int GetEnvironmentValue(String name, int defaultValue) + { + String valueString = null; +#if !SINGULARITY + valueString = Environment.GetEnvironmentVariable(name); +#endif + if (valueString == null) { + return defaultValue; + } else { + return Int32.Parse(valueString); + } + } + + internal override void EnableHeap() { + // waitingThreads = new int[Thread.maxThreads] + waitingThreads = new int[Thread.maxThreads]; + for (int i = 0; i < waitingThreads.Length; i++) { + waitingThreads[i] = waitingThreadsUnusedValue; + } + firstWaitingThread = -1; + // Construct the collector thread(s) +#if SINGULARITY_KERNEL + workerThread1 = + Thread.CreateThread(Process.kernelProcess, + new ThreadStart(CollectionTask)); + if (UseOverlappingPhases) { + workerThread2 = + Thread.CreateThread(Process.kernelProcess, + new ThreadStart(CollectionTask)); + } +#else + workerThread1 = new Thread(new ThreadStart(CollectionTask)); + if (UseOverlappingPhases) { + workerThread2 = new Thread(new ThreadStart(CollectionTask)); + } +#endif + workerThread1.Start(); + if (UseOverlappingPhases) { + workerThread2.Start(); + } + markThread = workerThread1; + sweepPhaseMutex = new AutoResetEvent(true); + } + + internal override void Shutdown() + { + if (GC.IsProfiling) { + GcProfiler.NotifyShutdown(); + } + killCollectorThreads = true; + workerThread1.SignalEvent(); + if (workerThread2!=null) { + workerThread2.SignalEvent(); + } + workerThread1.Join(); + if (workerThread2!=null) { + workerThread2.Join(); + } + } + + /// + /// Destroy the heap. Nothing to do here. + /// + internal override void DestructHeap() + { + base.DestructHeap(); + if (VTable.enableFinalGCTiming) { + VTable.DebugPrint("total trace time = "); + VTable.DebugPrint((long) totalTraceTime); + VTable.DebugPrint("\n"); + VTable.DebugPrint("total sweep time = "); + VTable.DebugPrint((long) totalSweepTime); + VTable.DebugPrint("\n"); + VTable.DebugPrint("num traces = "); + VTable.DebugPrint((long) numTraces); + VTable.DebugPrint("\n"); + VTable.DebugPrint("num sweeps = "); + VTable.DebugPrint((long) numSweeps); + VTable.DebugPrint("\n"); + } + } + + /// + /// Verify the heap. + /// + internal override void VerifyHeap(bool beforeCollection) { + SegregatedFreeList.VisitAllObjects(VerifyVisitor.visitor); + Verifier.segregatedFreeListVerifier.VerifyHeap(); + } + + private static char ToHexDigit(int number, int position) { + int digit = (number >> (position * 4)) & 0xf; + return (char) (digit + ((digit <= 9) ? '0' : ('A' - 10))); + } + private static int flag; // = 0; + private static void DebugTrace(String text, int threadIndex) { + while (Interlocked.CompareExchange(ref flag, 1, 0) != 0) { } + VTable.DebugPrint(text+" "+ + ToHexDigit(threadIndex, 2)+ + ToHexDigit(threadIndex, 1)+ + ToHexDigit(threadIndex, 0)+ + "\n"); + Interlocked.Exchange(ref flag, 0); + } + + /// Routines to keep track of requests for collection work + + private static int collectorStack; // 0:idle, 1:work, 2+:work+pending + + internal static void AddCollectionRequest() { + int stackHeight = Interlocked.Increment(ref collectorStack); + VTable.Assert(stackHeight > 0); + if (stackHeight == 1) { + MakeTraceRequest(); + } + } + + private static void ResetCollectorRequests() { + Interlocked.Exchange(ref collectorStack, 1); + } + + private static void SatisfyCollectorRequest() { + int stackHeight = Interlocked.Decrement(ref collectorStack); + VTable.Assert(stackHeight >= 0); + if (stackHeight > 0 ) { + MakeTraceRequest(); + } + } + + /// Routines to keep track of threads that must be notified when + /// a collection has been completed. + + private static void AddThreadToWaitList(int threadIndex) { + int listHead = firstWaitingThread; + waitingThreads[threadIndex] = listHead; + while (Interlocked.CompareExchange(ref firstWaitingThread, + threadIndex, listHead) != + listHead) { + listHead = firstWaitingThread; + waitingThreads[threadIndex] = listHead; + } + } + + private static bool ThreadIsWaiting(int threadIndex) { + return (waitingThreads[threadIndex] != waitingThreadsUnusedValue); + } + + private static int StealWaitingThreadsList() { + return Interlocked.Exchange(ref firstWaitingThread, + waitingThreadsNullValue); + } + + private static void SignalWaitingThreads(int listHead) { + while (listHead != waitingThreadsNullValue) { + int threadIndex = listHead; + listHead = waitingThreads[threadIndex]; + waitingThreads[threadIndex] = waitingThreadsUnusedValue; + Thread.threadTable[threadIndex].SignalEvent(); + } + } + + /// Routines to control the commencement of the tracing phase + + private static void MakeTraceRequest() { + MarkingPhase oldPhase = (MarkingPhase) + Interlocked.Exchange(ref CMSMarking.currentMarkingPhase, + (int) MarkingPhase.Requested); + VTable.Assert(oldPhase == MarkingPhase.Idle); + markThread.SignalEvent(); + } + + private static bool TakeChargeOfTraceRequest() { + MarkingPhase oldPhase = (MarkingPhase) + Interlocked.CompareExchange(ref CMSMarking.currentMarkingPhase, + (int) MarkingPhase.Preparing, + (int) MarkingPhase.Requested); + return (oldPhase == MarkingPhase.Requested); + } + + // Routines to keep track of what phases the collector threads are in. + + private static void StartRootMarkingPhase() { + CMSMarking.referenceCheckIsFast = false; + MarkingPhase oldPhase = (MarkingPhase) + Interlocked.Exchange(ref CMSMarking.currentMarkingPhase, + (int) MarkingPhase.ComputingRoots); + VTable.Assert(oldPhase == MarkingPhase.Preparing); + } + + private static void StartTracingPhase() { + MarkingPhase oldPhase = (MarkingPhase) + Interlocked.Exchange(ref CMSMarking.currentMarkingPhase, + (int) MarkingPhase.Tracing); + VTable.Assert(oldPhase == MarkingPhase.ComputingRoots); + } + + private static void StartSTWPhase() { + MarkingPhase oldPhase = (MarkingPhase) + Interlocked.Exchange(ref CMSMarking.currentMarkingPhase, + (int) MarkingPhase.StopTheWorld); + VTable.Assert(oldPhase == MarkingPhase.Tracing); + } + + private static void FinishSTWPhase() { + MarkingPhase oldPhase = (MarkingPhase) + Interlocked.Exchange(ref CMSMarking.currentMarkingPhase, + (int) MarkingPhase.Tracing); + VTable.Assert(oldPhase == MarkingPhase.StopTheWorld); + } + + private static void FinishTracingPhase() { + MarkingPhase oldPhase = (MarkingPhase) + Interlocked.Exchange(ref CMSMarking.currentMarkingPhase, + (int) MarkingPhase.Idle); + VTable.Assert(oldPhase == MarkingPhase.Tracing); + CMSMarking.referenceCheckIsFast = true; + } + + private static void StartReclamationPhase() { + CurrentReclamationPhase = ReclamationPhase.Reclaiming; + } + + private static void FinishReclamationPhase() { + CurrentReclamationPhase = ReclamationPhase.Idle; + } + + // Routines to manage marking colors + + private static void advanceMarkColors() + { + unmarkedColor = markedColor; + markedColor = nextColor(markedColor); + } + + private static UIntPtr nextColor(UIntPtr originalColor) + { + switch ((uint) originalColor) { + case markOne: return (UIntPtr) markTwo; + case markTwo: return (UIntPtr) markThree; + case markThree: return (UIntPtr) markOne; + default: throw new Exception("nextColor failure!"); + } + } + + private static Thread nextWorkerThread(Thread currentThread) { + if (UseOverlappingPhases) { + return ((currentThread == workerThread1) ? + workerThread2 : + workerThread1); + } else { + return currentThread; + } + } + + /// + /// Walk the allocation structures and reclaim any free cells. + /// + [NoInline] + private static void Sweep() { + SegregatedFreeList.VisitAllObjects(sweepVisitor); + } + + /// + /// Update alloc heap to account for data just freed. + /// + private static void CommitSweep() { + SegregatedFreeList.CommitFreedData(); + } + + // This is a quick-and-dirty check of whether something is an object. + // All objects have a vtable. A VTable is an object that itself has a + // vtable. All VTable objects have the same vtable, which we rely + // upon for this fast check. This predicate will gracefully fail in + // corrupted memory situations where a real type check will cause a + // catastrophic failure. + private static bool IsPossiblyObject(Object obj) + { + // First, check for a null reference + if (obj == null) { return false; } + // Second, check for a null vtable + VTable vtable = obj.vtable; + if (vtable == null) { return false; } + // Third, check for a null vtable of the vtable + VTable vtableVtable = vtable.vtable; + if (vtableVtable == null) { return false; } + // Finally, rely on the invariant that all vtables have the + // same vtable. + return (vtableVtable == vtableVtable.vtable); + } + + /// + /// Routines for updating pointers to new locations of marked objects + /// No objects are resurrected using this mechanism. + /// As this is mark sweep the value does not need to be updated. + /// + internal class UpdateReferenceVisitor: MutableReferenceVisitor + { + + internal virtual UIntPtr ForwardIfNecessary(UIntPtr addr) + { + return addr; + } + + internal unsafe override void Visit(UIntPtr *loc) { + while (true) { + UIntPtr foundAddr = *loc; + // Ignore pointers out of our memory area + if (foundAddr == UIntPtr.Zero || + PageTable.IsForeignAddr(foundAddr)) { + return; + } + UIntPtr page = PageTable.Page(foundAddr); + PageType pageType = PageTable.Type(page); + if (!PageTable.IsGcPage(pageType)) { + VTable.Assert((PageTable.IsNonGcPage(pageType) && + PageTable.IsMyPage(page)) || + PageTable.IsStackPage(pageType), + "update.visit invalid page"); + return; + } + VTable.Assert(PageTable.IsMyPage(page)); + UIntPtr forwardedAddr = ForwardIfNecessary(foundAddr); + Object obj = Magic.fromAddress(forwardedAddr); + VTable.Assert(IsPossiblyObject(obj), + "update visit: bad object/vtable"); + if (ThreadHeaderQueue.GcMark(obj) == unmarkedColor) { + // The object is not live + *loc = UIntPtr.Zero; + return; + } + // The object is alive, install forwarded value, + // if necessary. + if (foundAddr == forwardedAddr || + (Interlocked.CompareExchange(loc, forwardedAddr, + foundAddr) + == foundAddr)) { + return; + } + } + } + } + + /// + /// Select the generation to collect (always generation 0) + /// + internal override int CollectionGeneration(int genRequest) { + return MinGeneration; + } + + /// + /// This visitor is the core of the tracing functionality. + /// It builds up a buffer of references (the root set), and + /// then at a later point the tracing thread processes these + /// buffers. + /// + internal class MarkReferenceVisitor : MutableReferenceVisitor + { + [NoBarriers] + internal static Thread markThread; + + [Inline] + protected override unsafe + void Filter(UIntPtr *location, ref ObjectDescriptor objDesc) + { + this.Visit(location); + } + + [Inline] + internal override unsafe void Visit(UIntPtr *location) + { + this.VisitValueMaybeNull(*location); + } + + // This method simply forces the compiler to generate a copy + // of VisitReferenceFieldsTemplate in this class. + [ManualRefCounts] + [Inline] + internal override UIntPtr VisitReferenceFields(Object obj) + { + return this.VisitReferenceFields(Magic.addressOf(obj), + obj.vtable); + } + + // This method simply forces the compiler to generate a copy + // of VisitReferenceFieldsTemplate in this class. + [ManualRefCounts] + [Inline] + internal override + UIntPtr VisitReferenceFields(UIntPtr objectBase, VTable vtable) + { + ObjectDescriptor objDesc = + new ObjectDescriptor(vtable, objectBase); + return VisitReferenceFieldsTemplate(ref objDesc); + } + + [Inline] + internal void VisitValueNonNull(UIntPtr addr) + { + // that's a slow assertion to make! + //VTable.Assert(Thread.CurrentThread == markThread); + VisitValueNonNull(addr, markThread); + } + + [Inline] + internal void VisitValueMaybeNull(UIntPtr addr) + { + // that's a slow assertion to make! + //VTable.Assert(Thread.CurrentThread == markThread); + VisitValueMaybeNull(addr, markThread); + } + + [Inline] + internal void VisitValueAnyThreadMaybeNull(UIntPtr addr) + { + VisitValueMaybeNull(addr, Thread.CurrentThread); + } + + [Inline] + internal void VisitValueMaybeNull(UIntPtr addr, Thread currentThread) + { + // Ignore null pointers + if (addr == UIntPtr.Zero) { + return; + } + VisitValueNonNull(addr, currentThread); + } + + [Inline] + internal void VisitValueNonNull(UIntPtr addr, Thread currentThread) + { + // Ignore pointers to strange memory areas + if (PageTable.IsForeignAddr(addr)) { + return; + } + UIntPtr page = PageTable.Page(addr); + PageType pageType = PageTable.Type(page); + if (!PageTable.IsGcPage(pageType)) { + VTable.Assert((PageTable.IsNonGcPage(pageType) && + PageTable.IsMyPage(page)) || + PageTable.IsStackPage(pageType), + "value.visit invalid page"); + return; + } + VTable.Assert(PageTable.IsMyPage(page)); + Object obj = Magic.fromAddress(addr); + VTable.Assert(IsPossiblyObject(obj), + "mark visit: bad object/vtable"); + CMSMarking.MarkObject(addr, currentThread); + } + + /// + /// Process all marked objects from queues stored in + /// thread objects. + /// + internal virtual void ProcessGrayObjects() + { + ThreadHeaderQueue.LocalList workList = + new ThreadHeaderQueue.LocalList(); + while (!killCollectorThreads && AcquireWork(ref workList)) { + ProcessMyGrayObjects(ref workList); + } + } + + [NoBarriers] + internal virtual + void ProcessMyGrayObjects(ref ThreadHeaderQueue.LocalList workList) + { + while (!killCollectorThreads && !workList.IsEmpty()) { + // Pop the next value + Object obj = workList.Pop(markedColor); + // Visit Fields + this.VisitReferenceFields(obj); + } + } + + /// + /// Look through other threads and see if any have some values on + /// their queues that we can acquire. + /// + private bool AcquireWork(ref ThreadHeaderQueue.LocalList workList) + { + bool foundWork = false; + UIntPtr stealColor = (UIntPtr) markedColor; + do { + ThreadHeaderQueue.transferAttempt = false; + Thread[] threadTable = Thread.threadTable; + // Attempt to acquire work from live threads + for (int i = 0; i < threadTable.Length; i++) { + Thread thread = threadTable[i]; + if (thread != null && + workList.StealFrom(thread, stealColor)) { + foundWork = true; + } + } + } while (!foundWork && ThreadHeaderQueue.transferAttempt); + return foundWork; + } + + internal virtual void Init() + { + } + + /// + /// Clean up after processing all queues. This involves calling + /// reset on each thread's queue. + /// + internal virtual void Cleanup() + { + Thread[] threadTable = Thread.threadTable; + for (int i = 0; i < threadTable.Length; i++) { + Thread t = threadTable[i]; + if (t != null) { + VTable.Assert(ThreadHeaderQueue.IsEmpty(t)); + ThreadHeaderQueue.Reset(t); + } + } + } + + } + + /// + /// This class maps an interior pointer back to the containing object + /// pointer and then passes it on to the object marking visitor. + /// + internal class StackMarkReferenceVisitor : NonNullReferenceVisitor + { + + internal unsafe virtual void ProcessObjectPtr(UIntPtr objectPtr, + UIntPtr *loc, + UIntPtr addr) + { + markReferenceVisitor.VisitValueAnyThreadMaybeNull(objectPtr); + } + + /// + /// Visit an interior pointer stored in loc. + /// + internal unsafe override void Visit(UIntPtr *loc) { + UIntPtr addr = *loc; + // Ignore pointers out of our memory area + if (PageTable.IsForeignAddr(addr)) { + return; + } + UIntPtr page = PageTable.Page(addr); + PageType pageType = PageTable.Type(page); + if (!PageTable.IsGcPage(pageType)) { + VTable.Assert((PageTable.IsNonGcPage(pageType) && + PageTable.IsMyPage(page)) || + PageTable.IsStackPage(pageType) || + PageTable.IsSharedPage(pageType), + "interior.visit invalid page"); + return; + } + UIntPtr objectPtr = SegregatedFreeList.Find(addr); + VTable.Assert(IsPossiblyObject(Magic.fromAddress(objectPtr)), + "stack visit: bad object/vtable"); + ProcessObjectPtr(objectPtr, loc, addr); + } + + } + + /// + /// This class is used to visit every object in the heap + /// replacing markedColor with unmarkedColor. It is used + /// only in the StopTheWorld phase of the collector. + /// + internal class UnmarkVisitor : SegregatedFreeList.ObjectVisitor + { + internal override void VisitSmall(Object obj, UIntPtr memAddr) + { + if (ThreadHeaderQueue.GcMark(obj) == markedColor) { + ThreadHeaderQueue.SetGcMark(obj, unmarkedColor); + } + } + + internal override UIntPtr VisitLarge(Object obj) + { + if (ThreadHeaderQueue.GcMark(obj) == markedColor) { + ThreadHeaderQueue.SetGcMark(obj, unmarkedColor); + } + return ObjectLayout.Sizeof(obj); + } + } + + /// + /// This class is used to visit every object, determine if it + /// is marked and free it if not. + /// + internal class SweepVisitor : SegregatedFreeList.ObjectVisitor + { + + private SegregatedFreeList.TempList tempList; + + internal override void VisitSmall(Object obj, UIntPtr memAddr) + { + if (ThreadHeaderQueue.GcMark(obj) == reclamationColor) { + // Not marked. + tempList.Add(memAddr); + } + } + + internal override void VisitSmallPageEnd() { + SegregatedFreeList.FreeSmallList(ref tempList); + } + + internal override UIntPtr VisitLarge(Object obj) + { + UIntPtr objectSize = ObjectLayout.Sizeof(obj); + if (ThreadHeaderQueue.GcMark(obj) == reclamationColor) { + // Not marked. + SegregatedFreeList.FreeLarge(obj); + } + return objectSize; + } + + internal override bool Continue { + get { + return !killCollectorThreads; + } + } + } + + /// + /// Find the object address for a given interior pointer. + /// + internal override UIntPtr FindObjectAddr(UIntPtr interiorPtr) { + return SegregatedFreeList.Find(interiorPtr); + } + + /// + /// Visit all objects in the heap with a specified visitor. + /// + internal override + void VisitObjects(ObjectLayout.ObjectVisitor objectVisitor, + UIntPtr lowAddr, UIntPtr highAddr) + { + VTable.Assert(PageTable.PageAligned(lowAddr), + "low not page aligned"); + VTable.Assert(PageTable.PageAligned(highAddr), + "high not page aligned"); + UIntPtr lowPage = PageTable.Page(lowAddr); + UIntPtr highPage = PageTable.Page(highAddr); + SegregatedFreeList.VisitObjects(lowPage, highPage, objectVisitor); + } + + /// + /// A new thread has been created, set any allocator/collector state. + /// + internal override void NewThreadNotification(Thread newThread, + bool initial) + { + base.NewThreadNotification(newThread, initial); + threadColor[newThread.threadIndex] = markedColor; + ThreadHeaderQueue.Reset(newThread); + SegregatedFreeList.NewThreadNotification(newThread, initial); + if (CurrentMarkingPhase == MarkingPhase.ComputingRoots) { + Transitions.MakeGCRequest(newThread.threadIndex); + } + } + + internal override void DeadThreadNotification(Thread deadThread) + { + MultiUseWord.CollectFromThread(deadThread); + SegregatedFreeList.DeadThreadNotification(deadThread); + ThreadHeaderQueue.DeadThreadNotification(deadThread, markedColor); + threadColor[deadThread.threadIndex] = (UIntPtr) noColor; + base.DeadThreadNotification(deadThread); + } + + internal override void ThreadStartNotification(int currentThreadIndex) + { + base.ThreadStartNotification(currentThreadIndex); + threadColor[currentThreadIndex] = markedColor; + if (CurrentMarkingPhase == MarkingPhase.ComputingRoots) { + Transitions.MakeGCRequest(currentThreadIndex); + } + } + + internal override void ThreadDormantGCNotification(int threadIndex) { + // We could scan our own stack, but instead we try to get + // some work done while the Trace thread scans our stack. + if (UseSTWTracingPhase && + CurrentMarkingPhase == MarkingPhase.StopTheWorld) { + Thread.SignalGCEvent(markThread.threadIndex); + } + base.ThreadDormantGCNotification(threadIndex); + } + + /// + /// This class is used to verify that there are no dangling pointers. + /// + private class VerifyVisitor : SegregatedFreeList.ObjectVisitor + { + + internal static VerifyVisitor visitor = new VerifyVisitor(); + + internal override void VisitSmall(Object obj, UIntPtr memAddr) { + if (ThreadHeaderQueue.GcMark(obj) == markedColor) { + VerifyMarkVisitor.visitor.VisitReferenceFields(obj); + } else { + VTable.Assert(ThreadHeaderQueue.GcMark(obj) == + unmarkedColor); + } + } + + internal override UIntPtr VisitLarge(Object obj) { + UIntPtr size; + if (ThreadHeaderQueue.GcMark(obj) == markedColor) { + // The object has the mark color, so it should only + // reference other objects with the mark color. + size = VerifyMarkVisitor.visitor.VisitReferenceFields(obj); + } else { + VTable.Assert(ThreadHeaderQueue.GcMark(obj) == + unmarkedColor); + size = ObjectLayout.Sizeof(obj); + } + return size; + } + + } + + /// + /// This class is used to check that all the pointers within a marked + /// object point into other marked objects. + /// + private class VerifyMarkVisitor : MutableReferenceVisitor + { + + internal static VerifyMarkVisitor visitor + = new VerifyMarkVisitor(); + + internal unsafe override void Visit(UIntPtr *loc) { + UIntPtr addr = *loc; + UIntPtr page = PageTable.Page(addr); + if (PageTable.IsGcPage(page)) { + Object obj = Magic.fromAddress(addr); + VTable.Assert(ThreadHeaderQueue.GcMark(obj) == markedColor, + "dangling pointer!"); + } + } + + } + + /// + /// This method loops through all non-null threads and asserts that + /// no thread has any work on its marking queue. + /// + private static void VerifyEmptyQueues() { + Thread[] threadTable = Thread.threadTable; + for (int i = 0; i < threadTable.Length; i++) { + Thread t = threadTable[i]; + if (t != null) { + VTable.Assert(ThreadHeaderQueue.IsEmpty(t), + "Non-empty Queue!"); + } + } + } + + private static void VerifyResetQueues() { + Thread[] threadTable = Thread.threadTable; + for (int i = 0; i < threadTable.Length; i++) { + Thread t = threadTable[i]; + if (t != null) { + VTable.Assert(ThreadHeaderQueue.IsReset(t), + "Non-reset queue"); + } + } + } + + /// + /// This method walks through all objects in the heap to ensure + /// that no objects have values in their queue header field + /// + private static void VerifyQueueHeaders() { + SegregatedFreeList.VisitAllObjects(VerifyHeaderVisitor.visitor); + } + + /// + /// This visitor trivially asserts that the objects queue header + /// field is zero. + /// + private class VerifyHeaderVisitor : SegregatedFreeList.ObjectVisitor + { + + internal static VerifyHeaderVisitor visitor + = new VerifyHeaderVisitor(); + + /// + /// Visit small objects, checking queue header. + /// + internal unsafe override void VisitSmall(Object obj, + UIntPtr memAddr) + { + VTable.Deny(ThreadHeaderQueue.IsInQueue(obj), + "Object in ThreadHeaderQueue"); + } + + internal override UIntPtr VisitLarge(Object obj) { + VTable.Deny(ThreadHeaderQueue.IsInQueue(obj), + "Object in ThreadHeaderQueue"); + return ObjectLayout.Sizeof(obj); + } + + } + + private static bool fDebug { get { return false; } } + private static bool fDebugPhasing { get { return false; } } + } + +} diff --git a/base/Kernel/Bartok/GCs/CopyScan.cs b/base/Imported/Bartok/runtime/shared/GCs/CopyScan.cs similarity index 94% rename from base/Kernel/Bartok/GCs/CopyScan.cs rename to base/Imported/Bartok/runtime/shared/GCs/CopyScan.cs index bf3420a..6fa92e9 100644 --- a/base/Kernel/Bartok/GCs/CopyScan.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/CopyScan.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,9 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// namespace System.GCs { @@ -43,7 +44,7 @@ namespace System.GCs { internal Object Copy(Object obj) { UIntPtr size = ObjectLayout.Sizeof(obj); - if (GenerationalCollector.IsLargeObjectSize(size)) { + if (GenerationalGCData.IsLargeObjectSize(size)) { return CopyLarge(obj, size); } else { return CopySmall(obj, size); @@ -66,7 +67,7 @@ namespace System.GCs { "FwdRef: large object: {0} gen: {1} pagesize: {2}", __arglist(Magic.addressOf(obj), this.pageType, sizeOfPages)); - GenerationalCollector.gcPromotedTable[(int)this.pageType-1] += + GenerationalGCData.gcPromotedTable[(int)this.pageType-1] += sizeOfPages; this.AddWork(startAddr, startAddr + size); return obj; @@ -78,7 +79,7 @@ namespace System.GCs { UIntPtr newObjectAddr = this.allocator.AllocateFast(size, alignment); if (newObjectAddr == UIntPtr.Zero) { - int threadIndex = StopTheWorldCollector.collectorThreadIndex; + int threadIndex = StopTheWorldGCData.collectorThreadIndex; Thread thread = Thread.threadTable[threadIndex]; UIntPtr oldAllocNew = this.allocator.AllocNew; newObjectAddr = @@ -98,7 +99,7 @@ namespace System.GCs { this.pageType, size)); VTable.Assert(this.pageType == PageTable.Type(PageTable.Page(dstAddr))); - GenerationalCollector.gcPromotedTable[(int)this.pageType-1] += + GenerationalGCData.gcPromotedTable[(int)this.pageType-1] += size; Util.MemCopy(dstAddr, srcAddr, size); *obj.VTableFieldAddr = newObjectAddr; diff --git a/base/Imported/Bartok/runtime/shared/GCs/DeferredReferenceCountingCollector.cs b/base/Imported/Bartok/runtime/shared/GCs/DeferredReferenceCountingCollector.cs new file mode 100644 index 0000000..6369320 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/DeferredReferenceCountingCollector.cs @@ -0,0 +1,3328 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +// #define DEBUG +// #define MEASURE_RCPHASES + +namespace System.GCs { + + using Microsoft.Bartok.Options; + using Microsoft.Bartok.Runtime; + using System.Runtime.InteropServices; + using System.Runtime.CompilerServices; + using System.Threading; + using System.Collections; + + [NoCCtor] + internal class DeferredReferenceCountingCollector : + SingleThreadedCollector { + // This is a compiler intrinsic whose value is controlled by + // /StageControl.GCReferenceCountingVerifyRefCounts. + internal static extern bool VerificationMode { + [Intrinsic] + get; + } + + // This is a compiler intrinsic whose value is controlled by + // /StageControl.RCCollectorVerifyLeakedCycles. + internal static extern bool VerifyLeakedCycles { + [Intrinsic] + get; + } + + // This is a compiler intrinsic whose value is controlled by + // /StageControl.GCReferenceCountingGenerateProfile. + internal static extern bool ProfilingMode { + [Intrinsic] + get; + } + + [MixinConditional("DeferredReferenceCountingGC")] + [Mixin(typeof(PreHeader))] + [RequiredByBartok] + internal struct PreHeaderDeferredRCGC { + internal uint plcIndex; + } + + [MixinConditional("DeferredReferenceCountingGC")] + [Mixin(typeof(Object))] + internal class DeferredRCGCObject : System.Object { + internal new PreHeaderDeferredRCGC preHeader; + [RequiredByBartok] + internal new PostHeaderRC postHeader; + + internal new uint REF_STATE { + [Inline] + [ManualRefCounts] + [MixinOverride] + get { + return this.postHeader.refState; + } + [Inline] + [ManualRefCounts] + [MixinOverride] + set { + this.postHeader.refState = value; + } + } + } + + [MixinConditional("DeferredReferenceCountingGCVerification")] + [Mixin(typeof(PreHeader))] + [RequiredByBartok] + internal unsafe struct PreHeaderDeferredRCGCVerification { + internal UIntPtr backupRefCount; + internal UIntPtr dfsDiscoveryTime; + internal UIntPtr dfsFinishingTime; + } + + [MixinConditional("DeferredReferenceCountingGCVerification")] + [Mixin(typeof(Object))] + internal class DeferredRCVerificationObject : System.Object { + internal new PreHeaderDeferredRCGCVerification preHeader; + } + + [Inline] + [ManualRefCounts] + internal static uint GetPLCIndex(Object obj) { + return ((DeferredRCGCObject)obj).preHeader.plcIndex; + } + + [Inline] + [ManualRefCounts] + internal static void SetPLCIndex(Object obj, uint index) { + ((DeferredRCGCObject)obj).preHeader.plcIndex = index; + } + + /* + * Every object maintains a 32-bit "reference state" (RS). + * The RS consists of a marking bit, a bit flagging alignment, + * a bit flagging whether reference counting is enabled or + * disabled on the object, and a 29-bit reference count (RC). + * + * Given a virtual memory size of 2GB and an object size of + * at least 12 bytes (multi-use-word, RS field and the vtable), + * there can't be more than 2^28 objects. The reference count + * can be more, due to multiple references from an object + * to the same target, but will be less than 2^29. + */ + + internal const int markFlagBit = 31; + internal const int acyclicFlagBit = 30; + internal const int countingONFlagBit = 29; + + internal const uint markFlagMask = 1U << markFlagBit; + internal const uint acyclicFlagMask = 1U << acyclicFlagBit; + internal const uint countingONFlagMask = 1U << countingONFlagBit; + + internal const uint refCountMask = + ~(markFlagMask | acyclicFlagMask | countingONFlagMask); + + private static NonNullReferenceVisitor refCountIncrementer; + private static NonNullReferenceVisitor refCountDecrementer; + + internal static DeferredReferenceCountingCollector instance; + private static bool isInGC; + + private static Object delayedDeallocationList; + private static Object objectBeingDeallocated; + private static uint delayedDeallocationLength; + private static uint releasedObjectCount; + private static uint numCollections; + + private const uint collectionTrigger = 1 << 15; + private const uint recycleTrigger = collectionTrigger << 4; + private const int deallocationSpan = 1 << 20; + private const int numCollectionsTrigger = 8; + + private static NonNullReferenceVisitor stackRefCountIncrementer; + private static NonNullReferenceVisitor stackRefCountDecrementer; + + private const double triggerFraction = 0.90; + + // For cycle collection. + private static InternalIncrementer internalIncrementer; + private static InternalDecrementer internalDecrementer; + private static InternalScanner internalScanner; + private static InternalReclaimer internalReclaimer; + + private static UIntPtr plcRawSize, plcRawAddr; + private static UIntPtr[] plcBuffer; + private static uint freePLCHead; + private const uint initialPLCNumEntries = 1 << 12; + private const uint maxPLCNumEntries = 1 << 20; + private static bool needCleanPLCBuffer; + + private static ulong maxCyclicGarbage; + private static ulong totalCyclicGarbage; + private static uint cycleCollections; + private static bool forceCycleCollectionAtEnd { + get { return true; } + } + + // Used only in verification mode. + private static bool canVerifyHeap; + private static BackupInitializer backupInit; + private static BackupRefCount backupRefCount; + private static BackupReconciler backupReconciler; + private static IncrementBackupRefCount incrementBackupRefCount; + private static RootsScanner rootsScanner; + private static NonNullReferenceVisitor resetRoots; + private static ObjectVisitor resetTraversal; + private static LeakAccumulator leakAccumulator; + private static LeakedNodesDFS leakedNodesDFS; + private static LeakedCycleClosure leakedCycleClosure; + private static DFS dfs; + private static CycleClosure cycleClosure; + private static BFSMarker bfsMarker; + private static LeakedRootsCounter leakedRootsCounter; + private static LeakedRoots leakedRoots; + + // Used only in profiling mode. + private static String[] methodNames; + private static int[] increments; + private static int[] decrements; + private static int[] nonNullIncrements; + private static int[] nonNullDecrements; + private static int[] aIncrements; + private static int[] aDecrements; + private static int[] nonNullAIncrements; + private static int[] nonNullADecrements; + private static int[] vIncrements; + private static int[] vDecrements; + private static int[] rIncrements; + private static int[] rDecrements; + + // Used to measure the cumulative times in the various RC phases. +#if MEASURE_RCPHASES + private static bool ddListPhase; + private static long ddListProcTime; + private static long ddListProcCount; + private static long ddListPLCProcTime; + private static long ddListPLCProcCount; + + private static bool plcBufferPhase; + private static long plcBufferProcTime; + private static long plcBufferProcCount; + private static long plcBufferDDListProcTime; + private static long plcBufferDDListProcCount; + + private static long segFreeTime; + private static long segCommitTime; + + private static long stackScanCount; +#endif // MEASURE_RCPHASES + + [PreInitRefCounts] + public new static unsafe void Initialize() { + SegregatedFreeList.Initialize(); + instance = + (DeferredReferenceCountingCollector)BootstrapMemory. + Allocate(typeof(DeferredReferenceCountingCollector)); + isInGC = false; + + ZeroCountTable.Initialize(); + + refCountIncrementer = + (RefCountIncrementer)BootstrapMemory. + Allocate(typeof(RefCountIncrementer)); + refCountDecrementer = + (RefCountDecrementer)BootstrapMemory. + Allocate(typeof(RefCountDecrementer)); + stackRefCountIncrementer = + (StackRefCountIncrementer)BootstrapMemory. + Allocate(typeof(StackRefCountIncrementer)); + stackRefCountDecrementer = + (StackRefCountDecrementer)BootstrapMemory. + Allocate(typeof(StackRefCountDecrementer)); + + internalIncrementer = + (InternalIncrementer)BootstrapMemory. + Allocate(typeof(InternalIncrementer)); + internalDecrementer = + (InternalDecrementer)BootstrapMemory. + Allocate(typeof(InternalDecrementer)); + internalScanner = + (InternalScanner)BootstrapMemory. + Allocate(typeof(InternalScanner)); + internalReclaimer = + (InternalReclaimer)BootstrapMemory. + Allocate(typeof(InternalReclaimer)); + + allocatePLCBuffer(initialPLCNumEntries); + stitchFreePLCSlots(1); + needCleanPLCBuffer = false; + + canVerifyHeap = true; + if (VerificationMode) { + backupInit = + (BackupInitializer)BootstrapMemory. + Allocate(typeof(BackupInitializer)); + backupRefCount = + (BackupRefCount)BootstrapMemory. + Allocate(typeof(BackupRefCount)); + incrementBackupRefCount = + (IncrementBackupRefCount)BootstrapMemory. + Allocate(typeof(IncrementBackupRefCount)); + backupReconciler = + (BackupReconciler)BootstrapMemory. + Allocate(typeof(BackupReconciler)); + rootsScanner = + (RootsScanner)BootstrapMemory. + Allocate(typeof(RootsScanner)); + resetRoots = + (ResetRoots)BootstrapMemory. + Allocate(typeof(ResetRoots)); + resetTraversal = + (ResetTraversal)BootstrapMemory. + Allocate(typeof(ResetTraversal)); + + leakAccumulator = + (LeakAccumulator)BootstrapMemory. + Allocate(typeof(LeakAccumulator)); + leakedNodesDFS = + (LeakedNodesDFS)BootstrapMemory. + Allocate(typeof(LeakedNodesDFS)); + leakedCycleClosure = + (LeakedCycleClosure)BootstrapMemory. + Allocate(typeof(LeakedCycleClosure)); + dfs = + (DFS)BootstrapMemory. + Allocate(typeof(DFS)); + cycleClosure = + (CycleClosure)BootstrapMemory. + Allocate(typeof(CycleClosure)); + bfsMarker = + (BFSMarker)BootstrapMemory. + Allocate(typeof(BFSMarker)); + leakedRoots = + (LeakedRoots)BootstrapMemory. + Allocate(typeof(LeakedRoots)); + leakedRootsCounter = + (LeakedRootsCounter)BootstrapMemory. + Allocate(typeof(LeakedRootsCounter)); + } + } + + + [NoInline] + [ManualRefCounts] + internal override void CollectStoppable(int currentThreadIndex, + int generation) { + int startTicks = 0; + bool enableGCTiming = VTable.enableGCTiming; + if (enableGCTiming) { + VTable.enableGCTiming = false; + startTicks = Environment.TickCount; +#if MEASURE_RCPHASES + stackScanCount++; +#endif + } + + isInGC = true; + CallStack.ScanStacks(stackRefCountIncrementer, + stackRefCountIncrementer); + ZeroCountTable.ProcessZeroCountTable(); + if (needCleanPLCBuffer) { + processPLCBuffer(); + needCleanPLCBuffer = false; + } + deallocateObjects(); + CallStack.ScanStacks(stackRefCountDecrementer, + stackRefCountDecrementer); + StartGCCycle(); + isInGC = false; + + if (enableGCTiming) { + int elapsedTicks = Environment.TickCount-startTicks; + BaseCollector.RegisterPause(elapsedTicks); + VTable.enableGCTiming = true; + } + } + + internal override int CollectionGeneration(int gen) { + return MinGeneration; + } + + [NoInline] + [ManualRefCounts] + internal override UIntPtr AllocateObjectMemory(UIntPtr numBytes, + uint alignment, + Thread currentThread) { + // "numBytes" must be in multiples of double words + // (four bytes). Note that it includes the space for + // the object header field. + + VTable.Assert(Util.dwordAlign(numBytes) == numBytes, + @"Util.dwordAlign(numBytes) == numBytes"); + + UIntPtr resultAddr; + if (GC.HeapSizeConfigurable) { + resultAddr = AllocateObjectMemoryFast(numBytes, + alignment, + currentThread); + if (resultAddr == UIntPtr.Zero) { + resultAddr = + AllocateObjectMemorySlow(numBytes, + alignment, + currentThread); + } + } else { // Old collection-triggering scheme. + if (delayedDeallocationLength > collectionTrigger) { + GC.Collect(); + } + resultAddr = SegregatedFreeList.Allocate(currentThread, + numBytes, + alignment); + } + + return resultAddr; + } + + [Inline] + [ManualRefCounts] + private UIntPtr AllocateObjectMemoryFast(UIntPtr numBytes, + uint alignment, + Thread currentThread) { + UIntPtr resultAddr = + SegregatedFreeList.AllocateFast(currentThread, + numBytes, + alignment); + return resultAddr; + } + + [ManualRefCounts] + [DisableNullChecks] + private UIntPtr AllocateObjectMemorySlow(UIntPtr numBytes, + uint alignment, + Thread currentThread) { + VTable.Assert(GC.HeapSizeConfigurable, + @"GC.HeapSizeConfigurable"); + + // Allocate on "slow" path, noting levels before and after. + uint before = (uint)SegregatedFreeList.AllocatedPages; + UIntPtr resultAddr = + SegregatedFreeList.AllocateSlow(currentThread, + numBytes, + alignment); + uint after = (uint)SegregatedFreeList.AllocatedPages; + + // If page usage hasn't increased, simply return. + if (!(after > before)) { + return resultAddr; + } + + // Otherwise, check if memory usage has crossed a threshold. + uint zctPages = + (uint)(ZeroCountTable.Size >> PageTable.PageBits); + uint heapPages = after+zctPages; + int maxHeapPages = GC.MaxHeapPages; + if (heapPages > triggerFraction*maxHeapPages) { + if (heapPages < maxHeapPages) { + GC.Collect(); + if (++numCollections > numCollectionsTrigger) { + numCollections = 0; + recycleAllocator(); + } + } else { + Console.Error.Write("Heap size exceeds "); + Console.Error.Write(maxHeapPages << + (PageTable.PageBits-10)); + Console.Error.WriteLine("KB!"); + System.Environment.Exit(-2); + } + } + + return resultAddr; + } + + [Inline] + [ManualRefCounts] + protected override void CreateObject(Object obj, + VTable vtable, + Thread currentThread) { + base.CreateObject(obj, vtable, currentThread); + obj.REF_STATE = vtable.isAcyclicRefType ? acyclicFlagMask : 0; + ZeroCountTable.Add(obj); + } + + internal override int GetGeneration(Object obj) { + return MinGeneration; + } + + internal override int MaxGeneration { + get { + return (int)PageType.Owner0; + } + } + + internal override int MinGeneration { + get { + return (int)PageType.Owner0; + } + } + + internal override long TotalMemory { + get { + UIntPtr allocatorPageCount = + SegregatedFreeList.AllocatedPages; + UIntPtr zctPageCount = + (UIntPtr)(ZeroCountTable.Size >> PageTable.PageBits); + UIntPtr pageCount = allocatorPageCount+zctPageCount; + return (long)PageTable.RegionSize(pageCount); + } + } + + + internal override void EnableHeap() { + // Do nothing + } + + [NoInline] + [ManualRefCounts] + internal override void DestructHeap() { +#if MEASURE_RCPHASES + Console.Error.Write("DD list processing time (ms): "); + Console.Error.WriteLine(ddListProcTime); + Console.Error.Write("DD list processing count: "); + Console.Error.WriteLine(ddListProcCount); + Console.Error.Write("\tPLC buffer processing time (ms): "); + Console.Error.WriteLine(ddListPLCProcTime); + Console.Error.Write("\tPLC buffer processing count: "); + Console.Error.WriteLine(ddListPLCProcCount); + + Console.Error.Write("PLC buffer processing time (ms): "); + Console.Error.WriteLine(plcBufferProcTime); + Console.Error.Write("PLC buffer processing count: "); + Console.Error.WriteLine(plcBufferProcCount); + Console.Error.Write("\tDD list processing time (ms): "); + Console.Error.WriteLine(plcBufferDDListProcTime); + Console.Error.Write("\tDD list processing count: "); + Console.Error.WriteLine(plcBufferDDListProcCount); + + Console.Error.Write("Seg. free time (ms): "); + Console.Error.WriteLine(segFreeTime); + Console.Error.Write("Seg. commit time (ms): "); + Console.Error.WriteLine(segCommitTime); + + Console.Error.Write("Stack scan count: "); + Console.Error.WriteLine(stackScanCount); +#endif // MEASURE_RCPHASES + + if (VTable.enableGCVerify) { + VerifyHeap(true); + } + base.DestructHeap(); + if (VTable.enableGCProfiling) { + if (forceCycleCollectionAtEnd) { + processPLCBuffer(); + } + + Console.Error.Write("Cycle collections: "); + Console.Error.WriteLine(cycleCollections); + Console.Error.Write("Max. Cyclic garbage (B): "); + Console.Error.WriteLine(maxCyclicGarbage); + Console.Error.Write("Total Cyclic garbage (B): "); + Console.Error.WriteLine(totalCyclicGarbage); + + if (DeferredReferenceCountingCollector.ProfilingMode) { + EmitRefCountsProfile(); + } + } + } + + [NoInline] + [ManualRefCounts] + internal override void VerifyHeap(bool beforeCollection) { + VTable.Assert(DeferredReferenceCountingCollector.VerificationMode, + @"DeferredReferenceCountingCollector.VerificationMode"); + + if (!canVerifyHeap) { + return; + } + + isInGC = true; + + CallStack.ScanStacks(stackRefCountIncrementer, + stackRefCountIncrementer); + // Clean up ZeroCountTable + ZeroCountTable.ProcessZeroCountTable(); + + while (nonEmptyDDList()) { + // Ensure the integrity of the delayed deallocation list. + deallocationListChecker(); + + // Deallocate objects on the delayed deallocation list. + purgeDeallocationList(); + + // Capture leaked cycles. + processPLCBuffer(); + } + + // Recycle allocator. + recycleAllocator(); + + // Initialize the "backup" reference count. + SegregatedFreeList.VisitAllObjects(backupInit); + + // Count all references and managed pointers. + rootsScanner.Initialize(backupRefCount); + CallStack.ScanStacks(rootsScanner, rootsScanner); + Thread.VisitBootstrapData(rootsScanner); + StaticData.ScanStaticData(rootsScanner); + MultiUseWord.VisitStrongRefs(rootsScanner, false); + + CallStack.ScanStacks(resetRoots, resetRoots); + Thread.VisitBootstrapData(resetRoots); + StaticData.ScanStaticData(resetRoots); + + SegregatedFreeList.VisitAllObjects(resetTraversal); + + // Reconcile with actual reference count. + SegregatedFreeList.VisitAllObjects(backupReconciler); + + // Actual leaks (refCount > 0 and backup refCount = 0). + leakAccumulator.Initialize(); + SegregatedFreeList.VisitAllObjects(leakAccumulator); + VTable.DebugPrint("Leaked storage: "); + VTable.DebugPrint((int)leakAccumulator.Size); + VTable.DebugPrint("B"); + + if (VerifyLeakedCycles) { + // Find leaked data that should have been reclaimed. + // (If L is the set of all leaked nodes, and L' the + // transitive closure of leaked cycles, then L-L' is + // the set of nodes that should have been captured + // by a pure reference counting collector.) + SegregatedFreeList.VisitAllObjects(leakedNodesDFS); + SegregatedFreeList.VisitAllObjects(resetTraversal); + SegregatedFreeList.VisitAllObjects(leakedCycleClosure); + SegregatedFreeList.VisitAllObjects(resetTraversal); + leakAccumulator.Initialize(); + SegregatedFreeList.VisitAllObjects(leakAccumulator); + VTable.DebugPrint(" ("); + VTable.DebugPrint((int)leakAccumulator.Size); + VTable.DebugPrint("B acyclic)"); + } + + // Find the roots of leaked data. + leakedRoots.Initialize(); + SegregatedFreeList.VisitAllObjects(leakedRoots); + leakedRootsCounter.Initialize(); + SegregatedFreeList.VisitAllObjects(leakedRootsCounter); + SegregatedFreeList.VisitAllObjects(resetTraversal); + VTable.DebugPrint("; leaked heap roots: "); + VTable.DebugPrint((int)leakedRootsCounter.Total); + VTable.DebugPrint("\n"); + + CallStack.ScanStacks(stackRefCountDecrementer, + stackRefCountDecrementer); + isInGC = false; + } + + internal override UIntPtr FindObjectAddr(UIntPtr interiorPtr) { + return SegregatedFreeList.Find(interiorPtr); + } + + internal override + void VisitObjects(ObjectLayout.ObjectVisitor objVisitor, + UIntPtr lowAddr, + UIntPtr highAddr) { + UIntPtr lowPage = PageTable.Page(lowAddr); + UIntPtr highPage = PageTable.Page(highAddr); + SegregatedFreeList.VisitObjects(lowPage, + highPage, + objVisitor); + } + + [NoInline] + [ManualRefCounts] + [RequiredByBartok] + [GCAnnotation(GCOption.NOGC)] + internal static unsafe void AccumulateRCUpdates(String methodName, + int methodIndex, + uint maxIndex, + int incCount, + int decCount, + int nIncCount, + int nDecCount, + int aIncCount, + int aDecCount, + int nAIncCount, + int nADecCount, + int vIncCount, + int vDecCount, + int rIncCount, + int rDecCount) { + VTable.Assert(DeferredReferenceCountingCollector.ProfilingMode, + @"DeferredReferenceCountingCollector.ProfilingMode"); + + // Return if the page table hasn't been set up yet. + if (PageTable.pageTableCount == UIntPtr.Zero) { + return; + } + + if (methodNames == null) { + VTable.Assert(increments == null, + @"increments == null"); + VTable.Assert(decrements == null, + @"decrements == null"); + VTable.Assert(nonNullIncrements == null, + @"nonNullIncrements == null"); + VTable.Assert(nonNullDecrements == null, + @"nonNullDecrements == null"); + VTable.Assert(aIncrements == null, + @"aIncrements == null"); + VTable.Assert(aDecrements == null, + @"aDecrements == null"); + VTable.Assert(nonNullAIncrements == null, + @"nonNullAIncrements == null"); + VTable.Assert(nonNullADecrements == null, + @"nonNullADecrements == null"); + VTable.Assert(vIncrements == null, + @"vIncrements == null"); + VTable.Assert(vDecrements == null, + @"vDecrements == null"); + VTable.Assert(rIncrements == null, + @"rIncrements == null"); + VTable.Assert(rDecrements == null, + @"rDecrements == null"); + + // Allocate storage for the tables. Note that this is + // requisitioned directly from the memory manager. Care + // should be taken to ensure that AccumulateRCUpdates + // does not indirectly call methods that may have + // compiler-inserted RC updates. + VTable strArrayVtable = + ((RuntimeType)typeof(String[])).classVtable; + VTable intArrayVtable = + ((RuntimeType)typeof(int[])).classVtable; + UIntPtr methodNamesSize = + ObjectLayout.ArraySize(strArrayVtable, maxIndex+1); + UIntPtr incrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr decrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr nonNullIncrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr nonNullDecrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr aIncrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr aDecrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr nonNullAIncrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr nonNullADecrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr vIncrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr vDecrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr rIncrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr rDecrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr totalSize = + methodNamesSize+ + incrementsSize+decrementsSize+ + nonNullIncrementsSize+nonNullDecrementsSize+ + aIncrementsSize+aDecrementsSize+ + nonNullAIncrementsSize+nonNullADecrementsSize+ + vIncrementsSize+vDecrementsSize+ + rIncrementsSize+rDecrementsSize; + + BumpAllocator profileData = + new BumpAllocator(PageType.NonGC); + UIntPtr profileDataStart = + MemoryManager.AllocateMemory(totalSize); + profileData.SetZeroedRange(profileDataStart, totalSize); + PageManager.SetStaticDataPages(profileDataStart, + totalSize); + + methodNames = + (String[])AllocateArray(ref profileData, + strArrayVtable, + methodNamesSize); + VTable.Assert(methodNames != null, + @"methodNames != null"); + + increments = + (int[])AllocateArray(ref profileData, intArrayVtable, + incrementsSize); + VTable.Assert(increments != null, + @"increments != null"); + + decrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + decrementsSize); + VTable.Assert(decrements != null, + @"decrements != null"); + + nonNullIncrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + nonNullIncrementsSize); + VTable.Assert(nonNullIncrements != null, + @"nonNullIncrements != null"); + + nonNullDecrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + nonNullDecrementsSize); + VTable.Assert(nonNullDecrements != null, + @"nonNullDecrements != null"); + + aIncrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + aIncrementsSize); + VTable.Assert(aIncrements != null, + @"aIncrements != null"); + + aDecrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + aDecrementsSize); + VTable.Assert(aDecrements != null, + @"aDecrements != null"); + + nonNullAIncrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + nonNullAIncrementsSize); + VTable.Assert(nonNullAIncrements != null, + @"nonNullAIncrements != null"); + + nonNullADecrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + nonNullADecrementsSize); + VTable.Assert(nonNullADecrements != null, + @"nonNullADecrements != null"); + + vIncrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + vIncrementsSize); + VTable.Assert(vIncrements != null, + @"vIncrements != null"); + + vDecrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + vDecrementsSize); + VTable.Assert(vDecrements != null, + @"vDecrements != null"); + + rIncrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + rIncrementsSize); + VTable.Assert(rIncrements != null, + @"rIncrements != null"); + + rDecrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + rDecrementsSize); + VTable.Assert(rDecrements != null, + @"rDecrements != null"); + + *(uint*)(Magic.addressOf(methodNames)+ + PostHeader.Size) = maxIndex+1; + *(uint*)(Magic.addressOf(increments)+ + PostHeader.Size) = maxIndex+1; + *(uint*)(Magic.addressOf(decrements)+ + PostHeader.Size) = maxIndex+1; + *(uint*)(Magic.addressOf(nonNullIncrements)+ + PostHeader.Size) = maxIndex+1; + *(uint*)(Magic.addressOf(nonNullDecrements)+ + PostHeader.Size) = maxIndex+1; + *(uint*)(Magic.addressOf(aIncrements)+ + PostHeader.Size) = maxIndex+1; + *(uint*)(Magic.addressOf(aDecrements)+ + PostHeader.Size) = maxIndex+1; + *(uint*)(Magic.addressOf(nonNullAIncrements)+ + PostHeader.Size) = maxIndex+1; + *(uint*)(Magic.addressOf(nonNullADecrements)+ + PostHeader.Size) = maxIndex+1; + *(uint*)(Magic.addressOf(vIncrements)+ + PostHeader.Size) = maxIndex+1; + *(uint*)(Magic.addressOf(vDecrements)+ + PostHeader.Size) = maxIndex+1; + *(uint*)(Magic.addressOf(rIncrements)+ + PostHeader.Size) = maxIndex+1; + *(uint*)(Magic.addressOf(rDecrements)+ + PostHeader.Size) = maxIndex+1; + } + VTable.Assert(methodNames.Length == maxIndex+1, + @"methodNames.Length == maxIndex+1"); + VTable.Assert(increments.Length == maxIndex+1, + @"increments.Length == maxIndex+1"); + VTable.Assert(decrements.Length == maxIndex+1, + @"decrements.Length == maxIndex+1"); + VTable.Assert(nonNullIncrements.Length == maxIndex+1, + @"nonNullIncrements.Length == maxIndex+1"); + VTable.Assert(nonNullDecrements.Length == maxIndex+1, + @"nonNullDecrements.Length == maxIndex+1"); + VTable.Assert(aIncrements.Length == maxIndex+1, + @"aIncrements.Length == maxIndex+1"); + VTable.Assert(aDecrements.Length == maxIndex+1, + @"aDecrements.Length == maxIndex+1"); + VTable.Assert(nonNullAIncrements.Length == maxIndex+1, + @"nonNullAIncrements.Length == maxIndex+1"); + VTable.Assert(nonNullADecrements.Length == maxIndex+1, + @"nonNullADecrements.Length == maxIndex+1"); + VTable.Assert(vIncrements.Length == maxIndex+1, + @"vIncrements.Length == maxIndex+1"); + VTable.Assert(vDecrements.Length == maxIndex+1, + @"vDecrements.Length == maxIndex+1"); + VTable.Assert(rIncrements.Length == maxIndex+1, + @"rIncrements.Length == maxIndex+1"); + VTable.Assert(rDecrements.Length == maxIndex+1, + @"rDecrements.Length == maxIndex+1"); + + if (methodNames[methodIndex] == null) { + methodNames[methodIndex] = methodName; + } + // Not "methodNames[methodIndex] == methodName" because + // the Equality operator carries compiler-inserted + // RC updates! + VTable.Assert(Magic.addressOf(methodNames[methodIndex]) == + Magic.addressOf(methodName), + @"Magic.addressOf(methodNames[methodIndex]) == + Magic.addressOf(methodName)"); + + increments[methodIndex] += incCount; + decrements[methodIndex] += decCount; + nonNullIncrements[methodIndex] += nIncCount; + nonNullDecrements[methodIndex] += nDecCount; + aIncrements[methodIndex] += aIncCount; + aDecrements[methodIndex] += aDecCount; + nonNullAIncrements[methodIndex] += nAIncCount; + nonNullADecrements[methodIndex] += nADecCount; + vIncrements[methodIndex] += vIncCount; + vDecrements[methodIndex] += vDecCount; + rIncrements[methodIndex] += rIncCount; + rDecrements[methodIndex] += rDecCount; + } + + [ManualRefCounts] + internal static Object AllocateArray(ref BumpAllocator profileData, + VTable vtable, + UIntPtr numBytes) { + UIntPtr resultAddr = + profileData.AllocateFast(numBytes, vtable.baseAlignment); + Object result = Magic.fromAddress(resultAddr); + uint refState = markFlagMask; + result.REF_STATE = vtable.isAcyclicRefType ? + (acyclicFlagMask | refState) : refState; + result.vtable = vtable; + return result; + } + + [NoInline] + [ManualRefCounts] + internal static void EmitRefCountsProfile() { + VTable.Assert(DeferredReferenceCountingCollector.ProfilingMode, + @"DeferredReferenceCountingCollector.ProfilingMode"); + + if (methodNames == null) { // No RC updates present. + return; + } + VTable.Assert(increments != null, + @"increments != null"); + VTable.Assert(decrements != null, + @"decrements != null"); + VTable.Assert(nonNullIncrements != null, + @"nonNullIncrements != null"); + VTable.Assert(nonNullDecrements != null, + @"nonNullDecrements != null"); + VTable.Assert(aIncrements != null, + @"aIncrements != null"); + VTable.Assert(aDecrements != null, + @"aDecrements != null"); + VTable.Assert(nonNullAIncrements != null, + @"nonNullAIncrements != null"); + VTable.Assert(nonNullADecrements != null, + @"nonNullADecrements != null"); + VTable.Assert(vIncrements != null, + @"vIncrements != null"); + VTable.Assert(vDecrements != null, + @"vDecrements != null"); + VTable.Assert(rIncrements != null, + @"rIncrements != null"); + VTable.Assert(rDecrements != null, + @"rDecrements != null"); + + // Bubble sort in decreasing order of sums. + for (int i = 0; i < methodNames.Length; i++) { + for (int j = methodNames.Length-1; j > i; j--) { + if (increments[j]+decrements[j]+ + nonNullIncrements[j]+nonNullDecrements[j]+ + aIncrements[j]+aDecrements[j]+ + nonNullAIncrements[j]+nonNullADecrements[j] > + increments[j-1]+decrements[j-1]+ + nonNullIncrements[j-1]+nonNullDecrements[j-1]+ + aIncrements[j-1]+aDecrements[j-1]+ + nonNullAIncrements[j-1]+nonNullADecrements[j-1]) { + // Swap contents. + int temp = increments[j]; + increments[j] = increments[j-1]; + increments[j-1] = temp; + + temp = decrements[j]; + decrements[j] = decrements[j-1]; + decrements[j-1] = temp; + + temp = nonNullIncrements[j]; + nonNullIncrements[j] = nonNullIncrements[j-1]; + nonNullIncrements[j-1] = temp; + + temp = nonNullDecrements[j]; + nonNullDecrements[j] = nonNullDecrements[j-1]; + nonNullDecrements[j-1] = temp; + + temp = aIncrements[j]; + aIncrements[j] = aIncrements[j-1]; + aIncrements[j-1] = temp; + + temp = aDecrements[j]; + aDecrements[j] = aDecrements[j-1]; + aDecrements[j-1] = temp; + + temp = nonNullAIncrements[j]; + nonNullAIncrements[j] = nonNullAIncrements[j-1]; + nonNullAIncrements[j-1] = temp; + + temp = nonNullADecrements[j]; + nonNullADecrements[j] = nonNullADecrements[j-1]; + nonNullADecrements[j-1] = temp; + + temp = vIncrements[j]; + vIncrements[j] = vIncrements[j-1]; + vIncrements[j-1] = temp; + + temp = vDecrements[j]; + vDecrements[j] = vDecrements[j-1]; + vDecrements[j-1] = temp; + + temp = rIncrements[j]; + rIncrements[j] = rIncrements[j-1]; + rIncrements[j-1] = temp; + + temp = rDecrements[j]; + rDecrements[j] = rDecrements[j-1]; + rDecrements[j-1] = temp; + + String s = methodNames[j]; + methodNames[j] = methodNames[j-1]; + methodNames[j-1] = s; + } + } + } + + VTable.DebugPrint("\n"); + VTable.DebugPrint("Incs\t\tDecs"); + VTable.DebugPrint("\t\tNIncs\t\tNDecs"); + VTable.DebugPrint("\t\tAIncs\t\tADecs"); + VTable.DebugPrint("\t\tNAIncs\t\tNADecs"); + VTable.DebugPrint("\t\tV+\t\tV-"); + VTable.DebugPrint("\t\tR+\t\tR-"); + VTable.DebugPrint("\t\tMethod\n"); + VTable.DebugPrint("----\t\t----"); + VTable.DebugPrint("\t\t-----\t\t-----"); + VTable.DebugPrint("\t\t-----\t\t-----"); + VTable.DebugPrint("\t\t------\t\t------"); + VTable.DebugPrint("\t\t--\t\t--"); + VTable.DebugPrint("\t\t--\t\t--"); + VTable.DebugPrint("\t\t------\n"); + VTable.DebugPrint("\n"); + for (int i = 0; i < methodNames.Length; i++) { + if (increments[i]+nonNullIncrements[i]+ + aIncrements[i]+nonNullAIncrements[i] == 0 && + decrements[i]+nonNullDecrements[i]+ + aDecrements[i]+nonNullADecrements[i] == 0) { + continue; + } + VTable.DebugPrint(increments[i]); + if (increments[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(decrements[i]); + if (decrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(nonNullIncrements[i]); + if (nonNullIncrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(nonNullDecrements[i]); + if (nonNullDecrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(aIncrements[i]); + if (aIncrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(aDecrements[i]); + if (aDecrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(nonNullAIncrements[i]); + if (nonNullAIncrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(nonNullADecrements[i]); + if (nonNullADecrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(vIncrements[i]); + if (vIncrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(vDecrements[i]); + if (vDecrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(rIncrements[i]); + if (rIncrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(rDecrements[i]); + if (rDecrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(methodNames[i]); + VTable.DebugPrint("\n"); + } + } + + [NoInline] + [ManualRefCounts] + [RequiredByBartok] + internal static unsafe void IndirectIncrementRefCount(UIntPtr loc) { + UIntPtr pageLoc = PageTable.Page(loc); + PageType pageType = PageTable.Type(pageLoc); + if (pageType == PageType.Stack) { + return; + } + + UIntPtr objAddr = *(UIntPtr*)loc; + Object obj = Magic.fromAddress(objAddr); + if (obj != null) { + NonNullIncrementRefCount(obj); + } + } + + [NoInline] + [ManualRefCounts] + [RequiredByBartok] + internal static unsafe void IndirectDecrementRefCount(UIntPtr loc) { + UIntPtr pageLoc = PageTable.Page(loc); + PageType pageType = PageTable.Type(pageLoc); + if (pageType == PageType.Stack) { + return; + } + + UIntPtr objAddr = *(UIntPtr*)loc; + Object obj = Magic.fromAddress(objAddr); + if (obj != null) { + NonNullDecrementRefCount(obj); + } + } + + [NoInline] + [ManualRefCounts] + [RequiredByBartok] + [GCAnnotation(GCOption.NOGC)] + internal static unsafe void nonNullLocalIncrementRefCount(Object obj) { + VTable.Assert(obj != null, + @"obj != null"); + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) < refCountMask, + @"(refState & refCountMask) < refCountMask"); + + if ((refState & countingONFlagMask) == 0) { + return; + } + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + + if ((refState & markFlagMask) != 0) { + ZeroCountTable.Remove(obj); + refState = obj.REF_STATE; + VTable.Assert((refState & markFlagMask) == 0, + @"(refState & markFlagMask) == 0"); + + obj.REF_STATE = 1 | (refState & ~refCountMask); + } else { + obj.REF_STATE = refState+1; + + // Exclude the object from leaked cycle processing. + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + // If the object is present in the PLC ("potentially + // leaked cycle") buffer, remove it. + if (index != 0) { + // Reset the PLC index in the object. + SetPLCIndex(obj, 0); + removeFromPLCBuffer(index); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + } + } + + [NoInline] + [ManualRefCounts] + [RequiredByBartok] + internal static void IncrementRefCount(Object obj) { + if (obj == null) { + return; + } + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) < refCountMask, + @"(refState & refCountMask) < refCountMask"); + + if ((refState & countingONFlagMask) == 0) { + return; + } + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + + if ((refState & markFlagMask) != 0) { + ZeroCountTable.Remove(obj); + refState = obj.REF_STATE; + VTable.Assert((refState & markFlagMask) == 0, + @"(refState & markFlagMask) == 0"); + + obj.REF_STATE = 1 | (refState & ~refCountMask); + // Include the object for leaked cycle processing. + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + // Insert the object into the PLC buffer only + // if it hasn't already been inserted. + if (index == 0) { + addToPLCBuffer(obj); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + } else { + obj.REF_STATE = refState+1; + + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + if (index != 0) { + SetPLCIndex(obj, 0); + removeFromPLCBuffer(index); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + } + } + + [NoInline] + [ManualRefCounts] + [RequiredByBartok] + internal static void NonNullIncrementRefCount(Object obj) { + VTable.Assert(obj != null, + @"obj != null"); + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) < refCountMask, + @"(refState & refCountMask) < refCountMask"); + + if ((refState & countingONFlagMask) == 0) { + return; + } + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + + if ((refState & markFlagMask) != 0) { + ZeroCountTable.Remove(obj); + refState = obj.REF_STATE; + VTable.Assert((refState & markFlagMask) == 0, + @"(refState & markFlagMask) == 0"); + + obj.REF_STATE = 1 | (refState & ~refCountMask); + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + if (index == 0) { + addToPLCBuffer(obj); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + } else { + obj.REF_STATE = refState+1; + + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + if (index != 0) { + SetPLCIndex(obj, 0); + removeFromPLCBuffer(index); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + } + } + + [Inline] + [ManualRefCounts] + [RequiredByBartok] + [GCAnnotation(GCOption.NOGC)] + internal static void AcyclicIncrementRefCount(Object obj) { + if (obj == null) { + return; + } + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) < refCountMask, + @"(refState & refCountMask) < refCountMask"); + + if ((refState & countingONFlagMask) == 0) { + return; + } + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + + if ((refState & markFlagMask) != 0) { + ZeroCountTable.Remove(obj); + refState = obj.REF_STATE; + VTable.Assert((refState & markFlagMask) == 0, + @"(refState & markFlagMask) == 0"); + + obj.REF_STATE = 1 | (refState & ~refCountMask); + } else { + obj.REF_STATE = refState+1; + } + +#if DEBUG + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); +#endif // DEBUG + } + + [Inline] + [ManualRefCounts] + [RequiredByBartok] + [GCAnnotation(GCOption.NOGC)] + internal static void NonNullAcyclicIncrementRefCount(Object obj) { + VTable.Assert(obj != null, + @"obj != null"); + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) < refCountMask, + @"(refState & refCountMask) < refCountMask"); + + if ((refState & countingONFlagMask) == 0) { + return; + } + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + + if ((refState & markFlagMask) != 0) { + ZeroCountTable.Remove(obj); + refState = obj.REF_STATE; + VTable.Assert((refState & markFlagMask) == 0, + @"(refState & markFlagMask) == 0"); + + obj.REF_STATE = 1 | (refState & ~refCountMask); + } else { + obj.REF_STATE = refState+1; + } + +#if DEBUG + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); +#endif // DEBUG + } + + [NoInline] + [ManualRefCounts] + [RequiredByBartok] + [GCAnnotation(GCOption.NOGC)] + internal static void PLCFreeNonNullLocalIncrementRefCount(Object obj) { + VTable.Assert(obj != null, + @"obj != null"); + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) < refCountMask, + @"(refState & refCountMask) < refCountMask"); + + if ((refState & countingONFlagMask) == 0) { + return; + } + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + + if ((refState & markFlagMask) != 0) { + ZeroCountTable.Remove(obj); + refState = obj.REF_STATE; + VTable.Assert((refState & markFlagMask) == 0, + @"(refState & markFlagMask) == 0"); + + obj.REF_STATE = 1 | (refState & ~refCountMask); + } else { + obj.REF_STATE = refState+1; + } + } + + [NoInline] + [ManualRefCounts] + [RequiredByBartok] + internal static void PLCFreeIncrementRefCount(Object obj) { + if (obj == null) { + return; + } + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) < refCountMask, + @"(refState & refCountMask) < refCountMask"); + + if ((refState & countingONFlagMask) == 0) { + return; + } + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + + if ((refState & markFlagMask) != 0) { + ZeroCountTable.Remove(obj); + refState = obj.REF_STATE; + VTable.Assert((refState & markFlagMask) == 0, + @"(refState & markFlagMask) == 0"); + + obj.REF_STATE = 1 | (refState & ~refCountMask); + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + if (index == 0) { + addToPLCBuffer(obj); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + } else { + obj.REF_STATE = refState+1; + } + } + + [NoInline] + [ManualRefCounts] + [RequiredByBartok] + internal static void PLCFreeNonNullIncrementRefCount(Object obj) { + VTable.Assert(obj != null, + @"obj != null"); + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) < refCountMask, + @"(refState & refCountMask) < refCountMask"); + + if ((refState & countingONFlagMask) == 0) { + return; + } + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + + if ((refState & markFlagMask) != 0) { + ZeroCountTable.Remove(obj); + refState = obj.REF_STATE; + VTable.Assert((refState & markFlagMask) == 0, + @"(refState & markFlagMask) == 0"); + + obj.REF_STATE = 1 | (refState & ~refCountMask); + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + if (index == 0) { + addToPLCBuffer(obj); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + } else { + obj.REF_STATE = refState+1; + } + } + + [NoInline] + [ManualRefCounts] + internal static void nonNullLocalDecrementRefCount(Object obj) { + VTable.Assert(isInGC, + @"isInGC"); + VTable.Assert(obj != null, + @"obj != null"); + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) >= 0, + @"(refState & refCountMask) >= 0"); + + if ((refState & countingONFlagMask) == 0) { + return; + } + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + VTable.Assert((refState & markFlagMask) == 0, + @"(refState & markFlagMask) == 0"); + + if ((refState & refCountMask) == 1) { + MultiUseWord muw = MultiUseWord.GetForObject(obj); + if (muw.IsMonitorOrInflatedTag()) { + MultiUseWord.RefCountGCDeadObjHook(muw); + } + + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + if (index != 0) { + SetPLCIndex(obj, 0); + removeFromPLCBuffer(index); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + + deallocateLazily(obj); + obj.REF_STATE--; + } else { + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + + // Include the object for leaked cycle processing. + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + if (index == 0) { + addToPLCBuffer(obj); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + obj.REF_STATE--; + } + } + + [NoInline] + [ManualRefCounts] + [RequiredByBartok] + internal static void DecrementRefCount(Object obj) { + if (obj == null) { + return; + } + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) >= 0, + @"(refState & refCountMask) >= 0"); + + if ((refState & countingONFlagMask) == 0) { + return; + } + + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + VTable.Assert((refState & markFlagMask) == 0, + @"(refState & markFlagMask) == 0"); + + if ((refState & refCountMask) == 1) { + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + if (index != 0) { + SetPLCIndex(obj, 0); + removeFromPLCBuffer(index); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + + ZeroCountTable.Add(obj); + } else { + VTable.Assert((refState & refCountMask) > 1, + @"(refState & refCountMask) > 1"); + + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + if (index == 0) { + addToPLCBuffer(obj); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + + // May GC, reload refState, and check again + refState = obj.REF_STATE; + if ((refState & refCountMask) == 1) { + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + if (index != 0) { + SetPLCIndex(obj, 0); + removeFromPLCBuffer(index); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + + ZeroCountTable.Add(obj); // should not GC + } else { + obj.REF_STATE = refState-1; + } + } + } + + [NoInline] + [ManualRefCounts] + [RequiredByBartok] + internal static void NonNullDecrementRefCount(Object obj) { + VTable.Assert(obj != null, + @"obj != null"); + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) >= 0, + @"(refState & refCountMask) >= 0"); + + if ((refState & countingONFlagMask) == 0) { + return; + } + + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + VTable.Assert((refState & markFlagMask) == 0, + @"(refState & markFlagMask) == 0"); + + if ((refState & refCountMask) == 1) { + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + if (index != 0) { + SetPLCIndex(obj, 0); + removeFromPLCBuffer(index); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + + ZeroCountTable.Add(obj); + } else { + VTable.Assert((refState & refCountMask) > 1, + @"(refState & refCountMask) > 1"); + + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + if (index == 0) { + addToPLCBuffer(obj); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + + // May GC, reload refState, and check again + refState = obj.REF_STATE; + if ((refState & refCountMask) == 1) { + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + if (index != 0) { + SetPLCIndex(obj, 0); + removeFromPLCBuffer(index); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + + ZeroCountTable.Add(obj); // should not GC + } else { + obj.REF_STATE = refState-1; + } + } + } + + [Inline] + [ManualRefCounts] + [RequiredByBartok] + internal static void AcyclicDecrementRefCount(Object obj) { + if (obj == null) { + return; + } + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) >= 0, + @"(refState & refCountMask) >= 0"); + + if ((refState & countingONFlagMask) == 0) { + return; + } + + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + VTable.Assert((refState & markFlagMask) == 0, + @"(refState & markFlagMask) == 0"); + + if ((refState & refCountMask) == 1) { +#if DEBUG + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); +#endif // DEBUG + + ZeroCountTable.Add(obj); + } else { + VTable.Assert((refState & refCountMask) > 1, + @"(refState & refCountMask) > 1"); + +#if DEBUG + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); +#endif // DEBUG + + // May GC, reload refState, and check again + refState = obj.REF_STATE; + if ((refState & refCountMask) == 1) { + ZeroCountTable.Add(obj); // should not GC + } else { + obj.REF_STATE = refState-1; + } + } + } + + [Inline] + [ManualRefCounts] + [RequiredByBartok] + internal static void NonNullAcyclicDecrementRefCount(Object obj) { + VTable.Assert(obj != null, + @"obj != null"); + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) >= 0, + @"(refState & refCountMask) >= 0"); + + if ((refState & countingONFlagMask) == 0) { + return; + } + + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + VTable.Assert((refState & markFlagMask) == 0, + @"(refState & markFlagMask) == 0"); + + if ((refState & refCountMask) == 1) { +#if DEBUG + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); +#endif // DEBUG + + ZeroCountTable.Add(obj); + } else { + VTable.Assert((refState & refCountMask) > 1, + @"(refState & refCountMask) > 1"); + +#if DEBUG + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); +#endif // DEBUG + + // May GC, reload refState, and check again + refState = obj.REF_STATE; + if ((refState & refCountMask) == 1) { + ZeroCountTable.Add(obj); // should not GC + } else { + obj.REF_STATE = refState-1; + } + } + } + + + [RequiredByBartok] + [DisableNullChecks] + internal static void IncrementReferentRefCounts(UIntPtr objAddr, + VTable vtable) { + refCountIncrementer.VisitReferenceFields(objAddr, vtable); + } + + [RequiredByBartok] + [DisableNullChecks] + internal static void DecrementReferentRefCounts(UIntPtr objAddr, + VTable vtable) { + refCountDecrementer.VisitReferenceFields(objAddr, vtable); + } + + [ManualRefCounts] + [DisableNullChecks] + internal static unsafe void IncrementReferentRefCounts + (UIntPtr objAddr, + VTable vtable, + int start, + int span) { + uint objTag = (uint)vtable.pointerTrackingMask & 0xf; + + switch (objTag) { + case ObjectLayout.PTR_VECTOR_TAG: + case ObjectLayout.PTR_ARRAY_TAG: { +#if DEBUG + Object obj = Magic.fromAddress(objAddr); + Array array = Magic.toArray(obj); + VTable.Assert(span <= array.Length, + @"span <= array.Length"); +#endif // DEBUG + + UIntPtr* baseAddr = (UIntPtr*)(objAddr+ + vtable.baseLength-PreHeader.Size); + UIntPtr* begin = baseAddr+start; + UIntPtr* end = begin+span; + for (UIntPtr* el = begin; el < end; el++) { + UIntPtr addr = *el; + incrementRefCount(addr); + } + break; + } + + case ObjectLayout.OTHER_VECTOR_TAG: + case ObjectLayout.OTHER_ARRAY_TAG: { + if (vtable.arrayOf != StructuralType.Struct) { + break; + } + VTable elVTable = vtable.arrayElementClass; + uint elMask = (uint)elVTable.pointerTrackingMask; + if (elMask == ObjectLayout.SPARSE_TAG || + elMask == ObjectLayout.DENSE_TAG) { + break; + } +#if DEBUG + Object obj = Magic.fromAddress(objAddr); + Array array = Magic.toArray(obj); + VTable.Assert(span <= array.Length, + @"span <= array.Length"); +#endif // DEBUG + + UIntPtr baseAddr = objAddr+vtable.baseLength- + PreHeader.Size-PostHeader.Size; + int elSize = vtable.arrayElementSize; + UIntPtr begin = baseAddr+elSize*start; + UIntPtr end = begin+elSize*span; + for (UIntPtr el = begin; el < end; el += elSize) { + refCountIncrementer. + VisitReferenceFields(el, elVTable); + } + break; + } + + case ObjectLayout.STRING_TAG: { + break; + } + + default: { + VTable.NotReached("An unsupported tag found!"); + break; + } + } + } + + [ManualRefCounts] + [DisableNullChecks] + internal static unsafe void DecrementReferentRefCounts + (UIntPtr objAddr, + VTable vtable, + int start, + int span) { + uint objTag = (uint)vtable.pointerTrackingMask & 0xf; + + switch (objTag) { + case ObjectLayout.PTR_VECTOR_TAG: + case ObjectLayout.PTR_ARRAY_TAG: { +#if DEBUG + Object obj = Magic.fromAddress(objAddr); + Array array = Magic.toArray(obj); + VTable.Assert(span <= array.Length, + @"span <= array.Length"); +#endif // DEBUG + + UIntPtr* baseAddr = (UIntPtr*)(objAddr+ + vtable.baseLength-PreHeader.Size); + UIntPtr* begin = baseAddr+start; + UIntPtr* end = begin+span; + for (UIntPtr* el = begin; el < end; el++) { + UIntPtr addr = *el; + decrementRefCount(addr); + } + break; + } + + case ObjectLayout.OTHER_VECTOR_TAG: + case ObjectLayout.OTHER_ARRAY_TAG: { + if (vtable.arrayOf != StructuralType.Struct) { + break; + } + VTable elVTable = vtable.arrayElementClass; + uint elMask = (uint)elVTable.pointerTrackingMask; + if (elMask == ObjectLayout.SPARSE_TAG || + elMask == ObjectLayout.DENSE_TAG) { + break; + } +#if DEBUG + Object obj = Magic.fromAddress(objAddr); + Array array = Magic.toArray(obj); + VTable.Assert(span <= array.Length, + @"span <= array.Length"); +#endif // DEBUG + + UIntPtr baseAddr = objAddr+vtable.baseLength- + PreHeader.Size-PostHeader.Size; + int elSize = vtable.arrayElementSize; + UIntPtr begin = baseAddr+elSize*start; + UIntPtr end = begin+elSize*span; + for (UIntPtr el = begin; el < end; el += elSize) { + refCountDecrementer. + VisitReferenceFields(el, elVTable); + } + break; + } + + case ObjectLayout.STRING_TAG: { + break; + } + + default: { + VTable.NotReached("An unsupported tag found!"); + break; + } + } + } + + + [Inline] + [ManualRefCounts] + internal static void deallocateLazily(Object obj) { +#if DEBUG + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); +#endif // DEBUG + + setNextLink(obj, delayedDeallocationList); + delayedDeallocationList = obj; + if (!GC.HeapSizeConfigurable) { + delayedDeallocationLength++; + } + } + + [ManualRefCounts] + private static void deallocateObjects() { + int startTicks = 0; + bool enableGCTiming = VTable.enableGCTiming; + if (enableGCTiming) { + VTable.enableGCTiming = false; + startTicks = Environment.TickCount; + } + +#if MEASURE_RCPHASES + int ddListTicks = 0; + bool phaseFlag = ddListPhase; + if (!phaseFlag) { + ddListPhase = true; + ddListTicks = Environment.TickCount; + } +#endif // MEASURE_RCPHASES + + // Either continue working on old object, or extract + // a new object from the delayed deallocation list. + if (objectBeingDeallocated == null) { + objectBeingDeallocated = extractObjectFromDDList(); + } + int work = 0; + Object obj = objectBeingDeallocated; + while (obj != null) { + UIntPtr objAddr = Magic.addressOf(obj); + VTable vt = obj.vtable; + uint ptrMask = (uint)vt.pointerTrackingMask; + bool ongoing; + do { + ongoing = incrementalDecrement(objAddr, vt, ptrMask, + ref work); + } while (ongoing && work < deallocationSpan); + + if (!ongoing) { // Release object back to allocator. + releaseToAllocator(obj); + obj = extractObjectFromDDList(); + } + if (work >= deallocationSpan) { + break; + } + } + objectBeingDeallocated = obj; + +#if MEASURE_RCPHASES + if (!phaseFlag) { + int elapsed = Environment.TickCount-ddListTicks; + if (plcBufferPhase) { + plcBufferDDListProcTime += elapsed; + plcBufferDDListProcCount++; + } else { + ddListProcTime += elapsed; + ddListProcCount++; + } + ddListPhase = false; + } +#endif // MEASURE_RCPHASES + + if (enableGCTiming) { + int elapsedTicks = Environment.TickCount-startTicks; + BaseCollector.RegisterPause(elapsedTicks); + VTable.enableGCTiming = true; + } + } + + [ManualRefCounts] + private static Object extractObjectFromDDList() { + Object obj = delayedDeallocationList; + if (obj != null) { + delayedDeallocationList = getNextLink(obj); + if (!GC.HeapSizeConfigurable) { + delayedDeallocationLength--; + } + + UIntPtr objAddr = Magic.addressOf(obj); + initIncrementalDecrement(objAddr, obj.vtable); + +#if DEBUG + UIntPtr page = PageTable.Page(objAddr); + VTable.Assert(PageTable.IsGcPage(page), + @"PageTable.IsGcPage(page)"); +#endif // DEBUG + } + + return obj; + } + + [ManualRefCounts] + [DisableNullChecks] + private static unsafe void initIncrementalDecrement + (UIntPtr objAddr, + VTable vtable) { + uint ptrMask = (uint)vtable.pointerTrackingMask; + uint objTag = ptrMask & 0xf; + + switch (objTag) { + case ObjectLayout.SPARSE_TAG: + case ObjectLayout.DENSE_TAG: + case ObjectLayout.STRING_TAG : { + break; + } + + case ObjectLayout.PTR_VECTOR_TAG: + case ObjectLayout.OTHER_VECTOR_TAG: + case ObjectLayout.PTR_ARRAY_TAG: + case ObjectLayout.OTHER_ARRAY_TAG: { + Object obj = Magic.fromAddress(objAddr); + Array array = Magic.toArray(obj); + setLastTracked(objAddr, array.Length); + break; + } + + default: { + int* ptrDescriptor = (int*)ptrMask; + int initialCount = *ptrDescriptor; + setLastTracked(objAddr, initialCount); + break; + } + } + } + + [ManualRefCounts] + [DisableNullChecks] + private static unsafe bool incrementalDecrement(UIntPtr objAddr, + VTable vtable, + uint ptrMask, + ref int work) { + uint objTag = ptrMask & 0xf; + bool ongoing = false; + + if (objTag == ObjectLayout.SPARSE_TAG) { + UIntPtr* sparseObj = (UIntPtr*)objAddr; + work += 7; + for (ptrMask >>= 4; ptrMask != 0; ptrMask >>= 4) { + uint index = ptrMask & 0xf; + UIntPtr* loc = sparseObj+unchecked((int)index); + UIntPtr addr = *loc; + localDecrementRefCount(addr); + } + } else if (objTag == ObjectLayout.DENSE_TAG) { + UIntPtr* denseObj = (UIntPtr*)(objAddr+PostHeader.Size); + work += 28; + for (ptrMask >>= 4; ptrMask != 0; ptrMask >>= 1) { + if ((ptrMask & 0x1) != 0) { + UIntPtr addr = *denseObj; + localDecrementRefCount(addr); + } + denseObj++; + } + } else if (objTag == ObjectLayout.OTHER_VECTOR_TAG || + objTag == ObjectLayout.OTHER_ARRAY_TAG) { + if (vtable.arrayOf != StructuralType.Struct) { + return ongoing; + } + VTable elVTable = vtable.arrayElementClass; + uint elMask = (uint)elVTable.pointerTrackingMask; + if (elMask == ObjectLayout.SPARSE_TAG || + elMask == ObjectLayout.DENSE_TAG) { + return ongoing; + } + uint elObjTag = elMask & 0xf; + VTable.Assert(elObjTag == ObjectLayout.SPARSE_TAG || + elObjTag == ObjectLayout.DENSE_TAG, + @"elObjTag == ObjectLayout.SPARSE_TAG || + elObjTag == ObjectLayout.DENSE_TAG"); + VTable.Assert(work < deallocationSpan, + @"work < deallocationSpan"); + + int prev = getLastTracked(objAddr); + int elShift = elObjTag == ObjectLayout.SPARSE_TAG ? 2 : 4; + int workChunk = ((deallocationSpan-work) >> elShift)+1; + int last = prev > workChunk ? prev-workChunk : 0; + ongoing = last != 0; + if (ongoing) { + setLastTracked(objAddr, last); + } + + UIntPtr baseAddr = objAddr+vtable.baseLength- + PreHeader.Size-PostHeader.Size; + int size = vtable.arrayElementSize; + UIntPtr begin = baseAddr+(UIntPtr)(size*(prev-1)); + UIntPtr end = baseAddr+(UIntPtr)(size*last); + for (UIntPtr el = begin; el >= end; el -= size) { + incrementalDecrement(el, elVTable, elMask, ref work); + } + } else if (objTag != ObjectLayout.STRING_TAG) { + VTable.Assert(work < deallocationSpan, + @"work < deallocationSpan"); + + int prev = getLastTracked(objAddr); + int workChunk = deallocationSpan-work; + int last = prev > workChunk ? prev-workChunk : 0; + ongoing = last != 0; + if (ongoing) { + setLastTracked(objAddr, last); + } + work += prev-last; + + if (objTag == ObjectLayout.PTR_VECTOR_TAG || + objTag == ObjectLayout.PTR_ARRAY_TAG) { + UIntPtr baseAddr = + objAddr+vtable.baseLength-PreHeader.Size; + UIntPtr* begin = (UIntPtr*)baseAddr+prev-1; + UIntPtr* end = (UIntPtr*)baseAddr+last; + for (UIntPtr* el = begin; el >= end; el--) { + UIntPtr addr = *el; + localDecrementRefCount(addr); + } + } else { + VTable.Assert((objTag & 0x1) == 0, + @"(objTag & 0x1) == 0"); + + UIntPtr* largeObj = (UIntPtr*)objAddr; + int* ptrDescriptor = (int*)ptrMask; + for (int index = prev; index > last; index--) { + UIntPtr* loc = largeObj+*(ptrDescriptor+index); + UIntPtr addr = *loc; + localDecrementRefCount(addr); + } + } + } + + return ongoing; + } + + [Inline] + [ManualRefCounts] + private static void localDecrementRefCount(UIntPtr objAddr) { + if (objAddr != UIntPtr.Zero) { + nonNullLocalDecrementRefCount(Magic.fromAddress(objAddr)); + } + } + + [Inline] + [ManualRefCounts] + private static void incrementRefCount(UIntPtr objAddr) { + if (objAddr != UIntPtr.Zero) { + NonNullIncrementRefCount(Magic.fromAddress(objAddr)); + } + } + + [Inline] + [ManualRefCounts] + private static void decrementRefCount(UIntPtr objAddr) { + if (objAddr != UIntPtr.Zero) { + NonNullDecrementRefCount(Magic.fromAddress(objAddr)); + } + } + + + [ManualRefCounts] + [DisableNullChecks] + private static Object getNextLink(Object obj) { + return Magic.fromAddress(MultiUseWord.GetValForObject(obj)); + } + + [ManualRefCounts] + [DisableNullChecks] + private static void setNextLink(Object obj, Object next) { + MultiUseWord.SetValForObject(obj, Magic.addressOf(next)); + } + + [ManualRefCounts] + [DisableNullChecks] + private static int getLastTracked(UIntPtr objAddr) { + Object obj = Magic.fromAddress(objAddr); + return unchecked((int)(uint)MultiUseWord.GetValForObject(obj)); + } + + [ManualRefCounts] + [DisableNullChecks] + private static void setLastTracked(UIntPtr objAddr, int last) { + Object obj = Magic.fromAddress(objAddr); + MultiUseWord.SetValForObject(obj, (UIntPtr)unchecked((uint)last)); + } + + [ManualRefCounts] + private static UIntPtr getBackupRefCount(Object obj) { + return ((DeferredRCVerificationObject)obj).preHeader. + backupRefCount; + } + + [ManualRefCounts] + private static void setBackupRefCount(Object obj, UIntPtr count) { + ((DeferredRCVerificationObject)obj).preHeader. + backupRefCount = count; + } + + [ManualRefCounts] + private static UIntPtr getDfsDiscoveryTime(Object obj) { + return ((DeferredRCVerificationObject)obj).preHeader. + dfsDiscoveryTime; + } + + [ManualRefCounts] + private static void setDfsDiscoveryTime(Object obj, + UIntPtr time) { + ((DeferredRCVerificationObject)obj).preHeader. + dfsDiscoveryTime = time; + } + + [ManualRefCounts] + private static UIntPtr getDfsFinishingTime(Object obj) { + return ((DeferredRCVerificationObject)obj).preHeader. + dfsFinishingTime; + } + + [ManualRefCounts] + private static void setDfsFinishingTime(Object obj, + UIntPtr time) { + ((DeferredRCVerificationObject)obj).preHeader. + dfsFinishingTime = time; + } + + [ManualRefCounts] + private static void allocatePLCBuffer(uint count) { + VTable vtable = ((RuntimeType)typeof(UIntPtr[])).classVtable; + + plcRawSize = ObjectLayout.ArraySize(vtable, count); + plcRawAddr = MemoryManager.AllocateMemory(plcRawSize); + PageManager.SetStaticDataPages(plcRawAddr, plcRawSize); + + BumpAllocator pool = new BumpAllocator(PageType.NonGC); + pool.SetZeroedRange(plcRawAddr, plcRawSize); + uint alignment = vtable.baseAlignment; + UIntPtr addr = pool.AllocateFast(plcRawSize, alignment); + Array result = Magic.toArray(Magic.fromAddress(addr)); + + result.InitializeVectorLength(unchecked((int)count)); + result.REF_STATE = 1 & ~countingONFlagMask; + result.vtable = vtable; + + plcBuffer = Magic.toUIntPtrVector(result); + } + + [ManualRefCounts] + [DisableBoundsChecks] + private static void reallocatePLCBuffer() { + UIntPtr oldPLCRawSize = plcRawSize; + UIntPtr oldPLCRawAddr = plcRawAddr; + UIntPtr[] oldPLCBuffer = plcBuffer; + + uint oldNumEntries = unchecked((uint)oldPLCBuffer.Length); + uint newNumEntries = oldNumEntries << 1; + allocatePLCBuffer(newNumEntries); + + UIntPtr[] newPLCBuffer = plcBuffer; + for (uint i = 0; i < oldNumEntries; i++) { + newPLCBuffer[i] = oldPLCBuffer[i]; + } + + MemoryManager.FreeMemory(oldPLCRawAddr, oldPLCRawSize); + } + + [ManualRefCounts] + [DisableBoundsChecks] + private static void stitchFreePLCSlots(uint firstFreeSlot) { + UIntPtr[] plcBuffer = + DeferredReferenceCountingCollector.plcBuffer; + + int plcNumEntries = plcBuffer.Length; + for (uint i = firstFreeSlot; i < plcNumEntries-1; i++) { + plcBuffer[i] = (UIntPtr)(markFlagMask | (i+1)); + } + plcBuffer[plcNumEntries-1] = (UIntPtr)markFlagMask; + freePLCHead = firstFreeSlot; + } + + [ManualRefCounts] + [DisableBoundsChecks] + private static void addToPLCBuffer(Object obj) { + UIntPtr[] plcBuffer = + DeferredReferenceCountingCollector.plcBuffer; + uint freePLCHead = + DeferredReferenceCountingCollector.freePLCHead; + + // Check if free PLC buffer entries are available. + if (freePLCHead == 0) { + uint plcNumEntries = unchecked((uint)plcBuffer.Length); + if ((plcNumEntries < maxPLCNumEntries) || isInGC) { + reallocatePLCBuffer(); + plcBuffer = + DeferredReferenceCountingCollector.plcBuffer; + stitchFreePLCSlots(plcNumEntries); + + if (isInGC) { + // If in GC, you should not trigger another GC. + // Therefore allocate, and set a flag. PLC buffer + // will be cleaned up in next step of current GC. + needCleanPLCBuffer = true; + } + } else { + // NOT in GC, then collect. + needCleanPLCBuffer = true; + GC.Collect(); + } + freePLCHead = + DeferredReferenceCountingCollector.freePLCHead; + } + VTable.Assert((obj.REF_STATE & refCountMask) >= 1, + @"obj.REF_STATE & refCountMask >=1"); + VTable.Assert(!ZeroCountTable.isInZCT(obj), + @"!ZeroCountTable.isInZCT(obj)"); + + // GC.Collect may already add it into the PLC buffer. + uint index = GetPLCIndex(obj); + if (index != 0) { + return; + } + + // Insert object into the PLC buffer. + uint entry = (uint)plcBuffer[freePLCHead]; + uint newFreePLCHead = entry & ~markFlagMask; + plcBuffer[freePLCHead] = Magic.addressOf(obj); + + // Point the object to its slot in the PLC buffer. + SetPLCIndex(obj, freePLCHead); + + // Update the free PLC entries' head. + DeferredReferenceCountingCollector.freePLCHead = + newFreePLCHead; + } + + [Inline] + [ManualRefCounts] + [DisableBoundsChecks] + internal static void removeFromPLCBuffer(uint index) { + // The object needs to be removed from the PLC buffer. + plcBuffer[index] = (UIntPtr)(freePLCHead | markFlagMask); + freePLCHead = index; + } + + [ManualRefCounts] + [DisableNullChecks] + [DisableBoundsChecks] + private static void processPLCBuffer() { + int startTicks = 0; + bool enableGCTiming = VTable.enableGCTiming; + if (enableGCTiming) { + VTable.enableGCTiming = false; + startTicks = Environment.TickCount; + } + +#if MEASURE_RCPHASES + int plcBufferTicks = 0; + bool phaseFlag = plcBufferPhase; + if (!phaseFlag) { + plcBufferPhase = true; + plcBufferTicks = Environment.TickCount; + } +#endif // MEASURE_RCPHASES + + UIntPtr[] plcBuffer = + DeferredReferenceCountingCollector.plcBuffer; + + int plcNumEntries = plcBuffer.Length; + + // Let S be the subgraph of heap objects reachable from + // the PLC buffer. Decrement counts due to references in S. + for (int i = 1; i < plcNumEntries; i++) { + UIntPtr objAddr = plcBuffer[i]; + if (((uint)objAddr & markFlagMask) != 0) { + continue; + } + + VTable.Assert(objAddr != UIntPtr.Zero, + @"objAddr != UIntPtr.Zero"); + + Object obj = Magic.fromAddress(objAddr); + uint refState = obj.REF_STATE; + VTable.Assert((refState & countingONFlagMask) != 0, + @"(refState & countingONFlagMask) != 0"); + VTable.Assert((refState & acyclicFlagMask) == 0, + @"(refState & acyclicFlagMask) == 0"); + + if ((refState & markFlagMask) == 0) { + obj.REF_STATE = refState | markFlagMask; + internalDecrementer.Traverse(objAddr); + } + } + + // Objects that now have non-zero counts are those that + // have references external to S incident on them. + // Recompute counts due to reachability from such objects. + for (int i = 1; i < plcNumEntries; i++) { + UIntPtr objAddr = plcBuffer[i]; + if (((uint)objAddr & markFlagMask) != 0) { + continue; + } + + internalScanner.Traverse(objAddr); + } + + // String together objects with reference count + // of zero for reclamation. + internalReclaimer.Initialize(); + for (int i = 1; i < plcNumEntries; i++) { + UIntPtr objAddr = plcBuffer[i]; + if (((uint)objAddr & markFlagMask) != 0) { + continue; + } + + Object obj = Magic.fromAddress(objAddr); + uint refState = obj.REF_STATE; + const uint mask = markFlagMask | acyclicFlagMask; + VTable.Assert((refState & mask) == 0 || + refState == ~countingONFlagMask, + @"(refState & mask) == 0 || + refState == ~countingONFlagMask"); + + if (refState == countingONFlagMask) { + internalReclaimer.Traverse(objAddr); + } else { + SetPLCIndex(obj, 0); + } + } + ulong reclaimedBytes = 0; + Object reclaimedObj = internalReclaimer.ReclaimedObjects; + while (reclaimedObj != null) { + if (VTable.enableGCProfiling) { + UIntPtr size = ObjectLayout.Sizeof(reclaimedObj); + reclaimedBytes += (ulong)size; + } + Object nextReclaimedObj = getNextLink(reclaimedObj); + releaseToAllocator(reclaimedObj); + reclaimedObj = nextReclaimedObj; + } + + // Recycle the PLC buffer. + stitchFreePLCSlots(1); + + // Release the memory used up by work lists. + UnmanagedPageList.ReleaseStandbyPages(); + + if (VTable.enableGCProfiling) { + if (maxCyclicGarbage < reclaimedBytes) { + maxCyclicGarbage = reclaimedBytes; + } + totalCyclicGarbage += reclaimedBytes; + cycleCollections++; + } + +#if MEASURE_RCPHASES + if (!phaseFlag) { + int elapsed = Environment.TickCount-plcBufferTicks; + if (ddListPhase) { + ddListPLCProcTime += elapsed; + ddListPLCProcCount++; + } else { + plcBufferProcTime += elapsed; + plcBufferProcCount++; + } + plcBufferPhase = false; + } +#endif // MEASURE_RCPHASES + + if (enableGCTiming) { + int elapsedTicks = Environment.TickCount-startTicks; + BaseCollector.RegisterPause(elapsedTicks); + VTable.enableGCTiming = true; + } + } + + + [NoInline] + [ManualRefCounts] + private static void deallocationListChecker() { + // Check for nonzero reference counts. + for (Object block = delayedDeallocationList; + block != null; block = getNextLink(block)) { + UIntPtr objAddr = Magic.addressOf(block); + UIntPtr page = PageTable.Page(objAddr); + if (!PageTable.IsGcPage(page)) { + VTable.DebugPrint("Non-GC memory for freeing!\n"); + VTable.DebugBreak(); + } + if ((block.REF_STATE & refCountMask) != 0) { + VTable.DebugPrint("Non-zero reference count!\n"); + VTable.DebugBreak(); + } + } + + // Check for loops in the delayed deallocation list. + for (Object block = delayedDeallocationList; + block != null; block = getNextLink(block)) { + block.REF_STATE++; + } + for (Object block = delayedDeallocationList; + block != null; block = getNextLink(block)) { + if ((block.REF_STATE & refCountMask) != 1) { + VTable.DebugPrint("Loops in DD list!\n"); + VTable.DebugBreak(); + } + } + for (Object block = delayedDeallocationList; + block != null; block = getNextLink(block)) { + block.REF_STATE--; + } + } + + [Inline] + [ManualRefCounts] + private static bool nonEmptyDDList() { + return objectBeingDeallocated != null || + delayedDeallocationList != null; + } + + [NoInline] + [ManualRefCounts] + private static void purgeDeallocationList() { + while (nonEmptyDDList()) { + deallocateObjects(); + } + } + + [Inline] + [ManualRefCounts] + private static void releaseToAllocator(Object obj) { +#if MEASURE_RCPHASES + int segFreeTicks = Environment.TickCount; +#endif // MEASURE_RCPHASES + + UIntPtr objStart = Magic.addressOf(obj)-PreHeader.Size; + UIntPtr page = PageTable.Page(objStart); + PageType pageType = PageTable.Type(page); + if (pageType == SegregatedFreeList.SMALL_OBJ_PAGE) { + uint alignment = obj.vtable.baseAlignment; + SegregatedFreeList.LocalFreeSmall(objStart, alignment); + } else { + VTable.Assert(pageType == SegregatedFreeList. + LARGE_OBJ_START, + @"pageType == SegregatedFreeList. + LARGE_OBJ_START"); + + SegregatedFreeList.FreeLarge(obj); + } + +#if MEASURE_RCPHASES + segFreeTime += Environment.TickCount-segFreeTicks; +#endif // MEASURE_RCPHASES + + if (!GC.HeapSizeConfigurable) { + releasedObjectCount++; + if (releasedObjectCount > recycleTrigger) { + recycleAllocator(); + } + } + } + + [Inline] + [ManualRefCounts] + private static void recycleAllocator() { +#if MEASURE_RCPHASES + int segCommitTicks = Environment.TickCount; +#endif // MEASURE_RCPHASES + + if (!GC.HeapSizeConfigurable) { + releasedObjectCount = 0; + } + SegregatedFreeList.LocalRecycleGlobalPages(); + SegregatedFreeList.CommitFreedData(); + +#if MEASURE_RCPHASES + segCommitTime += Environment.TickCount-segCommitTicks; +#endif // MEASURE_RCPHASES + } + + + private class RefCountIncrementer : NonNullReferenceVisitor { + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + incrementRefCount(objAddr); + } + } + + private class RefCountDecrementer : NonNullReferenceVisitor { + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + decrementRefCount(objAddr); + } + } + + private class StackRefCountIncrementer: NonNullReferenceVisitor { + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr *loc) { + UIntPtr addr = *loc; + + UIntPtr page = PageTable.Page(addr); + if (!PageTable.IsGcPage(page)) { +#if DEBUG + PageType pageType = PageTable.Type(page); + VTable.Assert(pageType == PageType.NonGC || + pageType == PageType.Stack, + @"pageType == PageType.NonGC || + pageType == PageType.Stack"); +#endif // DEBUG + + return; + } + + UIntPtr objAddr = SegregatedFreeList.Find(addr); + if (objAddr != UIntPtr.Zero) { + Object obj = Magic.fromAddress(objAddr); + nonNullLocalIncrementRefCount(obj); + } + } + } + + private class StackRefCountDecrementer: NonNullReferenceVisitor { + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr *loc) { + UIntPtr addr = *loc; + + UIntPtr page = PageTable.Page(addr); + if (!PageTable.IsGcPage(page)) { +#if DEBUG + PageType pageType = PageTable.Type(page); + VTable.Assert(pageType == PageType.NonGC || + pageType == PageType.Stack, + @"pageType == PageType.NonGC || + pageType == PageType.Stack"); +#endif // DEBUG + + return; + } + + UIntPtr objAddr = SegregatedFreeList.Find(addr); + Object obj = Magic.fromAddress(objAddr); + if (obj != null) { + NonNullDecrementRefCount(obj); + } + } + } + + private class InternalDecrementer : NonNullReferenceVisitor { + [ManualRefCounts] + internal void Traverse(UIntPtr objAddr) { + Object obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + while (!this.workList.IsEmpty) { + objAddr = this.workList.Read(); + obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + } + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + Object obj = Magic.fromAddress(objAddr); + uint refState = obj.REF_STATE; + if ((refState & countingONFlagMask) == 0) { + return; + } + uint refCount = refState & refCountMask; + VTable.Assert(refCount > 0, + @"refCount > 0"); + + refState--; + const uint mask = markFlagMask | acyclicFlagMask; + if ((refState & mask) == 0) { + refState |= markFlagMask; + this.workList.Write(objAddr); + } + obj.REF_STATE = refState; + } + + private UIntPtrQueue workList; + } + + private class InternalScanner : NonNullReferenceVisitor { + [ManualRefCounts] + internal unsafe void Traverse(UIntPtr objAddr) { + this.Visit(&objAddr); + while (!this.workList.IsEmpty) { + objAddr = this.workList.Read(); + Object obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + } + } + + [ManualRefCounts] + [DisableNullChecks] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + Object obj = Magic.fromAddress(objAddr); + uint refState = obj.REF_STATE; + const uint mask1 = countingONFlagMask | markFlagMask; + const uint mask2 = mask1 | acyclicFlagMask; + if ((refState & mask2) != mask1) { + return; + } + + obj.REF_STATE = refState & ~markFlagMask; + if (refState > mask1) { + internalIncrementer.Traverse(objAddr); + } else { + this.workList.Write(objAddr); + } + } + + private UIntPtrQueue workList; + } + + private class InternalIncrementer : NonNullReferenceVisitor { + [ManualRefCounts] + internal void Traverse(UIntPtr objAddr) { + Object obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + while (!this.workList.IsEmpty) { + objAddr = this.workList.Read(); + obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + } + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + Object obj = Magic.fromAddress(objAddr); + uint refState = obj.REF_STATE; + if ((refState & countingONFlagMask) == 0) { + return; + } + uint refCount = refState & refCountMask; + VTable.Assert(refCount < refCountMask, + @"refCount < refCountMask"); + + refState++; + const uint mask1 = markFlagMask | acyclicFlagMask; + const uint mask2 = countingONFlagMask | 1; + if ((refState & mask1) == markFlagMask) { + // The object hasn't been visited either + // by the scanner or the incrementer. + refState &= ~markFlagMask; + this.workList.Write(objAddr); + } else if (refState == mask2) { + // The object has been visited in the + // past, but only by the scanner. + this.workList.Write(objAddr); + } + obj.REF_STATE = refState; + } + + private UIntPtrQueue workList; + } + + private class InternalReclaimer : NonNullReferenceVisitor { + internal Object ReclaimedObjects; + + [ManualRefCounts] + internal void Initialize() { + this.ReclaimedObjects = null; + } + + [ManualRefCounts] + internal unsafe void Traverse(UIntPtr objAddr) { + this.Reclaim(objAddr); + while (!this.workList.IsEmpty) { + objAddr = this.workList.Read(); + Object obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + } + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + Object obj = Magic.fromAddress(objAddr); + uint refState = obj.REF_STATE; + const uint mask = countingONFlagMask | acyclicFlagMask; + if (refState == mask) { + deallocateLazily(obj); + obj.REF_STATE = mask | markFlagMask; + } else if (refState == countingONFlagMask) { + Reclaim(objAddr); + } + } + + [ManualRefCounts] + internal void Reclaim(UIntPtr objAddr) { + Object obj = Magic.fromAddress(objAddr); + setNextLink(obj, this.ReclaimedObjects); + obj.REF_STATE = ~countingONFlagMask; + this.ReclaimedObjects = obj; + this.workList.Write(objAddr); + } + + private UIntPtrQueue workList; + } + + private abstract class ObjectVisitor : + SegregatedFreeList.ObjectVisitor { + [ManualRefCounts] + internal override void VisitSmall(Object obj, + UIntPtr memAddr) { + this.Visit(obj); + } + + [ManualRefCounts] + internal override UIntPtr VisitLarge(Object obj) { + return this.Visit(obj); + } + + internal abstract override UIntPtr Visit(Object obj); + + } + + private class BackupInitializer : ObjectVisitor { + [ManualRefCounts] + internal override UIntPtr Visit(Object obj) { + setBackupRefCount(obj, UIntPtr.Zero); + + return ObjectLayout.Sizeof(obj); + } + } + + private class BackupRefCount : NonNullReferenceVisitor { + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr addr = *loc; + + UIntPtr page = PageTable.Page(addr); + if (!PageTable.IsGcPage(page)) { + PageType pageType = PageTable.Type(page); + VTable.Assert(pageType == PageType.NonGC || + pageType == PageType.Stack, + @"pageType == PageType.NonGC || + pageType == PageType.Stack"); + + return; + } + + UIntPtr objAddr = SegregatedFreeList.Find(addr); + incrementBackupRefCount.Traverse(objAddr); + } + } + + private class IncrementBackupRefCount : NonNullReferenceVisitor { + [ManualRefCounts] + internal void Traverse(UIntPtr objAddr) { + Object obj = Magic.fromAddress(objAddr); + UIntPtr count = getBackupRefCount(obj); + setBackupRefCount(obj, count+1); + if (obj.GcMark((UIntPtr)1)) { + this.VisitReferenceFields(obj); + } + while (!this.workList.IsEmpty) { + objAddr = this.workList.Read(); + obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + } + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr addr = *loc; + + UIntPtr page = PageTable.Page(addr); + if (!PageTable.IsGcPage(page)) { + PageType pageType = PageTable.Type(page); + VTable.Assert(pageType == PageType.NonGC || + pageType == PageType.Stack, + @"pageType == PageType.NonGC || + pageType == PageType.Stack"); + + return; + } + + UIntPtr objAddr = SegregatedFreeList.Find(addr); + Object obj = Magic.fromAddress(objAddr); + UIntPtr count = getBackupRefCount(obj); + setBackupRefCount(obj, count+1); + if (obj.GcMark((UIntPtr)1)) { + this.workList.Write(objAddr); + } + } + + private UIntPtrQueue workList; + } + + private class BackupReconciler : ObjectVisitor { + [ManualRefCounts] + internal override UIntPtr Visit(Object obj) { + VTable vtable = obj.vtable; + UIntPtr objAddr = Magic.addressOf(obj); + UIntPtr size = ObjectLayout.ObjectSize(objAddr, vtable); + + uint refState = obj.REF_STATE; + if ((refState & countingONFlagMask) != 0) { + UIntPtr refCount = + (UIntPtr)(refState & refCountMask); + UIntPtr count = getBackupRefCount(obj); + if (count > refCount) { + VTable.DebugPrint("count > refCount!\n"); + VTable.DebugBreak(); + } + } + + return size; + } + } + + + private class RootsScanner : NonNullReferenceVisitor { + internal void Initialize(NonNullReferenceVisitor v) { + this.visitor = v; + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr pageLoc = PageTable.Page((UIntPtr)loc); + PageType pageType = PageTable.Type(pageLoc); + if (pageType != PageType.NonGC && + pageType != PageType.Stack) { + VTable.Assert(PageTable.IsGcPage(pageLoc), + @"PageTable.IsGcPage(pageLoc)"); + + return; + } + + uint addr = (uint)*loc; + if (pageType == PageType.NonGC || (addr & 0x03) == 0) { + this.visitor.Visit(loc); + } + if (pageType == PageType.Stack) { + *loc = (UIntPtr)(addr | 0x01); + } + } + + NonNullReferenceVisitor visitor; + } + + private class ResetRoots : NonNullReferenceVisitor { + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr pageLoc = PageTable.Page((UIntPtr)loc); + PageType pageType = PageTable.Type(pageLoc); + if (pageType != PageType.NonGC && + pageType != PageType.Stack) { + VTable.Assert(PageTable.IsGcPage(pageLoc), + @"PageTable.IsGcPage(pageLoc)"); + + return; + } + + if (pageType == PageType.Stack) { + *loc = *loc & ~((UIntPtr)0x3); + } + } + } + + private class LeakAccumulator : ObjectVisitor { + internal UIntPtr Size; + + internal void Initialize() { + this.Size = UIntPtr.Zero; + } + + [ManualRefCounts] + internal override UIntPtr Visit(Object obj) { + VTable vtable = obj.vtable; + UIntPtr objAddr = Magic.addressOf(obj); + UIntPtr size = ObjectLayout.ObjectSize(objAddr, vtable); + + uint refState = obj.REF_STATE; + UIntPtr refCount = (UIntPtr)(refState & refCountMask); + if ((refState & countingONFlagMask) != 0 && + refCount > 0) { + // This object is considered live by the + // reference counting collector. + UIntPtr count = getBackupRefCount(obj); + if (count == 0) { + // But it is actually unreachable. + this.Size += size; + } + } + + return size; + } + } + + private class LeakedNodesDFS : ObjectVisitor { + [ManualRefCounts] + internal override unsafe UIntPtr Visit(Object obj) { + VTable vtable = obj.vtable; + UIntPtr objAddr = Magic.addressOf(obj); + UIntPtr size = ObjectLayout.ObjectSize(objAddr, vtable); + + uint refState = obj.REF_STATE; + UIntPtr refCount = (UIntPtr)(refState & refCountMask); + if ((refState & countingONFlagMask) != 0 && + refCount > 0) { + UIntPtr count = getBackupRefCount(obj); + if (count == 0) { + dfs.Visit(&objAddr); + } + } + + return size; + } + } + + private class DFS : NonNullReferenceVisitor { + internal void Initialize() { + this.time = UIntPtr.Zero; + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + + UIntPtr page = PageTable.Page(objAddr); + if (!PageTable.IsGcPage(page)) { + PageType pageType = PageTable.Type(page); + VTable.Assert(pageType == PageType.NonGC || + pageType == PageType.Stack, + @"pageType == PageType.NonGC || + pageType == PageType.Stack"); + + return; + } + + Object obj = Magic.fromAddress(objAddr); + if (obj.GcMark((UIntPtr)1)) { + this.time = this.time+1; + setDfsDiscoveryTime(obj, this.time); + + UIntPtr vtableAddr = Magic.addressOf(obj.vtable); + this.Visit(&vtableAddr); + this.VisitReferenceFields(obj); + + this.time = this.time+1; + setDfsDiscoveryTime(obj, this.time); + } + } + + private UIntPtr time; + } + + private class LeakedCycleClosure : ObjectVisitor { + [ManualRefCounts] + internal override UIntPtr Visit(Object obj) { + VTable vtable = obj.vtable; + UIntPtr objAddr = Magic.addressOf(obj); + UIntPtr size = ObjectLayout.ObjectSize(objAddr, vtable); + + uint refState = obj.REF_STATE; + UIntPtr refCount = (UIntPtr)(refState & refCountMask); + if ((refState & countingONFlagMask) != 0 && + refCount > 0) { + UIntPtr count = getBackupRefCount(obj); + if (count == 0) { + UIntPtr dTime = getDfsDiscoveryTime(obj); + UIntPtr fTime = getDfsFinishingTime(obj); + cycleClosure.Initialize(dTime, fTime); + cycleClosure.VisitReferenceFields(obj); + } + } + + return size; + } + } + + private class CycleClosure : NonNullReferenceVisitor { + internal void Initialize(UIntPtr dTime, UIntPtr fTime) { + this.predDiscoveryTime = dTime; + this.predFinishingTime = fTime; + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + + UIntPtr page = PageTable.Page(objAddr); + if (!PageTable.IsGcPage(page)) { + PageType pageType = PageTable.Type(page); + VTable.Assert(pageType == PageType.NonGC || + pageType == PageType.Stack, + @"pageType == PageType.NonGC || + pageType == PageType.Stack"); + + return; + } + + Object obj = Magic.fromAddress(objAddr); + UIntPtr dTime = getDfsDiscoveryTime(obj); + UIntPtr fTime = getDfsFinishingTime(obj); + VTable.Assert(this.predDiscoveryTime > UIntPtr.Zero && + this.predFinishingTime > UIntPtr.Zero && + dTime > UIntPtr.Zero && + fTime > UIntPtr.Zero, + @"this.predDiscoveryTime > UIntPtr.Zero && + this.predFinishingTime > UIntPtr.Zero && + dTime > UIntPtr.Zero && + fTime > UIntPtr.Zero"); + + if (dTime < this.predDiscoveryTime && + this.predDiscoveryTime < this.predFinishingTime && + this.predFinishingTime < fTime) { + // A back edge is incident on this node; + // therefore, the node is part of a cycle. + backupRefCount.Visit(&objAddr); + } + } + + private UIntPtr predDiscoveryTime; + private UIntPtr predFinishingTime; + } + + private class ResetTraversal : ObjectVisitor { + [ManualRefCounts] + internal override UIntPtr Visit(Object obj) { + obj.GcMark(UIntPtr.Zero); + VTable vtable = obj.vtable; + UIntPtr objAddr = Magic.addressOf(obj); + UIntPtr size = ObjectLayout.ObjectSize(objAddr, vtable); + + return size; + } + } + + private class BFSMarker : NonNullReferenceVisitor { + internal void Initialize(bool isVisited) { + this.isVisited = (UIntPtr)(isVisited ? 1 : 0); + } + + [ManualRefCounts] + internal void Traverse(Object obj) { + this.VisitReferenceFields(obj); + while (!this.workList.IsEmpty) { + UIntPtr objAddr = this.workList.Read(); + obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + } + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr addr = *loc; + + UIntPtr page = PageTable.Page(addr); + if (!PageTable.IsGcPage(page)) { + PageType pageType = PageTable.Type(page); + VTable.Assert(pageType == PageType.NonGC || + pageType == PageType.Stack, + @"pageType == PageType.NonGC || + pageType == PageType.Stack"); + + return; + } + + UIntPtr objAddr = SegregatedFreeList.Find(addr); + Object obj = Magic.fromAddress(objAddr); + if (obj.GcMark(this.isVisited)) { + this.workList.Write(objAddr); + } + } + + private UIntPtr isVisited; + private UIntPtrQueue workList; + } + + private class LeakedRoots : ObjectVisitor { + internal void Initialize() { + bfsMarker.Initialize(true); + } + + [ManualRefCounts] + internal override UIntPtr Visit(Object obj) { + VTable vtable = obj.vtable; + UIntPtr objAddr = Magic.addressOf(obj); + UIntPtr size = ObjectLayout.ObjectSize(objAddr, vtable); + + uint refState = obj.REF_STATE; + UIntPtr refCount = (UIntPtr)(refState & refCountMask); + if ((refState & countingONFlagMask) != 0 && + refCount > 0) { + UIntPtr count = getBackupRefCount(obj); + if (count == 0 && obj.GcMark() == UIntPtr.Zero) { + bfsMarker.Traverse(obj); + } + } + + return size; + } + } + + private class LeakedRootsCounter : ObjectVisitor { + internal uint Total; + + internal void Initialize() { + this.Total = 0; + } + + [ManualRefCounts] + internal override UIntPtr Visit(Object obj) { + VTable vtable = obj.vtable; + UIntPtr objAddr = Magic.addressOf(obj); + UIntPtr size = ObjectLayout.ObjectSize(objAddr, vtable); + + uint refState = obj.REF_STATE; + UIntPtr refCount = (UIntPtr)(refState & refCountMask); + if ((refState & countingONFlagMask) != 0 && + refCount > 0) { + UIntPtr count = getBackupRefCount(obj); + if (count == 0 && obj.GcMark() == UIntPtr.Zero) { + this.Total++; + } + } + + return size; + } + } + } +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/EmptyWriteBarrier.cs b/base/Imported/Bartok/runtime/shared/GCs/EmptyWriteBarrier.cs new file mode 100644 index 0000000..1bd9e21 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/EmptyWriteBarrier.cs @@ -0,0 +1,109 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using Microsoft.Bartok.Runtime; + using System.Runtime.CompilerServices; + using System.Threading; + + //[NoBarriers] + internal unsafe class EmptyWriteBarrier : RefWriteBarrier + { + + internal static EmptyWriteBarrier instance; + + [NoBarriers] + internal static new void Initialize() { + EmptyWriteBarrier.instance = (EmptyWriteBarrier) + BootstrapMemory.Allocate(typeof(EmptyWriteBarrier)); + } + + [Inline] + protected override void CopyStructImpl(Object srcObj, + Object dstObj, + VTable vtable, + UIntPtr srcPtr, + UIntPtr dstPtr) + { + CopyStructNoBarrier(vtable, srcPtr, dstPtr); + } + + [NoBarriers] + [Inline] + protected override Object AtomicSwapImpl(ref Object reference, + Object value, + int mask) + { + return Interlocked.Exchange(ref reference, value); + } + + [NoBarriers] + [Inline] + protected override + Object AtomicCompareAndSwapImpl(ref Object reference, + Object newValue, + Object comparand, + int mask) + { + return Interlocked.CompareExchange(ref reference, + newValue, + comparand); + } + + [Inline] + protected override void CloneImpl(Object srcObject, Object dstObject) + { + CloneNoBarrier(srcObject, dstObject); + } + + // 'offset' is not relative to the lower bound, but is a count + // of elements from the first element in the array. + [Inline] + protected override void ArrayZeroImpl(Array array, + int offset, + int length) + { + ArrayZeroNoBarrier(array, offset, length); + } + + // 'offset' is not relative to the lower bound, but is a count + // of elements from the first element in the array. + [Inline] + protected override void ArrayCopyImpl(Array srcArray, int srcOffset, + Array dstArray, int dstOffset, + int length) + { + ArrayCopyNoBarrier(srcArray, srcOffset, + dstArray, dstOffset, + length); + } + + [Inline] + protected override void WriteImpl(UIntPtr *location, + Object value, + int mask) + { + *location = Magic.addressOf(value); + } + + [Inline] + [NoBarriers] + protected override void WriteImplByRef(ref Object location, + Object value, + int mask) + { + location = value; + } + + } + +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/ExpandingCoCoBarrier.cs b/base/Imported/Bartok/runtime/shared/GCs/ExpandingCoCoBarrier.cs new file mode 100644 index 0000000..2e9f14c --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/ExpandingCoCoBarrier.cs @@ -0,0 +1,1620 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using Microsoft.Bartok.Runtime; + using Microsoft.Bartok.Options; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using System.Threading; + + internal unsafe class ExpandingCoCoBarrier: LowAbortCoCoBarrier { + + internal static new ExpandingCoCoBarrier instance; + + [NoBarriers] + internal static new void Initialize() + { + ExpandingCoCoBarrier.instance = + (ExpandingCoCoBarrier) + BootstrapMemory.Allocate(typeof(ExpandingCoCoBarrier)); + ExpandingCoCoBarrier.instance.InitEarly(); + LowAbortCoCoBarrier.Initialize(); + } + + internal enum ObjState { + SimpleOrForwarded = 0, + Tagged = 1, + Tainted = 2, + Expanded = 3, + } + + [NoBarriers] + internal struct ForwardingWord { + private UIntPtr original; + private UIntPtr CoCoWord; + + internal ForwardingWord(Object original, + UIntPtr CoCoWord) + { + this.original = Magic.addressOf(original); + this.CoCoWord = CoCoWord; + } + + internal ForwardingWord(UIntPtr original, + UIntPtr CoCoWord) + { + this.original = original; + this.CoCoWord = CoCoWord; + } + + internal ForwardingWord(Object original, + ObjState state, + UIntPtr forward) + { + this.original = Magic.addressOf(original); + this.CoCoWord = ((UIntPtr)(uint)(int)state)|forward; + } + + internal ForwardingWord(Object original, + ObjState state, + Object forward) + { + this.original = Magic.addressOf(original); + this.CoCoWord = ((UIntPtr)(uint)(int)state)|Magic.addressOf(forward); + } + + internal ForwardingWord(UIntPtr original, + ObjState state, + UIntPtr forward) + { + this.original = original; + this.CoCoWord = ((UIntPtr)(uint)(int)state)|forward; + } + + internal bool IsClear + { + get { + return CoCoWord == UIntPtr.Zero; + } + } + + internal UIntPtr Bits + { + get { + return CoCoWord; + } + } + + // This is messed up. I don't like it. + + internal UIntPtr ForwardBitsRaw + { + get { + return CoCoWord&~(UIntPtr)3; + } + } + + internal UIntPtr ForwardBits + { + get { + UIntPtr result = UIntPtr.Zero; + if (State==ObjState.SimpleOrForwarded) { + result = ForwardBitsRaw; + } + if (result==UIntPtr.Zero) { + return original; + } else { + return result; + } + } + } + + internal Object Forward { + get { return Magic.fromAddress(ForwardBits); } + } + + internal WideObject Wide + { + get { + return (WideObject)Magic.fromAddress(ForwardBitsRaw); + } + } + + internal UIntPtr ForwardInternal(UIntPtr oldPtr) + { + return (oldPtr-original)+ForwardBits; + } + + internal ObjState State + { + get { + return (ObjState)(int)(CoCoWord&3); + } + } + + internal ForwardingWord WithState(ObjState state) + { + return new ForwardingWord(original, + state, + ForwardBitsRaw); + } + + internal ForwardingWord WithForwardBits(UIntPtr forwardBits) + { + return new ForwardingWord(original, + State, + forwardBits); + } + + internal ForwardingWord WithForward(Object o) + { + return WithForwardBits(Magic.addressOf(o)); + } + + internal ForwardingWord WithWide(WideObject w) + { + return WithForward(w); + } + + internal ForwardingWord Clear() + { + return new ForwardingWord(original, + ObjState.SimpleOrForwarded, + UIntPtr.Zero); + } + } + + [NoBarriers] + internal static ForwardingWord GetForwardingWord(Object o) + { + UIntPtr word; + if (fVerbose && DebugThread) { + VTable.DebugPrint("GetForwardingWord: reading from "); + VTable.DebugPrint((ulong)Magic.addressOf(o)); + VTable.DebugPrint("\n"); + + UIntPtr *wordPtr = + Magic.toPointer(ref MixinObject(o).preHeader.CoCoWord); + + VTable.DebugPrint("GetForwardingWord: wordPtr = "); + VTable.DebugPrint((ulong)wordPtr); + VTable.DebugPrint("\n"); + + word = *wordPtr; + + VTable.DebugPrint("GetForwardingWord: from "); + VTable.DebugPrint((ulong)Magic.addressOf(o)); + VTable.DebugPrint(" read "); + VTable.DebugPrint((ulong)word); + VTable.DebugPrint("\n"); + } else { + word = MixinObject(o).preHeader.CoCoWord; + } + return new ForwardingWord(o, word); + } + + [Inline] + [NoBarriers] + internal override UIntPtr ToSpaceImplBeforeReadyNonNull(Object o) + { + UIntPtr CoCoWord = MixinObject(o).preHeader.CoCoWord; + UIntPtr ForwardBitsRaw=CoCoWord&~(UIntPtr)(uint)3; + if (ForwardBitsRaw == 0) { + return Magic.addressOf(o); + } else if (((uint)(CoCoWord&3)) == (uint)ObjState.SimpleOrForwarded) { + return ForwardBitsRaw; + } else { + return Magic.addressOf(((WideObject)Magic.fromAddress(ForwardBitsRaw)).copy); + } + } + + [Inline] + [NoBarriers] + internal override bool IsInToSpace(UIntPtr ptr) + { + UIntPtr CoCoWord = MixinObject(Magic.fromAddress(ptr)).preHeader.CoCoWord; + UIntPtr ForwardBitsRaw=CoCoWord&~(UIntPtr)(uint)3; + return ForwardBitsRaw == 0 + || ((uint)(CoCoWord&3)) != (uint)ObjState.SimpleOrForwarded; + } + + [Inline] + [NoBarriers] + internal override UIntPtr ToSpaceImplNonNull(Object o) + { + // this needs some memory barriers. + UIntPtr CoCoWord = MixinObject(o).preHeader.CoCoWord; + UIntPtr ForwardBitsRaw=CoCoWord&~(UIntPtr)(uint)3; + if (ForwardBitsRaw == 0 || + ((uint)(CoCoWord&3)) != (uint)ObjState.SimpleOrForwarded) { + return Magic.addressOf(o); + } else { + return ForwardBitsRaw; + } + } + + [NoBarriers] + internal static bool SwapObjectState(Object o, + ForwardingWord value, + ForwardingWord comparand) + { + return CAS(ref MixinObject(o).preHeader.CoCoWord, + value.Bits, + comparand.Bits) + == comparand.Bits; + } + + [NoBarriers] + internal static void SetObjectState(Object o, + ForwardingWord f) + { + MixinObject(o).preHeader.CoCoWord = f.Bits; + } + + internal enum WordState { + Original = 0, + InWide = 1, + InCopy = 2, + } + + [NoBarriers] + internal struct WordStatus { + private UIntPtr word; + + internal WordStatus(UIntPtr word) + { + this.word = word; + } + + internal WordStatus(WordState state, + bool inAction, + UIntPtr version) + { + word = ((UIntPtr)state) + | (UIntPtr)(inAction?4:0) + | (version<<3); + } + + internal UIntPtr Word + { + get { + return word; + } + } + + internal UIntPtr* asPointer { + get { return Magic.toPointer(ref word); } + } + + internal WordState State + { + get { + return (WordState)(int)(word&(UIntPtr)3); + } + set { + word=((word&~(UIntPtr)3)|((UIntPtr)value)); + } + } + + internal bool InAction { + get { return (word&4)!=0; } + set { + if (value) word|=(UIntPtr)4; + else word&=~(UIntPtr)4; + } + } + + internal UIntPtr Version { + get { return word>>3; } + set { word=(value<<3); } + } + + internal WordStatus WithState(WordState value) { + WordStatus result=this; + result.State=value; + return result; + } + + internal WordStatus WithInAction(bool value) { + WordStatus result=this; + result.InAction=value; + return result; + } + + internal WordStatus WithVersion(UIntPtr value) { + WordStatus result=this; + result.Version=value; + return result; + } + } + + internal enum ActionResult { + None = 0, + Success = 1, + Failure = 2, + } + + internal struct WordAction { + internal int index; + + internal WordStatus statusValue; + internal UIntPtr payloadValue; + + internal WordStatus statusComparand; + internal UIntPtr payloadComparand; + + internal WordAction(int index, + WordStatus statusValue, + UIntPtr payloadValue, + WordStatus statusComparand, + UIntPtr payloadComparand) + { + this.index = index; + this.statusValue = statusValue; + this.payloadValue = payloadValue; + this.statusComparand = statusComparand; + this.payloadComparand = payloadComparand; + } + } + + internal class Action { + private int res; + internal WordAction[] words; + + internal Action(uint n) { + res=(int)ActionResult.None; + words=new WordAction[n]; + } + + internal ActionResult Res { + get { return (ActionResult)res; } + } + + private UIntPtr myVersion { + get { return Magic.addressOf(this)>>3; } + } + + [NoBarriers] + private bool setResult(ActionResult newRes) { + int oldRes=CAS(ref this.res, + (int)newRes, + (int)ActionResult.None); + return oldRes == (int)ActionResult.None + || oldRes == (int)newRes; + } + + private void rollback(WideObject wide, + uint limit) { + // NOTE: this completely and truly reverts the status and + // payload. No trace is left of this action having ever + // taken place, not even in the version number. + for (uint i=0;i The CAS really should succeed. + // -> There is at least one other concurrent attempt to + // complete this action. There was a non-atomic write to + // the words that we're writing that occurred after the + // other attempt had already concluded failure; this + // attempt makes the words appear as if they are correct + // for the CAS to succeed. In this case the CAS can + // either succeed or fail - either would be a linearizable + // result. So, we race to be the first to set the CAS + // result. + + if (setResult(ActionResult.Success)) { + for (uint i=0;i>32)&lower32); + } + + [NoBarriers] + internal bool CAS(WordStatus statusValue, + UIntPtr payloadValue, + WordStatus statusComparand, + UIntPtr payloadComparand) { + WordStatus statusResult; + UIntPtr payloadResult; + CAS(statusValue,payloadValue, + statusComparand,payloadComparand, + out statusResult,out payloadResult); + return statusResult.Word==statusComparand.Word + && payloadResult==payloadComparand; + } + + [NoBarriers] + internal void AtomicRead(out WordStatus statusResult, + out UIntPtr payloadResult) { + // BUGBUG: rudely assuming little-endian 32-bit architecture + long result=*(long*)status.asPointer; + long lower32=(((long)1)<<32)-1; + statusResult=new WordStatus((UIntPtr)(result&lower32)); + payloadResult=(UIntPtr)((result>>32)&lower32); + } + + [NoBarriers] + internal WideWord AtomicRead() { + WordStatus statusResult; + UIntPtr payloadResult; + AtomicRead(out statusResult,out payloadResult); + return new WideWord(statusResult,payloadResult); + } + } + + internal class WideObject { + internal WideObject next; + internal Object from; + internal Object copy; + internal Object action; /* must be Object not Action because + of CompareExchange! */ + + internal WideWord[] words; + + internal WideObject(WideObject next, + Object from, + Object copy) { + this.next=next; + this.from=from; + this.copy=copy; + + UIntPtr nwords= + (ObjectLayout.Sizeof(from)+sizeof(UIntPtr)-1) + /(uint)sizeof(UIntPtr); + + this.words=new WideWord[(int)nwords]; + + this.action=null; + } + + internal UIntPtr spaceOverhead { + get { + return ObjectLayout.Sizeof(this) + + ObjectLayout.Sizeof(words); + } + } + + internal void completeOneAction() { +#if !ARM && !ISA_ARM + // HACK: MemoryBarrier is unimplemented in ARM + // and causes compile-time failures when building + // mscorlib in sepcomp mode. This change will break + // CoCo if built with ARM. + Thread.MemoryBarrier(); +#endif + Action a=(Action)this.action; + if (a!=null) { + a.complete(this); + CAS(ref this.action,null,a); + } + } + } + + internal static int ToIndex(UIntPtr offset) + { + return (int)((offset + PreHeader.Size)/(uint)sizeof(UIntPtr)); + } + + internal static UIntPtr ToOffset(int index) + { + return (UIntPtr)index*(UIntPtr)sizeof(UIntPtr) + - PreHeader.Size; + } + + internal override UIntPtr DoPin(UIntPtr address, + Pinner pinner) + { + if (fCount) { + numPins++; + } + + UIntPtr baseAddr = FindObjectForInteriorPtr(address); + if (baseAddr != UIntPtr.Zero) { + Object o = Magic.fromAddress(baseAddr); + + ForwardingWord f; + + bool waited=false; + for (;;) { + f = GetForwardingWord(o); + if (f.State == ObjState.SimpleOrForwarded) { + break; + } else if (f.State == ObjState.Expanded) { + VTable.Assert(pinner==Pinner.Barrier); + if (fCount) { + if (!waited) { + waited=true; + numWaitPins++; + } + numPinWaits++; + } + // wait until it's copied + lock (interlock) { + CoCoThread t = MixinThread(Thread.CurrentThread); + t.pinnedOut = true; + Monitor.PulseAll(interlock); + while (t.pinnedOut) { + Monitor.Wait(interlock); + } + } + // ok, now try again (by the time we get here the + // object could already be in the process of being + // copied agagin!) + } else { + SwapObjectState(o,f.Clear(),f); + NotifyPin(baseAddr); + } + } + + // what does it mean to get here? the object cannot + // be moved until the next pinning safepoint prior to + // a transition out of Idle. + + VTable.Assert(f.State == ObjState.SimpleOrForwarded); + + // correct address + f = GetForwardingWord(o); + address -= baseAddr; + address += f.ForwardBits; + } + + return address; + } + + [NoInline] + [CalledRarely] + protected override UIntPtr ReadWordSlow(Object o, + UIntPtr offset) + { + // FIXME: add debug here! + ForwardingWord f=GetForwardingWord(o); + if (fVerbose && DebugThread) { + VTable.DebugPrint("f.State = "); + VTable.DebugPrint((int)f.State); + VTable.DebugPrint(", f.ForwardBits = "); + VTable.DebugPrint((ulong)f.ForwardBits); + VTable.DebugPrint("\n"); + } + UIntPtr result; + if (IgnoreOffset(offset)) { + result=*(UIntPtr*)(Magic.addressOf(o) + offset); + } else if (f.State==ObjState.Expanded) { + /* + if (false && phase==Phase.Idle) { + VTable.DebugPrint("PROBLEM: found expanded object in idle phase, o = "); + VTable.DebugPrint((ulong)Magic.addressOf(o)); + VTable.DebugPrint("\n"); + VTable.NotReached(); + } + */ + if (true && f.Wide.from!=o) { + VTable.DebugPrint("PROBLEM: expanded object is corrupt. object = "); + VTable.DebugPrint((ulong)Magic.addressOf(o)); + VTable.DebugPrint(", forwarding word = "); + VTable.DebugPrint((ulong)f.Bits); + VTable.DebugPrint(", wide.from = "); + VTable.DebugPrint((ulong)Magic.addressOf(f.Wide.from)); + VTable.DebugPrint(", ToSpace(o) = "); + VTable.DebugPrint((ulong)ToSpaceAsPtr(o)); + VTable.DebugPrint(", ToSpace(wide.from) = "); + VTable.DebugPrint((ulong)ToSpaceAsPtr(f.Wide.from)); + VTable.DebugPrint("\n"); + VTable.NotReached(); + } + WideObject wide=f.Wide; + WideWord ww=wide.words[ToIndex(offset)].AtomicRead(); + WordState s=ww.status.State; + switch (s) { + case WordState.Original: + result=*(UIntPtr*)(Magic.addressOf(wide.from)+offset); + break; + case WordState.InWide: + if (fVerbose) { + VTable.DebugPrint("actually reading from wide\n"); + } + result=ww.payload; + break; + case WordState.InCopy: + if (fVerbose) { + VTable.DebugPrint("actually reading from copy\n"); + } + result=*(UIntPtr*)(Magic.addressOf(wide.copy)+offset); + break; + default: + VTable.NotReached(); + result=UIntPtr.Zero; // make compiler happy + break; + } + } else { + result=*(UIntPtr*)(f.ForwardBits + offset); + } + if (fVerboseRead) { + UIntPtr oldRes=*(UIntPtr*)(Magic.addressOf(o)+offset); + if (result!=oldRes) { + VTable.DebugPrint("read from "); + VTable.DebugPrint((ulong)Magic.addressOf(o)); + VTable.DebugPrint("+"); + VTable.DebugPrint((ulong)offset); + VTable.DebugPrint(": result = "); + VTable.DebugPrint((ulong)result); + VTable.DebugPrint(", oldRes = "); + VTable.DebugPrint((ulong)oldRes); + VTable.DebugPrint("\n"); + } + } + return result; + } + + internal static void WriteWordNoForward(Object o, + UIntPtr offset, + UIntPtr mask, + UIntPtr shiftedValue, + bool isObject) + { + UIntPtr *ptr = (UIntPtr*)(Magic.addressOf(o) + offset); + if (mask == UIntPtr.MaxValue) { + if (isObject) { + TargetBarrierWithForward(*ptr); + } + *ptr = shiftedValue; + } else { + for (;;) { + UIntPtr oldVal=*ptr; + if (CAS(ptr, + (oldVal&~mask)|(shiftedValue&mask), + oldVal) + == oldVal) { + return; + } + } + } + } + + internal static void WriteWordOriginal(ForwardingWord f, + UIntPtr offset, + UIntPtr mask, + UIntPtr shiftedValue, + bool isObject) { + WriteWordNoForward(f.Forward,offset,mask,shiftedValue,isObject); + } + + internal static bool ExpandIfNecessary(ref ForwardingWord f, + Object o) { + if (f.State == ObjState.Tagged) { + // the Object is not yet expanded so attempt to + // expand it. + if (fVerbose) { + VTable.DebugPrint(" >> Expanding: "); + VTable.DebugPrint((ulong)Magic.addressOf(o)); + VTable.DebugPrint("\n"); + } + ForwardingWord newf=f.WithState(ObjState.Expanded); + if (!SwapObjectState(o,newf,f)) { + return false; + } + f=newf; + } + return true; + } + + [NoInline] + [CalledRarely] + protected override void WriteWordSlow(Object o, + UIntPtr offset, + UIntPtr mask, + UIntPtr shiftedValue, + bool isObject) + { + if (fVerbose && DebugThread) { + VTable.DebugPrint("Writing slowly to "); + VTable.DebugPrint((ulong)Magic.addressOf(o)); + VTable.DebugPrint(" + "); + VTable.DebugPrint((ulong)offset); + VTable.DebugPrint(" the value "); + VTable.DebugPrint((ulong)shiftedValue); + VTable.DebugPrint(" & "); + VTable.DebugPrint((ulong)mask); + if (isObject) { + VTable.DebugPrint(", which represents an object"); + } + VTable.DebugPrint(".\n"); + } + + // this is a transaction that keeps retrying until it + // succeeds. it is lock-free but not wait-free. for + // word-sized fields it could be wait-free if we wanted + // it to be. to understand this code, I'd suggest looking + // for the return statements - these signify the commit + // points of the transaction. any time you fall out of the + // if branches and reloop, this signifies an abort-and-retry. + // at one point there is also an abort-and-retry performed + // using a continue statement. + for (;;) { + Phase p=phase; + ForwardingWord f=GetForwardingWord(o); + if (IgnoreOffset(offset)) { + WriteWordNoForward(o,offset,mask,shiftedValue,isObject); + return; + } else if (f.State==ObjState.SimpleOrForwarded || + p==Phase.Idle) { + // If we get here it means that one of the following + // is going on: + // -> The object is in some weird state but we're still + // in the idle phase. + // -> The object became Simple at some point after the + // write fast path observed that it was not Simple. + // This could have occurred through the use of + // pinning. + // -> The object became Forwarded at some point after + // the write fast path observed that it was not + // Forwarded. This could have happened because + // copying for this object concluded. + + WriteWordOriginal(f,offset,mask,shiftedValue,isObject); + return; + } else if (f.State == ObjState.Tainted && + SwapObjectState(o,f.Clear(),f)) { + // -> The object was Tainted because of a concurrent + // write, and we managed to pin it (because that's + // our conservative way of handling writes that + // happen at the same time as tainted writes). + + numTaintPins++; + NotifyPin(Magic.addressOf(o)); + WriteWordOriginal(f,offset,mask,shiftedValue,isObject); + return; + } else if (p==Phase.Prep && + f.State==ObjState.Tagged && + SwapObjectState(o, + f.WithState(ObjState.Tainted), + f)) { + // This means that we are (or were) in the Prep phase + // and tried to write into a Tagged object. Since + // some threads may still be Idle, we cannot write by + // expanding the object - but at the same time we have + // to be aware of threads that may be in Copy, and so + // may be actively expanding objects. The compromise is + // to taint the object. This will prevent object + // expansion for the duration of the write. + WriteWordNoForward(o,offset,mask,shiftedValue,isObject); + + // The write has succeeded, so we attempt to untaint the + // object and make it tagged again. Of course this + // will fail if a concurrent write had pinned the object. + SwapObjectState(o,f,f.WithState(ObjState.Tainted)); + return; + } else if (p==Phase.Copy && + (f.State==ObjState.Tagged || + f.State==ObjState.Expanded)) { + // Now we have the interesting case: we're in the Copy + // phase and we have an object that is either tagged + // for expansion or is already expanded. + + if (!ExpandIfNecessary(ref f,o)) { + continue; + } + + // Now we have a forwarding word that is + // guaranteed to have a reference to a wide + // object. We know that the world is either in the + // Copy phase or in the Idle phase following the + // Copy phase. At worst, the wide object is "defunct", + // meaning that all of the fields are in the InCopy + // state. That's fine, since the wide object will + // have a reference to the to-space copy. + + WideObject wide=f.Wide; + WideWord ww=wide.words[ToIndex(offset)]; + WordStatus stat=ww.status; + if (stat.InAction) { + wide.completeOneAction(); + } else if (stat.State==WordState.InCopy) { + WriteWordNoForward(wide.copy,offset,mask, + shiftedValue,isObject); + return; + } else if (stat.State==WordState.Original) { + if (fVerbose) { + VTable.DebugPrint(" ** copying field into wide object: from = "); + VTable.DebugPrint((ulong)Magic.addressOf(o)); + VTable.DebugPrint(", wide = "); + VTable.DebugPrint((ulong)Magic.addressOf(wide)); + VTable.DebugPrint(", offset = "); + VTable.DebugPrint((ulong)offset); + VTable.DebugPrint("\n"); + } + UIntPtr oldVal=*(UIntPtr*)(Magic.addressOf(o)+offset); + if (isObject) { + TargetBarrierWithForward(oldVal); + } + if (wide.words[ToIndex(offset)] + .CAS(stat.WithState(WordState.InWide), + (shiftedValue&mask)|(oldVal&~mask), + stat,UIntPtr.Zero)) { + return; + } + } else /* stat.State==InWide */{ + if (isObject) { + TargetBarrierWithForward(ww.payload); + } + if (wide.words[ToIndex(offset)] + .CAS(stat,(shiftedValue&mask)|(ww.payload&~mask), + stat,ww.payload)) { + return; + } + } + } + } + } + + static bool WeakCASWordNoForward(Object o, + UIntPtr offset, + UIntPtr mask, + UIntPtr shiftedValue, + UIntPtr shiftedComparand) { + UIntPtr *ptr=(UIntPtr*)(Magic.addressOf(o)+offset); + UIntPtr oldVal=*ptr; + UIntPtr comparand = (oldVal&~mask)|(shiftedComparand&mask); + return CAS(ptr, + (oldVal&~mask)|(shiftedValue&mask), + comparand) + == comparand; + } + + static bool WeakCASWordOriginal(ForwardingWord f, + UIntPtr offset, + UIntPtr mask, + UIntPtr shiftedValue, + UIntPtr shiftedComparand) { + return WeakCASWordNoForward(f.Forward,offset,mask, + shiftedValue,shiftedComparand); + } + + [NoInline] + [CalledRarely] + protected override bool WeakCASWordSlow(Object o, + UIntPtr offset, + UIntPtr mask, + UIntPtr shiftedValue, + UIntPtr shiftedComparand, + bool isObject) { + // This is more-or-less modeled after WriteWordSlow, except + // that it will fail spuriously. Thus the large comment + // blocks are removed. + + Phase p=phase; + ForwardingWord f=GetForwardingWord(o); + if (IgnoreOffset(offset)) { + return WeakCASWordNoForward(o,offset,mask, + shiftedValue, + shiftedComparand); + } else if (f.State==ObjState.SimpleOrForwarded || + p==Phase.Idle) { + return WeakCASWordOriginal(f,offset,mask, + shiftedValue, + shiftedComparand); + } else if (f.State==ObjState.Tainted && + SwapObjectState(o,f.Clear(),f)) { + numTaintPins++; + NotifyPin(Magic.addressOf(o)); + return WeakCASWordOriginal(f,offset,mask, + shiftedValue, + shiftedComparand); + } else if (p==Phase.Prep && + f.State==ObjState.Tagged && + SwapObjectState(o,f.WithState(ObjState.Tainted),f)) { + bool result= + WeakCASWordNoForward(o,offset,mask, + shiftedValue, + shiftedComparand); + SwapObjectState(o,f,f.WithState(ObjState.Tainted)); + return result; + } else if (p==Phase.Copy && + (f.State==ObjState.Tagged || + f.State==ObjState.Expanded)) { + // Now we have the interesting case: we're in the Copy + // phase and we have an object that is either tagged + // for expansion or is already expanded. + + if (!ExpandIfNecessary(ref f,o)) { + return false; + } + + WideObject wide=f.Wide; + WideWord ww=wide.words[ToIndex(offset)]; + WordStatus stat=ww.status; + if (stat.InAction) { + wide.completeOneAction(); + } else if (stat.State==WordState.InCopy) { + return WeakCASWordNoForward(wide.copy,offset,mask, + shiftedValue, + shiftedComparand); + } else if (stat.State==WordState.Original) { + UIntPtr oldVal=*(UIntPtr*)(Magic.addressOf(o)+offset); + if ((oldVal&mask)!=(shiftedComparand&mask)) { + return false; + } + return wide.words[ToIndex(offset)] + .CAS(stat.WithState(WordState.InWide), + (oldVal&~mask)|(shiftedValue&mask), + stat,UIntPtr.Zero); + } else /* stat.State == InWide */ { + return wide.words[ToIndex(offset)] + .CAS(stat,(ww.payload&~mask)|(shiftedValue&mask), + stat,(ww.payload&~mask)|(shiftedComparand&mask)); + } + } + return false; + } + + static bool WeakCASArbitraryNoForward(Object o, + UIntPtr offset, + UIntPtr size, + ulong value, + ulong comparand) { + UIntPtr ptr=Magic.addressOf(o)+offset; + if (size==4) { + return CAS((int*)ptr,(int)value,(int)comparand) + == (int)comparand; + } else if (size==8) { + return CAS((long*)ptr,(long)value,(long)comparand) + == (long)comparand; + } else { + VTable.NotReached(); + return false; + } + } + + static bool WeakCASArbitraryOriginal(ForwardingWord f, + UIntPtr offset, + UIntPtr size, + ulong value, + ulong comparand) { + return WeakCASArbitraryNoForward(f.Forward,offset,size, + value,comparand); + } + + [NoInline] + [CalledRarely] + protected override bool WeakCASArbitrarySlow(Object o, + UIntPtr offset, + UIntPtr size, + ulong value, + ulong comparand) { + Phase p=phase; + ulong pv=CurThreadPhaseVersion; + ForwardingWord f=GetForwardingWord(o); + if (f.State==ObjState.SimpleOrForwarded || + p==Phase.Idle) { + return WeakCASArbitraryOriginal(f,offset,size, + value, + comparand); + } else if (f.State==ObjState.Tainted && + SwapObjectState(o,f.Clear(),f)) { + numTaintPins++; + NotifyPin(Magic.addressOf(o)); + return WeakCASArbitraryOriginal(f,offset,size, + value, + comparand); + } else if (p==Phase.Prep && + f.State==ObjState.Tagged && + SwapObjectState(o,f.WithState(ObjState.Tainted),f)) { + bool result= + WeakCASArbitraryNoForward(o,offset,size, + value, + comparand); + SwapObjectState(o,f,f.WithState(ObjState.Tainted)); + return result; + } else if (p==Phase.Copy && + (f.State==ObjState.Tagged || + f.State==ObjState.Expanded)) { + if (!ExpandIfNecessary(ref f,o)) { + return false; + } + + WideObject wide=f.Wide; + + UIntPtr maxLowOff=(UIntPtr)sizeof(UIntPtr); + UIntPtr lowMask=maxLowOff-1; + UIntPtr lowOff=offset&lowMask; + UIntPtr baseOff=offset&~lowMask; + UIntPtr tailOff=(offset+size+lowMask)&~lowMask; + + // check what the deal is with field states. + // if all fields are copied, then we just do + // a CAS directly on the to-space copy. if + // none of the fields are copied then we do + // an action-CAS. else we completeOneAction() + // and fail. + bool foundNotCopied=false; + bool foundCopied=false; + for (UIntPtr i=baseOff;i=0 && j<(int)size) { + *curValueWordPtr=*valuePtr; + *curComparandWordPtr=*comparandPtr; + *curWordMaskPtr=255; + } + valuePtr++; + comparandPtr++; + curValueWordPtr++; + curComparandWordPtr++; + curWordMaskPtr++; + } + + a.words[(int)((i-baseOff)/(uint)sizeof(UIntPtr))] + =new WordAction(ToIndex(i), + ww.status.WithState(WordState.InWide), + (curValueWord&curWordMask) + |(oldVal&~curWordMask), + ww.status, + (curComparandWord&curWordMask) + |(oldVal&~curWordMask)); + } + + // really work hard to try to install this action - + // it took enough effort to build that giving up + // would be silly. + while (CAS(ref wide.action,a,null) != null) { + wide.completeOneAction(); + } + + // this either completes our action or some action + // that follows it. + wide.completeOneAction(); + + // our action must be complete here. if it isn't then + // it's a bug and we should abort. + if (a.Res==ActionResult.None) { + VTable.NotReached(); + } + + return a.Res==ActionResult.Success; + } /* !foundCopied */ + } /* p==Copy && + (f.state==Tagged || + f.state==Expanded) */ + + return false; + } + + internal static WideObject wideHead; + + internal override bool AnyTaggedForCopying { + get { + return wideHead!=null; + } + } + + internal override bool TagObjectForCopy(Object from, + Object to, + out UIntPtr spaceOverhead) + { + WideObject wide= + new WideObject(wideHead, + from, + to); + spaceOverhead=wide.spaceOverhead; + if (fVerbose) { + VTable.DebugPrint(" $$ tagging object "); + VTable.DebugPrint((ulong)Magic.addressOf(from)); + VTable.DebugPrint(", wide = "); + VTable.DebugPrint((ulong)Magic.addressOf(wide)); + VTable.DebugPrint(", copy = "); + VTable.DebugPrint((ulong)Magic.addressOf(to)); + VTable.DebugPrint("\n"); + } + if (SwapObjectState(from, + new ForwardingWord(from, + ObjState.Tagged, + wide), + new ForwardingWord(from, + UIntPtr.Zero))) { + wideHead=wide; + return true; + } else { + if (fDebug) { + VTable.DebugPrint(" did not tag object because the CAS on the forwarding word failed\n"); + } + return false; + } + } + + internal override bool NeedsPrepPhase { + get { + return true; + } + } + + [NoBarriers] + internal override ulong Copy() + { + if (fDebug) { + VTable.DebugPrint(" --> CoCo in Copy\n"); + } + + ulong numAttempted=0, numCopied=0, numActionCopied=0; + WideObject wideHead=ExpandingCoCoBarrier.wideHead; + ExpandingCoCoBarrier.wideHead=null; + for (WideObject wide=wideHead; + wide!=null; + wide=wide.next) { + + numAttempted++; + + Object from=wide.from; + + if (fGorierCopy && from==interlock) { + VTable.DebugPrint(" AWESOME: attempting to move interlock from "); + VTable.DebugPrint((ulong)Magic.addressOf(interlock)); + VTable.DebugPrint(" to "); + VTable.DebugPrint((ulong)Magic.addressOf(wide.copy)); + VTable.DebugPrint("\n"); + } + + if (fGorierCopy) { + VTable.DebugPrint(" ---> operating on "); + VTable.DebugPrint((ulong)Magic.addressOf(wide)); + VTable.DebugPrint(" (from = "); + VTable.DebugPrint((ulong)Magic.addressOf(wide.from)); + VTable.DebugPrint(", copy = "); + VTable.DebugPrint((ulong)Magic.addressOf(wide.copy)); + VTable.DebugPrint(")\n"); + } + + ForwardingWord f; + for (long cnt=0;;cnt++) { + f=GetForwardingWord(from); + if (cnt>10 && fDebug) { + VTable.DebugPrint(" for "); + VTable.DebugPrint((ulong)Magic.addressOf(from)); + VTable.DebugPrint(", forwarding word = "); + VTable.DebugPrint((ulong)f.Bits); + VTable.DebugPrint("\n"); + } + if (f.State==ObjState.Tainted || + (f.State==ObjState.Tagged && + ShouldPin(Magic.addressOf(from)))) { + numTaintPins++; + NotifyPin(Magic.addressOf(from)); + SwapObjectState(from,f.Clear(),f); + } else if (f.State==ObjState.SimpleOrForwarded || + f.State==ObjState.Expanded) { + break; + } else if (f.State==ObjState.Tagged) { + SwapObjectState(from,f.WithState(ObjState.Expanded),f); + } + } + if (f.State!=ObjState.Expanded) { + continue; + } + + if (fGorierCopy && from==interlock) { + VTable.DebugPrint(" AWESOME: moving interlock from "); + VTable.DebugPrint((ulong)Magic.addressOf(interlock)); + VTable.DebugPrint(" to "); + VTable.DebugPrint((ulong)Magic.addressOf(wide.copy)); + VTable.DebugPrint("\n"); + } + + if (fVerboseCopy) { + VTable.DebugPrint(" - actually copying "); + VTable.DebugPrint((ulong)Magic.addressOf(wide)); + VTable.DebugPrint(" (from = "); + VTable.DebugPrint((ulong)Magic.addressOf(wide.from)); + VTable.DebugPrint(", copy = "); + VTable.DebugPrint((ulong)Magic.addressOf(wide.copy)); + VTable.DebugPrint(")\n"); + } + + // copy fields into wide object + for (int i=0;i=(IntPtr)PostHeader.Size && + + // there have to be two or more words left for us to + // do a double-word copy. + i+1=UIntPtr.Size*2) { + + if (fGorierCopy) { + VTable.DebugPrint("\n*** using action-copy ***\n"); + } + didActionCopy=true; + + // double-word copying needed + UIntPtr *trg=(UIntPtr*) + (Magic.addressOf(wide.copy) + offset); + for (;;) { + WordStatus s1 = wide.words[i+0].status; + UIntPtr p1 = wide.words[i+0].payload; + + WordStatus s2 = wide.words[i+1].status; + UIntPtr p2 = wide.words[i+1].payload; + + if (s1.InAction || s2.InAction) { + wide.completeOneAction(); + continue; + } + + trg[0] = p1; + trg[1] = p2; + + Action a = new Action(2); + + a.words[0].index = i+0; + a.words[0].statusValue = s1.WithState(WordState.InCopy); + a.words[0].payloadValue = p1; + a.words[0].statusComparand = s1; + a.words[0].payloadComparand = p1; + + a.words[1].index = i+1; + a.words[1].statusValue = s2.WithState(WordState.InCopy); + a.words[1].payloadValue = p2; + a.words[1].statusComparand = s2; + a.words[1].payloadComparand = p2; + + while (CAS(ref wide.action,a,null) != null) { + wide.completeOneAction(); + } + + wide.completeOneAction(); + + if (a.Res == ActionResult.None) { + VTable.NotReached(); + } + + if (a.Res == ActionResult.Success) { + break; + } + } + + i++; + } else { + // only need single-word copying + UIntPtr *trg=(UIntPtr*) + (Magic.addressOf(wide.copy) + offset); + for (;;) { + WideWord ww=wide.words[i]; + if (ww.status.InAction) { + // this is highly unlikely + wide.completeOneAction(); + continue; + } + *trg=ww.payload; + if (wide.words[i] + .CAS(ww.status.WithState(WordState.InCopy), + ww.payload, + ww.status, + ww.payload)) { + if (fGorierCopy) { + VTable.DebugPrint(" ["); + VTable.DebugPrint((ulong)offset); + VTable.DebugPrint(", "); + VTable.DebugPrint((ulong)ww.payload); + VTable.DebugPrint("]"); + } + break; + } + } + } + } + + if (fGorierCopy) { + VTable.DebugPrint("\n"); + } + + if (didActionCopy) { + numActionCopied++; + } + + // finish up for this object + ForwardingWord newf= + f.WithState(ObjState.SimpleOrForwarded) + .WithForward(wide.copy); + + bool res = SwapObjectState(from,newf,f); + if (fGorierCopy) { + VTable.DebugPrint(" --> res = "); + VTable.DebugPrint(res); + VTable.DebugPrint("\n"); + } + VTable.Assert(res, "object transitioned out of expanded without our approval"); + + numCopied++; + + if (fDebug && (numAttempted%10000)==0) { + VTable.DebugPrint(" ---> copied "); + VTable.DebugPrint(numCopied); + VTable.DebugPrint(" / "); + VTable.DebugPrint(numAttempted); + VTable.DebugPrint("\n"); + } + } + if (fDebug) { + VTable.DebugPrint(" --> copied "); + VTable.DebugPrint(numCopied); + VTable.DebugPrint(" / "); + VTable.DebugPrint(numAttempted); + VTable.DebugPrint("; "); + VTable.DebugPrint(numActionCopied); + VTable.DebugPrint(" action-copied\n"); + } + + return numCopied; + } + } +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/FrameDescriptor.cs b/base/Imported/Bartok/runtime/shared/GCs/FrameDescriptor.cs new file mode 100644 index 0000000..7fd1307 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/FrameDescriptor.cs @@ -0,0 +1,146 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + // This is the struct that we should use when traversing the stack. + // It can be created from the encoded information in the stack + // descriptor table. + internal struct FrameDescriptor { + + internal const uint ESCAPE32_TAG = 0x0; + internal const uint ESCAPE16_TAG = 0x1; + internal const uint ESCAPE8_TAG = 0x2; + internal const uint COMPRESSED_MASK_TAG = 0x3; + + internal bool isFramePointerOmitted; + internal bool hasTransitionRecord; + internal bool hasPinnedVariables; + internal uint argumentCount; + internal uint argumentTag; + internal UIntPtr argumentMaskOrTable; + internal UIntPtr calleeSaveValueMask; + internal UIntPtr calleeSaveMask; + internal int frameSize; + internal int inBetweenSlotsAbove; + internal int inBetweenSlotsBelow; + + internal unsafe + FrameDescriptor(CompressedFrameDescriptor smallDescriptor) + { + if (smallDescriptor.IsCompressed()) { + this.argumentCount = smallDescriptor.StackArgSize(); + this.hasPinnedVariables = false; + this.hasTransitionRecord = + smallDescriptor.HasTransitionRecord(); + if (smallDescriptor.IsFramePointerOmitted()) { + this.isFramePointerOmitted = true; + this.argumentTag = COMPRESSED_MASK_TAG; + this.argumentMaskOrTable = + smallDescriptor.ArgumentMaskNoFP(); + this.calleeSaveValueMask = + smallDescriptor.CalleeSaveValueMaskNoFP(); + this.calleeSaveMask = smallDescriptor.CalleeSaveMaskNoFP(); + this.inBetweenSlotsAbove = + smallDescriptor.InBetweenSlotsAboveNoFP(); + this.inBetweenSlotsBelow = + smallDescriptor.InBetweenSlotsBelowNoFP(); + this.frameSize = smallDescriptor.FrameSizeNoFP(); + } else { + this.isFramePointerOmitted = false; + this.argumentTag = COMPRESSED_MASK_TAG; + this.argumentMaskOrTable = + smallDescriptor.ArgumentMaskWithFP(); + this.calleeSaveValueMask = + smallDescriptor.CalleeSaveValueMaskWithFP(); + this.calleeSaveMask = + smallDescriptor.CalleeSaveMaskWithFP(); + this.inBetweenSlotsAbove = + smallDescriptor.InBetweenSlotsAboveWithFP(); + this.inBetweenSlotsBelow = + smallDescriptor.InBetweenSlotsBelowWithFP(); + this.frameSize = 0; + } + } else { + this.argumentCount = 0; + OverflowFrameDescriptor *overflow = + smallDescriptor.OverflowDescriptor(); + this.hasTransitionRecord = + smallDescriptor.HasTransitionRecord(overflow); + if (smallDescriptor.IsFramePointerOmitted(overflow)) { + this.isFramePointerOmitted = true; + this.argumentTag = + smallDescriptor.EntrySizeNoFP(overflow); + this.argumentMaskOrTable = (UIntPtr) + (&overflow->variableData); + this.calleeSaveMask = + smallDescriptor.CalleeSaveMaskNoFP(overflow); + this.calleeSaveValueMask = + smallDescriptor.CalleeSaveValueMaskNoFP(overflow); + this.inBetweenSlotsAbove = 0; + this.inBetweenSlotsBelow = 0; + this.frameSize = + smallDescriptor.FrameSizeNoFP(overflow); + this.hasPinnedVariables = + smallDescriptor.HasPinnedPointersNoFP(overflow); + } else { + this.isFramePointerOmitted = false; + this.argumentTag = + smallDescriptor.EntrySizeWithFP(overflow); + this.argumentMaskOrTable = (UIntPtr) + (&overflow->variableData); + this.calleeSaveMask = + smallDescriptor.CalleeSaveMaskWithFP(overflow); + this.calleeSaveValueMask = + smallDescriptor.CalleeSaveValueMaskWithFP(overflow); + this.inBetweenSlotsAbove = 0; + this.inBetweenSlotsBelow = 0; + this.frameSize = 0; + this.hasPinnedVariables = + smallDescriptor.HasPinnedPointersWithFP(overflow); + } + } + } + + } + + internal struct CompressedFrameDescriptorTemplate { + + internal bool IsCompressed() { return true; } + internal bool IsFramePointerOmitted() { return true; } + internal bool HasTransitionRecord() { return true; } + internal uint StackArgSize() { return 0;} + internal UIntPtr ArgumentMaskNoFP() { return UIntPtr.Zero;} + internal UIntPtr CalleeSaveValueMaskNoFP() { return UIntPtr.Zero; } + internal UIntPtr CalleeSaveMaskNoFP() { return UIntPtr.Zero;} + internal int FrameSizeNoFP() { return 0; } + internal int InBetweenSlotsAboveNoFP() { return 0; } + internal int InBetweenSlotsBelowNoFP() { return 0; } + internal UIntPtr ArgumentMaskWithFP() { return UIntPtr.Zero; } + internal UIntPtr CalleeSaveValueMaskWithFP() { return UIntPtr.Zero; } + internal UIntPtr CalleeSaveMaskWithFP() { return UIntPtr.Zero; } + internal int InBetweenSlotsAboveWithFP() { return 0; } + internal int InBetweenSlotsBelowWithFP() { return 0; } + internal unsafe OverflowFrameDescriptor *OverflowDescriptor() { return null; } + internal unsafe bool IsFramePointerOmitted(OverflowFrameDescriptor *overflow) { return true; } + internal unsafe bool HasTransitionRecord(OverflowFrameDescriptor *overflow) { return true; } + internal unsafe UIntPtr CalleeSaveValueMaskNoFP(OverflowFrameDescriptor *overflow) { return UIntPtr.Zero; } + internal unsafe UIntPtr CalleeSaveMaskNoFP(OverflowFrameDescriptor *overflow) { return UIntPtr.Zero; } + internal unsafe bool HasPinnedPointersNoFP(OverflowFrameDescriptor *overflow) { return true; } + internal unsafe int FrameSizeNoFP(OverflowFrameDescriptor *overflow) { return 0; } + internal unsafe uint EntrySizeNoFP(OverflowFrameDescriptor *overflow) { return 0; } + internal unsafe UIntPtr CalleeSaveValueMaskWithFP(OverflowFrameDescriptor *overflow) { return UIntPtr.Zero; } + internal unsafe UIntPtr CalleeSaveMaskWithFP(OverflowFrameDescriptor *overflow) { return UIntPtr.Zero; } + internal unsafe bool HasPinnedPointersWithFP(OverflowFrameDescriptor *overflow) { return true; } + internal unsafe uint EntrySizeWithFP(OverflowFrameDescriptor *overflow) { return 0; } + internal unsafe void ExpandDescriptor(OverflowFrameDescriptor *overflow, ref FrameDescriptor res) {} + } +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/FrameDescriptorARM.cs b/base/Imported/Bartok/runtime/shared/GCs/FrameDescriptorARM.cs new file mode 100644 index 0000000..395248c --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/FrameDescriptorARM.cs @@ -0,0 +1,209 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using System.Runtime.InteropServices; + using Microsoft.Bartok.Runtime; + +// ISA_ is the prefix used by Singularity for the system architecture +#if ARM || ISA_ARM + [StructLayout(LayoutKind.Sequential)] + internal struct CompressedFrameDescriptor /* : CompressedFrameDescriptorTemplate */ + { + private UIntPtr mask; + + internal bool IsCompressed() + { + return ((this.mask & (UIntPtr) Constants.CompactMask) != 0); + } + + internal bool IsFramePointerOmitted() + { + return ((this.mask & (UIntPtr) Constants.CompactOmitFPMask) != 0); + } + + internal bool HasTransitionRecord() + { + return ((this.mask & (UIntPtr) Constants.CompactTransitionRecordMask) != 0); + } + + internal uint StackArgSize() + { + return ((this.mask >> Constants.CompactArgMaskStart) & Constants.CompactArgMask); + } + + internal UIntPtr ArgumentMaskNoFP() + { + // Current bridge doesn't support omitting frame pointer. + VTable.Assert(false, "Omit FP not supported."); + return (this.mask >> 5); + } + + internal UIntPtr CalleeSaveValueMaskNoFP() + { + // Current bridge doesn't support omitting frame pointer. + VTable.Assert(false, "Omit FP not supported."); + return ((this.mask >> Constants.CompactCalleeSaveUseStartNoFP) & (UIntPtr) Constants.CompactCalleeSaveUseMaskNoFP); + } + + internal UIntPtr CalleeSaveMaskNoFP() + { + // Current bridge doesn't support omitting frame pointer. + VTable.Assert(false, "Omit FP not supported."); + return ((this.mask >> 8) & (UIntPtr) 0xff); + } + + internal int FrameSizeNoFP() + { + // Current bridge doesn't support omitting frame pointer. + VTable.Assert(false, "Omit FP not supported."); + return unchecked((int) (this.mask & 0x1f)); + } + + internal int InBetweenSlotsAboveNoFP() + { + // Current bridge doesn't support omitting frame pointer. + VTable.Assert(false, "Omit FP not supported."); + return Constants.InbetweenSlotsAboveNoFP; + } + + internal int InBetweenSlotsBelowNoFP() + { + // Current bridge doesn't support omitting frame pointer. + VTable.Assert(false, "Omit FP not supported."); + return Constants.InbetweenSlotsBelowNoFP; + } + + internal UIntPtr ArgumentMaskWithFP() + { + return (this.mask >> Constants.CompactStackBitMaskStartFP); + } + + internal UIntPtr CalleeSaveValueMaskWithFP() + { + return ((this.mask >> Constants.CompactCalleeSaveUseStartFP) & (UIntPtr) Constants.CompactCalleeSaveUseMaskFP); + } + + internal UIntPtr CalleeSaveMaskWithFP() + { + return ((this.mask >> Constants.CompactCalleeSaveStartFP) & (UIntPtr) Constants.CompactCalleeSaveMaskFP); + } + + internal int InBetweenSlotsAboveWithFP() + { + return Constants.InbetweenSlotsAboveFP; + } + + internal int InBetweenSlotsBelowWithFP() + { + return Constants.InbetweenSlotsBelowFP; + } + + internal unsafe OverflowFrameDescriptor *OverflowDescriptor() + { + return (OverflowFrameDescriptor *) this.mask; + } + + internal unsafe + bool IsFramePointerOmitted(OverflowFrameDescriptor *overflow) + { + return ((overflow->mask & (UIntPtr) Constants.FullOmitFPMask) != 0); + } + + internal unsafe + bool HasTransitionRecord(OverflowFrameDescriptor *overflow) + { + return ((overflow->mask & (UIntPtr) Constants.FullTransitionRecordMask) != 0); + } + + internal unsafe + UIntPtr CalleeSaveValueMaskNoFP(OverflowFrameDescriptor *overflow) + { + // Current bridge doesn't support omitting frame pointer. + VTable.Assert(false, "Omit FP not supported."); + return this.CalleeSaveValueMaskNoFP(); + } + + internal unsafe + UIntPtr CalleeSaveMaskNoFP(OverflowFrameDescriptor *overflow) + { + // Current bridge doesn't support omitting frame pointer. + VTable.Assert(false, "Omit FP not supported."); + return this.CalleeSaveMaskNoFP(); + } + + internal unsafe + bool HasPinnedPointersNoFP(OverflowFrameDescriptor *overflow) + { + // Current bridge doesn't support omitting frame pointer. + VTable.Assert(false, "Omit FP not supported."); + return ((overflow->mask & (UIntPtr) 0x1) != 0); + } + + internal unsafe int FrameSizeNoFP(OverflowFrameDescriptor *overflow) + { + // Current bridge doesn't support omitting frame pointer. + VTable.Assert(false, "Omit FP not supported."); + return unchecked((int) (overflow->mask >> 4)); + } + + internal unsafe uint EntrySizeNoFP(OverflowFrameDescriptor *overflow) + { + // Current bridge doesn't support omitting frame pointer. + VTable.Assert(false, "Omit FP not supported."); + return unchecked((uint) ((overflow->mask >> 1) & (UIntPtr) 0x7)); + } + + internal unsafe + UIntPtr CalleeSaveValueMaskWithFP(OverflowFrameDescriptor *overflow) + { + return ((overflow->mask >> Constants.FullCalleeSaveUseStartFP) & (UIntPtr) Constants.FullCalleeSaveUseMaskFP); + } + + internal unsafe + UIntPtr CalleeSaveMaskWithFP(OverflowFrameDescriptor *overflow) + { + return ((overflow->mask >> Constants.FullCalleeSaveStartFP) & (UIntPtr) Constants.FullCalleeSaveMaskFP); + } + + internal unsafe + bool HasPinnedPointersWithFP(OverflowFrameDescriptor *overflow) + { + return (((overflow->mask >> Constants.FullPinnedPosFP) & (UIntPtr) 0x1) != 0); + } + + internal unsafe uint EntrySizeWithFP(OverflowFrameDescriptor *overflow) + { + return unchecked((uint) ((overflow->mask >> Constants.FullRecordSizePosFP) & (UIntPtr) Constants.FullRecordMask)); + } + + internal static unsafe UIntPtr NextCallSite(bool omitFP, UIntPtr *fp, out UIntPtr *sp) + { + if (omitFP) { + VTable.Assert(false, "ARM doesn't support FPO"); + sp = (UIntPtr*)UIntPtr.Zero; + } else { + sp = (UIntPtr*)*(fp - 2); + return *(fp - 1); + } + return UIntPtr.Zero; + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct OverflowFrameDescriptor { + internal UIntPtr mask; + internal int variableData; + } +#endif + +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/FrameDescriptorX64.cs b/base/Imported/Bartok/runtime/shared/GCs/FrameDescriptorX64.cs new file mode 100644 index 0000000..ed9b5f1 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/FrameDescriptorX64.cs @@ -0,0 +1,175 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using System.Runtime.InteropServices; + +// ISA_ is the prefix used by Singularity for the system architecture +#if AMD64 || ISA_IX64 + internal struct CompressedFrameDescriptor /* : CompressedFrameDescriptorTemplate */ + { + private UIntPtr descriptorMask; + + internal bool IsCompressed() + { + return ((this.descriptorMask & (UIntPtr) 0x1) != 0); + } + + internal bool IsFramePointerOmitted() + { + return ((this.descriptorMask & (UIntPtr) 0x2) != 0); + } + + internal bool HasTransitionRecord() + { + return ((this.descriptorMask & (UIntPtr) 0x40) != 0); + } + + internal uint StackArgSize() + { + return ((this.descriptorMask >> 2) & 0xf); + } + + internal UIntPtr ArgumentMaskNoFP() + { + return (this.descriptorMask >> 36); + } + + internal UIntPtr CalleeSaveValueMaskNoFP() + { + return ((this.descriptorMask >> 15) & (UIntPtr) 0xffff); + } + + internal UIntPtr CalleeSaveMaskNoFP() + { + return ((this.descriptorMask >> 7) & (UIntPtr) 0xff); + } + + internal int FrameSizeNoFP() + { + return unchecked((int) ((this.descriptorMask >> 31) & 0x1f)); + } + + internal int InBetweenSlotsAboveNoFP() + { + return 1; + } + + internal int InBetweenSlotsBelowNoFP() + { + return 0; + } + + internal UIntPtr ArgumentMaskWithFP() + { + return (this.descriptorMask >> 28); + } + + internal UIntPtr CalleeSaveValueMaskWithFP() + { + return ((this.descriptorMask >> 14) & (UIntPtr) 0x3fff); + } + + internal UIntPtr CalleeSaveMaskWithFP() + { + return ((this.descriptorMask >> 7) & (UIntPtr) 0x7f); + } + + internal int InBetweenSlotsAboveWithFP() + { + return 2; + } + + internal int InBetweenSlotsBelowWithFP() + { + return 0; + } + + internal unsafe OverflowFrameDescriptor *OverflowDescriptor() + { + return (OverflowFrameDescriptor *) this.descriptorMask; + } + + internal unsafe + bool IsFramePointerOmitted(OverflowFrameDescriptor *overflow) + { + return ((overflow->mask & (UIntPtr) 0x1) != 0); + } + + internal unsafe + bool HasTransitionRecord(OverflowFrameDescriptor *overflow) + { + return ((overflow->mask & (UIntPtr) 0x2) != 0); + } + + internal unsafe + UIntPtr CalleeSaveValueMaskNoFP(OverflowFrameDescriptor *overflow) + { + return ((overflow->mask >> 10) & (UIntPtr) 0xffff); + } + + internal unsafe + UIntPtr CalleeSaveMaskNoFP(OverflowFrameDescriptor *overflow) + { + return ((overflow->mask >> 2) & (UIntPtr) 0xff); + } + + internal unsafe + bool HasPinnedPointersNoFP(OverflowFrameDescriptor *overflow) + { + return ((overflow->mask & (UIntPtr) 0x20000000) != 0); + } + + internal unsafe int FrameSizeNoFP(OverflowFrameDescriptor *overflow) + { + return unchecked((int) (overflow->mask >> 38)); + } + + internal unsafe uint EntrySizeNoFP(OverflowFrameDescriptor *overflow) + { + return unchecked((uint) ((overflow->mask >> 26) & (UIntPtr) 0x7)); + } + + internal unsafe + UIntPtr CalleeSaveValueMaskWithFP(OverflowFrameDescriptor *overflow) + { + return ((overflow->mask >> 9) & (UIntPtr) 0x3fff); + } + + internal unsafe + UIntPtr CalleeSaveMaskWithFP(OverflowFrameDescriptor *overflow) + { + return ((overflow->mask >> 2) & (UIntPtr) 0x7f); + } + + internal unsafe + bool HasPinnedPointersWithFP(OverflowFrameDescriptor *overflow) + { + return ((overflow->mask & (UIntPtr) 0x08000000) != 0); + } + + internal unsafe uint EntrySizeWithFP(OverflowFrameDescriptor *overflow) + { + return unchecked((uint) ((overflow->mask >> 23) & (UIntPtr) 0x7)); + } + + } + + [StructLayout(LayoutKind.Sequential)] + internal struct OverflowFrameDescriptor { + internal UIntPtr mask; + internal int variableData; + } + +#endif + +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/FrameDescriptorX86.cs b/base/Imported/Bartok/runtime/shared/GCs/FrameDescriptorX86.cs new file mode 100644 index 0000000..693f92b --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/FrameDescriptorX86.cs @@ -0,0 +1,175 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using System.Runtime.InteropServices; + +// ISA_ is the prefix used by Singularity for the system architecture +#if X86 || ISA_IX86 + internal struct CompressedFrameDescriptor /* : CompressedFrameDescriptorTemplate */ + { + private UIntPtr descriptorMask; + + internal bool IsCompressed() + { + return ((this.descriptorMask & (UIntPtr) 0x1) != 0); + } + + internal bool IsFramePointerOmitted() + { + return ((this.descriptorMask & (UIntPtr) 0x2) != 0); + } + + internal bool HasTransitionRecord() + { + return ((this.descriptorMask & (UIntPtr) 0x40) != 0); + } + + internal uint StackArgSize() + { + return ((this.descriptorMask >> 2) & 0xf); + } + + internal UIntPtr ArgumentMaskNoFP() + { + return (this.descriptorMask >> 24); + } + + internal UIntPtr CalleeSaveValueMaskNoFP() + { + return ((this.descriptorMask >> 11) & (UIntPtr) 0xff); + } + + internal UIntPtr CalleeSaveMaskNoFP() + { + return ((this.descriptorMask >> 7) & (UIntPtr) 0xf); + } + + internal int FrameSizeNoFP() + { + return unchecked((int) ((this.descriptorMask >> 19) & 0x1f)); + } + + internal int InBetweenSlotsAboveNoFP() + { + return 1; + } + + internal int InBetweenSlotsBelowNoFP() + { + return 0; + } + + internal UIntPtr ArgumentMaskWithFP() + { + return (this.descriptorMask >> 16); + } + + internal UIntPtr CalleeSaveValueMaskWithFP() + { + return ((this.descriptorMask >> 10) & (UIntPtr) 0x3f); + } + + internal UIntPtr CalleeSaveMaskWithFP() + { + return ((this.descriptorMask >> 7) & (UIntPtr) 0x7); + } + + internal int InBetweenSlotsAboveWithFP() + { + return 2; + } + + internal int InBetweenSlotsBelowWithFP() + { + return 0; + } + + internal unsafe OverflowFrameDescriptor *OverflowDescriptor() + { + return (OverflowFrameDescriptor *) this.descriptorMask; + } + + internal unsafe + bool IsFramePointerOmitted(OverflowFrameDescriptor *overflow) + { + return ((overflow->mask & (UIntPtr) 0x1) != 0); + } + + internal unsafe + bool HasTransitionRecord(OverflowFrameDescriptor *overflow) + { + return ((overflow->mask & (UIntPtr) 0x2) != 0); + } + + internal unsafe + UIntPtr CalleeSaveValueMaskNoFP(OverflowFrameDescriptor *overflow) + { + return ((overflow->mask >> 6) & (UIntPtr) 0xff); + } + + internal unsafe + UIntPtr CalleeSaveMaskNoFP(OverflowFrameDescriptor *overflow) + { + return ((overflow->mask >> 2) & (UIntPtr) 0xf); + } + + internal unsafe + bool HasPinnedPointersNoFP(OverflowFrameDescriptor *overflow) + { + return ((overflow->mask & (UIntPtr) 0x20000) != 0); + } + + internal unsafe int FrameSizeNoFP(OverflowFrameDescriptor *overflow) + { + return unchecked((int) (overflow->mask >> 22)); + } + + internal unsafe uint EntrySizeNoFP(OverflowFrameDescriptor *overflow) + { + return unchecked((uint) ((overflow->mask >> 14) & (UIntPtr) 0x7)); + } + + internal unsafe + UIntPtr CalleeSaveValueMaskWithFP(OverflowFrameDescriptor *overflow) + { + return ((overflow->mask >> 5) & (UIntPtr) 0x3f); + } + + internal unsafe + UIntPtr CalleeSaveMaskWithFP(OverflowFrameDescriptor *overflow) + { + return ((overflow->mask >> 2) & (UIntPtr) 0x7); + } + + internal unsafe + bool HasPinnedPointersWithFP(OverflowFrameDescriptor *overflow) + { + return ((overflow->mask & (UIntPtr) 0x4000) != 0); + } + + internal unsafe uint EntrySizeWithFP(OverflowFrameDescriptor *overflow) + { + return unchecked((uint) ((overflow->mask >> 11) & (UIntPtr) 0x7)); + } + + } + + [StructLayout(LayoutKind.Sequential)] + internal struct OverflowFrameDescriptor { + internal UIntPtr mask; + internal int variableData; + } + +#endif + +} diff --git a/base/Kernel/Bartok/GCs/GCProfiler.cs b/base/Imported/Bartok/runtime/shared/GCs/GCProfiler.cs similarity index 95% rename from base/Kernel/Bartok/GCs/GCProfiler.cs rename to base/Imported/Bartok/runtime/shared/GCs/GCProfiler.cs index e385b21..645bead 100644 --- a/base/Kernel/Bartok/GCs/GCProfiler.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/GCProfiler.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,10 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - namespace System.GCs { @@ -23,7 +23,9 @@ namespace System.GCs // Receive notifications from the GC and deliver them to a profiler. This class // should be subtyped and then an instance associated with the GC via // System.GC.SetProfiler(). +#if SINGULARITY [CLSCompliant(false)] +#endif public abstract class GCProfiler { public GCProfiler() diff --git a/base/Kernel/Bartok/GCs/GenerationalCollector.cs b/base/Imported/Bartok/runtime/shared/GCs/GenerationalCollector.cs similarity index 79% rename from base/Kernel/Bartok/GCs/GenerationalCollector.cs rename to base/Imported/Bartok/runtime/shared/GCs/GenerationalCollector.cs index dbfe124..3ebf0e8 100644 --- a/base/Kernel/Bartok/GCs/GenerationalCollector.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/GenerationalCollector.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,10 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - //#define DONT_RECORD_OBJALLOC_IN_OFFSETTABLE namespace System.GCs { @@ -21,44 +21,87 @@ namespace System.GCs { internal abstract class GenerationalCollector: StopTheWorldCollector { - [TrustedNonNull] - internal static RememberedSet installedRemSet; - - // PageType.Owner0 is not 0 now. One side effect is that the following arrays - // have unused entries. - - internal static short[] gcCountTable; - internal static short[] gcFrequencyTable; - - internal static UIntPtr[] gcPromotedTable; - internal static UIntPtr[] gcPromotedLimitTable; - - internal static int[] fromSpacePageCounts; - - private static UIntPtr previousPageCount; - internal static UIntPtr nurserySize; - internal static UIntPtr pretenuredSinceLastFullGC; - internal const PageType nurseryGeneration = PageType.Owner0; - internal const PageType largeObjectGeneration = PageType.Owner1; - internal static PageType defaultGeneration; - - internal static PageType MIN_GENERATION { - get { return PageType.Owner0; } - } - internal static PageType MAX_GENERATION { - get { return PageType.Owner1; } + protected static RememberedSet installedRemSet { + get { return GenerationalGCData.installedRemSet; } + set { GenerationalGCData.installedRemSet = value; } } - // These two arrays contain the range of pages each generation is within - internal static UIntPtr[] MinGenPage; - internal static UIntPtr[] MaxGenPage; + protected static short[] gcCountTable { + get { return GenerationalGCData.gcCountTable; } + set { GenerationalGCData.gcCountTable = value; } + } + + protected static short[] gcFrequencyTable { + get { return GenerationalGCData.gcFrequencyTable; } + set { GenerationalGCData.gcFrequencyTable = value; } + } + + protected static UIntPtr[] gcPromotedTable { + get { return GenerationalGCData.gcPromotedTable; } + set { GenerationalGCData.gcPromotedTable = value; } + } + + protected static UIntPtr[] gcPromotedLimitTable { + get { return GenerationalGCData.gcPromotedLimitTable; } + set { GenerationalGCData.gcPromotedLimitTable = value; } + } + + protected static int[] fromSpacePageCounts { + get { return GenerationalGCData.fromSpacePageCounts; } + set { GenerationalGCData.fromSpacePageCounts = value; } + } + + protected static UIntPtr nurserySize { + get { return GenerationalGCData.nurserySize; } + set { GenerationalGCData.nurserySize = value; } + } + + protected static UIntPtr pretenuredSinceLastFullGC { + get { return GenerationalGCData.pretenuredSinceLastFullGC; } + set { GenerationalGCData.pretenuredSinceLastFullGC = value; } + } + + protected static PageType nurseryGeneration { + get { return GenerationalGCData.nurseryGeneration; } + } + + protected static PageType largeObjectGeneration { + get { return GenerationalGCData.largeObjectGeneration; } + } + + protected static PageType defaultGeneration { + get { return GenerationalGCData.defaultGeneration; } + set { GenerationalGCData.defaultGeneration = value; } + } + + protected static PageType MIN_GENERATION { + get { return GenerationalGCData.MIN_GENERATION; } + } + + protected static PageType MAX_GENERATION { + get { return GenerationalGCData.MAX_GENERATION; } + } + + protected static UIntPtr[] MinGenPage { + get { return GenerationalGCData.MinGenPage; } + set { GenerationalGCData.MinGenPage = value; } + } + + protected static UIntPtr[] MaxGenPage { + get { return GenerationalGCData.MaxGenPage; } + set { GenerationalGCData.MaxGenPage = value; } + } + + [Inline] + internal static bool IsLargeObjectSize(UIntPtr size) { + return GenerationalGCData.IsLargeObjectSize(size); + } [PreInitRefCounts] internal static new void Initialize() { if (gcCountTable != null) { return; } - GC.Initialize(); StopTheWorldCollector.Initialize(); defaultGeneration = MAX_GENERATION; uint tableSize = (uint) MAX_GENERATION+1; @@ -75,7 +118,6 @@ namespace System.GCs { // gcPromotedLimitTable = new UIntPtr[MAX_GENERATION+1]; gcPromotedLimitTable = (UIntPtr[]) BootstrapMemory.Allocate(typeof(UIntPtr[]), tableSize); - previousPageCount = UIntPtr.Zero; pretenuredSinceLastFullGC = UIntPtr.Zero; // fromSpacePageCounts = new int[MAX_GENERATION+1]; fromSpacePageCounts = (int[]) @@ -143,8 +185,12 @@ namespace System.GCs { { VTable.Assert(currentThreadIndex == collectorThreadIndex); int maxGeneration = (int) MAX_GENERATION; - if (generation < 0) { - if (generation == -1) { + VTable.Assert(GC.IsRealCollectionRequest(generation)); + if (GC.IsUserCollectionRequest(generation)) { + generation = Math.Max(generation, MinGeneration); + generation = Math.Min(generation, maxGeneration); + } else { + if (generation == (int)GC.SpecialGeneration.InvokeCollection) { generation = MinGeneration; while (generation < maxGeneration && gcCountTable[generation] >= @@ -157,11 +203,10 @@ namespace System.GCs { generation++; } } else { + VTable.Assert(generation == (int) + GC.SpecialGeneration.InvokeMajorCollection); generation = maxGeneration; } - } else { - generation = Math.Max(generation, MinGeneration); - generation = Math.Min(generation, maxGeneration); } generation = this.CollectionGeneration(generation); for (int i = MinGeneration; i < generation; i++) { @@ -205,7 +250,7 @@ namespace System.GCs { //Trace.Log(Trace.Area.Allocate, // "AllocateObjectMemorySlow numBytes={0}, alignment={1}, currentThread={2}", __arglist(numBytes, alignment, currentThread)); GC.CheckForNeededGCWork(currentThread); - VTable.Assert(CurrentPhase != STWPhase.SingleThreaded || + VTable.Assert(CurrentPhase != StopTheWorldPhase.SingleThreaded || currentThread.threadIndex==collectorThreadIndex); if (GenerationalCollector.IsLargeObjectSize(numBytes)) { return AllocateBig(numBytes, alignment, currentThread); @@ -239,14 +284,6 @@ namespace System.GCs { } } - internal override void EnableHeap() { - // Do nothing - } - - internal override void DestructHeap() { - // Do nothing - } - internal override void VerifyHeap(bool beforeCollection) { this.TruncateAllocationAreas((int)MAX_GENERATION); Verifier.bumpAllocatorVerifier.VerifyHeap(); @@ -330,13 +367,6 @@ namespace System.GCs { get { return (UIntPtr) (1 << 27); } } - [Inline] - internal static bool IsLargeObjectSize(UIntPtr size) { - UIntPtr largeObjectSize = - (UIntPtr) (1 << Constants.LargeObjectBits); - return size >= largeObjectSize; - } - internal override int CollectionGeneration(int genRequest) { if (pretenuredSinceLastFullGC > PretenureSoftGCTrigger) { genRequest = GC.MaxGeneration; @@ -415,13 +445,22 @@ namespace System.GCs { internal static void ReclaimZombiePages(UIntPtr heapPageCount, int generation) { - UIntPtr reservePages; - if (heapPageCount < previousPageCount) { - reservePages = (previousPageCount + heapPageCount) / 2; + // to indicate if we want to release pages back to the OS + bool releasePages = true; + UIntPtr reservePages = UIntPtr.Zero; + if (generation == (int) nurseryGeneration) { + // don't bother when we do nursery collection since + // nursery size is small. + releasePages = false; } else { reservePages = heapPageCount; + UIntPtr alreadyReservedPages = PageManager.TotalUnusedPages(); + if (reservePages > alreadyReservedPages) { + reservePages = reservePages - alreadyReservedPages; + } else { + reservePages = UIntPtr.Zero; + } } - previousPageCount = heapPageCount; // MarkZombiePages updates the range for this generation, so we do // not need to take the union ranges of all target generations @@ -437,10 +476,17 @@ namespace System.GCs { } while (IsMyZombiePage(endPage)); InteriorPtrTable.ClearFirst(startPage, endPage); if (GC.remsetType == RemSetType.Cards) { - OffsetTable.ClearLast(PageTable.PageAddr(startPage), + OffsetTable.ClearLast(PageTable.PageAddr(startPage), PageTable.PageAddr(endPage)-1); } - if (reservePages > UIntPtr.Zero) { + if (!releasePages) { + // Don't need to worry about giving the pages back + // Zero out the memory for reuse + UIntPtr pageCount = endPage - startPage; + PageManager.ReleaseUnusedPages(startPage, + pageCount, + false); + } else if (reservePages > UIntPtr.Zero) { // Keep sufficient pages for the new nursery UIntPtr pageCount = endPage - startPage; if (pageCount > reservePages) { @@ -518,9 +564,9 @@ namespace System.GCs { if (GC.remsetType == RemSetType.Cards) { #if DONT_RECORD_OBJALLOC_IN_OFFSETTABLE #else - OffsetTable.SetLast(resultAddr); + OffsetTable.SetLast(resultAddr); #endif - } + } return resultAddr; } @@ -531,9 +577,9 @@ namespace System.GCs { internal override void CheckForNeededGCWork(Thread currentThread) { base.CheckForNeededGCWork(currentThread); - if (CurrentPhase == STWPhase.Idle && - GC.newBytesSinceGC >= nurserySize && - !GC.allocationInhibitGC) { + if (CurrentPhase == StopTheWorldPhase.Idle && + NewBytesSinceGCExceeds(nurserySize) && + GC.allocationGCInhibitCount == 0) { GC.InvokeCollection(currentThread); } } diff --git a/base/Imported/Bartok/runtime/shared/GCs/GenerationalGCData.cs b/base/Imported/Bartok/runtime/shared/GCs/GenerationalGCData.cs new file mode 100644 index 0000000..49394a8 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/GenerationalGCData.cs @@ -0,0 +1,55 @@ +namespace System.GCs { + + using Microsoft.Bartok.Runtime; + using System.Runtime.CompilerServices; + using System.Threading; + + // this class contains all of the things from the generational GC that + // is accessed by the greater runtime in a non-virtual fashion. putting + // it here is necessary to allow devirtualization of critical + // non-generational GC methods. + [NoCCtor] + internal class GenerationalGCData + { + + [TrustedNonNull] + internal static RememberedSet installedRemSet; + + // PageType.Owner0 is not 0 now. One side effect is that the following arrays + // have unused entries. + + internal static short[] gcCountTable; + + internal static short[] gcFrequencyTable; + + internal static UIntPtr[] gcPromotedTable; + internal static UIntPtr[] gcPromotedLimitTable; + + internal static int[] fromSpacePageCounts; + + internal static UIntPtr nurserySize; + internal static UIntPtr pretenuredSinceLastFullGC; + internal const PageType nurseryGeneration = PageType.Owner0; + internal const PageType largeObjectGeneration = PageType.Owner1; + internal static PageType defaultGeneration; + + internal static PageType MIN_GENERATION { + get { return PageType.Owner0; } + } + internal static PageType MAX_GENERATION { + get { return PageType.Owner1; } + } + + // These two arrays contain the range of pages each generation is within + internal static UIntPtr[] MinGenPage; + internal static UIntPtr[] MaxGenPage; + + [Inline] + internal static bool IsLargeObjectSize(UIntPtr size) { + UIntPtr largeObjectSize = + (UIntPtr) (1 << Constants.LargeObjectBits); + return size >= largeObjectSize; + } + + } +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/GenerationalWriteBarrier.cs b/base/Imported/Bartok/runtime/shared/GCs/GenerationalWriteBarrier.cs new file mode 100644 index 0000000..668745d --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/GenerationalWriteBarrier.cs @@ -0,0 +1,192 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using Microsoft.Bartok.Runtime; + using System.Runtime.CompilerServices; + using System.Threading; + + //[NoBarriers] + internal unsafe class GenerationalWriteBarrier : RefWriteBarrier + { + + internal static GenerationalWriteBarrier instance; + + [NoBarriers] + internal static new void Initialize() { + GenerationalWriteBarrier.instance = (GenerationalWriteBarrier) + BootstrapMemory.Allocate(typeof(GenerationalWriteBarrier)); + } + + [Inline] + [NoBarriers] + protected override void StoreStaticFieldImpl(ref Object staticField, + Object value, + int mask) + { + // No need to perform a ReferenceCheck here! + staticField = value; + } + + protected override void CopyStructImpl(Object srcObj, + Object dstObj, + VTable vtable, + UIntPtr srcPtr, + UIntPtr dstPtr) + { + CopyStructWithBarrier(vtable, srcPtr, dstPtr); + } + + [Inline] + protected override Object AtomicSwapImpl(ref Object reference, + Object value, + int mask) + { + UIntPtr *ptr=Magic.toPointer(ref reference); + UIntPtr resultAddr = + Interlocked.Exchange(ptr, + Magic.addressOf(value)); + ReferenceCheck(ptr, value); + return Magic.fromAddress(resultAddr); + } + + [Inline] + protected override + Object AtomicCompareAndSwapImpl(ref Object reference, + Object newValue, + Object comparand, + int mask) + { + UIntPtr *ptr=Magic.toPointer(ref reference); + UIntPtr resultAddr = + Interlocked.CompareExchange(ptr, + Magic.addressOf(newValue), + Magic.addressOf(comparand)); + ReferenceCheck(ptr, newValue); + return Magic.fromAddress(resultAddr); + } + + [Inline] + protected override void CloneImpl(Object srcObject, Object dstObject) + { + CloneNoBarrier(srcObject, dstObject); + ReferenceCheck(dstObject); + } + + // 'offset' is not relative to the lower bound, but is a count + // of elements from the first element in the array. + protected override void ArrayZeroImpl(Array array, + int offset, + int length) + { + ArrayZeroNoBarrier(array, offset, length); + } + + // 'offset' is not relative to the lower bound, but is a count + // of elements from the first element in the array. + protected override void ArrayCopyImpl(Array srcArray, int srcOffset, + Array dstArray, int dstOffset, + int length) + { + if ((length > 1000) || ((length << 2) >= dstArray.Length)) { + ArrayCopyNoBarrier(srcArray, srcOffset, + dstArray, dstOffset, + length); + ReferenceCheck(dstArray); + } else { + ArrayCopyWithBarrier(srcArray, srcOffset, + dstArray, dstOffset, + length); + } + } + + [Inline] + protected override void WriteImpl(UIntPtr *location, + Object value, + int mask) + { + WriteImplNoMask(location, value); + } + + [RequiredByBartok] + [Inline] + private static void WriteImplNoMask(UIntPtr *location, + Object value) + { + *location = Magic.addressOf(value); + ReferenceCheck(location, value); + } + + [Inline] + protected override void WriteImplByRef(ref Object location, + Object value, + int mask) + { + WriteImpl(Magic.toPointer(ref location), value, mask); + } + + [Inline] + private static void ReferenceCheck(Object obj) { + PageType pageType = + PageTable.Type(PageTable.Page(Magic.addressOf(obj))); + if (GenerationalGCData.MAX_GENERATION == PageType.Owner1) { + if (pageType == PageType.Owner1) { + GenerationalGCData. + installedRemSet.RecordClonedObject(obj); + } + } else { + if (pageType != GenerationalGCData.nurseryGeneration) { + GenerationalGCData. + installedRemSet.RecordClonedObject(obj); + } + } + } + + [Inline] + private static void ReferenceCheck(UIntPtr *addr, Object value) + { + PageType addrType = PageTable.Type(PageTable.Page((UIntPtr) addr)); + if (GenerationalGCData.MAX_GENERATION == PageType.Owner1) { + if (addrType != PageType.Owner1) { + return; + } else { + ReferenceCheck(addrType, addr, value); + } + } else { + if (PageTable.IsLiveGcPage(addrType)){ + ReferenceCheck(addrType, addr, value); + } + } + } + + [NoInline] + private static void ReferenceCheck(PageType addrType, UIntPtr *addr, + Object value) { + VTable.Assert(PageTable.IsGcPage(addrType)); + + if (GC.remsetType == RemSetType.Cards) { + GenerationalGCData. + installedRemSet.RecordReference(addr, value); + return; + } + + UIntPtr valueAddr = Magic.addressOf(value); + PageType valType = PageTable.Type(PageTable.Page(valueAddr)); + if (PageTable.IsGcPage(valType) && (addrType > valType)){ + GenerationalGCData. + installedRemSet.RecordReference(addr, value); + } + } + + } + +} diff --git a/base/Kernel/Bartok/GCs/InteriorPtrTable.cs b/base/Imported/Bartok/runtime/shared/GCs/InteriorPtrTable.cs similarity index 91% rename from base/Kernel/Bartok/GCs/InteriorPtrTable.cs rename to base/Imported/Bartok/runtime/shared/GCs/InteriorPtrTable.cs index a65c7ba..c41399a 100644 --- a/base/Kernel/Bartok/GCs/InteriorPtrTable.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/InteriorPtrTable.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,10 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - namespace System.GCs { using Microsoft.Bartok.Runtime; @@ -19,8 +19,8 @@ namespace System.GCs { // WARNING: don't initialize any static fields in this class // without manually running the class constructor at startup! - internal const short OFFSET_NO_DATA = 0; - internal const short OFFSET_SKEW = 1; + internal const uint OFFSET_NO_DATA = 0; + internal const uint OFFSET_SKEW = 1; /* * Returns a pointer to the beginning of an object such that the @@ -29,7 +29,7 @@ namespace System.GCs { */ private static UIntPtr Before(UIntPtr addr) { UIntPtr page = PageTable.Page(addr); - short offset = PageTable.Extra(page); + uint offset = PageTable.Extra(page); // OFFSET_NO_DATA and negative offsets should always fail this // test. if (PageTable.PageAddr(page) + (offset-OFFSET_SKEW) > addr) { @@ -70,7 +70,7 @@ namespace System.GCs { */ internal static UIntPtr First(UIntPtr page) { - short offset = PageTable.Extra(page); + uint offset = PageTable.Extra(page); UIntPtr pageAddr = PageTable.PageAddr(page); UIntPtr currAddr; if (offset != OFFSET_NO_DATA) { @@ -151,11 +151,15 @@ namespace System.GCs { } } + // Finds the object base for an interior pointer. In the case of a + // pointer to the tail of an object and the head of another, it will + // return the former object (the one whose tail we point at). To + // get the base pointer for a pointer into the pre-header, you should + // add PreHeader.Size before calling this. internal static UIntPtr Find(UIntPtr addr) { UIntPtr page = PageTable.Page(addr); UIntPtr currAddr = InteriorPtrTable.First(page); - UIntPtr endAddr = PageTable.PageAddr(page + 1); // Look out for the unused space token: this page may not // have been completely allocated: its "first" object might not // be valid. @@ -205,8 +209,6 @@ namespace System.GCs { // Clear the lowest bits, if set vtableAddr &= ~((UIntPtr)3); } - VTable.Assert((UIntPtr.Size == 8) || - ((vtableAddr & 0x80000000) == 0)); VTable vtable = Magic.toVTable(Magic.fromAddress(vtableAddr)); return ObjectLayout.ObjectSize(addr, vtable); @@ -214,10 +216,11 @@ namespace System.GCs { [Inline] internal static unsafe void SetFirst(UIntPtr newAddr) { - VTable.Assert(PageTable.IsGcPage(PageTable.Page(newAddr))); + VTable.Assert(PageTable.IsGcPage(PageTable.Page(newAddr)), + "SetFirst on a non-GC page"); UIntPtr page = PageTable.Page(newAddr); UIntPtr offset = newAddr - PageTable.PageAddr(page); - PageTable.SetExtra(page, unchecked((short)(offset+OFFSET_SKEW))); + PageTable.SetExtra(page, unchecked((uint)(offset+OFFSET_SKEW))); } [Inline] @@ -241,7 +244,7 @@ namespace System.GCs { UIntPtr previousPage = PageTable.Page(previousObjectAddr); UIntPtr pageCursor = previousPage + 1; while (pageCursor < page) { - short cursorOffset = PageTable.Extra(pageCursor); + uint cursorOffset = PageTable.Extra(pageCursor); UIntPtr objAddr = (PageTable.PageAddr(pageCursor) + cursorOffset - OFFSET_SKEW); if(!(cursorOffset <= OFFSET_NO_DATA || @@ -265,7 +268,7 @@ namespace System.GCs { pageCursor++; } } - short offset = PageTable.Extra(page); + uint offset = PageTable.Extra(page); if (offset > OFFSET_NO_DATA) { UIntPtr firstAddr = PageTable.PageAddr(page) + offset - OFFSET_SKEW; diff --git a/base/Imported/Bartok/runtime/shared/GCs/LowAbortCoCoBarrier.cs b/base/Imported/Bartok/runtime/shared/GCs/LowAbortCoCoBarrier.cs new file mode 100644 index 0000000..237e301 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/LowAbortCoCoBarrier.cs @@ -0,0 +1,1839 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using Microsoft.Bartok.Runtime; + using Microsoft.Bartok.Options; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using System.Threading; + + internal unsafe abstract class LowAbortCoCoBarrier: CoCoBarrier { + + // note that this the mixinConditional + // for CoCo + + [MixinConditional("LowAbortCoCo")] + [Mixin(typeof(PreHeader))] + internal struct CoCoPreHeader { + internal MultiUseWord muw; + internal UIntPtr CoCoWord; + } + + [MixinConditional("LowAbortCoCo")] + [Mixin(typeof(Object))] + internal class CoCoObject: System.Object { + internal new CoCoPreHeader preHeader; + } + + internal static CoCoObject MixinObject(Object o) + { + return (CoCoObject)o; + } + + [NoBarriers] + internal static new void Initialize() + { + CoCoBarrier.Initialize(); + } + + internal override void InitLateStub() + { + CoCoWordOffset = + (UIntPtr)Magic.toPointer(ref MixinObject(interlock).preHeader.CoCoWord) + - Magic.addressOf(interlock); + } + + // shifts 1 left by size bytes. if size is 4, returns 0. + [Inline] + internal static int Shlb1(UIntPtr size) + { + int halfAmount=(int)(uint)size*4; + return 1<>(int)(uint)(lowOffset*8))&(ulong)UIntPtr.MaxValue); + } + + // These methods are for reading. When we read, we want to + // reassemble words into a ulong. The words and the ulong may not + // be aligned. Thus, for each word, we want to be able to + // mask off the part of the word that belongs to the ulong, and then + // shift it into position. It turns out that there are only + // two cases (covered ShiftMask and UnShiftMask). It is never + // necessary to take an arbitrary part of a word and shift it to + // an arbitrary part of the resulting ulong. + + // Mask off the first size bytes of the word and then shift it up + // to lowOffset. + [Inline] + internal static ulong ShiftMask(UIntPtr value, + UIntPtr lowOffset, + UIntPtr size) + { + return ((ulong)(value&(UIntPtr)(uint)(Shlb1(size)-1)))<<(int)(uint)(lowOffset*8); + } + + // take size bytes above lowOffset and shift them all the way + // down. + [Inline] + internal static ulong UnShiftMask(UIntPtr value, + UIntPtr lowOffset, + UIntPtr size) + { + return (ulong)((value>>(int)(uint)(lowOffset*8))&(UIntPtr)(uint)(Shlb1(size)-1)); + } + + [NoBarriers] + [TrustedNonNull] + internal static LowAbortCoCoBarrier laInstance; + + internal void InitEarly() + { + instance = laInstance = this; + } + + internal LowAbortCoCoBarrier() + { + } + + [NoBarriers] + internal override bool ObjectIsNotCopied(Object o) + { + return MixinObject(o).preHeader.CoCoWord==0; + } + + [NoInline] + [CalledRarely] + internal static UIntPtr PinSlow(UIntPtr address) + { + return laInstance.DoPin(address, Pinner.Barrier); + } + + [Inline] + protected override UIntPtr PinImpl(UIntPtr address, + int mask) + { + if (AllowPinFastPath(mask)) { + return address; + } else { + return PinSlow(address); + } + } + + internal static bool EqImpl(UIntPtr a, UIntPtr b, bool isObject) + { + return a == b + || (isObject && forwarding && + ToSpaceBeforeReadyImpl(Magic.fromAddress(a)) + == ToSpaceBeforeReadyImpl(Magic.fromAddress(b))); + } + + [NoBarriers] + [NoInline] + [CalledRarely] + internal static bool EqImplSlow(Object a, Object b) + { + return ToSpaceBeforeReadyImpl(a) == ToSpaceBeforeReadyImpl(b); + } + + [NoBarriers] + [Inline] + protected override bool EqImpl(Object a, Object b, int mask) + { + // why this works: either a==b or not. if not, then + // this trivially works. if a==b, then either the object + // that a and b refer to is forwarded, is going to be + // forwarded while this runs, or it isn't forwarded. if + // it isn't, a, b, ToSpace(a) and ToSpace(b) will all + // trivially give the same address. if it is forwarded + // right now, then forcing forwarding on both pointers + // guarantees correctness. if it will be forwarded as + // this runs, ToSpace(a) == ToSpace(b) may fail (one may + // observe forwarding while the other doesn't), but + // a == b will be correct. + if ((mask & BarrierMask.PathSpec.UseMask)!=0) { + if (StrictlyAllowFastPath(mask)) { + return a==b; + } else { + return a == b || EqImplSlow(a, b); + } + } else { + return a == b || (forwarding && EqImplSlow(a, b)); + } + } + + [AssertDevirtualize] + protected abstract UIntPtr ReadWordSlow(Object o, + UIntPtr offset); + + [NoInline] + [CalledRarely] + internal static UIntPtr ReadWordSlow(Object o, + void *ptr) + { + UIntPtr offset = (UIntPtr)ptr-Magic.addressOf(o); + if (fVerbose && DebugThread) { + VTable.DebugPrint("o = "); + VTable.DebugPrint((ulong)Magic.addressOf(o)); + VTable.DebugPrint(", ptr = "); + VTable.DebugPrint((ulong)ptr); + VTable.DebugPrint(", offset = "); + VTable.DebugPrint((ulong)offset); + VTable.DebugPrint("\n"); + } + return laInstance.ReadWordSlow(o, offset); + } + + [NoInline] + [CalledRarely] + internal static Object ReadObjImplSlow(Object o, UIntPtr *ptr) + { + return ToSpaceAsObj(ReadWordSlow(o, ptr)); + } + + [Inline] + protected override Object ReadObjImpl(Object o, + UIntPtr *ptr, + int mask) + { + if (AllowFastPath(mask)) { + return Magic.fromAddress(*ptr); + } else { + return ReadObjImplSlow(o, ptr); + } + } + + [Inline] + internal static ulong LoadSlow(Object o, + UIntPtr offset, + UIntPtr size) + { + UIntPtr maxLowOff=(UIntPtr)sizeof(UIntPtr); + UIntPtr lowMask=maxLowOff-1; + UIntPtr lowOff=offset&lowMask; + offset&=~lowMask; + if (lowOff+size>maxLowOff*2) { + return UnShiftMask(laInstance.ReadWordSlow(o, offset), + lowOff, + maxLowOff-lowOff) + | ShiftMask(laInstance.ReadWordSlow(o, offset+maxLowOff), + maxLowOff-lowOff, + UIntPtr.MaxValue) + | ShiftMask(laInstance.ReadWordSlow(o, offset+maxLowOff*2), + maxLowOff*2-lowOff, + lowOff+size-maxLowOff); + } else if (lowOff+size>maxLowOff) { + return UnShiftMask(laInstance.ReadWordSlow(o, offset), + lowOff, + maxLowOff-lowOff) + | ShiftMask(laInstance.ReadWordSlow(o, offset+maxLowOff), + maxLowOff-lowOff, + lowOff+size-maxLowOff); + } else { + return UnShiftMask(laInstance.ReadWordSlow(o, offset), + lowOff, + size); + } + } + + [NoInline] + [CalledRarely] + internal static ulong LoadSlow(Object o, + UIntPtr offset, + int size) + { + return LoadSlow(o, offset, (UIntPtr)size); + } + + [NoInline] + [CalledRarely] + internal static ulong LoadSlow(Object o, + void *ptr, + int size) + { + return LoadSlow(o, (UIntPtr)ptr-Magic.addressOf(o), (UIntPtr)size); + } + + [NoInline] + [CalledRarely] + internal static float ReadSlow(Object o, + float *ptr) + { + return IntToFloatBits((uint)LoadSlow(o, ptr, 4)); + } + + [Inline] + protected override float ReadImpl(Object o, + float *ptr, + int mask) + { + if (AllowIdleFastPath(mask)) { + return *ptr; + } else { + return ReadSlow(o, ptr); + } + } + + [NoInline] + [CalledRarely] + internal static double ReadSlow(Object o, + double *ptr) + { + return LongToDoubleBits(LoadSlow(o, ptr, 8)); + } + + [Inline] + protected override double ReadImpl(Object o, + double *ptr, + int mask) + { + if (AllowIdleFastPath(mask)) { + return *ptr; + } else { + return ReadSlow(o, ptr); + } + } + + [NoInline] + [CalledRarely] + internal static byte ReadSlow(Object o, + byte *ptr) + { + return (byte)LoadSlow(o, ptr, 1); + } + + [Inline] + protected override byte ReadImpl(Object o, + byte *ptr, + int mask) + { + if (AllowIdleFastPath(mask)) { + return *ptr; + } else { + return ReadSlow(o, ptr); + } + } + + [NoInline] + [CalledRarely] + internal static ushort ReadSlow(Object o, + ushort *ptr) + { + return (ushort)LoadSlow(o, ptr, 2); + } + + [Inline] + protected override ushort ReadImpl(Object o, + ushort *ptr, + int mask) + { + if (AllowIdleFastPath(mask)) { + return *ptr; + } else { + return ReadSlow(o, ptr); + } + } + + [NoInline] + [CalledRarely] + internal static uint ReadSlow(Object o, + uint *ptr) + { + return (uint)LoadSlow(o, ptr, 4); + } + + [Inline] + protected override uint ReadImpl(Object o, + uint *ptr, + int mask) + { + if (AllowIdleFastPath(mask)) { + return *ptr; + } else { + return ReadSlow(o, ptr); + } + } + + [NoInline] + [CalledRarely] + internal static ulong ReadSlow(Object o, + ulong *ptr) + { + return (ulong)LoadSlow(o, ptr, 8); + } + + [Inline] + protected override ulong ReadImpl(Object o, + ulong *ptr, + int mask) + { + if (AllowIdleFastPath(mask)) { + return *ptr; + } else { + return ReadSlow(o, ptr); + } + } + + [NoInline] + [CalledRarely] + internal static UIntPtr ReadSlow(Object o, + UIntPtr *ptr) + { + return (UIntPtr)LoadSlow(o, ptr, sizeof(UIntPtr)); + } + + [Inline] + protected override UIntPtr ReadImpl(Object o, + UIntPtr *ptr, + int mask) + { + if (AllowIdleFastPath(mask)) { + return *ptr; + } else { + return ReadSlow(o, ptr); + } + } + + [NoInline] + [CalledRarely] + internal static ulong LoadSlow(void *ptr, int size) + { + UIntPtr baseAddr = FindObjectForPreInteriorPtr((UIntPtr)ptr); + if (baseAddr == UIntPtr.Zero) { + switch (size) { + case 1: return (ulong)*(byte*)ptr; + case 2: return (ulong)*(ushort*)ptr; + case 4: return (ulong)*(uint*)ptr; + case 8: return *(ulong*)ptr; + default: VTable.NotReached(); return 0; + } + } else { + return LoadSlow(Magic.fromAddress(baseAddr), ptr, size); + } + } + + [NoInline] + [CalledRarely] + internal static Object ReadObjSlow(UIntPtr *ptr) + { + return ToSpaceAsObj((UIntPtr)LoadSlow(ptr, sizeof(UIntPtr))); + } + + [Inline] + protected override Object ReadObjImpl(UIntPtr *ptr, + int mask) + { + if (AllowFastPath(mask)) { + return Magic.fromAddress(*ptr); + } else { + return ReadObjSlow(ptr); + } + } + + [NoInline] + [CalledRarely] + internal static float ReadSlow(float *ptr) + { + return IntToFloatBits((uint)LoadSlow(ptr, 4)); + } + + [Inline] + protected override float ReadImpl(float *ptr, + int mask) + { + if (AllowIdleFastPath(mask)) { + return *ptr; + } else { + return ReadSlow(ptr); + } + } + + [NoInline] + [CalledRarely] + internal static double ReadSlow(double *ptr) + { + return LongToDoubleBits((ulong)LoadSlow(ptr, 8)); + } + + [Inline] + protected override double ReadImpl(double *ptr, + int mask) + { + if (AllowIdleFastPath(mask)) { + return *ptr; + } else { + return ReadSlow(ptr); + } + } + + [NoInline] + [CalledRarely] + internal static byte ReadSlow(byte *ptr) + { + return (byte)LoadSlow(ptr, 1); + } + + [Inline] + protected override byte ReadImpl(byte *ptr, + int mask) + { + if (AllowIdleFastPath(mask)) { + return *ptr; + } else { + return ReadSlow(ptr); + } + } + + [NoInline] + [CalledRarely] + internal static ushort ReadSlow(ushort *ptr) + { + return (ushort)LoadSlow(ptr, 2); + } + + [Inline] + protected override ushort ReadImpl(ushort *ptr, + int mask) + { + if (AllowIdleFastPath(mask)) { + return *ptr; + } else { + return ReadSlow(ptr); + } + } + + [NoInline] + [CalledRarely] + internal static uint ReadSlow(uint *ptr) + { + return (uint)LoadSlow(ptr, 4); + } + + [Inline] + protected override uint ReadImpl(uint *ptr, + int mask) + { + if (AllowIdleFastPath(mask)) { + return *ptr; + } else { + return ReadSlow(ptr); + } + } + + [NoInline] + [CalledRarely] + internal static ulong ReadSlow(ulong *ptr) + { + return (ulong)LoadSlow(ptr, 8); + } + + [Inline] + protected override ulong ReadImpl(ulong *ptr, + int mask) + { + if (AllowIdleFastPath(mask)) { + return *ptr; + } else { + return ReadSlow(ptr); + } + } + + [NoInline] + [CalledRarely] + internal static UIntPtr ReadSlow(UIntPtr *ptr) + { + return (UIntPtr)LoadSlow(ptr, sizeof(UIntPtr)); + } + + [Inline] + protected override UIntPtr ReadImpl(UIntPtr *ptr, + int mask) + { + if (AllowIdleFastPath(mask)) { + return *ptr; + } else { + return ReadSlow(ptr); + } + } + + [Inline] + protected override Object ReadImplByRef(ref Object loc, + int mask) + { + return ReadObjImpl(Magic.toPointer(ref loc), mask); + } + + [Inline] + protected override float ReadImplByRef(ref float loc, + int mask) + { + return ReadImpl((float*)Magic.toPointer(ref loc), mask); + } + + [Inline] + protected override double ReadImplByRef(ref double loc, + int mask) + { + return ReadImpl((double*)Magic.toPointer(ref loc), mask); + } + + [Inline] + protected override byte ReadImplByRef(ref byte loc, + int mask) + { + return ReadImpl((byte*)Magic.toPointer(ref loc), mask); + } + + [Inline] + protected override ushort ReadImplByRef(ref ushort loc, + int mask) + { + return ReadImpl((ushort*)Magic.toPointer(ref loc), mask); + } + + [Inline] + protected override uint ReadImplByRef(ref uint loc, + int mask) + { + return ReadImpl((uint*)Magic.toPointer(ref loc), mask); + } + + [Inline] + protected override ulong ReadImplByRef(ref ulong loc, + int mask) + { + return ReadImpl((ulong*)Magic.toPointer(ref loc), mask); + } + + [Inline] + protected override UIntPtr ReadImplByRef(ref UIntPtr loc, + int mask) + { + return ReadImpl((UIntPtr*)Magic.toPointer(ref loc), mask); + } + + // this method is expected to invoke the target barrier + [AssertDevirtualize] + protected abstract void WriteWordSlow(Object o, + UIntPtr offset, + UIntPtr mask, + UIntPtr shiftedValue, + bool isObject); + + [NoInline] + [CalledRarely] + internal static void WriteWordSlow(Object o, + UIntPtr offset, + UIntPtr mask, + UIntPtr shiftedValue) + { + laInstance.WriteWordSlow(o, offset, mask, shiftedValue, false); + } + + [NoInline] + [CalledRarely] + internal static void WriteWordSlowObj(Object o, + UIntPtr *ptr, + UIntPtr value) + { + laInstance.WriteWordSlow(o, + (UIntPtr)ptr-Magic.addressOf(o), + UIntPtr.MaxValue, + value, + true); + } + + [NoInline] + [CalledRarely] + internal static void WriteWordSlowObj(Object o, + UIntPtr *ptr, + Object value) + { + UIntPtr valueBits=Magic.addressOf(value); + SourceBarrierWithForward(valueBits); + WriteWordSlowObj(o, ptr, valueBits); + } + + [ForceInline] + protected override void WriteImpl(Object o, + UIntPtr *ptr, + Object value, + int mask) + { + if (AllowFastPath(mask)) { + UIntPtr valueBits=Magic.addressOf(value); + TargetAndSourceBarrierNoForward(ptr, valueBits); + *ptr = valueBits; + } else { + WriteWordSlowObj(o, ptr, value); + } + } + + [NoInline] + [CalledRarely] + internal static void StoreSlow(Object o, + void *ptr, + UIntPtr size, + ulong value) + { + UIntPtr offset=(UIntPtr)ptr-Magic.addressOf(o); + + if (fVerbose && DebugThread) { + VTable.DebugPrint("Doing a StoreSlow on "); + VTable.DebugPrint((ulong)Magic.addressOf(o)); + VTable.DebugPrint(" + "); + VTable.DebugPrint((ulong)offset); + VTable.DebugPrint(" the value "); + VTable.DebugPrint(value); + VTable.DebugPrint(" of size "); + VTable.DebugPrint((ulong)size); + VTable.DebugPrint("\n"); + } + + UIntPtr maxLowOff=(UIntPtr)sizeof(UIntPtr); + UIntPtr lowMask=maxLowOff-1; + UIntPtr lowOff=offset&lowMask; + offset&=~lowMask; + if (lowOff+size>maxLowOff*2) { + WriteWordSlow(o, + offset, + MakeMask(lowOff, + maxLowOff-lowOff), + Shift(value, + lowOff)); + WriteWordSlow(o, + offset+maxLowOff, + UIntPtr.MaxValue, + UnShift(value, + maxLowOff-lowOff)); + WriteWordSlow(o, + offset+maxLowOff*2, + MakeMask(0, + lowOff+size-maxLowOff), + UnShift(value, + maxLowOff*2-lowOff)); + } else if (lowOff+size>maxLowOff) { + WriteWordSlow(o, + offset, + MakeMask(lowOff, + maxLowOff-lowOff), + Shift(value, + lowOff)); + WriteWordSlow(o, + offset+maxLowOff, + MakeMask(0, + lowOff+size-maxLowOff), + UnShift(value, + maxLowOff-lowOff)); + } else { + WriteWordSlow(o, + offset, + MakeMask(lowOff, + size), + Shift(value, + lowOff)); + } + } + + [NoInline] + [CalledRarely] + internal static void StoreSlow(Object o, + void *ptr, + int size, + ulong value) + { + StoreSlow(o, ptr, (UIntPtr)size, value); + } + + [NoInline] + [CalledRarely] + internal static void WriteSlow(Object o, + float *ptr, + float value) + { + StoreSlow(o, ptr, 4, FloatToIntBits(value)); + } + + [Inline] + protected override void WriteImpl(Object o, + float *ptr, + float value, + int mask) + { + if (AllowIdleFastPath(mask)) { + *ptr = value; + } else { + WriteSlow(o, ptr, value); + } + } + + [NoInline] + [CalledRarely] + internal static void WriteSlow(Object o, + double *ptr, + double value) + { + StoreSlow(o, ptr, 8, DoubleToLongBits(value)); + } + + [Inline] + protected override void WriteImpl(Object o, + double *ptr, + double value, + int mask) + { + if (AllowIdleFastPath(mask)) { + *ptr = value; + } else { + WriteSlow(o, ptr, value); + } + } + + [NoInline] + [CalledRarely] + internal static void WriteSlow(Object o, + byte *ptr, + byte value) + { + StoreSlow(o, ptr, 1, value); + } + + [Inline] + protected override void WriteImpl(Object o, + byte *ptr, + byte value, + int mask) + { + if (AllowIdleFastPath(mask)) { + *ptr = value; + } else { + WriteSlow(o, ptr, value); + } + } + + [NoInline] + [CalledRarely] + internal static void WriteSlow(Object o, + ushort *ptr, + ushort value) + { + StoreSlow(o, ptr, 2, value); + } + + [Inline] + protected override void WriteImpl(Object o, + ushort *ptr, + ushort value, + int mask) + { + if (AllowIdleFastPath(mask)) { + *ptr = value; + } else { + WriteSlow(o, ptr, value); + } + } + + [NoInline] + [CalledRarely] + internal static void WriteSlow(Object o, + uint *ptr, + uint value) + { + StoreSlow(o, ptr, 4, value); + } + + [Inline] + protected override void WriteImpl(Object o, + uint *ptr, + uint value, + int mask) + { + if (AllowIdleFastPath(mask)) { + *ptr = value; + } else { + WriteSlow(o, ptr, value); + } + } + + [NoInline] + [CalledRarely] + internal static void WriteSlow(Object o, + ulong *ptr, + ulong value) + { + StoreSlow(o, ptr, 8, value); + } + + [Inline] + protected override void WriteImpl(Object o, + ulong *ptr, + ulong value, + int mask) + { + if (AllowIdleFastPath(mask)) { + *ptr = value; + } else { + WriteSlow(o, ptr, value); + } + } + + [NoInline] + [CalledRarely] + internal static void WriteSlow(Object o, + UIntPtr *ptr, + UIntPtr value) + { + StoreSlow(o, ptr, sizeof(UIntPtr), (ulong)value); + } + + [Inline] + protected override void WriteImpl(Object o, + UIntPtr *ptr, + UIntPtr value, + int mask) + { + if (AllowIdleFastPath(mask)) { + *ptr = value; + } else { + WriteSlow(o, ptr, value); + } + } + + [NoInline] + [CalledRarely] + internal static void WriteWordSlowObj(UIntPtr *ptr, + Object value) + { + UIntPtr valueBits=Magic.addressOf(value); + SourceBarrierWithForward(valueBits); + UIntPtr baseAddr = FindObjectForPreInteriorPtr((UIntPtr)ptr); + if (baseAddr == UIntPtr.Zero) { + TargetBarrierWithForward(*ptr); + *ptr=valueBits; + } else { + WriteWordSlowObj(Magic.fromAddress(baseAddr), + ptr, + valueBits); + } + } + + [ForceInline] + protected override void WriteImpl(UIntPtr *ptr, + Object value, + int mask) + { + if (AllowFastPath(mask)) { + UIntPtr valueBits=Magic.addressOf(value); + TargetAndSourceBarrierNoForward(ptr, valueBits); + *ptr=valueBits; + } else { + WriteWordSlowObj(ptr, value); + } + } + + internal static void StoreSlow(void *ptr, + int size, + ulong value) + { + UIntPtr baseAddr = FindObjectForPreInteriorPtr((UIntPtr)ptr); + if (baseAddr == UIntPtr.Zero) { + switch (size) { + case 1: *(byte*)ptr=(byte)value; break; + case 2: *(ushort*)ptr=(ushort)value; break; + case 4: *(uint*)ptr=(uint)value; break; + case 8: *(ulong*)ptr=value; break; + default: VTable.NotReached(); break; + } + } else { + StoreSlow(Magic.fromAddress(baseAddr), ptr, size, value); + } + } + + [NoInline] + [CalledRarely] + internal static void WriteSlow(float *ptr, + float value) + { + StoreSlow(ptr, 4, FloatToIntBits(value)); + } + + [Inline] + protected override void WriteImpl(float *ptr, + float value, + int mask) + { + if (AllowIdleFastPath(mask)) { + *ptr=value; + } else { + WriteSlow(ptr, value); + } + } + + [NoInline] + [CalledRarely] + internal static void WriteSlow(double *ptr, + double value) + { + StoreSlow(ptr, 8, DoubleToLongBits(value)); + } + + [Inline] + protected override void WriteImpl(double *ptr, + double value, + int mask) + { + if (AllowIdleFastPath(mask)) { + *ptr=value; + } else { + WriteSlow(ptr, value); + } + } + + [NoInline] + [CalledRarely] + internal static void WriteSlow(byte *ptr, + byte value) + { + StoreSlow(ptr, 1, value); + } + + [Inline] + protected override void WriteImpl(byte *ptr, + byte value, + int mask) + { + if (AllowIdleFastPath(mask)) { + *ptr=value; + } else { + WriteSlow(ptr, value); + } + } + + [NoInline] + [CalledRarely] + internal static void WriteSlow(ushort *ptr, + ushort value) + { + StoreSlow(ptr, 2, value); + } + + [Inline] + protected override void WriteImpl(ushort *ptr, + ushort value, + int mask) + { + if (AllowIdleFastPath(mask)) { + *ptr=value; + } else { + WriteSlow(ptr, value); + } + } + + [NoInline] + [CalledRarely] + internal static void WriteSlow(uint *ptr, + uint value) + { + StoreSlow(ptr, 4, value); + } + + [Inline] + protected override void WriteImpl(uint *ptr, + uint value, + int mask) + { + if (AllowIdleFastPath(mask)) { + *ptr=value; + } else { + WriteSlow(ptr, value); + } + } + + [NoInline] + [CalledRarely] + internal static void WriteSlow(ulong *ptr, + ulong value) + { + StoreSlow(ptr, 8, value); + } + + [Inline] + protected override void WriteImpl(ulong *ptr, + ulong value, + int mask) + { + if (AllowIdleFastPath(mask)) { + *ptr=value; + } else { + WriteSlow(ptr, value); + } + } + + [NoInline] + [CalledRarely] + internal static void WriteSlow(UIntPtr *ptr, + UIntPtr value) + { + StoreSlow(ptr, sizeof(UIntPtr), (ulong)value); + } + + [Inline] + protected override void WriteImpl(UIntPtr *ptr, + UIntPtr value, + int mask) + { + if (AllowIdleFastPath(mask)) { + *ptr=value; + } else { + WriteSlow(ptr, value); + } + } + + [ForceInline] + protected override void WriteImplByRef(ref Object loc, + Object value, + int mask) + { + WriteImpl(Magic.toPointer(ref loc), value, mask); + } + + [Inline] + protected override void WriteImplByRef(ref byte loc, + byte value, + int mask) + { + WriteImpl(Magic.toPointer(ref loc), value, mask); + } + + [Inline] + protected override void WriteImplByRef(ref ushort loc, + ushort value, + int mask) + { + WriteImpl(Magic.toPointer(ref loc), value, mask); + } + + [Inline] + protected override void WriteImplByRef(ref uint loc, + uint value, + int mask) + { + WriteImpl(Magic.toPointer(ref loc), value, mask); + } + + [Inline] + protected override void WriteImplByRef(ref ulong loc, + ulong value, + int mask) + { + WriteImpl(Magic.toPointer(ref loc), value, mask); + } + + [Inline] + protected override void WriteImplByRef(ref UIntPtr loc, + UIntPtr value, + int mask) + { + WriteImpl(Magic.toPointer(ref loc), value, mask); + } + + [Inline] + protected override void WriteImplByRef(ref float loc, + float value, + int mask) + { + WriteImpl(Magic.toPointer(ref loc), value, mask); + } + + [Inline] + protected override void WriteImplByRef(ref double loc, + double value, + int mask) + { + WriteImpl(Magic.toPointer(ref loc), value, mask); + } + + // this method is NOT expected to invoke either the source or + // target barrier. + [AssertDevirtualize] + protected abstract bool WeakCASWordSlow(Object o, + UIntPtr offset, + UIntPtr mask, + UIntPtr shiftedValue, + UIntPtr shiftedComparand, + bool isObject); + + // returns the shifted result. does not guarantee the correctness + // of the result for anything but the bits in the mask. + [NoInline] + [CalledRarely] + static UIntPtr StrongCASWordSlow(Object o, + UIntPtr offset, + UIntPtr mask, + UIntPtr shiftedValue, + UIntPtr shiftedComparand, + bool isObject) + { + if (fVerbose && DebugThread) { + VTable.DebugPrint("StrongCASWordSlow: o = "); + VTable.DebugPrint((ulong)Magic.addressOf(o)); + VTable.DebugPrint(", offset = "); + VTable.DebugPrint((ulong)offset); + VTable.DebugPrint(", mask = "); + VTable.DebugPrint((ulong)mask); + VTable.DebugPrint(", shiftedValue = "); + VTable.DebugPrint((ulong)shiftedValue); + VTable.DebugPrint(", shiftedComparand = "); + VTable.DebugPrint((ulong)shiftedComparand); + VTable.DebugPrint("\n"); + } + for (;;) { + UIntPtr oldVal=laInstance.ReadWordSlow(o, offset); + if (fVerbose && DebugThread) { + VTable.DebugPrint("StrongCASWordSlow: oldVal = "); + VTable.DebugPrint((ulong)oldVal); + VTable.DebugPrint("\n"); + } + if (!EqImpl(oldVal&mask, + shiftedComparand&mask, + isObject) || + laInstance.WeakCASWordSlow(o, offset, mask, + shiftedValue, oldVal, + isObject)) { + return oldVal; + } + } + } + + [NoInline] + [CalledRarely] + static UIntPtr StrongXCHGWordSlow(Object o, + UIntPtr offset, + UIntPtr mask, + UIntPtr shiftedValue, + bool isObject) + { + for (;;) { + UIntPtr oldVal=laInstance.ReadWordSlow(o, offset); + if (laInstance.WeakCASWordSlow(o, offset, mask, + shiftedValue, oldVal, + isObject)) { + return oldVal; + } + } + } + + [AssertDevirtualize] + protected abstract bool WeakCASArbitrarySlow(Object o, + UIntPtr offset, + UIntPtr size, + ulong value, + ulong comparand); + + [NoInline] + [CalledRarely] + static ulong StrongCASArbitrarySlow(Object o, + UIntPtr offset, + UIntPtr size, + ulong value, + ulong comparand) + { + for (;;) { + ulong oldVal=LoadSlow(o, offset, size); + if (oldVal!=comparand) { + if (laInstance.WeakCASArbitrarySlow(o, offset, size, + oldVal, oldVal)) { + return oldVal; + } + } else if (laInstance.WeakCASArbitrarySlow(o, offset, size, + value, comparand)) { + return oldVal; + } + } + } + + [NoInline] + [CalledRarely] + static ulong StrongXCHGArbitrarySlow(Object o, + UIntPtr offset, + UIntPtr size, + ulong value) + { + for (;;) { + ulong oldVal=LoadSlow(o, offset, size); + if (laInstance.WeakCASArbitrarySlow(o, offset, size, + value, oldVal)) { + return oldVal; + } + } + } + + [NoInline] + [CalledRarely] + internal static UIntPtr + AtomicCompareAndSwapObjSlow(UIntPtr *ptr, + UIntPtr newValueBits, + UIntPtr comparandBits) + { + if (fCount) { + numAtomics++; + } + UIntPtr result; + UIntPtr addr=(UIntPtr)ptr; + UIntPtr baseAddr=baseAddr = FindObjectForPreInteriorPtr(addr); + if (baseAddr == UIntPtr.Zero) { + TargetBarrierWithForward(*ptr); + result= + Interlocked.CompareExchange(ptr, + newValueBits, + comparandBits); + } else { + Object o=Magic.fromAddress(baseAddr); + TargetBarrierWithForward(laInstance.ReadWordSlow(o, addr-baseAddr)); + result=StrongCASWordSlow(o, addr-baseAddr, + UIntPtr.MaxValue, + newValueBits, + comparandBits, + true /* isObject */); + } + return result; + } + + [Inline] + protected override Object + AtomicCompareAndSwapImpl(ref Object reference, + Object newValue, + Object comparand, + int mask) + { + UIntPtr newValueBits = Magic.addressOf(newValue); + UIntPtr comparandBits = Magic.addressOf(comparand); + + SourceBarrierWithForward(newValueBits); + + UIntPtr result; + + UIntPtr *ptr=Magic.toPointer(ref reference); + if (AllowFastPath(mask)) { + TargetBarrierNoForward(*ptr); + result= + Interlocked.CompareExchange(ptr, + newValueBits, + comparandBits); + } else { + result=AtomicCompareAndSwapObjSlow(ptr, + newValueBits, + comparandBits); + } + + if (StrictlyAllowFastPath(mask)) { + return Magic.fromAddress(result); + } else { + return ToSpaceAsObj(result); + } + } + + [NoInline] + [CalledRarely] + internal static UIntPtr AtomicSwapObjSlow(UIntPtr *ptr, + UIntPtr newValueBits) + { + if (fCount) { + numAtomics++; + } + UIntPtr result; + UIntPtr addr=(UIntPtr)ptr; + UIntPtr baseAddr = FindObjectForPreInteriorPtr(addr); + if (baseAddr == UIntPtr.Zero) { + TargetBarrierWithForward(*ptr); + result= + Interlocked.Exchange(ptr, + newValueBits); + } else { + Object o=Magic.fromAddress(baseAddr); + TargetBarrierWithForward(laInstance.ReadWordSlow(o, addr-baseAddr)); + result=StrongXCHGWordSlow(o, addr-baseAddr, + UIntPtr.MaxValue, + newValueBits, + true); + } + return result; + } + + [Inline] + protected override Object AtomicSwapImpl(ref Object reference, + Object newValue, + int mask) + { + UIntPtr newValueBits=Magic.addressOf(newValue); + + SourceBarrierWithForward(newValueBits); + + UIntPtr result; + + UIntPtr *ptr=Magic.toPointer(ref reference); + if (AllowFastPath(mask)) { + TargetBarrierNoForward(*ptr); + result= + Interlocked.Exchange(ptr, + newValueBits); + } else { + result=AtomicSwapObjSlow(ptr, newValueBits); + } + + if (StrictlyAllowFastPath(mask)) { + return Magic.fromAddress(result); + } else { + return ToSpaceAsObj(result); + } + } + + static ulong StrongCASSlow(Object o, + UIntPtr offset, + UIntPtr size, + ulong value, + ulong comparand) + { + if (fVerbose && DebugThread) { + VTable.DebugPrint("StrongCASSlow: offset = "); + VTable.DebugPrint((ulong)offset); + VTable.DebugPrint(", size = "); + VTable.DebugPrint((ulong)size); + VTable.DebugPrint("\n"); + } + + UIntPtr maxLowOff=(UIntPtr)sizeof(UIntPtr); + UIntPtr lowMask=maxLowOff-1; + UIntPtr lowOff=offset&lowMask; + offset&=~lowMask; + if (lowOff+size>maxLowOff) { + if (fVerbose && DebugThread) { + VTable.DebugPrint("StrongCASSlow: WEIRD! we're using arbitrary-slow CAS.\n"); + } + return StrongCASArbitrarySlow(o, offset, size, + value, comparand); + } else { + return UnShiftMask(StrongCASWordSlow(o, offset, + MakeMask(lowOff, + size), + Shift(value, + lowOff), + Shift(comparand, + lowOff), + false /* not isObject */), + lowOff, size); + } + } + + static ulong StrongCASSlow(Object o, + UIntPtr offset, + int size, + ulong value, + ulong comparand) + { + return StrongCASSlow(o, offset, (UIntPtr)size, + value, comparand); + } + + static ulong StrongXCHGSlow(Object o, + UIntPtr offset, + UIntPtr size, + ulong value) + { + UIntPtr maxLowOff=(UIntPtr)sizeof(UIntPtr); + UIntPtr lowMask=maxLowOff-1; + UIntPtr lowOff=offset&lowMask; + offset&=~lowMask; + if (lowOff+size>maxLowOff) { + return StrongXCHGArbitrarySlow(o, offset, size, + value); + } else { + return UnShiftMask(StrongXCHGWordSlow(o, offset, + MakeMask(lowOff, + size), + Shift(value, + lowOff), + false /* not object */), + lowOff, size); + } + } + + static ulong StrongXCHGSlow(Object o, + UIntPtr offset, + int size, + ulong value) + { + return StrongXCHGSlow(o, offset, (UIntPtr)size, value); + } + + [NoInline] + [CalledRarely] + internal static int AtomicCompareAndSwapSlow(int *ptr, + int value, + int comparand) + { + if (fCount) { + numAtomics++; + } + UIntPtr addr=(UIntPtr)ptr; + UIntPtr baseAddr = FindObjectForPreInteriorPtr(addr); + if (baseAddr == UIntPtr.Zero) { + return Interlocked.CompareExchange(ptr, value, comparand); + } else { + return (int)StrongCASSlow(Magic.fromAddress(baseAddr), + addr-baseAddr, + 4, + (ulong)value, + (ulong)comparand); + } + } + + [Inline] + protected override int + AtomicCompareAndSwapImpl(ref int reference, + int value, + int comparand, + int mask) + { + int *ptr=Magic.toPointer(ref reference); + if (AllowIdleFastPath(mask)) { + return Interlocked.CompareExchange(ptr, value, comparand); + } else { + return AtomicCompareAndSwapSlow(ptr, value, comparand); + } + } + + [NoInline] + [CalledRarely] + internal static int AtomicSwapSlow(int *ptr, + int value) + { + if (fCount) { + numAtomics++; + } + UIntPtr addr=(UIntPtr)ptr; + UIntPtr baseAddr = FindObjectForPreInteriorPtr(addr); + if (baseAddr == UIntPtr.Zero) { + return Interlocked.Exchange(ptr, value); + } else { + return (int)StrongXCHGSlow(Magic.fromAddress(baseAddr), + addr-baseAddr, + 4, + (ulong)value); + } + } + + [Inline] + protected override int AtomicSwapImpl(ref int reference, + int value, + int mask) + { + int *ptr=Magic.toPointer(ref reference); + if (AllowIdleFastPath(mask)) { + return Interlocked.Exchange(ptr, value); + } else { + return AtomicSwapSlow(ptr, value); + } + } + + [NoInline] + [CalledRarely] + internal static long AtomicCompareAndSwapSlow(long *ptr, + long value, + long comparand) + { + if (fCount) { + numAtomics++; + } + UIntPtr addr=(UIntPtr)ptr; + UIntPtr baseAddr = FindObjectForPreInteriorPtr(addr); + if (baseAddr == UIntPtr.Zero) { + return Interlocked.CompareExchange(ptr, value, comparand); + } else { + return (long)StrongCASSlow(Magic.fromAddress(baseAddr), + addr-baseAddr, + 8, + (ulong)value, + (ulong)comparand); + } + } + + [Inline] + protected override long + AtomicCompareAndSwapImpl(ref long reference, + long value, + long comparand, + int mask) + { + long *ptr=Magic.toPointer(ref reference); + UIntPtr addr=(UIntPtr)ptr; + if (AllowIdleFastPath(mask)) { + return Interlocked.CompareExchange(ptr, value, comparand); + } else { + return AtomicCompareAndSwapSlow(ptr, value, comparand); + } + } + + [NoInline] + [CalledRarely] + internal static UIntPtr AtomicCompareAndSwapSlow(UIntPtr *ptr, + UIntPtr value, + UIntPtr comparand) + { + if (fCount) { + numAtomics++; + } + UIntPtr addr=(UIntPtr)ptr; + UIntPtr baseAddr = FindObjectForPreInteriorPtr(addr); + if (baseAddr == UIntPtr.Zero) { + return Interlocked.CompareExchange(ptr, value, comparand); + } else { + return (UIntPtr)StrongCASSlow(Magic.fromAddress(baseAddr), + addr-baseAddr, + UIntPtr.Size, + (ulong)value, + (ulong)comparand); + } + } + + [Inline] + protected override UIntPtr + AtomicCompareAndSwapImpl(ref UIntPtr reference, + UIntPtr value, + UIntPtr comparand, + int mask) + { + UIntPtr *ptr=Magic.toPointer(ref reference); + if (AllowIdleFastPath(mask)) { + return Interlocked.CompareExchange(ptr, value, comparand); + } else { + return AtomicCompareAndSwapSlow(ptr, value, comparand); + } + } + + [NoInline] + [CalledRarely] + internal static UIntPtr AtomicSwapSlow(UIntPtr *ptr, + UIntPtr value) + { + if (fCount) { + numAtomics++; + } + UIntPtr addr=(UIntPtr)ptr; + UIntPtr baseAddr = FindObjectForPreInteriorPtr(addr); + if (baseAddr == UIntPtr.Zero) { + return Interlocked.Exchange(ptr, value); + } else { + return (UIntPtr)StrongXCHGSlow(Magic.fromAddress(baseAddr), + addr-baseAddr, + UIntPtr.Size, + (ulong)value); + } + } + + [Inline] + protected override UIntPtr AtomicSwapImpl(ref UIntPtr reference, + UIntPtr value, + int mask) + { + if (fCount) { + numAtomics++; + } + UIntPtr *ptr=Magic.toPointer(ref reference); + if (AllowIdleFastPath(mask)) { + return Interlocked.Exchange(ptr, value); + } else { + return AtomicSwapSlow(ptr, value); + } + } + + protected override void CopyStructImpl(Object srcObj, + Object dstObj, + VTable vtable, + UIntPtr srcPtr, + UIntPtr dstPtr) + { + if (allowFastPath) { + if (NeedsTargetBarrier) { + PreWriteStruct(vtable, dstPtr); + } + CopyStructNoBarrier(vtable, srcPtr, dstPtr); + } else { + if (fCount) { + numSlowCopyStructs++; + } + CopyStructWithSlowBarrier(srcObj, dstObj, vtable, + srcPtr-Magic.addressOf(srcObj), + dstPtr-Magic.addressOf(dstObj)); + } + } + + protected override void CloneImpl(Object srcObject, + Object dstObject) + { + if (allowFastPath) { + // we're cloning, so nothing of note in the destination + /* + if (false && NeedsTargetBarrier) { + PreWriteObject(dstObject); + } + */ + CloneNoBarrier(srcObject, dstObject); + } else { + if (fCount) { + numSlowClones++; + } + CloneWithSlowBarrier(srcObject, dstObject); + } + } + + protected override void ArrayZeroImpl(Array array, + int offset, + int length) + { + if (allowFastPath) { + if (NeedsTargetBarrier) { + PreWriteArray(array, offset, length); + } + ArrayZeroNoBarrier(array, offset, length); + } else { + if (fCount) { + numSlowArrayZeroes++; + } + ArrayZeroWithSlowBarrier(array, offset, length); + } + } + + protected override void ArrayCopyImpl(Array srcArray, int srcOffset, + Array dstArray, int dstOffset, + int length) + { + if (allowFastPath) { + if (NeedsTargetBarrier) { + PreWriteArray(dstArray, dstOffset, length); + } + ArrayCopyNoBarrier(srcArray, srcOffset, + dstArray, dstOffset, + length); + } else { + if (fCount) { + numSlowArrayCopies++; + } + ArrayCopyWithSlowBarrier(srcArray, srcOffset, + dstArray, dstOffset, + length); + } + } + + [NoInline] + [CalledRarely] + static void StoreStaticFieldSlow(ref Object staticField, + Object value) + { + UIntPtr *staticFieldAddr=(UIntPtr*)Magic.toPointer(ref staticField); + TargetBarrierWithForward(*staticFieldAddr); + UIntPtr valueAddr=Magic.addressOf(value); + SourceBarrierWithForward(valueAddr); + // NOTE: we don't have to forward value because before the + // static data is scanned (and forwarded), we'll already have + // instituted the to-space invariant. + *staticFieldAddr = valueAddr; + } + + [ForceInline] + protected override void StoreStaticFieldImpl(ref Object staticField, + Object value, + int mask) + { + if (AllowFastPath(mask)) { + UIntPtr *staticFieldAddr=(UIntPtr*)Magic.toPointer(ref staticField); + UIntPtr valueAddr=Magic.addressOf(value); + TargetAndSourceBarrierNoForward(staticFieldAddr, valueAddr); + // NOTE: we don't have to forward value because before the + // static data is scanned (and forwarded), we'll already have + // instituted the to-space invariant. + *staticFieldAddr = valueAddr; + } else { + StoreStaticFieldSlow(ref staticField, value); + } + } + + [NoInline] + [CalledRarely] + static Object LoadStaticFieldSlow(ref Object staticField) + { + return ToSpaceAsObj(staticField); + } + + [Inline] + [NoBarriers] + protected override Object LoadStaticFieldImpl(ref Object staticField, + int mask) + { + if (AllowFastPath(mask)) { + return staticField; + } else { + return LoadStaticFieldSlow(ref staticField); + } + } + + internal override void PinningEnabledHook() + { + } + + internal override bool PinOffsetPointers { + [Inline] + get { return false; } + } + + } +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/MarkSweepCollector.cs b/base/Imported/Bartok/runtime/shared/GCs/MarkSweepCollector.cs new file mode 100644 index 0000000..a9cd895 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/MarkSweepCollector.cs @@ -0,0 +1,630 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using Microsoft.Bartok.Runtime; + using System.Runtime.CompilerServices; + using System.Threading; + +#if SINGULARITY + using Microsoft.Singularity; +#if SINGULARITY_PROCESS + using Microsoft.Singularity.V1.Services; + using Microsoft.Singularity.V1.Threads; +#else + using Microsoft.Singularity.Memory; +#endif +#endif + + [NoCCtor] + internal class MarkSweepCollector: StopTheWorldCollector + { +#if OS_WINCE // || ISA_ARM + private const uint InitialTrigger = (uint)(1 << 20); + private const uint MinTrigger = (uint)(1 << 20); + private const uint MaxTrigger = (uint)(1 << 22); +#else + private const uint InitialTrigger = (uint)(1 << 24); + private const uint MinTrigger = (uint)(1 << 24); + private const uint MaxTrigger = (uint)(1 << 26); +#endif + + private static UIntPtr collectionTrigger; + + internal static MarkSweepCollector instance; + + // Visitor instances used for marking objects + private static MarkReferenceVisitor markReferenceVisitor; + private static MarkAndProcessReferenceVisitor markAndProcessReferenceVisitor; + private static UpdateReferenceVisitor updateReferenceVisitor; + private static ThreadMarkReferenceVisitor threadMarkReferenceVisitor; + private static SweepVisitor sweepVisitor; + + private static int traceTime; + private static int sweepTime; + private static int numCollections; + + private MarkSweepCollector() { + } + + public static new void Initialize() { + StopTheWorldCollector.Initialize(); + SegregatedFreeList.Initialize(); + // instance = new MarkSweepCollector(); + MarkSweepCollector.instance = (MarkSweepCollector) + BootstrapMemory.Allocate(typeof(MarkSweepCollector)); + // markReferenceVisitor = new MarkReferenceVisitor(); + markReferenceVisitor = (MarkReferenceVisitor) + BootstrapMemory.Allocate(typeof(MarkReferenceVisitor)); + // markAndProcessReferenceVisitor = new MarkAndProcessReferenceVisitor(); + markAndProcessReferenceVisitor = (MarkAndProcessReferenceVisitor) + BootstrapMemory.Allocate(typeof(MarkAndProcessReferenceVisitor)); + // updateReferenceVisitor = new UpdateReferenceVisitor(); + updateReferenceVisitor = (UpdateReferenceVisitor) + BootstrapMemory.Allocate(typeof(UpdateReferenceVisitor)); + // threadMarkReferenceVisitor = new ThreadMarkReferenceVisitor(); + threadMarkReferenceVisitor = (ThreadMarkReferenceVisitor) + BootstrapMemory.Allocate(typeof(ThreadMarkReferenceVisitor)); + // sweepVisitor = new SweepVisitor(); + sweepVisitor = (SweepVisitor) + BootstrapMemory.Allocate(typeof(SweepVisitor)); + collectionTrigger = (UIntPtr) InitialTrigger; + } + + // GCInterface methods + internal override bool IsOnTheFlyCollector { + get { + return false; + } + } + + internal override void CollectStopped(int currentThreadIndex, + int generation) + { +#if SINGULARITY +#if DEBUG + #if THREAD_TIME_ACCOUNTING + UIntPtr preGcTotalBytes = SegregatedFreeList.TotalBytes; + #endif + DebugStub.WriteLine("~~~~~ Start MarkSweep Cleanup [data={0:x8}, pid={1:x3}]", + __arglist(SegregatedFreeList.TotalBytes, + PageTable.processTag >> 16)); +#endif +#if SINGULARITY_KERNEL + #if THREAD_TIME_ACCOUNTING + TimeSpan ticks = Thread.CurrentThread.ExecutionTime; + TimeSpan ticks2 = SystemClock.KernelUpTime; + #else + TimeSpan ticks = SystemClock.KernelUpTime; + #endif +#elif SINGULARITY_PROCESS + #if THREAD_TIME_ACCOUNTING + TimeSpan ticks = ProcessService.GetThreadTime(); + TimeSpan ticks2 = ProcessService.GetUpTime(); + #else + TimeSpan ticks = ProcessService.GetUpTime(); + #endif +#endif +#endif + int before=0; + if (VTable.enableGCTiming) { + before=Environment.TickCount; + } + if (GC.IsProfiling) { + GcProfiler.NotifyPreGC(MinGeneration); + // non-generational collector, so pretend Gen0 + // Calls like ResurrectCandidates below can cause + // allocations and thus, potentially, profiler + // notifications. However, at that time the heap is + // damaged in the sense that VPtrs have bits OR-ed in + // for object marking. We do not want to accept + // profiling during this window. + // + // There is no synchronization issue with setting this + // flag because it will only be consulted by the + // thread that sets and resets it. + HeapDamaged = true; + } + // 1) Mark the live objects + CollectorStatistics.Event(GCEvent.TraceStart); +#if !VC + TryAllManager.PreGCHookTryAll(); +#endif + MultiUseWord.PreGCHook(false /* don't use shadows */); + Finalizer.PrepareCollectFinalizers(); + int countThreads = + CallStack.ScanStacks(threadMarkReferenceVisitor, + threadMarkReferenceVisitor); + Thread.VisitBootstrapData(markAndProcessReferenceVisitor); +#if SINGULARITY_KERNEL + Kernel.VisitSpecialData(markAndProcessReferenceVisitor); +#endif + MultiUseWord.VisitStrongRefs(markAndProcessReferenceVisitor, + false /* Don't use shadows */); +#if !VC + TryAllManager.VisitStrongRefs(markAndProcessReferenceVisitor); +#endif + StaticData.ScanStaticData(markAndProcessReferenceVisitor); + CollectorStatistics.Event(GCEvent.TraceSpecial); + WeakReference.Process(updateReferenceVisitor, true, true); + Finalizer.ResurrectCandidates(updateReferenceVisitor, + markAndProcessReferenceVisitor, true); + markReferenceVisitor.Cleanup(); + UnmanagedPageList.ReleaseStandbyPages(); + // 2) Sweep the garbage objects + int afterTrace=0; + if (VTable.enableGCTiming) { + afterTrace=Environment.TickCount; + } + CollectorStatistics.Event(GCEvent.SweepStart, TotalMemory); + WeakReference.Process(updateReferenceVisitor, true, false); + MultiUseWord.VisitWeakRefs(updateReferenceVisitor, + false /* Don't use shadows */); +#if !VC + TryAllManager.VisitWeakRefs(updateReferenceVisitor); +#endif + SegregatedFreeList.VisitAllObjects(sweepVisitor); + SegregatedFreeList.RecycleGlobalPages(); + SegregatedFreeList.CommitFreedData(); + CollectorStatistics.Event(GCEvent.SweepSpecial); + MultiUseWord.PostGCHook(); + if (GC.IsProfiling) { + HeapDamaged = false; + // Allocations may occur inside the PostGCHook. Hopefully a + // sufficiently limited quantity that we don't recursively + // trigger a GC. + GcProfiler.NotifyPostGC(ProfileRoots, ProfileObjects); + } + Finalizer.ReleaseCollectFinalizers(); +#if !VC + TryAllManager.PostGCHookTryAll(); +#endif + CollectorStatistics.Event(GCEvent.CollectionComplete, + TotalMemory); + if (VTable.enableGCTiming) { + int after=Environment.TickCount; + numCollections++; + traceTime+=(afterTrace-before); + sweepTime+=(after-afterTrace); + } + // 3) Determine a new collection trigger + UIntPtr testTrigger = (UIntPtr) this.TotalMemory >> 2; + UIntPtr minTrigger = (UIntPtr) MinTrigger; + UIntPtr maxTrigger = (UIntPtr) MaxTrigger; + collectionTrigger = + (testTrigger > minTrigger) ? + (testTrigger < maxTrigger ? + testTrigger : maxTrigger) : minTrigger; +#if SINGULARITY +#if SINGULARITY_KERNEL + #if THREAD_TIME_ACCOUNTING + int procId = Thread.CurrentProcess.ProcessId; + ticks = Thread.CurrentThread.ExecutionTime - ticks; + ticks2 = SystemClock.KernelUpTime - ticks2; + #else + ticks = SystemClock.KernelUpTime - ticks; + #endif + //Thread.CurrentProcess.SetGcPerformanceCounters(ticks, (long) SegregatedFreeList.TotalBytes); +#elif SINGULARITY_PROCESS + #if THREAD_TIME_ACCOUNTING + ushort procId = ProcessService.GetCurrentProcessId(); + ticks = ProcessService.GetThreadTime() - ticks; + ticks2 = ProcessService.GetUpTime() - ticks2; + #else + ticks = ProcessService.GetUpTime() - ticks; + #endif + //ProcessService.SetGcPerformanceCounters(ticks, (long) SegregatedFreeList.TotalBytes); +#endif +#if DEBUG +#if THREAD_TIME_ACCOUNTING + DebugStub.WriteLine("~~~~~ Finish MarkSweep Cleanup [data={0:x8}, diff={7:x8} pid={1:x3}, ms(Thread)={2:d6}, ms(System)={3:d6}, thds={4}, procId={5}, tid={6}]", + __arglist(SegregatedFreeList.TotalBytes, + PageTable.processTag >> 16, + ticks.Milliseconds, + ticks2.Milliseconds, + countThreads, + procId, + Thread.GetCurrentThreadIndex(), + preGcTotalBytes - SegregatedFreeList.TotalBytes + )); +#else + DebugStub.WriteLine("~~~~~ Finish MarkSweep Cleanup [data={0:x8}, pid={1:x3}, ms={2:d6}, thds={3}]", + __arglist(SegregatedFreeList.TotalBytes, + PageTable.processTag >> 16, + ticks.Milliseconds, + countThreads)); +#endif +#endif +#endif + } + + internal override int CollectionGeneration(int genRequest) + { + return MinGeneration; + } + + [Inline] + protected override void CreateObject(Object obj, + VTable vtable, + Thread currentThread) + { + base.CreateObject(obj, vtable, currentThread); + ProfileAllocation(obj); + } + + [Inline] + internal override UIntPtr AllocateObjectMemory(UIntPtr numBytes, + uint alignment, + Thread currentThread) + { + UIntPtr resultAddr = + SegregatedFreeList.AllocateFast(currentThread, + numBytes, alignment); + if (resultAddr == UIntPtr.Zero) { + resultAddr = this.AllocateObjectMemorySlow(numBytes, alignment, + currentThread); + } + return resultAddr; + } + + [NoInline] + private UIntPtr AllocateObjectMemorySlow(UIntPtr numBytes, + uint alignment, + Thread currentThread) + { + if (NewBytesSinceGCExceeds(collectionTrigger) && + GC.allocationGCInhibitCount == 0) { + //REVIEW: This actually happens after the trigger... + GC.InvokeCollection(currentThread); + } + return SegregatedFreeList.AllocateSlow(currentThread, + numBytes, alignment); + } + + internal override int GetGeneration(Object obj) { + return MinGeneration; + } + + internal override int MaxGeneration { + get { return (int)PageType.Owner0; } + } + + internal override int MinGeneration { + get { return (int)PageType.Owner0; } + } + + internal override long TotalMemory { + get { + return (long)SegregatedFreeList.TotalBytes; +#if false + UIntPtr pageCount = UIntPtr.Zero; + for (UIntPtr i=UIntPtr.Zero; i MaxHeapWatermark) { MaxHeapWatermark = totalMemory; } - AvgHeapSize = - (AvgHeapSize*Watermarks+totalMemory)/(Watermarks+1); - Watermarks++; + Records++; + AvgHeapSize = (AvgHeapSize*Records+totalMemory)/Records; + + return totalMemory; } internal static uint TotalNumPages(PageType kind) { diff --git a/base/Kernel/Bartok/GCs/MemoryManager.cs b/base/Imported/Bartok/runtime/shared/GCs/MemoryManager.cs similarity index 96% rename from base/Kernel/Bartok/GCs/MemoryManager.cs rename to base/Imported/Bartok/runtime/shared/GCs/MemoryManager.cs index 8747d24..aef275e 100644 --- a/base/Kernel/Bartok/GCs/MemoryManager.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/MemoryManager.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,10 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - namespace System.GCs { using System.Runtime.CompilerServices; @@ -133,8 +133,9 @@ namespace System.GCs { } #else // not SINGULARITY + [NoBarriers] [PreInitRefCounts] - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe void Initialize() { SYSTEM_INFO systemInfo; GetSystemInfo(out systemInfo); @@ -230,12 +231,14 @@ namespace System.GCs { [GCAnnotation(GCOption.NOGC)] [NoStackLinkCheck] [StackBound(1024)] + [NoBarriers] private static unsafe extern void GetSystemInfo(out SYSTEM_INFO systemInfo); [DllImport("BRT")] [GCAnnotation(GCOption.NOGC)] [NoStackLinkCheck] [StackBound(1024)] + [NoBarriers] private static unsafe extern void GlobalMemoryStatusEx(ref MEMORYSTATUSEX memoryStatus); // Low-level routines based on the operating system interface diff --git a/base/Imported/Bartok/runtime/shared/GCs/ModifiedFirstFit.cs b/base/Imported/Bartok/runtime/shared/GCs/ModifiedFirstFit.cs new file mode 100644 index 0000000..36d17f4 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/ModifiedFirstFit.cs @@ -0,0 +1,378 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using System.Threading; + using System.Runtime.InteropServices; + using System.Runtime.CompilerServices; + + internal class ModifiedFirstFit { + private const uint minimumBlockSize = 12; + + private static UIntPtr osCommitSize; + + private UIntPtr head; + private UIntPtr tail; + private UIntPtr last; + + private UIntPtr totalMemory; + + [PreInitRefCounts] + internal static void Initialize() { + osCommitSize = MemoryManager.OperatingSystemCommitSize; + UIntPtr heapCommitSize = new UIntPtr(1 << 20); + VTable.Assert(heapCommitSize > osCommitSize, + @"heapCommitSize > osCommitSize"); + + PageManager.Initialize(osCommitSize, heapCommitSize); + } + + [Inline] + [ManualRefCounts] + internal UIntPtr AllocateBlock(UIntPtr blockSize, + Thread t) { + UIntPtr prev; + + if (head == UIntPtr.Zero) { // Free list exhausted + // or no free list yet. + prev = growFreeList(blockSize, t); + } else { + VTable.Assert(tail >= head, + @"tail >= head"); + + // Perform a first-fit search from the last + // free block that served up an allocation + // request, or the free block after that if + // it was fully served (next fit). + + prev = last; + + UIntPtr desiredBlockSize = blockSize+minimumBlockSize; + + while (getBlockSize(prev) < desiredBlockSize) { + prev = getNextBlock(prev); + + if (prev == last) { // Search has wrapped around. + prev = growFreeList(blockSize, t); + break; + } + } + } + + // At this point, either the block pointed by "prev" + // has a size that exceeds the requested size by + // at least "minimumBlockSize", or precedes a block + // whose size equals the requested size or exceeds + // it by at least "minimumBlockSize". + + UIntPtr prevBlockSize = getBlockSize(prev); + VTable.Assert(prevBlockSize >= blockSize+ + minimumBlockSize || + getBlockSize(getNextBlock(prev)) == + blockSize || + getBlockSize(getNextBlock(prev)) >= + blockSize+minimumBlockSize, + @"prevBlockSize >= blockSize+ + minimumBlockSize || + getBlockSize(getNextBlock(prev)) == + blockSize || + getBlockSize(getNextBlock(prev)) >= + blockSize+minimumBlockSize"); + + UIntPtr resultAddr; + + if (prevBlockSize >= + blockSize+minimumBlockSize) { // Break block. + setBlockSize(prev, prevBlockSize-blockSize); + resultAddr = prev+(prevBlockSize-blockSize); + last = prev; + } else { // Process request from the next block. + UIntPtr curr = getNextBlock(prev); + UIntPtr currBlockSize = getBlockSize(curr); + + if (currBlockSize >= blockSize+minimumBlockSize) { + setBlockSize(curr, currBlockSize-blockSize); + resultAddr = curr+(currBlockSize-blockSize); + last = curr; + } else { // Return entire block. + VTable.Assert(currBlockSize == blockSize, + @"currBlockSize == blockSize"); + + resultAddr = curr; + if (prev == curr) { // Free list becomes empty. + head = UIntPtr.Zero; + tail = UIntPtr.Zero; + last = UIntPtr.Zero; + } else { // Skip a block in the free list. + if (curr == head) { + curr = getNextBlock(curr); + setNextBlock(prev, curr); + head = curr; + } else if (curr == tail) { + curr = head; + setNextBlock(prev, curr); + tail = prev; + } else { + curr = getNextBlock(curr); + setNextBlock(prev, curr); + } + last = curr; + } + } + } + + // Wipe out old contents. + Util.MemClear(resultAddr, blockSize); + + // Adjust memory audit figure. + totalMemory -= blockSize; + + return resultAddr; + } + + // Free lists used in this implementation have the invariant + // that their blocks are linked in increasing order of + // addresses. FreeBlock preserves this invariant. It also + // coalesces the inserted block with abutting free blocks. + // + // It returns the block just before the point of insertion. + + [Inline] + [ManualRefCounts] + internal UIntPtr FreeBlock(UIntPtr block, UIntPtr size) { + VTable.Assert(block != UIntPtr.Zero, + @"block != UIntPtr.Zero"); + VTable.Assert(head <= tail, + @"head <= tail"); + VTable.Assert(size > 0, + @"size > 0"); + + // Adjust memory audit figure. + totalMemory += size; + + UIntPtr below; + + if (block < head) { + VTable.Assert(head != UIntPtr.Zero, + @"head != UIntPtr.Zero"); + VTable.Assert(tail != UIntPtr.Zero, + @"tail != UIntPtr.Zero"); + VTable.Assert(block+size <= head, + @"block+size <= head"); + + if (block+size == head) { + setBlockSize(block, size+getBlockSize(head)); + + if (head == tail) { + tail = block; + } else { + setNextBlock(block, getNextBlock(head)); + } + if (last == head) { + last = block; + } + } else { + setBlockSize(block, size); + setNextBlock(block, head); + } + below = tail; + setNextBlock(below, block); + head = block; + } else if (head == UIntPtr.Zero) { + VTable.Assert(tail == UIntPtr.Zero, + @"tail == UIntPtr.Zero"); + VTable.Assert(last == UIntPtr.Zero, + @"last == UIntPtr.Zero"); + + tail = block; + setBlockSize(block, size); + setNextBlock(block, block); + below = block; + head = block; + last = block; + } else if (block > tail) { + VTable.Assert(tail != UIntPtr.Zero, + @"tail != UIntPtr.Zero"); + + below = tail; + UIntPtr lastBlockSize = getBlockSize(below); + VTable.Assert(below+lastBlockSize <= block, + @"below+lastBlockSize <= block"); + + if (below+lastBlockSize == block) { + setBlockSize(below, lastBlockSize+size); + } else { + setNextBlock(below, block); + setBlockSize(block, size); + setNextBlock(block, head); + tail = block; + } + } else { + VTable.Assert(UIntPtr.Zero < head, + @"UIntPtr.Zero < head"); + VTable.Assert(head < block, + @"head < block"); + VTable.Assert(block < tail, + @"block < tail"); + + // UIntPtr.Zero < head < block < tail. + + // The blocks referenced by "below" and "above" + // ultimately straddle the inserted block. + + below = head; + UIntPtr above = getNextBlock(below); + + while (above < block) { + below = above; + above = getNextBlock(below); + } + // At this point, below < block < above. + VTable.Assert(below < block, + @"below < block"); + VTable.Assert(below+getBlockSize(below) <= block, + @"below+getBlockSize(below) <= block"); + VTable.Assert(block < above, + @"block < above"); + VTable.Assert(block+size <= above, + @"block+size <= above"); + + // Coalesce blocks if possible. + + if (block+size == above) { + size += getBlockSize(above); + setBlockSize(block, size); + if (tail == above) { + tail = block; + } + if (last == above) { + last = block; + } + above = getNextBlock(above); + } else { + setBlockSize(block, size); + } + setNextBlock(block, above); + + UIntPtr belowBlockSize = getBlockSize(below); + if (below+belowBlockSize == block) { + setBlockSize(below, belowBlockSize+size); + setNextBlock(below, above); + if (tail == block) { + tail = below; + } + if (last == block) { + last = below; + } + } else { + setNextBlock(below, block); + } + } + + return below; + } + + internal UIntPtr TotalMemory { + [Inline] + [ManualRefCounts] + get { + return totalMemory; + } + } + + [Inline] + [ManualRefCounts] + internal unsafe void CheckConsistency() { + if (head == UIntPtr.Zero) { + VTable.Assert(tail == UIntPtr.Zero, + @"tail == UIntPtr.Zero"); + } else if (tail == UIntPtr.Zero) { + VTable.Assert(head == UIntPtr.Zero, + @"head == UIntPtr.Zero"); + } else { + VTable.Assert(head <= tail, + @"head <= tail"); + VTable.Assert(getNextBlock(tail) == head, + @"getNextBlock(tail) == head"); + + UIntPtr leastBlockSize = new UIntPtr(2*sizeof(uint)); + + UIntPtr block = head; + while (block != tail) { + UIntPtr blockSize = getBlockSize(block); + UIntPtr nextBlock = getNextBlock(block); + + VTable.Assert(blockSize >= leastBlockSize, + @"blockSize >= leastBlockSize"); + VTable.Assert(block+blockSize < nextBlock, + @"block+blockSize < nextBlock"); + + block = nextBlock; + } + + VTable.Assert(getBlockSize(block) >= leastBlockSize, + @"getBlockSize(block) >= leastBlockSize"); + } + } + + // Grow free list by allocating and inserting a block of + // size at least "blockSize". + + [Inline] + [ManualRefCounts] + private UIntPtr growFreeList(UIntPtr blockSize, Thread t) { + UIntPtr pageCount = PageTable.PageCount(blockSize); + bool fCleanPages = true; + UIntPtr startPage = PageManager.EnsurePages(t, pageCount, + PageType.Owner0, + ref fCleanPages); + UIntPtr newBlockSize = PageTable.RegionSize(pageCount); + UIntPtr newBlockAddr = PageTable.PageAddr(startPage); + + return FreeBlock(newBlockAddr, newBlockSize); + } + + + // Layout of a free block: + // + // Low address ------> High address + // -------------------------------- + // | | | | + // | A | B | | + // | | | | + // -------------------------------- + // <----------- size -----------> + // + // A: UIntPtr field that contains a UIntPtr to the next block + // on the free list. + // + // B: UIntPtr field that contains the size of the free block. + + private unsafe static UIntPtr getNextBlock(UIntPtr block) { + return *((UIntPtr*)block+0); + } + + private unsafe static void setNextBlock(UIntPtr block, + UIntPtr nextBlock) { + *((UIntPtr*)block+0) = nextBlock; + } + + private unsafe static UIntPtr getBlockSize(UIntPtr block) { + return *((UIntPtr*)block+1); + } + + private unsafe static void setBlockSize(UIntPtr block, + UIntPtr size) { + *((UIntPtr*)block+1) = size; + } + } +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/NullCollector.cs b/base/Imported/Bartok/runtime/shared/GCs/NullCollector.cs new file mode 100644 index 0000000..586a39c --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/NullCollector.cs @@ -0,0 +1,103 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using System.Threading; + + internal class NullCollector : BaseCollector { + + internal override void Collect(Thread currentThread, int generation) + { + GC.CollectTransition(currentThread, generation); + } + + internal override void CollectStoppable(int currentThreadIndex, + int generation) + { + VTable.NotReached("OutOfMemory: NullCollector: Collect called"); + } + + internal override void NewThreadNotification(Thread newThread, + bool initial) + { + base.NewThreadNotification(newThread, initial); + BumpAllocator.NewThreadNotification(newThread, PageType.Owner0); + } + + internal override void CheckForNeededGCWork(Thread currentThread) { + if (NewBytesSinceGCExceeds((UIntPtr) 1500000000)) { + VTable.NotReached + ("OutOfMemory: NullCollector: Out of original memory quota"); + } + } + + internal override int CollectionGeneration(int gen) { + VTable.NotReached + ("OutOfMemory: NullCollector: CollectionGeneration called"); + return MinGeneration; + } + + internal override UIntPtr AllocateObjectMemory(UIntPtr numBytes, + uint alignment, + Thread currentThread) { + return BumpAllocator.Allocate(currentThread, numBytes, alignment); + } + + internal override int GetGeneration(Object obj) { + return MinGeneration; + } + + internal override int MaxGeneration { + get { + return (int)PageType.Owner0; + } + } + + internal override int MinGeneration { + get { + return (int)PageType.Owner0; + } + } + + internal override long TotalMemory { + get { + VTable.NotReached + ("OutOfMemory: NullCollector: TotalMemory called"); + return 0; + } + } + + internal override void EnableHeap() { + } + + internal override void VerifyHeap(bool beforeCollection) { + } + + internal override UIntPtr FindObjectAddr(UIntPtr interiorPtr) { + VTable.NotReached + ("OutOfMemory: NullCollector: FindObjectAddr called"); + return UIntPtr.Zero; + } + + internal override void VisitObjects + (ObjectLayout.ObjectVisitor objectVisitor, + UIntPtr lowAddr, + UIntPtr highAddr) { + } + + internal override bool IsOnTheFlyCollector { + get { + return false; + } + } + } +} diff --git a/base/Kernel/Bartok/GCs/ObjectLayout.cs b/base/Imported/Bartok/runtime/shared/GCs/ObjectLayout.cs similarity index 92% rename from base/Kernel/Bartok/GCs/ObjectLayout.cs rename to base/Imported/Bartok/runtime/shared/GCs/ObjectLayout.cs index 1df18d0..4365905 100644 --- a/base/Kernel/Bartok/GCs/ObjectLayout.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/ObjectLayout.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,10 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - namespace System.GCs { @@ -34,8 +34,10 @@ namespace System.GCs } // Returns object size in bytes + [NoBarriers] [ManualRefCounts] [NoHeapAllocation] + [Inline] internal static UIntPtr ObjectSize(VTable vtable) { uint baseLength = unchecked((uint)vtable.baseLength); @@ -44,8 +46,10 @@ namespace System.GCs } // Returns string size in bytes + [NoBarriers] [ManualRefCounts] [NoHeapAllocation] + [Inline] internal static unsafe UIntPtr StringSize(VTable vtable, uint arrayLength) { @@ -55,9 +59,11 @@ namespace System.GCs } // Returns array size in bytes + [NoBarriers] [ManualRefCounts] [NoInline] [NoHeapAllocation] + [Inline] internal static UIntPtr ArraySize(VTable vtable, uint numElements) { UIntPtr baseLength = (UIntPtr) @@ -71,6 +77,7 @@ namespace System.GCs // Returns object size in bytes [ManualRefCounts] [NoHeapAllocation] + [Inline] internal unsafe static UIntPtr Sizeof(Object obj) { return ObjectSize(Magic.addressOf(obj), obj.vtable); } diff --git a/base/Kernel/Bartok/GCs/OffsetTable.cs b/base/Imported/Bartok/runtime/shared/GCs/OffsetTable.cs similarity index 96% rename from base/Kernel/Bartok/GCs/OffsetTable.cs rename to base/Imported/Bartok/runtime/shared/GCs/OffsetTable.cs index 50b3ece..7a08dcd 100644 --- a/base/Kernel/Bartok/GCs/OffsetTable.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/OffsetTable.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,9 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// //#define DEBUG_OFFSETTABLE namespace System.GCs { @@ -299,9 +300,6 @@ namespace System.GCs { // Clear the lowest bits, if set vtableAddr &= ~((UIntPtr)3); } - VTable.Assert((UIntPtr.Size == 8) || - ((vtableAddr & 0x80000000) == 0), - "Unaligned vtable addr"); VTable vtable = Magic.toVTable(Magic.fromAddress(vtableAddr)); return ObjectLayout.ObjectSize(addr, vtable); diff --git a/base/Kernel/Bartok/GCs/PageManager.cs b/base/Imported/Bartok/runtime/shared/GCs/PageManager.cs similarity index 83% rename from base/Kernel/Bartok/GCs/PageManager.cs rename to base/Imported/Bartok/runtime/shared/GCs/PageManager.cs index 7d7a90b..aac49e3 100644 --- a/base/Kernel/Bartok/GCs/PageManager.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/PageManager.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,10 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - #if !SINGULARITY || (SINGULARITY_KERNEL && SINGULARITY_MP) #define USE_SPINLOCK #elif SINGULARITY_PROCESS @@ -23,19 +23,19 @@ namespace System.GCs { #if SINGULARITY using Microsoft.Singularity; #endif - + internal unsafe class PageManager { // WARNING: don't initialize any static fields in this class // without manually running the class constructor at startup! - // 2006/04/20: Makes old-generation collections take 5+ seconds: + // Makes old-generation collections take 5+ seconds: private static bool SlowDebug { get { return false; } } - // 2006/04/20: Known to slow down selfhost if true: + // Known to slow down selfhost if true: private static bool AggressiveMemReset { get { return false; } } @@ -62,7 +62,8 @@ namespace System.GCs { // node in the 'unusedMemoryBlocks' array. The last page of region // simply points back to the beginning of the region. - private struct UnusedBlockHeader { + internal struct UnusedBlockHeader { + private const int magicNumber = 0x1234567; // Simply a sanity check against random overwrites of the region. @@ -82,101 +83,86 @@ namespace System.GCs { // normal header fields. internal UnusedBlockHeader * curr; - internal void Initialize(UIntPtr count) + internal static unsafe void Initialize(UnusedBlockHeader *header, + UIntPtr count) { - this.magic = (UIntPtr) magicNumber; - this.next = null; - this.prev = null; - this.count = count; - - UIntPtr thisAddr; - fixed (UnusedBlockHeader *thisFixedAddr = &this) { - thisAddr = (UIntPtr) thisFixedAddr; - } - - UIntPtr tailAddr = thisAddr + PageTable.RegionSize(count - 1); - UnusedBlockHeader * tail = (UnusedBlockHeader *) tailAddr; - - tail->curr = (UnusedBlockHeader *) thisAddr; + header->magic = (UIntPtr) magicNumber; + header->next = null; + header->prev = null; + header->count = count; + UnusedBlockHeader *tailBlock = (UnusedBlockHeader *) + (((UIntPtr) header) + PageTable.RegionSize(count - 1)); + tailBlock->curr = header; } [System.Diagnostics.Conditional("DEBUG")] - internal void Verify() { - VTable.Assert(this.magic == (UIntPtr) magicNumber, + internal static unsafe void Verify(UnusedBlockHeader *header) + { + VTable.Assert(header->magic == (UIntPtr) magicNumber, "Bad magic number in UnusedBlockHeader"); - VTable.Assert(this.count > 0, + VTable.Assert(header->count > 0, "Count <= 0 in UnusedBlockHeader"); - fixed (UnusedBlockHeader *thisAddr = &this) { - VTable.Assert(this.prev->next == thisAddr, - "UnusedBlockHeader not linked properly (1)"); - if (this.next != null) { - VTable.Assert - (this.next->prev == thisAddr, - "UnusedBlockHeader not linked properly (2)"); - } - UIntPtr tailAddr = - ((UIntPtr) thisAddr) - + PageTable.RegionSize(this.count - 1); - UnusedBlockHeader * tail = (UnusedBlockHeader *) tailAddr; - VTable.Assert(tail->curr == (UnusedBlockHeader *) thisAddr, - "UnusedBlockHeader tail->curr is incorrect"); - if (PageManager.SlowDebug) { - UIntPtr page = PageTable.Page((UIntPtr)thisAddr); - for (UIntPtr i = UIntPtr.Zero; i < this.count; i++) { - VTable.Assert(PageTable.IsUnusedPage(page+i) && - PageTable.IsMyPage(page+i), - "Incorrect page in unused region"); - } + VTable.Assert(header->prev->next == header, + "UnusedBlockHeader not linked properly (1)"); + if (header->next != null) { + VTable.Assert(header->next->prev == header, + "UnusedBlockHeader not linked properly (2)"); + } + UIntPtr count = header->count; + UnusedBlockHeader *tailBlock = (UnusedBlockHeader *) + (((UIntPtr) header) + PageTable.RegionSize(count - 1)); + VTable.Assert(tailBlock->curr == header, + "UnusedBlockHeader tail->curr is incorrect"); + if (PageManager.SlowDebug) { + UIntPtr page = PageTable.Page((UIntPtr)header); + for (UIntPtr i = UIntPtr.Zero; i < count; i++) { + VTable.Assert(PageTable.IsUnusedPage(page+i) && + PageTable.IsMyPage(page+i), + "Incorrect page in unused region"); } } } - internal void InsertNext(UnusedBlockHeader *newNext) { + internal static unsafe void InsertNext(UnusedBlockHeader *header, + UnusedBlockHeader *newNext) + { //Trace.Log(Trace.Area.Page, // "UnusedBlockHeader.InsertNext {0} count={1}", // __arglist(newNext, newNext->count)); - UnusedBlockHeader *oldNext = this.next; - this.next = newNext; + UnusedBlockHeader *oldNext = header->next; + header->next = newNext; newNext->next = oldNext; - fixed (UnusedBlockHeader *thisAddr = &this) { - newNext->prev = thisAddr; - } + newNext->prev = header; if (oldNext != null) { oldNext->prev = newNext; } - newNext->Verify(); + UnusedBlockHeader.Verify(newNext); } - internal UIntPtr Remove() { + internal static unsafe UIntPtr Remove(UnusedBlockHeader *header) + { //Trace.Log(Trace.Area.Page, // "UnusedBlockHeader.Remove {0} count={1}", // __arglist(this.prev->next, this.count)); - this.Verify(); - this.prev->next = this.next; - if (this.next != null) { - this.next->prev = this.prev; + UnusedBlockHeader.Verify(header); + header->prev->next = header->next; + if (header->next != null) { + header->next->prev = header->prev; } - UIntPtr result = this.count; - this.magic = UIntPtr.Zero; - this.prev = null; - this.next = null; - this.count = UIntPtr.Zero; - - UIntPtr thisAddr; - fixed (UnusedBlockHeader *thisFixedAddr = &this) { - thisAddr = (UIntPtr) thisFixedAddr; - } - - UIntPtr tailAddr = thisAddr + PageTable.RegionSize(result - 1); - UnusedBlockHeader * tail = (UnusedBlockHeader *) tailAddr; - - tail->curr = null; - + UIntPtr result = header->count; + header->magic = UIntPtr.Zero; + header->prev = null; + header->next = null; + header->count = UIntPtr.Zero; + UnusedBlockHeader *tailBlock = (UnusedBlockHeader *) + (((UIntPtr) header) + PageTable.RegionSize(result - 1)); + tailBlock->curr = null; return result; } } + [NoBarriers] [PreInitRefCounts] internal static void Initialize(UIntPtr os_commit_size, UIntPtr heap_commit_size) @@ -190,6 +176,9 @@ namespace System.GCs { outOfMemoryException = (OutOfMemoryException) BootstrapMemory.Allocate(typeof(OutOfMemoryException)); #if SINGULARITY_KERNEL +#if USE_SPINLOCK + Lock = new SpinLock(SpinLock.Types.PageManager); +#endif avoidDirtyPages = true; #else avoidDirtyPages = false; @@ -200,6 +189,7 @@ namespace System.GCs { { #if USE_MUTEX mutex = new Mutex(); + GC.SuppressFinalize(mutex); #endif } @@ -208,8 +198,8 @@ namespace System.GCs { if (currentThread != null #if !SINGULARITY || SEMISPACE_COLLECTOR || SLIDING_COLLECTOR || ADAPTIVE_COPYING_COLLECTOR || MARK_SWEEP_COLLECTOR && - StopTheWorldCollector.CurrentPhase != - StopTheWorldCollector.STWPhase.SingleThreaded + StopTheWorldGCData.CurrentPhase != + StopTheWorldPhase.SingleThreaded #endif ) { #if SINGULARITY_KERNEL @@ -230,8 +220,8 @@ namespace System.GCs { if (currentThread != null #if !SINGULARITY || SEMISPACE_COLLECTOR || SLIDING_COLLECTOR || ADAPTIVE_COPYING_COLLECTOR || MARK_SWEEP_COLLECTOR && - StopTheWorldCollector.CurrentPhase != - StopTheWorldCollector.STWPhase.SingleThreaded + StopTheWorldGCData.CurrentPhase != + StopTheWorldPhase.SingleThreaded #endif ) { #if USE_MUTEX @@ -274,10 +264,7 @@ namespace System.GCs { internal static UIntPtr AllocateNonheapMemory(Thread currentThread, UIntPtr size) { - bool iflag = false; - if (currentThread != null) { - iflag = EnterMutex(currentThread); - } + bool iflag = EnterMutex(currentThread); try { UIntPtr result = MemoryManager.AllocateMemory(size); if (result != UIntPtr.Zero) { @@ -285,9 +272,7 @@ namespace System.GCs { } return result; } finally { - if (currentThread != null) { - LeaveMutex(currentThread, iflag); - } + LeaveMutex(currentThread, iflag); } } @@ -364,6 +349,9 @@ namespace System.GCs { } } if (startAddr == UIntPtr.Zero) { + // BUGBUG: if in CMS, should wait on one complete GC cycle and + // the retry. for STW, we may get here even if the collector + // hasn't triggered just prior. PageTable.Dump("Out of memory"); throw outOfMemoryException; } @@ -376,7 +364,7 @@ namespace System.GCs { // Mark the new memory pages as allocated-but-unused MarkUnusedPages(/* avoid recursive locking */ null, startPage+pageCount, extraPages, - fCleanPages); + true); } return startPage; } finally { @@ -582,14 +570,43 @@ namespace System.GCs { } } -#if !SINGULARITY +#if !SINGULARITY // Needed before Bartok runtime is initialized [NoStackLinkCheck] #endif internal static unsafe void SetStaticDataPages(UIntPtr startAddr, UIntPtr size) { + +#if SINGULARITY + // It's perfectly fine to be given memory outside the page table region, so + // long as we intend to treat that memory as NonGC anyway. + if (startAddr < PageTable.baseAddr) + { + if (startAddr + size < PageTable.baseAddr) + { + // nothing to do. All pages are below the base covered by the page table + return; + } + // The range overlaps with the region covered by the page table + size -= (PageTable.baseAddr - startAddr); + startAddr = PageTable.baseAddr; + } + UIntPtr endAddr = startAddr + size; + if (endAddr > PageTable.limitAddr) + { + if (startAddr > PageTable.limitAddr) + { + // nothing to do. All pages are above the limit covered by the page table + return; + } + // The range overlaps with the region covered by the page table + size -= (PageTable.limitAddr - endAddr); + } +#endif + UIntPtr startIndex = PageTable.Page(startAddr); UIntPtr pageCount = PageTable.PageCount(size); + PageTable.SetType(startIndex, pageCount, PageType.NonGC); } @@ -608,10 +625,10 @@ namespace System.GCs { UIntPtr endPage = PageTable.Page(endAddr); UIntPtr pageCount = endPage - startPage; PageTable.VerifyType(startPage, pageCount, PageType.Unallocated); - PageTable.VerifyExtra(startPage, pageCount, (short) 0); + PageTable.VerifyExtra(startPage, pageCount, 0); PageTable.SetType(startPage, pageCount, PageType.Stack); PageTable.SetExtra(startPage, pageCount, - (short) thread.threadIndex); + (uint) thread.threadIndex); } internal static unsafe void MarkThreadStack(Thread thread) { @@ -651,7 +668,8 @@ namespace System.GCs { [ManualRefCounts] private static void LinkUnusedPages(UIntPtr startPage, - UIntPtr pageCount) + UIntPtr pageCount, + bool asVictim) { if (PageManager.SlowDebug) { for (UIntPtr i = startPage; i < startPage + pageCount; i++) { @@ -672,9 +690,26 @@ namespace System.GCs { PageTable.IsMyPage(startPage + pageCount)); UnusedBlockHeader *header = (UnusedBlockHeader *) PageTable.PageAddr(startPage); - header->Initialize(pageCount); + UnusedBlockHeader.Initialize(header, pageCount); int slot = SlotFromCount(pageCount); - unusedMemoryBlocks[slot].InsertNext(header); + + // Unused blocks are linked into the free list either as the result of a collection + // or as a result of carving a big block into a smaller allocation and a remainder. + // When such a remainder is linked back into the free list, it is identified as a + // victim. We favor subsequent allocations from these victims, in an attempt to + // reduce fragmentation. This is achieved by keeping victims at the head of the + // free list. + // + // TODO: the long term solution is to perform best fit on the free list. + if (asVictim || unusedMemoryBlocks[slot].next == null) { + fixed (UnusedBlockHeader *listHeader = &unusedMemoryBlocks[slot]) { + UnusedBlockHeader.InsertNext(listHeader, header); + } + } + else { + UnusedBlockHeader *listHeader = unusedMemoryBlocks[slot].next; + UnusedBlockHeader.InsertNext(listHeader, header); + } } private static UIntPtr UnlinkUnusedPages(UIntPtr startPage) @@ -686,7 +721,7 @@ namespace System.GCs { PageTable.IsMyPage(startPage-1)); UnusedBlockHeader *header = (UnusedBlockHeader *) PageTable.PageAddr(startPage); - UIntPtr pageCount = header->Remove(); + UIntPtr pageCount = UnusedBlockHeader.Remove(header); Trace.Log(Trace.Area.Page, "UnlinkUnusedPages start={0:x} count={1:x}", __arglist(startPage, pageCount)); @@ -712,10 +747,7 @@ namespace System.GCs { Util.MemClear(dirtyStartAddr, dirtySize); fCleanPages = true; } - bool iflag = false; - if (currentThread != null) { - iflag = EnterMutex(currentThread); - } + bool iflag = EnterMutex(currentThread); try { if (endPage < PageTable.pageTableCount) { if (PageTable.IsUnusedPage(endPage) && @@ -740,11 +772,9 @@ namespace System.GCs { PageType pageType = fCleanPages ? PageType.UnusedClean : PageType.UnusedDirty; PageTable.SetType(startPage, pageCount, pageType); - LinkUnusedPages(newStartPage, endPage - newStartPage); + LinkUnusedPages(newStartPage, endPage - newStartPage, false); } finally { - if (currentThread != null) { - LeaveMutex(currentThread, iflag); - } + LeaveMutex(currentThread, iflag); } } @@ -797,7 +827,7 @@ namespace System.GCs { SetPageTypeClean(startPage, pageCount, newType); if (regionPages > pageCount) { UIntPtr suffixPages = regionPages - pageCount; - LinkUnusedPages(endPage, suffixPages); + LinkUnusedPages(endPage, suffixPages, true); } } finally { LeaveMutex(currentThread, iflag); @@ -839,8 +869,8 @@ namespace System.GCs { UIntPtr bytesNeeded = PageTable.RegionSize(pagesNeeded); UIntPtr allocSize = Util.Pad(bytesNeeded, heap_commit_size); UIntPtr startAddr = PageTable.PageAddr(index); - bool iflag = EnterMutex(currentThread); bool gotMemory = false; + bool iflag = EnterMutex(currentThread); try { gotMemory = MemoryManager.AllocateMemory(startAddr, allocSize); @@ -893,7 +923,7 @@ namespace System.GCs { if (regionSize > pageCount) { UIntPtr restCount = regionSize - pageCount; UIntPtr endPage = startPage + pageCount; - LinkUnusedPages(endPage, restCount); + LinkUnusedPages(endPage, restCount, true); } Trace.Log(Trace.Area.Page, "FindUnusedPages success {0:x}", @@ -936,9 +966,7 @@ namespace System.GCs { bool result = true; while (count != 0) { result = false; - UIntPtr dirtySize = *cursor; *cursor-- = UIntPtr.Zero; - UIntPtr dirtyStartAddr = *cursor; *cursor-- = UIntPtr.Zero; count--; } @@ -946,6 +974,25 @@ namespace System.GCs { return result; } + [ManualRefCounts] + internal static UIntPtr TotalUnusedPages() { + Thread currentThread = Thread.CurrentThread; + UIntPtr pageCount = (UIntPtr) 0; + bool iflag = EnterMutex(currentThread); + try { + for (int slot = 0; slot < 32; slot++) { + UnusedBlockHeader *header = unusedMemoryBlocks[slot].next; + while (header != null) { + pageCount += header->count; + header = header->next; + } + } + return pageCount; + } finally { + LeaveMutex(currentThread, iflag); + } + } + [System.Diagnostics.Conditional("DEBUG")] internal static void VerifyUnusedPage(UIntPtr page, bool containsHeader) { @@ -1019,12 +1066,12 @@ namespace System.GCs { // list of unused memory blocks int slot = SlotFromCount(pageCount); UnusedBlockHeader *header = unusedMemoryBlocks[slot].next; - header->Verify(); + UnusedBlockHeader.Verify(header); while (regionAddr != (UIntPtr) header) { header = header->next; VTable.Assert(header != null, "Unused region not list for its slot number"); - header->Verify(); + UnusedBlockHeader.Verify(header); } } diff --git a/base/Kernel/Bartok/GCs/PageTable.cs b/base/Imported/Bartok/runtime/shared/GCs/PageTable.cs similarity index 87% rename from base/Kernel/Bartok/GCs/PageTable.cs rename to base/Imported/Bartok/runtime/shared/GCs/PageTable.cs index 0a5eab6..54dc6f4 100644 --- a/base/Kernel/Bartok/GCs/PageTable.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/PageTable.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,10 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - namespace System.GCs { using System.Runtime.CompilerServices; @@ -26,6 +26,7 @@ namespace System.GCs { #endif [RequiredByBartok] + [AccessedByRuntime("referenced from brtforgc.asm/halforgc.asm")] abstract internal class PageTable { // WARNING: don't initialize any static fields in this class @@ -59,17 +60,20 @@ namespace System.GCs { internal static UIntPtr pageTableCount; #if SINGULARITY - protected static UIntPtr baseAddr; - protected static UIntPtr limitAddr; + public static UIntPtr baseAddr; + public static UIntPtr limitAddr; #endif - [RequiredByBartok] [AccessedByRuntime("referenced from brtforgc.asm/halforgc.asm")] internal static unsafe uint *halPageDescriptor; [Intrinsic] internal static PTType ptType; +#if SINGULARITY + internal const uint kernelProcessTag = 0x10000; +#endif + #if SINGULARITY_KERNEL internal const uint ProcessPageMask = Sing_MemoryManager.ProcessPageMask; internal static uint processTag; @@ -78,10 +82,10 @@ namespace System.GCs { { Tracing.Log(Tracing.Debug, "Initialize."); - processTag = 0x10000; + processTag = kernelProcessTag; baseAddr = Sing_MemoryManager.KernelBaseAddr; pageTableCount = Sing_MemoryManager.KernelPageCount; - limitAddr = baseAddr + pageTableCount << PageBits; + limitAddr = baseAddr + (pageTableCount << PageBits); halPageDescriptor = Sing_MemoryManager.KernelPageTable; } #elif SINGULARITY_PROCESS @@ -123,7 +127,7 @@ namespace System.GCs { [PreInitRefCounts] [NoStackLinkCheck] - internal static unsafe void Initialize() { + internal static void Initialize() { switch(ptType) { case PTType.CentralPT: { CentralPT.Initialize(); @@ -243,8 +247,9 @@ namespace System.GCs { PageType lastType = Type(UIntPtr.Zero); ushort lastProcess = Process((UIntPtr) 1); +#if NOTHING UIntPtr begin = UIntPtr.Zero; - +#endif UIntPtr freePages = UIntPtr.Zero; UIntPtr usedPages = UIntPtr.Zero; UIntPtr stackPages = UIntPtr.Zero; @@ -301,7 +306,9 @@ namespace System.GCs { #endif lastType = type; lastProcess = Process(i); +#if NOTHING begin = i; +#endif } } @@ -346,7 +353,7 @@ namespace System.GCs { } [Inline] - internal unsafe static PageType MyType(UIntPtr page) { + internal static PageType MyType(UIntPtr page) { #if SINGULARITY uint value = PageTableEntry(page); if ((value & ProcessPageMask) == processTag) { @@ -361,13 +368,13 @@ namespace System.GCs { } [Inline] - internal unsafe static PageType Type(UIntPtr page) { + internal static PageType Type(UIntPtr page) { VTable.Assert(page < pageTableCount, "page out of count"); return (PageType)(PageTableEntry(page) & 0xf); } [Inline] - internal unsafe static void SetType(UIntPtr page, PageType newType) { + internal static void SetType(UIntPtr page, PageType newType) { VTable.Assert(page < pageTableCount); // for the Generational Collector, keep track of the max and min @@ -380,25 +387,26 @@ namespace System.GCs { case GCType.SlidingCollector: int generation = (int)newType; - if (generation >= (int)GCs.GenerationalCollector.MIN_GENERATION - && generation <= (int)GCs.GenerationalCollector.MAX_GENERATION) + if (generation >= (int)GCs.GenerationalGCData.MIN_GENERATION + && generation <= (int)GCs.GenerationalGCData.MAX_GENERATION) { - if (page < GCs.GenerationalCollector.MinGenPage[generation]) + if (page < GCs.GenerationalGCData.MinGenPage[generation]) { - GCs.GenerationalCollector.MinGenPage[generation] = page; + GCs.GenerationalGCData.MinGenPage[generation] = page; } - if (page > GCs.GenerationalCollector.MaxGenPage[generation]) + if (page > GCs.GenerationalGCData.MaxGenPage[generation]) { - GCs.GenerationalCollector.MaxGenPage[generation] = page; + GCs.GenerationalGCData.MaxGenPage[generation] = page; } } break; #endif -#if !SINGULARITY || MARK_SWEEP_COLLECTOR || CONCURRENT_MS_COLLECTOR +#if !SINGULARITY || MARK_SWEEP_COLLECTOR || CONCURRENT_MS_COLLECTOR || NULL_COLLECTOR case GCType.MarkSweepCollector: case GCType.TableMarkSweepCollector: case GCType.ReferenceCountingCollector: case GCType.ConcurrentMSCollector: + case GCType.CoCoMSCollector: case GCType.AtomicRCCollector: case GCType.DeferredReferenceCountingCollector: case GCType.NullCollector: @@ -421,20 +429,23 @@ namespace System.GCs { } [Inline] - internal static short Extra(UIntPtr page) { + internal static uint Extra(UIntPtr page) { VTable.Assert(page < pageTableCount); - return (short)((PageTableEntry(page) & 0xfff0) >> 4); + uint result = (PageTableEntry(page) & 0xfff0U) >> 4; + VTable.Assert(((short) result)>=0); + return result; } [Inline] - internal static void SetExtra(UIntPtr page, short extra) { + internal static void SetExtra(UIntPtr page, uint extra) { VTable.Assert(page < pageTableCount); - uint value = (PageTableEntry(page) & ~0xfff0U) | ((uint) extra << 4); + VTable.Assert(extra <= 0xfff); + uint value = (PageTableEntry(page) & ~0xfff0U) | (extra << 4); SetPageTableEntry(page, value); } [System.Diagnostics.Conditional("DEBUG")] - internal static void VerifyExtra(UIntPtr page, short extra) { + internal static void VerifyExtra(UIntPtr page, uint extra) { VTable.Assert(Extra(page) == extra); } @@ -450,7 +461,7 @@ namespace System.GCs { [Inline] [System.Diagnostics.Conditional("SINGULARITY")] - internal unsafe static void SetProcess(UIntPtr page, ushort processId) { + internal static void SetProcess(UIntPtr page, ushort processId) { VTable.Assert(page < pageTableCount); uint value = (PageTableEntry(page) & ~0xffff0000U) | ((uint) processId << 16); SetPageTableEntry(page, value); @@ -458,7 +469,7 @@ namespace System.GCs { #if SINGULARITY [Inline] - private unsafe static uint ProcessTag(UIntPtr page) { + private static uint ProcessTag(UIntPtr page) { VTable.Assert(page < pageTableCount); return PageTableEntry(page) & 0xffff0000u; } @@ -466,8 +477,9 @@ namespace System.GCs { [Inline] [System.Diagnostics.Conditional("SINGULARITY")] - private unsafe static void SetProcessTag(UIntPtr page, uint processTag) { + private static void SetProcessTag(UIntPtr page, uint processTag) { VTable.Assert(page < pageTableCount); + VTable.Assert(processTag > 0x0000ffffU); uint value = (PageTableEntry(page) & ~0xffff0000U) | processTag; SetPageTableEntry(page, value); } @@ -499,7 +511,7 @@ namespace System.GCs { [Inline] internal static void SetExtra(UIntPtr startPage, UIntPtr pageCount, - short extra) + uint extra) { while (pageCount > UIntPtr.Zero) { SetExtra(startPage, extra); @@ -511,7 +523,7 @@ namespace System.GCs { [System.Diagnostics.Conditional("DEBUG")] internal static void VerifyExtra(UIntPtr startPage, UIntPtr pageCount, - short extra) + uint extra) { while (pageCount > UIntPtr.Zero) { VerifyExtra(startPage, extra); @@ -525,7 +537,6 @@ namespace System.GCs { internal static void SetProcess(UIntPtr startPage, UIntPtr pageCount) { - uint processId = processTag; PageTable.SetProcessTag(startPage, pageCount, processTag); } @@ -642,7 +653,7 @@ namespace System.GCs { } [Inline] - internal unsafe static bool IsMyPage(UIntPtr page) { + internal static bool IsMyPage(UIntPtr page) { #if SINGULARITY return PageTable.ProcessTag(page) == processTag; #else @@ -650,6 +661,13 @@ namespace System.GCs { #endif } +#if SINGULARITY + [Inline] + internal static bool IsKernelPage(UIntPtr page) { + return PageTable.ProcessTag(page) == kernelProcessTag; + } +#endif + [Inline] internal static bool IsForeignAddr(UIntPtr addr) { #if SINGULARITY @@ -669,6 +687,15 @@ namespace System.GCs { #endif } + [Inline] + internal static bool IsAddrOnPage(UIntPtr addr, UIntPtr page) { +#if SINGULARITY + return ((addr - baseAddr) >> PageBits) == page; +#else + return (addr >> PageBits) == page; +#endif + } + [Inline] internal static UIntPtr PageCount(UIntPtr size) { return ((size + PageMask) >> PageBits); @@ -713,7 +740,7 @@ namespace System.GCs { // any potential "new static" confusion, we have the names with "Impl" suffix. [Inline] - internal static unsafe uint PageTableEntry(UIntPtr page) + internal static uint PageTableEntry(UIntPtr page) { switch(ptType) { case PTType.CentralPT: @@ -733,7 +760,7 @@ namespace System.GCs { } [Inline] - internal static unsafe void SetPageTableEntry(UIntPtr page, uint value) + internal static void SetPageTableEntry(UIntPtr page, uint value) { switch(ptType) { case PTType.CentralPT: @@ -755,8 +782,8 @@ namespace System.GCs { } } - internal static unsafe void CreateNewPageTablesIfNecessary(UIntPtr startPage, - UIntPtr pageCount) + internal static void CreateNewPageTablesIfNecessary(UIntPtr startPage, + UIntPtr pageCount) { #if !SINGULARITY if (ptType == PTType.FlatDistributedPT ||ptType == PTType.FlatDistributedPTTest) { diff --git a/base/Kernel/Bartok/GCs/PageType.cs b/base/Imported/Bartok/runtime/shared/GCs/PageType.cs similarity index 94% rename from base/Kernel/Bartok/GCs/PageType.cs rename to base/Imported/Bartok/runtime/shared/GCs/PageType.cs index 202cb01..c5404d9 100644 --- a/base/Kernel/Bartok/GCs/PageType.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/PageType.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,10 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - namespace System.GCs { using System.Runtime.CompilerServices; @@ -25,7 +25,7 @@ namespace System.GCs { // change the order of Owner0...Owner7. [FlagsAttribute] - internal enum PageType : byte { + public enum PageType : byte { // Let Unallocated be 0. Then after the page table is allocated by VirtualAlloc, // which automatically initializes the memory to 0, the page table entries diff --git a/base/Imported/Bartok/runtime/shared/GCs/ProbabilisticCoCoBarrier.cs b/base/Imported/Bartok/runtime/shared/GCs/ProbabilisticCoCoBarrier.cs new file mode 100644 index 0000000..e78a0ec --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/ProbabilisticCoCoBarrier.cs @@ -0,0 +1,643 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using Microsoft.Bartok.Runtime; + using Microsoft.Bartok.Options; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using System.Threading; + + internal unsafe class ProbabilisticCoCoBarrier: LowAbortCoCoBarrier { + internal static new ProbabilisticCoCoBarrier instance; + + [NoBarriers] + internal static new void Initialize() + { + if (fDietDebug) { + VTable.DebugPrint("Diet DoCo!\n"); + } + + ProbabilisticCoCoBarrier.instance = + (ProbabilisticCoCoBarrier) + BootstrapMemory.Allocate(typeof(ProbabilisticCoCoBarrier)); + ProbabilisticCoCoBarrier.instance.InitEarly(); + LowAbortCoCoBarrier.Initialize(); + } + + internal static UIntPtr Alpha { + get { + // FIXME: make this random + return (UIntPtr)0xD1E7C0C0; + } + } + + // handling of CoCo word + + // four states: + // - simple + // - tagged + // - copying + // - forwarded + + // copying means that the object is tagged and there is the chance + // that some field's most up-to-date value is in to-space. copying + // means that we cannot pin without waiting. + + // forwarded means that all fields are copied. + + [Inline] + static internal UIntPtr ForwardPtr(UIntPtr CoCoWord) + { + return CoCoWord&~(UIntPtr)3; + } + + [Inline] + static internal bool IsTagged(UIntPtr CoCoWord) + { + return (CoCoWord&(UIntPtr)3) == (UIntPtr)1; + } + + [Inline] + static internal bool IsCopying(UIntPtr CoCoWord) + { + return (CoCoWord&(UIntPtr)3) == (UIntPtr)2; + } + + [Inline] + static internal bool IsForwarded(UIntPtr CoCoWord) + { + return (CoCoWord&(UIntPtr)3) == (UIntPtr)3; + } + + [Inline] + static internal bool IsSimple(UIntPtr CoCoWord) + { + return CoCoWord==UIntPtr.Zero; + } + + [Inline] + static internal UIntPtr Simple() + { + return UIntPtr.Zero; + } + + [Inline] + static internal UIntPtr Tagged(UIntPtr forward) + { + VTable.Assert(ForwardPtr(forward)==forward); + return forward|(UIntPtr)1; + } + + [Inline] + static internal UIntPtr Copying(UIntPtr CoCoWord) + { + return ForwardPtr(CoCoWord)|(UIntPtr)2; + } + + [Inline] + static internal UIntPtr Forwarded(UIntPtr CoCoWord) + { + return ForwardPtr(CoCoWord)|(UIntPtr)3; + } + + [Inline] + [NoBarriers] + internal override UIntPtr ToSpaceImplBeforeReadyNonNull(Object o) + { + UIntPtr CoCoWord = MixinObject(o).preHeader.CoCoWord; + if (IsSimple(CoCoWord)) { + return Magic.addressOf(o); + } else { + return ForwardPtr(CoCoWord); + } + } + + [Inline] + [NoBarriers] + internal override bool IsInToSpace(UIntPtr ptr) + { + return !IsForwarded(MixinObject(Magic.fromAddress(ptr)).preHeader.CoCoWord); + } + + [Inline] + [NoBarriers] + internal override UIntPtr ToSpaceImplNonNull(Object o) + { + UIntPtr CoCoWord = MixinObject(o).preHeader.CoCoWord; + if (IsForwarded(CoCoWord)) { + return ForwardPtr(CoCoWord); + } else { + return Magic.addressOf(o); + } + } + + [Inline] + [NoBarriers] + internal static bool CASCoCoWord(Object o, + UIntPtr value, + UIntPtr comparand) + { + return CAS(ref MixinObject(o).preHeader.CoCoWord, + value,comparand) + == comparand; + } + + internal static UIntPtr PinDirect(UIntPtr baseAddr, + UIntPtr address, + Pinner pinner) + { + Object o = Magic.fromAddress(baseAddr); + + UIntPtr CoCoWord; + + bool waited=false; + for (;;) { + CoCoWord = MixinObject(o).preHeader.CoCoWord; + if (IsSimple(CoCoWord) || IsForwarded(CoCoWord)) { + break; + } else if (IsCopying(CoCoWord)) { + VTable.Assert(pinner==Pinner.Barrier); + if (fCount) { + if (!waited) { + waited=true; + numWaitPins++; + } + numPinWaits++; + } + // wait until it's copied + lock (interlock) { + CoCoThread t = MixinThread(Thread.CurrentThread); + t.pinnedOut = true; + Monitor.PulseAll(interlock); + while (t.pinnedOut) { + Monitor.Wait(interlock); + } + } + // ok, now try again (by the time we get here the + // object could already be in the process of being + // copied agagin!) + } else if (IsTagged(CoCoWord)) { + CASCoCoWord(o,Simple(),CoCoWord); + NotifyPin(baseAddr); + } + } + + // what does it mean to get here? the object cannot + // be moved until the next pinning safepoint prior to + // a transition out of Idle. + + VTable.Assert(IsSimple(CoCoWord) || IsForwarded(CoCoWord)); + + // correct address + if (IsForwarded(CoCoWord)) { + address -= baseAddr; + address += ForwardPtr(CoCoWord); + } + + return address; + } + + [NoInline] + private static UIntPtr PinDirectAndForwardValue(UIntPtr baseAddr, + UIntPtr address, + ref Object objValue) + { + return PinDirect(baseAddr,address,Pinner.Barrier); + } + + // Worst Hack Ever. PinDirect may cause an unbounded number of + // phase changes, meaning that the pointer we're writing into the + // heap may be "from-spaced" any number of times - causing it to + // potentially point at rotting garbage. So, we force the collector + // to do some forwarding for us. + [NoInline] + private static UIntPtr PinDirectAndForwardValue(UIntPtr baseAddr, + UIntPtr address, + bool isObject, + ref UIntPtr shiftedValue) + { + Object objValue = null; + if (isObject) { + objValue = Magic.fromAddress(shiftedValue); + } + address=PinDirectAndForwardValue(baseAddr,address,ref objValue); + if (isObject) { + shiftedValue = Magic.addressOf(objValue); + } + return address; + } + + internal override UIntPtr DoPin(UIntPtr address, + Pinner pinner) + { + if (fCount) { + numPins++; + } + + UIntPtr baseAddr = FindObjectForInteriorPtr(address); + if (baseAddr != UIntPtr.Zero) { + address=PinDirect(baseAddr, address, pinner); + } + + return address; + } + + protected override UIntPtr ReadWordSlow(Object o, + UIntPtr offset) + { + UIntPtr word = *(UIntPtr*)(Magic.addressOf(o) + offset); + if (word == Alpha) { + UIntPtr CoCoWord = MixinObject(o).preHeader.CoCoWord; + if (IsSimple(CoCoWord)) { + return word; + } else { + return *(UIntPtr*)(ForwardPtr(CoCoWord) + offset); + } + } else { + return word; + } + } + + static void WriteWordNoForward(UIntPtr *ptr, + UIntPtr offset, + UIntPtr mask, + UIntPtr shiftedValue, + bool isObject) + { + if (mask == UIntPtr.MaxValue) { + if (isObject) { + TargetBarrierWithForward(*ptr); + } + *ptr = shiftedValue; + } else { + for (;;) { + UIntPtr oldVal=*ptr; + if (CAS(ptr, + (oldVal&~mask)|(shiftedValue&mask), + oldVal) + == oldVal) { + return; + } + } + } + } + + static void WriteWordNoForward(Object o, + UIntPtr offset, + UIntPtr mask, + UIntPtr shiftedValue, + bool isObject) + { + UIntPtr *ptr = (UIntPtr*)(Magic.addressOf(o) + offset); + WriteWordNoForward(ptr, offset, mask, shiftedValue, isObject); + } + + static void WriteWordForwarded(Object o, + UIntPtr offset, + UIntPtr mask, + UIntPtr shiftedValue, + bool isObject) + { + WriteWordNoForward(Magic.fromAddress(ForwardPtr(MixinObject(o).preHeader.CoCoWord)), + offset, mask, shiftedValue, isObject); + } + + protected override void WriteWordSlow(Object o, + UIntPtr offset, + UIntPtr mask, + UIntPtr shiftedValue, + bool isObject) + { + UIntPtr *ptr = (UIntPtr*)(Magic.addressOf(o) + offset); + for (;;) { + UIntPtr word = *ptr; + if (phase == Phase.Idle || + IgnoreOffset(offset)) { + WriteWordNoForward(o,offset,mask,shiftedValue,isObject); + return; + } else if (word == Alpha) { + UIntPtr CoCoWord = MixinObject(o).preHeader.CoCoWord; + if (IsCopying(CoCoWord) || + IsForwarded(CoCoWord) || + (phase == Phase.Copy + && IsTagged(CoCoWord) + && CASCoCoWord(o,Copying(CoCoWord),CoCoWord))) { + // we saw an Alpha, and the object state indicates that + // Alphas mean forwarding. but, we might not get + // here if the object was tagged and we failed to flip it to + // copying. that would only happen if someone managed to + // pin the object. + WriteWordForwarded(o,offset,mask,shiftedValue,isObject); + return; + } else if (IsSimple(CoCoWord)) { + // object was either never tagged or has been pinned by someone + // else. + WriteWordNoForward(o,offset,mask,shiftedValue,isObject); + return; + } else if (phase == Phase.Prep + && IsTagged(CoCoWord) + && CASCoCoWord(o,Simple(),CoCoWord)) { + // we pinned the object. + numTaintPins++; + NotifyPin(Magic.addressOf(o)); + WriteWordNoForward(o,offset,mask,shiftedValue,isObject); + return; + } + } else { + UIntPtr newWord = (word&~mask)|(shiftedValue&mask); + if (newWord == Alpha) { + // FIXME: do stats or debug + // FIXME: potentially wrong. the code does not assume that + // a write barrier may change phases. on the other hand, + // if we end up in here then it means that the code was alredy + // assuming slow-path, and if we emerge from here, then it means + // we had a sync point ... so it should be fine. + + ptr=(UIntPtr*)PinDirectAndForwardValue(Magic.addressOf(o), + (UIntPtr)ptr, + isObject, + ref shiftedValue); + + // ok, object is pinned or forwarded; write to the new + // location for this field. + WriteWordNoForward(ptr, offset, mask, shiftedValue, isObject); + return; + } + // we get here if neither the new or the old word was an Alpha. + // this is the common case. + if (isObject) { + TargetBarrierWithForward(word); + } + if (CAS(ptr,newWord,word) == word) { + return; + } + } + } + } + + static bool WeakCASNoForward(UIntPtr *ptr, + UIntPtr offset, + UIntPtr mask, + UIntPtr shiftedValue, + UIntPtr shiftedComparand) + { + UIntPtr oldVal=*ptr; + UIntPtr comparand = (oldVal&~mask)|(shiftedComparand&mask); + return CAS(ptr, + (oldVal&~mask)|(shiftedValue&mask), + comparand) + == comparand; + } + + static bool WeakCASNoForward(Object o, + UIntPtr offset, + UIntPtr mask, + UIntPtr shiftedValue, + UIntPtr shiftedComparand) + { + UIntPtr *ptr = (UIntPtr*)(Magic.addressOf(o) + offset); + return WeakCASNoForward(ptr, offset, mask, shiftedValue, shiftedComparand); + } + + static bool WeakCASForwarded(Object o, + UIntPtr offset, + UIntPtr mask, + UIntPtr shiftedValue, + UIntPtr shiftedComparand) + { + return WeakCASNoForward(Magic.fromAddress(ForwardPtr(MixinObject(o).preHeader.CoCoWord)), + offset, mask, shiftedValue, shiftedComparand); + } + + protected override bool WeakCASWordSlow(Object o, + UIntPtr offset, + UIntPtr mask, + UIntPtr shiftedValue, + UIntPtr shiftedComparand, + bool isObject) + { + UIntPtr *ptr = (UIntPtr*)(Magic.addressOf(o) + offset); + UIntPtr word = *ptr; + if (phase == Phase.Idle || + IgnoreOffset(offset)) { + return WeakCASNoForward(o,offset,mask,shiftedValue,shiftedComparand); + } else if (word == Alpha) { + UIntPtr CoCoWord = MixinObject(o).preHeader.CoCoWord; + if (IsCopying(CoCoWord) || + IsForwarded(CoCoWord) || + (phase == Phase.Copy + && IsTagged(CoCoWord) + && CASCoCoWord(o,Copying(CoCoWord),CoCoWord))) { + // we saw an Alpha, and the object state indicates that + // Alphas mean forwarding. but, we might not get + // here if the object was tagged and we failed to flip it to + // copying. that would only happen if someone managed to + // pin the object. + return WeakCASForwarded(o,offset,mask,shiftedValue,shiftedComparand); + } else if (IsSimple(CoCoWord)) { + // object was either never tagged or has been pinned by someone + // else. + return WeakCASNoForward(o,offset,mask,shiftedValue,shiftedComparand); + } else if (phase == Phase.Prep + && IsTagged(CoCoWord) + && CASCoCoWord(o,Simple(),CoCoWord)) { + // we pinned the object. + numTaintPins++; + NotifyPin(Magic.addressOf(o)); + return WeakCASNoForward(o,offset,mask,shiftedValue,shiftedComparand); + } + } else if ((word&mask) == (shiftedComparand&mask)) { + UIntPtr newWord = (word&~mask)|(shiftedValue&mask); + if (newWord == Alpha) { + // FIXME: do stats or debug + ptr=(UIntPtr*)PinDirectAndForwardValue(Magic.addressOf(o), + (UIntPtr)ptr, + isObject, + ref shiftedValue); + // ok, object is pinned or forwarded; write to the new + // location for this field. + return WeakCASNoForward(ptr, offset, mask, shiftedValue, shiftedComparand); + } + return CAS(ptr,newWord,word) == word; + } + return false; + } + + protected override bool WeakCASArbitrarySlow(Object o, + UIntPtr offset, + UIntPtr size, + ulong value, + ulong comparand) { + VTable.NotImplemented(); + VTable.DebugBreak(); + return false; + } + + class TagNode { + internal Object from; + internal TagNode next; + } + + static TagNode tagHead; + + internal override bool AnyTaggedForCopying { + get { + return tagHead!=null; + } + } + + internal override bool TagObjectForCopy(Object from, + Object to, + out UIntPtr spaceOverhead) + { + TagNode tn=new TagNode(); + spaceOverhead=ObjectLayout.Sizeof(tn); + tn.from=from; + tn.next=tagHead; + + // prepare to-space object + UIntPtr begin=UIntPtr.Zero-(UIntPtr)PreHeader.Size; + UIntPtr end= + ((ObjectLayout.Sizeof(from)+sizeof(UIntPtr)-1) + &~((UIntPtr)sizeof(UIntPtr)-1)) + -PreHeader.Size; + for (UIntPtr offset=begin; + offset!=end; + offset+=sizeof(UIntPtr)) { + if (!IgnoreOffset(offset)) { + *(UIntPtr*)(Magic.addressOf(to)+offset)=Alpha; + } + } + + // now we can tag from-space + if (CASCoCoWord(from,Tagged(Magic.addressOf(to)),Simple())) { + tagHead=tn; + return true; + } else { + return false; + } + } + + internal override bool NeedsPrepPhase { + get { + return true; + } + } + + [NoBarriers] + static bool CopyObject(Object from) + { + if (fDietVerboseCopyDebug) { + VTable.DebugPrint(" Copying "); + VTable.DebugPrint((ulong)Magic.addressOf(from)); + VTable.DebugPrint(" with CoCoWord = "); + VTable.DebugPrint((ulong)MixinObject(from).preHeader.CoCoWord); + VTable.DebugPrint("\n"); + } + for (;;) { + UIntPtr CoCoWord=MixinObject(from).preHeader.CoCoWord; + if (IsSimple(CoCoWord)) { + // got pinned .. ignore + return false; + } else if (IsTagged(CoCoWord)) { + if (ShouldPin(Magic.addressOf(from))) { + if (CASCoCoWord(from,Simple(),CoCoWord)) { + // pinned + NotifyPin(Magic.addressOf(from)); + return false; + } + } else { + CASCoCoWord(from,Copying(CoCoWord),CoCoWord); + } + } else if (IsCopying(CoCoWord)) { + Object to=Magic.fromAddress(ForwardPtr(CoCoWord)); + UIntPtr begin=UIntPtr.Zero-(UIntPtr)PreHeader.Size; + UIntPtr end= + ((ObjectLayout.Sizeof(from)+sizeof(UIntPtr)-1) + &~((UIntPtr)sizeof(UIntPtr)-1)) + -PreHeader.Size; + if (fDietVerboseCopyDebug) { + VTable.DebugPrint(" copying to "); + VTable.DebugPrint((ulong)Magic.addressOf(to)); + VTable.DebugPrint("; begin = "); + VTable.DebugPrint((ulong)begin); + VTable.DebugPrint("; end = "); + VTable.DebugPrint((ulong)end); + VTable.DebugPrint("; PreHeader.Size = "); + VTable.DebugPrint((ulong)PreHeader.Size); + VTable.DebugPrint("; Sizeof(from) = "); + VTable.DebugPrint((ulong)ObjectLayout.Sizeof(from)); + VTable.DebugPrint("; Sizeof(to) = "); + VTable.DebugPrint((ulong)ObjectLayout.Sizeof(to)); + VTable.DebugPrint("\n"); + } + for (UIntPtr offset=begin; + offset!=end; + offset+=sizeof(UIntPtr)) { + if (!IgnoreOffset(offset)) { + UIntPtr *fptr=(UIntPtr*)(Magic.addressOf(from)+offset); + UIntPtr *tptr=(UIntPtr*)(Magic.addressOf(to)+offset); + for (;;) { + UIntPtr word=*fptr; + if (word==Alpha) { + // NOTE: this case will only be hit the first time + // around the loop. if it's not hit the first time + // it'll never get hit. + break; + } + *tptr=word; + if (InternalImmutableOffset(from,offset) || + CAS(fptr,Alpha,word)==word) { + break; + } + } + } + } + MixinObject(from).preHeader.CoCoWord=Forwarded(CoCoWord); + return true; + } else { + VTable.NotReached(); + } + } + } + + [NoBarriers] + internal override ulong Copy() + { + if (fDietDebug) { + VTable.DebugPrint("Doing Diet CoCo Copy\n"); + } + + ulong numCopied=0; + TagNode tagHead=ProbabilisticCoCoBarrier.tagHead; + ProbabilisticCoCoBarrier.tagHead=null; + for (TagNode cur=tagHead; + cur!=null; + cur=cur.next) { + if (CopyObject(cur.from)) { + numCopied++; + } + } + + if (fDietDebug) { + VTable.DebugPrint("Done With Diet CoCo Copy\n"); + } + + return numCopied; + } + + private static bool fDietDebug { get { return false; } } + private static bool fDietVerboseCopyDebug { get { return false; } } + } +} + diff --git a/base/Imported/Bartok/runtime/shared/GCs/ProfilingBaseCollector.cs b/base/Imported/Bartok/runtime/shared/GCs/ProfilingBaseCollector.cs new file mode 100644 index 0000000..8180e0e --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/ProfilingBaseCollector.cs @@ -0,0 +1,141 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs +{ + + using Microsoft.Bartok.Options; + using System.Runtime.CompilerServices; + using System.Threading; + + [NoCCtor] + [MixinConditional("GCProfiling")] + [Mixin(typeof(BaseCollector))] + internal abstract class ProfilingBaseCollector : BaseCollector + { + +#region Allocation + + [ManualRefCounts] + internal override Object AllocateObject(VTable vtable, + Thread currentThread) + { + Object result = base.AllocateObject(vtable, currentThread); + if (GC.IsProfiling) { + ProfileAllocation(result); + } + if (VTable.enableGCProfiling) { + ulong size = (ulong) ObjectLayout.ObjectSize(vtable); + RegisterNewObject(size); + } + return result; + } + + [ManualRefCounts] + internal override Array AllocateVector(VTable vtable, + int numElements, + Thread currentThread) + { + Array result = + base.AllocateVector(vtable, numElements, currentThread); + if (VTable.enableGCProfiling) { + ulong size = (ulong) + ObjectLayout.ArraySize(vtable, + unchecked((uint) numElements)); + RegisterNewObject(size); + } + return result; + } + + [ManualRefCounts] + internal override Array AllocateArray(VTable vtable, + int rank, + int totalElements, + Thread currentThread) + { + Array result = + base.AllocateArray(vtable, rank, totalElements, currentThread); + if (VTable.enableGCProfiling) { + ulong size = (ulong) + ObjectLayout.ArraySize(vtable, + unchecked((uint)totalElements)); + RegisterNewObject(size); + } + return result; + } + + [ManualRefCounts] + internal override String AllocateString(int stringLength, + Thread currentThread) + { + String result = base.AllocateString(stringLength, currentThread); + if (VTable.enableGCProfiling) { + ulong size = (ulong) + ObjectLayout.StringSize(vtable, + unchecked((uint)(stringLength+1))); + RegisterNewObject(size); + } + return result; + } + +#endregion + +#region Accounting + + internal override void RegisterHeapSize(ulong heapSize) + { + base.RegisterHeapSize(heapSize); + if (minHeapSize < heapSize) { + minHeapSize = heapSize; + } + lastLiveHeapSize = heapSize; + bytesSinceGC = 0; + } + + internal override void RegisterNewObject(ulong objectSize) + { + base.RegisterNewObject(objectSize); + bytesAllocated += objectSize; + objectsAllocated++; + if (VTable.enableGCAccurateHeapSize) { + bytesSinceGC += objectSize; + if (lastLiveHeapSize + bytesSinceGC > minHeapSize) { + GC.Collect(); + } + } + } + + internal override void PrintAllocations() + { + if (VTable.enableGCProfiling) { +#if !SINGULARITY + Console.Error.WriteLine("Objects allocated: "+ + objectsAllocated); + Console.Error.WriteLine("Total bytes allocated (KB): "+ + (bytesAllocated >> 10)); + Console.Error.WriteLine("Min. heap size (KB): "+ + (minHeapSize >> 10)); +#endif + } + base.PrintAllocations(); + } + + private static ulong lastLiveHeapSize; + private static ulong minHeapSize; + private static ulong bytesSinceGC; + private static ulong bytesAllocated; + private static ulong objectsAllocated; + +#endregion + + } + +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/RCCollector.cs b/base/Imported/Bartok/runtime/shared/GCs/RCCollector.cs new file mode 100644 index 0000000..3f48bb2 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/RCCollector.cs @@ -0,0 +1,379 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +// #define DEBUG + +namespace System.GCs { + + using Microsoft.Bartok.Options; + using Microsoft.Bartok.Runtime; + using System.Runtime.InteropServices; + using System.Runtime.CompilerServices; + using System.Threading; + using System.Collections; + + [NoCCtor] + [RequiredByBartok] + internal abstract class RCCollector : Collector { + [PreInitRefCounts] + public static void Initialize() { + SegregatedFreeList.Initialize(); + } + + + // This is a compiler intrinsic whose value is controlled by + // /StageControl.RCCollectorVerifyRefCounts. + internal static extern bool VerificationMode { + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + get; + } + + // This is a compiler intrinsic whose value is controlled by + // /StageControl.RCCollectorVerifyLeakedCycles. + internal static extern bool VerifyLeakedCycles { + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + get; + } + + // This is a compiler intrinsic whose value is controlled by + // /StageControl.RCCollectorGenerateProfile. + internal static extern bool ProfilingMode { + [Intrinsic] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + get; + } + + [MixinConditional("RCGC")] + [Mixin(typeof(PostHeader))] + [StructLayout(LayoutKind.Sequential)] + [RequiredByBartok] + internal struct PostHeaderRCGC { + [CompilerInitField(2)] + internal uint refState; +#if !SINGULARITY + [RequiredByBartok] +#endif + internal VTable vtableObject; + } + + [MixinConditional("RCGC")] + [Mixin(typeof(Object))] + internal class RCGCObject : System.Object { + internal new PostHeaderRCGC postHeader; + + internal new uint REF_STATE { + [Inline] + [ManualRefCounts] + [MixinOverride] + get { + return this.postHeader.refState; + } + [Inline] + [ManualRefCounts] + [MixinOverride] + set { + this.postHeader.refState = value; + } + } + } + + [MixinConditional("RCGCVerification")] + [Mixin(typeof(PreHeader))] + [RequiredByBartok] + internal struct PreHeaderRCGCVerification { + internal UIntPtr backupRefcount; + internal UIntPtr dfsDiscoveryTime; + internal UIntPtr dfsFinishingTime; + } + + [MixinConditional("RCGCVerification")] + [Mixin(typeof(Object))] + internal class RCGCVerificationObject : System.Object { + internal new PreHeaderRCGCVerification preHeader; + } + + /* + * Every object maintains a 32-bit "reference state" (RS). + * The RS consists of a 29-bit reference count, with the + * remaining bits available for flagging purposes. + * + * In a 32-bit architecture, 4GB of memory is addressable. + * Of this, the upper 2GB is usually reserved for use by + * the system. So given an object size of at least + * 12 bytes (sync block index, RS field and the vtable), + * there can't be more than 2^28 objects. The reference + * count can be more, due to multiple references from an + * object to the same target (think of one large array of + * references), but will be less than 2^29. + * + * Of the remaining three bits, one is reserved to ignore + * reference-counting (RC) operations on objects. + */ + + internal struct RSMasks { + internal const uint refCount = 0x1fffffff; + + internal const uint countingFlag = 0x80000000; + } + + // Used only in profiling mode. + internal struct UpdatePairs { + public int Increments; + public int Decrements; + + public static UpdatePairs operator+(UpdatePairs a, + UpdatePairs b) { + a.Increments += b.Increments; + a.Decrements += b.Decrements; + + return a; + } + + public int Total { + get { + return Increments+Decrements; + } + } + } // Increment and decrement counts of one kind of RC update. + + internal struct AcctRecord { + // General RC update, on a "maybe-null" reference. + public UpdatePairs MaybeNull; + // General RC update, but on a non-null reference. + public UpdatePairs NonNull; + + public UpdatePairs VtableOperand; + public UpdatePairs RuntimeTypeOperand; + + public String MethodName; + + public static AcctRecord operator+(AcctRecord a, + AcctRecord b) { + VTable.Assert(Magic.addressOf(a.MethodName) == + Magic.addressOf(b.MethodName), + @"Magic.addressOf(a.MethodName) == + Magic.addressOf(b.MethodName)"); + + a.MaybeNull += b.MaybeNull; + a.NonNull += b.NonNull; + + a.VtableOperand += b.VtableOperand; + a.RuntimeTypeOperand += b.RuntimeTypeOperand; + + return a; + } + + public int CompareTo(AcctRecord rhs) { + int lhsTotal = MaybeNull.Total+NonNull.Total; + int rhsTotal = rhs.MaybeNull.Total+rhs.NonNull.Total; + + return lhsTotal < rhsTotal; + } + + public void DispCountsHeader() { + VTable.DebugPrint("GIncs\t\tGDecs"); + VTable.DebugPrint("\t\tNIncs\t\tNDecs"); + VTable.DebugPrint("\t\tV+\t\tV-"); + VTable.DebugPrint("\t\tR+\t\tR-"); + } + + public void DispMethodNameHeader() { + VTable.DebugPrint("\t\tMethod"); + } + + public void DispCounts() { + VTable.DebugPrint(MaybeNull.Increments); + if (MaybeNull.Increments < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(MaybeNull.Decrements); + if (MaybeNull.Decrements < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(NonNull.Increments); + if (NonNull.Increments < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(NonNull.Decrements); + if (NonNull.Decrements < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + } + + public void DispMethodName() { + VTable.DebugPrint(MethodName); + } + + public void Disp() { + DispCounts(); + DispMethodName(); + } + } + + + internal override long TotalMemory { + get { + UIntPtr pageCount = UIntPtr.Zero; + for (UIntPtr i = UIntPtr.Zero; + i < PageTable.pageTableCount; i++) { + if (PageTable.IsMyGcPage(i) && + PageTable.Type(i) != + SegregatedFreeList.INIT_PAGE) { + pageCount++; + } + } + return (long)PageTable.RegionSize(pageCount); + } + } + + + internal override UIntPtr FindObjectAddr(UIntPtr interiorPtr) { + return SegregatedFreeList.Find(interiorPtr); + } + + internal override void VisitObjects + (ObjectLayout.ObjectVisitor objVisitor, + UIntPtr lowAddr, + UIntPtr highAddr) { + UIntPtr lowPage = PageTable.Page(lowAddr); + UIntPtr highPage = PageTable.Page(highAddr); + SegregatedFreeList.VisitObjects(lowPage, + highPage, + objVisitor); + } + + + [NoInline] + [ManualRefCounts] + [RequiredByBartok] + internal static unsafe bool AccumulateRCUpdates(String methodName, + int methodIndex, + uint maxIndex, + AcctRecord rec) { + VTable.Assert(RCCollector.ProfilingMode, + @"RCCollector.ProfilingMode"); + + // Return if the page table hasn't been set up yet. + if (PageTable.pageTableCount == UIntPtr.Zero) { + return false; + } + + if (methods == null) { + // Allocate up front storage for the accounting records. + // + // This is requisitioned directly from the memory + // manager. Care should be taken to ensure that + // AccumulateRCUpdates does not indirectly call + // methods that may have compiler-inserted RC updates. + VTable vtable = + ((RuntimeType)typeof(AcctRecord[])).classVtable; + UIntPtr size = + ObjectLayout.ArraySize(vtable, maxIndex+1); + + BumpAllocator profileData = + new BumpAllocator(PageType.NonGC); + UIntPtr profileDataStart = + MemoryManager.AllocateMemory(size); + profileData.SetRange(profileDataStart, size); + PageManager.SetStaticDataPages(profileDataStart, size); + + methods = + (AcctRecord[])Allocate(ref profileData, vtable, size); + VTable.Assert(methods != null, + @"methods != null"); + + *(uint*)(Magic.addressOf(methods)+ + PostHeader.Size) = maxIndex+1; + } + + VTable.Assert(methods.Length == maxIndex+1, + @"methods.Length == maxIndex+1"); + + if (methods[methodIndex].methodName == null) { + methodNames[methodIndex].methodName = methodName; + } + // Not "methodNames[methodIndex].methodName == methodName" + // because the Equality operator carries compiler-inserted + // RC updates! + VTable.Assert(Magic.addressOf(methodNames[methodIndex]. + methodName) == + Magic.addressOf(methodName), + @"Magic.addressOf(methodNames[methodIndex]. + methodName) == + Magic.addressOf(methodName)"); + + methods[methodIndex] += rec; + + return true; + } + + [NoInline] + [ManualRefCounts] + internal static void EmitRefCountsProfile() { + VTable.Assert(RCCollector.ProfilingMode, + @"RCCollector.ProfilingMode"); + + if (methods == null) { // No RC updates present. + return; + } + + // Bubble sort in decreasing order of sums. + for (int i = 0; i < methods.Length; i++) { + for (int j = methods.Length-1; j > i; j--) { + if (methods[j].CompareTo(methods[j-1])) { + // Swap contents. + AcctRecord temp = methods[j]; + methods[j] = methods[j-1]; + methods[j-1] = temp; + } + } + } + + VTable.DebugPrint("\n"); + AcctRecord.DispCountsHeader(); + AcctRecord.DispMethodNameHeader(); + VTable.DebugPrint("\n"); + for (int i = 0; i < methods.Length; i++) { + if (methods[i].increments.Total == 0 && + methods[i].decrements.Total == 0) { + continue; + } + methods[i].DispCounts(); + methods[i].DispMethodName(); + VTable.DebugPrint("\n"); + } + } + + + private static Object Allocate(ref BumpAllocator profileData, + VTable vtable, + UIntPtr numBytes) { + UIntPtr resultAddr = + profileData.AllocateFast(numBytes, vtable.baseAlignment); + Object result = Magic.fromAddress(resultAddr); + result.REF_STATE = 1 ; + result.vtable = vtable; + return result; + } + + private static AcctRecord[] methods; + } +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/RefWriteBarrier.cs b/base/Imported/Bartok/runtime/shared/GCs/RefWriteBarrier.cs new file mode 100644 index 0000000..4bc3d8f --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/RefWriteBarrier.cs @@ -0,0 +1,342 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs +{ + using Microsoft.Bartok.Runtime; + using System.Runtime.CompilerServices; + using System.Threading; + + internal abstract unsafe class RefWriteBarrier: Barrier + { + [NoBarriers] + protected override int AtomicSwapImpl(ref int location, + int value, + int mask) + { + return Interlocked.Exchange(ref location, value); + } + + [NoBarriers] + protected override UIntPtr AtomicSwapImpl(ref UIntPtr location, + UIntPtr value, + int mask) + { + return Interlocked.Exchange(ref location, value); + } + + [NoBarriers] + protected override int AtomicCompareAndSwapImpl(ref int location, + int newValue, + int comparand, + int mask) + { + return Interlocked.CompareExchange(ref location, + newValue, comparand); + } + + [NoBarriers] + protected override long AtomicCompareAndSwapImpl(ref long location, + long newValue, + long comparand, + int mask) + { + return Interlocked.CompareExchange(ref location, + newValue, comparand); + } + + [NoBarriers] + protected override + UIntPtr AtomicCompareAndSwapImpl(ref UIntPtr location, + UIntPtr newValue, + UIntPtr comparand, + int mask) + { + return Interlocked.CompareExchange(ref location, + newValue, comparand); + } + + [Inline] + [NoBarriers] + protected override Object WeakRefReadImpl(UIntPtr addr, int mask) + { + return Magic.fromAddress(addr); + } + + [Inline] + [NoBarriers] + protected override UIntPtr WeakRefWriteImpl(Object obj, + int mask) + { + return Magic.addressOf(obj); + } + + [Inline] + [NoBarriers] + protected override bool EqImpl(Object a,Object b, int mask) + { + return a==b; + } + + [Inline] + [NoBarriers] + protected override bool AllowFastPathImpl() { + return false; + } + + [Inline] + protected override void WriteImpl(float *location, + float value, + int mask) + { + *location = value; + } + + [Inline] + protected override void WriteImpl(double *location, + double value, + int mask) + { + *location = value; + } + + [Inline] + protected override void WriteImpl(byte *location, + byte value, + int mask) + { + *location = value; + } + + [Inline] + protected override void WriteImpl(ushort *location, + ushort value, + int mask) + { + *location = value; + } + + [Inline] + protected override void WriteImpl(uint *location, + uint value, + int mask) + { + *location = value; + } + + [Inline] + protected override void WriteImpl(ulong *location, + ulong value, + int mask) + { + *location = value; + } + + [Inline] + protected override void WriteImpl(UIntPtr *location, + UIntPtr value, + int mask) + { + *location = value; + } + + [Inline] + protected override Object ReadObjImpl(UIntPtr *location, + int mask) + { + return Magic.fromAddress(*location); + } + + [Inline] + protected override float ReadImpl(float *location, + int mask) + { + return *location; + } + + [Inline] + protected override double ReadImpl(double *location, + int mask) + { + return *location; + } + + [Inline] + protected override byte ReadImpl(byte *location, + int mask) + { + return *location; + } + + [Inline] + protected override ushort ReadImpl(ushort *location, + int mask) + { + return *location; + } + + [Inline] + protected override uint ReadImpl(uint *location, + int mask) + { + return *location; + } + + [Inline] + protected override ulong ReadImpl(ulong *location, + int mask) + { + return *location; + } + + [Inline] + protected override UIntPtr ReadImpl(UIntPtr *location, + int mask) + { + return *location; + } + + [NoBarriers] + [Inline] + protected override void WriteImplByRef(ref float location, + float value, + int mask) + { + location = value; + } + + [NoBarriers] + [Inline] + protected override void WriteImplByRef(ref double location, + double value, + int mask) + { + location = value; + } + + [NoBarriers] + [Inline] + protected override void WriteImplByRef(ref byte location, + byte value, + int mask) + { + location = value; + } + + [NoBarriers] + [Inline] + protected override void WriteImplByRef(ref ushort location, + ushort value, + int mask) + { + location = value; + } + + [NoBarriers] + [Inline] + protected override void WriteImplByRef(ref uint location, + uint value, + int mask) + { + location = value; + } + + [NoBarriers] + [Inline] + protected override void WriteImplByRef(ref ulong location, + ulong value, + int mask) + { + location = value; + } + + [NoBarriers] + [Inline] + protected override void WriteImplByRef(ref UIntPtr location, + UIntPtr value, + int mask) + { + location = value; + } + + [NoBarriers] + [Inline] + protected override Object ReadImplByRef(ref Object location, + int mask) + { + return location; + } + + [NoBarriers] + [Inline] + protected override float ReadImplByRef(ref float location, + int mask) + { + return location; + } + + [NoBarriers] + [Inline] + protected override double ReadImplByRef(ref double location, + int mask) + { + return location; + } + + [NoBarriers] + [Inline] + protected override byte ReadImplByRef(ref byte location, + int mask) + { + return location; + } + + [NoBarriers] + [Inline] + protected override ushort ReadImplByRef(ref ushort location, + int mask) + { + return location; + } + + [NoBarriers] + [Inline] + protected override uint ReadImplByRef(ref uint location, + int mask) + { + return location; + } + + [NoBarriers] + [Inline] + protected override ulong ReadImplByRef(ref ulong location, + int mask) + { + return location; + } + + [NoBarriers] + [Inline] + protected override UIntPtr ReadImplByRef(ref UIntPtr location, + int mask) + { + return location; + } + + [Inline] + protected override Object LoadStaticFieldImpl(ref Object staticField, + int mask) + { + return this.ReadImplByRef(ref staticField, mask); + } + } +} + diff --git a/base/Imported/Bartok/runtime/shared/GCs/ReferenceCountingCollector.cs b/base/Imported/Bartok/runtime/shared/GCs/ReferenceCountingCollector.cs new file mode 100644 index 0000000..672bdf0 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/ReferenceCountingCollector.cs @@ -0,0 +1,2841 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +// #define DEBUG +// #define MEASURE_RCPHASES + +namespace System.GCs { + + using Microsoft.Bartok.Options; + using Microsoft.Bartok.Runtime; + using System.Runtime.InteropServices; + using System.Runtime.CompilerServices; + using System.Threading; + using System.Collections; + + [NoCCtor] + internal class ReferenceCountingCollector : SingleThreadedCollector { + + // This is a compiler intrinsic whose value is controlled by + // /StageControl.RCCollectorVerifyRefCounts. + internal static extern bool VerificationMode { + [Intrinsic] + get; + } + + // This is a compiler intrinsic whose value is controlled by + // /StageControl.RCCollectorVerifyLeakedCycles. + internal static extern bool VerifyLeakedCycles { + [Intrinsic] + get; + } + + // This is a compiler intrinsic whose value is controlled by + // /StageControl.RCCollectorGenerateProfile. + internal static extern bool ProfilingMode { + [Intrinsic] + get; + } + + [MixinConditional("ReferenceCountingGC")] + [Mixin(typeof(PreHeader))] + [RequiredByBartok] + internal struct PreHeaderEagerRCGC { + internal uint plcIndex; + } + + [MixinConditional("ReferenceCountingGC")] + [Mixin(typeof(Object))] + internal class EagerRCGCObject : System.Object { + internal new PreHeaderEagerRCGC preHeader; + [RequiredByBartok] + internal new PostHeaderRC postHeader; + + internal new uint REF_STATE { + [Inline] + [ManualRefCounts] + [MixinOverride] + get { + return this.postHeader.refState; + } + [Inline] + [ManualRefCounts] + [MixinOverride] + set { + this.postHeader.refState = value; + } + } + + } + + [MixinConditional("ReferenceCountingGCVerification")] + [Mixin(typeof(PreHeader))] + [RequiredByBartok] + internal unsafe struct PreHeaderEagerRCGCVerification { + internal UIntPtr backupRefcount; + internal UIntPtr dfsDiscoveryTime; + internal UIntPtr dfsFinishingTime; + } + + [MixinConditional("ReferenceCountingGCVerification")] + [Mixin(typeof(Object))] + internal class EagerRCVerificationObject : System.Object { + internal new PreHeaderEagerRCGCVerification preHeader; + } + + [Inline] + [ManualRefCounts] + private static uint GetPLCIndex(Object obj) { + return ((EagerRCGCObject)obj).preHeader.plcIndex; + } + + [Inline] + [ManualRefCounts] + private static void SetPLCIndex(Object obj, uint index) { + ((EagerRCGCObject)obj).preHeader.plcIndex = index; + } + + /* + * Every object maintains a 32-bit "reference state" (RS). + * The RS consists of a marking bit, a bit flagging alignment, + * a bit flagging whether reference counting is enabled or + * disabled on the object, and a 29-bit reference count (RC). + * + * Given a virtual memory size of 2GB and an object size of + * at least 12 bytes (multi-use-word, RS field and the vtable), + * there can't be more than 2^28 objects. The reference count + * can be more, due to multiple references from an object + * to the same target, but will be less than 2^29. + */ + + internal const int markFlagBit = 31; + internal const int acyclicFlagBit = 30; + internal const int countingONFlagBit = 29; + + internal const uint markFlagMask = 1U << markFlagBit; + internal const uint acyclicFlagMask = 1U << acyclicFlagBit; + internal const uint countingONFlagMask = 1U << countingONFlagBit; + + internal const uint refCountMask = + ~(markFlagMask | acyclicFlagMask | countingONFlagMask); + + private static NonNullReferenceVisitor refCountIncrementer; + private static NonNullReferenceVisitor refCountDecrementer; + + internal static ReferenceCountingCollector instance; + + private static Object delayedDeallocationList; + private static Object objectBeingDeallocated; + private static uint delayedDeallocationLength; + private static uint releasedObjectCount; + private static uint numCollections; + + private const uint collectionTrigger = 1 << 15; + private const uint recycleTrigger = collectionTrigger << 4; + private const int deallocationSpan = 1 << 20; + private const int numCollectionsTrigger = 8; + + private const double triggerFraction = 0.90; + + // For cycle collection. + private static InternalIncrementer internalIncrementer; + private static InternalDecrementer internalDecrementer; + private static InternalScanner internalScanner; + private static InternalReclaimer internalReclaimer; + + private static UIntPtr plcRawSize, plcRawAddr; + private static UIntPtr[] plcBuffer; + private static uint freePLCHead; + private const uint initialPLCNumEntries = 1 << 12; + private const uint maxPLCNumEntries = 1 << 20; + + private static ulong maxCyclicGarbage; + private static ulong totalCyclicGarbage; + private static uint cycleCollections; + private static bool forceCycleCollectionAtEnd { + get { return true; } + } + + // Used only in verification mode. + private static BackupInitializer backupInit; + private static BackupRefCount backupRefCount; + private static IncrementBackupRefCount incrementBackupRefCount; + private static RootsScanner rootsScanner; + private static NonNullReferenceVisitor resetRoots; + private static ObjectVisitor resetTraversal; + private static LeakAccumulator leakAccumulator; + private static LeakedNodesDFS leakedNodesDFS; + private static LeakedCycleClosure leakedCycleClosure; + private static DFS dfs; + private static CycleClosure cycleClosure; + private static BFSMarker bfsMarker; + private static LeakedRootsCounter leakedRootsCounter; + private static LeakedRoots leakedRoots; + + // Used only in profiling mode. + private static String[] methodNames; + private static int[] increments; + private static int[] decrements; + private static int[] nonNullIncrements; + private static int[] nonNullDecrements; + private static int[] aIncrements; + private static int[] aDecrements; + private static int[] nonNullAIncrements; + private static int[] nonNullADecrements; + private static int[] vIncrements; + private static int[] vDecrements; + private static int[] rIncrements; + private static int[] rDecrements; + + // Used to measure the cumulative times in the various RC phases. +#if MEASURE_RCPHASES + private static bool ddListPhase; + private static long ddListProcTime; + private static long ddListProcCount; + private static long ddListPLCProcTime; + private static long ddListPLCProcCount; + + private static bool plcBufferPhase; + private static long plcBufferProcTime; + private static long plcBufferProcCount; + private static long plcBufferDDListProcTime; + private static long plcBufferDDListProcCount; + + private static long segFreeTime; + private static long segCommitTime; + + private static long stackScanCount; +#endif // MEASURE_RCPHASES + + [PreInitRefCounts] + public new static unsafe void Initialize() { + SegregatedFreeList.Initialize(); + instance = + (ReferenceCountingCollector)BootstrapMemory. + Allocate(typeof(ReferenceCountingCollector)); + + refCountIncrementer = + (RefCountIncrementer)BootstrapMemory. + Allocate(typeof(RefCountIncrementer)); + refCountDecrementer = + (RefCountDecrementer)BootstrapMemory. + Allocate(typeof(RefCountDecrementer)); + + internalIncrementer = + (InternalIncrementer)BootstrapMemory. + Allocate(typeof(InternalIncrementer)); + internalDecrementer = + (InternalDecrementer)BootstrapMemory. + Allocate(typeof(InternalDecrementer)); + internalScanner = + (InternalScanner)BootstrapMemory. + Allocate(typeof(InternalScanner)); + internalReclaimer = + (InternalReclaimer)BootstrapMemory. + Allocate(typeof(InternalReclaimer)); + + allocatePLCBuffer(initialPLCNumEntries); + stitchFreePLCSlots(1); + + if (VerificationMode) { + backupInit = + (BackupInitializer)BootstrapMemory. + Allocate(typeof(BackupInitializer)); + backupRefCount = + (BackupRefCount)BootstrapMemory. + Allocate(typeof(BackupRefCount)); + incrementBackupRefCount = + (IncrementBackupRefCount)BootstrapMemory. + Allocate(typeof(IncrementBackupRefCount)); + rootsScanner = + (RootsScanner)BootstrapMemory. + Allocate(typeof(RootsScanner)); + resetRoots = + (ResetRoots)BootstrapMemory. + Allocate(typeof(ResetRoots)); + resetTraversal = + (ResetTraversal)BootstrapMemory. + Allocate(typeof(ResetTraversal)); + + leakAccumulator = + (LeakAccumulator)BootstrapMemory. + Allocate(typeof(LeakAccumulator)); + leakedNodesDFS = + (LeakedNodesDFS)BootstrapMemory. + Allocate(typeof(LeakedNodesDFS)); + leakedCycleClosure = + (LeakedCycleClosure)BootstrapMemory. + Allocate(typeof(LeakedCycleClosure)); + dfs = + (DFS)BootstrapMemory. + Allocate(typeof(DFS)); + cycleClosure = + (CycleClosure)BootstrapMemory. + Allocate(typeof(CycleClosure)); + bfsMarker = + (BFSMarker)BootstrapMemory. + Allocate(typeof(BFSMarker)); + leakedRoots = + (LeakedRoots)BootstrapMemory. + Allocate(typeof(LeakedRoots)); + leakedRootsCounter = + (LeakedRootsCounter)BootstrapMemory. + Allocate(typeof(LeakedRootsCounter)); + } + } + + + [NoInline] + [ManualRefCounts] + internal override void CollectStoppable(int currentThreadIndex, + int generation) { + int startTicks = 0; + bool enableGCTiming = VTable.enableGCTiming; + if (enableGCTiming) { + VTable.enableGCTiming = false; + startTicks = Environment.TickCount; + } + + if (VTable.enableGCVerify) { + VerifyHeap(true); + } + processPLCBuffer(); + deallocateObjects(); + StartGCCycle(); + if (enableGCTiming) { + int elapsedTicks = Environment.TickCount-startTicks; + BaseCollector.RegisterPause(elapsedTicks); + VTable.enableGCTiming = true; + } + } + + internal override int CollectionGeneration(int gen) { + return MinGeneration; + } + + [NoInline] + [ManualRefCounts] + internal override UIntPtr AllocateObjectMemory(UIntPtr numBytes, + uint alignment, + Thread currentThread) { + // "numBytes" must be in multiples of double words + // (four bytes). Note that it includes the space for + // the object header field. + + VTable.Assert(Util.dwordAlign(numBytes) == numBytes, + @"Util.dwordAlign(numBytes) == numBytes"); + + UIntPtr resultAddr; + if (GC.HeapSizeConfigurable) { + resultAddr = AllocateObjectMemoryFast(numBytes, + alignment, + currentThread); + if (resultAddr == UIntPtr.Zero) { + resultAddr = + AllocateObjectMemorySlow(numBytes, + alignment, + currentThread); + } + } else { // Old collection-triggering scheme. + if (delayedDeallocationLength > collectionTrigger) { + deallocateObjects(); + } + resultAddr = SegregatedFreeList.Allocate(currentThread, + numBytes, + alignment); + } + + return resultAddr; + } + + [Inline] + [ManualRefCounts] + private UIntPtr AllocateObjectMemoryFast(UIntPtr numBytes, + uint alignment, + Thread currentThread) { + UIntPtr resultAddr = + SegregatedFreeList.AllocateFast(currentThread, + numBytes, + alignment); + return resultAddr; + } + + [ManualRefCounts] + [DisableNullChecks] + private UIntPtr AllocateObjectMemorySlow(UIntPtr numBytes, + uint alignment, + Thread currentThread) { + VTable.Assert(GC.HeapSizeConfigurable, + @"GC.HeapSizeConfigurable"); + + // Allocate on "slow" path, noting levels before and after. + uint before = (uint)SegregatedFreeList.AllocatedPages; + UIntPtr resultAddr = + SegregatedFreeList.AllocateSlow(currentThread, + numBytes, + alignment); + uint after = (uint)SegregatedFreeList.AllocatedPages; + + // If page usage hasn't increased, simply return. + if (!(after > before)) { + return resultAddr; + } + + // Otherwise, check if memory usage has crossed a threshold. + uint heapPages = after; + int maxHeapPages = GC.MaxHeapPages; + if (heapPages > triggerFraction*maxHeapPages) { + if (heapPages < maxHeapPages) { + deallocateObjects(); + if (++numCollections > numCollectionsTrigger) { + numCollections = 0; + recycleAllocator(); + } + } else { + Console.Error.Write("Heap size exceeds "); + Console.Error.Write(maxHeapPages << + (PageTable.PageBits-10)); + Console.Error.WriteLine("KB!"); + System.Environment.Exit(-2); + } + } + + return resultAddr; + } + + [Inline] + [ManualRefCounts] + protected override void CreateObject(Object obj, + VTable vtable, + Thread currentThread) { + base.CreateObject(obj, vtable, currentThread); + uint refState = 1 | countingONFlagMask; + obj.REF_STATE = vtable.isAcyclicRefType ? + (refState | acyclicFlagMask) : refState; + } + + internal override int GetGeneration(Object obj) { + return MinGeneration; + } + + internal override int MaxGeneration { + get { + return (int)PageType.Owner0; + } + } + + internal override int MinGeneration { + get { + return (int)PageType.Owner0; + } + } + + internal override long TotalMemory { + get { + UIntPtr pageCount = SegregatedFreeList.AllocatedPages; + return (long)PageTable.RegionSize(pageCount); + } + } + + + internal override void EnableHeap() { + // Do nothing + } + + [NoInline] + [ManualRefCounts] + internal override void DestructHeap() { +#if MEASURE_RCPHASES + Console.Error.Write("DD list processing time (ms): "); + Console.Error.WriteLine(ddListProcTime); + Console.Error.Write("DD list processing count: "); + Console.Error.WriteLine(ddListProcCount); + Console.Error.Write("\tPLC buffer processing time (ms): "); + Console.Error.WriteLine(ddListPLCProcTime); + Console.Error.Write("\tPLC buffer processing count: "); + Console.Error.WriteLine(ddListPLCProcCount); + + Console.Error.Write("PLC buffer processing time (ms): "); + Console.Error.WriteLine(plcBufferProcTime); + Console.Error.Write("PLC buffer processing count: "); + Console.Error.WriteLine(plcBufferProcCount); + Console.Error.Write("\tDD list processing time (ms): "); + Console.Error.WriteLine(plcBufferDDListProcTime); + Console.Error.Write("\tDD list processing count: "); + Console.Error.WriteLine(plcBufferDDListProcCount); + + Console.Error.Write("Seg. free time (ms): "); + Console.Error.WriteLine(segFreeTime); + Console.Error.Write("Seg. commit time (ms): "); + Console.Error.WriteLine(segCommitTime); + + Console.Error.Write("Stack scan count: "); + Console.Error.WriteLine(stackScanCount); +#endif // MEASURE_RCPHASES + + if (VTable.enableGCVerify) { + VerifyHeap(true); + } + base.DestructHeap(); + if (VTable.enableGCProfiling) { + if (forceCycleCollectionAtEnd) { + processPLCBuffer(); + } + + Console.Error.Write("Cycle collections: "); + Console.Error.WriteLine(cycleCollections); + Console.Error.Write("Max. Cyclic garbage (B): "); + Console.Error.WriteLine(maxCyclicGarbage); + Console.Error.Write("Total Cyclic garbage (B): "); + Console.Error.WriteLine(totalCyclicGarbage); + + if (ReferenceCountingCollector.ProfilingMode) { + EmitRefCountsProfile(); + } + } + } + + [NoInline] + [ManualRefCounts] + internal override void VerifyHeap(bool beforeCollection) { + VTable.Assert(ReferenceCountingCollector.VerificationMode, + @"ReferenceCountingCollector.VerificationMode"); + + while (nonEmptyDDList()) { + // Ensure the integrity of the delayed deallocation list. + deallocationListChecker(); + + // Deallocate objects on the delayed deallocation list. + purgeDeallocationList(); + + // Capture leaked cycles. + processPLCBuffer(); + } + + // Recycle allocator. + recycleAllocator(); + + // Initialize the "backup" reference count. + SegregatedFreeList.VisitAllObjects(backupInit); + + // Count all references and managed pointers. + rootsScanner.Initialize(backupRefCount); + CallStack.ScanStacks(rootsScanner, rootsScanner); + Thread.VisitBootstrapData(rootsScanner); + StaticData.ScanStaticData(rootsScanner); + MultiUseWord.VisitStrongRefs(rootsScanner, false); + + CallStack.ScanStacks(resetRoots, resetRoots); + Thread.VisitBootstrapData(resetRoots); + StaticData.ScanStaticData(resetRoots); + + SegregatedFreeList.VisitAllObjects(resetTraversal); + + // Actual leaks (refCount > 0 and backup refCount = 0). + leakAccumulator.Initialize(); + SegregatedFreeList.VisitAllObjects(leakAccumulator); + VTable.DebugPrint("Leaked storage: "); + VTable.DebugPrint((int)leakAccumulator.Size); + VTable.DebugPrint("B"); + + if (VerifyLeakedCycles) { + // Find leaked data that should have been reclaimed. + // (If L is the set of all leaked nodes, and L' the + // transitive closure of leaked cycles, then L-L' is + // the set of nodes that should have been captured + // by a pure reference counting collector.) + SegregatedFreeList.VisitAllObjects(leakedNodesDFS); + SegregatedFreeList.VisitAllObjects(resetTraversal); + SegregatedFreeList.VisitAllObjects(leakedCycleClosure); + SegregatedFreeList.VisitAllObjects(resetTraversal); + leakAccumulator.Initialize(); + SegregatedFreeList.VisitAllObjects(leakAccumulator); + VTable.DebugPrint(" ("); + VTable.DebugPrint((int)leakAccumulator.Size); + VTable.DebugPrint("B acyclic)"); + } + + // Find the roots of leaked data. + leakedRoots.Initialize(); + SegregatedFreeList.VisitAllObjects(leakedRoots); + leakedRootsCounter.Initialize(); + SegregatedFreeList.VisitAllObjects(leakedRootsCounter); + SegregatedFreeList.VisitAllObjects(resetTraversal); + VTable.DebugPrint("; leaked heap roots: "); + VTable.DebugPrint((int)leakedRootsCounter.Total); + VTable.DebugPrint("\n"); + } + + internal override UIntPtr FindObjectAddr(UIntPtr interiorPtr) { + return SegregatedFreeList.Find(interiorPtr); + } + + internal override + void VisitObjects(ObjectLayout.ObjectVisitor objVisitor, + UIntPtr lowAddr, + UIntPtr highAddr) { + UIntPtr lowPage = PageTable.Page(lowAddr); + UIntPtr highPage = PageTable.Page(highAddr); + SegregatedFreeList.VisitObjects(lowPage, + highPage, + objVisitor); + } + + + [NoInline] + [ManualRefCounts] + [RequiredByBartok] + [GCAnnotation(GCOption.NOGC)] + internal static unsafe void AccumulateRCUpdates(String methodName, + int methodIndex, + uint maxIndex, + int incCount, + int decCount, + int nIncCount, + int nDecCount, + int aIncCount, + int aDecCount, + int nAIncCount, + int nADecCount, + int vIncCount, + int vDecCount, + int rIncCount, + int rDecCount) { + VTable.Assert(ReferenceCountingCollector.ProfilingMode, + @"ReferenceCountingCollector.ProfilingMode"); + + // Return if the page table hasn't been set up yet. + if (PageTable.pageTableCount == UIntPtr.Zero) { + return; + } + + if (methodNames == null) { + VTable.Assert(increments == null, + @"increments == null"); + VTable.Assert(decrements == null, + @"decrements == null"); + VTable.Assert(nonNullIncrements == null, + @"nonNullIncrements == null"); + VTable.Assert(nonNullDecrements == null, + @"nonNullDecrements == null"); + VTable.Assert(aIncrements == null, + @"aIncrements == null"); + VTable.Assert(aDecrements == null, + @"aDecrements == null"); + VTable.Assert(nonNullAIncrements == null, + @"nonNullAIncrements == null"); + VTable.Assert(nonNullADecrements == null, + @"nonNullADecrements == null"); + VTable.Assert(vIncrements == null, + @"vIncrements == null"); + VTable.Assert(vDecrements == null, + @"vDecrements == null"); + VTable.Assert(rIncrements == null, + @"rIncrements == null"); + VTable.Assert(rDecrements == null, + @"rDecrements == null"); + + // Allocate storage for the tables. Note that this is + // requisitioned directly from the memory manager. Care + // should be taken to ensure that AccumulateRCUpdates + // does not indirectly call methods that may have + // compiler-inserted RC updates. + VTable strArrayVtable = + ((RuntimeType)typeof(String[])).classVtable; + VTable intArrayVtable = + ((RuntimeType)typeof(int[])).classVtable; + UIntPtr methodNamesSize = + ObjectLayout.ArraySize(strArrayVtable, maxIndex+1); + UIntPtr incrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr decrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr nonNullIncrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr nonNullDecrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr aIncrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr aDecrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr nonNullAIncrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr nonNullADecrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr vIncrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr vDecrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr rIncrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr rDecrementsSize = + ObjectLayout.ArraySize(intArrayVtable, maxIndex+1); + UIntPtr totalSize = + methodNamesSize+ + incrementsSize+decrementsSize+ + nonNullIncrementsSize+nonNullDecrementsSize+ + aIncrementsSize+aDecrementsSize+ + nonNullAIncrementsSize+nonNullADecrementsSize+ + vIncrementsSize+vDecrementsSize+ + rIncrementsSize+rDecrementsSize; + + BumpAllocator profileData = + new BumpAllocator(PageType.NonGC); + UIntPtr profileDataStart = + MemoryManager.AllocateMemory(totalSize); + profileData.SetZeroedRange(profileDataStart, totalSize); + PageManager.SetStaticDataPages(profileDataStart, + totalSize); + + methodNames = + (String[])AllocateArray(ref profileData, + strArrayVtable, + methodNamesSize); + VTable.Assert(methodNames != null, + @"methodNames != null"); + + increments = + (int[])AllocateArray(ref profileData, intArrayVtable, + incrementsSize); + VTable.Assert(increments != null, + @"increments != null"); + + decrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + decrementsSize); + VTable.Assert(decrements != null, + @"decrements != null"); + + nonNullIncrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + nonNullIncrementsSize); + VTable.Assert(nonNullIncrements != null, + @"nonNullIncrements != null"); + + nonNullDecrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + nonNullDecrementsSize); + VTable.Assert(nonNullDecrements != null, + @"nonNullDecrements != null"); + + aIncrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + aIncrementsSize); + VTable.Assert(aIncrements != null, + @"aIncrements != null"); + + aDecrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + aDecrementsSize); + VTable.Assert(aDecrements != null, + @"aDecrements != null"); + + nonNullAIncrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + nonNullAIncrementsSize); + VTable.Assert(nonNullAIncrements != null, + @"nonNullAIncrements != null"); + + nonNullADecrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + nonNullADecrementsSize); + VTable.Assert(nonNullADecrements != null, + @"nonNullADecrements != null"); + + vIncrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + vIncrementsSize); + VTable.Assert(vIncrements != null, + @"vIncrements != null"); + + vDecrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + vDecrementsSize); + VTable.Assert(vDecrements != null, + @"vDecrements != null"); + + rIncrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + rIncrementsSize); + VTable.Assert(rIncrements != null, + @"rIncrements != null"); + + rDecrements = + (int[])AllocateArray(ref profileData, intArrayVtable, + rDecrementsSize); + VTable.Assert(rDecrements != null, + @"rDecrements != null"); + + *(uint*)(Magic.addressOf(methodNames)+PostHeader.Size) = + maxIndex+1; + *(uint*)(Magic.addressOf(increments)+PostHeader.Size) = + maxIndex+1; + *(uint*)(Magic.addressOf(decrements)+PostHeader.Size) = + maxIndex+1; + *(uint*)(Magic.addressOf(nonNullIncrements)+ + PostHeader.Size) = maxIndex+1; + *(uint*)(Magic.addressOf(nonNullDecrements)+ + PostHeader.Size) = maxIndex+1; + *(uint*)(Magic.addressOf(aIncrements)+PostHeader.Size) = + maxIndex+1; + *(uint*)(Magic.addressOf(aDecrements)+PostHeader.Size) = + maxIndex+1; + *(uint*)(Magic.addressOf(nonNullAIncrements)+ + PostHeader.Size)= maxIndex+1; + *(uint*)(Magic.addressOf(nonNullADecrements)+ + PostHeader.Size)= maxIndex+1; + *(uint*)(Magic.addressOf(vIncrements)+PostHeader.Size) = + maxIndex+1; + *(uint*)(Magic.addressOf(vDecrements)+PostHeader.Size) = + maxIndex+1; + *(uint*)(Magic.addressOf(rIncrements)+PostHeader.Size) = + maxIndex+1; + *(uint*)(Magic.addressOf(rDecrements)+PostHeader.Size) = + maxIndex+1; + } + VTable.Assert(methodNames.Length == maxIndex+1, + @"methodNames.Length == maxIndex+1"); + VTable.Assert(increments.Length == maxIndex+1, + @"increments.Length == maxIndex+1"); + VTable.Assert(decrements.Length == maxIndex+1, + @"decrements.Length == maxIndex+1"); + VTable.Assert(nonNullIncrements.Length == maxIndex+1, + @"nonNullIncrements.Length == maxIndex+1"); + VTable.Assert(nonNullDecrements.Length == maxIndex+1, + @"nonNullDecrements.Length == maxIndex+1"); + VTable.Assert(aIncrements.Length == maxIndex+1, + @"aIncrements.Length == maxIndex+1"); + VTable.Assert(aDecrements.Length == maxIndex+1, + @"aDecrements.Length == maxIndex+1"); + VTable.Assert(nonNullAIncrements.Length == maxIndex+1, + @"nonNullAIncrements.Length == maxIndex+1"); + VTable.Assert(nonNullADecrements.Length == maxIndex+1, + @"nonNullADecrements.Length == maxIndex+1"); + VTable.Assert(vIncrements.Length == maxIndex+1, + @"vIncrements.Length == maxIndex+1"); + VTable.Assert(vDecrements.Length == maxIndex+1, + @"vDecrements.Length == maxIndex+1"); + VTable.Assert(rIncrements.Length == maxIndex+1, + @"rIncrements.Length == maxIndex+1"); + VTable.Assert(rDecrements.Length == maxIndex+1, + @"rDecrements.Length == maxIndex+1"); + + if (methodNames[methodIndex] == null) { + methodNames[methodIndex] = methodName; + } + // Not "methodNames[methodIndex] == methodName" because + // the Equality operator carries compiler-inserted + // RC updates! + VTable.Assert(Magic.addressOf(methodNames[methodIndex]) == + Magic.addressOf(methodName), + @"Magic.addressOf(methodNames[methodIndex]) == + Magic.addressOf(methodName)"); + + increments[methodIndex] += incCount; + decrements[methodIndex] += decCount; + nonNullIncrements[methodIndex] += nIncCount; + nonNullDecrements[methodIndex] += nDecCount; + aIncrements[methodIndex] += aIncCount; + aDecrements[methodIndex] += aDecCount; + nonNullAIncrements[methodIndex] += nAIncCount; + nonNullADecrements[methodIndex] += nADecCount; + vIncrements[methodIndex] += vIncCount; + vDecrements[methodIndex] += vDecCount; + rIncrements[methodIndex] += rIncCount; + rDecrements[methodIndex] += rDecCount; + } + + private static Object AllocateArray(ref BumpAllocator profileData, + VTable vtable, + UIntPtr numBytes) { + UIntPtr resultAddr = + profileData.AllocateFast(numBytes, vtable.baseAlignment); + Object result = Magic.fromAddress(resultAddr); + uint refState = 1; + result.REF_STATE = vtable.isAcyclicRefType ? + (refState | acyclicFlagMask) : refState; + result.vtable = vtable; + return result; + } + + [NoInline] + [ManualRefCounts] + internal static void EmitRefCountsProfile() { + VTable.Assert(ReferenceCountingCollector.ProfilingMode, + @"ReferenceCountingCollector.ProfilingMode"); + + if (methodNames == null) { // No RC updates present. + return; + } + VTable.Assert(increments != null, + @"increments != null"); + VTable.Assert(decrements != null, + @"decrements != null"); + VTable.Assert(nonNullIncrements != null, + @"nonNullIncrements != null"); + VTable.Assert(nonNullDecrements != null, + @"nonNullDecrements != null"); + VTable.Assert(aIncrements != null, + @"aIncrements != null"); + VTable.Assert(aDecrements != null, + @"aDecrements != null"); + VTable.Assert(nonNullAIncrements != null, + @"nonNullAIncrements != null"); + VTable.Assert(nonNullADecrements != null, + @"nonNullADecrements != null"); + VTable.Assert(vIncrements != null, + @"vIncrements != null"); + VTable.Assert(vDecrements != null, + @"vDecrements != null"); + VTable.Assert(rIncrements != null, + @"rIncrements != null"); + VTable.Assert(rDecrements != null, + @"rDecrements != null"); + + // Bubble sort in decreasing order of sums. + for (int i = 0; i < methodNames.Length; i++) { + for (int j = methodNames.Length-1; j > i; j--) { + if (increments[j]+decrements[j]+ + nonNullIncrements[j]+nonNullDecrements[j]+ + aIncrements[j]+aDecrements[j]+ + nonNullAIncrements[j]+nonNullADecrements[j] > + increments[j-1]+decrements[j-1]+ + nonNullIncrements[j-1]+nonNullDecrements[j-1]+ + aIncrements[j-1]+aDecrements[j-1]+ + nonNullAIncrements[j-1]+nonNullADecrements[j-1]) { + // Swap contents. + int temp = increments[j]; + increments[j] = increments[j-1]; + increments[j-1] = temp; + + temp = decrements[j]; + decrements[j] = decrements[j-1]; + decrements[j-1] = temp; + + temp = nonNullIncrements[j]; + nonNullIncrements[j] = nonNullIncrements[j-1]; + nonNullIncrements[j-1] = temp; + + temp = nonNullDecrements[j]; + nonNullDecrements[j] = nonNullDecrements[j-1]; + nonNullDecrements[j-1] = temp; + + temp = aIncrements[j]; + aIncrements[j] = aIncrements[j-1]; + aIncrements[j-1] = temp; + + temp = aDecrements[j]; + aDecrements[j] = aDecrements[j-1]; + aDecrements[j-1] = temp; + + temp = nonNullAIncrements[j]; + nonNullAIncrements[j] = nonNullAIncrements[j-1]; + nonNullAIncrements[j-1] = temp; + + temp = nonNullADecrements[j]; + nonNullADecrements[j] = nonNullADecrements[j-1]; + nonNullADecrements[j-1] = temp; + + temp = vIncrements[j]; + vIncrements[j] = vIncrements[j-1]; + vIncrements[j-1] = temp; + + temp = vDecrements[j]; + vDecrements[j] = vDecrements[j-1]; + vDecrements[j-1] = temp; + + temp = rIncrements[j]; + rIncrements[j] = rIncrements[j-1]; + rIncrements[j-1] = temp; + + temp = rDecrements[j]; + rDecrements[j] = rDecrements[j-1]; + rDecrements[j-1] = temp; + + String s = methodNames[j]; + methodNames[j] = methodNames[j-1]; + methodNames[j-1] = s; + } + } + } + + VTable.DebugPrint("\n"); + VTable.DebugPrint("Incs\t\tDecs"); + VTable.DebugPrint("\t\tNIncs\t\tNDecs"); + VTable.DebugPrint("\t\tAIncs\t\tADecs"); + VTable.DebugPrint("\t\tNAIncs\t\tNADecs"); + VTable.DebugPrint("\t\tV+\t\tV-"); + VTable.DebugPrint("\t\tR+\t\tR-"); + VTable.DebugPrint("\t\tMethod\n"); + VTable.DebugPrint("----\t\t----"); + VTable.DebugPrint("\t\t-----\t\t-----"); + VTable.DebugPrint("\t\t-----\t\t-----"); + VTable.DebugPrint("\t\t------\t\t------"); + VTable.DebugPrint("\t\t--\t\t--"); + VTable.DebugPrint("\t\t--\t\t--"); + VTable.DebugPrint("\t\t------\n"); + VTable.DebugPrint("\n"); + for (int i = 0; i < methodNames.Length; i++) { + if (increments[i]+nonNullIncrements[i]+ + aIncrements[i]+nonNullAIncrements[i] == 0 && + decrements[i]+nonNullDecrements[i]+ + aDecrements[i]+nonNullADecrements[i] == 0) { + continue; + } + VTable.DebugPrint(increments[i]); + if (increments[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(decrements[i]); + if (decrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(nonNullIncrements[i]); + if (nonNullIncrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(nonNullDecrements[i]); + if (nonNullDecrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(aIncrements[i]); + if (aIncrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(aDecrements[i]); + if (aDecrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(nonNullAIncrements[i]); + if (nonNullAIncrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(nonNullADecrements[i]); + if (nonNullADecrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(vIncrements[i]); + if (vIncrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(vDecrements[i]); + if (vDecrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(rIncrements[i]); + if (rIncrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(rDecrements[i]); + if (rDecrements[i] < 10000000) { + VTable.DebugPrint("\t\t"); + } else { + VTable.DebugPrint("\t"); + } + VTable.DebugPrint(methodNames[i]); + VTable.DebugPrint("\n"); + } + } + + [NoInline] + [ManualRefCounts] + [RequiredByBartok] + [GCAnnotation(GCOption.NOGC)] + internal static void IncrementRefCount(Object obj) { + if (obj == null) { + return; + } + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) < refCountMask, + @"(refState & refCountMask) < refCountMask"); + + if ((refState & countingONFlagMask) == 0) { + return; + } + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + + obj.REF_STATE = refState+1; + + // Exclude the object from leaked cycle processing. + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + // If the object hasn't already been removed from + // the PLC ("potentially leaked cycle") buffer, remove it. + if (index != 0) { + // Reset the PLC index in the object. + SetPLCIndex(obj, 0); + removeFromPLCBuffer(index); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + } + + [NoInline] + [ManualRefCounts] + [RequiredByBartok] + [GCAnnotation(GCOption.NOGC)] + internal static void NonNullIncrementRefCount(Object obj) { + VTable.Assert(obj != null, + @"obj != null"); + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) < refCountMask, + @"(refState & refCountMask) < refCountMask"); + + if ((refState & countingONFlagMask) == 0) { + return; + } + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + + obj.REF_STATE = refState+1; + + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + if (index != 0) { + SetPLCIndex(obj, 0); + removeFromPLCBuffer(index); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + } + + [Inline] + [ManualRefCounts] + [RequiredByBartok] + [GCAnnotation(GCOption.NOGC)] + internal static void AcyclicIncrementRefCount(Object obj) { + if (obj == null) { + return; + } + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) < refCountMask, + @"(refState & refCountMask) < refCountMask"); + + if ((refState & countingONFlagMask) == 0) { + return; + } + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + VTable.Assert((refState & acyclicFlagMask) != 0, + @"(refState & acyclicFlagMask) != 0"); + + obj.REF_STATE = refState+1; + } + + [Inline] + [ManualRefCounts] + [RequiredByBartok] + [GCAnnotation(GCOption.NOGC)] + internal static void NonNullAcyclicIncrementRefCount(Object obj) { + VTable.Assert(obj != null, + @"obj != null"); + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) < refCountMask, + @"(refState & refCountMask) < refCountMask"); + + if ((refState & countingONFlagMask) == 0) { + return; + } + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + VTable.Assert((refState & acyclicFlagMask) != 0, + @"(refState & acyclicFlagMask) != 0"); + + obj.REF_STATE = refState+1; + } + + [Inline] + [ManualRefCounts] + [RequiredByBartok] + [GCAnnotation(GCOption.NOGC)] + internal static void PLCFreeIncrementRefCount(Object obj) { + if (obj == null) { + return; + } + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) < refCountMask, + @"(refState & refCountMask) < refCountMask"); + + if ((refState & countingONFlagMask) == 0) { + return; + } + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + + obj.REF_STATE = refState+1; + } + + [Inline] + [ManualRefCounts] + [RequiredByBartok] + [GCAnnotation(GCOption.NOGC)] + internal static void PLCFreeNonNullIncrementRefCount(Object obj) { + VTable.Assert(obj != null, + @"obj != null"); + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) < refCountMask, + @"(refState & refCountMask) < refCountMask"); + + if ((refState & countingONFlagMask) == 0) { + return; + } + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + + obj.REF_STATE = refState+1; + } + + [NoInline] + [ManualRefCounts] + [RequiredByBartok] + [GCAnnotation(GCOption.NOGC)] + internal static void DecrementRefCount(Object obj) { + if (obj == null) { + return; + } + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + + if ((refState & refCountMask) == 1) { + VTable.Assert((refState & countingONFlagMask) != 0, + @"(refState & countingONFlagMask) != 0"); + + MultiUseWord muw = MultiUseWord.GetForObject(obj); + if (muw.IsMonitorOrInflatedTag()) { + MultiUseWord.RefCountGCDeadObjHook(muw); + } + + // Remove the object from the PLC buffer. + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + if (index != 0) { + removeFromPLCBuffer(index); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + + // Set aside the object for delayed deallocation. + deallocateLazily(obj); + } else { + if ((refState & countingONFlagMask) == 0) { + return; + } + + // Include the object for leaked cycle processing. + // Insert the object into the PLC buffer only + // if it hasn't already been inserted. + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + if (index == 0) { + addToPLCBuffer(obj); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + } + + obj.REF_STATE = refState-1; + } + + [NoInline] + [ManualRefCounts] + [RequiredByBartok] + [GCAnnotation(GCOption.NOGC)] + internal static void NonNullDecrementRefCount(Object obj) { + VTable.Assert(obj != null, + @"obj != null"); + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + + if ((refState & refCountMask) == 1) { + VTable.Assert((refState & countingONFlagMask) != 0, + @"(refState & countingONFlagMask) != 0"); + + MultiUseWord muw = MultiUseWord.GetForObject(obj); + if (muw.IsMonitorOrInflatedTag()) { + MultiUseWord.RefCountGCDeadObjHook(muw); + } + + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + if (index != 0) { + removeFromPLCBuffer(index); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + + deallocateLazily(obj); + } else { + if ((refState & countingONFlagMask) == 0) { + return; + } + + if ((refState & acyclicFlagMask) == 0) { + uint index = GetPLCIndex(obj); + if (index == 0) { + addToPLCBuffer(obj); + } + } +#if DEBUG + else { + uint index = GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + } + + obj.REF_STATE = refState-1; + } + + [Inline] + [ManualRefCounts] + [RequiredByBartok] + [GCAnnotation(GCOption.NOGC)] + internal static void AcyclicDecrementRefCount(Object obj) { + if (obj == null) { + return; + } + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + + if ((refState & refCountMask) == 1) { + VTable.Assert((refState & countingONFlagMask) != 0, + @"(refState & countingONFlagMask) != 0"); + + MultiUseWord muw = MultiUseWord.GetForObject(obj); + if (muw.IsMonitorOrInflatedTag()) { + MultiUseWord.RefCountGCDeadObjHook(muw); + } + + VTable.Assert((refState & acyclicFlagMask) != 0, + @"(refState & acyclicFlagMask) != 0"); + + deallocateLazily(obj); + } else if ((refState & countingONFlagMask) == 0) { + return; + } + + obj.REF_STATE = refState-1; + } + + [Inline] + [ManualRefCounts] + [RequiredByBartok] + [GCAnnotation(GCOption.NOGC)] + internal static void NonNullAcyclicDecrementRefCount(Object obj) { + VTable.Assert(obj != null, + @"obj != null"); + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + + if ((refState & refCountMask) == 1) { + VTable.Assert((refState & countingONFlagMask) != 0, + @"(refState & countingONFlagMask) != 0"); + + MultiUseWord muw = MultiUseWord.GetForObject(obj); + if (muw.IsMonitorOrInflatedTag()) { + MultiUseWord.RefCountGCDeadObjHook(muw); + } + + VTable.Assert((refState & acyclicFlagMask) != 0, + @"(refState & acyclicFlagMask) != 0"); + + deallocateLazily(obj); + } else if ((refState & countingONFlagMask) == 0) { + return; + } + + obj.REF_STATE = refState-1; + } + + [Inline] + [ManualRefCounts] + [RequiredByBartok] + [GCAnnotation(GCOption.NOGC)] + internal static void LiveAcyclicDecrementRefCount(Object obj) { + if (obj == null) { + return; + } + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + + uint i = (refState & countingONFlagMask) >> countingONFlagBit; + obj.REF_STATE = refState-i; + } + + [Inline] + [ManualRefCounts] + [RequiredByBartok] + [GCAnnotation(GCOption.NOGC)] + internal static void LiveNonNullAcyclicDecrementRefCount(Object obj) { + VTable.Assert(obj != null, + @"obj != null"); + + uint refState = obj.REF_STATE; + VTable.Assert((refState & refCountMask) > 0, + @"(refState & refCountMask) > 0"); + + uint i = (refState & countingONFlagMask) >> countingONFlagBit; + obj.REF_STATE = refState-i; + } + + [RequiredByBartok] + [DisableNullChecks] + [GCAnnotation(GCOption.NOGC)] + internal static void IncrementReferentRefCounts(UIntPtr objAddr, + VTable vtable) { + refCountIncrementer.VisitReferenceFields(objAddr, vtable); + } + + [RequiredByBartok] + [DisableNullChecks] + [GCAnnotation(GCOption.NOGC)] + internal static void DecrementReferentRefCounts(UIntPtr objAddr, + VTable vtable) { + refCountDecrementer.VisitReferenceFields(objAddr, vtable); + } + + [ManualRefCounts] + [DisableNullChecks] + [GCAnnotation(GCOption.NOGC)] + internal static unsafe void IncrementReferentRefCounts + (UIntPtr objAddr, + VTable vtable, + int start, + int span) { + uint objTag = (uint)vtable.pointerTrackingMask & 0xf; + + switch (objTag) { + case ObjectLayout.PTR_VECTOR_TAG: + case ObjectLayout.PTR_ARRAY_TAG: { +#if DEBUG + Object obj = Magic.fromAddress(objAddr); + Array array = Magic.toArray(obj); + VTable.Assert(span <= array.Length, + @"span <= array.Length"); +#endif // DEBUG + + UIntPtr* baseAddr = (UIntPtr*)(objAddr+ + vtable.baseLength-PreHeader.Size); + UIntPtr* begin = baseAddr+start; + UIntPtr* end = begin+span; + for (UIntPtr* el = begin; el < end; el++) { + UIntPtr addr = *el; + incrementRefCount(addr); + } + break; + } + + case ObjectLayout.OTHER_VECTOR_TAG: + case ObjectLayout.OTHER_ARRAY_TAG: { + if (vtable.arrayOf != StructuralType.Struct) { + break; + } + VTable elVTable = vtable.arrayElementClass; + uint elMask = (uint)elVTable.pointerTrackingMask; + if (elMask == ObjectLayout.SPARSE_TAG || + elMask == ObjectLayout.DENSE_TAG) { + break; + } +#if DEBUG + Object obj = Magic.fromAddress(objAddr); + Array array = Magic.toArray(obj); + VTable.Assert(span <= array.Length, + @"span <= array.Length"); +#endif // DEBUG + + UIntPtr baseAddr = objAddr+vtable.baseLength- + PreHeader.Size-PostHeader.Size; + int elSize = vtable.arrayElementSize; + UIntPtr begin = baseAddr+elSize*start; + UIntPtr end = begin+elSize*span; + for (UIntPtr el = begin; el < end; el += elSize) { + refCountIncrementer. + VisitReferenceFields(el, elVTable); + } + break; + } + + case ObjectLayout.STRING_TAG: { + break; + } + + default: { + VTable.NotReached("An unsupported tag found!"); + break; + } + } + } + + [ManualRefCounts] + [DisableNullChecks] + [GCAnnotation(GCOption.NOGC)] + internal static unsafe void DecrementReferentRefCounts + (UIntPtr objAddr, + VTable vtable, + int start, + int span) { + uint objTag = (uint)vtable.pointerTrackingMask & 0xf; + + switch (objTag) { + case ObjectLayout.PTR_VECTOR_TAG: + case ObjectLayout.PTR_ARRAY_TAG: { +#if DEBUG + Object obj = Magic.fromAddress(objAddr); + Array array = Magic.toArray(obj); + VTable.Assert(span <= array.Length, + @"span <= array.Length"); +#endif // DEBUG + + UIntPtr* baseAddr = (UIntPtr*)(objAddr+ + vtable.baseLength-PreHeader.Size); + UIntPtr* begin = baseAddr+start; + UIntPtr* end = begin+span; + for (UIntPtr* el = begin; el < end; el++) { + UIntPtr addr = *el; + decrementRefCount(addr); + } + break; + } + + case ObjectLayout.OTHER_VECTOR_TAG: + case ObjectLayout.OTHER_ARRAY_TAG: { + if (vtable.arrayOf != StructuralType.Struct) { + break; + } + VTable elVTable = vtable.arrayElementClass; + uint elMask = (uint)elVTable.pointerTrackingMask; + if (elMask == ObjectLayout.SPARSE_TAG || + elMask == ObjectLayout.DENSE_TAG) { + break; + } +#if DEBUG + Object obj = Magic.fromAddress(objAddr); + Array array = Magic.toArray(obj); + VTable.Assert(span <= array.Length, + @"span <= array.Length"); +#endif // DEBUG + + UIntPtr baseAddr = objAddr+vtable.baseLength- + PreHeader.Size-PostHeader.Size; + int elSize = vtable.arrayElementSize; + UIntPtr begin = baseAddr+elSize*start; + UIntPtr end = begin+elSize*span; + for (UIntPtr el = begin; el < end; el += elSize) { + refCountDecrementer. + VisitReferenceFields(el, elVTable); + } + break; + } + + case ObjectLayout.STRING_TAG: { + break; + } + + default: { + VTable.NotReached("An unsupported tag found!"); + break; + } + } + } + + + [Inline] + [ManualRefCounts] + private static void deallocateLazily(Object obj) { + setNextLink(obj, delayedDeallocationList); + delayedDeallocationList = obj; + if (!GC.HeapSizeConfigurable) { + delayedDeallocationLength++; + } + } + + [ManualRefCounts] + private static void deallocateObjects() { + int startTicks = 0; + bool enableGCTiming = VTable.enableGCTiming; + if (enableGCTiming) { + VTable.enableGCTiming = false; + startTicks = Environment.TickCount; + } + +#if MEASURE_RCPHASES + int ddListTicks = 0; + bool phaseFlag = ddListPhase; + if (!phaseFlag) { + ddListPhase = true; + ddListTicks = Environment.TickCount; + } +#endif // MEASURE_RCPHASES + + // Either continue working on old object, or extract + // a new object from the delayed deallocation list. + if (objectBeingDeallocated == null) { + objectBeingDeallocated = extractObjectFromDDList(); + } + int work = 0; + Object obj = objectBeingDeallocated; + while (obj != null) { + UIntPtr objAddr = Magic.addressOf(obj); + VTable vt = obj.vtable; + uint ptrMask = (uint)vt.pointerTrackingMask; + bool ongoing; + do { + ongoing = incrementalDecrement(objAddr, vt, ptrMask, + ref work); + } while (ongoing && work < deallocationSpan); + + if (!ongoing) { // Release object back to allocator. + releaseToAllocator(obj); + obj = extractObjectFromDDList(); + } + if (work >= deallocationSpan) { + break; + } + } + objectBeingDeallocated = obj; + +#if MEASURE_RCPHASES + if (!phaseFlag) { + int elapsed = Environment.TickCount-ddListTicks; + if (plcBufferPhase) { + plcBufferDDListProcTime += elapsed; + plcBufferDDListProcCount++; + } else { + ddListProcTime += elapsed; + ddListProcCount++; + } + ddListPhase = false; + } +#endif // MEASURE_RCPHASES + + if (enableGCTiming) { + int elapsedTicks = Environment.TickCount-startTicks; + BaseCollector.RegisterPause(elapsedTicks); + VTable.enableGCTiming = true; + } + } + + [ManualRefCounts] + private static Object extractObjectFromDDList() { + Object obj = delayedDeallocationList; + if (obj != null) { + delayedDeallocationList = getNextLink(obj); + if (!GC.HeapSizeConfigurable) { + delayedDeallocationLength--; + } + + UIntPtr objAddr = Magic.addressOf(obj); + initIncrementalDecrement(objAddr, obj.vtable); + +#if DEBUG + UIntPtr page = PageTable.Page(objAddr); + VTable.Assert(PageTable.IsGcPage(page), + @"PageTable.IsGcPage(page)"); +#endif // DEBUG + } + + return obj; + } + + [ManualRefCounts] + [DisableNullChecks] + private static unsafe void initIncrementalDecrement + (UIntPtr objAddr, + VTable vtable) { + uint ptrMask = (uint)vtable.pointerTrackingMask; + uint objTag = ptrMask & 0xf; + + switch (objTag) { + case ObjectLayout.SPARSE_TAG: + case ObjectLayout.DENSE_TAG: + case ObjectLayout.STRING_TAG : { + break; + } + + case ObjectLayout.PTR_VECTOR_TAG: + case ObjectLayout.OTHER_VECTOR_TAG: + case ObjectLayout.PTR_ARRAY_TAG: + case ObjectLayout.OTHER_ARRAY_TAG: { + Object obj = Magic.fromAddress(objAddr); + Array array = Magic.toArray(obj); + setLastTracked(objAddr, array.Length); + break; + } + + default: { + int* ptrDescriptor = (int*)ptrMask; + int initialCount = *ptrDescriptor; + setLastTracked(objAddr, initialCount); + break; + } + } + } + + [ManualRefCounts] + [DisableNullChecks] + private static unsafe bool incrementalDecrement(UIntPtr objAddr, + VTable vtable, + uint ptrMask, + ref int work) { + uint objTag = ptrMask & 0xf; + bool ongoing = false; + + if (objTag == ObjectLayout.SPARSE_TAG) { + UIntPtr* sparseObj = (UIntPtr*)objAddr; + work += 7; + for (ptrMask >>= 4; ptrMask != 0; ptrMask >>= 4) { + uint index = ptrMask & 0xf; + UIntPtr* loc = sparseObj+unchecked((int)index); + UIntPtr addr = *loc; + decrementRefCount(addr); + } + } else if (objTag == ObjectLayout.DENSE_TAG) { + UIntPtr* denseObj = (UIntPtr*)(objAddr+PostHeader.Size); + work += 28; + for (ptrMask >>= 4; ptrMask != 0; ptrMask >>= 1) { + if ((ptrMask & 0x1) != 0) { + UIntPtr addr = *denseObj; + decrementRefCount(addr); + } + denseObj++; + } + } else if (objTag == ObjectLayout.OTHER_VECTOR_TAG || + objTag == ObjectLayout.OTHER_ARRAY_TAG) { + if (vtable.arrayOf != StructuralType.Struct) { + return ongoing; + } + VTable elVTable = vtable.arrayElementClass; + uint elMask = (uint)elVTable.pointerTrackingMask; + if (elMask == ObjectLayout.SPARSE_TAG || + elMask == ObjectLayout.DENSE_TAG) { + return ongoing; + } + uint elObjTag = elMask & 0xf; + VTable.Assert(elObjTag == ObjectLayout.SPARSE_TAG || + elObjTag == ObjectLayout.DENSE_TAG, + @"elObjTag == ObjectLayout.SPARSE_TAG || + elObjTag == ObjectLayout.DENSE_TAG"); + VTable.Assert(work < deallocationSpan, + @"work < deallocationSpan"); + + int prev = getLastTracked(objAddr); + int elShift = elObjTag == ObjectLayout.SPARSE_TAG ? 2 : 4; + int workChunk = ((deallocationSpan-work) >> elShift)+1; + int last = prev > workChunk ? prev-workChunk : 0; + ongoing = last != 0; + if (ongoing) { + setLastTracked(objAddr, last); + } + + UIntPtr baseAddr = objAddr+vtable.baseLength- + PreHeader.Size-PostHeader.Size; + int size = vtable.arrayElementSize; + UIntPtr begin = baseAddr+(UIntPtr)(size*(prev-1)); + UIntPtr end = baseAddr+(UIntPtr)(size*last); + for (UIntPtr el = begin; el >= end; el -= size) { + incrementalDecrement(el, elVTable, elMask, ref work); + } + } else if (objTag != ObjectLayout.STRING_TAG) { + VTable.Assert(work < deallocationSpan, + @"work < deallocationSpan"); + + int prev = getLastTracked(objAddr); + int workChunk = deallocationSpan-work; + int last = prev > workChunk ? prev-workChunk : 0; + ongoing = last != 0; + if (ongoing) { + setLastTracked(objAddr, last); + } + work += prev-last; + + if (objTag == ObjectLayout.PTR_VECTOR_TAG || + objTag == ObjectLayout.PTR_ARRAY_TAG) { + UIntPtr baseAddr = + objAddr+vtable.baseLength-PreHeader.Size; + UIntPtr* begin = (UIntPtr*)baseAddr+prev-1; + UIntPtr* end = (UIntPtr*)baseAddr+last; + for (UIntPtr* el = begin; el >= end; el--) { + UIntPtr addr = *el; + decrementRefCount(addr); + } + } else { + VTable.Assert((objTag & 0x1) == 0, + @"(objTag & 0x1) == 0"); + + UIntPtr* largeObj = (UIntPtr*)objAddr; + int* ptrDescriptor = (int*)ptrMask; + for (int index = prev; index > last; index--) { + UIntPtr* loc = largeObj+*(ptrDescriptor+index); + UIntPtr addr = *loc; + decrementRefCount(addr); + } + } + } + + return ongoing; + } + + [Inline] + [ManualRefCounts] + private static void incrementRefCount(UIntPtr objAddr) { + if (objAddr != UIntPtr.Zero) { + NonNullIncrementRefCount(Magic.fromAddress(objAddr)); + } + } + + [Inline] + [ManualRefCounts] + private static void decrementRefCount(UIntPtr objAddr) { + if (objAddr != UIntPtr.Zero) { + NonNullDecrementRefCount(Magic.fromAddress(objAddr)); + } + } + + + [ManualRefCounts] + [DisableNullChecks] + private static Object getNextLink(Object obj) { + return Magic.fromAddress(MultiUseWord.GetValForObject(obj)); + } + + [ManualRefCounts] + [DisableNullChecks] + private static void setNextLink(Object obj, Object next) { + MultiUseWord.SetValForObject(obj, Magic.addressOf(next)); + } + + [ManualRefCounts] + [DisableNullChecks] + private static int getLastTracked(UIntPtr objAddr) { + Object obj = Magic.fromAddress(objAddr); + return unchecked((int)(uint)MultiUseWord.GetValForObject(obj)); + } + + [ManualRefCounts] + [DisableNullChecks] + private static void setLastTracked(UIntPtr objAddr, int last) { + Object obj = Magic.fromAddress(objAddr); + MultiUseWord.SetValForObject(obj, (UIntPtr)unchecked((uint)last)); + } + + [ManualRefCounts] + private static UIntPtr getBackupRefcount(Object obj) { + return ((EagerRCVerificationObject)obj).preHeader. + backupRefcount; + } + + [ManualRefCounts] + private static void setBackupRefcount(Object obj, UIntPtr count) { + ((EagerRCVerificationObject)obj).preHeader.backupRefcount = + count; + } + + [ManualRefCounts] + private static UIntPtr getDfsDiscoveryTime(Object obj) { + return ((EagerRCVerificationObject)obj).preHeader. + dfsDiscoveryTime; + } + + [ManualRefCounts] + private static void setDfsDiscoveryTime(Object obj, + UIntPtr time) { + ((EagerRCVerificationObject)obj).preHeader.dfsDiscoveryTime = + time; + } + + [ManualRefCounts] + private static UIntPtr getDfsFinishingTime(Object obj) { + return ((EagerRCVerificationObject)obj).preHeader. + dfsFinishingTime; + } + + [ManualRefCounts] + private static void setDfsFinishingTime(Object obj, + UIntPtr time) { + ((EagerRCVerificationObject)obj).preHeader.dfsFinishingTime = + time; + } + + [ManualRefCounts] + private static void allocatePLCBuffer(uint count) { + VTable vtable = ((RuntimeType)typeof(UIntPtr[])).classVtable; + + plcRawSize = ObjectLayout.ArraySize(vtable, count); + plcRawAddr = MemoryManager.AllocateMemory(plcRawSize); + PageManager.SetStaticDataPages(plcRawAddr, plcRawSize); + + BumpAllocator pool = new BumpAllocator(PageType.NonGC); + pool.SetZeroedRange(plcRawAddr, plcRawSize); + uint alignment = vtable.baseAlignment; + UIntPtr addr = pool.AllocateFast(plcRawSize, alignment); + Array result = Magic.toArray(Magic.fromAddress(addr)); + + result.InitializeVectorLength(unchecked((int)count)); + result.REF_STATE = 2 & ~countingONFlagMask; + result.vtable = vtable; + + plcBuffer = Magic.toUIntPtrVector(result); + } + + [ManualRefCounts] + [DisableBoundsChecks] + private static void reallocatePLCBuffer() { + UIntPtr oldPLCRawSize = plcRawSize; + UIntPtr oldPLCRawAddr = plcRawAddr; + UIntPtr[] oldPLCBuffer = plcBuffer; + + uint oldNumEntries = unchecked((uint)oldPLCBuffer.Length); + uint newNumEntries = oldNumEntries << 1; + allocatePLCBuffer(newNumEntries); + + UIntPtr[] newPLCBuffer = plcBuffer; + for (uint i = 0; i < oldNumEntries; i++) { + newPLCBuffer[i] = oldPLCBuffer[i]; + } + + MemoryManager.FreeMemory(oldPLCRawAddr, oldPLCRawSize); + } + + [ManualRefCounts] + [DisableBoundsChecks] + private static void stitchFreePLCSlots(uint firstFreeSlot) { + UIntPtr[] plcBuffer = ReferenceCountingCollector.plcBuffer; + + int plcNumEntries = plcBuffer.Length; + for (uint i = firstFreeSlot; i < plcNumEntries-1; i++) { + plcBuffer[i] = (UIntPtr)(markFlagMask | (i+1)); + } + plcBuffer[plcNumEntries-1] = (UIntPtr)markFlagMask; + freePLCHead = firstFreeSlot; + } + + [ManualRefCounts] + [DisableBoundsChecks] + private static void addToPLCBuffer(Object obj) { + UIntPtr[] plcBuffer = ReferenceCountingCollector.plcBuffer; + uint freePLCHead = ReferenceCountingCollector.freePLCHead; + + // Check if free PLC buffer entries are available. + if (freePLCHead == 0) { + uint plcNumEntries = unchecked((uint)plcBuffer.Length); + if (plcNumEntries < maxPLCNumEntries) { + reallocatePLCBuffer(); + plcBuffer = ReferenceCountingCollector.plcBuffer; + stitchFreePLCSlots(plcNumEntries); + } else { + processPLCBuffer(); + } + freePLCHead = ReferenceCountingCollector.freePLCHead; + } + + // Insert object into the PLC buffer. + uint entry = (uint)plcBuffer[freePLCHead]; + uint newFreePLCHead = entry & ~markFlagMask; + plcBuffer[freePLCHead] = Magic.addressOf(obj); + + // Point the object to its slot in the PLC buffer. + SetPLCIndex(obj, freePLCHead); + + // Update the free PLC entries' head. + ReferenceCountingCollector.freePLCHead = newFreePLCHead; + } + + [Inline] + [ManualRefCounts] + [DisableBoundsChecks] + private static void removeFromPLCBuffer(uint index) { + // The object needs to be removed from the PLC buffer. + plcBuffer[index] = (UIntPtr)(freePLCHead | markFlagMask); + freePLCHead = index; + } + + [ManualRefCounts] + [DisableNullChecks] + [DisableBoundsChecks] + private static void processPLCBuffer() { + int startTicks = 0; + bool enableGCTiming = VTable.enableGCTiming; + if (enableGCTiming) { + VTable.enableGCTiming = false; + startTicks = Environment.TickCount; + } + +#if MEASURE_RCPHASES + int plcBufferTicks = 0; + bool phaseFlag = plcBufferPhase; + if (!phaseFlag) { + plcBufferPhase = true; + plcBufferTicks = Environment.TickCount; + } +#endif // MEASURE_RCPHASES + + UIntPtr[] plcBuffer = ReferenceCountingCollector.plcBuffer; + + int plcNumEntries = plcBuffer.Length; + + // Let S be the subgraph of heap objects reachable from + // the PLC buffer. Decrement counts due to references in S. + for (int i = 1; i < plcNumEntries; i++) { + UIntPtr objAddr = plcBuffer[i]; + if (((uint)objAddr & markFlagMask) != 0) { + continue; + } + + VTable.Assert(objAddr != UIntPtr.Zero, + @"objAddr != UIntPtr.Zero"); + + Object obj = Magic.fromAddress(objAddr); + uint refState = obj.REF_STATE; + VTable.Assert((refState & countingONFlagMask) != 0, + @"(refState & countingONFlagMask) != 0"); + VTable.Assert((refState & acyclicFlagMask) == 0, + @"(refState & acyclicFlagMask) == 0"); + + if ((refState & markFlagMask) == 0) { + obj.REF_STATE = refState | markFlagMask; + internalDecrementer.Traverse(objAddr); + } + } + + // Objects that now have non-zero counts are those that + // have references external to S incident on them. + // Recompute counts due to reachability from such objects. + for (int i = 1; i < plcNumEntries; i++) { + UIntPtr objAddr = plcBuffer[i]; + if (((uint)objAddr & markFlagMask) != 0) { + continue; + } + + internalScanner.Traverse(objAddr); + } + + // String together objects with reference count + // of zero for reclamation. + internalReclaimer.Initialize(); + for (int i = 1; i < plcNumEntries; i++) { + UIntPtr objAddr = plcBuffer[i]; + if (((uint)objAddr & markFlagMask) != 0) { + continue; + } + + Object obj = Magic.fromAddress(objAddr); + uint refState = obj.REF_STATE; + const uint mask = markFlagMask | acyclicFlagMask; + VTable.Assert((refState & mask) == 0 || + refState == ~countingONFlagMask, + @"(refState & mask) == 0 || + refState == ~countingONFlagMask"); + + if (refState == countingONFlagMask) { + internalReclaimer.Traverse(objAddr); + } else { + SetPLCIndex(obj, 0); + } + } + ulong reclaimedBytes = 0; + Object reclaimedObj = internalReclaimer.ReclaimedObjects; + while (reclaimedObj != null) { + if (VTable.enableGCProfiling) { + UIntPtr size = ObjectLayout.Sizeof(reclaimedObj); + reclaimedBytes += (ulong)size; + } + Object nextReclaimedObj = getNextLink(reclaimedObj); + releaseToAllocator(reclaimedObj); + reclaimedObj = nextReclaimedObj; + } + + // Recycle the PLC buffer. + stitchFreePLCSlots(1); + + // Release the memory used up by work lists. + UnmanagedPageList.ReleaseStandbyPages(); + + if (VTable.enableGCProfiling) { + if (maxCyclicGarbage < reclaimedBytes) { + maxCyclicGarbage = reclaimedBytes; + } + totalCyclicGarbage += reclaimedBytes; + cycleCollections++; + } + +#if MEASURE_RCPHASES + if (!phaseFlag) { + int elapsed = Environment.TickCount-plcBufferTicks; + if (ddListPhase) { + ddListPLCProcTime += elapsed; + ddListPLCProcCount++; + } else { + plcBufferProcTime += elapsed; + plcBufferProcCount++; + } + plcBufferPhase = false; + } +#endif // MEASURE_RCPHASES + + if (enableGCTiming) { + int elapsedTicks = Environment.TickCount-startTicks; + BaseCollector.RegisterPause(elapsedTicks); + VTable.enableGCTiming = true; + } + } + + + [NoInline] + [ManualRefCounts] + private static void deallocationListChecker() { + // Check for nonzero reference counts. + for (Object block = delayedDeallocationList; + block != null; block = getNextLink(block)) { + UIntPtr objAddr = Magic.addressOf(block); + UIntPtr page = PageTable.Page(objAddr); + if (!PageTable.IsGcPage(page)) { + VTable.DebugPrint("Non-GC memory for freeing!\n"); + VTable.DebugBreak(); + } + if ((block.REF_STATE & refCountMask) != 0) { + VTable.DebugPrint("Non-zero reference count!\n"); + VTable.DebugBreak(); + } + } + + // Check for loops in the delayed deallocation list. + for (Object block = delayedDeallocationList; + block != null; block = getNextLink(block)) { + block.REF_STATE++; + } + for (Object block = delayedDeallocationList; + block != null; block = getNextLink(block)) { + if ((block.REF_STATE & refCountMask) != 1) { + VTable.DebugPrint("Loops in DD list!\n"); + VTable.DebugBreak(); + } + } + for (Object block = delayedDeallocationList; + block != null; block = getNextLink(block)) { + block.REF_STATE--; + } + } + + [Inline] + [ManualRefCounts] + private static bool nonEmptyDDList() { + return objectBeingDeallocated != null || + delayedDeallocationList != null; + } + + [NoInline] + [ManualRefCounts] + private static void purgeDeallocationList() { + while (nonEmptyDDList()) { + deallocateObjects(); + } + } + + [Inline] + [ManualRefCounts] + private static void releaseToAllocator(Object obj) { +#if MEASURE_RCPHASES + int segFreeTicks = Environment.TickCount; +#endif // MEASURE_RCPHASES + + UIntPtr objStart = Magic.addressOf(obj)-PreHeader.Size; + UIntPtr page = PageTable.Page(objStart); + PageType pageType = PageTable.Type(page); + if (pageType == SegregatedFreeList.SMALL_OBJ_PAGE) { + uint alignment = obj.vtable.baseAlignment; + SegregatedFreeList.LocalFreeSmall(objStart, alignment); + } else { + VTable.Assert(pageType == SegregatedFreeList. + LARGE_OBJ_START, + @"pageType == SegregatedFreeList. + LARGE_OBJ_START"); + + SegregatedFreeList.FreeLarge(obj); + } + +#if MEASURE_RCPHASES + segFreeTime += Environment.TickCount-segFreeTicks; +#endif // MEASURE_RCPHASES + + if (!GC.HeapSizeConfigurable) { + releasedObjectCount++; + if (releasedObjectCount > recycleTrigger) { + recycleAllocator(); + } + } + } + + [Inline] + [ManualRefCounts] + private static void recycleAllocator() { +#if MEASURE_RCPHASES + int segCommitTicks = Environment.TickCount; +#endif // MEASURE_RCPHASES + + if (!GC.HeapSizeConfigurable) { + releasedObjectCount = 0; + } + SegregatedFreeList.LocalRecycleGlobalPages(); + SegregatedFreeList.CommitFreedData(); + +#if MEASURE_RCPHASES + segCommitTime += Environment.TickCount-segCommitTicks; +#endif // MEASURE_RCPHASES + } + + + private class RefCountIncrementer : NonNullReferenceVisitor { + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + incrementRefCount(objAddr); + } + } + + private class RefCountDecrementer : NonNullReferenceVisitor { + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + decrementRefCount(objAddr); + } + } + + private class InternalDecrementer : NonNullReferenceVisitor { + [ManualRefCounts] + internal void Traverse(UIntPtr objAddr) { + Object obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + while (!this.workList.IsEmpty) { + objAddr = this.workList.Read(); + obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + } + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + Object obj = Magic.fromAddress(objAddr); + uint refState = obj.REF_STATE; + if ((refState & countingONFlagMask) == 0) { + return; + } + uint refCount = refState & refCountMask; + VTable.Assert(refCount > 0, + @"refCount > 0"); + + refState--; + const uint mask = markFlagMask | acyclicFlagMask; + if ((refState & mask) == 0) { + refState |= markFlagMask; + this.workList.Write(objAddr); + } + obj.REF_STATE = refState; + } + + private UIntPtrQueue workList; + } + + private class InternalScanner : NonNullReferenceVisitor { + [ManualRefCounts] + internal unsafe void Traverse(UIntPtr objAddr) { + this.Visit(&objAddr); + while (!this.workList.IsEmpty) { + objAddr = this.workList.Read(); + Object obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + } + } + + [ManualRefCounts] + [DisableNullChecks] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + Object obj = Magic.fromAddress(objAddr); + uint refState = obj.REF_STATE; + const uint mask1 = countingONFlagMask | markFlagMask; + const uint mask2 = mask1 | acyclicFlagMask; + if ((refState & mask2) != mask1) { + return; + } + + obj.REF_STATE = refState & ~markFlagMask; + if (refState > mask1) { + internalIncrementer.Traverse(objAddr); + } else { + this.workList.Write(objAddr); + } + } + + private UIntPtrQueue workList; + } + + private class InternalIncrementer : NonNullReferenceVisitor { + [ManualRefCounts] + internal void Traverse(UIntPtr objAddr) { + Object obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + while (!this.workList.IsEmpty) { + objAddr = this.workList.Read(); + obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + } + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + Object obj = Magic.fromAddress(objAddr); + uint refState = obj.REF_STATE; + if ((refState & countingONFlagMask) == 0) { + return; + } + uint refCount = refState & refCountMask; + VTable.Assert(refCount < refCountMask, + @"refCount < refCountMask"); + + refState++; + const uint mask1 = markFlagMask | acyclicFlagMask; + const uint mask2 = countingONFlagMask | 1; + if ((refState & mask1) == markFlagMask) { + // The object hasn't been visited either + // by the scanner or the incrementer. + refState &= ~markFlagMask; + this.workList.Write(objAddr); + } else if (refState == mask2) { + // The object has been visited in the + // past, but only by the scanner. + this.workList.Write(objAddr); + } + obj.REF_STATE = refState; + } + + private UIntPtrQueue workList; + } + + private class InternalReclaimer : NonNullReferenceVisitor { + internal Object ReclaimedObjects; + + [ManualRefCounts] + internal void Initialize() { + this.ReclaimedObjects = null; + } + + [ManualRefCounts] + internal unsafe void Traverse(UIntPtr objAddr) { + this.Reclaim(objAddr); + while (!this.workList.IsEmpty) { + objAddr = this.workList.Read(); + Object obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + } + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + Object obj = Magic.fromAddress(objAddr); + uint refState = obj.REF_STATE; + const uint mask = countingONFlagMask | acyclicFlagMask; + if (refState == mask) { + deallocateLazily(obj); + obj.REF_STATE = mask | markFlagMask; + } else if (refState == countingONFlagMask) { + Reclaim(objAddr); + } + } + + [ManualRefCounts] + internal void Reclaim(UIntPtr objAddr) { + Object obj = Magic.fromAddress(objAddr); + setNextLink(obj, this.ReclaimedObjects); + obj.REF_STATE = ~countingONFlagMask; + this.ReclaimedObjects = obj; + this.workList.Write(objAddr); + } + + private UIntPtrQueue workList; + } + + private abstract class ObjectVisitor : + SegregatedFreeList.ObjectVisitor { + [ManualRefCounts] + internal override void VisitSmall(Object obj, + UIntPtr memAddr) { + this.Visit(obj); + } + + [ManualRefCounts] + internal override UIntPtr VisitLarge(Object obj) { + return this.Visit(obj); + } + + internal abstract override UIntPtr Visit(Object obj); + } + + + private class BackupInitializer : ObjectVisitor { + [ManualRefCounts] + internal override UIntPtr Visit(Object obj) { + setBackupRefcount(obj, UIntPtr.Zero); + + VTable vtable = obj.vtable; + UIntPtr objAddr = Magic.addressOf(obj); + return ObjectLayout.ObjectSize(objAddr, vtable); + } + } + + private class BackupRefCount : NonNullReferenceVisitor { + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr addr = *loc; + + UIntPtr page = PageTable.Page(addr); + if (!PageTable.IsGcPage(page)) { + PageType pageType = PageTable.Type(page); + VTable.Assert(pageType == PageType.NonGC || + pageType == PageType.Stack, + @"pageType == PageType.NonGC || + pageType == PageType.Stack"); + + return; + } + + UIntPtr objAddr = SegregatedFreeList.Find(addr); + incrementBackupRefCount.Traverse(objAddr); + } + } + + private class IncrementBackupRefCount : NonNullReferenceVisitor { + [ManualRefCounts] + internal void Traverse(UIntPtr objAddr) { + Object obj = Magic.fromAddress(objAddr); + UIntPtr count = getBackupRefcount(obj); + setBackupRefcount(obj, count+1); + if (obj.GcMark((UIntPtr)1)) { + this.VisitReferenceFields(obj); + } + while (!this.workList.IsEmpty) { + objAddr = this.workList.Read(); + obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + } + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr addr = *loc; + + UIntPtr page = PageTable.Page(addr); + if (!PageTable.IsGcPage(page)) { + PageType pageType = PageTable.Type(page); + VTable.Assert(pageType == PageType.NonGC || + pageType == PageType.Stack, + @"pageType == PageType.NonGC || + pageType == PageType.Stack"); + + return; + } + + UIntPtr objAddr = SegregatedFreeList.Find(addr); + Object obj = Magic.fromAddress(objAddr); + UIntPtr count = getBackupRefcount(obj); + setBackupRefcount(obj, count+1); + if (obj.GcMark((UIntPtr)1)) { + this.workList.Write(objAddr); + } + } + + private UIntPtrQueue workList; + } + + private class RootsScanner : NonNullReferenceVisitor { + internal void Initialize(NonNullReferenceVisitor v) { + this.visitor = v; + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr pageLoc = PageTable.Page((UIntPtr)loc); + PageType pageType = PageTable.Type(pageLoc); + if (pageType != PageType.NonGC && + pageType != PageType.Stack) { + VTable.Assert(PageTable.IsGcPage(pageLoc), + @"PageTable.IsGcPage(pageLoc)"); + + return; + } + + uint addr = (uint)*loc; + if (pageType == PageType.NonGC || (addr & 0x03) == 0) { + this.visitor.Visit(loc); + } + if (pageType == PageType.Stack) { + *loc = (UIntPtr)(addr | 0x01); + } + } + + NonNullReferenceVisitor visitor; + } + + private class ResetRoots : NonNullReferenceVisitor { + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr pageLoc = PageTable.Page((UIntPtr)loc); + PageType pageType = PageTable.Type(pageLoc); + if (pageType != PageType.NonGC && + pageType != PageType.Stack) { + VTable.Assert(PageTable.IsGcPage(pageLoc), + @"PageTable.IsGcPage(pageLoc)"); + + return; + } + + if (pageType == PageType.Stack) { + *loc = (UIntPtr)((uint)*loc & 0xfffffffc); + } + } + } + + private class LeakAccumulator : ObjectVisitor { + internal UIntPtr Size; + + internal void Initialize() { + this.Size = UIntPtr.Zero; + } + + [ManualRefCounts] + internal override UIntPtr Visit(Object obj) { + VTable vtable = obj.vtable; + UIntPtr objAddr = Magic.addressOf(obj); + UIntPtr size = ObjectLayout.ObjectSize(objAddr, vtable); + + uint refState = obj.REF_STATE; + UIntPtr refCount = (UIntPtr)(refState & refCountMask); + if ((refState & countingONFlagMask) != 0 && + refCount > 0) { + // This object is considered live by the + // reference counting collector. + UIntPtr count = getBackupRefcount(obj); + if (count == 0) { + // But it is actually unreachable. + this.Size += size; + } + } + + return size; + } + } + + private class LeakedNodesDFS : ObjectVisitor { + [ManualRefCounts] + internal override unsafe UIntPtr Visit(Object obj) { + VTable vtable = obj.vtable; + UIntPtr objAddr = Magic.addressOf(obj); + UIntPtr size = ObjectLayout.ObjectSize(objAddr, vtable); + + uint refState = obj.REF_STATE; + UIntPtr refCount = (UIntPtr)(refState & refCountMask); + if ((refState & countingONFlagMask) != 0 && + refCount > 0) { + UIntPtr count = getBackupRefcount(obj); + if (count == 0) { + dfs.Visit(&objAddr); + } + } + + return size; + } + } + + private class DFS : NonNullReferenceVisitor { + internal void Initialize() { + this.time = UIntPtr.Zero; + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + + UIntPtr page = PageTable.Page(objAddr); + if (!PageTable.IsGcPage(page)) { + PageType pageType = PageTable.Type(page); + VTable.Assert(pageType == PageType.NonGC || + pageType == PageType.Stack, + @"pageType == PageType.NonGC || + pageType == PageType.Stack"); + + return; + } + + Object obj = Magic.fromAddress(objAddr); + if (obj.GcMark((UIntPtr)1)) { + this.time = this.time+1; + setDfsDiscoveryTime(obj, this.time); + + UIntPtr vtableAddr = Magic.addressOf(obj.vtable); + this.Visit(&vtableAddr); + this.VisitReferenceFields(obj); + + this.time = this.time+1; + setDfsFinishingTime(obj, this.time); + } + } + + private UIntPtr time; + } + + private class LeakedCycleClosure : ObjectVisitor { + [ManualRefCounts] + internal override UIntPtr Visit(Object obj) { + VTable vtable = obj.vtable; + UIntPtr objAddr = Magic.addressOf(obj); + UIntPtr size = ObjectLayout.ObjectSize(objAddr, vtable); + + uint refState = obj.REF_STATE; + UIntPtr refCount = (UIntPtr)(refState & refCountMask); + if ((refState & countingONFlagMask) != 0 && + refCount > 0) { + UIntPtr count = getBackupRefcount(obj); + if (count == 0) { + UIntPtr dTime = getDfsDiscoveryTime(obj); + UIntPtr fTime = getDfsFinishingTime(obj); + cycleClosure.Initialize(dTime, fTime); + cycleClosure.VisitReferenceFields(obj); + } + } + + return size; + } + } + + private class CycleClosure : NonNullReferenceVisitor { + internal void Initialize(UIntPtr dTime, UIntPtr fTime) { + this.predDiscoveryTime = dTime; + this.predFinishingTime = fTime; + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + + UIntPtr page = PageTable.Page(objAddr); + if (!PageTable.IsGcPage(page)) { + PageType pageType = PageTable.Type(page); + VTable.Assert(pageType == PageType.NonGC || + pageType == PageType.Stack, + @"pageType == PageType.NonGC || + pageType == PageType.Stack"); + + return; + } + + Object obj = Magic.fromAddress(objAddr); + UIntPtr dTime = getDfsDiscoveryTime(obj); + UIntPtr fTime = getDfsFinishingTime(obj); + VTable.Assert(this.predDiscoveryTime > UIntPtr.Zero && + this.predFinishingTime > UIntPtr.Zero && + dTime > UIntPtr.Zero && + fTime > UIntPtr.Zero, + @"this.predDiscoveryTime > UIntPtr.Zero && + this.predFinishingTime > UIntPtr.Zero && + dTime > UIntPtr.Zero && + fTime > UIntPtr.Zero"); + + if (dTime < this.predDiscoveryTime && + this.predDiscoveryTime < this.predFinishingTime && + this.predFinishingTime < fTime) { + // A back edge is incident on this node; + // therefore, the node is part of a cycle. + backupRefCount.Visit(&objAddr); + } + } + + private UIntPtr predDiscoveryTime; + private UIntPtr predFinishingTime; + } + + private class ResetTraversal : ObjectVisitor { + [ManualRefCounts] + internal override UIntPtr Visit(Object obj) { + obj.GcMark(UIntPtr.Zero); + VTable vtable = obj.vtable; + UIntPtr objAddr = Magic.addressOf(obj); + UIntPtr size = ObjectLayout.ObjectSize(objAddr, vtable); + + return size; + } + } + + private class BFSMarker : NonNullReferenceVisitor { + internal void Initialize(bool isVisited) { + this.isVisited = (UIntPtr)(isVisited ? 1 : 0); + } + + [ManualRefCounts] + internal void Traverse(Object obj) { + this.VisitReferenceFields(obj); + while (!this.workList.IsEmpty) { + UIntPtr objAddr = this.workList.Read(); + obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + } + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr addr = *loc; + + UIntPtr page = PageTable.Page(addr); + if (!PageTable.IsGcPage(page)) { + PageType pageType = PageTable.Type(page); + VTable.Assert(pageType == PageType.NonGC || + pageType == PageType.Stack, + @"pageType == PageType.NonGC || + pageType == PageType.Stack"); + + return; + } + + UIntPtr objAddr = SegregatedFreeList.Find(addr); + Object obj = Magic.fromAddress(objAddr); + if (obj.GcMark(this.isVisited)) { + this.workList.Write(objAddr); + } + } + + private UIntPtr isVisited; + private UIntPtrQueue workList; + } + + private class LeakedRoots : ObjectVisitor { + internal void Initialize() { + bfsMarker.Initialize(true); + } + + [ManualRefCounts] + internal override UIntPtr Visit(Object obj) { + VTable vtable = obj.vtable; + UIntPtr objAddr = Magic.addressOf(obj); + UIntPtr size = ObjectLayout.ObjectSize(objAddr, vtable); + + uint refState = obj.REF_STATE; + UIntPtr refCount = (UIntPtr)(refState & refCountMask); + if ((refState & countingONFlagMask) != 0 && + refCount > 0) { + UIntPtr count = getBackupRefcount(obj); + if (count == 0 && obj.GcMark() == UIntPtr.Zero) { + bfsMarker.Traverse(obj); + } + } + + return size; + } + } + + private class LeakedRootsCounter : ObjectVisitor { + internal uint Total; + + internal void Initialize() { + this.Total = 0; + } + + [ManualRefCounts] + internal override UIntPtr Visit(Object obj) { + VTable vtable = obj.vtable; + UIntPtr objAddr = Magic.addressOf(obj); + UIntPtr size = ObjectLayout.ObjectSize(objAddr, vtable); + + uint refState = obj.REF_STATE; + UIntPtr refCount = (UIntPtr)(refState & refCountMask); + if ((refState & countingONFlagMask) != 0 && + refCount > 0) { + UIntPtr count = getBackupRefcount(obj); + if (count == 0 && obj.GcMark() == UIntPtr.Zero) { + this.Total++; + } + } + + return size; + } + } + } +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/ReferenceVisitor.cs b/base/Imported/Bartok/runtime/shared/GCs/ReferenceVisitor.cs new file mode 100644 index 0000000..e3e00f7 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/ReferenceVisitor.cs @@ -0,0 +1,452 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs +{ + using Microsoft.Bartok.Runtime; + using System.Runtime.CompilerServices; + + internal abstract class ReferenceVisitor { + + internal struct ObjectDescriptor { + [ManualRefCounts] + [Inline] + internal ObjectDescriptor(VTable vtable, UIntPtr objectBase) : + this(vtable, objectBase, UIntPtr.Zero, UIntPtr.Zero, + null, null) + { + } + + [ManualRefCounts] + [Inline] + internal ObjectDescriptor(VTable vtable, UIntPtr objectBase, + UIntPtr secondBase) : + this(vtable, objectBase, secondBase, UIntPtr.Zero, null, null) + { + } + + [ManualRefCounts] + [Inline] + internal ObjectDescriptor(VTable vtable, + UIntPtr objectBase, + UIntPtr secondBase, + UIntPtr extra) : + this(vtable, objectBase, secondBase, extra, null, null) + {} + + [ManualRefCounts] + [Inline] + internal ObjectDescriptor(VTable vtable, + UIntPtr objectBase, + UIntPtr secondBase, + UIntPtr extra, + Object realObjectBase, + Object realSecondBase) + { + this.vtable = vtable; + this.objectBase = objectBase; + this.secondBase = secondBase; + this.extra = extra; + this.realObjectBase = realObjectBase; + this.realSecondBase = realSecondBase; + } + + // this struct is never in the heap, so the fields should + // be accessed the quick way. (and no, this struct isn't + // always accessed through a local var - it might be accessed + // via a managed pointer in the NoInline reference fields + // visitor methods.) + + [NoBarriers] + internal new VTable vtable; + [NoBarriers] + internal UIntPtr objectBase; + [NoBarriers] + internal UIntPtr secondBase; + [NoBarriers] + internal UIntPtr extra; + [NoBarriers] + internal Object realObjectBase; + [NoBarriers] + internal Object realSecondBase; + } + + // Rationale for cutting the fact that no stack probes are permitted in this + // tree of calls: + // + // There is no rationale. It is a bug which is tracked with all the cases that + // require fixing. + // + // Bug 436 + [NoStackLinkCheckTransCut] + [ManualRefCounts] + [Inline] + internal virtual UIntPtr VisitReferenceFields(Object obj) + { + return this.VisitReferenceFields(Magic.addressOf(obj), + obj.vtable); + } + + [Inline] + internal abstract UIntPtr VisitReferenceFields(UIntPtr objectBase, + VTable vtable); + + [Inline] + protected abstract unsafe + void Filter(UIntPtr *location, ref ObjectDescriptor objDesc); + + // Rationale for cutting the fact that no stack probes are permitted in this + // tree of calls: + // + // There is no rationale. This is a bug which has been filed. It is a temporary + // hack to break the recursion in this method. Recursion needs to open up an + // opportunity for stack probing. Eventually, perhaps this method can be + // rewritten to avoid the recursion. + // + // Bug 436 + [NoStackLinkCheckTransCut] + [ManualRefCounts] + [NoInline] + protected unsafe + UIntPtr VisitReferenceFieldsTemplateNoInline(ref ObjectDescriptor objDesc) + { + return VisitReferenceFieldsTemplate(ref objDesc); + } + + [ManualRefCounts] + [Inline] + protected unsafe + UIntPtr VisitReferenceFieldsTemplate(ref ObjectDescriptor objDesc) + { + UIntPtr pointerTracking = objDesc.vtable.pointerTrackingMask; + uint objectTag = (pointerTracking & 0xf); + UIntPtr size; + switch (objectTag) { + case ObjectLayout.SPARSE_TAG: { + UIntPtr *sparseObject = (UIntPtr *) objDesc.objectBase; + size = ObjectLayout.ObjectSize(objDesc.vtable); + pointerTracking >>= 4; + while (pointerTracking != 0) { + uint index = pointerTracking & 0xf; + pointerTracking >>= 4; + // The cast to int prevents C# from taking the + // index * sizeof(UIntPtr) to long: + UIntPtr *loc = sparseObject + (int) index; + this.Filter(loc, ref objDesc); + } + break; + } + case ObjectLayout.DENSE_TAG: { + // skip vtable + int postHeaderSize = PostHeader.Size; + UIntPtr *denseObject = (UIntPtr *) + (objDesc.objectBase + postHeaderSize); + size = ObjectLayout.ObjectSize(objDesc.vtable); + pointerTracking >>= 4; + while (pointerTracking != 0) { + if ((pointerTracking & ((UIntPtr)0x1)) != 0) { + this.Filter(denseObject, ref objDesc); + } + pointerTracking >>= 1; + denseObject++; + } + break; + } + case ObjectLayout.PTR_VECTOR_TAG: { + int postHeaderSize = PostHeader.Size; + uint length = *(uint*)(objDesc.objectBase + postHeaderSize); + size = ObjectLayout.ArraySize(objDesc.vtable, length); + int preHeaderSize = PreHeader.Size; + UIntPtr *elementAddress = (UIntPtr *) + (objDesc.objectBase + objDesc.vtable.baseLength - + preHeaderSize); + for (uint i = 0; i < length; i++, elementAddress++) { + this.Filter(elementAddress, ref objDesc); + } + break; + } + case ObjectLayout.OTHER_VECTOR_TAG: { + int postHeaderSize = PostHeader.Size; + uint length = *(uint*)(objDesc.objectBase + postHeaderSize); + size = ObjectLayout.ArraySize(objDesc.vtable, length); + if (objDesc.vtable.arrayOf == StructuralType.Struct) { + // pretend the struct is boxed and account for the + // presence of the vtable field + VTable elementVTable = objDesc.vtable.arrayElementClass; + UIntPtr elementMask = elementVTable.pointerTrackingMask; + // A structure with no references will have a SPARSE + // descriptor with no offset values. + if (elementMask != (UIntPtr) ObjectLayout.SPARSE_TAG) { + int preHeaderSize = PreHeader.Size; + UIntPtr elementAddress = (objDesc.objectBase + + objDesc.vtable.baseLength - + preHeaderSize - + postHeaderSize); + int elementSize = objDesc.vtable.arrayElementSize; + objDesc.vtable = elementVTable; + for (uint i = 0; i < length; i++) { + objDesc.objectBase = elementAddress; + this.VisitReferenceFieldsTemplateNoInline(ref objDesc); + elementAddress += elementSize; + } + } + } + break; + } + case ObjectLayout.PTR_ARRAY_TAG: { + int postHeaderSize = PostHeader.Size; + uint length = *(uint*)(objDesc.objectBase + postHeaderSize + + sizeof(uint)); + size = ObjectLayout.ArraySize(objDesc.vtable, length); + int preHeaderSize = PreHeader.Size; + UIntPtr *elementAddress = (UIntPtr *) + (objDesc.objectBase + objDesc.vtable.baseLength - + preHeaderSize); + for (uint i = 0; i < length; i++, elementAddress++) { + this.Filter(elementAddress, ref objDesc); + } + break; + } + case ObjectLayout.OTHER_ARRAY_TAG: { + int postHeaderSize = PostHeader.Size; + uint length = *(uint*)(objDesc.objectBase + postHeaderSize + + sizeof(uint)); + size = ObjectLayout.ArraySize(objDesc.vtable, length); + if (objDesc.vtable.arrayOf == StructuralType.Struct) { + // pretend the struct is boxed and account for the + // presence of the PostHeader + VTable elementVTable = objDesc.vtable.arrayElementClass; + UIntPtr elementMask = elementVTable.pointerTrackingMask; + // A structure with no references will have a SPARSE + // descriptor with no offset values. + if (elementMask != (UIntPtr) ObjectLayout.SPARSE_TAG) { + int preHeaderSize = PreHeader.Size; + int elementSize = objDesc.vtable.arrayElementSize; + UIntPtr elementAddress = + objDesc.objectBase + objDesc.vtable.baseLength - + preHeaderSize - postHeaderSize; + objDesc.vtable = elementVTable; + for (uint i = 0; i < length; i++) { + objDesc.objectBase = elementAddress; + this.VisitReferenceFieldsTemplateNoInline(ref objDesc); + elementAddress += elementSize; + } + } + } + break; + } + case ObjectLayout.STRING_TAG: { + int postHeaderSize = PostHeader.Size; + uint arrayLength = + *(uint*)(objDesc.objectBase + postHeaderSize); + size = ObjectLayout.StringSize(objDesc.vtable, arrayLength); + break; + } + default: { + // escape case + VTable.Assert((objectTag & 0x1) == 0, + "ReferenceVisitor: (objectTag & 0x1) == 0"); + UIntPtr *largeObject = (UIntPtr *) objDesc.objectBase; + size = ObjectLayout.ObjectSize(objDesc.vtable); + int *pointerDescription = (int *) pointerTracking; + int count = *pointerDescription; + for (int i = 1; i <= count; i++) { + UIntPtr *loc = largeObject + *(pointerDescription+i); + this.Filter(loc, ref objDesc); + } + break; + } + } + return size; + } + + // Rationale for cutting the fact that no stack probes are permitted in this + // tree of calls: + // + // There is no rationale. This is a bug which has been filed. It is a temporary + // hack to break the recursion in this method. Recursion needs to open up an + // opportunity for stack probing. Eventually, perhaps this method can be + // rewritten to avoid the recursion. + // + // Bug 436 + [NoStackLinkCheckTransCut] + [ManualRefCounts] + protected unsafe + void VisitReferenceFieldsTemplate(ref ObjectDescriptor objDesc, + int count) + { + UIntPtr pointerTracking = objDesc.vtable.pointerTrackingMask; + uint objectTag = (pointerTracking & 0xf); + switch (objectTag) { + case ObjectLayout.PTR_VECTOR_TAG: + case ObjectLayout.PTR_ARRAY_TAG: { + UIntPtr *elementAddress = (UIntPtr *) objDesc.objectBase; + for (int i = 0; i < count; i++, elementAddress++) { + this.Filter(elementAddress, ref objDesc); + } + break; + } + case ObjectLayout.OTHER_VECTOR_TAG: + case ObjectLayout.OTHER_ARRAY_TAG: { + if (objDesc.vtable.arrayOf == StructuralType.Struct) { + // pretend the struct is boxed and account for the + // presence of the vtable field + VTable elementVTable = objDesc.vtable.arrayElementClass; + UIntPtr elementMask = elementVTable.pointerTrackingMask; + // A structure with no references will have a SPARSE + // descriptor with no offset values. + if (elementMask != (UIntPtr) ObjectLayout.SPARSE_TAG) { + int postHeaderSize = PostHeader.Size; + objDesc.objectBase -= postHeaderSize; + objDesc.secondBase -= postHeaderSize; + objDesc.extra += postHeaderSize; + int elementSize = objDesc.vtable.arrayElementSize; + objDesc.vtable = elementVTable; + for (int i = 0; i < count; i++) { + this.VisitReferenceFieldsTemplateNoInline(ref objDesc); + objDesc.objectBase += elementSize; + objDesc.secondBase += elementSize; + objDesc.extra -= elementSize; + } + objDesc.objectBase += postHeaderSize; + objDesc.secondBase += postHeaderSize; + objDesc.extra -= postHeaderSize; + } + } + break; + } + default: { + throw new Exception("Indexing non-indexed type"); + } + } + } + + } + + internal abstract class DirectReferenceVisitor : ReferenceVisitor + { + + [Inline] + internal abstract unsafe void Visit(UIntPtr *location); + + } + + // This visitor should be used in concurrent settings. + internal abstract class MutableReferenceVisitor : DirectReferenceVisitor + { + +#region HELP_DEVIRT + // TODO: Evaluate the performance impact of these [Inline] annotations, + // which appeared as part of the CoCo code dump. + + // This method simply forces the compiler to generate a copy + // of VisitReferenceFieldsTemplate in this class. + [Inline] + [ManualRefCounts] + internal override UIntPtr VisitReferenceFields(Object obj) + { + return this.VisitReferenceFields(Magic.addressOf(obj), + obj.vtable); + } + + // This method simply forces the compiler to generate a copy + // of VisitReferenceFieldsTemplate in this class. + [Inline] + [ManualRefCounts] + internal override UIntPtr VisitReferenceFields(UIntPtr objectBase, + VTable vtable) + { + ObjectDescriptor objDesc = + new ObjectDescriptor(vtable, objectBase); + return VisitReferenceFieldsTemplate(ref objDesc); + } +#endregion + + [Inline] + protected override unsafe + void Filter(UIntPtr *location, ref ObjectDescriptor objDesc) + { + this.Visit(location); + } + + } + + // This visitor should only be used in situations where the + // reference fields are known to not be modified by other threads + // while the visitor is traversing the object. + internal abstract class NonNullReferenceVisitor : DirectReferenceVisitor + { + +#region HELP_DEVIRT + // This method simply forces the compiler to generate a copy + // of VisitReferenceFieldsTemplate in this class. + [ManualRefCounts] + [Inline] + internal override UIntPtr VisitReferenceFields(Object obj) + { + return this.VisitReferenceFields(Magic.addressOf(obj), + obj.vtable); + } + + // This method simply forces the compiler to generate a copy + // of VisitReferenceFieldsTemplate in this class. + [ManualRefCounts] + [Inline] + internal override UIntPtr VisitReferenceFields(UIntPtr objectBase, + VTable vtable) + { + ObjectDescriptor objDesc = + new ObjectDescriptor(vtable, objectBase); + return VisitReferenceFieldsTemplate(ref objDesc); + } +#endregion + + [Inline] + protected override unsafe + void Filter(UIntPtr *location, ref ObjectDescriptor objDesc) + { + if (*location != UIntPtr.Zero) { + this.Visit(location); + } + } + + } + + internal abstract class OffsetReferenceVisitor : ReferenceVisitor + { + + internal abstract void FieldOffset(UIntPtr offset, + ref ObjectDescriptor objDesc); + +#region HELP_DEVIRT + // This method simply forces the compiler to generate a copy + // of VisitReferenceFieldsTemplate in this class. + [ManualRefCounts] + internal override sealed + UIntPtr VisitReferenceFields(UIntPtr objectBase, VTable vtable) + { + ObjectDescriptor objDesc = + new ObjectDescriptor(vtable, objectBase); + return VisitReferenceFieldsTemplate(ref objDesc); + } +#endregion + + [Inline] + protected override unsafe sealed + void Filter(UIntPtr *location, ref ObjectDescriptor objDesc) + { + UIntPtr offset = ((UIntPtr) location) - objDesc.objectBase; + this.FieldOffset(offset, ref objDesc); + } + + } + +} diff --git a/base/Kernel/Bartok/GCs/RememberedSet.cs b/base/Imported/Bartok/runtime/shared/GCs/RememberedSet.cs similarity index 93% rename from base/Kernel/Bartok/GCs/RememberedSet.cs rename to base/Imported/Bartok/runtime/shared/GCs/RememberedSet.cs index 46d5484..f19e21e 100644 --- a/base/Kernel/Bartok/GCs/RememberedSet.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/RememberedSet.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,10 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - namespace System.GCs { diff --git a/base/Imported/Bartok/runtime/shared/GCs/SegregatedFreeList.cs b/base/Imported/Bartok/runtime/shared/GCs/SegregatedFreeList.cs new file mode 100644 index 0000000..747af9c --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/SegregatedFreeList.cs @@ -0,0 +1,1926 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using Microsoft.Bartok.Options; + using Microsoft.Bartok.Runtime; + + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using System.Threading; + +#if SINGULARITY + using Microsoft.Singularity; +#endif + + /// + /// This class supports Allocate/Free operations using memory obtained + /// from the PageManager. + /// + /// Objects that are small enough that multiple instances of them can fit + /// on a page are allocated from pages of identically sized memory cells. + /// + /// Large objects are allocated on separate pages. + /// + /// This class is designed to be thread safe on both allocation and free + /// operations. At this stage it is required that RecycleGlobalPages is + /// called periodically to reconsider previously full pages for inclusion + /// in allocation lists. + /// + /// The free operation is currently more expensive than required due to + /// space accounting. + /// + /// This class also keeps up to date memory accounting information. Note + /// that modifications to this data is not synchronized so it is possible + /// that the counts drift from actual figures over time. + /// + internal unsafe struct SegregatedFreeList /* : Allocator */ + { + + #region Mixins + + [MixinConditional("SegregatedFreeList")] + [MixinConditional("AllThreadMixins")] + [Mixin(typeof(Thread))] + // Should be private, but mixin implementation will warn if visibility + // does not match that of Thread. + public sealed class SegregatedFreeListThread : Object { + // Thread-specific segregated free-list allocator + [RequiredByBartok] + internal SegregatedFreeList segregatedFreeList; + } + + [Inline] + private static SegregatedFreeListThread MixinThread(Thread t) + { + return (SegregatedFreeListThread) (Object) t; + } + + #endregion + + #region Global (Safe from the context of any thread) + + #region Constants + + /// + /// Pages that are the process of being initialized, either as + /// a page of small chunks or for a large object. + /// + internal const PageType INIT_PAGE = PageType.Owner1; + + /// + /// PageType for pages containing small objects. + /// + internal const PageType SMALL_OBJ_PAGE = PageType.Owner2; + + /// + /// For any large object, the first page is marked + /// LARGE_OBJ_START, and subsequent pages marked + /// LARGE_OBJ_TAIL. + /// + internal const PageType LARGE_OBJ_START = PageType.Owner3; + internal const PageType LARGE_OBJ_TAIL = PageType.Owner4; + + /// + /// BUGBUG: Duplicated constant. + /// + private const int LOG_PAGE_SIZE = 12; + + /// + /// The size of each block (which contains a single object size) + /// + internal static int BLOCK_SIZE { + get { return (1 << LOG_PAGE_SIZE) - PageHeader.Size; } + } + + /// + /// The threshold where objects become large objects. + /// + internal static UIntPtr LARGE_OBJECT_THRESHOLD { + get { return (UIntPtr) unchecked((uint) (BLOCK_SIZE >> 1) + 1); } + } + + /// + /// The largest possible size class. + /// + internal static uint SIZE_CLASSES { + get { + return 33 + unchecked((uint)((LARGE_OBJECT_THRESHOLD-1) >> 7)); + } + } + + internal static bool EagerMemClear { + get { + return true; + } + } + + internal static bool LazyMemClear { + get { + return false; + } + } + + [Inline] + private static void DoLazyMemClear(UIntPtr regionPtr, + uint sizeClass) + { + if (LazyMemClear) { + Util.MemClear(regionPtr-PreHeader.Size, + (UIntPtr) GetCellSize(sizeClass)); + } + } + + internal static bool fVerifyLists { + get { + return false; + } + } + + #endregion + + #region Size Class Mappings + /// + /// Returns the size class index for an object of the specified size. + /// + /// BUGBUG: We hope that from an inlined allocation site this is + /// resolved as a constant. That is why this was changed to remove + /// indirection through an index lookup table. + /// + [Inline] + private static uint GetSizeClass(UIntPtr bytes) { + // Exact request rounds down to lower size class. + bytes = bytes - 1; + // Sizes 0 -> 64 are in classes 0 -> 15 + // Sizes 72 -> 128 are in classes 16 -> 23 + // Sizes 140 -> 256 are in classes 24 -> 31 + // Sizes 256 -> 512 are in classes 32 -> 35 + // We skip a power of 2 because large objects are rare. + //Sizes 512 and up are in classes 36 -> 41 + if (bytes < 64) return 0 + ((uint)bytes >> 2); + if (bytes < 128) return 8 + ((uint)bytes >> 3); + if (bytes < 256) return 16 + ((uint)bytes >> 4); + if (bytes < 512) return 28 + ((uint)bytes >> 6); + if (bytes < LARGE_OBJECT_THRESHOLD) return 32 + ((uint)bytes >> 7); + VTable.DebugPrint("GetSizeClass called on large object size"); + VTable.DebugBreak(); + return 0; + } + + /// + /// Returns the cell size for a given size class. + /// + private static uint GetCellSize(uint sizeClass) { + // REVIEW: This should be made into a table lookup! + VTable.Assert(sizeClass < SIZE_CLASSES, + "Attempt cellSize for invalid sizeClass"); + + uint sc = sizeClass + 1; + + uint bytes; + if (sc <= 16) bytes = (sc - 0) << 2; + else if (sc <= 24) bytes = (sc - 8) << 3; + else if (sc <= 32) bytes = (sc - 16) << 4; + else if (sc <= 36) bytes = (sc - 28) << 6; + else bytes = (sc - 32) << 7; + + return bytes; + } + #endregion + + #region Memory Accounting + /// + /// A count of the bytes allocated for small objects. + /// + internal static UIntPtr SmallBytes; + + /// + /// A count of the bytes for objects the process of being freed. + /// + internal static UIntPtr SmallFreedBytes; + + /// + /// A count of the small pages in the process of being freed. + /// + internal static UIntPtr SmallFreedPages; + + /// + /// A count of the large pages in the process of being freed. + /// + internal static UIntPtr LargeFreedPages; + + /// + /// A count of the bytes allocated for large objects. + /// + internal static UIntPtr LargeBytes { + get { + return PageTable.RegionSize(LargePages); + } + } + + /// + /// The number of pages reserved for large objects. + /// + internal static UIntPtr LargePages; + + /// + /// The number of pages reserved for small objects. + /// + internal static UIntPtr SmallPages; + + /// + /// A partial free page visitor that does nothing. + /// + internal static NullPartialFreePageVisitor nullPartialFreePageVisitor; + + /// + /// This is the total size of data (including object headers) + /// + internal static UIntPtr TotalBytes { + get { + return SmallBytes + LargeBytes; + } + } + + internal static UIntPtr TotalPages { + get { + return SmallPages + LargePages; + } + } + + internal static UIntPtr AllocatedPages { + get { + return (SmallPages-SmallFreedPages) + + (LargePages-LargeFreedPages); + } + } + + /// + /// The number of pages managed by the alloc heap, including space + /// occupied by empty cells. + /// + internal static UIntPtr ReservedBytes { + get { + return PageTable.RegionSize(SmallPages) + LargeBytes; + } + } + + /// + /// Increment the counter of the number of small bytes allocated. + /// + [Inline] + private static void AddSmallBytes(UIntPtr newBytes) { + SmallBytes += newBytes; + BaseCollector.IncrementNewBytesSinceGC(newBytes); + } + + /// + /// Increment the counter of the number of large bytes allocated. + /// + [Inline] + private static void AddLargePages(UIntPtr pageCount) { + LargePages += pageCount; + UIntPtr regionSize = PageTable.RegionSize(pageCount); + BaseCollector.IncrementNewBytesSinceGC(regionSize); + } + + /// + /// Decrement the counter of the number of small bytes allocated. + /// + [Inline] + private static void SubSmallBytes(UIntPtr newBytes) { + SmallFreedBytes += newBytes; + } + + /// + /// Decrement the counter of the number of large pages allocated. + /// + [Inline] + private static void SubLargePages(UIntPtr pageCount) { + LargeFreedPages += pageCount; + } + + /// + /// Increment the counter of the number of small pages allocated. + /// + [Inline] + private static void AddSmallPage(PageHeader *page) + { + SmallPages += (UIntPtr) 1; + UIntPtr payloadSize = + (UIntPtr)page->cellSize * (UIntPtr) page->freeCount; + AddSmallBytes(PageTable.PageSize - payloadSize); + } + + /// + /// Decrement the counter of the number of small pages allocated. + /// + [Inline] + private static void SubSmallPage(PageHeader *page) + { + SmallFreedPages += (UIntPtr) 1; + UIntPtr payloadSize = + (UIntPtr)page->cellSize * (UIntPtr) page->freeCount; + SubSmallBytes(PageTable.PageSize - payloadSize); + } + + /// + /// Subtract and zero the freed data counts. + /// + internal static void CommitFreedData() { + SmallPages -= SmallFreedPages; + SmallFreedPages = UIntPtr.Zero; + LargePages -= LargeFreedPages; + LargeFreedPages = UIntPtr.Zero; + SmallBytes -= SmallFreedBytes; + SmallFreedBytes = UIntPtr.Zero; + } + #endregion + + /// + /// When notified of the creation of a new thread we initialize the + /// alloc heap in that thread. + /// + internal unsafe static void NewThreadNotification(Thread newThread, + bool initial) + { + SegregatedFreeListThread mixinThread = MixinThread(newThread); + if (initial) { + // Initialise the initial thread. + mixinThread.segregatedFreeList.localPages = (PageHeader*[]) + BootstrapMemory.Allocate(typeof(PageHeader*[]), + SIZE_CLASSES); + mixinThread.segregatedFreeList.freeList = (UIntPtr[]) + BootstrapMemory.Allocate(typeof(UIntPtr[]), SIZE_CLASSES); + } else { + // We have to create the thread-specific array of pages. + mixinThread.segregatedFreeList.freeList = + new UIntPtr[SIZE_CLASSES]; + mixinThread.segregatedFreeList.localPages = + new PageHeader*[SIZE_CLASSES]; + } + } + + /// + /// A thread has finished, so we release any local pages. + /// + internal static void DeadThreadNotification(Thread deadThread) + { + SegregatedFreeListThread mixinThread = MixinThread(deadThread); + for (uint i = 0; i < SIZE_CLASSES; i++) { + if (mixinThread.segregatedFreeList.localPages[i] != null) { + mixinThread.segregatedFreeList.ReleaseLocalPage(i); + } + } + } + + /// + /// Allocate a large object. Large objects don't share pages with + /// any other objects. Get memory for the object directly from the + /// PageManager. + /// + [ManualRefCounts] + private static unsafe UIntPtr AllocateLarge(uint alignment, + UIntPtr bytes, + Thread currentThread) + { + UIntPtr pageCount = PageTable.PageCount(PageTable.PagePad(bytes)); + bool fCleanPages = true; + UIntPtr page = PageManager.EnsurePages(currentThread, + pageCount, INIT_PAGE, + ref fCleanPages); + + UIntPtr regionSize = PageTable.RegionSize(pageCount); + AddLargePages(pageCount); + + int unusedBytes = (int) (regionSize - bytes); + int unusedCacheLines = unusedBytes >> 5; + int offset; + if (unusedCacheLines != 0) { + offset = (largeOffset % unusedCacheLines) << 5; + largeOffset = + (largeOffset + 1) & ((int)PageTable.PageSize - 1); + } else { + offset = 0; + } + UIntPtr pageAddr = PageTable.PageAddr(page); + UIntPtr startAddr = pageAddr + offset + PreHeader.Size; + UIntPtr resultAddr = + Allocator.AlignedObjectPtr(startAddr, alignment); + uint shortOff = (uint) (resultAddr - pageAddr); + VTable.Assert((short) shortOff > 0, "offset not positive"); + PageTable.SetExtra(page, shortOff); + // Ready to be visited + PageTable.SetType(page, LARGE_OBJ_START); + if (pageCount > 1) { + PageTable.SetType(page+1, pageCount-1, LARGE_OBJ_TAIL); + } + return resultAddr; + } + + /// + /// Free the specified object. For large objects the page becomes + /// immediately available. For small objects it may require a call + /// to RecycleGlobalPages. + /// + [ManualRefCounts] + internal static void Free(Object obj) { + UIntPtr objectStart = Magic.addressOf(obj) - PreHeader.Size; + UIntPtr page = PageTable.Page(objectStart); + PageType pageType = PageTable.Type(page); + // Free the object + if (pageType == SMALL_OBJ_PAGE) { + uint alignment = obj.vtable.baseAlignment; + FreeSmall(objectStart, alignment); + } else { + VTable.Assert(pageType == LARGE_OBJ_START, + "Found GC Page not small or large"); + FreeLarge(obj); + } + } + + /// + /// Free a small object. + /// + [ManualRefCounts] + internal static void FreeSmall(UIntPtr objectStart, + uint alignment) { + PageHeader *pageHeader; + UIntPtr cellStart = + freeCell(objectStart, alignment, out pageHeader); + // Put the object memory cell on the freelist for the page + UIntPtr oldFreeList; + do { + oldFreeList = pageHeader->freeList; + *(UIntPtr *)cellStart = oldFreeList; + } while (Interlocked.CompareExchange(ref pageHeader->freeList, + cellStart, oldFreeList) != + oldFreeList); + Interlocked.Increment(ref pageHeader->freeCount); + } + + [ManualRefCounts] + internal static void LocalFreeSmall(UIntPtr objectStart, + uint alignment) { + PageHeader *pageHeader; + UIntPtr cell = + freeCell(objectStart, alignment, out pageHeader); + // Put the object memory cell on the freelist for the page + *(UIntPtr *)cell = pageHeader->freeList; + pageHeader->freeList = cell; + pageHeader->freeCount++; + } + + [Inline] + [ManualRefCounts] + private static UIntPtr freeCell(UIntPtr objectStart, + uint alignment, + out PageHeader* pageHeader) { + UIntPtr pageAddr = PageTable.PageAlign(objectStart); + pageHeader = (PageHeader*) pageAddr; + UIntPtr cellStart; + if (alignment > UIntPtr.Size) { + cellStart = FindSmallCell(objectStart); + // REVIEW: Cast to 'int' is needed in Singularity because + // Singularity defines implicit conversions from uint and ulong + // (?) causing an ambiguous call to UIntPtr.operator + from + VTable.Assert(cellStart > (UIntPtr) pageHeader && + cellStart <= objectStart && + cellStart+(int)pageHeader->cellSize >= + objectStart+PreHeader.Size, + "find small cell found invalid start"); + } else { + cellStart = objectStart; + } + if (EagerMemClear) { + Util.MemClear(cellStart, (UIntPtr) pageHeader->cellSize); + } + SubSmallBytes((UIntPtr) pageHeader->cellSize); + + return cellStart + PreHeader.Size; + } + + internal unsafe struct TempList { + + private UIntPtr next; + + internal void Add(UIntPtr memAddr) { + *(UIntPtr *) memAddr = this.next; + this.next = memAddr; + } + + internal UIntPtr GetList() { + UIntPtr result = this.next; + this.next = UIntPtr.Zero; + return result; + } + + } + + [ManualRefCounts] + internal static void FreeSmallList(ref TempList tempList) + { + UIntPtr cell = tempList.GetList(); + if (cell != UIntPtr.Zero) { + PageHeader *pageHeader = (PageHeader *) + PageTable.PageAlign(cell); + UIntPtr newChain = UIntPtr.Zero; + UIntPtr newChainTail = cell + PreHeader.Size; + UIntPtr cellSize = (UIntPtr) pageHeader->cellSize; + int count = 0; + do { + count++; + UIntPtr next = *(UIntPtr *) cell; + if (EagerMemClear) { + Util.MemClear(cell, cellSize); + } + UIntPtr cellPtrAddr = cell + PreHeader.Size; + *(UIntPtr *) cellPtrAddr = newChain; + newChain = cellPtrAddr; + cell = next; + } while (cell != UIntPtr.Zero); + SubSmallBytes((UIntPtr) count * cellSize); + UIntPtr oldFreeList; + do { + oldFreeList = pageHeader->freeList; + *(UIntPtr*)newChainTail = oldFreeList; + } while (Interlocked.CompareExchange(ref pageHeader->freeList, + newChain, oldFreeList) != + oldFreeList); + AtomicAdd(ref pageHeader->freeCount, count); + } + } + + /// + /// Free a large object. + /// + [ManualRefCounts] + internal static void FreeLarge(Object obj) { + UIntPtr objectStart = Magic.addressOf(obj) - PreHeader.Size; + UIntPtr firstPage = PageTable.Page(objectStart); + // Release the page(s) that the object resides on + UIntPtr pageAddr = PageTable.PageAlign(objectStart); + UIntPtr objectSize = ObjectLayout.Sizeof(obj); + UIntPtr limitPage = + PageTable.Page(PageTable.PagePad(objectStart + objectSize)); + UIntPtr pageCount = limitPage - firstPage; + PageTable.SetType(firstPage, pageCount, INIT_PAGE); + PageTable.SetExtra(PageTable.Page(pageAddr), 0); + PageManager.ReleaseUnusedPages(firstPage, pageCount, false); + SubLargePages(pageCount); + } + + /// + /// Determines whether a given address could be an interior pointer + /// into an object. If the function returns true, FindObjectAddr + /// must be able to find the object containing the given address if + /// such an object exists. If the given address is outside the + /// memory area for any object, it is possible for the function to + /// return true, in which case FindObjectAddr is expected to + /// be able to find a nearby object. + /// Note: this method assumes that a pointer to the tail of one + /// object and the head of another is really a pointer to the tail + /// of the former. In order to use method for pointers into the + /// PreHeader, add PreHeader.Size to the argument. + /// + internal static unsafe bool IsGcPtr(UIntPtr addr) + { + // For an array, a pointer just past the last element in + // the array is considered to be an interior pointer into + // the array. Given our chosen object layout, the address + // of the first field in the PreHeader can be identical to + // the address of the location just past the last element + // of an adjacently allocated array. In effect, the address + // of the first field of an object's PreHeader cannot be + // considered an interior pointer into the object. + // This function makes the assumption that it will not + // be asked about addresses of elements in an object's + // PreHeader. Consequently, it can subtract the size of the + // PreHeader from the given address and then determine if the + // resulting address is in a memory area that could have been + // allocated for an object. The subtraction also conveniently + // solves the problem of addresses past the last element of an + // array falling outside the areas of memory that may have + // been allocated for an object. + // For small pages (those containing a bunch of cells of + // a given size), only the memory reserved for the PageHeader + // is outside the memory that we assume could be allocated to + // an object. For large pages (those containing large objects), + // the initial offset is excluded. + if (addr==UIntPtr.Zero) { + return false; + } + UIntPtr baseAddr = addr - PreHeader.Size; + UIntPtr page = PageTable.Page(baseAddr); + if (page>=PageTable.pageTableCount) { + return false; + } + UIntPtr pageAddr = PageTable.PageAlign(baseAddr); + PageType pageType = PageTable.Type(page); + switch (pageType) { + case SMALL_OBJ_PAGE: { + return baseAddr-pageAddr >= (UIntPtr)PageHeader.Size; + } + case LARGE_OBJ_START: { + uint brickData = PageTable.Extra(page); + return (baseAddr+PreHeader.Size-pageAddr >= + (UIntPtr) brickData); + } + case LARGE_OBJ_TAIL: { + // It is too expensive to find the end of the + // object, so we will simply assume that the pointer + // is inside the object area. + return true; + } + default: { + return false; + } + } + } + + /// + /// Given a possibly interior pointer to an object, return the + /// real address of the object. Note that this assumes that + /// a pointer to the tail of one object and the head of another + /// is really a pointer to the tail of the former. In order + /// to use this for identifying pointers to PreHeader fields, + /// add PreHeader.Size to the argument. + /// + internal static unsafe UIntPtr Find(UIntPtr addr) + { + addr -= PreHeader.Size; + UIntPtr page = PageTable.Page(addr); + PageType pageType = PageTable.Type(page); + VTable.Assert(PageTable.IsGcPage(pageType), + "Find called on non-GC page"); + if (pageType == SMALL_OBJ_PAGE) { + return FindSmall(addr); + } else { + return FindLarge(addr); + } + } + + /// + /// Find a small object (after determining it is on a small page) + /// + private static UIntPtr FindSmall(UIntPtr cellAddr) { + UIntPtr objectAddr = FindSmallCell(cellAddr) + PreHeader.Size; + objectAddr = Allocator.SkipAlignment(objectAddr); + return objectAddr; + } + + /// + /// Find the cell for a given object address + /// + private static unsafe UIntPtr FindSmallCell(UIntPtr objectStart) { + UIntPtr pageAddr = PageTable.PageAlign(objectStart); + VTable.Assert(objectStart-pageAddr >= (UIntPtr)PageHeader.Size, + "FindSmallCell went back into the page header"); + PageHeader *pageHeader = (PageHeader *) pageAddr; + UIntPtr firstAddr = pageAddr + PageHeader.Size; + int cellSize = (int) pageHeader->cellSize; + return (objectStart - ((int)(objectStart - firstAddr) % cellSize)); + } + + /// + /// Find a large object (after determining it is on a large page) + /// + private static UIntPtr FindLarge(UIntPtr addr) { + UIntPtr page = PageTable.Page(addr); + while (PageTable.Type(page) == LARGE_OBJ_TAIL) { + page--; + } + VTable.Assert(PageTable.Type(page) == LARGE_OBJ_START); + uint brickData = PageTable.Extra(page); + return (PageTable.PageAddr(page) + brickData); + } + + internal abstract class ObjectVisitor : ObjectLayout.ObjectVisitor { + + internal virtual void VisitSmall(Object obj, UIntPtr memAddr) { + this.Visit(obj); + } + + internal virtual void VisitSmallPageEnd() { } + + internal virtual UIntPtr VisitLarge(Object obj) { + return this.Visit(obj); + } + + internal override UIntPtr Visit(Object obj) { + VTable.NotReached("Someone forgot an override method in a "+ + "subclass of SegregatedFreeList.ObjectVisitor"); + return UIntPtr.Zero; + } + + internal virtual bool Continue { + get { + return true; + } + } + + } + + // Wraps a SegregatedFreeList.ObjectVisitor around a + // ObjectLayout.ObjectVisitor. + // Both large and small objects are visited by the same + // ObjectLayout.ObjectVisitor. + internal class ObjectVisitorWrapper : ObjectVisitor { + + private ObjectLayout.ObjectVisitor visitor; + + internal ObjectVisitorWrapper(ObjectLayout.ObjectVisitor visitor) { + this.visitor = visitor; + } + + internal override void VisitSmall(Object obj, UIntPtr memAddr) { + this.visitor.Visit(obj); + } + + internal override UIntPtr VisitLarge(Object obj) { + return this.visitor.Visit(obj); + } + + } + + /// + /// Visit each object in the heap across all pages. + /// + [ManualRefCounts] + [Inline] + internal static void VisitAllObjects(ObjectVisitor visitor) + { + VisitObjects(UIntPtr.Zero, PageTable.pageTableCount, visitor); + } + + /// + /// Visit each object in the heap across a range of pages. + /// + /// This can be run concurrent to allocations, but not frees. + /// + [ManualRefCounts] + [Inline] + internal static void VisitObjects(UIntPtr lowPage, + UIntPtr highPage, + ObjectVisitor visitor) + { + for (UIntPtr i = lowPage; + i < highPage && visitor.Continue; + i++) { + PageType pageType = PageTable.MyType(i); + switch (pageType) { + case INIT_PAGE: { + // The page is either not yet ready for + // allocating objects or it contains an object + // that is not necessarily fully initialized, so + // we can't visit any objects on the page. + break; + } + case SMALL_OBJ_PAGE: { + VisitSmallObjects(i, visitor); + break; + } + case LARGE_OBJ_START: { + UIntPtr largeObjectPageCount = + VisitLargeObject(i, visitor); + i += largeObjectPageCount - 1; + break; + } + case LARGE_OBJ_TAIL: { + // To get here, another thread must have been + // concurrently updating the page table or + // initializing a large object. The other + // thread could be in the process of converting + // from INIT_PAGE or reclaiming the pages. + // Alternatively, the large object at a + // preceding LARGE_OBJ_START page may not have + // been fully initialized (e.g. length of an + // array is missing), but in that case the size + // estimate will always err on the small size. + // In any case, there are no objects to visit on + // this page. + break; + } + } + } + } + + [Inline] + internal static void VisitObjects(UIntPtr lowPage, + UIntPtr highPage, + ObjectLayout.ObjectVisitor visitor) + { + ObjectVisitor myObjectVisitor = visitor as ObjectVisitor; + VTable.Assert(myObjectVisitor != null, + "SegregatedFreeList requires specialized ObjectVisitor"); + VisitObjects(lowPage, highPage, myObjectVisitor); + } + + /// + /// Visit small objects in a single page. + /// + [ManualRefCounts] + [Inline] + private static unsafe void VisitSmallObjects(UIntPtr page, + ObjectVisitor visitor) + { + // The free cells on a small page are linked together in a + // linked list. The links are at the vtable offset in each cell. + // The possible values we can see (barring the presence of an + // alignment marker at the very beginning of the cell) is: + // (1) a link to another cell on the page, meaning that the cell + // is free, (2) a null value, meaning either that the cell is the + // last in the linked list of free cells or that it has just been + // unlinked from that list and the vtable value has not yet been + // written, or (3) a vtable pointer, meaning that the cell is + // populated with an object. + VTable.Assert(PageTable.Type(page) == SMALL_OBJ_PAGE, + "Visiting small objects on invalid page"); + UIntPtr pageAddr = PageTable.PageAddr(page); + PageHeader *pageHeader = (PageHeader *) pageAddr; + UIntPtr cellSize = (UIntPtr) pageHeader->cellSize; + VTable.Assert(cellSize != UIntPtr.Zero, + "zero cellSize visiting small"); + UIntPtr lowAddr = pageAddr + PageHeader.Size; + UIntPtr highAddr = PageTable.PagePad(lowAddr); + while (lowAddr <= highAddr - cellSize) { + UIntPtr objectAddr = lowAddr + PreHeader.Size; + UIntPtr linkPtr = *(UIntPtr *) objectAddr; + if (PageTable.IsAddrOnPage(linkPtr, page) && + !Allocator.IsAlignment(objectAddr)) { + // We have found a link to another cell on this page. + // If we had found an alignment token at lowAddr, then + // the value seen is not a link pointer. Instead, it + // must be a value from the PreHeader, most likely the + // MultiUseWord. The IsAlignment test is guarding + // against the possibility that the MultiUseWord value + // happens to be an address on this page. + } else if (linkPtr == UIntPtr.Zero && + !Allocator.IsAlignment(objectAddr)) { + // We have found the last unused cell in the list of + // unused cells on this page. + } else { + objectAddr = Allocator.SkipAlignment(objectAddr); + Object maybeObject = Magic.fromAddress(objectAddr); + UIntPtr vtablePtr = *maybeObject.VTableFieldAddr; + // We need to check for alignment tokens again after reading + // the vtablePtr value due to race conditions. + // BUGBUG: We really should put a memory fence here! + if (!PageTable.IsForeignAddr(vtablePtr) && + PageTable.IsNonGcPage(PageTable.Type(PageTable.Page(vtablePtr))) && + !Allocator.IsAlignment(objectAddr)) { + // We have found a slot that contains an object. + visitor.VisitSmall(maybeObject, lowAddr); + } else { + // It looks like we have found a cell that is in the + // process of being allocated. Either the linkPtr is + // zero/null, or someone is allocating an object that + // has non-standard alignment requirements. + if (!(linkPtr == UIntPtr.Zero || + Allocator.IsAlignmentMarkerAddr(lowAddr))) { + Util.PrintPageContents(page); + VTable.DebugPrint("Bad object at 0x{0:x}\n", + __arglist(linkPtr)); + VTable.NotReached(); + } + } + } + lowAddr += cellSize; + } + visitor.VisitSmallPageEnd(); + } + + /// + /// Visit a large object on the specified page. + /// + [ManualRefCounts] + private static UIntPtr VisitLargeObject(UIntPtr page, + ObjectVisitor visitor) + { + VTable.Assert(PageTable.Type(page) == LARGE_OBJ_START, + "Visiting large object on invalid page"); + // Find the object + UIntPtr pageAddr = PageTable.PageAddr(page); + uint brickData = PageTable.Extra(page); + if (brickData == 0) { + // Possibly in the process of being allocated. + return (UIntPtr) 1; + } + UIntPtr objectAddr = pageAddr + brickData; + Object obj = Magic.fromAddress(objectAddr); + if (obj.vtable == null) { + // Memory has been allocated, but object is not initialized + return (UIntPtr) 1; + } + // Visit the object + UIntPtr objectSize = visitor.VisitLarge(obj); + // Return the page count + UIntPtr objectEnd = objectAddr + objectSize - PreHeader.Size; + UIntPtr limitPage = PageTable.Page(PageTable.PagePad(objectEnd)); + return limitPage - page; + } + + /// + /// This is the the free list of pages to allocate from. + /// + private static UIntPtr[] globalFreePages; + + /// + /// This is the list of pages released by threads. These pages must + /// be periodically processes to release them back for allocation if + /// necessary. + /// + private static UIntPtr[] globalPages; + + // Used by RecycleGlobalPages to avoid the ABA problem of + // lock-free data structures. + private static PageHeader*[] futureGlobalFreePages; + + /// + /// Initialize the alloc heap by setting up the heads for all the + /// linked lists. + /// + [PreInitRefCounts] + internal static unsafe void Initialize() { + // Global array of allocated pages + globalPages = (UIntPtr[]) + BootstrapMemory.Allocate(typeof(UIntPtr[]), SIZE_CLASSES); + // Global array of pages with free elements + globalFreePages = (UIntPtr[]) + BootstrapMemory.Allocate(typeof(UIntPtr[]), SIZE_CLASSES); + // Temporary list holders used by RecycleGlobalPages + futureGlobalFreePages = (PageHeader*[]) + BootstrapMemory.Allocate(typeof(PageHeader*[]), SIZE_CLASSES); + nullPartialFreePageVisitor = (NullPartialFreePageVisitor) + BootstrapMemory.Allocate(typeof(NullPartialFreePageVisitor)); + } + + /// + /// Take all global pages that have had elements freed and put them in + /// the allocation queues. + /// + internal static void RecycleGlobalPages() { + RecycleGlobalPagesPhase1(); + RecycleGlobalPagesPhase2(); + } + + internal static void RecycleGlobalPagesPhase1() + { + RecycleGlobalPagesPhase1(nullPartialFreePageVisitor); + } + + internal static void + RecycleGlobalPagesPhase1(PartialFreePageVisitor pageVisitor) + { + for (uint i = 0; i < SIZE_CLASSES; i++) { + // Steal full chain, split into chains of full and + // non-full pages + PageHeader* globalFull = AtomicPopChain(ref globalPages[i]); + PageSeparation pageSeparation; + int cellSize = (int) GetCellSize(i); + if (SplitList(globalFull, cellSize, 0, pageVisitor, + out pageSeparation)) { + // Reinsert chain of full pages into full chain + if (!pageSeparation.fullList.IsEmpty) { + AtomicPushIncrementList(ref globalPages[i], + pageSeparation.fullList); + } + // Store away information about the future free chain. + VTable.Assert(futureGlobalFreePages[i] == null, + "Non-empty list of future free pages"); + PageHeaderList freeList = new PageHeaderList(); + freeList.Append(pageSeparation.partialList); + freeList.Append(pageSeparation.emptyList); + futureGlobalFreePages[i] = freeList.head; + } + } + } + + internal enum PartialPageAction { + CommitFree, + CommitFull, + Hold + } + + internal abstract class PartialFreePageVisitor + { + internal abstract void Start(); + internal abstract PartialPageAction VisitPage(UIntPtr page, + PageHeader *ph, + int cells); + internal abstract void ObserveEmptyPage(); + internal abstract void Finish(); + } + + internal class NullPartialFreePageVisitor : PartialFreePageVisitor + { + + internal override void Start() + { + } + + internal override PartialPageAction VisitPage(UIntPtr page, + PageHeader *ph, + int cells) + { + return PartialPageAction.CommitFree; + } + + internal override void ObserveEmptyPage() + { + } + + internal override void Finish() + { + } + + } + + internal static void RecycleGlobalPagesPhase2() + { + RecycleGlobalPagesPhase2(nullPartialFreePageVisitor); + } + + internal static + void RecycleGlobalPagesPhase2(PartialFreePageVisitor pageVisitor) + { + for (uint i = 0; i < SIZE_CLASSES; i++) { + PageHeader* futureFreeHead = futureGlobalFreePages[i]; + if (futureFreeHead != null) { + futureGlobalFreePages[i] = null; + // Swap the existing free chain with the chain of recycled + // non-full pages. + PageHeader* futureTail = null; + if (fVerifyLists) { + futureTail = VerifySimpleList(futureFreeHead); + } + PageHeader* oldFreeHead = (PageHeader *) + AtomicSwitchChains(ref globalFreePages[i], + futureFreeHead); + if (oldFreeHead == null) { + continue; + } + if (fVerifyLists) { + PageHeader* oldTail = VerifySimpleList(oldFreeHead); + VTable.Deny(futureTail == oldTail, + "Old and future free lists share a tail"); + } + PageSeparation pageSeparation; + int cellSize = (int) GetCellSize(i); + if (SplitList(oldFreeHead, cellSize, 2, pageVisitor, + out pageSeparation)) { + if (pageSeparation.emptyList.head == oldFreeHead) { + // We cannot put the first element of the old + // chain back on the globalFreePages list + // since that would invite the possibility of + // ABA problems. Put it on the globalPages + // list, since we know that doing so is safe. + PageHeader* removedHead = + pageSeparation.emptyList.RemoveHead(); + VTable.Assert(removedHead == oldFreeHead); + AtomicPush(ref globalPages[i], removedHead); + } else if (pageSeparation.partialList.head == + oldFreeHead) { + PageHeader* removedHead = + pageSeparation.partialList.RemoveHead(); + VTable.Assert(removedHead == oldFreeHead); + AtomicPush(ref globalPages[i], removedHead); + } else { + VTable.Assert(pageSeparation.fullList.head == + oldFreeHead); + } + PageHeaderList freeList = new PageHeaderList(); + freeList.Append(pageSeparation.partialList); + freeList.Append(pageSeparation.emptyList); + if (!freeList.IsEmpty) { + PageHeader *skippedGlobalFreeHead = + AtomicPushDecrementList(ref globalFreePages[i], + freeList); + if (skippedGlobalFreeHead != null) { + // Put the node removed from globalFreePages + // as part of the list merge onto the + // globalPage list, as that is always safe. + AtomicPush(ref globalPages[i], + skippedGlobalFreeHead); + } + } + // Reinsert chain of full pages into full chain + if (!pageSeparation.fullList.IsEmpty) { + AtomicPushIncrementList(ref globalPages[i], + pageSeparation.fullList); + } + } + } + } + } + + internal static void LocalRecycleGlobalPages() { + nullPartialFreePageVisitor.Start(); + for (uint i=0; i < SIZE_CLASSES; i++) { + PageHeader* globalFull = (PageHeader*) globalPages[i]; + globalPages[i] = UIntPtr.Zero; + + PageSeparation pageSeparation; + + int cellSize = (int)GetCellSize(i); + if (SplitList(globalFull, cellSize, 0, + nullPartialFreePageVisitor, + out pageSeparation)) { + // Reinsert values onto the free and full chains + if (!pageSeparation.fullList.IsEmpty) { + globalPages[i] = (UIntPtr) + pageSeparation.fullList.head; + } + PageHeaderList freeList = new PageHeaderList(); + freeList.Append(pageSeparation.partialList); + freeList.Append(pageSeparation.emptyList); + if (!freeList.IsEmpty) { + PageHeader* globalFree = + (PageHeader *) globalFreePages[i]; + globalFreePages[i] = (UIntPtr) freeList.head; + + if (SplitList(globalFree, cellSize, 1, + nullPartialFreePageVisitor, + out pageSeparation)) { + VTable.Assert(pageSeparation.fullList.IsEmpty, + "full free pages found"); + freeList = new PageHeaderList(); + freeList.Append(pageSeparation.partialList); + freeList.Append(pageSeparation.emptyList); + if (!freeList.IsEmpty) { + freeList.tail->nextPage = + (PageHeader *) globalFreePages[i]; + globalFreePages[i] = (UIntPtr) freeList.head; + } + } + } + } + } + nullPartialFreePageVisitor.Finish(); + } + + // reclamationRate: the fraction of completely free pages that + // should be returned to the PageManager. A value of 0 means + // that no pages are returned. A value of 5 means that 1/5 of + // the eligible pages are returned. + private static bool SplitList(PageHeader* inputChain, + int cellSize, + int reclamationRate, + PartialFreePageVisitor pageVisitor, + out PageSeparation pageSeparation) + { + pageSeparation = new PageSeparation(); + if (fVerifyLists) { + VerifySimpleList(inputChain); + } + // Start with free pages (they can not become full) + PageHeader* current = inputChain; + // Determine starting point + if (current == null) { + return false; + } + // Number of cells of this size class in a block + int cells = BLOCK_SIZE / cellSize; + VTable.Assert(cells > 0 && cells < (BLOCK_SIZE >> 1), + "invalid cell count"); + // Iterate through list + int reclamationCount = 0; + while (current != null) { + PageHeader *page = current; + current = page->nextPage; + if (page->freeCount == cells && + ++reclamationCount == reclamationRate) { + // Completely Free Page + reclamationCount = 0; + SubSmallPage(page); + UIntPtr pageNum = PageTable.Page((UIntPtr) page); + PageTable.SetType(pageNum, INIT_PAGE); + PageTable.SetExtra(pageNum, 0); + PageManager.ReleaseUnusedPages(pageNum, + (UIntPtr) 1, + false); + continue; + } + if (page->freeCount == cells) { + pageVisitor.ObserveEmptyPage(); + pageSeparation.emptyList.Append(page); + } else if (page->freeCount > 0) { + // Partially Free Page + PartialPageAction action = + pageVisitor.VisitPage(PageTable.Page((UIntPtr) page), + page, cells); + if (action == PartialPageAction.CommitFree) { + pageSeparation.partialList.Append(page); + } else if (action == PartialPageAction.CommitFull) { + pageSeparation.fullList.Append(page); + } else { + VTable.NotReached("CMS PartialPageAction.Hold"); + } + } else { + // Completely Full Page + pageSeparation.fullList.Append(page); + } + } + if (fVerifyLists) { + pageSeparation.emptyList.VerifyTailedList(); + pageSeparation.partialList.VerifyTailedList(); + pageSeparation.fullList.VerifyTailedList(); + } + return true; + } + + /// + /// Atomically add a value to a field. + /// + private static void AtomicAdd(ref int addr, int value) { + int oldValue, newValue; + do { + oldValue = addr; + newValue = oldValue + value; + } while (Interlocked.CompareExchange(ref addr, newValue, oldValue) + != oldValue); + } + + /// + /// Atomically push a value onto a linked list. The linked list + /// may be concurrently added to, but may not be concurrently + /// removed from. + /// + private static void AtomicPush(ref UIntPtr head, PageHeader *page) { + VTable.Assert(page->nextPage == null, + "Expected single page, found page list"); + UIntPtr oldHead; + do { + oldHead = head; + page->nextPage = (PageHeader*) oldHead; + } while (Interlocked.CompareExchange(ref head, (UIntPtr) page, + oldHead) != oldHead); + } + + /// + /// Atomically remove a value from the linked list. Returns null + /// if the list is empty. The list may be concurrently removed + /// from by means of AtomicPop and concurrently added to by means + /// of AtomicPushDecrementList. + /// + private static PageHeader * AtomicPop(ref UIntPtr head) { + PageHeader* oldHead; + PageHeader* newHead; + do { + oldHead = (PageHeader*) head; + if (oldHead == null) { + // Empty list. + return null; + } + newHead = oldHead->nextPage; + } while(Interlocked.CompareExchange(ref head, (UIntPtr) newHead, + (UIntPtr) oldHead) + != (UIntPtr) oldHead); + oldHead->nextPage = null; + return oldHead; + } + + /// + /// Steal an entire list. Atomic PopChain can be done concurrently + /// with any other atomic operation. The removal of an entire chain + /// does not by itself invite the ABA problem of lock-free algorithms. + /// + private static PageHeader* AtomicPopChain(ref UIntPtr head) { + return (PageHeader *) Interlocked.Exchange(ref head, UIntPtr.Zero); + } + + private static PageHeader* AtomicSwitchChains(ref UIntPtr head, + PageHeader *newHead) { + return (PageHeader*) Interlocked.Exchange(ref head, + (UIntPtr) newHead); + } + + /// + /// Push a whole chain onto a list. + /// The list may be concurrently added to by means to AtomicPush. + /// + private static void AtomicPushIncrementList(ref UIntPtr head, + PageHeaderList list) + { + if (fVerifyLists) { + PageHeader* tail = VerifySimpleList((PageHeader*)head); + list.VerifyTailedList(); + VTable.Deny(tail == list.tail, "Lists have shared tail"); + } + UIntPtr oldHead; + UIntPtr newHead = (UIntPtr) list.head; + do { + oldHead = head; + list.tail->nextPage = (PageHeader*) oldHead; + } while (Interlocked.CompareExchange(ref head, newHead, oldHead) + != oldHead); + } + + /// + /// Push a whole chain onto a list. + /// The list may be concurrently removed from by AtomicPop. + /// The removal is done along with removal of the first element of + /// the list, and the removed node is returned. The removal of + /// the first element of the list ensures the absence of an ABA + /// problem. AtomicPop and AtomicPushDecrementList are both + /// tolerant of the particular kind of ABA change that could occur + /// without removal of the first element of the list, but the + /// defensive mechanism is used anyway to ensure that potential + /// future problems are avoided. + /// + private static PageHeader* AtomicPushDecrementList(ref UIntPtr head, + PageHeaderList list) + { + if (fVerifyLists) { + list.VerifyTailedList(); + } + PageHeader* oldHead; + PageHeader* oldNext; + UIntPtr newHead = (UIntPtr) list.head; + do { + oldHead = (PageHeader *) head; + oldNext = (oldHead == null) ? null : oldHead->nextPage; + list.tail->nextPage = oldNext; + + } while (Interlocked.CompareExchange(ref head, newHead, + (UIntPtr) oldHead) + != (UIntPtr) oldHead); + if (oldHead != null) { + oldHead->nextPage = null; + } + return oldHead; + } + + internal static PageHeader* VerifySimpleList(PageHeader *head) + { + VTable.Assert(fVerifyLists); + if (head == null) { + return null; + } + PageHeader* probe = head->nextPage; + PageHeader* slow = head; + PageHeader* tail = head; + // Use the old fast+slow trick to catch circular lists! + while (true) { + if (probe == null) { + return tail; + } + VTable.Assert(probe != slow, "Circular page list!"); + tail = probe; + probe = tail->nextPage; + if (probe == null) { + return tail; + } + VTable.Assert(probe != slow, "Circular page list!"); + tail = probe; + probe = tail->nextPage; + slow = slow->nextPage; + } + } + + private struct PageSeparation { + internal PageHeaderList emptyList; + internal PageHeaderList partialList; + internal PageHeaderList fullList; + } + + private struct PageHeaderList { + + internal PageHeader *head; + internal PageHeader *tail; + + internal PageHeaderList(PageHeader *page) + { + this.head = page; + this.tail = page; + if (fVerifyLists) { + VTable.Assert(page->nextPage == null, + "Expected single page, got a list of them"); + this.VerifyTailedList(); + } + } + + internal bool IsEmpty + { + get { return this.head == null; } + } + + /// + /// Add a page onto a local linked list (possibly the first page) + /// + internal void Append(PageHeader *page) + { + page->nextPage = null; + if (this.head == null) { + this.head = page; + this.tail = page; + return; + } else { + VTable.Assert(this.tail->nextPage == null, + "Add expected a single page, got a list"); + } + this.tail->nextPage = page; + this.tail = page; + } + + internal void Append(PageHeaderList appendList) { + if (appendList.IsEmpty) { + // Do nothing + } else if (this.IsEmpty) { + this.head = appendList.head; + this.tail = appendList.tail; + } else { + this.tail->nextPage = appendList.head; + this.tail = appendList.tail; + } + } + + internal PageHeader* RemoveHead() { + PageHeader *oldHead = this.head; + this.head = this.head->nextPage; + oldHead->nextPage = null; + return oldHead; + } + + internal bool Contains(PageHeader *page) + { + PageHeader *cursor = this.head; + while (cursor != null) { + if (cursor == page) { + return true; + } + cursor = cursor->nextPage; + } + return false; + } + + internal void VerifyTailedList() + { + VTable.Assert(fVerifyLists); + if (this.head == null) { + VTable.Assert(this.tail == null, + "Null head and non-null tail"); + return; + } else { + VTable.Assert(this.tail != null, + "Non-null head and null tail"); + VTable.Assert(this.tail->nextPage == null, + "Tail->nextPage is non-null"); + } + PageHeader* foundTail = VerifySimpleList(this.head); + VTable.Assert(this.tail == foundTail, + "Lists are not disjoint"); + } + + } + + /// + /// This struct represents the header data stored in each + /// small object page. + /// + /// BUGBUG: Not space efficient. + /// + [StructLayout(LayoutKind.Sequential)] + internal struct PageHeader { + + internal static int Size { + get { return sizeof(PageHeader); } + } + + /// + /// The next page in the linked list. + /// + internal PageHeader* nextPage; + + /// + /// The head of the free list for this page. This is not + /// used when a page is assigned to a thread. + /// + internal UIntPtr freeList; + + /// + /// The cell size for objects in this page. + /// + internal ushort cellSize; + + /// + /// User value. + /// + // Note that this should be more than a user value. In + // particular, when CoCo marks a page for evacuation then + // the page should not be used for any subsequent allocation. + internal ushort userValue; + + /// + /// The number of cells that have been freed. This is used + /// for accounting purposes. + /// + internal int freeCount; + } + + #endregion + + #region Local (Safe from the context of owner thread) + /// + /// This is a thread's local free list for each size class. + /// + [RequiredByBartok] + private UIntPtr[] freeList; + + /// + /// This is a thread's local set of pages for each size class. + /// + private PageHeader*[] localPages; + + [ManualRefCounts] + internal static UIntPtr Allocate(Thread thread, + UIntPtr bytes, uint alignment) + { + SegregatedFreeListThread mixinThread = MixinThread(thread); + return mixinThread.segregatedFreeList.Allocate(bytes, alignment, + thread); + } + + [ManualRefCounts] + internal UIntPtr Allocate(UIntPtr bytes, uint alignment, Thread thread) + { + UIntPtr resultAddr = this.AllocateFast(bytes, alignment); + if (resultAddr == UIntPtr.Zero) { + resultAddr = this.AllocateSlow(bytes, alignment, thread); + } + return resultAddr; + } + + [Inline] + [ManualRefCounts] + internal static UIntPtr AllocateFast(Thread thread, + UIntPtr bytes, + uint alignment) + { + SegregatedFreeListThread mixinThread = MixinThread(thread); + return mixinThread.segregatedFreeList.AllocateFast(bytes, + alignment); + } + + [RequiredByBartok] + [Inline] + [DisableBoundsChecks] + public static unsafe Object CompilerAllocateMarkSweep + (VTable vtable, Thread currentThread, UIntPtr bytes, uint alignment) + { + VTable.Assert((alignment == 4) + || ((alignment == 8) && (UIntPtr.Size == 8)) + || ((alignment == 8) && (PreHeader.Size == 4)), + "Unsupported object layout"); + VTable.Assert(UIntPtr.Size == PreHeader.Size, + "Unsupported preheader size"); + VTable.Assert(Util.IsAligned((uint) PreHeader.Size + + (uint)PostHeader.Size, alignment), + "Unsupported header sizes"); + VTable.Assert(bytes < LARGE_OBJECT_THRESHOLD, + "CompilerAllocate called for large object"); + + UIntPtr origBytes= bytes; + + // Room to ensure alignment + bool alignRequired = (alignment > UIntPtr.Size); + if (alignRequired) { + bytes = bytes + alignment - UIntPtr.Size; + } + uint sizeClass = GetSizeClass(bytes); + SegregatedFreeListThread mixinThread = MixinThread(currentThread); + UIntPtr region = mixinThread.segregatedFreeList.freeList[sizeClass]; + if (region != UIntPtr.Zero) { + mixinThread.segregatedFreeList.freeList[sizeClass] = + *(UIntPtr *)region; + + // Zero out the free list data structure. However, if the + // vtable is at offset zero, then we're going to overwrite it + // anyway. (and the optimizer does not see this through the + // unmanaged/managed conversion) + if (Magic.OffsetOfVTable != UIntPtr.Zero) { + *(UIntPtr *)region = UIntPtr.Zero; + } + + DoLazyMemClear(region, sizeClass); + + UIntPtr objAddr = region; + + if((alignment == 8) && (UIntPtr.Size == 4)) { + // Since 'objAddr' will be the actual object reference, we + // want to misalign it here so that the object payload will + // be aligned. (We know that PostHeader.Size & 8 == 4 + // because the PreHeader is 4 and the sum of the PreHeader + // and PostHeader sizes is a multiple of alignment (8)) + + // Store alignment token at objAddr. This will be where an + // alignment token should go if it is required... + Allocator.WriteAlignment(objAddr); + // ... (align if necessary) ... + objAddr = Util.Align(objAddr, (UIntPtr) alignment); + // ... or where the object header will be if alignment was + // not necessary. This code zeroes the object header + // regardless and avoids a branch in this fast path. + *(UIntPtr *)objAddr = UIntPtr.Zero; + // Finally misalign 'objAddr' + objAddr += PreHeader.Size; + } + + Object obj = Magic.fromAddress(objAddr); + obj.vtable = vtable; + return obj; + } + + return GC.AllocateObjectNoInline(vtable, currentThread); + } + + [Inline] + [ManualRefCounts] + private UIntPtr AllocateFast(UIntPtr bytes, uint alignment) + { + UIntPtr origBytes = bytes; + // Room to ensure alignment + bool alignRequired = (alignment > UIntPtr.Size); + if (alignRequired) { + bytes = bytes + alignment - UIntPtr.Size; + } + // Is this a large object? + if (!(bytes < LARGE_OBJECT_THRESHOLD)) { + return UIntPtr.Zero; + } + uint sizeClass = GetSizeClass(bytes); + UIntPtr region = AllocateSmallFast(sizeClass); + if (region == UIntPtr.Zero) { + return UIntPtr.Zero; + } else { + DoLazyMemClear(region, sizeClass); + UIntPtr resultAddr = + Allocator.AlignedObjectPtr(region, alignment); + return resultAddr; + } + } + + [Inline] + [ManualRefCounts] + internal static UIntPtr AllocateSlow(Thread thread, + UIntPtr bytes, uint alignment) + { + SegregatedFreeListThread mixinThread = MixinThread(thread); + return mixinThread.segregatedFreeList.AllocateSlow(bytes, + alignment, + thread); + } + + [NoInline] + [ManualRefCounts] + private UIntPtr AllocateSlow(UIntPtr bytes, uint alignment, + Thread currentThread) + { + UIntPtr origBytes = bytes; + + // Room to ensure alignment + bool alignRequired = (alignment > UIntPtr.Size); + + if (alignRequired) { + bytes = bytes + alignment - UIntPtr.Size; + } + + // Is this a large object? + if (!(bytes < LARGE_OBJECT_THRESHOLD)) { + return AllocateLarge(alignment, bytes, currentThread); + } + + uint sizeClass = GetSizeClass(bytes); + UIntPtr region = AllocateSmall(sizeClass, currentThread); + DoLazyMemClear(region, sizeClass); + UIntPtr resultAddr = + Allocator.AlignedObjectPtr(region, alignment); + return resultAddr; + } + + /// + /// Used to attempt to spread large objects across pages to avoid + /// higher cache conflicts on low page addresses. + /// + private static int largeOffset; + + /// + /// Allocate an object of a specified size class from the + /// thread's local block. + /// + [Inline] + [ManualRefCounts] + private UIntPtr AllocateSmall(uint sizeClass, Thread currentThread) { + UIntPtr region = freeList[sizeClass]; + if (region != UIntPtr.Zero) { + freeList[sizeClass] = *(UIntPtr*)region; + *(UIntPtr*)region = UIntPtr.Zero; + return region; + } else { + return AllocateSmallSlow(sizeClass, currentThread); + } + } + + [Inline] + private UIntPtr AllocateSmallFast(uint sizeClass) { + UIntPtr region = freeList[sizeClass]; + if (region != UIntPtr.Zero) { + freeList[sizeClass] = *(UIntPtr*)region; + *(UIntPtr*)region = UIntPtr.Zero; + return region; + } else { + return UIntPtr.Zero; + } + } + + /// + /// Get a new thread-local page and allocate from it. + /// + [ManualRefCounts] + private UIntPtr AllocateSmallSlow(uint sizeClass, Thread currentThread) + { + // Get a new page. + PageHeader* newPage = GetLocalPage(sizeClass, currentThread); + VTable.Assert(newPage != null, "GetLocalPage returned null"); + // Return old page. + // After GetLocalPage, in case of allocation during that call. + if (this.localPages[sizeClass] != null) { + ReleaseLocalPage(sizeClass); + } + // Install new page. + VTable.Assert(this.localPages[sizeClass] == null, + "Already had a local page"); + this.localPages[sizeClass] = newPage; + // Read (and then zero) the free list. + VTable.Assert(freeList[sizeClass] == UIntPtr.Zero, + "Got page with empty free list"); + freeList[sizeClass] = + Interlocked.Exchange(ref this.localPages[sizeClass]->freeList, + UIntPtr.Zero); + + int gotFreeCount = CountListElements(freeList[sizeClass]); + AddSmallBytes((UIntPtr) this.localPages[sizeClass]->cellSize * + (UIntPtr) gotFreeCount); + AtomicAdd(ref this.localPages[sizeClass]->freeCount, + -gotFreeCount); + VTable.Assert(freeList[sizeClass] != UIntPtr.Zero, + "GetLocalPage returned empty page"); + + // Allocate off the free list + return AllocateSmallFast(sizeClass); + } + + private static int CountListElements(UIntPtr head) + { + // Count the reclaimed cells. + UIntPtr listPage = PageTable.Page(head); + int freeCount = 0; + UIntPtr next = head; + while (next != UIntPtr.Zero) { + VTable.Assert(PageTable.Page(next) == listPage); + next = *(UIntPtr*)next; + freeCount++; + } + return freeCount; + } + + private static bool FreeListContains(UIntPtr list, UIntPtr element) + { + while (list != UIntPtr.Zero) { + if (list == element) { + return true; + } + list = *(UIntPtr*)list; + } + return false; + } + + /// + /// Release a local allocation page into the pool of consumed pages. + /// + [ManualRefCounts] + private void ReleaseLocalPage(uint sizeClass) { + PageHeader *page = this.localPages[sizeClass]; + this.localPages[sizeClass] = null; + VTable.Assert(page->nextPage == null, "Local page on page list"); + // Prepare the page to be released + UIntPtr pageFreeList = freeList[sizeClass]; + if (pageFreeList != UIntPtr.Zero) { + // We are releasing the page 'early'. + freeList[sizeClass] = UIntPtr.Zero; + // Save our local free list as the page's free list. + // We use Interlocked.CompareExchange because a concurrent + // collector's sweep phase may asynchronously add to the + // free list. Attempts to acquire the list are disallowed. + UIntPtr oldFreeList = + Interlocked.CompareExchange(ref page->freeList, + pageFreeList, UIntPtr.Zero); + if (oldFreeList != UIntPtr.Zero) { + // Page already had a free list. + // page->freeList was not changed by CompareExchange. + // Follow the found free list to the end. + UIntPtr cursor = oldFreeList; + while (*(UIntPtr*)cursor != UIntPtr.Zero) { + cursor = *(UIntPtr*)cursor; + } + // Stitch our local free list onto the end of oldFreeList. + *(UIntPtr*)cursor = pageFreeList; + } else { + // Page did not have a free list. + // page->freeList was changed by CompareExchange. + // Nothing more to do. + } + // Count the number of elements not allocated and update + // the global statistics counters. + int freeCount = CountListElements(pageFreeList); + SubSmallBytes((UIntPtr) ((uint)freeCount * + (uint)page->cellSize)); + // Add the free'd blocks to the freeCount. + AtomicAdd(ref page->freeCount, freeCount); + // The free list should not have changed, as the page is + // not on any publicly visible list. + VTable.Assert(oldFreeList != UIntPtr.Zero || + FreeListContains(page->freeList, pageFreeList), + "Someone stole from our private free list - 1"); + VTable.Assert(oldFreeList == UIntPtr.Zero || + FreeListContains(page->freeList, oldFreeList), + "Someone stole from our private free list - 2"); + } + // Atomically insert the page in the globalPages queue. + AtomicPush(ref globalPages[sizeClass], page); + // Nobody should create another local list while we are + // deleting another one. + VTable.Assert(this.localPages[sizeClass] == null, + "Local page created during ReleaseLocalPage"); + } + + /// + /// Either reuse an existing page or acquire a completely new page + /// to allocate from. + /// + [ManualRefCounts] + private PageHeader * GetLocalPage(uint sizeClass, Thread currentThread) + { + GC.CheckForNeededGCWork(currentThread); + PageHeader *pageHeader = AtomicPop(ref globalFreePages[sizeClass]); + if (pageHeader == null) { + // We didn't get an existing page. Create a new local page. + pageHeader = NewLocalPage(sizeClass, currentThread); + } + VTable.Assert(pageHeader->freeList != UIntPtr.Zero, + "empty FreeList"); + return pageHeader; + } + + /// + /// Create a new page to allocate from. + /// + [ManualRefCounts] + private PageHeader * NewLocalPage(uint sizeClass, Thread currentThread) + { + VTable.Assert(sizeClass > 0, "non-positive sizeClass"); + bool fCleanPages = true; + UIntPtr page = PageManager.EnsurePages(currentThread, + (UIntPtr) 1, INIT_PAGE, + ref fCleanPages); + UIntPtr pageAddr = PageTable.PageAddr(page); + PageTable.SetExtra(page, sizeClass); + PageHeader *pageHeader = (PageHeader *) pageAddr; + + // Set up the free list of free slots + uint stride = GetCellSize(sizeClass); + VTable.Assert(stride != 0, "Zero Stride"); + pageHeader->cellSize = (ushort) stride; + UIntPtr cursor = + pageAddr + PageHeader.Size + PreHeader.Size; + pageHeader->freeList = cursor; + UIntPtr limit = + PageTable.PageAddr(page+1) - stride + PreHeader.Size; + int cellCount = 1; + UIntPtr nextAddr = cursor + stride; + while (nextAddr <= limit) { + cellCount++; + *(UIntPtr*)cursor = nextAddr; + cursor = nextAddr; + nextAddr = cursor + stride; + } + pageHeader->freeCount = cellCount; + AddSmallPage(pageHeader); + PageTable.SetType(page, SMALL_OBJ_PAGE); + return pageHeader; + } + + #endregion + + } + +} diff --git a/base/Kernel/Bartok/GCs/SemispaceCollector.cs b/base/Imported/Bartok/runtime/shared/GCs/SemispaceCollector.cs similarity index 97% rename from base/Kernel/Bartok/GCs/SemispaceCollector.cs rename to base/Imported/Bartok/runtime/shared/GCs/SemispaceCollector.cs index f1448af..93ed229 100644 --- a/base/Kernel/Bartok/GCs/SemispaceCollector.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/SemispaceCollector.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,10 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - namespace System.GCs { using Microsoft.Bartok.Runtime; @@ -169,6 +169,7 @@ namespace System.GCs { } internal override void EnableHeap() { + base.EnableHeap(); // Ensure that we don't call any type initializers during a GC ArrayList pageList = new ArrayList(); pageList.Add(new UIntPtr(11)); diff --git a/base/Kernel/Bartok/GCs/SequentialStoreBuffer.cs b/base/Imported/Bartok/runtime/shared/GCs/SequentialStoreBuffer.cs similarity index 92% rename from base/Kernel/Bartok/GCs/SequentialStoreBuffer.cs rename to base/Imported/Bartok/runtime/shared/GCs/SequentialStoreBuffer.cs index e7a818a..dd174fb 100644 --- a/base/Kernel/Bartok/GCs/SequentialStoreBuffer.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/SequentialStoreBuffer.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,10 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - namespace System.GCs { using Microsoft.Bartok.Options; @@ -21,7 +21,11 @@ namespace System.GCs { internal static SequentialStoreBuffer instance; - private struct ThreadLocal { + [AccessedByRuntime("referenced from halforgc.asm")] + // Should be private, but mixin implementation will warn if visibility + // of SequentialStoreBufferThread does not match that of Thread, and + // making that public makes the 'ssb' field internal. + internal struct ThreadLocal { internal UIntPtr overflowValue; [AccessedByRuntime("referenced from halforgc.asm")] internal UIntPtr *cursor; @@ -32,7 +36,10 @@ namespace System.GCs { [MixinConditional("SSB")] [MixinConditional("AllThreadMixins")] [Mixin(typeof(Thread))] - private class SequentialStoreBufferThread : Object { + [AccessedByRuntime("referenced from brtforgc.asm")] + // Should be private, but mixin implementation will warn if visibility + // does not match that of Thread. + public sealed class SequentialStoreBufferThread : Object { [AccessedByRuntime("referenced from brtforgc.asm")] internal ThreadLocal ssb; } @@ -108,6 +115,7 @@ namespace System.GCs { } } + [CalledRarely] private static void RecordSlow(Thread currentThread, UIntPtr value) { // Try to acquire a new chunk of the store buffer while (writeBufferIndex < writeBufferSize) { @@ -126,8 +134,8 @@ namespace System.GCs { } } // We have run out of write barrier space - if (StopTheWorldCollector.CurrentPhase == - StopTheWorldCollector.STWPhase.SingleThreaded) { + if (StopTheWorldGCData.CurrentPhase == + StopTheWorldPhase.SingleThreaded) { VTable.DebugBreak(); } VTable.Assert(MixinThread(currentThread).ssb.overflowValue == diff --git a/base/Imported/Bartok/runtime/shared/GCs/SingleThreadedCollector.cs b/base/Imported/Bartok/runtime/shared/GCs/SingleThreadedCollector.cs new file mode 100644 index 0000000..ad9b7a2 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/SingleThreadedCollector.cs @@ -0,0 +1,64 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using System.Threading; + using System.Runtime.CompilerServices; + + [NoCCtor] + internal abstract class SingleThreadedCollector : + StopTheWorldCollector { + [NoInline] + [ManualRefCounts] + internal override void CollectStopped(int currentThreadIndex, + int generation) { + } + + internal override bool IsOnTheFlyCollector { + get { + return false; + } + } + + internal override void CheckForNeededGCWork(Thread currentThread) { + } + + internal override void NewThreadNotification(Thread newThread, + bool initial) { + base.NewThreadNotification(newThread, initial); + SegregatedFreeList.NewThreadNotification(newThread, initial); + } + + internal override void DeadThreadNotification(Thread deadThread) { + MultiUseWord.CollectFromThread(deadThread); + SegregatedFreeList.DeadThreadNotification(deadThread); + base.DeadThreadNotification(deadThread); + } + + // The sole purpose of this override is to avoid doing the work + // specificed in StopTheWorldCollector.ThreadDormantGCNotification. + internal override void ThreadDormantGCNotification(int threadIndex) { + } + + // The sole purpose of this override is to avoid doing the work + // specificed in StopTheWorldCollector.StopTheWorld. + internal override void StopTheWorld() { + } + + // The sole purpose of this override is to avoid doing the work + // specificed in StopTheWorldCollector.ResumeTheWorld. + internal override void ResumeTheWorld() { + } + } +} + + diff --git a/base/Kernel/Bartok/GCs/SingleThreadedRCCollector.cs b/base/Imported/Bartok/runtime/shared/GCs/SingleThreadedRCCollector.cs similarity index 97% rename from base/Kernel/Bartok/GCs/SingleThreadedRCCollector.cs rename to base/Imported/Bartok/runtime/shared/GCs/SingleThreadedRCCollector.cs index ae5043f..e7b441a 100644 --- a/base/Kernel/Bartok/GCs/SingleThreadedRCCollector.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/SingleThreadedRCCollector.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,10 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - // #define DEBUG namespace System.GCs { diff --git a/base/Imported/Bartok/runtime/shared/GCs/SlidingCollector.cs b/base/Imported/Bartok/runtime/shared/GCs/SlidingCollector.cs new file mode 100644 index 0000000..468eda7 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/SlidingCollector.cs @@ -0,0 +1,782 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using Microsoft.Bartok.Runtime; + using System.Runtime.CompilerServices; + using System.Threading; + +#if SINGULARITY_KERNEL + using Microsoft.Singularity; +#endif + + [NoCCtor] + internal class SlidingCollector: GenerationalCollector { + + internal static SlidingCollector instance; + + // Visitor instances used for marking objects + private static MarkReferenceVisitor markReferenceVisitor; + private static RegisterThreadReference registerThreadReferenceVisitor; + private static RegisterPinnedReference registerPinnedReferenceVisitor; + private static UpdateThreadReference updateThreadReferenceVisitor; + private static ForwardReferenceVisitor forwardReferenceVisitor; + + // Internally used data structures + private UIntPtrQueue skippedPageQueue = new UIntPtrQueue(); + private UIntPtrQueue relocationQueue = new UIntPtrQueue(); + + // WARNING: don't initialize any static fields in this class + // without manually running the class constructor at startup! + + private SlidingCollector() { + } + + public static new void Initialize() { + GenerationalCollector.Initialize(); + // SlidingCollector.instance = new SlidingCollector(); + SlidingCollector.instance = (SlidingCollector) + BootstrapMemory.Allocate(typeof(SlidingCollector)); + // markReferenceVisitor = new MarkReferenceVisitor(); + markReferenceVisitor = (MarkReferenceVisitor) + BootstrapMemory.Allocate(typeof(MarkReferenceVisitor)); + // registerThreadReferenceVisitor = new RegisterThreadReference(); + registerThreadReferenceVisitor = (RegisterThreadReference) + BootstrapMemory.Allocate(typeof(RegisterThreadReference)); + // registerPinnedReferenceVisitor = new RegisterPinnedReference(); + registerPinnedReferenceVisitor = (RegisterPinnedReference) + BootstrapMemory.Allocate(typeof(RegisterPinnedReference)); + // updateThreadReferenceVisitor = new UpdateThreadReference(); + updateThreadReferenceVisitor = (UpdateThreadReference) + BootstrapMemory.Allocate(typeof(UpdateThreadReference)); + // forwardReferenceVisitor = new ForwardReferenceVisitor(); + forwardReferenceVisitor = (ForwardReferenceVisitor) + BootstrapMemory.Allocate(typeof(ForwardReferenceVisitor)); + } + + internal override void TruncateOlderAllocationAreas(int generation) { + // We don't use any allocators for the older generation, + // so there is nothing to do + } + + internal override void CollectGeneration(int generation, + UIntPtr generationPageCount) + { + VTable.Assert(IsValidGeneration(generation)); + // 1) Mark the live objects + CollectorStatistics.Event(GCEvent.TraceStart); + MultiUseWord.PreGCHook(true /* use shadows */); + Finalizer.PrepareCollectFinalizers(); + CallStack.ScanStacks(registerThreadReferenceVisitor, + registerPinnedReferenceVisitor); + registerThreadReferenceVisitor.ForwardReferences(); + if (generation < (int)MAX_GENERATION) { + // These calls must be done early, as they rely on the + // contents of Thread.threadTable being intact. + installedRemSet.Clean(); + installedRemSet.Uniquify(); + this.ScanRemSet((PageType) generation); + } + // Process runtime data that is allocated from SystemMemory + Thread.VisitBootstrapData(markReferenceVisitor); +#if SINGULARITY_KERNEL + Kernel.VisitSpecialData(markReferenceVisitor); +#endif + MultiUseWord.VisitStrongRefs(markReferenceVisitor, + true /* use shadows */); + StaticData.ScanStaticData(markReferenceVisitor); + CollectorStatistics.Event(GCEvent.TraceSpecial); + WeakReference.Process(forwardReferenceVisitor, false, true); + Finalizer.ResurrectCandidates(forwardReferenceVisitor, + markReferenceVisitor, + false); + markReferenceVisitor.Cleanup(); + // 2) Forward pointers and compact the live objects + CollectorStatistics.Event(GCEvent.SweepStart, TotalMemory); + WeakReference.Process(forwardReferenceVisitor, false, false); + MultiUseWord.VisitWeakRefs(forwardReferenceVisitor, + true /* use shadows */); + UIntPtr oldAllocPtr; + UIntPtr newLimit = this.ForwardReferences((PageType)generation, + out oldAllocPtr); + this.CompactHeapObjects(oldAllocPtr); +#if SINGULARITY + Thread.UpdateAfterGC(); +#endif + Thread currentThread = Thread.threadTable[collectorThreadIndex]; +#if SINGULARITY_KERNEL + Kernel.UpdateAfterGC(currentThread); +#endif + CallStack.ScanStacks(updateThreadReferenceVisitor, + updateThreadReferenceVisitor); + this.CompactPhaseCleanup(currentThread, + (PageType)generation, + newLimit); + // Resetting the GC state + CollectorStatistics.Event(GCEvent.SweepSpecial); + installedRemSet.Reset(); + MultiUseWord.PostGCHook(); + Finalizer.ReleaseCollectFinalizers(); + CollectorStatistics.Event(GCEvent.CollectionComplete, TotalMemory); + } + + // Internal workings of the collector + + // Scan is large and will be inlined here. This is a performance + // workaround that performs manual specialization of the Scan method. + [NoInline] + private void ScanRemSet(PageType generation) { + installedRemSet.Scan(markReferenceVisitor, generation); + } + + + // Routines for updating pointers to new locations of marked objects + // No objects can be resurrected using this mechanism + private class ForwardReferenceVisitor : NonNullReferenceVisitor { + + internal unsafe override void Visit(UIntPtr *loc) { + UIntPtr addr = *loc; + UIntPtr page = PageTable.Page(addr); + PageType pageType = PageTable.Type(page); + if (!PageTable.IsZombiePage(pageType)) { + VTable.Assert(PageTable.IsGcPage(pageType) || + PageTable.IsNonGcPage(pageType) || + PageTable.IsStackPage(pageType) || + PageTable.IsSharedPage(pageType)); + return; + } + UIntPtr vtableAddr = Allocator.GetObjectVTable(addr); + if ((vtableAddr & 0x1) == 0x1) { + // Link this field to be updated + *loc = vtableAddr; + Allocator.SetObjectVTable(addr, (UIntPtr) loc+1); + } else { + // Zero the reference (not marked) + *loc = UIntPtr.Zero; + } + } + } + + // Routines for marking objects and linking object references + private class MarkReferenceVisitor : NonNullReferenceVisitor { + + internal unsafe override void Visit(UIntPtr *loc) { + UIntPtr addr = *loc; + UIntPtr page = PageTable.Page(addr); + PageType pageType = PageTable.Type(page); + if (!PageTable.IsZombiePage(pageType)) { + VTable.Assert(PageTable.IsGcPage(pageType) || + PageTable.IsNonGcPage(pageType) || + PageTable.IsStackPage(pageType) || + PageTable.IsSharedPage(pageType)); + return; + } + UIntPtr vtableAddr = Allocator.GetObjectVTable(addr); + // Mark object + if (vtableAddr == UIntPtr.Zero) { + VTable.DebugPrint("Found null vtable in MarkReference (loc = 0x{0:x8}, addr = 0x{1:x8})\n", + __arglist(((UIntPtr)loc), addr)); + VTable.NotReached(); + } + *loc = vtableAddr; + Allocator.SetObjectVTable(addr, (UIntPtr) loc+1); + // If first visit to the object, schedule visit of fields + if ((vtableAddr & 0x1) == 0) { + MarkVisit(addr, vtableAddr & (UIntPtr) ~2U); + } + } + + private void MarkVisit(UIntPtr addr, UIntPtr vtableAddr) { + // Mark scheduling + if (fMarkInProgress) { + ScheduleMarkedObject(addr, vtableAddr); + } else { + fMarkInProgress = true; + VTable vtable = + Magic.toVTable(Magic.fromAddress(vtableAddr)); + this.VisitReferenceFields(addr, vtable); + this.ProcessScheduledObjects(); + fMarkInProgress = false; + } + } + + private void ScheduleMarkedObject(UIntPtr addr, + UIntPtr vtableAddr) { + this.markQueue.Write(addr); + this.markQueue.Write(vtableAddr); + } + + private void ProcessScheduledObjects() { + while (!this.markQueue.IsEmpty) { + UIntPtr addr = this.markQueue.Read(); + UIntPtr vtableAddr = this.markQueue.Read(); + VTable vtable = + Magic.toVTable(Magic.fromAddress(vtableAddr)); + this.VisitReferenceFields(addr, vtable); + } + } + + internal void Cleanup() { + VTable.Assert(this.markQueue.IsEmpty); + this.markQueue.Cleanup(true); + } + + private bool fMarkInProgress; + private UIntPtrQueue markQueue = new UIntPtrQueue(); + + } + + // Routines for linking and updating thread pointers + private class RegisterThreadReference : NonNullReferenceVisitor { + + internal unsafe override void Visit(UIntPtr *loc) { + UIntPtr addr = *loc; + UIntPtr page = PageTable.Page(addr); + PageType pageType = PageTable.Type(page); + if (!PageTable.IsZombiePage(pageType)) { + VTable.Assert(PageTable.IsGcPage(pageType) || + PageTable.IsNonGcPage(pageType) || + PageTable.IsStackPage(pageType) || + PageTable.IsSharedPage(pageType)); + return; + } + UIntPtr objectAddr = InteriorPtrTable.Find(addr); + this.threadPtrQueue.Write(objectAddr); + this.threadPtrQueue.Write(addr - objectAddr); + } + + internal unsafe void ForwardReferences() { + UIntPtrQueue.Enumerator queueEnumerator = + new UIntPtrQueue.Enumerator(ref this.threadPtrQueue); + while (queueEnumerator.MoveNext()) { + markReferenceVisitor.Visit(queueEnumerator.CurrentAddr); + queueEnumerator.MoveNext(); + } + } + + internal void Cleanup() { + // Free up the last thread pointer page + this.threadPtrQueue.Cleanup(true); + } + + internal UIntPtrQueue threadPtrQueue = new UIntPtrQueue(); + + } + + private class RegisterPinnedReference : NonNullReferenceVisitor { + + internal unsafe override void Visit(UIntPtr *loc) { + UIntPtr addr = *loc; + UIntPtr page = PageTable.Page(addr); + PageType pageType = PageTable.Type(page); + if (!PageTable.IsZombiePage(pageType)) { + VTable.Assert(PageTable.IsGcPage(pageType) || + PageTable.IsNonGcPage(pageType) || + PageTable.IsStackPage(pageType) || + PageTable.IsSharedPage(pageType)); + return; + } + UIntPtr objectAddr = InteriorPtrTable.Find(addr); + registerThreadReferenceVisitor.threadPtrQueue.Write(objectAddr); + registerThreadReferenceVisitor.threadPtrQueue.Write(addr-objectAddr); + *Allocator.GetObjectVTableAddress(objectAddr) |= (UIntPtr) 2U; + } + + } + + private class UpdateThreadReference : NonNullReferenceVisitor { + + internal unsafe override void Visit(UIntPtr *loc) { + UIntPtr addr = *loc; + UIntPtr page = PageTable.Page(addr); + if (!PageTable.IsZombiePage(PageTable.Type(page))) { + return; + } + UIntPtr objectAddr = + registerThreadReferenceVisitor.threadPtrQueue.Read(); + UIntPtr addressDelta = + registerThreadReferenceVisitor.threadPtrQueue.Read(); + *loc = objectAddr + addressDelta; + } + + } + + // Reference updates and object relocation + + private unsafe UIntPtr ForwardReferences(PageType generation, + out UIntPtr oldAllocPtr) + { + VTable.Assert(IsValidGeneration((int)generation)); + + UIntPtr destPage = UIntPtr.Zero; + UIntPtr destCursor; + UIntPtr destLimit; + PageType destGeneration; + if (generation < MAX_GENERATION) { + destGeneration = generation + 1; + } else { + destGeneration = MAX_GENERATION; + } + destCursor = UIntPtr.Zero; + destLimit = UIntPtr.Zero; + oldAllocPtr = destCursor; + UIntPtr runLength = UIntPtr.Zero; + for (UIntPtr i=UIntPtr.Zero; i < PageTable.pageTableCount; i++) { + if (!IsMyZombiePage(i)) { + continue; + } + UIntPtr deltaBytes = (UIntPtr) 0x80000000; + UIntPtr sourceCursor = PageTable.PageAddr(i); + do { + i++; + } while (i < PageTable.pageTableCount && IsMyZombiePage(i)); + UIntPtr sourceLimit = PageTable.PageAddr(i); + while (true) { + if (sourceCursor >= sourceLimit) { + break; + } + if (Allocator.IsAlignmentMarkerAddr(sourceCursor)) { + sourceCursor += UIntPtr.Size; + deltaBytes += UIntPtr.Size; + continue; + } + if (BumpAllocator.IsUnusedMarkerAddr(sourceCursor)) { + sourceCursor += UIntPtr.Size; + sourceCursor = PageTable.PagePad(sourceCursor); + deltaBytes = (UIntPtr) 0x80000000; + continue; + } + UIntPtr objectAddr = sourceCursor + PreHeader.Size; + UIntPtr vtableOrMarker = + Allocator.GetObjectVTable(objectAddr); + if (vtableOrMarker == UIntPtr.Zero) { + // We found the end of an allocation page + sourceCursor = PageTable.PagePad(sourceCursor); + deltaBytes = (UIntPtr) 0x80000000; + continue; + } + UIntPtr vtableAddr; + if ((vtableOrMarker & 1) != 0) { + UIntPtr temp = *(UIntPtr *) (vtableOrMarker - 1); + while ((temp & 1) != 0) { + temp = *(UIntPtr *) (temp-1); + } + VTable.Assert(PageTable.IsNonGcPage(PageTable.Type(PageTable.Page(temp)))); + vtableAddr = temp; + if ((temp & 2) != 0) { + // Found pinned object + SkipDestinationAreas(ref destPage, destCursor, + ref destLimit, + sourceCursor); + deltaBytes -= (sourceCursor - destCursor); + destCursor = sourceCursor; + vtableAddr -= 2; // Remove "pinned" bit + } + Allocator.SetObjectVTable(objectAddr, vtableAddr); + } else { + vtableAddr = vtableOrMarker; + } + VTable vtable = + Magic.toVTable(Magic.fromAddress(vtableAddr)); + UIntPtr objectSize = + ObjectLayout.ObjectSize(objectAddr, vtable); + VTable.Assert(objectSize > 0); + if ((vtableOrMarker & 1) != 0) { + if (GenerationalCollector.IsLargeObjectSize + (objectSize)) { + // Don't move large objects + SkipDestinationAreas(ref destPage, + destCursor, + ref destLimit, + sourceCursor); + UIntPtr localDelta = + sourceCursor - destCursor; + deltaBytes -= localDelta; + if (deltaBytes == UIntPtr.Zero && + runLength != UIntPtr.Zero) { + runLength += localDelta; + } + destCursor = sourceCursor; + UIntPtr objLimit = sourceCursor + objectSize; + UIntPtr pageEndAddr = PageTable.PagePad(objLimit); + objectSize = (pageEndAddr - sourceCursor); + } else if (destCursor + objectSize > destLimit) { + UIntPtr oldDestCursor = destCursor; + FindDestinationArea(ref destPage, + ref destCursor, + ref destLimit, + objectSize, + destGeneration); + VTable.Assert(destCursor <= sourceCursor); + VTable.Assert(destCursor + objectSize <= + destLimit); + deltaBytes -= (destCursor - oldDestCursor); + } else if (vtable.baseAlignment > UIntPtr.Size) { + uint alignmentMask = vtable.baseAlignment - 1; + int offset = PreHeader.Size + UIntPtr.Size; + while (((destCursor+offset) & alignmentMask) != 0) { + destCursor += UIntPtr.Size; + deltaBytes -= UIntPtr.Size; + if (deltaBytes == UIntPtr.Zero && + runLength != UIntPtr.Zero) { + runLength += UIntPtr.Size; + } + } + } + if (runLength == UIntPtr.Zero || + deltaBytes != UIntPtr.Zero) { + if (runLength != UIntPtr.Zero) { + RegisterRelocationEnd(runLength); + } + RegisterRelocationStart(sourceCursor, + destCursor); + deltaBytes = UIntPtr.Zero; + runLength = UIntPtr.Zero; + } + UIntPtr newObjectAddr = destCursor + PreHeader.Size; + do { + UIntPtr *ptrAddr = (UIntPtr*) (vtableOrMarker-1); + vtableOrMarker = *ptrAddr; + *ptrAddr = newObjectAddr; + } while ((vtableOrMarker & 1) != 0); + destCursor += objectSize; + runLength += objectSize; + } else { + deltaBytes += objectSize; + if (runLength != UIntPtr.Zero) { + RegisterRelocationEnd(runLength); + } + runLength = UIntPtr.Zero; + } + sourceCursor += objectSize; + } + } + if (runLength != UIntPtr.Zero) { + RegisterRelocationEnd(runLength); + } + return destCursor; + } + + private unsafe void SkipDestinationAreas(ref UIntPtr destPage, + UIntPtr destCursor, + ref UIntPtr destLimit, + UIntPtr sourceCursor) + { + UIntPtr cursorPage = PageTable.Page(destCursor); + UIntPtr sourcePage = PageTable.Page(sourceCursor); + if (cursorPage != sourcePage) { + UIntPtr destPageLimit = PageTable.PagePad(destCursor); + if (destPageLimit != destCursor) { + cursorPage++; + } + VTable.Assert(PageTable.PageAligned(destLimit)); + UIntPtr limitPage = PageTable.Page(destLimit); + while (destPage < sourcePage) { + if (cursorPage < limitPage) { + this.RegisterSkippedPages(cursorPage, limitPage); + } + do { + destPage++; + } while (!IsMyZombiePage(destPage)); + cursorPage = destPage; + do { + destPage++; + } while (IsMyZombiePage(destPage)); + limitPage = destPage; + } + destLimit = PageTable.PageAddr(limitPage); + VTable.Assert(destPage > sourcePage); + VTable.Assert(cursorPage <= sourcePage); + if (cursorPage < sourcePage) { + this.RegisterSkippedPages(cursorPage, sourcePage); + cursorPage = sourcePage; + } + InteriorPtrTable.ClearFirst(cursorPage, destPage); + InteriorPtrTable.SetFirst(sourceCursor + PreHeader.Size); + if (GC.remsetType == RemSetType.Cards) { + OffsetTable.ClearLast(PageTable.PageAddr(cursorPage), + PageTable.PageAddr(destPage)-1); + } + } + } + + private unsafe void FindDestinationArea(ref UIntPtr destPage, + ref UIntPtr destCursor, + ref UIntPtr destLimit, + UIntPtr objectSize, + PageType destGeneration) + { + VTable.Assert(IsValidGeneration((int)destGeneration)); + + UIntPtr cursorPage = PageTable.Page(destCursor); + UIntPtr limitPage = PageTable.Page(destLimit); + UIntPtr pageAddr = PageTable.PagePad(destCursor); + UIntPtr testPage = limitPage; + UIntPtr endTestPage = PageTable.PageCount(destCursor+objectSize); + if (destCursor > UIntPtr.Zero && + IsMyZombiePage(PageTable.Page(destCursor-1))) { + VTable.Assert(destPage == limitPage); + while (IsMyZombiePage(testPage) || + (testPage < endTestPage && + (PageTable.IsUnusedPage(testPage)))) { + testPage++; + } + if (testPage >= endTestPage) { + // We can expand the current region + endTestPage = testPage; + VTable.Assert(PageTable.PageAligned(destLimit)); + InteriorPtrTable.ClearFirst(limitPage, testPage); + if (GC.remsetType == RemSetType.Cards) { + OffsetTable.ClearLast(PageTable.PageAddr(limitPage), + PageTable.PageAddr(testPage)-1); + } + while (limitPage != endTestPage) { + VTable.Assert(PageTable.IsUnusedPage(destPage)); + do { + destPage++; + } while (destPage < endTestPage && + PageTable.IsUnusedPage(destPage)); + bool fCleanPages = true; + bool status = + PageManager.TryReserveUnusedPages(null, limitPage, + destPage-limitPage, + nurseryGeneration, + ref fCleanPages); + VTable.Assert(status); + MakeZombiePages(limitPage, destPage - limitPage, + destGeneration); + while (destPage < endTestPage && + IsMyZombiePage(destPage)) { + destPage++; + } + limitPage = destPage; + } + destLimit = PageTable.PageAddr(limitPage); + return; + } + } + if (destCursor != pageAddr) { + cursorPage++; + } + if (cursorPage != limitPage) { + this.RegisterSkippedPages(cursorPage, limitPage); + } + // Find new region big enough to contain object + UIntPtr neededPages = PageTable.PageCount(objectSize); + UIntPtr prefixPage; + while (true) { + do { + destPage++; + } while (!IsMyZombiePage(destPage)); + cursorPage = destPage; + prefixPage = cursorPage; + do { + destPage++; + } while (IsMyZombiePage(destPage)); + limitPage = destPage; + if (neededPages <= limitPage - cursorPage) { + break; + } + // Check for following unused pages + endTestPage = cursorPage + neededPages; + VTable.Assert(endTestPage <= PageTable.pageTableCount); + while (destPage < endTestPage && + (PageTable.IsUnusedPage(destPage) || + (IsMyZombiePage(destPage)))) { + destPage++; + } + if (destPage == endTestPage) { + break; + } + // Check for preceding unused pages + if (destPage >= neededPages) { + endTestPage = destPage - neededPages; + prefixPage = cursorPage - 1; + while (prefixPage >= UIntPtr.Zero && + PageTable.IsUnusedPage(prefixPage)) { + prefixPage--; + } + prefixPage++; + if (prefixPage == endTestPage) { + break; + } + } + // Register any skipped regions of pages + this.RegisterSkippedPages(cursorPage, limitPage); + while (limitPage < destPage) { + VTable.Assert(PageTable.IsUnusedPage(limitPage)); + do { + limitPage++; + } while (limitPage < destPage && + PageTable.IsUnusedPage(limitPage)); + cursorPage = limitPage; + while (limitPage < destPage && IsMyZombiePage(limitPage)) { + limitPage++; + } + if (cursorPage != limitPage) { + this.RegisterSkippedPages(cursorPage, limitPage); + } + } + } + // We found an area big enough. Commit the pre- and + // postfix areas of unused pages + if (prefixPage != cursorPage) { + bool fCleanPages = true; + bool status = + PageManager.TryReserveUnusedPages(null, prefixPage, + cursorPage-prefixPage, + nurseryGeneration, + ref fCleanPages); + VTable.Assert(status); + MakeZombiePages(prefixPage, cursorPage - prefixPage, + destGeneration); + } + while (destPage != limitPage) { + // Mark the region of unused pages as fromspace + UIntPtr unusedPage = limitPage; + VTable.Assert(PageTable.IsUnusedPage(unusedPage)); + do { + unusedPage++; + } while (unusedPage < destPage && + PageTable.IsUnusedPage(unusedPage)); + bool fCleanPages = true; + bool status = + PageManager.TryReserveUnusedPages(null, limitPage, + unusedPage-limitPage, + nurseryGeneration, + ref fCleanPages); + VTable.Assert(status); + MakeZombiePages(limitPage, unusedPage - limitPage, + destGeneration); + // Skip any sections of pages already marked as fromspace + limitPage = unusedPage; + while (limitPage < destPage && IsMyZombiePage(limitPage)) { + limitPage++; + } + } + destCursor = PageTable.PageAddr(prefixPage); + destLimit = PageTable.PageAddr(limitPage); + // Take ownership of the new pages + InteriorPtrTable.ClearFirst(prefixPage, limitPage); + InteriorPtrTable.SetFirst(destCursor + PreHeader.Size); + if (GC.remsetType == RemSetType.Cards) { + OffsetTable.ClearLast(PageTable.PageAddr(prefixPage), + PageTable.PageAddr(limitPage)-1); + } + } + + private void RegisterSkippedPages(UIntPtr startPage, + UIntPtr limitPage) + { + this.skippedPageQueue.Write(startPage); + this.skippedPageQueue.Write(limitPage); + } + + private void RegisterRelocationStart(UIntPtr sourceAddress, + UIntPtr destinationAddress) + { + this.relocationQueue.Write(sourceAddress); + this.relocationQueue.Write(destinationAddress); + } + + private void RegisterRelocationEnd(UIntPtr runLength) + { + this.relocationQueue.Write(runLength); + } + + private unsafe void CompactHeapObjects(UIntPtr previousEnd) { + while (!this.relocationQueue.IsEmpty) { + UIntPtr sourceAddress = this.relocationQueue.Read(); + UIntPtr destinationAddress = this.relocationQueue.Read(); + UIntPtr runLength = this.relocationQueue.Read(); + if (previousEnd != destinationAddress) { + VTable.Assert(previousEnd < destinationAddress); + if (PageTable.Page(destinationAddress) != + PageTable.Page(previousEnd+PreHeader.Size)) { + if (!PageTable.PageAligned(previousEnd)) { + UIntPtr pageLimit = PageTable.PagePad(previousEnd); + BumpAllocator.WriteUnusedMarker(previousEnd); + previousEnd += UIntPtr.Size; + Util.MemClear(previousEnd, + pageLimit - previousEnd); + } + if (!PageTable.PageAligned(destinationAddress)) { + // This only happens before pinned objects and + // large objects + UIntPtr start = + PageTable.PageAlign(destinationAddress); + VTable.Assert(previousEnd <= start); + while (start < destinationAddress) { + Allocator.WriteAlignment(start); + start += UIntPtr.Size; + } + } + UIntPtr objAddr = destinationAddress + PreHeader.Size; + InteriorPtrTable.SetFirst(objAddr); + } else { + VTable.Assert(previousEnd < destinationAddress); + UIntPtr start = previousEnd; + while (start < destinationAddress) { + Allocator.WriteAlignment(start); + start += UIntPtr.Size; + } + } + } + Util.MemCopy(destinationAddress, sourceAddress, runLength); + previousEnd = destinationAddress + runLength; + } + // Zero out the end of the allocation page + if (!PageTable.PageAligned(previousEnd)) { + UIntPtr pageLimit = PageTable.PagePad(previousEnd); + Util.MemClear(previousEnd, pageLimit - previousEnd); + } + this.relocationQueue.Cleanup(true); + } + + private void CompactPhaseCleanup(Thread currentThread, + PageType generation, + UIntPtr newLimitPtr) + { + VTable.Assert(IsValidGeneration((int)generation)); + + registerThreadReferenceVisitor.Cleanup(); + // Free up skipped pages + while (!this.skippedPageQueue.IsEmpty) { + UIntPtr start = this.skippedPageQueue.Read(); + UIntPtr finish = this.skippedPageQueue.Read(); + InteriorPtrTable.ClearFirst(start, finish); + PageManager.FreePageRange(start, finish); + if (GC.remsetType == RemSetType.Cards) { + OffsetTable.ClearLast(PageTable.PageAddr(start), + PageTable.PageAddr(finish)-1); + } + } + this.skippedPageQueue.Cleanup(true); + // Release the queue standby pages + UnmanagedPageList.ReleaseStandbyPages(); + // Update the ownership information for the copied data + PageType destGeneration = + (generation == MAX_GENERATION) ? + MAX_GENERATION : + (PageType) (generation + 1); + UIntPtr limitPage = + PageTable.Page(PageTable.PagePad(newLimitPtr)); + for (UIntPtr i = UIntPtr.Zero; i < limitPage; i++) { + if (IsMyZombiePage(i)) { + PageTable.SetType(i, (PageType)destGeneration); + } + } + } + + } + +} diff --git a/base/Kernel/Bartok/GCs/StaticData.cs b/base/Imported/Bartok/runtime/shared/GCs/StaticData.cs similarity index 93% rename from base/Kernel/Bartok/GCs/StaticData.cs rename to base/Imported/Bartok/runtime/shared/GCs/StaticData.cs index 63e9b8a..de69759 100644 --- a/base/Kernel/Bartok/GCs/StaticData.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/StaticData.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,15 +9,10 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - namespace System.GCs { using System.Runtime.CompilerServices; - [RequiredByBartok] internal unsafe class StaticData { [RequiredByBartok] @@ -30,7 +29,7 @@ namespace System.GCs { private static uint **staticDataPointerBitMap; // unmanaged uint*[] [PreInitRefCounts] -#if !SINGULARITY +#if !SINGULARITY // Needed before Bartok runtime is initialized [NoStackLinkCheck] #endif internal static void Initialize() { @@ -51,7 +50,7 @@ namespace System.GCs { } internal static - void ScanStaticData(NonNullReferenceVisitor referenceVisitor) + void ScanStaticData(DirectReferenceVisitor referenceVisitor) { Finalizer.VisitBootstrapData(referenceVisitor); for (int section = 0; section < sectionCount; section++) { diff --git a/base/Imported/Bartok/runtime/shared/GCs/StopTheWorldCollector.cs b/base/Imported/Bartok/runtime/shared/GCs/StopTheWorldCollector.cs new file mode 100644 index 0000000..4236b01 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/StopTheWorldCollector.cs @@ -0,0 +1,344 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using System.Threading; + using System.Runtime.CompilerServices; + +#if SINGULARITY + using Microsoft.Singularity; + using Microsoft.Singularity.Isal; +#if SINGULARITY_KERNEL + using Microsoft.Singularity.Scheduling; +#else + using Microsoft.Singularity.V1.Services; +#endif + + [CLSCompliant(false)] + public enum GarbageCollectorEvent : ushort + { + StartStopTheWorld = 1, + EndStopTheWorld = 2, + StartCollection = 3, + EndCollection = 4, + } +#endif + + /// + /// Identifies the current collection phase + /// + internal enum StopTheWorldPhase { + Dummy, // Not used! + Idle, // No collection is taking place + Synchronizing, // Attempting to stop the world + SingleThreaded, // Stop-the-world phase + } + + [NoCCtor] + internal class StopTheWorldGCData + { + internal static int collectorThreadIndex; + + /// + /// The current state of the collector. + /// + internal static volatile StopTheWorldPhase CurrentPhase; + } + + [NoCCtor] + internal abstract class StopTheWorldCollector : BaseCollector + { + protected static int collectorThreadIndex { + get { return StopTheWorldGCData.collectorThreadIndex; } + set { StopTheWorldGCData.collectorThreadIndex = value; } + } + + protected static StopTheWorldPhase CurrentPhase { + get { return StopTheWorldGCData.CurrentPhase; } + set { StopTheWorldGCData.CurrentPhase = value; } + } + + [PreInitRefCounts] + internal static void Initialize() { + collectorThreadIndex = -1; + CurrentPhase = StopTheWorldPhase.Idle; + } + + internal override void EnableHeap() { + // Make sure that we have initialized everything we need to + // perform the thread rendez-vous before we start the first + // collection cycle. + int currentThreadIndex = Thread.GetCurrentThreadIndex(); + Thread.SignalGCEvent(currentThreadIndex); + Thread.WaitForGCEvent(currentThreadIndex); + } + + internal override void Collect(Thread currentThread, int generation) + { + try { + GC.CollectTransition(currentThread, generation); + } catch (Exception e) { + VTable.DebugPrint("Garbage collection failed with exception"); + VTable.DebugPrint(e.GetType().Name); + VTable.DebugBreak(); + } + } + + internal abstract void CollectStopped(int currentThreadIndex, + int generation); + + // Implementation of GCInterface + internal override bool IsOnTheFlyCollector { + get { + return false; + } + } + + internal override void CollectStoppable(int currentThreadIndex, + int generation) + { + int foundIndex = + Interlocked.CompareExchange(ref StopTheWorldGCData.collectorThreadIndex, + currentThreadIndex, -1); + if (foundIndex < 0) { + // We are the designated collector thread + PerformCollection(currentThreadIndex, generation); + } else { + Transitions.TakeDormantControlNoGC(currentThreadIndex); + if (Transitions.TakeGCControl(currentThreadIndex)) { + // The 'foundIndex' thread may have completed its + // collection and another thread may have started + // another collection after we read + // 'collectorThreadIndex'. The collector thread may + // have decided to wait for us before we entered + // DormantState, so we have to read + // 'collectorThreadIndex' again. + foundIndex = collectorThreadIndex; + if (foundIndex >= 0) { + Thread.SignalGCEvent(foundIndex); + } + } + Transitions.TakeMutatorControlNoGC(currentThreadIndex); + } + } + + private void PerformCollection(int currentThreadIndex, + int generation) + { + // Clear the GCRequest bit (if necessary) before doing + // anything that could cause a state transition. + if (Transitions.HasGCRequest(currentThreadIndex)) { + Transitions.ClearGCRequest(currentThreadIndex); + } + int startTicks = 0; + bool enableGCTiming = VTable.enableGCTiming; + if (enableGCTiming || VTable.enableFinalGCTiming) { + VTable.enableGCTiming = false; + startTicks = Environment.TickCount; + if (enableGCTiming) { + VTable.DebugPrint("[GC start: {0} bytes]\n", + __arglist(TotalMemory)); + } + } +#if SINGULARITY + Tracing.Log(Tracing.Debug,"GC start"); +#endif + CollectorStatistics.Event(GCEvent.StopTheWorld); + CurrentPhase = StopTheWorldPhase.Synchronizing; + StopTheWorld(); + CurrentPhase = StopTheWorldPhase.SingleThreaded; + StartGCCycle(); +#if SINGULARITY + long preGcMemoryUsage = GC.GetTotalMemory(false); +#if SINGULARITY_KERNEL +#if THREAD_TIME_ACCOUNTING + TimeSpan ticks = Thread.CurrentThread.ExecutionTime; + TimeSpan ticks2 = SystemClock.KernelUpTime; +#else + TimeSpan ticks = SystemClock.KernelUpTime; +#endif +#elif SINGULARITY_PROCESS +#if THREAD_TIME_ACCOUNTING + TimeSpan ticks = ProcessService.GetThreadTime(); + TimeSpan ticks2 = ProcessService.GetUpTime(); +#else + TimeSpan ticks = ProcessService.GetUpTime(); +#endif +#endif +#endif //singularity +#if SINGULARITY_KERNEL + bool iflag = Processor.DisableInterrupts(); + + // Disable interrupts on other CPU's + MpExecution.StopProcessorsForGC(); +#endif +#if SINGULARITY + ulong beg = Isa.GetCycleCount(); +#endif + // Preparation + GC.allocationGCInhibitCount++; + // Verify the heap before GC + if (VTable.enableGCVerify) { + this.VerifyHeap(true); + } + // Invoke the chosen collector +#if SINGULARITY + Monitoring.Log(Monitoring.Provider.GC, + (ushort)GarbageCollectorEvent.StartCollection); +#endif + this.CollectStopped(collectorThreadIndex, generation); +#if SINGULARITY + Monitoring.Log(Monitoring.Provider.GC, + (ushort)GarbageCollectorEvent.EndCollection); +#endif + // Verify the heap after GC + if (VTable.enableGCVerify) { + this.VerifyHeap(false); + } + if (VTable.enableGCAccounting) { + MemoryAccounting.Report(GC.gcType); + } + // Cleanup + CollectorStatistics.Event(GCEvent.ResumeTheWorld); + GC.allocationGCInhibitCount--; + CurrentPhase = StopTheWorldPhase.Idle; +#if SINGULARITY + long postGcMemoryUsage = GC.GetTotalMemory(false); +#endif + if (enableGCTiming || VTable.enableFinalGCTiming) { + int elapsedTicks = Environment.TickCount - startTicks; + BaseCollector.RegisterPause(elapsedTicks); + if (enableGCTiming) { + VTable.DebugPrint("[GC end : {0} bytes, {1} ms]\n", + __arglist(TotalMemory, elapsedTicks)); + VTable.enableGCTiming = true; + } + } + if (VTable.enableGCProfiling) { + ulong totalMemory = (ulong)GC.GetTotalMemory(false); + this.RegisterHeapSize(totalMemory); + } + ResumeTheWorld(); + collectorThreadIndex = -1; +#if SINGULARITY + Tracing.Log(Tracing.Debug,"GC stop"); + long pagesCollected = preGcMemoryUsage - postGcMemoryUsage; +#if SINGULARITY_KERNEL +#if THREAD_TIME_ACCOUNTING + int procId = Thread.CurrentProcess.ProcessId; + ticks = Thread.CurrentThread.ExecutionTime - ticks; + ticks2 = SystemClock.KernelUpTime - ticks2; + Process.kernelProcess.SetGcPerformanceCounters(ticks, (long) pagesCollected); +#else + ticks = SystemClock.KernelUpTime - ticks; +#endif + Thread.CurrentProcess.SetGcPerformanceCounters(ticks, (long) pagesCollected); +#elif SINGULARITY_PROCESS +#if THREAD_TIME_ACCOUNTING + ushort procId = ProcessService.GetCurrentProcessId(); + ticks = ProcessService.GetThreadTime() - ticks; + ticks2 = ProcessService.GetUpTime() - ticks2; +#else + ticks = ProcessService.GetUpTime() - ticks; +#endif + ProcessService.SetGcPerformanceCounters(ticks, (long) pagesCollected); +#endif + +#if DEBUG +#if THREAD_TIME_ACCOUNTING + DebugStub.WriteLine("~~~~~ StopTheWorld [collected pages={0:x8}, pid={1:x3}, ms(Thread)={2:d6}, ms(System)={3:d6}, procId={4}, tid={5}]", + __arglist(pagesCollected, + PageTable.processTag >> 16, + ticks.Milliseconds, + ticks2.Milliseconds, + procId, + Thread.GetCurrentThreadIndex() + )); +#endif +#endif +#endif + +#if SINGULARITY + DebugStub.AddToPerfCounter(GC.perfCounter, Isa.GetCycleCount() - beg); +#endif +#if SINGULARITY_KERNEL + // Resume interrupts on other CPU's + MpExecution.ResumeProcessorsAfterGC(); + + Processor.RestoreInterrupts(iflag); +#endif + } + + internal override void CheckForNeededGCWork(Thread currentThread) { + while (CurrentPhase == StopTheWorldPhase.Synchronizing && + currentThread.threadIndex != collectorThreadIndex) { + GC.InvokeCollection(currentThread); + } + } + + internal override void NewThreadNotification(Thread newThread, + bool initial) + { + base.NewThreadNotification(newThread, initial); + if (CurrentPhase == StopTheWorldPhase.Synchronizing) { + Transitions.MakeGCRequest(newThread.threadIndex); + } + } + + internal override void DeadThreadNotification(Thread deadThread) + { + MultiUseWord.CollectFromThread(deadThread); + base.DeadThreadNotification(deadThread); + } + + internal override void ThreadDormantGCNotification(int threadIndex) { + int ctid = collectorThreadIndex; + if (ctid >= 0) { + Thread.SignalGCEvent(ctid); + } + base.ThreadDormantGCNotification(threadIndex); + } + + internal virtual void StopTheWorld() { +#if SINGULARITY + //DebugStub.WriteLine("~~~~~ StopTheWorld()"); + Monitoring.Log(Monitoring.Provider.GC, + (ushort)GarbageCollectorEvent.StartStopTheWorld); +#if SINGULARITY_KERNEL + TimeSpan ticks = SystemClock.KernelUpTime; +#elif SINGULARITY_PROCESS + TimeSpan ticks = ProcessService.GetUpTime(); +#endif +#endif + VTable.Assert(Thread.GetCurrentThreadIndex() == + collectorThreadIndex); + BaseCollector.AllThreadRendezvous(collectorThreadIndex); +#if SINGULARITY +#if SINGULARITY_KERNEL + ticks = SystemClock.KernelUpTime - ticks; +#elif SINGULARITY_PROCESS + ticks = ProcessService.GetUpTime() - ticks; +#endif + Monitoring.Log(Monitoring.Provider.GC, + (ushort)GarbageCollectorEvent.EndStopTheWorld); +#endif + } + + internal virtual void ResumeTheWorld() { + VTable.Assert(Thread.GetCurrentThreadIndex() == + collectorThreadIndex); + BaseCollector.AllThreadRelease(collectorThreadIndex); + } + + } + +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/ThreadHeaderQueue.cs b/base/Imported/Bartok/runtime/shared/GCs/ThreadHeaderQueue.cs new file mode 100644 index 0000000..5b3c5f3 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/ThreadHeaderQueue.cs @@ -0,0 +1,571 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + + +#if !SINGULARITY_KERNEL +#define ATOMIC_PUSH +#endif + +namespace System.GCs { + + using Microsoft.Bartok.Options; + using Microsoft.Bartok.Runtime; + + using System; + using System.Threading; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + /// + /// This class defines a queue that is created as a linked list from + /// each thread object through pointers stored in object headers. + /// + /// This queue is used for the concurrent mark sweep collector to allow + /// it to trace through the heap without ever requiring allocation + /// or locking. + /// + /// Within a thread this is a FIFO queue; all mutations are made at the + /// head. + /// + internal struct ThreadHeaderQueue { + + [StructLayout(LayoutKind.Sequential)] + [MixinConditional("ConcurrentMSGC")] + [Mixin(typeof(PreHeader))] + [RequiredByBartok] + internal struct PreHeaderQueue { + // This use of NoBarriers is just an optimization. CoCo separately + // knows that when it is asked to operate on the link field, it + // should ignore it. This is accomplished by passing CoCo the + // offset of this field. That hackishness is necessary in case we + // acquire a pointer to this field and operate on it that way. + [NoBarriers] + internal UIntPtr link; + } + + [MixinConditional("ConcurrentMSGC")] + [Mixin(typeof(Object))] + internal class ThreadHeaderQueueObject : System.Object { + internal new PreHeaderQueue preHeader; + } + + [Inline] + internal static ThreadHeaderQueueObject MixinObject(Object obj) { + return (ThreadHeaderQueueObject) obj; + } + + [MixinConditional("ConcurrentMSGC")] + [MixinConditional("AllThreadMixins")] + [Mixin(typeof(Thread))] + // Should be private, but mixin implementation will warn if visibility + // does not match that of Thread. + public sealed class ThreadHeaderQueueThread : Object { + internal ThreadHeaderQueue gcQueue; + } + + [Inline] + internal static ThreadHeaderQueueThread MixinThread(Thread t) { + return (ThreadHeaderQueueThread) (Object) t; + } + + /// + /// A counter of ongoing attempts to update the queue. If the + /// push operations are non-interruptable, the possible values + /// are 0 and 1. In the Singularity kernel, the push operation + /// may be interrupted and code run in the interrupt handlers + /// may cause overlapping attempts to update the queue, so the + /// possible values range between 0 and the number of threads. + /// + [NoBarriers] + private volatile int ongoingUpdates; + + /// + /// Contains the pointer to the first object in the queue, or + /// the TAIL_MARKER value if the queue is empty. + /// + /// This field really should be declared volatile, but since C# + /// does not support atomic operations on volatile variables, + /// we have to use atomic operations on every read and write of + /// this variable to ensure is appears to be a volatile variable. + /// + [NoBarriers] + private UIntPtr head; + + /// + /// Contains a pointer to the object that was at the head of the + /// queue the last time it was 'stolen' by a consuming thread. + /// + /// If this.stolenHead is equal to this.head, then there is nothing + /// on the queue that has not been stolen. If they are different, + /// then the queue contains the list of nodes that starts at + /// this.head and ends at this.stolenHead, but does not include + /// the node at this.stolenHead. + /// + /// This field really should be declared volatile, but since C# + /// does not support atomic operations on volatile variables, + /// we have to use atomic operations on every read and write of + /// this variable to ensure is appears to be a volatile variable. + /// + [NoBarriers] + private UIntPtr stolenHead; + + /// + /// This flag indicates that there has been an attempt to transfer + /// the contents of a dead thread's ThreadHeaderQueue + /// to another thread's ThreadHeaderQueue. The flag is used when + /// trying to establish that all threads' ThreadHeaderQueues are + /// empty. The usage pattern is to first clear this flag, then + /// iterate through all the threads checking that the + /// ThreadHeaderQueues are empty, and finally checking that the + /// flag has not been set during the iteration through the threads. + /// Doing so prevents an unnoticed transfer of work from an unvisited + /// dying/dead thread to an already visited thread followed by the + /// disappearance of the dead thread. + /// + [NoBarriers] + internal static bool transferAttempt; + + /// + /// Every object that is in a ThreadHeaderQueue list must have a + /// non-zero link field. To ensure this invariant for the last + /// element in the list, a non-zero value is used to mark the + /// end of the list. TAIL_MARKER is that value. + /// + internal static UIntPtr TAIL_MARKER { + get { return ~(UIntPtr)3U; } + } + + /// + /// Atomically compare-and-swap (CAS) one value for another. + /// Return true if the CAS operation succeeds, false otherwise. + /// + private static bool CompareAndSwap(ref UIntPtr variable, + UIntPtr newValue, + UIntPtr comparand) + { + return (Interlocked.CompareExchange(ref variable, + newValue, + comparand) + == comparand); + } + + /// + /// Reset the queue of a thread. + /// + [Inline] + internal static void Reset(Thread t) { + MixinThread(t).gcQueue.Reset(); + } + + /// + /// Reset the queue. + /// + [Inline] + internal void Reset() + { + // NOTE: this.head MUST be written to before this.stolenHead! + Thread.VolatileWrite(ref this.head, TAIL_MARKER); + Thread.VolatileWrite(ref this.stolenHead, TAIL_MARKER); + this.ongoingUpdates = 0; + } + + /// + /// Is the queue empty? + /// + internal static bool IsEmpty(Thread t) { + return MixinThread(t).gcQueue.IsEmpty(); + } + + internal bool IsEmpty() { + UIntPtr foundHead = Thread.VolatileRead(ref this.head); + UIntPtr foundStolenHead = Thread.VolatileRead(ref this.stolenHead); + return (foundHead == foundStolenHead || + foundHead == TAIL_MARKER); + } + + internal static bool IsReset(Thread thread) { + return MixinThread(thread).gcQueue.IsReset(); + } + + private bool IsReset() { + return (Thread.VolatileRead(ref this.head) == TAIL_MARKER && + Thread.VolatileRead(ref this.stolenHead) == TAIL_MARKER); + } + + /// + /// Return the value of an object's link field. + /// + [Inline] + [DisableNullChecks] + private static UIntPtr QueueField(Object obj) + { + return MixinObject(obj).preHeader.link; + } + + /// + /// Set an object's link field to a given value. + /// + [Inline] + [DisableNullChecks] + [NoBarriers] + private static void SetQueueField(Object obj, UIntPtr value) + { + MixinObject(obj).preHeader.link = value; + } + + /// + /// Perform a CAS operation on an object's link field. + /// + [Inline] + [DisableNullChecks] + [NoBarriers] + private static bool ExchangeQueueField(Object obj, + UIntPtr val, + UIntPtr oldVal) + { + ThreadHeaderQueueObject obj2 = MixinObject(obj); + return CompareAndSwap(ref obj2.preHeader.link, val, oldVal); + } + + /// + /// An object's link field can contain both a link to another + /// object and two mark bits. This function returns the link + /// part of the link field. + /// + [Inline] + [DisableNullChecks] + internal static UIntPtr QueueLink(Object obj) + { + return QueueField(obj) & ~(UIntPtr)3U; + } + + /// + /// An object's link field can contain both a link to another + /// object and two mark bits. This function returns the mark + /// part of the link field. + /// + [Inline] + [DisableNullChecks] + internal static UIntPtr GcMark(Object obj) + { + VTable.Assert(obj != null, "Can not get mark of null"); + return (QueueField(obj) & (UIntPtr)3U); + } + + /// + /// Sets the mark value in the header word of the object. + /// + [Inline] + [DisableNullChecks] + internal static void SetGcMark(UIntPtr objAddr, UIntPtr markBits) + { + SetGcMark(Magic.fromAddress(objAddr), markBits); + } + + [Inline] + [DisableNullChecks] + internal static void SetGcMark(Object obj, UIntPtr markBits) + { + SetQueueField(obj, markBits); + } + + internal static bool IsInQueue(Object obj) { + return (QueueLink(obj) != UIntPtr.Zero); + } + + /// + /// Link a new value at the head of the queue. It is assumed that + /// if the object is unmarked, the value in the header word will + /// simply be 'unmarkedColor'. The function returns true if the + /// object was marked and linked into the queue. The function + /// returns false if the object has already linked into an(other) + /// queue. + /// + [Inline] + [DisableNullChecks] + internal static bool Push(Thread t, + UIntPtr objAddr, + UIntPtr markedColor, + UIntPtr unmarkedColor) + { + return MixinThread(t).gcQueue.Push(objAddr, + markedColor, + unmarkedColor); + } + + [Inline] + [DisableNullChecks] + [NoBarriers] + private bool Push(UIntPtr objAddr, + UIntPtr markedColor, + UIntPtr unmarkedColor) + { + VTable.Assert(objAddr != UIntPtr.Zero, "Can not push null!"); + this.ongoingUpdates++; + UIntPtr oldHead = Thread.VolatileRead(ref this.head); + Object obj = Magic.fromAddress(objAddr); + if (ExchangeQueueField(obj, + oldHead + markedColor, + unmarkedColor)) { +#if ATOMIC_PUSH + Thread.VolatileWrite(ref this.head, objAddr); +#else // ATOMIC_PUSH + // We took ownership of the object, but since the + // queue may be updated due to code run in interrupt + // handlers, we have to use an iterated approach to + // adding the object to the queue. + // REVIEW: We don't really need LOCK CMPXCHG, but we do + // need CMPXCHG (needs to be atomic with respect to the + // current processor, only). + UIntPtr foundHead = Interlocked.CompareExchange(ref this.head, + objAddr, + oldHead); + while (foundHead != oldHead) { + oldHead = foundHead; + SetQueueField(obj, oldHead + markedColor); + foundHead = Interlocked.CompareExchange(ref this.head, + objAddr, + oldHead); + } +#endif // ATOMIC_PUSH + this.ongoingUpdates--; + return true; + } else { + // Someone else enqueued the object + VTable.Assert(GcMark(obj) == markedColor); + this.ongoingUpdates--; + return false; + } + } + + /// + /// Link a new value at the head of the queue, knowing that the + /// object is a thread local object. Since the object is + /// thread local, we don't need to use a CAS operation to set + /// the link field of the object. + /// + [Inline] + [DisableNullChecks] + internal static void PushPrivateObject(Thread t, + Object obj, + UIntPtr markBits) + { + MixinThread(t).gcQueue.PushPrivateObject(obj, markBits); + } + + [Inline] + [DisableNullChecks] + [NoBarriers] + private void PushPrivateObject(Object obj, UIntPtr markBits) + { + VTable.Assert(obj != null, "Can not push null!"); +#if ATOMIC_PUSH + SetQueueField(obj, Thread.VolatileRead(ref this.head) + markBits); + Thread.VolatileWrite(ref this.head, Magic.addressOf(obj)); +#else // ATOMIC_PUSH + // REVIEW: We don't really need LOCK CMPXCHG, but we do + // need CMPXCHG (needs to be atomic with respect to the + // current processor, only). + UIntPtr oldHead; + UIntPtr foundHead = Thread.VolatileRead(ref this.head); + do { + oldHead = foundHead; + SetQueueField(obj, oldHead + markBits); + foundHead = Interlocked.CompareExchange(ref this.head, + Magic.addressOf(obj), + oldHead); + } while (foundHead != oldHead); +#endif // ATOMIC_PUSH + } + + internal static void DeadThreadNotification(Thread deadThread, + UIntPtr markedColor) + { + StealDead(Thread.CurrentThread, deadThread, markedColor); + } + + /// + /// This method attempts to take values from the passed-in queue + /// and place it in the 'this' queue. The method is tolerant + /// of concurrent attempts to steal from the fromQueue. The + /// 'fromThread' is assumed to be dead, which means that no + /// additions to its queue is possible. + /// + /// Rather than reading the old mark value from the header word + /// of the tail object in the 'fromQueue', the new mark value in + /// said object is going to be 'markedColor' + /// + [NoBarriers] + internal static void StealDead(Thread toThread, + Thread fromThread, + UIntPtr markedColor) + { + ThreadHeaderQueueThread myToThread = MixinThread(toThread); + ThreadHeaderQueueThread myFromThread = MixinThread(fromThread); + myToThread.gcQueue.StealFromDead(ref myFromThread.gcQueue, + markedColor); + } + + [NoBarriers] + private void StealFromDead(ref ThreadHeaderQueue fromQueue, + UIntPtr markedColor) + { + // It is assumed that there are no concurrent accesses to + // fromQueue. The thread owning the queue is supposed to + // be dead, and there should only be one other thread + // trying to steal the dead thread's queue. + if (Thread.VolatileRead(ref fromQueue.head) != + Thread.VolatileRead(ref fromQueue.stolenHead)) { + UIntPtr fromHead, fromTail; + this.ongoingUpdates++; + ThreadHeaderQueue.transferAttempt = true; + if (fromQueue.StealList(out fromHead, out fromTail)) { + // Prepend the stolen list segment to our list + Object tailObject = Magic.fromAddress(fromTail); +#if ATOMIC_PUSH + // NOTE: We don't try to be thread-safe on this.head + VTable.Assert(GcMark(tailObject) == markedColor); + SetQueueField(tailObject, this.head + markedColor); + Thread.VolatileWrite(ref this.head, fromHead); +#else // ATOMIC_PUSH + // REVIEW: We don't really need LOCK CMPXCHG, but + // we do need CMPXCHG (needs to be atomic with + // respect to the current processor, only). + UIntPtr oldHead; + UIntPtr foundHead = Thread.VolatileRead(ref this.head); + do { + oldHead = foundHead; + SetQueueField(tailObject, oldHead + markedColor); + foundHead = Interlocked.CompareExchange(ref this.head, + fromHead, + oldHead); + } while (foundHead != oldHead); +#endif // ATOMIC_PUSH + } + this.ongoingUpdates--; + } + } + + [NoBarriers] + private bool StealList(out UIntPtr outStolenHead, + out UIntPtr outStolenTail) + { + while (true) { + UIntPtr thisStolen = Thread.VolatileRead(ref this.stolenHead); + UIntPtr thisHead = Thread.VolatileRead(ref this.head); + while (thisStolen != thisHead) { + if (thisHead==TAIL_MARKER && thisStolen==UIntPtr.Zero) { + // A new thread has been created, and we caught the + // thread partway through ThreadHeaderQueue.Reset. + break; + } + VTable.Assert(thisHead != TAIL_MARKER, "thisHead should not be TAIL_MARKER if it is not equal to stolenHead"); + if (CompareAndSwap(ref this.stolenHead, + thisHead, + thisStolen)) { + // We managed to steal part of the list. + // Find the end of the stolen list segment. + outStolenHead = thisHead; + Object obj = Magic.fromAddress(thisHead); + UIntPtr next = QueueLink(obj); + while (next != thisStolen) { + obj = Magic.fromAddress(next); + next = QueueLink(obj); + } + outStolenTail = Magic.addressOf(obj); + return true; + } + thisStolen = Thread.VolatileRead(ref this.stolenHead); + thisHead = Thread.VolatileRead(ref this.head); + } + if (this.ongoingUpdates == 0 && + Thread.VolatileRead(ref this.stolenHead) == + Thread.VolatileRead(ref this.head)) { + // There is nothing to steal. + outStolenHead = UIntPtr.Zero; + outStolenTail = UIntPtr.Zero; + return false; + } + // Someone must be in the process of inserting something + // into the queue (or stealing something from it). + Thread.Yield(); + } + } + + internal struct LocalList { + + + private UIntPtr head; + + /// + /// Is the queue empty? + /// + internal bool IsEmpty() { + return this.head == UIntPtr.Zero; + } + + /// + /// This method attempts to take values from the passed-in queue + /// and place it in the 'this' local queue. It assumes that the + /// 'this' local queue is not concurrently added to. However, + /// the method is tolerant of concurrent attempts to steal from + /// the fromQueue. + /// + /// If any values are stolen the method returns true. + /// + [NoBarriers] + internal bool StealFrom(Thread fromThread, UIntPtr markedColor) + { + ThreadHeaderQueueThread myFromThread = MixinThread(fromThread); + return this.StealFrom(ref myFromThread.gcQueue, markedColor); + } + + [NoBarriers] + private bool StealFrom(ref ThreadHeaderQueue fromQueue, + UIntPtr markedColor) + { + UIntPtr fromHead, fromTail; + if (fromQueue.StealList(out fromHead, out fromTail)) { + // Prepend the stolen list segment to our list + Object tailObject = Magic.fromAddress(fromTail); + VTable.Assert(ThreadHeaderQueue.GcMark(tailObject) == + markedColor); + SetQueueField(tailObject, this.head + markedColor); + this.head = fromHead; + return true; + } else { + return false; + } + } + + /// + /// Unlink a value from the head of the queue. This + /// method is NOT thread safe. The method is supposed to be + /// called only by the thread that populates the queue. + /// + [Inline] + [DisableNullChecks] + internal Object Pop(UIntPtr markedColor) + { + VTable.Assert(!this.IsEmpty(), "Queue is empty!"); + Object obj = Magic.fromAddress(this.head); + VTable.Assert(ThreadHeaderQueue.GcMark(obj) == markedColor); + UIntPtr newHead = QueueLink(obj); + this.head = newHead; + SetQueueField(obj, markedColor); + return obj; + } + + } + + } + +} diff --git a/base/Kernel/Bartok/GCs/Trace.cs b/base/Imported/Bartok/runtime/shared/GCs/Trace.cs similarity index 94% rename from base/Kernel/Bartok/GCs/Trace.cs rename to base/Imported/Bartok/runtime/shared/GCs/Trace.cs index 95c588f..cb250ad 100644 --- a/base/Kernel/Bartok/GCs/Trace.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/Trace.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,10 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - namespace System.GCs { internal class Trace { diff --git a/base/Kernel/Bartok/GCs/Transitions.cs b/base/Imported/Bartok/runtime/shared/GCs/Transitions.cs similarity index 84% rename from base/Kernel/Bartok/GCs/Transitions.cs rename to base/Imported/Bartok/runtime/shared/GCs/Transitions.cs index 405e495..ef562e8 100644 --- a/base/Kernel/Bartok/GCs/Transitions.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/Transitions.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,10 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - namespace System.GCs { using System.Runtime.CompilerServices; @@ -16,12 +16,12 @@ namespace System.GCs #if SINGULARITY using Microsoft.Singularity; - using Microsoft.Singularity.X86; #endif #if SINGULARITY_KERNEL using Microsoft.Singularity.Scheduling; #endif + [AccessedByRuntime("Referenced in halforgc.asm")] internal class Transitions { @@ -141,7 +141,7 @@ namespace System.GCs internal static unsafe void ThreadStart() { ThreadContext *currentThreadContext = Processor.GetCurrentThreadContext(); - TakeMutatorControlNoGC(currentThreadContext); + TakeInitialMutatorControl(currentThreadContext); } #else // To be called at the beginning of a thread @@ -160,7 +160,7 @@ namespace System.GCs // To be called at the beginning of a thread internal static void ThreadStart(int currentThreadIndex) { VTable.Assert(InDormantState(currentThreadIndex)); - TakeMutatorControlNoGC(currentThreadIndex); + TakeInitialMutatorControl(currentThreadIndex); } #endif // SINGULARITY @@ -174,6 +174,8 @@ namespace System.GCs #if SINGULARITY [AccessedByRuntime("called from halforgc.asm")] + [NoStackLinkCheckTrans] // This is called from __pushStackMark, which cannot tolerate an + // ABI call to allocate a new stack internal static unsafe void LeaveManagedSpace(ThreadContext *currentThreadContext) { @@ -182,6 +184,8 @@ namespace System.GCs // To be used for returning from calls to outside managed space [AccessedByRuntime("called from halforgc.asm")] + [NoStackLinkCheckTrans] // This is called from __popStackMark, which cannot tolerate an + // ABI call to allocate a new stack [Inline] internal static unsafe void ReturnToManagedSpace(ThreadContext *currentThreadContext) @@ -254,21 +258,25 @@ namespace System.GCs #region Helper Functions (Independent of where statusWord is) [Inline] + [NoHeapAllocation] private static bool fInDormantState(int statusWord) { return ((statusWord & DormantState) != 0); } [Inline] + [NoHeapAllocation] private static bool fInMutatorState(int statusWord) { return ((statusWord & MutatorState) != 0); } [Inline] + [NoHeapAllocation] private static bool fHasGCRequest(int statusWord) { return ((statusWord & GCRequest) != 0); } [Inline] + [NoHeapAllocation] private static bool fUnderGCControl(int statusWord) { return ((statusWord & GCControl) != 0); } @@ -335,7 +343,7 @@ namespace System.GCs #if SINGULARITY [AccessedByRuntime("referenced from halasm.asm")] - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe void RestoreMutatorControlIfNeeded() { ThreadContext *currentThreadContext = @@ -349,15 +357,21 @@ namespace System.GCs // when an kernel exception is supposed to pass over an // application stack segment. int oldValue, newValue; + bool consumedSignal = false; + int threadIndex = currentThreadContext->threadIndex; do { oldValue = currentThreadContext->gcStates; if (fUnderGCControl(oldValue)) { - Thread.WaitForGCEvent(currentThreadContext->threadIndex); + Thread.WaitForGCEvent(threadIndex); + consumedSignal = true; } newValue = ((oldValue|MutatorState|OtherDormantState) & ~(DormantState|OtherMutatorState)); } while (!CompareAndSwap(ref currentThreadContext->gcStates, newValue, oldValue)); + if (consumedSignal) { + Thread.SignalGCEvent(threadIndex); + } } } #endif @@ -386,45 +400,72 @@ namespace System.GCs VTable.Deny(fUnderGCControl(statusWord)); } - [NoInline] - [StackLinkCheck] - private static void TakeMutatorControlSlow(ref int statusWord, - int currentThreadIndex) + private static void TakeInitialMutatorControl(ref int statusWord, + int currentThreadIndex) { - while (true) { - if (SwitchToMutatorState(ref statusWord)) { - break; - } - if (SwitchToMutatorStateWithGCRequest(ref statusWord)) { - Thread currentThread = - Thread.threadTable[currentThreadIndex]; - GC.InvokeCollection(currentThread); - break; - } - if (fUnderGCControl(statusWord)) { - Thread.WaitForGCEvent(currentThreadIndex); - } + if (!SwitchToMutatorState(ref statusWord)) { + TakeInitialMutatorControlSlow(ref statusWord, + currentThreadIndex); } + VTable.Assert(fInMutatorState(statusWord)); + VTable.Deny(fInDormantState(statusWord)); + VTable.Deny(fUnderGCControl(statusWord)); } [NoInline] - [StackLinkCheck] + // [StackLinkCheck] Removed due to call from ReturnToManagedSpace + private static void TakeMutatorControlSlow(ref int statusWord, + int currentThreadIndex) + { + TakeMutatorControlSlow(ref statusWord, currentThreadIndex, + true, true); + } + + [NoInline] + // [StackLinkCheck] Removed due to call from LeaveManagedSpace private static void TakeMutatorControlSlowNoGC(ref int statusWord, int currentThreadIndex) { + TakeMutatorControlSlow(ref statusWord, currentThreadIndex, + false, true); + } + + private static + void TakeInitialMutatorControlSlow(ref int statusWord, + int currentThreadIndex) + { + TakeMutatorControlSlow(ref statusWord, currentThreadIndex, + false, false); + } + + private static void TakeMutatorControlSlow(ref int statusWord, + int currentThreadIndex, + bool allowGC, + bool preserveSignals) + { + bool consumedSignal = false; while (true) { if (SwitchToMutatorState(ref statusWord)) { break; } if (SwitchToMutatorStateWithGCRequest(ref statusWord)) { - // Do not call the GC, even though a GC handshake - // has been requested. + if (allowGC) { + Thread currentThread = + Thread.threadTable[currentThreadIndex]; + if (currentThread != null) { + GC.InvokeCollection(currentThread); + } + } break; } if (fUnderGCControl(statusWord)) { Thread.WaitForGCEvent(currentThreadIndex); + consumedSignal = true; } } + if (consumedSignal && preserveSignals) { + Thread.SignalGCEvent(currentThreadIndex); + } } [Inline] @@ -465,15 +506,14 @@ namespace System.GCs private static void ReleaseGCControl(ref int statusWord, int threadIndex) { - VTable.Assert(fInDormantState(statusWord)); - VTable.Assert(fHasGCRequest(statusWord)); - VTable.Assert(fUnderGCControl(statusWord)); // There is only one possible transition out of this state. int oldValue, newValue; do { oldValue = statusWord; - newValue = ((oldValue|DormantState)& - ~(MutatorState|GCRequest|GCControl)); + VTable.Assert(fInDormantState(oldValue)); + VTable.Assert(fHasGCRequest(oldValue)); + VTable.Assert(fUnderGCControl(oldValue)); + newValue = (oldValue& ~(GCRequest|GCControl)); } while (!CompareAndSwap(ref statusWord, newValue, oldValue)); Thread.SignalGCEvent(threadIndex); } @@ -504,6 +544,7 @@ namespace System.GCs newStatus = (oldStatus & ~GCRequest); } while (!CompareAndSwap(ref statusWord, newStatus, oldStatus)); } + #endregion #if SINGULARITY @@ -539,6 +580,7 @@ namespace System.GCs #if SINGULARITY + [NoBarriers] [PreInitRefCounts] internal static void Initialize() { @@ -559,6 +601,11 @@ namespace System.GCs internal static unsafe void ReturnFromManagedSpace() { ThreadContext *currentThreadContext = Processor.GetCurrentThreadContext(); +#if SINGULARITY_KERNEL + // In Singularity kernel this function is the exit point of a kernel ABI. + // If the thread is requested to be aborted, we stop it right here + currentThreadContext->thread.ProcessAbortIfRequested(AbortRequestSource.ABIExit); +#endif TransferMutatorControl(currentThreadContext); } @@ -571,6 +618,11 @@ namespace System.GCs { ThreadContext *currentThreadContext = Processor.GetCurrentThreadContext(); +#if SINGULARITY_KERNEL + // In Singularity kernel this function is the exit point of a kernel ABI. + // If the thread is requested to be aborted, we stop it right here + currentThreadContext->thread.ProcessAbortIfRequested(AbortRequestSource.ABINoGCExit); +#endif TakeDormantControl(currentThreadContext); } @@ -588,6 +640,7 @@ namespace System.GCs } [Inline] + [NoHeapAllocation] internal static unsafe bool InMutatorState(ThreadContext *threadContext) { return (threadContext != null && @@ -666,6 +719,13 @@ namespace System.GCs threadContext->threadIndex); } + internal static unsafe + void TakeInitialMutatorControl(ThreadContext *threadContext) + { + TakeInitialMutatorControl(ref threadContext->gcStates, + threadContext->threadIndex); + } + [Inline] internal static unsafe void TransferMutatorControl(ThreadContext *currentThreadContext) @@ -710,7 +770,9 @@ namespace System.GCs [Inline] internal static unsafe void ReleaseGCControl(int threadIndex) { ThreadContext *threadContext = GetThreadContext(threadIndex); - ReleaseGCControl(ref threadContext->gcStates, threadIndex); + if (threadContext != null) { + ReleaseGCControl(ref threadContext->gcStates, threadIndex); + } } [Inline] @@ -728,10 +790,12 @@ namespace System.GCs ClearGCRequest(ref threadContext->gcStates, threadIndex); } } + #else private static int[] gcReadyTable; + [NoBarriers] [PreInitRefCounts] internal static void Initialize() { @@ -807,6 +871,12 @@ namespace System.GCs currentThreadIndex); } + internal static void TakeInitialMutatorControl(int currentThreadIndex) + { + TakeInitialMutatorControl(ref gcReadyTable[currentThreadIndex], + currentThreadIndex); + } + [Inline] internal static bool TakeGCControl(int threadIndex) { return TakeGCControl(ref gcReadyTable[threadIndex]); @@ -826,6 +896,7 @@ namespace System.GCs internal static void ClearGCRequest(int threadIndex) { ClearGCRequest(ref gcReadyTable[threadIndex], threadIndex); } + #endif #endregion diff --git a/base/Imported/Bartok/runtime/shared/GCs/TrialDeletion.cs b/base/Imported/Bartok/runtime/shared/GCs/TrialDeletion.cs new file mode 100644 index 0000000..b385c68 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/TrialDeletion.cs @@ -0,0 +1,456 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +// #define DEBUG + +namespace System.GCs { + + using Microsoft.Bartok.Options; + using Microsoft.Bartok.Runtime; + using System.Runtime.InteropServices; + using System.Runtime.CompilerServices; + using System.Threading; + using System.Collections; + + [RequiredByBartok] + internal class TrialDeletion { + [PreInitRefCounts] + public static void Initialize() { + Object plcLinkObj = BootstrapMemory.Allocate(typeof(PLCLink)); + firstPLCLink = + (PLCLink*)(Magic.addressOf(plcLinkObj)+PostHeader.Size); + plcListVtable = ((RuntimeType)typeof(PLCLink[])).classVtable; + plcListChunkBytes = + ObjectLayout.ArraySize(plcListVtable, plcListChunkLength); + } + + + /* + * Reserve bits in the RS field for marking purposes, + * and for flagging acyclic objects. + */ + [MixinConditional("TrialDeletion")] + [Mixin(typeof(RCCollector.RSMasks))] + internal new struct RSMasks { + internal const uint markFlag = 0x40000000; + internal const uint acyclicFlag = 0x20000000; + } + + [MixinConditional("TrialDeletion")] + [Mixin(typeof(Microsoft.Bartok.Runtime.PreHeader))] + internal unsafe struct PreHeader { + internal PLCLink* plcLink; + } + + // Each link in the "potentially leaked cycles" list. + internal unsafe struct PLCLink { + // The next link in the list of potentially leaked cycles. + public PLCLink* next; + + // Address of an object lying on a potentially leaked cycle; + public UIntPtr objAddr; + } + + private static unsafe PLCLink* getPLCLink(Object obj) { + return ((PreHeader)(obj.preHeader)).plcLink; + } + + private static unsafe void setPLCLink(Object obj, PLCLink* link) { + ((PreHeader)(obj.preHeader)).plcLink = link; + } + + private static InternalIncrementer internalIncrementer; + private static InternalDecrementer internalDecrementer; + private static InternalScanner internalScanner; + private static InternalReclaimer internalReclaimer; + + private static PLCLink* firstPLCLink; + private static VTable plcListVtable; + private static UIntPtr plcListChunkBytes; + private static PLCLink* plcListChunk; + private static uint numPLCChunks; + + private const uint plcListChunkLength = 1 << 12; + private const uint maxPLCChunks = 256; + + private static ulong maxCyclicGarbage; + private static ulong totalCyclicGarbage; + private static uint cycleCollections; + private static bool forceCycleCollectionAtEnd { + get { return false; } + } + + [PreInitRefCounts] + public static unsafe void Initialize() { + internalIncrementer = + (InternalIncrementer)BootstrapMemory. + Allocate(typeof(InternalIncrementer)); + internalDecrementer = + (InternalDecrementer)BootstrapMemory. + Allocate(typeof(InternalDecrementer)); + internalScanner = + (InternalScanner)BootstrapMemory. + Allocate(typeof(InternalScanner)); + internalReclaimer = + (InternalReclaimer)BootstrapMemory. + Allocate(typeof(InternalReclaimer)); + } + + + [Inline] + [ManualRefCounts] + private static unsafe void addToPLCList(Object obj) { + // Check if free PLC links are available. + if (plcListChunk == null) { + if (numPLCChunks < maxPLCChunks) { + // Allocate a chunk of PLC links. + int threadIndex = Thread.GetCurrentThreadIndex(); + Thread t = Thread.threadTable[threadIndex]; + UIntPtr resultAddr = + t.segregatedFreeList. + Allocate(plcListChunkBytes, + plcListVtable.baseAlignment); + Object result = Magic.fromAddress(resultAddr); + result.REF_STATE = 2 & ~countingONFlagMask; + result.vtable = plcListVtable; + numPLCChunks++; + + // Point to the first element + // in the allocated array of PLC links and string + // the elements into a free PLC links' list. + plcListChunk = (PLCLink*) + (resultAddr+PostHeader.Size+UIntPtr.Size); + for (PLCLink* link = plcListChunk; + link < plcListChunk+ + plcListChunkLength-1; + link++) { + link->next = link+1; + } + } else { + processPLCList(); + } + } + VTable.Assert(plcListChunk != null, + @"plcListChunk != null"); + + // Insert object into the PLC list by moving a link + // from the free PLC links' chunk. + firstPLCLink->objAddr = Magic.addressOf(obj); + PLCLink* currPLCLink = plcListChunk; + plcListChunk = plcListChunk->next; + currPLCLink->next = firstPLCLink; + firstPLCLink = currPLCLink; + firstPLCLink->objAddr = UIntPtr.Zero; + + // Point the object to its link in the PLC list. + setPLCLink(obj, firstPLCLink); + } + + [ManualRefCounts] + private static unsafe void removeFromPLCList(PLCLink* pLinkAddr) { + // The object needs to be removed from the PLC list. + PLCLink* currPLCLink = pLinkAddr; +#if DEBUG + VTable.Assert(currPLCLink->next != null, + @"currPLCLink->next != null"); +#endif // DEBUG + + PLCLink* linkToSkip = currPLCLink->next; + PLCLink* linkAfter = linkToSkip->next; + currPLCLink->next = linkAfter; + linkToSkip->next = plcListChunk; + plcListChunk = linkToSkip; + + // Update the PLC link pointer in the following object. + if (linkAfter != null) { + UIntPtr nextObjAddr = linkAfter->objAddr; + Object nextObj = Magic.fromAddress(nextObjAddr); +#if DEBUG + VTable.Assert(getPLCLink(nextObj) == linkToSkip, + @"getPLCLink(nextObj) == linkToSkip"); +#endif // DEBUG + + setPLCLink(nextObj, currPLCLink); + } + } + + [ManualRefCounts] + private static unsafe void processPLCList() { + int startTicks = 0; + bool enableGCTiming = VTable.enableGCTiming; + if (enableGCTiming) { + VTable.enableGCTiming = false; + startTicks = Environment.TickCount; + } + if (VTable.enableGCWatermarks) { + MemoryAccounting.RecordHeapWatermarks(); + } + +#if DEBUG + VTable.Assert(firstPLCLink->objAddr == UIntPtr.Zero, + @"firstPLCLink->objAddr == UIntPtr.Zero"); +#endif // DEBUG + + // Let S be the subgraph of heap objects reachable from + // the PLC list. Decrement counts due to references in S. + for (PLCLink* link = firstPLCLink->next; link != null; + link = link->next) { + UIntPtr objAddr = link->objAddr; + VTable.Assert(objAddr != UIntPtr.Zero, + @"objAddr != UIntPtr.Zero"); + + Object obj = Magic.fromAddress(objAddr); + VTable.Assert((obj.REF_STATE & + countingONFlagMask) != 0, + @"(obj.REF_STATE & + countingONFlagMask) != 0"); + + uint refState = obj.REF_STATE; + if ((refState & markFlagMask) == 0) { + obj.REF_STATE = refState | markFlagMask; + internalDecrementer.Traverse(objAddr); + } + } + + // Objects that now have non-zero counts are those that + // have references external to S incident on them. + // Recompute counts due to reachability from such objects. + for (PLCLink* link = firstPLCLink->next; link != null; + link = link->next) { + UIntPtr objAddr = link->objAddr; + internalScanner.Traverse(objAddr); + } + + // String together objects with reference count + // of zero for reclamation. + internalReclaimer.Initialize(); + for (PLCLink* link = firstPLCLink->next; link != null; + link = link->next) { + UIntPtr objAddr = link->objAddr; + internalReclaimer.Traverse(objAddr); + } + ulong reclaimedBytes = 0; + Object reclaimedObj = internalReclaimer.ReclaimedObjects; + while (reclaimedObj != null) { + if (VTable.enableGCProfiling) { + UIntPtr size = ObjectLayout.Sizeof(reclaimedObj); + reclaimedBytes += (ulong)size; + } + Object nextReclaimedObj = getNextLink(reclaimedObj); + SegregatedFreeList.Free(reclaimedObj); + reclaimedObj = nextReclaimedObj; + } + + // Recycle the PLC list. + if (firstPLCLink->next != null) { + PLCLink* lastPLCLink = firstPLCLink; + do { + lastPLCLink = lastPLCLink->next; + } while (lastPLCLink->next != null); + lastPLCLink->next = plcListChunk; + plcListChunk = firstPLCLink->next; + firstPLCLink->next = null; + } + + // Release the memory used up by work lists. + UIntPtrQueue.ReleaseStandbyPages(null); + + SegregatedFreeList.RecycleGlobalPages(); + SegregatedFreeList.CommitFreedData(); + GC.newBytesSinceGC = UIntPtr.Zero; + + if (enableGCTiming) { + int elapsedTicks = Environment.TickCount - startTicks; + System.GC.gcTotalTime += elapsedTicks; + if (System.GC.maxPauseTime < elapsedTicks) { + System.GC.maxPauseTime = elapsedTicks; + } + System.GC.pauseCount++; + VTable.enableGCTiming = true; + } + + if (VTable.enableGCProfiling) { + if (maxCyclicGarbage < reclaimedBytes) { + maxCyclicGarbage = reclaimedBytes; + } + totalCyclicGarbage += reclaimedBytes; + cycleCollections++; + } + } + + + private class InternalDecrementer : NonNullReferenceVisitor { + [ManualRefCounts] + internal void Traverse(UIntPtr objAddr) { + Object obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + while (!this.workList.IsEmpty) { + objAddr = this.workList.Read(); + obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + } + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + Object obj = Magic.fromAddress(objAddr); + uint refState = obj.REF_STATE; + if ((refState & countingONFlagMask) == 0) { + return; + } + uint refCount = refState & refCountMask; + VTable.Assert(refCount > 0, + @"refCount > 0"); + + refState--; + if ((refState & markFlagMask) != 0) { + obj.REF_STATE = refState; + } else { + obj.REF_STATE = refState | markFlagMask; + this.workList.Write(objAddr); + } + } + + private UIntPtrQueue workList; + } + + private class InternalScanner : NonNullReferenceVisitor { + [ManualRefCounts] + internal unsafe void Traverse(UIntPtr objAddr) { + this.Visit(&objAddr); + while (!this.workList.IsEmpty) { + objAddr = this.workList.Read(); + Object obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + } + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + Object obj = Magic.fromAddress(objAddr); + uint refState = obj.REF_STATE; + if ((refState & countingONFlagMask) == 0 || + (refState & markFlagMask) == 0) { + return; + } + obj.REF_STATE = refState & ~markFlagMask; + + uint refCount = refState & refCountMask; + if (refCount > 0) { + // This object isn't leaked data, so + // clear the PLC link in its header. + setPLCLink(obj, null); + internalIncrementer.Traverse(objAddr); + } else { + this.workList.Write(objAddr); + } + } + + private UIntPtrQueue workList; + } + + private class InternalIncrementer : NonNullReferenceVisitor { + [ManualRefCounts] + internal void Traverse(UIntPtr objAddr) { + Object obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + while (!this.workList.IsEmpty) { + objAddr = this.workList.Read(); + obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + } + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + Object obj = Magic.fromAddress(objAddr); + uint refState = obj.REF_STATE; + if ((refState & countingONFlagMask) == 0) { + return; + } + uint refCount = refState & refCountMask; + VTable.Assert(refCount < refCountMask, + @"refCount < refCountMask"); + + refState++; + if ((refState & markFlagMask) != 0) { + // The object hasn't been visited either + // by the scanner or the incrementer. + obj.REF_STATE = refState & ~markFlagMask; + this.workList.Write(objAddr); + // This object isn't leaked data, so + // clear the PLC link in its header. + setPLCLink(obj, null); + } else if ((refState & refCountMask) == 1) { + // The object has been visited in the + // past, but only by the scanner. + obj.REF_STATE = refState; + this.workList.Write(objAddr); + // This object isn't leaked data, so + // clear the PLC link in its header. + setPLCLink(obj, null); + } else { + obj.REF_STATE = refState; +#if DEBUG + // The PLC link in this object's header + // should already be cleared. + PLCLink* pLinkAddr = getPLCLink(obj); + VTable.Assert(pLinkAddr == null, + @"pLinkAddr == UIntPtr.Zero"); +#endif // DEBUG + } + } + + private UIntPtrQueue workList; + } + + private class InternalReclaimer : NonNullReferenceVisitor { + internal Object ReclaimedObjects; + + [ManualRefCounts] + internal void Initialize() { + this.ReclaimedObjects = null; + } + + [ManualRefCounts] + internal unsafe void Traverse(UIntPtr objAddr) { + this.Visit(&objAddr); + while (!this.workList.IsEmpty) { + objAddr = this.workList.Read(); + Object obj = Magic.fromAddress(objAddr); + this.VisitReferenceFields(obj); + } + } + + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr* loc) { + UIntPtr objAddr = *loc; + Object obj = Magic.fromAddress(objAddr); + uint refState = obj.REF_STATE; + if ((refState & countingONFlagMask) == 0) { + return; + } + uint refCount = refState & refCountMask; + if (refCount == 0) { + setNextLink(obj, this.ReclaimedObjects); + obj.REF_STATE = ~countingONFlagMask; + this.ReclaimedObjects = obj; + this.workList.Write(objAddr); + } + } + + private UIntPtrQueue workList; + } + } +} diff --git a/base/Kernel/Bartok/GCs/UIntPtrQueue.cs b/base/Imported/Bartok/runtime/shared/GCs/UIntPtrQueue.cs similarity index 96% rename from base/Kernel/Bartok/GCs/UIntPtrQueue.cs rename to base/Imported/Bartok/runtime/shared/GCs/UIntPtrQueue.cs index 3bc64b0..108d690 100644 --- a/base/Kernel/Bartok/GCs/UIntPtrQueue.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/UIntPtrQueue.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,10 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - namespace System.GCs { using System.Runtime.CompilerServices; diff --git a/base/Kernel/Bartok/GCs/UIntPtrStack.cs b/base/Imported/Bartok/runtime/shared/GCs/UIntPtrStack.cs similarity index 93% rename from base/Kernel/Bartok/GCs/UIntPtrStack.cs rename to base/Imported/Bartok/runtime/shared/GCs/UIntPtrStack.cs index e60fdca..ce8eec7 100644 --- a/base/Kernel/Bartok/GCs/UIntPtrStack.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/UIntPtrStack.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,10 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - namespace System.GCs { using System.Runtime.CompilerServices; @@ -28,6 +28,7 @@ namespace System.GCs { } } + [Inline] internal void Write(UIntPtr value) { if (this.stackPtr == stackTop) { AdvancePage(); @@ -36,6 +37,7 @@ namespace System.GCs { this.stackPtr++; } + [Inline] internal void Write(UIntPtr value1, UIntPtr value2) { if (this.stackPtr + 2 <= stackTop) { *this.stackPtr = value1; @@ -47,6 +49,7 @@ namespace System.GCs { } } + [Inline] internal UIntPtr Read() { if (this.stackPtr == stackBottom) { RetractPage(); @@ -55,6 +58,7 @@ namespace System.GCs { return *this.stackPtr; } + [Inline] internal UIntPtr Read(out UIntPtr value2) { if (this.stackPtr - 2 >= stackBottom) { value2 = *(this.stackPtr - 1); diff --git a/base/Kernel/Bartok/GCs/UniversalWriteBarrier.cs b/base/Imported/Bartok/runtime/shared/GCs/UniversalWriteBarrier.cs similarity index 82% rename from base/Kernel/Bartok/GCs/UniversalWriteBarrier.cs rename to base/Imported/Bartok/runtime/shared/GCs/UniversalWriteBarrier.cs index a8d538e..943e00e 100644 --- a/base/Kernel/Bartok/GCs/UniversalWriteBarrier.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/UniversalWriteBarrier.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,19 +9,17 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - namespace System.GCs { using System.Runtime.CompilerServices; - internal unsafe abstract class UniversalWriteBarrier : WriteBarrier + internal unsafe abstract class UniversalWriteBarrier : RefWriteBarrier { [Inline] - protected override void CopyStructImpl(VTable vtable, + protected override void CopyStructImpl(Object srcObj, + Object dstObj, + VTable vtable, UIntPtr srcPtr, UIntPtr dstPtr) { diff --git a/base/Kernel/Bartok/GCs/UnmanagedPageList.cs b/base/Imported/Bartok/runtime/shared/GCs/UnmanagedPageList.cs similarity index 95% rename from base/Kernel/Bartok/GCs/UnmanagedPageList.cs rename to base/Imported/Bartok/runtime/shared/GCs/UnmanagedPageList.cs index d37490d..8298f1c 100644 --- a/base/Kernel/Bartok/GCs/UnmanagedPageList.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/UnmanagedPageList.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,10 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - namespace System.GCs { using System.Runtime.CompilerServices; diff --git a/base/Imported/Bartok/runtime/shared/GCs/Util.cs b/base/Imported/Bartok/runtime/shared/GCs/Util.cs new file mode 100644 index 0000000..b52d0fb --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/Util.cs @@ -0,0 +1,168 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + + +namespace System.GCs +{ + using System.Runtime.CompilerServices; + + // Util contains common functions for alignment and padding + // arithmetic and for calculating object sizes. + internal class Util + { + // WARNING: don't initialize any static fields in this class + // without manually running the class constructor at startup! + + [NoHeapAllocation] + internal static uint dwordAlign(uint numBytes) { + return ((numBytes+3) & ~3U); + } + + [NoHeapAllocation] + internal static UIntPtr dwordAlign(UIntPtr numBytes) { + return ((numBytes+3) & new UIntPtr(~3U)); + } + + [NoHeapAllocation] + static UIntPtr qwordAlign(UIntPtr addr) { + return ((addr+7) & new UIntPtr(~7U)); + } + + [Inline] + [NoHeapAllocation] + internal static bool IsAligned(uint bytes, uint size) + { + return (bytes & (size - 1)) == 0; + } + + [Inline] + [NoHeapAllocation] + internal static bool IsAligned(UIntPtr bytes, UIntPtr size) + { + return (bytes & (size - 1)) == UIntPtr.Zero; + } + + [Inline] + [NoHeapAllocation] + internal static uint Align(uint bytes, uint size) + { + return (bytes & ~(size - 1)); + } + + [Inline] + [NoHeapAllocation] + internal static UIntPtr Align(UIntPtr bytes, UIntPtr size) + { + return (bytes & ~(size - 1)); + } + + [Inline] + [NoHeapAllocation] + internal static UIntPtr UIntPtrAlign(UIntPtr bytes) + { + return Align(bytes, (UIntPtr) UIntPtr.Size); + } + + [Inline] + [NoHeapAllocation] + internal static uint Pad(uint data, uint size) + { + return ((data + (size - 1)) & ~(size - 1)); + } + + [Inline] + [NoHeapAllocation] + internal static UIntPtr Pad(UIntPtr data, UIntPtr size) + { + return ((data + (size - 1)) & ~(size - 1)); + } + + [Inline] + [NoHeapAllocation] + internal static UIntPtr UIntPtrPad(UIntPtr bytes) + { + return Pad(bytes, (UIntPtr) UIntPtr.Size); + } + + internal static unsafe void MemClear(UIntPtr startAddr, + UIntPtr size) + { +#if SINGULARITY + // On Singularity we use the common optimized functions. + Buffer.ZeroMemory((byte*)startAddr, (int)size); +#else + VTable.Assert(UIntPtr.Size !=4 || ((startAddr & ((UIntPtr)0x3)) == ((UIntPtr)0)), "Not-aligned address in Util.MemClear"); + VTable.Assert(UIntPtr.Size !=8 || ((startAddr & ((UIntPtr)0x7)) == ((UIntPtr)0)), "Not-aligned address in Util.MemClear"); + VTable.Assert((size & ((UIntPtr)0x3)) == ((UIntPtr)0), "size expected to be multiple of 4 (size of int) in Util.MemClear"); + Buffer.ZeroMemory((byte*)startAddr, size); +#endif + } + + internal static unsafe void MemCopy(UIntPtr toAddress, + UIntPtr fromAddress, + UIntPtr count) + { +#if SINGULARITY + // On Singularity we use the common optimized functions. + Buffer.MoveMemory((byte*)toAddress, (byte*)fromAddress, (int)count); +#else + int wordCount = (int) (count >> 2); + int *from = (int *) fromAddress; + int *to = (int *) toAddress; + for (int i = 0; i < wordCount; i++) { + to[i] = from[i]; + } +#endif + } + + internal static void PrintPageContents(UIntPtr page) + { + UIntPtr startAddr = PageTable.PageAddr(page); + UIntPtr endAddr = PageTable.PageAddr(page + 1); + PrintMemoryContents(startAddr, endAddr); + } + + internal static unsafe void PrintMemoryContents(UIntPtr startAddr, + UIntPtr endAddr) + { + if (UIntPtr.Size == 4) { + VTable.DebugPrint("Memory contents for {0,8:x8}..{1,8:x8}\n", + __arglist(startAddr, endAddr)); + for (UIntPtr addr = startAddr; addr < endAddr; addr += 32) { + UIntPtr *ptr = (UIntPtr *) addr; + // Only print 6 digits of the address in order to + // fit on 80 column output devices + uint printAddress = unchecked((uint) addr) & 0xffffff; + VTable.DebugPrint("{0,6:x6}: "+ + "{1,8:x8} {2,8:x8} {3,8:x8} {4,8:x8} "+ + "{5,8:x8} {6,8:x8} {7,8:x8} {8,8:x8}\n", + __arglist(printAddress, *ptr, *(ptr+1), + *(ptr+2), *(ptr+3), *(ptr+4), + *(ptr+5), *(ptr+6), *(ptr+7))); + } + } else { + VTable.DebugPrint("Memory contents for "+ + "{0,16:x16}..{1,16:x16}\n", + __arglist(startAddr, endAddr)); + for (UIntPtr addr = startAddr; addr < endAddr; addr += 32) { + UIntPtr *ptr = (UIntPtr *) addr; + uint printAddress = unchecked((uint) addr); + VTable.DebugPrint("{0,8:x8}: {1,16:x16} {2,16:x16} "+ + "{3,16:x16} {4,16:x16}\n", + __arglist(printAddress, *ptr, + *(ptr+1), *(ptr+2), *(ptr+3))); + } + } + } + + } + +} diff --git a/base/Kernel/Bartok/GCs/Verifier.cs b/base/Imported/Bartok/runtime/shared/GCs/Verifier.cs similarity index 97% rename from base/Kernel/Bartok/GCs/Verifier.cs rename to base/Imported/Bartok/runtime/shared/GCs/Verifier.cs index 7aaa35e..50c89de 100644 --- a/base/Kernel/Bartok/GCs/Verifier.cs +++ b/base/Imported/Bartok/runtime/shared/GCs/Verifier.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,10 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - //#define DEBUG_OFFSETTABLE namespace System.GCs { diff --git a/base/Imported/Bartok/runtime/shared/GCs/WriteBarrierCMS.cs b/base/Imported/Bartok/runtime/shared/GCs/WriteBarrierCMS.cs new file mode 100644 index 0000000..8964e67 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/WriteBarrierCMS.cs @@ -0,0 +1,138 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System.GCs { + + using Microsoft.Bartok.Runtime; + using System.Threading; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + //[NoBarriers] + internal unsafe class WriteBarrierCMS : UniversalWriteBarrier + { + + internal static WriteBarrierCMS instance; + + [StructLayout(LayoutKind.Sequential)] + struct FakeObjectBytes + { + public PreHeader preBytes; + public PostHeader postBytes; + } + private static FakeObjectBytes memoryForFakeObject; + + [NoBarriers] + [NoHeapAllocation] + internal static WriteBarrierCMS MakeEarlyInstance() + { + // We need a write barrier even if we haven't set up enough of the + // memory system to support allocating from bootstrap memory yet. + VTable vtable = + ((RuntimeType) typeof(WriteBarrierCMS)).classVtable; + UIntPtr numBytes = ObjectLayout.ObjectSize(vtable); + if (numBytes > (UIntPtr) sizeof(FakeObjectBytes)) { + return null; // too big to allocate in memoryForFakeObject + } + UIntPtr fakeObjectAddr; + fixed (PostHeader *middlePtr = & memoryForFakeObject.postBytes) { + fakeObjectAddr = (UIntPtr) middlePtr; + } + Object result = Magic.fromAddress(fakeObjectAddr); + *result.VTableFieldAddr = Magic.addressOf(vtable); + return (WriteBarrierCMS) result; + } + + [PreInitRefCounts] + [NoBarriers] + internal static new void Initialize() { + if (WriteBarrierCMS.instance == null) { + WriteBarrierCMS.instance = (WriteBarrierCMS) + BootstrapMemory.Allocate(typeof(WriteBarrierCMS)); + } + } + + // NOTE: this code uses ForceInline instead of Inline to indicate that + // inlining should occur even if the caller is huge. In general, this + // attribute should be used with great care. DO NOT USE IT ELSEWHERE + // IN THE RUNTIME UNLESS YOU ARE WILLING TO DOCUMENT YOUR USE IN + // IrSimpleInliner.cs AND Attributes.cs! AND NEVER USE IT IN + // APPLICATION OR OS CODE! + + [ForceInline] + protected override bool AllowFastPathImpl() + { + return CMSMarking.referenceCheckIsFast; + } + + [ForceInline] + [NoBarriers] + [NoStackLinkCheckTrans] + protected override Object AtomicSwapImpl(ref Object reference, + Object value, + int mask) + { + CMSMarking.ReferenceCheck(ref reference, value, mask); + UIntPtr resultAddr = + Interlocked.Exchange(Magic.toPointer(ref reference), + Magic.addressOf(value)); + return Magic.fromAddress(resultAddr); + } + + [ForceInline] + [NoBarriers] + [NoStackLinkCheckTrans] + protected override + Object AtomicCompareAndSwapImpl(ref Object reference, + Object newValue, + Object comparand, + int mask) + { + CMSMarking.ReferenceCheck(ref reference, newValue, mask); + UIntPtr resultAddr = + Interlocked.CompareExchange(Magic.toPointer(ref reference), + Magic.addressOf(newValue), + Magic.addressOf(comparand)); + return Magic.fromAddress(resultAddr); + } + + [Inline] + [NoBarriers] + [NoStackLinkCheckTrans] + protected override void CloneImpl(Object srcObject, Object dstObject) + { + // There is no need to keep track of initial writes, so do nothing! + CloneNoBarrier(srcObject, dstObject); + } + + [ForceInline] + [NoBarriers] + [NoStackLinkCheckTrans] + protected override void WriteImpl(UIntPtr *location, + Object value, + int mask) + { + CMSMarking.ReferenceCheck(location, value, mask); + *location = Magic.addressOf(value); + } + + [ForceInline] + [NoBarriers] + [NoStackLinkCheckTrans] + protected override void WriteImplByRef(ref Object location, + Object value, + int mask) + { + CMSMarking.ReferenceCheck(ref location, value, mask); + location = value; + } + } +} diff --git a/base/Imported/Bartok/runtime/shared/GCs/ZeroCountTable.cs b/base/Imported/Bartok/runtime/shared/GCs/ZeroCountTable.cs new file mode 100644 index 0000000..0bd627c --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/GCs/ZeroCountTable.cs @@ -0,0 +1,232 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +// #define DEBUG + +namespace System.GCs { + + using Microsoft.Bartok.Runtime; + using System.Threading; + using System.Runtime.InteropServices; + using System.Runtime.CompilerServices; + + /* This class implements the ZeroCountTable for DeferredReferenceCounting + * Collector. The ZCT is an array of 10000 (Consider dynamic sizing later) + * uint entries. Initially, we link all entries together as a free list + * using their array index. When object's RC goes to Zero, we take an + * entry from free list, and put the object's address in it. In that + * object's refState, the first bit(markFlagMask) is set to indicate + * that it is in ZCT. When an object's RC becomes larger than 1, it + * is removed from ZCT, its corresponding entry is cleared and put at the + * front of the freelist. The freelist is a LIFO one. + * + * - table[0] is never used, so that 0 can be used for check, instead of + * using -1 + * If ZCT running out of entries, we trigger GC. + */ + internal unsafe class ZeroCountTable { + // Note: 1. in this class, every method should be ManualRefCounted + // 2. all methods/fields are static, no need to allocate in + // BootstrapMemory. But the space for ZCT is dynamically + // allocated in a special pool + // 3. As the pool is marked as NonGC, pointers will not be traced, + // and the pool will not be scanned, therefore keep RC untouched + + // Internal variables + private static uint freeHead; + private static UIntPtr[] zeroCountTable; + private static uint maxEntries; + private static ZCTGarbagePicker zctGarbagePicker; + + private static UIntPtr tableSize; + + [PreInitRefCounts] + public static unsafe void Initialize() { + maxEntries = 1 << 16; + VTable UIntPtrArrayVtable = + ((RuntimeType)typeof(UIntPtr[])).classVtable; + tableSize = + ObjectLayout.ArraySize(UIntPtrArrayVtable, maxEntries); + + // Allocate a pool for ZCT + BumpAllocator entryPool = new BumpAllocator(PageType.NonGC); + UIntPtr memStart = MemoryManager.AllocateMemory(tableSize); + entryPool.SetZeroedRange(memStart, tableSize); + PageManager.SetStaticDataPages(memStart, tableSize); + + // Initialize ZCT + zeroCountTable = (UIntPtr[]) + DeferredReferenceCountingCollector. + AllocateArray(ref entryPool, + UIntPtrArrayVtable, + tableSize); + VTable.Assert(zeroCountTable != null, + @"zeroCountTable != null"); + + *(uint*)(Magic.addressOf(zeroCountTable)+PostHeader.Size) = + maxEntries; + VTable.Assert(zeroCountTable.Length == maxEntries, + @"zeroCountTable.Length == maxEntries"); + + // Build ZCT freeEntries list + freeHead = 1; + for (uint i = 1; i < maxEntries-1; i++) { + zeroCountTable[i]=(UIntPtr)(((i+1) << 2) | 0x01); + } + zeroCountTable[maxEntries-1] = (UIntPtr)0x01; + + zctGarbagePicker = + (ZCTGarbagePicker)BootstrapMemory. + Allocate(typeof(ZCTGarbagePicker)); + } + + + [Inline] + [ManualRefCounts] + [DisableBoundsChecks] + private static uint GetFreeEntry() { + VTable.Assert(freeHead != 0, + @"freeHead != 0"); + + uint entry = freeHead; + freeHead = ((uint)zeroCountTable[entry]) >> 2; + + return entry; + } + + [Inline] + [ManualRefCounts] + [DisableBoundsChecks] + private static void PutFreeEntry(uint index) { + zeroCountTable[index] = (UIntPtr)((freeHead << 2) | 0x01); + freeHead = index; + } + + [ManualRefCounts] + [DisableBoundsChecks] + public static void Add(Object obj) { + if (freeHead == 0) { + GC.Collect(); // GC may add object into the PLC buffer. + } + + uint refState = obj.REF_STATE; + if ((refState & + DeferredReferenceCountingCollector.markFlagMask) != 0) { + return; // GC may already put this object in ZCT + } + + uint position = GetFreeEntry(); + if ((refState & DeferredReferenceCountingCollector. + acyclicFlagMask) == 0) { + uint index = + DeferredReferenceCountingCollector.GetPLCIndex(obj); + if (index != 0) { + DeferredReferenceCountingCollector. + SetPLCIndex(obj, 0); + DeferredReferenceCountingCollector. + removeFromPLCBuffer(index); + } + } +#if DEBUG + else { + uint index = + DeferredReferenceCountingCollector.GetPLCIndex(obj); + VTable.Assert(index == 0, + @"index == 0"); + } +#endif // DEBUG + + zeroCountTable[position] = Magic.addressOf(obj); + obj.REF_STATE = position | + DeferredReferenceCountingCollector.markFlagMask | + DeferredReferenceCountingCollector.countingONFlagMask | + (refState & + DeferredReferenceCountingCollector.acyclicFlagMask); + } + + [ManualRefCounts] + public static void Remove(Object obj) { + uint refState = obj.REF_STATE; + uint position = refState & + DeferredReferenceCountingCollector.refCountMask; + PutFreeEntry(position); + + obj.REF_STATE = + DeferredReferenceCountingCollector.countingONFlagMask | + (refState & + DeferredReferenceCountingCollector.acyclicFlagMask); + } + + [ManualRefCounts] + [DisableBoundsChecks] + private static unsafe uint PurgeZCT(NonNullReferenceVisitor entryVisitor) + { + uint purgedSlots = 0; + for (uint i = 1; i < maxEntries; i++) { + UIntPtr content = zeroCountTable[i]; + if (((uint)content & 0x01) != 0) { + continue; // Node in free list, skip + } + purgedSlots++; + entryVisitor.Visit(&content); + } + + return purgedSlots; + } + + [ManualRefCounts] + public static bool OutOfEntries() { + return freeHead == 0; + } + + [ManualRefCounts] + public static bool isInZCT(Object obj) { + return (obj.REF_STATE & + DeferredReferenceCountingCollector. + markFlagMask) != 0; + } + + [ManualRefCounts] + public static void ProcessZeroCountTable() { + uint purgedSlots; + + do { + purgedSlots = PurgeZCT(zctGarbagePicker); + } while (purgedSlots > 0); + } + + public static long Size { + get { + return (long)tableSize; + } + } + + private class ZCTGarbagePicker : NonNullReferenceVisitor { + [ManualRefCounts] + internal override unsafe void Visit(UIntPtr *loc) { + UIntPtr objAddr = *loc; + Object obj = Magic.fromAddress(objAddr); + + // 1. remove from ZCT + Remove(obj); + // 2. decrement RC on objects retained via multiuseword + MultiUseWord muw = MultiUseWord.GetForObject(obj); + if (muw.IsMonitorOrInflatedTag()) { + MultiUseWord.RefCountGCDeadObjHook(muw); + } + // 3. add to deallocation list + DeferredReferenceCountingCollector.deallocateLazily(obj); + } + } + } +} + + diff --git a/base/Kernel/Bartok/ArrayHelper.cs b/base/Imported/Bartok/runtime/shared/System/ArrayHelper.cs similarity index 96% rename from base/Kernel/Bartok/ArrayHelper.cs rename to base/Imported/Bartok/runtime/shared/System/ArrayHelper.cs index 9f19038..1af7bbc 100644 --- a/base/Kernel/Bartok/ArrayHelper.cs +++ b/base/Imported/Bartok/runtime/shared/System/ArrayHelper.cs @@ -1,13 +1,14 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ /* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ +/* made to the Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// namespace System { @@ -20,7 +21,7 @@ namespace System { [RequiredByBartok] internal class ArrayHelper { - // these should probably all be replaced with calls to Buffer.MoveMemory. + // These should probably all be replaced with calls to Buffer.MoveMemory. [RequiredByBartok] public static void CopyBoolUp(bool[] srcArray, int srcOffset, bool[] dstArray, int dstOffset, @@ -628,7 +629,7 @@ namespace System { public static unsafe void InitArray(System.Array srcArray, System.Array dstArray) { #if !REFERENCE_COUNTING_GC && !DEFERRED_REFERENCE_COUNTING_GC - WriteBarrier.ArrayCopy(srcArray, 0, dstArray, 0, srcArray.Length); + Barrier.ArrayCopy(srcArray, 0, dstArray, 0, srcArray.Length); #else fixed (int *srcFieldPtr = &srcArray.field1) { void *src = srcArray.GetFirstElementAddress(srcFieldPtr); diff --git a/base/Kernel/Bartok/Finalizer.cs b/base/Imported/Bartok/runtime/shared/System/Finalizer.cs similarity index 91% rename from base/Kernel/Bartok/Finalizer.cs rename to base/Imported/Bartok/runtime/shared/System/Finalizer.cs index 76d9dc9..d215c2a 100644 --- a/base/Kernel/Bartok/Finalizer.cs +++ b/base/Imported/Bartok/runtime/shared/System/Finalizer.cs @@ -1,13 +1,14 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ /* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ +/* made to the Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// namespace System { @@ -81,7 +82,7 @@ namespace System { // never allocate or do anything else significant while holding the lock. #if SINGULARITY - bool disabled = Processor.DisableInterrupts(); + bool disabled = Processor.DisableLocalPreemption(); #endif spinLock.Acquire(); bool lockHeld = true; @@ -112,11 +113,11 @@ restart: lockHeld = false; spinLock.Release(); #if SINGULARITY - Processor.RestoreInterrupts(disabled); + Processor.RestoreLocalPreemption(disabled); #endif table = new UIntPtr[CandidateTable[i-1].Length*2]; #if SINGULARITY - disabled = Processor.DisableInterrupts(); + disabled = Processor.DisableLocalPreemption(); #endif spinLock.Acquire(); lockHeld = true; @@ -153,7 +154,7 @@ restart: VTable.Assert(table != null, "Candidate Algorithmic inconsistency"); // We have found the table! - table[index] = objPtr; + table[index] = Magic.addressOf(obj); // The following looks like a 64-bit porting issue (32-bit truncation). // But actually I'm just ensuring that object pointers have the low bit @@ -164,7 +165,7 @@ restart: if (lockHeld) { spinLock.Release(); #if SINGULARITY - Processor.RestoreInterrupts(disabled); + Processor.RestoreLocalPreemption(disabled); #endif } } @@ -184,7 +185,7 @@ restart: // we really need a lock around the whole thing. int logicalIndex = 0; #if SINGULARITY - bool disabled = Processor.DisableInterrupts(); + bool disabled = Processor.DisableLocalPreemption(); #endif spinLock.Acquire(); try { @@ -204,7 +205,7 @@ restart: finally { spinLock.Release(); #if SINGULARITY - Processor.RestoreInterrupts(disabled); + Processor.RestoreLocalPreemption(disabled); #endif } #endif // REFERENCE_COUNTING_GC @@ -316,10 +317,14 @@ restart: return; } +// HACK: Threading issues are preventing use of a Finalizer thread. +// Disabling for ARM so the GC can be used without it. +#if !OS_WINCE && !ISA_ARM WorkExistsForFinalizerThread = new AutoResetEvent(false); WaitForPendingShouldReturn = new ManualResetEvent(true); #if SINGULARITY_KERNEL + spinLock = new SpinLock(SpinLock.Types.Finalizer); finalizerThread = Thread.CreateThread(null, new ThreadStart(ThreadLoop)); VTable.Assert(finalizerThread != null); @@ -336,6 +341,7 @@ restart: finalizerThread.Start(); #endif // !SINGULARITY #endif // REFERENCE_COUNTING_GC +#endif // !OS_WINCE } /// @@ -362,6 +368,10 @@ restart: Interlocked.Decrement(ref WaitingToRun); } WaitForPendingShouldReturn.Set(); + + // now that the finalizer thread has shutdown, we need to prevent + // a GC from occuring + Interlocked.Increment(ref GC.allocationGCInhibitCount); #endif // REFERENCE_COUNTING_GC } @@ -389,6 +399,12 @@ restart: internal static void ReleaseCollectFinalizers() { #if !(REFERENCE_COUNTING_GC || DEFERRED_REFERENCE_COUNTING_GC) if (madeRunnable) { + // Review: For now we will assert against a GC running after the + // finalization thread has shut down. However, if this assert + // fires and the situation is somewhat reasonable, then we should + // allow a GC to occur. + VTable.Assert(running, + "Attempt to request finalizer while finalization thread is not running."); WaitForPendingShouldReturn.Reset(); WorkExistsForFinalizerThread.Set(); } @@ -401,7 +417,7 @@ restart: /// pointers. /// internal unsafe static - void VisitBootstrapData(NonNullReferenceVisitor visitor) + void VisitBootstrapData(DirectReferenceVisitor visitor) { #if !(REFERENCE_COUNTING_GC || DEFERRED_REFERENCE_COUNTING_GC) VisitAllRunFinalizer(visitor, true, false); @@ -417,7 +433,7 @@ restart: /// This method visits all the objects in the RunFinalizer structures. /// private unsafe static - void VisitAllRunFinalizer(NonNullReferenceVisitor visitor, + void VisitAllRunFinalizer(DirectReferenceVisitor visitor, bool copyFirst, bool markedOnly) { #if !(REFERENCE_COUNTING_GC || DEFERRED_REFERENCE_COUNTING_GC) @@ -451,8 +467,8 @@ restart: /// Find all candidates that have become unreachable. /// internal unsafe static - void ResurrectCandidates(NonNullReferenceVisitor forwardVisitor, - NonNullReferenceVisitor resurrectVisitor, + void ResurrectCandidates(DirectReferenceVisitor forwardVisitor, + DirectReferenceVisitor resurrectVisitor, bool copyFirst) { #if !(REFERENCE_COUNTING_GC || DEFERRED_REFERENCE_COUNTING_GC) @@ -469,7 +485,7 @@ restart: // involving the spinLock and any locking that occurs as part of a // GC provoked by an allocation attempt. #if SINGULARITY - bool disabled = Processor.DisableInterrupts(); + bool disabled = Processor.DisableLocalPreemption(); #endif spinLock.Acquire(); bool lockHeld = true; @@ -514,11 +530,11 @@ restart: lockHeld = false; spinLock.Release(); #if SINGULARITY - Processor.RestoreInterrupts(disabled); + Processor.RestoreLocalPreemption(disabled); #endif UIntPtr[] newTable = new UIntPtr[length]; #if SINGULARITY - disabled = Processor.DisableInterrupts(); + disabled = Processor.DisableLocalPreemption(); #endif spinLock.Acquire(); lockHeld = true; @@ -570,7 +586,7 @@ outer: if (lockHeld) { spinLock.Release(); #if SINGULARITY - Processor.RestoreInterrupts(disabled); + Processor.RestoreLocalPreemption(disabled); #endif } } @@ -609,7 +625,7 @@ outer: for (int j = entryStart; j < table.Length; j++) { if (table[j] != UIntPtr.Zero) { - Object obj = Magic.fromAddress(table[j]); + Object obj = Magic.fromAddress(table[j] & ~(UIntPtr)1); table[j] = UIntPtr.Zero; Finalizer.LastTableRun = i; Finalizer.LastEntryRun = j; diff --git a/base/Imported/Bartok/runtime/shared/System/GC.cs b/base/Imported/Bartok/runtime/shared/System/GC.cs new file mode 100644 index 0000000..8254591 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/System/GC.cs @@ -0,0 +1,701 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to the Bartok Depot and propagated to the Singularity Depot. */ +/*******************************************************************/ + + +namespace System +{ + using Microsoft.Bartok.Runtime; + using System.GCs; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using System.Threading; + +#if SINGULARITY + using Microsoft.Singularity; +#endif + + // The GC has only static members and doesn't require the serializable + // keyword. + [CCtorIsRunDuringStartup] + [RequiredByBartok] + [AccessedByRuntime("referenced in {hal,brt}{asm,forgc}.asm")] +#if SINGULARITY + [CLSCompliant(false)] +#endif + public sealed class GC + { + // This is a compiler intrinsic whose value is controlled by + // /StageControl.HeapSizeConfigurable. + internal static extern bool HeapSizeConfigurable { + [Intrinsic] + get; + } + // The *initial* maximum heap size, used if + // HeapSizeConfigurable is true. + internal static int MaxHeapPages = 96; + + // Bartok runtime "magic" function + // It saves the callee-save registers in a transition record and + // calls System.GC.CollectBody(thread, generation) + [ManualRefCounts] + [MethodImpl(MethodImplOptions.InternalCall)] + [GCAnnotation(GCOption.GCFRIEND)] + [StackBound(128)] + private static extern void CollectBodyTransition(Thread thread, + int generation); +#if SINGULARITY_KERNEL + internal static uint perfCounter = 6; +#else + internal static uint perfCounter = 5; +#endif + + [TrustedNonNull] + internal static Collector installedGC; + + private static bool isProfiling; + + // Flag to indicate to the collector that it should perform a + // retrace in a stop-the-world mode after finishing a mostly + // normal trace. This allows us to measure the cost of various + // barriers by disabling them. Currently only recognized by CONCMS. + internal static bool enableSTWRetrace; + + [RequiredByBartok] + private static Object dummyGlobal; // Used by KeepAlive + + [AccessedByRuntime("referenced in halasm.asm/brtasm.asm")] + internal static int allocationGCInhibitCount = 0; + + [Intrinsic] + internal static GCType gcType; + + [Intrinsic] + internal static WBType wbType; + + [Intrinsic] + internal static RemSetType remsetType; + + [Intrinsic] + internal static CopyScanType copyscanType; + +#if !SINGULARITY // Needed before Bartok runtime is initialized + [NoStackLinkCheck] +#endif + [PreInitRefCounts] + internal static void ConstructHeap() { + PageTable.Initialize(); + + MemoryManager.Initialize(); +#if OS_WINCE + UIntPtr heap_commit_size = new UIntPtr(1 << 16); +#elif SINGULARITY + UIntPtr heap_commit_size = new UIntPtr(1 << 16); +#else + UIntPtr heap_commit_size = new UIntPtr(1 << 20); +#endif + UIntPtr os_commit_size = MemoryManager.OperatingSystemCommitSize; + VTable.Assert(os_commit_size > UIntPtr.Zero); + VTable.Assert(heap_commit_size >= os_commit_size); + UIntPtr bootstrapSize; + if (UIntPtr.Size == 8) { + if (gcType == GCType.ConcurrentMSCollector) { + // increase bootstrap size so that + // the concurrent mark sweep collector will run on + // 64-bit Windows + bootstrapSize = (UIntPtr) 1<<16; + } else { + bootstrapSize = (UIntPtr) 1<<15; + } + } else { + bootstrapSize = (UIntPtr) 1<<14; + } + if (bootstrapSize < os_commit_size) { + bootstrapSize = os_commit_size; + } + BootstrapMemory.Initialize(bootstrapSize); + StaticData.Initialize(); + PageManager.Initialize(os_commit_size, heap_commit_size); + CallStack.Initialize(); + } + + // Called after the GC is up, but before multi-threading is enabled. + internal static void FinishInitializeThread() + { + PageManager.FinishInitializeThread(); + } + + // NB: This is called from VTable.Initialize() + [PreInitRefCounts] + static GC() // Class Constructor (cctor) + { + GC.Initialize(); + switch(gcType) { +#if !SINGULARITY || ADAPTIVE_COPYING_COLLECTOR + case GCType.AdaptiveCopyingCollector: { + AdaptiveCopyingCollector.Initialize(); + GC.installedGC = AdaptiveCopyingCollector.instance; + break; + } +#endif +#if !SINGULARITY || MARK_SWEEP_COLLECTOR + case GCType.MarkSweepCollector: { + MarkSweepCollector.Initialize(); + GC.installedGC = MarkSweepCollector.instance; + break; + } +#endif +#if !SINGULARITY || TABLE_MARK_SWEEP_COLLECTOR + case GCType.TableMarkSweepCollector: { + SimpleMarkSweepCollector.Initialize(); + GC.installedGC = SimpleMarkSweepCollector.instance; + break; + } +#endif +#if !SINGULARITY || SEMISPACE_COLLECTOR + case GCType.SemispaceCollector: { + SemispaceCollector.Initialize(); + GC.installedGC = SemispaceCollector.instance; + break; + } +#endif +#if !SINGULARITY || SLIDING_COLLECTOR + case GCType.SlidingCollector: { + SlidingCollector.Initialize(); + GC.installedGC = SlidingCollector.instance; + break; + } +#endif +#if !SINGULARITY || CONCURRENT_MS_COLLECTOR + case GCType.ConcurrentMSCollector: { + ConcurrentMSCollector.Initialize(); + GC.installedGC = ConcurrentMSCollector.instance; + break; + } +#endif +#if !SINGULARITY || ATOMIC_RC_COLLECTOR + case GCType.AtomicRCCollector: { + AtomicRCCollector.Initialize(); + GC.installedGC = AtomicRCCollector.instance; + break; + } +#endif +#if !SINGULARITY + case GCType.ReferenceCountingCollector: { + ReferenceCountingCollector.Initialize(); + GC.installedGC = ReferenceCountingCollector.instance; + break; + } +#endif +#if !SINGULARITY + case GCType.DeferredReferenceCountingCollector: { + DeferredReferenceCountingCollector.Initialize(); + GC.installedGC = DeferredReferenceCountingCollector.instance; + break; + } +#endif +#if !SINGULARITY || NULL_COLLECTOR + case GCType.NullCollector: { + VTable.Assert(wbType == 0, "No need for a write barrier"); + GC.installedGC = + (NullCollector) + BootstrapMemory.Allocate(typeof(NullCollector)); + break; + } +#endif +#if !SINGULARITY + case GCType.CoCoMSCollector: { + CoCoMSCollector.Initialize(); + GC.installedGC = CoCoMSCollector.instance; + break; + } +#endif + default: { + VTable.NotReached("Unknown GC type: "+gcType); + break; + } + } + GC.installedGC.NewThreadNotification(Thread.initialThread, true); + GC.installedGC.ThreadStartNotification(Thread.initialThread.threadIndex); + } + + [NoBarriers] + [PreInitRefCounts] + private static void Initialize() + { + VTable.Assert(GC.allocationGCInhibitCount == 0); + GC.allocationGCInhibitCount = 1; + Transitions.Initialize(); + Barrier.Initialize(); + } + +#if !SINGULARITY + private static DateTime LogMessage(String message) + { + DateTime currentTime = System.DateTime.Now; + System.Text.StringBuilder sb = new System.Text.StringBuilder(); + String hourString = currentTime.Hour.ToString(); + if (hourString.Length == 1) { + sb.Append('0'); + } + sb.Append(hourString); + sb.Append(':'); + String minuteString = currentTime.Minute.ToString(); + if (minuteString.Length == 1) { + sb.Append('0'); + } + sb.Append(minuteString); + sb.Append(':'); + String secondString = currentTime.Second.ToString(); + if (secondString.Length == 1) { + sb.Append('0'); + } + sb.Append(secondString); + sb.Append('.'); + String milliString = currentTime.Millisecond.ToString(); + if (milliString.Length < 3) { + sb.Append('0'); + } + if (milliString.Length < 2) { + sb.Append('0'); + } + sb.Append(milliString); + sb.Append(": "); + sb.Append(message); + Console.Out.WriteLine(sb.ToString()); + return currentTime; + } +#endif + + // This empty class allows us to easily spot the HeapCritialSection + // mutex when debugging. + private class HeapMonitor + { + } + + internal static void CheckForNeededGCWork(Thread currentThread) { + installedGC.CheckForNeededGCWork(currentThread); + } + +#if SINGULARITY + // This is a Singularity special not in the CLR + public static void Verify() + { + DebugStub.WriteLine("Calling VerifyHeap()"); + bool oldGCVerify = VTable.enableGCVerify; + VTable.enableGCVerify = true; + Collect(); + VTable.enableGCVerify = oldGCVerify; + DebugStub.WriteLine("Verification finished."); + } + + public static void PerformanceCounters(out int collectorCount, + out long collectorMillis, + out long collectorBytes) + { + collectorCount = BaseCollector.gcTotalCount; + collectorMillis = BaseCollector.gcTotalTime; + collectorBytes = BaseCollector.gcTotalBytes; + } +#endif + + // Garbage Collect all generations. + [ManualRefCounts] + public static void Collect() + { + installedGC.Collect(Thread.CurrentThread, MaxGeneration); + } + + public static void Collect(int generation) + { + if (generation < 0) { + throw new ArgumentOutOfRangeException( + "generation", + "Argument should be positive!"); + } + installedGC.Collect(Thread.CurrentThread, generation); + } + + // This method is to be used by various runtime routines that + // need to trigger a collection. If there are different kinds + // of collection, most likely a minor collection will be + // performed. + internal static void InvokeCollection(Thread currentThread) + { + installedGC.Collect(currentThread, + (int)SpecialGeneration.InvokeCollection); + } + + // This method is to be used by various runtime routines that + // need to trigger a collection. If there are different kinds + // of collections, most likely a major collection will be + // performed. + internal static void InvokeMajorCollection(Thread currentThread) + { + installedGC.Collect(currentThread, + (int)SpecialGeneration.InvokeMajorCollection); + } + + // This method is to be used by various garbage collector routines + // that need to trigger a scanning for roots of the call stacks + // of the program threads. Calling this method is not meant + // to trigger a new garbage collection cycle. + internal static void InvokeStackScanOnly(Thread currentThread) + { + installedGC.Collect(currentThread, + (int)SpecialGeneration.InvokeStackScanOnly); + } + + // This method is to be used by various garbage collector routines + // that need to trigger an update of all references on the call + // stacks of the program threads. Calling this method is not meant + // to trigger a new garbage collection cycle. + internal static void InvokeStackFixupOnly(Thread currentThread) + { + installedGC.Collect(currentThread, + (int)SpecialGeneration.InvokeStackFixupOnly); + } + + internal static void CollectTransition(Thread currentThread, + int generation) + { + CollectBodyTransition(currentThread, generation); + } + + // DO NOT REMOVE THE StackLinkCheck ATTRIBUTE FROM THIS + // FUNCTION! + // + // It is called from native code System.GC.CollectBodyTransition + // that only has an attribute for the amount of stack space that + // the native code requires. + [StackLinkCheck] + [ManualRefCounts] + [AccessedByRuntime("called from halforgc.asm/brtforgc.asm")] + private static unsafe Thread CollectBody(Thread currentThread, + int generation) + { + // NOTE: Refrain from creating any GC safe points in this + // method. That includes allocation and system calls, + // which in turn includes print statements. Putting a GC + // safe point before the call to the collector may cause + // infinite recursion. Putting a GC safe point after the + // call to the collector may cause recursion when other + // threads are triggering collections before the current + // thread exits this method. (Bjarne) + int currentThreadIndex = currentThread.threadIndex; + installedGC.CollectStoppable(currentThreadIndex, generation); + return Thread.threadTable[currentThreadIndex]; + } + + [RequiredByBartok] + [AccessedByRuntime("called from brtasm.asm")] + [Inline] + [ManualRefCounts] + internal static Object AllocateObject(VTable vtable) + { + return AllocateObject(vtable, Thread.CurrentThread); + } + + [RequiredByBartok] + [NoInline] + [CalledRarely] + [ManualRefCounts] + internal static Object AllocateObjectNoInline(VTable vtable) + { + return AllocateObject(vtable); + } + + [RequiredByBartok] + [Inline] + [ManualRefCounts] + internal static Object AllocateObject(VTable vtable, + Thread currentThread) + { + VTable.Deny(Transitions.UnderGCControl(currentThread.threadIndex)); + return installedGC.AllocateObject(vtable, currentThread); + } + + [RequiredByBartok] + [NoInline] + [CalledRarely] + [ManualRefCounts] + internal static Object AllocateObjectNoInline(VTable vtable, + Thread currentThread) + { + return AllocateObject(vtable, currentThread); + } + + [RequiredByBartok] + [Inline] + [ManualRefCounts] + internal static Object AllocateObject(VTable vtable, + UIntPtr numBytes, + uint baseAlignment, + Thread currentThread) + { + VTable.Deny(Transitions.UnderGCControl(currentThread.threadIndex)); + return installedGC.AllocateObject(vtable, + numBytes, + baseAlignment, + currentThread); + } + + // Currently only used by MDIL as the compiler explicitly adds the + // RegisterCandidate call in normal builds. In the future, however, we + // would probably like to have this detail expressed in C# rather than + // in the compiler sources. + [RequiredByBartok] + [NoInline] + [ManualRefCounts] + internal static Object AllocateFinalizableObject(VTable vtable) { + Object obj = AllocateObject(vtable); + Finalizer.RegisterCandidate(obj); + return obj; + } + + [RequiredByBartok] + [Inline] + [ManualRefCounts] + internal static Array AllocateVector(VTable vtable, int numElements) + { + return AllocateVector(vtable, numElements, Thread.CurrentThread); + } + + [Inline] + [ManualRefCounts] + internal static Array AllocateVector(VTable vtable, + int numElements, + Thread currentThread) + { + VTable.Deny(Transitions.UnderGCControl(currentThread.threadIndex)); + return installedGC.AllocateVector(vtable, numElements, + currentThread); + } + + [RequiredByBartok] + [Inline] + [ManualRefCounts] + internal static Array AllocateArray(VTable vtable, int rank, + int totalElements) + { + return AllocateArray(vtable, rank, totalElements, + Thread.CurrentThread); + } + + [Inline] + [ManualRefCounts] + internal static Array AllocateArray(VTable vtable, int rank, + int totalElements, + Thread currentThread) + { + VTable.Deny(Transitions.UnderGCControl(currentThread.threadIndex)); + return installedGC.AllocateArray(vtable, rank, totalElements, + currentThread); + } + + [RequiredByBartok] + [Inline] + [ManualRefCounts] + internal static String AllocateString(int stringLength) + { + return AllocateString(stringLength, Thread.CurrentThread); + } + + [Inline] + [ManualRefCounts] + internal static String AllocateString(int stringLength, + Thread currentThread) + { + VTable.Deny(Transitions.UnderGCControl(currentThread.threadIndex)); + return installedGC.AllocateString(stringLength, currentThread); + } + + public static int GetGeneration(Object obj) + { + return installedGC.GetGeneration(obj); + } + + public static int MaxGeneration { + get { return installedGC.MaxGeneration; } + } + + internal enum SpecialGeneration { + InvokeCollection = -1, + InvokeMajorCollection = -2, + // Not all collectors use these next two values. + InvokeStackScanOnly = -3, + InvokeStackFixupOnly = -4 + } + + internal static bool IsUserCollectionRequest(int gen) + { + return gen >= 0; + } + + internal static bool IsInternalCollectionRequest(int gen) + { + return gen == (int)SpecialGeneration.InvokeCollection + || gen == (int)SpecialGeneration.InvokeMajorCollection; + } + + internal static bool IsRealCollectionRequest(int gen) + { + return gen >= 0 + || gen == (int)SpecialGeneration.InvokeCollection + || gen == (int)SpecialGeneration.InvokeMajorCollection; + } + + [NoInline] + [RequiredByBartok] + public static void KeepAlive(Object obj) + { + dummyGlobal = obj; + dummyGlobal = null; + } + + public static void WaitForPendingFinalizers() + { + Finalizer.WaitForPending(); + } + + public static long GetTotalMemory(bool forceFullCollection) + { + long size = installedGC.TotalMemory; + if (!forceFullCollection) { + return size; + } + // If we force a full collection, we will run the finalizers on all + // existing objects and do a collection until the value stabilizes. + // The value is "stable" when either the value is within 5% of the + // previous call to installedGC.TotalMemory, or if we have been sitting + // here for more than x times (we don't want to loop forever here). + for (int reps = 0; reps < 8; reps++) { + WaitForPendingFinalizers(); + Collect(); + long newSize = installedGC.TotalMemory; + long bound = size / 20; // 5% + long diff = newSize - size; + size = newSize; + if (diff >= -bound && diff <= bound) { + break; + } + } + return size; + } + + public static void SuppressFinalize(Object obj) + { + if (obj == null) { + throw new ArgumentNullException("obj"); + } + Finalizer.SuppressCandidate(obj); + } + + internal static void nativeSuppressFinalize(Object obj) { + Finalizer.SuppressCandidate(obj); + } + + public static void ReRegisterForFinalize(Object obj) + { + if (obj == null) { + throw new ArgumentNullException("obj"); + } + Finalizer.RegisterCandidate(obj); + } + + public static int GetGeneration(WeakReference wo) + { + Object obj = wo.Target; + if (obj == null) { + throw new ArgumentException("wo", "target already collected"); + } + return GetGeneration(obj); + } + + public static void SetProfiler(GCProfiler profiler) + { + installedGC.SetProfiler(profiler); + isProfiling = true; + } + + public static bool IsProfiling + { + get { + return isProfiling; + } + } + + internal static void ProfileAllocation(Object obj) + { + if (isProfiling) { + installedGC.ProfileAllocation(obj); + } + } + + private static void SetCleanupCache() + { + // REVIEW: will not ever clean up these caches (such as Assembly + // strong names) + } + + internal static void EnableHeap() + { + VTable.Assert(GC.allocationGCInhibitCount == 1); + GC.allocationGCInhibitCount = 0; + CollectorStatistics.Initialize(); + CollectorStatistics.Event(GCEvent.CreateHeap); + GC.installedGC.EnableHeap(); + Finalizer.StartFinalizerThread(); + } + + internal static void Shutdown() + { + if(installedGC != null) { + installedGC.Shutdown(); + } + } + + // Called on VM shutdown. + internal static void DestructHeap() + { + if (installedGC != null) { + installedGC.DestructHeap(); + } + } + + internal static void NewThreadNotification(Thread newThread, + bool initial) + { + GC.installedGC.NewThreadNotification(newThread, initial); + } + + internal static void DeadThreadNotification(Thread deadThread) + { + GC.installedGC.DeadThreadNotification(deadThread); + } + + internal static void ThreadStartNotification(int currentThreadIndex) + { + GC.installedGC.ThreadStartNotification(currentThreadIndex); + } + + internal static void ThreadEndNotification(Thread dyingThread) + { + GC.installedGC.ThreadEndNotification(dyingThread); + } + + internal static void ThreadDormantGCNotification(int threadIndex) + { + GC.installedGC.ThreadDormantGCNotification(threadIndex); + } + + } + +} diff --git a/base/Imported/Bartok/runtime/shared/System/Headers.cs b/base/Imported/Bartok/runtime/shared/System/Headers.cs new file mode 100644 index 0000000..7f1da8c --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/System/Headers.cs @@ -0,0 +1,201 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + + +// The header part of an object is split into a PreHeader and a +// PostHeader component. The PreHeader component is the part of the +// object residing in memory preceding the pointer to the object and +// the PostHeader component is the part at and after the pointer to +// the object. +// +// The StageControl.ObjectHeaderKind option controls which kind of +// header to use. However, this option provides only basic control. +// Fields can be added to the headers outside the control of this +// StageControl option by using the mixin mechanism. +// +// The "Default" header kind has a MultiUseWord in the PreHeader and a +// VTable in the PostHeader. The "PostRC" header kind has a +// MultiUseWord in the PreHeader and a refState followed by a VTable +// in the PostHeader. + +namespace Microsoft.Bartok.Runtime { + + using Microsoft.Bartok.Options; + + using System; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using Interlocked = System.Threading.Interlocked; + + [RequiredByBartok] + internal struct PreHeader + { + [Intrinsic] + internal static int Size; + } + + [RequiredByBartok] +#if SINGULARITY + [AccessedByRuntime("Accessed from halexn.cpp")] +#endif + internal struct PostHeader + { + [Intrinsic] + internal static int Size; + } + + [MixinConditional("ObjectHeaderDefault")] + [MixinConditional("ObjectHeaderPostRC")] + [Mixin(typeof(PreHeader))] + [RequiredByBartok] + internal struct PreHeaderDefault /* : PreHeader */ + { + internal MultiUseWord muw; + } + + [MixinConditional("ObjectHeaderDefault")] + [Mixin(typeof(PostHeader))] + [RequiredByBartok] + internal struct PostHeaderDefault /* : PostHeader */ + { + +#if SINGULARITY + [AccessedByRuntime("accessed from halexn.cpp")] +#else + [RequiredByBartok] +#endif + [NoBarriers] + internal VTable vtableObject; + } + + [RequiredByBartok] + [MixinConditional("ObjectHeaderPostRC")] + [Mixin(typeof(PostHeader))] + [StructLayout(LayoutKind.Sequential)] + internal struct PostHeaderRC /* : PostHeader */ + { + [CompilerInitField(2)] + internal uint refState; + +#if !SINGULARITY + [RequiredByBartok] +#endif + [NoBarriers] + internal VTable vtableObject; + + } + + // This class does not add any fields, nor should it ever be + // referenced by any other code. Its sole purpose is to add + // implementations of the get_vtable, set_vtable, and + // get_VTableFieldAddr methods to all objects. + [MixinConditional("ObjectHeaderDefault")] + [Mixin(typeof(System.Object))] + internal class ObjectPostVTable + { +#if SINGULARITY + [AccessedByRuntime("Accessed from halexn.cpp")] +#endif + [RequiredByBartok] + internal new PostHeaderDefault postHeader; + + internal new VTable vtable { + [Inline] + get { return this.postHeader.vtableObject; } + [Inline] + set { this.postHeader.vtableObject = value; } + } + + internal unsafe new UIntPtr* VTableFieldAddr { + [ManualRefCounts] + [NoBarriers] + get { return Magic.toPointer(ref this.postHeader.vtableObject); } + } + + } + + // This class does not add any fields, nor should it ever be + // referenced by any other code. Its sole purpose is to add + // implementations of the get_vtable, set_vtable, and + // get_VTableFieldAddr methods to all objects. + [MixinConditional("ObjectHeaderPostRC")] + [Mixin(typeof(System.Object))] + internal class ObjectPostVTableRC + { + [RequiredByBartok] + internal new PostHeaderRC postHeader; + + internal new VTable vtable { + [Inline] + get { return this.postHeader.vtableObject; } + [Inline] + set { this.postHeader.vtableObject = value; } + } + + internal unsafe new UIntPtr* VTableFieldAddr { + [ManualRefCounts] + [NoBarriers] + get { return Magic.toPointer(ref this.postHeader.vtableObject); } + } + + } + + // This class does not add any fields, nor should it ever be + // referenced by any other code. Its sole purpose is to declare + // that all objects have get_muw, set_muw, and compareExchangeMUW + // methods when the object header includes a MultiUseWord field. + [MixinConditional("ObjectHeaderDefault")] + [MixinConditional("ObjectHeaderPostRC")] + [Mixin(typeof(System.Object))] + internal class ObjectMUW { + + internal extern MultiUseWord muw { + [Inline] + get; + [Inline] + set; + } + + [Inline] + internal extern UIntPtr CompareExchangeMUW(UIntPtr newValue, + UIntPtr oldValue); + + } + + // This class does not add any fields, nor should it ever be + // referenced by any other code. Its sole purpose is to add + // implementations of the get_muw, set_muw, and compareExchangeMUW + // methods to all objects when they have a MultiUseWord field. + [MixinConditional("ObjectHeaderDefault")] + [MixinConditional("ObjectHeaderPostRC")] + [Mixin(typeof(ObjectMUW))] + internal class ObjectPreMUW : ObjectMUW { + + internal new PreHeaderDefault preHeader; + + internal new MultiUseWord muw { + [Inline] + get { return this.preHeader.muw; } + [Inline] + set { this.preHeader.muw = value; } + } + + [Inline] + internal new UIntPtr CompareExchangeMUW(UIntPtr newValue, + UIntPtr oldValue) + { + return Interlocked.CompareExchange(ref this.preHeader.muw.value, + newValue, oldValue); + } + + } + +} diff --git a/base/Imported/Bartok/runtime/shared/System/Magic.cs b/base/Imported/Bartok/runtime/shared/System/Magic.cs new file mode 100644 index 0000000..6e98aac --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/System/Magic.cs @@ -0,0 +1,194 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + + +// Provides a number of "magic" casting functions needed by trusted components +// of the runtime. + +namespace Microsoft.Bartok.Runtime { + + using System; + using System.Runtime.CompilerServices; + using System.Threading; + + internal sealed class Magic { + + // Returns the offset of the vtable from the base of an object. + // The base is the beginning of the PostHeader. + // Typically callers should access Object's vtable property directly, + // but this is used by a few places that may be abusing the vtable slot + // to hold values of type UIntPtr. + internal static extern UIntPtr OffsetOfVTable { + [NoHeapAllocation] + [Intrinsic] + get; + } + + // Cast an object to a UIntPtr, equivalent to MSIL conv.u. + [Intrinsic] + [NoHeapAllocation] + internal static extern UIntPtr addressOf(Object o); + + // The toPointer family of methods cast a managed pointer to T to an + // unmanaged pointer to T. Exceptions are the Object and VTable + // versions which return UIntPtr* instead since Object* and VTable* + // are illegal. These are equivalent to MSIL conv.u. + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern UIntPtr *toPointer(ref Object o); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern bool *toPointer(ref bool o); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern char *toPointer(ref char o); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern float *toPointer(ref float o); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern double *toPointer(ref double o); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern sbyte *toPointer(ref sbyte o); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern short *toPointer(ref short o); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern int *toPointer(ref int o); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern long *toPointer(ref long o); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern IntPtr *toPointer(ref IntPtr o); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern byte *toPointer(ref byte o); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern ushort *toPointer(ref ushort o); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern uint *toPointer(ref uint o); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern ulong *toPointer(ref ulong o); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern UIntPtr *toPointer(ref UIntPtr o); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern UIntPtr *toPointer(ref VTable o); + + // Cast a UIntPtr to an object. This has no MSIL instruction + // equivalent. This is used, for example, at the birth point of an + // object. + [Intrinsic] + [NoHeapAllocation] + internal static extern Object fromAddress(UIntPtr v); + + // The various toXXX methods are unchecked (and therefore unsafe) + // downcasts. They are used in places where vtables may be invalid (and + // hence checked casts would fail). Uses as hand-optimizations should + // be avoided when possible. + [Intrinsic] + [NoHeapAllocation] + internal static extern Thread toThread(Object o); + + [Intrinsic] + [NoHeapAllocation] + internal static extern Monitor toMonitor(Object o); + + [Intrinsic] + [NoHeapAllocation] + internal static extern EMU toEMU(Object o); + + [Intrinsic] + [NoHeapAllocation] + internal static extern VTable toVTable(Object o); + + [Intrinsic] + [NoHeapAllocation] + internal static extern Array toArray(Object o); + + [Intrinsic] + [NoHeapAllocation] + internal static extern UIntPtr[] toUIntPtrVector(Array a); + + [Intrinsic] + [NoHeapAllocation] + internal static extern String toString(Object o); + + [Intrinsic] + [NoHeapAllocation] + internal static extern RuntimeType toRuntimeType(Object o); + + [Intrinsic] + [NoHeapAllocation] + internal static extern Type toType(Object o); + + [Intrinsic] + [NoHeapAllocation] + internal static extern uint[] toUIntArray(Object o); + + [Intrinsic] + internal static extern WeakReference toWeakReference(Object o); + + // Performs an indirect call on the pointer 'p'. + // This is equivalent to MSIL calli on the signature ()->void. + [Intrinsic] + internal static extern void calli(System.UIntPtr p); + + // Performs an indirect call on the pointer 'p' with argument 'v'. + // This is equivalent to MSIL calli on the signature (UIntPtr)->void. + [Intrinsic] + internal static extern void calli(System.UIntPtr p, System.UIntPtr v); + + // Calls the Finalize method on an object. This is needed for the + // finalizer implementation because C# will not allow direct calls to + // finalizers. + [Intrinsic] + internal static extern void callFinalizer(Object o); + + // These are very dangerous utilties that essentially do + // + // [o + offset] = data + // + // They are currently used for undoing heap stores collected in the + // tryall/atomic logs. Any other use should be cleared by at least the + // Bartok team. + [Intrinsic] + internal static extern void SetAt(Object o, UIntPtr offset, + UIntPtr data); + [Intrinsic] + internal static extern void SetAt(Object o, UIntPtr offset, + Object data); + } + +} + diff --git a/base/Kernel/Bartok/MultiUseWord.cs b/base/Imported/Bartok/runtime/shared/System/MultiUseWord.cs similarity index 89% rename from base/Kernel/Bartok/MultiUseWord.cs rename to base/Imported/Bartok/runtime/shared/System/MultiUseWord.cs index 14c4dd9..52c55cd 100644 --- a/base/Kernel/Bartok/MultiUseWord.cs +++ b/base/Imported/Bartok/runtime/shared/System/MultiUseWord.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,9 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// /* @@ -46,25 +47,34 @@ different kinds of object in the heap. Placing this bit in the MUW memory accounting to be invoked at any time (e.g. after a crash during a GC). - MUW structure ------------- -The structure of the MUW is as follows: +The structure of the MUW on a 32-bit system is as follows: - | 31 | 30 2 | 1 0 | - | mark | payload | tag | + | 31 3 | 2 | 1 0 | + | payload |mark| tag | -The single mark bit is the MSB. By convention, all modules using the -mark bit must (a) work with the world stopped and (b) leave all object's -mark bits 0 after their operation. (They may assume this is true when -they start). This restriction avoids needing to mask off the mark bit -in common code paths in this file. +And on a 64-bit system: + + | 63 3 | 2 | 1 0 | + | payload |mark| tag | + +On 32-bit systems, this allows us to support pointers through the entire +4 GB address range. However, it means that pointers must be constrained +to fit in the limited space of the payload. For instance, the Monitor +is 8-byte aligned so the low 3 bits are available for encoding the tag +and mark. + +By convention, all modules using the mark bit must (a) work with the +world stopped and (b) leave all object's mark bits 0 after their operation. +(They may assume this is true when they start). This restriction avoids +needing to mask off the mark bit in common code paths in this file. The payload field forms the majority of the MUW. The contents of the -payload are taken from bits 2..30 in the MUW, padded with two 0-bits +payload are taken from bits 3..31 in the MUW, padded with three 0-bits at the least significant end. This means that the payload can hold a -pointer to a 4-byte aligned address. +pointer to an 8-byte aligned address. The tag values distinguish between four states that the MUW can be in: @@ -73,7 +83,7 @@ The tag values distinguish between four states that the MUW can be in: 01 => The payload holds the object's hash code or StringState bits. -10 => The payload refers to the Monitor for the object. +10 => The payload refers to the object's Monitor. 11 => The payload refers to an external multi-use object (EMU). @@ -130,6 +140,9 @@ namespace System { // Structure of the multi-use word internal const uint TAG_MASK = 0x00000003; + // Hash codes are restricted to positive int32, even on 64-bit systems. + internal const int HASHCODE_MASK = 0x7ffffff8; + //---------------------------------------------------------------------- // // Constructors @@ -149,8 +162,11 @@ namespace System { __arglist(tag, payload)); } + // Objects encoded in the payload must be 8-byte aligned. However, + // this alignment refers to the object's data portion, so that is + // what we store. internal MultiUseWord(uint tag, Object payload) { - UIntPtr addr = Magic.addressOf(payload); + UIntPtr addr = Magic.addressOf(payload) + PostHeader.Size; VTable.Assert(!(payload is uint)); VTable.Assert((tag == MONITOR_TAG) || (tag == INFLATED_TAG)); @@ -221,10 +237,13 @@ namespace System { // Asserts that the payload is non-null and that the object that it // refers to has strong enough alignment requirements for the number // of payload bits available. + // + // Recall that for objects we store a pointer to the data portion rather + // than to the PostHeader, since this is what the caller has aligned. internal unsafe Object GetRefPayload() { UIntPtr payload = (UIntPtr) this.GetPayload(); - Object result = Magic.fromAddress(payload); + Object result = Magic.fromAddress(payload - PostHeader.Size); VTable.Assert(result != null); VTable.Assert(result.vtable != null); VTable.Assert((payload & (result.vtable.baseAlignment - 1)) == 0); @@ -235,7 +254,7 @@ namespace System { // cast. This is needed during GCs that use the vtable word. internal unsafe EMU GetEMURefPayload() { UIntPtr payload = (UIntPtr) this.GetPayload(); - EMU result = EMU.FromAddress(payload); + EMU result = EMU.FromAddress(payload - PostHeader.Size); VTable.Assert(result != null); VTable.Assert((payload & (result.vtable.baseAlignment - 1)) == 0); return result; @@ -247,7 +266,7 @@ namespace System { [ManualRefCounts] internal static MultiUseWord GetForObject(Object obj) { - MultiUseWord result = obj.preHeader.muw; + MultiUseWord result = ((ObjectPreMUW) obj).muw; DebugPrint("GetForObject({0:x}) = {1:x}\n", __arglist(Magic.addressOf(obj), result.value)); @@ -255,36 +274,47 @@ namespace System { return result; } + [ManualRefCounts] internal static void SetForObject(Object obj, MultiUseWord w) { DebugPrint("SetForObject({0:x}, {1:x})\n", __arglist(Magic.addressOf(obj), w.value)); - obj.preHeader.muw = w; + ((ObjectPreMUW) obj).muw = w; } - internal static MultiUseWord CompareExchangeForObject(Object obj, - MultiUseWord newWord, - MultiUseWord oldWord) { + private static + MultiUseWord CompareExchangeForObject(Object obj, + MultiUseWord newWord, + MultiUseWord oldWord) + { DebugPrint("CompareExchangeForObject({0:x}, new={1:x}, old={2:x})\n", __arglist(Magic.addressOf(obj), newWord.value, oldWord.value)); - UIntPtr saw = - Interlocked.CompareExchange(ref obj.preHeader.muw.value, - newWord.value, oldWord.value); + CompareExchangeForObject(obj, newWord.value, oldWord.value); DebugPrint("CompareExchangeForObject saw {0:x}\n", __arglist(saw)); return new MultiUseWord(saw); } + internal static UIntPtr CompareExchangeForObject(Object obj, + UIntPtr newValue, + UIntPtr oldValue) + { + UIntPtr saw = + ((ObjectPreMUW) obj).CompareExchangeMUW(newValue, oldValue); + return saw; + } + // Get and set the underlying UIntPtr value held in the same // header word that is used for the MUW. These methods are needed // in MemoryAccounting where the header field is re-used (after // making a private copy of the MUW value). + [ManualRefCounts] internal static UIntPtr GetValForObject(Object obj) { - UIntPtr result = obj.preHeader.muw.value; + UIntPtr result = GetForObject(obj).value; DebugPrint("GetValForObject({0:x}) = {1:x}\n", __arglist(Magic.addressOf(obj), result)); @@ -292,24 +322,23 @@ namespace System { return result; } + [ManualRefCounts] internal static void SetValForObject(Object obj, UIntPtr w) { DebugPrint("SeValtForObject({0:x}, {1:x})\n", __arglist(Magic.addressOf(obj), w)); - obj.preHeader.muw.value = w; + SetForObject(obj, new MultiUseWord(w)); } internal static UIntPtr PAYLOAD_MASK { get { return (UIntPtr.Size == 8)? - (UIntPtr)0x7ffffffffffffffc : - (UIntPtr)0x7ffffffc; } + (UIntPtr)0xfffffffffffffff8 : + (UIntPtr)0xfffffff8; } } internal static UIntPtr MARK_BIT_MASK { - get { return (UIntPtr.Size == 8)? - (UIntPtr)0x8000000000000000 : - (UIntPtr)0x80000000; } - } + get { return (UIntPtr)0x04; } + } //---------------------------------------------------------------------- // @@ -328,8 +357,10 @@ namespace System { // allow shakeCtr to overshoot shakeLim then it doesn't matter: we'll // just go around another shakeLim times. +#if SHAKE private static int shakeCtr = 0; private static int shakeLim = 1; +#endif [Inline] internal static bool Occasionally() { @@ -394,7 +425,6 @@ namespace System { // directly from the MUW. if (muw.IsUnused() && (!Occasionally())) { - Thread thread = Thread.CurrentThread; MultiUseWord newWord; DebugPrint("MUW currently unused for {0:x}, allocating monitor\n", __arglist(Magic.addressOf(obj))); @@ -405,7 +435,7 @@ namespace System { #if REFERENCE_COUNTING_GC || DEFERRED_REFERENCE_COUNTING_GC DebugPrint("+rc on monitor {0:x}\n", __arglist(Magic.addressOf(m))); - IncrementRefCount(m); + NonNullIncrementRefCount(m); #else EMU emu = new EMU(); IncrementMonitorEMUAllocCount(); @@ -425,7 +455,7 @@ namespace System { RegisterLostObjForVerifyHeap(obj, m); } else { DebugPrint("-rc on monitor {0:x}\n", __arglist(Magic.addressOf(m))); - DecrementRefCount(m); + NonNullDecrementRefCount(m); } #else if (now.Eq(muw)) { @@ -681,7 +711,7 @@ namespace System { // Hash code is int32. Unfortunately we cannot change that assumption. // The following cast will return only the lower bits in x64. // But it should not affect correctness. - result = (int) (Magic.addressOf(obj) & PAYLOAD_MASK); + result = unchecked((int) (Magic.addressOf(obj) & HASHCODE_MASK)); } DebugPrint("Chosen hash code {0:x} for {1:x}\n", __arglist(result, Magic.addressOf(obj))); @@ -730,11 +760,11 @@ namespace System { internal UIntPtr value; - internal STMSnapshot (UIntPtr value) { + internal STMSnapshot (MultiUseWord muw) { #if REFERENCE_COUNTING_GC || DEFERRED_REFERENCE_COUNTING_GC VTable.Assert(false, "STM not supported with RC collector"); #endif - this.value = value; + this.value = muw.value; } // Map from an STMSnapshot to an STMWord. Ordinarily we @@ -752,8 +782,8 @@ namespace System { } else if (snapshotTag == INFLATED_TAG) { // STMWord is held out-of-line in the EMU - EMU emu = EMU.FromAddress((UIntPtr) - (this.value & PAYLOAD_MASK)); + UIntPtr payload = (this.value & PAYLOAD_MASK); + EMU emu = EMU.FromAddress(payload - PostHeader.Size); result = emu.stmWord; } else { @@ -788,14 +818,16 @@ namespace System { // b. It contains an STMWord: unpack that, then call the // STM's method than knows how to deal with STMWords. - internal unsafe void Visit(NonNullReferenceVisitor v) { + internal unsafe void Visit(DirectReferenceVisitor v) { MultiUseWord muw = new MultiUseWord(this.value); DebugPrint("Visit STM snapshot {0:x}\n", __arglist(muw.value)); uint tag = muw.GetTag(); if (tag == MONITOR_TAG || tag == INFLATED_TAG) { // Pointer-derived value managed by MultiUseWord.cs UIntPtr payload = (UIntPtr) muw.GetPayload(); - v.Visit(&payload); + UIntPtr addr = (payload - PostHeader.Size); + v.Visit(&addr); + payload = (addr + PostHeader.Size); muw = new MultiUseWord(tag, payload); } @@ -841,26 +873,27 @@ namespace System { #endif internal unsafe STMSnapshot GetSTMSnapshot() { Object obj = Magic.fromAddress(this.addr); - return new STMSnapshot(obj.preHeader.muw.value); + return new STMSnapshot(GetForObject(obj)); } internal unsafe void SetSTMWordAtAllocation(STMWord s) { VTable.Assert(GetSTMSnapshot().value == UIntPtr.Zero); VTable.Assert((s.value & TAG_MASK) == STM_TAG); Object obj = Magic.fromAddress(this.addr); - obj.preHeader.muw.value = s.value; + SetForObject(obj, new MultiUseWord(s.value)); } - internal unsafe void SetSTMWordAtGC(NonNullReferenceVisitor rv, + internal unsafe void SetSTMWordAtGC(DirectReferenceVisitor rv, STMWord s) { VTable.Assert((s.value & TAG_MASK) == STM_TAG); UIntPtr curSnapVal = GetSTMSnapshot().value; uint curTag = (curSnapVal & TAG_MASK); if (curTag == STM_TAG) { Object obj = Magic.fromAddress(this.addr); - obj.preHeader.muw.value = s.value; + SetForObject(obj, new MultiUseWord(s.value)); } else { - UIntPtr ptrVal = (UIntPtr)curSnapVal & (UIntPtr)PAYLOAD_MASK; + UIntPtr payload = (curSnapVal & PAYLOAD_MASK); + UIntPtr ptrVal = (payload - PostHeader.Size); rv.Visit(&ptrVal); EMU emu = EMU.FromAddress(ptrVal); emu.stmWord.value = s.value; @@ -895,9 +928,8 @@ namespace System { if (oldTag == STM_TAG) { VTable.Assert(oldWord == oldSnapshot.value); Object obj = Magic.fromAddress(this.addr); - result = (Interlocked.CompareExchange(ref obj.preHeader.muw.value, - newWord, - oldWord) == oldWord); + result = (CompareExchangeForObject(obj, newWord, oldWord) + == oldWord); } else { if (oldTag != INFLATED_TAG) { DebugPrint("CompareExchangeSTMWord triggered inflation\n"); @@ -920,15 +952,14 @@ namespace System { __arglist(this.addr, newWord.value)); Object obj = Magic.fromAddress(this.addr); - UIntPtr curVal = obj.preHeader.muw.value; + UIntPtr curVal = GetForObject(obj).value; uint curTag = (curVal & TAG_MASK); VTable.Assert((curTag == STM_TAG) || (curTag == INFLATED_TAG)); if (curTag == STM_TAG) { VTable.Assert((newWord.value & TAG_MASK) == 0); - UIntPtr saw = Interlocked.CompareExchange(ref obj.preHeader.muw.value, - newWord.value, - curVal); + UIntPtr saw = + CompareExchangeForObject(obj, newWord.value, curVal); // NB: we have the object opened for update, and so if our // Interlocked.CompareExchange failed then it must be because another @@ -954,7 +985,7 @@ namespace System { // Visit the object that the STMHandle refers to. This is usually used // with a visitor that treats the reference as weak; we return false // if the visitor null's out the object reference. - internal unsafe bool Visit(NonNullReferenceVisitor v) { + internal unsafe bool Visit(DirectReferenceVisitor v) { UIntPtr t = this.addr; v.Visit(&t); if (t == UIntPtr.Zero) { @@ -967,20 +998,20 @@ namespace System { internal unsafe bool IsMarked() { Object obj = Magic.fromAddress(this.addr); - MultiUseWord muw = obj.preHeader.muw; + MultiUseWord muw = GetForObject(obj); return muw.IsMarked(); } internal unsafe void SetMark(bool mark) { Object obj = Magic.fromAddress(this.addr); - MultiUseWord muw = obj.preHeader.muw; + MultiUseWord muw = GetForObject(obj); MultiUseWord newMuw; if (mark) { newMuw = new MultiUseWord(muw.value | (UIntPtr)MARK_BIT_MASK); } else { newMuw = new MultiUseWord(muw.value & (UIntPtr)(~MARK_BIT_MASK)); } - obj.preHeader.muw = newMuw; + SetForObject(obj, newMuw); } internal static void ReInflate(Object obj, STMWord newWord) { @@ -1064,10 +1095,10 @@ namespace System { if (sawTag != MONITOR_TAG) { DebugPrint("+rc on monitor {0:x}\n", __arglist(Magic.addressOf(monitor))); - IncrementRefCount(monitor); + NonNullIncrementRefCount(monitor); } DebugPrint("+rc on emu {0:x}\n", __arglist(Magic.addressOf(emu))); - IncrementRefCount(emu); + NonNullIncrementRefCount(emu); #endif emu.Target = obj; @@ -1102,11 +1133,11 @@ namespace System { if (sawTag != MONITOR_TAG) { DebugPrint("-rc on monitor {0:x}\n", __arglist(Magic.addressOf(monitor))); - DecrementRefCount(monitor); + NonNullDecrementRefCount(monitor); } DebugPrint("-rc on emu {0:x}\n", __arglist(Magic.addressOf(emu))); - DecrementRefCount(emu); + NonNullDecrementRefCount(emu); #endif IncrementAbandonedEMUsCount(); } @@ -1157,21 +1188,22 @@ namespace System { "tag == MONITOR_TAG || tag == INFLATED_TAG"); UIntPtr payload = (UIntPtr) muw.GetPayload(); - Object refPayload = Magic.fromAddress(payload); + Object refPayload = Magic.fromAddress(payload - PostHeader.Size); if (tag == MONITOR_TAG) { - Monitor m = (Monitor) refPayload; + Monitor m = Magic.toMonitor(refPayload); DebugPrint("-rc on monitor on dead obj {0:x}\n", __arglist(Magic.addressOf(m))); - DecrementRefCount(m); + NonNullDecrementRefCount(m); } else { - EMU emu = (EMU) refPayload; - Monitor m = (Monitor) Magic.fromAddress(emu.monitor); + EMU emu = Magic.toEMU(refPayload); + Monitor m = + Magic.toMonitor(Magic.fromAddress(emu.monitor)); DebugPrint("-rc on monitor on dead obj {0:x}\n", __arglist(Magic.addressOf(m))); - DecrementRefCount(m); + NonNullDecrementRefCount(m); DebugPrint("-rc on emu on dead obj {0:x}\n", __arglist(Magic.addressOf(emu))); - DecrementRefCount(emu); + NonNullDecrementRefCount(emu); } #endif } @@ -1391,7 +1423,7 @@ namespace System { // Called during the mark phase. Responsible for keeping alive the // EMU objects and the Monitors reachable from them. internal unsafe static - void VisitStrongRefs(NonNullReferenceVisitor strongReferenceVisitor, + void VisitStrongRefs(DirectReferenceVisitor strongReferenceVisitor, bool useShadows) { DebugPrint("GC: VisitStrongRefs\n"); @@ -1417,7 +1449,7 @@ namespace System { #if REFERENCE_COUNTING_GC || DEFERRED_REFERENCE_COUNTING_GC internal unsafe static - void VisitStrongRefsRC(NonNullReferenceVisitor strongRefVisitor) + void VisitStrongRefsRC(DirectReferenceVisitor strongRefVisitor) { VTable.Assert(VerificationMode, @"VerificationMode"); @@ -1433,7 +1465,7 @@ namespace System { private class MUWVisitor : SegregatedFreeList.ObjectVisitor { - private NonNullReferenceVisitor Visitor; + private DirectReferenceVisitor Visitor; [ManualRefCounts] internal override void VisitSmall(Object obj, UIntPtr memAddr) { @@ -1451,11 +1483,12 @@ namespace System { uint tag = muw.GetTag(); if (tag == MONITOR_TAG || tag == INFLATED_TAG) { UIntPtr payload = (UIntPtr) muw.GetPayload(); + UIntPtr addr = (payload - PostHeader.Size); DebugPrint("GC: VisitStrongRefsRC visiting {0:x}\n", - __arglist(payload)); - this.Visitor.Visit(&payload); + __arglist(addr)); + this.Visitor.Visit(&addr); if (tag == INFLATED_TAG) { - EMU emu = EMU.FromAddress((UIntPtr)muw.GetPayload()); + EMU emu = EMU.FromAddress(muw.GetPayload() - PostHeader.Size); UIntPtr m = emu.monitor; DebugPrint("GC: VisitStrongRefsRC visiting {0:x}\n", __arglist(m)); @@ -1467,7 +1500,7 @@ namespace System { } [ManualRefCounts] - internal void Initialize(NonNullReferenceVisitor visitor) { + internal void Initialize(DirectReferenceVisitor visitor) { this.Visitor = visitor; } } @@ -1499,13 +1532,15 @@ namespace System { #if REFERENCE_COUNTING_GC [ManualRefCounts] - internal static void IncrementRefCount(Object obj) { - ReferenceCountingCollector.IncrementRefCount(obj); + internal static void NonNullIncrementRefCount(Object obj) { + ReferenceCountingCollector. + NonNullIncrementRefCount(obj); } [ManualRefCounts] - internal static void DecrementRefCount(Object obj) { - ReferenceCountingCollector.DecrementRefCount(obj); + internal static void NonNullDecrementRefCount(Object obj) { + ReferenceCountingCollector. + NonNullDecrementRefCount(obj); } internal static bool VerificationMode { @@ -1518,13 +1553,15 @@ namespace System { #if DEFERRED_REFERENCE_COUNTING_GC [ManualRefCounts] - internal static void IncrementRefCount(Object obj) { - DeferredReferenceCountingCollector.IncrementRefCount(obj); + internal static void NonNullIncrementRefCount(Object obj) { + DeferredReferenceCountingCollector. + NonNullIncrementRefCount(obj); } [ManualRefCounts] - internal static void DecrementRefCount(Object obj) { - DeferredReferenceCountingCollector.DecrementRefCount(obj); + internal static void NonNullDecrementRefCount(Object obj) { + DeferredReferenceCountingCollector. + NonNullDecrementRefCount(obj); } internal static bool VerificationMode { @@ -1542,11 +1579,11 @@ namespace System { #if !(REFERENCE_COUNTING_GC || DEFERRED_REFERENCE_COUNTING_GC) internal unsafe static - void VisitStrongRefsNonRC(NonNullReferenceVisitor strongRefVisitor, + void VisitStrongRefsNonRC(DirectReferenceVisitor strongRefVisitor, bool useShadows) { DebugPrint("GC: VisitStrongRefsNonRC\n"); - + fixed (UIntPtr *loc = &emuGCList) { if (*loc != UIntPtr.Zero) { strongRefVisitor.Visit(loc); @@ -1611,7 +1648,7 @@ namespace System { // weakReferenceVisitor nulling out the target field. Any such EMUs // are then cut out of the global list in PostGCHook. internal unsafe static - void VisitWeakRefs(NonNullReferenceVisitor weakReferenceVisitor, + void VisitWeakRefs(DirectReferenceVisitor weakReferenceVisitor, bool useShadows) { DebugPrint("GC: VisitWeakRefs\n"); @@ -1946,6 +1983,8 @@ __arglist(InflationFromCount[STM_TAG], // 0 } } + // Because EMUs are encoded in the MultiUseWord payload, they must be 8-byte aligned + [StructAlign(8)] internal class EMU { // Scalar fields holding scalar values internal int hashCode; @@ -1982,13 +2021,15 @@ __arglist(InflationFromCount[STM_TAG], // 0 public Object Target { get { - return Magic.fromAddress(target); + return Barrier.WeakRefRead(target, 0); } set { - target = Magic.addressOf(value); + target = Barrier.WeakRefWrite(value, 0); } } + // FIXME: this other stuff could use some barriers!! + public Monitor Monitor { get { return (Monitor)(Magic.fromAddress(this.monitor)); @@ -2006,7 +2047,7 @@ __arglist(InflationFromCount[STM_TAG], // 0 next = Magic.addressOf(value); } } - + public EMU NextShadow { get { return FromAddress(this.nextShadow); @@ -2015,7 +2056,7 @@ __arglist(InflationFromCount[STM_TAG], // 0 nextShadow = Magic.addressOf(value); } } - + internal void Register() { Thread thread = Thread.CurrentThread; if (thread.externalMultiUseObjAllocListHead == UIntPtr.Zero) { @@ -2053,8 +2094,8 @@ __arglist(InflationFromCount[STM_TAG], // 0 public enum StringState { Undetermined = 0, // Undetermined == 0 assumed in MultiUseWord.cs - HighChars = 4, - FastOps = 8, - SpecialSort = 12, + HighChars = 8, + FastOps = 16, + SpecialSort = 24, } } diff --git a/base/Kernel/Bartok/Options.cs b/base/Imported/Bartok/runtime/shared/System/Options.cs similarity index 93% rename from base/Kernel/Bartok/Options.cs rename to base/Imported/Bartok/runtime/shared/System/Options.cs index 9fcdb53..b0830b2 100644 --- a/base/Kernel/Bartok/Options.cs +++ b/base/Imported/Bartok/runtime/shared/System/Options.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,9 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// namespace Microsoft.Bartok.Options { diff --git a/base/Imported/Bartok/runtime/shared/System/Shared.cs b/base/Imported/Bartok/runtime/shared/System/Shared.cs new file mode 100644 index 0000000..2c72821 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/System/Shared.cs @@ -0,0 +1,385 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +// This code is used in both mscorlibOverride and for building applications +// (in particular Bartok). This makes sure the constant used in Bartok +// compiler and mscorlibOverride are the same + +using System; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace Microsoft.Bartok.Runtime { +#if SINGULARITY + [AccessedByRuntime("Referenced from C++")] +#endif + public enum StructuralType { + None = 0x00, +#if SINGULARITY + [AccessedByRuntime("Referenced from C++")] +#endif + Reference = 0x01, + UntracedPointer = 0x02, + Struct = 0x03, +#if SINGULARITY + [AccessedByRuntime("Referenced from C++")] +#endif + Bool = 0x04, +#if SINGULARITY + [AccessedByRuntime("Referenced from C++")] +#endif + Char = 0x05, +#if SINGULARITY + [AccessedByRuntime("Referenced from C++")] +#endif + Int8 = 0x06, +#if SINGULARITY + [AccessedByRuntime("Referenced from C++")] +#endif + Int16 = 0x07, +#if SINGULARITY + [AccessedByRuntime("Referenced from C++")] +#endif + Int32 = 0x08, +#if SINGULARITY + [AccessedByRuntime("Referenced from C++")] +#endif + Int64 = 0x09, +#if SINGULARITY + [AccessedByRuntime("Referenced from C++")] +#endif + UInt8 = 0x0a, +#if SINGULARITY + [AccessedByRuntime("Referenced from C++")] +#endif + UInt16 = 0x0b, +#if SINGULARITY + [AccessedByRuntime("Referenced from C++")] +#endif + UInt32 = 0x0c, +#if SINGULARITY + [AccessedByRuntime("Referenced from C++")] +#endif + UInt64 = 0x0d, +#if SINGULARITY + [AccessedByRuntime("Referenced from C++")] +#endif + Float32 = 0x0e, +#if SINGULARITY + [AccessedByRuntime("Referenced from C++")] +#endif + Float64 = 0x0f, +#if SINGULARITY + [AccessedByRuntime("Referenced from C++")] +#endif + IntPtr = 0x10, +#if SINGULARITY + [AccessedByRuntime("Referenced from C++")] +#endif + UIntPtr = 0x11, + Void = 0x12, + }; + + public enum GCType{ + AdaptiveCopyingCollector = 0x00, + MarkSweepCollector = 0x01, + SemispaceCollector = 0x02, + SlidingCollector = 0x03, + ReferenceCountingCollector = 0x04, + ConcurrentMSCollector = 0x05, + DeferredReferenceCountingCollector = 0x06, + NullCollector = 0x07, + AtomicRCCollector = 0x08, + TableMarkSweepCollector = 0x09, + CoCoMSCollector = 0x0a + }; + + public enum PTType{ + CentralPT = 0, + CentralPTHimem, + FlatDistributedPT, + FlatDistributedPTTest + }; + + public enum WBType { + noWB = 0, + Generational = 1, + CMS = 2, + ARC = 3, + AllCards = 4, + ExpandingCoCo = 5, + ProbabilisticCoCo = 6, + AbortingCoCo = 7, + BrooksTest = 8, + BrooksCMSTest = 9, + }; + + public class BarrierMask { + public class PathSpec { + public const int AllowFast = 1; + public const int UseMask = 2; + } + public class Forward { + public const int Nullable = 4; + public const int Writable = 8; + } + } + + public enum RemSetType { + noRemSet = 0, + SSB = 1, + Cards = 2, + }; + + public enum CopyScanType { + noCopyScan = 0, + CheneyScan = 1, + HierarchicalScan = 2, + NestedHierarchicalScan = 3, + }; + +#if SINGULARITY + [AccessedByRuntime("Referenced from C++")] +#endif + public class Constants { + public const int TypeTestDisplaySize = 6; + public const int TypeTestDisplayPosCache = TypeTestDisplaySize + 1; +#if true + public const bool TypeTestDisplayIncludesObject = false; + public const int TypeTestDisplayObjectOffset = 0; +#else + public const bool TypeTestDisplayIncludesObject = true; + public const int TypeTestDisplayObjectOffset = 1; +#endif + public const int LargeObjectBits = 16; + + // shared by compact record + public const int CompactEntryMaskStart = 6; + public const int CompactArgMaskStart = 2; + public const int CompactArgMask = 0xf; + + // shared by full record + public const int FullEntryMaskStart = 1; + public const int FullRecordMask = 0x7; + +// ISA_ is the prefix used by Singularity for the system architecture +#if X86 || ISA_IX86 + // constants used by GC activation descriptor table entry + public const int InbetweenSlotsAboveNoFP = 1; + public const int InbetweenSlotsBelowNoFP = 0; + public const int InbetweenSlotsAboveFP = 2; + public const int InbetweenSlotsBelowFP = 0; + + public const int LastBitPos = 31; + // Compact record Frame pointer omitted + public const int CompactStackBitMaskStartNoFP = 24; + public const int CompactEntryMaskNoFP = 0x1f; + public const int CompactCalleeSaveUseStartNoFP = 11; + public const int CompactCalleeSaveUseMaskNoFP = 0xff; + public const int CompactFrameSizeStartNoFP = 19; + public const int CompactFrameSizeMaskNoFP = 0x1f; + + // Compact record Use frame pointer + public const int CompactStackBitMaskStartFP = 16; + public const int CompactEntryMaskFP = 0xf; + public const int CompactCalleeSaveUseStartFP = 10; + public const int CompactCalleeSaveUseMaskFP = 0x3f; + + // Full record Frame pointer omitted. + public const int FullEntryMaskNoFP = 0x1f; + public const int FullPinnedPosNoFP = 17; + public const int FullPinnedStartNoFP = 18; + public const int FullCalleeSaveUseStartNoFP = 6; + public const int FullCalleeSaveUseMaskNoFP = 0xff; + public const int FullRecordSizePosNoFP = 14; + public const int FullFrameSizeStartNoFP = 22; + + // Full record Use Frame pointer + public const int FullEntryMaskFP = 0xf; + public const int FullPinnedPosFP = 14; + public const int FullPinnedStartFP = 15; + public const int FullCalleeSaveUseStartFP = 5; + public const int FullCalleeSaveUseMaskFP = 0x3f; + public const int FullRecordSizePosFP = 11; + +// ISA_ is the prefix used by Singularity for the system architecture +#elif AMD64 || ISA_IX64 + // constants used by GC activation descriptor table entry + public const int InbetweenSlotsAboveNoFP = 1; + public const int InbetweenSlotsBelowNoFP = 0; + public const int InbetweenSlotsAboveFP = 2; + public const int InbetweenSlotsBelowFP = 0; + + public const int LastBitPos = 63; + // Compact record Frame pointer omitted + public const int CompactStackBitMaskStartNoFP = 36; + public const int CompactEntryMaskNoFP = 0x1ff; + public const int CompactCalleeSaveUseStartNoFP = 15; + public const int CompactCalleeSaveUseMaskNoFP = 0xffff; + public const int CompactFrameSizeStartNoFP =31; + public const int CompactFrameSizeMaskNoFP = 0x1f; + + // Compact record Use frame pointer + public const int CompactStackBitMaskStartFP = 28; + public const int CompactEntryMaskFP = 0xff; + public const int CompactCalleeSaveUseStartFP = 14; + public const int CompactCalleeSaveUseMaskFP = 0x3fff; + + // Full record Frame pointer omitted + public const int FullEntryMaskNoFP = 0x1ff; + public const int FullPinnedPosNoFP = 29; + public const int FullPinnedStartNoFP = 30; + public const int FullCalleeSaveUseStartNoFP = 10; + public const int FullCalleeSaveUseMaskNoFP = 0xffff; + public const int FullRecordSizePosNoFP = 26; + public const int FullFrameSizeStartNoFP = 38; + + // Full record Use Frame pointer + public const int FullEntryMaskFP = 0xff; + public const int FullPinnedPosFP = 26; + public const int FullPinnedStartFP = 27; + public const int FullCalleeSaveUseStartFP = 9; + public const int FullCalleeSaveUseMaskFP = 0x3fff; + public const int FullRecordSizePosFP = 23; + +// ISA_ is the prefix used by Singularity for the system architecture +#elif ARM || ISA_ARM +// FIX + // constants used by GC activation descriptor table entry + public const int InbetweenSlotsAboveNoFP = 0; + public const int InbetweenSlotsBelowNoFP = 2; + public const int InbetweenSlotsAboveFP = 0; + public const int InbetweenSlotsBelowFP = 3; + + public const int LastBitPos = 31; + + // Shared by all records + public const int CompactMask = 0x1; + + // Compact record common constants + public const int CompactOmitFPMask = 0x2; + public const int CompactTransitionRecordMask = (0x1 << 6); + + // Compact record Frame pointer omitted + // BUGBUG: The bridge does not support FPO. These constants have not been fixed. + public const int CompactStackBitMaskStartNoFP = 36; + public const int CompactEntryMaskNoFP = 0x1f; + public const int CompactCalleeSaveUseStartNoFP = 15; + public const int CompactCalleeSaveUseMaskNoFP = 0xffff; + public const int CompactFrameSizeStartNoFP =31; + public const int CompactFrameSizeMaskNoFP = 0x1f; + + // Compact record Use frame pointer + public const int CompactStackBitMaskStartFP = 16; + public const int CompactEntryMaskFP = 0xf; + public const int CompactCalleeSaveStartFP = 7; + public const int CompactCalleeSaveMaskFP = 0x7; + public const int CompactCalleeSaveUseStartFP = 10; + public const int CompactCalleeSaveUseMaskFP = 0x3f; + + // Full record common constants + public const int FullOmitFPMask = 0x1; + public const int FullTransitionRecordMask = (0x1 << 1); + + + // Full record Frame pointer omitted + // BUGBUG: The bridge does not support FPO. These constants have not been fixed. + public const int FullEntryMaskNoFP = 0x3ff; + public const int FullPinnedPosNoFP = 29; + public const int FullPinnedStartNoFP = 30; + public const int FullCalleeSaveUseStartNoFP = 10; + public const int FullCalleeSaveUseMaskNoFP = 0xffff; + public const int FullRecordSizePosNoFP = 26; + public const int FullFrameSizeStartNoFP = 38; + + // Full record Use Frame pointer + public const int FullEntryMaskFP = 0xff; + public const int FullPinnedPosFP = 26; + public const int FullPinnedStartFP = 27; + public const int FullCalleeSaveStartFP = 2; + public const int FullCalleeSaveMaskFP = 0x7f; + public const int FullCalleeSaveUseStartFP = 9; + public const int FullCalleeSaveUseMaskFP = 0x3fff; + public const int FullRecordSizePosFP = 23; +#else +#error Undefined architecture +#endif + + // Workaround for lack of enum printing in Bartok +#if SINGULARITY + [AccessedByRuntime("Referenced from C++")] +#endif + public static string[] StructuralTypeNames = { + "None", + "Reference", + "UntracedPointer", + "Struct", + "Bool", + "Char", + "Int8", + "Int16", + "Int32", + "Int64", + "UnsignedInt8", + "UnsignedInt16", + "UnsignedInt32", + "UnsignedInt64", + "Float32", + "Float64", + "IntPtr", + "UIntPtr", + "Void", + }; + + // BUGBUG: what about entry for structs? +#if SINGULARITY + [AccessedByRuntime("Referenced from C++")] +#endif + public int[] arrayOfStride = { + 0, + 4, + 4, + 0, + 1, + 2, + 1, + 2, + 4, + 8, + 1, + 2, + 4, + 8, + 4, + 8, + 4, + 4, + 4, + }; + }; + +#if !BARTOK_SYSTEM_EXTENSION + [RequiredByBartok] +#endif + public enum TypeInitState { + Ready = 0, + Running = 1, + Failed = 2, + Completed = 3 + }; + + public enum StageControlOption { + TryAllSupport = 0, + InstrumentVirtualCalls = 1, + PInvoke = 2, + }; +} + diff --git a/base/Kernel/Bartok/StackHeight.cs b/base/Imported/Bartok/runtime/shared/System/StackHeight.cs similarity index 95% rename from base/Kernel/Bartok/StackHeight.cs rename to base/Imported/Bartok/runtime/shared/System/StackHeight.cs index a3b0cff..4f7d2f1 100644 --- a/base/Kernel/Bartok/StackHeight.cs +++ b/base/Imported/Bartok/runtime/shared/System/StackHeight.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,9 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// namespace System { using System.GCs; diff --git a/base/Kernel/Bartok/TryAll.cs b/base/Imported/Bartok/runtime/shared/System/TryAll.cs similarity index 95% rename from base/Kernel/Bartok/TryAll.cs rename to base/Imported/Bartok/runtime/shared/System/TryAll.cs index f943240..0dcf4af 100644 --- a/base/Kernel/Bartok/TryAll.cs +++ b/base/Imported/Bartok/runtime/shared/System/TryAll.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,7 +9,7 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// Copyright (c) Microsoft Corporation. All rights reserved. + /* @@ -91,8 +95,7 @@ The caller must guarantee that: called. The roll-back code here is, of course, largely derived from the -try_all implementation by Avraham Shinnar, David Tarditi, Mark Plesko -and Bjarne Steensgaard. +try_all implementation. Dynamically nested try_all and atomic blocks are notionally implemented, but I've not tested them at all thoroughly (indeed, I may @@ -236,10 +239,6 @@ Known problems avoid splitting the Log* operations into try_all and atomic variants. - - -tharris, 9 May 2006 - */ @@ -311,87 +310,87 @@ namespace System [NoCCtor] internal struct STMWord { - // IS_OWNED_MASK is the bit we claim to distinguish owned / not owned - // STM words. PAYLOAD_MASK is what remains after this bit and those - // used by the multi-use word. - internal const uint IS_OWNED_MASK = 0x00000004; - internal const uint PAYLOAD_MASK = 0x7ffffff8; - -#if DEBUG - // Constrain us to an artificially small portion of the version number - // space in debug builds. This will force us to trigger GCs to - // reclaim version numbers and to test out the validate-during-GC code - // paths. - internal const uint VERSION_MASK = 0x7fff0000; - internal const int VERSION_SHIFT = 16; -#else - // During ordinary builds, use the largest version number space - // available. - internal const uint VERSION_MASK = PAYLOAD_MASK; - internal const int VERSION_SHIFT = 3; -#endif + // IS_OWNED_MASK is the bit we claim to distinguish owned / not owned + // STM words. PAYLOAD_MASK is what remains after this bit and those + // used by the multi-use word. + internal const uint IS_OWNED_MASK = 0x00000008; + internal const uint PAYLOAD_MASK = 0xfffffff0; - internal UIntPtr value; + #if DEBUG + // Constrain us to an artificially small portion of the version number + // space in debug builds. This will force us to trigger GCs to + // reclaim version numbers and to test out the validate-during-GC code + // paths. + internal const uint VERSION_MASK = 0xffff0000; + internal const int VERSION_SHIFT = 16; + #else + // During ordinary builds, use the largest version number space + // available. + internal const uint VERSION_MASK = PAYLOAD_MASK; + internal const int VERSION_SHIFT = 4; + #endif - // Constructors + internal UIntPtr value; - internal STMWord (UIntPtr value) { - this.value = value; - } + // Constructors - internal STMWord (UIntPtr payload, bool owned) { - VTable.Assert((payload & PAYLOAD_MASK) == (uint)payload); - this.value = payload + (UIntPtr)(owned ? IS_OWNED_MASK : 0); - } + internal STMWord (UIntPtr value) { + this.value = value; + } - // Accessors + internal STMWord (UIntPtr payload, bool owned) { + VTable.Assert((payload & PAYLOAD_MASK) == (uint)payload); + this.value = payload + (UIntPtr)(owned ? IS_OWNED_MASK : 0); + } - internal UIntPtr GetPayload() { - return (this.value & (UIntPtr)PAYLOAD_MASK); - } + // Accessors - internal UIntPtr GetPayloadWhenOwned() { - VTable.Assert((this.value & IS_OWNED_MASK) == IS_OWNED_MASK); - return (this.value - IS_OWNED_MASK); - } + internal UIntPtr GetPayload() { + return (this.value & (UIntPtr)PAYLOAD_MASK); + } - internal unsafe TryAllManager GetOwner() { - VTable.Assert((this.value & IS_OWNED_MASK) != 0); - UIntPtr ptr = (UIntPtr)(this.value - IS_OWNED_MASK); + internal UIntPtr GetPayloadWhenOwned() { + VTable.Assert((this.value & IS_OWNED_MASK) == IS_OWNED_MASK); + return (this.value - IS_OWNED_MASK); + } - UpdateEnlistmentLog.Entry *entry = - (UpdateEnlistmentLog.Entry *) ptr; - TryAllManager m = TryAllManager.toTryAllManager(Magic.fromAddress(entry -> m)); - return m; - } + internal unsafe TryAllManager GetOwner() { + VTable.Assert((this.value & IS_OWNED_MASK) != 0); + UIntPtr ptr = (UIntPtr)(this.value - IS_OWNED_MASK); - internal unsafe STMWord GetOwnersVersion() { - VTable.Assert((this.value & IS_OWNED_MASK) != 0); - UIntPtr ptr = (UIntPtr)(this.value - IS_OWNED_MASK); - UpdateEnlistmentLog.Entry *entry = - (UpdateEnlistmentLog.Entry *) ptr; - STMWord ver = entry -> v; - TryAllManager.DebugPrint("STMWord={0:x} ptr={1:x} ver={2:x}\n", - __arglist(this.value, - ptr, - ver.value)); - return ver; - } + UpdateEnlistmentLog.Entry *entry = + (UpdateEnlistmentLog.Entry *) ptr; + TryAllManager m = TryAllManager.toTryAllManager(Magic.fromAddress(entry -> m)); + return m; + } - internal bool IsQuiescent() { - return !IsOwned(); - } + internal unsafe STMWord GetOwnersVersion() { + VTable.Assert((this.value & IS_OWNED_MASK) != 0); + UIntPtr ptr = (UIntPtr)(this.value - IS_OWNED_MASK); + UpdateEnlistmentLog.Entry *entry = + (UpdateEnlistmentLog.Entry *) ptr; + STMWord ver = entry -> v; + TryAllManager.DebugPrint("STMWord={0:x} ptr={1:x} ver={2:x}\n", + __arglist(this.value, + ptr, + ver.value)); + return ver; + } - internal bool IsOwned() { - return ((this.value & IS_OWNED_MASK) != 0); - } + internal bool IsQuiescent() { + return !IsOwned(); + } - internal STMWord GetNextVersion() { - return new STMWord((this.value + (1 << STMWord.VERSION_SHIFT)) & - (UIntPtr)STMWord.VERSION_MASK); - } + internal bool IsOwned() { + return ((this.value & IS_OWNED_MASK) != 0); + } - internal unsafe void Visit(NonNullReferenceVisitor referenceVisitor) + internal STMWord GetNextVersion() { + return new STMWord((this.value + (1 << STMWord.VERSION_SHIFT)) & + (UIntPtr)STMWord.VERSION_MASK); + } + + internal unsafe void Visit(DirectReferenceVisitor referenceVisitor) { #if ENABLE_GC_TRACING VTable.DebugPrint("Visit STM word {0:x}\n", __arglist(this.value)); @@ -621,8 +620,8 @@ namespace System fixed (UpdateEnlistmentLog.Entry *entry = &entries[0]) { entryAddr = (UIntPtr)entry; UIntPtr start = Magic.addressOf(entries); - entries[0].offset = ((UIntPtr)entry) - start; - stmWord = new STMWord((UIntPtr)entry, true); + entries[0].offset = (entryAddr) - start; + stmWord = new STMWord(entryAddr, true); } this.savedTryAlls[i].locallyAllocatedSTMWord = stmWord; @@ -1662,7 +1661,7 @@ namespace System // field may have already been visited. internal static - void VisitStrongRefs(NonNullReferenceVisitor referenceVisitor) + void VisitStrongRefs(DirectReferenceVisitor referenceVisitor) { if (visitStrongRefsDelegate != null) { visitStrongRefsDelegate(referenceVisitor); @@ -1670,7 +1669,7 @@ namespace System } internal static - void VisitWeakRefs(NonNullReferenceVisitor referenceVisitor) + void VisitWeakRefs(DirectReferenceVisitor referenceVisitor) { if (visitWeakRefsDelegate != null) { visitWeakRefsDelegate(referenceVisitor); @@ -1802,7 +1801,7 @@ namespace System // during GC -- e.g. we can be run after dealing with pinned objects // (some of which may lie on the same page as our data structures). - internal unsafe static Object ReadBarrier(NonNullReferenceVisitor v, + internal unsafe static Object ReadBarrier(DirectReferenceVisitor v, Object o) { Object result; @@ -1880,7 +1879,7 @@ namespace System // Visit strong refs from the transaction logs. These occur only in the // 'old value' entries in the undo log. - private static void doVisitStrongRefs(NonNullReferenceVisitor rv) { + private static void doVisitStrongRefs(DirectReferenceVisitor rv) { TryAllManager.DebugPrintGC("GC - VisitLogData with ptrVisitor={0}\n", __arglist((uint)Magic.addressOf(rv))); @@ -1911,7 +1910,7 @@ namespace System // read-enlistment and undo logs. This simplifies processing of the // read-enlistment log when handling entries that occur in both // enlistment logs. - private static void doVisitWeakRefs(NonNullReferenceVisitor rv) { + private static void doVisitWeakRefs(DirectReferenceVisitor rv) { TryAllManager.DebugPrintGC("GC - Visit logs (weak) with {0}\n", __arglist((uint)Magic.addressOf(rv))); @@ -1953,7 +1952,7 @@ namespace System // Visit strong refs from the transaction logs. These occur in the // overwritten values in the undo log. private static void VisitStrongRefs(TryAllManager m, - NonNullReferenceVisitor rv) { + DirectReferenceVisitor rv) { UpdateLog updateLog = toUpdateLog(ReadBarrier(rv, m.updateLog)); @@ -1961,13 +1960,13 @@ namespace System } private static void VisitWeakRefsPhase1(TryAllManager m, - NonNullReferenceVisitor rv) { + DirectReferenceVisitor rv) { UpdateEnlistmentLog uEnlistmentLog = m.uEnlistmentLog; uEnlistmentLog.VisitWeakRefs(m, rv); } private static void VisitWeakRefsPhase2(TryAllManager m, - NonNullReferenceVisitor rv) { + DirectReferenceVisitor rv) { UpdateLog updateLog; ReadEnlistmentLog rEnlistmentLog; @@ -3864,7 +3863,7 @@ namespace System (uint)(*(getLoc(obj,offset))), (uint)data)); - System.ILHelpers.SetAt(obj, offset, data); + Magic.SetAt(obj, offset, data); } internal static unsafe void setAt(Object obj, UIntPtr offset, @@ -3876,7 +3875,7 @@ namespace System (uint)(*(getLoc(obj,offset))), (uint)Magic.addressOf(data))); - System.ILHelpers.SetAt(obj, offset, data); + Magic.SetAt(obj, offset, data); } internal static unsafe void setAtStack @@ -3891,7 +3890,7 @@ namespace System (uint)(*(getLoc(obj,offset))), (uint)data)); - System.ILHelpers.SetAt(obj, offset, data); + Magic.SetAt(obj, offset, data); } } @@ -3907,7 +3906,7 @@ namespace System (uint)(*(getLoc(obj,offset))), (uint)Magic.addressOf(data))); - System.ILHelpers.SetAt(obj, offset, data); + Magic.SetAt(obj, offset, data); } } @@ -4015,7 +4014,7 @@ namespace System private static GCHookMethod preGCHookDelegate; private static GCHookMethod postGCHookDelegate; - delegate void VisitMethod(NonNullReferenceVisitor visitor); + delegate void VisitMethod(DirectReferenceVisitor visitor); delegate void GCHookMethod(); internal uint[] addressCache; @@ -4193,7 +4192,9 @@ namespace System this.currentChunk = new LogChunk(entries, null); } - [Inline] + // TODO: Inliner will not inline this method because of the pinned var + // (and will warn repeatedly about it). + //[Inline] #if !DEBUG [DisableBoundsChecks] [DisableNullChecks] @@ -4213,7 +4214,9 @@ namespace System return result; } - [Inline] + // TODO: Inliner will not inline this method because of the pinned var + // (and will warn repeatedly about it). + //[Inline] #if !DEBUG [DisableBoundsChecks] [DisableNullChecks] @@ -4503,7 +4506,7 @@ namespace System [DisableNullChecks] #endif // !DEBUG internal unsafe void VisitWeakRefs(TryAllManager m, - NonNullReferenceVisitor rv) { + DirectReferenceVisitor rv) { LogChunk fromChunk = this.currentChunk; Entry[] fromEntries = fromChunk.entries; int fromEntry = fromChunk.nextEntry; @@ -4515,9 +4518,6 @@ namespace System int toNumBlocks = 1; for (int depth = m.nextSavedTryAll - 1; depth >= 0; depth --) { - LogChunk startToChunk = toChunk; - int startToEntry = toEntry; - LogChunk endFromChunk = m.savedTryAlls[depth].rEnlistmentLogAtStart.node; int endFromEntry = m.savedTryAlls[depth].rEnlistmentLogAtStart.entry; @@ -5032,7 +5032,9 @@ namespace System AddCapacity(m); } - [Inline] + // TODO: Inliner will not inline this method because of the pinned var + // (and will warn repeatedly about it). + //[Inline] internal static unsafe Entry *AddrOfEntry(Entry[] entries, int idx) { UIntPtr addrAsUIntPtr; Entry *result; @@ -5047,7 +5049,9 @@ namespace System return result; } - [Inline] + // TODO: Inliner will not inline this method because of the pinned var + // (and will warn repeatedly about it). + //[Inline] #if !DEBUG [DisableBoundsChecks] [DisableNullChecks] @@ -5370,7 +5374,8 @@ namespace System [DisableBoundsChecks] [DisableNullChecks] #endif // !DEBUG - internal unsafe void VisitWeakRefs(TryAllManager m, NonNullReferenceVisitor v) { + internal unsafe void VisitWeakRefs(TryAllManager m, + DirectReferenceVisitor v) { LogChunk fromChunk = this.currentChunk; Entry[] fromEntries = fromChunk.entries; int fromEntry = fromChunk.nextEntry; @@ -5588,9 +5593,9 @@ namespace System // NB: filtering depends on the updated-object log entries being // written - if ((TryAllManager.DISABLE_BEFORE_WRITING_LOG == false) && ( - TryAllManager.TRACK_TRANSACTION_LOCALS || - TryAllManager.BITMAP)) { + if (!TryAllManager.DISABLE_BEFORE_WRITING_LOG + && (TryAllManager.TRACK_TRANSACTION_LOCALS + || TryAllManager.BITMAP)) { STMHandle h = new STMHandle(obj); STMSnapshot s = h.GetSTMSnapshot(); @@ -5609,7 +5614,7 @@ namespace System uint wordOffset = (uint)offset >> 2; STMWord w = s.GetSTMWord(); - // XXX tharris -- See known problem: this test could be removed + // See known problem: this test could be removed // at the cost of splitting the Log* operations into try_all // and atomic variants. if (w.IsOwned()) { @@ -5804,7 +5809,9 @@ namespace System m._updateLogWriter.limit = limit; } - [Inline] + // TODO: Inliner will not inline this method because of the pinned var + // (and will warn repeatedly about it). + //[Inline] #if !DEBUG [DisableBoundsChecks] [DisableNullChecks] @@ -5824,7 +5831,9 @@ namespace System return result; } - [Inline] + // TODO: Inliner will not inline this method because of the pinned var + // (and will warn repeatedly about it). + //[Inline] #if !DEBUG [DisableBoundsChecks] [DisableNullChecks] @@ -6066,7 +6075,7 @@ namespace System [DisableBoundsChecks] [DisableNullChecks] #endif // !DEBUG - internal unsafe void VisitStrongRefs(TryAllManager m, NonNullReferenceVisitor v) + internal unsafe void VisitStrongRefs(TryAllManager m, DirectReferenceVisitor v) { LogChunk chunk = TryAllManager.toUpdateLogChunk(TryAllManager.ReadBarrier(v, this.currentChunk)); @@ -6120,7 +6129,8 @@ namespace System [DisableBoundsChecks] [DisableNullChecks] #endif // !DEBUG - internal unsafe void VisitWeakRefs(TryAllManager m, NonNullReferenceVisitor rv) { + internal unsafe void VisitWeakRefs(TryAllManager m, + DirectReferenceVisitor rv) { LogChunk fromChunk = this.currentChunk; Entry[] fromEntries = fromChunk.entries; int fromEntry = fromChunk.nextEntry; @@ -6657,7 +6667,7 @@ newBytesSinceGC: {3} ", __arglist(invalidDuringGC, // 0 updateOverflow, // 1 enlistmentOverflow, // 2 - (int)GC.newBytesSinceGC // 3 + (int)BaseCollector.DebugNewBytesSinceGC // 3 )); VTable.DebugPrint(@" diff --git a/base/Kernel/Bartok/TryAllAttributes.cs b/base/Imported/Bartok/runtime/shared/System/TryAllAttributes.cs similarity index 93% rename from base/Kernel/Bartok/TryAllAttributes.cs rename to base/Imported/Bartok/runtime/shared/System/TryAllAttributes.cs index ff26a09..c489613 100644 --- a/base/Kernel/Bartok/TryAllAttributes.cs +++ b/base/Imported/Bartok/runtime/shared/System/TryAllAttributes.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,9 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// namespace System { diff --git a/base/Kernel/Bartok/TryAllException.cs b/base/Imported/Bartok/runtime/shared/System/TryAllException.cs similarity index 88% rename from base/Kernel/Bartok/TryAllException.cs rename to base/Imported/Bartok/runtime/shared/System/TryAllException.cs index 4a79e8b..a3a05bf 100644 --- a/base/Kernel/Bartok/TryAllException.cs +++ b/base/Imported/Bartok/runtime/shared/System/TryAllException.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,9 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// // This file contains the runtime support code for tryall support. diff --git a/base/Imported/Bartok/runtime/shared/System/UserMagic.cs b/base/Imported/Bartok/runtime/shared/System/UserMagic.cs new file mode 100644 index 0000000..31b1127 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/System/UserMagic.cs @@ -0,0 +1,20 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/*******************************************************************/ +/* WARNING */ +/* This file should be identical in the Bartok and Singularity */ +/* depots. Master copy resides in Bartok Depot. Changes should be */ +/* made to Bartok Depot and propagated to Singularity Depot. */ +/*******************************************************************/ + +namespace System { + public sealed class UserMagic + { + public static UIntPtr addressOf(Object o) + { + return Microsoft.Bartok.Runtime.Magic.addressOf(o); + } + } +} diff --git a/base/Kernel/Bartok/VTable.cs b/base/Imported/Bartok/runtime/shared/System/VTable.cs similarity index 84% rename from base/Kernel/Bartok/VTable.cs rename to base/Imported/Bartok/runtime/shared/System/VTable.cs index eaa4cfc..c769ce8 100644 --- a/base/Kernel/Bartok/VTable.cs +++ b/base/Imported/Bartok/runtime/shared/System/VTable.cs @@ -1,3 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + /*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ @@ -5,9 +9,6 @@ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// #define OLD_FAST_TESTS #if MARKSWEEPCOLLECTOR @@ -47,48 +48,70 @@ namespace System { [CCtorIsRunDuringStartup] [NoLoggingForUndo] [RequiredByBartok] + [AccessedByRuntime("Referenced from brt{main,exn}.cpp and brtasm.asm")] internal sealed class VTable { [RequiredByBartok] + [NoBarriers] public readonly VTable depth1; + [NoBarriers] [RequiredByBartok] public readonly VTable depth2; + [NoBarriers] [RequiredByBartok] public readonly VTable depth3; + [NoBarriers] [RequiredByBartok] public readonly VTable depth4; + [NoBarriers] [RequiredByBartok] public readonly VTable depth5; + [NoBarriers] [RequiredByBartok] public readonly VTable depth6; + [NoBarriers] public VTable posCache; [RequiredByBartok] + [NoBarriers] public int depth; [RequiredByBartok] + [NoBarriers] public readonly StructuralType arrayOf; [RequiredByBartok] + [NoBarriers] public readonly VTable arrayElementClass; [RequiredByBartok] + [NoBarriers] public readonly int arrayElementSize; [RequiredByBartok] + [NoBarriers] public readonly InterfaceInfo[] interfaces; [RequiredByBartok] + [NoBarriers] public readonly uint baseLength; [RequiredByBartok] + [NoBarriers] public readonly uint baseAlignment; [RequiredByBartok] + [NoBarriers] public readonly UIntPtr pointerTrackingMask; - [AccessedByRuntime("Referenced from C++")] + [RequiredByBartok] + [NoBarriers] public readonly StructuralType structuralView; [AccessedByRuntime("Referenced from C++")] + [NoBarriers] public readonly RuntimeType vtableType; [RequiredByBartok] + [NoBarriers] public readonly int marshalSize; [RequiredByBartok] + [NoBarriers] public readonly VTable vectorClass; [AccessedByRuntime("Type information available to runtime.")] + [NoBarriers] public readonly bool isAcyclicRefType; // one less than size, which must be a power of two + [NoBarriers] internal static int tryAllCheckMask = 2047; #if SINGULARITY_KERNEL private static bool multiThreaded; @@ -98,18 +121,33 @@ namespace System { { } + [NoBarriers] public static readonly ClassConstructorLock cctorLock = new ClassConstructorLock(); + [NoBarriers] public static int[] handle_counts = new int[4]; + [NoBarriers] public static bool runtimeInitialized = false; + [NoBarriers] public static System.Collections.Hashtable interfaceOffsetTable; + [NoBarriers] public static bool callTraceInProgress; +#if ARM + // TODO: ARM isn't supported by the BartokLinker yet, so this + // function won't be correctly generated. + [StackBound(252)] + [RequiredByBartok] + private static void Initialize() { + } + +#else // This method is generated by Bartok. // It calls all the auto init class constructors. [MethodImpl(MethodImplOptions.InternalCall)] [StackBound(252)] [RequiredByBartok] private static extern void Initialize(); +#endif // NB: This is called from Kernel.Main() before the VTable.cctor. // It calls all the manual init class constructors then @@ -127,6 +165,9 @@ namespace System { #endif // !SINGULARITY // The remaining cctors may allocate memory normally +#if !SINGULARITY + InitializeLimitedType(typeof(Microsoft.Win32.Win32Native)); +#endif InitializeLimitedType(typeof(System.Delegate)); InitializeLimitedType(typeof(System.MultiUseWord)); InitializeLimitedType(typeof(System.WeakReference)); @@ -162,9 +203,6 @@ namespace System { #endif VTable.runtimeInitialized = true; - if(VTable.enableGCAccounting) { - GCs.MemoryAccounting.Initialize(GC.gcType); - } VTable.Initialize(); #if !SINGULARITY GC.EnableHeap(); @@ -184,6 +222,7 @@ namespace System { #if (REFERENCE_COUNTING_GC || DEFERRED_REFERENCE_COUNTING_GC) [NoInline] #endif // REFERENCE_COUNTING_GC + [NoBarriers] [PreInitRefCounts] private static void initializeGC() { // Create the heap management data structures @@ -248,6 +287,21 @@ namespace System { } } +#if (REFERENCE_COUNTING_GC || DEFERRED_REFERENCE_COUNTING_GC) + // The CheckVectorStore opcode returns a value to model a + // dependency with respect to StoreVectorElement. + // CheckVectorStore gets lowered to a call to checkArrayStore. + // If the call is subsequently inlined, the type checker may + // complain, due to effects like t := Id(). + // + // The bug shows up in release builds with the RC collector, + // when the RCCollectorOptInlineRCUpdates stage-control is + // also turned on. + // + // Attaching [NoInline] to checkArrayStore is a temporary + // solution to get around this bug. + [NoInline] +#endif // REFERENCE_COUNTING_GC [RequiredByBartok] internal static void checkArrayStore(object obj1,object obj2) { Assert(obj1.vtable.vtableType.IsArray); @@ -297,6 +351,7 @@ namespace System { } } + [NoBarriers] [PreInitRefCounts] private static bool isValidAssignmentArray(RuntimeType ty, RuntimeType objTy) { @@ -328,11 +383,13 @@ namespace System { return isValidAssignment(tyElementType, objElementType); } + [NoBarriers] [PreInitRefCounts] internal static bool isValidAssignment(RuntimeType ty,RuntimeType objTy) { return (ty == objTy) || isValidAssignmentMedium(ty, objTy); } + [NoBarriers] [PreInitRefCounts] internal static bool isValidAssignmentMedium(RuntimeType ty, RuntimeType objTy) @@ -347,6 +404,7 @@ namespace System { return isValidAssignmentSlow(ty, objTy); } + [NoBarriers] [PreInitRefCounts] private static bool isValidAssignmentSlow(RuntimeType ty, RuntimeType objTy) @@ -399,6 +457,7 @@ namespace System { // null ok [RequiredByBartok] + [NoBarriers] [PreInitRefCounts] internal static void checkClassCast(RuntimeType ty,object obj) { if(obj != null && !isValidAssignment(ty, obj.vtable.vtableType)) { @@ -422,6 +481,7 @@ namespace System { // so that the callers here in VTable can be partially evaluated at // compile-time. [Inline] + [NoBarriers] [PreInitRefCounts] internal unsafe static bool newIsValidAssignment(VTable vtarget, VTable vobj, @@ -536,6 +596,7 @@ namespace System { } [Inline] + [NoBarriers] [PreInitRefCounts] internal static void newCheckClassCast(VTable v, object obj, @@ -546,7 +607,6 @@ namespace System { if(mayBeNull && obj == null) { return; } - if(!newIsValidAssignment(v, obj.vtable, vtargetDepth, mustBeExact, alwaysSimple)) { #if SINGULARITY @@ -563,6 +623,7 @@ namespace System { } [Inline] + [NoBarriers] [PreInitRefCounts] [RequiredByBartok] internal static void newCheckClassCastNonNullExact(VTable v, @@ -572,6 +633,7 @@ namespace System { } [Inline] + [NoBarriers] [PreInitRefCounts] [RequiredByBartok] internal static void newCheckClassCastExact(VTable v, @@ -581,6 +643,7 @@ namespace System { } [Inline] + [NoBarriers] [PreInitRefCounts] [RequiredByBartok] internal static void newCheckClassCastNonNullSimple(VTable v, @@ -590,6 +653,7 @@ namespace System { } [Inline] + [NoBarriers] [PreInitRefCounts] [RequiredByBartok] internal static void newCheckClassCastSimple(VTable v, @@ -599,6 +663,7 @@ namespace System { } [Inline] + [NoBarriers] [PreInitRefCounts] [RequiredByBartok] internal static void newCheckClassCastNonNullComplex(VTable v, @@ -609,6 +674,7 @@ namespace System { } [Inline] + [NoBarriers] [PreInitRefCounts] [RequiredByBartok] internal static void newCheckClassCastComplex(VTable v, @@ -709,6 +775,7 @@ namespace System { } [Inline] + [NoBarriers] [PreInitRefCounts] [RequiredByBartok] internal static void newCheckClassCastSimple(VTable v, @@ -721,6 +788,7 @@ namespace System { } [NoInline] + [NoBarriers] [PreInitRefCounts] internal static void newCheckClassCastHelp(VTable v, VTable vobj) { @@ -844,12 +912,14 @@ namespace System { } } + [NoBarriers] [PreInitRefCounts] internal static void InitializeLimitedType(Type t) { - InitializeLimitedType((RuntimeType)t); + InitializeLimitedType(Magic.toRuntimeType(t)); } + [NoBarriers] [PreInitRefCounts] private static void InitializeLimitedType(RuntimeType ty) { @@ -870,8 +940,22 @@ namespace System { ty.cctorState = TypeInitState.Completed; } - [StackLinkCheck] + // Rationale for cutting the fact that no stack probes are permitted in this + // tree of calls: + // + // There is no legitimate rationale. A bug exists in the database for all + // intractable violations, listing each one including this one. This violation + // is somewhat intractable because initType requires a link check and yet it + // is potentially called from stack linking code that cannot tolerate any + // linking. + // + // We need to rethink the .NET model for implicit lazy class initialization, + // since it is hard to reason about the failure points. + // + // Bug 436 + [NoStackLinkCheckTransCut] [RequiredByBartok] + [CalledRarely] internal static void initType(RuntimeType ty) { // Abort early if the type has already been initialized. if (ty.cctorState == TypeInitState.Completed) { @@ -1086,6 +1170,7 @@ namespace System { } } + [NoStackLinkCheckTransCut] [NoInline] internal static void throwNewClassCastException() { throw new InvalidCastException(); @@ -1096,6 +1181,17 @@ namespace System { throw new ArgumentOutOfRangeException(); } + // Rationale for cutting the fact that no stack probes are permitted in this + // tree of calls: + // + // Without this, then trees of code that cannot probe (like the stack link and + // unlink code itself) cannot use arrays. This is too high a burden. Instead, + // we change it to the *unchecked* requirement that this code cannot attempt to + // over-index an array. There is a bug that captures the fact that we cannot + // check this behavior. + // + // Bug 436 + [NoStackLinkCheckTransCut] [NoInline] [RequiredByBartok] internal static void throwNewIndexOutOfRangeException() { @@ -1113,18 +1209,45 @@ namespace System { throw new ArrayTypeMismatchException(); } + // Rationale for cutting the fact that no stack probes are permitted in this + // tree of calls: + // + // Without this, then trees of code that cannot probe (like the stack link and + // unlink code itself) cannot use arrays. This is too high a burden. Instead, + // we change it to the *unchecked* requirement that this code cannot attempt to + // over-index an array. There is a bug that captures the fact that we cannot + // check this behavior. + // + // Bug 436 + [NoStackLinkCheckTransCut] [NoInline] [AccessedByRuntime("referenced from halasm.asm")] internal static void throwNewOverflowException() { +#if DEBUG && SINGULARITY + DebugStub.WriteLine("Overflow exception"); + DebugStub.Break(); +#endif throw new OverflowException(); } + // Rationale for cutting the fact that no stack probes are permitted in this + // tree of calls: + // + // Without this, then trees of code that cannot probe (like the stack link and + // unlink code itself) cannot use arrays. This is too high a burden. Instead, + // we change it to the *unchecked* requirement that this code cannot attempt to + // over-index an array. There is a bug that captures the fact that we cannot + // check this behavior. + // + // Bug 436 + [NoStackLinkCheckTransCut] [NoInline] [RequiredByBartok] internal static void throwNewDivideByZeroException() { throw new DivideByZeroException(); } + [NoStackLinkCheckTransCut] [NoInline] [RequiredByBartok] internal static void throwNewArithmeticException() { @@ -1132,6 +1255,15 @@ namespace System { } [System.Diagnostics.Conditional("DEBUG")] + // Rationale for cutting the fact that no stack probes are permitted in this + // tree of calls: + // + // This is DEBUG-only code. It is reachable from every interface call, which + // means we would have to effectively prevent usage of interface calls when + // stack probes are transitively forbidden. This is a needless burden for + // DEBUG-only code. + [NoStackLinkCheckTransCut] + [StackLinkCheck] private static void profileInterfaceOffset(VTable v, RuntimeType ty, int index) { // REVIEW: Counts could be off in a multithreaded setting, but these @@ -1316,18 +1448,58 @@ namespace System { return result; } + [RequiredByBartok] + static public ulong doubleToULong(double val) { + double two63 = 2147483648.0 * 4294967296.0; + ulong ret; + if (val < two63) { + ret = (ulong)doubleToLong(val); + } else { + // subtract 0x8000000000000000, do the convert then add + // it back again + ret = (ulong)doubleToLong(val - two63) + (0x8000000000000000L); + } + return ret; + } + + [RequiredByBartok] + static public ulong checkedDoubleToULong(double val) { + double two64 = 4294967296.0 * 4294967296.0; + // Note that this expression also works properly for val = NaN case + if (val > -1.0 && val < two64) { + const double two63 = 2147483648.0 * 4294967296.0; + ulong ret; + if (val < two63) { + ret = (ulong)doubleToLong(val); + } else { + // subtract 0x8000000000000000, do the convert then add + // it back again + ret = (ulong)doubleToLong(val - two63) + (0x8000000000000000L); + } + return ret; + } + // throw + VTable.throwNewOverflowException(); + return 0; + } + public const bool enableLibraryOptions = true; internal static bool enableDebugPrint = false; /*true to log initType calls*/ + internal static bool enableUserTiming = false; internal static bool enableGCVerify = false; internal static bool enableGCProfiling = false; internal static bool enableGCTiming = false; + internal static bool enableFinalGCTiming = false; internal static bool enableGCAccounting = false; - internal static bool enableGCWatermarks = false; + internal static bool enableGCAccurateHeapSize = false; internal static bool enableDumpMemStats = false; internal static bool enableDumpMultiUseWords = false; internal static bool enableDumpInterface = false; internal static bool enableDumpTryAllStats = false; + internal static int beforeUser; + internal static int afterUser; + [NoHeapAllocation] public static bool EnableLibraryNotImplemented() { return(false); @@ -1366,6 +1538,7 @@ namespace System { [NoInline] [ManualRefCounts] [NoHeapAllocation] + [NoStackLinkCheckTrans] public static void Assert(bool expr) { if (VTable.enableLibraryOptions && EnableLibraryAsserts() && !expr) { failAssert(null); @@ -1376,6 +1549,7 @@ namespace System { [NoInline] [ManualRefCounts] [NoHeapAllocation] + [NoStackLinkCheckTrans] public static void Deny(bool expr) { if (VTable.enableLibraryOptions && EnableLibraryAsserts() && expr) { failAssert(null); @@ -1417,6 +1591,13 @@ namespace System { } } + // Rationale for cutting the fact that no stack probes are permitted in this + // tree of calls: + // + // * Asserts are debug only. + // * When an assert fails, a future stack overflow is not our biggest problem. + // * We want the ability to freely assert in trees that forbid stack probes. + [NoStackLinkCheckTransCut] [ManualRefCounts] [NoHeapAllocation] private static void failAssert(String s) { @@ -1489,14 +1670,15 @@ namespace System { [MethodImpl(MethodImplOptions.InternalCall)] [StackBound(312)] [NoHeapAllocation] + [GCAnnotation(GCOption.NOGC)] static public extern void DebugPrint(String v); [MethodImpl(MethodImplOptions.InternalCall)] //[StackBound(610)] [NoHeapAllocation] + [GCAnnotation(GCOption.NOGC)] static private unsafe extern void DebugPrintHelper(char *p_str, int length); - const int DEBUG_MESSAGE_BUFFER_SIZE = 4095; [NoHeapAllocation] static public unsafe void DebugPrint(String v, ArgIterator args) @@ -1518,37 +1700,44 @@ namespace System { [MethodImpl(MethodImplOptions.InternalCall)] [StackBound(610)] [NoHeapAllocation] + [GCAnnotation(GCOption.NOGC)] static public extern void DebugPrint(byte v); [MethodImpl(MethodImplOptions.InternalCall)] [StackBound(610)] [RequiredByBartok] [NoHeapAllocation] + [GCAnnotation(GCOption.NOGC)] static public extern void DebugPrint(int v); [MethodImpl(MethodImplOptions.InternalCall)] [StackBound(618)] [RequiredByBartok] [NoHeapAllocation] + [GCAnnotation(GCOption.NOGC)] static public extern void DebugPrint(long v); [MethodImpl(MethodImplOptions.InternalCall)] [StackBound(618)] [NoHeapAllocation] + [GCAnnotation(GCOption.NOGC)] static public extern void DebugPrint(ulong v); [MethodImpl(MethodImplOptions.InternalCall)] [StackBound(618)] [NoHeapAllocation] + [GCAnnotation(GCOption.NOGC)] static public extern void DebugPrint(int v, int width); [MethodImpl(MethodImplOptions.InternalCall)] [StackBound(626)] [NoHeapAllocation] + [GCAnnotation(GCOption.NOGC)] static public extern void DebugPrint(long v, int width); [MethodImpl(MethodImplOptions.InternalCall)] [NoHeapAllocation] + [GCAnnotation(GCOption.NOGC)] static public extern void DebugPrint(ulong v, int width); [NoHeapAllocation] @@ -1565,11 +1754,13 @@ namespace System { [MethodImpl(MethodImplOptions.InternalCall)] [StackBound(638)] [NoHeapAllocation] + [GCAnnotation(GCOption.NOGC)] static public extern void DebugDump(object o); [MethodImpl(MethodImplOptions.InternalCall)] [StackBound(638)] [NoHeapAllocation] + [GCAnnotation(GCOption.NOGC)] static public extern void DebugBreak(); #endif @@ -1601,7 +1792,9 @@ namespace System { int i=0; +#if !SINGULARITY_KERNEL ++i; // dump program name argument +#endif for( ; i> + (GCs.PageTable.PageBits-10); + } catch(FormatException) { + DebugPrint("--brt-maxheapsizekb requires argument\r\n"); + DebugBreak(); + } + continue; // argument loop + } + /* if(arg == "--brt-ssbsize") { ++i; @@ -1852,7 +2091,14 @@ namespace System { DebugBreak(); } - String[] mainArgs = new String[args.Length - i]; + if(VTable.enableGCAccounting) { + GCs.MemoryAccounting.Initialize(GC.gcType); + } + + int mainArgsLength = args.Length - i; + VTable.Assert(mainArgsLength >= 0, + "negative number of args to Main"); + String[] mainArgs = new String[mainArgsLength]; for(int j=0; i public virtual Object Target { get { - return Magic.fromAddress(objPtr); + return Barrier.WeakRefRead(this.objPtr, 0); } set { - this.objPtr = Magic.addressOf(value); + this.objPtr = Barrier.WeakRefWrite(value, 0); } } @@ -127,7 +128,7 @@ namespace System { /// that are not live and updating references as necessary. /// internal static - void Process(NonNullReferenceVisitor updateReferenceVisitor, + void Process(DirectReferenceVisitor updateReferenceVisitor, bool copyFirst, bool ignoreLong) { diff --git a/base/Imported/Bartok/runtime/shared/native/arch/amd64/gc.asm b/base/Imported/Bartok/runtime/shared/native/arch/amd64/gc.asm new file mode 100644 index 0000000..9af0a21 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/native/arch/amd64/gc.asm @@ -0,0 +1,392 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; + +include core.inc + +PAGE_BITS EQU 12 +MASK_OWNER EQU 03h + +;; Let's define some shorter names for commonly used structures +ifdef SINGULARITY +ThreadContext TYPEDEF Struct_Microsoft_Singularity_X86_ThreadContext +endif + +Thread TYPEDEF Class_System_Threading_Thread +TransitionRecord TYPEDEF Struct_System_GCs_CallStack_TransitionRecord + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; pushStackMark: If a function may be called by C, push a pointer to its +; frame on to a stack at the beginning of the function. +; +; Transition record layout: +; +; (lower addresses) +; -------------------------- +; |Old stack marker record | +; -------------------------- +; |Addr of call instr | +; -------------------------- +; |Bottom of stack frame | +; -------------------------- +; |rbx | +; -------------------------- +; |rdi | +; -------------------------- +; |rsi | +; -------------------------- +; |rbp | +; -------------------------- +; (higher addresses); +; + +align 16 +__pushStackMark proc frame + ; save caller-save registers + PrologPush rax + PrologPush rcx + PrologPush rdx + PrologPush r8 + PrologPush r9 + PrologPush r10 + PrologPush r11 + .endprolog + lea rcx, [rbp-(SIZE TransitionRecord)] ; calculate new stack marker address +ifdef SINGULARITY + CurrentThreadContext(rax) ; get current threadcontext + mov rdx, [eax].ThreadContext._stackMarkers ;save old stack marker address +else + CurrentThread rax,eax, rdx ; get current thread + mov rdx, [rax].Thread._asmStackMarker ;save old stack marker address +endif + mov [rcx].TransitionRecord._oldTransitionRecord, rdx +ifdef SINGULARITY + mov [rax].ThreadContext._stackMarkers, ecx ; update thread record field +else + mov [rax].Thread._asmStackMarker,rcx ; update thread record field +endif + ;; REVIEWx64: x86=12, x64=56, symbolic value maybe? + mov rdx, [rsp+56] ; load return address of this call + mov [rcx].TransitionRecord._callAddr,rdx + ;; REVIEWx64: x86=16, x64=64, symbolic value maybe? + lea rdx, [rsp+64] + mov [rcx].TransitionRecord._stackBottom, rdx ; save bottom of stack frame + mov [rcx].TransitionRecord._calleeSaveRegisters._EBX, rbx ; save callee-save registers + mov [rcx].TransitionRecord._calleeSaveRegisters._EDI, rdi + mov [rcx].TransitionRecord._calleeSaveRegisters._ESI, rsi + mov [rcx].TransitionRecord._calleeSaveRegisters._EBP, rbp + mov [rcx].TransitionRecord._calleeSaveRegisters._R12, r12 + mov [rcx].TransitionRecord._calleeSaveRegisters._R13, r13 + mov [rcx].TransitionRecord._calleeSaveRegisters._R14, r14 + mov [rcx].TransitionRecord._calleeSaveRegisters._R15, r15 + mov rcx, rax +ifdef SINGULARITY +; call ?g_LeaveManagedSpace@Class_System_GCs_Transitions@@SAXUStruct_Microsoft_Singularity_X86_ThreadContext@@@Z +else + call ?g_LeaveManagedSpace@Class_System_GCs_Transitions@@SAXPEAUClass_System_Threading_Thread@@@Z +endif + pop r11 + pop r10 + pop r9 + pop r8 + pop rdx + pop rcx + pop rax + ret +__pushStackMark endp + +; +; popStackMark: pop the pointer before returning from the function +; +align 16 +__popStackMark proc frame + ; save caller-save registers + PrologPush rax + PrologPush rcx + PrologPush rdx + PrologPush r8 + PrologPush r9 + PrologPush r10 + PrologPush r11 + .endprolog +ifdef SINGULARITY +; CurrentThreadContext(ecx) ; get current thread +; call ?g_ReturnToManagedSpace@Class_System_GCs_Transitions@@SAPEAUClass_System_Threading_Thread@@UStruct_Microsoft_Singularity_X86_ThreadContext@@@Z + CurrentThreadContext(rax) ; get current thread +else + CurrentThreadIndex rax,eax, rcx ; get current thread + mov rcx, rax + call ?g_ReturnToManagedSpace@Class_System_GCs_Transitions@@SAPEAUClass_System_Threading_Thread@@H@Z +endif + lea rcx, [rbp-(SIZE TransitionRecord)] + mov rdx, [rcx].TransitionRecord._oldTransitionRecord ; get old stack marker value +ifdef SINGULARITY + mov [rax].ThreadContext._stackMarkers, rdx; update thread record field +else + mov [rax].Thread._asmStackMarker, rdx; update thread record field +endif + mov rbx, [rcx].TransitionRecord._calleeSaveRegisters._EBX ; restore callee-save registers + mov rdi, [rcx].TransitionRecord._calleeSaveRegisters._EDI + mov rsi, [rcx].TransitionRecord._calleeSaveRegisters._ESI + mov rbp, [rcx].TransitionRecord._calleeSaveRegisters._EBP + mov r12, [rcx].TransitionRecord._calleeSaveRegisters._R12 + mov r13, [rcx].TransitionRecord._calleeSaveRegisters._R13 + mov r14, [rcx].TransitionRecord._calleeSaveRegisters._R14 + mov r15, [rcx].TransitionRecord._calleeSaveRegisters._R15 + pop r11 + pop r10 + pop r9 + pop r8 + pop rdx ; restore caller-save registers + pop rcx + pop rax + ret +__popStackMark endp + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; void CollectBodyTransition(Thread thread, int generation): +; Save the callee-save registers in a transition record and then call +; System.GC.CollectBody(thread, generation) +; +align 16 +?g_CollectBodyTransition@Class_System_GC@@SAXPEAUClass_System_Threading_Thread@@H@Z proc frame +; static void __fastcall GC.CollectBodyTransition(Thread, int) + PrologPush rbp + SetFramePointer rbp + .endprolog + sub rsp, (SIZE TransitionRecord)+32 ; 32 is to shadow parameters to the call to CollectBody + and rsp, NOT 0Fh ; 16-byte align stack frame for call below + push rdx + lea rdx, [rbp-(SIZE TransitionRecord)] +ifdef SINGULARITY + mov rax, [rcx].Thread._context._stackMarkers +else + mov rax, [rcx].Thread._asmStackMarker ; get old marker address +endif + mov [rdx].TransitionRecord._oldTransitionRecord, rax ; link from current record + mov rax, qword ptr [rbp+8] ; load return address + mov [rdx].TransitionRecord._callAddr, rax + lea rax, [rbp+16] ; skip pushed PC and SP + mov [rdx].TransitionRecord._stackBottom, rax ; (bottom of stack frame) + mov [rdx].TransitionRecord._calleeSaveRegisters._EBX, rbx ; save callee-save registers + mov [rdx].TransitionRecord._calleeSaveRegisters._EDI, rdi + mov [rdx].TransitionRecord._calleeSaveRegisters._ESI, rsi + mov [rdx].TransitionRecord._calleeSaveRegisters._R12, r12 + mov [rdx].TransitionRecord._calleeSaveRegisters._R13, r13 + mov [rdx].TransitionRecord._calleeSaveRegisters._R14, r14 + mov [rdx].TransitionRecord._calleeSaveRegisters._R15, r15 + mov rax, qword ptr [rbp] ; get EBP value from stack + mov [rdx].TransitionRecord._calleeSaveRegisters._EBP, rax +ifdef SINGULARITY + mov [rcx].Thread._context._stackMarkers, rdx ; update thread field +else + mov [rcx].Thread._asmStackMarker, rdx ; update thread field +endif + pop rdx + call ?g_CollectBody@Class_System_GC@@SAPEAUClass_System_Threading_Thread@@PEAU2@H@Z + ; static void __fastcall GC.CollectBody(Thread,int) + lea rdi, [rbp-(SIZE TransitionRecord)] + mov rsi, [rdi].TransitionRecord._oldTransitionRecord ; get old marker address +ifdef SINGULARITY + mov [rax].Thread._context._stackMarkers, rsi ; restore thread field +else + mov [rax].Thread._asmStackMarker, rsi ; restore thread field +endif + mov rbx, [rdi].TransitionRecord._calleeSaveRegisters._EBP + mov qword ptr [rbp], rbx ; restore EBP value to stack + mov rbx, [rdi].TransitionRecord._calleeSaveRegisters._EBX ; restore callee-save regs + mov rsi, [rdi].TransitionRecord._calleeSaveRegisters._ESI + mov r12, [rdi].TransitionRecord._calleeSaveRegisters._R12 + mov r13, [rdi].TransitionRecord._calleeSaveRegisters._R13 + mov r14, [rdi].TransitionRecord._calleeSaveRegisters._R14 + mov r15, [rdi].TransitionRecord._calleeSaveRegisters._R15 + mov rdi, [rdi].TransitionRecord._calleeSaveRegisters._EDI + mov rsp, rbp + pop rbp + ret +?g_CollectBodyTransition@Class_System_GC@@SAXPEAUClass_System_Threading_Thread@@H@Z endp + +ifdef NYI +ifdef SINGULARITY +;; __throwDispatcherUnwind depends on this only modifying eax +?g_ReturnToUnlinkStackMethod@Class_System_GCs_CallStack@@SA_NPEAUuintPtr@@@Z proc ;frame + ;.endprolog + mov eax, _UnlinkStackBegin + cmp ecx, eax + jl return_false + mov eax, _UnlinkStackLimit + cmp ecx, eax + jg return_false + mov eax, 1 + ret 0 +return_false: + mov eax, 0 + ret +?g_ReturnToUnlinkStackMethod@Class_System_GCs_CallStack@@SA_NPEAUuintPtr@@@Z endp +endif +endif + +; Below are 3 assembly implementations for the function: +; static void ZeroMemory(byte* dest, UIntPtr len). +; Assumption: Len is in terms of bytes, and is multiple of 4 bytes (dword). +; TODO: reverse the direction of zeroing and compare the results +?g_ZeroMemorySTOS@Class_System_Buffer@@SAXPEAUUntracedPtr_uint8@@PEAUuintPtr@@@Z proc frame + PrologPush rdi + .endprolog + mov rdi, rcx ;dest + mov rax, 0 + mov rcx, rdx + shr rcx, 3 ; the number of qwords + rep stos qword ptr [rdi] + shr edx, 2 ; the number of dwords + and edx, 1 ; the number of dwords remained to be moved + mov ecx, edx + rep stos dword ptr [edi] + pop rdi + ret + ?g_ZeroMemorySTOS@Class_System_Buffer@@SAXPEAUUntracedPtr_uint8@@PEAUuintPtr@@@Z endp + +?g_ZeroMemoryXMM@Class_System_Buffer@@SAXPEAUUntracedPtr_uint8@@PEAUuintPtr@@@Z proc + ; rcx: dist rdx: len + + ; since dist is UIntPtr, whose value is multiple of 8, and we are going + ; to use movntdq, which requires the address to be aligned to 16 bytes. + ; Here align the dist to 16 bytes first. + test rcx, 8 + jz init64Bytes + mov qword ptr [rcx + 0], 0 + add rcx, 8 + sub rdx, 8 + +init64Bytes: + pxor xmm4, xmm4 + + ; zero 64-bytes, if any + cmp rdx, 64 + jl short init32Bytes + + mov rax, rdx + shr rax, 6 + shl rax, 6 + +next: + movntdq [rcx + 0], xmm4 + movntdq [rcx + 16], xmm4 + movntdq [rcx + 32], xmm4 + movntdq [rcx + 48], xmm4 + add rcx, 64 + sub rax, 64 + ja next + + ; now zero remaining 32-bytes, if any +init32Bytes: + test rdx, 32 + jz short init16Bytes + movntdq [rcx + 0], xmm4 + movntdq [rcx + 16], xmm4 + add rcx, 32 + + ; now zero remaining 16-bytes, if any +init16Bytes: + test rdx, 16 + jz short init8Bytes + movntdq [rcx + 0], xmm4 + add rcx, 16 + + ; now zero remaining 8-bytes, if any +init8Bytes: + test rdx, 8 + jz short init4Bytes + mov qword ptr [rcx + 0], 0 + add rcx, 8 + + ; now zero remaining 4-bytes, if any +init4Bytes: + test rdx, 4 + jz short exitZeroMem + mov dword ptr [rcx + 0], 0 + add rcx, 4 + +exitZeroMem: + sfence + ret +?g_ZeroMemoryXMM@Class_System_Buffer@@SAXPEAUUntracedPtr_uint8@@PEAUuintPtr@@@Z endp + +?g_ZeroMemoryMM0@Class_System_Buffer@@SAXPEAUUntracedPtr_uint8@@PEAUuintPtr@@@Z proc + ; rcx: dist rdx: len + + pxor mm0, mm0 + + ; since dist is UIntPtr, whose value is multiple of 8, and we are going + ; to use movntdq, which requires the address to be aligned to 16 bytes. + ; Here align the dist to 16 bytes first. + test rcx, 8 + jz init64Bytes + movntq [rcx + 0], mm0 + add rcx, 8 + sub rdx, 8 + +init64Bytes: + + ; zero 64-bytes, if any + cmp rdx, 64 + jl short init32Bytes + + mov rax, rdx + shr rax, 6 + shl rax, 6 + +next: + movntq [rcx + 0], mm0 + movntq [rcx + 8], mm0 + movntq [rcx + 16], mm0 + movntq [rcx + 24], mm0 + movntq [rcx + 32], mm0 + movntq [rcx + 40], mm0 + movntq [rcx + 48], mm0 + movntq [rcx + 56], mm0 + add rcx, 64 + sub rax, 64 + ja next + + ; now zero remaining 32-bytes, if any +init32Bytes: + test rdx, 32 + jz short init16Bytes + movntq [rcx + 0], mm0 + movntq [rcx + 8], mm0 + movntq [rcx + 16], mm0 + movntq [rcx + 24], mm0 + add rcx, 32 + + ; now zero remaining 16-bytes, if any +init16Bytes: + test rdx, 16 + jz short init8Bytes + movntq [rcx + 0], mm0 + movntq [rcx + 8], mm0 + add rcx, 16 + + ; now zero remaining 8-bytes, if any +init8Bytes: + test rdx, 8 + jz short init4Bytes + movntq [rcx + 0], mm0 + add rcx, 8 + + ; now zero remaining 4-bytes, if any +init4Bytes: + test rdx, 4 + jz short exitZeroMem + mov dword ptr [rcx + 0], 0 + add rcx, 4 + +exitZeroMem: + sfence + emms + ret +?g_ZeroMemoryMM0@Class_System_Buffer@@SAXPEAUUntracedPtr_uint8@@PEAUuintPtr@@@Z endp +end diff --git a/base/Imported/Bartok/runtime/shared/native/arch/amd64/lib.asm b/base/Imported/Bartok/runtime/shared/native/arch/amd64/lib.asm new file mode 100644 index 0000000..4393fb3 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/native/arch/amd64/lib.asm @@ -0,0 +1,1299 @@ +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; + +include core.inc + +externdef __throwDispatcher:NEAR +externdef __throwDispatcherExplicitAddrAfter:NEAR +externdef __throwDispatcherExplicitAddrAfterCore:NEAR +; was externdef ?ExceptionTableLookup@@YI_KPEAUClass_System_Exception@@I@Z:NEAR +; don't understand the I -> _K change yet +externdef ?ExceptionTableLookup@@YA?AUPtrPair@@PEAUClass_System_Exception@@_K@Z:NEAR + +externdef __throwDivideByZeroException:NEAR +externdef __throwNullPointerException:NEAR +externdef __throwOverflowException:NEAR +externdef __throwStackOverflowException:NEAR + +if EXCLUDED +externdef __checkFPStackDepth0:NEAR +externdef __checkFPStackDepth1:NEAR +externdef __checkFPStackDepth2:NEAR +externdef __checkFPStackDepth3:NEAR +externdef __checkFPStackDepth4:NEAR +externdef __checkFPStackDepth5:NEAR +externdef __checkFPStackDepth6:NEAR +externdef __checkFPStackDepth7:NEAR +endif ;; EXCLUDED + + align 8 +$DBLMAXINT DQ 041dfffffffc00000r ; 2.14747e+009 +$DBLMININT DQ 0c1e0000000000000r ; -2.14748e+009 +$MAXLONG DQ 07fffffffffffffffh +$MINLONG DQ 08000000000000000h + +; +; __throwDispatcher(ecx=exception) +; +; Description: this function is called to explicitly throw an exception. +; It assumes that the return address points to the instruction immediately +; after the one where the exception was thrown +; +; Arguments: +; ecx = exception object +; [esp] = the return address +; Effects: +; 1. Create ebp chain entry +; 2. Get return address and uses it to calculate the address of the +; instruction that threw the exception. +; 3. Looks up the appropriate handler and jumps to code to process it. + +align 16 + +__throwDispatcher proc frame + PrologPush rbp ; create ebp chain entry + SetFramePointer rbp ; set new ebp + SubRsp 16 + .endprolog + mov rdx, [rbp+8] ; get return address + mov [rbp-8], rcx ; save exception + sub rdx, 1 ; adjust to point to throw location + ;; set up parameter + mov r8, rdx + mov rdx, rcx + sub rsp, 16 ; space reserved for return value + mov rcx, rsp + ;; set up the frame for parameter + sub rsp, 32 + call ?ExceptionTableLookup@@YA?AUPtrPair@@PEAUClass_System_Exception@@_K@Z + add rsp, 32 + pop rax + pop rdx + mov rcx, [rbp-8] ; restore exception + mov rsp, rbp + pop rbp ; remove ebp chain + add rsp, 8 ; remove eip from the stack +; mov rdx is already ok + jmp __throwDispatcherHandler +__throwDispatcher endp + +; +; __throwDispatcherExplicitAddr (ecx=exception, edx=throwAddress) +; +; Description: +; This is to be used when the address where the exception occurred +; is passed in as an extra argument +; +; Arguments: +; ecx = exception object +; edx = address where the exception was thrown +; + + +align 16 +__throwDispatcherExplicitAddr proc frame + PrologPush rcx ; save exception + ;; set up paramter + mov r8, rdx + mov rdx, rcx + SubRsp 16 + mov rcx, rsp + SubRsp 32 + .endprolog + call ?ExceptionTableLookup@@YA?AUPtrPair@@PEAUClass_System_Exception@@_K@Z + add rsp, 32 + pop rax + pop rdx + pop rcx ; restore exception +; mov rdx is already ok + jmp __throwDispatcherHandler +__throwDispatcherExplicitAddr endp + +; +; __throwDispatcherExplictAddrAfterCore (ecx=exception, edx=throwAddress) +; +; Description: +; This is to be used when the address of the instruction immediately after +; the one that actually threw the exception is passed as an argument. +; +; Arguments: +; ecx = pointer to exception object being thrown +; edx = address of the instruction following the one where +; the exception was thrown +; +; This is used, for example, in stack unwinding, where edx is the +; return address on the stack for the current procedure. +; +; Stack unwinding occurs when the current procedure does not have a handler +; for the routine. The idea is to pop the stack frame and treat the call +; instruction in the caller as though it threw. We only have the return +; address, though, which points to the instruction *after* the call. +; + +align 16 +__throwDispatcherExplicitAddrAfterCore proc frame + PrologPush rcx ; save exception + dec rdx + ;; set up paramter + mov r8, rdx + mov rdx, rcx + SubRsp 16 + mov rcx, rsp + SubRsp 32 + .endprolog + call ?ExceptionTableLookup@@YA?AUPtrPair@@PEAUClass_System_Exception@@_K@Z + add rsp, 32 + pop rax + pop rdx + pop rcx ; restore exception +; mov edx is already ok + jmp __throwDispatcherHandler +__throwDispatcherExplicitAddrAfterCore endp + +; __throwDispatcherHandler (eax=frameSetupInfo or exceptionType, +; ecx=exception, +; edx=spillSize,frameSetupSize, or handlerAddress +; +; Description: +; After the exception table entry has been found, the values are passed here +; for processing. This method simply checks for the easy case of an explicit +; handler (known if the exceptionType is given <-- low bit is zero). +; In this case it simply jumps to the handler. Otherwise it passes the +; information along to __throwDispatcherUnwind. +; +; Arguments: +; If low bit of eax is set: +; eax = frame setup information +; ecx = exception object +; edx = spill size excluding callee-save register saves +; or offset from ebp to end of callee-save register saves +; Otherwise: +; eax = exception type (unused) +; ecx = exception object +; edx = handler address + +align 16 +__throwDispatcherHandler proc ;frame + ;.endprolog + test rax, 1 + jne __throwDispatcherUnwind + ;; ecx=exception, edx=handler + jmp rdx +__throwDispatcherHandler endp + +; __throwDispatcherUnwind (eax=frame setup info, ecx=exception, edx=spill size +; +; Description: +; This is the global unwind handler. It is used to unwind a single stack +; frame if there are no explicit handlers that catch an exception in a given +; function. +; +; Arguments: +; eax = frame setup information, must have low bit set +; ecx = exception object +; edx = spill size excluding callee-save register saves +; or offset from ebp to end of callee-save register saves +; +; See tables\ExceptionTable.cs for details on these values + +align 16 +__throwDispatcherUnwind proc ;frame + ;.endprolog + ;; eax=frame info + ;; edx=spill size + + ;; obviously ebp isn't useful under frame pointer omission + ;; but less obviously esp may be invalid if ebp is good + ;; (e.g. under varargs we may not have known how many arguments to + ;; pop; this is one reason why varargs turns off frame pointer omission) + test rax, 2h + jne esp_is_good + ;; ebp_is_good + add rdx, rbp + mov rsp, rdx + jmp esp_is_setup +esp_is_good: + ;; pop spill slots + add rsp, rdx + +esp_is_setup: + + ;; restore callee-saves and pop values from stack + ;; (excludes ebp if used as the frame pointer) + +; restore float registers + test rax, 100000h + je skip_xmm15_restore + ;; restore xmm15 + movdqa xmm15, [rsp] + add rsp, 16 +skip_xmm15_restore: + test rax, 80000h + je skip_xmm14_restore + ;; restore xmm14 + movdqa xmm14, [rsp] + add rsp, 16 +skip_xmm14_restore: + test rax, 40000h + je skip_xmm13_restore + ;; restore xmm13 + movdqa xmm13, [rsp] + add rsp, 16 +skip_xmm13_restore: + test rax, 20000h + je skip_xmm12_restore + ;; restore xmm12 + movdqa xmm12, [rsp] + add rsp, 16 +skip_xmm12_restore: + test rax, 10000h + je skip_xmm11_restore + ;; restore xmm11 + movdqa xmm11, [rsp] + add rsp, 16 +skip_xmm11_restore: + test rax, 8000h + je skip_xmm10_restore + ;; restore xmm10 + movdqa xmm10, [rsp] + add rsp, 16 +skip_xmm10_restore: + test rax, 4000h + je skip_xmm9_restore + ;; restore xmm9 + movdqa xmm9, [rsp] + add rsp, 16 +skip_xmm9_restore: + test rax, 2000h + je skip_xmm8_restore + ;; restore xmm8 + movdqa xmm8, [rsp] + add rsp, 16 +skip_xmm8_restore: + test rax, 1000h + je skip_xmm7_restore + ;; restore xmm7 + movdqa xmm7, [rsp] + add rsp, 16 +skip_xmm7_restore: + test rax, 800h + je skip_xmm6_restore + ;; restore xmm6 + movdqa xmm6, [rsp] + add rsp, 16 +skip_xmm6_restore: + +; account of alignment + test rax, 200000h + je skip_align_xmm + ;; add 8 to rsp for alignment + add rsp, 8 +skip_align_xmm: + +; restore int registers + test rax, 400h + je skip_r15_restore + ;; restore r15 + pop r15 +skip_r15_restore: + test rax, 200h + je skip_r14_restore + ;; restore r14 + pop r14 +skip_r14_restore: + test rax, 100h + je skip_r13_restore + ;; restore r13 + pop r13 +skip_r13_restore: + test rax, 80h + je skip_r12_restore + ;; restore r12 + pop r12 +skip_r12_restore: + test rax, 4h + je skip_edi_restore + ;; restore edi + pop rdi +skip_edi_restore: + test rax, 8h + je skip_esi_restore + ;; restore esi + pop rsi +skip_esi_restore: + test rax, 10h + je skip_ebp_restore + ;; restore ebp + pop rbp +skip_ebp_restore: + test rax, 20h + je skip_ebx_restore + ;; restore ebx + pop rbx +skip_ebx_restore: + + test rax, 40h + je skip_jump_transition_record + ;; jump over transition record + add rsp, (SIZE Struct_System_GCs_CallStack_TransitionRecord) +skip_jump_transition_record: + + ;; restore ebp if it was used as the frame pointer + test rax, 2h + jne skip_frame_pointer_restore + ;; restore frame pointer (esp == ebp already) + pop rbp +skip_frame_pointer_restore: + + ;; set edx=return address + pop rdx + + ;; no arguments to pop + + ;; At this point + ;; ecx=exception, edx=return address + ;; esi/edi/ebx/ebp/esp have been restored + ;; eax is scratch + + ;; set up next handler search + jmp __throwDispatcherExplicitAddrAfter +__throwDispatcherUnwind endp + +; +; __throwDivideByZeroException: instantiate an divide-by-zero exception +; and throw it. +; +; Assumes rdx points to the address after the one that threw. +; + +align 16 +__throwDivideByZeroException proc frame + PrologPush rbx + PrologPush rsi + .endprolog + mov rbx,rdx ; save address + lock inc ?c_allocationGCInhibitCount@Class_System_GC@@2HA + mov rcx,offset ??_7System_DivideByZeroException@@6B@ + call ?g_AllocateObject@Class_System_GC@@SAPEAUClass_System_Object@@PEAUClass_System_VTable@@@Z + mov rsi,rax ; save pointer to instance of exception + mov rcx,rax ; initialize instance + call ?m__ctor@Class_System_DivideByZeroException@@SAXPEAU1@@Z + lock dec ?c_allocationGCInhibitCount@Class_System_GC@@2HA + mov rcx,rsi + mov rdx,rbx + pop rsi + pop rbx + jmp __throwDispatcherExplicitAddr +__throwDivideByZeroException endp + +; +; __throwStackOverflowException: instantiate an StackOverflow exception +; and throw it. +; +; Assumes edx points to the address of the instruction that faulted +; + +align 16 +__throwStackOverflowException proc frame + PrologPush rbx + PrologPush rsi + .endprolog + mov rbx,rdx ; save address + lock inc ?c_allocationGCInhibitCount@Class_System_GC@@2HA + mov rcx,offset ??_7System_StackOverflowException@@6B@ + call ?g_AllocateObject@Class_System_GC@@SAPEAUClass_System_Object@@PEAUClass_System_VTable@@@Z + mov rsi,rax ; save pointer to instance of exception + mov rcx,rax ; initialize instance + call ?m__ctor@Class_System_StackOverflowException@@SAXPEAU1@@Z + lock dec ?c_allocationGCInhibitCount@Class_System_GC@@2HA + ;; set up paramter + mov rdx, rsi + mov r8, rbx + sub rsp, 16 + mov rcx, rsp + sub rsp, 32 + call ?ExceptionTableLookup@@YA?AUPtrPair@@PEAUClass_System_Exception@@_K@Z + add rsp, 32 + pop rax + pop rdx + ResetGuardPageInStackOverflow + pop rsi + pop rbx + mov rcx,rax +; mov rdx is already ok + jmp rdx +__throwStackOverflowException endp + +; +; __throwNullReferenceException: instantiate an NullReference exception +; and throw it. +; +; Assumes edx points to the address of the instruction that faulted +; + +align 16 +__throwNullReferenceException proc frame + PrologPush rbx + PrologPush rsi + .endprolog + mov rbx,rdx ; save address + lock inc ?c_allocationGCInhibitCount@Class_System_GC@@2HA + mov rcx,offset ??_7System_NullReferenceException@@6B@ + call ?g_AllocateObject@Class_System_GC@@SAPEAUClass_System_Object@@PEAUClass_System_VTable@@@Z + mov rsi,rax ; save pointer to instance of exception + mov rcx,rax ; initialize instance + call ?m__ctor@Class_System_NullReferenceException@@SAXPEAU1@@Z + lock dec ?c_allocationGCInhibitCount@Class_System_GC@@2HA + mov rcx,rsi + mov rdx,rbx + pop rsi + pop rbx + jmp __throwDispatcherExplicitAddr +__throwNullReferenceException endp + +; +; __throwDivideByZeroException: instantiate an divide-by-zero exception +; and throw it. +; +; Assumes rdx points to the address of the instruction that faulted +; + +align 16 +__throwOverflowException proc frame + PrologPush rbx + PrologPush rsi + .endprolog + mov rbx,rdx ; save address + lock inc ?c_allocationGCInhibitCount@Class_System_GC@@2HA + mov rcx,offset ??_7System_OverflowException@@6B@ + call ?g_AllocateObject@Class_System_GC@@SAPEAUClass_System_Object@@PEAUClass_System_VTable@@@Z + mov rsi,rax ; save pointer to instance of exception + mov rcx,rax ; initialize instance + call ?m__ctor@Class_System_OverflowException@@SAXPEAU1@@Z + lock dec ?c_allocationGCInhibitCount@Class_System_GC@@2HA + mov rcx,rsi + mov rdx,rbx + pop rsi + pop rbx + jmp __throwDispatcherExplicitAddr +__throwOverflowException endp + +; +; int System.VTable.doubleToInt(double) +; + +align 16 +?g_doubleToInt@Class_System_VTable@@SAHN@Z proc frame + PrologPush rbp + SetFramePointer rbp + SubRsp 8 + .endprolog + movsd real8 ptr [rbp+16], xmm0 + fld real8 ptr [rbp+16] + wait + fnstcw word ptr [rbp-2] + wait + mov ax,word ptr [rbp-2] + or eax,0C00h + mov word ptr [rbp-4],ax + fldcw word ptr [rbp-4] + fistp dword ptr [rbp-8] + fldcw word ptr [rbp-2] + mov eax,dword ptr [rbp-8] + + cmp eax,080000000h + je possible_overflow +return: + mov rsp,rbp + pop rbp + ret + +possible_overflow: + fld real8 ptr [rbp+16] + fcomp real8 ptr $DBLMAXINT + fnstsw ax + test ah,4 + jne short return_zero + test ah,1 + jne short return_MININT + +return_MAXINT: + mov eax, 07fffffffh + jmp short return + +return_zero: + xor eax, eax + jmp short return + +return_MININT: + mov eax, 080000000h + jmp short return + +?g_doubleToInt@Class_System_VTable@@SAHN@Z endp + +; +; long System.VTable.doubleToLong(double) +; + +align 16 +?g_doubleToLong@Class_System_VTable@@SA_JN@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + add rsp,-12 + movsd real8 ptr [rbp+16], xmm0 + fld real8 ptr [rbp+16] + wait + fnstcw word ptr [rbp-2] + wait + mov ax,word ptr [rbp-2] + or eax,0C00h + mov word ptr [rbp-4],ax + fldcw word ptr [rbp-4] + fistp qword ptr [rbp-12] + fldcw word ptr [rbp-2] + mov rax,qword ptr [rbp-12] + mov rdx,08000000000000000h + cmp rax,rdx + je possible_overflow +return: + mov rsp,rbp + pop rbp + ret + +possible_overflow: + mov rdx,rax ; save lsw + fld real8 ptr [rbp+16] + fild qword ptr $MAXLONG + fcompp + fnstsw ax + test ah,4 + jne short return_zero + test ah,65 + je short check_MINLONG + jmp return_MINLONG + +return_zero: + xor rax, rax + jmp short return + +check_MINLONG: + fld real8 ptr [rbp+16] + fild qword ptr $MINLONG + fcompp + fnstsw ax + test ah,1 + jne short return_original + +return_MINLONG: + mov rax, 08000000000000000h + jmp short return + +return_original: + mov rax, rdx ; restore lsw to eax + mov rdx, 08000000000000000h + and rax, rdx + jmp short return + +?g_doubleToLong@Class_System_VTable@@SA_JN@Z endp + +; +; int System.VTable.floatToInt(float) +; + +align 16 +?g_floatToInt@Class_System_VTable@@SAHM@Z proc frame + PrologPush rbp + SetFramePointer rbp + SubRsp 8 + .endprolog + movss real4 ptr [rbp+16], xmm0 + fld real4 ptr [rbp+16] + wait + fnstcw word ptr [rbp-2] + wait + xor eax,eax + mov ax,word ptr [rbp-2] + or eax,0C00h + mov word ptr [rbp-4],ax + fldcw word ptr [rbp-4] + fistp dword ptr [rbp-8] + fldcw word ptr [rbp-2] + mov eax,dword ptr [rbp-8] + + cmp eax,080000000h + je possible_overflow +return: + mov rsp,rbp + pop rbp + ret + +possible_overflow: + fld real4 ptr [rbp+16] + fcomp real8 ptr $DBLMAXINT + fnstsw ax + test ah,4 + jne short return_zero + test ah,1 + jne short return_MININT + mov eax, 07fffffffh + jmp short return + +return_zero: + xor eax, eax + jmp short return + +return_MININT: + mov eax, 080000000h + jmp short return + +?g_floatToInt@Class_System_VTable@@SAHM@Z endp + +; +; long System.VTable.floatToLong(float) +; + +align 16 +?g_floatToLong@Class_System_VTable@@SA_JM@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + add rsp,-12 + movss real4 ptr [rbp+16], xmm0 + fld real4 ptr [rbp+16] + wait + fnstcw word ptr [rbp-2] + wait + mov ax,word ptr [rbp-2] + or eax,0C00h + mov word ptr [rbp-4],ax + fldcw word ptr [rbp-4] + fistp qword ptr [rbp-12] + fldcw word ptr [rbp-2] + mov rax,qword ptr [rbp-12] + mov rdx,08000000000000000h + cmp rax,rdx + je possible_overflow +return: + mov rsp,rbp + pop rbp + ret + +possible_overflow: + mov rdx,rax ; save lsw + fld real4 ptr [rbp+16] + fild qword ptr $MAXLONG + fcompp + fnstsw ax + test ah,4 + jne short return_zero + test ah,65 + je short check_MINLONG + +return_MAXLONG: + mov rax, 07fffffffffffffffh + jmp short return + +return_zero: + xor rax, rax + jmp short return + +check_MINLONG: + fld real4 ptr [rbp+16] + fild qword ptr $MINLONG + fcompp + fnstsw ax + test ah,1 + jne short return_original + +return_MINLONG: + mov rax, 08000000000000000h + jmp short return + +return_original: + mov rax, rdx ; restore lsw to eax + mov rdx, 08000000000000000h + and rax, rdx + jmp short return + +?g_floatToLong@Class_System_VTable@@SA_JM@Z endp + +; +; int System.VTable.checkedDoubleToInt(double) +; + +align 16 +?g_checkedDoubleToInt@Class_System_VTable@@SAHN@Z proc frame + PrologPush rbp + SetFramePointer rbp + SubRsp 8 + .endprolog + movsd real8 ptr [rbp+16], xmm0 + fld real8 ptr [rbp+16] + wait + fnstcw word ptr [rbp-2] + wait + mov ax,word ptr [rbp-2] + or eax,0C00h + mov word ptr [rbp-4],ax + fldcw word ptr [rbp-4] + fistp dword ptr [rbp-8] + fldcw word ptr [rbp-2] + mov eax,dword ptr [rbp-8] + + cmp eax,080000000h + je possible_overflow +return: + mov rsp,rbp + pop rbp + ret + +possible_overflow: + fld real8 ptr [rbp+16] + fcomp real8 ptr $DBLMAXINT + fnstsw ax + test ah,4 ; test for unordered + jne short throw_exception + test ah,1 ; test for <$DBLMAXINT + jne short return_MININT + ; src > $DBLMAXINT + ; throw an overflow exception + jmp short throw_exception + +return_MININT: + ; check against $DBLMININT + fld real8 ptr [rbp+16] + fcomp real8 ptr $DBLMININT + fnstsw ax + test ah, 1 ; test for < $DBLMININT + jne short throw_exception ; throw exception if true + mov eax, 080000000h + jmp short return + +throw_exception: + ; throw an overflow exception + ; set up stack frame so that it looks like a call to throwNewOverflowException + ; from the caller of this function. + mov rsp,rbp + pop rbp +; pop rax ; grab return address +; add rsp, 8 ; move rsp pass the first parameter. +; mov [rsp],rax ; overwrite argument + jmp ?g_throwNewOverflowException@Class_System_VTable@@SAXXZ + +?g_checkedDoubleToInt@Class_System_VTable@@SAHN@Z endp + +; +; long System.VTable.checkedDoubleToLong(double) +; + +align 16 +?g_checkedDoubleToLong@Class_System_VTable@@SA_JN@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + add rsp,-12 + movsd real8 ptr [rbp+16], xmm0 + fld real8 ptr [rbp+16] + wait + fnstcw word ptr [rbp-2] + wait + mov ax,word ptr [rbp-2] + or eax,0C00h + mov word ptr [rbp-4],ax + fldcw word ptr [rbp-4] + fistp qword ptr [rbp-12] + fldcw word ptr [rbp-2] + mov rax,qword ptr [rbp-12] + mov rdx,08000000000000000h + cmp rax, rdx + je possible_overflow +return: + mov rsp,rbp + pop rbp + ret + +possible_overflow: + mov rdx,rax ; save lsw + fld real8 ptr [rbp+16] + fild qword ptr $MAXLONG + fcompp + fnstsw ax + test ah,4 ; test for unordered + jne short return_zero + test ah,65 ; test for <= $MAXLONG + je short check_MINLONG + +return_MAXLONG: + ; src > $MAXLONG + ; throw an exception + jmp short throw_exception + +return_zero: + jmp short throw_exception + +check_MINLONG: + ; src <= $MINLONG + fild qword ptr $MINLONG + fld real8 ptr [rbp+16] + fcompp ; real8 ptr [rbp+8] < $MINLONG + fnstsw ax + test ah,1 + jne short throw_exception + +return_MINLONG: + mov rax, rdx ; restore lsw to eax + mov rdx, 08000000000000000h + and rax, rdx + jmp short return + + +throw_exception: + ; throw an overflow exception + ; set up stack frame so that it looks like a call to throwNewOverflowException + ; from the caller of this function. + mov rsp,rbp + pop rbp + ;pop rax ; grab return address + ;add rsp, 8 ; move rsp pass the first parameter. + ;mov [rsp],eax ; overwrite argument + jmp ?g_throwNewOverflowException@Class_System_VTable@@SAXXZ + +?g_checkedDoubleToLong@Class_System_VTable@@SA_JN@Z endp + +; +; int System.VTable.checkedFloatToInt(float) +; + +align 16 +?g_checkedFloatToInt@Class_System_VTable@@SAHM@Z proc frame + PrologPush rbp + SetFramePointer rbp + SubRsp 8 + .endprolog + movss real4 ptr [rbp+16], xmm0 + fld real4 ptr [rbp+16] + wait + fnstcw word ptr [rbp-2] + wait + xor eax,eax + mov ax,word ptr [rbp-2] + or eax,0C00h + mov word ptr [rbp-4],ax + fldcw word ptr [rbp-4] + fistp dword ptr [rbp-8] + fldcw word ptr [rbp-2] + mov eax,dword ptr [rbp-8] + + cmp eax,080000000h + je possible_overflow + +return: + mov rsp,rbp + pop rbp + ret + +possible_overflow: + fld real4 ptr [rbp+16] + fcomp real8 ptr $DBLMAXINT + fnstsw ax + test ah,4 ; test for unordered + jne short throw_exception + test ah,1 ; test for src < $DBLMAXINT + jne short return_MININT + ; src > $DBLMAXINT + ; throw an overflow exception + jmp short throw_exception + +return_MININT: + ; need to check against $DBLMININT, if it is less than, + ; then throw an overflow exception + fld real4 ptr [rbp+16] + fcomp real8 ptr $DBLMININT + fnstsw ax + test ah,1 ; test for less than + jne short throw_exception + mov eax, 080000000h + jmp short return + +throw_exception: + ; throw an overflow exception + ; set up stack frame so that it looks like a call to throwNewOverflowException + ; from the caller of this function. + mov rsp,rbp + pop rbp + ;pop rax ; grab return address + ;mov [rsp],eax ; overwrite argument + jmp ?g_throwNewOverflowException@Class_System_VTable@@SAXXZ + +?g_checkedFloatToInt@Class_System_VTable@@SAHM@Z endp + +; +; long System.VTable.checkedFloatToLong(float) +; + +align 16 +?g_checkedFloatToLong@Class_System_VTable@@SA_JM@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + add rsp,-12 + movss real4 ptr [rbp+16], xmm0 + fld real4 ptr [rbp+16] + wait + fnstcw word ptr [rbp-2] + wait + mov ax,word ptr [rbp-2] + or eax,0C00h + mov word ptr [rbp-4],ax + fldcw word ptr [rbp-4] + fistp qword ptr [rbp-12] + fldcw word ptr [rbp-2] + mov rax,qword ptr [rbp-12] + mov rdx,08000000000000000h + cmp rax,rdx + je possible_overflow +return: + mov rsp,rbp + pop rbp + ret + +possible_overflow: + mov rdx,rax ; save lsw + fld real4 ptr [rbp+16] + fild qword ptr $MAXLONG + fcompp + fnstsw ax + test ah,4 ; test for unordered + jne short return_zero + test ah,65 ; test for <= $MAXLONG + je short check_MINLONG + +return_MAXLONG: + ; src > $MAXLONG + ; throw an exception + jmp short throw_exception + +return_zero: + ; compare with $MAXLONG results in unordered + ; throw an overflow exception + jmp short throw_exception + +check_MINLONG: + ; src <= $MINLONG + fild qword ptr $MINLONG + fld real4 ptr [rbp+16] + fcompp ; real8 ptr [rbp+8] < $MINLONG + fnstsw ax + test ah,1 + jne short throw_exception ; throw an overflow exception when src < $MINLONG +return_MINLONG: + mov rax, rdx ; restore lsw + mov rdx, 08000000000000000h + and rax, rdx + jmp short return + +throw_exception: + ; throw an overflow exception + ; set up stack frame so that it looks like a call to throwNewOverflowException + ; from the caller of this function. + mov rsp,rbp + pop rbp + ;pop rax ; grab return address + ;mov [rsp],eax ; overwrite argument + jmp ?g_throwNewOverflowException@Class_System_VTable@@SAXXZ + +?g_checkedFloatToLong@Class_System_VTable@@SA_JM@Z endp + +; +; double System.Math.Sin(double) +; + +align 16 +?g_Sin@Class_System_Math@@SANN@Z proc ;frame + ;.endprolog + add rsp, -8 + movsd real8 ptr [rsp+16], xmm0 + fld real8 ptr [rsp+16] + fsin + fstp real8 ptr [rsp] + movsd xmm0, real8 ptr [rsp] + add rsp, 8 + ret +?g_Sin@Class_System_Math@@SANN@Z endp + +; +; double System.Math.Cos(double) +; + +align 16 +?g_Cos@Class_System_Math@@SANN@Z proc ;frame + ;.endprolog + add rsp, -8 + movsd real8 ptr [rsp+16], xmm0 + fld real8 ptr [rsp+16] + fcos + fstp real8 ptr [rsp] + movsd xmm0, real8 ptr [rsp] + add rsp, 8 + ret +?g_Cos@Class_System_Math@@SANN@Z endp + +; +; double System.Math.Tan(double) +; + +align 16 +?g_Tan@Class_System_Math@@SANN@Z proc ;frame + ;.endprolog + add rsp, -8 + movsd real8 ptr [rsp+16], xmm0 + fld real8 ptr [rsp+16] + fptan + fstp real8 ptr [rsp] + movsd xmm0, real8 ptr [rsp] + add rsp, 8 + ret +?g_Tan@Class_System_Math@@SANN@Z endp + +; +; +; double System.Math.Atan(double) +; + +align 16 +?g_Atan@Class_System_Math@@SANN@Z proc ;frame + ;.endprolog + add rsp, -8 + movsd real8 ptr [rsp+16], xmm0 + fld real8 ptr [rsp+16] + fld1 + fpatan + fstp real8 ptr [rsp] + movsd xmm0, real8 ptr [rsp] + add rsp, 8 + ret +?g_Atan@Class_System_Math@@SANN@Z endp + +; +; double System.Math.atan2(double,double) +; + +align 16 +?g_atan2@Class_System_Math@@SANNN@Z proc ;frame + ;.endprolog + add rsp, -8 + movsd real8 ptr [rsp+16], xmm0 + movsd real8 ptr [rsp+24], xmm1 + fld real8 ptr [rsp+16] + fld real8 ptr [rsp+24] + fpatan + fstp real8 ptr [rsp] + movsd xmm0, real8 ptr [rsp] + add rsp, 8 + ret +?g_atan2@Class_System_Math@@SANNN@Z endp + +; +; double System.Math.exp(double) +; + +align 16 +?g_exp@Class_System_Math@@SANN@Z proc frame + PrologPush rbp + SetFramePointer rbp + SubRsp 8 + .endprolog + + fldl2e + movsd real8 ptr [rbp+16], xmm0 + fmul real8 ptr [rbp+16] + fld st(0) + frndint + fxch st(1) + fsub st(0), st(1) + f2xm1 + fld1 + faddp st(1), st(0) + fscale +;isNaN?? + fstp st(1) + fstp real8 ptr [rbp-8] + movsd xmm0, real8 ptr [rbp-8] + mov rsp,rbp + pop rbp + ret +?g_exp@Class_System_Math@@SANN@Z endp + +; +; double System.Math.log(double) +; + +align 16 +?g_Log@Class_System_Math@@SANN@Z proc ;frame + ;.endprolog + add rsp, -8 + fldln2 + movsd real8 ptr [rsp+16], xmm0 + fld real8 ptr [rsp+16] + fyl2x + fstp real8 ptr [rsp] + movsd xmm0, real8 ptr [rsp] + add rsp, 8 + ret +?g_Log@Class_System_Math@@SANN@Z endp + +; +; double System.Math.Ceiling(double) +; + +align 16 +?g_Ceiling@Class_System_Math@@SANN@Z proc frame + PrologPush rbp + SetFramePointer rbp + SubRsp 8 + .endprolog + movsd real8 ptr [rbp+16], xmm0 + fld real8 ptr [rbp+16] + wait + fnstcw word ptr [rbp-2] + wait + mov ax,word ptr [rbp-2] + and ah,0F3h + or ah,008h + mov word ptr [rbp-4],ax + fldcw word ptr [rbp-4] + frndint + fldcw word ptr [rbp-2] + fstp real8 ptr [rbp-8] + movsd xmm0, real8 ptr [rbp-8] + mov rsp,rbp + pop rbp + ret +?g_Ceiling@Class_System_Math@@SANN@Z endp + +; +; double System.Math.Floor(double) +; + +align 16 +?g_Floor@Class_System_Math@@SANN@Z proc frame + PrologPush rbp + SetFramePointer rbp + SubRsp 8 + .endprolog + + movsd real8 ptr [rsp+16], xmm0 + fld real8 ptr [rbp+16] + wait + fnstcw word ptr [rbp-2] + wait + mov ax,word ptr [rbp-2] + and ah,0F3h + or ah,004h + mov word ptr [rbp-4],ax + fldcw word ptr [rbp-4] + frndint + fldcw word ptr [rbp-2] + fstp real8 ptr [rbp-8] + movsd xmm0, real8 ptr [rbp-8] + mov rsp,rbp + pop rbp + ret +?g_Floor@Class_System_Math@@SANN@Z endp + +; +; double System.Math.Round(double) +; + +align 16 +?g_Round@Class_System_Math@@SANN@Z proc ;frame + ;.endprolog + add rsp, -8 + movsd real8 ptr [rsp+16], xmm0 + fld real8 ptr [rsp+16] + frndint + fstp real8 ptr [rsp] + movsd xmm0, real8 ptr [rsp] + add rsp, 8 + ret +?g_Round@Class_System_Math@@SANN@Z endp + +; +; float System.Math.Abs(float) +; + +align 16 +?g_abs@Class_System_Math@@SAMM@Z proc ;frame + ;.endprolog + add rsp, -8 + movss real4 ptr [rsp+16], xmm0 + fld real4 ptr [rsp+16] + fabs + fstp real4 ptr [rsp] + movss xmm0, real4 ptr [rsp] + add rsp, 8 + ret +?g_abs@Class_System_Math@@SAMM@Z endp + +; +; double System.Math.Abs(double) +; + +align 16 +?g_abs@Class_System_Math@@SANN@Z proc ;frame + ;.endprolog + add rsp, -8 + movsd real8 ptr [rsp+16], xmm0 + fld real8 ptr [rsp+16] + fabs + fstp real8 ptr [rsp] + movsd xmm0, real8 ptr [rsp] + add rsp, 8 + ret +?g_abs@Class_System_Math@@SANN@Z endp + +align 16 +?g_floatRem@Class_System_VTable@@SAMMM@Z proc ;frame + ;.endprolog + add rsp, -8 + movss real4 ptr [rsp+16], xmm0 + movss real4 ptr [rsp+24], xmm1 + fld real4 ptr [rsp+24] + fld real4 ptr [rsp+16] +fremloop: + fprem + fstsw ax + fwait + sahf + jp fremloop ; Continue while the FPU status bit C2 is set + ffree st(1) + fstp real4 ptr [rsp] + movss xmm0, real4 ptr [rsp] + add rsp, 8 + ret +?g_floatRem@Class_System_VTable@@SAMMM@Z endp + +align 16 +?g_doubleRem@Class_System_VTable@@SANNN@Z proc ;frame + ;.endprolog + add rsp, -8 + movsd real8 ptr [rsp+16], xmm0 + movsd real8 ptr [rsp+24], xmm1 + fld real8 ptr [rsp+24] + fld real8 ptr [rsp+16] +fremloop: + fprem + fstsw ax + fwait + sahf + jp fremloop ; Continue while the FPU status bit C2 is set + ffree st(1) + fstp real8 ptr [rsp] + movsd xmm0, real8 ptr [rsp] + add rsp, 8 + ret +?g_doubleRem@Class_System_VTable@@SANNN@Z endp +end + diff --git a/base/Imported/Bartok/runtime/shared/native/arch/arm/gc.asm b/base/Imported/Bartok/runtime/shared/native/arch/arm/gc.asm new file mode 100644 index 0000000..c1aa1c2 --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/native/arch/arm/gc.asm @@ -0,0 +1,263 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; + + AREA |.text|, CODE, READONLY + +PAGE_BITS EQU 12 +MASK_OWNER EQU 0x3 + + INCLUDE core.inc + + ;;;; + ;; Placeholder stubs to satisfy the linker + ;;;; + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; pushStackMark: If a function may be called by C, push a pointer to its +; frame on to a stack at the beginning of the function. +; +; Transition record layout: +; +; (lower addresses) +; -------------------------- +; |Old stack marker record | +; -------------------------- +; |Addr of call instr | +; -------------------------- +; |Bottom of stack frame | +; -------------------------- +; |r4 | +; -------------------------- +; |r5 | +; -------------------------- +; |r6 | +; -------------------------- +; |r7 | +; -------------------------- +; |r8 | +; -------------------------- +; |r9 | +; -------------------------- +; |r10 | +; -------------------------- +; |r11 | +; -------------------------- +; (higher addresses); +; + + EXPORT __pushStackMark + NESTED_ENTRY __pushStackMark + PROLOG_END + + ;; Save the callee-save registers in the transition record. + ;; Since the transition record is located immediately below the FP, + ;; we index off the FP rather than the pointer to the struct itself. + stmdb fp, {r4-r11} ; Store callee-save registers in record + ;; From this point forward, we use r8, r9, and r10 as temps. + ;; R12 may hold the address of the call + ;; TransitionRecord *newRecord(r9) = FP - sizeof(TransitionRecord); + sub r9, fp, #|Struct_System_GCs_CallStack_TransitionRecord___SIZE| + ;; link new stack marker into chain starting at CurrentThread + ;; Thread *currentThread(r10) = Thread.CurrentThread() + CurrentThread r10, r8 + ;; TransitionRecord *oldRecord(r8) = currentThread(r10)->asmStackMarker + ldr r8, [r10, #|Class_System_Threading_Thread___asmStackMarker|] + ;; newRecord(r9)->oldTransitionRecord = oldRecord(r8) + str r8, [r9, #|Struct_System_GCs_CallStack_TransitionRecord___oldTransitionRecord|] + ;; currentThread(r10)->asmStackMarker = newRecord(r9) + str r9, [r10, #|Class_System_Threading_Thread___asmStackMarker|] + ;; newRecord(r9)->callAddr = return address of this call (lr). + str lr, [r10, #|Struct_System_GCs_CallStack_TransitionRecord___callAddr|] + ;; newRecord(r9)->stackBottom = bottom of stack frame (sp) + str sp, [r10, #|Struct_System_GCs_CallStack_TransitionRecord___stackBottom|] + ;; Restore registers r9 and r10 (and the ummodified r11) + ldmdb fp, {r8-r11} + ;; return + mov pc, lr + + ENTRY_END + + EXPORT __popStackMark + NESTED_ENTRY __popStackMark + PROLOG_END + + ;; From this point forward, we use r8, r9, and r10 as temps. + ;; R12 may hold the address of the call + ;; TransitionRecord *newRecord(r9) = FP - sizeof(TransitionRecord); + sub r9, fp, #|Struct_System_GCs_CallStack_TransitionRecord___SIZE| + ;; Thread *currentThread(r10) = Thread.CurrentThread() + CurrentThread r10, r8 + ;; TransitionRecord *oldRecord(r8) = newRecord(r9)->oldTransitionRecord + ldr r8, [r9, #|Struct_System_GCs_CallStack_TransitionRecord___oldTransitionRecord|] + ;; currentThread(r10) = oldRecord(r8) + str r8, [r10, #|Class_System_Threading_Thread___asmStackMarker|] + ;; Restore callee-save registers from the transition record. + ;; Since the transition record is located immediately below the FP, + ;; we index off the FP rather than the pointer to the struct itself. + ldmdb fp, {r4-r11} + ;; return + mov pc, lr + + ENTRY_END + + EXPORT |?g_CollectBodyTransition@Class_System_GC@@SAXPAUClass_System_Threading_Thread@@H@Z| + NESTED_ENTRY "?g_CollectBodyTransition@Class_System_GC@@SAXPAUClass_System_Threading_Thread@@H@Z" + + mov r12, sp + ;; Save the two words of the arguments (only two are in use) + stmdb sp!, {r0-r1} + ;; Save the FP, SP, and the LR + stmdb sp!, {r4-r11, r12, lr} + ;; Establish the new FP + sub fp, r12, #8 + + ;; TransitionRecord transitionRecord = new TransitionRecord() + ;; TransitionRecord *newRecord(sp) = &transitionRecord + ;; The transition record includes the saves. Sub an extra 8 for the sp and lr + sub sp, fp, #|Struct_System_GCs_CallStack_TransitionRecord___SIZE| + sub sp, sp, #8 + + PROLOG_END + + ;; TransitionRecord *oldRecord(r9) = currentThread(r10)->asmStackMarker + ldr r9, [r0, #|Class_System_Threading_Thread___asmStackMarker|] + ;; newRecord(sp)->oldTransitionRecord = oldRecord(r9) + str r9, [sp, #|Struct_System_GCs_CallStack_TransitionRecord___oldTransitionRecord|] + ;; currentThread(r10)->asmStackMarker = newRecord(sp) + str sp, [r0, #|Class_System_Threading_Thread___asmStackMarker|] + ;; newRecord(sp)->callAddr = return address of this call (lr). + str lr, [sp, #|Struct_System_GCs_CallStack_TransitionRecord___callAddr|] + ;; newRecord(sp)->stackBottom = bottom of stack frame (r12) + str r12, [sp, #|Struct_System_GCs_CallStack_TransitionRecord___stackBottom|] + ;; Thread *currentThread(r0) = System.GC.CollectBody(r0, r1) + bl |?g_CollectBody@Class_System_GC@@SAPAUClass_System_Threading_Thread@@PAU2@H@Z| + ;; TransitionRecord *newRecord(sp) = &transitionRecord + ;; TransitionRecord *oldRecord(r9) = newRecord(sp)->oldTransitionRecord + ldr r9, [sp, #|Struct_System_GCs_CallStack_TransitionRecord___oldTransitionRecord|] + ;; currentThread(r0)->asmStackMarker = oldRecord(r9) + str r9, [r0, #|Class_System_Threading_Thread___asmStackMarker|] + + ;; Restore permanents, FP, SP, and return + ldmdb fp, {r4-r11, sp, pc} + + ENTRY_END + + +|?g_ZeroMemoryMM0@Class_System_Buffer@@SAXPAUUntracedPtr_uint8@@PAUuintPtr@@@Z| + EXPORT |?g_ZeroMemoryMM0@Class_System_Buffer@@SAXPAUUntracedPtr_uint8@@PAUuintPtr@@@Z| + + mov r0, #0xde + mov r0, r0 LSL #8 + orr r0, r0, #0xad + mov r0, r0 LSL #16 + mov pc, lr + + LTORG + + END + +;;;;;;;;;;;;;;;;;;;;;;; +;;;;; TODO ;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;; + +;; Let's define some shorter names for commonly used structures +ifdef SINGULARITY +ThreadContext TYPEDEF Struct_Microsoft_Singularity_X86_ThreadContext +endif + +Thread TYPEDEF Class_System_Threading_Thread +TransitionRecord TYPEDEF Struct_System_GCs_CallStack_TransitionRecord + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; void CollectBodyTransition(Thread thread, int generation): +; Save the callee-save registers in a transition record and then call +; System.GC.CollectBody(thread, generation) +; +align 16 +?g_CollectBodyTransition@Class_System_GC@@SIXPAUClass_System_Threading_Thread@@H@Z proc +; static void __fastcall GC.CollectBodyTransition(Thread, int) + push ebp + mov ebp, esp + sub esp, (SIZE TransitionRecord) + push edx + lea edx, [ebp-(SIZE TransitionRecord)] +ifdef SINGULARITY + mov eax, [ecx].Thread._context._stackMarkers +else + mov eax, [ecx].Thread._asmStackMarker ; get old marker address +endif + mov [edx].TransitionRecord._oldTransitionRecord, eax ; link from current record + mov eax, dword ptr [ebp+4] ; load return address + mov [edx].TransitionRecord._callAddr, eax + lea eax, [ebp+8] ; skip pushed PC and SP + mov [edx].TransitionRecord._stackBottom, eax ; (bottom of stack frame) + mov [edx].TransitionRecord._calleeSaveRegisters._EBX, ebx ; save callee-save registers + mov [edx].TransitionRecord._calleeSaveRegisters._EDI, edi + mov [edx].TransitionRecord._calleeSaveRegisters._ESI, esi + mov eax, dword ptr [ebp] + mov [edx].TransitionRecord._calleeSaveRegisters._EBP, eax ; save old ebp value +ifdef SINGULARITY + mov [ecx].Thread._context._stackMarkers, edx ; update thread field +else + mov [ecx].Thread._asmStackMarker, edx ; update thread field +endif + pop edx + call ?g_CollectBody@Class_System_GC@@SIPAUClass_System_Threading_Thread@@PAU2@H@Z + ; static void __fastcall GC.CollectBody(Thread,int) + lea edi, [ebp-(SIZE TransitionRecord)] + mov esi, [edi].TransitionRecord._oldTransitionRecord ; get old marker address +ifdef SINGULARITY + mov [eax].Thread._context._stackMarkers, esi ; restore thread field +else + mov [eax].Thread._asmStackMarker, esi ; restore thread field +endif + mov ebx, [edi].TransitionRecord._calleeSaveRegisters._EBX ; restore callee-save regs + mov esi, [edi].TransitionRecord._calleeSaveRegisters._ESI + mov ebp, [edi].TransitionRecord._calleeSaveRegisters._EBP + mov edi, [edi].TransitionRecord._calleeSaveRegisters._EDI + add esp, (SIZE TransitionRecord)+4 ; skip FP + ret +?g_CollectBodyTransition@Class_System_GC@@SIXPAUClass_System_Threading_Thread@@H@Z endp + +ifdef NYI +ifdef SINGULARITY +;; __throwDispatcherUnwind depends on this only modifying eax +?g_ReturnToUnlinkStackMethod@Class_System_GCs_CallStack@@SI_NPAUuintPtr@@@Z proc + mov eax, _UnlinkStackBegin + cmp ecx, eax + jl return_false + mov eax, _UnlinkStackLimit + cmp ecx, eax + jg return_false + mov eax, 1 + ret 0 +return_false: + mov eax, 0 + ret +?g_ReturnToUnlinkStackMethod@Class_System_GCs_CallStack@@SI_NPAUuintPtr@@@Z endp +endif +endif + +; The assembly versions of Buffer.ZeroMemory have not been implemented for x86 + +align 16 +?g_ZeroMemoryXMM@Class_System_Buffer@@SIXPAUUntracedPtr_uint8@@PAUuintPtr@@@Z proc + int 3 +?g_ZeroMemoryXMM@Class_System_Buffer@@SIXPAUUntracedPtr_uint8@@PAUuintPtr@@@Z endp + +align 16 +?g_ZeroMemoryMM0@Class_System_Buffer@@SIXPAUUntracedPtr_uint8@@PAUuintPtr@@@Z proc + int 3 +?g_ZeroMemoryMM0@Class_System_Buffer@@SIXPAUUntracedPtr_uint8@@PAUuintPtr@@@Z endp + +align 16 +?g_ZeroMemorySTOS@Class_System_Buffer@@SIXPAUUntracedPtr_uint8@@PAUuintPtr@@@Z proc + int 3 +?g_ZeroMemorySTOS@Class_System_Buffer@@SIXPAUUntracedPtr_uint8@@PAUuintPtr@@@Z endp + +end diff --git a/base/Imported/Bartok/runtime/shared/native/arch/arm/lib.asm b/base/Imported/Bartok/runtime/shared/native/arch/arm/lib.asm new file mode 100644 index 0000000..c88fc3f --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/native/arch/arm/lib.asm @@ -0,0 +1,1356 @@ +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; + + AREA |.text|, CODE, READONLY + + INCLUDE core.inc + + IMPORT |?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z| + IMPORT InterlockedIncrement + IMPORT InterlockedDecrement + + IMPORT |?m__ctor@Class_System_DivideByZeroException@@SAXPAU1@@Z| + IMPORT |??_7System_DivideByZeroException@@6B@| + IMPORT |?m__ctor@Class_System_NullReferenceException@@SAXPAU1@@Z| + IMPORT |??_7System_NullReferenceException@@6B@| + IMPORT |?m__ctor@Class_System_OverflowException@@SAXPAU1@@Z| + IMPORT |??_7System_OverflowException@@6B@| + IMPORT |?m__ctor@Class_System_StackOverflowException@@SAXPAU1@@Z| + IMPORT |??_7System_StackOverflowException@@6B@| + + ;;;; + ;; Placeholder stubs to satisfy the linker + ;;;; + + EXPORT __throwDispatcherUnwind + +__throwDispatcherUnwind + + mov r0, #0xde + mov r0, r0 LSL #8 + orr r0, r0, #0xad + mov r0, r0 LSL #16 + mov pc, lr + +;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;; ACTUAL ;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;; + + IMPORT |__stoi64| + EXPORT |?g_floatToLong@Class_System_VTable@@SA_JM@Z| +|?g_floatToLong@Class_System_VTable@@SA_JM@Z| + b __stoi64 + + IMPORT |__stoi| + EXPORT |?g_floatToInt@Class_System_VTable@@SAHM@Z| +|?g_floatToInt@Class_System_VTable@@SAHM@Z| + b __stoi + + IMPORT |__dtoi64| + EXPORT |?g_doubleToLong@Class_System_VTable@@SA_JN@Z| +|?g_doubleToLong@Class_System_VTable@@SA_JN@Z| + b __dtoi64 + + IMPORT |__dtoi| + EXPORT |?g_doubleToInt@Class_System_VTable@@SAHN@Z| +|?g_doubleToInt@Class_System_VTable@@SAHN@Z| + b __dtoi + + +; +; __throwDispatcher(r0=exception) +; +; Description: this function is called to explicitly throw an exception. +; It assumes that the return address points to the instruction immediately +; after the one where the exception was thrown +; +; Arguments: +; R0 = exception object +; LR = the return address +; Effects: +; 1. Create FP chain entry +; 2. Get return address and uses it to calculate the address of the +; instruction that threw the exception. +; 3. Looks up the appropriate handler and jumps to code to process it. + + EXPORT __throwDispatcher + NESTED_ENTRY __throwDispatcher + + ; Save data we'll need later + stmdb sp!, {r4} + PROLOG_END + mov r4, r0 ; Exception + + ; Lookup the handler + mov r1, lr ; Return address + sub r1, r1, #4 ; Address of throw + + bl |?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z| + + ; r0 - frame setup / exception type + ; r1 - spill size / frame setup offset / handler + mov r2, r4 ; r2 - exception + + ; Restore saved registers and branch to handler + ldmia sp!, {r4} + b __throwDispatcherHandler + + ENTRY_END + +; __throwDispatcherHandler (r0=frameSetupInfo or exceptionType, +; r1=spillSize,frameSetupSize, or handlerAddress, +; r2=exception +; +; Description: +; After the exception table entry has been found, the values are passed here +; for processing. This method simply checks for the easy case of an explicit +; handler (known if the exceptionType is given <-- low bit is zero). +; In this case it simply jumps to the handler. Otherwise it passes the +; information along to __throwDispatcherUnwind. +; +; Arguments: +; If low bit of r0 is set: +; r0 = frame setup information +; r1 = spill size excluding callee-save register saves +; or offset from ebp to end of callee-save register saves +; r2 = exception object +; Otherwise: +; r0 = exception type (unused) +; r1 = handler address +; r2 = exception object + + LEAF_ENTRY __throwDispatcherHandler + + tst r0, #1 + bne __throwDispatcherUnwind + + ; r0 = exception type, r1 = handler, r2 = exception + mov pc, r1 + + ENTRY_END + +; +; __throwDispatcherExplicitAddr (r0=exception, r1=throwAddress) +; +; Description: +; This is to be used when the address where the exception occurred +; is passed in as an extra argument +; +; Arguments: +; r0 = exception object +; r1 = address where the exception was thrown +; + + + NESTED_ENTRY __throwDispatcherExplicitAddr + + stmdb sp!, {r0} ; save exception + + PROLOG_END + + bl |?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z| + + ; r0 - frame setup / exception type + ; r1 - spill size / frame setup offset / handler + ldmia sp!, {r2} ; r2 - exception + + b __throwDispatcherHandler + + ENTRY_END + +; +; __throwDispatcherExplictAddrAfterCore (r0=exception, r1=throwAddress) +; +; Description: +; This is to be used when the address of the instruction immediately after +; the one that actually threw the exception is passed as an argument. +; +; Arguments: +; r1 = address of the instruction following the one where +; the exception was thrown +; r2 = pointer to exception object being thrown +; +; This is used, for example, in stack unwinding, where R1 is the +; return address on the stack for the current procedure. +; +; Stack unwinding occurs when the current procedure does not have a handler +; for the routine. The idea is to pop the stack frame and treat the call +; instruction in the caller as though it threw. We only have the return +; address, though, which points to the instruction *after* the call. +; + + EXPORT __throwDispatcherExplicitAddrAfterCore + NESTED_ENTRY __throwDispatcherExplicitAddrAfterCore + + stmdb sp!, {r2} ; save exception + PROLOG_END + + mov r0, r2 ; set exception + sub r1, r1, #4 ; set return address + + bl |?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z| + + ; r0 - frame setup / exception type + ; r1 - spill size / frame setup offset / handler + ldmia sp!, {r2} ; r2 - exception + + b __throwDispatcherHandler + + ENTRY_END +; +; __throwNullReferenceException: instantiate an null reference exception +; and throw it. +; +; Assumes r1 points to the address of the instruction that faulted +; + +nullRefSymbols + dcd |??_7System_NullReferenceException@@6B@| +nullRefConstructor + dcd |?m__ctor@Class_System_NullReferenceException@@SAXPAU1@@Z| + + EXPORT __throwNullReferenceException + LEAF_ENTRY __throwNullReferenceException + + ldr r2, nullRefSymbols + ldr r3, nullRefConstructor + + b __throwExceptionHelper + + ENTRY_END + +; +; __throwStackOverflowException: instantiate an stack overflow exception +; and throw it. +; +; Assumes r1 points to the address of the instruction that faulted +; + +stackVTable + dcd |??_7System_StackOverflowException@@6B@| +stackConstructor + dcd |?m__ctor@Class_System_StackOverflowException@@SAXPAU1@@Z| + + EXPORT __throwStackOverflowException + LEAF_ENTRY __throwStackOverflowException + + ldr r2, stackVTable + ldr r3, stackConstructor + + b __throwExceptionHelper + + ENTRY_END + +; +; __throwOverflowException: instantiate an overflow exception +; and throw it. +; +; Assumes r1 points to the address of the instruction that faulted +; + +overflowVTable + dcd |??_7System_OverflowException@@6B@| +overflowConstructor + dcd |?m__ctor@Class_System_OverflowException@@SAXPAU1@@Z| + + EXPORT __throwOverflowException + LEAF_ENTRY __throwOverflowException + + ldr r2, overflowVTable + ldr r3, overflowConstructor + + b __throwExceptionHelper + + ENTRY_END + +; +; __throwDivideByZeroException: instantiate an divide-by-zero exception +; and throw it. +; +; Assumes r1 points to the address of the instruction that faulted +; + +divZeroVTable + dcd |??_7System_DivideByZeroException@@6B@| +divZeroConstructor + dcd |?m__ctor@Class_System_DivideByZeroException@@SAXPAU1@@Z| + + EXPORT __throwDivideByZeroException + LEAF_ENTRY __throwDivideByZeroException + + ldr r2, divZeroVTable + ldr r3, divZeroConstructor + + b __throwExceptionHelper + + ENTRY_END + +; __throwExceptionHelper +; +; Description: +; This method helps turn a system fault into an exception. The various +; different faults all do basically the same thing with the exception +; of the type of exception they generate. +; +; Arguments: +; r1 = address of instruction that faulted +; r2 = address of exception type VTable +; r3 = address of exception type constructor + +allocationInhibit + dcd |?c_allocationGCInhibitCount@Class_System_GC@@2HA| + + NESTED_ENTRY __throwExceptionHelper + + stmdb sp!, {r1, r4-r7} + + PROLOG_END + + mov r6, r2 + mov r7, r3 + + ldr r4, allocationInhibit + + mov r0, r4 + bl InterlockedIncrement + + mov r0, r6 + bl |?g_AllocateObject@Class_System_GC@@SAPAUClass_System_Object@@PAUClass_System_VTable@@@Z| + mov r5, r0 ; save pointer to instance of exception + mov lr, pc + mov pc, r7 + + mov r0, r4 + bl InterlockedDecrement + + mov r0, r5 + ldmia sp!, {r1, r4-r7} + + b __throwDispatcherExplicitAddr + + ENTRY_END + +; __throwDispatcherUnwind (r0=frame setup info, r1=spill size, r2=exception +; +; Description: +; This is the global unwind handler. It is used to unwind a single stack +; frame if there are no explicit handlers that catch an exception in a given +; function. +; +; Arguments: +; r0 = frame setup information, must have low bit set +; r1 = spill size excluding callee-save register saves +; or offset from ebp to end of callee-save register saves +; r2 = exception object +; +; See tables\ExceptionTable.cs for details on these values + +;__throwDispatcherUnwind + + + END + +;;;;;;;;;;;;;;;;;;;;;;; +;;;;; TEMP ;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;; + + +__throwDispatcherUnwind proc + ;; eax=frame info + ;; edx=spill size + + ;; obviously ebp isn't useful under frame pointer omission + ;; but less obviously esp may be invalid if ebp is good + ;; (e.g. under varargs we may not have known how many arguments to + ;; pop; this is one reason why varargs turns off frame pointer omission) + test eax, 2h + jne esp_is_good + ;; ebp_is_good + add edx, ebp + mov esp, edx + jmp esp_is_setup +esp_is_good: + ;; pop spill slots + add esp, edx + +esp_is_setup: + + ;; restore callee-saves and pop values from stack + ;; (excludes ebp if used as the frame pointer) + test eax, 4h + je skip_edi_restore + ;; restore edi + pop edi +skip_edi_restore: + test eax, 8h + je skip_esi_restore + ;; restore esi + pop esi +skip_esi_restore: + test eax, 10h + je skip_ebp_restore + ;; restore ebp + pop ebp +skip_ebp_restore: + test eax, 20h + je skip_ebx_restore + ;; restore ebx + pop ebx +skip_ebx_restore: + + test eax, 40h + je skip_jump_transition_record + ;; jump over transition record + add esp, (SIZE Struct_System_GCs_CallStack_TransitionRecord) +skip_jump_transition_record: + + ;; restore ebp if it was used as the frame pointer + test eax, 2h + jne skip_frame_pointer_restore + ;; restore frame pointer (esp == ebp already) + pop ebp +skip_frame_pointer_restore: + + ;; set edx=return address + pop edx + + ;; pop arguments + shr eax, 16 + add esp, eax + + ;; At this point + ;; ecx=exception, edx=return address + ;; esi/edi/ebx/ebp/esp have been restored + ;; eax is scratch + + ;; set up next handler search + jmp __throwDispatcherExplicitAddrAfter +__throwDispatcherUnwind endp + +;;;;;;;;;;;;;;;;;;;;;;; +;;;;; TODO ;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;; + + IF :DEF:SINGULARITY + ELSE + EXTERN ?ResetGuardPage@@YIXXZ:NEAR + ENDIF ;; !SINGULARITY + + IF :DEF:SINGULARITY_KERNEL + EXTERN ?g_setStopContext@Class_System_Threading_Thread@@SIXPAU1@PAUClass_System_Exception@@@Z:NEAR + EXTERN __throwBeyondMarker:NEAR + ENDIF ; SINGULARITY_KERNEL + + IF :DEF:SINGULARITY + ELSE + EXTERN ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ:NEAR + EXTERN ?c_CheckCount@Class_System_GCs_StackManager@@2HA:int32 + EXTERN ?g_GetStackChunk@Class_System_GCs_StackManager@@SIPAUuintPtr@@PAU2@PAUClass_System_Threading_Thread@@_N@Z:NEAR + EXTERN ?g_ReturnStackChunk@Class_System_GCs_StackManager@@SIXPAUClass_System_Threading_Thread@@_N@Z:NEAR +; +; externals used to construct instances of machine-level exceptions +; + + EXTERN ?g_AllocateObject@Class_System_GC@@SIPAUClass_System_Object@@PAUClass_System_VTable@@@Z:NEAR + EXTERN ?m__ctor@Class_System_ArithmeticException@@SIXPAU1@@Z:NEAR + EXTERN ??_7System_ArithmeticException@@6B@:dword ; vtable + EXTERN ?m__ctor@Class_System_DivideByZeroException@@SIXPAU1@@Z:NEAR + EXTERN ??_7System_DivideByZeroException@@6B@:dword ; vtable + EXTERN ?m__ctor@Class_System_NullReferenceException@@SIXPAU1@@Z:NEAR + EXTERN ??_7System_NullReferenceException@@6B@:dword ; vtable + EXTERN ?m__ctor@Class_System_OverflowException@@SIXPAU1@@Z:NEAR + EXTERN ??_7System_OverflowException@@6B@:dword ; vtable + EXTERN ?m__ctor@Class_System_StackOverflowException@@SIXPAU1@@Z:NEAR + EXTERN ??_7System_StackOverflowException@@6B@:PTR_Class_System_VTable + + EXTERN ?c_allocationInhibitGC@Class_System_GC@@2_NA:dword + + EXTERN ?g_doubleToInt@Class_System_VTable@@SIHN@Z:NEAR + EXTERN ?g_doubleToLong@Class_System_VTable@@SI_JN@Z:NEAR + EXTERN ?g_floatToInt@Class_System_VTable@@SIHM@Z:NEAR + EXTERN ?g_floatToLong@Class_System_VTable@@SI_JM@Z:NEAR + EXTERN ?g_checkedDoubleToInt@Class_System_VTable@@SIHN@Z:NEAR + EXTERN ?g_checkedDoubleToLong@Class_System_VTable@@SI_JN@Z:NEAR + EXTERN ?g_checkedFloatToInt@Class_System_VTable@@SIHM@Z:NEAR + EXTERN ?g_checkedFloatToLong@Class_System_VTable@@SI_JM@Z:NEAR + + EXTERN ?g_Abs@Class_System_Math@@SIMM@Z:NEAR + EXTERN ?g_Abs@Class_System_Math@@SINN@Z:NEAR + EXTERN ?g_Atan@Class_System_Math@@SINN@Z:NEAR + EXTERN ?g_Ceiling@Class_System_Math@@SINN@Z:NEAR + EXTERN ?g_Cos@Class_System_Math@@SINN@Z:NEAR + EXTERN ?g_Floor@Class_System_Math@@SINN@Z:NEAR + EXTERN ?g_Log@Class_System_Math@@SINN@Z:NEAR + EXTERN ?g_Round@Class_System_Math@@SINN@Z:NEAR + EXTERN ?g_Sin@Class_System_Math@@SINN@Z:NEAR + EXTERN ?g_Tan@Class_System_Math@@SINN@Z:NEAR + EXTERN ?g_atan2@Class_System_Math@@SINNN@Z:NEAR + EXTERN ?g_exp@Class_System_Math@@SINN@Z:NEAR + ENDIF ;; !SINGULARITY + + IF EXCLUDED + EXTERN __checkFPStackDepth0:NEAR + EXTERN __checkFPStackDepth1:NEAR + EXTERN __checkFPStackDepth2:NEAR + EXTERN __checkFPStackDepth3:NEAR + EXTERN __checkFPStackDepth4:NEAR + EXTERN __checkFPStackDepth5:NEAR + EXTERN __checkFPStackDepth6:NEAR + EXTERN __checkFPStackDepth7:NEAR + ENDIF ;; EXCLUDED + + align 8 +$DBLMAXINT DQ 041dfffffffc00000r ; 2.14747e+009 +$DBLMININT DQ 0c1e0000000000000r ; -2.14748e+009 +$MAXLONG DQ 07fffffffffffffffh +$MINLONG DQ 08000000000000000h + +; __throwDispatcherUnwind (eax=frame setup info, ecx=exception, edx=spill size +; +; Description: +; This is the global unwind handler. It is used to unwind a single stack +; frame if there are no explicit handlers that catch an exception in a given +; function. +; +; Arguments: +; eax = frame setup information, must have low bit set +; ecx = exception object +; edx = spill size excluding callee-save register saves +; or offset from ebp to end of callee-save register saves +; +; See tables\ExceptionTable.cs for details on these values + +align 16 +__throwDispatcherUnwind proc + ;; eax=frame info + ;; edx=spill size + + ;; obviously ebp isn't useful under frame pointer omission + ;; but less obviously esp may be invalid if ebp is good + ;; (e.g. under varargs we may not have known how many arguments to + ;; pop; this is one reason why varargs turns off frame pointer omission) + test eax, 2h + jne esp_is_good + ;; ebp_is_good + add edx, ebp + mov esp, edx + jmp esp_is_setup +esp_is_good: + ;; pop spill slots + add esp, edx + +esp_is_setup: + + ;; restore callee-saves and pop values from stack + ;; (excludes ebp if used as the frame pointer) + test eax, 4h + je skip_edi_restore + ;; restore edi + pop edi +skip_edi_restore: + test eax, 8h + je skip_esi_restore + ;; restore esi + pop esi +skip_esi_restore: + test eax, 10h + je skip_ebp_restore + ;; restore ebp + pop ebp +skip_ebp_restore: + test eax, 20h + je skip_ebx_restore + ;; restore ebx + pop ebx +skip_ebx_restore: + + test eax, 40h + je skip_jump_transition_record + ;; jump over transition record + add esp, (SIZE Struct_System_GCs_CallStack_TransitionRecord) +skip_jump_transition_record: + + ;; restore ebp if it was used as the frame pointer + test eax, 2h + jne skip_frame_pointer_restore + ;; restore frame pointer (esp == ebp already) + pop ebp +skip_frame_pointer_restore: + + ;; set edx=return address + pop edx + + ;; pop arguments + shr eax, 16 + add esp, eax + + ;; At this point + ;; ecx=exception, edx=return address + ;; esi/edi/ebx/ebp/esp have been restored + ;; eax is scratch + + ;; set up next handler search + jmp __throwDispatcherExplicitAddrAfter +__throwDispatcherUnwind endp + +; +; int System.VTable.checkedDoubleToInt(double) +; + +align 16 +?g_checkedDoubleToInt@Class_System_VTable@@SIHN@Z proc + push ebp + mov ebp,esp + add esp,-8 + fld real8 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + mov ax,word ptr [ebp-2] + or eax,0C00h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + fistp dword ptr [ebp-8] + fldcw word ptr [ebp-2] + mov eax,dword ptr [ebp-8] + + cmp eax,080000000h + je possible_overflow +return: + mov esp,ebp + pop ebp + ret 8 + +possible_overflow: + fld real8 ptr [ebp+8] + fcomp real8 ptr $DBLMAXINT + fnstsw ax + test ah,4 ; test for unordered + jne short throw_exception + test ah,1 ; test for <$DBLMAXINT + jne short return_MININT + ; src > $DBLMAXINT + ; throw an overflow exception + jmp short throw_exception + +return_MININT: + ; check against $DBLMININT + fld real8 ptr [ebp+8] + fcomp real8 ptr $DBLMININT + fnstsw ax + test ah, 1 ; test for < $DBLMININT + jne short throw_exception ; throw exception if true + mov eax, 080000000h + jmp short return + +throw_exception: + ; throw an overflow exception + ; set up stack frame so that it looks like a call to throwNewOverflowException + ; from the caller of this function. + mov esp,ebp + pop ebp + pop eax ; grab return address + add esp, 4 ; move esp pass the first parameter. + mov [esp],eax ; overwrite argument + jmp ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ + +?g_checkedDoubleToInt@Class_System_VTable@@SIHN@Z endp + +; +; long System.VTable.checkedDoubleToLong(double) +; + +align 16 +?g_checkedDoubleToLong@Class_System_VTable@@SI_JN@Z proc + push ebp + mov ebp,esp + add esp,-12 + fld real8 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + mov ax,word ptr [ebp-2] + or eax,0C00h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + fistp qword ptr [ebp-12] + fldcw word ptr [ebp-2] + mov eax,dword ptr [ebp-12] + mov edx,dword ptr [ebp-8] + + cmp edx,080000000h + je possible_overflow +return: + mov esp,ebp + pop ebp + ret 8 + +possible_overflow: + mov edx,eax ; save lsw + fld real8 ptr [ebp+8] + fild qword ptr $MAXLONG + fcompp + fnstsw ax + test ah,4 ; test for unordered + jne short return_zero + test ah,65 ; test for <= $MAXLONG + je short check_MINLONG + +return_MAXLONG: + ; src > $MAXLONG + ; throw an exception + jmp short throw_exception + +return_zero: + jmp short throw_exception + +check_MINLONG: + ; src <= $MINLONG + fild qword ptr $MINLONG + fld real8 ptr [ebp+8] + fcompp ; real8 ptr [ebp+8] < $MINLONG + fnstsw ax + test ah,1 + jne short throw_exception + +return_MINLONG: + mov eax, edx ; restore lsw to eax + mov edx, 080000000h + jmp short return + + +throw_exception: + ; throw an overflow exception + ; set up stack frame so that it looks like a call to throwNewOverflowException + ; from the caller of this function. + mov esp,ebp + pop ebp + pop eax ; grab return address + add esp, 4 ; move esp pass the first parameter. + mov [esp],eax ; overwrite argument + jmp ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ + +?g_checkedDoubleToLong@Class_System_VTable@@SI_JN@Z endp + +; +; int System.VTable.checkedFloatToInt(float) +; + +align 16 +?g_checkedFloatToInt@Class_System_VTable@@SIHM@Z proc + push ebp + mov ebp,esp + add esp,-8 + fld real4 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + xor eax,eax + mov ax,word ptr [ebp-2] + or eax,0C00h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + fistp dword ptr [ebp-8] + fldcw word ptr [ebp-2] + mov eax,dword ptr [ebp-8] + + cmp eax,080000000h + je possible_overflow + +return: + mov esp,ebp + pop ebp + ret 4 + +possible_overflow: + fld real4 ptr [ebp+8] + fcomp real8 ptr $DBLMAXINT + fnstsw ax + test ah,4 ; test for unordered + jne short throw_exception + test ah,1 ; test for src < $DBLMAXINT + jne short return_MININT + ; src > $DBLMAXINT + ; throw an overflow exception + jmp short throw_exception + +return_MININT: + ; need to check against $DBLMININT, if it is less than, + ; then throw an overflow exception + fld real4 ptr [ebp+8] + fcomp real8 ptr $DBLMININT + fnstsw ax + test ah,1 ; test for less than + jne short throw_exception + mov eax, 080000000h + jmp short return + +throw_exception: + ; throw an overflow exception + ; set up stack frame so that it looks like a call to throwNewOverflowException + ; from the caller of this function. + mov esp,ebp + pop ebp + pop eax ; grab return address + mov [esp],eax ; overwrite argument + jmp ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ + +?g_checkedFloatToInt@Class_System_VTable@@SIHM@Z endp + +; +; long System.VTable.checkedFloatToLong(float) +; + +align 16 +?g_checkedFloatToLong@Class_System_VTable@@SI_JM@Z proc + push ebp + mov ebp,esp + add esp,-12 + fld real4 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + mov ax,word ptr [ebp-2] + or eax,0C00h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + fistp qword ptr [ebp-12] + fldcw word ptr [ebp-2] + mov eax,dword ptr [ebp-12] + mov edx,dword ptr [ebp-8] + + cmp edx,080000000h + je possible_overflow +return: + mov esp,ebp + pop ebp + ret 4 + +possible_overflow: + mov edx,eax ; save lsw + fld real4 ptr [ebp+8] + fild qword ptr $MAXLONG + fcompp + fnstsw ax + test ah,4 ; test for unordered + jne short return_zero + test ah,65 ; test for <= $MAXLONG + je short check_MINLONG + +return_MAXLONG: + ; src > $MAXLONG + ; throw an exception + jmp short throw_exception + +return_zero: + ; compare with $MAXLONG results in unordered + ; throw an overflow exception + jmp short throw_exception + +check_MINLONG: + ; src <= $MINLONG + fild qword ptr $MINLONG + fld real4 ptr [ebp+8] + fcompp ; real8 ptr [ebp+8] < $MINLONG + fnstsw ax + test ah,1 + jne short throw_exception ; throw an overflow exception when src < $MINLONG +return_MINLONG: + mov eax, edx ; restore lsw + mov edx, 080000000h + jmp short return + +throw_exception: + ; throw an overflow exception + ; set up stack frame so that it looks like a call to throwNewOverflowException + ; from the caller of this function. + mov esp,ebp + pop ebp + pop eax ; grab return address + mov [esp],eax ; overwrite argument + jmp ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ + +?g_checkedFloatToLong@Class_System_VTable@@SI_JM@Z endp + +; +; double System.Math.Sin(double) +; + +align 16 +?g_Sin@Class_System_Math@@SINN@Z proc + fld real8 ptr [esp+4] + fsin + ret 8 +?g_Sin@Class_System_Math@@SINN@Z endp + +; +; double System.Math.Cos(double) +; + +align 16 +?g_Cos@Class_System_Math@@SINN@Z proc + fld real8 ptr [esp+4] + fcos + ret 8 +?g_Cos@Class_System_Math@@SINN@Z endp + +; +; double System.Math.Tan(double) +; + +align 16 +?g_Tan@Class_System_Math@@SINN@Z proc + fld real8 ptr [esp+4] + fptan + fstp real8 ptr [esp+4] + ret 8 +?g_Tan@Class_System_Math@@SINN@Z endp + +; +; +; double System.Math.Atan(double) +; + +align 16 +?g_Atan@Class_System_Math@@SINN@Z proc + fld real8 ptr [esp+4] + fld1 + fpatan + ret 8 +?g_Atan@Class_System_Math@@SINN@Z endp + +; +; double System.Math.atan2(double,double) +; + +align 16 +?g_atan2@Class_System_Math@@SINNN@Z proc + fld real8 ptr [esp+4] + fld real8 ptr [esp+12] + fpatan + ret 16 +?g_atan2@Class_System_Math@@SINNN@Z endp + +; +; double System.Math.exp(double) +; + +align 16 +?g_exp@Class_System_Math@@SINN@Z proc + push ebp + mov ebp,esp + + fldl2e + fmul real8 ptr [ebp+8] + fld st(0) + frndint + fxch st(1) + fsub st(0), st(1) + f2xm1 + fld1 + faddp st(1), st(0) + fscale +;isNaN?? + fstp st(1) + + mov esp,ebp + pop ebp + ret 8 +?g_exp@Class_System_Math@@SINN@Z endp + +; +; double System.Math.log(double) +; + +align 16 +?g_Log@Class_System_Math@@SINN@Z proc + fldln2 + fld real8 ptr [esp+4] + fyl2x + ret 8 +?g_Log@Class_System_Math@@SINN@Z endp + +; +; double System.Math.Ceiling(double) +; + +align 16 +?g_Ceiling@Class_System_Math@@SINN@Z proc + push ebp + mov ebp,esp + add esp,-4 + + fld real8 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + mov ax,word ptr [ebp-2] + and ah,0F3h + or ah,008h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + frndint + fldcw word ptr [ebp-2] + + mov esp,ebp + pop ebp + ret 8 +?g_Ceiling@Class_System_Math@@SINN@Z endp + +; +; double System.Math.Floor(double) +; + +align 16 +?g_Floor@Class_System_Math@@SINN@Z proc + push ebp + mov ebp,esp + add esp,-4 + + fld real8 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + mov ax,word ptr [ebp-2] + and ah,0F3h + or ah,004h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + frndint + fldcw word ptr [ebp-2] + + mov esp,ebp + pop ebp + ret 8 +?g_Floor@Class_System_Math@@SINN@Z endp + +; +; double System.Math.Round(double) +; + +align 16 +?g_Round@Class_System_Math@@SINN@Z proc + fld QWORD PTR [ESP+4] + frndint + ret 8 +?g_Round@Class_System_Math@@SINN@Z endp + +; +; float System.Math.Abs(float) +; + +align 16 +?g_abs@Class_System_Math@@SIMM@Z proc + fld real4 ptr [esp+4] + fabs + ret 4 +?g_abs@Class_System_Math@@SIMM@Z endp + +; +; double System.Math.Abs(double) +; + +align 16 +?g_abs@Class_System_Math@@SINN@Z proc + fld real8 ptr [esp+4] + fabs + ret 8 +?g_abs@Class_System_Math@@SINN@Z endp + +align 16 +?g_floatRem@Class_System_VTable@@SIMMM@Z proc + fld real4 ptr [esp+8] + fld real4 ptr [esp+4] +fremloop: + fprem + fstsw ax + fwait + sahf + jp fremloop ; Continue while the FPU status bit C2 is set + ffree st(1) + ret 8 +?g_floatRem@Class_System_VTable@@SIMMM@Z endp + +align 16 +?g_doubleRem@Class_System_VTable@@SINNN@Z proc + fld real8 ptr [esp+12] + fld real8 ptr [esp+4] +fremloop: + fprem + fstsw ax + fwait + sahf + jp fremloop ; Continue while the FPU status bit C2 is set + ffree st(1) + ret 16 +?g_doubleRem@Class_System_VTable@@SINNN@Z endp + + +if EXCLUDED +; +; void __checkFPStackDepth0 +; + +align 16 +__checkFPStackDepth0 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,0 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth0 endp + +; +; void __checkFPStackDepth1 +; + +align 16 +__checkFPStackDepth1 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-1 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth1 endp + +; +; void __checkFPStackDepth2 +; + +align 16 +__checkFPStackDepth2 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-2 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth2 endp + +; +; void __checkFPStackDepth3 +; + +align 16 +__checkFPStackDepth3 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-3 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth3 endp + +; +; void __checkFPStackDepth4 +; + +align 16 +__checkFPStackDepth4 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-4 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth4 endp + +; +; void __checkFPStackDepth5 +; + +align 16 +__checkFPStackDepth5 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-5 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth5 endp + +; +; void __checkFPStackDepth6 +; + +align 16 +__checkFPStackDepth6 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-6 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth6 endp + +; +; void __checkFPStackDepth7 +; + +align 16 +__checkFPStackDepth7 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-7 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth7 endp +endif ; EXCLUDED + +end diff --git a/base/Imported/Bartok/runtime/shared/native/arch/x86/gc.asm b/base/Imported/Bartok/runtime/shared/native/arch/x86/gc.asm new file mode 100644 index 0000000..195187a --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/native/arch/x86/gc.asm @@ -0,0 +1,206 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; + +include core.inc + +PAGE_BITS EQU 12 +MASK_OWNER EQU 03h + +;; Let's define some shorter names for commonly used structures +ifdef SINGULARITY +ThreadContext TYPEDEF Struct_Microsoft_Singularity_X86_ThreadContext +endif + +Thread TYPEDEF Class_System_Threading_Thread +TransitionRecord TYPEDEF Struct_System_GCs_CallStack_TransitionRecord + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; pushStackMark: If a function may be called by C, push a pointer to its +; frame on to a stack at the beginning of the function. +; +; Transition record layout: +; +; (lower addresses) +; -------------------------- +; |Old stack marker record | +; -------------------------- +; |Addr of call instr | +; -------------------------- +; |Bottom of stack frame | +; -------------------------- +; |ebx | +; -------------------------- +; |edi | +; -------------------------- +; |esi | +; -------------------------- +; |ebp | +; -------------------------- +; (higher addresses); +; + +align 16 +__pushStackMark proc + push eax ; save caller-save registers + push ecx + push edx + lea ecx, [ebp-(SIZE TransitionRecord)] ; calculate new stack marker address +ifdef SINGULARITY + CurrentThreadContext(eax) ; get current threadcontext + mov edx, [eax].ThreadContext._stackMarkers ;save old stack marker address +else + CurrentThread(eax) ; get current thread + mov edx, [eax].Thread._asmStackMarker ;save old stack marker address +endif + mov [ecx].TransitionRecord._oldTransitionRecord, edx +ifdef SINGULARITY + mov [eax].ThreadContext._stackMarkers, ecx ; update thread record field +else + mov [eax].Thread._asmStackMarker,ecx ; update thread record field +endif + mov edx, [esp+12] ; load return address of this call + mov [ecx].TransitionRecord._callAddr,edx + lea edx,[esp+16] + mov [ecx].TransitionRecord._stackBottom, edx ; save bottom of stack frame + mov [ecx].TransitionRecord._calleeSaveRegisters._EBX, ebx ; save callee-save registers + mov [ecx].TransitionRecord._calleeSaveRegisters._EDI, edi + mov [ecx].TransitionRecord._calleeSaveRegisters._ESI, esi + mov [ecx].TransitionRecord._calleeSaveRegisters._EBP, ebp + mov ecx, eax +ifdef SINGULARITY +; call ?g_LeaveManagedSpace@Class_System_GCs_Transitions@@SIXUStruct_Microsoft_Singularity_X86_ThreadContext@@@Z +else + call ?g_LeaveManagedSpace@Class_System_GCs_Transitions@@SIXPAUClass_System_Threading_Thread@@@Z +endif + pop edx + pop ecx + pop eax + ret +__pushStackMark endp + +; +; popStackMark: pop the pointer before returning from the function +; +align 16 +__popStackMark proc + push eax ; save caller-save registers + push ecx + push edx +ifdef SINGULARITY +; CurrentThreadContext(ecx) ; get current thread +; call ?g_ReturnToManagedSpace@Class_System_GCs_Transitions@@SIPAUClass_System_Threading_Thread@@UStruct_Microsoft_Singularity_X86_ThreadContext@@@Z + CurrentThreadContext(eax) ; get current thread +else + CurrentThreadIndex(eax) ; get current thread + mov ecx, eax + call ?g_ReturnToManagedSpace@Class_System_GCs_Transitions@@SIPAUClass_System_Threading_Thread@@H@Z +endif + lea ecx, [ebp-(SIZE TransitionRecord)] + mov edx, [ecx].TransitionRecord._oldTransitionRecord ; get old stack marker value +ifdef SINGULARITY + mov [eax].ThreadContext._stackMarkers, edx ; update thread record field +else + mov [eax].Thread._asmStackMarker, edx ; update thread record field +endif + mov ebx, [ecx].TransitionRecord._calleeSaveRegisters._EBX ; restore callee-save registers + mov edi, [ecx].TransitionRecord._calleeSaveRegisters._EDI + mov esi, [ecx].TransitionRecord._calleeSaveRegisters._ESI + mov ebp, [ecx].TransitionRecord._calleeSaveRegisters._EBP + pop edx ; restore caller-save registers + pop ecx + pop eax + ret +__popStackMark endp + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; void CollectBodyTransition(Thread thread, int generation): +; Save the callee-save registers in a transition record and then call +; System.GC.CollectBody(thread, generation) +; +align 16 +?g_CollectBodyTransition@Class_System_GC@@SIXPAUClass_System_Threading_Thread@@H@Z proc +; static void __fastcall GC.CollectBodyTransition(Thread, int) + push ebp + mov ebp, esp + sub esp, (SIZE TransitionRecord) + push edx + lea edx, [ebp-(SIZE TransitionRecord)] +ifdef SINGULARITY + mov eax, [ecx].Thread._context._stackMarkers +else + mov eax, [ecx].Thread._asmStackMarker ; get old marker address +endif + mov [edx].TransitionRecord._oldTransitionRecord, eax ; link from current record + mov eax, dword ptr [ebp+4] ; load return address + mov [edx].TransitionRecord._callAddr, eax + lea eax, [ebp+8] ; skip pushed PC and SP + mov [edx].TransitionRecord._stackBottom, eax ; (bottom of stack frame) + mov [edx].TransitionRecord._calleeSaveRegisters._EBX, ebx ; save callee-save registers + mov [edx].TransitionRecord._calleeSaveRegisters._EDI, edi + mov [edx].TransitionRecord._calleeSaveRegisters._ESI, esi + mov eax, dword ptr [ebp] + mov [edx].TransitionRecord._calleeSaveRegisters._EBP, eax ; save old ebp value +ifdef SINGULARITY + mov [ecx].Thread._context._stackMarkers, edx ; update thread field +else + mov [ecx].Thread._asmStackMarker, edx ; update thread field +endif + pop edx + call ?g_CollectBody@Class_System_GC@@SIPAUClass_System_Threading_Thread@@PAU2@H@Z + ; static void __fastcall GC.CollectBody(Thread,int) + lea edi, [ebp-(SIZE TransitionRecord)] + mov esi, [edi].TransitionRecord._oldTransitionRecord ; get old marker address +ifdef SINGULARITY + mov [eax].Thread._context._stackMarkers, esi ; restore thread field +else + mov [eax].Thread._asmStackMarker, esi ; restore thread field +endif + mov ebx, [edi].TransitionRecord._calleeSaveRegisters._EBX ; restore callee-save regs + mov esi, [edi].TransitionRecord._calleeSaveRegisters._ESI + mov ebp, [edi].TransitionRecord._calleeSaveRegisters._EBP + mov edi, [edi].TransitionRecord._calleeSaveRegisters._EDI + add esp, (SIZE TransitionRecord)+4 ; skip FP + ret +?g_CollectBodyTransition@Class_System_GC@@SIXPAUClass_System_Threading_Thread@@H@Z endp + +ifdef NYI +ifdef SINGULARITY +;; __throwDispatcherUnwind depends on this only modifying eax +?g_ReturnToUnlinkStackMethod@Class_System_GCs_CallStack@@SI_NPAUuintPtr@@@Z proc + mov eax, _UnlinkStackBegin + cmp ecx, eax + jl return_false + mov eax, _UnlinkStackLimit + cmp ecx, eax + jg return_false + mov eax, 1 + ret 0 +return_false: + mov eax, 0 + ret +?g_ReturnToUnlinkStackMethod@Class_System_GCs_CallStack@@SI_NPAUuintPtr@@@Z endp +endif +endif + +; The assembly versions of Buffer.ZeroMemory have not been implemented for x86 + +align 16 +?g_ZeroMemoryXMM@Class_System_Buffer@@SIXPAUUntracedPtr_uint8@@PAUuintPtr@@@Z proc + int 3 +?g_ZeroMemoryXMM@Class_System_Buffer@@SIXPAUUntracedPtr_uint8@@PAUuintPtr@@@Z endp + +align 16 +?g_ZeroMemoryMM0@Class_System_Buffer@@SIXPAUUntracedPtr_uint8@@PAUuintPtr@@@Z proc + int 3 +?g_ZeroMemoryMM0@Class_System_Buffer@@SIXPAUUntracedPtr_uint8@@PAUuintPtr@@@Z endp + +align 16 +?g_ZeroMemorySTOS@Class_System_Buffer@@SIXPAUUntracedPtr_uint8@@PAUuintPtr@@@Z proc + int 3 +?g_ZeroMemorySTOS@Class_System_Buffer@@SIXPAUUntracedPtr_uint8@@PAUuintPtr@@@Z endp + +end diff --git a/base/Imported/Bartok/runtime/shared/native/arch/x86/lib.asm b/base/Imported/Bartok/runtime/shared/native/arch/x86/lib.asm new file mode 100644 index 0000000..a44c13f --- /dev/null +++ b/base/Imported/Bartok/runtime/shared/native/arch/x86/lib.asm @@ -0,0 +1,1339 @@ +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; + +include core.inc + +externdef __throwDispatcher:NEAR +externdef __throwDispatcherExplicitAddrAfter:NEAR +externdef __throwDispatcherExplicitAddrAfterCore:NEAR +externdef ?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z:NEAR + +externdef __throwDivideByZeroException:NEAR +externdef __throwNullPointerException:NEAR +externdef __throwOverflowException:NEAR +externdef __throwStackOverflowException:NEAR + +if EXCLUDED +externdef __checkFPStackDepth0:NEAR +externdef __checkFPStackDepth1:NEAR +externdef __checkFPStackDepth2:NEAR +externdef __checkFPStackDepth3:NEAR +externdef __checkFPStackDepth4:NEAR +externdef __checkFPStackDepth5:NEAR +externdef __checkFPStackDepth6:NEAR +externdef __checkFPStackDepth7:NEAR +endif ;; EXCLUDED + + align 8 +$DBLMAXINT DQ 041dfffffffc00000r ; 2.14747e+009 +$DBLMININT DQ 0c1e0000000000000r ; -2.14748e+009 +$MAXLONG DQ 07fffffffffffffffh +$MINLONG DQ 08000000000000000h + +; +; __throwDispatcher(ecx=exception) +; +; Description: this function is called to explicitly throw an exception. +; It assumes that the return address points to the instruction immediately +; after the one where the exception was thrown +; +; Arguments: +; ecx = exception object +; [esp] = the return address +; Effects: +; 1. Create ebp chain entry +; 2. Get return address and uses it to calculate the address of the +; instruction that threw the exception. +; 3. Looks up the appropriate handler and jumps to code to process it. + +align 16 + +__throwDispatcher proc + push ebp ; create ebp chain entry + mov ebp, esp ; set new ebp + mov edx, [ebp+4] ; get return address + push ecx ; save exception + sub edx, 1 ; adjust to point to throw location + call ?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z + pop ecx ; restore exception + pop ebp ; remove ebp chain + add esp, 4 ; remove eip from the stack +; mov edx is already ok + jmp __throwDispatcherHandler +__throwDispatcher endp + +; +; __throwDispatcherExplicitAddr (ecx=exception, edx=throwAddress) +; +; Description: +; This is to be used when the address where the exception occurred +; is passed in as an extra argument +; +; Arguments: +; ecx = exception object +; edx = address where the exception was thrown +; + + +align 16 +__throwDispatcherExplicitAddr proc + push ecx ; save exception + call ?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z + pop ecx ; restore exception +; mov edx is already ok + jmp __throwDispatcherHandler +__throwDispatcherExplicitAddr endp + +; +; __throwDispatcherExplictAddrAfter (ecx=exception, edx=throwAddress) +; +; Description: +; This is to be used when the address of the instruction immediately after +; the one that actually threw the exception is passed as an argument. +; +; Arguments: +; ecx = pointer to exception object being thrown +; edx = address of the instruction following the one where +; the exception was thrown +; +; This is used, for example, in stack unwinding, where edx is the +; return address on the stack for the current procedure. +; +; Stack unwinding occurs when the current procedure does not have a handler +; for the routine. The idea is to pop the stack frame and treat the call +; instruction in the caller as though it threw. We only have the return +; address, though, which points to the instruction *after* the call. +; + +align 16 +__throwDispatcherExplicitAddrAfterCore proc + push ecx ; save exception + dec edx + call ?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z + pop ecx ; restore exception +; mov edx is already ok + jmp __throwDispatcherHandler +__throwDispatcherExplicitAddrAfterCore endp + +; __throwDispatcherHandler (eax=frameSetupInfo or exceptionType, +; ecx=exception, +; edx=spillSize,frameSetupSize, or handlerAddress +; +; Description: +; After the exception table entry has been found, the values are passed here +; for processing. This method simply checks for the easy case of an explicit +; handler (known if the exceptionType is given <-- low bit is zero). +; In this case it simply jumps to the handler. Otherwise it passes the +; information along to __throwDispatcherUnwind. +; +; Arguments: +; If low bit of eax is set: +; eax = frame setup information +; ecx = exception object +; edx = spill size excluding callee-save register saves +; or offset from ebp to end of callee-save register saves +; Otherwise: +; eax = exception type (unused) +; ecx = exception object +; edx = handler address + +align 16 +__throwDispatcherHandler proc + test eax, 1 + jne __throwDispatcherUnwind + ;; ecx=exception, edx=handler + jmp edx +__throwDispatcherHandler endp + +; __throwDispatcherUnwind (eax=frame setup info, ecx=exception, edx=spill size +; +; Description: +; This is the global unwind handler. It is used to unwind a single stack +; frame if there are no explicit handlers that catch an exception in a given +; function. +; +; Arguments: +; eax = frame setup information, must have low bit set +; ecx = exception object +; edx = spill size excluding callee-save register saves +; or offset from ebp to end of callee-save register saves +; +; See tables\ExceptionTable.cs for details on these values + +align 16 +__throwDispatcherUnwind proc + ;; eax=frame info + ;; edx=spill size + + ;; obviously ebp isn't useful under frame pointer omission + ;; but less obviously esp may be invalid if ebp is good + ;; (e.g. under varargs we may not have known how many arguments to + ;; pop; this is one reason why varargs turns off frame pointer omission) + test eax, 2h + jne esp_is_good + ;; ebp_is_good + add edx, ebp + mov esp, edx + jmp esp_is_setup +esp_is_good: + ;; pop spill slots + add esp, edx + +esp_is_setup: + + ;; restore callee-saves and pop values from stack + ;; (excludes ebp if used as the frame pointer) + test eax, 4h + je skip_edi_restore + ;; restore edi + pop edi +skip_edi_restore: + test eax, 8h + je skip_esi_restore + ;; restore esi + pop esi +skip_esi_restore: + test eax, 10h + je skip_ebp_restore + ;; restore ebp + pop ebp +skip_ebp_restore: + test eax, 20h + je skip_ebx_restore + ;; restore ebx + pop ebx +skip_ebx_restore: + + test eax, 40h + je skip_jump_transition_record + ;; jump over transition record + add esp, (SIZE Struct_System_GCs_CallStack_TransitionRecord) +skip_jump_transition_record: + + ;; restore ebp if it was used as the frame pointer + test eax, 2h + jne skip_frame_pointer_restore + ;; restore frame pointer (esp == ebp already) + pop ebp +skip_frame_pointer_restore: + + ;; set edx=return address + pop edx + + ;; pop arguments + shr eax, 16 + add esp, eax + + ;; At this point + ;; ecx=exception, edx=return address + ;; esi/edi/ebx/ebp/esp have been restored + ;; eax is scratch + + ;; set up next handler search + jmp __throwDispatcherExplicitAddrAfter +__throwDispatcherUnwind endp + +; +; __throwDivideByZeroException: instantiate an divide-by-zero exception +; and throw it. +; +; Assumes edx points to the address after the one that threw. +; + +align 16 +__throwDivideByZeroException proc + push ebx + push esi + mov ebx,edx ; save address + lock inc ?c_allocationGCInhibitCount@Class_System_GC@@2HA + mov ecx,offset ??_7System_DivideByZeroException@@6B@ + call ?g_AllocateObject@Class_System_GC@@SIPAUClass_System_Object@@PAUClass_System_VTable@@@Z + mov esi,eax ; save pointer to instance of exception + mov ecx,eax ; initialize instance + call ?m__ctor@Class_System_DivideByZeroException@@SIXPAU1@@Z + lock dec ?c_allocationGCInhibitCount@Class_System_GC@@2HA + mov ecx,esi + mov edx,ebx + pop esi + pop ebx + jmp __throwDispatcherExplicitAddr +__throwDivideByZeroException endp + +; +; __throwStackOverflowException: instantiate an StackOverflow exception +; and throw it. +; +; Assumes edx points to the address of the instruction that faulted +; + +align 16 +__throwStackOverflowException proc + push ebx + push esi + mov ebx,edx ; save address + lock inc ?c_allocationGCInhibitCount@Class_System_GC@@2HA + mov ecx,offset ??_7System_StackOverflowException@@6B@ + call ?g_AllocateObject@Class_System_GC@@SIPAUClass_System_Object@@PAUClass_System_VTable@@@Z + mov esi,eax ; save pointer to instance of exception + mov ecx,eax ; initialize instance + call ?m__ctor@Class_System_StackOverflowException@@SIXPAU1@@Z + lock dec ?c_allocationGCInhibitCount@Class_System_GC@@2HA + mov ecx,esi + mov edx,ebx + call ?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z + ResetGuardPageOnStackOverflow + pop esi + pop ebx + mov ecx,eax +; mov edx is already ok + jmp edx +__throwStackOverflowException endp + +; +; __throwNullReferenceException: instantiate an NullReference exception +; and throw it. +; +; Assumes edx points to the address of the instruction that faulted +; + +align 16 +__throwNullReferenceException proc + push ebx + push esi + mov ebx,edx ; save address + lock inc ?c_allocationGCInhibitCount@Class_System_GC@@2HA + mov ecx,offset ??_7System_NullReferenceException@@6B@ + call ?g_AllocateObject@Class_System_GC@@SIPAUClass_System_Object@@PAUClass_System_VTable@@@Z + mov esi,eax ; save pointer to instance of exception + mov ecx,eax ; initialize instance + call ?m__ctor@Class_System_NullReferenceException@@SIXPAU1@@Z + lock dec ?c_allocationGCInhibitCount@Class_System_GC@@2HA + mov ecx,esi + mov edx,ebx + pop esi + pop ebx + jmp __throwDispatcherExplicitAddr +__throwNullReferenceException endp + +; +; __throwDivideByZeroException: instantiate an divide-by-zero exception +; and throw it. +; +; Assumes edx points to the address of the instruction that faulted +; + +align 16 +__throwOverflowException proc + push ebx + push esi + mov ebx,edx ; save address + lock inc ?c_allocationGCInhibitCount@Class_System_GC@@2HA + mov ecx,offset ??_7System_OverflowException@@6B@ + call ?g_AllocateObject@Class_System_GC@@SIPAUClass_System_Object@@PAUClass_System_VTable@@@Z + mov esi,eax ; save pointer to instance of exception + mov ecx,eax ; initialize instance + call ?m__ctor@Class_System_OverflowException@@SIXPAU1@@Z + lock dec ?c_allocationGCInhibitCount@Class_System_GC@@2HA + mov ecx,esi + mov edx,ebx + pop esi + pop ebx + jmp __throwDispatcherExplicitAddr +__throwOverflowException endp + +; +; int System.VTable.doubleToInt(double) +; + +align 16 +?g_doubleToInt@Class_System_VTable@@SIHN@Z proc + push ebp + mov ebp,esp + add esp,-8 + fld real8 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + mov ax,word ptr [ebp-2] + or eax,0C00h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + fistp dword ptr [ebp-8] + fldcw word ptr [ebp-2] + mov eax,dword ptr [ebp-8] + + cmp eax,080000000h + je possible_overflow +return: + mov esp,ebp + pop ebp + ret 8 + +possible_overflow: + fld real8 ptr [ebp+8] + fcomp real8 ptr $DBLMAXINT + fnstsw ax + test ah,4 + jne short return_zero + test ah,1 + jne short return_MININT + +return_MAXINT: + mov eax, 07fffffffh + jmp short return + +return_zero: + xor eax, eax + jmp short return + +return_MININT: + mov eax, 080000000h + jmp short return + +?g_doubleToInt@Class_System_VTable@@SIHN@Z endp + +; +; long System.VTable.doubleToLong(double) +; + +;*********************************************************************/ +; copied from act\doc\resources\clr\src\jithelp.asm + +;Purpose: +; converts a double to a long truncating toward zero (C semantics) +; +; uses stdcall calling conventions +; +; note that changing the rounding mode is very expensive. This +; routine basiclly does the truncation sematics without changing +; the rounding mode, resulting in a win. +; +align 16 +?g_doubleToLong@Class_System_VTable@@SI_JN@Z proc + fld qword ptr[ESP+4] ; fetch arg + lea ecx,[esp-8] + sub esp,16 ; allocate frame + and ecx,-8 ; align pointer on boundary of 8 + fld st(0) ; duplciate top of stack + fistp qword ptr[ecx] ; leave arg on stack, also save in temp + fild qword ptr[ecx] ; arg, round(arg) now on stack + mov edx,[ecx+4] ; high dword of integer + mov eax,[ecx] ; low dword of integer + test eax,eax + je integer_QNaN_or_zero + +arg_is_not_integer_QNaN: + fsubp st(1),st ; TOS=d-round(d), + ; { st(1)=st(1)-st & pop ST } + test edx,edx ; what's sign of integer + jns positive + ; number is negative + ; dead cycle + ; dead cycle + fstp dword ptr[ecx] ; result of subtraction + mov ecx,[ecx] ; dword of difference(single precision) + add esp,16 + xor ecx,80000000h + add ecx,7fffffffh ; if difference>0 then increment integer + adc eax,0 ; inc eax (add CARRY flag) + adc edx,0 ; propagate carry flag to upper bits + ret 8 + +positive: + fstp dword ptr[ecx] ;17-18 ; result of subtraction + mov ecx,[ecx] ; dword of difference (single precision) + add esp,16 + add ecx,7fffffffh ; if difference<0 then decrement integer + sbb eax,0 ; dec eax (subtract CARRY flag) + sbb edx,0 ; propagate carry flag to upper bits + ret 8 + +integer_QNaN_or_zero: + test edx,7fffffffh + jnz arg_is_not_integer_QNaN + fstp st(0) ;; pop round(arg) + fstp st(0) ;; arg + add esp,16 + ret 8 + +?g_doubleToLong@Class_System_VTable@@SI_JN@Z endp + +; +; int System.VTable.floatToInt(float) +; + +align 16 +?g_floatToInt@Class_System_VTable@@SIHM@Z proc + push ebp + mov ebp,esp + add esp,-8 + fld real4 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + xor eax,eax + mov ax,word ptr [ebp-2] + or eax,0C00h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + fistp dword ptr [ebp-8] + fldcw word ptr [ebp-2] + mov eax,dword ptr [ebp-8] + + cmp eax,080000000h + je possible_overflow +return: + mov esp,ebp + pop ebp + ret 4 + +possible_overflow: + fld real4 ptr [ebp+8] + fcomp real8 ptr $DBLMAXINT + fnstsw ax + test ah,4 + jne short return_zero + test ah,1 + jne short return_MININT + mov eax, 07fffffffh + jmp short return + +return_zero: + xor eax, eax + jmp short return + +return_MININT: + mov eax, 080000000h + jmp short return + +?g_floatToInt@Class_System_VTable@@SIHM@Z endp + +; +; long System.VTable.floatToLong(float) +; + +align 16 +?g_floatToLong@Class_System_VTable@@SI_JM@Z proc + push ebp + mov ebp,esp + add esp,-12 + fld real4 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + mov ax,word ptr [ebp-2] + or eax,0C00h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + fistp qword ptr [ebp-12] + fldcw word ptr [ebp-2] + mov eax,dword ptr [ebp-12] + mov edx,dword ptr [ebp-8] + + cmp edx,080000000h + je possible_overflow +return: + mov esp,ebp + pop ebp + ret 4 + +possible_overflow: + mov edx,eax ; save lsw + fld real4 ptr [ebp+8] + fild qword ptr $MAXLONG + fcompp + fnstsw ax + test ah,4 + jne short return_zero + test ah,65 + je short check_MINLONG + +return_MAXLONG: + mov eax, 0ffffffffh + mov edx, 07fffffffh + jmp short return + +return_zero: + xor eax, eax + xor edx, edx + jmp short return + +check_MINLONG: + fld real4 ptr [ebp+8] + fild qword ptr $MINLONG + fcompp + fnstsw ax + test ah,1 + jne short return_original + +return_MINLONG: + xor edx, edx ; zero lsw + +return_original: + mov eax, edx ; restore lsw to eax + mov edx, 080000000h + jmp short return + +?g_floatToLong@Class_System_VTable@@SI_JM@Z endp + +; +; int System.VTable.checkedDoubleToInt(double) +; + +align 16 +?g_checkedDoubleToInt@Class_System_VTable@@SIHN@Z proc + push ebp + mov ebp,esp + add esp,-8 + fld real8 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + mov ax,word ptr [ebp-2] + or eax,0C00h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + fistp dword ptr [ebp-8] + fldcw word ptr [ebp-2] + mov eax,dword ptr [ebp-8] + + cmp eax,080000000h + je possible_overflow +return: + mov esp,ebp + pop ebp + ret 8 + +possible_overflow: + fld real8 ptr [ebp+8] + fcomp real8 ptr $DBLMAXINT + fnstsw ax + test ah,4 ; test for unordered + jne short throw_exception + test ah,1 ; test for <$DBLMAXINT + jne short return_MININT + ; src > $DBLMAXINT + ; throw an overflow exception + jmp short throw_exception + +return_MININT: + ; check against $DBLMININT + fld real8 ptr [ebp+8] + fcomp real8 ptr $DBLMININT + fnstsw ax + test ah, 1 ; test for < $DBLMININT + jne short throw_exception ; throw exception if true + mov eax, 080000000h + jmp short return + +throw_exception: + ; throw an overflow exception + ; set up stack frame so that it looks like a call to throwNewOverflowException + ; from the caller of this function. + mov esp,ebp + pop ebp + pop eax ; grab return address + add esp, 4 ; move esp pass the first parameter. + mov [esp],eax ; overwrite argument + jmp ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ + +?g_checkedDoubleToInt@Class_System_VTable@@SIHN@Z endp + +; +; long System.VTable.checkedDoubleToLong(double) +; + +align 16 +?g_checkedDoubleToLong@Class_System_VTable@@SI_JN@Z proc + push ebp + mov ebp,esp + add esp,-12 + fld real8 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + mov ax,word ptr [ebp-2] + or eax,0C00h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + fistp qword ptr [ebp-12] + fldcw word ptr [ebp-2] + mov eax,dword ptr [ebp-12] + mov edx,dword ptr [ebp-8] + + cmp edx,080000000h + je possible_overflow +return: + mov esp,ebp + pop ebp + ret 8 + +possible_overflow: + mov edx,eax ; save lsw + fld real8 ptr [ebp+8] + fild qword ptr $MAXLONG + fcompp + fnstsw ax + test ah,4 ; test for unordered + jne short return_zero + test ah,65 ; test for <= $MAXLONG + je short check_MINLONG + +return_MAXLONG: + ; src > $MAXLONG + ; throw an exception + jmp short throw_exception + +return_zero: + jmp short throw_exception + +check_MINLONG: + ; src <= $MINLONG + fild qword ptr $MINLONG + fld real8 ptr [ebp+8] + fcompp ; real8 ptr [ebp+8] < $MINLONG + fnstsw ax + test ah,1 + jne short throw_exception + +return_MINLONG: + mov eax, edx ; restore lsw to eax + mov edx, 080000000h + jmp short return + + +throw_exception: + ; throw an overflow exception + ; set up stack frame so that it looks like a call to throwNewOverflowException + ; from the caller of this function. + mov esp,ebp + pop ebp + pop eax ; grab return address + add esp, 4 ; move esp pass the first parameter. + mov [esp],eax ; overwrite argument + jmp ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ + +?g_checkedDoubleToLong@Class_System_VTable@@SI_JN@Z endp + +; +; int System.VTable.checkedFloatToInt(float) +; + +align 16 +?g_checkedFloatToInt@Class_System_VTable@@SIHM@Z proc + push ebp + mov ebp,esp + add esp,-8 + fld real4 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + xor eax,eax + mov ax,word ptr [ebp-2] + or eax,0C00h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + fistp dword ptr [ebp-8] + fldcw word ptr [ebp-2] + mov eax,dword ptr [ebp-8] + + cmp eax,080000000h + je possible_overflow + +return: + mov esp,ebp + pop ebp + ret 4 + +possible_overflow: + fld real4 ptr [ebp+8] + fcomp real8 ptr $DBLMAXINT + fnstsw ax + test ah,4 ; test for unordered + jne short throw_exception + test ah,1 ; test for src < $DBLMAXINT + jne short return_MININT + ; src > $DBLMAXINT + ; throw an overflow exception + jmp short throw_exception + +return_MININT: + ; need to check against $DBLMININT, if it is less than, + ; then throw an overflow exception + fld real4 ptr [ebp+8] + fcomp real8 ptr $DBLMININT + fnstsw ax + test ah,1 ; test for less than + jne short throw_exception + mov eax, 080000000h + jmp short return + +throw_exception: + ; throw an overflow exception + ; set up stack frame so that it looks like a call to throwNewOverflowException + ; from the caller of this function. + mov esp,ebp + pop ebp + pop eax ; grab return address + mov [esp],eax ; overwrite argument + jmp ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ + +?g_checkedFloatToInt@Class_System_VTable@@SIHM@Z endp + +; +; long System.VTable.checkedFloatToLong(float) +; + +align 16 +?g_checkedFloatToLong@Class_System_VTable@@SI_JM@Z proc + push ebp + mov ebp,esp + add esp,-12 + fld real4 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + mov ax,word ptr [ebp-2] + or eax,0C00h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + fistp qword ptr [ebp-12] + fldcw word ptr [ebp-2] + mov eax,dword ptr [ebp-12] + mov edx,dword ptr [ebp-8] + + cmp edx,080000000h + je possible_overflow +return: + mov esp,ebp + pop ebp + ret 4 + +possible_overflow: + mov edx,eax ; save lsw + fld real4 ptr [ebp+8] + fild qword ptr $MAXLONG + fcompp + fnstsw ax + test ah,4 ; test for unordered + jne short return_zero + test ah,65 ; test for <= $MAXLONG + je short check_MINLONG + +return_MAXLONG: + ; src > $MAXLONG + ; throw an exception + jmp short throw_exception + +return_zero: + ; compare with $MAXLONG results in unordered + ; throw an overflow exception + jmp short throw_exception + +check_MINLONG: + ; src <= $MINLONG + fild qword ptr $MINLONG + fld real4 ptr [ebp+8] + fcompp ; real8 ptr [ebp+8] < $MINLONG + fnstsw ax + test ah,1 + jne short throw_exception ; throw an overflow exception when src < $MINLONG +return_MINLONG: + mov eax, edx ; restore lsw + mov edx, 080000000h + jmp short return + +throw_exception: + ; throw an overflow exception + ; set up stack frame so that it looks like a call to throwNewOverflowException + ; from the caller of this function. + mov esp,ebp + pop ebp + pop eax ; grab return address + mov [esp],eax ; overwrite argument + jmp ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ + +?g_checkedFloatToLong@Class_System_VTable@@SI_JM@Z endp + +; +; double System.Math.Sin(double) +; + +align 16 +?g_Sin@Class_System_Math@@SINN@Z proc + fld real8 ptr [esp+4] + fsin + ret 8 +?g_Sin@Class_System_Math@@SINN@Z endp + +; +; double System.Math.Cos(double) +; + +align 16 +?g_Cos@Class_System_Math@@SINN@Z proc + fld real8 ptr [esp+4] + fcos + ret 8 +?g_Cos@Class_System_Math@@SINN@Z endp + +; +; double System.Math.Tan(double) +; + +align 16 +?g_Tan@Class_System_Math@@SINN@Z proc + fld real8 ptr [esp+4] + fptan + fstp real8 ptr [esp+4] + ret 8 +?g_Tan@Class_System_Math@@SINN@Z endp + +; +; +; double System.Math.Atan(double) +; + +align 16 +?g_Atan@Class_System_Math@@SINN@Z proc + fld real8 ptr [esp+4] + fld1 + fpatan + ret 8 +?g_Atan@Class_System_Math@@SINN@Z endp + +; +; double System.Math.atan2(double,double) +; + +align 16 +?g_atan2@Class_System_Math@@SINNN@Z proc + fld real8 ptr [esp+4] + fld real8 ptr [esp+12] + fpatan + ret 16 +?g_atan2@Class_System_Math@@SINNN@Z endp + +; +; double System.Math.exp(double) +; + +align 16 +?g_exp@Class_System_Math@@SINN@Z proc + push ebp + mov ebp,esp + + fldl2e + fmul real8 ptr [ebp+8] + fld st(0) + frndint + fxch st(1) + fsub st(0), st(1) + f2xm1 + fld1 + faddp st(1), st(0) + fscale +;isNaN?? + fstp st(1) + + mov esp,ebp + pop ebp + ret 8 +?g_exp@Class_System_Math@@SINN@Z endp + +; +; double System.Math.log(double) +; + +align 16 +?g_Log@Class_System_Math@@SINN@Z proc + fldln2 + fld real8 ptr [esp+4] + fyl2x + ret 8 +?g_Log@Class_System_Math@@SINN@Z endp + +; +; double System.Math.Ceiling(double) +; + +align 16 +?g_Ceiling@Class_System_Math@@SINN@Z proc + push ebp + mov ebp,esp + add esp,-4 + + fld real8 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + mov ax,word ptr [ebp-2] + and ah,0F3h + or ah,008h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + frndint + fldcw word ptr [ebp-2] + + mov esp,ebp + pop ebp + ret 8 +?g_Ceiling@Class_System_Math@@SINN@Z endp + +; +; double System.Math.Floor(double) +; + +align 16 +?g_Floor@Class_System_Math@@SINN@Z proc + push ebp + mov ebp,esp + add esp,-4 + + fld real8 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + mov ax,word ptr [ebp-2] + and ah,0F3h + or ah,004h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + frndint + fldcw word ptr [ebp-2] + + mov esp,ebp + pop ebp + ret 8 +?g_Floor@Class_System_Math@@SINN@Z endp + +; +; double System.Math.Round(double) +; + +align 16 +?g_Round@Class_System_Math@@SINN@Z proc + fld QWORD PTR [ESP+4] + frndint + ret 8 +?g_Round@Class_System_Math@@SINN@Z endp + +; +; float System.Math.Abs(float) +; + +align 16 +?g_abs@Class_System_Math@@SIMM@Z proc + fld real4 ptr [esp+4] + fabs + ret 4 +?g_abs@Class_System_Math@@SIMM@Z endp + +; +; double System.Math.Abs(double) +; + +align 16 +?g_abs@Class_System_Math@@SINN@Z proc + fld real8 ptr [esp+4] + fabs + ret 8 +?g_abs@Class_System_Math@@SINN@Z endp + +align 16 +?g_floatRem@Class_System_VTable@@SIMMM@Z proc + fld real4 ptr [esp+8] + fld real4 ptr [esp+4] +fremloop: + fprem + fstsw ax + fwait + sahf + jp fremloop ; Continue while the FPU status bit C2 is set + ffree st(1) + ret 8 +?g_floatRem@Class_System_VTable@@SIMMM@Z endp + +align 16 +?g_doubleRem@Class_System_VTable@@SINNN@Z proc + fld real8 ptr [esp+12] + fld real8 ptr [esp+4] +fremloop: + fprem + fstsw ax + fwait + sahf + jp fremloop ; Continue while the FPU status bit C2 is set + ffree st(1) + ret 16 +?g_doubleRem@Class_System_VTable@@SINNN@Z endp + + +if EXCLUDED +; +; void __checkFPStackDepth0 +; + +align 16 +__checkFPStackDepth0 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,0 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth0 endp + +; +; void __checkFPStackDepth1 +; + +align 16 +__checkFPStackDepth1 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-1 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth1 endp + +; +; void __checkFPStackDepth2 +; + +align 16 +__checkFPStackDepth2 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-2 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth2 endp + +; +; void __checkFPStackDepth3 +; + +align 16 +__checkFPStackDepth3 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-3 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth3 endp + +; +; void __checkFPStackDepth4 +; + +align 16 +__checkFPStackDepth4 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-4 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth4 endp + +; +; void __checkFPStackDepth5 +; + +align 16 +__checkFPStackDepth5 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-5 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth5 endp + +; +; void __checkFPStackDepth6 +; + +align 16 +__checkFPStackDepth6 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-6 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth6 endp + +; +; void __checkFPStackDepth7 +; + +align 16 +__checkFPStackDepth7 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-7 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth7 endp +endif ; EXCLUDED + +end diff --git a/base/Imported/Bartok/runtime/singularity/CSharp.targets b/base/Imported/Bartok/runtime/singularity/CSharp.targets new file mode 100644 index 0000000..a417294 --- /dev/null +++ b/base/Imported/Bartok/runtime/singularity/CSharp.targets @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ADAPTIVE_COPYING_COLLECTOR + /AdaptiveCopying + ADAPTIVE_COPYING_COLLECTOR + + + + + + + + + + + + + + + + + + + CONCURRENT_MS_COLLECTOR + /ConcurrentMSGC + CONCURRENT_MS_COLLECTOR + + + + + + + + + + + + MARK_SWEEP_COLLECTOR + /MarkSweepGC + MARK_SWEEP_COLLECTOR + + + + + + + + + + NULL_COLLECTOR + /NullGC + NULL_COLLECTOR + + + + + + + + + SEMISPACE_COLLECTOR + /SemispaceGC + SEMISPACE_COLLECTOR + + + + + + + + + + + + + + + + + SLIDING_COLLECTOR + /SlidingGC + SLIDING_COLLECTOR + + + + + + + + + + + + + + + + + The 'Collector' property has not been specified, or is not a recognized value. + + + + + + $(DefineConstants); + $(DefineConstants)$(GCDefine) + + + diff --git a/base/Imported/Bartok/runtime/singularity/Native.targets b/base/Imported/Bartok/runtime/singularity/Native.targets new file mode 100644 index 0000000..ce23504 --- /dev/null +++ b/base/Imported/Bartok/runtime/singularity/Native.targets @@ -0,0 +1,30 @@ + + + + + + + + + + + + + $(BartokAsmArgs) /I$(BartokSrcDir)\runtime\singularity\native\arch\$(BARTOK_MACHINE) + + + diff --git a/base/Imported/Bartok/runtime/singularity/native/arch/amd64/core.inc b/base/Imported/Bartok/runtime/singularity/native/arch/amd64/core.inc new file mode 100644 index 0000000..bc6c805 --- /dev/null +++ b/base/Imported/Bartok/runtime/singularity/native/arch/amd64/core.inc @@ -0,0 +1,13 @@ +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; + +include hal.inc + +ifdef SINGULARITY_KERNEL +externdef ?g_setStopContext@Class_System_Threading_Thread@@SAXPEAU1@PEAUClass_System_Exception@@@Z:NEAR +externdef __throwBeyondMarker:NEAR +endif ; SINGULARITY_KERNEL + +ResetGuardPageInStackOverflow MACRO + ENDM diff --git a/base/Imported/Bartok/runtime/singularity/native/arch/amd64/gc.asm b/base/Imported/Bartok/runtime/singularity/native/arch/amd64/gc.asm new file mode 100644 index 0000000..1c54c66 --- /dev/null +++ b/base/Imported/Bartok/runtime/singularity/native/arch/amd64/gc.asm @@ -0,0 +1,471 @@ +;; ---------------------------------------------------------------------------- +;; +;; Copyright (c) Microsoft Corporation. All rights reserved. +;; +;; ---------------------------------------------------------------------------- + +.code + +include hal.inc + +PAGE_BITS EQU 12 +MASK_OWNER EQU 03h + +extern ?g_CollectBody@Class_System_GC@@SAPEAUClass_System_Threading_Thread@@PEAU2@H@Z:proc +externdef ?g_CollectBody@Class_System_GC@@SAPEAUClass_System_Threading_Thread@@PEAU2@H@Z:NEAR +; static void __fastcall GC.CollectBody(Thread, int) + +ifdef SINGULARITY +externdef ?g_LeaveManagedSpace@Class_System_GCs_Transitions@@SAXPEAUStruct_Microsoft_Singularity_X86_ThreadContext@@@Z:NEAR +externdef ?g_ReturnToManagedSpace@Class_System_GCs_Transitions@@SAXPEAUStruct_Microsoft_Singularity_X86_ThreadContext@@@Z:NEAR +else +externdef ?g_LeaveManagedSpace@Class_System_GCs_Transitions@@SAXPEAUClass_System_Threading_Thread@@@Z:NEAR +externdef ?g_ReturnToManagedSpace@Class_System_GCs_Transitions@@SAPEAUClass_System_Threading_Thread@@H@Z:NEAR +endif + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; pushStackMark: If a function may be called by C, push a pointer to its +; frame on to a stack at the beginning of the function. +; +; Transition record layout: +; +; (lower addresses) +; -------------------------- +; |Old stack marker record | +; -------------------------- +; |Addr of call instr | +; -------------------------- +; |Bottom of stack frame | +; -------------------------- +; |rbx | +; -------------------------- +; |rdi | +; -------------------------- +; |rsi | +; -------------------------- +; |rbp | +; -------------------------- +; (higher addresses); +; + +align 16 +__pushStackMark proc frame + ; save caller-save registers + PrologPush rax + PrologPush rcx + PrologPush rdx + PrologPush r8 + PrologPush r9 + PrologPush r10 + PrologPush r11 + .endprolog + lea rcx, [rbp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)] ; calculate new stack marker address +ifdef SINGULARITY + CurrentThreadContext(rax) ; get current threadcontext + mov rdx, [eax].Struct_Microsoft_Singularity_ThreadContext._stackMarkers ;save old stack marker address +else + CurrentThread rax,eax, rdx ; get current thread + mov rdx, [rax].Class_System_Threading_Thread._asmStackMarker ;save old stack marker address +endif + mov [rcx].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord, rdx +ifdef SINGULARITY + mov [rax].Struct_Microsoft_Singularity_ThreadContext._stackMarkers, rcx ; update thread record field +else + mov [rax].Class_System_Threading_Thread._asmStackMarker,rcx ; update thread record field +endif + ;; REVIEWx64: x86=12, x64=56, symbolic value maybe? + mov rdx, [rsp+56] ; load return address of this call + mov [rcx].Struct_System_GCs_CallStack_TransitionRecord._callAddr,rdx + ;; REVIEWx64: x86=16, x64=64, symbolic value maybe? + lea rdx, [rsp+64] + mov [rcx].Struct_System_GCs_CallStack_TransitionRecord._stackBottom, rdx ; save bottom of stack frame + mov [rcx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBX, rbx ; save callee-save registers + mov [rcx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EDI, rdi + mov [rcx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._ESI, rsi + mov [rcx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBP, rbp + mov [rcx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R12, r12 + mov [rcx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R13, r13 + mov [rcx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R14, r14 + mov [rcx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R15, r15 + mov rcx, rax +ifdef SINGULARITY + call ?g_LeaveManagedSpace@Class_System_GCs_Transitions@@SAXPEAUStruct_Microsoft_Singularity_ThreadContext@@@Z +else + call ?g_LeaveManagedSpace@Class_System_GCs_Transitions@@SAXPEAUClass_System_Threading_Thread@@@Z +endif + pop r11 + pop r10 + pop r9 + pop r8 + pop rdx + pop rcx + pop rax + ret +__pushStackMark endp + + +; +; popStackMark: pop the pointer before returning from the function +; +align 16 +__popStackMark proc frame + ; save caller-save registers + PrologPush rax + PrologPush rcx + PrologPush rdx + PrologPush r8 + PrologPush r9 + PrologPush r10 + PrologPush r11 + .endprolog +ifdef SINGULARITY + CurrentThreadContext rcx ; get current thread + call ?g_ReturnToManagedSpace@Class_System_GCs_Transitions@@SAXPEAUStruct_Microsoft_Singularity_ThreadContext@@@Z + CurrentThreadContext(rax) ; get current thread +else + CurrentThreadIndex rax,eax, rcx ; get current thread + mov rcx, rax + call ?g_ReturnToManagedSpace@Class_System_GCs_Transitions@@SAPEAUClass_System_Threading_Thread@@H@Z +endif + lea rcx, [rbp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)] + mov rdx, [rcx].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord ; get old stack marker value +ifdef SINGULARITY + mov [rax].Struct_Microsoft_Singularity_ThreadContext._stackMarkers, rdx; update thread record field +else + mov [rax].Class_System_Threading_Thread._asmStackMarker, rdx; update thread record field +endif + mov rbx, [rcx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBX ; restore callee-save registers + mov rdi, [rcx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EDI + mov rsi, [rcx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._ESI + mov rbp, [rcx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBP + mov r12, [rcx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R12 + mov r13, [rcx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R13 + mov r14, [rcx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R14 + mov r15, [rcx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R15 + pop r11 + pop r10 + pop r9 + pop r8 + pop rdx ; restore caller-save registers + pop rcx + pop rax + ret +__popStackMark endp + +;++ +; +; Routine Description: +; +; This routine creates and links a new transition record containig all callee-saved +; registers, invokes System.GC.CollectBody, and then removes the transition record. +; +; Arguments: +; +; Thread (rcx) - Supplies the current thread. +; +; Generation (rdx) - Supplies generation. +; +; Return Value: +; +; None. +; +;-- + +align 16 +?g_CollectBodyTransition@Class_System_GC@@SAXPEAUClass_System_Threading_Thread@@H@Z proc frame + +; +; Create frame. +; + + PrologPush rbp + SetFramePointer rbp + .endprolog + +; +; Allocate a transition record on the stack. +; + + sub rsp, (size Struct_System_GCs_CallStack_TransitionRecord) + 32 + and rsp, NOT 0Fh + +; +; Save arguments. +; + + push rcx + push rdx + +; +; Calculate the address of the transition record. +; + + lea rdx, qword ptr [rbp - (size Struct_System_GCs_CallStack_TransitionRecord)] + +; +; Get the current stack marker. +; + +ifdef SINGULARITY_KERNEL + + mov rax, qword ptr [rcx].Class_System_Threading_Thread._context.Struct_Microsoft_Singularity_ThreadContext._stackMarkers + +else + + mov rax, qword ptr [rcx].Class_System_Threading_Thread._context + mov rax, qword ptr [rax].Struct_Microsoft_Singularity_ThreadContext._stackMarkers + +endif + +; +; Initialize new transition record. +; + + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord, rax + + mov rax, qword ptr [rbp + 8] + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._callAddr, rax + + lea rax, qword ptr [rbp + 16] + + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._stackBottom, rax + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBX, rbx + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EDI, rdi + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._ESI, rsi + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R12, r12 + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R13, r13 + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R14, r14 + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R15, r15 + + mov rax, qword ptr [rbp] + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBP, rax + +; +; Set new stack marker. +; + +ifdef SINGULARITY_KERNEL + + mov qword ptr [rcx].Class_System_Threading_Thread._context.Struct_Microsoft_Singularity_ThreadContext._stackMarkers, rdx + +else + + mov rax, qword ptr [rcx].Class_System_Threading_Thread._context + mov qword ptr [rax].Struct_Microsoft_Singularity_ThreadContext._stackMarkers, rdx + +endif + +; +; Restore arguments +; + + pop rdx + pop rcx + +ifdef DEBUG + +; +; Check if the stack pointer is properly aligned. +; + + mov rax, rsp + and rax, 08h + cmp rax, 0 + je @F + + int 3 + +@@: + +endif + +; +; Invoke System.GC.CollectBody, which returns the current thread address. +; + + call ?g_CollectBody@Class_System_GC@@SAPEAUClass_System_Threading_Thread@@PEAU2@H@Z + +; +; Restore previous stack marker. +; + + lea rdx, qword ptr [rbp - (size Struct_System_GCs_CallStack_TransitionRecord)] + mov rcx, qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord + +ifdef SINGULARITY_KERNEL + + mov qword ptr [rax].Class_System_Threading_Thread._context.Struct_Microsoft_Singularity_ThreadContext._stackMarkers, rcx + +else + + mov rax, qword ptr [rax].Class_System_Threading_Thread._context + mov qword ptr [rax].Struct_Microsoft_Singularity_ThreadContext._stackMarkers, rcx + +endif + +; +; Restore all callee-saved registers from the transition record. +; + + ; NOTE: DO NOT restore rbp direct, otherwise upon return we will + ; skip a frame. Instead, restore value to the rbp stack slot. + mov rbx, qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBP + mov qword ptr [rbp], rbx + + mov rbx, qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBX + mov rsi, qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._ESI + mov r12, qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R12 + mov r13, qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R13 + mov r14, qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R14 + mov r15, qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R15 + mov rdi, qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EDI + + mov rsp, rbp + pop rbp + ret + +?g_CollectBodyTransition@Class_System_GC@@SAXPEAUClass_System_Threading_Thread@@H@Z endp + +ifdef SINGULARITY_KERNEL + +;++ +; +; Routine Description: +; +; This routine suspends GC for the old context, switches to and revives GC for the new context. +; +; Arguments: +; +; OldContext (rcx) - Supplies the old context. +; +; NewContext (rdx) - Supplies the new context. +; +; Return Value: +; +; None. +; +;-- + +align 16 +?g_SwitchToThreadContext@Class_Microsoft_Singularity_Processor@@SAXPEAUStruct_Microsoft_Singularity_ThreadContext@@0@Z proc frame + +SwapGCContext label byte + +; +; Prepare call frame. +; + + PrologPush rbp + SetFramePointer rbp + .endprolog + +; +; Allocate a transition record on the stack. +; + + sub rsp, (size Struct_System_GCs_CallStack_TransitionRecord) + 16 + 32 + and rsp, NOT 0Fh + +; +; Save arguments. +; + + mov qword ptr [rbp - (size Struct_System_GCs_CallStack_TransitionRecord) - 16], rcx + mov qword ptr [rbp - (size Struct_System_GCs_CallStack_TransitionRecord) - 8], rdx + +; +; Calculate the address of the transition record. +; + + lea rdx, qword ptr [rbp - (size Struct_System_GCs_CallStack_TransitionRecord)] + +; +; Get the current stack marker. +; + + mov rax, qword ptr [rcx].Struct_Microsoft_Singularity_ThreadContext._stackMarkers + +; +; Initialize new transition record. +; + + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord, rax + + mov rax, qword ptr [rbp + 8] + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._callAddr, rax + + lea rax, qword ptr [rbp + 16] + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._stackBottom, rax + + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBX, rbx + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EDI, rdi + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._ESI, rsi + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R12, r12 + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R13, r13 + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R14, r14 + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R15, r15 + + mov rax, qword ptr [rbp] + mov qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBP, rax + +; +; Set new stack marker. +; + + mov qword ptr [rcx].Struct_Microsoft_Singularity_ThreadContext._stackMarkers, rdx + +; +; Suspend GC for the old thread. +; + + call ?g_SuspendThread@Class_System_GCs_Transitions@@SAXPEAUStruct_Microsoft_Singularity_ThreadContext@@@Z + +; +; Switch context. +; + + mov rcx, qword ptr [rbp - (size Struct_System_GCs_CallStack_TransitionRecord) - 8] + call ?g_SwitchToThreadContextNoGC@Class_Microsoft_Singularity_Processor@@SAXPEAUStruct_Microsoft_Singularity_ThreadContext@@@Z + +; +; Revive GC. +; + + mov rcx, qword ptr [rbp - (size Struct_System_GCs_CallStack_TransitionRecord) - 16] + call ?g_ReviveThread@Class_System_GCs_Transitions@@SAXPEAUStruct_Microsoft_Singularity_ThreadContext@@@Z + +; +; Restore previous stack marker. +; + + mov rcx, qword ptr [rbp - (size Struct_System_GCs_CallStack_TransitionRecord) - 16] + lea rdx, qword ptr [rbp - (size Struct_System_GCs_CallStack_TransitionRecord)] + + mov rax, qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord + mov qword ptr [rcx].Struct_Microsoft_Singularity_ThreadContext._stackMarkers, rax + +; +; Restore all callee-saved registers from the transition record. +; + + mov rbx, qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBX + mov rsi, qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._ESI + mov rbp, qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBP + mov r12, qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R12 + mov r13, qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R13 + mov r14, qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R14 + mov r15, qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._R15 + mov rdi, qword ptr [rdx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EDI + + mov rsp, rbp + pop rbp + ret + +?g_SwitchToThreadContext@Class_Microsoft_Singularity_Processor@@SAXPEAUStruct_Microsoft_Singularity_ThreadContext@@0@Z endp + +public SwapGCContext + +endif + +end diff --git a/base/Imported/Bartok/runtime/singularity/native/arch/amd64/lib.asm b/base/Imported/Bartok/runtime/singularity/native/arch/amd64/lib.asm new file mode 100644 index 0000000..755e221 --- /dev/null +++ b/base/Imported/Bartok/runtime/singularity/native/arch/amd64/lib.asm @@ -0,0 +1,246 @@ +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; + +include core.inc + +externdef __throwDispatcherExplicitAddrAfter:NEAR +externdef __throwDispatcherExplicitAddrAfterCore:NEAR + +; +; __throwDispatcherExplictAddrAfter (ecx=exception, edx=throwAddress) +; +; Description: +; This is to be used when the address of the instruction immediately after +; the one that actually threw the exception is passed as an argument. +; +; Arguments: +; ecx = pointer to exception object being thrown +; edx = address of the instruction following the one where +; the exception was thrown +; +; This is used, for example, in stack unwinding, where edx is the +; return address on the stack for the current procedure. +; +; Stack unwinding occurs when the current procedure does not have a handler +; for the routine. The idea is to pop the stack frame and treat the call +; instruction in the caller as though it threw. We only have the return +; address, though, which points to the instruction *after* the call. +; + +align 16 +__throwDispatcherExplicitAddrAfter proc + push rcx + push rdx + mov rcx, rdx + call ?g_IsUnlinkStack@Class_System_Exception@@SA_NPEAUuintPtr@@@Z + pop rdx + pop rcx + test al, al + je normal + mov rax, rcx ; save exception type + mov rcx, rdx + mov rdx, [rbp+8] ; save the return addr in caller + push rax + mov rax, afterUnlinkStack + mov [rbp+8], rax ; override return addr to instr after + pop rax + jmp rcx ; unlink stack which saves eax, edx +afterUnlinkStack: + mov rcx, rax ; restore return addr in caller +normal: + jmp __throwDispatcherExplicitAddrAfterCore +__throwDispatcherExplicitAddrAfter endp + +ifdef SINGULARITY_KERNEL +; There are 3 control paths that merge here: +; (1) When Thread.cs stops a process mode thread, it sets eip to point here. +; (2) When Thread.cs stops a blocked kernel thread, it sets eip to point here. +; (3) The stack unwinder falls through to this case when a kernel exception +; reaches a process's frames. +; For control path (3), a finally block puts us in a gc safe state +; just before we reach here. +; We also expect to be in the gc safe state most of the +; time for control paths (1) and (2), but +; because pushStackMark and popStackMark are not atomic operations, +; we cannot be 100% sure that we're in the gc safe state. +; eax: kernel->process or kernel->kernel marker +; ecx: exception +__throwBeyondMarker LABEL NEAR + push rcx + push rax + ; Leave the gc safe state (if we're actually in it) + call ?g_RestoreMutatorControlIfNeeded@Class_System_GCs_Transitions@@SAXXZ + pop rax + pop rcx +; eax: kernel->process or kernel->kernel marker +; ecx: exception + ; Restore state from the marker, skipping over any intermediate frames. + ; Keep the original exception in ecx. + ; Get the return address for the frame beyond the marker into edx: + mov rdx, qword ptr [rax].Struct_System_GCs_CallStack_TransitionRecord._stackBottom + mov rdx, qword ptr [rdx - 8] + ; Restore the kernel's ebx, edi, esi, ebp from the marker: + mov rbx, qword ptr [rax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBX + mov rdi, qword ptr [rax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EDI + mov rsi, qword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._ESI + mov rbp, qword ptr [rax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBP + ; Keep a copy of transition record (eax) and oldTransitionRecord + push rax + push qword ptr [rax].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord + ; Read _stackBottom (the new esp) from the kernel->process marker: + mov rax, qword ptr [rax].Struct_System_GCs_CallStack_TransitionRecord._stackBottom + ; Save ecx, edx to new stack -- this may overwrite the transition record, + ; so don't read any more fields from the transition record after this point. + mov [rax - 8], ecx ; (exception) + mov [rax - 16], edx ; (return address) + ; Set up the two arguments to DiscardStackSegments + pop rdx ; (oldTransitionRecord) + pop rcx ; (marker) + ; Restore the kernel's esp from the kernel->process marker: + lea rsp, [rax - 16] + ; Free any stack segments that we skipped over: + ;; TODO: are we sure there's enough stack space to call this? + call ?g_DiscardSkippedStackSegments@Class_System_Threading_Thread@@SAXPEAUStruct_System_GCs_CallStack_TransitionRecord@@0@Z + pop rdx ; (return address) + pop rcx ; (exception) + +; +; void Thread.setStopContext(Thread t, Exception exn) +; + +align 16 + +?g_setStopContext@Class_System_Threading_Thread@@SAXPEAU1@PEAUClass_System_Exception@@@Z proc frame + PrologPush rbp ; create ebp chain entry + SetFramePointer rbp ; set new ebp + .endprolog + ; rcx = rcx.context + add rcx, Class_System_Threading_Thread._context + ; context.eip = __throwBeyondMarker + ; context.ecx = processStopException + ; context.eax = context.stackMarkers + mov rax, __throwBeyondMarker + mov [rcx].Struct_Microsoft_Singularity_ThreadContext._threadRecord._spill._ip, rax + mov [rcx].Struct_Microsoft_Singularity_ThreadContext._threadRecord._spill._cx, rdx + mov rax, [rcx].Struct_Microsoft_Singularity_ThreadContext._stackMarkers + mov [rcx].Struct_Microsoft_Singularity_ThreadContext._threadRecord._spill._ax, rax + ; Epilogue + mov esp, ebp + pop rbp; + ret +?g_setStopContext@Class_System_Threading_Thread@@SAXPEAU1@PEAUClass_System_Exception@@@Z endp + +endif ; SINGULARITY_KERNEL + + +?g_ZeroPages@Class_System_Buffer@@SAXPEAEH@Z proc frame + ;; ECX = dst + ;; EDX = len (bytes) + PrologPush rbp + SetFramePointer rbp + .endprolog + pxor mm0, mm0 +next: + movntq [rcx + 0], mm0 + movntq [rcx + 8], mm0 + movntq [rcx + 16], mm0 + movntq [rcx + 24], mm0 + movntq [rcx + 32], mm0 + movntq [rcx + 40], mm0 + movntq [rcx + 48], mm0 + movntq [rcx + 56], mm0 + add rcx, 64 + sub rdx, 64 + ja next + + sfence + emms + pop rbp + ret +?g_ZeroPages@Class_System_Buffer@@SAXPEAEH@Z endp + +?g_CopyPages@Class_System_Buffer@@SAXPEAE0H@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + + ;; RCX = dst + ;; RX = src + ;; r8 = len (bytes) + + mov rax, r8 + + cmp rcx, rdx + js down + + ;; destination is lower than source + add rcx, rax + add rdx, rax + sub rcx, 64 + sub rdx, 64 + +up: + movq mm0, [rdx + 0] + movq mm1, [rdx + 8] + movq mm2, [rdx + 16] + movq mm3, [rdx + 24] + movq mm4, [rdx + 32] + movq mm5, [rdx + 40] + movq mm6, [rdx + 48] + movq mm7, [rdx + 56] + movntq [rcx + 0], mm0 + movntq [rcx + 8], mm1 + movntq [rcx + 16], mm2 + movntq [rcx + 24], mm3 + movntq [rcx + 32], mm4 + movntq [rcx + 40], mm5 + movntq [rcx + 48], mm6 + movntq [rcx + 56], mm7 + sub rcx, 64 + sub rdx, 64 + sub rax, 64 + ja up + + sfence + emms + pop rbp + ret + + ;; destination is higher than source +down: + movq mm0, [rdx + 0] + movq mm1, [rdx + 8] + movq mm2, [rdx + 16] + movq mm3, [rdx + 24] + movq mm4, [rdx + 32] + movq mm5, [rdx + 40] + movq mm6, [rdx + 48] + movq mm7, [rdx + 56] + movntq [rcx + 0], mm0 + movntq [rcx + 8], mm1 + movntq [rcx + 16], mm2 + movntq [rcx + 24], mm3 + movntq [rcx + 32], mm4 + movntq [rcx + 40], mm5 + movntq [rcx + 48], mm6 + movntq [rcx + 56], mm7 + add rcx, 64 + add rdx, 64 + sub rax, 64 + ja down + + sfence + emms + pop rbp + ret +?g_CopyPages@Class_System_Buffer@@SAXPEAE0H@Z endp + +extern ?brtmain@@3P6AHPEAUClassVector_Class_System_String@@@ZEA:qword + +align 16 +?g_CallMain@Class_Microsoft_Singularity_AppRuntime@@SAHPEAUClassVector_Class_System_String@@@Z proc + jmp qword ptr [?brtmain@@3P6AHPEAUClassVector_Class_System_String@@@ZEA] +?g_CallMain@Class_Microsoft_Singularity_AppRuntime@@SAHPEAUClassVector_Class_System_String@@@Z endp + +end diff --git a/base/Imported/Bartok/runtime/singularity/native/arch/arm/core.inc b/base/Imported/Bartok/runtime/singularity/native/arch/arm/core.inc new file mode 100644 index 0000000..b25aae7 --- /dev/null +++ b/base/Imported/Bartok/runtime/singularity/native/arch/arm/core.inc @@ -0,0 +1,10 @@ +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; + +EXCLUDED equ 0 + + INCLUDE hal.inc + + END + diff --git a/base/Imported/Bartok/runtime/singularity/native/arch/arm/gc.asm b/base/Imported/Bartok/runtime/singularity/native/arch/arm/gc.asm new file mode 100644 index 0000000..62f3921 --- /dev/null +++ b/base/Imported/Bartok/runtime/singularity/native/arch/arm/gc.asm @@ -0,0 +1,364 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code for transitioning GC domain boundaries. +;;; + + AREA |.text|, CODE, READONLY + +|defining ?g_SwitchToThreadContext@Class_Microsoft_Singularity_Processor@@SAXPAUStruct_Microsoft_Singularity_ThreadContext@@0@Z| EQU 1 +|defining ?g_CollectBodyTransition@Class_System_GC@@SIXPAUClass_System_Threading_Thread@@H@Z| EQU 1 +|defining ?g_setStopContext@Class_System_Threading_Thread@@SAXPAU1@PAUClass_System_Exception@@@Z| EQU 1 + +PAGE_BITS EQU 12 + + include hal.inc + + MACRO + BREAKPOINT + ;; bkpt 0xffff + swi 0xffff03 + MEND + + +; static void __fastcall VTable.ssbRecordWriteBarrier(void*) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; pushStackMark: If a function may be called by C, push a pointer to its +; frame on to a stack at the beginning of the function. +; +; Transition record layout: +; +; (lower addresses) +; -------------------------- +; |Old stack marker record | +; -------------------------- +; |Addr of call instr | +; -------------------------- +; |Bottom of stack frame | +; -------------------------- +; |r4 | +; -------------------------- +; |r5 | +; -------------------------- +; |r6 | +; -------------------------- +; |r7 | +; -------------------------- +; |r8 | +; -------------------------- +; |r9 | +; -------------------------- +; |r10 | +; -------------------------- +; |r11 | +; -------------------------- +; (higher addresses); +; + + EXPORT __pushStackMark + NESTED_ENTRY __pushStackMark + + stmdb sp!, {lr} + sub sp, sp, #16 ; Space to save r0-r3 + + PROLOG_END + + ;; Save the callee-save registers in the transition record. + ;; Since the transition record is located immediately below the FP, + ;; we index off the FP rather than the pointer to the struct itself. + stmdb fp, {r4-r11} ; Store callee-save registers in record + ;; From this point forward, we use r8, r9, and r10 as temps. + ;; R12 may hold the address of the call + ;; TransitionRecord *newRecord(r9) = FP - sizeof(TransitionRecord); + sub r9, fp, #|Struct_System_GCs_CallStack_TransitionRecord___SIZE| + + ;; link new stack marker into chain starting at CurrentThread + ;; ThreadContext *currentThread(r10) = Thread.CurrentThread() + GET_THREAD_ADDR r10, r8 + ldr r10, [r10] ; GET_THREAD_ADDR returns ThreadContext** + + ;; Get the old transition record (r8) + ldr r8, [r10, #|Struct_Microsoft_Singularity_ThreadContext___stackMarkers|] + + ;; newRecord(r9)->oldTransitionRecord = oldRecord(r8) + str r8, [r9, #|Struct_System_GCs_CallStack_TransitionRecord___oldTransitionRecord|] + + ;; Set the new transition record + str r9, [r10, #|Struct_Microsoft_Singularity_ThreadContext___stackMarkers|] + + ;; newRecord(r9)->callAddr = return address of this call (lr). + str lr, [r9, #|Struct_System_GCs_CallStack_TransitionRecord___callAddr|] + ;; newRecord(r9)->stackBottom = bottom of stack frame (sp) + str sp, [r9, #|Struct_System_GCs_CallStack_TransitionRecord___stackBottom|] + + ; r0 = &threadContext->gcStates; + stmia sp, {r0-r3} + add r0, r10, #|Struct_Microsoft_Singularity_ThreadContext___gcStates| + + ldr r7, =0x800360a0 + cmp r0, r7 + bne NotTheRightThreadInPush + BREAKPOINT +NotTheRightThreadInPush + + ;; Temp hack -- certain threads on ARM are not getting initialized like + ;; they do on x86 -- the gcStates field is still 0 at first... + mov r1, #|Class_System_GCs_Transitions_MutatorState| + |Class_System_GCs_Transitions_OtherDormantState| + mov r2, #0 + mov r8, r0 ;; save r0 for the next call to CompareExchange + ;;; uint32 InterlockedCompareExchange(uint32 * dst, uint32 exc, uint32 cmp) + bl |?g_CompareExchange@Class_System_Threading_Interlocked@@SAIPAIII@Z| + mov r0, r8 ;; restore r0 for the next call to CompareExchange + ;; End temp hack + + ;; Allow the GC to work while this thread is out + mov r1, #|Class_System_GCs_Transitions_DormantState| + |Class_System_GCs_Transitions_OtherMutatorState| + mov r2, #|Class_System_GCs_Transitions_MutatorState| + |Class_System_GCs_Transitions_OtherDormantState| + ;;; uint32 InterlockedCompareExchange(uint32 * dst, uint32 exc, uint32 cmp) + bl |?g_CompareExchange@Class_System_Threading_Interlocked@@SAIPAIII@Z| + + ;; We skip the call to g_LeaveManagedSpace if we modified gcStates, + ;; which is to say that the thread was previously in Mutator. + cmps r0, #|Class_System_GCs_Transitions_MutatorState| + |Class_System_GCs_Transitions_OtherDormantState| + ldmia sp, {r0-r3} + beq pushFastPath + + ;; Get the min thread context + mov r0, r10 + + stmia sp, {r0-r3} + mov r0, r8 + bl |?g_LeaveManagedSpace@Class_System_GCs_Transitions@@SAXPAUStruct_Microsoft_Singularity_ThreadContext@@@Z| + ldmia sp, {r0-r3} + +pushFastPath + add sp, sp, #16 + ldmia sp!, {lr} + ;; Restore registers r7-r10 (and the ummodified r11) + ldmdb fp, {r7-r11} + ;; return + mov pc, lr + + ENTRY_END + + +; +; popStackMark: pop the pointer before returning from the function +; + EXPORT __popStackMark + NESTED_ENTRY __popStackMark + + stmdb sp!, {lr} + sub sp, sp, #16 ; Space to save r0-r3 + + PROLOG_END + + ;; Save the callee-save registers in the transition record. + ;; Since the transition record is located immediately below the FP, + ;; we index off the FP rather than the pointer to the struct itself. + stmdb fp, {r4-r11} ; Store callee-save registers in record + + ;; From this point forward, we use r7, r8, r9, and r10 as temps. + ;; R12 may hold the address of the call + ;; TransitionRecord *newRecord(r9) = FP - sizeof(TransitionRecord); + sub r9, fp, #|Struct_System_GCs_CallStack_TransitionRecord___SIZE| + + ;; ThreadContext *currentThread(r10) = Thread.CurrentThread() + GET_THREAD_ADDR r10, r8 + ldr r10, [r10] ; GET_THREAD_ADDR returns ThreadContext** + + mov r8, r10 + + stmia sp, {r0-r3} ;; push these here so we can use r0 as a scratch variable + + ; r0 = &threadContext->gcStates; + add r0, r10, #|Struct_Microsoft_Singularity_ThreadContext___gcStates| + + ldr r7, =0x800360a0 + cmp r0, r7 + bne NotTheRightThreadInPop + BREAKPOINT +NotTheRightThreadInPop + + ; r1 = *r0; + ; if (r1 & MutatorState) ReturnToManagedSpace + ; *r0 = r1; + ldr r1, [r0] + tst r1, #|Class_System_GCs_Transitions_MutatorState| + bne popFastPath ;; even if we don't have to call ReturnToManagedSpace, still need to pop r0-r3 + + mov r0, r10 + bl |?g_ReturnToManagedSpace@Class_System_GCs_Transitions@@SAXPAUStruct_Microsoft_Singularity_ThreadContext@@@Z| +popFastPath + ldmia sp, {r0-r3} + + ;; TransitionRecord *oldRecord(r8) = newRecord(r9)->oldTransitionRecord + ldr r7, [r9, #|Struct_System_GCs_CallStack_TransitionRecord___oldTransitionRecord|] + + ;; currentThread(r10) = oldRecord(r8) + str r7, [r10, #|Struct_Microsoft_Singularity_ThreadContext___stackMarkers|] + + ;; Restore callee-save registers from the transition record. + ;; Since the transition record is located immediately below the FP, + ;; we index off the FP rather than the pointer to the struct itself. + add sp, sp, #16 + ldmdb fp, {r4-r11} + ldmia sp!, {lr} + ;; Restore registers r7-r10 (and the ummodified r11) + ldmdb fp, {r7-r11} + ;; return + mov pc, lr + + ENTRY_END + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; void CollectBodyTransition(Thread thread, int generation): +; Save the callee-save registers in a transition record and then call +; System.GC.CollectBody(thread, generation) +; + +; +; Note: If you modify the amount of stack that this function uses, +; you MUST make update the StackBound attribute for +; System.GC.CollectBodyTransition. +; +; It is conservatively set to 128 bytes. This includes the frame size +; for the function + 88 bytes for a possible linked stack call in +; CollectBody + + EXPORT |?g_CollectBodyTransition@Class_System_GC@@SAXPAUClass_System_Threading_Thread@@H@Z| + NESTED_ENTRY "?g_CollectBodyTransition@Class_System_GC@@SAXPAUClass_System_Threading_Thread@@H@Z" + + mov r12, sp + ;; Save the two words of the arguments (only two are in use) + stmdb sp!, {r0-r1} + ;; Save the FP, SP, and the LR + stmdb sp!, {r4-r11, r12, lr} + ;; Establish the new FP + sub fp, r12, #8 + + ;; TransitionRecord transitionRecord = new TransitionRecord() + ;; TransitionRecord *newRecord(sp) = &transitionRecord + ;; The transition record includes the saves. Sub an extra 8 for the sp and lr + sub sp, fp, #|Struct_System_GCs_CallStack_TransitionRecord___SIZE| + sub sp, sp, #8 + PROLOG_END + + ;; ThreadContext* context(10) + IF :DEF:SINGULARITY_KERNEL + add r10, r0, #|Class_System_Threading_Thread___context| + ELSE ; SINGULARITY_KERNEL + ldr r10, [r0, #|Class_System_Threading_Thread___context|] + ENDIF ; SINGULARITY_KERNEL + + ;; Get the old transition record (r9) + ldr r9, [r10, #|Struct_Microsoft_Singularity_ThreadContext___stackMarkers|] + + ;; newRecord(sp)->oldTransitionRecord = oldRecord(r9) + str r9, [sp, #|Struct_System_GCs_CallStack_TransitionRecord___oldTransitionRecord|] + + ;; Set the new transition record + str sp, [r10, #|Struct_Microsoft_Singularity_ThreadContext___stackMarkers|] + + ;; newRecord(sp)->callAddr = return address of this call (lr). + str lr, [sp, #|Struct_System_GCs_CallStack_TransitionRecord___callAddr|] + ;; newRecord(sp)->stackBottom = bottom of stack frame (r12) + str r12, [sp, #|Struct_System_GCs_CallStack_TransitionRecord___stackBottom|] + ;; Thread *currentThread(r0) = System.GC.CollectBody(r0, r1) + bl |?g_CollectBody@Class_System_GC@@SAPAUClass_System_Threading_Thread@@PAU2@H@Z| + ;; TransitionRecord *newRecord(sp) = &transitionRecord + ;; TransitionRecord *oldRecord(r9) = newRecord(sp)->oldTransitionRecord + ldr r9, [sp, #|Struct_System_GCs_CallStack_TransitionRecord___oldTransitionRecord|] + + ;; Restore the old transition record + str r9, [r10, #|Struct_Microsoft_Singularity_ThreadContext___stackMarkers|] + + ;; Restore permanents, FP, SP, and return + ldmdb fp, {r4-r11, sp, pc} + + ENTRY_END + + + IF :DEF:SINGULARITY_KERNEL +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; void Thread.setStopContext(Thread t, Exception exn) +; + EXPORT |?g_setStopContext@Class_System_Threading_Thread@@SAXPAU1@PAUClass_System_Exception@@@Z| + NESTED_ENTRY "?g_setStopContext@Class_System_Threading_Thread@@SAXPAU1@PAUClass_System_Exception@@@Z" + + mov r12, sp + stmdb sp!, {r8-r11, r12, lr} + + PROLOG_END + + BREAKPOINT + ldr r8, [r0, #|Struct_Microsoft_Singularity_ThreadContext___stackMarkers|] + + ;; TODO -- Actually implement this??? + + ;; Epilog & restore all registers + ldmia sp!, {r8-r11, sp, lr} + bx lr + ENTRY_END + ENDIF ;; SINGULARITY_KERNEL + + + IF :DEF:SINGULARITY_KERNEL +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; void Processor.SwitchToThreadContext(ref ThreadContext oldContext, +; ref ThreadContext newContext); +; +; precondition: Scheduler.dispatchLock held +; + IMPORT |?g_SwitchToThreadContextNoGC@Class_Microsoft_Singularity_Processor@@SAXPAUStruct_Microsoft_Singularity_ThreadContext@@@Z| + + NESTED_ENTRY ?g_SwitchToThreadContext@Class_Microsoft_Singularity_Processor@@SAXPAUStruct_Microsoft_Singularity_ThreadContext@@0@Z + + mov r12, sp + stmdb sp!, {r4-r11, r12, lr} + sub sp, sp, #12 ;; Extra room for additional TR fields + + PROLOG_END + + ;; Store SP and LR + str r12, [sp, #|Struct_System_GCs_CallStack_TransitionRecord___stackBottom|] + str lr, [sp, #|Struct_System_GCs_CallStack_TransitionRecord___callAddr|] + + ;; Link in the new record + ldr r6, [r0, #|Struct_Microsoft_Singularity_ThreadContext___stackMarkers|] + str r6, [sp, #|Struct_System_GCs_CallStack_TransitionRecord___oldTransitionRecord|] + str sp, [r0, #|Struct_Microsoft_Singularity_ThreadContext___stackMarkers|] + + ;; Save the contexts + mov r4, r0 + mov r5, r1 + + ;; Suspend/Revive the thread around switching context + bl |?g_SuspendThread@Class_System_GCs_Transitions@@SAXPAUStruct_Microsoft_Singularity_ThreadContext@@@Z| + mov r0, r5 + bl |?g_SwitchToThreadContextNoGC@Class_Microsoft_Singularity_Processor@@SAXPAUStruct_Microsoft_Singularity_ThreadContext@@@Z| + mov r0, r4 + bl |?g_ReviveThread@Class_System_GCs_Transitions@@SAXPAUStruct_Microsoft_Singularity_ThreadContext@@@Z| + + ;; Unlink the transition record + ldr r6, [sp, #|Struct_System_GCs_CallStack_TransitionRecord___oldTransitionRecord|] + str r6, [r4, #|Struct_Microsoft_Singularity_ThreadContext___stackMarkers|] + + ;; Epilog & restore all registers + add sp, sp, #12 + ldmia sp!, {r4-r11, sp, lr} + bx lr + NESTED_END + + ENDIF ;; SINGULARITY_KERNEL + + END diff --git a/base/Imported/Bartok/runtime/singularity/native/arch/arm/lib.asm b/base/Imported/Bartok/runtime/singularity/native/arch/arm/lib.asm new file mode 100644 index 0000000..90922e0 --- /dev/null +++ b/base/Imported/Bartok/runtime/singularity/native/arch/arm/lib.asm @@ -0,0 +1,9 @@ +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; + + AREA |.text|, CODE, READONLY + + INCLUDE core.inc + + END diff --git a/base/Imported/Bartok/runtime/singularity/native/arch/x86/core.inc b/base/Imported/Bartok/runtime/singularity/native/arch/x86/core.inc new file mode 100644 index 0000000..1624280 --- /dev/null +++ b/base/Imported/Bartok/runtime/singularity/native/arch/x86/core.inc @@ -0,0 +1,17 @@ +; +; Bartok +; Copyright (C) Microsoft Corporation. All Rights Reserved. +; + +; Common include file for all assembly files. + +include hal.inc + +ifdef SINGULARITY_KERNEL +externdef ?g_setStopContext@Class_System_Threading_Thread@@SIXPAU1@PAUClass_System_Exception@@@Z:NEAR +externdef __throwBeyondMarker:NEAR +endif ; SINGULARITY_KERNEL + +; This is a nop on Singularity +ResetGuardPageOnStackOverflow MACRO + ENDM diff --git a/base/Imported/Bartok/runtime/singularity/native/arch/x86/gc.asm b/base/Imported/Bartok/runtime/singularity/native/arch/x86/gc.asm new file mode 100644 index 0000000..4fc1ea5 --- /dev/null +++ b/base/Imported/Bartok/runtime/singularity/native/arch/x86/gc.asm @@ -0,0 +1,314 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; + +include hal.inc + +PAGE_BITS EQU 12 +MASK_OWNER EQU 03h + +externdef ?g_ssbRecordWriteBarrier@Class_System_VTable@@SIXPAUUntracedPtr_void@@@Z:NEAR + +; static void __fastcall VTable.ssbRecordWriteBarrier(void*) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; pushStackMark: If a function may be called by C, push a pointer to its +; frame on to a stack at the beginning of the function. +; +; Transition record layout: +; +; (lower addresses) +; -------------------------- +; |Old stack marker record | +; -------------------------- +; |Addr of call instr | +; -------------------------- +; |Bottom of stack frame | +; -------------------------- +; |ebx | +; -------------------------- +; |edi | +; -------------------------- +; |esi | +; -------------------------- +; |ebp | +; -------------------------- +; (higher addresses); +; + +align 16 +__pushStackMark proc + ;; Free up a register + push ecx + ;; Fill the new transition record + lea ecx, [ebp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)] + ;; Stash the callee-save registers + mov [ecx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBX, ebx + mov [ecx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EDI, edi + mov [ecx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._ESI, esi + mov [ecx].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBP, ebp + ;; record.callAddr = + mov ebx, [esp+4] + mov [ecx].Struct_System_GCs_CallStack_TransitionRecord._callAddr, ebx + ;; record.stackBottom = + lea ebx, [esp+8] + mov [ecx].Struct_System_GCs_CallStack_TransitionRecord._stackBottom, ebx + +ifdef SINGULARITY + ;; Link in new transition record + CurrentThreadContext(ebx) + mov edi, [ebx].Struct_Microsoft_Singularity_ThreadContext._stackMarkers + mov [ecx].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord, edi + ;; The next instruction officially switches modes (process->kernel only). + ;; Make sure that the transition record is complete at this point. + mov [ebx].Struct_Microsoft_Singularity_ThreadContext._stackMarkers, ecx + ;; We are now officially in a different mode. + ;; Allow the GC to work while this thread is out + mov ecx, Class_System_GCs_Transitions_DormantState + Class_System_GCs_Transitions_OtherMutatorState + mov eax, Class_System_GCs_Transitions_MutatorState + Class_System_GCs_Transitions_OtherDormantState + lock cmpxchg [ebx].Struct_Microsoft_Singularity_ThreadContext._gcStates, ecx + jz pushFastPath + ;; Transitions.LeaveManagedSpace(threadContext) + mov ecx, ebx + push eax + push edx + call ?g_LeaveManagedSpace@Class_System_GCs_Transitions@@SIXPAUStruct_Microsoft_Singularity_ThreadContext@@@Z + pop edx + pop eax +pushFastPath: +else ; SINGULARITY + ;; Link in new transition record + CurrentThread(ebx) + mov edi, [ebx].Class_System_Threading_Thread._asmStackMarker + mov [ecx].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord, edi + mov [ebx].Class_System_Threading_Thread._asmStackMarker, ecx + ;; Allow the GC to work while this thread is out + mov ecx, ebx + push eax + push edx + call ?g_LeaveManagedSpace@Class_System_GCs_Transitions@@SIXPAUClass_System_Threading_Thread@@@Z + pop edx + pop eax +endif ; SINGULARITY + pop ecx + ret +__pushStackMark endp + +; +; popStackMark: pop the pointer before returning from the function +; +align 16 +__popStackMark proc + ;; Preserve caller-save registers + push eax +ifdef SINGULARITY + CurrentThreadContext(ebx) + ;; NOTE: replacing the following two instructions with a "test" + ;; instruction has a 1-cycle penalty! + mov edi, [ebx].Struct_Microsoft_Singularity_ThreadContext._gcStates + and edi, Class_System_GCs_Transitions_MutatorState + jnz popFastPath + push ecx + push edx + mov ecx, ebx + call ?g_ReturnToManagedSpace@Class_System_GCs_Transitions@@SIXPAUStruct_Microsoft_Singularity_ThreadContext@@@Z + pop edx + pop ecx +popFastPath: +ifndef SINGULARITY_KERNEL + cmp byte ptr [ebx].Struct_Microsoft_Singularity_ThreadContext._suspendAlert, 0 + je noAlert + push ecx + push edx + call ?g_SuspendBarrier@Struct_Microsoft_Singularity_V1_Processes_ProcessHandle@@SIXXZ + pop edx + pop ecx +noAlert: +endif ; SINGULARITY_KERNEL + ;; Unlink transition record + lea edi, [ebp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)] + mov esi, [edi].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord + ;; The next instruction officially switches modes (process<-kernel only). + mov [ebx].Struct_Microsoft_Singularity_ThreadContext._stackMarkers, esi + ;; We are now officially in a different mode. +ifdef SINGULARITY_KERNEL + ;; Assert(ebx == current context) + ;; Assert(edi == transition record in caller's frame) + ;; If we're returning to the kernel from a process that threw an + ;; exception, throw a new kernel exception: + cmp byte ptr [ebx].Struct_Microsoft_Singularity_ThreadContext._uncaughtFlag, 0 + je noProcessUncaughtException + add esp, 8 ; discard eax, retaddr + ;; push TransitionRecord.callAddr as the new return address + push [edi].Struct_System_GCs_CallStack_TransitionRecord._callAddr + ;; Restore callee-save registers + mov ebx, [edi].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBX + mov esi, [edi].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._ESI + mov ebp, [edi].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBP + mov edi, [edi].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EDI + ;; (We have to jump to the code; a "call" would push a return address + ;; that doesn't exist in the exception tables.) + jmp ?g_ThrowProcessUncaughtException@Class_Microsoft_Singularity_ProcessUncaughtException@@SIXXZ +noProcessUncaughtException: +endif ; SINGULARITY_KERNEL +else ; SINGULARITY + push ecx + push edx + CurrentThreadIndex(ecx) ; get current thread + call ?g_ReturnToManagedSpace@Class_System_GCs_Transitions@@SIPAUClass_System_Threading_Thread@@H@Z + pop edx + pop ecx + ;; Unlink transition record + lea edi, [ebp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)] + mov esi, [edi].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord + mov [eax].Class_System_Threading_Thread._asmStackMarker, esi +endif ; SINGULARITY + ;; Restore callee-save registers + mov ebx, [edi].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBX + mov esi, [edi].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._ESI + mov ebp, [edi].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBP + mov edi, [edi].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EDI + ;; Restore caller-save registers and return + pop eax + ret +__popStackMark endp + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; void CollectBodyTransition(Thread thread, int generation): +; Save the callee-save registers in a transition record and then call +; System.GC.CollectBody(thread, generation) +; + +; +; Note: If you modify the amount of stack that this function uses, +; you MUST make update the StackBound attribute for +; System.GC.CollectBodyTransition. +; +; It is conservatively set to 128 bytes. This includes the frame size +; for the function + 88 bytes for a possible linked stack call in +; CollectBody + +.code +align 16 +?g_CollectBodyTransition@Class_System_GC@@SIXPAUClass_System_Threading_Thread@@H@Z proc +; static void __fastcall GC.CollectBodyTransition(Thread, int) + ;; Prologue + push ebp + mov ebp, esp + sub esp, SIZE Struct_System_GCs_CallStack_TransitionRecord + ;; Fill the new transition record + mov eax, dword ptr [ebp+4] ; return address of this call + mov dword ptr [ebp-24], eax + lea eax, [ebp+8] ; skip pushed PC and SP + mov dword ptr [ebp-20], eax ; bottom of stack frame + mov dword ptr [ebp-16], ebx ; callee-save registers + mov dword ptr [ebp-12], edi + mov dword ptr [ebp-08], esi + mov eax, dword ptr [ebp] + mov dword ptr [ebp-04], eax ; old ebp value + ;; Link in new transition record + lea edi, dword ptr [ebp-28] ; address of new transition record +ifdef SINGULARITY +ifdef SINGULARITY_KERNEL + mov eax, dword ptr [ecx].Class_System_Threading_Thread._context.Struct_Microsoft_Singularity_ThreadContext._stackMarkers + mov dword ptr [ecx].Class_System_Threading_Thread._context.Struct_Microsoft_Singularity_ThreadContext._stackMarkers, edi +else + mov esi, dword ptr [ecx].Class_System_Threading_Thread._context + mov eax, dword ptr [esi].Struct_Microsoft_Singularity_ThreadContext._stackMarkers + mov dword ptr [esi].Struct_Microsoft_Singularity_ThreadContext._stackMarkers, edi +endif ; SINGULARITY_KERNEL +else ; SINGULARITY + mov eax, dword ptr [ecx].Class_System_Threading_Thread._asmStackMarker + mov dword ptr [ecx].Class_System_Threading_Thread._asmStackMarker, edi +endif ; SINGULARITY + mov dword ptr [edi], eax ; add in old transition record chain + ;; Call "Thread GC.CollectBody(Thread, int)" + call ?g_CollectBody@Class_System_GC@@SIPAUClass_System_Threading_Thread@@PAU2@H@Z + ;; Unlink transition record + mov edi, dword ptr [ebp-28] ; get old transition record chain +ifdef SINGULARITY +ifdef SINGULARITY_KERNEL + mov dword ptr [eax].Class_System_Threading_Thread._context.Struct_Microsoft_Singularity_ThreadContext._stackMarkers, edi +else + mov esi, dword ptr [eax].Class_System_Threading_Thread._context + mov dword ptr [esi].Struct_Microsoft_Singularity_ThreadContext._stackMarkers, edi +endif ; SINGULARITY_KERNEL +else ; SINGULARITY + mov dword ptr [eax].Class_System_Threading_Thread._asmStackMarker, edi +endif ; SINGULARITY + ;; Restore callee-save registers + mov ebx, [ebp-16] ; restore callee-save regs + mov edi, [ebp-12] + mov esi, [ebp-8] + mov ebp, [ebp-4] + add esp, (SIZE Struct_System_GCs_CallStack_TransitionRecord)+4 ; skip FP + ret +?g_CollectBodyTransition@Class_System_GC@@SIXPAUClass_System_Threading_Thread@@H@Z endp + + +ifdef SINGULARITY_KERNEL +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; void Processor.SwitchToThreadContext(ref X86_ThreadContext oldContext, +; ref X86_ThreadContext newContext); +; +; precondition: Scheduler.dispatchLock held +; +align 16 +?g_SwitchToThreadContext@Class_Microsoft_Singularity_Processor@@SIXPAUStruct_Microsoft_Singularity_ThreadContext@@0@Z proc + ;; Prologue + push ebp + mov ebp, esp + ;; TransitionRecord record; + ;; + sub esp, SIZE Struct_System_GCs_CallStack_TransitionRecord + 4 + mov [ebp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)-4], ecx + ;; record.oldTransitionRecord = oldContext.stackMarkers + mov eax, [ecx].Struct_Microsoft_Singularity_ThreadContext._stackMarkers + mov [ebp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord, eax + ;; oldContext.stackMarkers = &record + lea eax, [ebp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)] + mov [ecx].Struct_Microsoft_Singularity_ThreadContext._stackMarkers, eax + ;; record.callAddr = + mov ecx, dword ptr [ebp+4] + mov [eax].Struct_System_GCs_CallStack_TransitionRecord._callAddr, ecx + ;; record.stackBottom = + lea ecx, [ebp+8] ; Skip pushed PC and SP + mov [eax].Struct_System_GCs_CallStack_TransitionRecord._stackBottom, ecx + ;; + mov [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBX, ebx + mov [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EDI, edi + mov [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._ESI, esi + mov ecx, dword ptr [ebp] + mov [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBP, ecx + ;; Transitions.SuspendThread(oldContext) + mov ecx, [ebp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)-4] + mov edi, edx ; save newContext away in a callee-save reg + call ?g_SuspendThread@Class_System_GCs_Transitions@@SIXPAUStruct_Microsoft_Singularity_ThreadContext@@@Z + ;; Processor.SwitchToThreadContextNoGC(newContext) + mov ecx, edi + call ?g_SwitchToThreadContextNoGC@Class_Microsoft_Singularity_Processor@@SIXPAUStruct_Microsoft_Singularity_ThreadContext@@@Z + ;; Transitions.ReviveThread(oldContext) + mov ecx, [ebp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)-4] + call ?g_ReviveThread@Class_System_GCs_Transitions@@SIXPAUStruct_Microsoft_Singularity_ThreadContext@@@Z + ;; + lea eax, [ebp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)] + mov ebx, [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBX + mov edi, [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EDI + mov esi, [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._ESI + ;; oldContext.stackMarkers = record.oldTransitionRecord + mov ecx, [ebp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)-4] + mov edx, [eax].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord + mov [ecx].Struct_Microsoft_Singularity_ThreadContext._stackMarkers, edx + ;; Epilogue + mov esp, ebp + pop ebp + ret +?g_SwitchToThreadContext@Class_Microsoft_Singularity_Processor@@SIXPAUStruct_Microsoft_Singularity_ThreadContext@@0@Z endp + +endif ; SINGULARITY_KERNEL + +end diff --git a/base/Imported/Bartok/runtime/singularity/native/arch/x86/lib.asm b/base/Imported/Bartok/runtime/singularity/native/arch/x86/lib.asm new file mode 100644 index 0000000..0eb09bd --- /dev/null +++ b/base/Imported/Bartok/runtime/singularity/native/arch/x86/lib.asm @@ -0,0 +1,248 @@ +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; + +include core.inc + +externdef __throwDispatcherExplicitAddrAfterCore:NEAR + +; +; __throwDispatcherExplictAddrAfter (ecx=exception, edx=throwAddress) +; +; Description: +; This is to be used when the address of the instruction immediately after +; the one that actually threw the exception is passed as an argument. +; +; Arguments: +; ecx = pointer to exception object being thrown +; edx = address of the instruction following the one where +; the exception was thrown +; +; This is used, for example, in stack unwinding, where edx is the +; return address on the stack for the current procedure. +; +; Stack unwinding occurs when the current procedure does not have a handler +; for the routine. The idea is to pop the stack frame and treat the call +; instruction in the caller as though it threw. We only have the return +; address, though, which points to the instruction *after* the call. +; + +align 16 +__throwDispatcherExplicitAddrAfter proc + push ecx + push edx + mov ecx, edx + call ?g_IsUnlinkStack@Class_System_Exception@@SI_NPAUuintPtr@@@Z + pop edx + pop ecx + test al, al + je normal + mov eax, ecx ; save exception type + mov ecx, edx + mov edx, [ebp+4] ; save the return addr in caller + mov [ebp+4], afterUnlinkStack ; override return addr to instr after + jmp ecx ; unlink stack which saves eax, edx +afterUnlinkStack: + mov ecx, eax ; restore return addr in caller +normal: + ; Have we reached a kernel->process or process->kernel boundary? + ; Call Exception.CheckKernelProcessBoundary(esp, ebp, exception) + push ecx + push edx + push ecx ; arg 3 (exception) + mov ecx, esp ; arg 2 (ebp) + mov edx, ebp ; arg 1 (esp) + call ?g_CheckKernelProcessBoundary@Class_System_Threading_Thread@@SIPAUuintPtr@@PAU2@0PAUClass_System_Exception@@@Z + pop edx + pop ecx + ; A non-zero return value means we reached a boundary. + test eax, eax + jz normal2 +ifdef SINGULARITY_PROCESS + ;; The process's exception reached the kernel's frames. + ; Return gracefully to the kernel (i.e. jump to the kernel's + ; return address), and the kernel's popStackMark + ; will check uncaughtFlag and throw a new exception. + jmp edx +endif ; SINGULARITY_PROCESS +ifdef SINGULARITY_KERNEL +; There are 3 control paths that merge here: +; (1) When Thread.cs stops a process mode thread, it sets eip to point here. +; (2) When Thread.cs stops a blocked kernel thread, it sets eip to point here. +; (3) The stack unwinder falls through to this case when a kernel exception +; reaches a process's frames. +; For control path (3), a finally block puts us in a GC safe state +; just before we reach here. +; We also expect to be in the GC safe state most of the +; time for control paths (1) and (2), but +; because pushStackMark and popStackMark are not atomic operations, +; we cannot be 100% sure that we're in the GC safe state. +; eax: kernel->process or kernel->kernel marker +; ecx: exception +__throwBeyondMarker LABEL NEAR + push ecx + push eax + ; Leave the GC safe state (if we're actually in it) + call ?g_RestoreMutatorControlIfNeeded@Class_System_GCs_Transitions@@SIXXZ + pop eax + pop ecx +; eax: kernel->process or kernel->kernel marker +; ecx: exception + ; Restore state from the marker, skipping over any intermediate frames. + ; Keep the original exception in ecx. + ; Get the return address for the frame beyond the marker into edx: + mov edx, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._stackBottom + mov edx, dword ptr [edx - 4] + ; Restore the kernel's ebx, edi, esi, ebp from the marker: + mov ebx, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBX + mov edi, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EDI + mov esi, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._ESI + mov ebp, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBP + ; Keep a copy of transition record (eax) and oldTransitionRecord + push eax + push dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord + ; Read _stackBottom (the new esp) from the kernel->process marker: + mov eax, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._stackBottom + ; Save ecx, edx to new stack -- this may overwrite the transition record, + ; so don't read any more fields from the transition record after this point. + mov [eax - 4], ecx ; (exception) + mov [eax - 8], edx ; (return address) + ; Set up the two arguments to DiscardStackSegments + pop edx ; (oldTransitionRecord) + pop ecx ; (marker) + ; Restore the kernel's esp from the kernel->process marker: + lea esp, [eax - 8] + ; Free any stack segments that we skipped over: + ;; TODO: are we sure there's enough stack space to call this? + call ?g_DiscardSkippedStackSegments@Class_System_Threading_Thread@@SIXPAUStruct_System_GCs_CallStack_TransitionRecord@@0@Z + pop edx ; (return address) + pop ecx ; (exception) +endif ; SINGULARITY_KERNEL +normal2: + jmp __throwDispatcherExplicitAddrAfterCore +__throwDispatcherExplicitAddrAfter endp + + + +ifdef SINGULARITY_KERNEL +; +; void Thread.setStopContext(Thread t, Exception exn) +; + +align 16 +?g_setStopContext@Class_System_Threading_Thread@@SIXPAU1@PAUClass_System_Exception@@@Z proc + ; ecx = ecx.context + add ecx, Class_System_Threading_Thread._context + ; context.eip = __throwBeyondMarker + ; context.ecx = processStopException + ; context.eax = context.stackMarkers + mov [ecx].Struct_Microsoft_Singularity_ThreadContext._threadRecord._spill._ip, __throwBeyondMarker + mov [ecx].Struct_Microsoft_Singularity_ThreadContext._threadRecord._spill._cx, edx + mov eax, [ecx].Struct_Microsoft_Singularity_ThreadContext._stackMarkers + mov [ecx].Struct_Microsoft_Singularity_ThreadContext._threadRecord._spill._ax, eax + ret +?g_setStopContext@Class_System_Threading_Thread@@SIXPAU1@PAUClass_System_Exception@@@Z endp + +endif ; SINGULARITY_KERNEL + +?g_ZeroPages@Class_System_Buffer@@SIXPAEH@Z proc + ;; ECX = dst + ;; EDX = len (bytes) + + push ebp + mov ebp, esp + pxor mm0, mm0 +next: + movntq [ecx + 0], mm0 + movntq [ecx + 8], mm0 + movntq [ecx + 16], mm0 + movntq [ecx + 24], mm0 + movntq [ecx + 32], mm0 + movntq [ecx + 40], mm0 + movntq [ecx + 48], mm0 + movntq [ecx + 56], mm0 + add ecx, 64 + sub edx, 64 + ja next + + sfence + emms + pop ebp + ret +?g_ZeroPages@Class_System_Buffer@@SIXPAEH@Z endp + +?g_CopyPages@Class_System_Buffer@@SIXPAE0H@Z proc + ;; ECX = dst + ;; EDX = src + ;; [ebp+8] len (bytes) + + push ebp + mov ebp, esp + mov eax, [ebp + 8] + + cmp ecx, edx + js down + + ;; destination is lower than source + add ecx, eax + add edx, eax + sub ecx, 64 + sub edx, 64 + +up: + movq mm0, [edx + 0] + movq mm1, [edx + 8] + movq mm2, [edx + 16] + movq mm3, [edx + 24] + movq mm4, [edx + 32] + movq mm5, [edx + 40] + movq mm6, [edx + 48] + movq mm7, [edx + 56] + movntq [ecx + 0], mm0 + movntq [ecx + 8], mm1 + movntq [ecx + 16], mm2 + movntq [ecx + 24], mm3 + movntq [ecx + 32], mm4 + movntq [ecx + 40], mm5 + movntq [ecx + 48], mm6 + movntq [ecx + 56], mm7 + sub ecx, 64 + sub edx, 64 + sub eax, 64 + ja up + + sfence + emms + pop ebp + ret 4 + + ;; destination is higher than source +down: + movq mm0, [edx + 0] + movq mm1, [edx + 8] + movq mm2, [edx + 16] + movq mm3, [edx + 24] + movq mm4, [edx + 32] + movq mm5, [edx + 40] + movq mm6, [edx + 48] + movq mm7, [edx + 56] + movntq [ecx + 0], mm0 + movntq [ecx + 8], mm1 + movntq [ecx + 16], mm2 + movntq [ecx + 24], mm3 + movntq [ecx + 32], mm4 + movntq [ecx + 40], mm5 + movntq [ecx + 48], mm6 + movntq [ecx + 56], mm7 + add ecx, 64 + add edx, 64 + sub eax, 64 + ja down + + sfence + emms + pop ebp + ret 4 +?g_CopyPages@Class_System_Buffer@@SIXPAE0H@Z endp + +end diff --git a/base/Imported/Bartok/runtime/verified/GCs/MiniCopyingRegions.bpl b/base/Imported/Bartok/runtime/verified/GCs/MiniCopyingRegions.bpl new file mode 100644 index 0000000..dcdf0d1 --- /dev/null +++ b/base/Imported/Bartok/runtime/verified/GCs/MiniCopyingRegions.bpl @@ -0,0 +1,239 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +function{:expand false} T(i:int) returns (bool) { true } +const NO_ABS:int, memLo:int, memMid:int, memHi:int; +const MAP_NO_ABS:[int]int; +axiom (forall i:int::{T(i)} T(i) ==> MAP_NO_ABS[i] == NO_ABS); +axiom 0 < memLo && memLo <= memMid && memMid <= memHi; +function memAddr(i:int) returns (bool) { memLo <= i && i < memHi } + +var Mem:[int,int]int, FwdPtr:[int]int; +var $toAbs:[int]int, $AbsMem:[int,int]int; +var $r1:[int]int, $r2:[int]int; + +// Fromspace ranges from Fi to Fl, where Fk..Fl is empty +// Tospace ranges from Ti to Tl, where Tk..Tl is empty +var Fi:int; +var Fk:int; +var Fl:int; +var Ti:int; +var Tj:int; +var Tk:int; +var Tl:int; + +function WellFormed($r:[int]int) returns(bool) { + (forall i1:int,i2:int::{T(i1),T(i2)} T(i1) && T(i2) ==> memAddr(i1) + && memAddr(i2) + && $r[i1] != NO_ABS + && $r[i2] != NO_ABS + && i1 != i2 + ==> $r[i1] != $r[i2]) +} + +function Pointer($r:[int]int, ptr:int, $abs:int) returns (bool) { + memAddr(ptr) && $abs != NO_ABS + && $r[ptr] == $abs +} + +function ObjInv(i:int, $rs:[int]int, $rt:[int]int, $toAbs:[int]int, $AbsMem:[int,int]int, Mem:[int,int]int) returns (bool) { + $rs[i] != NO_ABS ==> + Pointer($rt, Mem[i,0], $AbsMem[$toAbs[i],0]) + && Pointer($rt, Mem[i,1], $AbsMem[$toAbs[i],1]) +} + +function GcInv(FwdPtr:[int]int, Fi:int, Fk:int, Fl:int, Ti:int, Tj:int, Tk:int, Tl:int, + $r1:[int]int, $r2:[int]int, $toAbs:[int]int, $AbsMem:[int,int]int, Mem:[int,int]int) returns (bool) { + WellFormed($toAbs) + && memLo <= Fi && Fi <= Fk && Fk <= Fl && Fl <= memHi + && memLo <= Ti && Ti <= Tj && Tj <= Tk && Tk <= Tl && Tl <= memHi + && (Fl <= Ti || Tl <= Fi) + && (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> + ($r2[i] != NO_ABS ==> $toAbs[i] == $r2[i]) + && ($r1[i] != NO_ABS <==> Fi <= i && i < Fk) + && ($r2[i] != NO_ABS <==> Ti <= i && i < Tk) + && (Fi <= i && i < Fk ==> + (FwdPtr[i] == 0 <==> $toAbs[i] != NO_ABS) + && (FwdPtr[i] != 0 ==> Pointer($r2, FwdPtr[i], $r1[i])) + && (FwdPtr[i] == 0 ==> $toAbs[i] == $r1[i] && ObjInv(i, $r1, $r1, $toAbs, $AbsMem, Mem))) + && (Ti <= i && i < Tk ==> FwdPtr[i] == 0 && $toAbs[i] != NO_ABS && $toAbs[i] == $r2[i]) + && (Ti <= i && i < Tj ==> ObjInv(i, $r2, $r2, $toAbs, $AbsMem, Mem)) + && (Tj <= i && i < Tk ==> ObjInv(i, $r2, $r1, $toAbs, $AbsMem, Mem))) +} + +function MutatorInv(FwdPtr:[int]int, Fi:int, Fk:int, Fl:int, Ti:int, Tj:int, Tk:int, Tl:int, + $toAbs:[int]int, $AbsMem:[int,int]int, Mem:[int,int]int) returns (bool) { + WellFormed($toAbs) + && memLo <= Fi && Fi <= Fk && Fk <= Fl && Fl <= memHi + && memLo <= Ti && Ti == Tj && Tj == Tk && Tk <= Tl && Tl <= memHi + && (Fl <= Ti || Tl <= Fi) + && (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> + ObjInv(i, $toAbs, $toAbs, $toAbs, $AbsMem, Mem) + && (Fi <= i && i < Fk ==> FwdPtr[i] == 0) + && ($toAbs[i] != NO_ABS <==> Fi <= i && i < Fk)) +} + +// As a region evolves, it adds new mappings, but each mapping is +// permanent: RExtend ensures that new mappings do not overwrite old mappings. +function RExtend(rOld:[int]int, rNew:[int]int) returns (bool) +{ + (forall i:int::{rOld[i]}{rNew[i]} rOld[i] != NO_ABS ==> rOld[i] == rNew[i]) +} + +procedure forwardFromspacePtr(ptr:int, $freshAbs:int) returns(ret:int) + requires GcInv(FwdPtr, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $r1, $r2, $toAbs, $AbsMem, Mem); + requires T(ptr) && Fi <= ptr && ptr < Fk; + requires T($freshAbs) && $freshAbs != NO_ABS; + requires (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> $toAbs[i] != $freshAbs); + modifies FwdPtr, $toAbs, $r2, Tk, Mem; + ensures GcInv(FwdPtr, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $r1, $r2, $toAbs, $AbsMem, Mem); + ensures T(ret) && Pointer($r2, ret, $r1[ptr]); + ensures (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> $toAbs[i] != $freshAbs); + ensures (forall i:int::{T(i)} T(i) ==> i != old(Tk) ==> Mem[i, 0] == old(Mem)[i, 0]); + ensures (forall i:int::{T(i)} T(i) ==> i != old(Tk) ==> Mem[i, 1] == old(Mem)[i, 1]); + ensures RExtend(old($r2), $r2); +{ + if (FwdPtr[ptr] != 0) { + // object already copied + ret := FwdPtr[ptr]; + } + else { + // copy object to to-space + while (Tk >= Tl) { + // out of memory + } + assert T(ptr) && T(Tk); + ret := Tk; + Mem[ret, 0] := Mem[ptr, 0]; + Mem[ret, 1] := Mem[ptr, 1]; + FwdPtr[ret] := 0; + $toAbs[ret] := $r1[ptr]; + $r2[ret] := $r1[ptr]; + $toAbs[ptr] := NO_ABS; + FwdPtr[ptr] := ret; + Tk := Tk + 1; + } +} + +procedure GarbageCollect(root:int, $freshAbs:int) returns(newRoot:int) + requires MutatorInv(FwdPtr, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $toAbs, $AbsMem, Mem); + requires root != 0 ==> + Pointer($toAbs, root, $toAbs[root]); + requires T($freshAbs) && $freshAbs != NO_ABS; + requires (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> $toAbs[i] != $freshAbs); + modifies FwdPtr, $toAbs, $r1, $r2, Fi, Fk, Fl, Ti, Tj, Tk, Tl, Mem; + ensures MutatorInv(FwdPtr, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $toAbs, $AbsMem, Mem); + ensures root != 0 ==> + Pointer($toAbs, newRoot, old($toAbs)[root]); + ensures (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> $toAbs[i] != $freshAbs); +{ + var fwd0:int, fwd1:int, temp:int; + assert T(root); + $r1 := $toAbs; + $r2 := MAP_NO_ABS; + if (root != 0) { + call newRoot := forwardFromspacePtr(root, $freshAbs); + } + while (Tj < Tk) + invariant T(Tj) && T(root) && T(newRoot); + invariant GcInv(FwdPtr, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $r1, $r2, $toAbs, $AbsMem, Mem); + invariant root != 0 ==> + Pointer($r2, newRoot, old($toAbs)[root]); + invariant (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> $toAbs[i] != $freshAbs); + { + assert T(Mem[Tj,0]) && T(Mem[Tj,1]); + call fwd0 := forwardFromspacePtr(Mem[Tj,0], $freshAbs); + call fwd1 := forwardFromspacePtr(Mem[Tj,1], $freshAbs); + Mem[Tj,0] := fwd0; + Mem[Tj,1] := fwd1; + Tj := Tj + 1; + } + temp := Fi; + Fi := Ti; + Ti := temp; + + temp := Fl; + Fl := Tl; + Tl := temp; + + Fk := Tk; + Tj := Ti; + Tk := Ti; + + $toAbs := $r2; +} + +procedure Initialize() + modifies $toAbs, FwdPtr, Fi, Fk, Fl, Ti, Tj, Tk, Tl; + ensures MutatorInv(FwdPtr, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $toAbs, $AbsMem, Mem); + ensures WellFormed($toAbs); +{ + $toAbs := MAP_NO_ABS; + Fi := memLo; + Fk := memLo; + Fl := memMid; + Ti := memMid; + Tj := memMid; + Tk := memMid; + Tl := memHi; +} + +procedure ReadField(ptr:int, field:int) returns (val:int) + requires MutatorInv(FwdPtr, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $toAbs, $AbsMem, Mem); + requires Pointer($toAbs, ptr, $toAbs[ptr]); + requires field == 0 || field == 1; + ensures Pointer($toAbs, val, + $AbsMem[$toAbs[ptr],field]); +{ + assert T(ptr); + val := Mem[ptr,field]; +} + +procedure WriteField(ptr:int, field:int, val:int) + requires MutatorInv(FwdPtr, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $toAbs, $AbsMem, Mem); + requires Pointer($toAbs, ptr, $toAbs[ptr]); + requires Pointer($toAbs, val, $toAbs[val]); + requires field == 0 || field == 1; + modifies $AbsMem, Mem; + ensures MutatorInv(FwdPtr, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $toAbs, $AbsMem, Mem); + ensures $AbsMem == + old($AbsMem)[$toAbs[ptr],field := $toAbs[val]]; +{ + assert T(ptr) && T(val); + Mem[ptr,field] := val; + $AbsMem[$toAbs[ptr],field] := $toAbs[val]; +} + +procedure Alloc(root:int, $abs:int) returns (newRoot:int,ptr:int) + requires MutatorInv(FwdPtr, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $toAbs, $AbsMem, Mem); + requires root != 0 ==> + Pointer($toAbs, root, $toAbs[root]); + requires $abs != NO_ABS; + requires (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> $toAbs[i] != $abs); + requires $AbsMem[$abs,0] == $abs; + requires $AbsMem[$abs,1] == $abs; + modifies FwdPtr, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $toAbs, Mem, $r1, $r2; + ensures MutatorInv(FwdPtr, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $toAbs, $AbsMem, Mem); + ensures WellFormed($toAbs); + ensures root != 0 ==> + Pointer($toAbs, newRoot, old($toAbs)[root]); + ensures Pointer($toAbs, ptr, $abs); +{ + newRoot := root; + assert T(root); + if (Fk >= Fl) { + call newRoot := GarbageCollect(root, $abs); + } + while (Fk >= Fl) { + // out of memory + } + assert T(newRoot) && T(Fk); + ptr := Fk; + $toAbs[ptr] := $abs; + $r1[ptr] := $abs; + Mem[ptr,0] := ptr; + Mem[ptr,1] := ptr; + FwdPtr[ptr] := 0; + Fk := Fk + 1; +} diff --git a/base/Imported/Bartok/runtime/verified/GCs/MiniMs.bpl b/base/Imported/Bartok/runtime/verified/GCs/MiniMs.bpl new file mode 100644 index 0000000..f289608 --- /dev/null +++ b/base/Imported/Bartok/runtime/verified/GCs/MiniMs.bpl @@ -0,0 +1,219 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +function{:expand false} T(i:int) returns (bool) { true } +const NO_ABS:int, memLo:int, memHi:int; +axiom 0 < memLo && memLo <= memHi; +function memAddr(i:int) returns (bool) { memLo <= i && i < memHi } + +function Unalloc(i:int) returns(bool) { i == 0 } +function White(i:int) returns(bool) { i == 1 } +function Gray(i:int) returns(bool) { i == 2 } +function Black(i:int) returns(bool) { i == 3 } + +var Mem:[int,int]int, Color:[int]int; +var $toAbs:[int]int, $AbsMem:[int,int]int; + +function WellFormed($toAbs:[int]int) returns(bool) { + (forall i1:int,i2:int::{T(i1),T(i2)} T(i1) && T(i2) ==> memAddr(i1) + && memAddr(i2) + && $toAbs[i1] != NO_ABS + && $toAbs[i2] != NO_ABS + && i1 != i2 + ==> $toAbs[i1] != $toAbs[i2]) +} + +function Pointer($toAbs:[int]int, ptr:int, $abs:int) returns (bool) { + memAddr(ptr) && $abs != NO_ABS + && $toAbs[ptr] == $abs +} + +function ObjInv(i:int, $toAbs:[int]int, $AbsMem:[int,int]int, Mem:[int,int]int) returns (bool) { + $toAbs[i] != NO_ABS ==> + Pointer($toAbs, Mem[i,0], $AbsMem[$toAbs[i],0]) + && Pointer($toAbs, Mem[i,1], $AbsMem[$toAbs[i],1]) +} + +function GcInv(Color:[int]int, $toAbs:[int]int, $AbsMem:[int,int]int, Mem:[int,int]int) returns (bool) { + WellFormed($toAbs) + && (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> + ObjInv(i, $toAbs, $AbsMem, Mem) + && 0 <= Color[i] && Color[i] < 4 + && (Black(Color[i]) ==> !White(Color[Mem[i,0]]) + && !White(Color[Mem[i,1]])) + && ($toAbs[i] == NO_ABS <==> Unalloc(Color[i]))) +} + +function MutatorInv(Color:[int]int, $toAbs:[int]int, $AbsMem:[int,int]int, Mem:[int,int]int) returns (bool) { + WellFormed($toAbs) + && (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> + ObjInv(i, $toAbs, $AbsMem, Mem) + && 0 <= Color[i] && Color[i] < 2 + && ($toAbs[i] == NO_ABS <==> Unalloc(Color[i]))) +} + +procedure Mark(ptr:int) + requires GcInv(Color, $toAbs, $AbsMem, Mem); + requires memAddr(ptr) && T(ptr); + requires $toAbs[ptr] != NO_ABS; + modifies Color; + ensures GcInv(Color, $toAbs, $AbsMem, Mem); + ensures (forall i:int::{T(i)} T(i) ==> !Black(Color[i]) ==> + Color[i] == old(Color)[i]); + ensures !White(Color[ptr]); +{ + if (White(Color[ptr])) { + Color[ptr] := 2; // make gray + call Mark(Mem[ptr,0]); + call Mark(Mem[ptr,1]); + Color[ptr] := 3; // make black + } +} + +procedure Sweep() + requires GcInv(Color, $toAbs, $AbsMem, Mem); + requires (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> !Gray(Color[i])); + modifies Color, $toAbs; + ensures MutatorInv(Color, $toAbs, $AbsMem, Mem); + ensures (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> + (Black(old(Color)[i]) ==> $toAbs[i] != NO_ABS) + && ($toAbs[i] != NO_ABS ==> + $toAbs[i] == old($toAbs)[i])); +{ + var ptr:int; + ptr := memLo; + while (ptr < memHi) + invariant T(ptr) && memLo <= ptr && ptr <= memHi; + invariant WellFormed($toAbs); + invariant (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> + 0 <= Color[i] && Color[i] < 4 + && !Gray(Color[i]) + && (Black(old(Color)[i]) ==> + $toAbs[i] != NO_ABS + && ObjInv(i, $toAbs, $AbsMem, Mem) + && (Mem[i,0] >= ptr ==> + !White(Color[Mem[i,0]])) + && (Mem[i,1] >= ptr ==> + !White(Color[Mem[i,1]]))) + && ($toAbs[i] == NO_ABS <==> Unalloc(Color[i])) + && ($toAbs[i] != NO_ABS ==> + $toAbs[i] == old($toAbs)[i]) + && (ptr <= i ==> Color[i] == old(Color)[i]) + && (i < ptr ==> 0 <= Color[i] && Color[i] < 2) + && (i < ptr && White(Color[i]) ==> + Black(old(Color)[i]))); + { + if (White(Color[ptr])) { + Color[ptr] := 0; // deallocate + $toAbs[ptr] := NO_ABS; + } + else if (Black(Color[ptr])) { + Color[ptr] := 1; // make white + } + ptr := ptr + 1; + } +} + +procedure GarbageCollect(root:int) + requires MutatorInv(Color, $toAbs, $AbsMem, Mem); + requires root != 0 ==> + Pointer($toAbs, root, $toAbs[root]); + modifies Color, $toAbs; + ensures MutatorInv(Color, $toAbs, $AbsMem, Mem); + ensures root != 0 ==> $toAbs[root] == old($toAbs)[root]; + ensures root != 0 ==> + Pointer($toAbs, root, $toAbs[root]); + ensures (forall i:int::{T(i)} T(i) ==> memAddr(i) && $toAbs[i] != NO_ABS ==> + $toAbs[i] == old($toAbs)[i]); +{ + assert T(root); + if (root != 0) { + call Mark(root); + } + call Sweep(); +} + +procedure Initialize() + modifies $toAbs, Color; + ensures MutatorInv(Color, $toAbs, $AbsMem, Mem); + ensures WellFormed($toAbs); +{ + var ptr:int; + ptr := memLo; + while (ptr < memHi) + invariant T(ptr) && memLo <= ptr && ptr <= memHi; + invariant (forall i:int::{T(i)} T(i) ==> memLo <= i && i + $toAbs[i] == NO_ABS && Unalloc(Color[i])); + { + Color[ptr] := 0; + $toAbs[ptr] := NO_ABS; + ptr := ptr + 1; + } +} + +procedure ReadField(ptr:int, field:int) returns (val:int) + requires MutatorInv(Color, $toAbs, $AbsMem, Mem); + requires Pointer($toAbs, ptr, $toAbs[ptr]); + requires field == 0 || field == 1; + ensures Pointer($toAbs, val, + $AbsMem[$toAbs[ptr],field]); +{ + assert T(ptr); + val := Mem[ptr,field]; +} + +procedure WriteField(ptr:int, field:int, val:int) + requires MutatorInv(Color, $toAbs, $AbsMem, Mem); + requires Pointer($toAbs, ptr, $toAbs[ptr]); + requires Pointer($toAbs, val, $toAbs[val]); + requires field == 0 || field == 1; + modifies $AbsMem, Mem; + ensures MutatorInv(Color, $toAbs, $AbsMem, Mem); + ensures $AbsMem == + old($AbsMem)[$toAbs[ptr],field := $toAbs[val]]; +{ + assert T(ptr) && T(val); + Mem[ptr,field] := val; + $AbsMem[$toAbs[ptr],field] := $toAbs[val]; +} + +procedure Alloc(root:int, $abs:int) returns (newRoot:int,ptr:int) + requires MutatorInv(Color, $toAbs, $AbsMem, Mem); + requires root != 0 ==> + Pointer($toAbs, root, $toAbs[root]); + requires $abs != NO_ABS; + requires (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> $toAbs[i] != $abs); + requires $AbsMem[$abs,0] == $abs; + requires $AbsMem[$abs,1] == $abs; + modifies Color, $toAbs, Mem; + ensures MutatorInv(Color, $toAbs, $AbsMem, Mem); + ensures WellFormed($toAbs); + ensures root != 0 ==> + Pointer($toAbs, newRoot, old($toAbs)[root]); + ensures Pointer($toAbs, ptr, $abs); +{ + while (true) + invariant MutatorInv(Color, $toAbs, $AbsMem, Mem); + invariant root != 0 ==> $toAbs[root] == old($toAbs)[root]; + invariant root != 0 ==> + Pointer($toAbs, root, $toAbs[root]); + invariant (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> $toAbs[i] != $abs); + { + ptr := memLo; + while (ptr < memHi) + invariant T(ptr) && memLo <= ptr && ptr <= memHi; + { + if (Unalloc(Color[ptr])) { + Color[ptr] := 1; // make white + $toAbs[ptr] := $abs; + Mem[ptr,0] := ptr; + Mem[ptr,1] := ptr; + newRoot := root; + return; + } + ptr := ptr + 1; + } + call GarbageCollect(root); + } +} diff --git a/base/Imported/Bartok/runtime/verified/GCs/MiniMsRegions.bpl b/base/Imported/Bartok/runtime/verified/GCs/MiniMsRegions.bpl new file mode 100644 index 0000000..7160338 --- /dev/null +++ b/base/Imported/Bartok/runtime/verified/GCs/MiniMsRegions.bpl @@ -0,0 +1,236 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +function{:expand false} T(i:int) returns (bool) { true } +const NO_ABS:int, memLo:int, memHi:int; +const MAP_NO_ABS:[int]int; +axiom (forall i:int::{T(i)} T(i) ==> MAP_NO_ABS[i] == NO_ABS); +axiom 0 < memLo && memLo <= memHi; +function memAddr(i:int) returns (bool) { memLo <= i && i < memHi } + +function Unalloc(i:int) returns(bool) { i == 0 } +function White(i:int) returns(bool) { i == 1 } +function Gray(i:int) returns(bool) { i == 2 } +function Black(i:int) returns(bool) { i == 3 } + +var Mem:[int,int]int, Color:[int]int; +var $toAbs:[int]int, $AbsMem:[int,int]int; +var $r1:[int]int, $r2:[int]int; + +function WellFormed($r:[int]int) returns(bool) { + (forall i1:int,i2:int::{T(i1),T(i2)} T(i1) && T(i2) ==> memAddr(i1) + && memAddr(i2) + && $r[i1] != NO_ABS + && $r[i2] != NO_ABS + && i1 != i2 + ==> $r[i1] != $r[i2]) +} + +function Pointer($r:[int]int, ptr:int, $abs:int) returns (bool) { + memAddr(ptr) && $abs != NO_ABS + && $r[ptr] == $abs +} + +function ObjInv(i:int, $rs:[int]int, $rt:[int]int, $toAbs:[int]int, $AbsMem:[int,int]int, Mem:[int,int]int) returns (bool) { + $rs[i] != NO_ABS ==> + Pointer($rt, Mem[i,0], $AbsMem[$toAbs[i],0]) + && Pointer($rt, Mem[i,1], $AbsMem[$toAbs[i],1]) +} + +function GcInv(Color:[int]int, $r1:[int]int, $r2:[int]int, $toAbs:[int]int, $AbsMem:[int,int]int, Mem:[int,int]int) returns (bool) { + WellFormed($toAbs) + && (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> + 0 <= Color[i] && Color[i] < 4 + && ($r1[i] == NO_ABS ==> $r2[i] == NO_ABS) + && ($r2[i] != NO_ABS ==> $r1[i] == $r2[i] && $toAbs[i] == $r2[i]) + && (White(Color[i]) ==> $r1[i] != NO_ABS && $r2[i] == NO_ABS + && ObjInv(i, $r1, $r1, $toAbs, $AbsMem, Mem)) + && (Gray(Color[i]) ==> $r1[i] != NO_ABS && $r2[i] != NO_ABS + && ObjInv(i, $r1, $r1, $toAbs, $AbsMem, Mem)) + && (Black(Color[i]) ==> $r1[i] != NO_ABS && $r2[i] != NO_ABS + && ObjInv(i, $r2, $r2, $toAbs, $AbsMem, Mem)) + && ($toAbs[i] == NO_ABS <==> Unalloc(Color[i]))) +} + +function MutatorInv(Color:[int]int, $toAbs:[int]int, $AbsMem:[int,int]int, Mem:[int,int]int) returns (bool) { + WellFormed($toAbs) + && (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> + ObjInv(i, $toAbs, $toAbs, $toAbs, $AbsMem, Mem) + && 0 <= Color[i] && Color[i] < 2 + && ($toAbs[i] == NO_ABS <==> Unalloc(Color[i]))) +} + +// As a region evolves, it adds new mappings, but each mapping is +// permanent: RExtend ensures that new mappings do not overwrite old mappings. +function RExtend(rOld:[int]int, rNew:[int]int) returns (bool) +{ + (forall i:int::{rOld[i]}{rNew[i]} rOld[i] != NO_ABS ==> rOld[i] == rNew[i]) +} + +procedure Mark(ptr:int) + requires GcInv(Color, $r1, $r2, $toAbs, $AbsMem, Mem); + requires $r1 == $toAbs; + requires memAddr(ptr) && T(ptr); + requires $toAbs[ptr] != NO_ABS; + modifies Color, $r2; + ensures GcInv(Color, $r1, $r2, $toAbs, $AbsMem, Mem); + ensures (forall i:int::{T(i)} T(i) ==> !Black(Color[i]) ==> + Color[i] == old(Color)[i]); + ensures $r2[ptr] == $r1[ptr]; + ensures RExtend(old($r2), $r2); +{ + if (White(Color[ptr])) { + Color[ptr] := 2; // make gray + $r2[ptr] := $r1[ptr]; + call Mark(Mem[ptr,0]); + call Mark(Mem[ptr,1]); + Color[ptr] := 3; // make black + } +} + +procedure Sweep() + requires GcInv(Color, $r1, $r2, $toAbs, $AbsMem, Mem); + requires (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> !Gray(Color[i])); + modifies Color, $toAbs; + ensures MutatorInv(Color, $toAbs, $AbsMem, Mem); + ensures (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> + (Black(old(Color)[i]) ==> $toAbs[i] != NO_ABS) + && ($toAbs[i] != NO_ABS ==> + $toAbs[i] == old($toAbs)[i])); +{ + var ptr:int; + ptr := memLo; + while (ptr < memHi) + invariant T(ptr) && memLo <= ptr && ptr <= memHi; + invariant WellFormed($toAbs); + invariant (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> + 0 <= Color[i] && Color[i] < 4 + && !Gray(Color[i]) + && (Black(old(Color[i])) ==> + $r1[i] != NO_ABS && $r2[i] != NO_ABS + && $toAbs[i] != NO_ABS + && ObjInv(i, $r2, $r2, $toAbs, $AbsMem, Mem)) + && ($toAbs[i] == NO_ABS <==> Unalloc(Color[i])) + && ($toAbs[i] != NO_ABS ==> + $toAbs[i] == old($toAbs)[i]) + && (ptr <= i ==> Color[i] == old(Color)[i]) + && (i < ptr ==> 0 <= Color[i] && Color[i] < 2) + && (i < ptr && $r2[i] == NO_ABS ==> Unalloc(Color[i]))); + { + if (White(Color[ptr])) { + Color[ptr] := 0; // deallocate + $toAbs[ptr] := NO_ABS; + } + else if (Black(Color[ptr])) { + Color[ptr] := 1; // make white + } + ptr := ptr + 1; + } + $toAbs := $r2; +} + +procedure GarbageCollect(root:int) + requires MutatorInv(Color, $toAbs, $AbsMem, Mem); + requires root != 0 ==> + Pointer($toAbs, root, $toAbs[root]); + modifies Color, $toAbs, $r1, $r2; + ensures MutatorInv(Color, $toAbs, $AbsMem, Mem); + ensures root != 0 ==> $toAbs[root] == old($toAbs)[root]; + ensures root != 0 ==> + Pointer($toAbs, root, $toAbs[root]); + ensures (forall i:int::{T(i)} T(i) ==> memAddr(i) && $toAbs[i] != NO_ABS ==> + $toAbs[i] == old($toAbs)[i]); +{ + assert T(root); + $r1 := $toAbs; + $r2 := MAP_NO_ABS; + if (root != 0) { + call Mark(root); + } + call Sweep(); +} + +procedure Initialize() + modifies $toAbs, Color; + ensures MutatorInv(Color, $toAbs, $AbsMem, Mem); + ensures WellFormed($toAbs); +{ + var ptr:int; + ptr := memLo; + while (ptr < memHi) + invariant T(ptr) && memLo <= ptr && ptr <= memHi; + invariant (forall i:int::{T(i)} T(i) ==> memLo <= i && i + $toAbs[i] == NO_ABS && Unalloc(Color[i])); + { + Color[ptr] := 0; + $toAbs[ptr] := NO_ABS; + ptr := ptr + 1; + } +} + +procedure ReadField(ptr:int, field:int) returns (val:int) + requires MutatorInv(Color, $toAbs, $AbsMem, Mem); + requires Pointer($toAbs, ptr, $toAbs[ptr]); + requires field == 0 || field == 1; + ensures Pointer($toAbs, val, + $AbsMem[$toAbs[ptr],field]); +{ + assert T(ptr); + val := Mem[ptr,field]; +} + +procedure WriteField(ptr:int, field:int, val:int) + requires MutatorInv(Color, $toAbs, $AbsMem, Mem); + requires Pointer($toAbs, ptr, $toAbs[ptr]); + requires Pointer($toAbs, val, $toAbs[val]); + requires field == 0 || field == 1; + modifies $AbsMem, Mem; + ensures MutatorInv(Color, $toAbs, $AbsMem, Mem); + ensures $AbsMem == + old($AbsMem)[$toAbs[ptr],field := $toAbs[val]]; +{ + assert T(ptr) && T(val); + Mem[ptr,field] := val; + $AbsMem[$toAbs[ptr],field] := $toAbs[val]; +} + +procedure Alloc(root:int, $abs:int) returns (newRoot:int,ptr:int) + requires MutatorInv(Color, $toAbs, $AbsMem, Mem); + requires root != 0 ==> + Pointer($toAbs, root, $toAbs[root]); + requires $abs != NO_ABS; + requires (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> $toAbs[i] != $abs); + requires $AbsMem[$abs,0] == $abs; + requires $AbsMem[$abs,1] == $abs; + modifies Color, $toAbs, Mem, $r1, $r2; + ensures MutatorInv(Color, $toAbs, $AbsMem, Mem); + ensures WellFormed($toAbs); + ensures root != 0 ==> + Pointer($toAbs, newRoot, old($toAbs)[root]); + ensures Pointer($toAbs, ptr, $abs); +{ + while (true) + invariant MutatorInv(Color, $toAbs, $AbsMem, Mem); + invariant root != 0 ==> $toAbs[root] == old($toAbs)[root]; + invariant root != 0 ==> + Pointer($toAbs, root, $toAbs[root]); + invariant (forall i:int::{T(i)} T(i) ==> memAddr(i) ==> $toAbs[i] != $abs); + { + ptr := memLo; + while (ptr < memHi) + invariant T(ptr) && memLo <= ptr && ptr <= memHi; + { + if (Unalloc(Color[ptr])) { + Color[ptr] := 1; // make white + $toAbs[ptr] := $abs; + Mem[ptr,0] := ptr; + Mem[ptr,1] := ptr; + newRoot := root; + return; + } + ptr := ptr + 1; + } + call GarbageCollect(root); + } +} diff --git a/base/Imported/Bartok/runtime/verified/GCs/README.txt b/base/Imported/Bartok/runtime/verified/GCs/README.txt new file mode 100644 index 0000000..b74a97b --- /dev/null +++ b/base/Imported/Bartok/runtime/verified/GCs/README.txt @@ -0,0 +1,77 @@ +This directory contains BoogiePL code for these verified garbage collectors: + - MiniMs.bpl: + miniature verified mark-sweep collector + - MiniMsRegions.bpl: + miniature verified mark-sweep collector, using region-based invariants + - MiniCopyingRegions.bpl: + miniature verified copying collector, using region-based invariants + - VerifiedCopyingCollector.bpl: + practical verified copying collector (x86 code encoded in BoogiePL) + - VerifiedMarkSweepCollector.bpl: + practical verified mark-sweep collector (x86 code encoded in BoogiePL) + +These can be verified with the April 2008 release of Boogie/Z3, available in the +April 2008 release of Spec# at: + + http://research.microsoft.com/specsharp/ + +To verify the collectors, type: + +\Spec#\bin\Boogie.exe /noinfer MiniMs.bpl +\Spec#\bin\Boogie.exe /noinfer MiniMsRegions.bpl +\Spec#\bin\Boogie.exe /noinfer MiniCopyingRegions.bpl +\Spec#\bin\Boogie.exe /bv:z /noinfer TrustedBitVectorsBuiltin.bpl VerifiedBitVectorsBuiltin.bpl VerifiedBitVectorsBuiltinImpl.bpl +\Spec#\bin\Boogie.exe /bv:z /noinfer Trusted.bpl TrustedBitVectors.bpl VerifiedBitVectorsBuiltin.bpl VerifiedBitVectors.bpl VerifiedBitVectorsImpl.bpl +\Spec#\bin\Boogie.exe /noinfer Trusted.bpl VerifiedBitVectors.bpl VerifiedCommon.bpl VerifiedCopyingCollector.bpl +\Spec#\bin\Boogie.exe /noinfer Trusted.bpl VerifiedBitVectors.bpl VerifiedCommon.bpl VerifiedMarkSweepCollector.bpl + +For example, verifying MiniMs.bpl should generate the following Boogie output: + + Boogie program verifier version 0.90, Copyright (c) 2003-2008, Microsoft. + + Boogie program verifier finished with 7 verified, 0 errors + + + +NOTES: + +The collectors lack some of the features supported by Bartok's native run-time +system. In particular, they only collect single-threaded programs, lack full +support for arrays of structs of pointers, and lack support for unsafe code +(e.g. pinning). + +We don't yet have a Singularity-compatible version of the small runtime system +that hosts the verified garbage collectors, so the verified collectors don't yet +run on Singularity. We hope to implement at least a limited +Singularity-compatible version in the not-too-distant future. + +The collectors currently assume that Boogie functions are "expand false" by +default. This default may change in future versions of Boogie, in which case +the function definitions in the collectors will need to be marked explicitly +as "expand false". + +Our BoogiePL-to-MASM translator (used for VerifiedCopyingCollector.bpl, +VerifiedMarkSweepCollector.bpl, and VerifiedCommon.bpl) enforces on the +following variable name conventions, where x and X represent any sequence of +letters, digits, and underscores, whose first non-underscore character is a +lower-case letter (for x) or uppercase letter (for X): + - X is a physical global variable, of type int, or a procedure name + - x is a physical local variable, of type int, or an inline procedure name + - $X is a ghost variable, of any type, which the untrusted code may read + - $x is a ghost variable, of any type, which the untrusted code may read/write + - ?X is an integer constant + - ?x is a const variable containing an integer constant +The translator signals an error if the untrusted code tries to write to +$X, ?x or ?X. In addition, the translator signals an error if the untrusted +code tries to write to the esp register, except as part of a procedure call +or return. Note that pure function names and pure function parameters are not +bound by these naming conventions. + +Boogie verifies the collectors (and their allocators), but we have not (yet) +used Boogie to verify mutators. The mutator is in charge of choosing abstract +nodes (when calling the allocation procedures) and defining the properties of +abstract nodes, such as numFields and $AbsMem. To ensure that the collector +can't replace non-GC pointer values (e.g. null, unmanaged pointers) +with GC pointers, the mutator should choose non-word integers to represent +abstract nodes. + diff --git a/base/Imported/Bartok/runtime/verified/GCs/Trusted.bpl b/base/Imported/Bartok/runtime/verified/GCs/Trusted.bpl new file mode 100644 index 0000000..e44d9e7 --- /dev/null +++ b/base/Imported/Bartok/runtime/verified/GCs/Trusted.bpl @@ -0,0 +1,738 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +/***************************************************************************** +****************************************************************************** +**** AXIOMS AND TRUSTED DEFINITIONS +****************************************************************************** +*****************************************************************************/ + +////////////////////////////////////////////////////////////////////////////// +// TRIGGERS +////////////////////////////////////////////////////////////////////////////// + +// Triggers for quantifiers +// We could use a single trigger for all values; the distinction between the +// various triggers below is just to help the prover run fast. + +// TV is a trigger for values in general, including addresses. +function{:expand false} TV(val:int) returns (bool) { true } + +// TO is a trigger specifically for word offsets from addresses, where +// word offset n is byte offset 4 * n. +function{:expand false} TO(wordOffset:int) returns (bool) { true } + +// TSlot is a trigger for slots in sparse tags +function{:expand false} TSlot(slot:int) returns (bool) { true } + +// TT is a trigger for tables +function{:expand false} TT(table:int) returns (bool) { true } + +////////////////////////////////////////////////////////////////////////////// +// WORDS +////////////////////////////////////////////////////////////////////////////// + +// i1 <= x < i2 +function between(i1:int, i2:int, x:int) returns(bool) { i1 <= x && x < i2 } + +// valid 32-bit unsigned words +// word(i) <==> 0 <= i < 2^32 +const WORD_HI:int; // 2^32 +axiom WORD_HI >= 100; // REVIEW: should we go ahead and set WORD_HI to exactly 2^32? +function word(val:int) returns(bool) { 0 <= val && val < WORD_HI } + +// converts 2's complement 32-bit val into signed integer +function asSigned(val:int) returns(int); + +// null value(s) +const NULL: int; axiom NULL == 0; + +////////////////////////////////////////////////////////////////////////////// +// ASSEMBLY LANGUAGE +////////////////////////////////////////////////////////////////////////////// + +// invariant: word(r) for each register r +// To maintain invariant, simply check word(exp) for each assignment r := exp + +// invariant: word(r) for each word w in memory +// To maintain invariant, simply check word(exp) for each store of exp to memory + +var eax:int; +var ebx:int; +var ecx:int; +var edx:int; +var esi:int; +var edi:int; +var ebp:int; +var esp:int; + +function neg (int) returns (int); +function and (int, int) returns (int); +function or (int, int) returns (int); +function xor (int, int) returns (int); +function shl (int, int) returns (int); +function shr (int, int) returns (int); + +function{:expand false} TVM(a:int, b:int) returns(bool) { true } +function Mult(a:int, b:int) returns(int); +axiom (forall a:int, b:int::{TVM(a, b)} Mult(a, b) == a * b); + +// REVIEW: one would hope that this axiom is derivable from +// Mult(a, b) == a * b, using b = b1 + b2, but Z3 couldn't seem to do it yet: +function{:expand false} TVM3(a:int, b1:int, b2:int) returns(bool) { true } +axiom (forall a:int, b1:int, b2:int::{TVM3(a, b1, b2)} Mult(a, b1 + b2) == a * (b1 + b2)); + +procedure Add(x:int, y:int) returns(ret:int); + requires word(x + y); + ensures ret == x + y; + +procedure Sub(x:int, y:int) returns(ret:int); + requires word(x - y); + ensures ret == x - y; + +procedure Mul(x:int, y:int) returns(ret:int, hi:int); + requires word(x * y); + ensures ret == x * y; + ensures ret == Mult(x, y); + +// Note: we only support 32-bit division, so the upper 32 bits must be 0 +procedure Div(x:int, zero:int, y:int) returns(ret:int, rem:int); + requires zero == 0; + requires y != 0; + ensures word(ret); + ensures word(rem); + ensures ret == x / y && ret * y + rem == x; + ensures rem == x % y && rem < y; + +procedure Not(x:int) returns(ret:int); + ensures ret == neg(x); + ensures word(ret); + +procedure And(x1:int, x2:int) returns(ret:int); + ensures ret == and(x1, x2); + ensures word(ret); + +procedure Or(x1:int, x2:int) returns(ret:int); + ensures ret == or(x1, x2); + ensures word(ret); + +procedure Xor(x1:int, x2:int) returns(ret:int); + ensures ret == xor(x1, x2); + ensures word(ret); + +procedure Shl(x1:int, x2:int) returns(ret:int); + requires x2 < 32; + ensures ret == shl(x1, x2); + ensures word(ret); + +procedure Shr(x1:int, x2:int) returns(ret:int); + requires x2 < 32; + ensures ret == shr(x1, x2); + ensures word(ret); + +// run-time overflow checked +procedure AddChecked(x:int, y:int) returns(ret:int); + ensures word(x + y); + ensures ret == x + y; + +// run-time overflow checked +procedure SubChecked(x:int, y:int) returns(ret:int); + ensures word(x - y); + ensures ret == x - y; + +// run-time overflow checked +procedure MulChecked(x:int, y:int) returns(ret:int, hi:int); + ensures word(x * y); + ensures word(Mult(x, y)); + ensures ret == x * y; + ensures ret == Mult(x, y); + +procedure Lea(addr:int) returns(ret:int); + requires word(addr); + ensures ret == addr; + +procedure LeaUnchecked(addr:int) returns(ret:int); + ensures word(ret); + +// REVIEW: add more general support for signed arithmetic? +procedure LeaSignedIndex(base:int, scale:int, index:int, offset:int) returns(ret:int); + requires scale == 1 || scale == 2 || scale == 4 || scale == 8; + requires word(base + scale * asSigned(index) + offset); + ensures ret == base + scale * asSigned(index) + offset; + +procedure DebugBreak(); + ensures false; // DebugBreak never returns. + +////////////////////////////////////////////////////////////////////////////// +// READ-ONLY MEMORY +////////////////////////////////////////////////////////////////////////////// + +function roS8(ptr:int) returns(int); +function roU8(ptr:int) returns(int); +function roS16(ptr:int) returns(int); +function roU16(ptr:int) returns(int); +function ro32(ptr:int) returns(int); + +function inRo(ptr:int, size:int) returns(bool); + +// read and zero-extend 8 bits +procedure RoLoadU8(ptr:int) returns (val:int); + requires inRo(ptr, 1); + ensures word(val); + ensures val == roU8(ptr); + +// read and sign-extend 8 bits +procedure RoLoadS8(ptr:int) returns (val:int); + requires inRo(ptr, 1); + ensures word(val); + ensures asSigned(val) == roS8(ptr); + +// read and zero-extend 16 bits +procedure RoLoadU16(ptr:int) returns (val:int); + requires inRo(ptr, 2); + ensures word(val); + ensures val == roU16(ptr); + +// read and sign-extend 16 bits +procedure RoLoadS16(ptr:int) returns (val:int); + requires inRo(ptr, 2); + ensures word(val); + ensures asSigned(val) == roS16(ptr); + +procedure RoLoad32(ptr:int) returns (val:int); + requires inRo(ptr, 4); + ensures word(val); + ensures val == ro32(ptr); + +////////////////////////////////////////////////////////////////////////////// +// GC-CONTROLLED MEMORY +////////////////////////////////////////////////////////////////////////////// + +// valid gc-controlled addresses (must be disjoint from null values) +// warning: because of interior pointers, ?gcHi must be a 32-bit word +// (it can equal 2^32 - 1, but not 2^32) +const ?gcLo: int; +const ?gcHi: int; +axiom NULL < ?gcLo; +axiom ?gcLo <= ?gcHi; +axiom ?gcHi < WORD_HI; +function gcAddr(i:int) returns (bool) {?gcLo <= i && i < ?gcHi} +function gcAddrEx(i:int) returns (bool) {?gcLo <= i && i <= ?gcHi} + +// Aligned(i) <==> i is a multiple of 4 +function Aligned(i:int) returns(bool); +axiom (forall i:int, j:int::{TV(i), TO(j)} TV(i) && TO(j) ==> Aligned(i) == Aligned(i + 4 * j)); +axiom Aligned(?gcLo); +axiom Aligned(?gcHi); + +// $GcMem[i] = data at address i, if gcAddr(i) and Aligned(i). +var $GcMem:[int]int; // Do not modify directly! Use procedures below. + +// Read a word from gc-controlled memory +procedure GcLoad(ptr:int) returns (val:int); + requires TV(ptr); + requires gcAddr(ptr); + requires Aligned(ptr); + ensures word(val); + ensures TV(val); + ensures val == $GcMem[ptr]; + +// Write a word to gc-controlled memory +procedure GcStore(ptr:int, val:int); + requires gcAddr(ptr); + requires Aligned(ptr); + requires word(val); + requires TV(ptr) && TV(val); + modifies $GcMem; + ensures $GcMem == old($GcMem)[ptr := val]; + +// MAP_ZERO maps all addresses to value zero. +const MAP_ZERO:[int]int; +axiom (forall i:int::{TV(i)} TV(i) ==> MAP_ZERO[i] == 0); + +////////////////////////////////////////////////////////////////////////////// +// ABSTRACT NODES +////////////////////////////////////////////////////////////////////////////// + +// The mutator controls an abstract graph consisting of abstract nodes, +// which contain abstract values. These abstract values may be +// abstract primitive values or abstract edges pointing to abstract nodes. + +// Abstract values are represented by integers. For any integer A, +// we may interpret A as an abstract primitive value or an abstract pointer: +// - the abstract primitive value A represents the concrete integer A +// - the abstract pointer A may be one of the following: +// - NO_ABS, representing no value (used for partial maps) +// - any other value, representing a node in the abstract graph +// Any primitive value A will satisfy word(A). To avoid confusion +// between abstract primitive and abstract pointer values, the +// mutator should choose abstract pointer values A such that !word(A). + +const NO_ABS:int; // the value "none" + +// Each abstract object node A has some number of fields, +// which is chosen when A is allocated, and never changes afterwards. +function numFields(abs:int) returns (int); + +// $AbsMem represents of the abstract graph's edges: for abstract node A +// and field j, $AbsMem[A][j] is the abstract node pointed to by A's field j. +// Do not modify $AbsMem directly! $AbsMem is controlled by the mutator. +var $AbsMem:[int][int]int; + +////////////////////////////////////////////////////////////////////////////// +// REACHABILITY +////////////////////////////////////////////////////////////////////////////// + +type Time; +var $Time:Time; + +// reached(A,T) means that the GC has reached abstract node A at some time +// after the initial time T. Initially (at time T), the mutator will +// say that reached(root, T). After that, the GC calls the "reach" +// ghost procedure to generate reached(A, T) for other A. +function reached(a:int, t:Time) returns (bool); + +// If we've reached A, and A points to A', then reach A'. +// Note: this depends on $AbsMem being defined by the mutator. The GC +// should not write to $AbsMem directly (if it did, then it could reach +// anything, trivially). +procedure reach($a:int, $j:int, $t:Time); + requires reached($a, $t); + requires $AbsMem[$a][$j] != NO_ABS; + ensures reached($AbsMem[$a][$j], $t); + +////////////////////////////////////////////////////////////////////////////// +// POINTERS AND VALUES +////////////////////////////////////////////////////////////////////////////// + +function Pointer(rev:[int]int, ptr:int, abs:int) returns (bool) +{ + gcAddr(ptr) && Aligned(ptr) && abs != NO_ABS && rev[ptr] == abs // AbsMapped(ptr,rev,abs) +} + +// An interior pointer ("interiorPtr") points to an actual +// object address ("ptr") plus some offset ("offset"): +// !nullAddr(interiorPtr) ==> interiorPtr == ptr + offset && 0 <= offset && offset <= 4 * numFields($toAbs[ptr - 4]) - 4; +function InteriorValue(isPtr:bool, rev:[int]int, val:int, abs:int, offset:int) returns (bool) +{ + (isPtr && word(val) && gcAddrEx(val) && Pointer(rev, val - 4 - offset, abs) + && !word(abs) + && 0 <= offset && offset <= 4 * numFields(abs) - 4) + || (isPtr && word(val) && !gcAddrEx(val) && abs == val) + || (!isPtr && word(val) && abs == val) +} + +function Value(isPtr:bool, rev:[int]int, val:int, abs:int) returns (bool) +{ + InteriorValue(isPtr, rev, val, abs, 0) +} + +////////////////////////////////////////////////////////////////////////////// +// BARTOK INTERFACE +////////////////////////////////////////////////////////////////////////////// + +function getBit(x:int, i:int) returns(bool) { 1 == and(shr(x, i), 1) } +function getNib(x:int, i:int) returns(int) { and(shr(x, i), 15) } + +const ?SPARSE_TAG:int; axiom ?SPARSE_TAG == 1; +const ?DENSE_TAG:int; axiom ?DENSE_TAG == 3; +const ?PTR_VECTOR_TAG:int; axiom ?PTR_VECTOR_TAG == 5; +const ?OTHER_VECTOR_TAG:int; axiom ?OTHER_VECTOR_TAG == 7; +const ?PTR_ARRAY_TAG:int; axiom ?PTR_ARRAY_TAG == 9; +const ?OTHER_ARRAY_TAG:int; axiom ?OTHER_ARRAY_TAG == 11; +const ?STRING_TAG:int; axiom ?STRING_TAG == 13; + +function isOtherTag(t:int) returns(bool) +{ + !( + t == ?SPARSE_TAG || t == ?DENSE_TAG + || t == ?PTR_VECTOR_TAG || t == ?OTHER_VECTOR_TAG + || t == ?PTR_ARRAY_TAG || t == ?OTHER_ARRAY_TAG + || t == ?STRING_TAG + ) +} + +function isVarSize(t:int) returns(bool) +{ + t == ?PTR_VECTOR_TAG || t == ?OTHER_VECTOR_TAG + || t == ?PTR_ARRAY_TAG || t == ?OTHER_ARRAY_TAG + || t == ?STRING_TAG +} + +function isReadonlyField(t:int, j:int) returns(bool) +{ + (j == 1) + || (t == ?PTR_VECTOR_TAG && j == 2) + || (t == ?OTHER_VECTOR_TAG && j == 2) + || (t == ?PTR_ARRAY_TAG && (j == 2 || j == 3)) + || (t == ?OTHER_ARRAY_TAG && (j == 2 || j == 3)) + || (t == ?STRING_TAG && j == 2) +} + +const ?STRING_VTABLE:int; + +const ?VT_MASK:int; axiom ?VT_MASK == 60; +const ?VT_BASE_LENGTH:int; axiom ?VT_BASE_LENGTH == 52; +const ?VT_ARRAY_ELEMENT_SIZE:int; axiom ?VT_ARRAY_ELEMENT_SIZE == 44; +const ?VT_ARRAY_ELEMENT_CLASS:int; axiom ?VT_ARRAY_ELEMENT_CLASS == 40; +const ?VT_ARRAY_OF:int; axiom ?VT_ARRAY_OF == 36; + +function mask(vt:int) returns(int) { ro32(vt + ?VT_MASK) } +function tag(vt:int) returns(int) { and(mask(vt), 15) } +function baseLength(vt:int) returns(int) { ro32(vt + ?VT_BASE_LENGTH) } +function arrayElementSize(vt:int) returns(int) { ro32(vt + ?VT_ARRAY_ELEMENT_SIZE) } +function arrayElementClass(vt:int) returns(int) { ro32(vt + ?VT_ARRAY_ELEMENT_CLASS) } +function arrayOf(vt:int) returns(int) { ro32(vt + ?VT_ARRAY_OF) } + +function baseWords(vt:int) returns(int) { shr(baseLength(vt), 2) } +function arrayElementWords(vt:int) returns(int) { shr(arrayElementSize(vt), 2) } + +const ?TYPE_STRUCT:int; axiom ?TYPE_STRUCT == 3; + +// true ==> field j is pointer +// false ==> field j is primitive +function VFieldPtr(abs:int, f:int) returns(bool); + +function fieldToSlot(vt:int, j:int) returns(k:int); + +// field 0 is the first non-header field +function Sparse1(abs:int, vt:int, j:int, field:int) returns(bool) +{ + VFieldPtr(abs, j) == (fieldToSlot(vt, field) != 0) + && (fieldToSlot(vt, field) != 0 ==> between(1, 8, fieldToSlot(vt, field)) + && getNib(mask(vt), 4 * fieldToSlot(vt, field)) - 1 == field) +} + +function Sparse2(vt:int, nFields:int) returns(bool) +{ + (forall k:int::{TSlot(k)} TSlot(k) ==> 1 <= k && k < 8 && getNib(mask(vt), 4 * k) != 0 ==> + between(0, nFields, getNib(mask(vt), 4 * k) - 1) + && fieldToSlot(vt, getNib(mask(vt), 4 * k) - 1) == k) +} + +function{:expand false} TVT(abs:int, vt:int) returns(bool) { true } +function VTable(abs:int, vt:int) returns(bool); +axiom (forall abs:int, vt:int::{TVT(abs, vt)} VTable(abs, vt) <==> +( + !VFieldPtr(abs, 0) // REVIEW: is this redundant? + && !VFieldPtr(abs, 1) // REVIEW: is this redundant? + && (tag(vt) == ?DENSE_TAG ==> (forall j:int::{TO(j)} TO(j) ==> 2 <= j && j < numFields(abs) ==> + VFieldPtr(abs, j) == (j < 30 && getBit(mask(vt), 2 + j))) // REVIEW: is the "j < 30" redundant? + ) + && (tag(vt) == ?SPARSE_TAG ==> + (forall j:int::{TO(j)} TO(j) ==> Sparse1(abs, vt, j, j - 2)) + && Sparse2(vt, numFields(abs) - 2) + ) + && (tag(vt) == ?STRING_TAG ==> + (forall j:int::{TO(j)} TO(j) ==> !VFieldPtr(abs, j)) + ) + && (tag(vt) == ?PTR_VECTOR_TAG ==> + between(3, numFields(abs), baseWords(vt)) + && (forall j:int::{TO(j)} TO(j) ==> (baseWords(vt) <= j && j < numFields(abs) <==> VFieldPtr(abs, j))) + ) + && (tag(vt) == ?OTHER_VECTOR_TAG ==> + !VFieldPtr(abs, 2) + && inRo(arrayElementClass(vt) + ?VT_MASK, 4) // REVIEW + && between(3, numFields(abs), baseWords(vt)) + && (arrayOf(vt) != ?TYPE_STRUCT ==> (forall j:int::{TO(j)} TO(j) ==> !VFieldPtr(abs, j))) + && (arrayOf(vt) == ?TYPE_STRUCT ==> !isVarSize(tag(arrayElementClass(vt)))) + && (arrayOf(vt) == ?TYPE_STRUCT && mask(arrayElementClass(vt)) == ?SPARSE_TAG ==> + (forall j:int::{TO(j)} TO(j) ==> !VFieldPtr(abs, j))) // REVIEW: redundant + && (arrayOf(vt) == ?TYPE_STRUCT && tag(arrayElementClass(vt)) == ?SPARSE_TAG ==> + (forall j:int::{TO(j)} TO(j) ==> + (VFieldPtr(abs, j) ==> baseWords(vt) <= j && j < numFields(abs)) + && (baseWords(vt) <= j && j < numFields(abs) ==> + (forall m:int::{TO(m)} TO(m) ==> + between(0, arrayElementWords(vt), j - Mult(arrayElementWords(vt), m) - baseWords(vt)) ==> + baseWords(vt) + Mult(arrayElementWords(vt), m) + arrayElementWords(vt) <= numFields(abs) + && Sparse1(abs, arrayElementClass(vt), j, j - Mult(arrayElementWords(vt), m) - baseWords(vt))))) + && arrayElementWords(vt) >= 0 + && Sparse2(arrayElementClass(vt), arrayElementWords(vt))) + ) + && (tag(vt) == ?PTR_ARRAY_TAG ==> + between(4, numFields(abs), baseWords(vt)) + && (forall j:int::{TO(j)} TO(j) ==> (baseWords(vt) <= j && j < numFields(abs) <==> VFieldPtr(abs, j))) + ) + && (tag(vt) == ?OTHER_ARRAY_TAG ==> + !VFieldPtr(abs, 2) + && !VFieldPtr(abs, 3) + && between(4, numFields(abs), baseWords(vt)) + && (arrayOf(vt) != ?TYPE_STRUCT ==> (forall j:int::{TO(j)} TO(j) ==> !VFieldPtr(abs, j))) + ) + && (isOtherTag(tag(vt)) ==> + (forall j:int::{TO(j)} TO(j) ==> + VFieldPtr(abs, j) == (fieldToSlot(vt, j) != 0) + && (fieldToSlot(vt, j) != 0 ==> between(1, 1 + ro32(mask(vt)), fieldToSlot(vt, j)) + && ro32(mask(vt) + 4 * fieldToSlot(vt, j)) + 1 == j)) + && (forall k:int::{TSlot(k)} TSlot(k) ==> 1 <= k && k < 1 + ro32(mask(vt)) ==> + inRo(mask(vt) + 4 * k, 4) + && (ro32(mask(vt) + 4 * k) != 0 ==> + between(0, numFields(abs), ro32(mask(vt) + 4 * k) + 1) + && fieldToSlot(vt, ro32(mask(vt) + 4 * k) + 1) == k)) + && inRo(mask(vt), 4) + && ro32(mask(vt)) >= 0 + ) +)); + +axiom (forall abs:int, vt:int::{TVT(abs, vt)} VTable(abs, vt) ==> + inRo(vt + ?VT_MASK, 4) + && inRo(vt + ?VT_BASE_LENGTH, 4) + && inRo(vt + ?VT_ARRAY_ELEMENT_SIZE, 4) + && inRo(vt + ?VT_ARRAY_ELEMENT_CLASS, 4) + && inRo(vt + ?VT_ARRAY_OF, 4) +); + +function pad(i:int) returns(int) +{ + and(i + 3, neg(3)) +} + +function{:expand false} TVL(abs:int) returns(bool) { true } +function ObjSize(abs:int, vt:int, nElems1:int, nElems2:int) returns(bool); +axiom (forall abs:int, vt:int, nElems1:int, nElems2:int::{TVL(abs), ObjSize(abs, vt, nElems1, nElems2)} ObjSize(abs, vt, nElems1, nElems2) <==> +( + numFields(abs) >= 2 + && (!isVarSize(tag(vt)) ==> 4 * numFields(abs) == baseLength(vt)) + && (tag(vt) == ?STRING_TAG ==> numFields(abs) >= 4 && 4 * numFields(abs) == pad(16 + 2 * nElems1)) + && (tag(vt) == ?PTR_VECTOR_TAG ==> numFields(abs) >= 3 && 4 * numFields(abs) == pad(baseLength(vt) + 4 * nElems1)) + && (tag(vt) == ?OTHER_VECTOR_TAG ==> numFields(abs) >= 3 && 4 * numFields(abs) == pad(baseLength(vt) + Mult(arrayElementSize(vt), nElems1))) + && (tag(vt) == ?PTR_ARRAY_TAG ==> numFields(abs) >= 4 && 4 * numFields(abs) == pad(baseLength(vt) + 4 * nElems2)) + && (tag(vt) == ?OTHER_ARRAY_TAG ==> numFields(abs) >= 4 && 4 * numFields(abs) == pad(baseLength(vt) + Mult(arrayElementSize(vt), nElems2))) +)); + +type $FrameLayout; +function frameLayoutArgs($FrameLayout) returns(int); +function frameLayoutLocals($FrameLayout) returns(int); +function frameHasPtr($FrameLayout, int) returns(bool); +function frameDescriptor($FrameLayout) returns(desc:int); +function frameFieldToSlot($FrameLayout, int) returns(int); + +var $FrameCount:int; +var $FrameSlice:[int]int; +var $FrameAddr:[int]int; +var $FrameLayout:[int]$FrameLayout; +var $FrameMem:[int]int; +var $FrameAbs:[int][int]int; +var $FrameOffset:[int]int; + +function inFrame(layout:$FrameLayout, j:int) returns(bool) +{ + -frameLayoutLocals(layout) <= j && j < 2 + frameLayoutArgs(layout) +} + +function{:expand false} TVF(l:$FrameLayout) returns(bool) { true } +axiom (forall l:$FrameLayout, j:int::{TVF(l),TO(j)} + (inFrame(l, 0) && !frameHasPtr(l, 0)) + && (inFrame(l, 1) && !frameHasPtr(l, 1)) + && (TO(j) && frameHasPtr(l, j) ==> inFrame(l, j)) + && (TO(j) && getBit(frameDescriptor(l), 0) && !getBit(frameDescriptor(l), 1) && and(shr(frameDescriptor(l), 6), 1023) == 0 ==> + between(0, 16, frameLayoutArgs(l)) + && frameLayoutArgs(l) == and(shr(frameDescriptor(l), 2), 15) + && (frameHasPtr(l, j) ==> 0 <= frameLayoutArgs(l) + 1 - j && frameLayoutArgs(l) - 1 - j < 16) + && (0 <= frameLayoutArgs(l) + 1 - j && frameLayoutArgs(l) - 1 - j < 16 ==> ( + (j >= 2 ==> frameHasPtr(l, j) == getBit(frameDescriptor(l), 16 + frameLayoutArgs(l) + 1 - j)) + && (j < 0 ==> frameHasPtr(l, j) == getBit(frameDescriptor(l), 16 + frameLayoutArgs(l) - 1 - j))) + )) + && (TO(j) && !getBit(frameDescriptor(l), 0) ==> inRo(frameDescriptor(l), 4)) + && (TO(j) && !getBit(frameDescriptor(l), 0) && ro32(frameDescriptor(l)) == 4096 && inFrame(l, j) ==> ( + inRo(frameDescriptor(l) + 4, 1) + && inRo(frameDescriptor(l) + 6 + frameFieldToSlot(l, j), 1) + && j == roS8(frameDescriptor(l) + 6 + frameFieldToSlot(l, j)) + && frameHasPtr(l, j) == ( + between(0, roU8(frameDescriptor(l) + 4), frameFieldToSlot(l, j)) + ) + ))); +axiom (forall l:$FrameLayout, k:int::{TVF(l),TSlot(k)} + TSlot(k) && !getBit(frameDescriptor(l), 0) && ro32(frameDescriptor(l)) == 4096 && between(0, roU8(frameDescriptor(l) + 4), k) ==> + inFrame(l, roS8(frameDescriptor(l) + 6 + k)) + && k == frameFieldToSlot(l, roS8(frameDescriptor(l) + 6 + k)) + ); + +const ?sectionCount:int; +const ?staticDataPointerBitMap:int; +const ?dataSectionEnd:int; +const ?dataSectionBase:int; + +function sectionSlice(ptr:int) returns(section:int); +function sectionBase(section:int) returns(ptr:int) { ro32(?dataSectionBase + 4 * section) } +function sectionEnd(section:int) returns(ptr:int) { ro32(?dataSectionEnd + 4 * section) } +function sectionHasPtr(section:int, j:int) returns(bool); + +function inSectionData(ptr:int) returns(bool); + +function{:expand false} TVS(s:int, j:int) returns(bool) { true } +axiom (forall s:int, j:int::{TVS(s, j)} + 0 <= s && s < ?sectionCount && 0 <= j ==> + inRo(?dataSectionBase + 4 * s, 4) + && inRo(?dataSectionEnd + 4 * s, 4) + && inRo(?staticDataPointerBitMap + 4 * s, 4) + && (sectionBase(s) + 4 * j < sectionEnd(s) ==> + inSectionData(ro32(?dataSectionBase + 4 * s) + 4 * j) + && inRo(ro32(?staticDataPointerBitMap + 4 * s) + 4 * shr(j, 5), 4) + && and(j, 31) < 32 // REVIEW: this is true, so just prove it + && sectionHasPtr(s, j) == getBit( + ro32(ro32(?staticDataPointerBitMap + 4 * s) + 4 * shr(j, 5)), + and(j, 31) + ))); + +const ?callSiteTableCount:int; +const ?codeBaseStartTable:int; +const ?returnAddressToCallSiteSetNumbers:int; +const ?callSiteSetCount:int; +const ?callSiteSetNumberToIndex:int; +const ?activationDescriptorTable:int; + +function LookupDesc(t:int, j:int) returns(int) +{ + ro32(ro32(?activationDescriptorTable + 4 * t) + 4 * + roU16(ro32(?callSiteSetNumberToIndex + 4 * t) + 2 * j)) +} + +function InTable(t:int, j:int) returns(bool) +{ + 0 <= t && t < ?callSiteTableCount && 0 <= j && j < ro32(ro32(?callSiteSetCount + 4 * t)) +} + +function RetAddrAt(t:int, j:int, retaddr:int) returns(bool) +{ + InTable(t, j) + && between(ro32(ro32(?returnAddressToCallSiteSetNumbers + 4 * t) + 4 * j), + ro32(ro32(?returnAddressToCallSiteSetNumbers + 4 * t) + 4 * (j + 1)), + retaddr - ro32(?codeBaseStartTable + 4 * t)) +} + +function{:expand false} TVFT(f:int) returns(bool) { true } + +function FrameNextInv(f:int, ra:int, nextFp:int, $FrameAddr:[int]int, $FrameLayout:[int]$FrameLayout) returns(bool); +axiom (forall f:int, ra:int, nextFp:int, $FrameAddr:[int]int, $FrameLayout:[int]$FrameLayout::{TVFT(f), FrameNextInv(f, ra, nextFp, $FrameAddr, $FrameLayout)} FrameNextInv(f, ra, nextFp, $FrameAddr, $FrameLayout) <==> +( + f >= 0 + && (f == 0 <==> !(exists t:int, j:int::{TT(t), TO(j)} TT(t) && TO(j) && RetAddrAt(t, j, ra))) + && (f > 0 ==> + $FrameAddr[f - 1] == nextFp + && (forall t:int, j:int::{TT(t), TO(j)} TT(t) && TO(j) ==> + RetAddrAt(t, j, ra) ==> frameDescriptor($FrameLayout[f - 1]) == LookupDesc(t, j))) +)); + +axiom (forall f:int::{TVFT(f)} +( + (forall t:int::{TT(t)} TT(t) ==> 0 <= t && t < ?callSiteTableCount ==> + inRo(?codeBaseStartTable + 4 * t, 4) + && inRo(?returnAddressToCallSiteSetNumbers + 4 * t, 4) + && inRo(?callSiteSetCount + 4 * t, 4) + && inRo(ro32(?callSiteSetCount + 4 * t), 4) + && inRo(?callSiteSetNumberToIndex + 4 * t, 4) + && inRo(?activationDescriptorTable + 4 * t, 4) + && (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j <= ro32(ro32(?callSiteSetCount + 4 * t)) ==> + inRo(ro32(?returnAddressToCallSiteSetNumbers + 4 * t) + 4 * j, 4) + && inRo(ro32(?callSiteSetNumberToIndex + 4 * t) + 2 * j, 2) + && inRo(ro32(?activationDescriptorTable + 4 * t) + + 4 * roU16(ro32(?callSiteSetNumberToIndex + 4 * t) + 2 * j), 4) + ) + ) +)); + +axiom (forall f:int::{TVFT(f)} +( + (forall t:int, j1:int, j2:int::{TT(t), TO(j1), TO(j2)} TT(t) && TO(j1) && TO(j2) ==> + 0 <= t && t < ?callSiteTableCount && 0 <= j1 && j1 < j2 && j2 <= ro32(ro32(?callSiteSetCount + 4 * t)) ==> + ro32(ro32(?returnAddressToCallSiteSetNumbers + 4 * t) + 4 * j1) < ro32(ro32(?returnAddressToCallSiteSetNumbers + 4 * t) + 4 * j2) + ) +)); + +// Note: we allow reads, but not writes, from $frame == $FrameCount, which +// is the garbage collector's own initial stack frame. This feature allows +// the collector to read the arguments and saved return address set +// by the mutator. It does not allow reading the collector's own data. +procedure FrameLoad($frame:int, ptr:int) returns(val:int); + requires 0 <= $frame && $frame <= $FrameCount; + requires $FrameSlice[ptr] == $frame; + ensures word(val); + ensures val == $FrameMem[ptr]; + +procedure FrameStore($frame:int, ptr:int, val:int); + requires 0 <= $frame && $frame < $FrameCount; + requires $FrameSlice[ptr] == $frame; + requires word(val); + modifies $FrameMem; + ensures $FrameMem == old($FrameMem)[ptr := val]; + +function FrameInv(f:int, l:$FrameLayout, r:[int]int, $FrameAddr:[int]int, $FrameSlice:[int]int, $FrameLayout:[int]$FrameLayout, $FrameMem:[int]int, $FrameAbs:[int][int]int, $FrameOffset:[int]int, $Time:Time) returns(bool) +{ + FrameNextInv(f, $FrameMem[$FrameAddr[f] + 4], $FrameMem[$FrameAddr[f]], $FrameAddr, $FrameLayout) + && (forall j:int::{TO(j)} TO(j) ==> inFrame(l, j) ==> + $FrameSlice[$FrameAddr[f] + 4 * j] == f + && word($FrameAddr[f] + 4 * j) + && InteriorValue(frameHasPtr(l, j), r, $FrameMem[$FrameAddr[f] + 4 * j], $FrameAbs[f][j], $FrameOffset[$FrameAddr[f] + 4 * j]) + && (frameHasPtr(l, j) && gcAddrEx($FrameMem[$FrameAddr[f] + 4 * j]) ==> reached($FrameAbs[f][j], $Time)) + ) +} + +function StackInv(r:[int]int, $FrameCount:int, $FrameAddr:[int]int, $FrameLayout:[int]$FrameLayout, + $FrameSlice:[int]int, $FrameMem:[int]int, $FrameAbs:[int][int]int, $FrameOffset:[int]int, t:Time) returns(bool) +{ + (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], r, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, t)) +} + +var $SectionMem:[int]int; +var $SectionAbs:[int][int]int; + +function SectionInv(s:int, i1:int, i2:int, r:[int]int, $SectionMem:[int]int, $SectionAbs:[int][int]int, t:Time) returns(bool) +{ + (forall j:int::{TO(j)} TO(j) ==> 0 <= j && i1 + 4 * j < i2 ==> + sectionSlice(i1 + 4 * j) == s + && Value(sectionHasPtr(s, j), r, $SectionMem[i1 + 4 * j], $SectionAbs[s][j]) + && (sectionHasPtr(s, j) && gcAddrEx($SectionMem[i1 + 4 * j]) ==> reached($SectionAbs[s][j], t)) + ) +} + +function StaticInv(r:[int]int, $SectionMem:[int]int, $SectionAbs:[int][int]int, t:Time) returns(bool) +{ + (forall s:int::{TV(s)} TV(s) ==> 0 <= s && s < ?sectionCount ==> + SectionInv(s, sectionBase(s), sectionEnd(s), r, $SectionMem, $SectionAbs, t)) +} + +procedure SectionLoad(ptr:int) returns(val:int); + requires inSectionData(ptr); + ensures word(val); + ensures val == $SectionMem[ptr]; + +procedure SectionStore(ptr:int, val:int); + requires inSectionData(ptr); + requires word(val); + modifies $SectionMem; + ensures $SectionMem == old($SectionMem)[ptr := val]; + +////////////////////////////////////////////////////////////////////////////// +// ABSTRACT AND CONCRETE GRAPHS +////////////////////////////////////////////////////////////////////////////// + +// Each integer i is considered a concrete node. +// The memory manager controls concrete nodes in memory. +// - Each abstract object node is either mapped to one concrete node or is unmapped +// - Each concrete node is either mapped to one abstract object node or is unmapped +// - If abstract object node A is mapped to concrete node C, then C is mapped to A +// Let the notation C<-->A indicate that A is mapped to C and C is mapped to A. +// Let the notation C-->none indicate that C is unmapped. +// Let the notation A-->none indicate that A is unmapped. + +// The variable $toAbs maps concrete nodes to abstract nodes, and thereby +// exactly describes all C<-->A and C-->none. The A-->none mappings are +// implied; if there is no C such that C<-->A, then A-->none. +var $toAbs:[int]int; // maps a concrete node C to an abstract node A or to "none" + +// MAP_NO_ABS has no C<-->A mappings. +const MAP_NO_ABS:[int]int; +axiom (forall i:int::{TV(i)} TV(i) ==> MAP_NO_ABS[i] == NO_ABS); + +// WellFormed($toAbs) ensures that if C1 != C2 and $toAbs[C1] != NO_ABS +// and $toAbs[C2] != NO_ABS then $toAbs[C1] != $toAbs[C2]. +function WellFormed($toAbs:[int]int) returns(bool) +{ + (forall i1:int, i2:int::{TV(i1), TV(i2)} TV(i1) && TV(i2) + && gcAddr(i1) && gcAddr(i2) && i1 != i2 && $toAbs[i1] != NO_ABS && $toAbs[i2] != NO_ABS + ==> $toAbs[i1] != $toAbs[i2]) +} + diff --git a/base/Imported/Bartok/runtime/verified/GCs/TrustedBitVectors.bpl b/base/Imported/Bartok/runtime/verified/GCs/TrustedBitVectors.bpl new file mode 100644 index 0000000..8781958 --- /dev/null +++ b/base/Imported/Bartok/runtime/verified/GCs/TrustedBitVectors.bpl @@ -0,0 +1,53 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +// Axioms about bit vectors, *not* enabling built-in Z3 support for bit vectors. + +// Note: the "/bv:z" option must still be used, because the type "bv32" appears +// below. However, because the ":bvbuiltin" flag is omitted from the +// declarations below, Z3 will treat "$add", "$sub" as uninterpreted functions +// rather than bit vector operations. This is needed for efficiency. + +// Imports: +// - Trusted.bpl + +axiom WORD_HI == 2147483647 + 2147483647 + 2; + +function $add(x:bv32, y:bv32) returns(bv32); +function $sub(x:bv32, y:bv32) returns(bv32); +function $mul(x:bv32, y:bv32) returns(bv32); +function $and(x:bv32, y:bv32) returns(bv32); +function $or(x:bv32, y:bv32) returns(bv32); +function $shr(x:bv32, y:bv32) returns(bv32); +function $shl(x:bv32, y:bv32) returns(bv32); +function $not(x:bv32) returns(bv32); +function $le(x:bv32, y:bv32) returns(bool); + +function{:expand false} TBV(b:bv32) returns(bool) { true } + +// meaning undefined if !word(i) +function B(i:int) returns(bv32); +function I(b:bv32) returns(int); + +axiom I(1bv32) == 1; + +axiom (forall i1:int, i2:int::{B(i1),B(i2)} word(i1) && word(i2) ==> (i1 == i2 <==> B(i1) == B(i2))); +axiom (forall b1:bv32, b2:bv32::{I(b1),I(b2)} b1 == b2 <==> I(b1) == I(b2)); + +axiom (forall b:bv32::{I(b)} word(I(b))); +axiom (forall b:bv32::{B(I(b))} B(I(b)) == b); +axiom (forall i:int::{I(B(i))} word(i) ==> I(B(i)) == i); + +axiom (forall b1:bv32, b2:bv32::{$add(b1, b2)}{TBV(b1),TBV(b2)} word(I(b1) + I(b2)) ==> I(b1) + I(b2) == I($add(b1, b2))); +axiom (forall b1:bv32, b2:bv32::{$sub(b1, b2)}{TBV(b1),TBV(b2)} word(I(b1) - I(b2)) ==> I(b1) - I(b2) == I($sub(b1, b2))); +axiom (forall b1:bv32, b2:bv32::{$mul(b1, b2)}{TBV(b1),TBV(b2)} word(I(b1) * I(b2)) ==> I(b1) * I(b2) == I($mul(b1, b2))); +axiom (forall b1:bv32, b2:bv32::{$le(b1, b2)}{TBV(b1),TBV(b2)} I(b1) <= I(b2) <==> $le(b1, b2)); +axiom (forall i1:int, i2:int::{and(i1, i2)} and(i1, i2) == I($and(B(i1), B(i2))) ); +axiom (forall i1:int, i2:int::{or(i1, i2)} or(i1, i2) == I($or(B(i1), B(i2))) ); +axiom (forall i1:int, i2:int::{shl(i1, i2)} shl(i1, i2) == I($shl(B(i1), B(i2))) ); +axiom (forall i1:int, i2:int::{shr(i1, i2)} shr(i1, i2) == I($shr(B(i1), B(i2))) ); +axiom (forall i:int::{neg(i)} neg(i) == I($not(B(i)))); + +axiom (forall b:bv32::{Aligned(I(b))} Aligned(I(b)) == ($and(b, 3bv32) == 0bv32)); + diff --git a/base/Imported/Bartok/runtime/verified/GCs/TrustedBitVectorsBuiltin.bpl b/base/Imported/Bartok/runtime/verified/GCs/TrustedBitVectorsBuiltin.bpl new file mode 100644 index 0000000..5911db7 --- /dev/null +++ b/base/Imported/Bartok/runtime/verified/GCs/TrustedBitVectorsBuiltin.bpl @@ -0,0 +1,30 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +// Axioms about bit vectors, using built-in Z3 support for bit vectors + +function {:bvbuiltin "bvadd"} $add(x:bv32, y:bv32) returns(bv32); +function {:bvbuiltin "bvsub"} $sub(x:bv32, y:bv32) returns(bv32); +function {:bvbuiltin "bvmul"} $mul(x:bv32, y:bv32) returns(bv32); +function {:bvbuiltin "bvand"} $and(x:bv32, y:bv32) returns(bv32); +function {:bvbuiltin "bvor"} $or(x:bv32, y:bv32) returns(bv32); +function {:bvbuiltin "bvlshr"} $shr(x:bv32, y:bv32) returns(bv32); +function {:bvbuiltin "bvshl"} $shl(x:bv32, y:bv32) returns(bv32); +function {:bvbuiltin "bvnot"} $not(x:bv32) returns(bv32); +function {:bvbuiltin "bvule"} $le(x:bv32, y:bv32) returns(bool); + +function{:expand false} TV(i:int) returns(bool) { true } +function{:expand false} TBV(b:bv32) returns(bool) { true } + +function word(i:int) returns(bool); + +// meaning undefined if !word(i) +function B(i:int) returns(bv32); +function I(b:bv32) returns(int); + +axiom (forall i1:int, i2:int::{B(i1),B(i2)} word(i1) && word(i2) ==> (i1 == i2 <==> B(i1) == B(i2))); +axiom (forall b1:bv32, b2:bv32::{I(b1),I(b2)} b1 == b2 <==> I(b1) == I(b2)); + +// i1 <= x < i2 +function between(i1:int, i2:int, x:int) returns(bool) { i1 <= x && x < i2 } diff --git a/base/Imported/Bartok/runtime/verified/GCs/VerifiedBitVectors.bpl b/base/Imported/Bartok/runtime/verified/GCs/VerifiedBitVectors.bpl new file mode 100644 index 0000000..b8b8279 --- /dev/null +++ b/base/Imported/Bartok/runtime/verified/GCs/VerifiedBitVectors.bpl @@ -0,0 +1,129 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +// NOTE: This file contains declarations of various lemmas proved by +// the implementations in VerifiedBitVectorsImpl.bpl: +// - Do not modify this file without verifying that the implementations in +// that file still prove the declarations in this file! +// - Do not add any declarations to this file without adding an +// implementation in that file! (All lemmas must be proved!) + +// Note: VerifiedBitVectorsImpl.bpl defines the following functions: +// function BitIndex(i0:int, i:int) returns(int); +// function BitZero(x:int, i0:int, i:int) returns(bool); +// function ColorIndex(i0:int, i:int) returns(int); +// function ColorGet(x:int, i0:int, i:int) returns(int); +// Anyone importing VerifiedBitVectors.bpl should treat these functions +// as uninterpreted. + +function bbvec4(a:[int]int, off:int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, g1:int, g2:int) returns(bool) +{ + (forall i:int::{TV(i)} TV(i) && i1 <= i && i < i2 && Aligned(i - i0) ==> + between(g1, g2, g1 + BitIndex(i0, i)) + && (a[aBase + (i - i0)] == off <==> BitZero(bb[g1 + BitIndex(i0, i)], i0, i)) + ) +} + +function bb2vec4(a:[int]int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, g1:int, g2:int) returns(bool) +{ + (forall i:int::{TV(i)} TV(i) && word(i - i0) && i1 <= i && i < i2 && Aligned(i - i0) ==> + between(g1, g2, g1 + ColorIndex(i0, i)) + && (a[aBase + (i - i0)] == ColorGet(bb[g1 + ColorIndex(i0, i)], i0, i)) + ) +} + +procedure __andAligned(x:int); + ensures word(x) ==> (and(x, 3) == 0 <==> Aligned(x)); + +procedure __addAligned(x:int, y:int); + ensures word(x) && word(y) && word(x + y) && Aligned(x) ==> + (Aligned(y) <==> Aligned(x + y)); + +procedure __subAligned(x:int, y:int); + ensures word(x) && word(y) && word(x - y) && Aligned(x) ==> + (Aligned(y) <==> Aligned(x - y)); + +procedure __notAligned(i:int); + requires Aligned(i); + requires word(i); + ensures !Aligned(i + 1); + ensures !Aligned(i + 2); + ensures !Aligned(i + 3); + ensures word(i + 1); + ensures word(i + 2); + ensures word(i + 3); + +procedure __initialize($unitSize:int, HeapLo:int); + requires word($unitSize * 256); + ensures BitIndex(HeapLo, HeapLo) == 0; + ensures BitIndex(HeapLo, HeapLo + 128 * $unitSize) == 4 * $unitSize; + ensures BitIndex(HeapLo, HeapLo + 256 * $unitSize) == 8 * $unitSize; + +procedure __bb4Zero(a:[int]int, off:int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, g1:int, g2:int, idx:int); + requires (forall i:int::{TV(i)} TV(i) && i1 <= i && i < i2 + 128 ==> a[aBase + (i - i0)] == off); + requires bbvec4(a, off, aBase, bb, i0, i1, i2, g1, g2); + requires word(i1 - i0) && word(i2 - i0) && word(i2 - i1) && word(i2 + 128 - i0); + requires word(idx) && word(g1); + requires Aligned(idx) && Aligned(g1); + requires i2 - i1 == 32 * (idx - g1); + requires i1 == i0; + requires between(g1, g2, idx); + ensures bbvec4(a, off, aBase, bb[idx := 0], i0, i1, i2 + 128, g1, g2); + +procedure __bb4GetBit(a:[int]int, off:int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, k:int, idx:int, bbb:int, g1:int, g2:int); + requires bbvec4(a, off, aBase, bb, i0, i1, i2, g1, g2); + requires TV(k) && word(k - i0) && i1 <= k && k < i2 && Aligned(k - i0); + requires idx == g1 + 4 * shr(k - i0, 7); + requires bbb == and(bb[idx], shl(1, and(shr(k - i0, 2), 31))); + requires word(i1 - i0) && word(i2 - i0); + ensures between(g1, g2, idx); + ensures and(shr(k - i0, 2), 31) < 32; + ensures bbb == 0 <==> a[aBase + (k - i0)] == off; + +procedure __bb4SetBit(a:[int]int, on:int, off:int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, k:int, idx:int, bbb:int, ret:[int]int, g1:int, g2:int); + requires bbvec4(a, off, aBase, bb, i0, i1, i2, g1, g2); + requires TV(k) && word(k - i0) && i1 <= k && k < i2 && Aligned(k - i0); + requires on != off; + requires idx == g1 + 4 * shr(k - i0, 7); + requires bbb == or(bb[idx], shl(1, and(shr(k - i0, 2), 31))); + requires ret == bb[idx := bbb]; + requires word(i1 - i0) && word(i2 - i0); + ensures bbvec4(a[aBase + (k - i0) := on], off, aBase, ret, i0, i1, i2, g1, g2); + ensures between(g1, g2, idx); + ensures and(shr(k - i0, 2), 31) < 32; + ensures 4 * shr(k - i0, 7) == BitIndex(i0, k); + +procedure __bb4Zero2(a:[int]int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, g1:int, g2:int, idx:int); + requires (forall i:int::{TV(i)} TV(i) && i1 <= i && i < i2 + 64 ==> a[aBase + (i - i0)] == 0); + requires bb2vec4(a, aBase, bb, i0, i1, i2, g1, g2); + requires word(i1 - i0) && word(i2 - i0) && word(i2 - i1) && word(i2 + 64 - i0); + requires word(idx) && word(g1); + requires Aligned(idx) && Aligned(g1); + requires i2 - i1 == 16 * (idx - g1); + requires i1 == i0; + requires between(g1, g2, idx); + ensures bb2vec4(a, aBase, bb[idx := 0], i0, i1, i2 + 64, g1, g2); + +procedure __bb4Get2Bit(a:[int]int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, k:int, idx:int, bbb:int, g1:int, g2:int); + requires bb2vec4(a, aBase, bb, i0, i1, i2, g1, g2); + requires TV(k) && word(k - i0) && i1 <= k && k < i2 && Aligned(k - i0); + requires idx == g1 + 4 * shr(k - i0, 6); + requires bbb == and(shr(bb[idx], and(shr(k - i0, 1), 31)), 3); + ensures a[aBase + (k - i0)] == bbb; + ensures between(g1, g2, idx); + ensures and(shr(k - i0, 1), 31) <= 31; + +procedure __bb4Set2Bit(a:[int]int, val:int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, k:int, idx:int, bbb:int, _bbb:int, ret:[int]int, g1:int, g2:int); + requires bb2vec4(a, aBase, bb, i0, i1, i2, g1, g2); + requires TV(k) && word(k - i0) && i1 <= k && k < i2 && Aligned(k - i0); + requires idx == g1 + 4 * shr(k - i0, 6); + requires 0 <= val && val <= 3; + requires bbb == and(bb[idx], neg(shl(3, and(shr(k - i0, 1), 31)))); + requires _bbb == or(bbb, shl(val, and(shr(k - i0, 1), 31))); + requires ret == bb[idx := _bbb]; + ensures bb2vec4(a[aBase + (k - i0) := val], aBase, ret, i0, i1, i2, g1, g2); + ensures between(g1, g2, idx); + ensures and(shr(k - i0, 1), 31) <= 31; + ensures 4 * shr(k - i0, 6) == ColorIndex(i0, k); + diff --git a/base/Imported/Bartok/runtime/verified/GCs/VerifiedBitVectorsBuiltin.bpl b/base/Imported/Bartok/runtime/verified/GCs/VerifiedBitVectorsBuiltin.bpl new file mode 100644 index 0000000..f971250 --- /dev/null +++ b/base/Imported/Bartok/runtime/verified/GCs/VerifiedBitVectorsBuiltin.bpl @@ -0,0 +1,145 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +// NOTE: This file contains declarations of various lemmas proved by +// the implementations in VerifiedBitVectorsBuiltinImpl.bpl: +// - Do not modify this file without verifying that the implementations in +// that file still prove the declarations in this file! +// - Do not add any declarations to this file without adding an +// implementation in that file! (All lemmas must be proved!) + +function $Aligned(b:bv32) returns(bool) +{ + $and(b, 3bv32) == 0bv32 +} + +function $bbvec4(a:[int]int, off:int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, g1:int, g2:int) returns(bool) +{ + (forall i:int::{TV(i)} TV(i) && word(i - i0) && i1 <= i && i < i2 && $Aligned(B(i - i0)) ==> + between(g1, g2, g1 + 4 * I($shr(B(i - i0), 7bv32))) + && (a[aBase + (i - i0)] == off <==> + 0bv32 == $and(B(bb[g1 + 4 * I($shr(B(i - i0), 7bv32))]), + $shl(1bv32, $and($shr(B(i - i0), 2bv32), 31bv32))))) +} + +function $bb2vec4(a:[int]int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, g1:int, g2:int) returns(bool) +{ + (forall i:int::{TV(i)} TV(i) && word(i - i0) && i1 <= i && i < i2 && $Aligned(B(i - i0)) ==> + between(g1, g2, g1 + 4 * I($shr(B(i - i0), 6bv32))) + && (B(a[aBase + (i - i0)]) == $and( + $shr(B(bb[g1 + 4 * I($shr(B(i - i0), 6bv32))]), $and($shr(B(i - i0), 1bv32), 31bv32)), + 3bv32 + ))) +} + +procedure $AndAligned(x:bv32); + ensures $and(x, 3bv32) == 0bv32 <==> $Aligned(x); + +procedure $AddAligned(x:bv32, y:bv32); + ensures $Aligned(x) ==> + ($Aligned(y) <==> $Aligned($add(x, y))); + +procedure $SubAligned(x:bv32, y:bv32); + ensures $Aligned(x) ==> + ($Aligned(y) <==> $Aligned($sub(x, y))); + +procedure $NotAligned(b:bv32); + requires $Aligned(b); + ensures !$Aligned($add(b, 1bv32)); + ensures !$Aligned($add(b, 2bv32)); + ensures !$Aligned($add(b, 3bv32)); + ensures $le(b, 4294967292bv32); + +procedure $Initialize($unitSize:bv32); + requires $le($unitSize, 16777215bv32); + ensures $shr(0bv32, 7bv32) == 0bv32; + ensures $shr($mul(128bv32, $unitSize), 7bv32) == $unitSize; + ensures $shr($mul(256bv32, $unitSize), 7bv32) == $add($unitSize, $unitSize); + +procedure _BB4Zero(a:[int]int, off:int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, g1:int, g2:int, idx:int); + requires (forall i:int::{TV(i)} TV(i) && i1 <= i && i < i2 + 128 ==> a[aBase + (i - i0)] == off); + requires $bbvec4(a, off, aBase, bb, i0, i1, i2, g1, g2); + requires $Aligned(B(idx)) && $Aligned(B(g1)); + requires B(i2 - i0) == $mul(32bv32, $sub(B(idx), B(g1))); + requires i1 == i0; + requires $le($shr(B(i2 - i0), 7bv32), 33554431bv32) && $mul(128bv32, $shr(B(i2 - i0), 7bv32)) == B(i2 - i0) ==> + idx - g1 == 4 * I($shr(B(i2 - i0), 7bv32)); + requires (forall i:int::{TV(i)} TV(i) && i2 <= i && i < i2 + 128 ==> + $le(B(i2 - i0), B(i - i0)) && $le(B(i - i0), $add(B(i2 - i0), 127bv32))); + requires between(g1, g2, idx); + requires B(0) == 0bv32; + ensures $bbvec4(a, off, aBase, bb[idx := 0], i0, i1, i2 + 128, g1, g2); + +procedure _BB4GetBit(i0:int, k:int); + ensures $le($and($shr(B(k - i0), 2bv32), 31bv32), 31bv32); + +procedure _BB4SetBit(a:[int]int, on:int, off:int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, k:int, idx:int, bbb:int, ret:[int]int, g1:int, g2:int); + requires $bbvec4(a, off, aBase, bb, i0, i1, i2, g1, g2); + requires TV(k) && word(k - i0) && i1 <= k && k < i2 && $Aligned(B(k - i0)); + requires on != off; + requires idx == g1 + 4 * I($shr(B(k - i0), 7bv32)); + requires B(bbb) == $or(B(bb[idx]), $shl(1bv32, $and($shr(B(k - i0), 2bv32), 31bv32))); + requires ret == bb[idx := bbb]; + ensures $bbvec4(a[aBase + (k - i0) := on], off, aBase, ret, i0, i1, i2, g1, g2); + ensures between(g1, g2, idx); + ensures $le($and($shr(B(k - i0), 2bv32), 31bv32), 31bv32); + +procedure _BB4Zero2(a:[int]int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, g1:int, g2:int, idx:int); + requires (forall i:int::{TV(i)} TV(i) && i1 <= i && i < i2 + 64 ==> a[aBase + (i - i0)] == 0); + requires $bb2vec4(a, aBase, bb, i0, i1, i2, g1, g2); + requires $Aligned(B(idx)) && $Aligned(B(g1)); + requires B(i2 - i0) == $mul(16bv32, $sub(B(idx), B(g1))); + requires i1 == i0; + requires $le($shr(B(i2 - i0), 6bv32), 67108863bv32) && $mul(64bv32, $shr(B(i2 - i0), 6bv32)) == B(i2 - i0) ==> + idx - g1 == 4 * I($shr(B(i2 - i0), 6bv32)); + requires (forall i:int::{TV(i)} TV(i) && i2 <= i && i < i2 + 64 ==> + $le(B(i2 - i0), B(i - i0)) && $le(B(i - i0), $add(B(i2 - i0), 63bv32))); + requires between(g1, g2, idx); + requires B(0) == 0bv32; + ensures $bb2vec4(a, aBase, bb[idx := 0], i0, i1, i2 + 64, g1, g2); + +procedure _BB4Get2Bit(i0:int, k:int); + ensures $le($and($shr(B(k - i0), 1bv32), 31bv32), 31bv32); + +procedure _BB4Set2Bit(a:[int]int, val:int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, k:int, idx:int, bbb:int, _bbb:int, ret:[int]int, g1:int, g2:int); + requires $bb2vec4(a, aBase, bb, i0, i1, i2, g1, g2); + requires TV(k) && word(k - i0) && i1 <= k && k < i2 && $Aligned(B(k - i0)); + requires idx == g1 + 4 * I($shr(B(k - i0), 6bv32)); + requires $le(B(val), 3bv32); + requires B(bbb) == $and(B(bb[idx]), $not($shl(3bv32, $and($shr(B(k - i0), 1bv32), 31bv32)))); + requires B(_bbb) == $or(B(bbb), $shl(B(val), $and($shr(B(k - i0), 1bv32), 31bv32))); + requires ret == bb[idx := _bbb]; + ensures $bb2vec4(a[aBase + (k - i0) := val], aBase, ret, i0, i1, i2, g1, g2); + ensures between(g1, g2, idx); + ensures $le($and($shr(B(k - i0), 1bv32), 31bv32), 31bv32); + +procedure $Const(); + ensures $sub(1bv32, 1bv32) == 0bv32; + ensures $add(1bv32, 1bv32) == 2bv32; + ensures $add(2bv32, 1bv32) == 3bv32; + ensures $add(2bv32, 2bv32) == 4bv32; + ensures $add(4bv32, 1bv32) == 5bv32; + ensures $add(5bv32, 1bv32) == 6bv32; + ensures $add(5bv32, 2bv32) == 7bv32; + ensures $mul(4bv32, 4bv32) == 16bv32; + ensures $add(16bv32, 16bv32) == 32bv32; + ensures $sub(32bv32, 1bv32) == 31bv32; + ensures $add(32bv32, 32bv32) == 64bv32; + ensures $sub(64bv32, 1bv32) == 63bv32; + ensures $mul(32bv32, 4bv32) == 128bv32; + ensures $sub(128bv32, 1bv32) == 127bv32; + ensures $mul(16bv32, 16bv32) == 256bv32; + ensures $add(256bv32, 256bv32) == 512bv32; + ensures $mul(256bv32, 256bv32) == 65536bv32; + ensures $sub(65536bv32, 1bv32) == 65535bv32; + ensures $mul(65536bv32, 256bv32) == 16777216bv32; + ensures $sub(16777216bv32, 1bv32) == 16777215bv32; + ensures $mul(65536bv32, 512bv32) == 33554432bv32; + ensures $sub(33554432bv32, 1bv32) == 33554431bv32; + ensures $add(33554432bv32, 33554432bv32) == 67108864bv32; + ensures $sub(67108864bv32, 1bv32) == 67108863bv32; + ensures $mul(65536bv32, 65535bv32) == 4294901760bv32; + ensures $add(4294901760bv32, 65535bv32) == 4294967295bv32; + ensures $sub(4294967295bv32, 3bv32) == 4294967292bv32; + diff --git a/base/Imported/Bartok/runtime/verified/GCs/VerifiedBitVectorsBuiltinImpl.bpl b/base/Imported/Bartok/runtime/verified/GCs/VerifiedBitVectorsBuiltinImpl.bpl new file mode 100644 index 0000000..de2a7c0 --- /dev/null +++ b/base/Imported/Bartok/runtime/verified/GCs/VerifiedBitVectorsBuiltinImpl.bpl @@ -0,0 +1,72 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +// This file contains proofs of the declarations in VerifiedBitVectorsBuiltin.bpl. +// Many proofs are totally automatic, so that the implementations below have +// empty bodies. Other proofs require assertions to guide the prover. + +// Verification requires the "/bv:z" option to enable Z3 support of bit vectors + +// Imports: +// - TrustedBitVectorsBuiltin.bpl +// Exports: +// - VerifiedBitVectorsBuiltin.bpl + +// \Spec#\bin\Boogie.exe /bv:z /noinfer TrustedBitVectorsBuiltin.bpl VerifiedBitVectorsBuiltin.bpl VerifiedBitVectorsBuiltinImpl.bpl + +implementation $AndAligned(x:bv32) +{ +} + +implementation $AddAligned(x:bv32, y:bv32) +{ +} + +implementation $SubAligned(x:bv32, y:bv32) +{ +} + +implementation $NotAligned(b:bv32) +{ +} + +implementation $Initialize($unitSize:bv32) +{ +} + +implementation _BB4Zero(a:[int]int, off:int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, g1:int, g2:int, idx:int) +{ + assert $mul(128bv32, $shr(B(i2 - i0), 7bv32)) == B(i2 - i0); + assert idx - g1 == 4 * I($shr(B(i2 - i0), 7bv32)); + assert (forall i:int::{TV(i)} TV(i) && i2 <= i && i < i2 + 128 ==> $shr(B(i - i0), 7bv32) == $shr(B(i2 - i0), 7bv32)); +} + +implementation _BB4GetBit(i0:int, k:int) +{ +} + +implementation _BB4SetBit(a:[int]int, on:int, off:int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, k:int, idx:int, bbb:int, ret:[int]int, g1:int, g2:int) +{ +} + +implementation _BB4Zero2(a:[int]int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, g1:int, g2:int, idx:int) +{ + assert $mul(64bv32, $shr(B(i2 - i0), 6bv32)) == B(i2 - i0); + assert idx - g1 == 4 * I($shr(B(i2 - i0), 6bv32)); + assert (forall i:int::{TV(i)} TV(i) && i2 <= i && i < i2 + 64 ==> $shr(B(i - i0), 6bv32) == $shr(B(i2 - i0), 6bv32)); +} + +implementation _BB4Get2Bit(i0:int, k:int) +{ +} + +implementation _BB4Set2Bit(a:[int]int, val:int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, k:int, idx:int, bbb:int, _bbb:int, ret:[int]int, g1:int, g2:int) +{ +} + +implementation $Const() +{ +} + + diff --git a/base/Imported/Bartok/runtime/verified/GCs/VerifiedBitVectorsImpl.bpl b/base/Imported/Bartok/runtime/verified/GCs/VerifiedBitVectorsImpl.bpl new file mode 100644 index 0000000..aa8ef29 --- /dev/null +++ b/base/Imported/Bartok/runtime/verified/GCs/VerifiedBitVectorsImpl.bpl @@ -0,0 +1,244 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +// This file contains proofs of the declarations in VerifiedBitVectors.bpl. +// The proofs require assertions to guide the prover. + +// Verification requires the "/bv:z" option to enable Z3 support of bit vectors + +// Imports: +// - Trusted.bpl +// - TrustedBitVectors.bpl +// - VerifiedBitVectorsBuiltin.bpl +// Exports: +// - VerifiedBitVectors.bpl + +// \Spec#\bin\Boogie.exe /bv:z /noinfer Trusted.bpl TrustedBitVectors.bpl VerifiedBitVectorsBuiltin.bpl VerifiedBitVectors.bpl VerifiedBitVectorsImpl.bpl + +function BitIndex(i0:int, i:int) returns(int) +{ + 4 * shr(i - i0, 7) +} + +function BitZero(x:int, i0:int, i:int) returns(bool) +{ + 0 == and(x, shl(1, and(shr(i - i0, 2), 31))) +} + +function ColorIndex(i0:int, i:int) returns(int) +{ + 4 * shr(i - i0, 6) +} + +function ColorGet(x:int, i0:int, i:int) returns(int) +{ + and(shr(x, and(shr(i - i0, 1), 31)), 3) +} + +procedure Const() + ensures 0 == I(0bv32); + ensures 2 == I(2bv32); + ensures 3 == I(3bv32); + ensures 4 == I(4bv32); + ensures 5 == I(5bv32); + ensures 6 == I(6bv32); + ensures 7 == I(7bv32); + ensures 16 == I(16bv32); + ensures 32 == I(32bv32); + ensures 31 == I(31bv32); + ensures 64 == I(64bv32); + ensures 63 == I(63bv32); + ensures 128 == I(128bv32); + ensures 127 == I(127bv32); + ensures 256 == I(256bv32); + ensures 16777215 == I(16777215bv32); + ensures 33554431 == I(33554431bv32); + ensures 67108863 == I(67108863bv32); +{ + call $Const(); + assert word(0); + call _S(1bv32, 1bv32); + assert word(2); + call _A(1bv32, 1bv32); + assert word(3); + call _A(2bv32, 1bv32); + assert word(4); + call _A(2bv32, 2bv32); + assert word(5); + call _A(4bv32, 1bv32); + assert word(6); + call _A(5bv32, 1bv32); + assert word(7); + call _A(5bv32, 2bv32); + assert word(16); + call _M(4bv32, 4bv32); + assert word(32); + call _A(16bv32, 16bv32); + assert word(31); + call _S(32bv32, 1bv32); + assert word(64); + call _A(32bv32, 32bv32); + assert word(63); + call _S(64bv32, 1bv32); + assert word(128); + call _M(32bv32, 4bv32); + assert word(127); + call _S(128bv32, 1bv32); + assert word(I(16bv32) * I(16bv32)); + call _M(16bv32, 16bv32); + assert word(512); + call _A(256bv32, 256bv32); + assert word(65536); + call _M(256bv32, 256bv32); + assert word(65535); + call _S(65536bv32, 1bv32); + assert word(16777216); + call _M(65536bv32, 256bv32); + assert word(16777215); + call _S(16777216bv32, 1bv32); + assert word(2147483647 + 2147483647 + 2 - 65536); + call _M(65536bv32, 65535bv32); + assert word(33554432); + call _M(65536bv32, 512bv32); + assert word(33554431); + call _S(33554432bv32, 1bv32); + assert word(67108864); + call _A(33554432bv32, 33554432bv32); + assert word(67108863); + call _S(67108864bv32, 1bv32); +} + +procedure Const2() + ensures 2147483647 + 2147483647 + 1 == I(4294967295bv32); + ensures 2147483647 + 2147483647 - 2 == I(4294967292bv32); +{ + call Const(); + call $Const(); + assert word(2147483647 + 2147483647 + 1); + call _A(4294901760bv32, 65535bv32); + assert word(I(4294967295bv32) - I(3bv32)); + call _S(4294967295bv32, 3bv32); +} + +procedure _A(b1:bv32, b2:bv32) + requires word(I(b1) + I(b2)); + ensures I(b1) + I(b2) == I($add(b1, b2)); +{ + assert TBV(b1) && TBV(b2); +} + +procedure _S(b1:bv32, b2:bv32) + requires word(I(b1) - I(b2)); + ensures I(b1) - I(b2) == I($sub(b1, b2)); +{ + assert TBV(b1) && TBV(b2); +} + +procedure _M(b1:bv32, b2:bv32) + requires word(I(b1) * I(b2)); + ensures I(b1) * I(b2) == I($mul(b1, b2)); +{ + assert TBV(b1) && TBV(b2); +} + +implementation __andAligned(x:int) +{ + call Const(); + call $AndAligned(B(x)); + assert Aligned(I(B(x))) <==> $Aligned(B(x)); +} + +implementation __addAligned(x:int, y:int) +{ + call Const(); + call $AddAligned(B(x), B(y)); + assert word(x) && word(y) && word(x + y) && Aligned(I(B(x))) ==> (Aligned(I(B(y))) <==> Aligned(I(B(x + y)))); +} + +implementation __subAligned(x:int, y:int) +{ + call Const(); + call $SubAligned(B(x), B(y)); + assert word(x) && word(y) && word(x - y) && Aligned(I(B(x))) ==> (Aligned(I(B(y))) <==> Aligned(I(B(x - y)))); +} + +implementation __notAligned(i:int) +{ + call Const(); + call Const2(); + call $NotAligned(B(i)); + assert TBV(B(i)) && TBV(4294967292bv32); + assert i <= 2147483647 + 2147483647 - 2; +} + +implementation __initialize($unitSize:int, HeapLo:int) +{ + call Const(); + assert $le(B($unitSize), B(16777215)); + call $Initialize(B($unitSize)); + assert BitIndex(HeapLo, HeapLo + 128 * $unitSize) == 4 * $unitSize; + assert BitIndex(HeapLo, HeapLo + 256 * $unitSize) == 8 * $unitSize; +} + +implementation __bb4Zero(a:[int]int, off:int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, g1:int, g2:int, idx:int) +{ + call Const(); + + assert word(idx) && word(g1); + assert word(I(B(idx)) - I(B(g1))); + assert $le($shr(B(i2 - i0), 7bv32), 33554431bv32) ==> I($mul(128bv32, $shr(B(i2 - i0), 7bv32))) == 128 * I($shr(B(i2 - i0), 7bv32)); + assert (forall i:int::{TV(i)} TV(i) && word(i - i0) && i1 <= i && i < i2 && Aligned(I(B(i - i0))) ==> $Aligned(B(i - i0))); + + call _BB4Zero(a, off, aBase, bb, i0, i1, i2, g1, g2, idx); +} + +implementation __bb4GetBit(a:[int]int, off:int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, k:int, idx:int, bbb:int, g1:int, g2:int) +{ + call Const(); + call _BB4GetBit(i0, k); +} + +implementation __bb4SetBit(a:[int]int, on:int, off:int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, k:int, idx:int, bbb:int, ret:[int]int, g1:int, g2:int) +{ + call Const(); + + assert B(shr(k - i0, 7)) == $shr(B(k - i0), 7bv32); + assert Aligned(I(B(k - i0))); + assert (forall i:int::{TV(i)} TV(i) && word(i - i0) && i1 <= i && i < i2 && Aligned(I(B(i - i0))) ==> $Aligned(B(i - i0))); + + call _BB4SetBit(a, on, off, aBase, bb, i0, i1, i2, k, idx, bbb, ret, g1, g2); + + assert $bbvec4(a[aBase + (k - i0) := on], off, aBase, ret, i0, i1, i2, g1, g2); +} + +implementation __bb4Zero2(a:[int]int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, g1:int, g2:int, idx:int) +{ + call Const(); + + assert word(idx) && word(g1); + assert word(I(B(idx)) - I(B(g1))); + assert $le($shr(B(i2 - i0), 6bv32), 67108863bv32) ==> I($mul(64bv32, $shr(B(i2 - i0), 6bv32))) == 64 * I($shr(B(i2 - i0), 6bv32)); + assert (forall i:int::{TV(i)} TV(i) && word(i - i0) && i1 <= i && i < i2 && Aligned(I(B(i - i0))) ==> $Aligned(B(i - i0))); + + call _BB4Zero2(a, aBase, bb, i0, i1, i2, g1, g2, idx); +} + +implementation __bb4Get2Bit(a:[int]int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, k:int, idx:int, bbb:int, g1:int, g2:int) +{ + call Const(); + call _BB4Get2Bit(i0, k); +} + +implementation __bb4Set2Bit(a:[int]int, val:int, aBase:int, bb:[int]int, i0:int, i1:int, i2:int, k:int, idx:int, bbb:int, _bbb:int, ret:[int]int, g1:int, g2:int) +{ + call Const(); + + assert (forall i:int::{TV(i)} TV(i) && word(i - i0) && i1 <= i && i < i2 && Aligned(I(B(i - i0))) ==> $Aligned(B(i - i0))); + + call _BB4Set2Bit(a, val, aBase, bb, i0, i1, i2, k, idx, bbb, _bbb, ret, g1, g2); +} + + + + diff --git a/base/Imported/Bartok/runtime/verified/GCs/VerifiedCommon.bpl b/base/Imported/Bartok/runtime/verified/GCs/VerifiedCommon.bpl new file mode 100644 index 0000000..e0238a4 --- /dev/null +++ b/base/Imported/Bartok/runtime/verified/GCs/VerifiedCommon.bpl @@ -0,0 +1,402 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +// Common definitions and procedures shared between garbage collectors. +// This file is included by the garbage collectors. + +// Imports: +// - Trusted.bpl +// - VerifiedBitVectors.bpl + +// Imported from VerifiedBitVectors.bpl: +function BitIndex(i0:int, i:int) returns(int); +function BitZero(x:int, i0:int, i:int) returns(bool); +function ColorIndex(i0:int, i:int) returns(int); +function ColorGet(x:int, i0:int, i:int) returns(int); + +/***************************************************************************** +****************************************************************************** +**** VERIFIED +****************************************************************************** +*****************************************************************************/ + +function AlignedHeapAddr(i:int) returns(bool) { gcAddr(i) && Aligned(i) } + +////////////////////////////////////////////////////////////////////////////// +// LOCAL REASONING +////////////////////////////////////////////////////////////////////////////// + +// As a region evolves, it adds new mappings, but each mapping is +// permanent: RExtend ensures that new mappings do not overwrite old mappings. +function RExtend(rOld:[int]int, rNew:[int]int) returns (bool) +{ + (forall i:int::{rOld[i]}{rNew[i]} rOld[i] != NO_ABS ==> rOld[i] == rNew[i]) +} + +function AbsMapped(val:int, rev:[int]int, abs:int) returns (bool) +{ + abs != NO_ABS && abs == rev[val] +} + +// Both the mark-sweep and copying collectors only have two regions at +// any given time. +var $r1:[int]int; +var $r2:[int]int; + +////////////////////////////////////////////////////////////////////////////// +// OBJECTS +////////////////////////////////////////////////////////////////////////////// + +// Each object occupies a "slice" of the heap. If an object occupies +// addresses i + 0 ... i + m, then slice[i + 0] == i && ... && slice[i + m] == i. +// This helps distinguish addresses that belong to different objects. +var $gcSlice:[int]int; + +// REVIEW: cut $toAbs here? +function ObjInvBase(i:int, rs:[int]int, $toAbs:[int]int, + $AbsMem:[int][int]int, $GcMem:[int]int, $gcSlice:[int]int) returns (bool) +{ + gcAddr(i) && rs[i] != NO_ABS ==> + Aligned(i) + && AlignedHeapAddr(i + 4) // REVIEW: this is convenient, but is it necessary? + && VTable(rs[i], $AbsMem[rs[i]][1]) + && !VFieldPtr(rs[i], 1) // REVIEW: necessary? + && numFields(rs[i]) >= 2 // REVIEW: necessary? + && ObjSize(rs[i], $AbsMem[rs[i]][1], $AbsMem[rs[i]][2], $AbsMem[rs[i]][3]) + && $gcSlice[i] == i + && $gcSlice[i + 4] == i // REVIEW: this is convenient, but is it necessary? +} + +function ObjInvField(i:int, j:int, rs:[int]int, rt:[int]int, $toAbs:[int]int, + $AbsMem:[int][int]int, $GcMem:[int]int, $gcSlice:[int]int) returns (bool) +{ + gcAddr(i) && rs[i] != NO_ABS ==> + gcAddr(i + 4 * j) // REVIEW: necessary? + && $gcSlice[i + 4 * j] == i + && Value(VFieldPtr(rs[i], j), rt, $GcMem[i + 4 * j], $AbsMem[$toAbs[i]][j]) +} + +function ObjInvPartial(i:int, j1:int, j2:int, rs:[int]int, rt:[int]int, $toAbs:[int]int, + $AbsMem:[int][int]int, $GcMem:[int]int, $gcSlice:[int]int) returns (bool) +{ + ObjInvBase(i, rs, $toAbs, $AbsMem, $GcMem, $gcSlice) + && (forall j:int::{TO(j)} TO(j) ==> j1 <= j && j < j2 ==> + ObjInvField(i, j, rs, rt, $toAbs, $AbsMem, $GcMem, $gcSlice)) +} + +function ObjInv(i:int, rs:[int]int, rt:[int]int, $toAbs:[int]int, $AbsMem:[int][int]int, + $GcMem:[int]int, $gcSlice:[int]int) returns (bool) +{ + ObjInvPartial(i, 0, numFields(rs[i]), rs, rt, $toAbs, $AbsMem, $GcMem, $gcSlice) +} + +procedure TableSearch($base:int, $count:int, $x:int) + requires ecx == $base && edx == $count && ebp == $x; + requires word(ecx) && word(edx) && word(ebp); + requires (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j <= $count ==> inRo($base + 4 * j, 4)); + requires (forall j1:int, j2:int::{TO(j1), TO(j2)} TO(j1) && TO(j2) ==> 0 <= j1 && j1 < j2 && j2 <= $count ==> + ro32($base + 4 * j1) < ro32($base + 4 * j2)); + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + // eax = ret + // edx = found + ensures edx != 0 <==> (exists j:int::{TO(j)} TO(j) && 0 <= j && j < $count && between(ro32($base + 4 * j), ro32($base + 4 * (j + 1)), $x)); + ensures edx != 0 ==> 0 <= eax && eax < $count && between(ro32($base + 4 * eax), ro32($base + 4 * (eax + 1)), $x); +{ + if (edx >= 0) { goto skip1; } + edx := 0; + esp := esp + 4; return; + skip1: + + assert TO(0); + call eax := RoLoad32(ecx); + if (ebp >= eax) { goto skip2; } + assert TO(0); + edx := 0; + esp := esp + 4; return; + skip2: + + assert TO($count); + call eax := RoLoad32(ecx + 4 * edx); + if (ebp < eax) { goto skip3; } + assert TO($count); + // REVIEW: can this be cleaned up? + assert (forall j:int::{TSlot(j)} TSlot(j) && TO(j) && TO(j + 1) && 0 <= j && j < $count ==> $x >= ro32($base + 4 * (j + 1))); + assert (forall j:int::{TO(j)} TO(j) && TSlot(j) && 0 <= j && j < $count ==> $x >= ro32($base + 4 * (j + 1))); + edx := 0; + esp := esp + 4; return; + skip3: + + esi := 0; + edi := edx; + +// while (esi + 1 < edi) + loop: + assert TO(esi) && TO(edi) && 0 <= esi && esi < edi && edi <= $count; + assert (exists j:int::{TO(j)} TO(j) && 0 <= j && j < $count && between(ro32($base + 4 * j), ro32($base + 4 * (j + 1)), $x)) ==> + ro32($base + 4 * esi) <= $x && $x < ro32($base + 4 * edi); + call eax := Lea(esi + 1); + if (eax >= edi) { goto loopEnd; } + + call ebx := LeaUnchecked(esi + 1 * edi); + call ebx := Shr(ebx, 1); + if (ebx <= esi) { goto do4; } + if (ebx >= edi) { goto do4; } + goto skip4; + do4: + call ebx := Lea(esi + 1); + skip4: + + assert TO(ebx); + call eax := RoLoad32(ecx + 4 * ebx); + if (eax <= ebp) { goto do5; } + edi := ebx; + goto skip5; + do5: + esi := ebx; + skip5: + goto loop; + loopEnd: + + eax := esi; + call ebx := RoLoad32(ecx + 4 * eax); + if (ebp < ebx) { goto skip6; } + call ebx := RoLoad32(ecx + 4 * eax + 4); + if (ebp >= ebx) { goto skip6; } + edx := 1; + esp := esp + 4; return; + skip6: + edx := 0; + esp := esp + 4; return; +} + +procedure TablesSearch($f:int, $ra:int, $nextFp:int) + requires ecx == $ra && edx == $nextFp; + requires word($ra); + requires FrameNextInv($f, $ra, $nextFp, $FrameAddr, $FrameLayout); + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + // eax = ret + // edx = found + ensures edx != 0 ==> $f > 0 && eax == frameDescriptor($FrameLayout[$f - 1]) && $FrameAddr[$f - 1] == $nextFp; + ensures edx == 0 ==> $f == 0; +{ + var ra:int; + var nextFp:int; + var table:int; + var index:int; + var tableBase:int; + var tmp1:int; + var tmp2:int; + ra := ecx; + nextFp := edx; + table := 0; + edx := 0; + + //while (table < ?callSiteTableCount) + loop: + assert TVFT($f); + assert TT(table) && 0 <= table; + assert edx == 0; + assert !(exists t:int, j:int::{TT(t), TO(j)} TT(t) && TO(j) && t < table && RetAddrAt(t, j, ra)); + assert word(table) && word(ra); + eax := table; + if (?callSiteTableCount <= eax) { goto loopEnd; } + + ebx := ?returnAddressToCallSiteSetNumbers; + call ecx := RoLoad32(ebx + 4 * eax); + ebx := ?callSiteSetCount; + call edx := RoLoad32(ebx + 4 * eax); + call edx := RoLoad32(edx); + ebx := ?codeBaseStartTable; + call esi := RoLoad32(ebx + 4 * eax); + + ebp := ra; + + assert TO(0); + call eax := RoLoad32(ecx); // REVIEW: better way to prove word(ro32(ecx)). + + if (esi > ebp) { goto skip1; } + call ebp := Sub(ebp, esi); + esp := esp - 4; call TableSearch(ecx, edx, ra - esi); + index := eax; + + assert TO(index); + if (edx == 0) { goto skip1; } + esi := table; + edi := ?activationDescriptorTable; + call ecx := RoLoad32(edi + 4 * esi); + edi := ?callSiteSetNumberToIndex; + call ebp := RoLoad32(edi + 4 * esi); + call ebp := RoLoadU16(ebp + 2 * eax); + call eax := RoLoad32(ecx + 4 * ebp); + esp := esp + 4; return; + skip1: + + call table := AddChecked(table, 1); + edx := 0; + goto loop; + loopEnd: + esp := esp + 4; return; +} + +procedure getSize($abs:int, $ptr:int, $vt:int, $_nElems1:int, $_nElems2:int) + requires ecx == $ptr && edx == $vt; + requires ObjSize($abs, $vt, $_nElems1, $_nElems2); + requires VTable($abs, $vt); + requires numFields($abs) >= 3 ==> AlignedHeapAddr($ptr + 8); + requires numFields($abs) >= 3 && !VFieldPtr($abs, 2) ==> $GcMem[$ptr + 8] == $_nElems1; + requires numFields($abs) >= 4 ==> AlignedHeapAddr($ptr + 12); + requires numFields($abs) >= 4 && !VFieldPtr($abs, 3) ==> $GcMem[$ptr + 12] == $_nElems2; + modifies eax, ebx, edx, esi, edi, ebp, esp; + ensures eax == 4 * numFields($abs); +{ + assert TVL($abs); + assert TVT($abs, $vt); + call ebp := RoLoad32(edx + ?VT_MASK); + call ebp := And(ebp, 15); + + if (ebp != ?SPARSE_TAG) { goto skip1; } + call eax := RoLoad32(edx + ?VT_BASE_LENGTH); + goto end; + skip1: + + if (ebp != ?DENSE_TAG) { goto skip2; } + call eax := RoLoad32(edx + ?VT_BASE_LENGTH); + goto end; + skip2: + + if (ebp != ?STRING_TAG) { goto skip3; } + assert TO(2); + call esi := GcLoad(ecx + 8); + //eax := pad(16 + 2 * esi); + eax := esi; + call eax := AddChecked(eax, eax); + call eax := AddChecked(eax, 19); + ebx := 3; + call ebx := Not(ebx); + call eax := And(eax, ebx); + goto end; + skip3: + + if (ebp != ?PTR_VECTOR_TAG) { goto skip4; } + assert TO(2); + call esi := GcLoad(ecx + 8); + call eax := RoLoad32(edx + ?VT_BASE_LENGTH); + //eax := pad(eax + 4 * esi); + call esi := AddChecked(esi, esi); + call esi := AddChecked(esi, esi); + call eax := AddChecked(eax, esi); + call eax := AddChecked(eax, 3); + ebx := 3; + call ebx := Not(ebx); + call eax := And(eax, ebx); + goto end; + skip4: + + if (ebp != ?OTHER_VECTOR_TAG) { goto skip5; } + assert TO(2); + call esi := GcLoad(ecx + 8); + call eax := RoLoad32(edx + ?VT_BASE_LENGTH); + call ebx := RoLoad32(edx + ?VT_ARRAY_ELEMENT_SIZE); + //eax := pad(eax + Mult(ebx, esi)); + ebp := eax; + eax := ebx; + call eax, edx := MulChecked(eax, esi); + call eax := AddChecked(eax, ebp); + call eax := AddChecked(eax, 3); + ebx := 3; + call ebx := Not(ebx); + call eax := And(eax, ebx); + goto end; + skip5: + + if (ebp != ?PTR_ARRAY_TAG) { goto skip6; } + assert TO(3); + call esi := GcLoad(ecx + 12); + call eax := RoLoad32(edx + ?VT_BASE_LENGTH); + //eax := pad(eax + 4 * esi); + call esi := AddChecked(esi, esi); + call esi := AddChecked(esi, esi); + call eax := AddChecked(eax, esi); + call eax := AddChecked(eax, 3); + ebx := 3; + call ebx := Not(ebx); + call eax := And(eax, ebx); + goto end; + skip6: + + if (ebp != ?OTHER_ARRAY_TAG) { goto skip7; } + assert TO(3); + call esi := GcLoad(ecx + 12); + call eax := RoLoad32(edx + ?VT_BASE_LENGTH); + call ebx := RoLoad32(edx + ?VT_ARRAY_ELEMENT_SIZE); + //eax := pad(eax + Mult(ebx, esi)); + ebp := eax; + eax := ebx; + call eax, edx := MulChecked(eax, esi); + call eax := AddChecked(eax, ebp); + call eax := AddChecked(eax, 3); + ebx := 3; + call ebx := Not(ebx); + call eax := And(eax, ebx); + goto end; + skip7: + + // else + call eax := RoLoad32(edx + ?VT_BASE_LENGTH); + end: +} + +procedure GetSize($ptr:int, $vt:int, $rs:[int]int, $rt:[int]int) + requires ecx == $ptr && edx == $vt; + requires gcAddr($ptr); + requires $rs[$ptr] != NO_ABS && $rs[$ptr] == $toAbs[$ptr]; + requires ObjInv($ptr, $rs, $rt, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires $vt == $AbsMem[$rs[$ptr]][1] || $vt == $GcMem[$ptr + 4]; + modifies eax, ebx, edx, esi, edi, ebp, esp; + ensures eax == 4 * numFields($rs[$ptr]); +{ + assert TV($ptr); + assert TO(1); + assert TO(2); + assert TO(3); + call getSize($rs[$ptr], $ptr, $vt, $AbsMem[$rs[$ptr]][2], $AbsMem[$rs[$ptr]][3]); + esp := esp + 4; return; +} + +procedure readTag($abs:int, $vt:int) + requires ecx == $vt; + requires VTable($abs, $vt); + modifies eax; + ensures eax == tag($vt); +{ + assert TVT($abs, $vt); + call eax := RoLoad32(ecx + ?VT_MASK); + call eax := And(eax, 15); +} + +procedure readArrayOf($abs:int, $vt:int) + requires ecx == $vt; + requires VTable($abs, $vt); + modifies ebp; + ensures ebp == arrayOf($vt); +{ + assert TVT($abs, $vt); + call ebp := RoLoad32(ecx + ?VT_ARRAY_OF); +} + +procedure readElementInfo($abs:int, $vt:int) + requires ecx == $vt; + requires VTable($abs, $vt); + requires tag($vt) == ?OTHER_VECTOR_TAG; + modifies esi, edi; + ensures esi == arrayElementSize($vt); + ensures edi == mask(arrayElementClass($vt)); +{ + assert TVT($abs, $vt); + call esi := RoLoad32(ecx + ?VT_ARRAY_ELEMENT_SIZE); + call edi := RoLoad32(ecx + ?VT_ARRAY_ELEMENT_CLASS); + call edi := RoLoad32(edi + ?VT_MASK); +} + + diff --git a/base/Imported/Bartok/runtime/verified/GCs/VerifiedCopyingCollector.bpl b/base/Imported/Bartok/runtime/verified/GCs/VerifiedCopyingCollector.bpl new file mode 100644 index 0000000..cdbb64d --- /dev/null +++ b/base/Imported/Bartok/runtime/verified/GCs/VerifiedCopyingCollector.bpl @@ -0,0 +1,2803 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +// Verified copying garbage collector +// +// medium term goal: support more Bartok array-of-struct and vector-of-struct object layouts +// medium term goal: make generational +// long term goal: support various other features: threads, pinning, stack markers, etc. + +// Imports: +// - Trusted.bpl +// - VerifiedBitVectors.bpl +// Includes: +// - VerifiedCommon.bpl + +// \Spec#\bin\Boogie.exe /noinfer Trusted.bpl VerifiedBitVectors.bpl VerifiedCommon.bpl VerifiedCopyingCollector.bpl + +/***************************************************************************** +****************************************************************************** +**** VERIFIED +****************************************************************************** +*****************************************************************************/ + +////////////////////////////////////////////////////////////////////////////// +// COPYING COLLECTOR +////////////////////////////////////////////////////////////////////////////// + +function IsFwdPtr(i:int) returns(bool) { gcAddrEx(i) } + +var $freshAbs:int; + +// The allocation bitmap ranges from ?gcLo..HeapLo +// The spaces (from and to) range from HeapLo..?gcHi +var HeapLo:int; +// Fromspace ranges from Fi to Fl, where Fk..Fl is empty +// Tospace ranges from Ti to Tl, where Tk..Tl is empty +var Fi:int; +var Fk:int; +var Fl:int; +var Ti:int; +var Tj:int; +var Tk:int; +var Tl:int; +// Bitmaps for fromspace and tospace: +var BF:int; +var BT:int; + +function ObjectSpc(i1:int, i2:int, r:[int]int) returns (bool) +{ + (forall i:int::{TV(i)} TV(i) ==> i1 <= i && i < i2 && r[i] != NO_ABS ==> + i + 4 * numFields(r[i]) <= i2 + && (forall ii:int::{TV(ii)} TV(ii) ==> i < ii && ii < i + 4 * numFields(r[i]) ==> r[ii] == NO_ABS)) +} + +function ObjectSeq(i1:int, i2:int, r:[int]int) returns (bool) +{ + (i1 == i2 || r[i1] != NO_ABS) + && (forall i:int::{TV(i)} TV(i) ==> i1 <= i && i < i2 && r[i] != NO_ABS ==> + (forall ii:int::{TV(ii)} TV(ii) ==> i < ii && ii < i + 4 * numFields(r[i]) ==> + r[ii] == NO_ABS) + && (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields(r[i]) ==> gcAddr(i + 4 * j)) // REVIEW: necessary? + && ( (i + 4 * numFields(r[i]) == i2) + || (i + 4 * numFields(r[i]) < i2 && r[i + 4 * numFields(r[i])] != NO_ABS))) +} + +function EmptySeq(i1:int, i2:int, r:[int]int, $toAbs:[int]int) returns (bool) +{ + (forall i:int::{TV(i)} TV(i) ==> i1 <= i && i < i2 ==> gcAddr(i) && r[i] == NO_ABS && $toAbs[i] == NO_ABS) // REVIEW: gcAddr necessary? +} + +// invariant: +// note: typically, Tj = _tj and Tk = _tk +function CopyGcInv($freshAbs:int, BF:int, BT:int, HeapLo:int, Fi:int, Fk:int, Fl:int, Ti:int, Tj:int, _tj:int, Tk:int, _tk:int, Tl:int, $Time:Time, $r1:[int]int, $r2:[int]int, + r1Live:bool, $toAbs:[int]int, $AbsMem:[int][int]int, + $GcMem:[int]int, $gcSlice:[int]int) returns (bool) +{ + ?gcLo <= HeapLo + && HeapLo <= ?gcHi + && ((Fi == HeapLo && Ti == Fl && BF == ?gcLo) || (Ti == HeapLo && Fi == Tl && BT == ?gcLo)) + && (Fi == HeapLo ==> ?gcLo + BitIndex(HeapLo, Fl) == BT && ?gcLo + BitIndex(HeapLo, Tl) == HeapLo) + && (Ti == HeapLo ==> ?gcLo + BitIndex(HeapLo, Tl) == BF && ?gcLo + BitIndex(HeapLo, Fl) == HeapLo) + && Fi <= Fk && Fk <= Fl && Fl <= ?gcHi + && Ti <= Tj && Tj <= _tj && _tj <= Tk && Tk <= _tk && _tk <= Tl && Tl <= ?gcHi + && Aligned(Fi) && Aligned(Fk) && Aligned(Ti) && Aligned(Tj) && Aligned(Tk) + && Aligned(?gcLo + BitIndex(HeapLo, Fi)) && Aligned(?gcLo + BitIndex(HeapLo, Ti)) + && ?gcLo <= ?gcLo + BitIndex(HeapLo, Fi) && ?gcLo + BitIndex(HeapLo, Fl) <= HeapLo + && ?gcLo <= ?gcLo + BitIndex(HeapLo, Ti) && ?gcLo + BitIndex(HeapLo, Tl) <= HeapLo + && BF == ?gcLo + BitIndex(HeapLo, Fi) && BT == ?gcLo + BitIndex(HeapLo, Ti) + && (Fl - Fi) == Mult(32, (BitIndex(HeapLo, Fl) - BitIndex(HeapLo, Fi))) // Mult is faster than "*" here + && (Tl - Ti) == Mult(32, (BitIndex(HeapLo, Tl) - BitIndex(HeapLo, Ti))) // Mult is faster than "*" here + && bbvec4($r1, NO_ABS, Fi, $GcMem, Fi, Fi, Fl, ?gcLo + BitIndex(HeapLo, Fi), ?gcLo + BitIndex(HeapLo, Fl)) + && bbvec4($toAbs, NO_ABS, Ti, $GcMem, Ti, Ti, Tl, ?gcLo + BitIndex(HeapLo, Ti), ?gcLo + BitIndex(HeapLo, Tl)) + && WellFormed($toAbs) + && ObjectSpc(Fi, Fk, $r1) + && EmptySeq( Fk, Fl, $r1, $toAbs) + && ObjectSpc(Ti, Tj, $r2) + && ObjectSeq(_tj, Tk, $r2) + && EmptySeq( _tk, Tl, $r2, $toAbs) + && (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $r1[i] != NO_ABS && r1Live ==> Fi <= i && i < Fk) + && (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $r2[i] != NO_ABS ==> Ti <= i && i < _tk) + && (forall i:int::{TV(i)} TV(i) ==> Fi <= i && i < Fk && $r1[i] != NO_ABS ==> + ( IsFwdPtr($GcMem[i + 4]) <==> $toAbs[i] == NO_ABS) + && ( IsFwdPtr($GcMem[i + 4]) ==> Pointer($r2, $GcMem[i + 4] - 4, $r1[i]) && AlignedHeapAddr(i + 4) && word($GcMem[i + 4])) + && (!IsFwdPtr($GcMem[i + 4]) ==> $toAbs[i] == $r1[i]) + && (!IsFwdPtr($GcMem[i + 4]) ==> r1Live ==> ObjInv(i, $r1, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice)) + && i + 4 < Fk // REVIEW: hack? + && Aligned(i) // REVIEW: redundant? + ) + && (forall i:int::{TV(i)} TV(i) ==> Fi <= i && i < Fl && $toAbs[i] != NO_ABS ==> $r1[i] != NO_ABS && $r1[i] != NO_ABS) + && (forall i:int::{TV(i)} TV(i) ==> Ti <= i && i < Tl && $toAbs[i] != NO_ABS ==> $r2[i] != NO_ABS && $r2[i] != NO_ABS) + && (forall i:int::{TV(i)} TV(i) ==> Ti <= i && i < Tj && $r2[i] != NO_ABS ==> + $toAbs[i] != NO_ABS && $toAbs[i] == $r2[i] + && reached($toAbs[i], $Time) + && ObjInv(i, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice) + && !IsFwdPtr($GcMem[i + 4]) + ) + && (Tj != _tj ==> (forall j:int::{TO(j)} TO(j) ==> 0 <= j && Tj + 4 * j < _tj ==> + gcAddr(Tj + 4 * j) && $gcSlice[Tj + 4 * j] == Tj)) // REVIEW: gcAddr necessary? + && (forall i:int::{TV(i)} TV(i) ==> Tj < i && i < _tj ==> $r2[i] == NO_ABS) + && (forall i:int::{TV(i)} TV(i) ==> _tj <= i && i < Tk && $r2[i] != NO_ABS ==> + $toAbs[i] != NO_ABS && $toAbs[i] == $r2[i] + && reached($toAbs[i], $Time) + && ObjInv(i, $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice) + && !IsFwdPtr($GcMem[i + 4]) + ) + && (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $freshAbs && $r2[i] != $freshAbs) +} + +function MutatorInv(BF:int, BT:int, HeapLo:int, Fi:int, Fk:int, Fl:int, Ti:int, Tj:int, Tk:int, Tl:int, + $GcMem:[int]int, $toAbs:[int]int, $AbsMem:[int][int]int, $gcSlice:[int]int) returns (bool) +{ + ?gcLo <= HeapLo + && HeapLo <= ?gcHi + && ((Fi == HeapLo && Ti == Fl && BF == ?gcLo) || (Ti == HeapLo && Fi == Tl && BT == ?gcLo)) + && (Fi == HeapLo ==> ?gcLo + BitIndex(HeapLo, Fl) == BT && ?gcLo + BitIndex(HeapLo, Tl) == HeapLo) + && (Ti == HeapLo ==> ?gcLo + BitIndex(HeapLo, Tl) == BF && ?gcLo + BitIndex(HeapLo, Fl) == HeapLo) + && Fi <= Fk && Fk <= Fl && Fl <= ?gcHi + && Ti <= Tj && Tj <= Tk && Tk <= Tl && Tl <= ?gcHi + && Aligned(Fi) && Aligned(Fk) && Aligned(Ti) && Aligned(Tj) && Aligned(Tk) + && Aligned(?gcLo + BitIndex(HeapLo, Fi)) && Aligned(?gcLo + BitIndex(HeapLo, Ti)) + && ?gcLo <= ?gcLo + BitIndex(HeapLo, Fi) && ?gcLo + BitIndex(HeapLo, Fl) <= HeapLo + && ?gcLo <= ?gcLo + BitIndex(HeapLo, Ti) && ?gcLo + BitIndex(HeapLo, Tl) <= HeapLo + && BF == ?gcLo + BitIndex(HeapLo, Fi) && BT == ?gcLo + BitIndex(HeapLo, Ti) + && (Fl - Fi) == Mult(32, (BitIndex(HeapLo, Fl) - BitIndex(HeapLo, Fi))) // Mult is faster than "*" here + && (Tl - Ti) == Mult(32, (BitIndex(HeapLo, Tl) - BitIndex(HeapLo, Ti))) // Mult is faster than "*" here + && bbvec4($toAbs, NO_ABS, Fi, $GcMem, Fi, Fi, Fl, ?gcLo + BitIndex(HeapLo, Fi), ?gcLo + BitIndex(HeapLo, Fl)) + && WellFormed($toAbs) + && ObjectSpc(Fi, Fk, $toAbs) + && EmptySeq( Fk, Fl, $toAbs, $toAbs) + && EmptySeq( Ti, Tl, $toAbs, $toAbs) + && (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> ($toAbs[i] != NO_ABS ==> Fi <= i && i < Fk)) + && (forall i:int::{TV(i)} TV(i) ==> Fi <= i && i < Fk && $toAbs[i] != NO_ABS ==> + $toAbs[i] != NO_ABS + && ObjInv(i, $toAbs, $toAbs, $toAbs, $AbsMem, $GcMem, $gcSlice) + && !IsFwdPtr($GcMem[i + 4]) + ) + && (forall i:int::{TV(i)} TV(i) ==> Fi <= i && i < Fl && $toAbs[i] != NO_ABS ==> $toAbs[i] != NO_ABS) + && (forall i:int::{TV(i)} TV(i) ==> Ti <= i && i < Tl && $toAbs[i] != NO_ABS ==> false) +} + +procedure BB4Zero($a:[int]int, $on:int, $off:int, $aBase:int, $i0:int, $i1:int, $i2:int, $k:int, $g1:int, $g2:int, $_i0:int, $_g1:int) + requires eax == $g1; + requires ebx == $g2; + requires (forall i:int::{TV(i)} TV(i) && $i1 <= i && i < $i2 ==> $a[$aBase + (i - $i0)] == $off); + requires Aligned($g1) && Aligned($g2); + requires $i2 - $i1 == 32 * ($g2 - $g1); + requires word($i1 - $i0) && word($i2 - $i0); + requires ?gcLo <= $g1 && $g1 <= $g2 && $g2 <= ?gcHi; + requires $i1 == $i0; + modifies $GcMem; + modifies eax, ebx, esi, edi, ebp, esp; + ensures bbvec4($a, $off, $aBase, $GcMem, $i0, $i1, $i2, $g1, $g2); + ensures (forall i:int::{TV(i)} TV(i) && !between($g1, $g2, i) ==> $GcMem[i] == old($GcMem)[i]); + ensures (forall i:int::{TV(i)} TV(i) && !between($g1, $g2, i + 4) ==> $GcMem[i + 4] == old($GcMem)[i + 4]); // REVIEW: HACK? + ensures (forall i:int,j:int::{TV(i),TO(j)} TV(i) && TO(j) && !between($g1, $g2, i + 4 * j) ==> $GcMem[i + 4 * j] == old($GcMem)[i + 4 * j]); // REVIEW: HACK? + ensures (forall i:int::{TV(i)} TV(i) && !between($g1, $g2, $_g1 + BitIndex($_i0, i)) ==> $GcMem[$_g1 + BitIndex($_i0, i)] == old($GcMem)[$_g1 + BitIndex($_i0, i)]); // REVIEW: HACK? +{ + var $iter:int; + esi := eax; + $iter := $i1; + //while (esi < $g2) + loop: + assert Aligned(esi) && TV(esi); + assert $g1 <= esi && esi <= $g2; + assert $iter - $i1 == 32 * (esi - $g1); + assert bbvec4($a, $off, $aBase, $GcMem, $i0, $i1, $iter, $g1, $g2); + assert (forall i:int::{TV(i)} TV(i) && !between($g1, $g2, i) ==> $GcMem[i] == old($GcMem)[i]); + assert (forall i:int::{TV(i)} TV(i) && !between($g1, $g2, i + 4) ==> $GcMem[i + 4] == old($GcMem)[i + 4]); // REVIEW: HACK? + assert (forall i:int,j:int::{TV(i),TO(j)} TV(i) && TO(j) && !between($g1, $g2, i + 4 * j) ==> $GcMem[i + 4 * j] == old($GcMem)[i + 4 * j]); // REVIEW: HACK? + assert (forall i:int::{TV(i)} TV(i) && !between($g1, $g2, $_g1 + BitIndex($_i0, i)) ==> $GcMem[$_g1 + BitIndex($_i0, i)] == old($GcMem)[$_g1 + BitIndex($_i0, i)]); // REVIEW: HACK? + if (esi >= ebx) { goto loopEnd; } + + call __notAligned(esi); + call __bb4Zero($a, $off, $aBase, $GcMem, $i0, $i1, $iter, $g1, $g2, esi); + call GcStore(esi, 0);//$GcMem[esi] := 0; + $iter := $iter + 128; + call esi := Add(esi, 4); + assert TO(1); + goto loop; + loopEnd: + + assert esi == $g2; + assert $iter == $i2; + esp := esp + 4; return; +} + +procedure bb4GetBit($a:[int]int, $off:int, $aBase:int, $i0:int, $i1:int, $i2:int, $k:int, $g1:int, $g2:int) + requires bbvec4($a, $off, $aBase, $GcMem, $i0, $i1, $i2, $g1, $g2); + requires word($k - $i0) && $i1 <= $k && $k < $i2; + requires word($k) && word($i0) && Aligned($k) && Aligned($i0); + requires word($i2 - $i0); + requires eax == $g1; + requires ebx == $k - $i0; + requires Aligned($g1) && ?gcLo <= $g1 && $g2 <= ?gcHi; + requires word($i1 - $i0) && word($i2 - $i0); + modifies ebx, ecx, edx; + ensures ebx == 0 <==> $a[$aBase + ($k - $i0)] == $off; +{ + var $idx:int; + var $bbb:int; + $idx := $g1 + 4 * shr($k - $i0, 7); + $bbb := and($GcMem[$idx], shl(1, and(shr($k - $i0, 2), 31))); + call __subAligned($k, $i0); + call __bb4GetBit($a, $off, $aBase, $GcMem, $i0, $i1, $i2, $k, $idx, $bbb, $g1, $g2); + + edx := ebx; + assert TV($g1); + assert TO(shr(ebx, 7)); + call ebx := Shr(ebx, 7); + call edx := Shr(edx, 2); + call ebx := Add(ebx, ebx); + call ebx := Add(ebx, ebx); + call ebx := Add(ebx, eax); + call ebx := GcLoad(ebx); + call edx := And(edx, 31); + ecx := edx; + edx := 1; + call edx := Shl(edx, ecx); + call ebx := And(ebx, edx); +} + +procedure bb4SetBit($a:[int]int, $on:int, $off:int, $aBase:int, $i0:int, $i1:int, $i2:int, $k:int, $g1:int, $g2:int) + requires bbvec4($a, $off, $aBase, $GcMem, $i0, $i1, $i2, $g1, $g2); + requires word($k - $i0) && $i1 <= $k && $k < $i2; + requires word($k) && word($i0) && Aligned($k) && Aligned($i0); + requires $on != $off; + requires word($i2 - $i0); + requires esi == $k - $i0; + requires edi == $g1; + requires Aligned($g1) && ?gcLo <= $g1 && $g2 <= ?gcHi; + requires word($i1 - $i0) && word($i2 - $i0); + modifies esi, edi, ecx, $GcMem; + ensures bbvec4($a[$aBase + ($k - $i0) := $on], $off, $aBase, $GcMem, $i0, $i1, $i2, $g1, $g2); + ensures $GcMem == old($GcMem)[$g1 + BitIndex($i0, $k) := edi]; + ensures Aligned($k - $i0); +{ + var $idx:int; + var $bbb:int; + $idx := $g1 + 4 * shr($k - $i0, 7); + $bbb := or($GcMem[$idx], shl(1, and(shr($k - $i0, 2), 31))); + call __subAligned($k, $i0); + call __bb4SetBit($a, $on, $off, $aBase, $GcMem, $i0, $i1, $i2, $k, $idx, $bbb, $GcMem[$idx := $bbb], $g1, $g2); + + ecx := esi; + assert TV($g1); + assert TO(shr(esi, 7)); + call esi := Shr(esi, 7); + call ecx := Shr(ecx, 2); + call esi := Add(esi, esi); + call esi := Add(esi, esi); + call esi := Add(esi, edi); + call ecx := And(ecx, 31); + edi := 1; + call edi := Shl(edi, ecx); + ecx := edi; // REVIEW: optimize this more + call edi := GcLoad(esi); + call edi := Or(edi, ecx); + call GcStore(esi, edi); +} + +procedure copyWord($ptr:int, $_tj:int, $ret:int, $ind:int, $s:int) + requires ecx == $ptr && esi == $ret && edi == $ind; + requires Pointer($r1, $ptr, $r1[$ptr]) && TV($ptr); + requires !IsFwdPtr($GcMem[$ptr + 4]); + requires $_tj <= Tk; + + requires $s == 4 * numFields($r1[$ptr]); + requires Tk == $ret + $s; + requires Tk <= Tl; + + requires TO($ind) && 0 <= $ind && $ind < numFields($r1[$ptr]); + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, $_tj, $ret, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind ==> gcAddr($ret + 4 * j) && $gcSlice[$ret + 4 * j] == $ret); // REVIEW: gcAddr necessary? + requires EmptySeq($ret + 4 * $ind, Tk, $r2, $toAbs); + requires (forall j:int::{TO(j)} TO(j) ==> + 0 <= j && j < $ind ==> Value(VFieldPtr($r1[$ptr], j), $r1, $GcMem[$ret + 4 * j], $AbsMem[$toAbs[$ptr]][j])); + requires (forall i:int::{TV(i)} TV(i) ==> $ret < i && i < $ret + 4 * $ind ==> $r2[i] == NO_ABS); + requires $r2[$ret] == NO_ABS; + + modifies $GcMem, $gcSlice; + modifies eax, ebx; + + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, $_tj, $ret, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind + 1 ==> gcAddr($ret + 4 * j) && $gcSlice[$ret + 4 * j] == $ret); // REVIEW: gcAddr necessary? + ensures (forall j:int::{TO(j)} TO(j) ==> + 0 <= j && j < $ind + 1 ==> Value(VFieldPtr($r1[$ptr], j), $r1, $GcMem[$ret + 4 * j], $AbsMem[$toAbs[$ptr]][j])); + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && Tj + 4 * j < $_tj ==> + $GcMem[Tj + 4 * j] == old($GcMem)[Tj + 4 * j] && $gcSlice[Tj + 4 * j] == old($gcSlice[Tj + 4 * j])); + + ensures RExtend(old($r2), $r2); +{ + assert TO(numFields($r1[$ptr])); + assert TV($ret); + + call eax := GcLoad(ecx + 4 * edi); + assert TV($ret + 4 * $ind); + $gcSlice[$ret + 4 * $ind] := $ret; + call GcStore(esi + 4 * edi, eax); + assert TO(1); +} + +procedure CopyAndForward($ptr:int, $_tj:int) + requires ecx == $ptr; + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, $_tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires Pointer($r1, $ptr, $r1[$ptr]) && TV($ptr); + requires !IsFwdPtr($GcMem[$ptr + 4]); + requires $_tj <= Tk; + requires reached($toAbs[$ptr], $Time); + modifies $r2, $GcMem, $toAbs, Tk, $gcSlice; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, $_tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures RExtend(old($r2), $r2); + ensures Pointer($r2, eax, $r1[$ptr]); + ensures Tj == old(Tj); + ensures Tk >= old(Tk); + ensures old($toAbs)[Tj] != NO_ABS ==> $toAbs[Tj] != NO_ABS && $toAbs[Tj] == old($toAbs)[Tj]; + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && Tj + 4 * j < $_tj ==> + $GcMem[Tj + 4 * j] == old($GcMem)[Tj + 4 * j] && $gcSlice[Tj + 4 * j] == old($gcSlice[Tj + 4 * j])); + ensures Ti <= eax && eax < Tk && gcAddrEx(eax + 4); +{ + var tmp:int; + + call edx := GcLoad(ecx + 4); + esp := esp - 4; call GetSize($ptr, edx, $r1, $r1); + ebp := eax; + assert TO(numFields($r1[$ptr])); + + esi := Tk; + call Tk := AddChecked(Tk, ebp); + assert TV(esi); + + eax := Tl; + if (Tk <= eax) { goto skip1; } + // out of memory + call DebugBreak(); + skip1: + + edi := 0; + edx := 0; + // while (edx < ebp) + loop: + assert 4 * edi == edx; + assert TO(edi) && 0 <= edi && edi <= numFields($r1[$ptr]); + assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, $_tj, esi, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert RExtend(old($r2), $r2); + assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < edi ==> gcAddr(esi + 4 * j) && $gcSlice[esi + 4 * j] == esi); // REVIEW: gcAddr necessary? + assert EmptySeq(esi + 4 * edi, Tk, $r2, $toAbs); + assert (forall j:int::{TO(j)} TO(j) ==> + 0 <= j && j < edi ==> Value(VFieldPtr($r1[$ptr], j), $r1, $GcMem[esi + 4 * j], $AbsMem[$toAbs[$ptr]][j])); + assert (forall i:int::{TV(i)} TV(i) ==> esi < i && i < esi + 4 * edi ==> $r2[i] == NO_ABS); + assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && Tj + 4 * j < $_tj ==> + $GcMem[Tj + 4 * j] == old($GcMem)[Tj + 4 * j] && $gcSlice[Tj + 4 * j] == old($gcSlice[Tj + 4 * j])); + assert $r2[esi] == NO_ABS; + if (edx >= ebp) { goto loopEnd; } + + call copyWord($ptr, $_tj, esi, edi, ebp); + call edi := Add(edi, 1); + call edx := Add(edx, 4); + goto loop; + loopEnd: + + call eax := Lea(esi + 4); + call GcStore(ecx + 4, eax); + + eax := esi; + + call esi := Sub(esi, Ti); + + edi := BT; + call bb4SetBit($toAbs, $r1[$ptr], NO_ABS, Ti, Ti, Ti, Tl, eax, ?gcLo + BitIndex(HeapLo, Ti), ?gcLo + BitIndex(HeapLo, Tl)); + + $r2[eax] := $r1[$ptr]; + $toAbs[eax] := $toAbs[$ptr]; + $toAbs[$ptr] := NO_ABS; + assert TO(1); + + assert TV(eax - Ti); + + esp := esp + 4; return; +} + +procedure forwardFromspacePtr($ptr:int, $abs:int, $_tj:int) + requires ecx == $ptr; + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, $_tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires word($ptr); + requires Pointer($r1, $ptr - 4, $abs); + requires $_tj <= Tk; + requires IsFwdPtr($GcMem[$ptr]) || reached($toAbs[$ptr - 4], $Time); + modifies $r2, $GcMem, $toAbs, Tk, $gcSlice; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, $_tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures RExtend(old($r2), $r2); + ensures Pointer($r2, eax - 4, $abs); + ensures Tj == old(Tj); + ensures Tk >= old(Tk); + ensures old($toAbs)[Tj] != NO_ABS ==> $toAbs[Tj] != NO_ABS && $toAbs[Tj] == old($toAbs)[Tj]; + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && Tj + 4 * j < $_tj ==> + $GcMem[Tj + 4 * j] == old($GcMem)[Tj + 4 * j] && $gcSlice[Tj + 4 * j] == old($gcSlice[Tj + 4 * j])); + ensures Ti <= eax - 4 && eax - 4 < Tk; + ensures gcAddrEx(eax); +{ + assert TV($ptr - 4); + call eax := GcLoad(ecx); + + if (?gcLo > eax) { goto skip; } + if (?gcHi >= eax) { goto done; } + skip: + call ecx := Sub(ecx, 4); + esp := esp - 4; call CopyAndForward($ptr - 4, $_tj); + call eax := Add(eax, 4); + done: + assert TV(eax - 4); +} + +procedure scanObjectDense($vt:int) + requires ecx == $vt; + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires Tj < Tk; + requires TV(Tj); + requires $vt == $AbsMem[$r2[Tj]][1]; + requires tag($vt) == ?DENSE_TAG; + modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures RExtend(old($r2), $r2); +{ + var save1:int; + var save2:int; + var save3:int; + assert TO(numFields($r2[Tj])); + + esi := 2; + assert TVT($r2[Tj], $vt); + assert TVL($r2[Tj]); + call ebp := RoLoad32(ecx + ?VT_BASE_LENGTH); + call edx := RoLoad32(ecx + ?VT_MASK); + edi := Tj; + call edi := Add(edi, 8); + call ebp := Add(ebp, Tj); + + //while (edi < ebp) + loop: + assert TO(esi);// && 0 < esi; + assert edi == Tj + 4 * esi && ebp == Tj + 4 * numFields($r2[Tj]) && edx == mask($vt); + assert 2 <= esi && esi <= numFields($r2[Tj]); + assert Pointer($r2, Tj, $r2[Tj]); + assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj + 4 * numFields($r2[Tj]), Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert RExtend(old($r2), $r2); + assert ObjInvPartial(Tj, 0, esi, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert ObjInvPartial(Tj, esi, numFields($r2[Tj]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert $toAbs[Tj] != NO_ABS && $toAbs[Tj] == $r2[Tj]; + if (edi >= ebp) { goto loopEnd; } + if (esi >= 30) { goto loopEnd; } + + assert TO(0) && TO(1); + assert TVT($r2[Tj], $vt); + assert TVL($r2[Tj]); + //assert getBit(mask($vt), 2 + esi) == VFieldPtr($r2[Tj], esi); + //if (getBit(mask, 2 + esi)) + call ecx := Lea(esi + 2); + ebx := edx; + call ebx := Shr(ebx, ecx); + call ebx := And(ebx, 1); + if (ebx != 1) { goto skip1; } + call ecx := GcLoad(edi); + + //if (gcAddrEx(ecx)) + if (ecx < ?gcLo) { goto skip2; } + if (ecx > ?gcHi) { goto skip2; } + assert TV(ecx - 4); + call reach($toAbs[Tj], esi, $Time); + + save1 := esi; + save2 := ebp; + save3 := edx; + call forwardFromspacePtr(ecx, $AbsMem[$toAbs[Tj]][esi], Tj + 4 * numFields($r2[Tj])); + esi := save1; + ebp := save2; + edx := save3; + edi := Tj; + call edi := Lea(edi + 4 * esi); + + call GcStore(edi, eax); + skip2: + skip1: + + call esi := Add(esi, 1); + call edi := Add(edi, 4); + goto loop; + loopEnd: + + assert TVT($r2[Tj], $vt); + assert TVL($r2[Tj]); + Tj := ebp; + assert TO(1); +} + +procedure scanObjectSparse($vt:int) + requires ecx == $vt; + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires Tj < Tk; + requires TV(Tj); + requires $vt == $AbsMem[$r2[Tj]][1]; + requires tag($vt) == ?SPARSE_TAG; + modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures RExtend(old($r2), $r2); +{ + var save1:int; + var save2:int; + var save3:int; + var save4:int; + assert TO(numFields($r2[Tj])); + + esi := 1; + + assert TO(numFields($r2[Tj])); + assert TVT($toAbs[Tj], $vt); + call ebp := RoLoad32(ecx + ?VT_BASE_LENGTH); + call edx := RoLoad32(ecx + ?VT_MASK); + assert TVL($r2[Tj]); + + esi := 1; + //while (esi < 8) + loop: + assert edx == mask($vt) && ebp == 4 * numFields($r2[Tj]); + assert TSlot(esi) && 0 < esi && esi <= 8; + assert Pointer($r2, Tj, $r2[Tj]); + assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj + 4 * numFields($r2[Tj]), Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert RExtend(old($r2), $r2); + assert ObjInvBase(Tj, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($r2[Tj]) ==> + between(1, esi, fieldToSlot($vt, j - 2)) ==> + ObjInvField(Tj, j, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice)); + assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($r2[Tj]) ==> + !between(1, esi, fieldToSlot($vt, j - 2)) ==> + ObjInvField(Tj, j, $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice)); + assert $toAbs[Tj] != NO_ABS && $toAbs[Tj] == $r2[Tj]; + if (esi >= 8) { goto loopEnd; } + + assert TO(0) && TO(1); + assert TVT($toAbs[Tj], $vt); + assert TO(getNib(edx, 4 * esi) + 1); + + //if (getNib(edx, 4 * esi) != 0) + ecx := 0; + call ecx := Lea(ecx + 4 * esi); + ebx := edx; + call ebx := Shr(ebx, ecx); + call ebx := And(ebx, 15); + if (ebx == 0) { goto skip1; } + eax := Tj; + call ecx := GcLoad(eax + 4 * ebx + 4); + + //if (gcAddrEx(ecx)) + if (ecx < ?gcLo) { goto skip2; } + if (ecx > ?gcHi) { goto skip2; } + assert TV(ecx - 4); + call reach($toAbs[Tj], 1 + ebx, $Time); + + save1 := esi; + save2 := ebp; + save3 := edx; + save4 := ebx; + call forwardFromspacePtr(ecx, $AbsMem[$toAbs[Tj]][1 + getNib(edx, 4 * esi)], Tj + ebp); + esi := save1; + ebp := save2; + edx := save3; + ebx := save4; + + ecx := Tj; + call GcStore(ecx + 4 * ebx + 4, eax); + skip2: + skip1: + + call esi := Add(esi, 1); + goto loop; + loopEnd: + + assert TVT($toAbs[Tj], $vt); + assert TVL($r2[Tj]); + call Tj := Add(Tj, ebp); + assert TO(1); +} + +procedure scanObjectOtherVectorNoPointers($vt:int) + requires ecx == $vt; + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires Tj < Tk; + requires TV(Tj); + requires $vt == $AbsMem[$r2[Tj]][1]; + requires tag($vt) == ?OTHER_VECTOR_TAG; + requires arrayOf($vt) != ?TYPE_STRUCT || (arrayOf($vt) == ?TYPE_STRUCT && mask(arrayElementClass($vt)) == ?SPARSE_TAG); + modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures RExtend(old($r2), $r2); +{ + edx := ecx; + ecx := Tj; + esp := esp - 4; call GetSize(Tj, $vt, $r2, $r1); + + assert TO(numFields($r2[Tj])); + assert TVT($r2[Tj], $vt); + call Tj := Add(Tj, eax); +} + +procedure scanObjectOtherVectorPointersSparseFields($vt:int, $jj:int, $index:int) + requires edx == mask(arrayElementClass($vt)); + requires edi == $jj; + requires Tj < Tk; + requires TV(Tj); + requires $vt == $AbsMem[$r2[Tj]][1]; + requires tag($vt) == ?OTHER_VECTOR_TAG; + requires arrayOf($vt) == ?TYPE_STRUCT && tag(arrayElementClass($vt)) == ?SPARSE_TAG; + requires TO($jj) && $jj == baseWords($vt) + Mult(arrayElementWords($vt), $index); + requires $jj < numFields($r2[Tj]); + requires TO($index) && 0 <= $index; // && $index <= nElems; + requires Mult(arrayElementWords($vt), $index) >= 0; + requires reached($toAbs[Tj], $Time); + requires Pointer($r2, Tj, $r2[Tj]); + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj + 4 * numFields($r2[Tj]), Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires ObjInvPartial(Tj, 0, $jj, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires ObjInvPartial(Tj, $jj, numFields($r2[Tj]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires $toAbs[Tj] != NO_ABS && $toAbs[Tj] == $r2[Tj]; + requires (forall j:int::{TO(j)} TO(j) ==> + between(0, arrayElementWords($vt), j - $jj) ==> + ObjInvField(Tj, j, $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice)); + + modifies $r2, $GcMem, $toAbs, Tk, $gcSlice; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + ensures Pointer($r2, Tj, $r2[Tj]); + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj + 4 * numFields($r2[Tj]), Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures RExtend(old($r2), $r2); + ensures ObjInvPartial(Tj, 0, $jj, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures ObjInvPartial(Tj, $jj + arrayElementWords($vt), numFields($r2[Tj]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures $toAbs[Tj] != NO_ABS && $toAbs[Tj] == $r2[Tj]; + ensures (forall j:int::{TO(j)} TO(j) ==> + between(0, arrayElementWords($vt), j - $jj) ==> + between(1, 8, fieldToSlot(arrayElementClass($vt), j - $jj)) ==> + ObjInvField(Tj, j, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice)); + ensures (forall j:int::{TO(j)} TO(j) ==> + between(0, arrayElementWords($vt), j - $jj) ==> + !between(1, 8, fieldToSlot(arrayElementClass($vt), j - $jj)) ==> + ObjInvField(Tj, j, $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice)); + ensures edi == old(edi); + ensures edx == old(edx); +{ + var save1:int; + var save2:int; + var save3:int; + var save4:int; + + assert TO(numFields($r2[Tj])); + assert TO(2); + assert TVT($r2[Tj], $vt); + assert TVL($r2[Tj]); + + esi := 1; + //while (esi < 8) + loop: + assert TSlot(esi) && 0 < esi && esi <= 8; + assert edx == mask(arrayElementClass($vt)); + assert edi == $jj; + assert Pointer($r2, Tj, $r2[Tj]); + assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj + 4 * numFields($r2[Tj]), Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert RExtend(old($r2), $r2); + assert ObjInvPartial(Tj, 0, $jj, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert ObjInvPartial(Tj, $jj + arrayElementWords($vt), numFields($r2[Tj]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert $toAbs[Tj] != NO_ABS && $toAbs[Tj] == $r2[Tj]; + assert (forall j:int::{TO(j)} TO(j) ==> + between(0, arrayElementWords($vt), j - $jj) ==> + between(1, esi, fieldToSlot(arrayElementClass($vt), j - $jj)) ==> + ObjInvField(Tj, j, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice)); + assert (forall j:int::{TO(j)} TO(j) ==> + between(0, arrayElementWords($vt), j - $jj) ==> + !between(1, esi, fieldToSlot(arrayElementClass($vt), j - $jj)) ==> + ObjInvField(Tj, j, $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice)); + if (esi >= 8) { goto loopEnd; } + + // ebx := getNib(mask(arrayElementClass($vt)), 4 * esi); + ecx := 0; + call ecx := Lea(ecx + 4 * esi); + ebx := edx; + call ebx := Shr(ebx, ecx); + call ebx := And(ebx, 15); + assert ebx == getNib(mask(arrayElementClass($vt)), 4 * esi); + + // if (ebx != 0) + if (ebx == 0) { goto skip1; } + call ebx := Sub(ebx, 1); + call ebx := Add(ebx, edi); + assert TO(ebx); + + eax := Tj; + call ecx := GcLoad(eax + 4 * ebx); + //if (gcAddrEx(ecx)) + if (ecx < ?gcLo) { goto skip2; } + if (ecx > ?gcHi) { goto skip2; } + + assert TV(ecx - 4); + call reach($toAbs[Tj], ebx, $Time); + + assert TO(0); + assert TO(1); + + save1 := esi; + save2 := edi; + save3 := edx; + save4 := ebx; + call forwardFromspacePtr(ecx, $AbsMem[$toAbs[Tj]][ebx], Tj + 4 * numFields($r2[Tj])); + esi := save1; + edi := save2; + edx := save3; + ebx := save4; + + ecx := Tj; + call GcStore(ecx + 4 * ebx, eax); + + skip2: + skip1: + call esi := Add(esi, 1); + goto loop; + loopEnd: +} + +procedure scanObjectOtherVectorPointers($vt:int) + requires ecx == $vt; + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires Tj < Tk; + requires TV(Tj); + requires $vt == $AbsMem[$r2[Tj]][1]; + requires tag($vt) == ?OTHER_VECTOR_TAG; + requires arrayOf($vt) == ?TYPE_STRUCT && tag(arrayElementClass($vt)) == ?SPARSE_TAG; + modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures RExtend(old($r2), $r2); +{ + var save1:int; + var save2:int; + + var $index:int; + + assert TO(numFields($r2[Tj])); + assert TO(2); + assert TVT($r2[Tj], $vt); + assert TVL($r2[Tj]); + + $index := 0; + + call edi := RoLoad32(ecx + ?VT_BASE_LENGTH); + call ebx := RoLoad32(ecx + ?VT_ARRAY_ELEMENT_SIZE); + edx := Tj; + call edx := GcLoad(edx + 8); + eax := ebx; + call eax, edx := MulChecked(eax, edx); + call eax := AddChecked(eax, edi); + call eax := AddChecked(eax, 3); + edx := 3; + call edx := Not(edx); + call eax := And(eax, edx); + ebp := eax; + call edi := Shr(edi, 2); + + call ebx := Shr(ebx, 2); // arrayElementWords($vt) + + assert TVM(ebx, 0); + call edx := RoLoad32(ecx + ?VT_ARRAY_ELEMENT_CLASS); + call edx := RoLoad32(edx + ?VT_MASK); + + + //while (4 * edi < ebp) + loop: + assert TO($index) && 0 <= $index; + assert Mult(ebx, $index) >= 0; + assert TO(edi) && edi == baseWords($vt) + Mult(ebx, $index); + + assert ebp == 4 * numFields($r2[Tj]); + assert edx == mask(arrayElementClass($vt)); + assert ebx == arrayElementWords($vt); + assert 4 * edi <= ebp; + + assert Pointer($r2, Tj, $r2[Tj]); + assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj + 4 * numFields($r2[Tj]), Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert RExtend(old($r2), $r2); + assert ObjInvPartial(Tj, 0, edi, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert ObjInvPartial(Tj, edi, numFields($r2[Tj]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert $toAbs[Tj] != NO_ABS && $toAbs[Tj] == $r2[Tj]; + eax := 0; + call eax := Lea(eax + 4 * edi); + if (eax >= ebp) { goto loopEnd; } + + save1 := ebx; + save2 := ebp; + call scanObjectOtherVectorPointersSparseFields($vt, edi, $index); + ebx := save1; + ebp := save2; + + assert TVM3(ebx, $index, 1); + assert TVM(ebx, $index); + $index := $index + 1; + call edi := Add(edi, ebx); + goto loop; + loopEnd: + + call Tj := Add(Tj, ebp); + assert TO(1); +} + +procedure scanObjectPtrArray($vt:int) + requires ecx == $vt; + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires Tj < Tk; + requires TV(Tj); + requires $vt == $AbsMem[$r2[Tj]][1]; + requires tag($vt) == ?PTR_ARRAY_TAG; + modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures RExtend(old($r2), $r2); +{ + var $ind:int; + var save1:int; + var save2:int; + + assert TO(numFields($r2[Tj])); + assert TO(3); + assert TVT($r2[Tj], $vt); + assert TVL($r2[Tj]); + + ebx := Tj; + call ebp := GcLoad(ebx + 12); + call esi := RoLoad32(ecx + ?VT_BASE_LENGTH); + // size := pad(esi + 4 * ebp); + call ebp := AddChecked(ebp, ebp); + call ebp := AddChecked(ebp, ebp); + call ebp := AddChecked(ebp, esi); + call ebp := AddChecked(ebp, 3); + eax := 3; + call eax := Not(eax); + call ebp := And(ebp, eax); + call esi := Shr(esi, 2); + $ind := esi; + + call edi := Lea(ebx + 4 * esi); + call ebp := Add(ebp, ebx); + + //while (edi < ebp) + loop: + assert edi == Tj + 4 * $ind; + assert ebp == Tj + 4 * numFields($r2[Tj]); + assert TO($ind) && baseWords($vt) <= $ind; // && $ind <= nElems + 3; + assert Pointer($r2, Tj, $r2[Tj]); + assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj + 4 * numFields($r2[Tj]), Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert RExtend(old($r2), $r2); + assert ObjInvPartial(Tj, 0, $ind, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert ObjInvPartial(Tj, $ind, numFields($r2[Tj]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert $toAbs[Tj] != NO_ABS && $toAbs[Tj] == $r2[Tj]; + if (edi >= ebp) { goto loopEnd; } + + assert TO(0) && TO(1) && TO(3); + assert TVT($r2[Tj], $vt); + assert TVL($r2[Tj]); + call ecx := GcLoad(edi); + //if (gcAddrEx(ecx)) + if (ecx < ?gcLo) { goto skip1; } + if (ecx > ?gcHi) { goto skip1; } + assert TV(ecx - 4); + call reach($toAbs[Tj], $ind, $Time); + + save1 := edi; + save2 := ebp; + call forwardFromspacePtr(ecx, $AbsMem[$toAbs[Tj]][$ind], ebp); + edi := save1; + ebp := save2; + + call GcStore(edi, eax); + skip1: + + $ind := $ind + 1; + call edi := Add(edi, 4); + goto loop; + loopEnd: + + Tj := ebp; +} + +procedure scanObjectPtrVector($vt:int) + requires ecx == $vt; + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires Tj < Tk; + requires TV(Tj); + requires $vt == $AbsMem[$r2[Tj]][1]; + requires tag($vt) == ?PTR_VECTOR_TAG; + modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures RExtend(old($r2), $r2); +{ + var $ind:int; + var save1:int; + var save2:int; + assert TO(numFields($r2[Tj])); + assert TO(2); + assert TVT($r2[Tj], $vt); + assert TVL($r2[Tj]); + + ebx := Tj; + call ebp := GcLoad(ebx + 8); + call esi := RoLoad32(ecx + ?VT_BASE_LENGTH); + // size := pad(esi + 4 * ebp); + call ebp := AddChecked(ebp, ebp); + call ebp := AddChecked(ebp, ebp); + call ebp := AddChecked(ebp, esi); + call ebp := AddChecked(ebp, 3); + eax := 3; + call eax := Not(eax); + call ebp := And(ebp, eax); + call esi := Shr(esi, 2); + $ind := esi; + + call edi := Lea(ebx + 4 * esi); + call ebp := Add(ebp, ebx); + + //while (edi < ebp) + loop: + assert edi == Tj + 4 * $ind; + assert ebp == Tj + 4 * numFields($r2[Tj]); + assert TO($ind) && baseWords($vt) <= $ind; // && $ind <= nElems + 3; + assert Pointer($r2, Tj, $r2[Tj]); + assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj + 4 * numFields($r2[Tj]), Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert RExtend(old($r2), $r2); + assert ObjInvPartial(Tj, 0, $ind, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert ObjInvPartial(Tj, $ind, numFields($r2[Tj]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert $toAbs[Tj] != NO_ABS && $toAbs[Tj] == $r2[Tj]; + if (edi >= ebp) { goto loopEnd; } + + assert TO(0) && TO(1) && TO(2); + assert TVT($r2[Tj], $vt); + call ecx := GcLoad(edi); + //if (gcAddrEx(ecx)) + if (ecx < ?gcLo) { goto skip1; } + if (ecx > ?gcHi) { goto skip1; } + assert TV(ecx - 4); + call reach($toAbs[Tj], $ind, $Time); + + save1 := edi; + save2 := ebp; + call forwardFromspacePtr(ecx, $AbsMem[$toAbs[Tj]][$ind], ebp); + edi := save1; + ebp := save2; + + call GcStore(edi, eax); + skip1: + + $ind := $ind + 1; + call edi := Add(edi, 4); + goto loop; + loopEnd: + + Tj := ebp; +} + +procedure scanObjectOtherArrayNoPointers($vt:int) + requires ecx == $vt; + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires Tj < Tk; + requires TV(Tj); + requires $vt == $AbsMem[$r2[Tj]][1]; + requires tag($vt) == ?OTHER_ARRAY_TAG; + requires arrayOf($vt) != ?TYPE_STRUCT; + modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures RExtend(old($r2), $r2); +{ + edx := ecx; + ecx := Tj; + esp := esp - 4; call GetSize(Tj, $vt, $r2, $r1); + + assert TO(numFields($r2[Tj])); + assert TVT($r2[Tj], $vt); + call Tj := Add(Tj, eax); +} + +procedure scanObjectString($vt:int) + requires ecx == $vt; + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires Tj < Tk; + requires TV(Tj); + requires $vt == $AbsMem[$r2[Tj]][1]; + requires tag($vt) == ?STRING_TAG; + modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures RExtend(old($r2), $r2); +{ + edx := ecx; + ecx := Tj; + esp := esp - 4; call GetSize(Tj, $vt, $r2, $r1); + + assert TO(numFields($r2[Tj])); + assert TVT($r2[Tj], $vt); + call Tj := Add(Tj, eax); +} + +procedure scanObjectOther($vt:int) + requires ecx == $vt; + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires Tj < Tk; + requires TV(Tj); + requires $vt == $AbsMem[$r2[Tj]][1]; + requires isOtherTag(tag($vt)); + modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures RExtend(old($r2), $r2); +{ + var save1:int; + var save2:int; + var save3:int; + var save4:int; + var save5:int; + + assert TO(numFields($r2[Tj])); + assert TVT($toAbs[Tj], $vt); + assert TVL($r2[Tj]); + + call edx := RoLoad32(ecx + ?VT_MASK); + call edi := RoLoad32(edx); + call ebp := RoLoad32(ecx + ?VT_BASE_LENGTH); + + esi := 1; + + //while (esi < edi + 1) + loop: + assert ebp == 4 * numFields($r2[Tj]); + assert edx == mask($vt); + assert edi == ro32(mask($vt)); + assert TSlot(esi) && 0 < esi && esi <= ro32(mask($vt)) + 1; + assert Pointer($r2, Tj, $r2[Tj]); + assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj + 4 * numFields($r2[Tj]), Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert RExtend(old($r2), $r2); + assert ObjInvBase(Tj, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($r2[Tj]) ==> + between(1, esi, fieldToSlot($vt, j)) ==> + ObjInvField(Tj, j, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice)); + assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($r2[Tj]) ==> + !between(1, esi, fieldToSlot($vt, j)) ==> + ObjInvField(Tj, j, $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice)); + assert $toAbs[Tj] != NO_ABS && $toAbs[Tj] == $r2[Tj]; + if (esi > edi) { goto loopEnd; } + + assert TO(0) && TO(1); + assert TVT($toAbs[Tj], $vt); + assert TVL($r2[Tj]); + assert TO(ro32(mask($vt) + 4 * esi) + 1); + call ebx := RoLoad32(edx + 4 * esi); + //if (ebx != 0) + if (ebx == 0) { goto skip1; } + eax := Tj; + call ecx := GcLoad(eax + 4 * ebx + 4); + + //if (gcAddrEx(ecx)) + if (ecx < ?gcLo) { goto skip2; } + if (ecx > ?gcHi) { goto skip2; } + assert TV(ecx - 4); + call reach($toAbs[Tj], 1 + ro32(edx + 4 * esi), $Time); + + save1 := ebx; + save2 := esi; + save3 := edi; + save4 := ebp; + save5 := edx; + call forwardFromspacePtr(ecx, $AbsMem[$toAbs[Tj]][1 + ro32(edx + 4 * esi)], Tj + ebp); + ebx := save1; + esi := save2; + edi := save3; + ebp := save4; + edx := save5; + + ecx := Tj; + call GcStore(ecx + 4 * ebx + 4, eax); + skip2: + skip1: + call esi := AddChecked(esi, 1); + goto loop; + loopEnd: + + assert TVT($toAbs[Tj], $vt); + assert TVL($r2[Tj]); + call Tj := Add(Tj, ebp); +} + +procedure scanObject() + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires Tj < Tk; + requires TV(Tj); + modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures RExtend(old($r2), $r2); +{ + var $vt:int; + + assert TO(1); + ecx := Tj; + call ecx := GcLoad(ecx + 4); + $vt := ecx; + + assert TO(numFields($r2[Tj])); + + call readTag($toAbs[Tj], $vt); + + if (eax != ?SPARSE_TAG) { goto skip1; } + call scanObjectSparse($vt); + goto end; + skip1: + + if (eax != ?DENSE_TAG) { goto skip2; } + call scanObjectDense($vt); + goto end; + skip2: + + if (eax != ?STRING_TAG) { goto skip3; } + call scanObjectString($vt); + goto end; + skip3: + + if (eax != ?PTR_VECTOR_TAG) { goto skip4; } + call scanObjectPtrVector($vt); + goto end; + skip4: + + if (eax != ?OTHER_VECTOR_TAG) { goto skip5; } + call readArrayOf($r2[Tj], $vt); + call readElementInfo($r2[Tj], $vt); + if (ebp != ?TYPE_STRUCT) { goto noPoint; } + if (ebp != ?TYPE_STRUCT) { goto vecSkip1; } + if (edi != ?SPARSE_TAG) { goto vecSkip1; } + noPoint: + call scanObjectOtherVectorNoPointers($vt); + goto end; + vecSkip1: + if (ebp != ?TYPE_STRUCT) { goto vecSkip2; } + + //if (tag(arrayElementClass(vt)) != ?SPARSE_TAG) { goto vecSkip2; } + eax := edi; + call eax := And(eax, 15); + if (eax != ?SPARSE_TAG) { goto vecSkip2; } + + call scanObjectOtherVectorPointers($vt); + goto end; + + vecSkip2: + assert !( + (ebp != ?TYPE_STRUCT) + || (ebp == ?TYPE_STRUCT && edi == ?SPARSE_TAG) + || (ebp == ?TYPE_STRUCT && tag(arrayElementClass($vt)) == ?SPARSE_TAG)); + call DebugBreak(); + + skip5: + + if (eax != ?PTR_ARRAY_TAG) { goto skip6; } + call scanObjectPtrArray($vt); + goto end; + skip6: + + if (eax != ?OTHER_ARRAY_TAG) { goto skip7; } + call readArrayOf($r2[Tj], $vt); + if (ebp == ?TYPE_STRUCT) { goto arraySkip1; } + call scanObjectOtherArrayNoPointers($vt); + goto end; + arraySkip1: + call DebugBreak(); + goto end; + skip7: + + call scanObjectOther($vt); + + end: +} + +procedure scanObjects() + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + modifies $r2, $GcMem, $toAbs, Tj, Tk, $gcSlice; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures RExtend(old($r2), $r2); + ensures Tj == Tk; +{ + entry: + loop: + assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert RExtend(old($r2), $r2); + eax := Tk; + if (Tj >= eax) { goto exit; } + call scanObject(); + goto loop; + exit: +} + +procedure FromInteriorPointer($iptr:int, $offset:int, $abs:int) + requires ecx == $iptr; + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires 0 <= $offset && $offset <= 4 * numFields($abs) - 4; + requires Pointer($r1, $iptr - $offset - 4, $abs); + modifies eax, ebx, ecx, edx, esp; + ensures eax == $offset; +{ + var save1:int; + var save2:int; + + eax := 0; +// while ($r1[$iptr - eax - 4] == NO_ABS) + loop: + assert $iptr - $offset <= $iptr - eax && $iptr - eax <= $iptr; + assert TV($iptr - eax - 4) && TV($iptr - $offset - 4); + assert ecx == $iptr; + + assert TV(?gcLo); + assert TO($iptr - eax - 4 - HeapLo); + + ebx := ecx; + call ebx := Sub(ebx, eax); + call ebx := Sub(ebx, 4); + call ebx := Sub(ebx, Fi); + edx := ebx; + call edx := And(edx, 3); + call __andAligned(ebx); + call __addAligned(Fi, ebx); + if (edx != 0) + { + goto skip1; + } + + save1 := eax; + save2 := ecx; + eax := BF; + call bb4GetBit($r1, NO_ABS, Fi, Fi, Fi, Fl, $iptr - save1 - 4, ?gcLo + BitIndex(HeapLo, Fi), ?gcLo + BitIndex(HeapLo, Fl)); + eax := save1; + ecx := save2; + + if (ebx == 0) { goto skip1; } + esp := esp + 4; return; + skip1: + call eax := Add(eax, 1); + goto loop; +} + +procedure scanStackUpdateInvs($r:[int]int, $f1:int, $f2:int, $frame:int, $addr:int, $v:int) + requires $FrameSlice[$addr] == $frame; + requires !($f1 <= $frame && $frame < $f2); + requires (forall f:int::{TV(f)} TV(f) ==> $f1 <= f && f < $f2 ==> + FrameInv(f, $FrameLayout[f], $r, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + ensures (forall f:int::{TV(f)} TV(f) ==> $f1 <= f && f < $f2 ==> + FrameInv(f, $FrameLayout[f], $r, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem[$addr := $v], $FrameAbs, $FrameOffset, $Time)); +{ + assert TO(0) && TO(1); + assert (forall f:int::{TV(f)} TV(f) ==> TVF($FrameLayout[f]) && TVFT(f)); +} + +procedure scanStackWordDenseCopy($frame:int, $addr:int, $desc:int, $addrp:int, $p:int, $args:int) + requires ecx == $addrp; + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires $desc == frameDescriptor($FrameLayout[$frame]); + requires $addr == $FrameAddr[$frame]; + requires $FrameSlice[$addrp] == $frame; + requires $args == frameLayoutArgs($FrameLayout[$frame]); + requires $addrp == $addr + 4 * $p; + requires getBit($desc, 0) && !getBit($desc, 1) && and(shr($desc, 6), 1023) == 0; + requires frameHasPtr($FrameLayout[$frame], $p); + requires 0 <= $frame && $frame < $FrameCount && TV($frame); + requires (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f < $frame ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + requires (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + + requires $p <= 1 + $args && $p > $args - 1 - 16 && TO($p); + requires (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f < $frame ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + requires (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + requires FrameNextInv($frame, $FrameMem[$FrameAddr[$frame] + 4], $FrameMem[$FrameAddr[$frame]], $FrameAddr, $FrameLayout); + requires (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j <= $p ==> + $FrameSlice[$addr + 4 * j] == $frame + && InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r1, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j]) + && (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time)) + ); + requires (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j > $p ==> + $FrameSlice[$addr + 4 * j] == $frame + && InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r2, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j]) + && (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time)) + ); + + modifies $r2, $GcMem, $toAbs, Tk, $gcSlice, $FrameMem; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f < $frame ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + ensures (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + ensures FrameNextInv($frame, $FrameMem[$FrameAddr[$frame] + 4], $FrameMem[$FrameAddr[$frame]], $FrameAddr, $FrameLayout); + ensures (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j <= $p - 1 ==> + $FrameSlice[$addr + 4 * j] == $frame + && InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r1, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j]) + && (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time)) + ); + ensures (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j > $p - 1 ==> + $FrameSlice[$addr + 4 * j] == $frame + && InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r2, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j]) + && (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time)) + ); + + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures RExtend(old($r2), $r2); +{ + var save1:int; + var v:int; + var offset:int; + assert TVF($FrameLayout[$frame]); + assert TV($FrameMem[$addrp] - 4); + + call eax := FrameLoad(($frame), ecx); + //if (gcAddrEx(eax)) + if (eax < ?gcLo) { goto skip1; } + if (eax > ?gcHi) { goto skip1; } + save1 := ecx; + v := eax; + + ecx := eax; + esp := esp - 4; call FromInteriorPointer(v, $FrameOffset[$addrp], $FrameAbs[$frame][$p]); + offset := eax; + + ecx := v; + call ecx := Sub(ecx, eax); + assert TV(ecx - 4); + + call forwardFromspacePtr(ecx, $FrameAbs[$frame][$p], Tj); + + assert TV(eax - 4); + call eax := Add(eax, offset); + call scanStackUpdateInvs($r1, 0, $frame, $frame, $addrp, eax); + call scanStackUpdateInvs($r2, $frame + 1, $FrameCount, $frame, $addrp, eax); + + ecx := save1; + call FrameStore(($frame), ecx, eax); + skip1: +} + +procedure scanStackDenseCopy($frame:int, $addr:int, $desc:int) + requires ecx == $addr; + requires edx == $desc; + requires $desc == frameDescriptor($FrameLayout[$frame]); + requires $addr == $FrameAddr[$frame]; + requires getBit($desc, 0) && !getBit($desc, 1) && and(shr($desc, 6), 1023) == 0; + requires 0 <= $frame && $frame < $FrameCount && TV($frame); + requires (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f <= $frame ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + requires (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + modifies $r2, $GcMem, $toAbs, Tk, $gcSlice, $FrameMem; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f <= $frame - 1 ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + ensures (forall f:int::{TV(f)} TV(f) ==> $frame - 1 < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures RExtend(old($r2), $r2); +{ + var b:int; + var v:int; + var offset:int; + var args:int; + var addr:int; + var desc:int; + var addrp:int; + assert TVF($FrameLayout[$frame]); + assert TO(0); + + addr := ecx; + desc := edx; + + eax := edx; + call eax := Shr(eax, 2); + call eax := And(eax, 15); + args := eax; // frameLayoutArgs($FrameLayout[$frame]); + b := 0; + ebx := 0; + call ebx := Lea(ebx + 4 * eax + 4); + call ebx := AddChecked(ebx, ecx); + addrp := ebx; + + //while (b < args) + loop1: + assert addrp == $addr + 4 * (args + 1 - b); + assert $frame < $FrameCount && TV($frame); + assert 0 <= b && b <= args && TO(args + 1 - b); + assert (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f < $frame ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + assert (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + + assert FrameNextInv($frame, $FrameMem[$FrameAddr[$frame] + 4], $FrameMem[$FrameAddr[$frame]], $FrameAddr, $FrameLayout); + + assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j <= (args + 1 - b) ==> + $FrameSlice[$addr + 4 * j] == $frame + && InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r1, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j]) + && (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time)) + ); + + assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j > (args + 1 - b) ==> + $FrameSlice[$addr + 4 * j] == $frame + && InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r2, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j]) + && (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time)) + ); + + assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert RExtend(old($r2), $r2); + eax := b; + if (eax >= args) { goto loopEnd1; } + + assert TVF($FrameLayout[$frame]); + call ecx := Lea(eax + 16); + ebx := desc; + call ebx := Shr(ebx, ecx); + call ebx := And(ebx, 1); + //if (getBit($desc, 16 + b)) + if (ebx != 1) { goto skip1; } + ecx := addrp; + call scanStackWordDenseCopy($frame, $addr, $desc, $addr + 4 * (args + 1 - b), args + 1 - b, args); + skip1: + + call b := Add(b, 1); + call addrp := Sub(addrp, 4); + goto loop1; + loopEnd1: + + assert TO(0); + assert TO(1); + assert TV($FrameMem[$addr]); + assert TV($FrameMem[$addr] - 4); + + call addrp := SubChecked(addrp, 8); + + //while (b < 16) + loop2: + assert addrp == $addr + 4 * (args - 1 - b); + assert $frame < $FrameCount && TV($frame); + assert (args - 1 - b) < 0 && (args - 1 - b) <= 1 + args && TO(args - 1 - b); + assert (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f < $frame ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + assert (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + + assert FrameNextInv($frame, $FrameMem[$FrameAddr[$frame] + 4], $FrameMem[$FrameAddr[$frame]], $FrameAddr, $FrameLayout); + + assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j <= (args - 1 - b) ==> + $FrameSlice[$addr + 4 * j] == $frame + && InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r1, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j]) + && (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time)) + ); + + assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j > (args - 1 - b) ==> + $FrameSlice[$addr + 4 * j] == $frame + && InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r2, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j]) + && (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time)) + ); + + assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert RExtend(old($r2), $r2); + eax := b; + if (eax >= 16) { goto loopEnd2; } + + assert TVF($FrameLayout[$frame]); + call ecx := Lea(eax + 16); + ebx := desc; + call ebx := Shr(ebx, ecx); + call ebx := And(ebx, 1); + //if (getBit($desc, 16 + b)) + if (ebx != 1) { goto skip2; } + ecx := addrp; + call scanStackWordDenseCopy($frame, $addr, $desc, $addr + 4 * (args - 1 - b), args - 1 - b, args); + skip2: + call b := Add(b, 1); + call addrp := SubChecked(addrp, 4); + goto loop2; + loopEnd2: +} + +procedure scanStackSparse8Copy($frame:int, $addr:int, $desc:int) + requires ecx == $addr; + requires edx == $desc; + requires $desc == frameDescriptor($FrameLayout[$frame]); + requires $addr == $FrameAddr[$frame]; + requires !getBit($desc, 0) && ro32($desc) == 4096; + requires 0 <= $frame && $frame < $FrameCount && TV($frame); + requires (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f <= $frame ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + requires (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + modifies $r2, $GcMem, $toAbs, Tk, $gcSlice, $FrameMem; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f <= $frame - 1 ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + ensures (forall f:int::{TV(f)} TV(f) ==> $frame - 1 < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures RExtend(old($r2), $r2); +{ + var p:int; + var v:int; + var addr:int; + var desc:int; + var addrp:int; + var offset:int; + var count:int; + assert TVF($FrameLayout[$frame]); + assert TO(0); + + addr := ecx; + desc := edx; + + p := 0; + call eax := RoLoadU8(edx + 4); + count := eax; + //while (p < count) + loop: + assert p >= 0 && TSlot(p); + + assert (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f < $frame ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + assert (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + + assert FrameNextInv($frame, $FrameMem[$FrameAddr[$frame] + 4], $FrameMem[$FrameAddr[$frame]], $FrameAddr, $FrameLayout); + + assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && !between(0, p, frameFieldToSlot($FrameLayout[$frame], j)) ==> + $FrameSlice[addr + 4 * j] == $frame + && InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r1, $FrameMem[addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[addr + 4 * j]) + && (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time)) + ); + + assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && between(0, p, frameFieldToSlot($FrameLayout[$frame], j)) ==> + $FrameSlice[addr + 4 * j] == $frame + && InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r2, $FrameMem[addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[addr + 4 * j]) + && (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time)) + ); + + assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert RExtend(old($r2), $r2); + eax := p; + if (eax >= count) { goto loopEnd; } + + assert TO(roS8(desc + 6 + p)); + assert TV($FrameMem[addr + 4 * roS8(desc + 6 + p)] - 4); + ebx := desc; + esi := addr; + call ebp := RoLoadS8(ebx + 1 * eax + 6); + call ebp := LeaSignedIndex(esi, 4, ebp, 0); + addrp := ebp; + call ecx := FrameLoad(($frame), ebp); + //if (gcAddrEx(ecx)) + if (ecx < ?gcLo) { goto skip1; } + if (ecx > ?gcHi) { goto skip1; } + v := ecx; + esp := esp - 4; call FromInteriorPointer(v, $FrameOffset[addr + 4 * roS8(desc + 6 + p)], $FrameAbs[$frame][roS8(desc + 6 + p)]); + offset := eax; + + ecx := v; + call ecx := Sub(ecx, eax); + assert TV(ecx - 4); + call forwardFromspacePtr(ecx, $FrameAbs[$frame][roS8(desc + 6 + p)], Tj); + assert TV(eax - 4); + call eax := Add(eax, offset); + call scanStackUpdateInvs($r1, 0, $frame, $frame, addr + 4 * roS8(desc + 6 + p), eax); + call scanStackUpdateInvs($r2, $frame + 1, $FrameCount, $frame, addr + 4 * roS8(desc + 6 + p), eax); + ebx := addrp; + call FrameStore(($frame), ebx, eax); + skip1: + + // This is just here to improve performance: + assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && + p == frameFieldToSlot($FrameLayout[$frame], j) ==> j == roS8(desc + 6 + p)); + + call p := Add(p, 1); + goto loop; + loopEnd: +} + +procedure scanStackCopy($ra:int, $nextFp:int) + requires ecx == $ra && word($ra); + requires edx == $nextFp; + requires FrameNextInv($FrameCount, $ra, $nextFp, $FrameAddr, $FrameLayout); + requires StackInv($r1, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + modifies $r2, $GcMem, $toAbs, Tk, $gcSlice, $FrameMem; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures StackInv($r2, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures RExtend(old($r2), $r2); +{ + var $frame:int; + var p:int; + var addr:int; + var desc:int; + var found:int; + var _ra:int; + var _nextFp:int; + _ra := ecx; + _nextFp := edx; + + $frame := $FrameCount - 1; + loop: + assert $frame < $FrameCount && TV($frame); + assert word(_ra); + assert FrameNextInv($frame + 1, _ra, _nextFp, $FrameAddr, $FrameLayout); + assert (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f <= $frame ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + assert (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert RExtend(old($r2), $r2); + + assert TVF($FrameLayout[$frame]); + + ecx := _ra; + edx := _nextFp; + esp := esp - 4; call TablesSearch($frame + 1, _ra, _nextFp); + desc := eax; + found := edx; + + if (edx == 0) { goto loopEnd; } + + ecx := _nextFp; + addr := ecx; + assert TV(addr); + assert TO(0); + assert TO(1); + call eax := FrameLoad(($frame), ecx); + _nextFp := eax; + call eax := FrameLoad(($frame), ecx + 4); + _ra := eax; + + // if (getBit(desc, 0) && !getBit(desc, 1) && and(shr(desc, 6), 1023) == 0) + eax := desc; + call eax := Shr(eax, 0); + call eax := And(eax, 1); + if (eax != 1) { goto skip1; } + eax := desc; + call eax := Shr(eax, 1); + call eax := And(eax, 1); + if (eax == 1) { goto skip1; } + eax := desc; + call eax := Shr(eax, 6); + call eax := And(eax, 1023); + if (eax != 0) { goto skip1; } + ecx := addr; + edx := desc; + call scanStackDenseCopy($frame, addr, desc); + $frame := $frame - 1; + goto loop; + skip1: + + // else if (!getBit(desc, 0) && ro32(desc) == 4096) + eax := desc; + call eax := Shr(eax, 0); + call eax := And(eax, 1); + if (eax == 1) { goto skip2; } + eax := desc; + call eax := RoLoad32(eax); + if (eax != 4096) { goto skip2; } + ecx := addr; + edx := desc; + call scanStackSparse8Copy($frame, addr, desc); + $frame := $frame - 1; + goto loop; + skip2: + + // else + assert !( (getBit(desc, 0) && !getBit(desc, 1) && and(shr(desc, 6), 1023) == 0) + || (!getBit(desc, 0) && ro32(desc) == 4096)); + call DebugBreak(); + + loopEnd: + assert $frame == 0 - 1; +} + +procedure scanStaticSectionCopy($section:int) + requires ecx == $section; + requires 0 <= $section && $section < ?sectionCount && TV($section); + requires (forall s:int::{TV(s)} TV(s) ==> $section <= s && s < ?sectionCount ==> + SectionInv(s, sectionBase(s), sectionEnd(s), $r1, $SectionMem, $SectionAbs, $Time)); + requires (forall s:int::{TV(s)} TV(s) ==> 0 <= s && s < $section ==> + SectionInv(s, sectionBase(s), sectionEnd(s), $r2, $SectionMem, $SectionAbs, $Time)); + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + modifies $r2, $GcMem, $toAbs, Tk, $gcSlice, $SectionMem; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures (forall s:int::{TV(s)} TV(s) ==> $section + 1 <= s && s < ?sectionCount ==> + SectionInv(s, sectionBase(s), sectionEnd(s), $r1, $SectionMem, $SectionAbs, $Time)); + ensures (forall s:int::{TV(s)} TV(s) ==> 0 <= s && s < $section + 1 ==> + SectionInv(s, sectionBase(s), sectionEnd(s), $r2, $SectionMem, $SectionAbs, $Time)); + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures RExtend(old($r2), $r2); +{ + var sEnd:int; + var addr:int; + var section:int; + var save1:int; + var save2:int; + var save3:int; + + section := ecx; + + assert TVS(section, 0); + + eax := ?dataSectionEnd; + call eax := RoLoad32(eax + 4 * ecx); + sEnd := eax; + + eax := ?staticDataPointerBitMap; + call edx := RoLoad32(eax + 4 * ecx); + + eax := ?dataSectionBase; + call eax := RoLoad32(eax + 4 * ecx); + addr := eax; + edi := eax; + + esi := 0; + //while (edi < sEnd) + loop: + assert edx == ro32(?staticDataPointerBitMap + 4 * $section); + assert edi == addr + 4 * esi; + assert 0 <= section && TV(section); + assert 0 <= esi && TO(esi); + assert (forall s:int::{TV(s)} TV(s) ==> section < s && s < ?sectionCount ==> + SectionInv(s, sectionBase(s), sectionEnd(s), $r1, $SectionMem, $SectionAbs, $Time)); + assert (forall s:int::{TV(s)} TV(s) ==> 0 <= s && s < section ==> + SectionInv(s, sectionBase(s), sectionEnd(s), $r2, $SectionMem, $SectionAbs, $Time)); + + assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && addr + 4 * j < sectionEnd(section) && j >= esi ==> + sectionSlice(addr + 4 * j) == section + && Value(sectionHasPtr(section, j), $r1, $SectionMem[addr + 4 * j], $SectionAbs[section][j]) + && (sectionHasPtr(section, j) && gcAddrEx($SectionMem[addr + 4 * j]) ==> reached($SectionAbs[section][j], $Time)) + ); + + assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && addr + 4 * j < sectionEnd(section) && j < esi ==> + sectionSlice(addr + 4 * j) == section + && Value(sectionHasPtr(section, j), $r2, $SectionMem[addr + 4 * j], $SectionAbs[section][j]) + && (sectionHasPtr(section, j) && gcAddrEx($SectionMem[addr + 4 * j]) ==> reached($SectionAbs[section][j], $Time)) + ); + + assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert RExtend(old($r2), $r2); + if (edi >= sEnd) { goto loopEnd; } + + assert TVS(section, esi); + eax := esi; + call eax := Shr(eax, 5); + call eax := RoLoad32(edx + 4 * eax); + // assert getBit(v, and(esi, 31)) == sectionHasPtr(section, esi); + //if (getBit(v, and(esi, 31))) + ecx := esi; + call ecx := And(ecx, 31); + call eax := Shr(eax, ecx); + call eax := And(eax, 1); + if (eax != 1) { goto skip1; } + assert TV($SectionMem[addr + 4 * esi] - 4); + call ecx := SectionLoad(edi); + //if (gcAddrEx(ecx)) + if (ecx < ?gcLo) { goto skip2; } + if (ecx > ?gcHi) { goto skip2; } + save1 := edi; + save2 := esi; + save3 := edx; + call forwardFromspacePtr(ecx, $SectionAbs[section][esi], Tj); + edi := save1; + esi := save2; + edx := save3; + + call SectionStore(edi, eax); + skip2: + skip1: + + call esi := Add(esi, 1); + call edi := AddChecked(edi, 4); + goto loop; + loopEnd: +} + +procedure scanStaticCopy() + requires StaticInv($r1, $SectionMem, $SectionAbs, $Time); + requires CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + modifies $r2, $GcMem, $toAbs, Tk, $gcSlice, $SectionMem; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures StaticInv($r2, $SectionMem, $SectionAbs, $Time); + ensures CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures RExtend(old($r2), $r2); +{ + var section:int; + section := 0; + //while (section < ?sectionCount) + loop: + assert 0 <= section && TV(section); + assert (forall s:int::{TV(s)} TV(s) ==> section <= s && s < ?sectionCount ==> + SectionInv(s, sectionBase(s), sectionEnd(s), $r1, $SectionMem, $SectionAbs, $Time)); + assert (forall s:int::{TV(s)} TV(s) ==> 0 <= s && s < section ==> + SectionInv(s, sectionBase(s), sectionEnd(s), $r2, $SectionMem, $SectionAbs, $Time)); + assert CopyGcInv($freshAbs, BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tj, Tk, Tk, Tl, $Time, $r1, $r2, true, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert RExtend(old($r2), $r2); + ecx := section; + if (ecx >= ?sectionCount) { goto loopEnd; } + + call scanStaticSectionCopy(section); + call section := AddChecked(section, 1); + goto loop; + loopEnd: +} + +procedure GarbageCollectCopying($ra:int, $nextFp:int) + requires ecx == $ra && word($ra); + requires edx == $nextFp; + requires FrameNextInv($FrameCount, $ra, $nextFp, $FrameAddr, $FrameLayout); + requires StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + requires StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + requires $freshAbs != NO_ABS; + requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $freshAbs); + + modifies $r1, $r2, $GcMem, $toAbs, $gcSlice, $SectionMem, $FrameMem; + modifies Ti, Tj, Tk, Tl, Fi, Fk, Fl, BF, BT; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + // postcondition same as precondition, plus reached: + ensures StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + ensures StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + ensures (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $freshAbs); + ensures (forall i:int::{TV(i)} TV(i) ==> Fi <= i && i < Fk && $toAbs[i] != NO_ABS ==> + reached($toAbs[i], $Time)); + ensures ebp == old(ebp); +{ + var saveEbp:int; + saveEbp := ebp; + + $r1 := $toAbs; + $r2 := MAP_NO_ABS; + eax := Ti; + Tj := eax; + Tk := eax; + + eax := BT; + ebx := Fi; + if (ebx != HeapLo) { goto skip1; } + ebx := HeapLo; + assert ?gcLo + BitIndex(HeapLo, Tl) == ebx; + goto skip2; + skip1: + ebx := BF; + assert ?gcLo + BitIndex(HeapLo, Tl) == ebx; + skip2: + assert TVM(32, (BitIndex(HeapLo, Fl) - BitIndex(HeapLo, Fi))); + assert TVM(32, (BitIndex(HeapLo, Tl) - BitIndex(HeapLo, Ti))); + + esp := esp - 4; call BB4Zero($toAbs, 0, NO_ABS, Ti, Ti, Ti, Tl, 0, ?gcLo + BitIndex(HeapLo, Ti), ?gcLo + BitIndex(HeapLo, Tl), Fi, ?gcLo + BitIndex(HeapLo, Fi)); + + call scanStackCopy($ra, $nextFp); + call scanStaticCopy(); + call scanObjects(); + + $toAbs := $r2; + + eax := Fi; + ebx := Ti; + Fi := ebx; + Ti := eax; + + eax := Fl; + ebx := Tl; + Fl := ebx; + Tl := eax; + + eax := Tk; + Fk := eax; + + eax := Ti; + Tk := eax; + Tj := eax; + + eax := BF; + ebx := BT; + BF := ebx; + BT := eax; + + ebp := saveEbp; + esp := esp + 4; return; +} + +procedure doAllocCopyingWord($ret:int, $ind:int) + requires esi == $ret + 4 * $ind; + + requires TO($ind) && $ind >= 0; + requires Aligned($ret) && Fk <= $ret && $ret + 4 * $ind + 4 <= Fl; + requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + requires (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind ==> $gcSlice[$ret + 4 * j] == $ret); + requires (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind ==> gcAddr($ret + 4 * j)); // REVIEW: necessary? + requires (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind ==> $GcMem[$ret + 4 * j] == NULL); + + modifies $GcMem, $gcSlice; + + ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind + 1 ==> $gcSlice[$ret + 4 * j] == $ret); + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind + 1 ==> gcAddr($ret + 4 * j)); // REVIEW: necessary? + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind + 1 ==> $GcMem[$ret + 4 * j] == NULL); + ensures ebp == old(ebp); +{ + assert TV($ret); + assert TV($ret + 4 * $ind);// && TV($ret + 4 * $ind + 1) && TV($ret + 4 * $ind + 2) && TV($ret + 4 * $ind + 3); + $gcSlice[$ret + 4 * $ind] := $ret; + + call GcStore(esi, 0); +} + +procedure doAllocCopyingWords($ret:int, $size:int, $nf:int) + requires eax == $ret; + requires ebx == $ret + $size; + requires $size == $nf * 4; + requires $nf >= 0; + requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + requires Aligned($ret) && Fk <= $ret && $ret + $size <= Fl; + + modifies $GcMem, $gcSlice; + modifies esi; + + ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $nf ==> $gcSlice[$ret + 4 * j] == $ret); + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $nf ==> gcAddr($ret + 4 * j)); // REVIEW: necessary? + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $nf ==> $GcMem[$ret + 4 * j] == NULL); + ensures ebp == old(ebp); +{ + var $ind:int; + $ind := 0; + + esi := ?gcLo; + esi := eax; + + //while (4 * $ind < $size) + if (esi >= ebx) { goto loopEnd; } + loop: + assert 4 * $ind < $size; + assert esi == $ret + 4 * $ind; + assert TO($ind) && $ind >= 0; + assert MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind ==> $gcSlice[$ret + 4 * j] == $ret); + assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind ==> gcAddr($ret + 4 * j)); // REVIEW: necessary? + assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind ==> $GcMem[$ret + 4 * j] == NULL); + + call doAllocCopyingWord($ret, $ind); + $ind := $ind + 1; + call esi := Add(esi, 4); + if (esi < ebx) { goto loop; } + loopEnd: +} + +procedure doAllocObjectCopying($ra:int, $nextFp:int, $abs:int, $vt:int, $size:int) + requires eax == $size; + requires ebx == Fk + $size; + requires ecx == $vt; + requires Fk + 4 * numFields($abs) <= Fl; + requires !VFieldPtr($abs, 0); + requires !VFieldPtr($abs, 1); + requires 2 <= numFields($abs); + requires $size == 4 * numFields($abs); + requires ObjSize($abs, $vt, $AbsMem[$abs][2], $AbsMem[$abs][3]); + requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + + // requirements on vtable and layout: + requires word($vt) && !gcAddrEx($vt); + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, 0, 0); + requires !isVarSize(tag($vt)); + + // require a fresh, empty abstract node: + requires $abs != NO_ABS; + requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs); + requires $AbsMem[$abs][0] == NULL; + requires $AbsMem[$abs][1] == $vt; + requires (forall j:int::{TO(j)} TO(j) ==> 2 <= j && j < numFields($abs) ==> $AbsMem[$abs][j] == NULL); + + modifies $GcMem, $toAbs, $gcSlice, Fk; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + ensures old($toAbs)[eax - 4] == NO_ABS; + ensures $toAbs == old($toAbs)[eax - 4 := $abs]; + ensures Pointer($toAbs, eax - 4, $abs); + ensures ebp == old(ebp); +{ + eax := Fk; + + assert TV(eax + 0) && TV(eax + 4); + assert TO(1) && TO(2); + assert TO(numFields($abs)); + + call doAllocCopyingWords(eax, $size, numFields($abs)); + + call GcStore(eax + 4, ecx); + + esi := eax; + call esi := Sub(esi, Fi); + edi := BF; + call bb4SetBit($toAbs, $abs, NO_ABS, Fi, Fi, Fi, Fl, Fk, ?gcLo + BitIndex(HeapLo, Fi), ?gcLo + BitIndex(HeapLo, Fl)); + assert TV(Fk - Fi); + + $toAbs[eax] := $abs; + assert TV(Fk); + assert TO(numFields($abs)); + + Fk := ebx; + call eax := Add(eax, 4); + + assert TV(eax + 4); + assert TO(0); +} + +procedure doAllocStringCopying($ra:int, $nextFp:int, $abs:int, $vt:int, $nElems:int) + requires ecx == pad(16 + 2 * $nElems); + requires ebx == Fk + pad(16 + 2 * $nElems); + requires edx == $nElems; + requires $vt == ?STRING_VTABLE; + requires Fk + pad(16 + 2 * $nElems) + 0 <= Fl; + requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + + // requirements on vtable and layout: + requires word($vt) && !gcAddrEx($vt); + requires word($nElems); + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, $nElems, 0); + requires tag($vt) == ?STRING_TAG; + + // require a fresh, empty abstract node: + requires $abs != NO_ABS; + requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs); + requires $AbsMem[$abs][0] == NULL; + requires $AbsMem[$abs][1] == $vt; + requires $AbsMem[$abs][2] == $nElems; + requires $AbsMem[$abs][3] == $nElems - 1; + requires (forall j:int::{TO(j)} TO(j) ==> 4 <= j && 4 * j < pad(16 + 2 * $nElems) ==> $AbsMem[$abs][j] == NULL); + + modifies $GcMem, $toAbs, $gcSlice, Fk; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + ensures old($toAbs)[eax - 4] == NO_ABS; + ensures $toAbs == old($toAbs)[eax - 4 := $abs]; + ensures Pointer($toAbs, eax - 4, $abs); + ensures ebp == old(ebp); +{ + eax := Fk; + assert TV(eax + 0) && TV(eax + 4) && TV(eax + 8) && TV(eax + 12); + assert TO(1) && TO(2) && TO(3); + assert TVL($abs); + assert TVT($abs, $vt); + assert TO(numFields($abs)); + + call doAllocCopyingWords(eax, pad(16 + 2 * $nElems), numFields($abs)); + + call GcStore(eax + 8, edx); + call edx := SubChecked(edx, 1); + call GcStore(eax + 12, edx); + edx := ?STRING_VTABLE; + call GcStore(eax + 4, edx); + + esi := eax; + call esi := Sub(esi, Fi); + edi := BF; + call bb4SetBit($toAbs, $abs, NO_ABS, Fi, Fi, Fi, Fl, Fk, ?gcLo + BitIndex(HeapLo, Fi), ?gcLo + BitIndex(HeapLo, Fl)); + assert TV(Fk - Fi); + + $toAbs[eax] := $abs; + assert TV(Fk); + assert TO(numFields($abs)); + + Fk := ebx; + call eax := Add(eax, 4); + + assert TV(eax + 4); + assert TO(0); +} + +procedure fromArrayInfo($abs:int, $vt:int) + requires VTable($abs, $vt); + requires tag($vt) == ?PTR_ARRAY_TAG || tag($vt) == ?OTHER_ARRAY_TAG; + ensures !VFieldPtr($abs, 0); + ensures !VFieldPtr($abs, 1); + ensures !VFieldPtr($abs, 2); + ensures !VFieldPtr($abs, 3); +{ + assert TO(0) && TO(1) && TO(2) && TO(3) && TO(4); + assert TVL($abs); + assert TVT($abs, $vt); +} + +procedure doAllocArrayCopyingHelper($abs:int, $vt:int, $rank:int, $nElems:int) + requires ecx == $vt; + requires esi == $nElems; + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, $rank, $nElems); + requires tag($vt) == ?PTR_ARRAY_TAG || tag($vt) == ?OTHER_ARRAY_TAG; + modifies eax, ebx, edx, edi; + ensures !VFieldPtr($abs, 0); + ensures !VFieldPtr($abs, 1); + ensures !VFieldPtr($abs, 2); + ensures !VFieldPtr($abs, 3); + ensures between(4, numFields($abs), baseWords($vt)); + ensures eax == 4 * numFields($abs); +{ + assert TO(2) && TO(3); + assert TVL($abs); + assert TVT($abs, $vt); + + call eax := RoLoad32(ecx + ?VT_BASE_LENGTH); + call ebx := RoLoad32(ecx + ?VT_ARRAY_ELEMENT_SIZE); + call edi := RoLoad32(ecx + ?VT_MASK); + call edi := And(edi, 15); + + //if (edi == ?PTR_ARRAY_TAG) + if (edi != ?PTR_ARRAY_TAG) { goto skip1; } + edi := esi; + call edi := AddChecked(edi, edi); + call edi := AddChecked(edi, edi); + call eax := AddChecked(eax, edi); + call eax := AddChecked(eax, 3); + edi := 3; + call edi := Not(edi); + call eax := And(eax, edi); + goto skip2; + //else + skip1: + edi := eax; + eax := ebx; + call eax, edx := MulChecked(eax, esi); + call eax := AddChecked(eax, edi); + call eax := AddChecked(eax, 3); + edi := 3; + call edi := Not(edi); + call eax := And(eax, edi); + skip2: +} + +procedure doAllocArrayCopying($ra:int, $nextFp:int, $abs:int, $vt:int, $rank:int, $nElems:int, $size:int) +// requires eax == $size; + requires ebx == Fk + $size; + requires ecx == $vt; + requires edx == $rank; + requires esi == $nElems; + requires Fk + 4 * numFields($abs) <= Fl; + requires !VFieldPtr($abs, 0); + requires !VFieldPtr($abs, 1); + requires !VFieldPtr($abs, 2); + requires !VFieldPtr($abs, 3); + requires 4 <= numFields($abs); + requires $size == 4 * numFields($abs); + requires ObjSize($abs, $vt, $rank, $nElems); + requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + + // requirements on vtable and layout: + requires word($vt) && !gcAddrEx($vt); + requires word($rank); + requires word($nElems); + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, $rank, $nElems); + requires tag($vt) == ?PTR_ARRAY_TAG || tag($vt) == ?OTHER_ARRAY_TAG; + + // require a fresh, empty abstract node: + requires $abs != NO_ABS; + requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs); + requires $AbsMem[$abs][0] == NULL; + requires $AbsMem[$abs][1] == $vt; + requires $AbsMem[$abs][2] == $rank; + requires $AbsMem[$abs][3] == $nElems; + requires (forall j:int::{TO(j)} TO(j) ==> 4 <= j && j < numFields($abs) ==> $AbsMem[$abs][j] == NULL); + + modifies $GcMem, $toAbs, $gcSlice, Fk; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + ensures old($toAbs)[eax - 4] == NO_ABS; + ensures $toAbs == old($toAbs)[eax - 4 := $abs]; + ensures Pointer($toAbs, eax - 4, $abs); + ensures ebp == old(ebp); +{ + var nElems:int; + eax := Fk; + nElems := esi; + + assert TV(eax + 0) && TV(eax + 4) && TV(eax + 8) && TV(eax + 12); + assert TO(1) && TO(2) && TO(3) && TO(4); + assert TO(numFields($abs)); + + call doAllocCopyingWords(eax, $size, numFields($abs)); + + esi := nElems; + call GcStore(eax + 4, ecx); + call GcStore(eax + 8, edx); + call GcStore(eax + 12, esi); + + esi := eax; + call esi := Sub(esi, Fi); + edi := BF; + call bb4SetBit($toAbs, $abs, NO_ABS, Fi, Fi, Fi, Fl, Fk, ?gcLo + BitIndex(HeapLo, Fi), ?gcLo + BitIndex(HeapLo, Fl)); + assert TV(Fk - Fi); + + $toAbs[eax] := $abs; + assert TV(Fk); + assert TO(numFields($abs)); + + Fk := ebx; + call eax := Add(eax, 4); + + assert TV(eax + 4); + assert TO(0); +} + +procedure doAllocVectorCopyingHelper($abs:int, $vt:int, $nElems:int) + requires ecx == $vt; + requires edx == $nElems; + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, $nElems, 0); + requires tag($vt) == ?PTR_VECTOR_TAG || tag($vt) == ?OTHER_VECTOR_TAG; + modifies eax, ebx, edx, esi, edi; + ensures !VFieldPtr($abs, 0); + ensures !VFieldPtr($abs, 1); + ensures !VFieldPtr($abs, 2); + ensures 3 <= numFields($abs); + ensures eax == 4 * numFields($abs); + ensures ObjSize($abs, $vt, $nElems, $AbsMem[$abs][3]); +{ + assert TO(2); + assert TVL($abs); + assert TVT($abs, $vt); + + call eax := RoLoad32(ecx + ?VT_BASE_LENGTH); + call ebx := RoLoad32(ecx + ?VT_ARRAY_ELEMENT_SIZE); + call edi := RoLoad32(ecx + ?VT_MASK); + call edi := And(edi, 15); + + //if (edi == ?PTR_ARRAY_TAG) + if (edi != ?PTR_VECTOR_TAG) { goto skip1; } + edi := edx; + call edi := AddChecked(edi, edi); + call edi := AddChecked(edi, edi); + call eax := AddChecked(eax, edi); + call eax := AddChecked(eax, 3); + edi := 3; + call edi := Not(edi); + call eax := And(eax, edi); + goto skip2; + //else + skip1: + edi := eax; + eax := ebx; + call eax, edx := MulChecked(eax, edx); + call eax := AddChecked(eax, edi); + call eax := AddChecked(eax, 3); + edi := 3; + call edi := Not(edi); + call eax := And(eax, edi); + skip2: +} + +procedure doAllocVectorCopying($ra:int, $nextFp:int, $abs:int, $vt:int, $nElems:int, $size:int) +// requires eax == $size; + requires ebx == Fk + $size; + requires ecx == $vt; + requires edx == $nElems; + requires Fk + 4 * numFields($abs) <= Fl; + requires !VFieldPtr($abs, 0); + requires !VFieldPtr($abs, 1); + requires !VFieldPtr($abs, 2); + requires 3 <= numFields($abs); + requires $size == 4 * numFields($abs); + requires ObjSize($abs, $vt, $nElems, $AbsMem[$abs][3]); + requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + + // requirements on vtable and layout: + requires word($vt) && !gcAddrEx($vt); + requires word($nElems); + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, $nElems, 0); + requires tag($vt) == ?PTR_VECTOR_TAG || tag($vt) == ?OTHER_VECTOR_TAG; + + // require a fresh, empty abstract node: + requires $abs != NO_ABS; + requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs); + requires $AbsMem[$abs][0] == NULL; + requires $AbsMem[$abs][1] == $vt; + requires $AbsMem[$abs][2] == $nElems; + requires (forall j:int::{TO(j)} TO(j) ==> 3 <= j && j < numFields($abs) ==> $AbsMem[$abs][j] == NULL); + + modifies $GcMem, $toAbs, $gcSlice, Fk; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + ensures old($toAbs)[eax - 4] == NO_ABS; + ensures $toAbs == old($toAbs)[eax - 4 := $abs]; + ensures Pointer($toAbs, eax - 4, $abs); + ensures ebp == old(ebp); +{ + eax := Fk; + + assert TV(eax + 0) && TV(eax + 4) && TV(eax + 8); + assert TO(1) && TO(2) && TO(3); + assert TO(numFields($abs)); + + call doAllocCopyingWords(eax, $size, numFields($abs)); + + call GcStore(eax + 4, ecx); + call GcStore(eax + 8, edx); + + esi := eax; + call esi := Sub(esi, Fi); + edi := BF; + call bb4SetBit($toAbs, $abs, NO_ABS, Fi, Fi, Fi, Fl, Fk, ?gcLo + BitIndex(HeapLo, Fi), ?gcLo + BitIndex(HeapLo, Fl)); + assert TV(Fk - Fi); + + $toAbs[eax] := $abs; + assert TV(Fk); + assert TO(numFields($abs)); + + Fk := ebx; + call eax := Add(eax, 4); + + assert TV(eax + 4); + assert TO(0); +} + +procedure doAllocObjectCopyingHelper($abs:int, $vt:int) + requires ecx == $vt; + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, 0, 0); + requires !isVarSize(tag($vt)); + modifies eax; + ensures !VFieldPtr($abs, 0); + ensures !VFieldPtr($abs, 1); + ensures 2 <= numFields($abs); + ensures eax == 4 * numFields($abs); + ensures ObjSize($abs, $vt, $AbsMem[$abs][2], $AbsMem[$abs][3]); +{ + assert TO(2); + assert TVL($abs); + assert TVT($abs, $vt); + call eax := RoLoad32(ecx + ?VT_BASE_LENGTH); +} + +////////////////////////////////////////////////////////////////////////////// +// +// The procedures below are the entry points from the mutator. +// +// Therefore, the requires, ensures, and modifies clauses for these procedures +// are part of the trusted computing base! +// +////////////////////////////////////////////////////////////////////////////// + +procedure Initialize() + modifies $toAbs, Ti, Tj, Tk, Tl, Fi, Fk, Fl, HeapLo, BF, BT; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + modifies $GcMem; + ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + ensures WellFormed($toAbs); + ensures ebp == old(ebp); +{ + var save:int; + var unitSize:int; + save := ebp; + + // The heap consists of two bitmaps, followed by two semispaces: + // <--4--><--4--><--128--><--128--> + // Each bit map must consist of at least 4 bytes. + // For each bit in the bit map, there is one word in the corresponding semispace. + // Thus, the total gc memory size must be a multiple of: + // 4 + 4 + 128 + 128 = 8 + 256 = 264 bytes + + // Compute gcMem size, in bytes and words + esi := ?gcHi; + call esi := Sub(esi, ?gcLo); + edi := esi; + + // Break into 264-byte units. Let ebp be the number of units. + edx := 0; + eax := edi; + ebx := 264; + call eax, edx := Div(eax, edx, ebx); + ebp := eax; + unitSize := ebp; + assert 264 * unitSize <= (?gcHi - ?gcLo); + + // Divide heap into ?gcLo <--4--> eax <--4--> ebx <--128--> ecx <--128--> ?gcHi + edx := 0; + call ebp := Lea(edx + 4 * ebp); + eax := ?gcLo; + BF := eax; + call eax := Add(eax, ebp); + BT := eax; + call ebx := Lea(eax + ebp); + call ebp := Lea(edx + 4 * ebp); + call ecx := Lea(ebx + 8 * ebp); + call edx := Lea(ecx + 8 * ebp); + + $toAbs := MAP_NO_ABS; + HeapLo := ebx; + Fi := ebx; + Fk := ebx; + Fl := ecx; + Ti := ecx; + Tj := ecx; + Tk := ecx; + Tl := edx; + + call __initialize(unitSize, HeapLo); + + assert TV(?gcLo); + assert TV(HeapLo); + assert TO(0); + assert TO(unitSize); + assert TO(2 * unitSize); + assert TO(32 * unitSize); + eax := ?gcLo; + esi := unitSize; + call ebx := Lea(eax + 4 * esi); + esp := esp - 4; call BB4Zero($toAbs, 0, NO_ABS, Fi, Fi, Fi, Fl, 0, ?gcLo, ?gcLo + 4 * unitSize, 0, 0); + + assert TVM(32, (BitIndex(HeapLo, Fl) - BitIndex(HeapLo, Fi))); + assert TVM(32, (BitIndex(HeapLo, Tl) - BitIndex(HeapLo, Ti))); + + ebp := save; + esp := esp + 4; return; +} + +// Prepare to call GcLoad (don't actually call it -- let the mutator call it) +procedure readCopying($ptr:int, $fld:int) returns ($val:int) + requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + requires Pointer($toAbs, $ptr, $toAbs[$ptr]); + requires 0 <= $fld && $fld < numFields($toAbs[$ptr]); + ensures gcAddr($ptr + 4 * $fld); + ensures $val == $GcMem[$ptr + 4 * $fld]; + ensures Value(VFieldPtr($toAbs[$ptr], $fld), $toAbs, $val, $AbsMem[$toAbs[$ptr]][$fld]); +{ +// call $val := GcLoad($ptr + 4 * $fld); + $val := $GcMem[$ptr + 4 * $fld]; + assert TV($val) && TV($ptr); + assert TO($fld); +} + +procedure writeCopying($ptr:int, $fld:int, $val:int, $abs:int) returns ($_gcData:[int]int, $_absData:[int][int]int) + requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + requires Pointer($toAbs, $ptr, $toAbs[$ptr]); + requires 0 <= $fld && $fld < numFields($toAbs[$ptr]); + requires !isReadonlyField(tag($AbsMem[$toAbs[$ptr]][1]), $fld); + requires Value(VFieldPtr($toAbs[$ptr], $fld), $toAbs, $val, $abs); + ensures gcAddr($ptr + 4 * $fld); + ensures word($val); + ensures $_gcData == $GcMem[$ptr + 4 * $fld := $val]; + ensures $_absData == $AbsMem[$toAbs[$ptr] := $AbsMem[$toAbs[$ptr]][$fld := $abs]]; + ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $_gcData, $toAbs, $_absData, $gcSlice); +{ +// call GcStore($ptr + 4 * $fld, $val); +// call AbsEdgeWrite($ptr, $fld, $val); + $_gcData := $GcMem[$ptr + 4 * $fld := $val]; + $_absData := $AbsMem[$toAbs[$ptr] := $AbsMem[$toAbs[$ptr]][$fld := $abs]]; + assert TVL($toAbs[$ptr]); + assert TV($val) && TV($ptr); + assert TO($fld); +// assert TO(1); +} + + +procedure AllocObject($ra:int, $abs:int, $vt:int) + // GC invariant: + requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + + // requirements on mutator root layout: + requires 0 <= $FrameCount; + requires $FrameSlice[esp] == $FrameCount && $FrameMem[esp] == $ra; + requires FrameNextInv($FrameCount, $ra, ebp, $FrameAddr, $FrameLayout); + requires StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + requires StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + + // requirements on vtable and layout: + requires ecx == $vt; + requires word($vt) && !gcAddrEx($vt); + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, 0, 0); + requires !isVarSize(tag($vt)); + + // require a fresh, empty abstract node: + requires $abs != NO_ABS; + requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs); + requires $AbsMem[$abs][0] == NULL; + requires $AbsMem[$abs][1] == $vt; + requires (forall j:int::{TO(j)} TO(j) ==> 2 <= j && j < numFields($abs) ==> $AbsMem[$abs][j] == NULL); + + modifies $r1, $r2, $GcMem, $toAbs, $gcSlice, $SectionMem, $FrameMem, $freshAbs; + modifies Ti, Tj, Tk, Tl, Fi, Fk, Fl, BF, BT; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + ensures StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + ensures StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + ensures Pointer($toAbs, eax - 4, $abs); + ensures WellFormed($toAbs); + ensures ebp == old(ebp); +{ + var size:int; + var vt:int; + call doAllocObjectCopyingHelper($abs, $vt); + + $freshAbs := $abs; + + ebx := Fk; + call ebx := AddChecked(ebx, eax); + + //if (!(Fk + size <= Fl)) + if (ebx <= Fl) { goto skip1; } + size := eax; + vt := ecx; + + call ecx := FrameLoad(($FrameCount), esp); + edx := ebp; + esp := esp - 4; call GarbageCollectCopying($ra, ebp); + + eax := size; + ecx := vt; + + ebx := Fk; + call ebx := AddChecked(ebx, eax); + if (ebx <= Fl) { goto skip1; } + // Out of memory + call DebugBreak(); + skip1: + + call doAllocObjectCopying($ra, ebp, $abs, $vt, eax); + esp := esp + 4; return; +} + +procedure AllocString($ra:int, $abs:int, $vt:int, $nElems:int) + // GC invariant: + requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + + // requirements on mutator root layout: + requires 0 <= $FrameCount; + requires $FrameSlice[esp] == $FrameCount && $FrameMem[esp] == $ra; + requires FrameNextInv($FrameCount, $ra, ebp, $FrameAddr, $FrameLayout); + requires StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + requires StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + + // requirements on vtable and layout: + requires ecx == $nElems - 1; + requires $vt == ?STRING_VTABLE; + requires word($vt) && !gcAddrEx($vt); + requires word($nElems); + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, $nElems, 0); + requires tag($vt) == ?STRING_TAG; + + // require a fresh, empty abstract node: + requires $abs != NO_ABS; + requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs); + requires $AbsMem[$abs][0] == NULL; + requires $AbsMem[$abs][1] == $vt; + requires $AbsMem[$abs][2] == $nElems; + requires $AbsMem[$abs][3] == $nElems - 1; + requires (forall j:int::{TO(j)} TO(j) ==> 4 <= j && 4 * j < pad(16 + 2 * $nElems) ==> $AbsMem[$abs][j] == NULL); + + modifies $r1, $r2, $GcMem, $toAbs, $gcSlice, $SectionMem, $FrameMem, $freshAbs; + modifies Ti, Tj, Tk, Tl, Fi, Fk, Fl, BF, BT; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + ensures StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + ensures StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + ensures Pointer($toAbs, eax - 4, $abs); + ensures WellFormed($toAbs); + ensures ebp == old(ebp); +{ + var size:int; + var nElems:int; + + $freshAbs := $abs; + + call ecx := AddChecked(ecx, 1); + edx := ecx; + call ecx := AddChecked(ecx, ecx); + call ecx := AddChecked(ecx, 19); + eax := 3; + call eax := Not(eax); + call ecx := And(ecx, eax); + ebx := Fk; + call ebx := AddChecked(ebx, ecx); + //if (!(Fk + pad(16 + 2 * $nElems) + 0 <= Fl)) + if (ebx <= Fl) { goto skip1; } + size := ecx; + nElems := edx; + + call ecx := FrameLoad(($FrameCount), esp); + edx := ebp; + esp := esp - 4; call GarbageCollectCopying($ra, ebp); + + ecx := size; + edx := nElems; + ebx := Fk; + call ebx := AddChecked(ebx, ecx); + if (ebx <= Fl) { goto skip1; } + // Out of memory + call DebugBreak(); + skip1: + + call doAllocStringCopying($ra, ebp, $abs, $vt, $nElems); + esp := esp + 4; return; +} + +procedure AllocArray($ra:int, $abs:int, $vt:int, $rank:int, $nElems:int) + // GC invariant: + requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + + // requirements on mutator root layout: + requires 0 <= $FrameCount; + requires $FrameSlice[esp] == $FrameCount && $FrameMem[esp] == $ra; + requires FrameNextInv($FrameCount, $ra, ebp, $FrameAddr, $FrameLayout); + requires StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + requires StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + + // requirements on vtable and layout: + requires ecx == $vt; + requires edx == $rank; + requires $FrameSlice[esp + 4] == $FrameCount && $FrameMem[esp + 4] == $nElems; + requires word($vt) && !gcAddrEx($vt); + requires word($rank); + requires word($nElems); + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, $rank, $nElems); + requires tag($vt) == ?PTR_ARRAY_TAG || tag($vt) == ?OTHER_ARRAY_TAG; + + // require a fresh, empty abstract node: + requires $abs != NO_ABS; + requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs); + requires $AbsMem[$abs][0] == NULL; + requires $AbsMem[$abs][1] == $vt; + requires $AbsMem[$abs][2] == $rank; + requires $AbsMem[$abs][3] == $nElems; + requires (forall j:int::{TO(j)} TO(j) ==> 4 <= j && j < numFields($abs) ==> $AbsMem[$abs][j] == NULL); + + modifies $r1, $r2, $GcMem, $toAbs, $gcSlice, $SectionMem, $FrameMem, $freshAbs; + modifies Ti, Tj, Tk, Tl, Fi, Fk, Fl, BF, BT; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + ensures StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + ensures StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + ensures Pointer($toAbs, eax - 4, $abs); + ensures WellFormed($toAbs); + ensures ebp == old(ebp); +{ + var vt:int; + var rank:int; + var size:int; + var nElems:int; + + $freshAbs := $abs; + + call esi := FrameLoad(($FrameCount), esp + 4); + + rank := edx; + call doAllocArrayCopyingHelper($abs, $vt, $rank, $nElems); + ebx := Fk; + call ebx := AddChecked(ebx, eax); + + //if (!(Fk + size <= Fl)) + if (ebx <= Fl) { goto skip1; } + size := eax; + vt := ecx; + nElems := esi; + + call ecx := FrameLoad(($FrameCount), esp); + edx := ebp; + esp := esp - 4; call GarbageCollectCopying($ra, ebp); + + eax := size; + ecx := vt; + esi := nElems; + ebx := Fk; + call ebx := AddChecked(ebx, eax); + if (ebx <= Fl) { goto skip1; } + // Out of memory + call DebugBreak(); + skip1: + + edx := rank; + call doAllocArrayCopying($ra, ebp, $abs, $vt, $rank, $nElems, eax); + esp := esp + 4; return; +} + +procedure AllocVector($ra:int, $abs:int, $vt:int, $nElems:int) + // GC invariant: + requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + + // requirements on mutator root layout: + requires 0 <= $FrameCount; + requires $FrameSlice[esp] == $FrameCount && $FrameMem[esp] == $ra; + requires FrameNextInv($FrameCount, $ra, ebp, $FrameAddr, $FrameLayout); + requires StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + requires StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + requires MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + + // requirements on vtable and layout: + requires ecx == $vt; + requires edx == $nElems; + requires word($vt) && !gcAddrEx($vt); + requires word($nElems); + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, $nElems, 0); + requires tag($vt) == ?PTR_VECTOR_TAG || tag($vt) == ?OTHER_VECTOR_TAG; + + // require a fresh, empty abstract node: + requires $abs != NO_ABS; + requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs); + requires $AbsMem[$abs][0] == NULL; + requires $AbsMem[$abs][1] == $vt; + requires $AbsMem[$abs][2] == $nElems; + requires (forall j:int::{TO(j)} TO(j) ==> 3 <= j && j < numFields($abs) ==> $AbsMem[$abs][j] == NULL); + + modifies $r1, $r2, $GcMem, $toAbs, $gcSlice, $SectionMem, $FrameMem, $freshAbs; + modifies Ti, Tj, Tk, Tl, Fi, Fk, Fl, BF, BT; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + ensures StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + ensures StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + ensures MutatorInv(BF, BT, HeapLo, Fi, Fk, Fl, Ti, Tj, Tk, Tl, $GcMem, $toAbs, $AbsMem, $gcSlice); + ensures Pointer($toAbs, eax - 4, $abs); + ensures WellFormed($toAbs); + ensures ebp == old(ebp); +{ + var size:int; + var vt:int; + var nElems:int; + + nElems := edx; + call doAllocVectorCopyingHelper($abs, $vt, $nElems); + + $freshAbs := $abs; + + ebx := Fk; + call ebx := AddChecked(ebx, eax); + + //if (!(Fk + size <= Fl)) + if (ebx <= Fl) { goto skip1; } + size := eax; + vt := ecx; + + call ecx := FrameLoad(($FrameCount), esp); + edx := ebp; + esp := esp - 4; call GarbageCollectCopying($ra, ebp); + + eax := size; + ecx := vt; + + ebx := Fk; + call ebx := AddChecked(ebx, eax); + if (ebx <= Fl) { goto skip1; } + // Out of memory + call DebugBreak(); + skip1: + + edx := nElems; + call doAllocVectorCopying($ra, ebp, $abs, $vt, $nElems, eax); + esp := esp + 4; return; +} + diff --git a/base/Imported/Bartok/runtime/verified/GCs/VerifiedMarkSweepCollector.bpl b/base/Imported/Bartok/runtime/verified/GCs/VerifiedMarkSweepCollector.bpl new file mode 100644 index 0000000..6d2d578 --- /dev/null +++ b/base/Imported/Bartok/runtime/verified/GCs/VerifiedMarkSweepCollector.bpl @@ -0,0 +1,3503 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +// Verified mark-sweep garbage collector +// +// medium term goal: support more Bartok array-of-struct and vector-of-struct object layouts +// long term goal: support various other features: threads, pinning, stack markers, etc. + +// Imports: +// - Trusted.bpl +// - VerifiedBitVectors.bpl +// Includes: +// - VerifiedCommon.bpl + + +// \Spec#\bin\Boogie.exe /noinfer Trusted.bpl VerifiedBitVectors.bpl VerifiedCommon.bpl VerifiedMarkSweepCollector.bpl + +/***************************************************************************** +****************************************************************************** +**** VERIFIED +****************************************************************************** +*****************************************************************************/ + +////////////////////////////////////////////////////////////////////////////// +// MARK-SWEEP COLLECTOR +////////////////////////////////////////////////////////////////////////////// + +// gc memory structure: +// ?gcLo [mark stack] ColorBase [color bit map] HeapLo [heap] HeapHi ?gcHi + +function Unallocated(i:int) returns(bool) { i == 0 } +function White(i:int) returns(bool) { i == 1 } +function Gray(i:int) returns(bool) { i == 2 } +function Black(i:int) returns(bool) { i == 3 } + +var $freshAbs:int; +var $color:[int]int; +var StackTop:int; +var $fs:[int]int; // size of free block +var $fn:[int]int; // next free block +var CachePtr:int; +var CacheSize:int; +var ColorBase:int; +var HeapLo:int; +var HeapHi:int; +var ReserveMin:int; // wilderness marker + +function ObjectSeq(i1:int, i2:int, r:[int]int, $fs:[int]int, $fn:[int]int) returns (bool) +{ + (forall i:int::{TV(i)} TV(i) ==> i1 <= i && i < i2 ==> + (r[i] != NO_ABS ==> + $fs[i] == 0 + && i + 4 * numFields(r[i]) <= i2 + && (forall ii:int::{TV(ii)} TV(ii) ==> i < ii && ii < i + 4 * numFields(r[i]) ==> r[ii] == NO_ABS && $fs[ii] == 0) + ) + && ($fs[i] != 0 ==> + r[i] == NO_ABS + && i + 8 <= i + $fs[i] && i + $fs[i] <= i2 + && Aligned(i) && Aligned(i + $fs[i]) + && ($fn[i] != 0 ==> between(i + $fs[i], i2, $fn[i]) && $fs[$fn[i]] != 0) + && (forall ii:int::{TV(ii)} TV(ii) ==> i < ii && ii < i + $fs[i] ==> r[ii] == NO_ABS && $fs[ii] == 0) + && (forall ii:int::{TV(ii)} TV(ii) ==> i < ii && ii < $fn[i] && $fs[ii] != 0 ==> $fn[ii] == 0) + ) + ) +} + +function Objects(i1:int, i2:int, r:[int]int, $fs:[int]int, $fn:[int]int) returns (bool) +{ + (forall i:int::{TV(i)} TV(i) ==> i1 <= i && i < i2 ==> + (r[i] != NO_ABS ==> + $fs[i] == 0 + && Aligned(i) + && i + 4 * numFields(r[i]) <= i2 + && (forall ii:int::{TV(ii)} TV(ii) ==> i < ii && ii < i + 4 * numFields(r[i]) ==> r[ii] == NO_ABS && $fs[ii] == 0) + ) + && ($fs[i] != 0 ==> Aligned(i)) + ) +} + +function Disconnected(i1:int, ptr:int, $fs:[int]int, $fn:[int]int) returns(bool) +{ + (forall i:int::{TV(i)} TV(i) ==> i1 <= i && i < ptr && $fs[i] != 0 ==> $fn[i] != ptr) +} + +function FreeInvBase(i1:int, i2:int, $fs:[int]int, $fn:[int]int, data:[int]int, CachePtr:int, CacheSize:int) returns (bool) +{ + i2 >= i1 + 8 + && $fs[i1] == 8 + && (CacheSize != 0 ==> between(i1, i2, CachePtr) && $fs[CachePtr] == CacheSize && Disconnected(i1, CachePtr, $fs, $fn)) +} + +function FreeInvI(i:int, i1:int, i2:int, $fs:[int]int, $fn:[int]int, data:[int]int, CachePtr:int, CacheSize:int) returns (bool) +{ + data[i] == $fn[i] + && data[i + 4] == $fs[i] +} + +function FreeInv(i1:int, i2:int, $fs:[int]int, $fn:[int]int, data:[int]int, CachePtr:int, CacheSize:int) returns (bool) +{ + FreeInvBase(i1, i2, $fs, $fn, data, CachePtr, CacheSize) + && (forall i:int::{TV(i)} TV(i) ==> i1 <= i && i < i2 && $fs[i] != 0 ==> + FreeInvI(i, i1, i2, $fs, $fn, data, CachePtr, CacheSize)) +} + +// REVIEW: get rid of this? +function AllocInv2($toAbs:[int]int, min:int, max:int, $r1:[int]int, $r2:[int]int, r1Live:bool) returns (bool) +{ + (forall i:int::{TV(i)} TV(i) ==> min <= i && i < max && r1Live ==> ($toAbs[i] == NO_ABS ==> $r1[i] == NO_ABS)) + && (forall i:int::{TV(i)} TV(i) ==> min <= i && i < max ==> ($toAbs[i] == NO_ABS ==> $r2[i] == NO_ABS)) +} + +function MsInvColorI(i:int, t:Time, $r1:[int]int, $r2:[int]int, $color:[int]int, r1Live:bool, + $GcMem:[int]int, $toAbs:[int]int, $AbsMem:[int][int]int, $gcSlice:[int]int) returns (bool) +{ + (White($color[i]) ==> $r1[i] != NO_ABS && $r2[i] == NO_ABS + && (r1Live ==> ObjInv(i, $r1, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice)) + ) + && (Gray($color[i]) ==> $r1[i] != NO_ABS && $r2[i] != NO_ABS + && ObjInv(i, $r1, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice) + && reached($toAbs[i], t) + ) + && (Black($color[i]) ==> $r1[i] != NO_ABS && $r2[i] != NO_ABS + && ObjInv(i, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice) + && reached($toAbs[i], t) + ) +} + +function MsInvBase(min:int, max:int, $r1:[int]int, $r2:[int]int, $color:[int]int, + r1Live:bool, $GcMem:[int]int, $toAbs:[int]int, $gcSlice:[int]int) returns (bool) +{ + (forall i:int::{TV(i)} TV(i) ==> min <= i && i < max + && r1Live && $r1[i] != NO_ABS && $r2[i] != NO_ABS ==> $r1[i] == $r2[i]) + && (forall i:int::{TV(i)} TV(i) ==> min <= i && i < max && $toAbs[i] != NO_ABS ==> + $toAbs[i] != NO_ABS && $toAbs[i] == $r1[i]) + && WellFormed($toAbs) +} + +function MsGcInv(min:int, max:int, t:Time, $r1:[int]int, $r2:[int]int, $color:[int]int, + r1Live:bool, $GcMem:[int]int, $toAbs:[int]int, $AbsMem:[int][int]int, $gcSlice:[int]int) returns (bool) +{ + MsInvBase(min, max, $r1, $r2, $color, r1Live, $GcMem, $toAbs, $gcSlice) + && (forall i:int::{TV(i)} TV(i) ==> min <= i && i < max && $toAbs[i] != NO_ABS ==> + MsInvColorI(i, t, $r1, $r2, $color, r1Live, $GcMem, $toAbs, $AbsMem, $gcSlice) + ) +} + +function MsInv(min:int, max:int, $color:[int]int, + $GcMem:[int]int, $toAbs:[int]int, $AbsMem:[int][int]int, $gcSlice:[int]int) returns (bool) +{ + (forall i:int::{TV(i)} TV(i) ==> min <= i && i < max ==> $toAbs[i] != NO_ABS ==> + $toAbs[i] != NO_ABS + && ObjInv(i, $toAbs, $toAbs, $toAbs, $AbsMem, $GcMem, $gcSlice) + ) + && (forall i:int::{TV(i)} TV(i) ==> min <= i && i < max ==> (White($color[i]) <==> $toAbs[i] != NO_ABS)) + && WellFormed($toAbs) +} + +function GcInv(ColorBase:int, HeapLo:int, HeapHi:int, $color:[int]int, $toAbs:[int]int, $r1:[int]int, $r2:[int]int, $GcMem:[int]int) returns(bool) +{ + ?gcLo <= ColorBase && ColorBase <= HeapLo && HeapLo <= HeapHi && HeapHi <= ?gcHi + && Aligned(ColorBase) && Aligned(HeapLo) && Aligned(HeapHi) + && bb2vec4($color, HeapLo, $GcMem, HeapLo, HeapLo, HeapHi, ColorBase, HeapLo) + && (forall i:int::{TV(i)} TV(i) ==> i < HeapLo || i >= HeapHi ==> + $toAbs[i] == NO_ABS && $r1[i] == NO_ABS && $r2[i] == NO_ABS) + && (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < HeapHi ==> between(0, 4, $color[i])) + && (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < HeapHi ==> (Unallocated($color[i]) <==> $toAbs[i] == NO_ABS)) +} + +function BigGcInv($freshAbs:int, ColorBase:int, HeapLo:int, HeapHi:int, $color:[int]int, $fs:[int]int, $fn:[int]int, + $toAbs:[int]int, $r1:[int]int, $r2:[int]int, $AbsMem:[int][int]int, $GcMem:[int]int, $gcSlice:[int]int, + CachePtr:int, CacheSize:int, $Time:Time) returns(bool) +{ + AllocInv2($toAbs, HeapLo, HeapHi, $r1, $r2, true) + && MsGcInv(HeapLo, HeapHi, $Time, $r1, $r2, $color, true, $GcMem, $toAbs, $AbsMem, $gcSlice) + && GcInv(ColorBase, HeapLo, HeapHi, $color, $toAbs, $r1, $r2, $GcMem) + && ObjectSeq(HeapLo, HeapHi, $toAbs, $fs, $fn) + && FreeInv(HeapLo, HeapHi, $fs, $fn, $GcMem, CachePtr, CacheSize) + && (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $freshAbs && $r2[i] != $freshAbs) +} + +function MutatorInv(ColorBase:int, HeapLo:int, HeapHi:int, $color:[int]int, $fs:[int]int, $fn:[int]int, + $toAbs:[int]int, $AbsMem:[int][int]int, $GcMem:[int]int, $gcSlice:[int]int, + CachePtr:int, CacheSize:int) returns(bool) +{ + MsInv(HeapLo, HeapHi, $color, $GcMem, $toAbs, $AbsMem, $gcSlice) + && GcInv(ColorBase, HeapLo, HeapHi, $color, $toAbs, $toAbs, $toAbs, $GcMem) + && ObjectSeq(HeapLo, HeapHi, $toAbs, $fs, $fn) + && FreeInv(HeapLo, HeapHi, $fs, $fn, $GcMem, CachePtr, CacheSize) +} + +function MarkStack(s1:int, s2:int, $toAbs:[int]int, $color:[int]int, stack:[int]int, extra:int) returns(bool) +{ + s1 <= s2 + && Aligned(s2) + && (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != NO_ABS && Gray($color[i]) && i != extra ==> + (exists s:int::{TV(s)} TV(s) && s1 <= s && s < s2 && Aligned(s) && stack[s] == i)) + && (forall s:int::{TV(s)} TV(s) ==> s1 <= s && s < s2 && Aligned(s) ==> + gcAddr(stack[s]) && $toAbs[stack[s]] != NO_ABS && Gray($color[stack[s]]) && stack[s] != extra) + && (forall s:int,t:int::{TV(s),TV(t)} TV(s) && TV(t) ==> + s1 <= s && s < s2 ==> s1 <= t && t < s2 && Aligned(s) && Aligned(t) ==> + s != t ==> stack[s] != stack[t]) +} + +procedure BB4Zero2($a:[int]int, $aBase:int, $i0:int, $i1:int, $i2:int, $k:int, $g1:int, $g2:int, $_i0:int, $_g1:int) + requires eax == $g1; + requires ebx == $g2; + requires (forall i:int::{TV(i)} TV(i) && $i1 <= i && i < $i2 ==> $a[$aBase + (i - $i0)] == 0); + requires Aligned($g1) && Aligned($g2); + requires $i2 - $i1 == 16 * ($g2 - $g1); + requires word($i1 - $i0) && word($i2 - $i0); + requires ?gcLo <= $g1 && $g1 <= $g2 && $g2 <= ?gcHi; + requires $i1 == $i0; + modifies $GcMem; + modifies eax, ebx, esi, edi, ebp, esp; + ensures bb2vec4($a, $aBase, $GcMem, $i0, $i1, $i2, $g1, $g2); + ensures (forall i:int::{TV(i)} TV(i) && !between($g1, $g2, i) ==> $GcMem[i] == old($GcMem)[i]); +{ + var $iter:int; + esi := eax; + $iter := $i1; + //while (esi < $g2) + loop: + assert Aligned(esi) && TV(esi); + assert $g1 <= esi && esi <= $g2; + assert $iter - $i1 == 16 * (esi - $g1); + assert bb2vec4($a, $aBase, $GcMem, $i0, $i1, $iter, $g1, $g2); + assert (forall i:int::{TV(i)} TV(i) && !between($g1, $g2, i) ==> $GcMem[i] == old($GcMem)[i]); + if (esi >= ebx) { goto loopEnd; } + + call __notAligned(esi); + call __bb4Zero2($a, $aBase, $GcMem, $i0, $i1, $iter, $g1, $g2, esi); + call GcStore(esi, 0); + $iter := $iter + 64; + call esi := Add(esi, 4); + assert TO(1); + goto loop; + loopEnd: + + assert esi == $g2; + assert $iter == $i2; + esp := esp + 4; return; +} + +procedure bb4GetColor($a:[int]int, $aBase:int, $i0:int, $i1:int, $i2:int, $k:int, $g1:int, $g2:int) + requires bb2vec4($a, $aBase, $GcMem, $i0, $i1, $i2, $g1, $g2); + requires word($k - $i0) && $i1 <= $k && $k < $i2; + requires word($k) && word($i0) && Aligned($k) && Aligned($i0); + requires word($i2 - $i0); + requires eax == $g1; + requires ebx == $k - $i0; + requires Aligned($g1) && ?gcLo <= $g1 && $g2 <= ?gcHi; + requires word($i1 - $i0) && word($i2 - $i0); + modifies ebx, ecx, edx; + ensures ebx == $a[$aBase + ($k - $i0)]; +{ + var $idx:int; + var $bbb:int; + $idx := $g1 + 4 * shr($k - $i0, 6); + $bbb := and(shr($GcMem[$idx], and(shr($k - $i0, 1), 31)), 3); + call __subAligned($k, $i0); + call __bb4Get2Bit($a, $aBase, $GcMem, $i0, $i1, $i2, $k, $idx, $bbb, $g1, $g2); + + assert TV($g1); + assert TO(shr(ebx, 6)); + + ecx := ebx; + call ecx := Shr(ecx, 6); + call edx := Lea(eax + 4 * ecx); + ecx := ebx; + call ecx := Shr(ecx, 1); + call ecx := And(ecx, 31); + call edx := GcLoad(edx); + ebx := edx; + call ebx := Shr(ebx, ecx); + call ebx := And(ebx, 3); +} + +procedure bb4SetColor($a:[int]int, $val:int, $aBase:int, $i0:int, $i1:int, $i2:int, $k:int, $g1:int, $g2:int) + requires bb2vec4($a, $aBase, $GcMem, $i0, $i1, $i2, $g1, $g2); + requires word($k - $i0) && $i1 <= $k && $k < $i2; + requires word($k) && word($i0) && Aligned($k) && Aligned($i0); + requires word($i2 - $i0); + requires 0 <= $val && $val <= 3; + requires esi == $k - $i0; + requires edi == $g1; + requires edx == $val; + requires Aligned($g1) && ?gcLo <= $g1 && $g2 <= ?gcHi; + requires word($i1 - $i0) && word($i2 - $i0); + modifies eax, esi, edi, ecx, edx, $GcMem; + ensures bb2vec4($a[$aBase + ($k - $i0) := $val], $aBase, $GcMem, $i0, $i1, $i2, $g1, $g2); + ensures $GcMem == old($GcMem)[$g1 + ColorIndex($i0, $k) := esi]; + ensures Aligned($k - $i0); +{ + var $idx:int; + var $bbb:int; + var $_bbb:int; + $idx := $g1 + 4 * shr($k - $i0, 6); + $bbb := and($GcMem[$idx], neg(shl(3, and(shr($k - $i0, 1), 31)))); + $_bbb := or($bbb, shl($val, and(shr($k - $i0, 1), 31))); + call __subAligned($k, $i0); + call __bb4Set2Bit($a, $val, $aBase, $GcMem, $i0, $i1, $i2, $k, $idx, $bbb, $_bbb, $GcMem[$idx := $_bbb], $g1, $g2); + assert TV($g1); + assert TO(shr(esi, 6)); + + ecx := esi; + call esi := Shr(esi, 6); + call edi := Lea(edi + 4 * esi); + //assert edi == $idx; + + call ecx := Shr(ecx, 1); + call ecx := And(ecx, 31); + eax := 3; + call eax := Shl(eax, ecx); + call eax := Not(eax); + call esi := GcLoad(edi); + call esi := And(esi, eax); + //assert $bbb == esi; + + call edx := Shl(edx, ecx); + call esi := Or(esi, edx); + assert $_bbb == esi; + call GcStore(edi, esi); +} + +procedure visit($_ptr:int, $abs:int, $extra:int) + requires ecx == $_ptr; + requires AllocInv2($toAbs, HeapLo, HeapHi, $r1, $r2, true); + requires MsGcInv(HeapLo, HeapHi, $Time, $r1, $r2, $color, true, $GcMem, $toAbs, $AbsMem, $gcSlice); + requires GcInv(ColorBase, HeapLo, HeapHi, $color, $toAbs, $r1, $r2, $GcMem); + requires Value(true, $r1, $_ptr, $abs); + requires !gcAddrEx($_ptr) || reached($toAbs[$_ptr - 4], $Time); + requires TV($_ptr - 4); + requires MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $extra); + requires $extra != 0 ==> Gray($color[$extra]); + requires StackTop <= ColorBase; + requires gcAddrEx($_ptr); + + modifies $r2, $color; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + modifies StackTop, $GcMem; + + ensures old(StackTop) <= StackTop && StackTop <= ColorBase; + ensures (forall ss:int::{TV(ss)} TV(ss) ==> HeapHi <= ss && ss < old(StackTop) ==> $GcMem[ss] == old($GcMem)[ss]); + ensures MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $extra); + ensures GcInv(ColorBase, HeapLo, HeapHi, $color, $toAbs, $r1, $r2, $GcMem); + ensures AllocInv2($toAbs, HeapLo, HeapHi, $r1, $r2, true); + ensures MsGcInv(HeapLo, HeapHi, $Time, $r1, $r2, $color, true, $GcMem, $toAbs, $AbsMem, $gcSlice); + ensures RExtend(old($r2), $r2); + ensures Value(true, $r2, $_ptr, $abs); + ensures !gcAddrEx($_ptr) || !White($color[$_ptr - 4]); + ensures (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> Gray(old($color)[i]) ==> Gray($color[i])); + ensures ( ( old(StackTop < ColorBase) && $GcMem == old($GcMem) + [ColorBase + ColorIndex(HeapLo, $_ptr - 4) := $GcMem[ColorBase + ColorIndex(HeapLo, $_ptr - 4)]] + [old(StackTop) := $GcMem[old(StackTop)]]) + && between(ColorBase, HeapLo, ColorBase + ColorIndex(HeapLo, $_ptr - 4))) + || $GcMem == old($GcMem); +{ + // call ebp := Lea(ecx - 4); REVIEW: add support for negative offsets to MASM generator + ebp := ecx; + call ebp := Sub(ebp, 4); + + ebx := ebp; + call ebx := Sub(ebx, HeapLo); + eax := ColorBase; + call bb4GetColor($color, HeapLo, HeapLo, HeapLo, HeapHi, ebp, ColorBase, HeapLo); + assert ebx == $color[ebp]; + + if (ebx != 1) { goto end; } // !white + eax := StackTop; + if (eax != ColorBase) { goto skip1; } + // stack overflow + call DebugBreak(); + skip1: + esi := ebp; + call esi := Sub(esi, HeapLo); + edi := ColorBase; + edx := 2; // gray + call bb4SetColor($color, 2, HeapLo, HeapLo, HeapLo, HeapHi, ebp, ColorBase, HeapLo); + $color[ebp] := 2; // gray + + $r2[ebp] := $r1[ebp]; + eax := StackTop; + call GcStore(eax, ebp); + assert TV(StackTop) && TO(1); + call __notAligned(StackTop); + call StackTop := Add(StackTop, 4); +end: +} + +procedure scanObjectDense($vt:int, $ptr:int, $abs:int) + requires ebx == $ptr; + requires ecx == $vt; + requires $vt == $AbsMem[$r1[$ptr]][1]; + requires tag($vt) == ?DENSE_TAG; + requires $abs == $r1[$ptr]; + requires BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + requires Pointer($r1, $ptr, $abs); + requires TV($ptr); + requires Gray($color[$ptr]); + requires MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + requires StackTop <= ColorBase; + + modifies $r2, $color; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + modifies StackTop, $GcMem; + + ensures old(StackTop) <= StackTop && StackTop <= ColorBase; + ensures (forall ss:int::{TV(ss)} TV(ss) ==> HeapHi <= ss && ss < old(StackTop) ==> $GcMem[ss] == old($GcMem)[ss]); + ensures MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + ensures BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + ensures RExtend(old($r2), $r2); + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($abs) ==> + Value(VFieldPtr($abs, j), $r2, $GcMem[$ptr + 4 * j], $AbsMem[$toAbs[$ptr]][j])); + +{ + var ptr:int; + var save1:int; + var save2:int; + var save3:int; + assert TO(numFields($r1[$ptr])); + ptr := ebx; + + esi := 2; + assert TVT($r1[$ptr], $vt); + assert TVL($r1[$ptr]); + call ebp := RoLoad32(ecx + ?VT_BASE_LENGTH); + call edx := RoLoad32(ecx + ?VT_MASK); + edi := ebx; + call edi := Add(edi, 8); + call ebp := Add(ebp, ebx); + + //while (edi < ebp) + loop: + assert TO(esi);// && 0 < esi; + assert edi == $ptr + 4 * esi && ebp == $ptr + 4 * numFields($r1[$ptr]) && edx == mask($vt); + assert 2 <= esi && esi <= numFields($r1[$ptr]); + assert Pointer($r2, $ptr, $r1[$ptr]); + assert BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + assert old(StackTop) <= StackTop && StackTop <= ColorBase; + assert (forall ss:int::{TV(ss)} TV(ss) ==> HeapHi <= ss && ss < old(StackTop) ==> $GcMem[ss] == old($GcMem)[ss]); + assert MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + assert Gray($color[$ptr]); + assert RExtend(old($r2), $r2); + assert ObjInvPartial($ptr, 0, esi, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert ObjInvPartial($ptr, esi, numFields($r1[$ptr]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert $toAbs[$ptr] != NO_ABS && $toAbs[$ptr] == $r1[$ptr]; + if (edi >= ebp) { goto loopEnd; } + if (esi >= 30) { goto loopEnd; } + + assert TO(0) && TO(1); + assert TVT($r1[$ptr], $vt); + assert TVL($r1[$ptr]); + call ecx := Lea(esi + 2); + ebx := edx; + call ebx := Shr(ebx, ecx); + call ebx := And(ebx, 1); + if (ebx != 1) { goto skip1; } + call ecx := GcLoad(edi); + + //if (gcAddrEx(ecx)) + if (ecx < ?gcLo) { goto skip2; } + if (ecx > ?gcHi) { goto skip2; } + assert TV(ecx - 4); + call reach($toAbs[$ptr], esi, $Time); + + save1 := esi; + save2 := ebp; + save3 := edx; + call visit(ecx, $AbsMem[$toAbs[$ptr]][esi], $ptr); + esi := save1; + ebp := save2; + edx := save3; + edi := ptr; + call edi := Lea(edi + 4 * esi); + skip2: + skip1: + + call esi := Add(esi, 1); + call edi := Add(edi, 4); + goto loop; + loopEnd: + + assert TVT($r1[$ptr], $vt); + assert TVL($r1[$ptr]); + assert TO(1); +} + +procedure scanObjectSparse($vt:int, $ptr:int, $abs:int) + requires ebx == $ptr; + requires ecx == $vt; + requires BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + requires MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + requires StackTop <= ColorBase; + requires Pointer($r1, $ptr, $abs); + requires TV($ptr); + requires Gray($color[$ptr]); + requires $vt == $AbsMem[$r1[$ptr]][1]; + requires tag($vt) == ?SPARSE_TAG; + modifies $r2, $color, StackTop, $GcMem; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + ensures MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + ensures old(StackTop) <= StackTop && StackTop <= ColorBase; + ensures (forall ss:int::{TV(ss)} TV(ss) ==> HeapHi <= ss && ss < old(StackTop) ==> $GcMem[ss] == old($GcMem)[ss]); + ensures RExtend(old($r2), $r2); + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($abs) ==> + Value(VFieldPtr($abs, j), $r2, $GcMem[$ptr + 4 * j], $AbsMem[$toAbs[$ptr]][j])); +{ + var save1:int; + var save2:int; + var save3:int; + var save4:int; + var ptr:int; + ptr := ebx; + assert TO(numFields($r1[$ptr])); + + esi := 1; + + assert TO(numFields($r1[$ptr])); + assert TVT($r1[$ptr], $vt); + call ebp := RoLoad32(ecx + ?VT_BASE_LENGTH); + call edx := RoLoad32(ecx + ?VT_MASK); + assert TVL($r1[$ptr]); + + esi := 1; + //while (esi < 8) + loop: + assert edx == mask($vt) && ebp == 4 * numFields($r1[$ptr]); + assert TSlot(esi) && 0 < esi && esi <= 8; + assert Pointer($r2, $ptr, $r1[$ptr]); + assert BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + assert old(StackTop) <= StackTop && StackTop <= ColorBase; + assert (forall ss:int::{TV(ss)} TV(ss) ==> HeapHi <= ss && ss < old(StackTop) ==> $GcMem[ss] == old($GcMem)[ss]); + assert MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + assert Gray($color[$ptr]); + assert RExtend(old($r2), $r2); + assert ObjInvBase($ptr, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($r1[$ptr]) ==> + between(1, esi, fieldToSlot($vt, j - 2)) ==> + ObjInvField($ptr, j, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice)); + assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($r1[$ptr]) ==> + !between(1, esi, fieldToSlot($vt, j - 2)) ==> + ObjInvField($ptr, j, $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice)); + assert $toAbs[$ptr] != NO_ABS && $toAbs[$ptr] == $r1[$ptr]; + if (esi >= 8) { goto loopEnd; } + + assert TO(0) && TO(1); + assert TVT($r1[$ptr], $vt); + assert TO(getNib(edx, 4 * esi) + 1); + + //if (getNib(edx, 4 * esi) != 0) + ecx := 0; + call ecx := Lea(ecx + 4 * esi); + ebx := edx; + call ebx := Shr(ebx, ecx); + call ebx := And(ebx, 15); + if (ebx == 0) { goto skip1; } + eax := ptr; + call ecx := GcLoad(eax + 4 * ebx + 4); + + //if (gcAddrEx(ecx)) + if (ecx < ?gcLo) { goto skip2; } + if (ecx > ?gcHi) { goto skip2; } + assert TV(ecx - 4); + call reach($toAbs[$ptr], 1 + ebx, $Time); + + save1 := esi; + save2 := ebp; + save3 := edx; + save4 := ebx; + call visit(ecx, $AbsMem[$toAbs[$ptr]][1 + getNib(edx, 4 * esi)], $ptr); + esi := save1; + ebp := save2; + edx := save3; + ebx := save4; + skip2: + skip1: + + call esi := Add(esi, 1); + goto loop; + loopEnd: + + assert TVT($r1[$ptr], $vt); + assert TVL($r1[$ptr]); + assert TO(1); +} + +procedure scanObjectOtherVectorNoPointers($vt:int, $ptr:int, $abs:int) + requires ebx == $ptr; + requires ecx == $vt; + requires BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + requires MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + requires StackTop <= ColorBase; + requires Pointer($r1, $ptr, $abs); + requires TV($ptr); + requires Gray($color[$ptr]); + requires $vt == $AbsMem[$r1[$ptr]][1]; + requires tag($vt) == ?OTHER_VECTOR_TAG; + requires arrayOf($vt) != ?TYPE_STRUCT || (arrayOf($vt) == ?TYPE_STRUCT && mask(arrayElementClass($vt)) == ?SPARSE_TAG); + modifies $r2, $color, StackTop, $GcMem; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + ensures MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + ensures old(StackTop) <= StackTop && StackTop <= ColorBase; + ensures (forall ss:int::{TV(ss)} TV(ss) ==> HeapHi <= ss && ss < old(StackTop) ==> $GcMem[ss] == old($GcMem)[ss]); + ensures RExtend(old($r2), $r2); + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($abs) ==> + Value(VFieldPtr($abs, j), $r2, $GcMem[$ptr + 4 * j], $AbsMem[$toAbs[$ptr]][j])); +{ + assert TO(numFields($r1[$ptr])); + assert TVT($r1[$ptr], $vt); +} + +procedure scanObjectOtherVectorPointersSparseFields($vt:int, $ptr:int, $abs:int, $jj:int, $index:int) + requires edx == mask(arrayElementClass($vt)); + requires edi == $jj; + requires ebp == $ptr; + requires tag($vt) == ?OTHER_VECTOR_TAG; + requires arrayOf($vt) == ?TYPE_STRUCT && tag(arrayElementClass($vt)) == ?SPARSE_TAG; + requires TO($jj) && $jj == baseWords($vt) + Mult(arrayElementWords($vt), $index); + requires $jj < numFields($abs); + requires TO($index) && 0 <= $index; // && $index <= nElems; + requires Mult(arrayElementWords($vt), $index) >= 0; + requires reached($abs, $Time); + requires BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + requires MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + requires StackTop <= ColorBase; + requires Pointer($r1, $ptr, $abs); + requires TV($ptr); + requires Gray($color[$ptr]); + requires $vt == $AbsMem[$r1[$ptr]][1]; + requires ObjInvPartial($ptr, 0, $jj, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires ObjInvPartial($ptr, $jj, numFields($r1[$ptr]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice); + requires $toAbs[$ptr] != NO_ABS && $toAbs[$ptr] == $r1[$ptr]; + requires (forall j:int::{TO(j)} TO(j) ==> + between(0, arrayElementWords($vt), j - $jj) ==> + ObjInvField($ptr, j, $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice)); + + modifies $r2, $color, StackTop, $GcMem; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + ensures BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + ensures MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + ensures old(StackTop) <= StackTop && StackTop <= ColorBase; + ensures (forall ss:int::{TV(ss)} TV(ss) ==> HeapHi <= ss && ss < old(StackTop) ==> $GcMem[ss] == old($GcMem)[ss]); + ensures Gray($color[$ptr]); + ensures RExtend(old($r2), $r2); + ensures ObjInvPartial($ptr, 0, $jj, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures ObjInvPartial($ptr, $jj + arrayElementWords($vt), numFields($r1[$ptr]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice); + ensures $toAbs[$ptr] != NO_ABS && $toAbs[$ptr] == $r2[$ptr]; + ensures (forall j:int::{TO(j)} TO(j) ==> + between(0, arrayElementWords($vt), j - $jj) ==> + between(1, 8, fieldToSlot(arrayElementClass($vt), j - $jj)) ==> + ObjInvField($ptr, j, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice)); + ensures (forall j:int::{TO(j)} TO(j) ==> + between(0, arrayElementWords($vt), j - $jj) ==> + !between(1, 8, fieldToSlot(arrayElementClass($vt), j - $jj)) ==> + ObjInvField($ptr, j, $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice)); + ensures edi == old(edi); + ensures edx == old(edx); +{ + var save1:int; + var save2:int; + var save3:int; + var save4:int; + var save5:int; + + assert TO(numFields($r2[$ptr])); + assert TO(2); + assert TVT($r2[$ptr], $vt); + assert TVL($r2[$ptr]); + + esi := 1; + //while (esi < 8) + loop: + assert TSlot(esi) && 0 < esi && esi <= 8; + assert edx == mask(arrayElementClass($vt)); + assert edi == $jj; + assert ebp == $ptr; + + assert BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + assert MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + assert old(StackTop) <= StackTop && StackTop <= ColorBase; + assert (forall ss:int::{TV(ss)} TV(ss) ==> HeapHi <= ss && ss < old(StackTop) ==> $GcMem[ss] == old($GcMem)[ss]); + + assert Gray($color[$ptr]); + assert RExtend(old($r2), $r2); + assert ObjInvPartial($ptr, 0, $jj, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert ObjInvPartial($ptr, $jj + arrayElementWords($vt), numFields($r2[$ptr]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert $toAbs[$ptr] != NO_ABS && $toAbs[$ptr] == $r2[$ptr]; + assert (forall j:int::{TO(j)} TO(j) ==> + between(0, arrayElementWords($vt), j - $jj) ==> + between(1, esi, fieldToSlot(arrayElementClass($vt), j - $jj)) ==> + ObjInvField($ptr, j, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice)); + assert (forall j:int::{TO(j)} TO(j) ==> + between(0, arrayElementWords($vt), j - $jj) ==> + !between(1, esi, fieldToSlot(arrayElementClass($vt), j - $jj)) ==> + ObjInvField($ptr, j, $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice)); + if (esi >= 8) { goto loopEnd; } + + // ebx := getNib(mask(arrayElementClass($vt)), 4 * esi); + ecx := 0; + call ecx := Lea(ecx + 4 * esi); + ebx := edx; + call ebx := Shr(ebx, ecx); + call ebx := And(ebx, 15); + assert ebx == getNib(mask(arrayElementClass($vt)), 4 * esi); + + // if (ebx != 0) + if (ebx == 0) { goto skip1; } + call ebx := Sub(ebx, 1); + call ebx := Add(ebx, edi); + assert TO(ebx); + + eax := ebp; + call ecx := GcLoad(eax + 4 * ebx); + //if (gcAddrEx(ecx)) + if (ecx < ?gcLo) { goto skip2; } + if (ecx > ?gcHi) { goto skip2; } + + assert TV(ecx - 4); + call reach($toAbs[$ptr], ebx, $Time); + + assert TO(0); + assert TO(1); + + save1 := esi; + save2 := edi; + save3 := edx; + save4 := ebx; + save5 := ebp; + call visit(ecx, $AbsMem[$toAbs[$ptr]][ebx], $ptr); + esi := save1; + edi := save2; + edx := save3; + ebx := save4; + ebp := save5; + + skip2: + skip1: + call esi := Add(esi, 1); + goto loop; + loopEnd: +} + +procedure scanObjectOtherVectorPointers($vt:int, $ptr:int, $abs:int) + requires ebx == $ptr; + requires ecx == $vt; + requires BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + requires MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + requires StackTop <= ColorBase; + requires Pointer($r1, $ptr, $abs); + requires TV($ptr); + requires Gray($color[$ptr]); + requires $vt == $AbsMem[$r1[$ptr]][1]; + requires tag($vt) == ?OTHER_VECTOR_TAG; + requires arrayOf($vt) == ?TYPE_STRUCT && tag(arrayElementClass($vt)) == ?SPARSE_TAG; + modifies $r2, $color, StackTop, $GcMem; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + ensures MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + ensures old(StackTop) <= StackTop && StackTop <= ColorBase; + ensures (forall ss:int::{TV(ss)} TV(ss) ==> HeapHi <= ss && ss < old(StackTop) ==> $GcMem[ss] == old($GcMem)[ss]); + ensures RExtend(old($r2), $r2); + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($abs) ==> + Value(VFieldPtr($abs, j), $r2, $GcMem[$ptr + 4 * j], $AbsMem[$toAbs[$ptr]][j])); +{ + var save1:int; + var save2:int; + var ptr:int; + + var $index:int; + + assert TO(numFields($r2[$ptr])); + assert TO(2); + assert TVT($r2[$ptr], $vt); + assert TVL($r2[$ptr]); + + $index := 0; + + ptr := ebx; + edx := ebx; + + call edi := RoLoad32(ecx + ?VT_BASE_LENGTH); + call ebx := RoLoad32(ecx + ?VT_ARRAY_ELEMENT_SIZE); + call edx := GcLoad(edx + 8); + eax := ebx; + call eax, edx := MulChecked(eax, edx); + call eax := AddChecked(eax, edi); + call eax := AddChecked(eax, 3); + edx := 3; + call edx := Not(edx); + call eax := And(eax, edx); + ebp := eax; + call edi := Shr(edi, 2); + + call ebx := Shr(ebx, 2); // arrayElementWords($vt) + + assert TVM(ebx, 0); + call edx := RoLoad32(ecx + ?VT_ARRAY_ELEMENT_CLASS); + call edx := RoLoad32(edx + ?VT_MASK); + + //while (4 * edi < ebp) + loop: + assert TO($index) && 0 <= $index; + assert Mult(ebx, $index) >= 0; + assert TO(edi) && edi == baseWords($vt) + Mult(ebx, $index); + + assert ebp == 4 * numFields($r2[$ptr]); + assert edx == mask(arrayElementClass($vt)); + assert ebx == arrayElementWords($vt); + assert 4 * edi <= ebp; + + assert Pointer($r2, $ptr, $r1[$ptr]); + assert BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + assert old(StackTop) <= StackTop && StackTop <= ColorBase; + assert (forall ss:int::{TV(ss)} TV(ss) ==> HeapHi <= ss && ss < old(StackTop) ==> $GcMem[ss] == old($GcMem)[ss]); + assert MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + assert Gray($color[$ptr]); + assert RExtend(old($r2), $r2); + assert ObjInvPartial($ptr, 0, edi, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert ObjInvPartial($ptr, edi, numFields($r2[$ptr]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert $toAbs[$ptr] != NO_ABS && $toAbs[$ptr] == $r2[$ptr]; + eax := 0; + call eax := Lea(eax + 4 * edi); + if (eax >= ebp) { goto loopEnd; } + + save1 := ebx; + save2 := ebp; + ebp := ptr; + call scanObjectOtherVectorPointersSparseFields($vt, $ptr, $abs, edi, $index); + ebx := save1; + ebp := save2; + + assert TVM3(ebx, $index, 1); + assert TVM(ebx, $index); + $index := $index + 1; + call edi := Add(edi, ebx); + goto loop; + loopEnd: + + assert TO(1); +} + +procedure scanObjectPtrArray($vt:int, $ptr:int, $abs:int) + requires ebx == $ptr; + requires ecx == $vt; + requires BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + requires MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + requires StackTop <= ColorBase; + requires Pointer($r1, $ptr, $abs); + requires TV($ptr); + requires Gray($color[$ptr]); + requires $vt == $AbsMem[$r1[$ptr]][1]; + requires tag($vt) == ?PTR_ARRAY_TAG; + modifies $r2, $color, StackTop, $GcMem; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + ensures MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + ensures old(StackTop) <= StackTop && StackTop <= ColorBase; + ensures (forall ss:int::{TV(ss)} TV(ss) ==> HeapHi <= ss && ss < old(StackTop) ==> $GcMem[ss] == old($GcMem)[ss]); + ensures RExtend(old($r2), $r2); + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($abs) ==> + Value(VFieldPtr($abs, j), $r2, $GcMem[$ptr + 4 * j], $AbsMem[$toAbs[$ptr]][j])); +{ + var $ind:int; + var save1:int; + var save2:int; + + assert TO(numFields($r1[$ptr])); + assert TO(3); + assert TVT($r1[$ptr], $vt); + assert TVL($r1[$ptr]); + + call ebp := GcLoad(ebx + 12); + call esi := RoLoad32(ecx + ?VT_BASE_LENGTH); + // size := pad(esi + 4 * ebp); + call ebp := AddChecked(ebp, ebp); + call ebp := AddChecked(ebp, ebp); + call ebp := AddChecked(ebp, esi); + call ebp := AddChecked(ebp, 3); + eax := 3; + call eax := Not(eax); + call ebp := And(ebp, eax); + call esi := Shr(esi, 2); + $ind := esi; + + call edi := Lea(ebx + 4 * esi); + call ebp := Add(ebp, ebx); + + //while (edi < ebp) + loop: + assert edi == $ptr + 4 * $ind; + assert ebp == $ptr + 4 * numFields($r1[$ptr]); + assert TO($ind) && baseWords($vt) <= $ind; // && $ind <= nElems + 3; + assert Pointer($r2, $ptr, $r1[$ptr]); + assert BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + assert old(StackTop) <= StackTop && StackTop <= ColorBase; + assert (forall ss:int::{TV(ss)} TV(ss) ==> HeapHi <= ss && ss < old(StackTop) ==> $GcMem[ss] == old($GcMem)[ss]); + assert MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + assert Gray($color[$ptr]); + assert RExtend(old($r2), $r2); + assert ObjInvPartial($ptr, 0, $ind, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert ObjInvPartial($ptr, $ind, numFields($r1[$ptr]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert $toAbs[$ptr] != NO_ABS && $toAbs[$ptr] == $r1[$ptr]; + if (edi >= ebp) { goto loopEnd; } + + assert TO(0) && TO(1) && TO(3); + assert TVT($r1[$ptr], $vt); + assert TVL($r1[$ptr]); + call ecx := GcLoad(edi); + //if (gcAddrEx(ecx)) + if (ecx < ?gcLo) { goto skip1; } + if (ecx > ?gcHi) { goto skip1; } + assert TV(ecx - 4); + call reach($toAbs[$ptr], $ind, $Time); + + save1 := edi; + save2 := ebp; + call visit(ecx, $AbsMem[$toAbs[$ptr]][$ind], $ptr); + edi := save1; + ebp := save2; + skip1: + + $ind := $ind + 1; + call edi := Add(edi, 4); + goto loop; + loopEnd: +} + +procedure scanObjectPtrVector($vt:int, $ptr:int, $abs:int) + requires ebx == $ptr; + requires ecx == $vt; + requires BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + requires MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + requires StackTop <= ColorBase; + requires Pointer($r1, $ptr, $abs); + requires TV($ptr); + requires Gray($color[$ptr]); + requires $vt == $AbsMem[$r1[$ptr]][1]; + requires tag($vt) == ?PTR_VECTOR_TAG; + modifies $r2, $color, StackTop, $GcMem; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + ensures MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + ensures old(StackTop) <= StackTop && StackTop <= ColorBase; + ensures (forall ss:int::{TV(ss)} TV(ss) ==> HeapHi <= ss && ss < old(StackTop) ==> $GcMem[ss] == old($GcMem)[ss]); + ensures RExtend(old($r2), $r2); + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($abs) ==> + Value(VFieldPtr($abs, j), $r2, $GcMem[$ptr + 4 * j], $AbsMem[$toAbs[$ptr]][j])); +{ + var $ind:int; + var save1:int; + var save2:int; + assert TO(numFields($r1[$ptr])); + assert TO(2); + assert TVT($r1[$ptr], $vt); + assert TVL($r1[$ptr]); + + call ebp := GcLoad(ebx + 8); + call esi := RoLoad32(ecx + ?VT_BASE_LENGTH); + // size := pad(esi + 4 * ebp); + call ebp := AddChecked(ebp, ebp); + call ebp := AddChecked(ebp, ebp); + call ebp := AddChecked(ebp, esi); + call ebp := AddChecked(ebp, 3); + eax := 3; + call eax := Not(eax); + call ebp := And(ebp, eax); + call esi := Shr(esi, 2); + $ind := esi; + + call edi := Lea(ebx + 4 * esi); + call ebp := Add(ebp, ebx); + + //while (edi < ebp) + loop: + assert edi == $ptr + 4 * $ind; + assert ebp == $ptr + 4 * numFields($r1[$ptr]); + assert TO($ind) && baseWords($vt) <= $ind; // && $ind <= nElems + 3; + assert Pointer($r2, $ptr, $r1[$ptr]); + assert BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + assert old(StackTop) <= StackTop && StackTop <= ColorBase; + assert (forall ss:int::{TV(ss)} TV(ss) ==> HeapHi <= ss && ss < old(StackTop) ==> $GcMem[ss] == old($GcMem)[ss]); + assert MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + assert Gray($color[$ptr]); + assert RExtend(old($r2), $r2); + assert ObjInvPartial($ptr, 0, $ind, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert ObjInvPartial($ptr, $ind, numFields($r1[$ptr]), $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert $toAbs[$ptr] != NO_ABS && $toAbs[$ptr] == $r1[$ptr]; + if (edi >= ebp) { goto loopEnd; } + + assert TO(0) && TO(1) && TO(2); + assert TVT($r1[$ptr], $vt); + call ecx := GcLoad(edi); + //if (gcAddrEx(ecx)) + if (ecx < ?gcLo) { goto skip1; } + if (ecx > ?gcHi) { goto skip1; } + assert TV(ecx - 4); + call reach($toAbs[$ptr], $ind, $Time); + + save1 := edi; + save2 := ebp; + call visit(ecx, $AbsMem[$toAbs[$ptr]][$ind], $ptr); + edi := save1; + ebp := save2; + skip1: + + $ind := $ind + 1; + call edi := Add(edi, 4); + goto loop; + loopEnd: +} + +procedure scanObjectOtherArrayNoPointers($vt:int, $ptr:int, $abs:int) + requires ebx == $ptr; + requires ecx == $vt; + requires BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + requires MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + requires StackTop <= ColorBase; + requires Pointer($r1, $ptr, $abs); + requires TV($ptr); + requires Gray($color[$ptr]); + requires $vt == $AbsMem[$r1[$ptr]][1]; + requires tag($vt) == ?OTHER_ARRAY_TAG; + requires arrayOf($vt) != ?TYPE_STRUCT; + modifies $r2, $color, StackTop, $GcMem; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + ensures MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + ensures old(StackTop) <= StackTop && StackTop <= ColorBase; + ensures (forall ss:int::{TV(ss)} TV(ss) ==> HeapHi <= ss && ss < old(StackTop) ==> $GcMem[ss] == old($GcMem)[ss]); + ensures RExtend(old($r2), $r2); + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($abs) ==> + Value(VFieldPtr($abs, j), $r2, $GcMem[$ptr + 4 * j], $AbsMem[$toAbs[$ptr]][j])); +{ + assert TO(numFields($r1[$ptr])); + assert TVT($r1[$ptr], $vt); +} + +procedure scanObjectString($vt:int, $ptr:int, $abs:int) + requires ebx == $ptr; + requires ecx == $vt; + requires BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + requires MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + requires StackTop <= ColorBase; + requires Pointer($r1, $ptr, $abs); + requires TV($ptr); + requires Gray($color[$ptr]); + requires $vt == $AbsMem[$r1[$ptr]][1]; + requires tag($vt) == ?STRING_TAG; + modifies $r2, $color, StackTop, $GcMem; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + ensures MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + ensures old(StackTop) <= StackTop && StackTop <= ColorBase; + ensures (forall ss:int::{TV(ss)} TV(ss) ==> HeapHi <= ss && ss < old(StackTop) ==> $GcMem[ss] == old($GcMem)[ss]); + ensures RExtend(old($r2), $r2); + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($abs) ==> + Value(VFieldPtr($abs, j), $r2, $GcMem[$ptr + 4 * j], $AbsMem[$toAbs[$ptr]][j])); +{ + assert TO(numFields($r1[$ptr])); + assert TVT($r1[$ptr], $vt); +} + +procedure scanObjectOther($vt:int, $ptr:int, $abs:int) + requires ebx == $ptr; + requires ecx == $vt; + requires BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + requires MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + requires StackTop <= ColorBase; + requires Pointer($r1, $ptr, $abs); + requires TV($ptr); + requires Gray($color[$ptr]); + requires $vt == $AbsMem[$r1[$ptr]][1]; + requires isOtherTag(tag($vt)); + modifies $r2, $color, StackTop, $GcMem; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + ensures MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + ensures old(StackTop) <= StackTop && StackTop <= ColorBase; + ensures (forall ss:int::{TV(ss)} TV(ss) ==> HeapHi <= ss && ss < old(StackTop) ==> $GcMem[ss] == old($GcMem)[ss]); + ensures RExtend(old($r2), $r2); + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($abs) ==> + Value(VFieldPtr($abs, j), $r2, $GcMem[$ptr + 4 * j], $AbsMem[$toAbs[$ptr]][j])); +{ + var save1:int; + var save2:int; + var save3:int; + var save4:int; + var save5:int; + var ptr:int; + ptr := ebx; + + assert TO(numFields($r1[$ptr])); + assert TVT($r1[$ptr], $vt); + assert TVL($r1[$ptr]); + + call edx := RoLoad32(ecx + ?VT_MASK); + call edi := RoLoad32(edx); + call ebp := RoLoad32(ecx + ?VT_BASE_LENGTH); + + esi := 1; + + //while (esi < edi + 1) + loop: + assert ebp == 4 * numFields($r1[$ptr]); + assert edx == mask($vt); + assert edi == ro32(mask($vt)); + assert TSlot(esi) && 0 < esi && esi <= ro32(mask($vt)) + 1; + assert Pointer($r2, $ptr, $r1[$ptr]); + assert BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + assert old(StackTop) <= StackTop && StackTop <= ColorBase; + assert (forall ss:int::{TV(ss)} TV(ss) ==> HeapHi <= ss && ss < old(StackTop) ==> $GcMem[ss] == old($GcMem)[ss]); + assert MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + assert Gray($color[$ptr]); + assert RExtend(old($r2), $r2); + assert ObjInvBase($ptr, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice); + assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($r1[$ptr]) ==> + between(1, esi, fieldToSlot($vt, j)) ==> + ObjInvField($ptr, j, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice)); + assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($r1[$ptr]) ==> + !between(1, esi, fieldToSlot($vt, j)) ==> + ObjInvField($ptr, j, $r2, $r1, $toAbs, $AbsMem, $GcMem, $gcSlice)); + assert $toAbs[$ptr] != NO_ABS && $toAbs[$ptr] == $r1[$ptr]; + if (esi > edi) { goto loopEnd; } + + assert TO(0) && TO(1); + assert TVT($r1[$ptr], $vt); + assert TVL($r1[$ptr]); + assert TO(ro32(mask($vt) + 4 * esi) + 1); + call ebx := RoLoad32(edx + 4 * esi); + //if (ebx != 0) + if (ebx == 0) { goto skip1; } + eax := ptr; + call ecx := GcLoad(eax + 4 * ebx + 4); + + //if (gcAddrEx(ecx)) + if (ecx < ?gcLo) { goto skip2; } + if (ecx > ?gcHi) { goto skip2; } + assert TV(ecx - 4); + call reach($toAbs[$ptr], 1 + ro32(edx + 4 * esi), $Time); + + save1 := ebx; + save2 := esi; + save3 := edi; + save4 := ebp; + save5 := edx; + call visit(ecx, $AbsMem[$toAbs[$ptr]][1 + ro32(edx + 4 * esi)], $ptr); + ebx := save1; + esi := save2; + edi := save3; + ebp := save4; + edx := save5; + skip2: + skip1: + call esi := AddChecked(esi, 1); + goto loop; + loopEnd: + + assert TVT($r1[$ptr], $vt); + assert TVL($r1[$ptr]); +} + +procedure scanObject($ptr:int, $abs:int) + requires ebx == $ptr; + requires $abs == $r1[$ptr]; + requires BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + requires Pointer($r1, $ptr, $abs); + requires TV($ptr); + requires Gray($color[$ptr]); + requires MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + requires StackTop <= ColorBase; + + modifies $r2, $color; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + modifies StackTop, $GcMem; + + ensures old(StackTop) <= StackTop && StackTop <= ColorBase; + ensures (forall ss:int::{TV(ss)} TV(ss) ==> HeapHi <= ss && ss < old(StackTop) ==> $GcMem[ss] == old($GcMem)[ss]); + ensures BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + ensures MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, $ptr); + ensures RExtend(old($r2), $r2); + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < numFields($abs) ==> + Value(VFieldPtr($abs, j), $r2, $GcMem[$ptr + 4 * j], $AbsMem[$toAbs[$ptr]][j])); +{ + var $vt:int; + + assert TO(1); + call ecx := GcLoad(ebx + 4); + $vt := ecx; + + assert TO(numFields($r1[$ptr])); + + call readTag($toAbs[$ptr], $vt); + + if (eax != ?SPARSE_TAG) { goto skip1; } + call scanObjectSparse($vt, $ptr, $abs); + goto end; + skip1: + + if (eax != ?DENSE_TAG) { goto skip2; } + call scanObjectDense($vt, $ptr, $abs); + goto end; + skip2: + + if (eax != ?STRING_TAG) { goto skip3; } + call scanObjectString($vt, $ptr, $abs); + goto end; + skip3: + + if (eax != ?PTR_VECTOR_TAG) { goto skip4; } + call scanObjectPtrVector($vt, $ptr, $abs); + goto end; + skip4: + + if (eax != ?OTHER_VECTOR_TAG) { goto skip5; } + call readArrayOf($r1[$ptr], $vt); + call readElementInfo($r1[$ptr], $vt); + if (ebp != ?TYPE_STRUCT) { goto noPoint; } + if (ebp != ?TYPE_STRUCT) { goto vecSkip1; } + if (edi != ?SPARSE_TAG) { goto vecSkip1; } + noPoint: + call scanObjectOtherVectorNoPointers($vt, $ptr, $abs); + goto end; + vecSkip1: + if (ebp != ?TYPE_STRUCT) { goto vecSkip2; } + + //if (tag(arrayElementClass(vt)) != ?SPARSE_TAG) { goto vecSkip2; } + eax := edi; + call eax := And(eax, 15); + if (eax != ?SPARSE_TAG) { goto vecSkip2; } + + call scanObjectOtherVectorPointers($vt, $ptr, $abs); + goto end; + + vecSkip2: + assert !( + (ebp != ?TYPE_STRUCT) + || (ebp == ?TYPE_STRUCT && edi == ?SPARSE_TAG) + || (ebp == ?TYPE_STRUCT && tag(arrayElementClass($vt)) == ?SPARSE_TAG)); + call DebugBreak(); + + skip5: + + if (eax != ?PTR_ARRAY_TAG) { goto skip6; } + call scanObjectPtrArray($vt, $ptr, $abs); + goto end; + skip6: + + if (eax != ?OTHER_ARRAY_TAG) { goto skip7; } + call readArrayOf($r1[$ptr], $vt); + if (ebp == ?TYPE_STRUCT) { goto arraySkip1; } + call scanObjectOtherArrayNoPointers($vt, $ptr, $abs); + goto end; + arraySkip1: + call DebugBreak(); + goto end; + skip7: + + call scanObjectOther($vt, $ptr, $abs); + end: +} + +procedure FromInteriorPointer($iptr:int, $offset:int, $abs:int) + requires ecx == $iptr; + requires BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + requires 0 <= $offset && $offset <= 4 * numFields($abs) - 4; + requires Pointer($r1, $iptr - $offset - 4, $abs); + modifies eax, ebx, ecx, edx, esp; + ensures eax == $offset; +{ + var save1:int; + var save2:int; + + eax := 0; +// while ($r1[$iptr - eax - 4] == NO_ABS) + loop: + assert $iptr - $offset <= $iptr - eax && $iptr - eax <= $iptr; + assert TV($iptr - eax - 4) && TV($iptr - $offset - 4); + assert ecx == $iptr; + + assert TV(?gcLo); + assert TO($iptr - eax - 4 - HeapLo); + + ebx := ecx; + call ebx := Sub(ebx, eax); + call ebx := Sub(ebx, 4); + call ebx := Sub(ebx, HeapLo); + edx := ebx; + call edx := And(edx, 3); + call __andAligned(ebx); + call __addAligned(HeapLo, ebx); + if (edx != 0) + { + goto skip1; + } + + save1 := eax; + save2 := ecx; + eax := ColorBase; + call bb4GetColor($color, HeapLo, HeapLo, HeapLo, HeapHi, $iptr - save1 - 4, ColorBase, HeapLo); + eax := save1; + ecx := save2; + + if (ebx == 0) { goto skip1; } + esp := esp + 4; return; + skip1: + call eax := Add(eax, 1); + goto loop; +} + +procedure scanStackUpdateInvs($r:[int]int, $f1:int, $f2:int, $frame:int, $addr:int, $v:int) + requires $FrameSlice[$addr] == $frame; + requires !($f1 <= $frame && $frame < $f2); + requires (forall f:int::{TV(f)} TV(f) ==> $f1 <= f && f < $f2 ==> + FrameInv(f, $FrameLayout[f], $r, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + ensures (forall f:int::{TV(f)} TV(f) ==> $f1 <= f && f < $f2 ==> + FrameInv(f, $FrameLayout[f], $r, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem[$addr := $v], $FrameAbs, $FrameOffset, $Time)); +{ + assert TO(0) && TO(1); + assert (forall f:int::{TV(f)} TV(f) ==> TVF($FrameLayout[f]) && TVFT(f)); +} + +procedure scanStackWordDense($frame:int, $addr:int, $desc:int, $addrp:int, $p:int, $args:int) + requires ecx == $addrp; + requires BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + requires MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + requires StackTop <= ColorBase; + requires $desc == frameDescriptor($FrameLayout[$frame]); + requires $addr == $FrameAddr[$frame]; + requires $FrameSlice[$addrp] == $frame; + requires $args == frameLayoutArgs($FrameLayout[$frame]); + requires $addrp == $addr + 4 * $p; + requires getBit($desc, 0) && !getBit($desc, 1) && and(shr($desc, 6), 1023) == 0; + requires frameHasPtr($FrameLayout[$frame], $p); + requires 0 <= $frame && $frame < $FrameCount && TV($frame); + requires (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f < $frame ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + requires (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + + requires $p <= 1 + $args && $p > $args - 1 - 16 && TO($p); + requires (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f < $frame ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + requires (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + requires FrameNextInv($frame, $FrameMem[$FrameAddr[$frame] + 4], $FrameMem[$FrameAddr[$frame]], $FrameAddr, $FrameLayout); + requires (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j <= $p ==> + $FrameSlice[$addr + 4 * j] == $frame + && InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r1, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j]) + && (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time)) + ); + requires (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j > $p ==> + $FrameSlice[$addr + 4 * j] == $frame + && InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r2, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j]) + && (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time)) + ); + + modifies $r2, $color; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + modifies StackTop, $GcMem; + + ensures (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f < $frame ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + ensures (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + ensures FrameNextInv($frame, $FrameMem[$FrameAddr[$frame] + 4], $FrameMem[$FrameAddr[$frame]], $FrameAddr, $FrameLayout); + ensures (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j <= $p - 1 ==> + $FrameSlice[$addr + 4 * j] == $frame + && InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r1, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j]) + && (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time)) + ); + ensures (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j > $p - 1 ==> + $FrameSlice[$addr + 4 * j] == $frame + && InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r2, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j]) + && (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time)) + ); + ensures BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + ensures MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + ensures StackTop <= ColorBase; + ensures RExtend(old($r2), $r2); +{ + var save1:int; + var v:int; + var offset:int; + assert TVF($FrameLayout[$frame]); + assert TV($FrameMem[$addrp] - 4); + + call eax := FrameLoad(($frame), ecx); + //if (gcAddrEx(eax)) + if (eax < ?gcLo) { goto skip1; } + if (eax > ?gcHi) { goto skip1; } + save1 := ecx; + v := eax; + + ecx := eax; + esp := esp - 4; call FromInteriorPointer(v, $FrameOffset[$addrp], $FrameAbs[$frame][$p]); + offset := eax; + + ecx := v; + call ecx := Sub(ecx, eax); + assert TV(ecx - 4); + + call visit(ecx, $FrameAbs[$frame][$p], 0); + + assert TV(eax - 4); + call scanStackUpdateInvs($r1, 0, $frame, $frame, $addrp, eax + offset); + call scanStackUpdateInvs($r2, $frame + 1, $FrameCount, $frame, $addrp, eax + offset); + + ecx := save1; + skip1: +} + +procedure scanStackDense($frame:int, $addr:int, $desc:int) + requires ecx == $addr; + requires edx == $desc; + requires $desc == frameDescriptor($FrameLayout[$frame]); + requires $addr == $FrameAddr[$frame]; + requires getBit($desc, 0) && !getBit($desc, 1) && and(shr($desc, 6), 1023) == 0; + requires 0 <= $frame && $frame < $FrameCount && TV($frame); + requires (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f <= $frame ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + requires (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + requires BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + requires MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + requires StackTop <= ColorBase; + + modifies $r2, $color; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + modifies StackTop, $GcMem; + + ensures (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f <= $frame - 1 ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + ensures (forall f:int::{TV(f)} TV(f) ==> $frame - 1 < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + ensures BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + ensures MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + ensures StackTop <= ColorBase; + ensures RExtend(old($r2), $r2); +{ + var b:int; + var v:int; + var offset:int; + var args:int; + var addr:int; + var desc:int; + var addrp:int; + assert TVF($FrameLayout[$frame]); + assert TO(0); + + addr := ecx; + desc := edx; + + eax := edx; + call eax := Shr(eax, 2); + call eax := And(eax, 15); + args := eax; // frameLayoutArgs($FrameLayout[$frame]); + b := 0; + ebx := 0; + call ebx := Lea(ebx + 4 * eax + 4); + call ebx := AddChecked(ebx, ecx); + addrp := ebx; + + //while (b < args) + loop1: + assert addrp == $addr + 4 * (args + 1 - b); + assert $frame < $FrameCount && TV($frame); + assert 0 <= b && b <= args && TO(args + 1 - b); + assert (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f < $frame ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + assert (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + + assert FrameNextInv($frame, $FrameMem[$FrameAddr[$frame] + 4], $FrameMem[$FrameAddr[$frame]], $FrameAddr, $FrameLayout); + + assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j <= (args + 1 - b) ==> + $FrameSlice[$addr + 4 * j] == $frame + && InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r1, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j]) + && (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time)) + ); + + assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j > (args + 1 - b) ==> + $FrameSlice[$addr + 4 * j] == $frame + && InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r2, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j]) + && (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time)) + ); + + assert BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + assert MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + assert StackTop <= ColorBase; + assert RExtend(old($r2), $r2); + eax := b; + if (eax >= args) { goto loopEnd1; } + + assert TVF($FrameLayout[$frame]); + call ecx := Lea(eax + 16); + ebx := desc; + call ebx := Shr(ebx, ecx); + call ebx := And(ebx, 1); + //if (getBit($desc, 16 + b)) + if (ebx != 1) { goto skip1; } + ecx := addrp; + call scanStackWordDense($frame, $addr, $desc, $addr + 4 * (args + 1 - b), args + 1 - b, args); + skip1: + + call b := Add(b, 1); + call addrp := Sub(addrp, 4); + goto loop1; + loopEnd1: + + assert TO(0); + assert TO(1); + assert TV($FrameMem[$addr]); + assert TV($FrameMem[$addr] - 4); + + call addrp := SubChecked(addrp, 8); + + //while (b < 16) + loop2: + assert addrp == $addr + 4 * (args - 1 - b); + assert $frame < $FrameCount && TV($frame); + assert (args - 1 - b) < 0 && (args - 1 - b) <= 1 + args && TO(args - 1 - b); + assert (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f < $frame ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + assert (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + + assert FrameNextInv($frame, $FrameMem[$FrameAddr[$frame] + 4], $FrameMem[$FrameAddr[$frame]], $FrameAddr, $FrameLayout); + + assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j <= (args - 1 - b) ==> + $FrameSlice[$addr + 4 * j] == $frame + && InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r1, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j]) + && (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time)) + ); + + assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && j > (args - 1 - b) ==> + $FrameSlice[$addr + 4 * j] == $frame + && InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r2, $FrameMem[$addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[$addr + 4 * j]) + && (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[$addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time)) + ); + + assert BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + assert MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + assert StackTop <= ColorBase; + assert RExtend(old($r2), $r2); + eax := b; + if (eax >= 16) { goto loopEnd2; } + + assert TVF($FrameLayout[$frame]); + call ecx := Lea(eax + 16); + ebx := desc; + call ebx := Shr(ebx, ecx); + call ebx := And(ebx, 1); + //if (getBit($desc, 16 + b)) + if (ebx != 1) { goto skip2; } + ecx := addrp; + call scanStackWordDense($frame, $addr, $desc, $addr + 4 * (args - 1 - b), args - 1 - b, args); + skip2: + call b := Add(b, 1); + call addrp := SubChecked(addrp, 4); + goto loop2; + loopEnd2: +} + +procedure scanStackSparse8($frame:int, $addr:int, $desc:int) + requires ecx == $addr; + requires edx == $desc; + requires $desc == frameDescriptor($FrameLayout[$frame]); + requires $addr == $FrameAddr[$frame]; + requires !getBit($desc, 0) && ro32($desc) == 4096; + requires 0 <= $frame && $frame < $FrameCount && TV($frame); + requires (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f <= $frame ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + requires (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + requires BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + requires MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + requires StackTop <= ColorBase; + + modifies $r2, $color; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + modifies StackTop, $GcMem; + + ensures (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f <= $frame - 1 ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + ensures (forall f:int::{TV(f)} TV(f) ==> $frame - 1 < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + ensures BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + ensures MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + ensures StackTop <= ColorBase; + ensures RExtend(old($r2), $r2); +{ + var p:int; + var v:int; + var addr:int; + var desc:int; + var addrp:int; + var offset:int; + var count:int; + assert TVF($FrameLayout[$frame]); + assert TO(0); + + addr := ecx; + desc := edx; + + p := 0; + call eax := RoLoadU8(edx + 4); + count := eax; + //while (p < count) + loop: + assert p >= 0 && TSlot(p); + + assert (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f < $frame ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + assert (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + + assert FrameNextInv($frame, $FrameMem[$FrameAddr[$frame] + 4], $FrameMem[$FrameAddr[$frame]], $FrameAddr, $FrameLayout); + + assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && !between(0, p, frameFieldToSlot($FrameLayout[$frame], j)) ==> + $FrameSlice[addr + 4 * j] == $frame + && InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r1, $FrameMem[addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[addr + 4 * j]) + && (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time)) + ); + + assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && between(0, p, frameFieldToSlot($FrameLayout[$frame], j)) ==> + $FrameSlice[addr + 4 * j] == $frame + && InteriorValue(frameHasPtr($FrameLayout[$frame], j), $r2, $FrameMem[addr + 4 * j], $FrameAbs[$frame][j], $FrameOffset[addr + 4 * j]) + && (frameHasPtr($FrameLayout[$frame], j) && gcAddrEx($FrameMem[addr + 4 * j]) ==> reached($FrameAbs[$frame][j], $Time)) + ); + + assert BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + assert MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + assert StackTop <= ColorBase; + assert RExtend(old($r2), $r2); + eax := p; + if (eax >= count) { goto loopEnd; } + + assert TO(roS8(desc + 6 + p)); + assert TV($FrameMem[addr + 4 * roS8(desc + 6 + p)] - 4); + ebx := desc; + esi := addr; + call ebp := RoLoadS8(ebx + 1 * eax + 6); + call ebp := LeaSignedIndex(esi, 4, ebp, 0); + addrp := ebp; + call ecx := FrameLoad(($frame), ebp); + //if (gcAddrEx(ecx)) + if (ecx < ?gcLo) { goto skip1; } + if (ecx > ?gcHi) { goto skip1; } + v := ecx; + esp := esp - 4; call FromInteriorPointer(v, $FrameOffset[addr + 4 * roS8(desc + 6 + p)], $FrameAbs[$frame][roS8(desc + 6 + p)]); + offset := eax; + + ecx := v; + call ecx := Sub(ecx, eax); + assert TV(ecx - 4); + + call visit(ecx, $FrameAbs[$frame][roS8(desc + 6 + p)], 0); + assert TV(eax - 4); + call scanStackUpdateInvs($r1, 0, $frame, $frame, addr + 4 * roS8(desc + 6 + p), eax); + call scanStackUpdateInvs($r2, $frame + 1, $FrameCount, $frame, addr + 4 * roS8(desc + 6 + p), eax); + ebx := addrp; + skip1: + + // This is just here to improve performance: + assert (forall j:int::{TO(j)} TO(j) ==> inFrame($FrameLayout[$frame], j) && + p == frameFieldToSlot($FrameLayout[$frame], j) ==> j == roS8(desc + 6 + p)); + + call p := Add(p, 1); + goto loop; + loopEnd: +} + +procedure scanStack($ra:int, $nextFp:int) + requires ecx == $ra && word($ra); + requires edx == $nextFp; + requires FrameNextInv($FrameCount, $ra, $nextFp, $FrameAddr, $FrameLayout); + requires StackInv($r1, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + requires BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + requires MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + requires StackTop <= ColorBase; + modifies $r2, $color; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + modifies StackTop, $GcMem; + ensures StackInv($r2, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + ensures BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + ensures RExtend(old($r2), $r2); + ensures MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + ensures StackTop <= ColorBase; +{ + var $frame:int; + var p:int; + var addr:int; + var desc:int; + var found:int; + var _ra:int; + var _nextFp:int; + _ra := ecx; + _nextFp := edx; + + $frame := $FrameCount - 1; + loop: + assert $frame < $FrameCount && TV($frame); + assert word(_ra); + assert FrameNextInv($frame + 1, _ra, _nextFp, $FrameAddr, $FrameLayout); + assert (forall f:int::{TV(f)} TV(f) ==> 0 <= f && f <= $frame ==> + FrameInv(f, $FrameLayout[f], $r1, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + assert (forall f:int::{TV(f)} TV(f) ==> $frame < f && f < $FrameCount ==> + FrameInv(f, $FrameLayout[f], $r2, $FrameAddr, $FrameSlice, $FrameLayout, $FrameMem, $FrameAbs, $FrameOffset, $Time)); + assert BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + assert MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + assert StackTop <= ColorBase; + assert RExtend(old($r2), $r2); + + assert TVF($FrameLayout[$frame]); + + ecx := _ra; + edx := _nextFp; + esp := esp - 4; call TablesSearch($frame + 1, _ra, _nextFp); + desc := eax; + found := edx; + + if (edx == 0) { goto loopEnd; } + + ecx := _nextFp; + addr := ecx; + assert TV(addr); + assert TO(0); + assert TO(1); + call eax := FrameLoad(($frame), ecx); + _nextFp := eax; + call eax := FrameLoad(($frame), ecx + 4); + _ra := eax; + + // if (getBit(desc, 0) && !getBit(desc, 1) && and(shr(desc, 6), 1023) == 0) + eax := desc; + call eax := Shr(eax, 0); + call eax := And(eax, 1); + if (eax != 1) { goto skip1; } + eax := desc; + call eax := Shr(eax, 1); + call eax := And(eax, 1); + if (eax == 1) { goto skip1; } + eax := desc; + call eax := Shr(eax, 6); + call eax := And(eax, 1023); + if (eax != 0) { goto skip1; } + ecx := addr; + edx := desc; + call scanStackDense($frame, addr, desc); + $frame := $frame - 1; + goto loop; + skip1: + + // else if (!getBit(desc, 0) && ro32(desc) == 4096) + eax := desc; + call eax := Shr(eax, 0); + call eax := And(eax, 1); + if (eax == 1) { goto skip2; } + eax := desc; + call eax := RoLoad32(eax); + if (eax != 4096) { goto skip2; } + ecx := addr; + edx := desc; + call scanStackSparse8($frame, addr, desc); + $frame := $frame - 1; + goto loop; + skip2: + + // else + assert !( (getBit(desc, 0) && !getBit(desc, 1) && and(shr(desc, 6), 1023) == 0) + || (!getBit(desc, 0) && ro32(desc) == 4096)); + call DebugBreak(); + + loopEnd: + assert $frame == 0 - 1; +} + +procedure scanStaticSection($section:int) + requires ecx == $section; + requires 0 <= $section && $section < ?sectionCount && TV($section); + requires (forall s:int::{TV(s)} TV(s) ==> $section <= s && s < ?sectionCount ==> + SectionInv(s, sectionBase(s), sectionEnd(s), $r1, $SectionMem, $SectionAbs, $Time)); + requires (forall s:int::{TV(s)} TV(s) ==> 0 <= s && s < $section ==> + SectionInv(s, sectionBase(s), sectionEnd(s), $r2, $SectionMem, $SectionAbs, $Time)); + ensures (forall s:int::{TV(s)} TV(s) ==> $section + 1 <= s && s < ?sectionCount ==> + SectionInv(s, sectionBase(s), sectionEnd(s), $r1, $SectionMem, $SectionAbs, $Time)); + ensures (forall s:int::{TV(s)} TV(s) ==> 0 <= s && s < $section + 1 ==> + SectionInv(s, sectionBase(s), sectionEnd(s), $r2, $SectionMem, $SectionAbs, $Time)); + + requires BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + requires MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + requires StackTop <= ColorBase; + + modifies $r2, $color; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + modifies StackTop, $GcMem; + + ensures BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + ensures StackTop <= ColorBase; + ensures MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + ensures RExtend(old($r2), $r2); +{ + var sEnd:int; + var addr:int; + var section:int; + var save1:int; + var save2:int; + var save3:int; + + section := ecx; + + assert TVS(section, 0); + + eax := ?dataSectionEnd; + call eax := RoLoad32(eax + 4 * ecx); + sEnd := eax; + + eax := ?staticDataPointerBitMap; + call edx := RoLoad32(eax + 4 * ecx); + + eax := ?dataSectionBase; + call eax := RoLoad32(eax + 4 * ecx); + addr := eax; + edi := eax; + + esi := 0; + //while (edi < sEnd) + loop: + assert edx == ro32(?staticDataPointerBitMap + 4 * $section); + assert edi == addr + 4 * esi; + assert 0 <= section && TV(section); + assert 0 <= esi && TO(esi); + assert (forall s:int::{TV(s)} TV(s) ==> section < s && s < ?sectionCount ==> + SectionInv(s, sectionBase(s), sectionEnd(s), $r1, $SectionMem, $SectionAbs, $Time)); + assert (forall s:int::{TV(s)} TV(s) ==> 0 <= s && s < section ==> + SectionInv(s, sectionBase(s), sectionEnd(s), $r2, $SectionMem, $SectionAbs, $Time)); + + assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && addr + 4 * j < sectionEnd(section) && j >= esi ==> + sectionSlice(addr + 4 * j) == section + && Value(sectionHasPtr(section, j), $r1, $SectionMem[addr + 4 * j], $SectionAbs[section][j]) + && (sectionHasPtr(section, j) && gcAddrEx($SectionMem[addr + 4 * j]) ==> reached($SectionAbs[section][j], $Time)) + ); + + assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && addr + 4 * j < sectionEnd(section) && j < esi ==> + sectionSlice(addr + 4 * j) == section + && Value(sectionHasPtr(section, j), $r2, $SectionMem[addr + 4 * j], $SectionAbs[section][j]) + && (sectionHasPtr(section, j) && gcAddrEx($SectionMem[addr + 4 * j]) ==> reached($SectionAbs[section][j], $Time)) + ); + + assert StackTop <= ColorBase; + assert MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + assert BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + assert RExtend(old($r2), $r2); + + if (edi >= sEnd) { goto loopEnd; } + + assert TVS(section, esi); + eax := esi; + call eax := Shr(eax, 5); + call eax := RoLoad32(edx + 4 * eax); + // assert getBit(v, and(esi, 31)) == sectionHasPtr(section, esi); + //if (getBit(v, and(esi, 31))) + ecx := esi; + call ecx := And(ecx, 31); + call eax := Shr(eax, ecx); + call eax := And(eax, 1); + if (eax != 1) { goto skip1; } + assert TV($SectionMem[addr + 4 * esi] - 4); + call ecx := SectionLoad(edi); + //if (gcAddrEx(ecx)) + if (ecx < ?gcLo) { goto skip2; } + if (ecx > ?gcHi) { goto skip2; } + save1 := edi; + save2 := esi; + save3 := edx; + call visit(ecx, $SectionAbs[section][esi], 0); + edi := save1; + esi := save2; + edx := save3; + skip2: + skip1: + + call esi := Add(esi, 1); + call edi := AddChecked(edi, 4); + goto loop; + loopEnd: +} + +procedure scanStatic() + requires StaticInv($r1, $SectionMem, $SectionAbs, $Time); + ensures StaticInv($r2, $SectionMem, $SectionAbs, $Time); + + requires BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + requires MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + requires StackTop <= ColorBase; + + modifies $r2, $color; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + modifies StackTop, $GcMem; + + ensures BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + ensures StackTop <= ColorBase; + ensures MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + ensures RExtend(old($r2), $r2); +{ + var section:int; + section := 0; + //while (section < ?sectionCount) + loop: + assert 0 <= section && TV(section); + assert (forall s:int::{TV(s)} TV(s) ==> section <= s && s < ?sectionCount ==> + SectionInv(s, sectionBase(s), sectionEnd(s), $r1, $SectionMem, $SectionAbs, $Time)); + assert (forall s:int::{TV(s)} TV(s) ==> 0 <= s && s < section ==> + SectionInv(s, sectionBase(s), sectionEnd(s), $r2, $SectionMem, $SectionAbs, $Time)); + assert BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + assert StackTop <= ColorBase; + assert MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + assert RExtend(old($r2), $r2); + ecx := section; + if (ecx >= ?sectionCount) { goto loopEnd; } + + call scanStaticSection(section); + call section := AddChecked(section, 1); + goto loop; + loopEnd: +} + +procedure AllocBlock($minSize:int, $maxSize:int, $maxWords:int) + requires ecx == $minSize; + requires edx == $maxSize; + requires MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + requires 8 <= $minSize && $minSize <= $maxSize; + requires $maxSize == 4 * $maxWords; + + modifies $fs, $fn, $GcMem, CacheSize; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + ensures MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + ensures eax != 0 ==> $fs[eax] >= $minSize; + ensures eax != 0 ==> HeapLo < eax && eax + $fs[eax] <= HeapHi; + ensures eax != 0 ==> Disconnected(HeapLo, eax, $fs, $fn); + ensures eax != 0 ==> CacheSize != 0 ==> eax != CachePtr; + ensures eax != 0 ==> Aligned(eax); +{ + edi := HeapLo; + + loop: + assert MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + assert HeapLo <= edi && edi < HeapHi; + assert $fs[edi] != 0; + + assert TV(edi) && TV($fn[edi]) && TO(1); + esi := edi; + call edi := GcLoad(edi); + if (edi != 0) { goto notEmpty; } + eax := 0; + esp := esp + 4; return; + notEmpty: + + call ebx := GcLoad(edi + 4); + //assert $fs[edi] == ebx; + if (ebx < ecx) { goto loop; } + ebp := ebx; + call ebp := Sub(ebp, 8); + if (ebp < edx) { goto skip1; } + // Allocate from the end of the block + assert TV(edi + $fs[edi]); + assert TO(0 - $maxWords); + call eax := Lea(edi + ebx); + call eax := Sub(eax, edx); + assert TV(eax); + + // If we dip below ReserveMin, skip this block + if (eax < ReserveMin) { goto loop; } + + call ebx := Sub(ebx, edx); + // If the remaining size is too small, allocate the whole block + // TUNING: min cache size + if (ebx < 256) { goto skip1; } +// if (ebx < 1024) { goto skip1; } + + $fn[eax] := 0; + $fs[eax] := edx; + call GcStore(eax, 0); + call GcStore(eax + 4, edx); + + $fs[edi] := $fs[edi] - edx; + call GcStore(edi + 4, ebx); + esp := esp + 4; return; + skip1: + // Allocate the whole block + + // If we dip below ReserveMin, skip this block + if (edi < ReserveMin) { goto loop; } + + $fn[esi] := $fn[edi]; + call ebx := GcLoad(edi); + call GcStore(esi, ebx); + $fn[edi] := 0; + call GcStore(edi, 0); + eax := edi; + esp := esp + 4; return; +} + +procedure allocFromCache($size:int, $nFields:int) + requires ecx == $size; + requires MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + requires 8 <= $size && $size + 8 <= CacheSize; + requires $size == 4 * $nFields; + + modifies $fs, $fn, $GcMem, CacheSize; + modifies eax, edx; + ensures MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + ensures $fs[eax] >= $size; + ensures HeapLo < eax && eax + $size <= HeapHi; + ensures Disconnected(HeapLo, eax, $fs, $fn); + ensures eax != CachePtr; + ensures Aligned(eax); +{ + assert TV(CachePtr) && TV(CachePtr + CacheSize); + assert TO(0 - $nFields) && TO(1); + call CacheSize := Sub(CacheSize, ecx); + $fs[CachePtr] := CacheSize; + eax := CacheSize; + edx := CachePtr; + call GcStore(edx + 4, eax); + + call eax := Add(eax, edx); + assert TV(CachePtr) && TV(eax); + + $fn[eax] := 0; + $fs[eax] := $size; + call GcStore(eax, 0); + call GcStore(eax + 4, ecx); +} + +procedure AllocSlow($size:int, $nFields:int) + requires ecx == $size; + requires MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + requires 8 <= $size; + requires $size == 4 * $nFields; + modifies $fs, $fn, $GcMem, CachePtr, CacheSize; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + ensures MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + ensures eax != 0 ==> $fs[eax] >= $size; + ensures eax != 0 ==> HeapLo < eax && eax + $size <= HeapHi; + ensures eax != 0 ==> Disconnected(HeapLo, eax, $fs, $fn); + ensures eax != 0 ==> CacheSize != 0 ==> eax != CachePtr; + ensures eax != 0 ==> Aligned(eax); +{ + var size:int; + + // TUNING: min cache size + if (ecx < 192) { goto skip1; } +// if (ecx < 768) { goto skip1; } +// if (ecx < 1536) { goto skip1; } + // Large object: allocate directly + edx := ecx; + esp := esp - 4; call AllocBlock($size, $size, $nFields); + esp := esp + 4; return; + //else + skip1: + // Small object: allocate from cache + size := ecx; + // TUNING: min cache size + ecx := 256; +// ecx := 1024; +// ecx := 2048; + edx := 65536; + // TUNING: min cache size + esp := esp - 4; call AllocBlock(256, 65536, 16384); +// esp := esp - 4; call AllocBlock(1024, 65536, 16384); +// esp := esp - 4; call AllocBlock(2048, 65536, 16384); + assert TV(eax); + if (eax == 0) { goto skip2; } + CachePtr := eax; + assert TO(1); + ecx := CachePtr; + call ecx := GcLoad(ecx + 4); + CacheSize := ecx; + ecx := size; + call allocFromCache($size, $nFields); + skip2: + esp := esp + 4; return; +} + +procedure processMarkStack() + requires BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + requires MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + requires StackTop <= ColorBase; + + modifies $r2, $color; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + modifies StackTop, $GcMem; + + ensures BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + ensures StackTop == ?gcLo; + ensures MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + ensures RExtend(old($r2), $r2); +{ + var ptr:int; + eax := StackTop; + + // while (StackTop != ?gcLo) + loop: + assert MarkStack(?gcLo, StackTop, $toAbs, $color, $GcMem, 0); + assert BigGcInv($freshAbs, ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, $r1, $r2, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize, $Time); + assert RExtend(old($r2), $r2); + assert StackTop <= ColorBase; + + eax := StackTop; + if (eax == ?gcLo) { goto loopEnd; } + + assert TV(StackTop) && TO(0 - 1); + call __notAligned(?gcLo); + call eax := Sub(eax, 4); + call ebx := GcLoad(eax); + ptr := ebx; + StackTop := eax; + assert TV(StackTop); + assert TV(ptr); + + call __notAligned(StackTop); + call scanObject(ptr, $r1[ptr]); + + esi := ptr; + call esi := Sub(esi, HeapLo); + edi := ColorBase; + edx := 3; // black + call bb4SetColor($color, 3, HeapLo, HeapLo, HeapLo, HeapHi, ptr, ColorBase, HeapLo); + $color[ptr] := 3; // black + goto loop; + loopEnd: +} + +procedure sweepObject($ptr:int, $prev:int, $regionStart:int) returns($_ptr:int, $_prev:int, $_regionStart:int) + requires ebx == $color[$ptr]; + requires esi == $ptr; + requires edi == $prev; + requires ebp == $regionStart; + requires $freshAbs != NO_ABS; + requires HeapLo + 8 <= HeapHi; + requires $toAbs[$ptr] != NO_ABS; + requires Black($color[$ptr]) ==> $toAbs[$ptr] == $r2[$ptr]; + requires $ptr < HeapHi; + + requires GcInv(ColorBase, HeapLo, HeapHi, $color, $toAbs, $r1, $r2, $GcMem); + requires ObjectSeq(HeapLo, $regionStart, $toAbs, $fs, $fn); + requires FreeInv(HeapLo, $regionStart, $fs, $fn, $GcMem, CachePtr, CacheSize); + requires Objects($ptr, HeapHi, $toAbs, $fs, $fn); + requires $fs[$prev] != 0; + requires $GcMem[$prev] == 0; + requires Aligned($prev) && HeapLo <= $prev && $prev + 8 <= $regionStart && $regionStart <= $ptr; + requires $ptr < HeapHi + 4; + requires (forall ii:int::{TV(ii)} TV(ii) ==> HeapLo <= ii && ii < $prev && $fs[ii] != 0 ==> $fn[ii] <= $prev); + requires (forall ii:int::{TV(ii)} TV(ii) ==> $prev <= ii && ii < $ptr && $fs[ii] != 0 ==> $fn[ii] == 0); + requires (forall ii:int::{TV(ii)} TV(ii) ==> $regionStart <= ii && ii < $ptr ==> $toAbs[ii] == NO_ABS && $fs[ii] == 0); + requires CacheSize == 0; + requires HeapLo <= $ptr; + requires Aligned($regionStart) && Aligned($ptr); + requires AllocInv2($toAbs, HeapLo, HeapHi, $r1, $r2, false); + requires (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < HeapHi ==> $toAbs[i] != NO_ABS ==> !Gray($color[i])); + requires (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < $ptr ==> (White($color[i]) <==> $toAbs[i] != NO_ABS)); + requires (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < $ptr && $toAbs[i] != NO_ABS ==> + $r1[i] != NO_ABS && $r2[i] != NO_ABS + && $toAbs[i] != NO_ABS && $toAbs[i] == $r2[i] + && ObjInv(i, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice)); + requires MsGcInv($ptr, HeapHi, $Time, $r1, $r2, $color, true, $GcMem, $toAbs, $AbsMem, $gcSlice); + + modifies $color; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + modifies $toAbs, $fs, $fn, $GcMem, CacheSize; + + ensures esi == $_ptr; + ensures edi == $_prev; + ensures ebp == $_regionStart; + ensures GcInv(ColorBase, HeapLo, HeapHi, $color, $toAbs, $r1, $r2, $GcMem); + ensures ObjectSeq(HeapLo, $_regionStart, $toAbs, $fs, $fn); + ensures FreeInv(HeapLo, $_regionStart, $fs, $fn, $GcMem, CachePtr, CacheSize); + ensures Objects($_ptr, HeapHi, $toAbs, $fs, $fn); + ensures $fs[$_prev] != 0; + ensures $GcMem[$_prev] == 0; + ensures Aligned($_prev) && HeapLo <= $_prev && $_prev + 8 <= $_regionStart && $_regionStart <= $_ptr; + ensures $_ptr < HeapHi + 4; + ensures (forall ii:int::{TV(ii)} TV(ii) ==> HeapLo <= ii && ii < $_prev && $fs[ii] != 0 ==> $fn[ii] <= $_prev); + ensures (forall ii:int::{TV(ii)} TV(ii) ==> $_prev <= ii && ii < $_ptr && $fs[ii] != 0 ==> $fn[ii] == 0); + ensures (forall ii:int::{TV(ii)} TV(ii) ==> $_regionStart <= ii && ii < $_ptr ==> $toAbs[ii] == NO_ABS && $fs[ii] == 0); + ensures CacheSize == 0; + ensures HeapLo <= $_ptr; + ensures Aligned($_regionStart) && Aligned($_ptr); + ensures AllocInv2($toAbs, HeapLo, HeapHi, $r1, $r2, false); + ensures (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < HeapHi ==> $toAbs[i] != NO_ABS ==> !Gray($color[i])); + ensures (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < $_ptr ==> (White($color[i]) <==> $toAbs[i] != NO_ABS)); + ensures (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < $_ptr && $toAbs[i] != NO_ABS ==> + $r1[i] != NO_ABS && $r2[i] != NO_ABS + && $toAbs[i] != NO_ABS && $toAbs[i] == $r2[i] + && ObjInv(i, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice)); + ensures MsGcInv($_ptr, HeapHi, $Time, $r1, $r2, $color, true, $GcMem, $toAbs, $AbsMem, $gcSlice); +{ + var size:int; + var save1:int; + var save2:int; + var save3:int; + + assert TV(esi) && TV(ebp) && TV(edi) && TO(1); + assert TO(numFields($toAbs[esi])); + + if (ebx != 3) { goto skip1; } // !black + save1 := esi; + save2 := edi; + save3 := ebp; + ecx := esi; + call edx := GcLoad(esi + 4); + esp := esp - 4; call GetSize($ptr, $GcMem[$ptr + 4], $r2, $r2); + size := eax; + assert size == 4 * numFields($toAbs[$ptr]); + esi := save1; + ebp := save3; + + call esi := Sub(esi, HeapLo); + edi := ColorBase; + edx := 1; // white + call bb4SetColor($color, 1, HeapLo, HeapLo, HeapLo, HeapHi, $ptr, ColorBase, HeapLo); + esi := save1; + edi := save2; + $color[esi] := 1; // white + + eax := esi; + call eax := Sub(eax, ebp); + // TUNING: min cache size + if (eax < 256) { goto skipFree; } +// if (eax < 1024) { goto skipFree; } +// if (eax < 2048) { goto skipFree; } + // Turn region into free block + $fn[edi] := ebp; + call GcStore(edi, ebp); + $fn[ebp] := 0; + $fs[ebp] := eax; + call GcStore(ebp, 0); + call GcStore(ebp + 4, eax); + edi := ebp; + skipFree: + // Start a new region. + call esi := Add(esi, size); + ebp := esi; + goto skip2; + //else + skip1: + assert White($color[esi]); + save1 := esi; + save2 := edi; + save3 := ebp; + ecx := esi; + call edx := GcLoad(esi + 4); + esp := esp - 4; call GetSize($ptr, $GcMem[$ptr + 4], $r1, $r1); + size := eax; + assert size == 4 * numFields($toAbs[$ptr]); + esi := save1; + edi := save2; + ebp := save3; + + $toAbs[esi] := NO_ABS; + call esi := Sub(esi, HeapLo); + edi := ColorBase; + edx := 0; // unallocated + call bb4SetColor($color, 0, HeapLo, HeapLo, HeapLo, HeapHi, $ptr, ColorBase, HeapLo); + esi := save1; + edi := save2; + $color[esi] := 0; // unallocated + + call esi := Add(esi, size); + skip2: + $_ptr := esi; + $_prev := edi; + $_regionStart := ebp; +} + +procedure Sweep() + requires AllocInv2($toAbs, HeapLo, HeapHi, $r1, $r2, false); + requires MsGcInv(HeapLo, HeapHi, $Time, $r1, $r2, $color, true, $GcMem, $toAbs, $AbsMem, $gcSlice); + requires (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < HeapHi ==> $toAbs[i] != NO_ABS ==> !Gray($color[i])); + requires (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < HeapHi ==> $toAbs[i] != NO_ABS && Black($color[i]) ==> + $toAbs[i] == $r2[i]); + requires GcInv(ColorBase, HeapLo, HeapHi, $color, $toAbs, $r1, $r2, $GcMem); + requires $freshAbs != NO_ABS; + requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $freshAbs); + requires Objects(HeapLo + 8, HeapHi, $toAbs, $fs, $fn); + requires $fs[HeapLo] == 8; + requires ObjectSeq(HeapLo, HeapLo + 8, $toAbs, $fs, $fn); + requires FreeInv(HeapLo, HeapLo + 8, $fs, $fn, $GcMem, CachePtr, CacheSize); + requires HeapLo + 8 <= HeapHi; + + modifies $color; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + modifies $toAbs, $fs, $fn, $GcMem, CacheSize; + + ensures (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < HeapHi ==> $toAbs[i] != NO_ABS ==> reached($toAbs[i], $Time)); + ensures MsInv(HeapLo, HeapHi, $color, $GcMem, $toAbs, $AbsMem, $gcSlice); + ensures GcInv(ColorBase, HeapLo, HeapHi, $color, $toAbs, $r1, $r2, $GcMem); + ensures (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $freshAbs); + ensures ObjectSeq(HeapLo, HeapHi, $toAbs, $fs, $fn); + ensures FreeInv(HeapLo, HeapHi, $fs, $fn, $GcMem, CachePtr, CacheSize); + ensures $toAbs == $r2; +{ + // esi = ptr + // edi = prev + // ebp = regionStart + var $_ptr:int; + var $_prev:int; + var $_regionStart:int; + assert TV(HeapLo); + assert TV(HeapLo + 8); + + CacheSize := 0; + edi := HeapLo; + call esi := Lea(edi + 8); + ebp := esi; + $fn[edi] := 0; + assert TV(edi); + call GcStore(edi, 0); + + // + // ... [edi:free ... ] ... [ebp:empty ... ] esi + // + + //while (esi < HeapHi) + loop: + assert GcInv(ColorBase, HeapLo, HeapHi, $color, $toAbs, $r1, $r2, $GcMem); + assert ObjectSeq(HeapLo, ebp, $toAbs, $fs, $fn); + assert FreeInv(HeapLo, ebp, $fs, $fn, $GcMem, CachePtr, CacheSize); + assert Objects(esi, HeapHi, $toAbs, $fs, $fn); + assert $fs[edi] != 0; + assert $GcMem[edi] == 0; + assert Aligned(edi); + assert HeapLo <= edi && edi + 8 <= ebp && ebp <= esi; + assert esi < HeapHi + 4; + assert (forall ii:int::{TV(ii)} TV(ii) ==> HeapLo <= ii && ii < edi && $fs[ii] != 0 ==> $fn[ii] <= edi); + assert (forall ii:int::{TV(ii)} TV(ii) ==> edi <= ii && ii < esi && $fs[ii] != 0 ==> $fn[ii] == 0); + assert (forall ii:int::{TV(ii)} TV(ii) ==> ebp <= ii && ii < esi ==> $toAbs[ii] == NO_ABS && $fs[ii] == 0); + assert CacheSize == 0; + + assert HeapLo <= esi; + assert Aligned(ebp) && Aligned(esi); + assert AllocInv2($toAbs, HeapLo, HeapHi, $r1, $r2, false); + assert (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < HeapHi ==> $toAbs[i] != NO_ABS ==> !Gray($color[i])); + + assert (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < esi ==> (White($color[i]) <==> $toAbs[i] != NO_ABS)); + assert (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < esi && $toAbs[i] != NO_ABS ==> + $r1[i] != NO_ABS && $r2[i] != NO_ABS + && $toAbs[i] != NO_ABS && $toAbs[i] == $r2[i] + && ObjInv(i, $r2, $r2, $toAbs, $AbsMem, $GcMem, $gcSlice)); + assert MsGcInv(esi, HeapHi, $Time, $r1, $r2, $color, true, $GcMem, $toAbs, $AbsMem, $gcSlice); + if (esi >= HeapHi) { goto loopEnd; } + + assert TV(esi) && TV(ebp) && TV(edi); + + ebx := esi; + call ebx := Sub(ebx, HeapLo); + eax := ColorBase; + call bb4GetColor($color, HeapLo, HeapLo, HeapLo, HeapHi, esi, ColorBase, HeapLo); + assert ebx == $color[esi]; + if (ebx == 0) { goto skip1; } // unallocated + call $_ptr, $_prev, $_regionStart := sweepObject(esi, edi, ebp); + goto loop; + //else + skip1: + assert TO(1); + call __notAligned(esi); + $fs[esi] := 0; + call esi := Add(esi, 4); + goto loop; + loopEnd: + + // don't forget the last region + eax := esi; + call eax := Sub(eax, ebp); + // TUNING: min cache size + if (eax < 256) { goto skip2; } +// if (eax < 1024) { goto skip2; } +// if (eax < 2048) { goto skip2; } + // Turn region into free block + $fn[edi] := ebp; + call GcStore(edi, ebp); + $fn[ebp] := 0; + $fs[ebp] := esi - ebp; + assert TV(ebp) && TO(1); + call GcStore(ebp, 0); + call GcStore(ebp + 4, eax); + edi := ebp; + skip2: + + call __notAligned(HeapHi); + $toAbs := $r2; + + esp := esp + 4; return; +} + +procedure GarbageCollect($ra:int, $nextFp:int) + requires ecx == $ra && word($ra); + requires edx == $nextFp; + requires FrameNextInv($FrameCount, $ra, $nextFp, $FrameAddr, $FrameLayout); + requires StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + requires StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + requires MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + requires $freshAbs != NO_ABS; + requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $freshAbs); + + modifies $r1, $r2, $GcMem, $toAbs, $gcSlice, $color, StackTop; + modifies $fs, $fn, CacheSize; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + // postcondition same as precondition, plus reached: + ensures StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + ensures StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + ensures MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + ensures (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $freshAbs); + ensures (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < HeapHi && $toAbs[i] != NO_ABS ==> + reached($toAbs[i], $Time)); + ensures ebp == old(ebp); +{ + var saveEbp:int; + saveEbp := ebp; + + $r1 := $toAbs; + $r2 := MAP_NO_ABS; + + eax := ?gcLo; + StackTop := eax; + call scanStack($ra, $nextFp); + call scanStatic(); + call processMarkStack(); + + assert TV(HeapLo); + $fn[HeapLo] := 0; + eax := HeapLo; + call GcStore(eax, 0); + CacheSize := 0; + + esp := esp - 4; call Sweep(); + + ebp := saveEbp; + esp := esp + 4; return; +} + +procedure allocObjectMemory($ra:int, $nextFp:int, $size:int, $nFields:int) + requires eax == $size; + requires ecx == $ra && word($ra); + requires edx == $nextFp; + requires FrameNextInv($FrameCount, $ra, $nextFp, $FrameAddr, $FrameLayout); + requires StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + requires StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + requires MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + requires 8 <= $size; + requires $size == 4 * $nFields; + requires $freshAbs != NO_ABS; + requires (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < HeapHi ==> $toAbs[i] != $freshAbs); + + modifies $r1, $r2, $GcMem, $toAbs, $gcSlice, $color, StackTop; + modifies $fs, $fn, CachePtr, CacheSize, ReserveMin; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + ensures StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + ensures StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + ensures MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + ensures $fs[eax] >= $size; + ensures HeapLo < eax && eax + $size <= HeapHi; + ensures Disconnected(HeapLo, eax, $fs, $fn); + ensures CacheSize != 0 ==> eax != CachePtr; + ensures Aligned(eax); + ensures (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < HeapHi ==> $toAbs[i] != $freshAbs); +{ + var ra:int; + var nextFp:int; + var size:int; + ra := ecx; + + ecx := eax; + call eax := AddChecked(eax, 8); + if (eax > CacheSize) { goto skip1; } + call allocFromCache($size, $nFields); + goto end; + //else + skip1: + nextFp := edx; + size := ecx; + esp := esp - 4; call AllocSlow($size, $nFields); + if (eax != 0) { goto end; } + ecx := ra; // $ra && word($ra); + edx := nextFp; // $nextFp; + + esp := esp - 4; call GarbageCollect($ra, $nextFp); + ecx := size; + esp := esp - 4; call AllocSlow($size, $nFields); + if (eax != 0) { goto end; } + // Try using some of the reserve: + ecx := size; + call ReserveMin := SubChecked(ReserveMin, ecx); + // TUNING: wilderness policy // REVIEW: this should be a parameter, not hard-wired into the code + call ReserveMin := SubChecked(ReserveMin, 1048576); // Bartok,Zinger +// call ReserveMin := SubChecked(ReserveMin, 65536); // Sat + esp := esp - 4; call AllocSlow($size, $nFields); + if (eax != 0) { goto end; } + // out of memory + call DebugBreak(); + end: +} + +procedure doAllocWord($ret:int, $ind:int, $nf:int) + requires esi == $ret + 4 * $ind; + + requires TO($ind) && $ind >= 0 && $ind < $nf; + requires Aligned($ret) && HeapLo <= $ret && $ret + 4 * $ind + 4 <= HeapHi; + requires MsInv(HeapLo, HeapHi, $color, $GcMem, $toAbs, $AbsMem, $gcSlice); + requires GcInv(ColorBase, HeapLo, HeapHi, $color, $toAbs, $toAbs, $toAbs, $GcMem); + requires ObjectSeq(HeapLo, HeapHi, $toAbs, $fs, $fn); + requires FreeInvBase(HeapLo, HeapHi, $fs, $fn, $GcMem, CachePtr, CacheSize); + requires (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < HeapHi && $fs[i] != 0 && i != $ret ==> + FreeInvI(i, HeapLo, HeapHi, $fs, $fn, $GcMem, CachePtr, CacheSize)); + requires (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind ==> $gcSlice[$ret + 4 * j] == $ret); + requires (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind ==> gcAddr($ret + 4 * j)); // REVIEW: necessary? + requires (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind ==> $GcMem[$ret + 4 * j] == NULL); + requires $fs[$ret] >= 4 * $nf; + requires CacheSize != 0 ==> $ret != CachePtr; + requires Disconnected(HeapLo, $ret, $fs, $fn); + + modifies $GcMem, $gcSlice; + + ensures MsInv(HeapLo, HeapHi, $color, $GcMem, $toAbs, $AbsMem, $gcSlice); + ensures GcInv(ColorBase, HeapLo, HeapHi, $color, $toAbs, $toAbs, $toAbs, $GcMem); + ensures ObjectSeq(HeapLo, HeapHi, $toAbs, $fs, $fn); + ensures FreeInvBase(HeapLo, HeapHi, $fs, $fn, $GcMem, CachePtr, CacheSize); + ensures (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < HeapHi && $fs[i] != 0 && i != $ret ==> + FreeInvI(i, HeapLo, HeapHi, $fs, $fn, $GcMem, CachePtr, CacheSize)); + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind + 1 ==> $gcSlice[$ret + 4 * j] == $ret); + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind + 1 ==> gcAddr($ret + 4 * j)); // REVIEW: necessary? + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind + 1 ==> $GcMem[$ret + 4 * j] == NULL); + ensures ebp == old(ebp); +{ + assert TV($ret); + assert TV($ret + 4 * $ind);// && TV($ret + 4 * $ind + 1) && TV($ret + 4 * $ind + 2) && TV($ret + 4 * $ind + 3); + $gcSlice[$ret + 4 * $ind] := $ret; + + call GcStore(esi, 0); +} + +procedure doAllocWords($ret:int, $size:int, $nf:int) + requires eax == $ret; + requires ebx == $ret + $size; + requires $size == $nf * 4; + requires $nf >= 0; + requires Aligned($ret) && HeapLo <= $ret && $ret + $size <= HeapHi; + requires MsInv(HeapLo, HeapHi, $color, $GcMem, $toAbs, $AbsMem, $gcSlice); + requires GcInv(ColorBase, HeapLo, HeapHi, $color, $toAbs, $toAbs, $toAbs, $GcMem); + requires ObjectSeq(HeapLo, HeapHi, $toAbs, $fs, $fn); + requires FreeInvBase(HeapLo, HeapHi, $fs, $fn, $GcMem, CachePtr, CacheSize); + requires (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < HeapHi && $fs[i] != 0 && i != $ret ==> + FreeInvI(i, HeapLo, HeapHi, $fs, $fn, $GcMem, CachePtr, CacheSize)); + requires $fs[$ret] >= 4 * $nf; + requires CacheSize != 0 ==> $ret != CachePtr; + requires Disconnected(HeapLo, $ret, $fs, $fn); + + modifies $GcMem, $gcSlice; + modifies esi; + + ensures MsInv(HeapLo, HeapHi, $color, $GcMem, $toAbs, $AbsMem, $gcSlice); + ensures GcInv(ColorBase, HeapLo, HeapHi, $color, $toAbs, $toAbs, $toAbs, $GcMem); + ensures ObjectSeq(HeapLo, HeapHi, $toAbs, $fs, $fn); + ensures FreeInvBase(HeapLo, HeapHi, $fs, $fn, $GcMem, CachePtr, CacheSize); + ensures (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < HeapHi && $fs[i] != 0 && i != $ret ==> + FreeInvI(i, HeapLo, HeapHi, $fs, $fn, $GcMem, CachePtr, CacheSize)); + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $nf ==> $gcSlice[$ret + 4 * j] == $ret); + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $nf ==> gcAddr($ret + 4 * j)); // REVIEW: necessary? + ensures (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $nf ==> $GcMem[$ret + 4 * j] == NULL); + ensures ebp == old(ebp); +{ + var $ind:int; + $ind := 0; + + esi := ?gcLo; + esi := eax; + + //while (4 * $ind < $size) + if (esi >= ebx) { goto loopEnd; } + loop: + assert 4 * $ind < $size; + assert esi == $ret + 4 * $ind; + assert TO($ind) && $ind >= 0; + assert MsInv(HeapLo, HeapHi, $color, $GcMem, $toAbs, $AbsMem, $gcSlice); + assert GcInv(ColorBase, HeapLo, HeapHi, $color, $toAbs, $toAbs, $toAbs, $GcMem); + assert ObjectSeq(HeapLo, HeapHi, $toAbs, $fs, $fn); + assert FreeInvBase(HeapLo, HeapHi, $fs, $fn, $GcMem, CachePtr, CacheSize); + assert (forall i:int::{TV(i)} TV(i) ==> HeapLo <= i && i < HeapHi && $fs[i] != 0 && i != $ret ==> + FreeInvI(i, HeapLo, HeapHi, $fs, $fn, $GcMem, CachePtr, CacheSize)); + assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind ==> $gcSlice[$ret + 4 * j] == $ret); + assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind ==> gcAddr($ret + 4 * j)); // REVIEW: necessary? + assert (forall j:int::{TO(j)} TO(j) ==> 0 <= j && j < $ind ==> $GcMem[$ret + 4 * j] == NULL); + + call doAllocWord($ret, $ind, $nf); + $ind := $ind + 1; + call esi := Add(esi, 4); + if (esi < ebx) { goto loop; } + loopEnd: +} + +procedure doAllocObject($ra:int, $nextFp:int, $ptr:int, $abs:int, $vt:int, $size:int) + requires eax == $ptr; + requires ebx == $ptr + $size; + requires ecx == $vt; + requires HeapLo < $ptr && $ptr + 4 * numFields($abs) <= HeapHi && Aligned($ptr); + requires !VFieldPtr($abs, 0); + requires !VFieldPtr($abs, 1); + requires 2 <= numFields($abs); + requires $size == 4 * numFields($abs); + requires ObjSize($abs, $vt, $AbsMem[$abs][2], $AbsMem[$abs][3]); + requires MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + requires $fs[$ptr] >= 4 * numFields($abs); + requires CacheSize != 0 ==> $ptr != CachePtr; + requires Disconnected(HeapLo, $ptr, $fs, $fn); + + // requirements on vtable and layout: + requires word($vt) && !gcAddrEx($vt); + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, 0, 0); + requires !isVarSize(tag($vt)); + + // require a fresh, empty abstract node: + requires $abs != NO_ABS; + requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs); + requires $AbsMem[$abs][0] == NULL; + requires $AbsMem[$abs][1] == $vt; + requires (forall j:int::{TO(j)} TO(j) ==> 2 <= j && j < numFields($abs) ==> $AbsMem[$abs][j] == NULL); + + modifies $GcMem, $toAbs, $gcSlice, $fs, $color; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + ensures MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + ensures old($toAbs)[eax - 4] == NO_ABS; + ensures $toAbs == old($toAbs)[eax - 4 := $abs]; + ensures Pointer($toAbs, eax - 4, $abs); + ensures ebp == old(ebp); +{ + assert TV($ptr) && TV($fn[$ptr]); + + assert TV(eax + 0) && TV(eax + 4); + assert TO(1) && TO(2); + assert TO(numFields($abs)); + + call doAllocWords(eax, $size, numFields($abs)); + + call GcStore(eax + 4, ecx); + + $toAbs[$ptr] := $abs; + ebx := eax; + esi := eax; + call esi := Sub(esi, HeapLo); + edi := ColorBase; + edx := 1; // white + call bb4SetColor($color, 1, HeapLo, HeapLo, HeapLo, HeapHi, $ptr, ColorBase, HeapLo); + $color[$ptr] := 1; // white + $fs[$ptr] := 0; + + assert TO(numFields($abs)); + + call eax := Lea(ebx + 4); + + assert TV(eax + 4); + assert TO(0); +} + +procedure doAllocString($ra:int, $nextFp:int, $ptr:int, $abs:int, $vt:int, $nElems:int) + requires eax == $ptr; + requires ebx == $ptr + pad(16 + 2 * $nElems); + requires ecx == pad(16 + 2 * $nElems); + requires edx == $nElems; + requires $vt == ?STRING_VTABLE; + requires $ptr + pad(16 + 2 * $nElems) + 0 <= HeapHi; + + requires HeapLo < $ptr && $ptr + 4 * numFields($abs) <= HeapHi && Aligned($ptr); + requires !VFieldPtr($abs, 0); + requires !VFieldPtr($abs, 1); + requires 2 <= numFields($abs); + requires ObjSize($abs, $vt, $AbsMem[$abs][2], $AbsMem[$abs][3]); + requires MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + requires $fs[$ptr] >= 4 * numFields($abs); + requires CacheSize != 0 ==> $ptr != CachePtr; + requires Disconnected(HeapLo, $ptr, $fs, $fn); + + // requirements on vtable and layout: + requires word($vt) && !gcAddrEx($vt); + requires word($nElems); + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, $nElems, 0); + requires tag($vt) == ?STRING_TAG; + + // require a fresh, empty abstract node: + requires $abs != NO_ABS; + requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs); + requires $AbsMem[$abs][0] == NULL; + requires $AbsMem[$abs][1] == $vt; + requires $AbsMem[$abs][2] == $nElems; + requires $AbsMem[$abs][3] == $nElems - 1; + requires (forall j:int::{TO(j)} TO(j) ==> 4 <= j && 4 * j < pad(16 + 2 * $nElems) ==> $AbsMem[$abs][j] == NULL); + + modifies $GcMem, $toAbs, $gcSlice, $fs, $color; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + ensures MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + ensures old($toAbs)[eax - 4] == NO_ABS; + ensures $toAbs == old($toAbs)[eax - 4 := $abs]; + ensures Pointer($toAbs, eax - 4, $abs); + ensures ebp == old(ebp); +{ + assert TV(eax + 0) && TV(eax + 4) && TV(eax + 8) && TV(eax + 12); + assert TO(1) && TO(2) && TO(3); + assert TVL($abs); + assert TVT($abs, $vt); + assert TO(numFields($abs)); + + call doAllocWords(eax, pad(16 + 2 * $nElems), numFields($abs)); + + call GcStore(eax + 8, edx); + call edx := SubChecked(edx, 1); + call GcStore(eax + 12, edx); + edx := ?STRING_VTABLE; + call GcStore(eax + 4, edx); + + $toAbs[$ptr] := $abs; + ebx := eax; + esi := eax; + call esi := Sub(esi, HeapLo); + edi := ColorBase; + edx := 1; // white + call bb4SetColor($color, 1, HeapLo, HeapLo, HeapLo, HeapHi, $ptr, ColorBase, HeapLo); + $color[$ptr] := 1; // white + $fs[$ptr] := 0; + + assert TO(numFields($abs)); + + call eax := Lea(ebx + 4); + + assert TV(eax + 4); + assert TO(0); +} + +procedure doAllocArrayHelper($abs:int, $vt:int, $rank:int, $nElems:int) + requires ecx == $vt; + requires esi == $nElems; + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, $rank, $nElems); + requires tag($vt) == ?PTR_ARRAY_TAG || tag($vt) == ?OTHER_ARRAY_TAG; + modifies eax, ebx, edx, edi; + ensures !VFieldPtr($abs, 0); + ensures !VFieldPtr($abs, 1); + ensures !VFieldPtr($abs, 2); + ensures !VFieldPtr($abs, 3); + ensures between(4, numFields($abs), baseWords($vt)); + ensures eax == 4 * numFields($abs); +{ + assert TO(2) && TO(3); + assert TVL($abs); + assert TVT($abs, $vt); + + call eax := RoLoad32(ecx + ?VT_BASE_LENGTH); + call ebx := RoLoad32(ecx + ?VT_ARRAY_ELEMENT_SIZE); + call edi := RoLoad32(ecx + ?VT_MASK); + call edi := And(edi, 15); + + //if (edi == ?PTR_ARRAY_TAG) + if (edi != ?PTR_ARRAY_TAG) { goto skip1; } + edi := esi; + call edi := AddChecked(edi, edi); + call edi := AddChecked(edi, edi); + call eax := AddChecked(eax, edi); + call eax := AddChecked(eax, 3); + edi := 3; + call edi := Not(edi); + call eax := And(eax, edi); + goto skip2; + //else + skip1: + edi := eax; + eax := ebx; + call eax, edx := MulChecked(eax, esi); + call eax := AddChecked(eax, edi); + call eax := AddChecked(eax, 3); + edi := 3; + call edi := Not(edi); + call eax := And(eax, edi); + skip2: +} + +procedure doAllocArray($ra:int, $nextFp:int, $ptr:int, $abs:int, $vt:int, $rank:int, $nElems:int, $size:int) + requires eax == $ptr; + requires ebx == $ptr + $size; + requires ecx == $vt; + requires edx == $rank; + requires esi == $nElems; + requires HeapLo < $ptr && $ptr + 4 * numFields($abs) <= HeapHi && Aligned($ptr); + requires !VFieldPtr($abs, 0); + requires !VFieldPtr($abs, 1); + requires !VFieldPtr($abs, 2); + requires !VFieldPtr($abs, 3); + requires 4 <= numFields($abs); + requires $size == 4 * numFields($abs); + requires ObjSize($abs, $vt, $rank, $nElems); + requires MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + requires $fs[$ptr] >= 4 * numFields($abs); + requires CacheSize != 0 ==> $ptr != CachePtr; + requires Disconnected(HeapLo, $ptr, $fs, $fn); + + // requirements on vtable and layout: + requires word($vt) && !gcAddrEx($vt); + requires word($rank); + requires word($nElems); + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, $rank, $nElems); + requires tag($vt) == ?PTR_ARRAY_TAG || tag($vt) == ?OTHER_ARRAY_TAG; + + // require a fresh, empty abstract node: + requires $abs != NO_ABS; + requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs); + requires $AbsMem[$abs][0] == NULL; + requires $AbsMem[$abs][1] == $vt; + requires $AbsMem[$abs][2] == $rank; + requires $AbsMem[$abs][3] == $nElems; + requires (forall j:int::{TO(j)} TO(j) ==> 4 <= j && j < numFields($abs) ==> $AbsMem[$abs][j] == NULL); + + modifies $GcMem, $toAbs, $gcSlice, $fs, $color; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + ensures MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + ensures old($toAbs)[eax - 4] == NO_ABS; + ensures $toAbs == old($toAbs)[eax - 4 := $abs]; + ensures Pointer($toAbs, eax - 4, $abs); + ensures ebp == old(ebp); +{ + var nElems:int; + nElems := esi; + + assert TV(eax + 0) && TV(eax + 4) && TV(eax + 8) && TV(eax + 12); + assert TO(1) && TO(2) && TO(3) && TO(4); + assert TO(numFields($abs)); + + call doAllocWords(eax, $size, numFields($abs)); + + esi := nElems; + call GcStore(eax + 4, ecx); + call GcStore(eax + 8, edx); + call GcStore(eax + 12, esi); + + $toAbs[$ptr] := $abs; + ebx := eax; + esi := eax; + call esi := Sub(esi, HeapLo); + edi := ColorBase; + edx := 1; // white + call bb4SetColor($color, 1, HeapLo, HeapLo, HeapLo, HeapHi, $ptr, ColorBase, HeapLo); + $color[$ptr] := 1; // white + $fs[$ptr] := 0; + + assert TO(numFields($abs)); + + call eax := Lea(ebx + 4); + + assert TV(eax + 4); + assert TO(0); +} + +procedure doAllocVectorHelper($abs:int, $vt:int, $nElems:int) + requires ecx == $vt; + requires edx == $nElems; + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, $nElems, 0); + requires tag($vt) == ?PTR_VECTOR_TAG || tag($vt) == ?OTHER_VECTOR_TAG; + modifies eax, ebx, edx, esi, edi; + ensures !VFieldPtr($abs, 0); + ensures !VFieldPtr($abs, 1); + ensures !VFieldPtr($abs, 2); + ensures 3 <= numFields($abs); + ensures eax == 4 * numFields($abs); + ensures ObjSize($abs, $vt, $nElems, $AbsMem[$abs][3]); +{ + assert TO(2); + assert TVL($abs); + assert TVT($abs, $vt); + + call eax := RoLoad32(ecx + ?VT_BASE_LENGTH); + call ebx := RoLoad32(ecx + ?VT_ARRAY_ELEMENT_SIZE); + call edi := RoLoad32(ecx + ?VT_MASK); + call edi := And(edi, 15); + + //if (edi == ?PTR_ARRAY_TAG) + if (edi != ?PTR_VECTOR_TAG) { goto skip1; } + edi := edx; + call edi := AddChecked(edi, edi); + call edi := AddChecked(edi, edi); + call eax := AddChecked(eax, edi); + call eax := AddChecked(eax, 3); + edi := 3; + call edi := Not(edi); + call eax := And(eax, edi); + goto skip2; + //else + skip1: + edi := eax; + eax := ebx; + call eax, edx := MulChecked(eax, edx); + call eax := AddChecked(eax, edi); + call eax := AddChecked(eax, 3); + edi := 3; + call edi := Not(edi); + call eax := And(eax, edi); + skip2: +} + +procedure doAllocVector($ra:int, $nextFp:int, $ptr:int, $abs:int, $vt:int, $nElems:int, $size:int) + requires eax == $ptr; + requires ebx == $ptr + $size; + requires ecx == $vt; + requires edx == $nElems; + requires HeapLo < $ptr && $ptr + 4 * numFields($abs) <= HeapHi && Aligned($ptr); + requires !VFieldPtr($abs, 0); + requires !VFieldPtr($abs, 1); + requires !VFieldPtr($abs, 2); + requires 3 <= numFields($abs); + requires $size == 4 * numFields($abs); + requires ObjSize($abs, $vt, $nElems, $AbsMem[$abs][3]); + requires MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + requires $fs[$ptr] >= 4 * numFields($abs); + requires CacheSize != 0 ==> $ptr != CachePtr; + requires Disconnected(HeapLo, $ptr, $fs, $fn); + + // requirements on vtable and layout: + requires word($vt) && !gcAddrEx($vt); + requires word($nElems); + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, $nElems, 0); + requires tag($vt) == ?PTR_VECTOR_TAG || tag($vt) == ?OTHER_VECTOR_TAG; + + // require a fresh, empty abstract node: + requires $abs != NO_ABS; + requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs); + requires $AbsMem[$abs][0] == NULL; + requires $AbsMem[$abs][1] == $vt; + requires $AbsMem[$abs][2] == $nElems; + requires (forall j:int::{TO(j)} TO(j) ==> 3 <= j && j < numFields($abs) ==> $AbsMem[$abs][j] == NULL); + + modifies $GcMem, $toAbs, $gcSlice, $fs, $color; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + ensures MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + ensures old($toAbs)[eax - 4] == NO_ABS; + ensures $toAbs == old($toAbs)[eax - 4 := $abs]; + ensures Pointer($toAbs, eax - 4, $abs); + ensures ebp == old(ebp); +{ + assert TV(eax + 0) && TV(eax + 4) && TV(eax + 8); + assert TO(1) && TO(2) && TO(3); + assert TO(numFields($abs)); + + call doAllocWords(eax, $size, numFields($abs)); + + call GcStore(eax + 4, ecx); + call GcStore(eax + 8, edx); + + + $toAbs[$ptr] := $abs; + ebx := eax; + esi := eax; + call esi := Sub(esi, HeapLo); + edi := ColorBase; + edx := 1; // white + call bb4SetColor($color, 1, HeapLo, HeapLo, HeapLo, HeapHi, $ptr, ColorBase, HeapLo); + $color[$ptr] := 1; // white + $fs[$ptr] := 0; + + assert TO(numFields($abs)); + + call eax := Lea(ebx + 4); + + assert TV(eax + 4); + assert TO(0); +} + +////////////////////////////////////////////////////////////////////////////// +// +// The procedures below are the entry points from the mutator. +// +// Therefore, the requires, ensures, and modifies clauses for these procedures +// are part of the trusted computing base! +// +////////////////////////////////////////////////////////////////////////////// + +procedure Initialize() + modifies $toAbs, $color; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + modifies $GcMem, HeapLo, HeapHi, ColorBase, $fn, $fs, CacheSize, ReserveMin; + ensures MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + ensures ebp == old(ebp); + ensures WellFormed($toAbs); +{ + var save:int; + var unitSize:int; + save := ebp; + + // The gc memory consists a mark stack, a color bit map, and a heap: + // <--4--><--4--><--64--> + // The bit map must consist of a multiple of 4 bytes. + // For each 2 bits in the bit map, there is one word in the heap. + + // In the worst case, the mark stack could have as many words + // as there are pointed-to objects in memory. Since an + // object requires at least 2 words (preheader, header), and + // a pointer requires 1 word, this means that the mark stack + // might need to be as big as 1/3 the heap size: 64 / 3 >= 22. + // We're more optimistic; we pick 1/16 the heap size: 64 / 16 = 4. + + // Thus, the total gc memory size must be a multiple of: + // 4 + 4 + 64 = 72 bytes + + // Compute gcMem size, in bytes and words + esi := ?gcHi; + call esi := Sub(esi, ?gcLo); + edi := esi; + + // Break into 72-byte units. Let ebp be the number of units. + edx := 0; + eax := edi; + ebx := 72; + call eax, edx := Div(eax, edx, ebx); + ebp := eax; + unitSize := ebp; + assert 72 * unitSize <= (?gcHi - ?gcLo); + + if (ebp != 0) { goto skip1; } + // not enough space for heap data structures + call DebugBreak(); + skip1: + + // Divide heap into ?gcLo <--4--> eax <--4--> ebx <--64--> ecx <--> ?gcHi + edx := 0; + call ebp := Lea(edx + 4 * ebp); + eax := ?gcLo; + call eax := Add(eax, ebp); + ColorBase := eax; +//assert ColorBase == ?gcLo + 4 * unitSize; + call ebx := Lea(eax + ebp); + HeapLo := ebx; +//assert HeapLo == ?gcLo + 8 * unitSize; + call ebp := Lea(edx + 4 * ebp); + call ecx := Lea(ebx + 4 * ebp); + HeapHi := ecx; +//assert HeapHi == ?gcLo + 72 * unitSize; + + $toAbs := MAP_NO_ABS; + + assert TV(?gcLo); + assert TV(ColorBase); + assert TV(HeapLo); + assert TV(HeapLo + 8); + assert TV(HeapHi); + assert TO(0); + assert TO(1); + assert TO(2); + assert TO(3); + assert TO(unitSize); + assert TO(2 * unitSize); + assert TO(16 * unitSize); + + $fs := MAP_ZERO; + $fn := MAP_ZERO; + $color := MAP_ZERO; + eax := ColorBase; + ebx := HeapLo; + +//assert Aligned(ColorBase); +//assert Aligned(HeapLo); +//assert HeapHi - HeapLo == 16 * (HeapLo - ColorBase); + + esp := esp - 4; call BB4Zero2($color, HeapLo, HeapLo, HeapLo, HeapHi, 0, ColorBase, HeapLo, 0, 0); + + $fn[HeapLo] := HeapLo + 8; + $fs[HeapLo] := 8; + eax := HeapLo; + call ebx := Lea(eax + 8); + call GcStore(eax, ebx); + call GcStore(eax + 4, 8); + $fs[HeapLo + 8] := HeapHi - (HeapLo + 8); + $fn[HeapLo + 8] := 0; + call GcStore(eax + 8, 0); + ecx := HeapHi; + call ecx := Sub(ecx, ebx); + call GcStore(eax + 12, ecx); + CacheSize := 0; + + eax := HeapLo; + // TUNING: wilderness size // REVIEW: this should be a parameter, not hard-wired into the code +// call eax := AddChecked(eax, 128000000); // Bartok +// call eax := AddChecked(eax, 8000000); // Zinger, Sat + ReserveMin := eax; + + ebp := save; + esp := esp + 4; return; +} + +// Prepare to call GcLoad (don't actually call it -- let the mutator call it) +procedure readField($ptr:int, $fld:int) returns ($val:int) + requires MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + requires Pointer($toAbs, $ptr, $toAbs[$ptr]); + requires 0 <= $fld && $fld < numFields($toAbs[$ptr]); + ensures gcAddr($ptr + 4 * $fld); + ensures $val == $GcMem[$ptr + 4 * $fld]; + ensures Value(VFieldPtr($toAbs[$ptr], $fld), $toAbs, $val, $AbsMem[$toAbs[$ptr]][$fld]); +{ +// call $val := GcLoad($ptr + 4 * $fld); + $val := $GcMem[$ptr + 4 * $fld]; + assert TV($val) && TV($ptr); + assert TO($fld); +} + +// Prepare to call GcStore (don't actually call it -- let the mutator call it) +procedure writeField($ptr:int, $fld:int, $val:int, $abs:int) returns ($_gcData:[int]int, $_absData:[int][int]int) + requires MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + requires Pointer($toAbs, $ptr, $toAbs[$ptr]); + requires 0 <= $fld && $fld < numFields($toAbs[$ptr]); + requires !isReadonlyField(tag($AbsMem[$toAbs[$ptr]][1]), $fld); + requires Value(VFieldPtr($toAbs[$ptr], $fld), $toAbs, $val, $abs); + ensures gcAddr($ptr + 4 * $fld); + ensures word($val); + ensures $_gcData == $GcMem[$ptr + 4 * $fld := $val]; + ensures $_absData == $AbsMem[$toAbs[$ptr] := $AbsMem[$toAbs[$ptr]][$fld := $abs]]; + ensures MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $_absData, $_gcData, $gcSlice, CachePtr, CacheSize); +{ +// call GcStore($ptr + 4 * $fld, $val); +// call AbsEdgeWrite($ptr, $fld, $val); + $_gcData := $GcMem[$ptr + 4 * $fld := $val]; + $_absData := $AbsMem[$toAbs[$ptr] := $AbsMem[$toAbs[$ptr]][$fld := $abs]]; + assert TVL($toAbs[$ptr]); + assert TV($val) && TV($ptr); + assert TO($fld); +// assert TO(1); +} + +procedure AllocObject($ra:int, $abs:int, $vt:int) + // GC invariant: + requires MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + + // requirements on mutator root layout: + requires 0 <= $FrameCount; + requires $FrameSlice[esp] == $FrameCount && $FrameMem[esp] == $ra; + requires FrameNextInv($FrameCount, $ra, ebp, $FrameAddr, $FrameLayout); + requires StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + requires StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + + // requirements on vtable and layout: + requires ecx == $vt; + requires word($vt) && !gcAddrEx($vt); + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, 0, 0); + requires !isVarSize(tag($vt)); + + // require a fresh, empty abstract node: + requires $abs != NO_ABS; + requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs); + requires $AbsMem[$abs][0] == NULL; + requires $AbsMem[$abs][1] == $vt; + requires (forall j:int::{TO(j)} TO(j) ==> 2 <= j && j < numFields($abs) ==> $AbsMem[$abs][j] == NULL); + + modifies $r1, $r2, $GcMem, $toAbs, $gcSlice, $color, StackTop, $freshAbs; + modifies $fs, $fn, CachePtr, CacheSize, ReserveMin; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + ensures StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + ensures StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + ensures MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + ensures Pointer($toAbs, eax - 4, $abs); + ensures WellFormed($toAbs); + ensures ebp == old(ebp); +{ + var vt:int; + var fp:int; + var size:int; + fp := ebp; + vt := ecx; + $freshAbs := $abs; + + assert TO(2); + assert TVL($abs); + assert TVT($abs, $vt); + call eax := RoLoad32(ecx + ?VT_BASE_LENGTH); + size := eax; + + call ecx := FrameLoad(($FrameCount), esp); + edx := ebp; + call allocObjectMemory($ra, fp, 4 * numFields($abs), numFields($abs)); + + edx := size; + call ebx := Lea(eax + edx); + ecx := vt; + call doAllocObject($ra, fp, eax, $abs, vt, 4 * numFields($abs)); + + ebp := fp; + esp := esp + 4; return; +} + +procedure AllocString($ra:int, $abs:int, $vt:int, $nElems:int) + // GC invariant: + requires MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + + // requirements on mutator root layout: + requires 0 <= $FrameCount; + requires $FrameSlice[esp] == $FrameCount && $FrameMem[esp] == $ra; + requires FrameNextInv($FrameCount, $ra, ebp, $FrameAddr, $FrameLayout); + requires StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + requires StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + + // requirements on vtable and layout: + requires ecx == $nElems - 1; + requires $vt == ?STRING_VTABLE; + requires word($vt) && !gcAddrEx($vt); + requires word($nElems); + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, $nElems, 0); + requires tag($vt) == ?STRING_TAG; + + // require a fresh, empty abstract node: + requires $abs != NO_ABS; + requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs); + requires $AbsMem[$abs][0] == NULL; + requires $AbsMem[$abs][1] == $vt; + requires $AbsMem[$abs][2] == $nElems; + requires $AbsMem[$abs][3] == $nElems - 1; + requires (forall j:int::{TO(j)} TO(j) ==> 4 <= j && 4 * j < pad(16 + 2 * $nElems) ==> $AbsMem[$abs][j] == NULL); + + modifies $r1, $r2, $GcMem, $toAbs, $gcSlice, $color, StackTop, $freshAbs; + modifies $fs, $fn, CachePtr, CacheSize, ReserveMin; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + ensures StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + ensures StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + ensures MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + ensures Pointer($toAbs, eax - 4, $abs); + ensures WellFormed($toAbs); + ensures ebp == old(ebp); +{ + var fp:int; + var size:int; + var nElems:int; + fp := ebp; + $freshAbs := $abs; + + assert TO(2); + assert TVL($abs); + assert TVT($abs, $vt); + call ecx := AddChecked(ecx, 1); + edx := ecx; + nElems := edx; + call ecx := AddChecked(ecx, ecx); + call ecx := AddChecked(ecx, 19); + eax := 3; + call eax := Not(eax); + call ecx := And(ecx, eax); + size := ecx; + eax := ecx; + + call ecx := FrameLoad(($FrameCount), esp); + edx := ebp; + call allocObjectMemory($ra, fp, 4 * numFields($abs), numFields($abs)); + + edx := nElems; + ecx := size; + call ebx := Lea(eax + ecx); + call doAllocString($ra, fp, eax, $abs, ?STRING_VTABLE, $nElems); + + ebp := fp; + esp := esp + 4; return; +} + +procedure AllocArray($ra:int, $abs:int, $vt:int, $rank:int, $nElems:int) + // GC invariant: + requires MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + + // requirements on mutator root layout: + requires 0 <= $FrameCount; + requires $FrameSlice[esp] == $FrameCount && $FrameMem[esp] == $ra; + requires FrameNextInv($FrameCount, $ra, ebp, $FrameAddr, $FrameLayout); + requires StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + requires StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + + // requirements on vtable and layout: + requires ecx == $vt; + requires edx == $rank; + requires $FrameSlice[esp + 4] == $FrameCount && $FrameMem[esp + 4] == $nElems; + requires word($vt) && !gcAddrEx($vt); + requires word($rank); + requires word($nElems); + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, $rank, $nElems); + requires tag($vt) == ?PTR_ARRAY_TAG || tag($vt) == ?OTHER_ARRAY_TAG; + + // require a fresh, empty abstract node: + requires $abs != NO_ABS; + requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs); + requires $AbsMem[$abs][0] == NULL; + requires $AbsMem[$abs][1] == $vt; + requires $AbsMem[$abs][2] == $rank; + requires $AbsMem[$abs][3] == $nElems; + requires (forall j:int::{TO(j)} TO(j) ==> 4 <= j && j < numFields($abs) ==> $AbsMem[$abs][j] == NULL); + + modifies $r1, $r2, $GcMem, $toAbs, $gcSlice, $color, StackTop, $freshAbs; + modifies $fs, $fn, CachePtr, CacheSize, ReserveMin; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + ensures StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + ensures StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + ensures MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + ensures Pointer($toAbs, eax - 4, $abs); + ensures WellFormed($toAbs); + ensures ebp == old(ebp); +{ + var vt:int; + var rank:int; + var size:int; + var nElems:int; + var fp:int; + + $freshAbs := $abs; + rank := edx; + fp := ebp; + vt := ecx; + call esi := FrameLoad(($FrameCount), esp + 4); + nElems := esi; + + call doAllocArrayHelper($abs, $vt, $rank, $nElems); + size := eax; + + call ecx := FrameLoad(($FrameCount), esp); + edx := ebp; + call allocObjectMemory($ra, fp, 4 * numFields($abs), numFields($abs)); + + edx := size; + call ebx := Lea(eax + edx); + edx := rank; + esi := nElems; + ecx := vt; + call doAllocArray($ra, fp, eax, $abs, vt, $rank, $nElems, 4 * numFields($abs)); + + ebp := fp; + esp := esp + 4; return; +} + +procedure AllocVector($ra:int, $abs:int, $vt:int, $nElems:int) + // GC invariant: + requires MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + + // requirements on mutator root layout: + requires 0 <= $FrameCount; + requires $FrameSlice[esp] == $FrameCount && $FrameMem[esp] == $ra; + requires FrameNextInv($FrameCount, $ra, ebp, $FrameAddr, $FrameLayout); + requires StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + requires StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + + // requirements on vtable and layout: + requires ecx == $vt; + requires edx == $nElems; + requires word($vt) && !gcAddrEx($vt); + requires word($nElems); + requires VTable($abs, $vt); + requires ObjSize($abs, $vt, $nElems, 0); + requires tag($vt) == ?PTR_VECTOR_TAG || tag($vt) == ?OTHER_VECTOR_TAG; + + // require a fresh, empty abstract node: + requires $abs != NO_ABS; + requires (forall i:int::{TV(i)} TV(i) ==> gcAddr(i) ==> $toAbs[i] != $abs); + requires $AbsMem[$abs][0] == NULL; + requires $AbsMem[$abs][1] == $vt; + requires $AbsMem[$abs][2] == $nElems; + requires (forall j:int::{TO(j)} TO(j) ==> 3 <= j && j < numFields($abs) ==> $AbsMem[$abs][j] == NULL); + + modifies $r1, $r2, $GcMem, $toAbs, $gcSlice, $color, StackTop, $freshAbs; + modifies $fs, $fn, CachePtr, CacheSize, ReserveMin; + modifies eax, ebx, ecx, edx, esi, edi, ebp, esp; + + ensures StaticInv($toAbs, $SectionMem, $SectionAbs, $Time); + ensures StackInv($toAbs, $FrameCount, $FrameAddr, $FrameLayout, $FrameSlice, $FrameMem, $FrameAbs, $FrameOffset, $Time); + ensures MutatorInv(ColorBase, HeapLo, HeapHi, $color, $fs, $fn, $toAbs, + $AbsMem, $GcMem, $gcSlice, CachePtr, CacheSize); + ensures Pointer($toAbs, eax - 4, $abs); + ensures WellFormed($toAbs); + ensures ebp == old(ebp); +{ + var vt:int; + var size:int; + var nElems:int; + var fp:int; + + $freshAbs := $abs; + fp := ebp; + vt := ecx; + nElems := edx; + + call doAllocVectorHelper($abs, $vt, $nElems); + size := eax; + + call ecx := FrameLoad(($FrameCount), esp); + edx := ebp; + call allocObjectMemory($ra, fp, 4 * numFields($abs), numFields($abs)); + + edx := size; + call ebx := Lea(eax + edx); + edx := nElems; + ecx := vt; + call doAllocVector($ra, fp, eax, $abs, vt, $nElems, 4 * numFields($abs)); + + ebp := fp; + esp := esp + 4; return; +} + diff --git a/base/Imported/SingSharp/Reflection/Attributes.cs b/base/Imported/SingSharp/Reflection/Attributes.cs new file mode 100644 index 0000000..186b11a --- /dev/null +++ b/base/Imported/SingSharp/Reflection/Attributes.cs @@ -0,0 +1,108 @@ +using System; + +namespace Microsoft.SingSharp.Reflection{ + public class AttributePatternAttribute : Attribute { + Object type; + public Object Type { + get { return type; } + set { type = value; } + } + + object[] expressions; + public object[] Expressions{ + get { return expressions; } + set { expressions = value; } + } + } + + public class ForAllStatementIndex : Attribute { + public ForAllStatementIndex(int index) {} + } + + public class ForAllSummaryAttribute : Attribute { + public ForAllSummaryAttribute(string summary){} + } + + public class ReflectionAttribute : Attribute { + public enum Type { + None, + Index, + IndexMap, + Generate, + Implement, + Container, + NodeMap, + Pattern, + Subtype, + Shadow, + Reflective, + ExpressionBinding, + Scope, + } + public ReflectionAttribute(Type type){} + + uint flags; + public uint Flags { + get {return flags; } + set {flags = value; } + } + + } + + #region parameter attributes + [AttributeUsage(AttributeTargets.Parameter)] + public class ParameterPatternAttribute : Attribute { + string type; + public string Type{ + get {return type; } + set {type = value; } + } + bool typeIsVariable; + public bool TypeIsVariable { + get {return typeIsVariable; } + set {typeIsVariable = value; } + } + } + + [AttributeUsage(AttributeTargets.Parameter)] + public class StarParameterPatternAttribute : ParameterPatternAttribute {} + #endregion + + [AttributeUsage(AttributeTargets.Module | AttributeTargets.Assembly, + AllowMultiple=true)] + public class TransformAttribute : Attribute { + public Type Transform; + public TransformAttribute(Type transformToRun) { + this.Transform = transformToRun; + } + + string[] namespaces; + public string[] Namespaces { + get { return namespaces; } + set { namespaces = value; } + } + string [] files; + public string[] Files { + get { return files; } + set { files = value; } + } + + Type[] types; + public Type[] Types { + get { return types; } + set { types = Types; } + } + } + +} + +#if CCINamespace +namespace Microsoft.Cci.TypeExtensions { +#else +namespace System.Compiler.TypeExtensions { +#endif + public interface IReflectionTransform {} + public interface IReflectionScopePattern { } + public interface IReflectionContainer {} + public interface IReflectionTypeVariable {} +} diff --git a/base/Imported/SingSharp/Reflection/Classes.cs b/base/Imported/SingSharp/Reflection/Classes.cs new file mode 100644 index 0000000..0b8ce8f --- /dev/null +++ b/base/Imported/SingSharp/Reflection/Classes.cs @@ -0,0 +1,15 @@ +using System; + + +namespace Microsoft.SingSharp.Reflection{ + + class IClassDummy {} + class IInterfaceDummy {} + class IStructDummy {} + interface IFieldDummy {} + interface IMethodDummy {} + interface IPropertyDummy {} + interface IAttributeDummy {} + + class ForAllException : Exception {} +} \ No newline at end of file diff --git a/base/Imported/SingSharp/System.Compiler.Runtime/AssemblyInfo.cs b/base/Imported/SingSharp/System.Compiler.Runtime/AssemblyInfo.cs new file mode 100644 index 0000000..6abe319 --- /dev/null +++ b/base/Imported/SingSharp/System.Compiler.Runtime/AssemblyInfo.cs @@ -0,0 +1,14 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("System.Compiler.Runtime")] +[assembly: AssemblyDescription("Extensions to the Common Language Runtime used by the Common Compiler Infrastructure")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyProduct("Microsoft (R) .NET Framework")] +[assembly: AssemblyCopyright("Copyright (C) Microsoft Corporation. All rights reserved")] +[assembly: AssemblyTrademark("Microsoft and Windows are either registered trademarks or trademarks of Microsoft Corporation in the U.S. and/or other countries")] +// Singularity specific version of this library +[assembly: AssemblyVersion("1.0.5031.5")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] + diff --git a/base/Imported/SingSharp/System.Compiler.Runtime/Classes.cs b/base/Imported/SingSharp/System.Compiler.Runtime/Classes.cs new file mode 100644 index 0000000..fc222c5 --- /dev/null +++ b/base/Imported/SingSharp/System.Compiler.Runtime/Classes.cs @@ -0,0 +1,6487 @@ +using System; + +#if WHIDBEYwithGenerics +#if CCINamespace +namespace Microsoft.Cci{ +#else +namespace System.Compiler{ +#endif + /// + /// Tells a sympathetic compiler that the name of the entity bearing this attribute should be suppressed. When applied to a class or enum, + /// this means that the members of the class or enum should be injected into the same scope as would have contained the class/enum name. + /// When applied to a field or property, it means that the members of structure of the type of the field/property should be visible + /// without qualification by the field/property name. + /// + [AttributeUsage(AttributeTargets.Field|AttributeTargets.Property|AttributeTargets.Class|AttributeTargets.Enum)] + public sealed class AnonymousAttribute: Attribute{ + private Anonymity type; + public AnonymousAttribute(){ + this.type = Anonymity.Structural; + } + public Anonymity Anonymity{ + get {return this.type;} + } + } + public enum Anonymity{ + Unknown, + None, + Structural, + Full + } + public enum CciMemberKind { + Unknown, + Regular, + Auxiliary, + FrameGuardGetter, + } + [AttributeUsage(AttributeTargets.All)] + public class CciMemberKindAttribute : Attribute { + public CciMemberKind Kind; + public CciMemberKindAttribute(CciMemberKind kind) { + this.Kind = kind; + } + } + [AttributeUsage(AttributeTargets.Class)] + public sealed class ComposerAttribute: Attribute{ + public string AssemblyName = null; + public string TypeName = null; + public ComposerAttribute(){ + } + } + [AttributeUsage(AttributeTargets.Assembly|AttributeTargets.Module, AllowMultiple=true)] + public sealed class CustomVisitorAttribute : Attribute{ + public string Assembly; + public string Class; + public string Phase; + public bool Replace; + public CustomVisitorAttribute(){ + } + } + [AttributeUsage(AttributeTargets.Class|AttributeTargets.Field|AttributeTargets.Property|AttributeTargets.Method)] + public sealed class ElementTypeAttribute: Attribute{ + public Type ElementType = null; + public ElementTypeAttribute(){ + } + } + public interface ITemplateParameter{ + } + [AttributeUsage(AttributeTargets.Interface|AttributeTargets.Class|AttributeTargets.Struct)] + public sealed class TemplateAttribute : System.Attribute{ + public Type[] TemplateParameters; + public TemplateAttribute(params Type[] parameters){ + this.TemplateParameters = parameters; + } + } + [AttributeUsage(AttributeTargets.Interface|AttributeTargets.Class|AttributeTargets.Delegate|AttributeTargets.Interface|AttributeTargets.Struct)] + public sealed class TemplateInstanceAttribute : System.Attribute{ + public Type Template; + public Type[] TemplateArguments; + public TemplateInstanceAttribute(Type template, params Type[] arguments){ + this.Template = template; + } + } + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct)] + public sealed class TemplateParameterFlagsAttribute : System.Attribute { + public int Flags; + public TemplateParameterFlagsAttribute(int flags) { + this.Flags = flags; + } + } + + namespace Diagnostics{ + public sealed class Debug{ + public static void Assert(bool condition){ + System.Diagnostics.Debug.Assert(condition); + } + } + } +} +#if !NoData +namespace System.Data{ + public interface IDbTransactable{ //TODO: move to Query namespace + IDbTransaction BeginTransaction(); + IDbTransaction BeginTransaction(IsolationLevel level); + } +} +#endif +namespace StructuralTypes{ +#if CCINamespace + using Microsoft.Cci; +#else + using System.Compiler; +#endif + using System.Collections; + using System.Collections.Generic; + using SCIEnumerator = System.Collections.IEnumerator; + + public interface IConstrainedType{} + public interface ITupleType{} + public interface ITypeUnion{} + public interface ITypeIntersection{} + public interface ITypeAlias{} + public interface ITypeDefinition{} + + public struct Boxed : IEnumerable, IEnumerable{ + private ElementType[] box; + public Boxed(ElementType value){ + this.box = new ElementType[]{value}; + } + public ElementType GetValue(){ + return this.box[0]; + } + public void SetValue(ElementType value){ + this.box[0] = value; + } + public object ToObject(){ + if (this.box == null) return null; + return this.box[0]; + } + public bool IsNull(){ + return this.box == null; + } + IEnumerator IEnumerable.GetEnumerator() { + return new BoxedEnumerator(this.box); + } + IEnumerator IEnumerable.GetEnumerator(){ + return new BoxedEnumerator(this.box); + } + public BoxedEnumerator GetEnumerator(){ + return new BoxedEnumerator(this.box); + } + public void Clear(){ + this.box = null; + } + public static implicit operator Boxed(ElementType value){ + return new Boxed(value); + } + public static explicit operator ElementType(Boxed boxed){ + return boxed.GetValue(); + } + public static bool operator == (Boxed a, Boxed b){ + if (a.box == null || b.box == null) return false; + return a.box[0].Equals(b.box[0]); + } + public static bool operator == (Boxed a, object o){ + return a.Equals(o); + } + public static bool operator != (Boxed a, object o){ + return !a.Equals(o); + } + public static bool operator == (object o, Boxed b){ + return b.Equals(o); + } + public static bool operator != (object o, Boxed b){ + return !b.Equals(o); + } + public static bool operator != (Boxed a, Boxed b){ + if (a.box == null || b.box == null) return false; + return !a.box[0].Equals(b.box[0]); + } + public override bool Equals(object o){ + if (this.box == null) return o == null; + if (!(o is Boxed)) return false; + Boxed b = (Boxed) o; + if (this.box == null || b.box == null) return this.box == b.box; + return this.box[0].Equals(b.box[0]); + + } + public override int GetHashCode(){ + return this.box[0].GetHashCode(); + } + } + public struct BoxedEnumerator: IEnumerator, IEnumerator{ + private ElementType[] box; + private int index; + internal BoxedEnumerator(ElementType[] box){ + this.box = box; + this.index = -1; + } + object IEnumerator.Current{ + get{ + return this.box[0]; + } + } + public ElementType Current{ + get{ + return this.box[0]; + } + } + void IEnumerator.Reset() { + this.index = -1; + } + void IDisposable.Dispose(){ + } + public bool MoveNext(){ + if (this.box == null) return false; + return ++this.index == 0; + } + } + public struct Invariant{ + private ElementType value; + public Invariant(ElementType value){ + if (value == null) throw new ArgumentNullException(); + if (value.GetType() != typeof(ElementType)) throw new ArgumentException(); + this.value = value; + } + public ElementType GetValue(){ + return this.value; + } + public object ToObject(){ + return this.value; + } + public static implicit operator ElementType(Invariant invariantValue){ + return invariantValue.value; + } + public static implicit operator NonNull(Invariant invariantValue){ + return new NonNull(invariantValue.value); + } + public static explicit operator Invariant(ElementType value){ + return new Invariant(value); + } + } + public struct NonNull: IEnumerable, IEnumerable{ + private ElementType value; + public NonNull(ElementType value){ + if (value == null) throw new ArgumentNullException(); + this.value = value; + } + public ElementType GetValue(){ + return this.value; + } + public object ToObject(){ + return this.value; + } + IEnumerator IEnumerable.GetEnumerator(){ + return new ValueEnumerator(this.value); + } + IEnumerator IEnumerable.GetEnumerator(){ + return new ValueEnumerator(this.value); + } + public ValueEnumerator GetEnumerator(){ + return new ValueEnumerator(this.value); + } + public static implicit operator ElementType(NonNull nonNullValue){ + return nonNullValue.value; + } + public static explicit operator NonNull(ElementType value){ + return new NonNull(value); + } + } + public struct NonEmptyIEnumerable: IEnumerable, IEnumerable{ + IEnumerable enumerable; + public NonEmptyIEnumerable(IEnumerable enumerable){ + this.enumerable = enumerable; + if (enumerable == null) + throw new ArgumentNullException(); + if (!(enumerable is IEnumerable)) + throw new ArgumentException(); + IEnumerator enumerator = enumerable.GetEnumerator(); + if (enumerator == null || !enumerator.MoveNext()) + throw new ArgumentException(); + } + public NonEmptyIEnumerable(ElementType element){ + this.enumerable = new NonNull(element); + } + public ElementType GetValue(){ + IEnumerator e = this.enumerable.GetEnumerator(); + if (e.MoveNext()){ + ElementType result = e.Current; + if (!e.MoveNext()) return result; + } + throw new ArgumentOutOfRangeException(); + } + public object ToObject(){ + if (this.enumerable is NonNull){ + NonNull nnull = (NonNull)this.enumerable; + return nnull.ToObject(); + } + if (this.enumerable is Boxed){ + Boxed boxed = (Boxed)this.enumerable; + return boxed.ToObject(); + } + return this.enumerable; + } + IEnumerator IEnumerable.GetEnumerator() { + return ((IEnumerable)this.enumerable).GetEnumerator(); + } + public IEnumerator GetEnumerator(){ + return this.enumerable.GetEnumerator(); + } + public static implicit operator NonEmptyIEnumerable(NonNull element){ + return new NonEmptyIEnumerable((IEnumerable)element); + } + public static explicit operator NonEmptyIEnumerable(ElementType element){ + return new NonEmptyIEnumerable(element); + } + public static explicit operator NonEmptyIEnumerable(Boxed element){ + return new NonEmptyIEnumerable((IEnumerable)element); + } + public static explicit operator NonEmptyIEnumerable(ElementType[] array){ + return new NonEmptyIEnumerable(array); + } + public static explicit operator NonEmptyIEnumerable(List list){ + return new NonEmptyIEnumerable(list); + } + public static explicit operator ElementType(NonEmptyIEnumerable nonEmptyIEnumerable){ + return nonEmptyIEnumerable.GetValue(); + } + } + public sealed class Unboxer{ + public static object ToObject(IEnumerable collection){ + if (collection is NonNull) + return ((NonNull)collection).ToObject(); + if (collection is Boxed) + return ((Boxed)collection).ToObject(); + return collection; + } + } + public struct ArrayEnumerator: IEnumerator, SCIEnumerator{ + private ElementType[] array; + private int index; + public ArrayEnumerator(ElementType[] array){ + this.array = array; + this.index = -1; + } + public ElementType Current{ + get{ + return this.array[this.index]; + } + } + object SCIEnumerator.Current{ + get{ + return this.array[this.index]; + } + } + void IDisposable.Dispose(){ + } + public bool MoveNext(){ + return ++this.index < this.array.Length; + } + void SCIEnumerator.Reset(){ + this.index = -1; + } + } + public sealed class GenericIEnumerableToGenericIListAdapter: IList, IEnumerable{ + private List list; + private IEnumerable collection; + public GenericIEnumerableToGenericIListAdapter(IEnumerable collection){ + if (collection == null) + throw new ArgumentNullException(); + if (!(collection is IEnumerable)) + throw new ArgumentException(); + this.collection = collection; + } + + private List/*!*/ List{ + get{ + List result = this.list; + if (result == null){ + result = new List(); + this.list = result; + if (this.collection != null){ + IEnumerator enumerator = this.collection.GetEnumerator(); + if (enumerator != null){ + while (enumerator.MoveNext()){ + //result.Add(enumerator.Current); + ElementType curr = enumerator.Current; + result.Add(curr); + } + } + } + } + return result; + } + } + IEnumerator IEnumerable.GetEnumerator() { + if (this.list != null) + return this.list.GetEnumerator(); + else + return ((IEnumerable)this.collection).GetEnumerator(); + } + public IEnumerator GetEnumerator(){ + if (this.list != null) + return this.list.GetEnumerator(); + else + return this.collection.GetEnumerator(); + } + public int Count{ + get{ + return this.List.Count; + } + } + public bool IsSynchronized{ + get{ + return false; + } + } + public object SyncRoot{ + get{ + return this; + } + } + void ICollection.CopyTo(ElementType[] array, int index){ + this.List.CopyTo(array, index); + } + + public bool IsFixedSize{ + get{ + return false; + } + } + public bool IsReadOnly{ + get{ + return false; + } + } + public ElementType this[int index]{ + get{ + return this.List[index]; + } + set{ + this.List[index] = value; + } + } + public void Add(ElementType value){ + this.List.Add(value); + } + public void Clear(){ + this.List.Clear(); + } + public bool Contains(ElementType value){ + return this.List.Contains(value); + } + public int IndexOf(ElementType value){ + return this.List.IndexOf(value); + } + public void Insert(int index, ElementType value){ + this.List.Insert(index, value); + } + public bool Remove(ElementType value){ + return this.List.Remove(value); + } + public void RemoveAt(int index){ + this.List.RemoveAt(index); + } + } + public struct ValueEnumerator: IEnumerator, IEnumerator{ + private ElementType value; + private int index; + + public ValueEnumerator(ElementType value){ + this.value = value; + this.index = -1; + } + + object IEnumerator.Current{ + get{ + return this.value; + } + } + public ElementType Current{ + get{ + return this.value; + } + } + void IEnumerator.Reset(){ + this.index = -1; + } + void IDisposable.Dispose(){ + } + public bool MoveNext(){ + return ++this.index == 0; + } + } + + public sealed class StreamUtility{ + private StreamUtility(){} + public static bool IsNull(IEnumerable stream){ + if (stream == null) return true; + IEnumerator enumerator = stream.GetEnumerator(); + while (enumerator.MoveNext()) + if (enumerator.Current != null) return false; + return true; + } + } +} +#if !NoData +namespace System.Query{ //TODO: rename to Microsoft.Cci.Query +#if CCINamespace + using Microsoft.Cci; +#else + using System.Compiler; +#endif + using System.Collections.Generic; + using System.Data.SqlTypes; + using System.Text; + + //TODO: this class should derive from SystemException and it should not contain a string + //The message, if any, should be obtained from a resource file + public class StreamNotSingletonException: Exception{ + public StreamNotSingletonException(): base("Stream not singleton"){ + } + } + + public interface IAggregate{ + } + public interface IAggregateGroup{ + } + + public class Min: IAggregateGroup{ + public struct MinOfByte: IAggregate{ + byte value; + bool hasValue; + public void Add(byte value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value < this.value){ + this.value = value; + } + } + public byte GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MinOfInt16: IAggregate{ + short value; + bool hasValue; + public void Add(short value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value < this.value){ + this.value = value; + } + } + public short GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MinOfInt32: IAggregate{ + int value; + bool hasValue; + public void Add(int value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value < this.value){ + this.value = value; + } + } + public int GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MinOfInt64: IAggregate{ + long value; + bool hasValue; + public void Add(long value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value < this.value) + this.value = value; + } + public long GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MinOfSingle: IAggregate{ + float value; + bool hasValue; + public void Add(float value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value < this.value){ + this.value = value; + } + } + public float GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MinOfDouble: IAggregate{ + double value; + bool hasValue; + public void Add(double value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value < this.value) + this.value = value; + } + public double GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MinOfDecimal: IAggregate{ + decimal value; + bool hasValue; + public void Add(decimal value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value < this.value) + this.value = value; + } + public decimal GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MinOfString: IAggregate{ + string value; + public void Add(string value){ + if (value != null){ + if (this.value == null || value.CompareTo(this.value) < 0) + this.value = value; + } + } + public string GetValue(){ + string result = this.value; + this.value = null; + return result; + } + } + public struct MinOfDateTime: IAggregate{ + DateTime value; + bool hasValue; + public void Add(DateTime value){ + if (!this.hasValue || value < this.value){ + this.value = value; + this.hasValue = true; + } + } + public DateTime GetValue(){ + DateTime result = this.value; + this.value = new DateTime(); + this.hasValue = false; + return result; + } + } + public struct MinOfSqlByte: IAggregate{ + byte value; + bool hasValue; + public void Add(SqlByte value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = (byte)value; + this.hasValue = true; + } + if (value < this.value){ + this.value = (byte)value; + } + } + } + public SqlByte GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlByte.Null; + } + } + public struct MinOfSqlInt16: IAggregate{ + short value; + bool hasValue; + public void Add(SqlInt16 value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = (short)value; + this.hasValue = true; + } + if (value < this.value){ + this.value = (short)value; + } + } + } + public SqlInt16 GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlInt16.Null; + } + } + public struct MinOfSqlInt32: IAggregate{ + int value; + bool hasValue; + public void Add(SqlInt32 value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = (int)value; + this.hasValue = true; + } + if (value < this.value){ + this.value = (int)value; + } + } + } + public SqlInt32 GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlInt32.Null; + } + } + public struct MinOfSqlInt64: IAggregate{ + long value; + bool hasValue; + public void Add(SqlInt64 value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = (long)value; + this.hasValue = true; + } + if (value < this.value){ + this.value = (long)value; + } + } + } + public SqlInt64 GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlInt64.Null; + } + } + public struct MinOfSqlSingle: IAggregate{ + float value; + bool hasValue; + public void Add(SqlSingle value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = value.Value; + this.hasValue = true; + } + if (value < this.value){ + this.value = value.Value; + } + } + } + public SqlSingle GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlSingle.Null; + } + } + public struct MinOfSqlDouble: IAggregate{ + double value; + bool hasValue; + public void Add(SqlDouble value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = (double)value; + this.hasValue = true; + } + if (value < this.value){ + this.value = (double)value; + } + } + } + public SqlDouble GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlDouble.Null; + } + } + public struct MinOfSqlDecimal: IAggregate{ + SqlDecimal value; + bool hasValue; + public void Add(SqlDecimal value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value < this.value){ + this.value = value; + } + } + } + public SqlDecimal GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlDecimal.Null; + } + } + public struct MinOfSqlMoney: IAggregate{ + SqlMoney value; + bool hasValue; + public void Add(SqlMoney value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value < this.value){ + this.value = value; + } + } + } + public SqlMoney GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlMoney.Null; + } + } + public struct MinOfSqlString: IAggregate{ + SqlString value; + bool hasValue; + public void Add(SqlString value){ + if (!value.IsNull){ + if (!this.hasValue || (bool)SqlString.LessThan(value, this.value)){ + this.value = value; + this.hasValue = true; + } + } + } + public SqlString GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlString.Null; + } + } + public struct MinOfSqlDateTime: IAggregate{ + SqlDateTime value; + bool hasValue; + public void Add(SqlDateTime value){ + if (!value.IsNull){ + if (!this.hasValue || (bool)SqlDateTime.LessThan(value, this.value)){ + this.value = value; + this.hasValue = true; + } + } + } + public SqlDateTime GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlDateTime.Null; + } + } + } + public class Max: IAggregateGroup{ + public struct MaxOfByte: IAggregate{ + byte value; + bool hasValue; + public void Add(byte value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value > this.value) + this.value = value; + } + public byte GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MaxOfInt16: IAggregate{ + short value; + bool hasValue; + public void Add(short value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value > this.value) + this.value = value; + } + public short GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MaxOfInt32: IAggregate{ + int value; + bool hasValue; + public void Add(int value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value > this.value) + this.value = value; + } + public int GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MaxOfInt64: IAggregate{ + long value; + bool hasValue; + public void Add(long value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value > this.value) + this.value = value; + } + public long GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MaxOfSingle: IAggregate{ + float value; + bool hasValue; + public void Add(float value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value > this.value) + this.value = value; + } + public float GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MaxOfDouble: IAggregate{ + double value; + bool hasValue; + public void Add(double value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value > this.value) + this.value = value; + } + public double GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MaxOfDecimal: IAggregate{ + decimal value; + bool hasValue; + public void Add(decimal value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value > this.value) + this.value = value; + } + public decimal GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MaxOfString: IAggregate{ + string value; + public void Add(string value){ + if (value != null){ + if (this.value == null || value.CompareTo(this.value) > 0) + this.value = value; + } + } + public string GetValue(){ + string result = this.value; + this.value = null; + return result; + } + } + public struct MaxOfDateTime: IAggregate{ + DateTime value; + bool hasValue; + public void Add(DateTime value){ + if (!this.hasValue || value > this.value){ + this.value = value; + this.hasValue = true; + } + } + public DateTime GetValue(){ + DateTime result = this.value; + this.value = new DateTime(); + this.hasValue = false; + return result; + } + } + public struct MaxOfSqlByte: IAggregate{ + byte value; + bool hasValue; + public void Add(SqlByte value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = (byte)value; + this.hasValue = true; + } + if (value > this.value) + this.value = (byte)value; + } + } + public SqlByte GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlByte.Null; + } + } + public struct MaxOfSqlInt16: IAggregate{ + short value; + bool hasValue; + public void Add(SqlInt16 value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = (short)value; + this.hasValue = true; + } + if (value > this.value) + this.value = (short)value; + } + } + public SqlInt16 GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlInt16.Null; + } + } + public struct MaxOfSqlInt32: IAggregate{ + int value; + bool hasValue; + public void Add(SqlInt32 value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = (int)value; + this.hasValue = true; + } + if (value > this.value) + this.value = (int)value; + } + } + public SqlInt32 GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlInt32.Null; + } + } + public struct MaxOfSqlInt64: IAggregate{ + long value; + bool hasValue; + public void Add(SqlInt64 value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = (long)value; + this.hasValue = true; + } + if (value > this.value) + this.value = (long)value; + } + } + public SqlInt64 GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlInt64.Null; + } + } + public struct MaxOfSqlSingle: IAggregate{ + float value; + bool hasValue; + public void Add(SqlSingle value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = value.Value; + this.hasValue = true; + } + if (value > this.value) + this.value = value.Value; + } + } + public SqlSingle GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlSingle.Null; + } + } + public struct MaxOfSqlDouble: IAggregate{ + double value; + bool hasValue; + public void Add(SqlDouble value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = (double)value; + this.hasValue = true; + } + if (value > this.value) + this.value = (double)value; + } + } + public SqlDouble GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlDouble.Null; + } + } + public struct MaxOfSqlDecimal: IAggregate{ + SqlDecimal value; + bool hasValue; + public void Add(SqlDecimal value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value > this.value) + this.value = value; + } + } + public SqlDecimal GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlDecimal.Null; + } + } + public struct MaxOfSqlMoney: IAggregate{ + SqlMoney value; + bool hasValue; + public void Add(SqlMoney value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value > this.value) + this.value = value; + } + } + public SqlMoney GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlMoney.Null; + } + } + public struct MaxOfSqlString: IAggregate{ + SqlString value; + bool hasValue; + public void Add(SqlString value){ + if (!value.IsNull){ + if (!this.hasValue || (bool)SqlString.GreaterThan(value, this.value)){ + this.value = value; + this.hasValue = true; + } + } + } + public SqlString GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlString.Null; + } + } + public struct MaxOfSqlDateTime: IAggregate{ + SqlDateTime value; + bool hasValue; + public void Add(SqlDateTime value){ + if (!value.IsNull){ + if (!this.hasValue || (bool)SqlDateTime.GreaterThan(value, this.value)){ + this.value = value; + this.hasValue = true; + } + } + } + public SqlDateTime GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlDateTime.Null; + } + } + } + public class Sum: IAggregateGroup{ + public struct SumOfInt32: IAggregate{ + int total; + public void Add(int value){ + this.total = this.total + value; + } + public int GetValue(){ + int ret = this.total; + this.total = 0; + return ret; + } + } + public struct SumOfInt64: IAggregate{ + long total; + public void Add(long value){ + this.total = this.total + value; + } + public long GetValue(){ + long ret = this.total; + this.total = 0; + return ret; + } + } + public struct SumOfDouble: IAggregate{ + double total; + public void Add(double value){ + this.total = this.total + value; + } + public double GetValue(){ + double ret = this.total; + this.total = 0.0; + return ret; + } + } + public struct SumOfDecimal: IAggregate{ + decimal total; + public void Add(decimal value){ + this.total = this.total + value; + } + public decimal GetValue(){ + decimal ret = this.total; + this.total = 0; + return ret; + } + } + public struct SumOfSqlInt32: IAggregate{ + int total; + bool hasValue; + public void Add(SqlInt32 value){ + if (!value.IsNull){ + if (this.hasValue) + this.total = this.total + (int) value; + else{ + this.total = (int) value; + this.hasValue = true; + } + } + } + public SqlInt32 GetValue(){ + if (!this.hasValue){ + return SqlInt32.Null; + } + this.hasValue = false; + return (SqlInt32) this.total; + } + } + public struct SumOfSqlInt64: IAggregate{ + long total; + bool hasValue; + public void Add(SqlInt64 value){ + if (!value.IsNull){ + if (this.hasValue) + this.total = this.total + (long) value; + else{ + this.total = (long) value; + this.hasValue = true; + } + } + } + public SqlInt64 GetValue(){ + if (!this.hasValue) return SqlInt64.Null; + this.hasValue = false; + return (SqlInt64) this.total; + } + } + public struct SumOfSqlDouble: IAggregate{ + SqlDouble total; + bool hasValue; + public void Add(SqlDouble value){ + if (!value.IsNull){ + if (this.hasValue){ + this.total = this.total + value; + }else{ + this.total = value; + this.hasValue = true; + } + } + } + public SqlDouble GetValue(){ + if (!this.hasValue) return SqlDouble.Null; + this.hasValue = false; + return this.total; + } + } + public struct SumOfSqlDecimal: IAggregate{ + SqlDecimal total; + bool hasValue; + public void Add(SqlDecimal value){ + if (!value.IsNull){ + if (this.hasValue) + this.total = this.total + value; + else{ + this.total = value; + this.hasValue = true; + } + } + } + public SqlDecimal GetValue(){ + if (!this.hasValue) return SqlDecimal.Null; + this.hasValue = false; + return this.total; + } + } + public struct SumOfSqlMoney: IAggregate{ + SqlMoney total; + bool hasValue; + public void Add(SqlMoney value){ + if (!value.IsNull){ + if (this.hasValue) + this.total = this.total + value; + else{ + this.total = value; + this.hasValue = true; + } + } + } + public SqlMoney GetValue(){ + if (!this.hasValue) return SqlMoney.Null; + this.hasValue = false; + return this.total; + } + } + } + public class Avg: IAggregateGroup{ + public struct AvgOfInt32: IAggregate{ + long total; + long count; + public void Add(int value){ + this.total += value; + this.count++; + } + public int GetValue(){ + int result = (int)(this.total / this.count); + this.total = 0; + this.count = 0; + return result; + } + } + public struct AvgOfInt64: IAggregate{ + decimal total; + long count; + public void Add(long value){ + this.total += value; + this.count++; + } + public long GetValue(){ + long result = (long)(this.total / this.count); + this.total = 0; + this.count = 0; + return result; + } + } + public struct AvgOfDouble: IAggregate{ + double total; + long count; + public void Add(double value){ + this.total += value; + this.count++; + } + public double GetValue(){ + double result = this.total / this.count; + this.total = 0; + this.count = 0; + return result; + } + } + public struct AvgOfDecimal: IAggregate{ + decimal total; + long count; + public void Add(decimal value){ + this.total += value; + this.count++; + } + public decimal GetValue(){ + decimal result = this.total / this.count; + this.total = 0; + this.count = 0; + return result; + } + } + public struct AvgOfSqlInt32: IAggregate{ + long total; + long count; + public void Add(SqlInt32 value){ + if (!value.IsNull){ + if (this.count == 0) + this.total = (int)value; + else + this.total += (int)value; + this.count++; + } + } + public SqlInt32 GetValue(){ + if (this.count == 0) return SqlInt32.Null; + int result = (int)(this.total / this.count); + this.count = 0; + return result; + } + } + public struct AvgOfSqlInt64: IAggregate{ + decimal total; + long count; + public void Add(SqlInt64 value){ + if (!value.IsNull){ + if (this.count == 0) + this.total = (long)value; + else + this.total += (long)value; + this.count++; + } + } + public SqlInt64 GetValue(){ + if (this.count == 0) return SqlInt64.Null; + long result = (long)(this.total / this.count); + this.count = 0; + return result; + } + } + public struct AvgOfSqlDouble: IAggregate{ + SqlDouble total; + long count; + public void Add(SqlDouble value){ + if (!value.IsNull){ + if (this.count == 0) + this.total = value; + else + this.total += value; + this.count++; + } + } + public SqlDouble GetValue(){ + if (this.count == 0) return SqlDouble.Null; + SqlDouble result = this.total / count; + this.count = 0; + return result; + } + } + public struct AvgOfSqlDecimal: IAggregate{ + SqlDecimal total; + long count; + public void Add(SqlDecimal value){ + if (!value.IsNull){ + if (this.count == 0) + this.total = value; + else + this.total += value; + this.count++; + } + } + public SqlDecimal GetValue(){ + if (this.count == 0) return SqlDecimal.Null; + SqlDecimal result = this.total / count; + this.count = 0; + return result; + } + } + public struct AvgOfSqlMoney: IAggregate{ + SqlMoney total; + long count; + public void Add(SqlMoney value){ + if (!value.IsNull){ + if (this.count == 0) + this.total = value; + else + this.total += value; + this.count++; + } + } + public SqlMoney GetValue(){ + if (this.count == 0) return SqlMoney.Null; + SqlMoney result = this.total / count; + this.count = 0; + return result; + } + } + } + public class Stdev: IAggregateGroup{ + public struct StdevOfDouble: IAggregate{ + double sumX; + double sumX2; + int count; + public void Add(double value){ + this.sumX += value; + this.sumX2 += (value * value); + this.count++; + } + public double GetValue(){ + int c = count - 1; + double result = Math.Sqrt((sumX2/c) - ((sumX * sumX)/count/c)); + this.sumX = 0; + this.sumX2 = 0; + this.count = 0; + return result; + } + } + public struct StdevOfDecimal: IAggregate{ + decimal sumX; + decimal sumX2; + int count; + public void Add(decimal value){ + this.sumX += value; + this.sumX2 += (value * value); + this.count++; + } + public double GetValue(){ + int c = count - 1; + // note: using Math.Sqrt(double) would lose precision, so use SqlDecimal.Power + SqlDecimal result = SqlDecimal.Power((sumX2/c) - ((sumX * sumX)/count/c), 0.5); + this.sumX = 0; + this.sumX2 = 0; + this.count = 0; + return (double)(SqlDouble)result; + } + } + public struct StdevOfSqlDouble: IAggregate{ + double sumX; + double sumX2; + int count; + public void Add(SqlDouble value){ + if (!value.IsNull){ + double dv = (double) value; + this.sumX += dv; + this.sumX2 += dv * dv; + this.count++; + } + } + public SqlDouble GetValue(){ + if (this.count < 2) return SqlDouble.Null; + int c = count - 1; + double result = Math.Sqrt((sumX2/c) - ((sumX * sumX)/count/c)); + this.sumX = 0; + this.sumX2 = 0; + this.count = 0; + return (SqlDouble) result; + } + } + public struct StdevOfSqlDecimal: IAggregate{ + SqlDecimal sumX; + SqlDecimal sumX2; + int count; + public void Add(SqlDecimal value){ + if (!value.IsNull){ + if (this.count == 0){ + this.sumX = value; + this.sumX2 = value * value; + }else{ + this.sumX += value; + this.sumX2 += value * value; + } + this.count++; + } + } + public SqlDouble GetValue(){ + if (this.count < 2) return SqlDecimal.Null; + int c = count - 1; + SqlDecimal result = SqlDecimal.Power((sumX2/c) - ((sumX * sumX)/count/c), 0.5); + this.sumX = 0; + this.sumX2 = 0; + this.count = 0; + return (SqlDouble)result; + } + } + public struct StdevOfSqlMoney: IAggregate{ + SqlMoney sumX; + SqlMoney sumX2; + int count; + public void Add(SqlMoney value){ + if (!value.IsNull){ + if (this.count == 0){ + this.sumX = value; + this.sumX2 = value * value; + }else{ + this.sumX += value; + this.sumX2 += value * value; + } + this.count++; + } + } + public SqlDouble GetValue(){ + if (this.count < 2) return SqlMoney.Null; + int c = count - 1; + SqlDecimal result = SqlDecimal.Power((sumX2/c) - ((sumX * sumX)/count/c), 0.5); + this.sumX = 0; + this.sumX2 = 0; + this.count = 0; + return (SqlDouble)result; + } + } + } + public class Count: IAggregateGroup{ + public struct CountOfObject: IAggregate{ + int count; + public void Add(object value){ + count++; + } + public int GetValue(){ + int result = count; + this.count = 0; + return result; + } + } + } + + [Anonymous] + public sealed class SqlFunctions{ + public static SqlByte Abs(SqlByte value){ + return value; + } + public static SqlInt16 Abs(SqlInt16 value){ + if (value.IsNull) return SqlInt16.Null; + return Math.Abs((short)value); + } + public static SqlInt32 Abs(SqlInt32 value){ + if (value.IsNull) return SqlInt32.Null; + return Math.Abs((int)value); + } + public static SqlInt64 Abs(SqlInt64 value){ + if (value.IsNull) return SqlInt64.Null; + return Math.Abs((long)value); + } + public static SqlDouble Abs(SqlDouble value){ + if (value.IsNull) return SqlDouble.Null; + return Math.Abs((double)value); + } + public static SqlDecimal Abs(SqlDecimal value){ + if (value.IsNull) return SqlDecimal.Null; + return (value < 0) ? -value : value; + } + public static SqlMoney Abs(SqlMoney value){ + if (value.IsNull) return SqlMoney.Null; + return (value < 0) ? -value : value; + } + public static SqlDouble Acos(SqlDouble value){ + if (value.IsNull) return SqlDouble.Null; + return Math.Acos((double)value); + } + public static SqlDouble Asin(SqlDouble value){ + if (value.IsNull) return SqlDouble.Null; + return Math.Asin((double)value); + } + public static SqlDouble Atan(SqlDouble value){ + if (value.IsNull) return SqlDouble.Null; + return Math.Atan((double)value); + } + public static SqlDouble Atn2(SqlDouble value1, SqlDouble value2){ + if (value1.IsNull || value2.IsNull) return SqlDouble.Null; + return Math.Atan2((double)value1, (double)value2); + } + public static SqlByte Ceiling(SqlByte value){ + return value; + } + public static SqlInt16 Ceiling(SqlInt16 value){ + return value; + } + public static SqlInt32 Ceiling(SqlInt32 value){ + return value; + } + public static SqlInt64 Ceiling(SqlInt64 value){ + return value; + } + public static SqlDouble Ceiling(SqlDouble value){ + if (value.IsNull) return SqlDouble.Null; + return Math.Ceiling((double)value); + } + public static SqlDecimal Ceiling(SqlDecimal value){ + return SqlDecimal.Ceiling(value); + } + public static SqlMoney Ceiling(SqlMoney value){ + return (SqlMoney)SqlDecimal.Ceiling(value); + } + public static SqlInt32 CharIndex(SqlString pattern, SqlString source){ + if (pattern.IsNull || source.IsNull) return SqlInt32.Null; + return ((string/*!*/)source.Value).IndexOf((string/*!*/)pattern.Value) + 1; + } + public static SqlDouble Cos(SqlDouble value){ + if (value.IsNull) return SqlDouble.Null; + return Math.Cos((double)value); + } + public static SqlDateTime DateAdd(SqlDatePart part, SqlInt32 value, SqlDateTime date){ + if (value.IsNull || date.IsNull) return SqlDateTime.Null; + int incr = (int)value; + DateTime dt = (DateTime)date; + switch (part){ + case SqlDatePart.Year: + return dt.AddYears(incr); + case SqlDatePart.Month: + return dt.AddMonths(incr); + case SqlDatePart.Day: + return dt.AddDays(incr); + case SqlDatePart.Hour: + return dt.AddHours(incr); + case SqlDatePart.Minute: + return dt.AddMinutes(incr); + case SqlDatePart.Second: + return dt.AddSeconds(incr); + case SqlDatePart.Millisecond: + return dt.AddMilliseconds(incr); + } + return dt; + } + public static SqlInt32 DatePart(SqlDatePart part, SqlDateTime date){ + if (date.IsNull) return SqlInt32.Null; + DateTime dt = (DateTime)date; + switch (part){ + case SqlDatePart.Year: + return dt.Year; + case SqlDatePart.Month: + return dt.Month; + case SqlDatePart.Week: + return (dt.DayOfYear + 6)/ 7; + case SqlDatePart.WeekDay: + return (int)dt.DayOfWeek; + case SqlDatePart.Day: + return dt.Day; + case SqlDatePart.DayOfYear: + return dt.DayOfYear; + case SqlDatePart.Hour: + return dt.Hour; + case SqlDatePart.Minute: + return dt.Minute; + case SqlDatePart.Second: + return dt.Second; + case SqlDatePart.Millisecond: + return dt.Millisecond; + } + return 0; + } + public static SqlDouble Degrees(SqlDouble radians){ + if (radians.IsNull) return SqlDouble.Null; + return ((double)radians) * Math.PI / 2; + } + public static SqlDouble Exp(SqlDouble exponent){ + return Math.Exp((double)exponent); + } + public static SqlByte Floor(SqlByte value){ + return value; + } + public static SqlInt16 Floor(SqlInt16 value){ + return value; + } + public static SqlInt32 Floor(SqlInt32 value){ + return value; + } + public static SqlInt64 Floor(SqlInt64 value){ + return value; + } + public static SqlDouble Floor(SqlDouble value){ + if (value.IsNull) return SqlDouble.Null; + return Math.Floor((double)value); + } + public static SqlDecimal Floor(SqlDecimal value){ + return SqlDecimal.Floor(value); + } + public static SqlMoney Floor(SqlMoney value){ + return (SqlMoney)SqlDecimal.Floor(value); + } + public static SqlDateTime GetDate(){ + return DateTime.Now; + } + public static SqlDateTime GetUtcDate(){ + return DateTime.UtcNow; + } + public static SqlBoolean IsDate(SqlString value){ + if (value.IsNull) return SqlBoolean.Null; + try{ DateTime.Parse((string)value); } + catch{ return false; } + return true; + } + public static SqlString Left(SqlString value, SqlInt32 length){ + if (value.IsNull || length.IsNull) return SqlString.Null; + int len = (int)length; + string str = (string/*!*/)value.Value; + return str.Substring(0, len); + } + public static SqlInt32 Len(SqlString value){ + if (value.IsNull) return SqlInt32.Null; + return ((string/*!*/)value.Value).Length; + } + public static SqlDouble Log(SqlDouble value){ + if (value.IsNull) return SqlDouble.Null; + return Math.Log((double)value); + } + public static SqlDouble Log10(SqlDouble value){ + if (value.IsNull) return SqlDouble.Null; + return Math.Log10((double)value); + } + public static SqlDouble Power(SqlDouble value, SqlDouble exponent){ + if (value.IsNull || exponent.IsNull) return SqlDouble.Null; + return Math.Pow((double)value, (double)exponent); + } + public static SqlString Replace(SqlString source, SqlString oldValue, SqlString newValue){ + if (source.IsNull || oldValue.IsNull || newValue.IsNull) return SqlString.Null; + return ((string/*!*/)source.Value).Replace((string/*!*/)oldValue.Value, (string/*!*/)newValue.Value); + } + public static SqlString Reverse(SqlString value){ + if (value.IsNull) return SqlString.Null; + string str = (string/*!*/)value.Value; + StringBuilder sb = new StringBuilder(str.Length); + for(int i = str.Length - 1; i >= 0; i--) + sb.Append(str[i]); + return sb.ToString(); + } + public static SqlString Right(SqlString value, SqlInt32 length){ + if (value.IsNull || length.IsNull) return SqlString.Null; + string str = (string/*!*/)value.Value; + int len = Math.Min((int)length, str.Length); + return str.Substring(str.Length - len - 1, len); + } + public static SqlDouble Round(SqlDouble value, SqlInt32 precision){ + if (value.IsNull || precision.IsNull) return SqlDouble.Null; + return Math.Round((double)value, (int)precision); + } + public static SqlDecimal Round(SqlDecimal value, SqlInt32 precision){ + if (value.IsNull || precision.IsNull) return SqlDecimal.Null; + return SqlDecimal.Round(value, (int)precision); + } + public static SqlMoney Round(SqlMoney value, SqlInt32 precision){ + if (value.IsNull || precision.IsNull) return SqlMoney.Null; + return (SqlMoney) SqlDecimal.Round(value, (int)precision); + } + public static SqlDouble Sin(SqlDouble value){ + if (value.IsNull) return SqlDouble.Null; + return Math.Sin((double)value); + } + public static SqlString Stuff(SqlString source, SqlInt32 position, SqlInt32 length, SqlString value){ + if (source.IsNull || position.IsNull || length.IsNull) return SqlString.Null; + int offset = ((int)position) - 1; + string result = ((string/*!*/)source.Value).Remove(offset, (int)length); + //^ assume result != null; + if (!value.IsNull) result = result.Insert(offset, (string/*!*/)value.Value); + //^ assume result != null; + return result; + } + public static SqlString Substring(SqlString source, SqlInt32 position, SqlInt32 length){ + if (source.IsNull || position.IsNull || length.IsNull) return SqlString.Null; + int offset = ((int)position) - 1; + return ((string/*!*/)source.Value).Substring(offset, (int)length); + } + } + + [Anonymous] + public enum SqlDatePart{ + Year, + Quarter, + Month, + Day, + DayOfYear, + Week, + WeekDay, + Hour, + Minute, + Second, + Millisecond + } + + [Anonymous] + public enum SqlHint{ + HoldLock, + Serializable, + RepeatableRead, + ReadCommitted, + ReadUncommitted, + NoLock, + RowLock, + PageLock, + TableLock, + TableLockExclusive, + ReadPast, + UpdateLock, + ExclusiveLock, + } +} +#endif +#else +#if CCINamespace +namespace Microsoft.Cci{ +#else +namespace System.Compiler{ +#endif + /// + /// Tells a sympathetic compiler that the name of the entity bearing this attribute should be suppressed. When applied to a class or enum, + /// this means that the members of the class or enum should be injected into the same scope as would have contained the class/enum name. + /// When applied to a field or property, it means that the members of structure of the type of the field/property should be visible + /// without qualification by the field/property name. + /// + [AttributeUsage(AttributeTargets.Field|AttributeTargets.Property|AttributeTargets.Class|AttributeTargets.Enum)] + public sealed class AnonymousAttribute: Attribute{ + private Anonymity type; + public AnonymousAttribute(){ + this.type = Anonymity.Structural; + } + public Anonymity Anonymity{ + get {return this.type;} + } + } + public enum Anonymity{ + Unknown, + None, + Structural, + Full + } + public enum CciMemberKind{ + Unknown, + Regular, + Auxiliary, + FrameGuardGetter, + } + [AttributeUsage(AttributeTargets.All)] + public class CciMemberKindAttribute: Attribute{ + public CciMemberKind Kind; + public CciMemberKindAttribute(CciMemberKind kind){ + this.Kind = kind; + } + } + [AttributeUsage(AttributeTargets.Class)] + public sealed class ComposerAttribute: Attribute{ + public string AssemblyName = null; + public string TypeName = null; + public ComposerAttribute(){ + } + } + [AttributeUsage(AttributeTargets.Assembly|AttributeTargets.Module, AllowMultiple=true)] + public sealed class CustomVisitorAttribute : Attribute{ + public string Assembly; + public string Class; + public string Phase; + public bool Replace; + public CustomVisitorAttribute(){ + } + } + [AttributeUsage(AttributeTargets.Class|AttributeTargets.Field|AttributeTargets.Property|AttributeTargets.Method)] + public sealed class ElementTypeAttribute: Attribute{ + public Type ElementType = null; + public ElementTypeAttribute(){ + } + } + public interface ITemplateParameter{ + } + [AttributeUsage(AttributeTargets.Class|AttributeTargets.Delegate|AttributeTargets.Interface|AttributeTargets.Struct)] + public sealed class TemplateAttribute : System.Attribute{ + public Type[] TemplateParameters; + public TemplateAttribute(params Type[] parameters){ + this.TemplateParameters = parameters; + } + } + [AttributeUsage(AttributeTargets.Class|AttributeTargets.Delegate|AttributeTargets.Interface|AttributeTargets.Struct)] + public sealed class TemplateInstanceAttribute : System.Attribute{ + public Type Template; + public Type[] TemplateArguments; + public TemplateInstanceAttribute(Type template, params Type[] arguments){ + this.Template = template; + } + } + + [AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface|AttributeTargets.Struct)] + public sealed class TemplateParameterFlagsAttribute : System.Attribute{ + public int Flags; + public TemplateParameterFlagsAttribute(int flags) { + this.Flags = flags; + } + } + namespace Diagnostics{ + public sealed class Debug{ + public static void Assert(bool condition){ + System.Diagnostics.Debug.Assert(condition); + } + } + } +} +#if !NoData && !ROTOR +namespace System.Data{ + public interface IDbTransactable{ + IDbTransaction BeginTransaction(); + IDbTransaction BeginTransaction(IsolationLevel level); + } +} +#endif +namespace StructuralTypes{ +#if CCINamespace + using Microsoft.Cci; +#else + using System.Compiler; +#endif + using System.Collections.Generic; + + public interface IConstrainedType{} + public interface ITupleType{} + public interface ITypeUnion{} + public interface ITypeIntersection{} + public interface ITypeAlias{} + public interface ITypeDefinition{} + + [Template(typeof(ElementType))] + public struct Boxed_1 : IEnumerable_1{ + private ElementType[] box; + public Boxed_1(ElementType value){ + this.box = new ElementType[]{value}; + } + public ElementType GetValue(){ + return this.box[0]; + } + public void SetValue(ElementType value){ + this.box[0] = value; + } + public object ToObject(){ + if (this.box == null) return null; + return this.box[0]; + } + public bool IsNull(){ + return this.box == null; + } + IEnumerator_1 IEnumerable_1.GetEnumerator(){ + return new BoxedEnumerator_1(this.box); + } + public BoxedEnumerator_1 GetEnumerator(){ + return new BoxedEnumerator_1(this.box); + } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator(){ + return new BoxedEnumerator_1(this.box); + } + public void Clear(){ + this.box = null; + } + public static implicit operator Boxed_1(ElementType value){ + return new Boxed_1(value); + } + public static explicit operator ElementType(Boxed_1 boxed){ + return boxed.GetValue(); + } + public static bool operator ==(Boxed_1 a, Boxed_1 b){ + if (a.box == null || b.box == null) return false; + return a.box[0].Equals(b.box[0]); + } + public static bool operator ==(Boxed_1 a, object o){ + return a.Equals(o); + } + public static bool operator !=(Boxed_1 a, object o){ + return !a.Equals(o); + } + public static bool operator ==(object o, Boxed_1 b){ + return b.Equals(o); + } + public static bool operator !=(object o, Boxed_1 b){ + return !b.Equals(o); + } + public static bool operator !=(Boxed_1 a, Boxed_1 b){ + if (a.box == null || b.box == null) return false; + return !a.box[0].Equals(b.box[0]); + } + public override bool Equals(object o){ + if (this.box == null) return o == null; + if (!(o is Boxed_1)) return false; + Boxed_1 b = (Boxed_1)o; + if (this.box == null || b.box == null) return this.box == b.box; + return this.box[0].Equals(b.box[0]); + } + public override int GetHashCode(){ + return this.box[0].GetHashCode(); + } + } + [Template(typeof(ElementType))] + public struct BoxedEnumerator_1: IEnumerator_1{ + private ElementType[] box; + private int index; + internal BoxedEnumerator_1(ElementType[] box){ + this.box = box; + this.index = -1; + } + public ElementType Current{ + get { + return this.box[0]; + } + } + object System.Collections.IEnumerator.Current{ + get{ + return this.box[0]; + } + } + void System.Collections.IEnumerator.Reset(){ + this.index = -1; + } + void IDisposable.Dispose(){ + } + public bool MoveNext(){ + if (this.box == null) return false; + return ++this.index == 0; + } + } + [Template(typeof(ElementType))] + public struct Invariant_1{ + private ElementType value; + public Invariant_1(ElementType value){ + if (value == null) throw new ArgumentNullException(); + if (value.GetType() != typeof(ElementType)) throw new ArgumentException(); + this.value = value; + } + public ElementType GetValue(){ + return this.value; + } + public object ToObject(){ + return this.value; + } + public static implicit operator ElementType(Invariant_1 invariantValue){ + return invariantValue.value; + } + public static implicit operator NonNull_1(Invariant_1 invariantValue){ + return new NonNull_1(invariantValue.value); + } + public static explicit operator Invariant_1(ElementType value){ + return new Invariant_1(value); + } + } + [Template(typeof(ElementType))] + public struct NonNull_1 : IEnumerable_1{ + private ElementType value; + public NonNull_1(ElementType value){ + if (value == null) throw new ArgumentNullException(); + this.value = value; + } + public ElementType GetValue(){ + return this.value; + } + public object ToObject(){ + return this.value; + } + IEnumerator_1 IEnumerable_1.GetEnumerator(){ + return new ValueEnumerator_1(this.value); + } + public ValueEnumerator_1 GetEnumerator(){ + return new ValueEnumerator_1(this.value); + } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator(){ + return new ValueEnumerator_1(this.value); + } + public static implicit operator ElementType(NonNull_1 nonNullValue){ + return nonNullValue.value; + } + public static explicit operator NonNull_1(ElementType value){ + return new NonNull_1(value); + } + } + [Template(typeof(ElementType))] + public struct NonEmptyIEnumerable_1 : IEnumerable_1{ + IEnumerable_1 enumerable; + public NonEmptyIEnumerable_1(IEnumerable_1 enumerable){ + this.enumerable = enumerable; + if (enumerable == null) + throw new ArgumentNullException(); + IEnumerator_1 enumerator = enumerable.GetEnumerator(); + if (enumerator == null || !enumerator.MoveNext()) + throw new ArgumentException(); + } + public NonEmptyIEnumerable_1(ElementType element){ + this.enumerable = new NonNull_1(element); + } + public ElementType GetValue(){ + IEnumerator_1 e = this.enumerable.GetEnumerator(); + if (e.MoveNext()){ + ElementType result = e.Current; + if (!e.MoveNext()) return result; + } + throw new ArgumentOutOfRangeException(); + } + public object ToObject(){ + if (this.enumerable is NonNull_1){ + NonNull_1 nnull = (NonNull_1)this.enumerable; + return nnull.ToObject(); + } + if (this.enumerable is Boxed_1){ + Boxed_1 boxed = (Boxed_1)this.enumerable; + return boxed.ToObject(); + } + return this.enumerable; + } + public IEnumerator_1 GetEnumerator(){ + return this.enumerable.GetEnumerator(); + } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator(){ + return this.enumerable.GetEnumerator(); + } + public static implicit operator NonEmptyIEnumerable_1(NonNull_1 element){ + return new NonEmptyIEnumerable_1((IEnumerable_1)element); + } + public static explicit operator NonEmptyIEnumerable_1(ElementType element){ + return new NonEmptyIEnumerable_1(element); + } + public static explicit operator NonEmptyIEnumerable_1(Boxed_1 element){ + return new NonEmptyIEnumerable_1((IEnumerable_1)element); + } + public static explicit operator NonEmptyIEnumerable_1(ElementType[] array){ + return new NonEmptyIEnumerable_1(new ArrayToIEnumerableAdapter_1(array)); + } + public static explicit operator NonEmptyIEnumerable_1(List_1 list){ + return new NonEmptyIEnumerable_1(list); + } + public static explicit operator ElementType(NonEmptyIEnumerable_1 nonEmptyIEnumerable){ + return nonEmptyIEnumerable.GetValue(); + } + } + [Template(typeof(ElementType))] + public sealed class Unboxer_1{ + public static object ToObject(IEnumerable_1 collection){ + if (collection is NonNull_1) + return ((NonNull_1)collection).ToObject(); + if (collection is Boxed_1) + return ((Boxed_1)collection).ToObject(); + return collection; + } + } + + [Template(typeof(ElementType))] + public sealed class ArrayToIEnumerableAdapter_1 : IEnumerable_1{ + private ElementType[] array; + public ArrayToIEnumerableAdapter_1(ElementType[] array){ + this.array = array; + } + public IEnumerator_1 GetEnumerator(){ + return new ArrayEnumerator_1(this.array); + } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator(){ + return new ArrayEnumerator_1(this.array); + } + } + [Template(typeof(ElementType))] + public sealed class GenericIEnumerableToGenericIListAdapter_1 : IList_1{ + private List_1 list; + private IEnumerable_1 collection; + public GenericIEnumerableToGenericIListAdapter_1(IEnumerable_1 collection){ + this.collection = collection; + } + + private List_1 List{ + get{ + List_1 result = this.list; + if (result == null){ + result = new List_1(); + this.list = result; + if (this.collection != null){ + IEnumerator_1 enumerator = this.collection.GetEnumerator(); + if (enumerator != null){ + while (enumerator.MoveNext()){ + ElementType curr = enumerator.Current; + result.Add(curr); + } + } + } + } + return result; + } + } + public IEnumerator_1 GetEnumerator(){ + if (this.list != null) + return this.list.GetEnumerator(); + else + return this.collection.GetEnumerator(); + } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator(){ + return this.GetEnumerator(); + } + public int Count{ + get{ + return this.List.Count; + } + } + public bool IsSynchronized{ + get{ + return false; + } + } + public object SyncRoot{ + get{ + return this; + } + } + public void CopyTo(Array array, int index){ + this.List.CopyTo(array, index); + } + public void CopyTo(ElementType[] array, int index){ + this.List.CopyTo(array, index); + } + + public bool IsFixedSize{ + get{ + return false; + } + } + public bool IsReadOnly{ + get{ + return false; + } + } + public ElementType this[int index]{ + get{ + return this.List[index]; + } + set{ + this.List[index] = value; + } + } + public void Add(ElementType value){ + this.List.Add(value); + } + public void Clear(){ + this.List.Clear(); + } + public bool Contains(ElementType value){ + return this.List.Contains(value); + } + public int IndexOf(ElementType value){ + return this.List.IndexOf(value); + } + public void Insert(int index, ElementType value){ + this.List.Insert(index, value); + } + public bool Remove(ElementType value){ + return this.List.Remove(value); + } + public void RemoveAt(int index){ + this.List.RemoveAt(index); + } + } + [Template(typeof(ElementType))] + public struct ValueEnumerator_1: IEnumerator_1{ + private ElementType value; + private int index; + + public ValueEnumerator_1(ElementType value){ + this.value = value; + this.index = -1; + } + + public ElementType Current{ + get{ + return this.value; + } + } + object System.Collections.IEnumerator.Current{ + get{ + return this.value; + } + } + void System.Collections.IEnumerator.Reset(){ + this.index = -1; + } + void IDisposable.Dispose(){ + } + public bool MoveNext(){ + return ++this.index == 0; + } + } + + [Template(typeof(ElementType))] + public sealed class StreamUtility_1{ + private StreamUtility_1(){} + public static bool IsNull(IEnumerable_1 stream){ + if (stream == null) return true; + IEnumerator_1 enumerator = stream.GetEnumerator(); + while (enumerator.MoveNext()) + if (enumerator.Current != null) return false; + return true; + } + } +} +namespace System{ +#if CCINamespace + using Microsoft.Cci; +#else + using System.Compiler; +#endif + using System.Collections.Generic; + + [Template(typeof(ElementType))] + public struct Nullable_1{ + internal ElementType value; + internal bool hasValue; + + public Nullable_1(ElementType value){ + this.value = value; + this.hasValue = true; + } + + public override bool Equals(object other){ + if (other is Nullable_1) + return Nullable_1.Equals(this, (Nullable_1)other); + return false; + } + + public override int GetHashCode(){ + return hasValue ? value.GetHashCode() : 0; + } + + public override string ToString(){ + return hasValue ? value.ToString() : ""; + } + + public bool HasValue{ + get{ + return hasValue; + } + } + + public ElementType Value{ + get{ + if (!hasValue) + throw new InvalidOperationException(); + return value; + } + } + + public bool Equals(Nullable_1 other){ + return Nullable_1.Equals(this, other); + } + + public ElementType GetValueOrDefault(){ + return value; + } + + public ElementType GetValueOrDefault(ElementType defaultValue){ + return hasValue ? value : defaultValue; + } + + public static implicit operator Nullable_1(ElementType value){ + return new Nullable_1(value); + } + + public static explicit operator ElementType(Nullable_1 value){ + return value.Value; + } + } +} +namespace System.Collections.Generic{ +#if CCINamespace + using Microsoft.Cci; +#else + using System.Compiler; +#endif + using StructuralTypes; + using SCIEnumerable = System.Collections.IEnumerable; + using SCIEnumerator = System.Collections.IEnumerator; + using SCIList = System.Collections.IList; + + public class ElementType : ITemplateParameter{ + } + + [Template(typeof(ElementType))] + public interface IEnumerable_1 : SCIEnumerable{ + [return:Microsoft.Contracts.NotNull] + new IEnumerator_1 GetEnumerator(); + } + + [Template(typeof(ElementType))] + public interface ICollection_1 : IEnumerable_1, SCIEnumerable{ + int Count {get;} + void Add(ElementType e); + void Clear(); + bool Contains(ElementType e); + void CopyTo(ElementType[] array, int index); + bool Remove(ElementType e); + bool IsReadOnly { get; } + } + + [Template(typeof(ElementType))] + public interface IList_1 : ICollection_1, IEnumerable_1, IEnumerable{ + ElementType this[int index] {get; set;} + int IndexOf(ElementType value); + void Insert(int index, ElementType value); + void RemoveAt(int index); + } + [Template(typeof(ElementType))] + public sealed class Stack_1 : IEnumerable_1, ICollection, IEnumerable{ + private ElementType[] elements; + private int capacity; + private int count; + public Stack_1(){ + this.capacity = 0; + this.count = 0; + } + public Stack_1(int capacity){ + this.capacity = capacity; + this.count = 0; + this.elements = new ElementType[capacity]; + } + public int Count{ + get{ + return this.count; + } + } + public void CopyTo(Array array, int index){ + ElementType[] elements = this.elements; + int n = this.count; + for (int i = 0; i < n; i++){ + object elem = elements[i]; + array.SetValue(elem, index++); + } + } + public bool IsSynchronized{ + get{ + return false; + } + } + public bool IsReadOnly{ + get{ + return false; + } + } + public object SyncRoot{ + get{ + return this; + } + } + IEnumerator_1 IEnumerable_1.GetEnumerator(){ + return new StackEnumerator_1(this); + } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator(){ + return new StackEnumerator_1(this); + } + public object Clone(){ + Stack_1 s = new Stack_1(this.capacity); + for (int i = 0, n = this.count; i < n; i++) + s.elements[i] = this.elements[i]; + return s; //REVIEW: should this clone the elements too? + } + private void DoubleCapacity(){ + int oldCapacity = this.capacity; + if (oldCapacity == 0){ + this.capacity = 16; + this.elements = new ElementType[16]; + return; + } + this.capacity = oldCapacity*2; + ElementType[] oldElements = this.elements; + ElementType[] newElements = this.elements = new ElementType[this.capacity]; + for (int i = 0, n = this.count; i < n; i++) + newElements[i] = oldElements[i]; + } + public void Push(ElementType e){ + int i = this.count; + int ip1 = i+1; + if (ip1 > this.capacity) this.DoubleCapacity(); + this.elements[i] = e; + this.count = ip1; + return; + } + public ElementType Pop(){ + if (this.count == 0) + throw new InvalidOperationException("Stack.Pop: empty stack"); + return this.elements[--this.count]; + } + public bool Empty(){ + return this.count == 0; + } + public ElementType Peek(){ + if (this.count == 0) + throw new InvalidOperationException("Stack.Peek: empty stack"); + return this.elements[this.count-1]; + } + public void Clear() { + this.count = 0; + } + } + [Template(typeof(ElementType))] + public struct StackEnumerator_1: IEnumerator_1, SCIEnumerator{ + private ElementType[] contents; + private int index; + private int count; + + public StackEnumerator_1(Stack_1 stack){ + this.count = stack.Count; + this.contents = new ElementType[this.count]; + stack.CopyTo(contents,0); + this.index = -1; + } + + public ElementType Current{ + get{ + return this.contents[this.index]; + } + } + object SCIEnumerator.Current{ + get{ + return this.contents[this.index]; + } + } + void IDisposable.Dispose(){ + } + public bool MoveNext(){ + return ++this.index < this.count; + } + public void Reset(){ + this.index = -1; + } + } + [Template(typeof(ElementType))] + public sealed class List_1 : IList_1, ICollection_1, IEnumerable_1, SCIList, ICollection, IEnumerable{ + private ElementType[] elements; + private int capacity; + private int count; + + public List_1(){ + this.capacity = 0; + this.count = 0; + } + public List_1(int capacity){ + this.capacity = capacity; + this.count = 0; + this.elements = new ElementType[capacity]; + } + public List_1(IEnumerable_1 collection) + : this(16){ + IEnumerator_1 enumerator = collection.GetEnumerator(); + while (enumerator.MoveNext()) + this.Add(enumerator.Current); + } + + public ListEnumerator_1 GetEnumerator(){ + return new ListEnumerator_1(this); + } + IEnumerator_1 IEnumerable_1.GetEnumerator(){ + return new ListEnumerator_1(this); + } + SCIEnumerator SCIEnumerable.GetEnumerator(){ + return new ListEnumerator_1(this); + } + public int Capacity{ + get{ + return this.capacity; + } + set{ + if (value < this.count) throw new ArgumentOutOfRangeException("value"); + ElementType[] oldElements = this.elements; + ElementType[] newElements = this.elements = new ElementType[this.capacity = value]; + for (int i = 0, n = this.count; i < n; i++) + newElements[i] = oldElements[i]; + } + } + public int Count{ + get{ + return this.count; + } + } + public bool IsSynchronized{ + get{ + return false; + } + } + public object SyncRoot{ + get{ + return this; + } + } + public void CopyTo(Array array, int index){ + ElementType[] elements = this.elements; + int n = this.count; + for (int i = 0; i < n; i++){ + object elem = elements[i]; + array.SetValue(elem, index++); + } + } + public void CopyTo(ElementType[] array, int index){ + ElementType[] elements = this.elements; + int n = this.count; + for (int i = 0; i < n; i++){ + object elem = elements[i]; + array.SetValue(elem, index++); + } + } + private void DoubleCapacity(){ + int oldCapacity = this.capacity; + if (oldCapacity == 0){ + this.capacity = 16; + this.elements = new ElementType[16]; + return; + } + this.Capacity = oldCapacity*2; + } + public bool IsFixedSize{ + get{ + return false; + } + } + public bool IsReadOnly{ + get{ + return false; + } + } + public ElementType this[int index]{ + get{ + if (index < 0 || index >= this.count) throw new ArgumentOutOfRangeException("index"); + return this.elements[index]; + } + set{ + if (index < 0 || index >= this.count) throw new ArgumentOutOfRangeException("index"); + this.elements[index] = value; + } + } + object SCIList.this[int index]{ + get{ + return this[index]; + } + set{ + this[index] = (ElementType)value; + } + } + public void Add(ElementType value){ + int i = this.count; + int ip1 = i+1; + if (ip1 > this.capacity) this.DoubleCapacity(); + this.elements[i] = value; + this.count = ip1; + } + int SCIList.Add(object value){ + int i = this.count; + this.Add((ElementType)value); + return i; + } + public void Clear(){ + this.count = 0; + } + public bool Contains(ElementType value){ + return this.IndexOf(value) >= 0; + } + bool SCIList.Contains(object value){ + return ((SCIList)this).IndexOf(value) >= 0; + } + public int IndexOf(ElementType value){ + return Array.IndexOf(this.elements, value, 0, this.count); + } + int SCIList.IndexOf(object value){ + return Array.IndexOf(this.elements, value, 0, this.count); + } + public void Insert(int index, ElementType value){ + if (index < 0 || index > this.count) throw new ArgumentOutOfRangeException("index"); + if (index == this.count) + this.Add(value); + else{ + int i = this.count; + int ip1 = i+1; + if (ip1 > this.capacity) this.DoubleCapacity(); + Array.Copy(this.elements, index, this.elements, index + 1, this.count - index); + this.elements[index] = value; + } + } + void SCIList.Insert(int index, object value){ + this.Insert(index, (ElementType)value); + } + public bool Remove(ElementType value){ + object oval = value; + for(int i = 0; i < this.count; i++){ + object oval2 = this.elements[i]; + if (oval2 == oval || (oval2 != null && oval2.Equals(oval))){ + this.RemoveAt(i); + return true; + } + } + return false; + } + void SCIList.Remove(object value){ + this.Remove((ElementType)value); + } + public void RemoveAt(int index){ + if (index < 0 || index > count) throw new ArgumentOutOfRangeException("index"); + if (index < count - 1){ + Array.Copy(this.elements, index + 1, this.elements, index, count - index); + } + this.count -= 1; + } + public object Clone(){ + return new List_1(this); //REVIEW: should this clone the elements too? + } + public void AddRange(IEnumerable_1 elts){ + foreach (ElementType o in elts) + this.Add(o); + } + public ICollection_1 AsReadOnly(){ + return this; + } + } + + [Template(typeof(ElementType))] + public sealed class Queue_1 : IEnumerable_1, System.Collections.ICollection, IEnumerable{ + private ElementType[] elements; + private int capacity; + private int count; + + public Queue_1(){ + this.capacity = 0; + this.count = 0; + } + public Queue_1(int capacity){ + this.capacity = capacity; + this.count = 0; + this.elements = new ElementType[capacity]; + } + public Queue_1(IEnumerable_1 collection) + : this(16){ + IEnumerator_1 enumerator = collection.GetEnumerator(); + while (enumerator.MoveNext()) + this.Enqueue(enumerator.Current); + } + + public ArrayEnumerator_1 GetEnumerator(){ + return new ArrayEnumerator_1(this.elements); + } + IEnumerator_1 IEnumerable_1.GetEnumerator(){ + return new ArrayEnumerator_1(this.elements); + } + SCIEnumerator SCIEnumerable.GetEnumerator(){ + return new ArrayEnumerator_1(this.elements); + } + public int Capacity{ + get{ + return this.capacity; + } + set{ + if (value < this.count) throw new ArgumentOutOfRangeException("value"); + ElementType[] oldElements = this.elements; + ElementType[] newElements = this.elements = new ElementType[this.capacity = value]; + for (int i = 0, n = this.count; i < n; i++) + newElements[i] = oldElements[i]; + } + } + public int Count{ + get{ + return this.count; + } + } + public bool IsSynchronized{ + get{ + return false; + } + } + public object SyncRoot{ + get{ + return this; + } + } + public void CopyTo(Array array, int index){ + ElementType[] elements = this.elements; + int n = this.count; + for (int i = 0; i < n; i++){ + object elem = elements[i]; + array.SetValue(elem, index++); + } + } + private void DoubleCapacity(){ + int oldCapacity = this.capacity; + if (oldCapacity == 0){ + this.capacity = 16; + this.elements = new ElementType[16]; + return; + } + this.Capacity = oldCapacity*2; + } + public bool IsFixedSize{ + get{ + return false; + } + } + public bool IsReadOnly{ + get{ + return false; + } + } + public void Enqueue(ElementType value){ + int i = this.count; + int ip1 = i+1; + if (ip1 > this.capacity) this.DoubleCapacity(); + this.elements[i] = value; + this.count = ip1; + } + public void Clear(){ + this.count = 0; + } + public bool Contains(ElementType value){ + return Array.IndexOf(this.elements, value, 0, this.count) >= 0; + } + public ElementType Dequeue(){ + if (this.count == 0){ + throw new InvalidOperationException(); + } + ElementType oval = this.elements[0]; + for(int i = 0; i < this.count-1; i++){ + this.elements[i] = this.elements[i+1]; + } + this.count--; + return oval; + } + public ElementType Peek(){ + if (this.count == 0){ + throw new InvalidOperationException(); + } + return this.elements[0]; + } + public object Clone(){ + return new Queue_1(this); //REVIEW: should this clone the elements too? + } + } + + [Template(typeof(ElementType))] + public interface IEnumerator_1 : IDisposable, SCIEnumerator{ + new ElementType Current{get;} + } + + [Template(typeof(ElementType))] + public struct ArrayEnumerator_1: IEnumerator_1, SCIEnumerator{ + private ElementType[] array; + private int index; + + public ArrayEnumerator_1(ElementType[] array){ + this.array = array; + this.index = -1; + } + + public ElementType Current{ + get{ + return this.array[this.index]; + } + } + object SCIEnumerator.Current{ + get{ + return this.array[this.index]; + } + } + void IDisposable.Dispose(){ + } + public bool MoveNext(){ + return ++this.index < this.array.Length; + } + void SCIEnumerator.Reset(){ + this.index = -1; + } + } + + [Template(typeof(ElementType))] + public struct ListEnumerator_1: IEnumerator_1, SCIEnumerator{ + private List_1 list; + private int index; + + public ListEnumerator_1(List_1 list){ + this.list = list; + this.index = -1; + } + + public ElementType Current{ + get{ + return this.list[this.index]; + } + } + object SCIEnumerator.Current{ + get{ + return this.list[this.index]; + } + } + void IDisposable.Dispose(){ + } + public bool MoveNext(){ + return ++this.index < this.list.Count; + } + public void Reset(){ + this.index = -1; + } + } +} + +#if !NoData && !ROTOR +namespace System.Query{ +#if CCINamespace + using Microsoft.Cci; +#else + using System.Compiler; +#endif + using System.Collections.Generic; + using System.Data.SqlTypes; + using System.Text; + + //TODO: this class should derive from SystemException and it should not contain a string + //The message, if any, should be obtained from a resource file + public class StreamNotSingletonException: Exception{ + public StreamNotSingletonException(): base("Stream not singleton"){ + } + } + + public interface IAggregate{ + } + public interface IAggregateGroup{ + } + + public class Min: IAggregateGroup{ + public struct MinOfByte: IAggregate{ + byte value; + bool hasValue; + public void Add(byte value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value < this.value){ + this.value = value; + } + } + public byte GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MinOfInt16: IAggregate{ + short value; + bool hasValue; + public void Add(short value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value < this.value){ + this.value = value; + } + } + public short GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MinOfInt32: IAggregate{ + int value; + bool hasValue; + public void Add(int value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value < this.value){ + this.value = value; + } + } + public int GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MinOfInt64: IAggregate{ + long value; + bool hasValue; + public void Add(long value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value < this.value) + this.value = value; + } + public long GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MinOfSingle: IAggregate{ + float value; + bool hasValue; + public void Add(float value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value < this.value){ + this.value = value; + } + } + public float GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MinOfDouble: IAggregate{ + double value; + bool hasValue; + public void Add(double value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value < this.value) + this.value = value; + } + public double GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MinOfDecimal: IAggregate{ + decimal value; + bool hasValue; + public void Add(decimal value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value < this.value) + this.value = value; + } + public decimal GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MinOfString: IAggregate{ + string value; + public void Add(string value){ + if (value != null){ + if (this.value == null || value.CompareTo(this.value) < 0) + this.value = value; + } + } + public string GetValue(){ + string result = this.value; + this.value = null; + return result; + } + } + public struct MinOfDateTime: IAggregate{ + DateTime value; + bool hasValue; + public void Add(DateTime value){ + if (!this.hasValue || value < this.value){ + this.value = value; + this.hasValue = true; + } + } + public DateTime GetValue(){ + DateTime result = this.value; + this.value = new DateTime(); + this.hasValue = false; + return result; + } + } + public struct MinOfSqlByte: IAggregate{ + byte value; + bool hasValue; + public void Add(SqlByte value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = (byte)value; + this.hasValue = true; + } + if (value < this.value){ + this.value = (byte)value; + } + } + } + public SqlByte GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlByte.Null; + } + } + public struct MinOfSqlInt16: IAggregate{ + short value; + bool hasValue; + public void Add(SqlInt16 value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = (short)value; + this.hasValue = true; + } + if (value < this.value){ + this.value = (short)value; + } + } + } + public SqlInt16 GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlInt16.Null; + } + } + public struct MinOfSqlInt32: IAggregate{ + int value; + bool hasValue; + public void Add(SqlInt32 value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = (int)value; + this.hasValue = true; + } + if (value < this.value){ + this.value = (int)value; + } + } + } + public SqlInt32 GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlInt32.Null; + } + } + public struct MinOfSqlInt64: IAggregate{ + long value; + bool hasValue; + public void Add(SqlInt64 value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = (long)value; + this.hasValue = true; + } + if (value < this.value){ + this.value = (long)value; + } + } + } + public SqlInt64 GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlInt64.Null; + } + } + public struct MinOfSqlSingle: IAggregate{ + float value; + bool hasValue; + public void Add(SqlSingle value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = value.Value; + this.hasValue = true; + } + if (value < this.value){ + this.value = value.Value; + } + } + } + public SqlSingle GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlSingle.Null; + } + } + public struct MinOfSqlDouble: IAggregate{ + double value; + bool hasValue; + public void Add(SqlDouble value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = (double)value; + this.hasValue = true; + } + if (value < this.value){ + this.value = (double)value; + } + } + } + public SqlDouble GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlDouble.Null; + } + } + public struct MinOfSqlDecimal: IAggregate{ + SqlDecimal value; + bool hasValue; + public void Add(SqlDecimal value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value < this.value){ + this.value = value; + } + } + } + public SqlDecimal GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlDecimal.Null; + } + } + public struct MinOfSqlMoney: IAggregate{ + SqlMoney value; + bool hasValue; + public void Add(SqlMoney value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value < this.value){ + this.value = value; + } + } + } + public SqlMoney GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlMoney.Null; + } + } + public struct MinOfSqlString: IAggregate{ + SqlString value; + bool hasValue; + public void Add(SqlString value){ + if (!value.IsNull){ + if (!this.hasValue || (bool)SqlString.LessThan(value, this.value)){ + this.value = value; + this.hasValue = true; + } + } + } + public SqlString GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlString.Null; + } + } + public struct MinOfSqlDateTime: IAggregate{ + SqlDateTime value; + bool hasValue; + public void Add(SqlDateTime value){ + if (!value.IsNull){ + if (!this.hasValue || (bool)SqlDateTime.LessThan(value, this.value)){ + this.value = value; + this.hasValue = true; + } + } + } + public SqlDateTime GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlDateTime.Null; + } + } + } + public class Max: IAggregateGroup{ + public struct MaxOfByte: IAggregate{ + byte value; + bool hasValue; + public void Add(byte value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value > this.value) + this.value = value; + } + public byte GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MaxOfInt16: IAggregate{ + short value; + bool hasValue; + public void Add(short value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value > this.value) + this.value = value; + } + public short GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MaxOfInt32: IAggregate{ + int value; + bool hasValue; + public void Add(int value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value > this.value) + this.value = value; + } + public int GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MaxOfInt64: IAggregate{ + long value; + bool hasValue; + public void Add(long value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value > this.value) + this.value = value; + } + public long GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MaxOfSingle: IAggregate{ + float value; + bool hasValue; + public void Add(float value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value > this.value) + this.value = value; + } + public float GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MaxOfDouble: IAggregate{ + double value; + bool hasValue; + public void Add(double value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value > this.value) + this.value = value; + } + public double GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MaxOfDecimal: IAggregate{ + decimal value; + bool hasValue; + public void Add(decimal value){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value > this.value) + this.value = value; + } + public decimal GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return 0; + } + } + public struct MaxOfString: IAggregate{ + string value; + public void Add(string value){ + if (value != null){ + if (this.value == null || value.CompareTo(this.value) > 0) + this.value = value; + } + } + public string GetValue(){ + string result = this.value; + this.value = null; + return result; + } + } + public struct MaxOfDateTime: IAggregate{ + DateTime value; + bool hasValue; + public void Add(DateTime value){ + if (!this.hasValue || value > this.value){ + this.value = value; + this.hasValue = true; + } + } + public DateTime GetValue(){ + DateTime result = this.value; + this.value = new DateTime(); + this.hasValue = false; + return result; + } + } + public struct MaxOfSqlByte: IAggregate{ + byte value; + bool hasValue; + public void Add(SqlByte value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = (byte)value; + this.hasValue = true; + } + if (value > this.value) + this.value = (byte)value; + } + } + public SqlByte GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlByte.Null; + } + } + public struct MaxOfSqlInt16: IAggregate{ + short value; + bool hasValue; + public void Add(SqlInt16 value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = (short)value; + this.hasValue = true; + } + if (value > this.value) + this.value = (short)value; + } + } + public SqlInt16 GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlInt16.Null; + } + } + public struct MaxOfSqlInt32: IAggregate{ + int value; + bool hasValue; + public void Add(SqlInt32 value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = (int)value; + this.hasValue = true; + } + if (value > this.value) + this.value = (int)value; + } + } + public SqlInt32 GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlInt32.Null; + } + } + public struct MaxOfSqlInt64: IAggregate{ + long value; + bool hasValue; + public void Add(SqlInt64 value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = (long)value; + this.hasValue = true; + } + if (value > this.value) + this.value = (long)value; + } + } + public SqlInt64 GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlInt64.Null; + } + } + public struct MaxOfSqlSingle: IAggregate{ + float value; + bool hasValue; + public void Add(SqlSingle value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = value.Value; + this.hasValue = true; + } + if (value > this.value) + this.value = value.Value; + } + } + public SqlSingle GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlSingle.Null; + } + } + public struct MaxOfSqlDouble: IAggregate{ + double value; + bool hasValue; + public void Add(SqlDouble value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = (double)value; + this.hasValue = true; + } + if (value > this.value) + this.value = (double)value; + } + } + public SqlDouble GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlDouble.Null; + } + } + public struct MaxOfSqlDecimal: IAggregate{ + SqlDecimal value; + bool hasValue; + public void Add(SqlDecimal value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value > this.value) + this.value = value; + } + } + public SqlDecimal GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlDecimal.Null; + } + } + public struct MaxOfSqlMoney: IAggregate{ + SqlMoney value; + bool hasValue; + public void Add(SqlMoney value){ + if (!value.IsNull){ + if (!this.hasValue){ + this.value = value; + this.hasValue = true; + } + if (value > this.value) + this.value = value; + } + } + public SqlMoney GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlMoney.Null; + } + } + public struct MaxOfSqlString: IAggregate{ + SqlString value; + bool hasValue; + public void Add(SqlString value){ + if (!value.IsNull){ + if (!this.hasValue || (bool)SqlString.GreaterThan(value, this.value)){ + this.value = value; + this.hasValue = true; + } + } + } + public SqlString GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlString.Null; + } + } + public struct MaxOfSqlDateTime: IAggregate{ + SqlDateTime value; + bool hasValue; + public void Add(SqlDateTime value){ + if (!value.IsNull){ + if (!this.hasValue || (bool)SqlDateTime.GreaterThan(value, this.value)){ + this.value = value; + this.hasValue = true; + } + } + } + public SqlDateTime GetValue(){ + if (this.hasValue){ + this.hasValue = false; + return this.value; + } + return SqlDateTime.Null; + } + } + } + public class Sum: IAggregateGroup{ + public struct SumOfInt32: IAggregate{ + int total; + public void Add(int value){ + this.total = this.total + value; + } + public int GetValue(){ + int ret = this.total; + this.total = 0; + return ret; + } + } + public struct SumOfInt64: IAggregate{ + long total; + public void Add(long value){ + this.total = this.total + value; + } + public long GetValue(){ + long ret = this.total; + this.total = 0; + return ret; + } + } + public struct SumOfDouble: IAggregate{ + double total; + public void Add(double value){ + this.total = this.total + value; + } + public double GetValue(){ + double ret = this.total; + this.total = 0.0; + return ret; + } + } + public struct SumOfDecimal: IAggregate{ + decimal total; + public void Add(decimal value){ + this.total = this.total + value; + } + public decimal GetValue(){ + decimal ret = this.total; + this.total = 0; + return ret; + } + } + public struct SumOfSqlInt32: IAggregate{ + int total; + bool hasValue; + public void Add(SqlInt32 value){ + if (!value.IsNull){ + if (this.hasValue) + this.total = this.total + (int) value; + else{ + this.total = (int) value; + this.hasValue = true; + } + } + } + public SqlInt32 GetValue(){ + if (!this.hasValue){ + return SqlInt32.Null; + } + this.hasValue = false; + return (SqlInt32) this.total; + } + } + public struct SumOfSqlInt64: IAggregate{ + long total; + bool hasValue; + public void Add(SqlInt64 value){ + if (!value.IsNull){ + if (this.hasValue) + this.total = this.total + (long) value; + else{ + this.total = (long) value; + this.hasValue = true; + } + } + } + public SqlInt64 GetValue(){ + if (!this.hasValue) return SqlInt64.Null; + this.hasValue = false; + return (SqlInt64) this.total; + } + } + public struct SumOfSqlDouble: IAggregate{ + SqlDouble total; + bool hasValue; + public void Add(SqlDouble value){ + if (!value.IsNull){ + if (this.hasValue){ + this.total = this.total + value; + }else{ + this.total = value; + this.hasValue = true; + } + } + } + public SqlDouble GetValue(){ + if (!this.hasValue) return SqlDouble.Null; + this.hasValue = false; + return this.total; + } + } + public struct SumOfSqlDecimal: IAggregate{ + SqlDecimal total; + bool hasValue; + public void Add(SqlDecimal value){ + if (!value.IsNull){ + if (this.hasValue) + this.total = this.total + value; + else{ + this.total = value; + this.hasValue = true; + } + } + } + public SqlDecimal GetValue(){ + if (!this.hasValue) return SqlDecimal.Null; + this.hasValue = false; + return this.total; + } + } + public struct SumOfSqlMoney: IAggregate{ + SqlMoney total; + bool hasValue; + public void Add(SqlMoney value){ + if (!value.IsNull){ + if (this.hasValue) + this.total = this.total + value; + else{ + this.total = value; + this.hasValue = true; + } + } + } + public SqlMoney GetValue(){ + if (!this.hasValue) return SqlMoney.Null; + this.hasValue = false; + return this.total; + } + } + } + public class Avg: IAggregateGroup{ + public struct AvgOfInt32: IAggregate{ + long total; + long count; + public void Add(int value){ + this.total += value; + this.count++; + } + public int GetValue(){ + int result = (int)(this.total / this.count); + this.total = 0; + this.count = 0; + return result; + } + } + public struct AvgOfInt64: IAggregate{ + decimal total; + long count; + public void Add(long value){ + this.total += value; + this.count++; + } + public long GetValue(){ + long result = (long)(this.total / this.count); + this.total = 0; + this.count = 0; + return result; + } + } + public struct AvgOfDouble: IAggregate{ + double total; + long count; + public void Add(double value){ + this.total += value; + this.count++; + } + public double GetValue(){ + double result = this.total / this.count; + this.total = 0; + this.count = 0; + return result; + } + } + public struct AvgOfDecimal: IAggregate{ + decimal total; + long count; + public void Add(decimal value){ + this.total += value; + this.count++; + } + public decimal GetValue(){ + decimal result = this.total / this.count; + this.total = 0; + this.count = 0; + return result; + } + } + public struct AvgOfSqlInt32: IAggregate{ + long total; + long count; + public void Add(SqlInt32 value){ + if (!value.IsNull){ + if (this.count == 0) + this.total = (int)value; + else + this.total += (int)value; + this.count++; + } + } + public SqlInt32 GetValue(){ + if (this.count == 0) return SqlInt32.Null; + int result = (int)(this.total / this.count); + this.count = 0; + return result; + } + } + public struct AvgOfSqlInt64: IAggregate{ + decimal total; + long count; + public void Add(SqlInt64 value){ + if (!value.IsNull){ + if (this.count == 0) + this.total = (long)value; + else + this.total += (long)value; + this.count++; + } + } + public SqlInt64 GetValue(){ + if (this.count == 0) return SqlInt64.Null; + long result = (long)(this.total / this.count); + this.count = 0; + return result; + } + } + public struct AvgOfSqlDouble: IAggregate{ + SqlDouble total; + long count; + public void Add(SqlDouble value){ + if (!value.IsNull){ + if (this.count == 0) + this.total = value; + else + this.total += value; + this.count++; + } + } + public SqlDouble GetValue(){ + if (this.count == 0) return SqlDouble.Null; + SqlDouble result = this.total / count; + this.count = 0; + return result; + } + } + public struct AvgOfSqlDecimal: IAggregate{ + SqlDecimal total; + long count; + public void Add(SqlDecimal value){ + if (!value.IsNull){ + if (this.count == 0) + this.total = value; + else + this.total += value; + this.count++; + } + } + public SqlDecimal GetValue(){ + if (this.count == 0) return SqlDecimal.Null; + SqlDecimal result = this.total / count; + this.count = 0; + return result; + } + } + public struct AvgOfSqlMoney: IAggregate{ + SqlMoney total; + long count; + public void Add(SqlMoney value){ + if (!value.IsNull){ + if (this.count == 0) + this.total = value; + else + this.total += value; + this.count++; + } + } + public SqlMoney GetValue(){ + if (this.count == 0) return SqlMoney.Null; + SqlMoney result = this.total / count; + this.count = 0; + return result; + } + } + } + public class Stdev: IAggregateGroup{ + public struct StdevOfDouble: IAggregate{ + double sumX; + double sumX2; + int count; + public void Add(double value){ + this.sumX += value; + this.sumX2 += (value * value); + this.count++; + } + public double GetValue(){ + int c = count - 1; + double result = Math.Sqrt((sumX2/c) - ((sumX * sumX)/count/c)); + this.sumX = 0; + this.sumX2 = 0; + this.count = 0; + return result; + } + } + public struct StdevOfDecimal: IAggregate{ + decimal sumX; + decimal sumX2; + int count; + public void Add(decimal value){ + this.sumX += value; + this.sumX2 += (value * value); + this.count++; + } + public double GetValue(){ + int c = count - 1; + // note: using Math.Sqrt(double) would lose precision, so use SqlDecimal.Power + SqlDecimal result = SqlDecimal.Power((sumX2/c) - ((sumX * sumX)/count/c), 0.5); + this.sumX = 0; + this.sumX2 = 0; + this.count = 0; + return (double)(SqlDouble)result; + } + } + public struct StdevOfSqlDouble: IAggregate{ + double sumX; + double sumX2; + int count; + public void Add(SqlDouble value){ + if (!value.IsNull){ + double dv = (double) value; + this.sumX += dv; + this.sumX2 += dv * dv; + this.count++; + } + } + public SqlDouble GetValue(){ + if (this.count < 2) return SqlDouble.Null; + int c = count - 1; + double result = Math.Sqrt((sumX2/c) - ((sumX * sumX)/count/c)); + this.sumX = 0; + this.sumX2 = 0; + this.count = 0; + return (SqlDouble) result; + } + } + public struct StdevOfSqlDecimal: IAggregate{ + SqlDecimal sumX; + SqlDecimal sumX2; + int count; + public void Add(SqlDecimal value){ + if (!value.IsNull){ + if (this.count == 0){ + this.sumX = value; + this.sumX2 = value * value; + }else{ + this.sumX += value; + this.sumX2 += value * value; + } + this.count++; + } + } + public SqlDouble GetValue(){ + if (this.count < 2) return SqlDecimal.Null; + int c = count - 1; + SqlDecimal result = SqlDecimal.Power((sumX2/c) - ((sumX * sumX)/count/c), 0.5); + this.sumX = 0; + this.sumX2 = 0; + this.count = 0; + return (SqlDouble)result; + } + } + public struct StdevOfSqlMoney: IAggregate{ + SqlMoney sumX; + SqlMoney sumX2; + int count; + public void Add(SqlMoney value){ + if (!value.IsNull){ + if (this.count == 0){ + this.sumX = value; + this.sumX2 = value * value; + }else{ + this.sumX += value; + this.sumX2 += value * value; + } + this.count++; + } + } + public SqlDouble GetValue(){ + if (this.count < 2) return SqlMoney.Null; + int c = count - 1; + SqlDecimal result = SqlDecimal.Power((sumX2/c) - ((sumX * sumX)/count/c), 0.5); + this.sumX = 0; + this.sumX2 = 0; + this.count = 0; + return (SqlDouble)result; + } + } + } + public class Count: IAggregateGroup{ + public struct CountOfObject: IAggregate{ + int count; + public void Add(object value){ + count++; + } + public int GetValue(){ + int result = count; + this.count = 0; + return result; + } + } + } + + [Anonymous] + public sealed class SqlFunctions{ + public static SqlByte Abs(SqlByte value){ + return value; + } + public static SqlInt16 Abs(SqlInt16 value){ + if (value.IsNull) return SqlInt16.Null; + return Math.Abs((short)value); + } + public static SqlInt32 Abs(SqlInt32 value){ + if (value.IsNull) return SqlInt32.Null; + return Math.Abs((int)value); + } + public static SqlInt64 Abs(SqlInt64 value){ + if (value.IsNull) return SqlInt64.Null; + return Math.Abs((long)value); + } + public static SqlDouble Abs(SqlDouble value){ + if (value.IsNull) return SqlDouble.Null; + return Math.Abs((double)value); + } + public static SqlDecimal Abs(SqlDecimal value){ + if (value.IsNull) return SqlDecimal.Null; + return (value < 0) ? -value : value; + } + public static SqlMoney Abs(SqlMoney value){ + if (value.IsNull) return SqlMoney.Null; + return (value < 0) ? -value : value; + } + public static SqlDouble Acos(SqlDouble value){ + if (value.IsNull) return SqlDouble.Null; + return Math.Acos((double)value); + } + public static SqlDouble Asin(SqlDouble value){ + if (value.IsNull) return SqlDouble.Null; + return Math.Asin((double)value); + } + public static SqlDouble Atan(SqlDouble value){ + if (value.IsNull) return SqlDouble.Null; + return Math.Atan((double)value); + } + public static SqlDouble Atn2(SqlDouble value1, SqlDouble value2){ + if (value1.IsNull || value2.IsNull) return SqlDouble.Null; + return Math.Atan2((double)value1, (double)value2); + } + public static SqlByte Ceiling(SqlByte value){ + return value; + } + public static SqlInt16 Ceiling(SqlInt16 value){ + return value; + } + public static SqlInt32 Ceiling(SqlInt32 value){ + return value; + } + public static SqlInt64 Ceiling(SqlInt64 value){ + return value; + } + public static SqlDouble Ceiling(SqlDouble value){ + if (value.IsNull) return SqlDouble.Null; + return Math.Ceiling((double)value); + } + public static SqlDecimal Ceiling(SqlDecimal value){ + return SqlDecimal.Ceiling(value); + } + public static SqlMoney Ceiling(SqlMoney value){ + return (SqlMoney)SqlDecimal.Ceiling(value); + } + public static SqlInt32 CharIndex(SqlString pattern, SqlString source){ + if (pattern.IsNull || source.IsNull) return SqlInt32.Null; + return ((string)source).IndexOf((string)pattern) + 1; + } + public static SqlDouble Cos(SqlDouble value){ + if (value.IsNull) return SqlDouble.Null; + return Math.Cos((double)value); + } + public static SqlDateTime DateAdd(SqlDatePart part, SqlInt32 value, SqlDateTime date){ + if (value.IsNull || date.IsNull) return SqlDateTime.Null; + int incr = (int)value; + DateTime dt = (DateTime)date; + switch (part){ + case SqlDatePart.Year: + return dt.AddYears(incr); + case SqlDatePart.Month: + return dt.AddMonths(incr); + case SqlDatePart.Day: + return dt.AddDays(incr); + case SqlDatePart.Hour: + return dt.AddHours(incr); + case SqlDatePart.Minute: + return dt.AddMinutes(incr); + case SqlDatePart.Second: + return dt.AddSeconds(incr); + case SqlDatePart.Millisecond: + return dt.AddMilliseconds(incr); + } + return dt; + } + public static SqlInt32 DatePart(SqlDatePart part, SqlDateTime date){ + if (date.IsNull) return SqlInt32.Null; + DateTime dt = (DateTime)date; + switch (part){ + case SqlDatePart.Year: + return dt.Year; + case SqlDatePart.Month: + return dt.Month; + case SqlDatePart.Week: + return (dt.DayOfYear + 6)/ 7; + case SqlDatePart.WeekDay: + return (int)dt.DayOfWeek; + case SqlDatePart.Day: + return dt.Day; + case SqlDatePart.DayOfYear: + return dt.DayOfYear; + case SqlDatePart.Hour: + return dt.Hour; + case SqlDatePart.Minute: + return dt.Minute; + case SqlDatePart.Second: + return dt.Second; + case SqlDatePart.Millisecond: + return dt.Millisecond; + } + return 0; + } + public static SqlDouble Degrees(SqlDouble radians){ + if (radians.IsNull) return SqlDouble.Null; + return ((double)radians) * Math.PI / 2; + } + public static SqlDouble Exp(SqlDouble exponent){ + return Math.Exp((double)exponent); + } + public static SqlByte Floor(SqlByte value){ + return value; + } + public static SqlInt16 Floor(SqlInt16 value){ + return value; + } + public static SqlInt32 Floor(SqlInt32 value){ + return value; + } + public static SqlInt64 Floor(SqlInt64 value){ + return value; + } + public static SqlDouble Floor(SqlDouble value){ + if (value.IsNull) return SqlDouble.Null; + return Math.Floor((double)value); + } + public static SqlDecimal Floor(SqlDecimal value){ + return SqlDecimal.Floor(value); + } + public static SqlMoney Floor(SqlMoney value){ + return (SqlMoney)SqlDecimal.Floor(value); + } + public static SqlDateTime GetDate(){ + return DateTime.Now; + } + public static SqlDateTime GetUtcDate(){ + return DateTime.UtcNow; + } + public static SqlBoolean IsDate(SqlString value){ + if (value.IsNull) return SqlBoolean.Null; + try{ DateTime.Parse((string)value); } + catch{ return false; } + return true; + } + public static SqlString Left(SqlString value, SqlInt32 length){ + if (value.IsNull || length.IsNull) return SqlString.Null; + int len = (int)length; + string str = (string)value; + return str.Substring(0, len); + } + public static SqlInt32 Len(SqlString value){ + if (value.IsNull) return SqlInt32.Null; + return ((string)value).Length; + } + public static SqlDouble Log(SqlDouble value){ + if (value.IsNull) return SqlDouble.Null; + return Math.Log((double)value); + } + public static SqlDouble Log10(SqlDouble value){ + if (value.IsNull) return SqlDouble.Null; + return Math.Log10((double)value); + } + public static SqlDouble Power(SqlDouble value, SqlDouble exponent){ + if (value.IsNull || exponent.IsNull) return SqlDouble.Null; + return Math.Pow((double)value, (double)exponent); + } + public static SqlString Replace(SqlString source, SqlString oldValue, SqlString newValue){ + if (source.IsNull || oldValue.IsNull || newValue.IsNull) return SqlString.Null; + return ((string)source).Replace((string)oldValue, (string)newValue); + } + public static SqlString Reverse(SqlString value){ + if (value.IsNull) return SqlString.Null; + string str = (string)value; + StringBuilder sb = new StringBuilder(str.Length); + for(int i = str.Length - 1; i >= 0; i--) + sb.Append(str[i]); + return sb.ToString(); + } + public static SqlString Right(SqlString value, SqlInt32 length){ + if (value.IsNull || length.IsNull) return SqlString.Null; + string str = (string)value; + int len = Math.Min((int)length, str.Length); + return str.Substring(str.Length - len - 1, len); + } + public static SqlDouble Round(SqlDouble value, SqlInt32 precision){ + if (value.IsNull || precision.IsNull) return SqlDouble.Null; + return Math.Round((double)value, (int)precision); + } + public static SqlDecimal Round(SqlDecimal value, SqlInt32 precision){ + if (value.IsNull || precision.IsNull) return SqlDecimal.Null; + return SqlDecimal.Round(value, (int)precision); + } + public static SqlMoney Round(SqlMoney value, SqlInt32 precision){ + if (value.IsNull || precision.IsNull) return SqlMoney.Null; + return (SqlMoney) SqlDecimal.Round(value, (int)precision); + } + public static SqlDouble Sin(SqlDouble value){ + if (value.IsNull) return SqlDouble.Null; + return Math.Sin((double)value); + } + public static SqlString Stuff(SqlString source, SqlInt32 position, SqlInt32 length, SqlString value){ + if (source.IsNull || position.IsNull || length.IsNull) return SqlString.Null; + int offset = ((int)position) - 1; + string result = ((string)source).Remove(offset, (int)length); + if (!value.IsNull) result = result.Insert(offset, (string)value); + return result; + } + public static SqlString Substring(SqlString source, SqlInt32 position, SqlInt32 length){ + if (source.IsNull || position.IsNull || length.IsNull) return SqlString.Null; + int offset = ((int)position) - 1; + return ((string)source).Substring(offset, (int)length); + } + } + + [Anonymous] + public enum SqlDatePart{ + Year, + Quarter, + Month, + Day, + DayOfYear, + Week, + WeekDay, + Hour, + Minute, + Second, + Millisecond + } + + [Anonymous] + public enum SqlHint{ + HoldLock, + Serializable, + RepeatableRead, + ReadCommitted, + ReadUncommitted, + NoLock, + RowLock, + PageLock, + TableLock, + TableLockExclusive, + ReadPast, + UpdateLock, + ExclusiveLock, + } +} +#endif +#endif +namespace Microsoft.Contracts{ + +#if CCINamespace + using Microsoft.Cci; +#else + using System.Compiler; +#endif + + [Template(typeof(ElementTypeAttribute))] + public class Range: System.Collections.IEnumerable{ + public int From, To, Step; + public int FromO, ToO; + //private bool ok; + //public Range(int f, int t, int s) { + //From=f-1; To=t; Step=s; ok = false; + //} + public Range(int f, int t) { + From=f; To=t; Step=1; //ok = false; + } + public RangeEnumerator GetEnumerator(){ + return new RangeEnumerator(From, To, Step); + } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator(){ + return new RangeEnumerator(From, To, Step); + } + + public struct RangeEnumerator:System.Collections.IEnumerator{ + private int from, to, step; + private bool ok ; + public RangeEnumerator(int f, int t, int s) { + from=f; to=t; step=s; ok = false; + } + public object Current{get {if(ok) return from; else throw new Exception("RangeEnumerator.Current: wrong protocol");}} + public bool MoveNext() {if (!ok) ok = true; else from+=step; return from<=to;} + public void Reset() {throw new Exception("RangeEnumerator.Reset: not implemented");} + } + } + + /// + /// Used as an optional modifier on type parameter occurrences to override any non-null + /// instantiations. + /// + public sealed class NullableType { + } + public sealed class NonNullType { + [Throws("System.NullException")] + [StateIndependent] + [System.Diagnostics.DebuggerStepThrough] + [System.Diagnostics.Conditional("DEBUG")] + public static void AssertNotNull([Delayed] UIntPtr ptr) { + if (ptr == (UIntPtr)0) { +#if KERNEL + Microsoft.Singularity.DebugStub.Break(); +#else + Microsoft.Singularity.V1.Services.DebugService.Break(); +#endif + throw new NullException(); + } + } + [Throws("System.NullException")] + [StateIndependent] + [System.Diagnostics.DebuggerStepThrough] + [System.Diagnostics.Conditional("DEBUG")] + public static void AssertNotNullImplicit([Delayed] UIntPtr ptr) { + if (ptr == (UIntPtr)0) { +#if KERNEL + Microsoft.Singularity.DebugStub.Break(); +#else + Microsoft.Singularity.V1.Services.DebugService.Break(); +#endif + throw new NullException(); + } + } + [Throws("System.NullException")] + [StateIndependent] + [System.Diagnostics.DebuggerStepThrough] + [System.Diagnostics.Conditional("DEBUG")] + public static void AssertNotNull([Delayed] object obj) { + if (obj == null) { +#if KERNEL + Microsoft.Singularity.DebugStub.Break(); +#else + Microsoft.Singularity.V1.Services.DebugService.Break(); +#endif + throw new NullException(); + } + } + [Throws("System.NullException")] + [StateIndependent] + [System.Diagnostics.DebuggerStepThrough] + [System.Diagnostics.Conditional("DEBUG")] + public static void AssertNotNullImplicit([Delayed] object obj) { + if (obj == null) { +#if KERNEL + Microsoft.Singularity.DebugStub.Break(); +#else + Microsoft.Singularity.V1.Services.DebugService.Break(); +#endif + throw new NullException(); + } + } + } + public class AssertHelpers { + #region Assert methods + [System.Diagnostics.DebuggerStepThrough] + public static void Assert(bool b) { + if (!b) + throw new Contracts.AssertException(); + } + [System.Diagnostics.DebuggerStepThrough] + public static void AssertLoopInvariant(bool b) { + if (!b) + throw new Contracts.LoopInvariantException(); + } + [System.Diagnostics.DebuggerStepThrough] + public static void Assert(bool b, string s) { + if (!b) + throw new Contracts.AssertException(s); + } + [System.Diagnostics.DebuggerStepThrough] + public static void Assert(bool b, string s, Exception inner) { + if (!b) + throw new Contracts.AssertException(s,inner); + } + /// + /// Is deserialized by Boogie. + /// + /// Serialized condition + [System.Diagnostics.DebuggerStepThrough] + [Obsolete("Use the two-argument version instead. Delete this one when the LKG > 7301.")] + public static void AssertStatement(string condition) { + } + /// + /// Is deserialized by Boogie. + /// + /// Serialized condition + /// Text to use in error messages + [System.Diagnostics.DebuggerStepThrough] + public static void AssertStatement(string condition, string messageForUser) { + } + /// + /// Is deserialized by Boogie. + /// + /// Serialized condition + [System.Diagnostics.DebuggerStepThrough] + [Obsolete("Use the two-argument version instead. Delete this one when the LKG > 7301.")] + public static void LoopInvariant(string condition) { + } + /// + /// Is deserialized by Boogie. + /// + /// Serialized condition + /// Text to use in error messages + [System.Diagnostics.DebuggerStepThrough] + public static void LoopInvariant(string condition, string messageForUser) { + } + #endregion Assert methods + #region Assume methods + [System.Diagnostics.DebuggerStepThrough] + public static void Assume(bool b) { + if (!b) + throw new Contracts.AssumeException(); + } + [System.Diagnostics.DebuggerStepThrough] + public static void Assume(bool b, string s) { + if (!b) + throw new Contracts.AssumeException(s); + } + [System.Diagnostics.DebuggerStepThrough] + public static void Assume(bool b, string s, Exception inner) { + if (!b) + throw new Contracts.AssumeException(s,inner); + } + /// + /// Is deserialized by Boogie. + /// + /// Serialized condition + [System.Diagnostics.DebuggerStepThrough] + public static void AssumeStatement(string condition) { + } + #endregion Assume methods + [return: Microsoft.Contracts.NotNull] + public static object OwnerIs(object owner, [Microsoft.Contracts.NotNull] object owned_object) { return owned_object; } + } + #region Exceptions + //--------------------------------------------------------------------------- + //Exceptions + //--------------------------------------------------------------------------- + + public class NoChoiceException : Exception {} + public class IllegalUpcastException : Exception {} + + public interface ICheckedException {} + public class CheckedException: Exception, ICheckedException {} + + /// + /// The Spec# compiler emits "throw new UnreachableException();" statements in places that it knows are unreachable, but that are considered reachable by the CLR verifier. + /// + public class UnreachableException : Exception { + public UnreachableException() { } + public UnreachableException(string s) : base(s) { } + public UnreachableException(string s, Exception inner) : base(s, inner) { } + } + public class ContractException : Exception { + public ContractException() {} + public ContractException(string s) : base(s) {} + public ContractException(string s, Exception inner) : base(s,inner) {} + } + public class AssertException : ContractException { + public AssertException() {} + public AssertException(string s) : base(s) {} + public AssertException(string s, Exception inner) : base(s,inner) {} + } + public class LoopInvariantException : AssertException { + public LoopInvariantException() {} + public LoopInvariantException(string s) : base(s) {} + public LoopInvariantException(string s, Exception inner) : base(s,inner) {} + } + public class AssumeException : ContractException { + public AssumeException() {} + public AssumeException(string s) : base(s) {} + public AssumeException(string s, Exception inner) : base(s,inner) {} + } + /// + /// Thrown when evaluation of a contract clause throws an exception. + /// + public class InvalidContractException : ContractException { + public InvalidContractException() {} + public InvalidContractException(string s) : base(s) {} + public InvalidContractException(string s, Exception inner) : base(s,inner) {} + } + public class RequiresException : ContractException { + public RequiresException() {} + public RequiresException(string s) : base(s) {} + public RequiresException(string s, Exception inner) : base(s,inner) {} + } + public class EnsuresException : ContractException { + public EnsuresException() {} + public EnsuresException(string s) : base(s) {} + public EnsuresException(string s, Exception inner) : base(s,inner) {} + } + public class ModifiesException : ContractException { + public ModifiesException() {} + public ModifiesException(string s) : base(s) {} + public ModifiesException(string s, Exception inner) : base(s,inner) {} + } + public class ThrowsException : ContractException { + public ThrowsException() {} + public ThrowsException(string s) : base(s) {} + public ThrowsException(string s, Exception inner) : base(s,inner) {} + } + public class DoesException : ContractException { + public DoesException() {} + public DoesException(string s) : base(s) {} + public DoesException(string s, Exception inner) : base(s,inner) {} + } + public class InvariantException : ContractException { + public InvariantException() {} + public InvariantException(string s) : base(s) {} + public InvariantException(string s, Exception inner) : base(s,inner) {} + } + public class NullException : ContractException { + public NullException() {} + public NullException(string s) : base(s) {} + public NullException(string s, Exception inner) : base(s,inner) {} + } + public class ContractMarkerException : ContractException { + } + #endregion Exceptions + #region Attributes + //--------------------------------------------------------------------------- + //Attributes + //--------------------------------------------------------------------------- + internal sealed class SpecTargets{ + internal const AttributeTargets Code = + AttributeTargets.Constructor|AttributeTargets.Method|AttributeTargets.Property|AttributeTargets.Event|AttributeTargets.Delegate; + internal const AttributeTargets Containers = + AttributeTargets.Struct|AttributeTargets.Class|AttributeTargets.Interface; + internal const AttributeTargets CodeAndTheirContainers = + Code|Containers; + internal const AttributeTargets + CodeFieldsAndTheirContainers = CodeAndTheirContainers|AttributeTargets.Field; + } + /// + /// We serialize constructs like Requires as custom attributes. But then there + /// is the problem of how to serialize custom attributes that have been put onto + /// such constructs. Since nested attributes are not allowed, we encode them + /// by serializing the nested attributes and then emitting an attribute of type + /// NestedAttributeCount with the number of nested attributes. That way, upon + /// deserialization, as long as the order of the serialized attributes can be + /// depended upon, we can reconstitute the nested attributes. + /// So the Spec# source: + /// "[A][B] requires P" + /// would be serialized as + /// "A(...), B(...), NestedAttributeCount(2), Requires(P,...)". + /// + /* + [AttributeUsage(AttributeTargets.All,AllowMultiple=true, Inherited = true)] + public class NestedAttributeCount : Attribute{ + public int numberOfNestedAttributes; + public NestedAttributeCount(int _numberOfNestedAttributes){ + numberOfNestedAttributes = _numberOfNestedAttributes; + } + } + */ + public class AttributeWithContext : Attribute{ + public string Filename = ""; + public int StartLine = 0; + public int StartColumn = 0; + public int EndLine = 0; + public int EndColumn = 0; + public string SourceText = ""; + public AttributeWithContext(){} + public AttributeWithContext(string filename, int startline, int startcol, int endline, int endcol) + : this(filename, startline, startcol, endline, endcol, "") + {} + public AttributeWithContext(string filename, int startline, int startcol, int endline, int endcol, string sourceText){ + this.Filename = filename; + this.StartLine = startline; + this.StartColumn = startcol; + this.EndLine = endline; + this.EndColumn = endcol; + this.SourceText = sourceText; + } + } + [AttributeUsage(Microsoft.Contracts.SpecTargets.Code, AllowMultiple=false, Inherited = true)] + public sealed class PureAttribute: Microsoft.Contracts.AttributeWithContext{ + } + /*Diego's Attributes */ + [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)] + public sealed class OnceAttribute : AttributeWithContext + { + } + [AttributeUsage(SpecTargets.Code,AllowMultiple=false, Inherited = true)] + public sealed class WriteConfinedAttribute: AttributeWithContext{ + } + [AttributeUsage(SpecTargets.Code,AllowMultiple=false, Inherited = true)] + public sealed class GlobalWriteAttribute: AttributeWithContext{ + public bool Value; + GlobalWriteAttribute() + { + Value = true; + } + GlobalWriteAttribute(bool value) + { + this.Value = value; + } + + } + [AttributeUsage(SpecTargets.Code,AllowMultiple=false, Inherited = true)] + public sealed class GlobalReadAttribute: AttributeWithContext{ + public bool Value; + GlobalReadAttribute() + { + Value = true; + } + GlobalReadAttribute(bool value) + { + this.Value = value; + } + + } + [AttributeUsage(SpecTargets.Code, AllowMultiple = false, Inherited = true)] + public sealed class GlobalAccessAttribute : AttributeWithContext + { + public bool Value; + GlobalAccessAttribute() + { + Value = true; + } + GlobalAccessAttribute(bool value) + { + this.Value = value; + } + + } + [AttributeUsage(SpecTargets.Code | AttributeTargets.ReturnValue | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = true, Inherited = true)] + public sealed class FreshAttribute: AttributeWithContext{ + } + [AttributeUsage(SpecTargets.Code | AttributeTargets.Parameter, AllowMultiple = true, Inherited = true)] + public sealed class EscapesAttribute: AttributeWithContext{ + public bool Value; + public bool Owned; + public EscapesAttribute() + { + Value = false; + Owned = false; + } + public EscapesAttribute(bool value) + { + this.Value = value; + this.Owned = true; + } + public EscapesAttribute(bool value, bool own) { + this.Value = value; + this.Owned = own; + } + } + /**/ + + [AttributeUsage(SpecTargets.Code,AllowMultiple=false, Inherited = true)] + public sealed class StateIndependentAttribute: AttributeWithContext{ + } + [AttributeUsage(AttributeTargets.ReturnValue|AttributeTargets.Field|AttributeTargets.Parameter,AllowMultiple=true, Inherited = false)] + public sealed class NotNullAttribute: AttributeWithContext{ + } + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)] + public sealed class DelayedAttribute : AttributeWithContext { + } + [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false)] + public sealed class NotDelayedAttribute : AttributeWithContext { + } + + [AttributeUsage(SpecTargets.Containers, AllowMultiple = false, Inherited = false)] + public sealed class NotNullGenericArgumentsAttribute : AttributeWithContext { + public string PositionOfNotNulls; + public NotNullGenericArgumentsAttribute(string positionOfNotNulls) { + PositionOfNotNulls = positionOfNotNulls; + } + } + [AttributeUsage(SpecTargets.CodeFieldsAndTheirContainers, AllowMultiple = false, Inherited = false)] + public sealed class EncodedTypeSpecAttribute : AttributeWithContext { + public int token; + public EncodedTypeSpecAttribute(int token) { + this.token = token; + } + } + [AttributeUsage(AttributeTargets.Field)] + public sealed class StrictReadonlyAttribute: Attribute{ + } + [AttributeUsage(SpecTargets.CodeFieldsAndTheirContainers,AllowMultiple=false, Inherited = true)] + public sealed class ModelAttribute: AttributeWithContext{ + } + [AttributeUsage(SpecTargets.CodeFieldsAndTheirContainers,AllowMultiple=false, Inherited = true)] + public sealed class SpecPublicAttribute: AttributeWithContext{ + } + [AttributeUsage(SpecTargets.CodeFieldsAndTheirContainers,AllowMultiple=false, Inherited = true)] + public sealed class SpecProtectedAttribute: AttributeWithContext{ + } + [AttributeUsage(SpecTargets.CodeFieldsAndTheirContainers,AllowMultiple=false, Inherited = true)] + public sealed class SpecInternalAttribute: AttributeWithContext{ + } + + /// + /// Used to mark the beginning of contracts so that static verification can recognize them + /// (either to ignore them or to treat them specially) + /// + public sealed class ContractMarkers { + public static void BeginRequires() { } + public static void EndRequires() { } + } + [AttributeUsage(SpecTargets.Code,AllowMultiple=true, Inherited = true)] + public sealed class RequiresAttribute: AttributeWithContext{ + public string Requires; + public RequiresAttribute(string _requires){ + Requires = _requires; + } + public RequiresAttribute(string _requires, string filename, int startline, int startcol, int endline, int endcol) + : base(filename,startline,startcol,endline,endcol){ + Requires = _requires; + } + public RequiresAttribute(string _requires, string filename, int startline, int startcol, int endline, int endcol, string sourceText) + : base(filename,startline,startcol,endline,endcol,sourceText){ + Requires = _requires; + } + } + [AttributeUsage(SpecTargets.Code,AllowMultiple=true, Inherited = true)] + public sealed class EnsuresAttribute: AttributeWithContext{ + public string Ensures; + public EnsuresAttribute(string _ensures) { + Ensures = _ensures; + } + public EnsuresAttribute(string _ensures, string filename, int startline, int startcol, int endline, int endcol) + : base(filename,startline,startcol,endline,endcol){ + Ensures = _ensures; + } + public EnsuresAttribute(string _ensures, string filename, int startline, int startcol, int endline, int endcol, string sourceText) + : base(filename,startline,startcol,endline,endcol,sourceText){ + Ensures = _ensures; + } +} + [AttributeUsage(SpecTargets.Code,AllowMultiple=true, Inherited = true)] + public sealed class ModifiesAttribute: AttributeWithContext{ + public string Modifies; + public ModifiesAttribute(string _modifies) { + Modifies = _modifies; + } + public ModifiesAttribute(string _modifies, string filename, int startline, int startcol, int endline, int endcol) + : base(filename,startline,startcol,endline,endcol){ + Modifies = _modifies; + } + public ModifiesAttribute(string _modifies, string filename, int startline, int startcol, int endline, int endcol, string sourceText) + : base(filename,startline,startcol,endline,endcol,sourceText){ + Modifies = _modifies; + } + } + [AttributeUsage(SpecTargets.Code, AllowMultiple = false, Inherited = false)] + public sealed class HasWitnessAttribute : AttributeWithContext { + } + [AttributeUsage(SpecTargets.Code, AllowMultiple = false, Inherited = false)] + public sealed class InferredReturnValueAttribute : AttributeWithContext { + public string Value; + public InferredReturnValueAttribute(string value) { + this.Value = value; + } + } + [AttributeUsage(SpecTargets.Code,AllowMultiple=true, Inherited = true)] + public sealed class ThrowsAttribute: AttributeWithContext{ + public string Throws; + public ThrowsAttribute(string _throws) { + Throws = _throws; + } + public ThrowsAttribute(string _throws, string filename, int startline, int startcol, int endline, int endcol) + : base(filename,startline,startcol,endline,endcol){ + Throws = _throws; + } + public ThrowsAttribute(string _throws, string filename, int startline, int startcol, int endline, int endcol, string sourceText) + : base(filename,startline,startcol,endline,endcol,sourceText){ + Throws = _throws; + } + } + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property,AllowMultiple=true, Inherited = false)] + public sealed class DoesAttribute: AttributeWithContext{ + public string Does; + public DoesAttribute(string _does) { + Does = _does; + } + public DoesAttribute(string _does, string filename, int startline, int startcol, int endline, int endcol) + : base(filename,startline,startcol,endline,endcol){ + Does = _does; + } + public DoesAttribute(string _does, string filename, int startline, int startcol, int endline, int endcol, string sourceText) + : base(filename,startline,startcol,endline,endcol,sourceText){ + Does = _does; + } + } + + + + + [AttributeUsage(SpecTargets.Containers,AllowMultiple=true, Inherited = true)] + public sealed class InvariantAttribute: AttributeWithContext{ + public string Invariant; + public InvariantAttribute(string _invariant) { + Invariant = _invariant; + } + public InvariantAttribute(string _invariant, string filename, int startline, int startcol, int endline, int endcol) { + Invariant = _invariant; + Filename = filename; + StartLine = startline; + StartColumn = startcol; + EndLine = endline; + EndColumn = endcol; + } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)] + public sealed class ShadowsAssemblyAttribute : System.Attribute{ + //TODO: we really want a single argument that provides a complete name and then construct an AssemblyReference from it. + //TODO: make the Spec# compiler aware of this attribute so that it can check that the argument really is a strong name. + public string PublicKey; + public string Version; + public string Name; + public ShadowsAssemblyAttribute(string publicKey, string version, string name) {PublicKey=publicKey; Version=version; Name=name;} + } + #endregion Attributes +} + +#if CCINamespace +namespace Microsoft.Cci.TypeExtensions { +#else +namespace System.Compiler.TypeExtensions { +#endif +} + +#if CCINamespace +namespace Microsoft.Cci { +#else +namespace System.Compiler { +#endif + /// + /// This attribute marks a template parameter T as being instantiable only with unmanaged structs + /// thereby allowing types such as T* in the generic code. + /// It needs to be present under Everett and Whidbey, since MSIL 2.0 does not have a way to encode + /// this AFAIK. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct)] + public sealed class UnmanagedStructTemplateParameterAttribute : System.Attribute + { + } + +} + +namespace Microsoft.Contracts{ +#if CCINamespace + using Microsoft.Cci; +#endif + using System.Collections; + using System.Threading; + + /// + /// Called by a object to initialize its guard sets. + /// + /// + /// This delegate should not call any methods other than + /// , + /// , + /// , + /// , + /// , and + /// . + /// + public delegate void InitGuardSetsDelegate(); + // TODO: Remove once the LKG compiler uses CheckInvariantDelegate. + public delegate bool InvariantHoldsDelegate(); + /// + /// Called by a object to check that its invariant holds, when it ends writing. + /// + /// If true and the invariant does not hold, throws an exception. If false and the invariant does not hold, returns false. + public delegate bool CheckInvariantDelegate(bool throwException); + /// + /// Called by the and methods + /// to decide whether to return or to wait. + /// + public delegate bool ThreadConditionDelegate(); + /// + /// Called by the delegate returned by the + /// method. + /// + public delegate void GuardThreadStart(); + /// + /// Delegate that grants access to the frame guards of the objects of a particular type. + /// + /// Object whose frame guard to return + /// The frame guard of + [return:NotNull] + public delegate Guard FrameGuardGetter(object o); + /// + /// Thrown by a object whenever a race condition, an ownership violation, or an invariant violation is + /// detected. + /// + public class GuardException : Exception { + public GuardException() {} + public GuardException(string message) : base(message) {} + } + + /// + /// Indicates the capability of a thread with respect to a guard (and the resource it protects). + /// + public enum ThreadCapability{ + /// + /// The thread cannot access the resource. + /// + None, + /// + /// The thread can read the resource. + /// + Readable, + /// + /// The thread can read and write the resource. + /// + Writable + } + /// + /// Indicates the activity being performed by a thread on a guard (and the resource it protects). + /// + public enum ThreadActivity{ + /// + /// The thread is not acting on the resource. + /// + None, + /// + /// The thread is reading the resource. + /// + Reading, + /// + /// The thread is writing the resource. + /// + Writing + } + /// + /// Indicates how a thread shares a guard (and the resource it protects). + /// + public enum SharingMode{ + /// + /// The thread does not share the resource. + /// + /// + /// Either the object itself is unshared, or the object is shared but not with the thread. + /// + Unshared, + /// + /// The thread shares the lock-protected resource. + /// + LockProtected, + /// + /// The thread shares the immutable resource. + /// + Immutable + } + /// + /// Helps guard an object or other resource against race conditions, enforce ownership, and enforce invariants. + /// + public sealed class Guard { + sealed class Commitment { + private Guard outer; + public Guard owner; + + internal Commitment(Guard outer, Guard owner){ + this.outer = outer; + this.owner = owner; + } + + public void Release(){ + LocalData locals = outer.Locals; + outer.owner = null; + locals.Capability = ThreadCapability.Writable; + outer = null; + } + } + sealed class RepFrame{ + public Guard/*!*/ frame; + public Commitment commitment; + + public RepFrame(Guard/*!*/ frame) { + this.frame = frame; + //^ base(); + } + + public void Commit(Guard/*!*/ owner) { + commitment = frame.Commit(owner); + } + + public void Release(){ + commitment.Release(); + commitment = null; + } + } + + sealed class LocalData { + public SharingMode SharingMode; + public ThreadCapability Capability; + public ThreadActivity Activity; + public override string ToString() { + return "<" + this.SharingMode.ToString() + "," + this.Capability.ToString() + "," + this.Activity.ToString() + ">"; + } + + } + + sealed class Scheduler { + bool writerActive; + int activeReaderCount; + QueueEntry nextWriter; + /// + /// The queue of entries that will be re-evaluated after the next ReleaseForWriting. + /// + Queue queue = new Queue(); + + // invariant writerActive ==> (activeReaderCount == 0 && nextWriter == null); + // invariant (!writerActive && nextWriter != null) ==> activeReaderCount != 0; + // The following invariants assume that IsEnabled changes only when writerActive. + // invariant nextWriter != null ==> nextWriter.IsEnabled; + // invariant (!writerActive && nextWriter == null) ==> forall(QueueEntry e in queue; !e.IsReader ==> !e.IsEnabled); + + sealed class QueueEntry { + public readonly bool IsReader; + readonly ThreadConditionDelegate condition; + bool runnable; + + public QueueEntry(bool reader, ThreadConditionDelegate condition){ + this.IsReader = reader; + this.condition = condition; + } + + public bool IsEnabled { + get { + return condition == null || condition(); + } + } + + public void Run(){ + lock (this){ + runnable = true; + Monitor.Pulse(this); + } + } + + public void Wait(){ + lock (this){ + while (!runnable) + Monitor.Wait(this); + } + } + } + + public void AcquireForReading(ThreadConditionDelegate condition){ + QueueEntry entry = new QueueEntry(true, condition); + lock (this){ + if (writerActive || nextWriter != null || !entry.IsEnabled){ + // This reader will get a chance after the next ReleaseForWriting + queue.Enqueue(entry); + } else { + activeReaderCount++; + entry.Run(); + } + } + entry.Wait(); + } + + public void AcquireForWriting(ThreadConditionDelegate condition){ + QueueEntry entry = new QueueEntry(false, condition); + lock (this){ + if (writerActive || nextWriter != null || !entry.IsEnabled){ + // This writer will get a chance after the next ReleaseForWriting + queue.Enqueue(entry); + } else if (activeReaderCount == 0){ + writerActive = true; + entry.Run(); + } else { + nextWriter = entry; + } + } + entry.Wait(); + } + + public void ReleaseForReading(){ + lock (this){ + activeReaderCount--; + if (activeReaderCount == 0 && nextWriter != null){ + nextWriter.Run(); + writerActive = true; + nextWriter = null; + } + } + } + + public void ReleaseForWriting(){ + lock (this){ + writerActive = false; + Queue newQueue = new Queue(); + while (queue.Count > 0){ + QueueEntry entry = (QueueEntry/*!*/)queue.Dequeue(); + if (entry.IsReader && entry.IsEnabled){ + activeReaderCount++; + entry.Run(); + } else if (!entry.IsReader && nextWriter == null && entry.IsEnabled){ + nextWriter = entry; + } else { + newQueue.Enqueue(entry); + } + } + queue = newQueue; + if (activeReaderCount == 0 && nextWriter != null){ + nextWriter.Run(); + writerActive = true; + nextWriter = null; + } + } + } + } + + readonly InitGuardSetsDelegate initFrameSets; + readonly CheckInvariantDelegate checkInvariant; + Scheduler scheduler; + readonly Hashtable/*!*/ repFrames = new Hashtable(); + readonly Hashtable/*!*/ sharedFrames = new Hashtable(); + /// + /// Thread-local data indicating the sharing mode, the capability and the activity of the current thread with respect + /// to this object. + /// + readonly LocalDataStoreSlot localData = Thread.AllocateDataSlot(); + object owner; + + class InvariantHoldsAdaptor{ + InvariantHoldsDelegate invariantHolds; + + public InvariantHoldsAdaptor(InvariantHoldsDelegate invariantHolds){ + this.invariantHolds = invariantHolds; + } + + public bool CheckInvariant(bool throwException){ + if (!invariantHolds()) + if (throwException) + throw new Microsoft.Contracts.ObjectInvariantException(); + else + return false; + return true; + } + } + + // TODO: Remove once the LKG uses the other constructor. + public Guard([Delayed] InitGuardSetsDelegate initGuardSets, [Delayed] InvariantHoldsDelegate invariantHolds) + : this(initGuardSets, new CheckInvariantDelegate(new InvariantHoldsAdaptor(invariantHolds).CheckInvariant)) + { + } + + /// + /// Creates a new guard object. + /// + /// + /// Initially, the object is unshared; + /// the current thread can write and is writing the guard; + /// and no other thread can access the guard. + /// + /// A delegate that is called to initialize the guard's guard sets. + /// A delegate that is called to check the guard's invariant. + public Guard([Delayed] InitGuardSetsDelegate initGuardSets, [Delayed] CheckInvariantDelegate checkInvariant){ + this.initFrameSets = initGuardSets; + this.checkInvariant = checkInvariant; + LocalData locals = Locals; + locals.Capability = ThreadCapability.Writable; + locals.Activity = ThreadActivity.Writing; + } + + LocalData/*!*/ Locals { + get { + LocalData locals; + object o = Thread.GetData(localData); + if (o == null) { + locals = new LocalData(); + Thread.SetData(localData, locals); + } else + locals = (LocalData) o; + return locals; + } + } + + /// + /// Returns true if the current thread can read the resource; otherwise, returns false. + /// + /// + /// Equivalent to Capability != ThreadCapability.None. + /// + public bool CanRead { + get { + return Locals.Capability != ThreadCapability.None; + } + } + + /// + /// Returns true if the current thread can write the resource; otherwise, returns false. + /// + /// + /// Equivalent to Capability == ThreadCapability.Writable. + /// + public bool CanWrite { + get { + return Locals.Capability == ThreadCapability.Writable; + } + } + + /// + /// Returns true if the current thread is reading or writing the resource; otherwise, returns false. + /// + /// + /// Equivalent to Activity != ThreadActivity.None. + /// + public bool IsActive { + get { + return Locals.Activity != ThreadActivity.None; + } + } + + public void CheckIsReading(){ + if (Locals.Activity == ThreadActivity.None) + throw new GuardException("Thread is not reading object."); + } + + public void CheckIsWriting(){ + if (Locals.Activity != ThreadActivity.Writing) + throw new GuardException("Thread is not writing object."); + } + + /// + /// Returns the current thread's sharing mode for the resource. + /// + public SharingMode SharingMode { + get { + return Locals.SharingMode; + } + } + + /// + /// Returns the current thread's capability with respect to the resource. + /// + public ThreadCapability Capability { + get { + return Locals.Capability; + } + } + + /// + /// Returns the current thread's activity with respect to the resource. + /// + public ThreadActivity Activity { + get { + return Locals.Activity; + } + } + + /// + /// Starts reading the resource. + /// + public void StartReading(){ + LocalData locals = Locals; + if (locals.Capability == ThreadCapability.None) + throw new GuardException("Object is not readable by current thread."); + if (locals.Activity != ThreadActivity.None) + throw new GuardException("Thread is already reading or writing object."); + RegisterSharedFrames(); + locals.Activity = ThreadActivity.Reading; + foreach (RepFrame repFrame in repFrames.Values) + repFrame.frame.Locals.Capability = ThreadCapability.Readable; + } + + /// + /// Starts reading the resource; starts reading any transitive owners as necessary. + /// + /// The furthest transitive owner that was not yet reading. + [return:Microsoft.Contracts.NotNull] + public Guard StartReadingTransitively(){ + Guard rootFrame; + Commitment commitment = owner as Commitment; + if (commitment != null) + rootFrame = commitment.owner.StartReadingTransitively(); + else + rootFrame = this; + StartReading(); + return rootFrame; + } + + /// + /// Ends reading the resource. + /// + public void EndReading(){ + EndReading(false); + } + + /// + /// Ends reading the resource; ends reading any transitive owned resources as necessary. + /// + public void EndReadingTransitively(){ + EndReading(true); + } + + void EndReading(bool transitively){ + LocalData locals = Locals; + if (locals.Activity != ThreadActivity.Reading) + throw new GuardException("Thread is not reading object."); + foreach (RepFrame repFrame in repFrames.Values) + if (repFrame.frame.Locals.Activity == ThreadActivity.Reading) + if (transitively) + repFrame.frame.EndReading(true); + else + throw new GuardException("Thread is still reading an owned object."); + foreach (RepFrame repFrame in repFrames.Values) + repFrame.frame.Locals.Capability = ThreadCapability.None; + locals.Activity = ThreadActivity.None; + } + + /// + /// Ends writing the resource. + /// + [Delayed] // This [Delayed] tag isn't actually verifiable, but we believe it does hold here. + public void EndWriting(){ + EndWriting(false, false); + } + + [Pure] + public bool CanStartWriting{ + get { + LocalData locals = Locals; + if (locals.Capability != ThreadCapability.Writable) + return false; + if (locals.Activity != ThreadActivity.None) + return false; + return true; + } + } + [Pure] + public bool CheckCanStartWriting(){ + LocalData locals = Locals; + if (locals.Capability != ThreadCapability.Writable) + throw new GuardException("Object is not writable by current thread."); + if (locals.Activity != ThreadActivity.None) + throw new GuardException("Thread is already reading or writing the object."); + return true; + } + [Pure] + public bool CanStartWritingTransitively{ + get { + if (this.CanStartWriting) + return true; + Commitment commitment = owner as Commitment; + if (commitment != null) + return commitment.owner.CanStartWritingTransitively; + else + return false; + } + } + + #region Unpack/Pack methods + /// + /// Starts writing the resource; starts writing any transitive owners as necessary. + /// + /// The furthest transitive owner that was not yet writing. + /// + [return:Microsoft.Contracts.NotNull] + public Guard/*!*/ StartWritingTransitively(){ + Guard rootFrame; + Commitment commitment = owner as Commitment; + if (commitment != null) + rootFrame = commitment.owner.StartWritingTransitively(); + else + rootFrame = this; + StartWriting(); + return rootFrame; + } + /// + /// Ends writing the resource; ends writing any transitive reps as necessary. + /// + public void EndWritingTransitively() { + EndWriting(true, false); + } + + /// + /// Starts writing the resource. + /// + public void StartWriting() { + // Console.WriteLine("StartWriting: " + locals.ToString()); + CheckCanStartWriting(); + RegisterSharedFrames(); + Locals.Activity = ThreadActivity.Writing; + foreach (RepFrame repFrame in repFrames.Values) + repFrame.Release(); + repFrames.Clear(); + sharedFrames.Clear(); + } + void EndWriting(bool transitively, bool reflexively) { + LocalData locals = Locals; + if (locals.Activity != ThreadActivity.Writing) + if (reflexively) + return; + else + throw new GuardException("Thread is not writing object."); + repFrames.Clear(); + sharedFrames.Clear(); + initFrameSets(); + if (transitively) + foreach (RepFrame repFrame in repFrames.Values) + repFrame.frame.EndWriting(true, true); + checkInvariant(true); + foreach (RepFrame repFrame in repFrames.Values) + repFrame.Commit(this); + locals.Activity = ThreadActivity.None; + } + + public static void StartWritingFrame([NotNull] object o, [NotNull] Type t) { } + public static void EndWritingFrame([NotNull] object o, [NotNull] Type t) { } + + // The next two pairs of methods are for use when an object is being exposed at + // one particular type, *without* being unpacked at all subtypes (which is how + // the "normal" unpacking is done). + + // The first pair, WritingAtNop, is for those occurrences where nothing + // should be done at runtime, but downstream tools may still need to know + // that the object is being unpacked. + public static void StartWritingAtNop([NotNull] object o, [NotNull] Type t) { } + public static void EndWritingAtNop([NotNull] object o, [NotNull] Type t) { } + + // The second pair, WritingAtTransitively, behaves at runtime just as WritingTransitively, + // but it knows to not check the invariant for any subtype below t. + [return: Microsoft.Contracts.NotNull] + public Guard/*!*/ StartWritingAtTransitively([NotNull] Type t) { return this.StartWritingTransitively(); } + public void EndWritingAtTransitively([NotNull] Type t) { this.EndWriting(true, false); } + #endregion Unpack/Pack methods + + /// + /// Commits the resource. + /// + /// The new owner. + /// A new commitment. + /// + /// A thread that calls this method afterwards no longer has access to the object. + /// Any thread can gain access to the object by calling the method on the new commitment. + /// + Commitment Commit(Guard/*!*/ owner) { + LocalData locals = Locals; + if (locals.Capability != ThreadCapability.Writable) + throw new GuardException("Object is not writable by current thread."); + if (locals.SharingMode != SharingMode.Unshared) + throw new GuardException("Object is shared."); + if (locals.Activity != ThreadActivity.None) + throw new GuardException("Thread is still reading or writing object."); + Commitment commitment = new Commitment(this, owner); + this.owner = commitment; + locals.Capability = ThreadCapability.None; + return commitment; + } + + /// + /// Adds a resource to this guard's set of reps. + /// + /// The resource being added. + /// + /// When a guard ends writing, it gains ownership of its reps. + /// + public void AddRep(Guard/*!*/ rep) { + LocalData locals = Locals; + if (locals.Activity != ThreadActivity.Writing) + throw new GuardException("Thread is not writing the object."); + repFrames[rep] = new RepFrame(rep); + } + + /// + /// Adds an object to this guard's set of reps. + /// + /// The object being added. + /// + /// If is not a guarded object, the method does nothing. + /// + public void AddRepObject(object repObject){ + if (repObject == null) return; + Guard g = Guard.GetObjectGuard(repObject); + if (g != null) + AddRep(g); + } + + /// + /// Adds a frame to this guard's set of reps. + /// + /// + /// If is not a guarded object, the method does nothing. + /// + public void AddRepFrame(object o, Type t){ + Guard g = Guard.GetFrameGuard(o, t); + if (g != null) + AddRep(g); + } + + /// + /// Turns this unshared resource into a lock-protected resource. + /// + /// + /// When this method returns, only the current thread shares the resource. + /// Share the resource with other threads using + /// the method + /// or + /// the method. + /// + public void ShareLockProtected(){ + Share(SharingMode.LockProtected); + } + + /// + /// Turns the unshared resource into an immutable resource. + /// + /// + /// When this method returns, only the current thread shares the resource. + /// Share the resource with other threads using + /// the method + /// or the method. + /// + public void ShareImmutable(){ + Share(SharingMode.Immutable); + } + + void Share(SharingMode mode){ + LocalData locals = Locals; +// Console.WriteLine("Share: " + locals.ToString() + ", asked to share as: " + mode.ToString()); + if (mode != SharingMode.Unshared && locals.SharingMode == mode) + return; + if (locals.Capability != ThreadCapability.Writable) + throw new GuardException("Object is not writable by the current thread."); + if (locals.Activity != ThreadActivity.None) + throw new GuardException("Thread is still reading or writing the object."); + if (locals.SharingMode != SharingMode.Unshared) + throw new GuardException("Object is already shared."); + locals.SharingMode = mode; + switch (mode){ + case SharingMode.LockProtected: + locals.Capability = ThreadCapability.None; + scheduler = new Scheduler(); + break; + case SharingMode.Unshared: + locals.Capability = ThreadCapability.None; + break; + default: + // assert mode == SharingMode.Immutable; + locals.Capability = ThreadCapability.Readable; + break; + } +// Console.WriteLine("Share: local state now: " + locals.ToString()); + } + + /// + /// Acquires exclusive access to this resource. + /// + /// If not null, indicates when the thread can acquire the resource. + public void AcquireForWriting(ThreadConditionDelegate condition){ + LocalData locals = Locals; + if (locals.SharingMode != SharingMode.LockProtected) + throw new GuardException("Object is not lock-protected."); + if (locals.Capability != ThreadCapability.None) + throw new GuardException("Thread has already acquired the object."); + locals.Capability = ThreadCapability.Readable; // Allow condition code to read the object. + scheduler.AcquireForWriting(condition); + locals.Capability = ThreadCapability.Writable; + } + + /// + /// Relinquishes exclusive access to this resource. + /// + public void ReleaseForWriting(){ + LocalData locals = Locals; + if (locals.SharingMode != SharingMode.LockProtected) + throw new GuardException("Object is not lock-protected."); + if (locals.Capability != ThreadCapability.Writable) + throw new GuardException("Thread has not acquired the object for writing."); + if (locals.Activity != ThreadActivity.None) + throw new GuardException("Thread is still reading or writing the object."); + locals.Capability = ThreadCapability.Readable; // Allow condition code to read the object. + scheduler.ReleaseForWriting(); + locals.Capability = ThreadCapability.None; + } + + /// + /// Acquires read access to this resource. + /// + /// If not null, indicates when the thread can acquire the resource. + public void AcquireForReading(ThreadConditionDelegate condition){ + LocalData locals = Locals; + if (locals.SharingMode != SharingMode.LockProtected) + throw new GuardException("Object is not lock-protected."); + if (locals.Capability != ThreadCapability.None) + throw new GuardException("Thread has already acquired the object."); + locals.Capability = ThreadCapability.Readable; // Allow condition code to read the object. + scheduler.AcquireForReading(condition); + } + + /// + /// Relinquishes read access to this resource. + /// + public void ReleaseForReading(){ + LocalData locals = Locals; + if (locals.SharingMode != SharingMode.LockProtected) + throw new GuardException("Object is not lock-protected."); + if (locals.Capability != ThreadCapability.Readable) + throw new GuardException("Thread has not acquired the object for reading."); + if (locals.Activity != ThreadActivity.None) + throw new GuardException("Thread is still reading the object."); + scheduler.ReleaseForReading(); + locals.Capability = ThreadCapability.None; + } + + /// + /// Adds a resource to this guard's set of lock-protected certificates. + /// + /// The resource being added. + /// + /// When a thread subsequently starts reading or writing this guard, + /// the resource is shared with the thread. + /// + public void AddLockProtectedCertificate(Guard/*!*/ guard) { + AddSharedFrame(guard, SharingMode.LockProtected); + } + + /// + /// Adds a resource to this guard's set of immutable certificates. + /// + /// The resource being added. + /// + /// When a thread subsequently starts reading or writing this guard, + /// the resource is shared with the thread. + /// + public void AddImmutableCertificate(Guard/*!*/ guard) { + AddSharedFrame(guard, SharingMode.Immutable); + } + + void AddSharedFrame(Guard/*!*/ frame, SharingMode mode) { + frame.Share(mode); + LocalData locals = Locals; + if (locals.Activity != ThreadActivity.Writing) + throw new GuardException("Thread is not writing the object."); + sharedFrames[frame] = mode; + } + + /// + /// Stores a certificate for a lock-protected object. + /// Does nothing if the object is not guarded. + /// + /// The object for which a certificate is stored. + /// + public void AddObjectLockProtectedCertificate(object sharedObject){ + if (sharedObject == null) return; + Guard g = Guard.GetObjectGuard(sharedObject); + if (g != null) + AddLockProtectedCertificate(g); + } + + /// + /// Stores a certificate for an immutable object. + /// Does nothing if the object is not guarded. + /// + /// The object for which a certificate is stored. + /// + public void AddObjectImmutableCertificate(object sharedObject){ + if (sharedObject == null) return; + Guard g = Guard.GetObjectGuard(sharedObject); + if (g != null) + AddImmutableCertificate(g); + } + + void RegisterSharingMode(SharingMode mode){ + LocalData sharedFrameLocals = Locals; + sharedFrameLocals.SharingMode = mode; + if (mode == SharingMode.Immutable) + sharedFrameLocals.Capability = ThreadCapability.Readable; + else if (mode == SharingMode.Unshared) + sharedFrameLocals.Capability = ThreadCapability.Writable; + } + + void RegisterSharedFrames(){ + foreach (DictionaryEntry entry in sharedFrames){ + Guard sharedFrame = (Guard)entry.Key; + //^ assume sharedFrame != null; + object value = entry.Value; + if (value == null) continue; + SharingMode mode = (SharingMode)value; + sharedFrame.RegisterSharingMode(mode); + } + } + + class ThreadStartTarget{ + Guard frame; + GuardThreadStart threadStart; + SharingMode sharingMode; + + public ThreadStartTarget(Guard frame, GuardThreadStart threadStart, SharingMode sharingMode){ + this.frame = frame; + this.threadStart = threadStart; + this.sharingMode = sharingMode; + } + + public void Start(){ + if (this.frame == null){ + // this makes Start() non-re-entrant + throw new GuardException("Start() method can be called at most once."); + } + frame.RegisterSharingMode(sharingMode); + this.frame = null; + threadStart(); + } + } + + ThreadStart CreateThreadStart(GuardThreadStart threadStart, SharingMode sharingMode){ + Share(sharingMode); + return new ThreadStart(new ThreadStartTarget(this, threadStart, sharingMode).Start); + } + + /// + /// Use this method to share the resource with a new thread. + /// + /// A method to be executed by the new sharing thread. + /// A delegate that shares the resource with the thread calling it (which is typically a newly started thread) + /// and then calls . + public ThreadStart CreateThreadStartForImmutable(GuardThreadStart threadStart){ + return CreateThreadStart(threadStart, SharingMode.Immutable); + } + + /// + /// Use this method to share the resource with a new thread. + /// + /// A method to be executed by the new sharing thread. + /// A delegate that shares the resource with the thread calling it (which is typically a newly started thread) + /// and then calls . + public ThreadStart CreateThreadStartForLockProtected(GuardThreadStart threadStart){ + return CreateThreadStart(threadStart, SharingMode.LockProtected); + } + /// + /// Use this method to transfer the resource to a new thread. + /// + /// A method to be executed by the new standalone thread. + /// A delegate that transfers the resource to the thread calling it (which is typically a newly started thread) + /// and then calls . + public ThreadStart CreateThreadStartForOwn(GuardThreadStart threadStart){ +// Console.WriteLine("CreateThreadStartForOwn: " + threadStart.ToString()); + return CreateThreadStart(threadStart, SharingMode.Unshared); + } + + /// + /// Holds the frame guard getter delegates of the guarded classes loaded into the AppDomain. + /// + /// + /// Lock this object to access it. + /// + static readonly Hashtable/*!*/ frameGuardGetters = new Hashtable(); + + public static void RegisterGuardedClass(Type t, FrameGuardGetter getter) { + if (getter == null) + throw new ArgumentNullException("getter"); +#if !SINGULARITY + if (System.Reflection.Assembly.GetCallingAssembly() != t.Assembly) + throw new ArgumentException("An assembly can register only its own types."); +#endif + lock (frameGuardGetters) { + frameGuardGetters.Add(t, getter); + } + } + + /// + /// Returns the frame guard of object at type , + /// or null if is not a guarded class. + /// + public static Guard GetFrameGuard([NotNull] object o, [NotNull] Type t){ + FrameGuardGetter getter; + // The Hashtable documentation allows one writer and multiple readers to access a non-synchronized Hashtable concurrently. + // See the documentation for the System.Collections.Hashtable.Synchronized method. + getter = (FrameGuardGetter)frameGuardGetters[t]; + return getter == null ? null : getter(o); + } + + /// + /// Returns the guard of object , or + /// null if is not guarded. + /// + public static Guard GetObjectGuard([NotNull] object o){ + Type t = o.GetType(); + while (t != null){ + Guard guard = GetFrameGuard(o, t); + if (guard != null) + return guard; + t = t.BaseType; + } + return null; + } + + [Pure] + public static bool IsConsistent([NotNull] object o){ + Guard objectGuard = GetObjectGuard(o); + if (objectGuard != null){ + return objectGuard.CanStartWriting; + }else{ + return true; // Objects that do not have an invariant are always consistent. + } + } + + [Pure] + public static bool IsPeerConsistent([NotNull] object o){ + return IsConsistent(o); + } + + [Pure] + public bool IsExposable{ + get{ + return this.CanStartWritingTransitively; + } + } + + [Pure] + public static bool FrameIsExposable([NotNull] object o, [NotNull] Type t){ + Guard guard = GetFrameGuard(o, t); + return guard == null ? true : guard.IsExposable; + } + + [Pure] + public static bool BaseIsConsistent([NotNull] object o, [NotNull] Type t){ + // System.Reflection does not seem to allow one to express a base call; this is a workaround. + return FrameIsExposable(o, t.BaseType); + } + + [Pure] + public bool IsExposed{ + get{ + return this.Activity == ThreadActivity.Writing; + } + } + + [Pure] + public static bool FrameIsExposed([NotNull] object o, [NotNull] Type t){ + Guard guard = GetFrameGuard(o, t); + return guard == null ? true : guard.IsExposed; + } + + [Pure] + public bool IsPrevalid{ + get{ + LocalData locals = Locals; + if (locals.Activity != ThreadActivity.Writing) + return false; + repFrames.Clear(); + sharedFrames.Clear(); + initFrameSets(); + foreach (RepFrame repFrame in repFrames.Values) + if (!repFrame.frame.IsExposable) + return false; + return true; + } + } + + [Pure] + public static bool FrameIsPrevalid([NotNull] object o, [NotNull] Type t){ + Guard guard = GetFrameGuard(o, t); + return guard == null ? true : guard.IsPrevalid; + } + + [Pure] + public bool IsValid{ + get{ + return this.IsPrevalid && this.checkInvariant(false); + } + } + + [Pure] + public static bool FrameIsValid([NotNull] object o, [NotNull] Type t){ + Guard guard = GetFrameGuard(o, t); + return guard == null ? true : guard.IsValid; + } + + public static void ShareLockProtected([NotNull] object o){ + Guard guard = GetObjectGuard(o); + if (guard != null) + guard.ShareLockProtected(); + } + + [Pure] + public static bool IsLockProtected(object o){ + Guard g = GetObjectGuard(o); + return g != null ? g.SharingMode == SharingMode.LockProtected : true; + } + + [Confined] + public static void ModifiesObject(object o){} + [Confined] + public static void ModifiesArray(object o) { } + [Pure] + public static void ModifiesPeers(object o) { } + [Pure] + public static void ModifiesNothing(object o) { } + + /// + /// In a method's ensures clause, indicates that was not allocated in the pre-state. + /// Note: this is a temporary measure that will be replaced with a more modular construct in the future. + /// + [Confined] + public static bool IsNew(object o) { return true; } + + /// + /// Used in the Boogie bytecode translator. + /// + public static void NoOp(){} + } +} + +namespace Microsoft.Contracts{ +#if CCINamespace + using Microsoft.Cci; +#endif + /// + /// Indicates that the field refers to a lock-protected object. + /// + /// + /// This attribute causes the Spec# compiler to emit a call to the + /// method in its implementation. + /// + [AttributeUsage(AttributeTargets.Field|AttributeTargets.Parameter)] + public class LockProtectedAttribute : Attribute {} + /// + /// Indicates that the method requires that the target is lock-protected. + /// + /// + /// This attribute causes the Spec# compiler's ThreadStart conversion feature + /// to share the lock-protected target object + /// with the newly started thread. + /// + [AttributeUsage(AttributeTargets.Method)] + public class RequiresLockProtectedAttribute : Attribute {} + /// + /// Indicates that the field refers to an immutable object. + /// + /// + /// This attribute causes the Spec# compiler to emit a call to the + /// method in its implementation. + /// + [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Field, AllowMultiple = false, Inherited = true)] + public sealed class ImmutableAttribute : Attribute { } + /// + /// Indicates that the method requires that the target is writeable. + /// + /// + /// This attribute causes the Spec# compiler's ThreadStart conversion feature to transfer the target object + /// to the newly started thread. + /// + [AttributeUsage(AttributeTargets.Method)] + public class RequiresCanWriteAttribute : Attribute {} + /// + /// Indicates that the method requires that the target is immutable. + /// + /// + /// This attribute causes the Spec# compiler's ThreadStart conversion feature to share the immutable target object + /// with the newly started thread. + /// + [AttributeUsage(AttributeTargets.Method)] + public class RequiresImmutableAttribute : Attribute {} + /// + /// Indicates that the method reads the target. + /// + /// + /// This attribute causes the Spec# compiler to enclose the method's body in a read (this) statement. + /// + [AttributeUsage(AttributeTargets.Method)] + public class ReaderAttribute : Attribute {} + /// + /// Indicates that the method writes the target. + /// + /// + /// This attribute causes the Spec# compiler to enclose the method's body in a write (this) statement. + /// + [AttributeUsage(AttributeTargets.Method)] + public class WriterAttribute : Attribute {} + [AttributeUsage(SpecTargets.Code,AllowMultiple=false, Inherited = true)] + public sealed class ConfinedAttribute: AttributeWithContext{ + } + [Obsolete("Use Microsoft.Contracts.NoDefaultContract instead")] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Constructor, AllowMultiple = true, Inherited = false)] + public sealed class NoDefaultActivityAttribute: AttributeWithContext{ + } + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Parameter,AllowMultiple=true, Inherited = false)] + public sealed class NoDefaultContractAttribute: AttributeWithContext{ + } + [Obsolete("Use Microsoft.Contracts.Rep or Microsoft.Contracts.Peer instead")] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class OwnedAttribute: Attribute{ + /// + /// True if the field is owned; false otherwise. + /// + public bool Value; + /// + /// True if the field is a peer field; false otherwise. + /// + public bool Peer; + public OwnedAttribute() { this.Value = true; this.Peer = false; } + public OwnedAttribute(bool value) { this.Value = value; this.Peer = false; } + public OwnedAttribute(string value) { + switch (value) { + case "peer": + this.Value = false; + this.Peer = true; + break; + case "this": + this.Value = true; + this.Peer = false; + break; + default: + System.Diagnostics.Debug.Assert(false); + break; + } + } + } + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class RepAttribute : Attribute { + } + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class PeerAttribute : Attribute { + } + /// + /// Fields of array-type or subtype of IEnumerable can be marked ElementsRep meaning that the elements of the + /// array or collection are rep of the array or collection. + /// + [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)] + public sealed class ElementsRepAttribute : AttributeWithContext { + } + /// + /// Fields of array-type or subtype of IEnumerable can be marked ElementsPeer meaning that the elements of the + /// array or collection are peer of the array or collection. + /// + [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)] + public sealed class ElementsPeerAttribute : AttributeWithContext { + } + /// + /// Methods of subtypes of IEnumerable can be marked Element meaning that the method returns an + /// element of the collection. + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class ElementAttribute : AttributeWithContext { + } + /// + /// Methods of subtypes of IEnumerable can be marked ElementCollection meaning that the method returns + /// a set of elements of the collection. + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class ElementCollectionAttribute : AttributeWithContext { + } + /// + /// Used to ensure acyclicity of method call chains in specifications of methods. + /// Indicates the maximum number of method calls before the call chain terminates. + /// E.g. zero if method's specification does not contain method calls. + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class RecursionTerminationAttribute : AttributeWithContext { + public int level; + public RecursionTerminationAttribute() { this.level = 0; } + public RecursionTerminationAttribute(int level) { this.level = level; } + } + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class NoReferenceComparisonAttribute : AttributeWithContext { + } + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class ResultNotNewlyAllocatedAttribute : AttributeWithContext { + } + /// + /// Indicates that a parameter, or the receiver parameter if applied to a method or constructor, starts in an unowned state and may cause the owner to change. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Constructor, AllowMultiple=false)] + public sealed class CapturedAttribute : Attribute{ + } + [AttributeUsage(AttributeTargets.Field, AllowMultiple = true, Inherited = false)] + public sealed class DependentAttribute : Attribute { + /// + /// The type that is allowed to have an invariant referring to the field + /// this attribute is attached to. + /// + public Type otherType; + public DependentAttribute(Type otherType) { this.otherType = otherType; } + } + public sealed class Owner{ + /// + /// As seen by the static program verifier: returns true iff c and d are owned by the same owner or + /// if they have no owner and belong to the same peer group. + /// Dynamically, this method always returns true. If you want the negation, you Different. + /// + /// + /// + /// + [Pure] + public static bool Same([NotNull] [Delayed] object c, [NotNull] object d) { return true; } + /// + /// As seen by the static program verifier: returns false iff c and d are owned by the same owner or + /// if they have no owner and belong to the same peer group. + /// Dynamically, this method always returns true. If you want the negation, you Same. + /// + /// + /// + /// + [Pure] + public static bool Different([NotNull] [Delayed] object c, [NotNull] object d) { return true; } + /// + /// As seen by the static program verifier: returns true iff subject has no owner. + /// Dynamically, this method always returns true. + /// + /// + /// + [Pure] + public static bool None([NotNull] [Delayed] object c) { return true; } + /// + /// As seen by the static program verifier: returns true iff subject is owned by (owner,frame). + /// Dynamically, this method always returns true. + /// + /// + /// + /// + /// + [Pure] + public static bool Is([NotNull] [Delayed] object subject, [NotNull] object owner, [NotNull] Type frame) { return true; } + /// + /// As seen by the static program verifier: returns true iff subject has no owner and is in + /// in peer group represented by itself. That is, returns true iff the subject's owner is + /// the same as it is in the post-state of a non-capturing constructor. + /// Dynamically, this method always returns true. + /// + /// + /// + [Pure] + public static bool New([NotNull] [Delayed] object c) { return true; } + /// + /// As seen by the static program verifier: Requires that subject initially has no owner, and sets the owner of + /// subject and any other objects in its peer group to (owner,frame). + /// That is: + /// requires Owner.None(subject); + /// ensures Owner.Is(subject, owner, frame); + /// Dynamically, this method is a no-op. + /// + /// + /// + /// + public static void Assign([NotNull] [Delayed] object subject, [NotNull] object owner, [NotNull] Type frame) { } + /// + /// As seen by the static program verifier: Requires that "subject" initially has no owner, and sets the owner of + /// "subject" and any other objects in its peer group to the same owner or peer group as "peer".. + /// That is: + /// requires Owner.None(subject); + /// ensures Owner.Same(subject, peer); + /// Dynamically, this method is a no-op. + /// + /// + /// + public static void AssignSame([NotNull] [Delayed] object subject, [NotNull] object peer) { } + } + public class ObjectInvariantException : Microsoft.Contracts.GuardException{ + public ObjectInvariantException() : base("Object invariant does not hold.") {} + } + /// + ///Instructs downstream tools to assume the correctness of this assembly, type or member without performing any verification. + /// Can use [SkipVerification(false)] to explicitly mark assembly, type or member as one to have verification performed on it. + /// Most specific element found (member, type, then assembly) takes precedence. + /// (That is useful if downstream tools allow a user to decide which polarity is the default, unmarked case.) + /// + /// + ///Apply this attribute to a type to apply to all members of the type, including nested types. + ///Apply this attribute to an assembly to skip verification of all types and members of the assembly. + /// Default is true, so [SkipVerification] is the same as [SkipVerification(true)]. + /// + [Obsolete("Use Microsoft.Contracts.Verify instead")] + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor)] + public sealed class SkipVerificationAttribute : Attribute { + public bool Value; + public SkipVerificationAttribute() { this.Value = true; } + public SkipVerificationAttribute(bool value) { this.Value = value; } + } + /// + ///Instructs downstream tools whether to assume the correctness of this assembly, type or member without performing any verification or not. + /// Can use [Verify(false)] to explicitly mark assembly, type or member as one to *not* have verification performed on it. + /// Most specific element found (member, type, then assembly) takes precedence. + /// (That is useful if downstream tools allow a user to decide which polarity is the default, unmarked case.) + /// + /// + ///Apply this attribute to a type to apply to all members of the type, including nested types. + ///Apply this attribute to an assembly to apply to all types and members of the assembly. + /// Apply this attribute to a property to apply to both the getter and setter. + ///Default is true, so [Verify] is the same as [Verify(true)]. + /// + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)] + public sealed class VerifyAttribute : Attribute { + public bool Value; + public VerifyAttribute() { this.Value = true; } + public VerifyAttribute(bool value) { this.Value = value; } + } + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue)] + public sealed class AdditiveAttribute : Attribute + { + public bool Value; + public AdditiveAttribute() { this.Value = true; } + public AdditiveAttribute(bool value) { this.Value = value; } + } + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.ReturnValue)] + public sealed class InsideAttribute : Attribute { + public bool Value; + public InsideAttribute() { this.Value = true; } + public InsideAttribute(bool value) { this.Value = value; } + } +} diff --git a/base/Interfaces/Bartok/ASCIIEncoding.csi b/base/Interfaces/Bartok/ASCIIEncoding.csi index 687e9d8..8d2d8b8 100644 --- a/base/Interfaces/Bartok/ASCIIEncoding.csi +++ b/base/Interfaces/Bartok/ASCIIEncoding.csi @@ -7,8 +7,8 @@ using System; using System.Runtime.CompilerServices; -namespace System.Text { - [RequiredByBartok] +namespace System.Text +{ public class ASCIIEncoding : Encoding { public ASCIIEncoding(); diff --git a/base/Interfaces/Bartok/ArrayHelper.csi b/base/Interfaces/Bartok/ArrayHelper.csi index aaa5b3c..c67dafd 100644 --- a/base/Interfaces/Bartok/ArrayHelper.csi +++ b/base/Interfaces/Bartok/ArrayHelper.csi @@ -1,12 +1,13 @@ // -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // using System.Collections; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; -namespace System { +namespace System +{ [RequiredByBartok] internal class ArrayHelper { diff --git a/base/Interfaces/Bartok/Assembly.csi b/base/Interfaces/Bartok/Assembly.csi index 751b647..8a8e6cb 100644 --- a/base/Interfaces/Bartok/Assembly.csi +++ b/base/Interfaces/Bartok/Assembly.csi @@ -4,7 +4,8 @@ // // ==--== using System.Runtime.CompilerServices; -namespace System.Reflection { +namespace System.Reflection +{ //| [RequiredByBartok] diff --git a/base/Interfaces/Bartok/Bartok.csproj b/base/Interfaces/Bartok/Bartok.csproj index 8f8be76..a70d612 100644 --- a/base/Interfaces/Bartok/Bartok.csproj +++ b/base/Interfaces/Bartok/Bartok.csproj @@ -1,4 +1,4 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Interfaces/Baseattrs/BaseAttrs.Kernel.csproj b/base/Interfaces/Baseattrs/BaseAttrs.Kernel.csproj new file mode 100644 index 0000000..f01120c --- /dev/null +++ b/base/Interfaces/Baseattrs/BaseAttrs.Kernel.csproj @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Interfaces/Baseattrs/Baseattrs.csproj b/base/Interfaces/Baseattrs/Baseattrs.csproj deleted file mode 100644 index c9156bd..0000000 --- a/base/Interfaces/Baseattrs/Baseattrs.csproj +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - Baseattrs - kernel.exe - Library - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - kernel.exe - - - - - diff --git a/base/Interfaces/Baseattrs/CLSCompliantAttribute.csi b/base/Interfaces/Baseattrs/CLSCompliantAttribute.csi index 49f57ba..7d426f0 100644 --- a/base/Interfaces/Baseattrs/CLSCompliantAttribute.csi +++ b/base/Interfaces/Baseattrs/CLSCompliantAttribute.csi @@ -3,18 +3,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================================= -** -** Class: CLSCompliantAttribute -** -** -** Purpose: Container for assemblies. -** -** Date: Jan 28, 2000 -** -=============================================================================*/ +//============================================================================= +// +// Class: CLSCompliantAttribute +// +// Purpose: Container for assemblies. +// +//============================================================================= -namespace System { +namespace System +{ //| [AttributeUsage (AttributeTargets.All, Inherited=true, AllowMultiple=false)] public sealed class CLSCompliantAttribute : Attribute diff --git a/base/Interfaces/Baseattrs/CompilerGlobalScopeAttribute.csi b/base/Interfaces/Baseattrs/CompilerGlobalScopeAttribute.csi index 902b47e..23feefc 100644 --- a/base/Interfaces/Baseattrs/CompilerGlobalScopeAttribute.csi +++ b/base/Interfaces/Baseattrs/CompilerGlobalScopeAttribute.csi @@ -3,18 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: CompilerGlobalScopeAttribute -** -** -** Purpose: Attribute used to communicate to the VS7 debugger -** that a class should be treated as if it has -** global scope. -** -** Date: Aug 09, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: CompilerGlobalScopeAttribute +// +// Purpose: Attribute used to communicate to the VS7 debugger +// that a class should be treated as if it has +// global scope. +// +//=========================================================== namespace System.Runtime.CompilerServices diff --git a/base/Interfaces/Baseattrs/ConditionalAttribute.csi b/base/Interfaces/Baseattrs/ConditionalAttribute.csi index c18384c..40319d3 100644 --- a/base/Interfaces/Baseattrs/ConditionalAttribute.csi +++ b/base/Interfaces/Baseattrs/ConditionalAttribute.csi @@ -5,7 +5,8 @@ // ==--== using System; -namespace System.Diagnostics { +namespace System.Diagnostics +{ //| [AttributeUsage(AttributeTargets.Method, AllowMultiple=true)] public sealed class ConditionalAttribute : Attribute diff --git a/base/Interfaces/Baseattrs/DebuggerAttributes.csi b/base/Interfaces/Baseattrs/DebuggerAttributes.csi index cbbe3a0..d8dac3d 100644 --- a/base/Interfaces/Baseattrs/DebuggerAttributes.csi +++ b/base/Interfaces/Baseattrs/DebuggerAttributes.csi @@ -3,18 +3,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: DebuggerAttributes -** -** -** Purpose: Attributes for debugger -** -** Date: Feb 01, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: DebuggerAttributes +// +// Purpose: Attributes for debugger +// +//=========================================================== -namespace System.Diagnostics { +namespace System.Diagnostics +{ //| [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | diff --git a/base/Interfaces/Baseattrs/DiscardableAttribute.csi b/base/Interfaces/Baseattrs/DiscardableAttribute.csi index d52504c..27c7200 100644 --- a/base/Interfaces/Baseattrs/DiscardableAttribute.csi +++ b/base/Interfaces/Baseattrs/DiscardableAttribute.csi @@ -7,7 +7,8 @@ using System; -namespace System.Runtime.CompilerServices { +namespace System.Runtime.CompilerServices +{ // Custom attribute to indicating a TypeDef is a discardable attribute //| public class DiscardableAttribute : Attribute diff --git a/base/Interfaces/Baseattrs/InteropServices.csi b/base/Interfaces/Baseattrs/InteropServices.csi index 6b736e7..c9eb6e9 100644 --- a/base/Interfaces/Baseattrs/InteropServices.csi +++ b/base/Interfaces/Baseattrs/InteropServices.csi @@ -7,7 +7,8 @@ using System; -namespace System.Runtime.InteropServices { +namespace System.Runtime.InteropServices +{ //| [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Interface | diff --git a/base/Interfaces/Baseattrs/LayoutKind.csi b/base/Interfaces/Baseattrs/LayoutKind.csi index 70a2d70..cc40eb3 100644 --- a/base/Interfaces/Baseattrs/LayoutKind.csi +++ b/base/Interfaces/Baseattrs/LayoutKind.csi @@ -7,7 +7,8 @@ using System; -namespace System.Runtime.InteropServices { +namespace System.Runtime.InteropServices +{ // Used in the StructLayoutAttribute class //| public enum LayoutKind diff --git a/base/Interfaces/Baseattrs/MethodImplAttribute.csi b/base/Interfaces/Baseattrs/MethodImplAttribute.csi index d0d1628..b4e4191 100644 --- a/base/Interfaces/Baseattrs/MethodImplAttribute.csi +++ b/base/Interfaces/Baseattrs/MethodImplAttribute.csi @@ -6,7 +6,8 @@ using System; -namespace System.Runtime.CompilerServices { +namespace System.Runtime.CompilerServices +{ // This Enum matches the miImpl flags defined in corhdr.h. It is used to specify // certain method properties. diff --git a/base/Interfaces/Baseattrs/NeutralResourcesLanguageAttribute.csi b/base/Interfaces/Baseattrs/NeutralResourcesLanguageAttribute.csi index 07107b2..a5422ce 100644 --- a/base/Interfaces/Baseattrs/NeutralResourcesLanguageAttribute.csi +++ b/base/Interfaces/Baseattrs/NeutralResourcesLanguageAttribute.csi @@ -6,7 +6,8 @@ using System; -namespace System.Resources { +namespace System.Resources +{ //| [AttributeUsage(AttributeTargets.Assembly, AllowMultiple=false)] diff --git a/base/Interfaces/Baseattrs/ObsoleteAttribute.csi b/base/Interfaces/Baseattrs/ObsoleteAttribute.csi index 13e3046..9b90808 100644 --- a/base/Interfaces/Baseattrs/ObsoleteAttribute.csi +++ b/base/Interfaces/Baseattrs/ObsoleteAttribute.csi @@ -3,17 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: ObsoleteAttribute -** -** -** Purpose: Attribute for functions, etc that will be removed. -** -** Date: September 22, 1999 -** -===========================================================*/ -namespace System { +//============================================================ +// +// Class: ObsoleteAttribute +// +// Purpose: Attribute for functions, etc that will be removed. +// +//=========================================================== +namespace System +{ // This attribute is attached to members that are not to be used any longer. // Message is some human readable explanation of what to use diff --git a/base/Interfaces/Baseattrs/ParamArrayAttribute.csi b/base/Interfaces/Baseattrs/ParamArrayAttribute.csi index 613490f..c9f0114 100644 --- a/base/Interfaces/Baseattrs/ParamArrayAttribute.csi +++ b/base/Interfaces/Baseattrs/ParamArrayAttribute.csi @@ -3,16 +3,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================================= -** -** Class: ParamArrayAttribute -** -** -** Purpose: Container for assemblies. -** -** Date: Mar 01, 2000 -** -=============================================================================*/ +//============================================================================= +// +// Class: ParamArrayAttribute +// +// Purpose: Container for assemblies. +// +//============================================================================= namespace System { //This class contains only static members and does not need to be serializable diff --git a/base/Interfaces/Baseattrs/SatelliteContractVersionAttribute.csi b/base/Interfaces/Baseattrs/SatelliteContractVersionAttribute.csi index 7dfbff3..320d8ad 100644 --- a/base/Interfaces/Baseattrs/SatelliteContractVersionAttribute.csi +++ b/base/Interfaces/Baseattrs/SatelliteContractVersionAttribute.csi @@ -3,21 +3,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: SatelliteContractVersionAttribute -** -** -** Purpose: Specifies which version of a satellite assembly -** the ResourceManager should ask for. -** -** Date: December 8, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: SatelliteContractVersionAttribute +// +// Purpose: Specifies which version of a satellite assembly +// the ResourceManager should ask for. +// +//=========================================================== using System; -namespace System.Resources { +namespace System.Resources +{ //| [AttributeUsage(AttributeTargets.Assembly, AllowMultiple=false)] diff --git a/base/Interfaces/Baseattrs/ThreadStaticAttribute.csi b/base/Interfaces/Baseattrs/ThreadStaticAttribute.csi index f4d0223..51b51ab 100644 --- a/base/Interfaces/Baseattrs/ThreadStaticAttribute.csi +++ b/base/Interfaces/Baseattrs/ThreadStaticAttribute.csi @@ -3,20 +3,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** File: ThreadStaticAttribute.cool -** -** Author(s): Tarun Anand (TarunA) -** -** Purpose: Custom attribute to indicate that the field should be treated -** as a static relative to a thread. -** -** -** Date: Jan 18, 2000 -** -===========================================================*/ -namespace System { +//============================================================ +// +// File: ThreadStaticAttribute.cool +// +// Purpose: Custom attribute to indicate that the field should be treated +// as a static relative to a thread. +// +//=========================================================== +namespace System +{ //| [AttributeUsage(AttributeTargets.Field, Inherited = false)] diff --git a/base/Interfaces/Basetypes/ArgumentException.csi b/base/Interfaces/Basetypes/ArgumentException.csi index 12bdb14..be787c9 100644 --- a/base/Interfaces/Basetypes/ArgumentException.csi +++ b/base/Interfaces/Basetypes/ArgumentException.csi @@ -3,20 +3,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================================= -** -** Class: ArgumentException -** -** -** Purpose: Exception class for invalid arguments to a method. -** -** Date: March 24, 1998 -** -=============================================================================*/ +//============================================================================= +// +// Class: ArgumentException +// +// Purpose: Exception class for invalid arguments to a method. +// +//============================================================================= using System; -namespace System { +namespace System +{ // The ArgumentException is thrown when an argument does not meet // the contract of the method. Ideally it should give a meaningful error diff --git a/base/Interfaces/Basetypes/ArgumentNullException.csi b/base/Interfaces/Basetypes/ArgumentNullException.csi index 803b15c..735cf97 100644 --- a/base/Interfaces/Basetypes/ArgumentNullException.csi +++ b/base/Interfaces/Basetypes/ArgumentNullException.csi @@ -6,7 +6,8 @@ using System; -namespace System { +namespace System +{ // The ArgumentException is thrown when an argument // is null when it shouldn't be. diff --git a/base/Interfaces/Basetypes/ArgumentOutOfRangeException.csi b/base/Interfaces/Basetypes/ArgumentOutOfRangeException.csi index 874493b..ed049b7 100644 --- a/base/Interfaces/Basetypes/ArgumentOutOfRangeException.csi +++ b/base/Interfaces/Basetypes/ArgumentOutOfRangeException.csi @@ -7,7 +7,8 @@ using System; using System.Runtime.CompilerServices; -namespace System { +namespace System +{ // The ArgumentOutOfRangeException is thrown when an argument // is outside the legal range for that argument. This may often be caused diff --git a/base/Interfaces/Basetypes/ArithmeticException.csi b/base/Interfaces/Basetypes/ArithmeticException.csi index 4a39b73..20ff5ef 100644 --- a/base/Interfaces/Basetypes/ArithmeticException.csi +++ b/base/Interfaces/Basetypes/ArithmeticException.csi @@ -3,19 +3,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================================= -** -** Class: ArithmeticException -** -** -** Purpose: Exception class for bad arithmetic conditions! -** -** Date: March 17, 1998 -** -=============================================================================*/ +//============================================================================= +// +// Class: ArithmeticException +// +// Purpose: Exception class for bad arithmetic conditions! +// +//============================================================================= using System.Runtime.CompilerServices; -namespace System { +namespace System +{ [RequiredByBartok] public class ArithmeticException : SystemException { diff --git a/base/Interfaces/Basetypes/AssemblyAttributes.csi b/base/Interfaces/Basetypes/AssemblyAttributes.csi index f07b3d4..bd46042 100644 --- a/base/Interfaces/Basetypes/AssemblyAttributes.csi +++ b/base/Interfaces/Basetypes/AssemblyAttributes.csi @@ -3,20 +3,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================================= -** -** File: AssemblyAttributes -** -** -** Purpose: For Assembly-related custom attributes. -** -** Date: April 12, 2000 -** -=============================================================================*/ +//============================================================================= +// +// File: AssemblyAttributes +// +// Purpose: For Assembly-related custom attributes. +// +//============================================================================= using System; -namespace System.Reflection { +namespace System.Reflection +{ public enum AssemblyHashAlgorithm { //| diff --git a/base/Interfaces/Basetypes/Attribute.csi b/base/Interfaces/Basetypes/Attribute.csi index d28345d..cbb0fb3 100644 --- a/base/Interfaces/Basetypes/Attribute.csi +++ b/base/Interfaces/Basetypes/Attribute.csi @@ -3,20 +3,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: Attribute -** -** -** Purpose: The class used as an attribute to denote that -** another class can be used as an attribute. -** -** Date: December 7, 1999 -** -===========================================================*/ +//============================================================ +// +// Class: Attribute +// +// Purpose: The class used as an attribute to denote that +// another class can be used as an attribute. +// +//=========================================================== using System; -namespace System { +namespace System +{ //| [AttributeUsageAttribute(AttributeTargets.All, Inherited = true, AllowMultiple=false)] // Base class for all attributes diff --git a/base/Interfaces/Basetypes/AttributeTargets.csi b/base/Interfaces/Basetypes/AttributeTargets.csi index e51af85..ed63ed7 100644 --- a/base/Interfaces/Basetypes/AttributeTargets.csi +++ b/base/Interfaces/Basetypes/AttributeTargets.csi @@ -7,7 +7,8 @@ //////////////////////////////////////////////////////////////////////////////// using System; -namespace System { +namespace System +{ // Enum used to indicate all the elements of the // VOS it is valid to attach this element to. diff --git a/base/Interfaces/Basetypes/AttributeUsageAttribute.csi b/base/Interfaces/Basetypes/AttributeUsageAttribute.csi index 2e87859..337c1fc 100644 --- a/base/Interfaces/Basetypes/AttributeUsageAttribute.csi +++ b/base/Interfaces/Basetypes/AttributeUsageAttribute.csi @@ -3,19 +3,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: AttributeUsageAttribute -** -** -** Purpose: The class denotes how to specify the usage of an attribute -** -** Date: December 7, 1999 -** -===========================================================*/ -namespace System { +//============================================================ +// +// Class: AttributeUsageAttribute +// +// Purpose: The class denotes how to specify the usage of an attribute +// +//=========================================================== +namespace System +{ - /* By default, attributes are inherited and multiple attributes are not allowed */ + // By default, attributes are inherited and multiple attributes are not allowed //| [AttributeUsage(AttributeTargets.Class, Inherited = true)] public sealed class AttributeUsageAttribute : Attribute diff --git a/base/Interfaces/Basetypes/BartokAttributes.csi b/base/Interfaces/Basetypes/BartokAttributes.csi index 946ba58..88b3a09 100644 --- a/base/Interfaces/Basetypes/BartokAttributes.csi +++ b/base/Interfaces/Basetypes/BartokAttributes.csi @@ -1,10 +1,11 @@ // -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // using System; -namespace Microsoft.Bartok.Options { +namespace Microsoft.Bartok.Options +{ #if false [AttributeUsage(AttributeTargets.Constructor| diff --git a/base/Interfaces/Basetypes/BartokRT.csi b/base/Interfaces/Basetypes/BartokRT.csi index 43dac59..d690fc6 100644 --- a/base/Interfaces/Basetypes/BartokRT.csi +++ b/base/Interfaces/Basetypes/BartokRT.csi @@ -1,5 +1,5 @@ // -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // // This code is included in both Bartok compiler and mscorlibOverride builds @@ -9,7 +9,8 @@ using System; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; -namespace Microsoft.Bartok.Runtime { +namespace Microsoft.Bartok.Runtime +{ #if !DONT_INCLUDE_TEMPORARY_STUFF_FOR_GC public enum StructuralType { None = 0x00, diff --git a/base/Interfaces/Basetypes/Basetypes.App.csproj b/base/Interfaces/Basetypes/Basetypes.App.csproj new file mode 100644 index 0000000..f7dc452 --- /dev/null +++ b/base/Interfaces/Basetypes/Basetypes.App.csproj @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Interfaces/Basetypes/Basetypes.Kernel.csproj b/base/Interfaces/Basetypes/Basetypes.Kernel.csproj new file mode 100644 index 0000000..f7dc452 --- /dev/null +++ b/base/Interfaces/Basetypes/Basetypes.Kernel.csproj @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Interfaces/Basetypes/Basetypes.csproj b/base/Interfaces/Basetypes/Basetypes.csproj deleted file mode 100644 index 4978490..0000000 --- a/base/Interfaces/Basetypes/Basetypes.csproj +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - Basetypes - Library - true - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/base/Interfaces/Basetypes/CompilerServicesAttributes.csi b/base/Interfaces/Basetypes/CompilerServicesAttributes.csi index 423cf83..f1af439 100644 --- a/base/Interfaces/Basetypes/CompilerServicesAttributes.csi +++ b/base/Interfaces/Basetypes/CompilerServicesAttributes.csi @@ -1,8 +1,9 @@ // -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // -namespace System.Runtime.CompilerServices { +namespace System.Runtime.CompilerServices +{ [AttributeUsage(AttributeTargets.Class| AttributeTargets.Struct| @@ -82,6 +83,7 @@ namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Method| AttributeTargets.Constructor, Inherited=false)] + [RequiredByBartok] public sealed class NoStackOverflowCheckAttribute: Attribute { } diff --git a/base/Interfaces/Basetypes/DefaultMemberAttribute.csi b/base/Interfaces/Basetypes/DefaultMemberAttribute.csi index 5d61eb0..4d254d5 100644 --- a/base/Interfaces/Basetypes/DefaultMemberAttribute.csi +++ b/base/Interfaces/Basetypes/DefaultMemberAttribute.csi @@ -10,12 +10,11 @@ // member used by Type.InvokeMember. The default member is simply a name given // to a type. // -// Date: Oct 99 -// using System; -namespace System.Reflection { +namespace System.Reflection +{ //| [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)] diff --git a/base/Interfaces/Basetypes/DivideByZeroException.csi b/base/Interfaces/Basetypes/DivideByZeroException.csi index 8c70903..cd329c8 100644 --- a/base/Interfaces/Basetypes/DivideByZeroException.csi +++ b/base/Interfaces/Basetypes/DivideByZeroException.csi @@ -7,7 +7,8 @@ using System; using System.Runtime.CompilerServices; -namespace System { +namespace System +{ [RequiredByBartok] public class DivideByZeroException : ArithmeticException { DivideByZeroException(); diff --git a/base/Interfaces/Basetypes/FlagsAttribute.csi b/base/Interfaces/Basetypes/FlagsAttribute.csi index 45583f6..7fb501f 100644 --- a/base/Interfaces/Basetypes/FlagsAttribute.csi +++ b/base/Interfaces/Basetypes/FlagsAttribute.csi @@ -7,7 +7,8 @@ using System; -namespace System { +namespace System +{ // Custom attribute to indicate that the enum // should be treated as a bitfield (or set of flags). diff --git a/base/Interfaces/Basetypes/ICollection.csi b/base/Interfaces/Basetypes/ICollection.csi index 9910c07..0688f2d 100644 --- a/base/Interfaces/Basetypes/ICollection.csi +++ b/base/Interfaces/Basetypes/ICollection.csi @@ -6,7 +6,8 @@ using System; -namespace System.Collections { +namespace System.Collections +{ // Base interface for all collections, defining enumerators, size, and // synchronization methods. public interface ICollection : IEnumerable diff --git a/base/Interfaces/Basetypes/IComparer.csi b/base/Interfaces/Basetypes/IComparer.csi index 0439a9a..2d2b950 100644 --- a/base/Interfaces/Basetypes/IComparer.csi +++ b/base/Interfaces/Basetypes/IComparer.csi @@ -3,20 +3,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Interface: IComparer -** -** -** Purpose: Interface for comparing two Objects. -** -** Date: September 25, 1999 -** -===========================================================*/ +//============================================================ +// +// Interface: IComparer +// +// Purpose: Interface for comparing two Objects. +// +//=========================================================== using System; -namespace System.Collections { +namespace System.Collections +{ // The IComparer interface implements a method that compares two objects. It is // used in conjunction with the Sort and BinarySearch methods on diff --git a/base/Interfaces/Basetypes/IEnumerator.csi b/base/Interfaces/Basetypes/IEnumerator.csi index 76c3506..1b87a84 100644 --- a/base/Interfaces/Basetypes/IEnumerator.csi +++ b/base/Interfaces/Basetypes/IEnumerator.csi @@ -3,20 +3,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Interface: IEnumerator -** -** -** Purpose: Base interface for all enumerators. -** -** Date: June 14, 1999 -** -===========================================================*/ +//============================================================ +// +// Interface: IEnumerator +// +// Purpose: Base interface for all enumerators. +// +//=========================================================== using System; -namespace System.Collections { +namespace System.Collections +{ // Base interface for all enumerators, providing a simple approach // to iterating over a collection. //| diff --git a/base/Interfaces/Basetypes/IList.csi b/base/Interfaces/Basetypes/IList.csi index 5280338..863983b 100644 --- a/base/Interfaces/Basetypes/IList.csi +++ b/base/Interfaces/Basetypes/IList.csi @@ -6,7 +6,8 @@ using System; -namespace System.Collections { +namespace System.Collections +{ // An IList is an ordered collection of objects. The exact ordering // is up to the implementation of the list, ranging from a sorted diff --git a/base/Interfaces/Basetypes/IndexOutOfRangeException.csi b/base/Interfaces/Basetypes/IndexOutOfRangeException.csi index 564c82d..47b331d 100644 --- a/base/Interfaces/Basetypes/IndexOutOfRangeException.csi +++ b/base/Interfaces/Basetypes/IndexOutOfRangeException.csi @@ -6,7 +6,8 @@ using System.Runtime.CompilerServices; -namespace System { +namespace System +{ [RequiredByBartok] public sealed class IndexOutOfRangeException : SystemException { public IndexOutOfRangeException(); diff --git a/base/Interfaces/Basetypes/InvalidCastException.csi b/base/Interfaces/Basetypes/InvalidCastException.csi index 048ef98..ae6f69b 100644 --- a/base/Interfaces/Basetypes/InvalidCastException.csi +++ b/base/Interfaces/Basetypes/InvalidCastException.csi @@ -4,7 +4,8 @@ // // ==--== -namespace System { +namespace System +{ public class InvalidCastException : SystemException { public InvalidCastException(); diff --git a/base/Interfaces/Basetypes/InvalidOperationException.csi b/base/Interfaces/Basetypes/InvalidOperationException.csi index 6dd24f7..1f05c89 100644 --- a/base/Interfaces/Basetypes/InvalidOperationException.csi +++ b/base/Interfaces/Basetypes/InvalidOperationException.csi @@ -6,7 +6,8 @@ using System; -namespace System { +namespace System +{ public class InvalidOperationException : SystemException { diff --git a/base/Interfaces/Basetypes/NullReferenceException.csi b/base/Interfaces/Basetypes/NullReferenceException.csi index 7c397fa..b14ac00 100644 --- a/base/Interfaces/Basetypes/NullReferenceException.csi +++ b/base/Interfaces/Basetypes/NullReferenceException.csi @@ -6,7 +6,8 @@ using System.Runtime.CompilerServices; -namespace System { +namespace System +{ [RequiredByBartok] public class NullReferenceException : SystemException { public NullReferenceException(); diff --git a/base/Interfaces/Basetypes/OutOfMemoryException.csi b/base/Interfaces/Basetypes/OutOfMemoryException.csi index 96032d8..44522ff 100644 --- a/base/Interfaces/Basetypes/OutOfMemoryException.csi +++ b/base/Interfaces/Basetypes/OutOfMemoryException.csi @@ -4,7 +4,8 @@ // // ==--== -namespace System { +namespace System +{ public class OutOfMemoryException : SystemException { public OutOfMemoryException(); diff --git a/base/Interfaces/Basetypes/OverflowException.csi b/base/Interfaces/Basetypes/OverflowException.csi index 29fe3ca..77c4abb 100644 --- a/base/Interfaces/Basetypes/OverflowException.csi +++ b/base/Interfaces/Basetypes/OverflowException.csi @@ -6,7 +6,8 @@ using System.Runtime.CompilerServices; -namespace System { +namespace System +{ [RequiredByBartok] public class OverflowException : ArithmeticException { public OverflowException(); diff --git a/base/Interfaces/Basetypes/RuntimeFieldHandle.csi b/base/Interfaces/Basetypes/RuntimeFieldHandle.csi new file mode 100644 index 0000000..f56a3c4 --- /dev/null +++ b/base/Interfaces/Basetypes/RuntimeFieldHandle.csi @@ -0,0 +1,22 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +namespace System +{ + // This value type is used for making classlib type safe. + // + // SECURITY : m_ptr cannot be set to anything other than null by untrusted + // code. + // + // This corresponds to EE FieldDesc. + //| + public struct RuntimeFieldHandle + { + private IntPtr m_ptr; + + //| + public IntPtr Value { get; } + } +} diff --git a/base/Interfaces/Basetypes/RuntimeType.csi b/base/Interfaces/Basetypes/RuntimeType.csi index c2c6108..402871c 100644 --- a/base/Interfaces/Basetypes/RuntimeType.csi +++ b/base/Interfaces/Basetypes/RuntimeType.csi @@ -11,8 +11,6 @@ // The public structure is known about by the runtime. __RuntimeXXX classes // are created only once per object in the system and support == comparisons. // -// Date: March 98 -// using Microsoft.Bartok.Runtime; using System; @@ -21,7 +19,8 @@ using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Text; -namespace System { +namespace System +{ #if !DONT_INCLUDE_TEMPORARY_STUFF_FOR_GC [CCtorIsRunDuringStartup] diff --git a/base/Interfaces/Basetypes/Single.csi b/base/Interfaces/Basetypes/Single.csi index 459f07d..75e1869 100644 --- a/base/Interfaces/Basetypes/Single.csi +++ b/base/Interfaces/Basetypes/Single.csi @@ -6,7 +6,8 @@ using System.Globalization; -namespace System { +namespace System +{ public struct Single : IComparable, IFormattable { #if !DONT_DO_BARTOK diff --git a/base/Interfaces/Basetypes/StackOverflowException.csi b/base/Interfaces/Basetypes/StackOverflowException.csi index 08478dc..d001bd6 100644 --- a/base/Interfaces/Basetypes/StackOverflowException.csi +++ b/base/Interfaces/Basetypes/StackOverflowException.csi @@ -4,7 +4,8 @@ // // ==--== -namespace System { +namespace System +{ public sealed class StackOverflowException : SystemException { public StackOverflowException(); diff --git a/base/Interfaces/Basetypes/SystemException.csi b/base/Interfaces/Basetypes/SystemException.csi index 997fb6b..b4487b7 100644 --- a/base/Interfaces/Basetypes/SystemException.csi +++ b/base/Interfaces/Basetypes/SystemException.csi @@ -4,7 +4,8 @@ // // ==--== -namespace System { +namespace System +{ public class SystemException : Exception { diff --git a/base/Interfaces/Basetypes/Void.csi b/base/Interfaces/Basetypes/Void.csi new file mode 100644 index 0000000..d0c5478 --- /dev/null +++ b/base/Interfaces/Basetypes/Void.csi @@ -0,0 +1,19 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//////////////////////////////////////////////////////////////////////////////// +// Void +// This class represents the void return type +//////////////////////////////////////////////////////////////////////////////// + +namespace System +{ + + //| + + public struct Void + { + } +} diff --git a/base/Interfaces/Basetypes/Vtable.csi b/base/Interfaces/Basetypes/Vtable.csi index cd6c2d0..bba971a 100644 --- a/base/Interfaces/Basetypes/Vtable.csi +++ b/base/Interfaces/Basetypes/Vtable.csi @@ -1,5 +1,5 @@ // -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // using Microsoft.Bartok.Runtime; @@ -11,7 +11,8 @@ using System.Runtime.InteropServices; using System.Threading; using System; -namespace System { +namespace System +{ #if !DONT_INCLUDE_TEMPORARY_STUFF_FOR_GC public struct InterfaceInfo { diff --git a/base/Interfaces/Channels/Channels.csi b/base/Interfaces/Channels/Channels.csi new file mode 100644 index 0000000..a403cf0 --- /dev/null +++ b/base/Interfaces/Channels/Channels.csi @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Channels.csi +// +// Note: +// + +using System; +using Microsoft.Singularity.Channels; +using Allocation = Microsoft.Singularity.Memory.SharedHeap.Allocation; + +[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] +[assembly: System.Reflection.AssemblyKeyFileAttribute("public.snk")] +[assembly: System.Reflection.AssemblyDelaySignAttribute(true)] + +namespace Microsoft.Singularity.Channels +{ + public class ChannelDeliveryImplService + { + public static void Initialize(); + } + +} diff --git a/base/Interfaces/Channels/Channels.csproj b/base/Interfaces/Channels/Channels.csproj new file mode 100644 index 0000000..8fa09b1 --- /dev/null +++ b/base/Interfaces/Channels/Channels.csproj @@ -0,0 +1,32 @@ + + + + + + + Channels + Library + $(KERNEL_IL_DIR) + true + true + + + + + kernel + + + + diff --git a/base/Interfaces/Collections/ArrayList.csi b/base/Interfaces/Collections/ArrayList.csi index c3aa6ff..16daeb9 100644 --- a/base/Interfaces/Collections/ArrayList.csi +++ b/base/Interfaces/Collections/ArrayList.csi @@ -6,7 +6,8 @@ using System; -namespace System.Collections { +namespace System.Collections +{ // Implements a variable-size List that uses an array of objects to store the // elements. A ArrayList has a capacity, which is the allocated length // of the internal array. As elements are added to a ArrayList, the capacity diff --git a/base/Interfaces/Collections/BitArray.csi b/base/Interfaces/Collections/BitArray.csi index f47f953..6cc4b89 100644 --- a/base/Interfaces/Collections/BitArray.csi +++ b/base/Interfaces/Collections/BitArray.csi @@ -7,7 +7,8 @@ using System; using System.Diagnostics; -namespace System.Collections { +namespace System.Collections +{ // A vector of bits. Use this to store bits efficiently, without having to do bit // shifting yourself. public sealed class BitArray : ICollection, ICloneable { diff --git a/base/Interfaces/Collections/CaseInsensitiveComparer.csi b/base/Interfaces/Collections/CaseInsensitiveComparer.csi index ab02120..9dbbf50 100644 --- a/base/Interfaces/Collections/CaseInsensitiveComparer.csi +++ b/base/Interfaces/Collections/CaseInsensitiveComparer.csi @@ -7,7 +7,8 @@ using System; using System.Globalization; -namespace System.Collections { +namespace System.Collections +{ public class CaseInsensitiveComparer : IComparer { public static readonly CaseInsensitiveComparer Default; diff --git a/base/Interfaces/Collections/CaseInsensitiveHashCodeProvider.csi b/base/Interfaces/Collections/CaseInsensitiveHashCodeProvider.csi index 5e1f3ca..2110af5 100644 --- a/base/Interfaces/Collections/CaseInsensitiveHashCodeProvider.csi +++ b/base/Interfaces/Collections/CaseInsensitiveHashCodeProvider.csi @@ -7,7 +7,8 @@ using System; using System.Globalization; -namespace System.Collections { +namespace System.Collections +{ public class CaseInsensitiveHashCodeProvider : IHashCodeProvider { public static readonly CaseInsensitiveHashCodeProvider Default; diff --git a/base/Interfaces/Collections/CollectionBase.csi b/base/Interfaces/Collections/CollectionBase.csi index 7eb626a..e8a9c56 100644 --- a/base/Interfaces/Collections/CollectionBase.csi +++ b/base/Interfaces/Collections/CollectionBase.csi @@ -7,7 +7,8 @@ using System; -namespace System.Collections { +namespace System.Collections +{ // Useful base class for typed read/write collections where items derive from object public abstract class CollectionBase : IList { ArrayList list; diff --git a/base/Interfaces/Collections/Collections.csproj b/base/Interfaces/Collections/Collections.csproj index cdf4079..f908a1b 100644 --- a/base/Interfaces/Collections/Collections.csproj +++ b/base/Interfaces/Collections/Collections.csproj @@ -13,38 +13,20 @@ --> - - - - Collections - Library - true - kernel.exe - - - - - - - - - - - - - - - - - - kernel.exe - - - kernel.exe - + + + + + + + + + + + + + + - - - diff --git a/base/Interfaces/Collections/Comparer.csi b/base/Interfaces/Collections/Comparer.csi index 81f8d06..44d5d03 100644 --- a/base/Interfaces/Collections/Comparer.csi +++ b/base/Interfaces/Collections/Comparer.csi @@ -7,7 +7,8 @@ using System; using System.Globalization; -namespace System.Collections { +namespace System.Collections +{ public sealed class Comparer : IComparer { diff --git a/base/Interfaces/Collections/DictionaryBase.csi b/base/Interfaces/Collections/DictionaryBase.csi index 36de998..d0591fe 100644 --- a/base/Interfaces/Collections/DictionaryBase.csi +++ b/base/Interfaces/Collections/DictionaryBase.csi @@ -8,7 +8,8 @@ using System; -namespace System.Collections { +namespace System.Collections +{ // Useful base class for typed read/write collections where items derive from object public abstract class DictionaryBase : IDictionary { Hashtable hashtable; diff --git a/base/Interfaces/Collections/DictionaryEntry.csi b/base/Interfaces/Collections/DictionaryEntry.csi index 40bf1fb..f3b2061 100644 --- a/base/Interfaces/Collections/DictionaryEntry.csi +++ b/base/Interfaces/Collections/DictionaryEntry.csi @@ -6,7 +6,8 @@ using System; -namespace System.Collections { +namespace System.Collections +{ // A DictionaryEntry holds a key and a value from a dictionary. // It is returned by IDictionaryEnumerator::GetEntry(). public struct DictionaryEntry diff --git a/base/Interfaces/Collections/Hashtable.csi b/base/Interfaces/Collections/Hashtable.csi index 44bf7ec..2161525 100644 --- a/base/Interfaces/Collections/Hashtable.csi +++ b/base/Interfaces/Collections/Hashtable.csi @@ -8,7 +8,8 @@ using System; using System.Diagnostics; using System.Runtime.CompilerServices; -namespace System.Collections { +namespace System.Collections +{ // The Hashtable class represents a dictionary of associated keys and // values with constant lookup time. diff --git a/base/Interfaces/Collections/IDictionary.csi b/base/Interfaces/Collections/IDictionary.csi index fbaa6b7..f0a7bd2 100644 --- a/base/Interfaces/Collections/IDictionary.csi +++ b/base/Interfaces/Collections/IDictionary.csi @@ -6,7 +6,8 @@ using System; -namespace System.Collections { +namespace System.Collections +{ // An IDictionary is a possibly unordered set of key-value pairs. // Keys can be any non-null object. Values can be any object. // You can look up a value in an IDictionary via the default indexed diff --git a/base/Interfaces/Collections/IDictionaryEnumerator.csi b/base/Interfaces/Collections/IDictionaryEnumerator.csi index 3d0ce70..c26f35a 100644 --- a/base/Interfaces/Collections/IDictionaryEnumerator.csi +++ b/base/Interfaces/Collections/IDictionaryEnumerator.csi @@ -6,7 +6,8 @@ using System; -namespace System.Collections { +namespace System.Collections +{ // This interface represents an enumerator that allows sequential access to the // elements of a dictionary. Upon creation, an enumerator is conceptually // positioned before the first element of the enumeration. The first call to the diff --git a/base/Interfaces/Collections/IHashCodeProvider.csi b/base/Interfaces/Collections/IHashCodeProvider.csi index a4a974e..c4a26be 100644 --- a/base/Interfaces/Collections/IHashCodeProvider.csi +++ b/base/Interfaces/Collections/IHashCodeProvider.csi @@ -6,7 +6,8 @@ using System; -namespace System.Collections { +namespace System.Collections +{ // Provides a mechanism for a hash table user to override the default // GetHashCode() function on Objects, providing their own hash function. diff --git a/base/Interfaces/Collections/Queue.csi b/base/Interfaces/Collections/Queue.csi index 79f42a7..4bb5b36 100644 --- a/base/Interfaces/Collections/Queue.csi +++ b/base/Interfaces/Collections/Queue.csi @@ -6,7 +6,8 @@ using System; -namespace System.Collections { +namespace System.Collections +{ // A simple Queue of objects. Internally it is implemented as a circular // buffer, so Enqueue can be O(n). Dequeue is O(1). public class Queue : ICollection, ICloneable { diff --git a/base/Interfaces/Collections/ReadOnlyCollectionBase.csi b/base/Interfaces/Collections/ReadOnlyCollectionBase.csi index ca6288c..516beb5 100644 --- a/base/Interfaces/Collections/ReadOnlyCollectionBase.csi +++ b/base/Interfaces/Collections/ReadOnlyCollectionBase.csi @@ -7,7 +7,8 @@ using System; -namespace System.Collections { +namespace System.Collections +{ public abstract class ReadOnlyCollectionBase : ICollection { ArrayList list; diff --git a/base/Interfaces/Collections/SortedList.csi b/base/Interfaces/Collections/SortedList.csi index 0d840f1..3ea035e 100644 --- a/base/Interfaces/Collections/SortedList.csi +++ b/base/Interfaces/Collections/SortedList.csi @@ -6,7 +6,8 @@ using System; -namespace System.Collections { +namespace System.Collections +{ // The SortedList class implements a sorted list of keys and values. Entries in // a sorted list are sorted by their keys and are accessible both by key and by // index. The keys of a sorted list can be ordered either according to a diff --git a/base/Interfaces/Collections/Stack.csi b/base/Interfaces/Collections/Stack.csi index 55f497f..8b2e94b 100644 --- a/base/Interfaces/Collections/Stack.csi +++ b/base/Interfaces/Collections/Stack.csi @@ -6,7 +6,8 @@ using System; -namespace System.Collections { +namespace System.Collections +{ // A simple stack of objects. Internally it is implemented as an array, // so Push can be O(n). Pop is O(1). diff --git a/base/Interfaces/Console/Console.App.csproj b/base/Interfaces/Console/Console.App.csproj new file mode 100644 index 0000000..56fee8e --- /dev/null +++ b/base/Interfaces/Console/Console.App.csproj @@ -0,0 +1,37 @@ + + + + + + + true + true + + + + Console.App + Library + $(APPILLSDIR) + + + + + + Corlib + + + + + diff --git a/base/Interfaces/Console/Console.Kernel.csproj b/base/Interfaces/Console/Console.Kernel.csproj new file mode 100644 index 0000000..0dc474d --- /dev/null +++ b/base/Interfaces/Console/Console.Kernel.csproj @@ -0,0 +1,37 @@ + + + + + + + true + true + + + + Console.Kernel + Library + $(KERNEL_IL_DIR) + + + + + + kernel + + + + + diff --git a/base/Interfaces/Console/Console.csproj b/base/Interfaces/Console/Console.csproj deleted file mode 100644 index 405aeed..0000000 --- a/base/Interfaces/Console/Console.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - true - true - - - - Console - Library - - - - - - - kernel.exe - - - - - - diff --git a/base/Interfaces/Corlib/Corlib.csproj b/base/Interfaces/Corlib/Corlib.csproj new file mode 100644 index 0000000..3024660 --- /dev/null +++ b/base/Interfaces/Corlib/Corlib.csproj @@ -0,0 +1,30 @@ + + + + + + + Corlib + Library + $(APPILLSDIR) + true + true + + + + + + + diff --git a/base/Interfaces/Diagnostics/Diagnostics.csproj b/base/Interfaces/Diagnostics/Diagnostics.csproj index e82ef25..a75500f 100644 --- a/base/Interfaces/Diagnostics/Diagnostics.csproj +++ b/base/Interfaces/Diagnostics/Diagnostics.csproj @@ -18,15 +18,14 @@ Diagnostics Library + $(KERNEL_IL_DIR) true true - - kernel.exe - + kernel diff --git a/base/Interfaces/Directory/Directory.csi b/base/Interfaces/Directory/Directory.csi index 2b7b856..8e3d1dd 100644 --- a/base/Interfaces/Directory/Directory.csi +++ b/base/Interfaces/Directory/Directory.csi @@ -16,6 +16,7 @@ using Microsoft.Singularity.Io; using Microsoft.Singularity.Memory; using Microsoft.Singularity.Xml; using Microsoft.Singularity.Security; +using Allocation = Microsoft.Singularity.Memory.SharedHeap.Allocation; [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyKeyFileAttribute("public.snk")] @@ -35,7 +36,12 @@ namespace Microsoft.Singularity.Directory // // Kernel only interfaces. // - + public static void StartUserSpaceDirectoryService(); + public static bool UserSpaceDirectoryServiceStarted(); + public static void InitializeRemoteDirectoryService(uint type); + public static void NewRemoteDirectoryEndpoint(out Allocation* imp, out Allocation* exp); + public static void ExportArmNamespace(); + public static void StartRemoteDirectoryEndpoint(Allocation* exp); public static bool CreateDirectory(DirNode dirNode, string path); public static bool CreateSymbolicLink(DirNode dirNode, string path, string link); public static DirNode FindDirectory(string dirName, bool createIfNull); diff --git a/base/Interfaces/Directory/Directory.csproj b/base/Interfaces/Directory/Directory.csproj index 1bbbd37..4ed43b9 100644 --- a/base/Interfaces/Directory/Directory.csproj +++ b/base/Interfaces/Directory/Directory.csproj @@ -18,18 +18,15 @@ Directory Library + $(KERNEL_IL_DIR) true true - kernel.exe - kernel.exe - kernel.exe - kernel.exe - - kernel.exe + kernel + Security diff --git a/base/Interfaces/Drivers/Drivers.csi b/base/Interfaces/Drivers/Drivers.csi index b155429..eec840a 100644 --- a/base/Interfaces/Drivers/Drivers.csi +++ b/base/Interfaces/Drivers/Drivers.csi @@ -10,6 +10,7 @@ // using System; +using Microsoft.Singularity.Channels; [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyKeyFileAttribute("public.snk")] diff --git a/base/Interfaces/Drivers/Drivers.csproj b/base/Interfaces/Drivers/Drivers.csproj index 39750df..59667a0 100644 --- a/base/Interfaces/Drivers/Drivers.csproj +++ b/base/Interfaces/Drivers/Drivers.csproj @@ -18,13 +18,14 @@ Drivers Library + $(KERNEL_IL_DIR) true true - kernel.exe + kernel diff --git a/base/Interfaces/Hal/Hal.csproj b/base/Interfaces/Hal/Hal.csproj index de89e54..e9741fd 100644 --- a/base/Interfaces/Hal/Hal.csproj +++ b/base/Interfaces/Hal/Hal.csproj @@ -12,33 +12,26 @@ ############################################################################## --> - + Hal Library + $(KERNEL_IL_DIR) true true - - - - - + + - - kernel.exe - - - kernel.exe - + kernel - + diff --git a/base/Interfaces/Hal/HalAcpi.csi b/base/Interfaces/Hal/HalAcpi.csi new file mode 100644 index 0000000..8cfa59e --- /dev/null +++ b/base/Interfaces/Hal/HalAcpi.csi @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Collections; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +using Microsoft.Singularity; + +[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] +[assembly: System.Reflection.AssemblyKeyFileAttribute("public.snk")] +[assembly: System.Reflection.AssemblyDelaySignAttribute(true)] + +namespace Microsoft.Singularity.Hal.Acpi +{ + public class AcpiTables + { + public static void Parse(); + public static AcpiDevice[] LoadDevices(); + } + + public class AcpiDevice + { + public string DeviceId { get; } + public ResourceDescriptor[] ResourceDescriptors { get; } + } + + public enum ConsumerProducer + { + ProducesAndConsumes = 0, + Consumes = 1 + } + + public abstract class ResourceDescriptor + { + } + + public class AddressSpaceDescriptor : ResourceDescriptor + { + public ulong Minimum { get; } + public ulong Maximum { get; } + public ulong Length { get; } + public ConsumerProducer ConsumerProducer { get; } + } + + public class MemoryRangeDescriptor : AddressSpaceDescriptor + { + public bool Writable { get; } + } + + public class IoRangeDescriptor : AddressSpaceDescriptor + { + } + + public class IrqDescriptor : ResourceDescriptor + { + public int[] InterruptNumbers { get; } + } + + public class DmaDescriptor : ResourceDescriptor + { + public int[] DmaChannelNumbers { get; } + } + + public class GenericRegisterDescriptor : ResourceDescriptor + { + } + + public class VendorDefinedDescriptor : ResourceDescriptor + { + } +} diff --git a/base/Interfaces/Hal/HalClock.csi b/base/Interfaces/Hal/HalClock.csi deleted file mode 100644 index c216d56..0000000 --- a/base/Interfaces/Hal/HalClock.csi +++ /dev/null @@ -1,62 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: HalDevices.csi -// -// Note: -// - -using System; -using System.Collections; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; - -using Microsoft.Singularity; - -[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyKeyFileAttribute("public.snk")] -[assembly: System.Reflection.AssemblyDelaySignAttribute(true)] - -namespace Microsoft.Singularity.Hal -{ - public class HalClock - { - public HalClock(); - - /// - /// Notification that system has received and processed clock - /// interrupt. - /// - [NoHeapAllocation] - public void ClearInterrupt(); - - /// Get the time elapsed since booting. - /// Ticks of 100ns of uptime. - /// - [NoHeapAllocation] - public long GetKernelTicks(); - - /// - /// Notification that processor is resuming from halted state. - /// Provides clock to re-sync if it uses the CPU timestamp counter. - /// - [NoHeapAllocation] - public void CpuResumeFromHaltEvent(); - - /// Get time from Real-Time Clock. - /// The number of 100-nanosecond intervals that - /// have elapsed since 12:00 A.M., January 1, 0001. - /// - [NoHeapAllocation] - public long GetRtcTime(); - - /// Set time of Real-Time Clock. - /// The number of 100-nanosecond intervals - /// that have elapsed since 12:00 A.M., January 1, 0001. - /// - public void SetRtcTime(long rtcTicks); - } -} diff --git a/base/Interfaces/Hal/HalDevices.csi b/base/Interfaces/Hal/HalDevices.csi deleted file mode 100644 index c025553..0000000 --- a/base/Interfaces/Hal/HalDevices.csi +++ /dev/null @@ -1,60 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: HalDevices.csi -// -// Note: -// - -using System; -using System.Collections; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; - -using Microsoft.Singularity; - -[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyKeyFileAttribute("public.snk")] -[assembly: System.Reflection.AssemblyDelaySignAttribute(true)] - -namespace Microsoft.Singularity.Hal -{ - public class HalDevices - { - public static void Initialize(Processor rootProcessor); - - new public static void Finalize(); - - [NoHeapAllocation] - public static void EnableIoInterrupt(byte irq); - - [NoHeapAllocation] - public static void DisableIoInterrupt(byte irq); - - [NoHeapAllocation] - public static bool InternalInterrupt(byte interrupt); - - [NoHeapAllocation] - public static byte GetMaximumIrq(); - - [NoHeapAllocation] - public static int GetProcessorCount(); - - public static void StartApProcessors(); - - [NoHeapAllocation] - public static void FreezeProcessors(); - - [NoHeapAllocation] - public static void SendFixedIPI(byte vector, int from, int to); - - [NoHeapAllocation] - public static void BroadcastFixedIPI(byte vector, bool includeSelf); - - [NoHeapAllocation] - public static void ClearFixedIPI(); - } -} diff --git a/base/Interfaces/Hal/HalDevicesFactory.csi b/base/Interfaces/Hal/HalDevicesFactory.csi new file mode 100644 index 0000000..0dcba25 --- /dev/null +++ b/base/Interfaces/Hal/HalDevicesFactory.csi @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: HalDevicesFactory.csi +// +// Note: +// + +using System; +using System.Collections; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +using Microsoft.Singularity; +using Microsoft.Singularity.Hal; + +[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] +[assembly: System.Reflection.AssemblyKeyFileAttribute("public.snk")] +[assembly: System.Reflection.AssemblyDelaySignAttribute(true)] + +namespace Microsoft.Singularity.Hal +{ + // This is implemented by the platform HAL to create the HalDevices + [CLSCompliant(false)] + public class HalDevicesFactory + { + // Returns HalDevices, but can't forward reference it here + public static object Initialize(Processor rootProcessor); + } +} + diff --git a/base/Interfaces/Hal/HalPic.csi b/base/Interfaces/Hal/HalPic.csi deleted file mode 100644 index b1473e1..0000000 --- a/base/Interfaces/Hal/HalPic.csi +++ /dev/null @@ -1,66 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: HalPic.csi -// -// Note: -// - -using System; -using System.Collections; -using System.Runtime.CompilerServices; -using System.Threading; - -namespace Microsoft.Singularity.Hal -{ - public class HalPic - { - /// - /// Maximum valid IRQ property. On legacy PC systems this value is - /// 15. On APIC PC systems this number will usually be larger. - /// - public byte MaximumIrq - { - [NoHeapAllocation] get; - } - - /// - /// Convert interrupt vector to interrupt request line. - /// - [NoHeapAllocation] - public byte InterruptToIrq(byte interrupt); - - /// - /// Convert interrupt request line to interrupt vector. - /// - [NoHeapAllocation] - public byte IrqToInterrupt(byte irq); - - /// - /// Acknowledge the interrupt request. (EOI) - /// - [NoHeapAllocation] - public void AckIrq(byte irq); - - /// - /// Enable interrupt request by removing mask. - /// - [NoHeapAllocation] - public void EnableIrq(byte irq); - - /// - /// Disable interrupt request by applying mask. - /// - [NoHeapAllocation] - public void DisableIrq(byte irq); - - /// - /// Acknowledge and mask interrupt. - /// - [NoHeapAllocation] - public void ClearInterrupt(byte interrupt); - } -} // namespace Microsoft.Singularity.Hal diff --git a/base/Interfaces/Hal/HalScreen.csi b/base/Interfaces/Hal/HalScreen.csi deleted file mode 100644 index 06e37c0..0000000 --- a/base/Interfaces/Hal/HalScreen.csi +++ /dev/null @@ -1,64 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: HalScreen.csi -// -// Note: -// - -using System; -using System.Runtime.CompilerServices; - -[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyKeyFileAttribute("public.snk")] -[assembly: System.Reflection.AssemblyDelaySignAttribute(true)] - -namespace Microsoft.Singularity.Hal -{ - public class HalScreen - { - private HalScreen(); - - [NoHeapAllocation] - public void Clear(); - - [NoHeapAllocation] - public void GetDisplayDimensions(out int columns, out int rows); - - [NoHeapAllocation] - public void GetCursorPosition(out int column, out int row); - - [NoHeapAllocation] - public bool SetCursorPosition(int column, int row); - - [NoHeapAllocation] - public void SetCursorSizeLarge(); - - [NoHeapAllocation] - public void SetCursorSizeSmall(); - - [NoHeapAllocation] - public void Write(byte[] buffer, int offset, int count); - - [NoHeapAllocation] - public void PutChar(char c); - - [NoHeapAllocation] - public bool PutCharAt(char c, int column, int row); - - [NoHeapAllocation] - public void ClearCursorToEndOfLine(); - - [NoHeapAllocation] - public void CursorFlash(); - - [NoHeapAllocation] - public void CursorHide(); - - [NoHeapAllocation] - public void CursorShow(); - } -} // namespace Microsoft.Singularity.Hal diff --git a/base/Interfaces/Hal/HalTimer.csi b/base/Interfaces/Hal/HalTimer.csi deleted file mode 100644 index 6f827be..0000000 --- a/base/Interfaces/Hal/HalTimer.csi +++ /dev/null @@ -1,62 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: HalTimer.csi -// -// Note: -// - -using System; -using System.Runtime.CompilerServices; - -namespace Microsoft.Singularity.Hal -{ - public class HalTimer - { - /// - /// Clear interrupt associated with timer. - /// - [NoHeapAllocation] - public void ClearInterrupt(); - - /// - /// Maximum value accepted by SetNextInterrupt (in units of 100ns). - /// - public long MaxInterruptInterval { - [NoHeapAllocation] - get; - } - - /// - /// Minimum value accepted by SetNextInterrupt (in units of 100ns). - /// - public long MinInterruptInterval { - [NoHeapAllocation] - get; - } - - /// - /// Granularity of interrupt timeout (in units of 100ns). - /// - public long InterruptIntervalGranularity { - [NoHeapAllocation] - get; - } - - /// - /// Set relative time of next interrupt. - /// - /// Relative time of next interrupt in units - /// of 100ns. The time should be with the range between - /// from SetNextInterruptMinDelta to - /// SetNextInterruptMaxDelta. - /// true on success. - /// - [NoHeapAllocation] - public bool SetNextInterrupt(long delta); - } - -} // namespace Microsoft.Singularity.Hal diff --git a/base/Interfaces/Hal/Processor.csproj b/base/Interfaces/Hal/Processor.csproj index 57532b7..a8f7266 100644 --- a/base/Interfaces/Hal/Processor.csproj +++ b/base/Interfaces/Hal/Processor.csproj @@ -13,20 +13,7 @@ --> - - - - Processor - Library - true - true - kernel.exe - - - - kernel.exe + - - diff --git a/base/Interfaces/Hypercall/Hypercall.csi b/base/Interfaces/Hypercall/Hypercall.csi deleted file mode 100644 index 04a2e16..0000000 --- a/base/Interfaces/Hypercall/Hypercall.csi +++ /dev/null @@ -1,24 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Diagnostics.csi -// -// Note: -// - -using System; - -[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyKeyFileAttribute("public.snk")] -[assembly: System.Reflection.AssemblyDelaySignAttribute(true)] - -namespace Microsoft.Singularity.Hypercall -{ - public class ConnectionService - { - static public void Initialize(); - } -} // namespace Microsoft.Singularity.Hypercall diff --git a/base/Interfaces/Hypercall/Hypercall.csproj b/base/Interfaces/Hypercall/Hypercall.csproj deleted file mode 100644 index 832a91f..0000000 --- a/base/Interfaces/Hypercall/Hypercall.csproj +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - Hypercall - Library - true - true - - - - - kernel.exe - - - - - diff --git a/base/Interfaces/Interfaces.proj b/base/Interfaces/Interfaces.proj index d536690..bc78c12 100644 --- a/base/Interfaces/Interfaces.proj +++ b/base/Interfaces/Interfaces.proj @@ -17,30 +17,26 @@ - - - - - - - - + + + + + + + + - - - - - + diff --git a/base/Interfaces/IoSystem/IoConfig.csi b/base/Interfaces/IoSystem/IoConfig.csi index eff36c8..828ffb0 100644 --- a/base/Interfaces/IoSystem/IoConfig.csi +++ b/base/Interfaces/IoSystem/IoConfig.csi @@ -31,10 +31,6 @@ namespace Microsoft.Singularity.Io public abstract class IoRange { } - - public class IoMemory - { - } } namespace System.Threading @@ -43,10 +39,3 @@ namespace System.Threading { } } - -namespace Microsoft.Singularity.Xml -{ - public class XmlNode - { - } -} diff --git a/base/Interfaces/IoSystem/IoConfig.csproj b/base/Interfaces/IoSystem/IoConfig.csproj index 1c160b8..819e172 100644 --- a/base/Interfaces/IoSystem/IoConfig.csproj +++ b/base/Interfaces/IoSystem/IoConfig.csproj @@ -13,20 +13,7 @@ --> - - - - IoConfig - Library - true - true - kernel.exe - - - - kernel.exe + - - diff --git a/base/Interfaces/IoSystem/IoSystem.csi b/base/Interfaces/IoSystem/IoSystem.csi index d8ccfc3..0350a5a 100644 --- a/base/Interfaces/IoSystem/IoSystem.csi +++ b/base/Interfaces/IoSystem/IoSystem.csi @@ -10,6 +10,7 @@ // using System; +using System.Collections; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Threading; @@ -28,8 +29,7 @@ namespace Microsoft.Singularity.Io void Finalize(); } - public delegate IDevice IoDeviceCreate(IoConfig config, - String instanceName); + public delegate IDevice IoDeviceCreate(IoConfig config, String instanceName); public class IoSystem { @@ -39,6 +39,8 @@ namespace Microsoft.Singularity.Io public static bool RegisterKernelDriver(Type type, IoDeviceCreate creator); + public static bool AddDevicesToTree(SortedList found, string busLocation, bool associate); + // device management public static void RegisterDrivers(); public static void ActivateDrivers(); diff --git a/base/Interfaces/IoSystem/IoSystem.csproj b/base/Interfaces/IoSystem/IoSystem.csproj index 2bcbeec..416861a 100644 --- a/base/Interfaces/IoSystem/IoSystem.csproj +++ b/base/Interfaces/IoSystem/IoSystem.csproj @@ -18,6 +18,7 @@ IoSystem Library + $(KERNEL_IL_DIR) true true @@ -25,13 +26,10 @@ - - kernel.exe - kernel.exe - kernel.exe - kernel.exe + + kernel - + diff --git a/base/Interfaces/KdFiles/KdFiles.csproj b/base/Interfaces/KdFiles/KdFiles.csproj new file mode 100644 index 0000000..8ce726d --- /dev/null +++ b/base/Interfaces/KdFiles/KdFiles.csproj @@ -0,0 +1,33 @@ + + + + + + + Singularity.KdFiles + Library + $(KERNEL_IL_DIR) + true + + + + + + kernel + + + + + diff --git a/base/Interfaces/KdFiles/KdFilesNamespace.csi b/base/Interfaces/KdFiles/KdFilesNamespace.csi new file mode 100644 index 0000000..bf02ab9 --- /dev/null +++ b/base/Interfaces/KdFiles/KdFilesNamespace.csi @@ -0,0 +1,19 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; + +[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] +[assembly: System.Reflection.AssemblyKeyFileAttribute("public.snk")] +[assembly: System.Reflection.AssemblyDelaySignAttribute(true)] + +namespace Microsoft.Singularity.KernelDebugger +{ + public class KdFilesNamespace + { + public static void StartNamespaceThread(); + } +} diff --git a/base/Interfaces/Kernel/Kernel.csproj b/base/Interfaces/Kernel/Kernel.csproj index 472dddb..e2d462d 100644 --- a/base/Interfaces/Kernel/Kernel.csproj +++ b/base/Interfaces/Kernel/Kernel.csproj @@ -18,17 +18,25 @@ Kernel Library + $(KERNEL_IL_DIR) true true - kernel.exe + + + + + + + + + - kernel.exe diff --git a/base/Interfaces/Loader/Binder.csi b/base/Interfaces/Loader/Binder.csi index 4e49f21..9747476 100644 --- a/base/Interfaces/Loader/Binder.csi +++ b/base/Interfaces/Loader/Binder.csi @@ -30,6 +30,7 @@ namespace Microsoft.Singularity.Loader /// public static void Initialize(XmlNode config); + public static void RedirectRootRef(); public static IoMemory LoadImage(Process parent, String application, out Manifest man); public static IoMemory LoadRawImage(string folderName, string exeName); diff --git a/base/Interfaces/Loader/Loader.csproj b/base/Interfaces/Loader/Loader.csproj index 33b810e..cb4e03f 100644 --- a/base/Interfaces/Loader/Loader.csproj +++ b/base/Interfaces/Loader/Loader.csproj @@ -18,6 +18,7 @@ Loader Library + $(KERNEL_IL_DIR) true true @@ -26,10 +27,7 @@ - - - - + kernel diff --git a/base/Interfaces/Loader/Manifest.csi b/base/Interfaces/Loader/Manifest.csi index ff7d76f..bb170f1 100644 --- a/base/Interfaces/Loader/Manifest.csi +++ b/base/Interfaces/Loader/Manifest.csi @@ -1,7 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // +// ---------------------------------------------------------------------------- using System; using Microsoft.Singularity.Memory; diff --git a/base/Interfaces/Memory/Memory.csproj b/base/Interfaces/Memory/Memory.csproj index bc0e0c2..7bdf187 100644 --- a/base/Interfaces/Memory/Memory.csproj +++ b/base/Interfaces/Memory/Memory.csproj @@ -13,22 +13,8 @@ --> - - - - Memory - Library - true - true - kernel.exe - - - - - kernel.exe + + - - - diff --git a/base/Interfaces/NVidiaDrivers/NVidiaDrivers.csi b/base/Interfaces/NVidiaDrivers/NVidiaDrivers.csi deleted file mode 100644 index c119c5b..0000000 --- a/base/Interfaces/NVidiaDrivers/NVidiaDrivers.csi +++ /dev/null @@ -1,24 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: NVidiaDrivers.csi -// -// Note: Driver interfaces for NVidia drivers. -// - -using System; - -[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyKeyFileAttribute("public.snk")] -[assembly: System.Reflection.AssemblyDelaySignAttribute(true)] - -namespace Microsoft.Singularity.Drivers -{ - public class NvPciLpcBridge - { - public static void Register(); - } -} diff --git a/base/Interfaces/NVidiaDrivers/NVidiaDrivers.csproj b/base/Interfaces/NVidiaDrivers/NVidiaDrivers.csproj deleted file mode 100644 index 3993314..0000000 --- a/base/Interfaces/NVidiaDrivers/NVidiaDrivers.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - NVidiaDrivers - Library - true - true - - - - - kernel.exe - - - - - diff --git a/base/Interfaces/Naming/Directory.csproj b/base/Interfaces/Naming/Directory.csproj index d579e39..2b46330 100644 --- a/base/Interfaces/Naming/Directory.csproj +++ b/base/Interfaces/Naming/Directory.csproj @@ -14,20 +14,15 @@ Directory Library + $(KERNEL_IL_DIR) true true - kernel.exe - kernel.exe - kernel.exe - kernel.exe - kernel.exe - diff --git a/base/Interfaces/Naming/SharedHeap.csproj b/base/Interfaces/Naming/SharedHeap.csproj deleted file mode 100644 index 3801a7e..0000000 --- a/base/Interfaces/Naming/SharedHeap.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - SharedHeap - Library - true - true - kernel.exe - - - - - kernel.exe - - kernel.exe - - - - - diff --git a/base/Interfaces/Scheduling/Scheduling.csproj b/base/Interfaces/Scheduling/Scheduling.csproj index 409669e..ab097ab 100644 --- a/base/Interfaces/Scheduling/Scheduling.csproj +++ b/base/Interfaces/Scheduling/Scheduling.csproj @@ -18,13 +18,14 @@ Scheduling Library + $(KERNEL_IL_DIR) true true - kernel.exe + kernel diff --git a/base/Interfaces/Security/Principal.csi b/base/Interfaces/Security/Principal.csi index 82e74d2..73bc327 100644 --- a/base/Interfaces/Security/Principal.csi +++ b/base/Interfaces/Security/Principal.csi @@ -1,7 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // +// ---------------------------------------------------------------------------- using System; diff --git a/base/Interfaces/Security/PrincipalImpl.csi b/base/Interfaces/Security/PrincipalImpl.csi index 62b4088..e3a3998 100644 --- a/base/Interfaces/Security/PrincipalImpl.csi +++ b/base/Interfaces/Security/PrincipalImpl.csi @@ -1,7 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // +// ---------------------------------------------------------------------------- using System; using Microsoft.Singularity.Loader; @@ -19,7 +20,10 @@ namespace Microsoft.Singularity.Security // this is a kernel-only constructor for the type Principal public static void Initialize(XmlNode config); - public static Principal NewInvocation(Principal parent, Manifest manifest, string role, IoMemory rawImage); + public static Principal NewInvocation(Principal parent, Manifest manifest, + string role, IoMemory rawImage); + // in the following, delegate may not already be a delegate + public static Principal NewDelegation(Principal delegator, Principal target); public static void Dispose(Principal pr); public static string ExpandAclIndirection(string name); diff --git a/base/Interfaces/Security/Security.csproj b/base/Interfaces/Security/Security.csproj index 1225bea..52d140d 100644 --- a/base/Interfaces/Security/Security.csproj +++ b/base/Interfaces/Security/Security.csproj @@ -18,6 +18,7 @@ Security Library + $(KERNEL_IL_DIR) true true @@ -26,12 +27,8 @@ - kernel.exe - - - kernel.exe - kernel.exe - kernel.exe + kernel + Loader diff --git a/base/Interfaces/Shell/Shell.csproj b/base/Interfaces/Shell/Shell.csproj index f05c0e6..125489a 100644 --- a/base/Interfaces/Shell/Shell.csproj +++ b/base/Interfaces/Shell/Shell.csproj @@ -18,13 +18,14 @@ Shell Library + $(KERNEL_IL_DIR) true true - kernel.exe + kernel diff --git a/base/Interfaces/Singularity.V1/Services/ChannelService.csi b/base/Interfaces/Singularity.V1/Services/ChannelService.csi index 0edd4a5..8ed70fa 100644 --- a/base/Interfaces/Singularity.V1/Services/ChannelService.csi +++ b/base/Interfaces/Singularity.V1/Services/ChannelService.csi @@ -29,7 +29,7 @@ namespace Microsoft.Singularity.V1.Services /// the trusted caller (currently trusted code NewChannel) /// [NoHeapAllocation] - public static Allocation* /*EndpointCore* opt(ExHeap)!*/ + public static Allocation* //EndpointCore* opt(ExHeap)! Allocate(uint size, SystemType st); /// @@ -55,7 +55,8 @@ namespace Microsoft.Singularity.V1.Services [NoHeapAllocation] public static void Connect( Allocation* /*EndpointCore* opt(ExHeap)!*/ imp, - Allocation* /*EndpointCore* opt(ExHeap)!*/ exp); + Allocation* /*EndpointCore* opt(ExHeap)!*/ exp, + Allocation* /*EndpointCore* opt(ExHeap)!*/ ep); /// /// Indicates if this endpoint is closed @@ -69,6 +70,13 @@ namespace Microsoft.Singularity.V1.Services [NoHeapAllocation] public static bool PeerClosed(ref EndpointCore ep); + /// + /// Indicates if this endpoint is closed (ABI call version) + /// + [NoHeapAllocation] + public static bool PeerClosedABI(ref EndpointCore ep); + + /// /// Set this endpoint to closed /// @@ -82,6 +90,13 @@ namespace Microsoft.Singularity.V1.Services public static Allocation* /*EndpointCore* opt(ExHeap) */ GetPeer(ref EndpointCore ep, out bool marshall); + /// + /// The endpoint to which this endpoint is connected. (ABI version) + /// + [NoHeapAllocation] + public static Allocation* GetPeerABI(ref EndpointCore ep, + out bool marshall); + /// /// The event to wait for messages on this endpoint. Used by Select. /// @@ -116,6 +131,13 @@ namespace Microsoft.Singularity.V1.Services ref EndpointCore transferee, ref EndpointCore target); + [NoHeapAllocation] + public static void AcceptDelegation(Allocation* /*EndpointCore* opt(ExHeap)!*/ imp, + Allocation* /*EndpointCore* opt(ExHeap)!*/ exp, + Allocation* /*EndpointCore* opt(ExHeap)!*/ ep); + + [NoHeapAllocation] + public static void EnableDelegation(ref EndpointCore ep, bool allowMediation); /// /// Obtain the process identifier of the owner. @@ -135,6 +157,13 @@ namespace Microsoft.Singularity.V1.Services [NoHeapAllocation] public static int GetPeerProcessID(ref EndpointCore ep); + /// + /// Obtain the process identifier of the owner of the other endpoint (ABI version) + /// + [NoHeapAllocation] + public static int GetPeerProcessIDABI(ref EndpointCore ep); + + /// /// Obtain the principal identifier of the owner of the other endpoint /// @@ -173,13 +202,9 @@ namespace Microsoft.Singularity.V1.Services /// end with a call to NotifyPeer. /// [NoHeapAllocation] - unsafe public static void MarshallMessage(ref EndpointCore ep, - byte* basep, byte* source, - int* tagAddress, int size); + unsafe public static void MarshallMessage(ref EndpointCore ep, byte* basep, byte* source, int* tagAddress, int msgSize); [NoHeapAllocation] - unsafe public static void MarshallPointer(ref EndpointCore ep, - byte* basep, byte** target, SystemType type); - + unsafe public static void MarshallPointer(ref EndpointCore ep, byte* basep, byte** target, SystemType type, byte* parent, int offset); } } diff --git a/base/Interfaces/Singularity.V1/Services/DeliveryHandle.csi b/base/Interfaces/Singularity.V1/Services/DeliveryHandle.csi new file mode 100644 index 0000000..6c39443 --- /dev/null +++ b/base/Interfaces/Singularity.V1/Services/DeliveryHandle.csi @@ -0,0 +1,24 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity - Singularity ABI +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: DeliveryHandle.csi +// +// Note: +// + +using System; +using System.Runtime.CompilerServices; + +namespace Microsoft.Singularity.V1.Services +{ + public struct DeliveryHandle + { + public readonly UIntPtr id; + + [NoHeapAllocation] + public static void Dispose(ref DeliveryHandle handle); + } +} diff --git a/base/Interfaces/Singularity.V1/Services/DeviceService.csi b/base/Interfaces/Singularity.V1/Services/DeviceService.csi index 0c0def3..8e311a7 100644 --- a/base/Interfaces/Singularity.V1/Services/DeviceService.csi +++ b/base/Interfaces/Singularity.V1/Services/DeviceService.csi @@ -22,9 +22,7 @@ namespace Microsoft.Singularity.V1.Services uint maxout); [NoHeapAllocation] - public static bool GetPciConfig(out ushort pciAddressPort, - out ushort pciDataPort, - out ushort identifier); + public static unsafe bool GetPciPort(out PciPortHandle handle); [NoHeapAllocation] public static int GetIrqCount(byte line); diff --git a/base/Interfaces/Singularity.V1/Services/DiagnosisService.csi b/base/Interfaces/Singularity.V1/Services/DiagnosisService.csi new file mode 100644 index 0000000..bd3ab67 --- /dev/null +++ b/base/Interfaces/Singularity.V1/Services/DiagnosisService.csi @@ -0,0 +1,127 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity - Singularity ABI +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: DiagnosisService.csi +// +// Note: +// + +using System; +using System.Runtime.CompilerServices; + +namespace Microsoft.Singularity.V1.Services +{ + public struct DiagnosisService + { + [NoHeapAllocation] + public static unsafe int GCProfileSettings( + ulong *defaultMemorySize, + ulong *Options); + + public static int GCProfileSettingsImpl( + out ulong defaultMemorySize, + out ulong Options); + + [NoHeapAllocation] + public static bool RegisterEventingController( + UIntPtr controllerHandle, + UIntPtr executionContextHandle); + + [NoHeapAllocation] + public static bool DebugPrintLogEntry(UIntPtr controllerHandle, + UIntPtr entryHandle); + + public static bool TestKernelStorage(); + + public static UIntPtr OpenGlobalStorage(UIntPtr storageId); + + [NoHeapAllocation] + public static void CloseGlobalStorage(UIntPtr storageHandle); + + [NoHeapAllocation] + public static unsafe UIntPtr LogSourceEntry(UIntPtr sourceHandle, + uint flags, + UIntPtr eventType, + byte * buffer, + int size); + + [NoHeapAllocation] + public static unsafe UIntPtr LogSourceEntry(UIntPtr sourceHandle, + uint flags, + UIntPtr eventType, + byte * buffer, + int size, + int stringsCount, + void ** strings); + + [NoHeapAllocation] + public static UIntPtr CreateQueryView(UIntPtr storageHandle, bool forward); + + [NoHeapAllocation] + public static void DeleteQueryView(UIntPtr storageHandle); + + [NoHeapAllocation] + public static unsafe UIntPtr GetNextEntry(UIntPtr queryHandle, + UIntPtr * type, + UInt32 * userOffset, + byte * buffer, + UInt16 bufferSize ); + [NoHeapAllocation] + public static bool RegisterEvent(char * eventName, char * eventDescription, UIntPtr * eventHandle); + + [NoHeapAllocation] + public static bool RegisterEventField(UIntPtr eventHandle, + char * fieldName, + UInt16 offset, + UInt16 type); + + [NoHeapAllocation] + public static bool RegisterEventGenericField(UIntPtr eventHandle, + char * fieldName, + UInt16 offset, + UInt16 size, + UIntPtr fieldTypeHandle); + + + [NoHeapAllocation] + public static bool RegisterEnum(char * enumName, UInt16 type, UIntPtr * eventHandle); + + [NoHeapAllocation] + public static bool RegisterEnumValue(UIntPtr eventHandle, + char * valueName, + UInt64 value, + byte flagChar); + + [NoHeapAllocation] + public static unsafe UIntPtr WalkEventDescriptor(UIntPtr eventHandle, + UIntPtr currentField, + UInt16 * offset, + UInt16 * type, + char * bufferName, + UInt16 bufferSize ); + [NoHeapAllocation] + public static unsafe bool GetSourceInformation(UIntPtr sourceHandle, + UIntPtr * storageHandle, + UIntPtr * eventType, + UInt16 * count, + char * bufferName, + UInt16 bufferSize ); + + [NoHeapAllocation] + public static unsafe int QuerySourcesList(UIntPtr * buffer, int size); + + [NoHeapAllocation] + public static unsafe int QueryEventTypeList(UIntPtr * buffer, int size); + + [NoHeapAllocation] + public static unsafe bool ReadActiveSourceItem(UIntPtr sourceHandle, + int item, + UIntPtr * type, + byte * buffer, + UInt16 bufferSize ); + + } +} diff --git a/base/Interfaces/Singularity.V1/Services/MemoryInfoService.csi b/base/Interfaces/Singularity.V1/Services/MemoryInfoService.csi new file mode 100644 index 0000000..a7c6298 --- /dev/null +++ b/base/Interfaces/Singularity.V1/Services/MemoryInfoService.csi @@ -0,0 +1,43 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity - Singularity ABI +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: MemoryInfoService.csi +// +// Note: +// + +using System.Runtime.CompilerServices; + +namespace Microsoft.Singularity.V1.Services +{ + public struct MemoryInfoService + { + // What are the error status codes? + [NoHeapAllocation] + public static unsafe int MemoryUsageInfo( + ulong *totalMemoryFree, + ulong *totalMemoryInUse, + ulong *kernelHeapInUse, + ulong *kernelStackInUse, + ulong *totalSIPHeapInUse, + ulong *totalSIPStackInUse, + ulong *kernelStackReservation, + ulong *kernelHeapReservation + ); + + public static int MemoryUsageInfo( + out ulong totalMemoryFree, + out ulong totalMemoryInUse, + out ulong kernelHeapInUse, + out ulong kernelStackInUse, + out ulong totalSIPHeapInUse, + out ulong totalSIPStackInUse, + out ulong kernelStackReservation, + out ulong kernelHeapReservation + ); + } +} + diff --git a/base/Interfaces/Singularity.V1/Services/PciPortHandle.csi b/base/Interfaces/Singularity.V1/Services/PciPortHandle.csi new file mode 100644 index 0000000..c4a537b --- /dev/null +++ b/base/Interfaces/Singularity.V1/Services/PciPortHandle.csi @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity - Singularity ABI +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: PciPortHandle.csi +// +// Note: +// + +using System; +using System.Runtime.CompilerServices; + +namespace Microsoft.Singularity.V1.Services +{ + public struct PciPortHandle + { + public readonly UIntPtr id; + + [NoHeapAllocation] + public static bool CreateImpl(PciPortHandle* h); + + [NoHeapAllocation] + public static bool Dispose(PciPortHandle h); + + public static bool Read8Impl(PciPortHandle h, int offset, byte* value); + public static bool Read16Impl(PciPortHandle h, int offset, ushort* value); + public static bool Read32Impl(PciPortHandle h, int offset, uint* value); + + public static bool Write8(PciPortHandle h, int offset, byte value); + public static bool Write16(PciPortHandle h, int offset, ushort value); + public static bool Write32(PciPortHandle h, int offset, uint value); + } +} diff --git a/base/Interfaces/Singularity.V1/Services/PlatformService.csi b/base/Interfaces/Singularity.V1/Services/PlatformService.csi new file mode 100644 index 0000000..c0b7de6 --- /dev/null +++ b/base/Interfaces/Singularity.V1/Services/PlatformService.csi @@ -0,0 +1,50 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity - Singularity ABI +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: DebugService.cs +// +// Note: +// + +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.Singularity; +using Microsoft.Singularity.Memory; + +namespace Microsoft.Singularity.V1.Services +{ + public struct PlatformService + { + [NoHeapAllocation] + public static bool DisableInterrupts(); + + [NoHeapAllocation] + public static void RestoreInterrupts(bool enabled); + + [NoHeapAllocation] + public static bool InterruptsDisabled(); + + [NoHeapAllocation] + [CLSCompliant(false)] + public static void CleanAndInvalidateDCache(UIntPtr addr, UIntPtr length); + + [NoHeapAllocation] + [CLSCompliant(false)] + public static void InvalidateDCache(UIntPtr addr, UIntPtr length); + + [NoHeapAllocation] + [CLSCompliant(false)] + public static void SetCacheAttributes(UIntPtr addr, UIntPtr length, + bool cacheable, bool bufferable); + + [NoHeapAllocation] + public static int GetProcessorContextOffset(); + + [NoHeapAllocation] + public static int GetThreadContextOffset(); + } +} diff --git a/base/Interfaces/Singularity.V1/Services/ProcessService.csi b/base/Interfaces/Singularity.V1/Services/ProcessService.csi index 481a9f2..7dc9374 100644 --- a/base/Interfaces/Singularity.V1/Services/ProcessService.csi +++ b/base/Interfaces/Singularity.V1/Services/ProcessService.csi @@ -41,6 +41,8 @@ namespace Microsoft.Singularity.V1.Services [NoHeapAllocation] public static DateTime GetUtcTime(); [NoHeapAllocation] + public static int GetCpuCount(); + [NoHeapAllocation] public static long GetCycleCount(); [NoHeapAllocation] public static long GetContextSwitchCount(); @@ -83,15 +85,13 @@ namespace Microsoft.Singularity.V1.Services public static unsafe int GetStartupArg(int arg, char * output, int maxout); [NoHeapAllocation] - public static unsafe void GetTracingHeaders(out LogEntry *logBegin, - out LogEntry *logLimit, - out LogEntry **logHead, - out byte *txtBegin, - out byte *txtLimit, - out byte **txtHead); + public static long GetStorageHandle(); [NoHeapAllocation] - public static unsafe void GetMonitoringHeaders(out byte * _buffer); + public static unsafe bool GetSharedSourceHandles(uint infoId, + out UIntPtr storageHandle, + out UIntPtr sourceHandle, + out UIntPtr eventTypeHandle); [NoHeapAllocation] public static void SetGcPerformanceCounters(TimeSpan spent, long bytes); @@ -134,23 +134,5 @@ namespace Microsoft.Singularity.V1.Services out int arrayLength, out int totalCharCount ); - - // haryadi -- ping pong - [NoHeapAllocation] - public static int RunPingPongInt(int start); - - [NoHeapAllocation] - public static int HelloProcessABI(int num, int num2); - - [ NoHeapAllocation ] - public static unsafe ulong TestAbiCallOne(ulong a); - - [ NoHeapAllocation ] - public static unsafe int TestAbiCallTwo(uint a, char *b); - - [ NoHeapAllocation ] - public static unsafe char* TestAbiCallThree(int a, int *b, byte c); - - } } diff --git a/base/Interfaces/Singularity.V1/Services/StackService.csi b/base/Interfaces/Singularity.V1/Services/StackService.csi index 85afb3c..3ab412e 100644 --- a/base/Interfaces/Singularity.V1/Services/StackService.csi +++ b/base/Interfaces/Singularity.V1/Services/StackService.csi @@ -9,6 +9,7 @@ // Note: // +using System; using System.Runtime.CompilerServices; namespace Microsoft.Singularity.V1.Services @@ -23,60 +24,13 @@ namespace Microsoft.Singularity.V1.Services public static void WalkStack(); [NoHeapAllocation] public static void GetUsageStatistics(out ulong gets, out ulong returns); + [NoHeapAllocation] + public static void StackOverflow(); [NoHeapAllocation] - public static void LinkStack0(); - [NoHeapAllocation] - public static void LinkStack4(); - [NoHeapAllocation] - public static void LinkStack8(); - [NoHeapAllocation] - public static void LinkStack12(); - [NoHeapAllocation] - public static void LinkStack16(); - [NoHeapAllocation] - public static void LinkStack20(); - [NoHeapAllocation] - public static void LinkStack24(); - [NoHeapAllocation] - public static void LinkStack28(); - [NoHeapAllocation] - public static void LinkStack32(); - [NoHeapAllocation] - public static void LinkStack36(); - [NoHeapAllocation] - public static void LinkStack40(); - [NoHeapAllocation] - public static void LinkStack44(); - [NoHeapAllocation] - public static void LinkStack48(); - [NoHeapAllocation] - public static void LinkStack52(); - [NoHeapAllocation] - public static void LinkStack56(); - [NoHeapAllocation] - public static void LinkStack60(); - [NoHeapAllocation] - public static void LinkStack64(); + public static unsafe UIntPtr AllocateStackSegment(UIntPtr growSize); -#if PAGING - // For use from the ring-3 code in halstack.asm [NoHeapAllocation] - public static unsafe UIntPtr GetStackSegmentAndCopy( - UIntPtr size, - ref ThreadContext context, - uint *arg2, - uint args, - UIntPtr esp, - UIntPtr begin, - UIntPtr limit); - - // For use from the ring-3 code in halstack.asm - [NoHeapAllocation] - public static unsafe void ReturnStackSegmentRaw( - ref ThreadContext context, - UIntPtr begin, - UIntPtr limit); -#endif + public static unsafe void FreeStackSegment(); } } diff --git a/base/Interfaces/Singularity.V1/Singularity.V1.App.csproj b/base/Interfaces/Singularity.V1/Singularity.V1.App.csproj new file mode 100644 index 0000000..5aea650 --- /dev/null +++ b/base/Interfaces/Singularity.V1/Singularity.V1.App.csproj @@ -0,0 +1,57 @@ + + + + + + + Singularity.V1.App + Library + $(APPILLSDIR) + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Corlib + + + + + diff --git a/base/Interfaces/Singularity.V1/Singularity.V1.Kernel.csproj b/base/Interfaces/Singularity.V1/Singularity.V1.Kernel.csproj new file mode 100644 index 0000000..3bf37c4 --- /dev/null +++ b/base/Interfaces/Singularity.V1/Singularity.V1.Kernel.csproj @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Interfaces/Singularity.V1/Singularity.V1.csproj b/base/Interfaces/Singularity.V1/Singularity.V1.csproj deleted file mode 100644 index e3f80e4..0000000 --- a/base/Interfaces/Singularity.V1/Singularity.V1.csproj +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - Singularity.V1 - Library - true - true - - - - - - - - - - - - - - - - - - - - - - - - corlib.exe - corlib.exe - - - - - diff --git a/base/Interfaces/Singularity.V1/Threads/InterruptHandle.csi b/base/Interfaces/Singularity.V1/Threads/InterruptHandle.csi index 0841df8..4f5a6ff 100644 --- a/base/Interfaces/Singularity.V1/Threads/InterruptHandle.csi +++ b/base/Interfaces/Singularity.V1/Threads/InterruptHandle.csi @@ -16,6 +16,7 @@ namespace Microsoft.Singularity.V1.Threads { public struct InterruptHandle // : public WaitHandle { + public readonly UIntPtr id; // could be moved to WaitHandle [NoHeapAllocation] @@ -33,5 +34,15 @@ namespace Microsoft.Singularity.V1.Threads [NoHeapAllocation] public static bool Ack(InterruptHandle handle); + + [NoHeapAllocation] + public static bool DisableInterrupts(); + + [NoHeapAllocation] + public static void RestoreInterrupts(bool enabled); + + // Use this method for assertions only! + [NoHeapAllocation] + public static bool InterruptsDisabled(); } } diff --git a/base/Interfaces/Singularity.V1/Threads/ThreadHandle.csi b/base/Interfaces/Singularity.V1/Threads/ThreadHandle.csi index 46119fd..de946d1 100644 --- a/base/Interfaces/Singularity.V1/Threads/ThreadHandle.csi +++ b/base/Interfaces/Singularity.V1/Threads/ThreadHandle.csi @@ -35,6 +35,11 @@ namespace Microsoft.Singularity.V1.Threads [NoHeapAllocation] public static TimeSpan GetExecutionTime(ThreadHandle thread); + [NoHeapAllocation] + public static int GetAffinity(ThreadHandle thread); + [NoHeapAllocation] + public static void SetAffinity(ThreadHandle thread, int val); + [NoHeapAllocation] public static bool Join(ThreadHandle thread); [NoHeapAllocation] diff --git a/base/Interfaces/Singularity.V1/Threads/ThreadState.csi b/base/Interfaces/Singularity.V1/Threads/ThreadState.csi index a0388dd..75787fc 100644 --- a/base/Interfaces/Singularity.V1/Threads/ThreadState.csi +++ b/base/Interfaces/Singularity.V1/Threads/ThreadState.csi @@ -17,10 +17,12 @@ namespace Microsoft.Singularity.V1.Threads [Flags] public enum ThreadState { - Unstarted = 0x00, - Running = 0x01, - Blocked = 0x02, - Suspended = 0x04, - Stopped = 0x08, + Undefined = 0x0, + Running = 0x1, + Unstarted = 0x2, + Stopped = 0x4, + Suspended = 0x8, + Blocked = 0x10, + Runnable = 0x20 } } diff --git a/base/Interfaces/Stress/Stress.csproj b/base/Interfaces/Stress/Stress.csproj index c7b287c..bd2525c 100644 --- a/base/Interfaces/Stress/Stress.csproj +++ b/base/Interfaces/Stress/Stress.csproj @@ -18,6 +18,7 @@ Stress Library + $(KERNEL_IL_DIR) true true @@ -26,9 +27,7 @@ - kernel.exe - kernel.exe - kernel.exe + kernel diff --git a/base/Interfaces/Xml/Xml.csproj b/base/Interfaces/Xml/Xml.csproj index 7903263..d081b1a 100644 --- a/base/Interfaces/Xml/Xml.csproj +++ b/base/Interfaces/Xml/Xml.csproj @@ -13,23 +13,7 @@ --> - - - - Xml - Library - true - true - kernel.exe - - - - kernel.exe - - kernel.exe - + - - diff --git a/base/Kernel/App.Corlib.csproj b/base/Kernel/App.Corlib.csproj deleted file mode 100644 index c3d92bf..0000000 --- a/base/Kernel/App.Corlib.csproj +++ /dev/null @@ -1,310 +0,0 @@ - - - - - - - true - true - true - false - SINGULARITY_PROCESS;ENDPOINT_STRUCT - 169,649 - Corlib - Library - none - ..\Applications\Runtime - ..\Libraries - C# - true - {F4C22616-B154-496C-A4CF-C49401E52C87} - - - - - - $(DefineConstants);PAGING - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - $(COLLECTOR_APP) - true - - - - - diff --git a/base/Kernel/Bartok/GC.cs b/base/Kernel/Bartok/GC.cs deleted file mode 100644 index b8a4200..0000000 --- a/base/Kernel/Bartok/GC.cs +++ /dev/null @@ -1,616 +0,0 @@ -/*******************************************************************/ -/* WARNING */ -/* This file should be identical in the Bartok and Singularity */ -/* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ -/*******************************************************************/ - -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -namespace System -{ - using Microsoft.Bartok.Runtime; - using System.GCs; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Threading; - -#if SINGULARITY - using Microsoft.Singularity; - using Microsoft.Singularity.X86; -#endif - - // The GC has only static members and doesn't require the serializable - // keyword. - [CCtorIsRunDuringStartup] - [RequiredByBartok] - [CLSCompliant(false)] - public sealed class GC - { - - // Bartok runtime "magic" function - // It saves the callee-save registers in a transition record and - // calls System.GC.CollectBody(thread, generation) - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.GCFRIEND)] - [StackBound(128)] - private static extern void CollectBodyTransition(Thread thread, - int generation); - internal static int gcTotalCount; - internal static long gcTotalTime; - internal static long maxPauseTime; - internal static long pauseCount; - internal static long gcTotalBytes; - internal static ulong bytesAllocated; - internal static ulong objectsAllocated; -#if SINGULARITY_KERNEL - internal static uint perfCounter = 6; -#else - internal static uint perfCounter = 5; -#endif - - [RequiredByBartok] - [TrustedNonNull] - internal static Collector installedGC; - - private static bool isProfiling; - - private static Object dummyGlobal; // Used by KeepAlive - - [AccessedByRuntime("referenced in halasm.asm/brtasm.asm")] - internal static bool allocationInhibitGC = false; - - internal static UIntPtr newBytesSinceGC; - - [Intrinsic] - internal static GCType gcType; - - [Intrinsic] - internal static WBType wbType; - - [Intrinsic] - internal static RemSetType remsetType; - - [Intrinsic] - internal static CopyScanType copyscanType; - -#if !SINGULARITY - [NoStackLinkCheck] -#endif - [PreInitRefCounts] - internal static void ConstructHeap() { - PageTable.Initialize(); - - MemoryManager.Initialize(); -#if SINGULARITY - UIntPtr heap_commit_size = new UIntPtr(1 << 16); -#else - UIntPtr heap_commit_size = new UIntPtr(1 << 20); -#endif - UIntPtr os_commit_size = MemoryManager.OperatingSystemCommitSize; - VTable.Assert(os_commit_size > UIntPtr.Zero); - VTable.Assert(heap_commit_size >= os_commit_size); - UIntPtr bootstrapSize = - UIntPtr.Size == 8 - ? (UIntPtr) 1 << 15 - : (UIntPtr) 1 << 14; - if (bootstrapSize < os_commit_size) { - bootstrapSize = os_commit_size; - } - BootstrapMemory.Initialize(bootstrapSize); - StaticData.Initialize(); - PageManager.Initialize(os_commit_size, heap_commit_size); - } - - // Called after the GC is up, but before multi-threading is enabled. - internal static void FinishInitializeThread() - { - PageManager.FinishInitializeThread(); - } - - // NB: This is called from VTable.Initialize() - [PreInitRefCounts] - static GC() // Class Constructor (cctor) - { - switch(gcType) { -#if !SINGULARITY || ADAPTIVE_COPYING_COLLECTOR - case GCType.AdaptiveCopyingCollector: { - AdaptiveCopyingCollector.Initialize(); - GC.installedGC = AdaptiveCopyingCollector.instance; - break; - } -#endif -#if !SINGULARITY || MARK_SWEEP_COLLECTOR - case GCType.MarkSweepCollector: { - MarkSweepCollector.Initialize(); - GC.installedGC = MarkSweepCollector.instance; - break; - } -#endif -#if !SINGULARITY || TABLE_MARK_SWEEP_COLLECTOR - case GCType.TableMarkSweepCollector: { - SimpleMarkSweepCollector.Initialize(); - GC.installedGC = SimpleMarkSweepCollector.instance; - break; - } -#endif -#if !SINGULARITY || SEMISPACE_COLLECTOR - case GCType.SemispaceCollector: { - SemispaceCollector.Initialize(); - GC.installedGC = SemispaceCollector.instance; - break; - } -#endif -#if !SINGULARITY || SLIDING_COLLECTOR - case GCType.SlidingCollector: { - SlidingCollector.Initialize(); - GC.installedGC = SlidingCollector.instance; - break; - } -#endif -#if !SINGULARITY || CONCURRENT_MS_COLLECTOR - case GCType.ConcurrentMSCollector: { - ConcurrentMSCollector.Initialize(); - GC.installedGC = ConcurrentMSCollector.instance; - break; - } -#endif -#if !SINGULARITY || ATOMIC_RC_COLLECTOR - case GCType.AtomicRCCollector: { - AtomicRCCollector.Initialize(); - GC.installedGC = AtomicRCCollector.instance; - break; - } -#endif -#if !SINGULARITY - case GCType.ReferenceCountingCollector: { - ReferenceCountingCollector.Initialize(); - GC.installedGC = ReferenceCountingCollector.instance; - break; - } -#endif -#if !SINGULARITY - case GCType.DeferredReferenceCountingCollector: { - DeferredReferenceCountingCollector.Initialize(); - GC.installedGC = DeferredReferenceCountingCollector.instance; - break; - } -#endif -#if !SINGULARITY - case GCType.NullCollector: { - VTable.Assert(wbType == 0, "No need for a write barrier"); - GC.Initialize(); - GC.installedGC = - (NullCollector) - BootstrapMemory.Allocate(typeof(NullCollector)); - break; - } -#endif - default: { - VTable.NotReached("Unknown GC type: "+gcType); - break; - } - } - GC.installedGC.NewThreadNotification(Thread.initialThread, true); - GC.installedGC.ThreadStartNotification(Thread.initialThread.threadIndex); - } - - [PreInitRefCounts] - internal static void Initialize() - { - Transitions.Initialize(); - WriteBarrier.Initialize(); - } - - private static void FinishedGCCycle() - { - gcTotalCount++; - gcTotalBytes += (long) newBytesSinceGC; - newBytesSinceGC = UIntPtr.Zero; - } - -#if !SINGULARITY - private static DateTime LogMessage(String message) - { - DateTime currentTime = System.DateTime.Now; - System.Text.StringBuilder sb = new System.Text.StringBuilder(); - String hourString = currentTime.Hour.ToString(); - if (hourString.Length == 1) { - sb.Append('0'); - } - sb.Append(hourString); - sb.Append(':'); - String minuteString = currentTime.Minute.ToString(); - if (minuteString.Length == 1) { - sb.Append('0'); - } - sb.Append(minuteString); - sb.Append(':'); - String secondString = currentTime.Second.ToString(); - if (secondString.Length == 1) { - sb.Append('0'); - } - sb.Append(secondString); - sb.Append('.'); - String milliString = currentTime.Millisecond.ToString(); - if (milliString.Length < 3) { - sb.Append('0'); - } - if (milliString.Length < 2) { - sb.Append('0'); - } - sb.Append(milliString); - sb.Append(": "); - sb.Append(message); - Console.Out.WriteLine(sb.ToString()); - return currentTime; - } -#endif - - // This empty class allows us to easily spot the HeapCritialSection - // mutex when debugging. - private class HeapMonitor - { - } - - internal static void CheckForNeededGCWork(Thread currentThread) { - installedGC.CheckForNeededGCWork(currentThread); - } - -#if SINGULARITY - // This is a Singularity special not in the CLR - public static void Verify() - { - DebugStub.WriteLine("Calling VerifyHeap()"); - bool oldGCVerify = VTable.enableGCVerify; - VTable.enableGCVerify = true; - Collect(); - VTable.enableGCVerify = oldGCVerify; - DebugStub.WriteLine("Verification finished."); - } - - public static void PerformanceCounters(out int collectorCount, - out long collectorMillis, - out long collectorBytes) - { - collectorCount = gcTotalCount; - collectorMillis = gcTotalTime; - collectorBytes = gcTotalBytes; - } -#endif - - // Garbage Collect all generations. - public static void Collect() - { - CollectBodyTransition(Thread.CurrentThread, MaxGeneration); - } - - public static void Collect(int generation) - { - if (generation < 0) { - throw new ArgumentOutOfRangeException( - "generation", - "Argument should be positive!"); - } - CollectBodyTransition(Thread.CurrentThread, generation); - } - - internal static void InvokeCollection(Thread currentThread) - { - CollectBodyTransition(currentThread, -1); - } - - internal static void InvokeMajorCollection(Thread currentThread) - { - CollectBodyTransition(currentThread, -2); - } - - // DO NOT REMOVE THE StackLinkCheck ATTRIBUTE FROM THIS - // FUNCTION! - // - // It is called from native code System.GC.CollectBodyTransition - // that only has an attribute for the amount of stack space that - // the native code requires. - [StackLinkCheck] - [AccessedByRuntime("called from halforgc.asm/brtforgc.asm")] - private static unsafe Thread CollectBody(Thread currentThread, - int generation) - { - int startTicks = 0; - bool enableGCTiming = VTable.enableGCTiming; - if (enableGCTiming) { - VTable.enableGCTiming = false; - pauseCount++; - startTicks = Environment.TickCount; - VTable.DebugPrint("[GC start: {0} bytes]\n", - __arglist(installedGC.TotalMemory)); - } - if (VTable.enableGCWatermarks) { - MemoryAccounting.RecordHeapWatermarks(); - } - - int currentThreadIndex = currentThread.threadIndex; - // Our stack is GC safe after going through CollectBodyTransition - installedGC.Collect(currentThreadIndex, generation); - FinishedGCCycle(); - - if (VTable.enableGCWatermarks) { - MemoryAccounting.RecordHeapWatermarks(); - } - if (enableGCTiming) { - int elapsedTicks = Environment.TickCount - startTicks; - gcTotalTime += elapsedTicks; - if (maxPauseTime < elapsedTicks) { - maxPauseTime = elapsedTicks; - } - VTable.DebugPrint("[GC end : {0} bytes, {1} ms]\n", - __arglist(installedGC.TotalMemory, - elapsedTicks)); - VTable.enableGCTiming = true; - } - return Thread.threadTable[currentThreadIndex]; - } - - [RequiredByBartok] - [AccessedByRuntime("called from brtasm.asm")] - [Inline] - [ManualRefCounts] - internal static Object AllocateObject(VTable vtable) - { - return AllocateObject(vtable, Thread.CurrentThread); - } - - [RequiredByBartok] - [Inline] - [ManualRefCounts] - internal static Object AllocateObject(VTable vtable, - Thread currentThread) - { - VTable.Deny(Transitions.UnderGCControl(currentThread.threadIndex)); - return installedGC.AllocateObject(vtable, currentThread); - } - - [NoInline] - [ManualRefCounts] - internal static Object AllocateObjectNoInline(VTable vtable, - Thread currentThread) - { - return GC.AllocateObject(vtable, currentThread); - } - - [RequiredByBartok] - [Inline] - [ManualRefCounts] - internal static Array AllocateVector(VTable vtable, int numElements) - { - return AllocateVector(vtable, numElements, Thread.CurrentThread); - } - - [Inline] - [ManualRefCounts] - internal static Array AllocateVector(VTable vtable, - int numElements, - Thread currentThread) - { - VTable.Deny(Transitions.UnderGCControl(currentThread.threadIndex)); - return installedGC.AllocateVector(vtable, numElements, - currentThread); - } - - [RequiredByBartok] - [Inline] - [ManualRefCounts] - internal static Array AllocateArray(VTable vtable, int rank, - int totalElements) - { - return AllocateArray(vtable, rank, totalElements, - Thread.CurrentThread); - } - - [Inline] - [ManualRefCounts] - internal static Array AllocateArray(VTable vtable, int rank, - int totalElements, - Thread currentThread) - { - VTable.Deny(Transitions.UnderGCControl(currentThread.threadIndex)); - return installedGC.AllocateArray(vtable, rank, totalElements, - currentThread); - } - - [RequiredByBartok] - [Inline] - [ManualRefCounts] - internal static String AllocateString(int stringLength) - { - return AllocateString(stringLength, Thread.CurrentThread); - } - - [Inline] - [ManualRefCounts] - internal static String AllocateString(int stringLength, - Thread currentThread) - { - VTable.Deny(Transitions.UnderGCControl(currentThread.threadIndex)); - return installedGC.AllocateString(stringLength, currentThread); - } - - public static int GetGeneration(Object obj) - { - return installedGC.GetGeneration(obj); - } - - public static int MaxGeneration { - get { return installedGC.MaxGeneration; } - } - - [NoInline] - public static void KeepAlive(Object obj) - { - dummyGlobal = obj; - dummyGlobal = null; - } - - public static void WaitForPendingFinalizers() - { - Finalizer.WaitForPending(); - } - - public static long GetTotalMemory(bool forceFullCollection) - { - long size = installedGC.TotalMemory; - if (!forceFullCollection) { - return size; - } - // If we force a full collection, we will run the finalizers on all - // existing objects and do a collection until the value stabilizes. - // The value is "stable" when either the value is within 5% of the - // previous call to installedGC.TotalMemory, or if we have been sitting - // here for more than x times (we don't want to loop forever here). - for (int reps = 0; reps < 8; reps++) { - WaitForPendingFinalizers(); - Collect(); - long newSize = installedGC.TotalMemory; - long bound = size / 20; // 5% - long diff = newSize - size; - size = newSize; - if (diff >= -bound && diff <= bound) { - break; - } - } - return size; - } - - public static void SuppressFinalize(Object obj) - { - if (obj == null) { - throw new ArgumentNullException("obj"); - } - Finalizer.SuppressCandidate(obj); - } - - internal static void nativeSuppressFinalize(Object obj) { - Finalizer.SuppressCandidate(obj); - } - - public static void ReRegisterForFinalize(Object obj) - { - if (obj == null) { - throw new ArgumentNullException("obj"); - } - Finalizer.RegisterCandidate(obj); - } - - public static int GetGeneration(WeakReference wo) - { - Object obj = wo.Target; - if (obj == null) { - throw new ArgumentException("wo", "target already collected"); - } - return GetGeneration(obj); - } - - public static void SetProfiler(GCProfiler profiler) - { - installedGC.SetProfiler(profiler); - isProfiling = true; - } - - public static bool IsProfiling - { - get { - return isProfiling; - } - } - - internal static void ProfileAllocation(Object obj) - { - if (isProfiling) { - installedGC.ProfileAllocation(obj); - } - } - - private static void SetCleanupCache() - { - // REVIEW: will not ever clean up these caches (such as Assembly - // strong names) - } - - internal static void EnableHeap() - { - CollectorStatistics.Initialize(); - CollectorStatistics.Event(GCEvent.CreateHeap); - GC.installedGC.EnableHeap(); - Finalizer.StartFinalizerThread(); - } - - // Called on VM shutdown. - internal static void DestructHeap() - { - if (VTable.enableGCWatermarks) { - MemoryAccounting.RecordHeapWatermarks(); - MemoryAccounting.ReportHeapWatermarks(); - } - - if (VTable.enableGCTiming) { -#if SINGULARITY - DebugStub.WriteLine("Total GC Time (ms): {0}", - __arglist(gcTotalTime)); -#else - Console.Error.WriteLine("Total GC Time (ms): " + gcTotalTime); - Console.Error.WriteLine("Max. Pause Time (ms): " + maxPauseTime); - if (pauseCount != 0) { - Console.Error.WriteLine("Avg. Pause Time (ms): " + - gcTotalTime/pauseCount); - } else { - Console.Error.WriteLine("Avg. Pause Time (ms): 0"); - } -#endif - } - if (VTable.enableGCProfiling) { -#if !SINGULARITY - Console.Error.WriteLine("Objects allocated: "+ - objectsAllocated); - Console.Error.WriteLine("Total bytes allocated (KB): "+ - (bytesAllocated >> 10)); -#endif - } - - if(installedGC != null) { - installedGC.DestructHeap(); - } - CollectorStatistics.Event(GCEvent.DestroyHeap); - CollectorStatistics.Summary(); - } - - internal static void NewThreadNotification(Thread newThread, - bool initial) - { - GC.installedGC.NewThreadNotification(newThread, initial); - } - - internal static void DeadThreadNotification(Thread deadThread) - { - GC.installedGC.DeadThreadNotification(deadThread); - } - - internal static void ThreadStartNotification(int currentThreadIndex) - { - GC.installedGC.ThreadStartNotification(currentThreadIndex); - } - - internal static void ThreadEndNotification(Thread dyingThread) - { - GC.installedGC.ThreadEndNotification(dyingThread); - } - - internal static void ThreadDormantGCNotification(int threadIndex) - { - GC.installedGC.ThreadDormantGCNotification(threadIndex); - } - - } - -} diff --git a/base/Kernel/Bartok/GCs/AllCardsWriteBarrier.cs b/base/Kernel/Bartok/GCs/AllCardsWriteBarrier.cs deleted file mode 100644 index 5303081..0000000 --- a/base/Kernel/Bartok/GCs/AllCardsWriteBarrier.cs +++ /dev/null @@ -1,132 +0,0 @@ -/*******************************************************************/ -/* WARNING */ -/* This file should be identical in the Bartok and Singularity */ -/* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ -/*******************************************************************/ - -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -namespace System.GCs { - - using Microsoft.Bartok.Runtime; - using System.Runtime.CompilerServices; - using System.Threading; - - internal unsafe class AllCardsWriteBarrier : WriteBarrier - { - - internal static AllCardsWriteBarrier instance; - - internal static new void Initialize() { - AllCardsWriteBarrier.instance = (AllCardsWriteBarrier) - BootstrapMemory.Allocate(typeof(AllCardsWriteBarrier)); - } - - [Inline] - protected override void StoreStaticFieldImpl(ref Object staticField, - Object value) - { - // No need to mark the card for a static field. - *Magic.toPointer(ref staticField) = Magic.addressOf(value); - } - - protected override void CopyStructImpl(VTable vtable, - UIntPtr srcPtr, - UIntPtr dstPtr) - { - CopyStructWithBarrier(vtable, srcPtr, dstPtr); - } - - [Inline] - protected override Object AtomicSwapImpl(ref Object reference, - Object value) - { - UIntPtr resultAddr = - Interlocked.Exchange(Magic.toPointer(ref reference), - Magic.addressOf(value)); - RecordReference(ref reference, value); - return Magic.fromAddress(resultAddr); - } - - [Inline] - protected override - Object AtomicCompareAndSwapImpl(ref Object reference, - Object newValue, - Object comparand) - { - UIntPtr resultAddr = - Interlocked.CompareExchange(Magic.toPointer(ref reference), - Magic.addressOf(newValue), - Magic.addressOf(comparand)); - RecordReference(ref reference, newValue); - return Magic.fromAddress(resultAddr); - } - - [Inline] - protected override void CloneImpl(Object srcObject, Object dstObject) - { - CloneNoBarrier(srcObject, dstObject); - RecordClone(dstObject); - } - - // 'offset' is not relative to the lower bound, but is a count - // of elements from the first element in the array. - protected override void ArrayZeroImpl(Array array, - int offset, - int length) - { - ArrayZeroNoBarrier(array, offset, length); - } - - // 'offset' is not relative to the lower bound, but is a count - // of elements from the first element in the array. - protected override void ArrayCopyImpl(Array srcArray, int srcOffset, - Array dstArray, int dstOffset, - int length) - { - if ((length > 1000) || ((length << 2) >= dstArray.Length)) { - ArrayCopyNoBarrier(srcArray, srcOffset, - dstArray, dstOffset, - length); - RecordClone(dstArray); - } else { - ArrayCopyWithBarrier(srcArray, srcOffset, - dstArray, dstOffset, - length); - } - } - - [Inline] - protected override void WriteReferenceImpl(UIntPtr *location, - Object value) - { - *location = Magic.addressOf(value); - RecordReference(location, value); - } - - [Inline] - private static void RecordClone(Object clone) { - GenerationalCollector.installedRemSet.RecordClonedObject(clone); - } - - [Inline] - private static void RecordReference(ref Object location, - Object value) - { - RecordReference(Magic.toPointer(ref location), value); - } - - [Inline] - private static void RecordReference(UIntPtr *location, - Object value) - { - GenerationalCollector. - installedRemSet.RecordReference(location, value); - } - - } - -} diff --git a/base/Kernel/Bartok/GCs/BaseCollector.cs b/base/Kernel/Bartok/GCs/BaseCollector.cs deleted file mode 100644 index 99a19ab..0000000 --- a/base/Kernel/Bartok/GCs/BaseCollector.cs +++ /dev/null @@ -1,145 +0,0 @@ -/*******************************************************************/ -/* WARNING */ -/* This file should be identical in the Bartok and Singularity */ -/* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ -/*******************************************************************/ - -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -namespace System.GCs -{ - - using Microsoft.Bartok.Runtime; - - using System.Threading; - using System.Runtime.CompilerServices; - - [NoCCtor] - internal abstract class BaseCollector : Collector - { - - internal override void NewThreadNotification(Thread newThread, - bool initial) - { - Transitions.NewThreadNotification(newThread.threadIndex, initial); - } - - internal override void DeadThreadNotification(Thread deadThread) - { - } - - internal override void ThreadStartNotification(int currentThreadIndex) - { - Thread currentThread = Thread.threadTable[currentThreadIndex]; -#if !SINGULARITY - PageManager.MarkThreadStack(currentThread); -#endif - } - - internal override void ThreadEndNotification(Thread currentThread) - { - } - - [ManualRefCounts] - internal override Object AllocateObject(VTable vtable, - Thread currentThread) - { - UIntPtr numBytes = ObjectLayout.ObjectSize(vtable); - UIntPtr objectAddr = - AllocateObjectMemory(numBytes, vtable.baseAlignment, - currentThread); - Object result = Magic.fromAddress(objectAddr); - this.CreateObject(result, vtable, currentThread); - if (GC.IsProfiling) { - ProfileAllocation(result); - } - if (VTable.enableGCProfiling) { - System.GC.bytesAllocated += (ulong)numBytes; - System.GC.objectsAllocated++; - } - - return result; - } - - [ManualRefCounts] - internal override Array AllocateVector(VTable vtable, - int numElements, - Thread currentThread) - { - UIntPtr numBytes = - ObjectLayout.ArraySize(vtable, unchecked((uint)numElements)); - UIntPtr vectorAddr = - AllocateObjectMemory(numBytes, vtable.baseAlignment, - currentThread); - Array result = Magic.toArray(Magic.fromAddress(vectorAddr)); - CreateObject(result, vtable, currentThread); - result.InitializeVectorLength(numElements); - if (VTable.enableGCProfiling) { - System.GC.bytesAllocated += (ulong)numBytes; - System.GC.objectsAllocated++; - } - return result; - } - - [ManualRefCounts] - internal override Array AllocateArray(VTable vtable, - int rank, - int totalElements, - Thread currentThread) - { - UIntPtr numBytes = - ObjectLayout.ArraySize(vtable, unchecked((uint)totalElements)); - UIntPtr arrayAddr = - AllocateObjectMemory(numBytes, vtable.baseAlignment, - currentThread); - Array result = Magic.toArray(Magic.fromAddress(arrayAddr)); - CreateObject(result, vtable, currentThread); - result.InitializeArrayLength(rank, totalElements); - if (VTable.enableGCProfiling) { - System.GC.bytesAllocated += (ulong)numBytes; - System.GC.objectsAllocated++; - } - return result; - } - - [ManualRefCounts] - internal override String AllocateString(int stringLength, - Thread currentThread) - { - VTable vtable = - Magic.toRuntimeType(typeof(System.String)).classVtable; - UIntPtr numBytes = - ObjectLayout.StringSize(vtable, - unchecked((uint) (stringLength+1))); - UIntPtr stringAddr = - AllocateObjectMemory(numBytes, unchecked((uint) UIntPtr.Size), - currentThread); - String result = Magic.toString(Magic.fromAddress(stringAddr)); - CreateObject(result, vtable, currentThread); - result.InitializeStringLength(stringLength); - if (VTable.enableGCProfiling) { - System.GC.bytesAllocated += (ulong)numBytes; - System.GC.objectsAllocated++; - } - return result; - } - - [ManualRefCounts] - [Inline] - protected virtual void CreateObject(Object obj, VTable vtable, - Thread currentThread) - { - obj.vtable = vtable; - } - - [Inline] - protected bool IsValidGeneration(int generation) - { - return ((generation >= MinGeneration) && (generation <= MaxGeneration)); - } - } - -} diff --git a/base/Kernel/Bartok/GCs/BootstrapMemory.cs b/base/Kernel/Bartok/GCs/BootstrapMemory.cs deleted file mode 100644 index 7709091..0000000 --- a/base/Kernel/Bartok/GCs/BootstrapMemory.cs +++ /dev/null @@ -1,153 +0,0 @@ -/*******************************************************************/ -/* WARNING */ -/* This file should be identical in the Bartok and Singularity */ -/* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ -/*******************************************************************/ - -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -namespace System.GCs { - - using Microsoft.Bartok.Runtime; - using System.Threading; - using System.Runtime.CompilerServices; - - internal unsafe class BootstrapMemory - { - - // WARNING: don't initialize any static fields in this class - // without manually running the class constructor at startup! - - private static BumpAllocator pool; - - [PreInitRefCounts] -#if !SINGULARITY - [NoStackLinkCheck] -#endif - internal static void Initialize(UIntPtr systemMemorySize) { - pool = new BumpAllocator(PageType.NonGC); - UIntPtr memStart = MemoryManager.AllocateMemory(systemMemorySize); - pool.SetZeroedRange(memStart, systemMemorySize); - if(GC.gcType != GCType.NullCollector) { - PageManager.SetStaticDataPages(memStart, systemMemorySize); -#if !SINGULARITY - PageTable.SetProcess(PageTable.Page(memStart), - PageTable.PageCount(systemMemorySize)); -#endif - } - } - - [ManualRefCounts] -#if !SINGULARITY - [NoStackLinkCheck] -#endif - internal static Object Allocate(VTable vtable) { - UIntPtr numBytes = ObjectLayout.ObjectSize(vtable); - UIntPtr objectAddr = - pool.AllocateFast(numBytes, vtable.baseAlignment); - VTable.Assert(objectAddr != UIntPtr.Zero, - "Out of BootstrapMemory"); - Object result = Magic.fromAddress(objectAddr); -#if REFERENCE_COUNTING_GC - result.REF_STATE = vtable.isAcyclicRefType ? - (ReferenceCountingCollector. - acyclicFlagMask | 1): 1; - result.REF_STATE = (result.REF_STATE | 2) & - ~ReferenceCountingCollector.countingONFlagMask; -#elif DEFERRED_REFERENCE_COUNTING_GC - result.REF_STATE = vtable.isAcyclicRefType ? - (DeferredReferenceCountingCollector. - acyclicFlagMask | - DeferredReferenceCountingCollector. - markFlagMask) : - DeferredReferenceCountingCollector. - markFlagMask; - result.REF_STATE &= - ~DeferredReferenceCountingCollector.countingONFlagMask; -#endif - *result.VTableFieldAddr = Magic.addressOf(vtable); - return result; - } - - [ManualRefCounts] -#if !SINGULARITY - [NoStackLinkCheck] -#endif - internal static Object Allocate(VTable vtable, uint count) { - UIntPtr numBytes = ObjectLayout.ArraySize(vtable, count); - UIntPtr objectAddr = - pool.AllocateFast(numBytes, vtable.baseAlignment); - VTable.Assert(objectAddr != UIntPtr.Zero, - "Out of BootstrapMemory"); - Array result = Magic.toArray(Magic.fromAddress(objectAddr)); -#if REFERENCE_COUNTING_GC - result.REF_STATE = vtable.isAcyclicRefType ? - (ReferenceCountingCollector. - acyclicFlagMask | 1): 1; - result.REF_STATE = (result.REF_STATE | 2) & - ~ReferenceCountingCollector.countingONFlagMask; -#elif DEFERRED_REFERENCE_COUNTING_GC - result.REF_STATE = vtable.isAcyclicRefType ? - (DeferredReferenceCountingCollector. - acyclicFlagMask | - DeferredReferenceCountingCollector. - markFlagMask) : - DeferredReferenceCountingCollector. - markFlagMask; - result.REF_STATE &= - ~DeferredReferenceCountingCollector.countingONFlagMask; -#endif - *result.VTableFieldAddr = Magic.addressOf(vtable); - result.InitializeVectorLength((int) count); - return result; - } - - [PreInitRefCounts] -#if !SINGULARITY - [NoStackLinkCheck] -#endif - internal static Object Allocate(Type t) { - return Allocate((RuntimeType) t); - } - - [PreInitRefCounts] -#if !SINGULARITY - [NoStackLinkCheck] -#endif - internal static Object Allocate(Type t, uint count) { - return Allocate((RuntimeType) t, count); - } - - [PreInitRefCounts] -#if !SINGULARITY - [NoStackLinkCheck] -#endif - internal static Object Allocate(RuntimeType t) { - return Allocate(t.classVtable); - } - - [PreInitRefCounts] -#if !SINGULARITY - [NoStackLinkCheck] -#endif - internal static Object Allocate(RuntimeType t, uint count) { - return Allocate(t.classVtable, count); - } - - internal static void Truncate() { - UIntPtr allocLimit = PageTable.PagePad(pool.AllocPtr); - UIntPtr unusedSize = pool.ReserveLimit - allocLimit; - if(GC.gcType != GCType.NullCollector) { - PageManager.ReleaseUnusedPages(PageTable.Page(allocLimit), - PageTable.PageCount(unusedSize), - true); - } - pool.Truncate(); - } - - } - -} diff --git a/base/Kernel/Bartok/GCs/CallStack.cs b/base/Kernel/Bartok/GCs/CallStack.cs deleted file mode 100644 index f8417e0..0000000 --- a/base/Kernel/Bartok/GCs/CallStack.cs +++ /dev/null @@ -1,950 +0,0 @@ -/*******************************************************************/ -/* WARNING */ -/* This file should be identical in the Bartok and Singularity */ -/* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ -/*******************************************************************/ - -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -namespace System.GCs { - - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Threading; - - using Microsoft.Bartok.Options; - using Microsoft.Bartok.Runtime; - -#if SINGULARITY_KERNEL - using Microsoft.Singularity.X86; -#elif SINGULARITY_PROCESS - using Microsoft.Singularity.X86; - using Microsoft.Singularity.V1.Services; -#endif - - [NoCCtor] - [RequiredByBartok] - internal unsafe class CallStack - { - - [Mixin(typeof(Thread))] - private class CallStackThread : Object { - [AccessedByRuntime("reference in brtstack.asm")] - internal UIntPtr asmStackBase; // Limit of thread's stack - [AccessedByRuntime("reference in brtstack.asm")] - internal UIntPtr asmStackLimit; - [AccessedByRuntime("reference in brtforgc.asm")] - internal unsafe TransitionRecord *asmStackMarker; - } - - private static CallStackThread MixinThread(Thread t) { - return (CallStackThread) (Object) t; - } - - internal static UIntPtr StackBase(Thread t) { - return MixinThread(t).asmStackBase; - } - - internal static void SetStackBase(Thread t, UIntPtr value) { - MixinThread(t).asmStackBase = value; - } - - internal static UIntPtr StackLimit(Thread t) { - return MixinThread(t).asmStackLimit; - } - - internal static void SetStackLimit(Thread t, UIntPtr value) { - MixinThread(t).asmStackLimit = value; - } - - internal static TransitionRecord* StackMarker(Thread t) - { - return MixinThread(t).asmStackMarker; - } - -#if X86 - [AccessedByRuntime("referenced from halforgc.asm")] - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct TransitionRecord { - [AccessedByRuntime("referenced from halforgc.asm")] - internal TransitionRecord *oldTransitionRecord; - [AccessedByRuntime("referenced from halforgc.asm")] - internal UIntPtr callAddr; - [AccessedByRuntime("referenced from halforgc.asm")] - internal UIntPtr stackBottom; - [AccessedByRuntime("referenced from halforgc.asm")] - internal UIntPtr EBX; - [AccessedByRuntime("referenced from halforgc.asm")] - internal UIntPtr EDI; - [AccessedByRuntime("referenced from halforgc.asm")] - internal UIntPtr ESI; - [AccessedByRuntime("referenced from halforgc.asm")] - internal UIntPtr EBP; - } -#elif AMD64 - [AccessedByRuntime("referenced from halforgc.asm")] - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct TransitionRecord { - [AccessedByRuntime("referenced from halforgc.asm")] - internal TransitionRecord *oldTransitionRecord; - [AccessedByRuntime("referenced from halforgc.asm")] - internal UIntPtr callAddr; - [AccessedByRuntime("referenced from halforgc.asm")] - internal UIntPtr stackBottom; - [AccessedByRuntime("referenced from halforgc.asm")] - internal UIntPtr EBX; - [AccessedByRuntime("referenced from halforgc.asm")] - internal UIntPtr EDI; - [AccessedByRuntime("referenced from halforgc.asm")] - internal UIntPtr ESI; - [AccessedByRuntime("referenced from halforgc.asm")] - internal UIntPtr EBP; - [AccessedByRuntime("referenced from halforgc.asm")] - internal UIntPtr R12; - [AccessedByRuntime("referenced from halforgc.asm")] - internal UIntPtr R13; - [AccessedByRuntime("referenced from halforgc.asm")] - internal UIntPtr R14; - [AccessedByRuntime("referenced from halforgc.asm")] - internal UIntPtr R15; - } -#endif - internal static Thread threadBeingProcessed; - - private struct CalleeSaveLocations { - -#if SINGULARITY_KERNEL - internal void SetCalleeSaves(ThreadContext *context) { - EBX.SetCalleeReg(&context->ebx); - EDI.SetCalleeReg(&context->edi); - ESI.SetCalleeReg(&context->esi); - EBP.SetCalleeReg(&context->ebp); - } -#endif - -#if X86 - internal void SetCalleeSaves(TransitionRecord *transitionRecord) - { - EBX.SetCalleeReg(&transitionRecord->EBX); - EDI.SetCalleeReg(&transitionRecord->EDI); - ESI.SetCalleeReg(&transitionRecord->ESI); - EBP.SetCalleeReg(&transitionRecord->EBP); - } - - internal void ClearCalleeSaves() { - EBX.ClearCalleeReg(); - ESI.ClearCalleeReg(); - EDI.ClearCalleeReg(); - EBP.ClearCalleeReg(); - } - - internal - void ScanLiveRegs(uint mask, - NonNullReferenceVisitor referenceVisitor) - { - EDI.ScanLiveReg((mask >> 2) & 0x3, referenceVisitor); - ESI.ScanLiveReg((mask >> 4) & 0x3, referenceVisitor); - EBX.ScanLiveReg((mask >> 0) & 0x3, referenceVisitor); - EBP.ScanLiveReg((mask >> 6) & 0x3, referenceVisitor); - } - - internal void PopFrame(UIntPtr *framePointer, - uint calleeSaveMask, - bool framePointerOmitted) - { - UIntPtr *calleeSaveStart; - if (framePointerOmitted) { - calleeSaveStart = framePointer - 1; - } else { - VTable.Assert((calleeSaveMask & 0x10) == 0, - "EBP should not be callee saved"); - calleeSaveStart = framePointer; - EBP.PopFrameReg(ref calleeSaveStart); - } - if ((calleeSaveMask & 0x1) != 0) { - calleeSaveStart -= - sizeof(TransitionRecord) / sizeof(UIntPtr); - } - - // Note: the order in which these appear is important! - if ((calleeSaveMask & 0x2) != 0) { - EBX.PopFrameReg(ref calleeSaveStart); - } - if ((calleeSaveMask & 0x10) != 0) { - EBP.PopFrameReg(ref calleeSaveStart); - } - if ((calleeSaveMask & 0x8) != 0) { - ESI.PopFrameReg(ref calleeSaveStart); - } - if ((calleeSaveMask & 0x4) != 0) { - EDI.PopFrameReg(ref calleeSaveStart); - } - } - - internal void ClearFrame(uint calleeSaveMask, - bool framePointerOmitted) - { - if (!framePointerOmitted) { - VTable.Assert((calleeSaveMask & 0x10) == 0, - "EBP should not be callee saved"); - EBP.ClearFrameReg(); - } - if ((calleeSaveMask & 0x2) != 0) { - EBX.ClearFrameReg(); - } - if ((calleeSaveMask & 0x10) != 0) { - EBP.ClearFrameReg(); - } - if ((calleeSaveMask & 0x8) != 0) { - ESI.ClearFrameReg(); - } - if ((calleeSaveMask & 0x4) != 0) { - EDI.ClearFrameReg(); - } - } -#elif AMD64 - internal void SetCalleeSaves(TransitionRecord *transitionRecord) - { - EBX.SetCalleeReg(&transitionRecord->EBX); - EDI.SetCalleeReg(&transitionRecord->EDI); - ESI.SetCalleeReg(&transitionRecord->ESI); - EBP.SetCalleeReg(&transitionRecord->EBP); - R12.SetCalleeReg(&transitionRecord->R12); - R13.SetCalleeReg(&transitionRecord->R13); - R14.SetCalleeReg(&transitionRecord->R14); - R15.SetCalleeReg(&transitionRecord->R15); - } - - internal void ClearCalleeSaves() { - EBX.ClearCalleeReg(); - ESI.ClearCalleeReg(); - EDI.ClearCalleeReg(); - EBP.ClearCalleeReg(); - R12.ClearCalleeReg(); - R13.ClearCalleeReg(); - R14.ClearCalleeReg(); - R15.ClearCalleeReg(); - } - - internal - void ScanLiveRegs(uint mask, - NonNullReferenceVisitor referenceVisitor) - { - EDI.ScanLiveReg((mask >> 2) & 0x3, referenceVisitor); - ESI.ScanLiveReg((mask >> 4) & 0x3, referenceVisitor); - EBX.ScanLiveReg((mask >> 0) & 0x3, referenceVisitor); - R12.ScanLiveReg((mask >> 6) & 0x3, referenceVisitor); - R13.ScanLiveReg((mask >> 8) & 0x3, referenceVisitor); - R14.ScanLiveReg((mask >> 10) & 0x3, referenceVisitor); - R15.ScanLiveReg((mask >> 12) & 0x3, referenceVisitor); - EBP.ScanLiveReg((mask >> 14) & 0x3, referenceVisitor); - } - - internal void PopFrame(UIntPtr *framePointer, - uint calleeSaveMask, - bool framePointerOmitted) - { - UIntPtr *calleeSaveStart; - if (framePointerOmitted) { - calleeSaveStart = framePointer - 1; - } else { - VTable.Assert((calleeSaveMask & 0x100) == 0, - "EBP should not be callee saved"); - calleeSaveStart = framePointer; - EBP.PopFrameReg(ref calleeSaveStart); - } - if ((calleeSaveMask & 0x1) != 0) { - calleeSaveStart -= - sizeof(TransitionRecord) / sizeof(UIntPtr); - } - - // Note: the order in which these appear is important! - if ((calleeSaveMask & 0x2) != 0) { - EBX.PopFrameReg(ref calleeSaveStart); - } - if ((calleeSaveMask & 0x100) != 0) { - EBP.PopFrameReg(ref calleeSaveStart); - } - if ((calleeSaveMask & 0x8) != 0) { - ESI.PopFrameReg(ref calleeSaveStart); - } - if ((calleeSaveMask & 0x4) != 0) { - EDI.PopFrameReg(ref calleeSaveStart); - } - if ((calleeSaveMask & 0x10) != 0) { - R12.PopFrameReg(ref calleeSaveStart); - } - if ((calleeSaveMask & 0x20) != 0) { - R13.PopFrameReg(ref calleeSaveStart); - } - if ((calleeSaveMask & 0x40) != 0) { - R14.PopFrameReg(ref calleeSaveStart); - } - if ((calleeSaveMask & 0x80) != 0) { - R15.PopFrameReg(ref calleeSaveStart); - } - } - - internal void ClearFrame(uint calleeSaveMask, - bool framePointerOmitted) - { - if (!framePointerOmitted) { - VTable.Assert((calleeSaveMask & 0x100) == 0, - "EBP should not be callee saved"); - EBP.ClearFrameReg(); - } - if ((calleeSaveMask & 0x2) != 0) { - EBX.ClearFrameReg(); - } - if ((calleeSaveMask & 0x100) != 0) { - EBP.ClearFrameReg(); - } - if ((calleeSaveMask & 0x8) != 0) { - ESI.ClearFrameReg(); - } - if ((calleeSaveMask & 0x4) != 0) { - EDI.ClearFrameReg(); - } - if ((calleeSaveMask & 0x10) != 0) { - R12.ClearFrameReg(); - } - if ((calleeSaveMask & 0x20) != 0) { - R13.ClearFrameReg(); - } - if ((calleeSaveMask & 0x40) != 0) { - R14.ClearFrameReg(); - } - if ((calleeSaveMask & 0x80) != 0) { - R15.ClearFrameReg(); - } - - } -#endif - - internal UIntPtr GetFramePointer() - { - return EBP.value; - } - - private struct RegLocation - { - - [Inline] - internal void SetCalleeReg(UIntPtr *regField) - { - this.pending = false; - this.value = *regField; - this.head = regField; - *regField = UIntPtr.Zero; - } - - internal void ClearCalleeReg() - { - VTable.Deny(this.pending); - UIntPtr *scan = this.head; - while (scan != null) { - UIntPtr temp = *scan; - *scan = value; - scan = (UIntPtr *) temp; - } - this.head = null; - } - - internal void ScanLiveReg(uint kind, - NonNullReferenceVisitor visitor) - { - switch (kind) { - case 0: { - // Value is not a traceable heap pointer - break; - } - case 1: { - // Value is a pointer variable - VTable.Deny(this.head == null); - if (value != UIntPtr.Zero) { - fixed (UIntPtr *valueField = &this.value) { - visitor.Visit(valueField); - } - } - ClearCalleeReg(); - break; - } - case 2: { - // Value is unchanged since function entry - VTable.Deny(this.pending); - this.pending = true; - break; - } - case 3: - default: { - VTable.NotReached("ScanLiveReg 3 or default"); - break; - } - } - } - - internal void PopFrameReg(ref UIntPtr *calleeSaveStart) - { - if (this.head != null && !this.pending) { - ClearCalleeReg(); - } - if (this.head == null) { - this.value = *calleeSaveStart; - } else { - VTable.Assert(this.pending, "pending should be true"); - VTable.Assert(*calleeSaveStart == this.value, - "values are not equal"); - } - this.pending = false; - *calleeSaveStart = (UIntPtr) this.head; - this.head = calleeSaveStart; - calleeSaveStart--; - } - - internal void ClearFrameReg() { - UIntPtr *scan = this.head; - while (scan != null) { - UIntPtr temp = *scan; - *scan = value; - scan = (UIntPtr *) temp; - } - this.head = null; - this.pending = false; - this.value = UIntPtr.Zero; - } - - internal bool pending; - internal UIntPtr value; - internal UIntPtr *head; - - } - -#if X86 - RegLocation EBX; - RegLocation EDI; - RegLocation ESI; - RegLocation EBP; -#elif AMD64 - RegLocation EBX; - RegLocation EDI; - RegLocation ESI; - RegLocation EBP; - RegLocation R12; - RegLocation R13; - RegLocation R14; - RegLocation R15; -#endif - } - - [RequiredByBartok] - private static int callSiteTableCount; - [AccessedByRuntime("referenced from halexn.cpp")] - private static UIntPtr *codeBaseStartTable; - [RequiredByBartok] - private static UIntPtr **returnAddressToCallSiteSetNumbers; - [RequiredByBartok] - private static int **callSiteSetCount; - - private static int CallSiteTableNumber(UIntPtr returnAddr) - { - UIntPtr address = returnAddr; - for (int i = 0; i < callSiteTableCount; i++) { - UIntPtr baseAddress = codeBaseStartTable[i]; - if (address < baseAddress) { - continue; - } - UIntPtr relativeAddress = address - baseAddress; - UIntPtr *ptr = returnAddressToCallSiteSetNumbers[i]; - int callSiteCount = *(callSiteSetCount[i]); - if (relativeAddress >= ptr[0] && - relativeAddress <= ptr[callSiteCount]) { - return i; - } - } - return -1; - } - - private static int CallSiteSetNumber(UIntPtr returnAddr, int index) - { - UIntPtr codeBaseAddr = codeBaseStartTable[index]; - UIntPtr relativeAddr = returnAddr - codeBaseAddr; - UIntPtr *callSiteTable = returnAddressToCallSiteSetNumbers[index]; - int callSiteCount = *(callSiteSetCount[index]); - int left = 0; - int right = callSiteCount; - // Loop invariant: - // callSiteTable[left] <= returnAddress < callSiteTable[right] - while (left < right-1) { - int mid = (left + right)/2; - if (callSiteTable[mid] <= relativeAddr) { - left = mid; - } else { - right = mid; - } - } - return left; - } - - [StructLayout(LayoutKind.Sequential)] - private struct FullDescriptor { - internal UIntPtr mask; - internal int variableData; - } - - private const uint ESCAPE32_TAG = 0x0; - private const uint ESCAPE16_TAG = 0x1; - private const uint ESCAPE8_TAG = 0x2; - - // Check whether the specified stack frame contains the - // transition record: if so then we're done scanning this - // segment of the stack. NB: the caller must ensure that the - // framePointer has been recomputed in a - // framePointerOmitted-method. - private static bool FrameContainsTransitionRecord(UIntPtr *framePointer, - UIntPtr *stackPointer, - TransitionRecord *stopMarker) { - bool result = false; - if ((framePointer >= stopMarker) && (stackPointer < stopMarker)) { - result = true; - } - return result; - } - - // Returns the return address in the calling method - private static - bool WalkStackFrame(ref UIntPtr *framePointer, - ref UIntPtr *stackPointer, - ref CalleeSaveLocations calleeSaves, - ref UIntPtr returnAddr, - NonNullReferenceVisitor threadReferenceVisitor, - NonNullReferenceVisitor pinnedReferenceVisitor, - UIntPtr frameDescriptorEntryPtr, - TransitionRecord *stopMarker, - Thread thread) - // BUGBUG: Remove thread argument when ThreadContext is on stack! - { - Trace.Log(Trace.Area.Stack, - "WalkStackFrame: fp={0:x}, sp={1:x}, returnAddr={2:x}, desc={3:x}", - __arglist(framePointer, stackPointer, - returnAddr, frameDescriptorEntryPtr)); - bool framePointerOmitted; - uint liveRegs, calleeSaveMask; - //uint frameDescriptorEntry = (uint) frameDescriptorEntryPtr; - UIntPtr frameDescriptorEntry = frameDescriptorEntryPtr; - if ((frameDescriptorEntry & 0x1) != 0) { - // Compact descriptor - UIntPtr mask; - framePointerOmitted = - (((frameDescriptorEntry >> 1) & 0x1) != 0); - int inbetweenSlots; - if (framePointerOmitted) { - // No frame pointer - inbetweenSlots = Constants.InbetweenSlotsNoFP; - mask = - frameDescriptorEntry >> Constants.CompactStackBitMaskStartNoFP; - calleeSaveMask = - (uint)((frameDescriptorEntry >> Constants.CompactEntryMaskStart) - & Constants.CompactEntryMaskNoFP); - liveRegs = (uint)((frameDescriptorEntry >> - Constants.CompactCalleeSaveUseStartNoFP) & - Constants.CompactCalleeSaveUseMaskNoFP); - // when frame pointer is omitted, the stack top - // equals stackPointer plus frameSize - int frameSize = - unchecked((int)(frameDescriptorEntry >> - Constants.CompactFrameSizeStartNoFP) & - Constants.CompactFrameSizeMaskNoFP); - framePointer = stackPointer + frameSize; - } else { - // Use frame pointer - inbetweenSlots = Constants.InbetweenSlotsFP; - mask = frameDescriptorEntry >> Constants.CompactStackBitMaskStartFP; - calleeSaveMask = - (uint)((frameDescriptorEntry >> Constants.CompactEntryMaskStart) - & Constants.CompactEntryMaskFP); - liveRegs = - (uint) ((frameDescriptorEntry >> - Constants.CompactCalleeSaveUseStartFP) & - Constants.CompactCalleeSaveUseMaskFP); - } - - Trace.Log(Trace.Area.Stack, - "Compact desc: fpo={0}, mask={1:x}, inbetweenSlots={2}, calleeSaveMask={3:x}, liveRegs={4:x}", - __arglist(framePointerOmitted, mask, inbetweenSlots, - calleeSaveMask, liveRegs)); - if (FrameContainsTransitionRecord(framePointer, - stackPointer, - stopMarker)) { - return true; // true: done - } - uint stackArgSize = - (uint)((frameDescriptorEntry >> Constants.CompactArgMaskStart) - & Constants.CompactArgMask); - UIntPtr *p = framePointer + stackArgSize + inbetweenSlots; - Trace.Log(Trace.Area.Stack, - "Compact desc: stackArgSize={0:x}, p={1:x}", - __arglist(stackArgSize, p)); - // Process the arguments - if (threadReferenceVisitor != null) { - for (int i = 0; mask != 0 && i <= stackArgSize; i++) { - if ((mask & 0x1) != 0 && *p != UIntPtr.Zero) { - // BUGBUG: threadReferenceVisitor.Visit(p); - VisitIfNotContext(thread, threadReferenceVisitor, - p); - } - mask >>= 1; - p--; - } - } else { - for (int i = 0; mask != 0 && i <= stackArgSize; i++) { - mask >>= 1; - } - } - // Process the local variables - bool transitionRecord = ((calleeSaveMask & 0x1) != 0); - uint savedRegs = (uint) (calleeSaveMask >> 1); - uint countRegs = ((savedRegs & 1) + - ((savedRegs >> 1) & 1) + - ((savedRegs >> 2) & 1) + - ((savedRegs >> 3) & 1) + - ((savedRegs >> 4) & 1) + - ((savedRegs >> 5) & 1) + - ((savedRegs >> 6) & 1) + - ((savedRegs >> 7) & 1)); - int transitionRecordSize = - transitionRecord ? - sizeof(TransitionRecord)/sizeof(UIntPtr) : - 0; - p = framePointer - 1 - countRegs - transitionRecordSize; - Trace.Log(Trace.Area.Stack, - "Compact desc: savedRegs={0:x}, countRegs={0:x}, transSize={0:x}, p={0:x}", __arglist(savedRegs, countRegs, transitionRecordSize)); - if (threadReferenceVisitor != null) { - while (mask != 0) { - if ((mask & 1) != 0 && *p != UIntPtr.Zero) { - // BUGBUG: threadReferenceVisitor.Visit(p); - VisitIfNotContext(thread, threadReferenceVisitor, - p); - } - mask >>= 1; - p--; - } - } - } else { - // Full descriptor - FullDescriptor *desc = - ((FullDescriptor *) frameDescriptorEntryPtr); - UIntPtr mask = (UIntPtr) desc->mask; - framePointerOmitted = ((mask & 1) != 0); - bool pinnedVars; - uint frameTag; - if (framePointerOmitted) { - // no frame pointer - calleeSaveMask = (uint) ((mask >> Constants.FullEntryMaskStart) & - Constants.FullEntryMaskNoFP); - pinnedVars = (((mask >> Constants.FullPinnedPosNoFP) & 1) != 0); - liveRegs = (uint) (( mask >> Constants.FullCalleeSaveUseStartNoFP) & - Constants.FullCalleeSaveUseMaskNoFP); - frameTag = (uint) ((mask >> Constants.FullRecordSizePosNoFP) & - Constants.FullRecordMask); - // when frame pointer is omitted, the stack top - // equals stackPointer plus frameSize - int frameSize = unchecked((int)(mask >> Constants.FullFrameSizeStartNoFP)); - framePointer = stackPointer + frameSize; - } else { - // use frame pointer - calleeSaveMask = (uint) ((mask >> Constants.FullEntryMaskStart) & - Constants.FullEntryMaskFP); - pinnedVars = (((mask >> Constants.FullPinnedPosFP) & 1) != 0); - liveRegs = (uint) (( mask >> Constants.FullCalleeSaveUseStartFP) & - Constants.FullCalleeSaveUseMaskFP); - frameTag = (uint) ((mask >> Constants.FullRecordSizePosFP) & - Constants.FullRecordMask); - } - - if (FrameContainsTransitionRecord(framePointer, - stackPointer, - stopMarker)) { - return true; // true: done - } - switch (frameTag) { - case ESCAPE8_TAG: { - int count = *(byte *) &desc->variableData; - byte pinnedCount = *((byte*)&desc->variableData+1); - sbyte *offsets = (sbyte *)&desc->variableData+2; - if (threadReferenceVisitor != null) { - for (int i = 0; i < count; i++) { - UIntPtr *loc = framePointer + offsets[i]; - if (*loc != UIntPtr.Zero) { - // BUGBUG: threadReferenceVisitor.Visit(loc); - VisitIfNotContext(thread, threadReferenceVisitor, loc); - } - } - } - if (pinnedVars && pinnedReferenceVisitor != null) { - int last = count + pinnedCount; - for (int i = count; i < last; i++) { - UIntPtr *loc = framePointer + offsets[i]; - if (*loc != UIntPtr.Zero) { - pinnedReferenceVisitor.Visit(loc); - } - } - } - break; - } - case ESCAPE16_TAG: { - int count = *((short *) &desc->variableData); - short pinnedCount = *((short*)&desc->variableData+1); - short *offsets = (short *)&desc->variableData+2; - if (threadReferenceVisitor != null) { - for (int i = 0; i < count; i++) { - UIntPtr *loc = framePointer + offsets[i]; - if (*loc != UIntPtr.Zero) { - // BUGBUG: threadReferenceVisitor.Visit(loc); - VisitIfNotContext(thread, threadReferenceVisitor, loc); - } - } - } - if (pinnedVars && pinnedReferenceVisitor != null) { - int last = count + pinnedCount; - for (int i = count; i < last; i++) { - UIntPtr *loc = framePointer + offsets[i]; - if (*loc != UIntPtr.Zero) { - pinnedReferenceVisitor.Visit(loc); - } - } - } - break; - } - case ESCAPE32_TAG: { - int count = *((int *) &desc->variableData); - int pinnedCount = *((int *) &desc->variableData+1); - int *offsets = (int *) &desc->variableData+2; - if (threadReferenceVisitor != null) { - for (int i = 0; i < count; i++) { - UIntPtr *loc = framePointer + offsets[i]; - if (*loc != UIntPtr.Zero) { - // BUGBUG: threadReferenceVisitor.Visit(loc); - VisitIfNotContext(thread, threadReferenceVisitor, loc); - } - } - } - if (pinnedVars && pinnedReferenceVisitor != null) { - int last = count + pinnedCount; - for (int i = count; i < last; i++) { - UIntPtr *loc = framePointer + offsets[i]; - if (*loc != UIntPtr.Zero) { - pinnedReferenceVisitor.Visit(loc); - } - } - } - break; - } - default: { - VTable.NotReached("GC mask switch failed"); - break; - } - } - } - if (liveRegs != 0 && threadReferenceVisitor != null) { - calleeSaves.ScanLiveRegs(liveRegs, threadReferenceVisitor); - } - UIntPtr nextReturnAddr; - if (framePointerOmitted) { - nextReturnAddr = *framePointer; - stackPointer = framePointer + 1; - } else { - nextReturnAddr = *(framePointer + 1); - stackPointer = framePointer + 2; - } - // In Singularity, the final return address of a thread is zero - if (nextReturnAddr != UIntPtr.Zero) { - calleeSaves.PopFrame(framePointer, calleeSaveMask, - framePointerOmitted); - } else { - calleeSaves.ClearFrame(calleeSaveMask, framePointerOmitted); - } - UIntPtr *calcedfp = (UIntPtr *) calleeSaves.GetFramePointer(); - if (calcedfp != null) { - framePointer = calcedfp; - } - returnAddr = nextReturnAddr; - return false; // false: not done scanning: proceed to next frame - } - -#if SINGULARITY_PROCESS - // BUGBUG: Get rid of this when ThreadContext is on stack! --rusa - [Inline] - private static - void VisitIfNotContext(Thread thread, - NonNullReferenceVisitor threadReferenceVisitor, - UIntPtr *p) - { - if (*p != (UIntPtr) thread.context && - (int *) *p != &thread.context->gcStates) { - threadReferenceVisitor.Visit(p); - } - } -#else - [Inline] - private static - void VisitIfNotContext(Thread thread, - NonNullReferenceVisitor threadReferenceVisitor, - UIntPtr *p) - { - threadReferenceVisitor.Visit(p); - } -#endif - - [RequiredByBartok] - private static ushort **callSetSiteNumberToIndex; - [RequiredByBartok] - private static UIntPtr **activationDescriptorTable; - - [PreInitRefCounts] - internal static - int ScanStacks(NonNullReferenceVisitor VisitThreadReference, - NonNullReferenceVisitor VisitPinnedReference) - { - int limit = Thread.threadTable.Length; - int countThreads = 0; - for (int i = 0; i < limit; i++) { - Thread t = Thread.threadTable[i]; - if (t != null) { - CollectorStatistics.Event(GCEvent.StackScanStart, i); - ScanStack(t, VisitThreadReference, VisitPinnedReference); - CollectorStatistics.Event(GCEvent.StackScanComplete, i); - countThreads++; - } - } - return countThreads; - } - - [PreInitRefCounts] - internal static - uint ScanStack(Thread thread, - NonNullReferenceVisitor VisitThreadReference, - NonNullReferenceVisitor VisitPinnedReference) - { - Trace.Log(Trace.Area.Stack, - "Scanning stack for thread {0:x}", - __arglist(thread.threadIndex)); - - uint numStackFrames = 0; - threadBeingProcessed = thread; - CalleeSaveLocations calleeSaves = new CalleeSaveLocations(); -#if SINGULARITY_KERNEL - TransitionRecord *marker = (TransitionRecord *) thread.context.stackMarkers; -#elif SINGULARITY_PROCESS - TransitionRecord *marker = null; - if (thread.context != null) { - marker = (TransitionRecord *) thread.context->stackMarkers; - } -#else - TransitionRecord *marker = (TransitionRecord *) - MixinThread(thread).asmStackMarker; -#endif - while (marker != null) { - Trace.Log(Trace.Area.Stack, - "Transition record: old={0:x}, callAddr={1:x}, stackBottom={2:x}, EBX={3:x}, EDI={4:x}, ESI={5:x}, EBP={6:x}", __arglist(marker->oldTransitionRecord, marker->callAddr, marker->stackBottom, marker->EBX, marker->EDI, marker->ESI, marker->EBP)); - - TransitionRecord *stopMarker = marker->oldTransitionRecord; - UIntPtr returnAddr = marker->callAddr; - UIntPtr *fp = (UIntPtr *) marker->EBP; - UIntPtr *sp = (UIntPtr *) marker->stackBottom; - calleeSaves.SetCalleeSaves(marker); - numStackFrames += - ScanStackSegment(ref calleeSaves, returnAddr, fp, sp, - stopMarker, VisitThreadReference, - VisitPinnedReference, thread); - marker = marker->oldTransitionRecord; - } - threadBeingProcessed = null; - return numStackFrames; - } - - [PreInitRefCounts] - private static - uint ScanStackSegment(ref CalleeSaveLocations calleeSaves, - UIntPtr returnAddr, - UIntPtr *fp, - UIntPtr *sp, - TransitionRecord *stopMarker, - NonNullReferenceVisitor VisitThreadReference, - NonNullReferenceVisitor VisitPinnedReference, - Thread thread) - // BUGBUG: Remove thread argument when ThreadContext is on stack! - { -#if SINGULARITY - UIntPtr unlinkBegin; - UIntPtr unlinkLimit; - - fixed (byte *begin = - &Microsoft.Singularity.Memory.Stacks.UnlinkStackBegin) { - unlinkBegin = (UIntPtr)begin; - } - fixed (byte *limit = - &Microsoft.Singularity.Memory.Stacks.UnlinkStackLimit) { - unlinkLimit = (UIntPtr)limit; - } -#endif - uint numStackFrames = 0; - while (true) { -#if SINGULARITY - if (returnAddr >= unlinkBegin && returnAddr <= unlinkLimit) { - returnAddr = WalkStack(ref fp, ref sp); - } -#endif - - // Exit loop if we have reached the of the stack segment - if (fp >= stopMarker && sp < stopMarker) { - break; - } - int tableIndex = CallSiteTableNumber(returnAddr); - if (tableIndex < 0) { - break; - } - int callSiteSet = CallSiteSetNumber(returnAddr, tableIndex); - if (callSiteSet < 0) { - break; - } - ushort *callSiteToIndexTable = - callSetSiteNumberToIndex[tableIndex]; - int activationIndex = (int) callSiteToIndexTable[callSiteSet]; - VTable.Assert(activationIndex >= 0); - UIntPtr *descriptorTable = - activationDescriptorTable[tableIndex]; - UIntPtr frameDescriptorEntry = descriptorTable[activationIndex]; - bool done = WalkStackFrame(ref fp, ref sp, ref calleeSaves, - ref returnAddr, - VisitThreadReference, - VisitPinnedReference, - frameDescriptorEntry, - stopMarker, thread); - if (done) { - break; - } - numStackFrames++; - } - calleeSaves.ClearCalleeSaves(); - return numStackFrames; - } - - private static UIntPtr WalkStack(ref UIntPtr *framePointer, - ref UIntPtr *stackPointer) { - // the frame pointer points to the ebp in the old frame - // since at the end of WalkStackFrame, it sets return addr - // and pop up a frame. However, since we are using linked - // stack, "pop up a frame" didn't actually set the frame - // to the caller's frame, it just goes from new frame to the - // old frame, therefore, we need to get return addr and pop - // a frame again. - stackPointer = framePointer + 2; - UIntPtr returnAddr = *(framePointer + 1); - framePointer = (UIntPtr*) *framePointer; - return returnAddr; - } - } -} diff --git a/base/Kernel/Bartok/GCs/ConcurrentMSCollector.cs b/base/Kernel/Bartok/GCs/ConcurrentMSCollector.cs deleted file mode 100644 index 667eb4f..0000000 --- a/base/Kernel/Bartok/GCs/ConcurrentMSCollector.cs +++ /dev/null @@ -1,1086 +0,0 @@ -/*******************************************************************/ -/* WARNING */ -/* This file should be identical in the Bartok and Singularity */ -/* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ -/*******************************************************************/ - -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -#define OVERLAPPING_PHASES - -namespace System.GCs { - - using Microsoft.Bartok.Runtime; - using System.Runtime.CompilerServices; - using System.Threading; - -#if SINGULARITY - using Microsoft.Singularity; -#if SINGULARITY_PROCESS - using Microsoft.Singularity.V1.Services; // Used for timing, only -#elif SINGULARITY_KERNEL - using Microsoft.Singularity.Scheduling; - using Microsoft.Singularity.X86; -#endif -#endif - - /// - /// This class implements a semi-concurrent version of MarkSweep. - /// - /// The goal for this collector is to perform as much of the processing - /// as possible during mutator time, and also have a fixed upper bound - /// on pause times. - /// - /// Additionally, the collector will be required in the future to - /// communicate with the scheduler to prevent real time applications - /// from competing with the GC. - /// - /// - /// BUGBUG: NEED TO REPLICATE THE ALLOCATION COLOR LOGIC TO THE INLINED - /// ALLOCATION SEQUENCE. [T-DANIF 12/3/2004] - /// - - [NoCCtor] - [RequiredByBartok] - internal class ConcurrentMSCollector: BaseCollector - { - - internal enum MarkingPhase { - Dummy, // Not used! - Idle, // No marking is occurring - Requested, // No marking, but will be soon - ComputingRoots, // Marking the roots - Tracing, // Tracing gray objects - } - - internal enum ReclamationPhase { - Dummy, // Not used! - Idle, // No reclamation is in progress - Reclaiming, // Reclamation is in progress - } - - internal static MarkingPhase CurrentMarkingPhase { - get { return (MarkingPhase) currentMarkingPhase; } - set { currentMarkingPhase = (int) value; } - } - - internal static int currentMarkingPhase; - - internal static ReclamationPhase CurrentReclamationPhase; - -#if SINGULARITY - private static TimeSpan infiniteWait; -#else - private static int infiniteWait; -#endif - - /// - /// Current mark state. This is flipped between collections. - /// - internal static UIntPtr markedColor; - internal static UIntPtr unmarkedColor; - internal static UIntPtr reclamationColor; - - private const uint markOne = 1U; - private const uint markTwo = 2U; - private const uint markThree = 3U; - - /// - /// Amount of memory to allocate between collections. - /// BUGBUG: This heuristic is not very good for CMS. - /// - private static UIntPtr collectionTrigger; - - internal static ConcurrentMSCollector instance; - - // Pointer visitors used for marking, etc - private static MarkReferenceVisitor markReferenceVisitor; - private static UpdateReferenceVisitor updateReferenceVisitor; - private static StackMarkReferenceVisitor stackMarkReferenceVisitor; - - // Object visitors used for sweeping, etc - private static SweepVisitor sweepVisitor; - - // Which threads are in what marking stage? - private static UIntPtr[] threadColor; - - // Threads waiting for a particular collection to finish - private static int[] waitingThreads; - private static int firstWaitingThread; - private const int waitingThreadsNullValue = -1; - private const int waitingThreadsUnusedValue = -2; - - private const uint noColor = 0xffffffff; - - // The threads that performs the collection work - private static Thread markThread; - private static Thread workerThread1; - private static Thread workerThread2; - - private static AutoResetEvent sweepPhaseMutex; - - /// - /// Does this collector compute a root set with threads running? - /// - internal override bool IsOnTheFlyCollector { - get { - return true; - } - } - - /// - /// Initialise the collector and allow allocations to commence. - /// - [PreInitRefCounts] - public static void Initialize() { - GC.Initialize(); - SegregatedFreeList.Initialize(); - unmarkedColor = (UIntPtr) markOne; - markedColor = (UIntPtr) markTwo; - reclamationColor = (UIntPtr) markThree; - // instance = new ConcurrentMSCollector(); - ConcurrentMSCollector.instance = (ConcurrentMSCollector) - BootstrapMemory.Allocate(typeof(ConcurrentMSCollector)); - // markReferenceVisitor = new MarkReferenceVisitor(); - markReferenceVisitor = (MarkReferenceVisitor) - BootstrapMemory.Allocate(typeof(MarkReferenceVisitor)); - // updateReferenceVisitor = new UpdateReferenceVisitor(); - updateReferenceVisitor = (UpdateReferenceVisitor) - BootstrapMemory.Allocate(typeof(UpdateReferenceVisitor)); - // stackMarkReferenceVisitor = new StackMarkReferenceVisitor(); - stackMarkReferenceVisitor = (StackMarkReferenceVisitor) - BootstrapMemory.Allocate(typeof(StackMarkReferenceVisitor)); - // sweepVisitor = new SweepVisitor(); - sweepVisitor = (SweepVisitor) - BootstrapMemory.Allocate(typeof(SweepVisitor)); - // threadColor = new UIntPtr[Thread.maxThreads]; - threadColor = (UIntPtr[]) - BootstrapMemory.Allocate(typeof(UIntPtr[]), Thread.maxThreads); - for (int i = 0; i < threadColor.Length; i++) { - threadColor[i] = (UIntPtr) noColor; - } - collectionTrigger = (UIntPtr) (1 << 24); - CurrentMarkingPhase = MarkingPhase.Idle; - CurrentReclamationPhase = ReclamationPhase.Idle; -#if SINGULARITY - infiniteWait = TimeSpan.Infinite; -#else - infiniteWait = Timeout.Infinite; -#endif - } - - /// - /// Perform a collection. Depending on the current phase of collection - /// this method will either: - /// - /// 1. Start a new collection and schedule the mark thread - /// 2. Notice that a collection is underway and exit - /// 3. Clean up after a collection - /// - /// BUGBUG: The interaction between the collector needs work! - /// - internal override void Collect(int currentThreadIndex, - int generation) - { - if (Transitions.HasGCRequest(currentThreadIndex)) { - // We are GC safe, so we may do this - Transitions.TakeDormantControlNoGC(currentThreadIndex); - if (Transitions.TakeGCControl(currentThreadIndex)) { - MutatorHandshake(currentThreadIndex); - Transitions.ReleaseGCControl(currentThreadIndex); - Thread.SignalGCEvent(markThread.threadIndex); - } - Transitions.TakeMutatorControlNoGC(currentThreadIndex); - } else { - if (generation >= 0) { - AddThreadToWaitList(currentThreadIndex); - } - AddCollectionRequest(); - if (generation >= 0) { - Thread currentThread = - Thread.threadTable[currentThreadIndex]; - while (ThreadIsWaiting(currentThreadIndex)) { - currentThread.WaitForEvent(infiniteWait); - } - } - } - } - - internal override void CheckForNeededGCWork(Thread currentThread) { - if (Transitions.HasGCRequest(currentThread.threadIndex)) { - GC.InvokeCollection(currentThread); - } - } - - private static void CollectorHandshake(Thread collectorThread) - { - Transitions.MakeGCRequests(collectorThread.threadIndex); - // Handshake with all the (other) threads. - for(int i=0; i < Thread.threadTable.Length; i++) { -#if SINGULARITY_KERNEL - if (Scheduler.IsIdleThread(i)) { - continue; - } -#endif - // Is there an unscanned thread here? - while (Transitions.HasGCRequest(i)) { - if (Transitions.TakeGCControl(i)) { - MutatorHandshake(i); - Transitions.ReleaseGCControl(i); - Thread.SignalGCEvent(i); - } else if (Thread.threadTable[i] == null) { - // The thread must have terminated but the - // state hasn't yet changed to DormantState. - break; - } else { - Thread.WaitForGCEvent(collectorThread.threadIndex); - } - } - } - - if (CurrentMarkingPhase == MarkingPhase.ComputingRoots && - !WriteBarrierCMS.InSnoopingPhase) { - MultiUseWord.CollectFromPreviousCollections(false); - } - } - - private static void MutatorHandshake(int threadIndex) - { - if (CurrentMarkingPhase == MarkingPhase.ComputingRoots && - !WriteBarrierCMS.InSnoopingPhase) { - Thread thread = Thread.threadTable[threadIndex]; - if (thread != null) { - ScanThreadRoots(thread); - MultiUseWord.CollectFromThread(thread); - } - } - } - - private static void ScanThreadRoots(Thread thread) { - long start = CollectorStatistics.PerformanceCounter; - CollectorStatistics.Event(GCEvent.StackScanStart, - thread.threadIndex); - CallStack.ScanStack(thread, stackMarkReferenceVisitor, - stackMarkReferenceVisitor); - threadColor[thread.threadIndex] = markedColor; - // Report the pause - long pause = - CollectorStatistics.PerformanceCounter - start; - CollectorStatistics.Event(GCEvent.StackScanComplete, - pause); - } - - private static bool terminateCollectorThreads; - - private static void TraceThreadNotification() { - terminateCollectorThreads = true; - GC.Collect(); - } - - /// - /// This method is run by the collector threads. - /// - private static void CollectionLoop() { - Thread currentThread = Thread.CurrentThread; -#if SINGULARITY_PROCESS - currentThread.MakeServiceThread(new Thread.StopServiceNotice(TraceThreadNotification)); -#endif - while (!terminateCollectorThreads) { - // Wait to be told to start working. - while (!TakeChargeOfTraceRequest()) { - currentThread.WaitForEvent(infiniteWait); - } -#if SINGULARITY -#if DEBUG - DebugStub.WriteLine("~~~~~ Start Concurrent Marking [data={0:x8}, pid={1:x3}]", - __arglist(SegregatedFreeList.TotalBytes, - PageTable.processTag >> 16)); -#endif - int startTicks = Environment.TickCount; -#endif - markThread = currentThread; - advanceMarkColors(); - // Construct the root set. - CollectorStatistics.Event(GCEvent.ComputeRootSet); - StartRootMarkingPhase(); - WriteBarrierCMS.EnterSnoopingPhase(); - // Start the process of recycling pages - SegregatedFreeList.RecycleGlobalPagesPhase1(); - // One handshake to ensure that everyone starts snooping - CollectorHandshake(currentThread); - // Complete the process of recycling pages - SegregatedFreeList.RecycleGlobalPagesPhase2(); - // Another handshake to ensure that all updates started by - // other threads prior to their handshake are done and - // snooping will affect all new updates. - CollectorHandshake(currentThread); - WriteBarrierCMS.LeaveSnoopingPhase(); - ScanThreadRoots(currentThread); - // A third handshake to get the threads to process their - // own roots. - CollectorHandshake(currentThread); - Finalizer.PrepareCollectFinalizers(); - Thread.VisitBootstrapData(markReferenceVisitor); -#if SINGULARITY_KERNEL - Kernel.VisitSpecialData(markReferenceVisitor); -#endif - MultiUseWord.VisitStrongRefs(markReferenceVisitor, - false /* Don't use shadows */); -#if !VC - TryAllManager.VisitStrongRefs(markReferenceVisitor); -#endif - StaticData.ScanStaticData(markReferenceVisitor); - CollectorStatistics.Event(GCEvent.RootSetComputed); - int waitingThreadList = StealWaitingThreadsList(); - // We are now in the concurrent tracing phase. - ResetCollectorRequests(); - StartTracingPhase(); - CollectorStatistics.Event(GCEvent.TraceStart, - ReservedMemory); - // Trace live objects from the root set. - markReferenceVisitor.ProcessScheduledObjects(); - CollectorStatistics.Event(GCEvent.TraceSpecial); - // Mark weak references that do not track resurrection as dead. - WeakReference.Process(updateReferenceVisitor, true, true); - // Resurrect any finalization candidates. - Finalizer.ResurrectCandidates(updateReferenceVisitor, - markReferenceVisitor, true); - // Complete closure from finalized objects. - markReferenceVisitor.ProcessScheduledObjects(); - // Mark appropriate weak references as dead - WeakReference.Process(updateReferenceVisitor, true, false); - MultiUseWord.VisitWeakRefs(updateReferenceVisitor, - false /* Don't use shadows */); -#if !VC - TryAllManager.VisitWeakRefs(updateReferenceVisitor); -#endif - MultiUseWord.PostGCHook(); - // Reset thread queues. They should all be empty. - markReferenceVisitor.Cleanup(); -#if SINGULARITY -#if DEBUG - int middleTicks = Environment.TickCount; - DebugStub.WriteLine("~~~~~ Finish Concurrent Marking [data={0:x8}, pid={1:x3} ms={2:d6}]", - __arglist(SegregatedFreeList.TotalBytes, - PageTable.processTag >> 16, - middleTicks - startTicks)); -#endif -#endif - - markThread = nextWorkerThread(currentThread); - sweepPhaseMutex.WaitOne(); - try { - reclamationColor = unmarkedColor; - FinishTracingPhase(); - SatisfyCollectorRequest(); // May start another trace phase - // Sweep garbage objects - StartReclamationPhase(); - CollectorStatistics.Event(GCEvent.SweepStart, - ReservedMemory); - Sweep(); - // Clean up after the collection - CollectorStatistics.Event(GCEvent.SweepSpecial, - ReservedMemory); - Finalizer.ReleaseCollectFinalizers(); - CollectorStatistics.Event(GCEvent.SweepPreCommit, - ReservedMemory); - // Commit accounting changes - CommitSweep(); - CollectorStatistics.Event(GCEvent.CollectionComplete, - ReservedMemory); - // TODO: Determine a new collection trigger? - SignalWaitingThreads(waitingThreadList); - FinishReclamationPhase(); - } finally { - sweepPhaseMutex.Set(); - } -#if SINGULARITY -#if DEBUG - int endTicks = Environment.TickCount; - DebugStub.WriteLine("~~~~~ Finish Concurrent Reclamation [data={0:x8}, pid={1:x3} ms={2:d6}]", - __arglist(ReservedMemory, - PageTable.processTag >> 16, - endTicks - middleTicks)); -#endif -#endif - } - } - - /// - /// Allocate memory for a new object, potentially triggering a - /// collection. - /// - [Inline] - internal override UIntPtr AllocateObjectMemory(UIntPtr numBytes, - uint alignment, - Thread currentThread) - { - UIntPtr resultAddr = - SegregatedFreeList.AllocateFast(currentThread, - numBytes, alignment); - if (resultAddr == UIntPtr.Zero) { - resultAddr = AllocateObjectMemorySlow(numBytes, alignment, - currentThread); - } - return resultAddr; - } - - [NoInline] - private UIntPtr AllocateObjectMemorySlow(UIntPtr numBytes, - uint alignment, - Thread currentThread) - { - if (GC.newBytesSinceGC > collectionTrigger) { - if (CurrentMarkingPhase == MarkingPhase.Idle || - Transitions.HasGCRequest(currentThread.threadIndex)) { - GC.InvokeCollection(currentThread); - } else if (GC.newBytesSinceGC > (collectionTrigger<<1)) { - // Slow down the allocating thread a bit - Thread.Yield(); - } - } - return SegregatedFreeList.AllocateSlow(currentThread, - numBytes, alignment); - } - - [Inline] - protected override void CreateObject(Object obj, VTable vtable, - Thread currentThread) - { - // We expect the color to be assigned before the vtable field - // is initialized. This ensures that every real object has a - // valid color. - UIntPtr markBits = threadColor[currentThread.threadIndex]; - ThreadHeaderQueue.SetGcMark(obj, markBits); - // The vtable field must be initialized before the object is - // inserted into a list of objects to be scanned. - base.CreateObject(obj, vtable, currentThread); - // If necessary, mark the object for future scanning - if (CurrentMarkingPhase == MarkingPhase.ComputingRoots && - Transitions.HasGCRequest(currentThread.threadIndex)) { - ThreadHeaderQueue.PushUnsafe(currentThread, obj, markBits); - } - } - - /// - /// Return the generation for an object. We only have one - /// generation so we always return generation zero. - /// - internal override int GetGeneration(Object obj) { - Verifier.genericObjectVisitor.Visit(obj); - return MinGeneration; - } - - /// - /// The maximum generation. For MarkSweep this is generation zero. - /// - internal override int MaxGeneration { - get { return (int)PageType.Owner0; } - } - - /// - /// The minimum generation. For MarkSweep this is generation zero. - /// - internal override int MinGeneration { - get { return (int)PageType.Owner0; } - } - - /// - /// This returns the total amount of memory that is allocated within - /// the collected heap. - /// - internal override long TotalMemory { - get { - return ReservedMemory; - } - } - - private static long ReservedMemory { - get { - return (long)SegregatedFreeList.TotalBytes; - } - } - - internal override void EnableHeap() { - // waitingThreads = new int[Thread.maxThreads] - waitingThreads = new int[Thread.maxThreads]; - for (int i = 0; i < waitingThreads.Length; i++) { - waitingThreads[i] = waitingThreadsUnusedValue; - } - firstWaitingThread = -1; - // Construct the collector thread(s) -#if SINGULARITY_KERNEL - workerThread1 = - Thread.CreateThread(Process.kernelProcess, - new ThreadStart(CollectionLoop)); - workerThread2 = - Thread.CreateThread(Process.kernelProcess, - new ThreadStart(CollectionLoop)); -#else - workerThread1 = new Thread(new ThreadStart(CollectionLoop)); - workerThread2 = new Thread(new ThreadStart(CollectionLoop)); -#endif - workerThread1.Start(); - workerThread2.Start(); - markThread = workerThread1; - sweepPhaseMutex = new AutoResetEvent(true); - } - - /// - /// Destroy the heap. Nothing to do here. - /// - internal override void DestructHeap() { - // Do nothing - while (CurrentMarkingPhase != MarkingPhase.Idle && - CurrentReclamationPhase != ReclamationPhase.Idle) { - Thread.Yield(); - } - } - - /// - /// Verify the heap. - /// - internal override void VerifyHeap(bool beforeCollection) { - SegregatedFreeList.VisitAllObjects(VerifyVisitor.visitor); - Verifier.segregatedFreeListVerifier.VerifyHeap(); - } - - private static char ToHexDigit(int number, int position) { - int digit = (number >> (position * 4)) & 0xf; - return (char) (digit + ((digit <= 9) ? '0' : ('A' - 10))); - } - private static int flag; // = 0; - private static void DebugTrace(String text, int threadIndex) { - while (Interlocked.CompareExchange(ref flag, 1, 0) != 0) { } - VTable.DebugPrint(text+" "+ - ToHexDigit(threadIndex, 2)+ - ToHexDigit(threadIndex, 1)+ - ToHexDigit(threadIndex, 0)+ - "\n"); - Interlocked.Exchange(ref flag, 0); - } - - /// Routines to keep track of requests for collection work - - private static int collectorStack; // 0:idle, 1:work, 2+:work+pending - - private static void AddCollectionRequest() { - int stackHeight = Interlocked.Increment(ref collectorStack); - VTable.Assert(stackHeight > 0); - if (stackHeight == 1) { - MakeTraceRequest(); - } - } - - private static void ResetCollectorRequests() { - Interlocked.Exchange(ref collectorStack, 1); - } - - private static void SatisfyCollectorRequest() { - int stackHeight = Interlocked.Decrement(ref collectorStack); - VTable.Assert(stackHeight >= 0); - if (stackHeight > 0 ) { - MakeTraceRequest(); - } - } - - /// Routines to control the commencement of the tracing phase - - private static void MakeTraceRequest() { - CurrentMarkingPhase = MarkingPhase.Requested; - markThread.SignalEvent(); - } - - private static bool TakeChargeOfTraceRequest() { - return - (Interlocked.CompareExchange(ref currentMarkingPhase, - (int) MarkingPhase.ComputingRoots, - (int) MarkingPhase.Requested) == - (int) MarkingPhase.Requested); - } - - /// Routines to keep track of threads that must be notified when - /// a collection has been completed. - - private static void AddThreadToWaitList(int threadIndex) { - int listHead = firstWaitingThread; - waitingThreads[threadIndex] = listHead; - while (Interlocked.CompareExchange(ref firstWaitingThread, - threadIndex, listHead) != - listHead) { - listHead = firstWaitingThread; - waitingThreads[threadIndex] = listHead; - } - } - - private static bool ThreadIsWaiting(int threadIndex) { - return (waitingThreads[threadIndex] != waitingThreadsUnusedValue); - } - - private static int StealWaitingThreadsList() { - return Interlocked.Exchange(ref firstWaitingThread, - waitingThreadsNullValue); - } - - private static void SignalWaitingThreads(int listHead) { - while (listHead != waitingThreadsNullValue) { - int threadIndex = listHead; - listHead = waitingThreads[threadIndex]; - waitingThreads[threadIndex] = waitingThreadsUnusedValue; - Thread.threadTable[threadIndex].SignalEvent(); - } - } - - // Routines to keep track of what phases the collector threads are in. - - private static void StartRootMarkingPhase() { - CurrentMarkingPhase = MarkingPhase.ComputingRoots; - } - - private static void StartTracingPhase() { - CurrentMarkingPhase = MarkingPhase.Tracing; - } - - private static void FinishTracingPhase() { - CurrentMarkingPhase = MarkingPhase.Idle; - } - - private static void StartReclamationPhase() { - CurrentReclamationPhase = ReclamationPhase.Reclaiming; - } - - private static void FinishReclamationPhase() { - CurrentReclamationPhase = ReclamationPhase.Idle; - } - - // Routines to manage marking colors - - private static void advanceMarkColors() - { - unmarkedColor = markedColor; - markedColor = nextColor(markedColor); - } - - private static UIntPtr nextColor(UIntPtr originalColor) - { - switch ((uint) originalColor) { - case markOne: return (UIntPtr) markTwo; - case markTwo: return (UIntPtr) markThree; - case markThree: return (UIntPtr) markOne; - default: throw new Exception("advanceColor failure!"); - } - } - - private static Thread nextWorkerThread(Thread currentThread) { -#if OVERLAPPING_PHASES - return ((currentThread == workerThread1) ? - workerThread2 : - workerThread1); -#else - return currentThread; -#endif - } - - /// - /// Walk the allocation structures and reclaim any free cells. - /// - private static void Sweep() { - SegregatedFreeList.VisitAllObjects(sweepVisitor); - } - - /// - /// Update alloc heap to account for data just freed. - /// - private static void CommitSweep() { - SegregatedFreeList.CommitFreedData(); - } - - /// - /// Routines for updating pointers to new locations of marked objects - /// No objects are resurrected using this mechanism. - /// As this is mark sweep the value does not need to be updated. - /// - private class UpdateReferenceVisitor: NonNullReferenceVisitor - { - - internal unsafe override void Visit(UIntPtr *loc) { - UIntPtr addr = *loc; - // Ignore pointers out of our memory area - if (PageTable.IsForeignAddr(addr)) { - return; - } - UIntPtr page = PageTable.Page(addr); - PageType pageType = PageTable.Type(page); - if (!PageTable.IsGcPage(pageType)) { - VTable.Assert((PageTable.IsNonGcPage(pageType) && - PageTable.IsMyPage(page)) || - PageTable.IsStackPage(pageType), - "update.visit invalid page"); - return; - } - VTable.Assert(PageTable.IsMyPage(page)); - Object obj = Magic.fromAddress(addr); - if (ThreadHeaderQueue.GcMark(obj) == unmarkedColor) { - // The object was not live - *loc = UIntPtr.Zero; - } - VTable.Assert(obj.vtable != null, "update.visit null vtable"); - } - } - - /// - /// Select the generation to collect (always generation 0) - /// - internal override int CollectionGeneration(int genRequest) { - return MinGeneration; - } - - - /// - /// This visitor is the core of the tracing functionality. - /// It builds up a buffer of references (the root set), and - /// then at a later point the tracing thread processes these - /// buffers. - /// - private class MarkReferenceVisitor : NonNullReferenceVisitor - { - - /// - /// Visit an object reference. - /// - internal unsafe override void Visit(UIntPtr *loc) { - UIntPtr addr = *loc; - if (addr == UIntPtr.Zero) return; - VisitValue(addr); - } - - - public void VisitValue(UIntPtr addr) { - // Ignore pointers out of our memory area - if (PageTable.IsForeignAddr(addr)) { - return; - } - UIntPtr page = PageTable.Page(addr); - PageType pageType = PageTable.Type(page); - if (!PageTable.IsGcPage(pageType)) { - VTable.Assert((PageTable.IsNonGcPage(pageType) && - PageTable.IsMyPage(page)) || - PageTable.IsStackPage(pageType), - "value.visit invalid page"); - return; - } - VTable.Assert(PageTable.IsMyPage(page)); - Object obj = Magic.fromAddress(addr); - VTable.Assert(obj.vtable != null, - "value.visit null vtable"); - WriteBarrierCMS.MarkObject(obj); - } - - /// - /// Process all marked objects from queues stored in - /// thread objects. - /// - public void ProcessScheduledObjects() { - Thread currentThread = Thread.CurrentThread; - StealWork(); - do { - while (!ThreadHeaderQueue.IsEmpty(currentThread)) { - // Pop the next value - Object obj = - ThreadHeaderQueue.Pop(currentThread, markedColor); - // Visit Fields - this.VisitReferenceFields(obj); - } - } while (StealWork()); - } - - /// - /// Look through other threads and see if any have some values on - /// their queues that we can steal. - /// - private bool StealWork() { - Thread[] threadTable = Thread.threadTable; - Thread me = Thread.CurrentThread; - bool foundWork = false; - // Attempt to steal work from live threads - for (int i = 0; i < threadTable.Length; i++) { - Thread t = threadTable[i]; - if (t != null && t != me && - ThreadHeaderQueue.Steal(me, t, markedColor)) { - foundWork = true; - } - } - return foundWork; - } - - /// - /// Clean up after processing all queues. This involves calling - /// reset on each thread's queue. - /// - internal void Cleanup() { - Thread[] threadTable = Thread.threadTable; - for (int i = 0; i < threadTable.Length; i++) { - Thread t = threadTable[i]; - if (t != null) { - if (!ThreadHeaderQueue.IsEmpty(t)) { - // The queues may contain new objects allocated - // during the root-set computation phase but added - // to the queue after the scanning has completed. - // This is benign and we can clear the queues. - do { - Object head = - ThreadHeaderQueue.Pop(t, markedColor); - VTable.Assert(ThreadHeaderQueue.GcMark(head) == - markedColor); - } while (!ThreadHeaderQueue.IsEmpty(t)); - } - ThreadHeaderQueue.Reset(t); - } - } - } - - } - - /// - /// This class maps an interior pointer back to the containing object - /// pointer and then passes it on to the object marking visitor. - /// - private class StackMarkReferenceVisitor : NonNullReferenceVisitor - { - - /// - /// Visit an interior pointer stored in loc. - /// - internal unsafe override void Visit(UIntPtr *loc) { - UIntPtr addr = *loc; - // Ignore pointers out of our memory area - if (PageTable.IsForeignAddr(addr)) { - return; - } - UIntPtr page = PageTable.Page(addr); - PageType pageType = PageTable.Type(page); - if (!PageTable.IsGcPage(pageType)) { - VTable.Assert((PageTable.IsNonGcPage(pageType) && - PageTable.IsMyPage(page)) || - PageTable.IsStackPage(pageType) || - PageTable.IsSharedPage(pageType), - "interior.visit invalid page"); - return; - } - UIntPtr realPtr = SegregatedFreeList.Find(addr); - VTable.Assert(Magic.fromAddress(realPtr).vtable != null, - "interior.visit null vtable"); - markReferenceVisitor.VisitValue(realPtr); - } - - } - - - /// - /// This class is used to visit every small object, determine if it - /// is marked and free it if not. - /// - private class SweepVisitor : SegregatedFreeList.ObjectVisitor - { - - private SegregatedFreeList.TempList tempList; - - internal override void VisitSmall(Object obj, UIntPtr memAddr) - { - if (ThreadHeaderQueue.GcMark(obj) == reclamationColor) { - // Not marked. - tempList.Add(memAddr); - } - } - - internal override void VisitSmallPageEnd() { - SegregatedFreeList.FreeSmallList(ref tempList); - } - - internal override UIntPtr VisitLarge(Object obj) - { - UIntPtr objectSize = ObjectLayout.Sizeof(obj); - if (ThreadHeaderQueue.GcMark(obj) == reclamationColor) { - // Not marked. - SegregatedFreeList.FreeLarge(obj); - } - return objectSize; - } - - } - - /// - /// Find the object address for a given interior pointer. - /// - internal override UIntPtr FindObjectAddr(UIntPtr interiorPtr) { - return SegregatedFreeList.Find(interiorPtr); - } - - /// - /// Visit all objects in the heap with a specified visitor. - /// - internal override - void VisitObjects(ObjectLayout.ObjectVisitor objectVisitor, - UIntPtr lowAddr, UIntPtr highAddr) - { - VTable.Assert(PageTable.PageAligned(lowAddr), - "low not page aligned"); - VTable.Assert(PageTable.PageAligned(highAddr), - "high not page aligned"); - UIntPtr lowPage = PageTable.Page(lowAddr); - UIntPtr highPage = PageTable.Page(highAddr); - SegregatedFreeList.VisitObjects(lowPage, highPage, objectVisitor); - } - - /// - /// A new thread has been created, set any allocator/collector state. - /// - internal override void NewThreadNotification(Thread newThread, - bool initial) - { - base.NewThreadNotification(newThread, initial); - threadColor[newThread.threadIndex] = markedColor; - ThreadHeaderQueue.Reset(newThread); - SegregatedFreeList.NewThreadNotification(newThread, initial); - if (CurrentMarkingPhase == MarkingPhase.ComputingRoots) { - Transitions.MakeGCRequest(newThread.threadIndex); - } - } - - internal override void DeadThreadNotification(Thread deadThread) - { - MultiUseWord.CollectFromThread(deadThread); - SegregatedFreeList.DeadThreadNotification(deadThread); - ThreadHeaderQueue.DeadThreadNotification(deadThread, markedColor); - threadColor[deadThread.threadIndex] = (UIntPtr) noColor; - base.DeadThreadNotification(deadThread); - } - - internal override void ThreadStartNotification(int currentThreadIndex) - { - base.ThreadStartNotification(currentThreadIndex); - threadColor[currentThreadIndex] = markedColor; - if (CurrentMarkingPhase == MarkingPhase.ComputingRoots) { - Transitions.MakeGCRequest(currentThreadIndex); - } - } - - internal override void ThreadDormantGCNotification(int threadIndex) { - // We could scan our own stack, but instead we try to get - // some work done while the Trace thread scans our stack. - Thread.SignalGCEvent(markThread.threadIndex); - } - - /// - /// This class is used to verify that there are no dangling pointers. - /// - private class VerifyVisitor : SegregatedFreeList.ObjectVisitor - { - - internal static VerifyVisitor visitor = new VerifyVisitor(); - - internal override void VisitSmall(Object obj, UIntPtr memAddr) { - if (ThreadHeaderQueue.GcMark(obj) == markedColor) { - VerifyMarkVisitor.visitor.VisitReferenceFields(obj); - } else { - VTable.Assert(ThreadHeaderQueue.GcMark(obj) == - unmarkedColor); - } - } - - internal override UIntPtr VisitLarge(Object obj) { - UIntPtr size; - if (ThreadHeaderQueue.GcMark(obj) == markedColor) { - // The object has the mark color, so it should only - // reference other objects with the mark color. - size = VerifyMarkVisitor.visitor.VisitReferenceFields(obj); - } else { - VTable.Assert(ThreadHeaderQueue.GcMark(obj) == - unmarkedColor); - size = ObjectLayout.Sizeof(obj); - } - return size; - } - - } - - /// - /// This class is used to check that all the pointers within a marked - /// object point into other marked objects. - /// - private class VerifyMarkVisitor : NonNullReferenceVisitor - { - - internal static VerifyMarkVisitor visitor - = new VerifyMarkVisitor(); - - internal unsafe override void Visit(UIntPtr *loc) { - UIntPtr addr = *loc; - UIntPtr page = PageTable.Page(addr); - if (PageTable.IsGcPage(page)) { - Object obj = Magic.fromAddress(addr); - - VTable.Assert(ThreadHeaderQueue.GcMark(obj) == markedColor, - "dangling pointer!"); - } - } - - } - - /// - /// This method loops through all non-null threads and asserts that - /// no thread has any work on its marking queue. - /// - private void VerifyEmptyQueues() { - Thread[] threadTable = Thread.threadTable; - for (int i = 0; i < threadTable.Length; i++) { - Thread t = threadTable[i]; - if (t != null) { - VTable.Assert(ThreadHeaderQueue.IsEmpty(t), - "Non-empty Queue!"); - } - } - } - - /// - /// This method walks through all objects in the heap to ensure - /// that no objects have values in their queue header field - /// - private void VerifyQueueHeaders() { - SegregatedFreeList.VisitAllObjects(VerifyHeaderVisitor.visitor); - } - - /// - /// This visitor trivially asserts that the objects queue header - /// field is zero. - /// - private class VerifyHeaderVisitor : SegregatedFreeList.ObjectVisitor - { - - internal static VerifyHeaderVisitor visitor - = new VerifyHeaderVisitor(); - - /// - /// Visit small objects, checking queue header. - /// - internal unsafe override void VisitSmall(Object obj, - UIntPtr memAddr) - { - VTable.Deny(ThreadHeaderQueue.IsInQueue(obj), - "Object in ThreadHeaderQueue"); - } - - internal override UIntPtr VisitLarge(Object obj) { - VTable.Deny(ThreadHeaderQueue.IsInQueue(obj), - "Object in ThreadHeaderQueue"); - return ObjectLayout.Sizeof(obj); - } - - } - - } - -} diff --git a/base/Kernel/Bartok/GCs/EmptyWriteBarrier.cs b/base/Kernel/Bartok/GCs/EmptyWriteBarrier.cs deleted file mode 100644 index 02a3511..0000000 --- a/base/Kernel/Bartok/GCs/EmptyWriteBarrier.cs +++ /dev/null @@ -1,89 +0,0 @@ -/*******************************************************************/ -/* WARNING */ -/* This file should be identical in the Bartok and Singularity */ -/* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ -/*******************************************************************/ - -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -namespace System.GCs { - - using Microsoft.Bartok.Runtime; - using System.Runtime.CompilerServices; - - internal unsafe class EmptyWriteBarrier : WriteBarrier - { - - internal static EmptyWriteBarrier instance; - - internal static new void Initialize() { - EmptyWriteBarrier.instance = (EmptyWriteBarrier) - BootstrapMemory.Allocate(typeof(EmptyWriteBarrier)); - } - - [Inline] - protected override void CopyStructImpl(VTable vtable, - UIntPtr srcPtr, - UIntPtr dstPtr) - { - CopyStructNoBarrier(vtable, srcPtr, dstPtr); - } - - [Inline] - protected override Object AtomicSwapImpl(ref Object reference, - Object value) - { - return AtomicSwapNoBarrier(ref reference, value); - } - - [Inline] - protected override - Object AtomicCompareAndSwapImpl(ref Object reference, - Object newValue, - Object comparand) - { - return AtomicCompareAndSwapNoBarrier(ref reference, newValue, - comparand); - } - - [Inline] - protected override void CloneImpl(Object srcObject, Object dstObject) - { - CloneNoBarrier(srcObject, dstObject); - } - - // 'offset' is not relative to the lower bound, but is a count - // of elements from the first element in the array. - [Inline] - protected override void ArrayZeroImpl(Array array, - int offset, - int length) - { - ArrayZeroNoBarrier(array, offset, length); - } - - // 'offset' is not relative to the lower bound, but is a count - // of elements from the first element in the array. - [Inline] - protected override void ArrayCopyImpl(Array srcArray, int srcOffset, - Array dstArray, int dstOffset, - int length) - { - ArrayCopyNoBarrier(srcArray, srcOffset, - dstArray, dstOffset, - length); - } - - [Inline] - protected override void WriteReferenceImpl(UIntPtr *location, - Object value) - { - *location = Magic.addressOf(value); - } - - } - -} diff --git a/base/Kernel/Bartok/GCs/GenerationalWriteBarrier.cs b/base/Kernel/Bartok/GCs/GenerationalWriteBarrier.cs deleted file mode 100644 index 1c8faef..0000000 --- a/base/Kernel/Bartok/GCs/GenerationalWriteBarrier.cs +++ /dev/null @@ -1,165 +0,0 @@ -/*******************************************************************/ -/* WARNING */ -/* This file should be identical in the Bartok and Singularity */ -/* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ -/*******************************************************************/ - -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -namespace System.GCs { - - using Microsoft.Bartok.Runtime; - using System.Runtime.CompilerServices; - using System.Threading; - - internal unsafe class GenerationalWriteBarrier : WriteBarrier - { - - internal static GenerationalWriteBarrier instance; - - internal static new void Initialize() { - GenerationalWriteBarrier.instance = (GenerationalWriteBarrier) - BootstrapMemory.Allocate(typeof(GenerationalWriteBarrier)); - } - - [Inline] - protected override void StoreStaticFieldImpl(ref Object staticField, - Object value) - { - // No need to perform a ReferenceCheck here! - *Magic.toPointer(ref staticField) = Magic.addressOf(value); - } - - protected override void CopyStructImpl(VTable vtable, - UIntPtr srcPtr, - UIntPtr dstPtr) - { - CopyStructWithBarrier(vtable, srcPtr, dstPtr); - } - - [Inline] - protected override Object AtomicSwapImpl(ref Object reference, - Object value) - { - UIntPtr resultAddr = - Interlocked.Exchange(Magic.toPointer(ref reference), - Magic.addressOf(value)); - ReferenceCheck(Magic.toPointer(ref reference), value); - return Magic.fromAddress(resultAddr); - } - - [Inline] - protected override - Object AtomicCompareAndSwapImpl(ref Object reference, - Object newValue, - Object comparand) - { - UIntPtr resultAddr = - Interlocked.CompareExchange(Magic.toPointer(ref reference), - Magic.addressOf(newValue), - Magic.addressOf(comparand)); - ReferenceCheck(Magic.toPointer(ref reference), newValue); - return Magic.fromAddress(resultAddr); - } - - [Inline] - protected override void CloneImpl(Object srcObject, Object dstObject) - { - CloneNoBarrier(srcObject, dstObject); - ReferenceCheck(dstObject); - } - - // 'offset' is not relative to the lower bound, but is a count - // of elements from the first element in the array. - protected override void ArrayZeroImpl(Array array, - int offset, - int length) - { - ArrayZeroNoBarrier(array, offset, length); - } - - // 'offset' is not relative to the lower bound, but is a count - // of elements from the first element in the array. - protected override void ArrayCopyImpl(Array srcArray, int srcOffset, - Array dstArray, int dstOffset, - int length) - { - if ((length > 1000) || ((length << 2) >= dstArray.Length)) { - ArrayCopyNoBarrier(srcArray, srcOffset, - dstArray, dstOffset, - length); - ReferenceCheck(dstArray); - } else { - ArrayCopyWithBarrier(srcArray, srcOffset, - dstArray, dstOffset, - length); - } - } - - [Inline] - protected override void WriteReferenceImpl(UIntPtr *location, - Object value) - { - *location = Magic.addressOf(value); - ReferenceCheck(location, value); - } - - [Inline] - private static void ReferenceCheck(Object obj) { - PageType pageType = - PageTable.Type(PageTable.Page(Magic.addressOf(obj))); - if (GenerationalCollector.MAX_GENERATION == PageType.Owner1) { - if (pageType == PageType.Owner1) { - GenerationalCollector. - installedRemSet.RecordClonedObject(obj); - } - } else { - if (pageType != GenerationalCollector.nurseryGeneration) { - GenerationalCollector. - installedRemSet.RecordClonedObject(obj); - } - } - } - - [Inline] - private static void ReferenceCheck(UIntPtr *addr, Object value) - { - PageType addrType = PageTable.Type(PageTable.Page((UIntPtr) addr)); - if (GenerationalCollector.MAX_GENERATION == PageType.Owner1) { - if (addrType != PageType.Owner1) { - return; - } else { - ReferenceCheck(addrType, addr, value); - } - } else { - if (PageTable.IsLiveGcPage(addrType)){ - ReferenceCheck(addrType, addr, value); - } - } - } - - [NoInline] - private static void ReferenceCheck(PageType addrType, UIntPtr *addr, - Object value) { - VTable.Assert(PageTable.IsGcPage(addrType)); - - if (GC.remsetType == RemSetType.Cards) { - GenerationalCollector. - installedRemSet.RecordReference(addr, value); - return; - } - - UIntPtr valueAddr = Magic.addressOf(value); - PageType valType = PageTable.Type(PageTable.Page(valueAddr)); - if (PageTable.IsGcPage(valType) && (addrType > valType)){ - GenerationalCollector. - installedRemSet.RecordReference(addr, value); - } - } - - } - -} diff --git a/base/Kernel/Bartok/GCs/MarkSweepCollector.cs b/base/Kernel/Bartok/GCs/MarkSweepCollector.cs deleted file mode 100644 index d3a2e8f..0000000 --- a/base/Kernel/Bartok/GCs/MarkSweepCollector.cs +++ /dev/null @@ -1,560 +0,0 @@ -/*******************************************************************/ -/* WARNING */ -/* This file should be identical in the Bartok and Singularity */ -/* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ -/*******************************************************************/ - -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -namespace System.GCs { - - using Microsoft.Bartok.Runtime; - using System.Runtime.CompilerServices; - using System.Threading; - -#if SINGULARITY - using Microsoft.Singularity; -#if SINGULARITY_PROCESS - using Microsoft.Singularity.V1.Services; - using Microsoft.Singularity.V1.Threads; -#else - using Microsoft.Singularity.Memory; -#endif -#endif - - [NoCCtor] - [RequiredByBartok] - internal class MarkSweepCollector: StopTheWorldCollector - { - private static UIntPtr collectionTrigger; - - internal static MarkSweepCollector instance; - - // Visitor instances used for marking objects - private static MarkReferenceVisitor markReferenceVisitor; - private static UpdateReferenceVisitor updateReferenceVisitor; - private static ThreadMarkReferenceVisitor threadMarkReferenceVisitor; - private static SweepVisitor sweepVisitor; - - private static GCProfiler gcProfiler; - private static ProfileRootsDelegate profileRoots; - private static ProfileObjectsDelegate profileObjects; - private static bool heapDamaged; - - private MarkSweepCollector() { - } - - public static new void Initialize() { - GC.Initialize(); - StopTheWorldCollector.Initialize(); - SegregatedFreeList.Initialize(); - // instance = new MarkSweepCollector(); - MarkSweepCollector.instance = (MarkSweepCollector) - BootstrapMemory.Allocate(typeof(MarkSweepCollector)); - // markReferenceVisitor = new MarkReferenceVisitor(); - markReferenceVisitor = (MarkReferenceVisitor) - BootstrapMemory.Allocate(typeof(MarkReferenceVisitor)); - // updateReferenceVisitor = new UpdateReferenceVisitor(); - updateReferenceVisitor = (UpdateReferenceVisitor) - BootstrapMemory.Allocate(typeof(UpdateReferenceVisitor)); - // threadMarkReferenceVisitor = new ThreadMarkReferenceVisitor(); - threadMarkReferenceVisitor = (ThreadMarkReferenceVisitor) - BootstrapMemory.Allocate(typeof(ThreadMarkReferenceVisitor)); - // sweepVisitor = new SweepVisitor(); - sweepVisitor = (SweepVisitor) - BootstrapMemory.Allocate(typeof(SweepVisitor)); - collectionTrigger = (UIntPtr) (1 << 24); - } - - // GCInterface methods - internal override bool IsOnTheFlyCollector { - get { - return false; - } - } - - internal override void SetProfiler(GCProfiler profiler) - { - if (gcProfiler != null) { - throw new InvalidOperationException("Only one GCProfiler can be active in a process"); - } - profileRoots = new ProfileRootsDelegate(ProfileScanRoots); - profileObjects = new ProfileObjectsDelegate(ProfileScanObjects); - gcProfiler = profiler; - } - - internal override void CollectStopped(int currentThreadIndex, - int generation) - { -#if SINGULARITY - UIntPtr preGcTotalBytes = SegregatedFreeList.TotalBytes; -#if DEBUG - DebugStub.WriteLine("~~~~~ Start MarkSweep Cleanup [data={0:x8}, pid={1:x3}]", - __arglist(SegregatedFreeList.TotalBytes, - PageTable.processTag >> 16)); -#endif -#if SINGULARITY_KERNEL - #if THREAD_TIME_ACCOUNTING - TimeSpan ticks = Thread.CurrentThread.ExecutionTime; - TimeSpan ticks2 = SystemClock.KernelUpTime; - #else - TimeSpan ticks = SystemClock.KernelUpTime; - #endif -#elif SINGULARITY_PROCESS - #if THREAD_TIME_ACCOUNTING - TimeSpan ticks = ProcessService.GetThreadTime(); - TimeSpan ticks2 = ProcessService.GetUpTime(); - #else - TimeSpan ticks = ProcessService.GetUpTime(); - #endif -#endif -#endif - if (GC.IsProfiling) { - gcProfiler.NotifyPreGC(MinGeneration); - // non-generational collector, so pretend Gen0 - // Calls like ResurrectCandidates below can cause - // allocations and thus, potentially, profiler - // notifications. However, at that time the heap is - // damaged in the sense that VPtrs have bits OR-ed in - // for object marking. We do not want to accept - // profiling during this window. - // - // There is no synchronization issue with setting this - // flag because it will only be consulted by the - // thread that sets and resets it. - heapDamaged = true; - } - // 1) Mark the live objects - CollectorStatistics.Event(GCEvent.TraceStart); -#if !VC - TryAllManager.PreGCHookTryAll(); -#endif - MultiUseWord.PreGCHook(false /* don't use shadows */); - Finalizer.PrepareCollectFinalizers(); - int countThreads = - CallStack.ScanStacks(threadMarkReferenceVisitor, - threadMarkReferenceVisitor); - Thread.VisitBootstrapData(markReferenceVisitor); -#if SINGULARITY_KERNEL - Kernel.VisitSpecialData(markReferenceVisitor); -#endif - MultiUseWord.VisitStrongRefs(markReferenceVisitor, - false /* Don't use shadows */); -#if !VC - TryAllManager.VisitStrongRefs(markReferenceVisitor); -#endif - StaticData.ScanStaticData(markReferenceVisitor); - CollectorStatistics.Event(GCEvent.TraceSpecial); - WeakReference.Process(updateReferenceVisitor, true, true); - Finalizer.ResurrectCandidates(updateReferenceVisitor, - markReferenceVisitor, true); - markReferenceVisitor.Cleanup(); - UnmanagedPageList.ReleaseStandbyPages(); - // 2) Sweep the garbage objects - CollectorStatistics.Event(GCEvent.SweepStart, TotalMemory); - WeakReference.Process(updateReferenceVisitor, true, false); - MultiUseWord.VisitWeakRefs(updateReferenceVisitor, - false /* Don't use shadows */); -#if !VC - TryAllManager.VisitWeakRefs(updateReferenceVisitor); -#endif - SegregatedFreeList.VisitAllObjects(sweepVisitor); - SegregatedFreeList.RecycleGlobalPages(); - SegregatedFreeList.CommitFreedData(); - CollectorStatistics.Event(GCEvent.SweepSpecial); - MultiUseWord.PostGCHook(); - if (GC.IsProfiling) { - heapDamaged = false; - // Allocations may occur inside the PostGCHook. Hopefully a - // sufficiently limited quantity that we don't recursively - // trigger a GC. - gcProfiler.NotifyPostGC(profileRoots, profileObjects); - } - Finalizer.ReleaseCollectFinalizers(); -#if !VC - TryAllManager.PostGCHookTryAll(); -#endif - CollectorStatistics.Event(GCEvent.CollectionComplete, - TotalMemory); - // 3) Determine a new collection trigger - UIntPtr testTrigger = (UIntPtr) this.TotalMemory >> 2; - UIntPtr minTrigger = (UIntPtr) (1 << 24); - UIntPtr maxTrigger = (UIntPtr) (1 << 26); - collectionTrigger = - (testTrigger > minTrigger) ? - (testTrigger < maxTrigger ? - testTrigger : maxTrigger) : minTrigger; -#if SINGULARITY -#if SINGULARITY_KERNEL - #if THREAD_TIME_ACCOUNTING - int procId = Thread.CurrentProcess.ProcessId; - ticks = Thread.CurrentThread.ExecutionTime - ticks; - ticks2 = SystemClock.KernelUpTime - ticks2; - #else - ticks = SystemClock.KernelUpTime - ticks; - #endif - //Thread.CurrentProcess.SetGcPerformanceCounters(ticks, (long) SegregatedFreeList.TotalBytes); -#elif SINGULARITY_PROCESS - #if THREAD_TIME_ACCOUNTING - ushort procId = ProcessService.GetCurrentProcessId(); - ticks = ProcessService.GetThreadTime() - ticks; - ticks2 = ProcessService.GetUpTime() - ticks2; - #else - ticks = ProcessService.GetUpTime() - ticks; - #endif - //ProcessService.SetGcPerformanceCounters(ticks, (long) SegregatedFreeList.TotalBytes); -#endif -#if DEBUG -#if THREAD_TIME_ACCOUNTING - DebugStub.WriteLine("~~~~~ Finish MarkSweep Cleanup [data={0:x8}, diff={7:x8} pid={1:x3}, ms(Thread)={2:d6}, ms(System)={3:d6}, thds={4}, procId={5}, tid={6}]", - __arglist(SegregatedFreeList.TotalBytes, - PageTable.processTag >> 16, - ticks.Milliseconds, - ticks2.Milliseconds, - countThreads, - procId, - Thread.GetCurrentThreadIndex(), - preGcTotalBytes - SegregatedFreeList.TotalBytes - )); -#else - DebugStub.WriteLine("~~~~~ Finish MarkSweep Cleanup [data={0:x8}, pid={1:x3}, ms={2:d6}, thds={3}]", - __arglist(SegregatedFreeList.TotalBytes, - PageTable.processTag >> 16, - ticks.Milliseconds, - countThreads)); -#endif -#endif -#endif - } - - internal override int CollectionGeneration(int genRequest) { - return MinGeneration; - } - - // A profiler can request a scan of all Roots, passing in a - // visitor for callback. - private void ProfileScanRoots(NonNullReferenceVisitor visitor) - { - CallStack.ScanStacks(visitor, visitor); - Thread.VisitBootstrapData(visitor); -#if SINGULARITY_KERNEL - Kernel.VisitSpecialData(visitor); -#endif - MultiUseWord.VisitStrongRefs(visitor, - false /* Don't use shadows */); - StaticData.ScanStaticData(visitor); - } - - // A profiler can request a scan of all Objects in the heap, - // passing in a visitor for callback. - private - void ProfileScanObjects(SegregatedFreeList.ObjectVisitor visitor) - { - SegregatedFreeList.VisitAllObjects(visitor); - } - - internal override void ProfileAllocation(Object obj) - { - if (GC.IsProfiling && !heapDamaged) { - UIntPtr size = ObjectLayout.Sizeof(obj); - gcProfiler.NotifyAllocation(Magic.addressOf(obj), - obj.GetType(), size); - } - } - - [Inline] - internal override UIntPtr AllocateObjectMemory(UIntPtr numBytes, - uint alignment, - Thread currentThread) - { - UIntPtr resultAddr = - SegregatedFreeList.AllocateFast(currentThread, - numBytes, alignment); - if (resultAddr == UIntPtr.Zero) { - resultAddr = this.AllocateObjectMemorySlow(numBytes, alignment, - currentThread); - } - return resultAddr; - } - - [NoInline] - private UIntPtr AllocateObjectMemorySlow(UIntPtr numBytes, - uint alignment, - Thread currentThread) - { - if (GC.newBytesSinceGC > collectionTrigger && - !GC.allocationInhibitGC) { - //REVIEW: This actually happens after the trigger... - GC.InvokeCollection(currentThread); - } - return SegregatedFreeList.AllocateSlow(currentThread, - numBytes, alignment); - } - - internal override int GetGeneration(Object obj) { - return MinGeneration; - } - - internal override int MaxGeneration { - get { return (int)PageType.Owner0; } - } - - internal override int MinGeneration { - get { return (int)PageType.Owner0; } - } - - internal override long TotalMemory { - get { - return (long)SegregatedFreeList.TotalBytes; -#if false - UIntPtr pageCount = UIntPtr.Zero; - for (UIntPtr i=UIntPtr.Zero; i>= 4; - while (pointerTracking != 0) { - uint index = pointerTracking & 0xf; - pointerTracking >>= 4; - // The cast to int prevents C# from taking the - // index * sizeof(UIntPtr) to long: - UIntPtr *loc = sparseObject + (int) index; - this.Filter(loc, ref objDesc); - } - break; - } - case ObjectLayout.DENSE_TAG: { - // skip vtable - int postHeaderSize = PostHeader.Size; - UIntPtr *denseObject = (UIntPtr *) - (objDesc.objectBase + postHeaderSize); - size = ObjectLayout.ObjectSize(objDesc.vtable); - pointerTracking >>= 4; - while (pointerTracking != 0) { - if ((pointerTracking & ((UIntPtr)0x1)) != 0) { - this.Filter(denseObject, ref objDesc); - } - pointerTracking >>= 1; - denseObject++; - } - break; - } - case ObjectLayout.PTR_VECTOR_TAG: { - int postHeaderSize = PostHeader.Size; - uint length = *(uint*)(objDesc.objectBase + postHeaderSize); - size = ObjectLayout.ArraySize(objDesc.vtable, length); - int preHeaderSize = PreHeader.Size; - UIntPtr *elementAddress = (UIntPtr *) - (objDesc.objectBase + objDesc.vtable.baseLength - - preHeaderSize); - for (uint i = 0; i < length; i++, elementAddress++) { - this.Filter(elementAddress, ref objDesc); - } - break; - } - case ObjectLayout.OTHER_VECTOR_TAG: { - int postHeaderSize = PostHeader.Size; - uint length = *(uint*)(objDesc.objectBase + postHeaderSize); - size = ObjectLayout.ArraySize(objDesc.vtable, length); - if (objDesc.vtable.arrayOf == StructuralType.Struct) { - // pretend the struct is boxed and account for the - // presence of the vtable field - VTable elementVTable = objDesc.vtable.arrayElementClass; - UIntPtr elementMask = elementVTable.pointerTrackingMask; - // A structure with no references will have a SPARSE - // descriptor with no offset values. - if (elementMask != (UIntPtr) ObjectLayout.SPARSE_TAG) { - int preHeaderSize = PreHeader.Size; - UIntPtr elementAddress = (objDesc.objectBase + - objDesc.vtable.baseLength - - preHeaderSize - - postHeaderSize); - int elementSize = objDesc.vtable.arrayElementSize; - objDesc.vtable = elementVTable; - for (uint i = 0; i < length; i++) { - objDesc.objectBase = elementAddress; - this.VisitReferenceFieldsTemplate(ref objDesc); - elementAddress += elementSize; - } - } - } - break; - } - case ObjectLayout.PTR_ARRAY_TAG: { - int postHeaderSize = PostHeader.Size; - uint length = *(uint*)(objDesc.objectBase + postHeaderSize + - sizeof(uint)); - size = ObjectLayout.ArraySize(objDesc.vtable, length); - int preHeaderSize = PreHeader.Size; - UIntPtr *elementAddress = (UIntPtr *) - (objDesc.objectBase + objDesc.vtable.baseLength - - preHeaderSize); - for (uint i = 0; i < length; i++, elementAddress++) { - this.Filter(elementAddress, ref objDesc); - } - break; - } - case ObjectLayout.OTHER_ARRAY_TAG: { - int postHeaderSize = PostHeader.Size; - uint length = *(uint*)(objDesc.objectBase + postHeaderSize + - sizeof(uint)); - size = ObjectLayout.ArraySize(objDesc.vtable, length); - if (objDesc.vtable.arrayOf == StructuralType.Struct) { - // pretend the struct is boxed and account for the - // presence of the PostHeader - VTable elementVTable = objDesc.vtable.arrayElementClass; - UIntPtr elementMask = elementVTable.pointerTrackingMask; - // A structure with no references will have a SPARSE - // descriptor with no offset values. - if (elementMask != (UIntPtr) ObjectLayout.SPARSE_TAG) { - int preHeaderSize = PreHeader.Size; - int elementSize = objDesc.vtable.arrayElementSize; - UIntPtr elementAddress = - objDesc.objectBase + objDesc.vtable.baseLength - - preHeaderSize - postHeaderSize; - objDesc.vtable = elementVTable; - for (uint i = 0; i < length; i++) { - objDesc.objectBase = elementAddress; - this.VisitReferenceFieldsTemplate(ref objDesc); - elementAddress += elementSize; - } - } - } - break; - } - case ObjectLayout.STRING_TAG: { - int postHeaderSize = PostHeader.Size; - uint arrayLength = - *(uint*)(objDesc.objectBase + postHeaderSize); - size = ObjectLayout.StringSize(objDesc.vtable, arrayLength); - break; - } - default: { - // escape case - VTable.Assert((objectTag & 0x1) == 0, - "ReferenceVisitor: (objectTag & 0x1) == 0"); - UIntPtr *largeObject = (UIntPtr *) objDesc.objectBase; - size = ObjectLayout.ObjectSize(objDesc.vtable); - int *pointerDescription = (int *) pointerTracking; - int count = *pointerDescription; - for (int i = 1; i <= count; i++) { - UIntPtr *loc = largeObject + *(pointerDescription+i); - this.Filter(loc, ref objDesc); - } - break; - } - } - return size; - } - - [ManualRefCounts] - protected unsafe - void VisitReferenceFieldsTemplate(ref ObjectDescriptor objDesc, - int count) - { - UIntPtr pointerTracking = objDesc.vtable.pointerTrackingMask; - uint objectTag = (pointerTracking & 0xf); - switch (objectTag) { - case ObjectLayout.PTR_VECTOR_TAG: - case ObjectLayout.PTR_ARRAY_TAG: { - UIntPtr *elementAddress = (UIntPtr *) objDesc.objectBase; - for (int i = 0; i < count; i++, elementAddress++) { - this.Filter(elementAddress, ref objDesc); - } - break; - } - case ObjectLayout.OTHER_VECTOR_TAG: - case ObjectLayout.OTHER_ARRAY_TAG: { - if (objDesc.vtable.arrayOf == StructuralType.Struct) { - // pretend the struct is boxed and account for the - // presence of the vtable field - VTable elementVTable = objDesc.vtable.arrayElementClass; - UIntPtr elementMask = elementVTable.pointerTrackingMask; - // A structure with no references will have a SPARSE - // descriptor with no offset values. - if (elementMask != (UIntPtr) ObjectLayout.SPARSE_TAG) { - int postHeaderSize = PostHeader.Size; - objDesc.objectBase -= postHeaderSize; - objDesc.secondBase -= postHeaderSize; - objDesc.extra += postHeaderSize; - int elementSize = objDesc.vtable.arrayElementSize; - objDesc.vtable = elementVTable; - for (int i = 0; i < count; i++) { - this.VisitReferenceFieldsTemplate(ref objDesc); - objDesc.objectBase += elementSize; - objDesc.secondBase += elementSize; - objDesc.extra -= elementSize; - } - objDesc.objectBase += postHeaderSize; - objDesc.secondBase += postHeaderSize; - objDesc.extra -= postHeaderSize; - } - } - break; - } - default: { - throw new Exception("Indexing non-indexed type"); - } - } - } - - } - - internal abstract class NonNullReferenceVisitor : ReferenceVisitor - { - - internal unsafe abstract void Visit(UIntPtr *location); - -#region HELP_DEVIRT - // This method simply forces the compiler to generate a copy - // of VisitReferenceFieldsTemplate in this class. - [ManualRefCounts] - internal override UIntPtr VisitReferenceFields(Object obj) - { - return this.VisitReferenceFields(Magic.addressOf(obj), - obj.vtable); - } - - // This method simply forces the compiler to generate a copy - // of VisitReferenceFieldsTemplate in this class. - [ManualRefCounts] - internal override - UIntPtr VisitReferenceFields(UIntPtr objectBase, VTable vtable) - { - ObjectDescriptor objDesc = - new ObjectDescriptor(vtable, objectBase); - return VisitReferenceFieldsTemplate(ref objDesc); - } -#endregion - - [Inline] - protected override unsafe - void Filter(UIntPtr *location, ref ObjectDescriptor objDesc) - { - if (*location != UIntPtr.Zero) { - this.Visit(location); - } - } - - } - - internal abstract class OffsetReferenceVisitor : ReferenceVisitor - { - - internal abstract void FieldOffset(UIntPtr offset, - ref ObjectDescriptor objDesc); - -#region HELP_DEVIRT - // This method simply forces the compiler to generate a copy - // of VisitReferenceFieldsTemplate in this class. - [ManualRefCounts] - internal override sealed - UIntPtr VisitReferenceFields(UIntPtr objectBase, VTable vtable) - { - ObjectDescriptor objDesc = - new ObjectDescriptor(vtable, objectBase); - return VisitReferenceFieldsTemplate(ref objDesc); - } -#endregion - - [Inline] - protected override unsafe sealed - void Filter(UIntPtr *location, ref ObjectDescriptor objDesc) - { - UIntPtr offset = ((UIntPtr) location) - objDesc.objectBase; - this.FieldOffset(offset, ref objDesc); - } - - } - -} diff --git a/base/Kernel/Bartok/GCs/SegregatedFreeList.cs b/base/Kernel/Bartok/GCs/SegregatedFreeList.cs deleted file mode 100644 index 3a6887d..0000000 --- a/base/Kernel/Bartok/GCs/SegregatedFreeList.cs +++ /dev/null @@ -1,1266 +0,0 @@ -/*******************************************************************/ -/* WARNING */ -/* This file should be identical in the Bartok and Singularity */ -/* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ -/*******************************************************************/ - -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -namespace System.GCs { - - using Microsoft.Bartok.Options; - using Microsoft.Bartok.Runtime; - - using System.Runtime.CompilerServices; - using System.Threading; - -#if SINGULARITY - using Microsoft.Singularity; -#endif - - /// - /// This class supports Allocate/Free operations using memory obtained - /// from the PageManager. - /// - /// Objects that are small enough that multiple instances of them can fit - /// on a page are allocated from pages of identically sized memory cells. - /// - /// Large objects are allocated on separate pages. - /// - /// This class is designed to be thread safe on both allocation and free - /// operations. At this stage it is required that RecycleGlobalPages is - /// called periodically to reconsider previously full pages for inclusion - /// in allocation lists. - /// - /// The free operation is currently more expensive than required due to - /// space accounting. - /// - /// This class also keeps up to date memory accounting information. Note - /// that modifications to this data is not synchronized so it is possible - /// that the counts drift from actual figures over time. - /// - internal unsafe struct SegregatedFreeList /* : Allocator */ - { - - #region Mixins - - [MixinConditional("SegregatedFreeList")] - [MixinConditional("AllThreadMixins")] - [Mixin(typeof(Thread))] - private class SegregatedFreeListThread : Object { - // Thread-specific segregated free-list allocator - [RequiredByBartok] - internal SegregatedFreeList segregatedFreeList; - } - - [Inline] - private static SegregatedFreeListThread MixinThread(Thread t) - { - return (SegregatedFreeListThread) (Object) t; - } - - #endregion - - #region Global (Safe from the context of any thread) - - #region Constants - /// - /// Small objects. - /// - internal const PageType SMALL_OBJ_PAGE = PageType.Owner2; - - /// - /// This is a page that is being set up. - /// - internal const PageType INIT_PAGE = PageType.Owner4; - - /// - /// Large objects. - /// - internal const PageType LARGE_OBJ_PAGE = PageType.Owner3; - - /// - /// BUGBUG: Duplicated constant. - /// - internal const int LOG_PAGE_SIZE = 12; - - /// - /// The size of each block (which contains a single object size) - /// - internal const int BLOCK_SIZE = (1 << LOG_PAGE_SIZE) - PageHeader.SIZEOF; - - /// - /// The threshold where objects become large objects. - /// - internal const int LARGE_OBJECT_THRESHOLD = (BLOCK_SIZE >> 1) + 1; - - /// - /// The largest possible size class. - /// - internal const int SIZE_CLASSES = 33 + ((LARGE_OBJECT_THRESHOLD-1) >> 7); - #endregion - - #region Size Class Mappings - /// - /// Returns the size class index for an object of the specified size. - /// - /// BUGBUG: We hope that from an inlined allocation site this is - /// resolved as a constant. That is why this was changed to remove - /// indirection through an index lookup table. - /// - [Inline] - private static int GetSizeClass(UIntPtr bytes) { - // Exact request rounds down to lower size class. - bytes = bytes - new UIntPtr(1); - // Sizes 0 -> 64 are in classes 0 -> 15 - // Sizes 72 -> 128 are in classes 16 -> 23 - // Sizes 140 -> 256 are in classes 24 -> 31 - // Sizes 256 -> 512 are in classes 32 -> 35 - // We skip a power of 2 because large objects are rare. - //Sizes 512 and up are in classes 36 -> 41 - if (bytes < 64) return 0 + ((int)bytes >> 2); - if (bytes < 128) return 8 + ((int)bytes >> 3); - if (bytes < 256) return 16 + ((int)bytes >> 4); - if (bytes < 512) return 28 + ((int)bytes >> 6); - if (bytes < LARGE_OBJECT_THRESHOLD) return 32 + ((int)bytes >> 7); - throw new Exception("GetSizeClass called on large object size"); - } - - /// - /// Returns the cell size for a given size class. - /// - private static UIntPtr GetCellSize(int sizeClass) { - VTable.Assert(sizeClass < SIZE_CLASSES, - "Attempt cellSize for invalid sizeClass"); - - uint sc = (uint)sizeClass + 1; - - uint bytes; - if (sc <= 16) bytes = (sc - 0) << 2; - else if (sc <= 24) bytes = (sc - 8) << 3; - else if (sc <= 32) bytes = (sc - 16) << 4; - else if (sc <= 36) bytes = (sc - 28) << 6; - else bytes = (sc - 32) << 7; - - return new UIntPtr(bytes); - } - #endregion - - #region Memory Accounting - /// - /// A count of the bytes allocated for small objects. - /// - internal static UIntPtr SmallBytes; - - /// - /// A count of the bytes for objects the process of being freed. - /// - internal static UIntPtr SmallFreedBytes; - - /// - /// A count of the small pages in the process of being freed. - /// - internal static UIntPtr SmallFreedPages; - - /// - /// A count of the large pages in the process of being freed. - /// - internal static UIntPtr LargeFreedPages; - - /// - /// A count of the bytes allocated for large objects. - /// - internal static UIntPtr LargeBytes { - get { - return PageTable.RegionSize(LargePages); - } - } - - /// - /// The number of pages reserved for large objects. - /// - internal static UIntPtr LargePages; - - /// - /// The number of pages reserved for small objects. - /// - internal static UIntPtr SmallPages; - - /// - /// This is the total size of data (including object headers) - /// - internal static UIntPtr TotalBytes { - get { - return SmallBytes + LargeBytes; - } - } - - /// - /// The number of pages managed by the alloc heap, including space - /// occupied by empty cells. - /// - internal static UIntPtr ReservedBytes { - get { - return PageTable.RegionSize(SmallPages) + LargeBytes; - } - } - - /// - /// Increment the counter of the number of small bytes allocated. - /// - [Inline] - private static void AddSmallBytes(UIntPtr newBytes) { - SmallBytes += newBytes; - GC.newBytesSinceGC += newBytes; - } - - /// - /// Increment the counter of the number of large bytes allocated. - /// - [Inline] - private static void AddLargePages(UIntPtr pageCount) { - LargePages += pageCount; - GC.newBytesSinceGC += PageTable.RegionSize(pageCount); - } - - /// - /// Decrement the counter of the number of small bytes allocated. - /// - [Inline] - private static void SubSmallBytes(UIntPtr newBytes) { - SmallFreedBytes += newBytes; - } - - /// - /// Decrement the counter of the number of large pages allocated. - /// - [Inline] - private static void SubLargePages(UIntPtr pageCount) { - LargeFreedPages += pageCount; - } - - /// - /// Increment the counter of the number of small pages allocated. - /// - [Inline] - private static void AddSmallPage() { - SmallPages += new UIntPtr(1); - AddSmallBytes(PageTable.PageSize); - } - - /// - /// Decrement the counter of the number of small pages allocated. - /// - [Inline] - private static void SubSmallPage(PageHeader *page) { - SmallFreedPages += new UIntPtr(1); - SubSmallBytes(PageTable.PageSize - - (page->cellSize * (UIntPtr) page->freeCount)); - } - - /// - /// Subtract and zero the freed data counts. - /// - internal static void CommitFreedData() { - SmallPages -= SmallFreedPages; - SmallFreedPages = new UIntPtr(0); - LargePages -= LargeFreedPages; - LargeFreedPages = new UIntPtr(0); - SmallBytes -= SmallFreedBytes; - SmallFreedBytes = new UIntPtr(0); - } - #endregion - - /// - /// When notified of the creation of a new thread we initialize the - /// alloc heap in that thread. - /// - internal unsafe static void NewThreadNotification(Thread newThread, - bool initial) - { - SegregatedFreeListThread mixinThread = MixinThread(newThread); - if (initial) { - // Initialise the initial thread. - mixinThread.segregatedFreeList.localPages = (PageHeader*[]) - BootstrapMemory.Allocate(typeof(PageHeader*[]), - SIZE_CLASSES); - mixinThread.segregatedFreeList.freeList = (UIntPtr[]) - BootstrapMemory.Allocate(typeof(UIntPtr[]), SIZE_CLASSES); - } else { - // We have to create the thread-specific array of pages. - mixinThread.segregatedFreeList.freeList = - new UIntPtr[SIZE_CLASSES]; - mixinThread.segregatedFreeList.localPages = - new PageHeader*[SIZE_CLASSES]; - } - } - - /// - /// A thread has finished, so we release any local pages. - /// - internal static void DeadThreadNotification(Thread deadThread) - { - SegregatedFreeListThread mixinThread = MixinThread(deadThread); - for(int i=0; i < SIZE_CLASSES; i++) { - if (mixinThread.segregatedFreeList.localPages[i] != null) { - mixinThread.segregatedFreeList.ReleaseLocalPage(i); - } - } - } - - /// - /// Allocate a large object. Large objects don't share pages with - /// any other objects. Get memory for the object directly from the - /// PageManager. - /// - [ManualRefCounts] - private static unsafe UIntPtr AllocateLarge(uint alignment, - UIntPtr bytes, - Thread currentThread) - { - UIntPtr pageCount = PageTable.PageCount(PageTable.PagePad(bytes)); - bool fCleanPages = true; - UIntPtr page = PageManager.EnsurePages(currentThread, - pageCount, INIT_PAGE, - ref fCleanPages); - - UIntPtr regionSize = PageTable.RegionSize(pageCount); - AddLargePages(pageCount); - - int unusedBytes = (int) (regionSize - bytes); - int unusedCacheLines = unusedBytes >> 5; - int offset; - if (unusedCacheLines != 0) { - offset = (largeOffset % unusedCacheLines) << 5; - largeOffset = - (largeOffset + 1) & ((int)PageTable.PageSize - 1); - } else { - offset = 0; - } - UIntPtr pageAddr = PageTable.PageAddr(page); - UIntPtr startAddr = pageAddr + offset + PreHeader.Size; - UIntPtr resultAddr = - Allocator.AlignedObjectPtr(startAddr, alignment); - short shortOff = (short) (resultAddr - pageAddr); - VTable.Assert(shortOff > 0, "offset not positive"); - PageTable.SetExtra(page, shortOff); - // Ready to be visited - PageTable.SetType(page, pageCount, LARGE_OBJ_PAGE); - return resultAddr; - } - - /// - /// Free the specified object. For large objects the page becomes - /// immediately available. For small objects it may require a call - /// to RecycleGlobalPages. - /// - [ManualRefCounts] - internal static void Free(Object obj) { - UIntPtr objectStart = Magic.addressOf(obj) - PreHeader.Size; - UIntPtr page = PageTable.Page(objectStart); - PageType pageType = PageTable.Type(page); - // Free the object - if (pageType == SMALL_OBJ_PAGE) { - FreeSmall(obj); - } else { - VTable.Assert(pageType == LARGE_OBJ_PAGE, - "Found GC Page not small or large"); - FreeLarge(obj); - } - } - - /// - /// Free a small object. - /// - [ManualRefCounts] - internal static void FreeSmall(Object obj) { - UIntPtr objectAddr = Magic.addressOf(obj); - // Put the object memory cell on the freelist for the page - UIntPtr pageAddr = PageTable.PageAlign(objectAddr); - PageHeader *pageHeader = (PageHeader*) pageAddr; - UIntPtr cellStart; - if (obj.vtable.baseAlignment > UIntPtr.Size) { - cellStart = FindSmallCell(objectAddr); - VTable.Assert(cellStart > (UIntPtr) pageHeader && - cellStart <= objectAddr && - cellStart+pageHeader->cellSize >= objectAddr, - "find small cell found invalid start"); - } else { - cellStart = objectAddr - PreHeader.Size; - } - Util.MemClear(cellStart, pageHeader->cellSize); - SubSmallBytes(pageHeader->cellSize); - UIntPtr oldFreeList; - cellStart = (cellStart + PreHeader.Size); - do { - oldFreeList = pageHeader->freeList; - *(UIntPtr *)cellStart = oldFreeList; - } while (Interlocked.CompareExchange(ref pageHeader->freeList, - cellStart, oldFreeList) != oldFreeList); - Interlocked.Increment(ref pageHeader->freeCount); - } - - internal unsafe struct TempList { - - private UIntPtr next; - - internal void Add(UIntPtr memAddr) { - *(UIntPtr *) memAddr = this.next; - this.next = memAddr; - } - - internal UIntPtr GetList() { - UIntPtr result = this.next; - this.next = UIntPtr.Zero; - return result; - } - - } - - [ManualRefCounts] - internal static void FreeSmallList(ref TempList tempList) - { - UIntPtr cell = tempList.GetList(); - if (cell != UIntPtr.Zero) { - PageHeader *pageHeader = (PageHeader *) - PageTable.PageAlign(cell); - UIntPtr newChain = UIntPtr.Zero; - UIntPtr newChainTail = cell + PreHeader.Size; - UIntPtr cellSize = pageHeader->cellSize; - int count = 0; - do { - count++; - UIntPtr next = *(UIntPtr *) cell; - Util.MemClear(cell, cellSize); - UIntPtr cellPtrAddr = cell + PreHeader.Size; - *(UIntPtr *) cellPtrAddr = newChain; - newChain = cellPtrAddr; - cell = next; - } while (cell != UIntPtr.Zero); - SubSmallBytes((UIntPtr) count * cellSize); - UIntPtr oldFreeList; - do { - oldFreeList = pageHeader->freeList; - *(UIntPtr*)newChainTail = oldFreeList; - } while (Interlocked.CompareExchange(ref pageHeader->freeList, - newChain, oldFreeList) != - oldFreeList); - int oldFreeCount, newFreeCount; - do { - oldFreeCount = pageHeader->freeCount; - newFreeCount = oldFreeCount + count; - } while (Interlocked.CompareExchange(ref pageHeader->freeCount, - newFreeCount, - oldFreeCount) != - oldFreeCount); - } - } - - /// - /// Free a large object. - /// - [ManualRefCounts] - internal static void FreeLarge(Object obj) { - UIntPtr objectStart = Magic.addressOf(obj) - PreHeader.Size; - UIntPtr firstPage = PageTable.Page(objectStart); - // Release the page(s) that the object resides on - UIntPtr pageAddr = PageTable.PageAlign(objectStart); - UIntPtr objectSize = ObjectLayout.Sizeof(obj); - UIntPtr limitPage = - PageTable.Page(PageTable.PagePad(objectStart + objectSize)); - UIntPtr pageCount = limitPage - firstPage; - PageTable.SetType(firstPage, pageCount, INIT_PAGE); - PageTable.SetExtra(PageTable.Page(pageAddr), 0); - PageManager.ReleaseUnusedPages(firstPage, pageCount, false); - SubLargePages(pageCount); - } - - /// - /// Given a possibly interior pointer to an object, return the - /// real address of the object. - /// - internal static unsafe UIntPtr Find(UIntPtr addr) { - UIntPtr page = PageTable.Page(addr); - PageType pageType = PageTable.Type(page); - VTable.Assert(PageTable.IsGcPage(pageType), - "Attempt find on non-GC page"); - if (pageType == SMALL_OBJ_PAGE) { - return FindSmall(addr); - } else { - return FindLarge(addr); - } - } - - /// - /// Find a small object (after determining it is on a small page) - /// - private static UIntPtr FindSmall(UIntPtr cellAddr) { - UIntPtr objectAddr = FindSmallCell(cellAddr) + PreHeader.Size; - objectAddr = Allocator.SkipAlignment(objectAddr); - return objectAddr; - } - - /// - /// Find the cell for a given object address - /// - private static unsafe UIntPtr FindSmallCell(UIntPtr addr) { - UIntPtr pageAddr = PageTable.PageAlign(addr - PreHeader.Size); - PageHeader *pageHeader = (PageHeader *) pageAddr; - UIntPtr firstAddr = pageAddr + PageHeader.SIZEOF; - return - addr - ((int)(addr - firstAddr) % (int)pageHeader->cellSize); - } - - /// - /// Find a large object (after determining it is on a large page) - /// - private static UIntPtr FindLarge(UIntPtr addr) { - UIntPtr page = PageTable.Page(addr - PreHeader.Size); - short brickData = PageTable.Extra(page); - while (brickData == 0) { - page--; - brickData = PageTable.Extra(page); - VTable.Assert(PageTable.Type(page) == LARGE_OBJ_PAGE, - "page type invalid"); - } - return (PageTable.PageAddr(page) + brickData); - } - - internal abstract class ObjectVisitor : ObjectLayout.ObjectVisitor { - - internal virtual void VisitSmall(Object obj, UIntPtr memAddr) { - this.Visit(obj); - } - - internal virtual void VisitSmallPageEnd() { } - - internal virtual UIntPtr VisitLarge(Object obj) { - return this.Visit(obj); - } - - internal override UIntPtr Visit(Object obj) { - VTable.NotReached("Someone forgot an override method in a "+ - "subclass of SegregatedFreeList.ObjectVisitor"); - return UIntPtr.Zero; - } - - } - - // Wraps an SegregatedFreeList.ObjectVisitor around a - // ObjectLayout.ObjectVisitor. - // Both large and small objects are visited by the same - // ObjectLayout.ObjectVisitor. - internal class ObjectVisitorWrapper : ObjectVisitor { - - private ObjectLayout.ObjectVisitor visitor; - - internal ObjectVisitorWrapper(ObjectLayout.ObjectVisitor visitor) { - this.visitor = visitor; - } - - internal override void VisitSmall(Object obj, UIntPtr memAddr) { - this.visitor.Visit(obj); - } - - internal override UIntPtr VisitLarge(Object obj) { - return this.visitor.Visit(obj); - } - - } - - /// - /// Visit each object in the heap across all pages. - /// - [ManualRefCounts] - internal static void VisitAllObjects(ObjectVisitor visitor) - { - VisitObjects(UIntPtr.Zero, PageTable.pageTableCount, visitor); - } - - /// - /// Visit each object in the heap across a range of pages. - /// - /// This can be run concurrent to allocations, but not frees. - /// - [ManualRefCounts] - internal static void VisitObjects(UIntPtr lowPage, - UIntPtr highPage, - ObjectVisitor visitor) - { - for (UIntPtr i = lowPage; i < highPage; i++) { - PageType pageType = PageTable.MyType(i); - if (pageType == INIT_PAGE) { - // Not yet ready for allocation so we can't visit it... - } else if (pageType == SMALL_OBJ_PAGE) { - VisitSmallObjects(i, visitor); - } else if (pageType == LARGE_OBJ_PAGE) { - UIntPtr j = i + 1; - while (j < highPage && - PageTable.MyType(j) == LARGE_OBJ_PAGE) { - j++; - } - UIntPtr largeObjectPageCount = - VisitLargeObject(i, visitor); - i += largeObjectPageCount - 1; - VTable.Assert(i <= j); - } - } - } - - internal static void VisitObjects(UIntPtr lowPage, - UIntPtr highPage, - ObjectLayout.ObjectVisitor visitor) - { - ObjectVisitor myObjectVisitor = visitor as ObjectVisitor; - VTable.Assert(myObjectVisitor != null, - "SegregatedFreeList requires specialized ObjectVisitor"); - VisitObjects(lowPage, highPage, myObjectVisitor); - } - - /// - /// Visit small objects in a single page. - /// - [ManualRefCounts] - private static unsafe void VisitSmallObjects(UIntPtr page, - ObjectVisitor visitor) - { - VTable.Assert(PageTable.Type(page) == SMALL_OBJ_PAGE, - "Visiting small objects on invalid page"); - UIntPtr pageAddr = PageTable.PageAddr(page); - PageHeader *pageHeader = (PageHeader *) pageAddr; - UIntPtr cellSize = pageHeader->cellSize; - VTable.Assert(cellSize != UIntPtr.Zero, - "zero cellSize visiting small"); - UIntPtr lowAddr = pageAddr + PageHeader.SIZEOF; - UIntPtr highAddr = PageTable.PagePad(lowAddr); - while (lowAddr <= highAddr - cellSize) { - UIntPtr objectAddr = lowAddr + PreHeader.Size; - objectAddr = Allocator.SkipAlignment(objectAddr); - Object maybeObject = Magic.fromAddress(objectAddr); - UIntPtr vtablePtr = *maybeObject.VTableFieldAddr; - if (vtablePtr != 0) { - // In Singularity, it is not always allowed - // to compute PageTable.Page(UIntPtr.Zero); - UIntPtr vtablePage = PageTable.Page(vtablePtr); - if (vtablePage != page) { - // We have found a slot containing an object - visitor.VisitSmall(maybeObject, lowAddr); - } - } - lowAddr += cellSize; - } - visitor.VisitSmallPageEnd(); - } - - /// - /// Visit a large object on the specified page. - /// - [ManualRefCounts] - private static UIntPtr VisitLargeObject(UIntPtr page, - ObjectVisitor visitor) - { - VTable.Assert(PageTable.Type(page) == LARGE_OBJ_PAGE, - "Visiting large object on invalid page"); - // Find the object - UIntPtr pageAddr = PageTable.PageAddr(page); - short brickData = PageTable.Extra(page); - if (brickData == 0) { - // Possibly in the process of being allocated. - return new UIntPtr(1); - } - UIntPtr objectAddr = pageAddr + brickData; - Object obj = Magic.fromAddress(objectAddr); - if (obj.vtable == null) { - // Memory has been allocated, but object is not initialized - return new UIntPtr(1); - } - // Visit the object - UIntPtr objectSize = visitor.VisitLarge(obj); - // Return the page count - UIntPtr objectEnd = objectAddr + objectSize - PreHeader.Size; - UIntPtr limitPage = PageTable.Page(PageTable.PagePad(objectEnd)); - return limitPage - page; - } - - /// - /// This is the the free list of pages to allocate from. - /// - private static UIntPtr[] globalFreePages; - - /// - /// This is the list of pages released by threads. These pages must - /// be periodically processes to release them back for allocation if - /// necessary. - /// - private static UIntPtr[] globalPages; - - // Used by RecycleGlobalPages to avoid the ABA problem of - // lock-free data structures. - private static UIntPtr[] stolenGlobalFreePages; - private static UIntPtr[] stolenGlobalFullPages; - - /// - /// Initialize the alloc heap by setting up the heads for all the - /// linked lists. - /// - [PreInitRefCounts] - internal static unsafe void Initialize() { - // Global array of allocated pages - globalPages = (UIntPtr[]) - BootstrapMemory.Allocate(typeof(UIntPtr[]), SIZE_CLASSES); - // Global array of pages with free elements - globalFreePages = (UIntPtr[]) - BootstrapMemory.Allocate(typeof(UIntPtr[]), SIZE_CLASSES); - // Temporary list holders used by RecycleGlobalPages - stolenGlobalFullPages = (UIntPtr[]) - BootstrapMemory.Allocate(typeof(UIntPtr[]), SIZE_CLASSES); - stolenGlobalFreePages = (UIntPtr[]) - BootstrapMemory.Allocate(typeof(UIntPtr[]), SIZE_CLASSES); - } - - /// - /// Take all global pages that have had elements freed and put them in - /// the allocation queues. - /// - internal static void RecycleGlobalPages() { - RecycleGlobalPagesPhase1(); - RecycleGlobalPagesPhase2(); - } - - internal static void RecycleGlobalPagesPhase1() { - for (int i=0; i < SIZE_CLASSES; i++) { - // Steal chains - VTable.Assert(stolenGlobalFullPages[i] == UIntPtr.Zero); - VTable.Assert(stolenGlobalFreePages[i] == UIntPtr.Zero); - stolenGlobalFullPages[i] = - AtomicPopChain(ref globalPages[i]); - stolenGlobalFreePages[i] = - AtomicPopChain(ref globalFreePages[i]); - } - } - - internal static void RecycleGlobalPagesPhase2() { - for (int i=0; i < SIZE_CLASSES; i++) { - UIntPtr globalFull = stolenGlobalFullPages[i]; - stolenGlobalFullPages[i] = UIntPtr.Zero; - UIntPtr globalFree = stolenGlobalFreePages[i]; - stolenGlobalFreePages[i] = UIntPtr.Zero; - - // Start with free pages (they can not become full) - UIntPtr current = globalFree; - - // New free and full chains - PageHeader *freeHead = null, freeTail = null; - PageHeader *fullHead = null, fullTail = null; - - // Determine starting point - if (current == UIntPtr.Zero) { - current = globalFull; - globalFull = UIntPtr.Zero; - - if (current == UIntPtr.Zero) { - continue; - } - } - - // Number of cells of this size class in a block - int cells = BLOCK_SIZE / (int) GetCellSize(i); - VTable.Assert(cells > 0 && cells < (BLOCK_SIZE >> 1), - "invalid cell count"); - - // Iterate through list - while (current != UIntPtr.Zero) { - PageHeader *page = (PageHeader*) current; - current = page->nextPage; - - if (page->freeCount == cells) { - // Completely Free Page - SubSmallPage(page); - UIntPtr pageNum = PageTable.Page(new UIntPtr(page)); - PageTable.SetType(pageNum, INIT_PAGE); - PageTable.SetExtra(pageNum, 0); - PageManager.ReleaseUnusedPages(pageNum, new UIntPtr(1), - false); - } else if (page->freeCount > 0) { - // Partially Free Page - AddPageToList(page, ref freeHead, ref freeTail); - } else { - // Completely Full Page - AddPageToList(page, ref fullHead, ref fullTail); - } - - if (current == UIntPtr.Zero) { - // Finished the free list, onto the full list. - current = globalFull; - globalFull = UIntPtr.Zero; - } - } - - // Reinsert values onto the free and full chains - if (fullHead != null) { - AtomicPushChain(ref globalPages[i], - fullHead, fullTail); - } - - if (freeHead != null) { - AtomicPushChain(ref globalFreePages[i], - freeHead, freeTail); - } - } - } - - /// - /// Add a page onto a local linked list (possibly the first page) - /// - private static void AddPageToList(PageHeader *page, - ref PageHeader *head, - ref PageHeader *tail) { - if (head == null) { - page->nextPage = UIntPtr.Zero; - head = page; - tail = page; - return; - } - tail->nextPage = new UIntPtr(page); - tail = page; - } - - /// - /// Atomically push a value onto the linked list. - /// - private static void AtomicPush(ref UIntPtr head, PageHeader *page) { - AtomicPushChain(ref head, page, page); - } - - /// - /// Atomically remove a value from the linked list. Returns null if the - /// list is empty. - /// - private static UIntPtr AtomicPop(ref UIntPtr head) { - UIntPtr oldHead; - PageHeader *oldPage; - UIntPtr newHead; - do { - oldHead = head; - if (oldHead == UIntPtr.Zero) { - // Empty list. - return UIntPtr.Zero; - } - oldPage = (PageHeader*) oldHead; - newHead = oldPage->nextPage; - } while(oldHead != Interlocked.CompareExchange - (ref head, newHead, oldHead)); - - oldPage->nextPage = UIntPtr.Zero; - return oldHead; - } - - /// - /// Steal an entire list. - /// - private static UIntPtr AtomicPopChain(ref UIntPtr head) { - return Interlocked.Exchange(ref head, UIntPtr.Zero); - } - - /// - /// Push a whole chain onto a list. - /// - private static void AtomicPushChain(ref UIntPtr head, - PageHeader *chainHead, - PageHeader *chainTail) { - UIntPtr oldHead; - UIntPtr newHead = new UIntPtr(chainHead); - do { - oldHead = head; - chainTail->nextPage = oldHead; - } while (oldHead != Interlocked.CompareExchange - (ref head, newHead, oldHead)); - } - - /// - /// This struct represents the header data stored in each - /// small object page. - /// - /// BUGBUG: Not space efficient. - /// - private struct PageHeader { - internal const int SIZEOF = 16; - - /// - /// The next page in the linked list. - /// - internal UIntPtr nextPage; - - /// - /// The head of the free list for this page. This is not - /// used when a page is assigned to a thread. - /// - internal UIntPtr freeList; - - /// - /// The cell size for objects in this page. - /// - internal UIntPtr cellSize; - - /// - /// The number of cells that have been freed. This is used - /// for accounting purposes. - /// - internal int freeCount; - } - #endregion - - #region Local (Safe from the context of owner thread) - /// - /// This is a thread's local free list for each size class. - /// - [RequiredByBartok] - private UIntPtr[] freeList; - - /// - /// This is a thread's local set of pages for each size class. - /// - private PageHeader*[] localPages; - - [ManualRefCounts] - internal static UIntPtr Allocate(Thread thread, - UIntPtr bytes, uint alignment) - { - SegregatedFreeListThread mixinThread = MixinThread(thread); - return mixinThread.segregatedFreeList.Allocate(bytes, alignment, - thread); - } - - [ManualRefCounts] - internal UIntPtr Allocate(UIntPtr bytes, uint alignment, Thread thread) - { - UIntPtr resultAddr = this.AllocateFast(bytes, alignment); - if (resultAddr == UIntPtr.Zero) { - resultAddr = this.AllocateSlow(bytes, alignment, thread); - } - return resultAddr; - } - - [Inline] - [ManualRefCounts] - internal static UIntPtr AllocateFast(Thread thread, - UIntPtr bytes, - uint alignment) - { - SegregatedFreeListThread mixinThread = MixinThread(thread); - return mixinThread.segregatedFreeList.AllocateFast(bytes, - alignment); - } - - [RequiredByBartok] - [Inline] - [DisableBoundsChecks] - public static unsafe Object CompilerAllocateMarkSweep - (VTable vtable, Thread currentThread, UIntPtr bytes, uint alignment) - { - VTable.Assert((alignment == 4) - || ((alignment == 8) && (UIntPtr.Size == 8)) - || ((alignment == 8) && (PreHeader.Size == 4)), - "Unsupported object layout"); - VTable.Assert(UIntPtr.Size == PreHeader.Size, - "Unsupported preheader size"); - VTable.Assert - (Util.IsAligned((uint) PreHeader.Size + (uint)PostHeader.Size, - alignment), - "Unsupported header sizes"); - VTable.Assert(bytes < LARGE_OBJECT_THRESHOLD, - "CompilerAllocate called for large object"); - - // Room to ensure alignment - bool alignRequired = (alignment > UIntPtr.Size); - if (alignRequired) { - bytes = bytes + alignment - UIntPtr.Size; - } - int sizeClass = GetSizeClass(bytes); - SegregatedFreeListThread mixinThread = MixinThread(currentThread); - UIntPtr region = mixinThread.segregatedFreeList.freeList[sizeClass]; - if (region != UIntPtr.Zero) { - mixinThread.segregatedFreeList.freeList[sizeClass] = - *(UIntPtr *)region; - - // Zero out the free list data structure. However, if the - // vtable is at offset zero, then we're going to overwrite it - // anyway. (and the optimizer does not see this through the - // unmanaged/managed conversion) - if (Magic.OffsetOfVTable != UIntPtr.Zero) { - *(UIntPtr *)region = UIntPtr.Zero; - } - - UIntPtr objAddr = region; - - if((alignment == 8) && (UIntPtr.Size == 4)) { - // Since 'objAddr' will be the actual object reference, we - // want to misalign it here so that the object payload will - // be aligned. (We know that PostHeader.Size & 8 == 4 - // because the PreHeader is 4 and the sum of the PreHeader - // and PostHeader sizes is a multiple of alignment (8)) - - // Store alignment token at objAddr. This will be where an - // alignment token should go if it is required... - Allocator.WriteAlignment(objAddr); - // ... (align if necessary) ... - objAddr = Util.Align(objAddr, (UIntPtr) alignment); - // ... or where the object header will be if alignment was - // not necessary. This code zeroes the object header - // regardless and avoids a branch in this fast path. - *(UIntPtr *)objAddr = UIntPtr.Zero; - // Finally misalign 'objAddr' - objAddr += PreHeader.Size; - } - - Object obj = Magic.fromAddress(objAddr); - obj.vtable = vtable; - return obj; - } - - return GC.AllocateObjectNoInline(vtable, currentThread); - } - - [Inline] - [ManualRefCounts] - private UIntPtr AllocateFast(UIntPtr bytes, uint alignment) - { - // Room to ensure alignment - bool alignRequired = (alignment > UIntPtr.Size); - if (alignRequired) { - bytes = bytes + alignment - UIntPtr.Size; - } - // Is this a large object? - if (!(bytes < LARGE_OBJECT_THRESHOLD)) { - return UIntPtr.Zero; - } - int sizeClass = GetSizeClass(bytes); - UIntPtr region = AllocateSmallFast(sizeClass); - if (region == UIntPtr.Zero) { - return UIntPtr.Zero; - } else { - UIntPtr resultAddr = - Allocator.AlignedObjectPtr(region, alignment); - return resultAddr; - } - } - - [Inline] - [ManualRefCounts] - internal static UIntPtr AllocateSlow(Thread thread, - UIntPtr bytes, uint alignment) - { - SegregatedFreeListThread mixinThread = MixinThread(thread); - return mixinThread.segregatedFreeList.AllocateSlow(bytes, - alignment, - thread); - } - - [NoInline] - [ManualRefCounts] - private UIntPtr AllocateSlow(UIntPtr bytes, uint alignment, - Thread currentThread) - { - // Room to ensure alignment - bool alignRequired = (alignment > UIntPtr.Size); - - if (alignRequired) { - bytes = bytes + alignment - UIntPtr.Size; - } - - // Is this a large object? - if (!(bytes < LARGE_OBJECT_THRESHOLD)) { - return AllocateLarge(alignment, bytes, currentThread); - } - - int sizeClass = GetSizeClass(bytes); - UIntPtr region = AllocateSmall(sizeClass, currentThread); - UIntPtr resultAddr = - Allocator.AlignedObjectPtr(region, alignment); - return resultAddr; - } - - /// - /// Used to attempt to spread large objects across pages to avoid - /// higher cache conflicts on low page addresses. - /// - private static int largeOffset; - - /// - /// Allocate an object of a specified size class from the - /// thread's local block. - /// - [Inline] - [ManualRefCounts] - private UIntPtr AllocateSmall(int sizeClass, Thread currentThread) { - UIntPtr region = freeList[sizeClass]; - if (region != UIntPtr.Zero) { - freeList[sizeClass] = *(UIntPtr*)region; - *(UIntPtr*)region = UIntPtr.Zero; - return region; - } else { - return AllocateSmallSlow(sizeClass, currentThread); - } - } - - [Inline] - private UIntPtr AllocateSmallFast(int sizeClass) { - UIntPtr region = freeList[sizeClass]; - if (region != UIntPtr.Zero) { - freeList[sizeClass] = *(UIntPtr*)region; - *(UIntPtr*)region = UIntPtr.Zero; - return region; - } else { - return UIntPtr.Zero; - } - } - - /// - /// Get a new page and allocate into it. - /// - /// - /// - [ManualRefCounts] - private UIntPtr AllocateSmallSlow(int sizeClass, Thread currentThread) - { - // Return our old page - if (localPages[sizeClass] != null) { - ReleaseLocalPage(sizeClass); - } - - // Get a new one. - localPages[sizeClass] = GetLocalPage(sizeClass, currentThread); - VTable.Assert(localPages[sizeClass] != null, "no local page"); - - // Read (and then zero) the free list. - freeList[sizeClass] = Interlocked.Exchange - (ref localPages[sizeClass]->freeList, UIntPtr.Zero); - - VTable.Assert(freeList[sizeClass] != UIntPtr.Zero, - "GetLocalPage returned empty page"); - - // Allocate off the free list - return AllocateSmallFast(sizeClass); - } - - /// - /// Release a local allocation page into the pool of consumed pages. - /// - [ManualRefCounts] - private void ReleaseLocalPage(int sizeClass) { - PageHeader *page = localPages[sizeClass]; - - // Prepare the page to be released - if (freeList[sizeClass] != UIntPtr.Zero) { - // We are releasing the page 'early'. - UIntPtr pageFreeList = freeList[sizeClass]; - freeList[sizeClass] = UIntPtr.Zero; - - // Save our local free list as the page's free list - UIntPtr oldFreeList = Interlocked.CompareExchange - (ref page->freeList, pageFreeList, UIntPtr.Zero); - - // Count the reclaimed cells. - int freeCount = 0; - UIntPtr next = pageFreeList; - while (next != UIntPtr.Zero) { - next = *(UIntPtr*)next; - freeCount++; - } - - SubSmallBytes(new UIntPtr((uint)freeCount * - (uint)page->cellSize)); - - if (oldFreeList != UIntPtr.Zero) { - // Page already had a free list, follow it to the end. - while(*(UIntPtr*)oldFreeList != UIntPtr.Zero) { - oldFreeList = *(UIntPtr*)oldFreeList; - } - - // And stitch the free lists together - *(UIntPtr*)oldFreeList = pageFreeList; - } - - // Set the reclaimed cells. - int old; - do { - old = page->freeCount; - } while (old != Interlocked.CompareExchange - (ref page->freeCount, old + freeCount, old)); - } - - // Atomically insert the page in the globalPages queue. - AtomicPush(ref globalPages[sizeClass], page); - } - - /// - /// Either reuse an existing or make a new page to allocate into. - /// - [ManualRefCounts] - private PageHeader * GetLocalPage(int sizeClass, Thread currentThread) - { - GC.CheckForNeededGCWork(currentThread); - UIntPtr page = AtomicPop(ref globalFreePages[sizeClass]); - if (page != UIntPtr.Zero) { - // We got the page - PageHeader *pageHeader = (PageHeader*) page; - - VTable.Assert(pageHeader->freeCount > 0, "empty FreePage"); - - AddSmallBytes(new UIntPtr((uint)pageHeader->freeCount * - (uint)pageHeader->cellSize)); - - pageHeader->freeCount = 0; - - return pageHeader; - } - - // Create a new local page. - return NewLocalPage(sizeClass, currentThread); - } - - /// - /// Create a new page to allocate into - /// - [ManualRefCounts] - private PageHeader * NewLocalPage(int sizeClass, Thread currentThread) - { - VTable.Assert(sizeClass > 0, "non-positive sizeClass"); - bool fCleanPages = true; - UIntPtr page = PageManager.EnsurePages(currentThread, - (UIntPtr) 1, INIT_PAGE, - ref fCleanPages); - AddSmallPage(); - UIntPtr pageAddr = PageTable.PageAddr(page); - PageTable.SetExtra(page, (short) sizeClass); - PageHeader *pageHeader = (PageHeader *) pageAddr; - - // Set up the free list of free slots - UIntPtr stride = GetCellSize(sizeClass); - VTable.Assert(stride != UIntPtr.Zero, "Zero Stride"); - pageHeader->cellSize = stride; - UIntPtr cursor = - pageAddr + PageHeader.SIZEOF + PreHeader.Size; - - pageHeader->freeList = cursor; - UIntPtr limit = - PageTable.PageAddr(page+1) - stride + PreHeader.Size; - UIntPtr nextAddr = cursor + stride; - while (nextAddr <= limit) { - *(UIntPtr*)cursor = nextAddr; - cursor = nextAddr; - nextAddr = cursor + stride; - } - - PageTable.SetType(page, SMALL_OBJ_PAGE); - return pageHeader; - } - - #endregion - - } - -} diff --git a/base/Kernel/Bartok/GCs/StopTheWorldCollector.cs b/base/Kernel/Bartok/GCs/StopTheWorldCollector.cs deleted file mode 100644 index 297df4c..0000000 --- a/base/Kernel/Bartok/GCs/StopTheWorldCollector.cs +++ /dev/null @@ -1,322 +0,0 @@ -/*******************************************************************/ -/* WARNING */ -/* This file should be identical in the Bartok and Singularity */ -/* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ -/*******************************************************************/ - -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -namespace System.GCs { - - using System.Threading; - using System.Runtime.CompilerServices; - -#if SINGULARITY -#if SINGULARITY_KERNEL - using Microsoft.Singularity; - using Microsoft.Singularity.Scheduling; -#else - using Microsoft.Singularity; - using Microsoft.Singularity.V1.Services; -#endif - - [CLSCompliant(false)] - public enum GarbageCollectorEvent : ushort - { - StartStopTheWorld = 1, - EndStopTheWorld = 2, - StartCollection = 3, - EndCollection = 4, - } -#endif - - [NoCCtor] - internal abstract class StopTheWorldCollector : BaseCollector - { - - internal static int collectorThreadIndex; - - /// - /// Identifies the current collection phase - /// - internal enum STWPhase { - Dummy, // Not used! - Idle, // No collection is taking place - Synchronizing, // Attempting to stop the world - SingleThreaded, // Stop-the-world phase - } - - /// - /// The current state of the collector. - /// - internal static STWPhase CurrentPhase; - - [PreInitRefCounts] - internal static void Initialize() { - collectorThreadIndex = -1; - CurrentPhase = STWPhase.Idle; - } - - internal abstract void CollectStopped(int currentThreadIndex, - int generation); - - // Implementation of GCInterface - internal override bool IsOnTheFlyCollector { - get { - return false; - } - } - - internal override void Collect(int currentThreadIndex, - int generation) - { - int foundIndex = - Interlocked.CompareExchange(ref collectorThreadIndex, - currentThreadIndex, -1); - if (foundIndex < 0) { - // We are the designated collector thread - PerformCollection(generation); - } else { - Transitions.TakeDormantControlNoGC(currentThreadIndex); - // The 'foundIndex' thread may have completed its - // collection and another thread may have started - // another collection after we read - // 'collectorThreadIndex'. The collector thread may - // have decided to wait for us before we entered - // DormantState, so we have to read - // 'collectorThreadIndex' again. - foundIndex = collectorThreadIndex; - if (foundIndex >= 0) { - Thread.SignalGCEvent(foundIndex); - } - Transitions.TakeMutatorControlNoGC(currentThreadIndex); - } - } - - private void PerformCollection(int generation) - { -#if SINGULARITY - Tracing.Log(Tracing.Debug,"GC start"); -#endif - CollectorStatistics.Event(GCEvent.StopTheWorld); - CurrentPhase = STWPhase.Synchronizing; - StopTheWorld(); - CurrentPhase = STWPhase.SingleThreaded; -#if SINGULARITY - long preGcMemoryUsage = GC.GetTotalMemory(false); -#if SINGULARITY_KERNEL -#if THREAD_TIME_ACCOUNTING - TimeSpan ticks = Thread.CurrentThread.ExecutionTime; - TimeSpan ticks2 = SystemClock.KernelUpTime; -#else - TimeSpan ticks = SystemClock.KernelUpTime; -#endif -#elif SINGULARITY_PROCESS -#if THREAD_TIME_ACCOUNTING - TimeSpan ticks = ProcessService.GetThreadTime(); - TimeSpan ticks2 = ProcessService.GetUpTime(); -#else - TimeSpan ticks = ProcessService.GetUpTime(); -#endif -#endif -#endif //singularity -#if SINGULARITY_KERNEL - bool iflag = Processor.DisableInterrupts(); -#endif -#if SINGULARITY - ulong beg = Processor.GetCycleCount(); -#endif - // Preparation - GC.allocationInhibitGC = true; - // Verify the heap before GC - if (VTable.enableGCVerify) { - this.VerifyHeap(true); - } - // Invoke the chosen collector -#if SINGULARITY - Monitoring.Log(Monitoring.Provider.GC, - (ushort)GarbageCollectorEvent.StartCollection); -#endif - this.CollectStopped(collectorThreadIndex, generation); -#if SINGULARITY - Monitoring.Log(Monitoring.Provider.GC, - (ushort)GarbageCollectorEvent.EndCollection); -#endif - // Verify the heap after GC - if (VTable.enableGCVerify) { - this.VerifyHeap(false); - } - if (VTable.enableGCAccounting) { - MemoryAccounting.Report(GC.gcType); - } - // Cleanup - CollectorStatistics.Event(GCEvent.ResumeTheWorld); - GC.allocationInhibitGC = false; - CurrentPhase = STWPhase.Idle; -#if SINGULARITY - long postGcMemoryUsage = GC.GetTotalMemory(false); -#endif - ResumeTheWorld(); - collectorThreadIndex = -1; -#if SINGULARITY - Tracing.Log(Tracing.Debug,"GC stop"); - long pagesCollected = preGcMemoryUsage - postGcMemoryUsage; -#if SINGULARITY_KERNEL -#if THREAD_TIME_ACCOUNTING - int procId = Thread.CurrentProcess.ProcessId; - ticks = Thread.CurrentThread.ExecutionTime - ticks; - ticks2 = SystemClock.KernelUpTime - ticks2; - Process.kernelProcess.SetGcPerformanceCounters(ticks, (long) pagesCollected); -#else - ticks = SystemClock.KernelUpTime - ticks; -#endif - Thread.CurrentProcess.SetGcPerformanceCounters(ticks, (long) pagesCollected); -#elif SINGULARITY_PROCESS -#if THREAD_TIME_ACCOUNTING - ushort procId = ProcessService.GetCurrentProcessId(); - ticks = ProcessService.GetThreadTime() - ticks; - ticks2 = ProcessService.GetUpTime() - ticks2; -#else - ticks = ProcessService.GetUpTime() - ticks; -#endif - ProcessService.SetGcPerformanceCounters(ticks, (long) pagesCollected); -#endif - -#if DEBUG -#if THREAD_TIME_ACCOUNTING - DebugStub.WriteLine("~~~~~ StopTheWorld [collected pages={0:x8}, pid={1:x3}, ms(Thread)={2:d6}, ms(System)={3:d6}, procId={4}, tid={5}]", - __arglist(pagesCollected, - PageTable.processTag >> 16, - ticks.Milliseconds, - ticks2.Milliseconds, - procId, - Thread.GetCurrentThreadIndex() - )); -#endif -#endif -#endif - -#if SINGULARITY - DebugStub.AddToPerfCounter(GC.perfCounter, Processor.GetCycleCount() - beg); -#endif -#if SINGULARITY_KERNEL -#else -#if false - DebugStub.WriteLine("GC{0} after {1,11}", - __arglist( - ProcessService.GetCurrentProcessId(), - (UIntPtr)GC.installedGC.TotalMemory)); - if (GC.installedGC.TotalMemory >= VTable.enableGCAccountingThreshold) { - DebugStub.WriteLine("GC memory {0,16}", __arglist(GC.installedGC.TotalMemory)); - } -#endif -#endif - -#if SINGULARITY_KERNEL - Processor.RestoreInterrupts(iflag); -#endif - } - - internal override void CheckForNeededGCWork(Thread currentThread) { - while (CurrentPhase == STWPhase.Synchronizing && - currentThread.threadIndex != collectorThreadIndex) { - GC.InvokeCollection(currentThread); - } - } - - internal override void NewThreadNotification(Thread newThread, - bool initial) - { - base.NewThreadNotification(newThread, initial); - if (CurrentPhase == STWPhase.Synchronizing) { - Transitions.MakeGCRequest(newThread.threadIndex); - } - } - - internal override void DeadThreadNotification(Thread deadThread) - { - MultiUseWord.CollectFromThread(deadThread); - base.DeadThreadNotification(deadThread); - } - - internal override void ThreadDormantGCNotification(int threadIndex) { - int ctid = collectorThreadIndex; - if (ctid >= 0) { - Thread.SignalGCEvent(ctid); - } - } - - private static void StopTheWorld() { -#if SINGULARITY - //DebugStub.WriteLine("~~~~~ StopTheWorld()"); - Monitoring.Log(Monitoring.Provider.GC, - (ushort)GarbageCollectorEvent.StartStopTheWorld); -#if SINGULARITY_KERNEL - TimeSpan ticks = SystemClock.KernelUpTime; -#elif SINGULARITY_PROCESS - TimeSpan ticks = ProcessService.GetUpTime(); -#endif -#endif - VTable.Assert(Thread.GetCurrentThreadIndex() == - collectorThreadIndex); - Transitions.MakeGCRequests(collectorThreadIndex); - // Force threads to take allocation slow path. - for (int i = 0; i < Thread.threadTable.Length; i++) { - Thread t = Thread.threadTable[i]; - if (t != null) { - BumpAllocator.Preempt(t); - } - } - for(int i = 0; i < Thread.threadTable.Length; i++) { - if (Thread.threadTable[i] == null) { - continue; - } - if (i == collectorThreadIndex) { - continue; - } - CollectorStatistics.Event(GCEvent.StopThread, i); - while (!Transitions.TakeGCControl(i) && - !Transitions.UnderGCControl(i) && - Transitions.HasGCRequest(i) && - Thread.threadTable[i] != null) { - Thread.WaitForGCEvent(collectorThreadIndex); - } - } -#if SINGULARITY -#if SINGULARITY_KERNEL - ticks = SystemClock.KernelUpTime - ticks; -#elif SINGULARITY_PROCESS - ticks = ProcessService.GetUpTime() - ticks; -#endif - Monitoring.Log(Monitoring.Provider.GC, - (ushort)GarbageCollectorEvent.EndStopTheWorld); -#endif - } - - private static void ResumeTheWorld() { - VTable.Assert(Thread.GetCurrentThreadIndex() == - collectorThreadIndex); - for(int i = 0; i < Thread.threadTable.Length; i++) { -#if SINGULARITY_KERNEL - if (Scheduler.IsIdleThread(i)) { - continue; - } -#endif - if (i == collectorThreadIndex) { - if (Transitions.HasGCRequest(i)) { - Transitions.ClearGCRequest(i); - } - } else if (Transitions.UnderGCControl(i)) { - Transitions.ReleaseGCControl(i); - } else if (Thread.threadTable[i] != null) { - Thread.SignalGCEvent(i); - } - } - } - - } - -} diff --git a/base/Kernel/Bartok/GCs/ThreadHeaderQueue.cs b/base/Kernel/Bartok/GCs/ThreadHeaderQueue.cs deleted file mode 100644 index 24347d3..0000000 --- a/base/Kernel/Bartok/GCs/ThreadHeaderQueue.cs +++ /dev/null @@ -1,407 +0,0 @@ -/*******************************************************************/ -/* WARNING */ -/* This file should be identical in the Bartok and Singularity */ -/* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ -/*******************************************************************/ - -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - - -namespace System.GCs { - - using Microsoft.Bartok.Options; - using Microsoft.Bartok.Runtime; - - using System; - using System.Threading; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - - /// - /// This class defines a queue that is created as a linked list from - /// each thread object through pointers stored in object headers. - /// - /// This queue is used for the concurrent mark sweep collector to allow - /// it to trace through the heap without ever requiring allocation - /// or locking. - /// - /// Within a thread this is a FIFO queue; all mutations are made at the - /// head. - /// - internal struct ThreadHeaderQueue { - - [StructLayout(LayoutKind.Sequential)] - [MixinConditional("ConcurrentMSGC")] - [Mixin(typeof(PreHeader))] - private struct PreHeaderQueue { - internal MultiUseWord muw; - internal UIntPtr link; - } - - [MixinConditional("ConcurrentMSGC")] - [Mixin(typeof(Object))] - private class ThreadHeaderQueueObject : System.Object { - internal new PreHeaderQueue preHeader; - } - - [Inline] - private static ThreadHeaderQueueObject MixinObject(Object obj) { - return (ThreadHeaderQueueObject) obj; - } - - [MixinConditional("ConcurrentMSGC")] - [MixinConditional("AllThreadMixins")] - [Mixin(typeof(Thread))] - private class ThreadHeaderQueueThread : Object { - [AccessedByRuntime("referenced in brtforgc.asm")] - internal ThreadHeaderQueue gcQueue; - } - - [Inline] - private static ThreadHeaderQueueThread MixinThread(Thread t) { - return (ThreadHeaderQueueThread) (Object) t; - } - - /// - /// Contains the pointer to an object that a thread is trying - /// to insert. - /// - internal UIntPtr newHead; - - /// - /// Contains the pointer to the first object in the queue, or Zero - /// if the queue is empty. - /// - internal UIntPtr head; - - /// - /// Contains a pointer to the object that was at the head of the - /// queue the last time it was 'stolen' by a consuming thread. - /// - /// If this matches the current head value then there is nothing - /// on the queue that has not been stolen. - /// - internal UIntPtr stolenHead; - - // Contains a count of nodes that have been stolen - internal static long stolenCount; - - internal static UIntPtr TAIL_MARKER { - get { return ~(UIntPtr)3U; } - } - - /// - /// Reset the queue. - /// - [Inline] - internal static void Reset(Thread t) { - MixinThread(t).gcQueue.Reset(); - } - - [Inline] - internal void Reset() - { - this.head = TAIL_MARKER; - this.newHead = TAIL_MARKER; - this.stolenHead = TAIL_MARKER; - } - - /// - /// Is the queue empty? - /// - internal static bool IsEmpty(Thread t) { - return MixinThread(t).gcQueue.IsEmpty(); - } - - private bool IsEmpty() { - return (this.head == this.stolenHead); - } - - [Inline] - private static UIntPtr QueueField(Object obj) - { - return MixinObject(obj).preHeader.link; - } - - [Inline] - private static void SetQueueField(Object obj, UIntPtr value) - { - MixinObject(obj).preHeader.link = value; - } - - private static bool ExchangeQueueField(Object obj, - UIntPtr val, - UIntPtr oldVal) - { - ThreadHeaderQueueObject obj2 = MixinObject(obj); - return Interlocked.CompareExchange(ref obj2.preHeader.link, - val, oldVal) == oldVal; - } - - /// - /// Get the mark value from the header word of the object. - /// - [Inline] - internal static UIntPtr GcMark(Object obj) - { - VTable.Assert(obj != null, "Can not get mark of null"); - return (QueueField(obj) & (UIntPtr)3U); - } - - /// - /// Sets the mark value in the header word of the object. - /// - [Inline] - internal static void SetGcMark(UIntPtr objAddr, UIntPtr markBits) - { - SetGcMark(Magic.fromAddress(objAddr), markBits); - } - - [Inline] - internal static void SetGcMark(Object obj, UIntPtr markBits) - { - SetQueueField(obj, markBits); - } - - internal static bool IsInQueue(Object obj) { - return (QueueField(obj) == UIntPtr.Zero); - } - - /// - /// Link a new value at the head of the queue. It is assumed that - /// if the object is unmarked, the value in the header word will - /// simply be 'unmarkedColor'. Returns true if the object was - /// marked an linked into the queue. - /// - [Inline] - internal static bool Push(Thread t, - UIntPtr objAddr, - UIntPtr markedColor, - UIntPtr unmarkedColor) - { - return MixinThread(t).gcQueue.Push(objAddr, - markedColor, - unmarkedColor); - } - - [Inline] - private bool Push(UIntPtr objAddr, - UIntPtr markedColor, - UIntPtr unmarkedColor) - { - VTable.Assert(objAddr != UIntPtr.Zero, "Can not push null!"); - this.newHead = objAddr; - Object obj = Magic.fromAddress(objAddr); - if (ExchangeQueueField(obj, this.head + markedColor, - unmarkedColor)) { - this.head = objAddr; - return true; - } else { - // Someone else enqueued the object, so restore this queue - this.newHead = this.head; - return false; - } - } - - /// - /// Link a new value at the head of the queue, knowing that the - /// object is a thread local object. - /// - [Inline] - internal static void PushUnsafe(Thread t, - Object obj, - UIntPtr markBits) - { - MixinThread(t).gcQueue.PushUnsafe(obj, markBits); - } - - [Inline] - private void PushUnsafe(Object obj, UIntPtr markBits) - { - VTable.Assert(obj != null, "Can not push null!"); - SetQueueField(obj, this.head + markBits); - this.newHead = Magic.addressOf(obj); - this.head = this.newHead; - } - - /// - /// Unlink a value from the head of the queue. This - /// method is not thread safe. The method is supposed to be - /// called only from the collector thread. - /// - [Inline] - internal static Object Pop(Thread thread, UIntPtr markedColor) { - return MixinThread(thread).gcQueue.Pop(markedColor); - } - - [Inline] - private Object Pop(UIntPtr markedColor) - { - VTable.Assert(!this.IsEmpty(), "Queue is empty!"); - Object obj = Magic.fromAddress(this.head); - this.head = QueueField(obj) & ~(UIntPtr)3U; - SetQueueField(obj, markedColor); - return obj; - } - - /// - /// Returns the number of values in the list. It is tolerant - /// of concurrent additions, although additions after the initial - /// read of the list head will not be counted. It is tolerant - /// of concurrent steals from the list, although the count may - /// be truncated if the list is stolen from during the count. - /// - internal int Count { - get { - UIntPtr cachedStolenHead = this.stolenHead; - if (this.head == cachedStolenHead) { - return 0; - } - Object obj = Magic.fromAddress(this.head); - UIntPtr next = QueueField(obj) & ~(UIntPtr)3U; - int listLength = 1; - while (next != cachedStolenHead && - this.stolenHead == cachedStolenHead) { - listLength++; - obj = Magic.fromAddress(next); - next = QueueField(obj) & ~(UIntPtr)3U; - } - return listLength; - } - } - - internal static void DeadThreadNotification(Thread deadThread, - UIntPtr markedColor) - { - StealSafe(Thread.CurrentThread, deadThread, markedColor); - } - - /// - /// This method attempts to take values from the passed-in queue - /// and place it in the 'this' queue. It assumes that the 'this' - /// queue is not concurrently added to. However, the method is - /// tolerant of concurrent attempts to steal from the fromQueue. - /// - /// Rather than reading the old mark value from the header word - /// of the tail object in the 'fromQueue', the new mark value in - /// said object is going to be 'markedColor' - /// - /// If any values are stolen the method returns true. - /// - internal static bool Steal(Thread toThread, - Thread fromThread, - UIntPtr markedColor) - { - ThreadHeaderQueueThread myToThread = MixinThread(toThread); - ThreadHeaderQueueThread myFromThread = MixinThread(fromThread); - return myToThread.gcQueue.StealFrom(ref myFromThread.gcQueue, - markedColor); - } - - private bool StealFrom(ref ThreadHeaderQueue fromQueue, - UIntPtr markedColor) - { - UIntPtr fromHead, fromTail; - if (fromQueue.Steal(out fromHead, out fromTail)) { - // Prepend the stolen list segment to our list - this.newHead = fromHead; - Object tailObject = Magic.fromAddress(fromTail); - SetQueueField(tailObject, this.head + markedColor); - this.head = fromHead; - return true; - } else { - return false; - } - } - - /// - /// This method attempts to take values from the passed-in queue - /// and place it in the 'this' queue. The method is tolerant of - /// concurrent attempts to add to the 'this' queue and is - /// tolerant of concurrent attempts to steal from the fromQueue. - /// - /// Rather than reading the old mark value from the header word - /// of the tail object in the 'fromQueue', the new mark value in - /// said object is going to be 'markedColor' - /// - /// If any values are stolen the method returns true. - /// - internal static void StealSafe(Thread toThread, - Thread fromThread, - UIntPtr markedColor) - { - ThreadHeaderQueueThread myToThread = MixinThread(toThread); - ThreadHeaderQueueThread myFromThread = MixinThread(fromThread); - myToThread.gcQueue.StealFromSafe(ref myFromThread.gcQueue, - markedColor); - } - - private void StealFromSafe(ref ThreadHeaderQueue fromQueue, - UIntPtr markedColor) - { - UIntPtr fromHead, fromTail; - this.newHead = fromQueue.head; - if (fromQueue.Steal(out fromHead, out fromTail)) { - VTable.Assert(this.newHead == fromHead); - // Prepend the stolen list segment to our list - UIntPtr thisHead = this.head; - while (Interlocked.CompareExchange(ref this.newHead, - fromHead, thisHead) != - thisHead) { - thisHead = this.head; - } - // We have won exclusive rights to insert into 'this'. - Object tailObject = Magic.fromAddress(fromTail); - SetQueueField(tailObject, thisHead + markedColor); - this.head = fromHead; - } else { - this.newHead = this.head; - } - } - - private bool Steal(out UIntPtr stolenHead, out UIntPtr stolenTail) - { - while (true) { - UIntPtr thisStolen = this.stolenHead; - UIntPtr thisHead = this.head; - while (thisStolen != thisHead) { - if (Interlocked.CompareExchange(ref this.stolenHead, - thisHead, thisStolen) == - thisStolen) { - // We managed to steal part of the list. - // Find the end of the stolen list segment. - Object obj = Magic.fromAddress(thisHead); - UIntPtr next = - QueueField(obj) & ~(UIntPtr)3U; - int listLength = 0; - while (next != thisStolen) { - listLength++; - obj = Magic.fromAddress(next); - next = QueueField(obj) & ~(UIntPtr)3U; - } - stolenCount += listLength; - stolenHead = thisHead; - stolenTail = Magic.addressOf(obj); - return true; - } - thisStolen = this.stolenHead; - thisHead = this.head; - } - if (this.newHead == thisHead) { - // There is nothing to steal. - stolenHead = UIntPtr.Zero; - stolenTail = UIntPtr.Zero; - return false; - } - // Someone must be in the process of inserting something - // into the queue. - Thread.Yield(); - } - } - - } - -} diff --git a/base/Kernel/Bartok/GCs/Util.cs b/base/Kernel/Bartok/GCs/Util.cs deleted file mode 100644 index 21dcb99..0000000 --- a/base/Kernel/Bartok/GCs/Util.cs +++ /dev/null @@ -1,127 +0,0 @@ -/*******************************************************************/ -/* WARNING */ -/* This file should be identical in the Bartok and Singularity */ -/* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ -/*******************************************************************/ - -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -namespace System.GCs -{ - using System.Runtime.CompilerServices; - - // Util contains common functions for alignment and padding - // arithmetic and for calculating object sizes. - internal class Util - { - // WARNING: don't initialize any static fields in this class - // without manually running the class constructor at startup! - - [NoHeapAllocation] - internal static uint dwordAlign(uint numBytes) { - return ((numBytes+3) & ~3U); - } - - [NoHeapAllocation] - internal static UIntPtr dwordAlign(UIntPtr numBytes) { - return ((numBytes+3) & new UIntPtr(~3U)); - } - - [NoHeapAllocation] - static UIntPtr qwordAlign(UIntPtr addr) { - return ((addr+7) & new UIntPtr(~7U)); - } - - [Inline] - [NoHeapAllocation] - internal static bool IsAligned(uint bytes, uint size) - { - return (bytes & (size - 1)) == 0; - } - - [Inline] - [NoHeapAllocation] - internal static bool IsAligned(UIntPtr bytes, UIntPtr size) - { - return (bytes & (size - 1)) == UIntPtr.Zero; - } - - [Inline] - [NoHeapAllocation] - internal static uint Align(uint bytes, uint size) - { - return (bytes & ~(size - 1)); - } - - [Inline] - [NoHeapAllocation] - internal static UIntPtr Align(UIntPtr bytes, UIntPtr size) - { - return (bytes & ~(size - 1)); - } - - [Inline] - [NoHeapAllocation] - internal static UIntPtr UIntPtrAlign(UIntPtr bytes) - { - return Align(bytes, (UIntPtr) UIntPtr.Size); - } - - [Inline] - [NoHeapAllocation] - internal static uint Pad(uint data, uint size) - { - return ((data + (size - 1)) & ~(size - 1)); - } - - [Inline] - [NoHeapAllocation] - internal static UIntPtr Pad(UIntPtr data, UIntPtr size) - { - return ((data + (size - 1)) & ~(size - 1)); - } - - [Inline] - [NoHeapAllocation] - internal static UIntPtr UIntPtrPad(UIntPtr bytes) - { - return Pad(bytes, (UIntPtr) UIntPtr.Size); - } - - internal static unsafe void MemClear(UIntPtr startAddr, - UIntPtr size) - { -#if SINGULARITY - // On Singularity we use the common optimized functions. - Buffer.ZeroMemory((byte*)startAddr, (int)size); -#else - VTable.Assert(UIntPtr.Size !=4 || ((startAddr & ((UIntPtr)0x3)) == ((UIntPtr)0)), "Not-aligned address in Util.MemClear"); - VTable.Assert(UIntPtr.Size !=8 || ((startAddr & ((UIntPtr)0x7)) == ((UIntPtr)0)), "Not-aligned address in Util.MemClear"); - VTable.Assert((size & ((UIntPtr)0x3)) == ((UIntPtr)0), "size expected to be multiple of 4 (size of int) in Util.MemClear"); - Buffer.ZeroMemory((byte*)startAddr, size); -#endif - } - - internal static unsafe void MemCopy(UIntPtr toAddress, - UIntPtr fromAddress, - UIntPtr count) - { -#if SINGULARITY - // On Singularity we use the common optimized functions. - Buffer.MoveMemory((byte*)toAddress, (byte*)fromAddress, (int)count); -#else - int wordCount = (int) (count >> 2); - int *from = (int *) fromAddress; - int *to = (int *) toAddress; - for (int i = 0; i < wordCount; i++) { - to[i] = from[i]; - } -#endif - } - - } - -} diff --git a/base/Kernel/Bartok/GCs/WriteBarrier.cs b/base/Kernel/Bartok/GCs/WriteBarrier.cs deleted file mode 100644 index f5c3f18..0000000 --- a/base/Kernel/Bartok/GCs/WriteBarrier.cs +++ /dev/null @@ -1,580 +0,0 @@ -/*******************************************************************/ -/* WARNING */ -/* This file should be identical in the Bartok and Singularity */ -/* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ -/*******************************************************************/ - -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -namespace System.GCs -{ - using Microsoft.Bartok.Runtime; - using System.Runtime.CompilerServices; - using System.Threading; - - [CCtorIsRunDuringStartup] - internal abstract unsafe class WriteBarrier - { - - [TrustedNonNull] - private static WriteBarrier installedWriteBarrier; - - [TrustedNonNull] - private static CopyFieldsVisitor copyFieldsVisitor; - - [TrustedNonNull] - private static ZeroFieldsVisitor zeroFieldsVisitor; - - [PreInitRefCounts] - internal static void Initialize() - { - switch (GC.wbType) { -#if !SINGULARITY || MARK_SWEEP_COLLECTOR - case WBType.noWB: { - EmptyWriteBarrier.Initialize(); - installedWriteBarrier = EmptyWriteBarrier.instance; - break; - } -#endif -#if !SINGULARITY || SEMISPACE_COLLECTOR || SLIDING_COLLECTOR || ADAPTIVE_COPYING_COLLECTOR - case WBType.Generational: { - GenerationalWriteBarrier.Initialize(); - installedWriteBarrier = GenerationalWriteBarrier.instance; - break; - } -#endif -#if !SINGULARITY || CONCURRENT_MS_COLLECTOR - case WBType.CMS: { - WriteBarrierCMS.Initialize(); - installedWriteBarrier = WriteBarrierCMS.instance; - break; - } -#endif -#if !SINGULARITY || ATOMIC_RC_COLLECTOR - case WBType.ARC: { - AtomicRCWriteBarrier.Initialize(); - installedWriteBarrier = AtomicRCWriteBarrier.instance; - break; - } -#endif -#if !SINGULARITY || SEMISPACE_COLLECTOR || SLIDING_COLLECTOR || ADAPTIVE_COPYING_COLLECTOR - case WBType.AllCards: { - AllCardsWriteBarrier.Initialize(); - installedWriteBarrier = AllCardsWriteBarrier.instance; - break; - } -#endif - default: { - VTable.NotReached("Unknown write barrier type: "+GC.wbType); - break; - } - } - // copyFieldsVisitor = new CopyFieldsVisitor(); - WriteBarrier.copyFieldsVisitor = (CopyFieldsVisitor) - BootstrapMemory.Allocate(typeof(CopyFieldsVisitor)); - // zeroFieldsVisitor = new ZeroFieldsVisitor(); - WriteBarrier.zeroFieldsVisitor = (ZeroFieldsVisitor) - BootstrapMemory.Allocate(typeof(ZeroFieldsVisitor)); - } - - [Inline] - protected virtual void StoreIndirectImpl(UIntPtr *location, - Object value) - { - this.WriteReferenceImpl(location, value); - } - - [Inline] - protected virtual void StoreIndirectImpl(ref Object reference, - Object value) - { - this.WriteReferenceImpl(ref reference, value); - } - - [Inline] - protected virtual void StoreObjectFieldImpl(Object obj, - UIntPtr fieldOffset, - Object value) - { - UIntPtr *fieldPtr = (UIntPtr *) - (Magic.addressOf(obj) + fieldOffset); - this.WriteReferenceImpl(fieldPtr, value); - } - - [Inline] - protected virtual void StoreStructFieldImpl(UIntPtr structPtr, - UIntPtr fieldOffset, - Object value) - { - UIntPtr *fieldPtr = (UIntPtr *) (structPtr + fieldOffset); - this.WriteReferenceImpl(fieldPtr, value); - } - - [Inline] - protected virtual void StoreVectorElementImpl(Array vector, - int index, - int arrayElementSize, - UIntPtr fieldOffset, - Object value) - { - UIntPtr *fieldPtr = - IndexedFieldPtr(vector, index, arrayElementSize, fieldOffset); - this.WriteReferenceImpl(fieldPtr, value); - } - - [Inline] - protected virtual void StoreArrayElementImpl(Array array, - int index, - int arrayElementSize, - UIntPtr fieldOffset, - Object value) - { - UIntPtr *fieldPtr = - IndexedFieldPtr(array, index, arrayElementSize, fieldOffset); - this.WriteReferenceImpl(fieldPtr, value); - } - - [Inline] - protected virtual void StoreStaticFieldImpl(ref Object staticField, - Object value) - { - this.WriteReferenceImpl(ref staticField, value); - } - - protected abstract void CopyStructImpl(VTable vtable, - UIntPtr srcPtr, - UIntPtr dstPtr); - - protected abstract Object AtomicSwapImpl(ref Object reference, - Object value); - - protected abstract Object AtomicCompareAndSwapImpl(ref Object reference, - Object newValue, - Object comparand); - - protected abstract void CloneImpl(Object srcObject, Object dstObject); - - protected void WriteReferenceImpl(ref Object reference, Object value) - { - this.WriteReferenceImpl(Magic.toPointer(ref reference), value); - } - - protected abstract void WriteReferenceImpl(UIntPtr *location, - Object value); - - // 'offset' is not relative to the lower bound, but is a count - // of elements from the first element in the array. - protected abstract void ArrayZeroImpl(Array array, - int offset, - int length); - - // 'offset' is not relative to the lower bound, but is a count - // of elements from the first element in the array. - protected abstract void ArrayCopyImpl(Array srcArray, int srcOffset, - Array dstArray, int dstOffset, - int length); - - [Inline] - protected static UIntPtr IndexedDataPtr(Array array) { - return (UIntPtr) (Magic.addressOf(array) + - (array.vtable.baseLength-(uint)PreHeader.Size)); - } - - [Inline] - protected static UIntPtr *IndexedFieldPtr(Array obj, - int index, - int arrayElementSize, - UIntPtr fieldOffset) - { - UIntPtr dataPtr = IndexedDataPtr(obj); - UIntPtr *fieldPtr = (UIntPtr *) - (dataPtr + index * arrayElementSize + fieldOffset); - return fieldPtr; - } - - [Inline] - protected void CopyStructNoBarrier(VTable vtable, - UIntPtr srcPtr, - UIntPtr dstPtr) - { - int preHeaderSize = PreHeader.Size; - int postHeaderSize = PostHeader.Size; - int structSize = ((int) ObjectLayout.ObjectSize(vtable) - - (preHeaderSize + postHeaderSize)); - Buffer.MoveMemory((byte *) dstPtr, (byte *) srcPtr, structSize); - } - - [Inline] - protected void CopyStructWithBarrier(VTable vtable, - UIntPtr srcPtr, - UIntPtr dstPtr) - { - copyFieldsVisitor.VisitReferenceFields(vtable, srcPtr, dstPtr); - } - - [Inline] - protected Object AtomicSwapNoBarrier(ref Object reference, - Object value) - { - UIntPtr resultAddr = - Interlocked.Exchange(Magic.toPointer(ref reference), - Magic.addressOf(value)); - return Magic.fromAddress(resultAddr); - } - - [Inline] - protected Object AtomicCompareAndSwapNoBarrier(ref Object reference, - Object newValue, - Object comparand) - { - UIntPtr resultAddr = - Interlocked.CompareExchange(Magic.toPointer(ref reference), - Magic.addressOf(newValue), - Magic.addressOf(comparand)); - return Magic.fromAddress(resultAddr); - } - - [Inline] - protected void CloneNoBarrier(Object srcObject, - Object dstObject) - { - UIntPtr objectSize = System.GCs.ObjectLayout.Sizeof(srcObject); - int preHeaderSize = PreHeader.Size; - int postHeaderSize = PostHeader.Size; - // We don't copy any of the header fields. - Util.MemCopy(Magic.addressOf(dstObject) + postHeaderSize, - Magic.addressOf(srcObject) + postHeaderSize, - objectSize - preHeaderSize - postHeaderSize); - } - - [Inline] - protected void CloneWithBarrier(Object srcObject, - Object dstObject) - { - copyFieldsVisitor.VisitReferenceFields(srcObject, dstObject); - } - - // 'offset' is not relative to the lower bound, but is a count - // of elements from the first element in the array. - [Inline] - protected void ArrayZeroNoBarrier(Array array, int offset, - int length) - { - UIntPtr dataPtr = IndexedDataPtr(array); - int elementSize = array.vtable.arrayElementSize; - Buffer.ZeroMemory((byte *)dataPtr + offset * elementSize, - length * elementSize); - } - - // 'offset' is not relative to the lower bound, but is a count - // of elements from the first element in the array. - [Inline] - protected void ArrayZeroWithBarrier(Array array, int offset, - int length) - { - UIntPtr dataPtr = IndexedDataPtr(array); - int elementSize = array.vtable.arrayElementSize; - UIntPtr startAddr = dataPtr + offset * elementSize; - zeroFieldsVisitor.VisitReferenceFields(array.vtable, - startAddr, length); - } - - // 'offset' is not relative to the lower bound, but is a count - // of elements from the first element in the array. - [Inline] - protected void ArrayCopyNoBarrier(Array srcArray, int srcOffset, - Array dstArray, int dstOffset, - int length) - { - UIntPtr srcDataAddr = IndexedDataPtr(srcArray); - UIntPtr dstDataAddr = IndexedDataPtr(dstArray); - int elementSize = srcArray.vtable.arrayElementSize; - VTable.Assert(elementSize == - dstArray.vtable.arrayElementSize); - Buffer.MoveMemory((byte *) (dstDataAddr + dstOffset * elementSize), - (byte *) (srcDataAddr + srcOffset * elementSize), - length * elementSize); - } - - // 'offset' is not relative to the lower bound, but is a count - // of elements from the first element in the array. - [Inline] - protected void ArrayCopyWithBarrier(Array srcArray, int srcOffset, - Array dstArray, int dstOffset, - int length) - { - UIntPtr srcDataAddr = IndexedDataPtr(srcArray); - UIntPtr dstDataAddr = IndexedDataPtr(dstArray); - int elementSize = srcArray.vtable.arrayElementSize; - VTable.Assert(elementSize == dstArray.vtable.arrayElementSize); - UIntPtr srcStartAddr = srcDataAddr + srcOffset * elementSize; - UIntPtr dstStartAddr = dstDataAddr + dstOffset * elementSize; - copyFieldsVisitor.VisitReferenceFields(srcArray.vtable, - srcStartAddr, - dstStartAddr, - length); - } - - [Inline] - internal static void StoreIndirect(UIntPtr *location, Object value) - { - installedWriteBarrier.StoreIndirectImpl(location, value); - } - - [RequiredByBartok] - [Inline] - internal static void StoreIndirect(ref Object reference, - Object value) - { - installedWriteBarrier.StoreIndirectImpl(ref reference, value); - } - - [RequiredByBartok] - [Inline] - internal static void StoreObjectField(Object obj, - UIntPtr fieldOffset, - Object value) - { - installedWriteBarrier.StoreObjectFieldImpl(obj, - fieldOffset, - value); - } - - [RequiredByBartok] - [Inline] - internal static void StoreStructField(UIntPtr structPtr, - UIntPtr fieldOffset, - Object value) - { - installedWriteBarrier.StoreStructFieldImpl(structPtr, - fieldOffset, - value); - } - - [RequiredByBartok] - [Inline] - internal static void StoreVectorElement(Array vector, - int index, - int arrayElementSize, - UIntPtr fieldOffset, - Object value) - { - installedWriteBarrier.StoreVectorElementImpl(vector, - index, - arrayElementSize, - fieldOffset, - value); - } - - [RequiredByBartok] - [Inline] - internal static void StoreArrayElement(Array array, - int index, - int arrayElementSize, - UIntPtr fieldOffset, - Object value) - { - installedWriteBarrier.StoreArrayElementImpl(array, - index, - arrayElementSize, - fieldOffset, - value); - } - - [RequiredByBartok] - [Inline] - internal static void StoreStaticField(ref Object staticField, - Object value) - { - installedWriteBarrier.StoreStaticFieldImpl(ref staticField, value); - } - - [Inline] - internal static void CopyStruct(VTable vtable, - UIntPtr srcPtr, - UIntPtr dstPtr) - { - installedWriteBarrier.CopyStructImpl(vtable, srcPtr, dstPtr); - } - - [RequiredByBartok] - [Inline] - internal static Object AtomicSwap(ref Object reference, - Object value) - { - return installedWriteBarrier.AtomicSwapImpl(ref reference, value); - } - - [RequiredByBartok] - [Inline] - internal static Object AtomicCompareAndSwap(ref Object reference, - Object newValue, - Object comparand) - { - return - installedWriteBarrier.AtomicCompareAndSwapImpl(ref reference, - newValue, - comparand); - } - - [Inline] - internal static void Clone(Object srcObject, Object dstObject) - { - installedWriteBarrier.CloneImpl(srcObject, dstObject); - } - - // 'offset' is not relative to the lower bound, but is a count - // of elements from the first element in the array. - [Inline] - internal static void ArrayZero(Array array, int offset, int length) - { - installedWriteBarrier.ArrayZeroImpl(array, offset, length); - } - - // 'offset' is not relative to the lower bound, but is a count - // of elements from the first element in the array. - [Inline] - internal static void ArrayCopy(Array srcArray, int srcOffset, - Array dstArray, int dstOffset, - int length) - { - installedWriteBarrier.ArrayCopyImpl(srcArray, srcOffset, - dstArray, dstOffset, - length); - } - - [Inline] - internal static void WriteReference(UIntPtr *location, Object value) - { - installedWriteBarrier.WriteReferenceImpl(location, value); - } - - private class CopyFieldsVisitor : OffsetReferenceVisitor - { - - // Struct copy - internal void VisitReferenceFields(VTable vtable, - UIntPtr srcPtr, - UIntPtr dstPtr) - { - int postHeaderSize = PostHeader.Size; - ObjectDescriptor objDesc = - new ObjectDescriptor(vtable, - srcPtr - postHeaderSize, - dstPtr - postHeaderSize, - (UIntPtr) postHeaderSize); - UIntPtr ignore = VisitReferenceFieldsTemplate(ref objDesc); - int preHeaderSize = PreHeader.Size; - UIntPtr limitSize = - ObjectLayout.ObjectSize(vtable) - preHeaderSize; - UIntPtr previouslyDone = objDesc.extra; - UIntPtr tailSize = limitSize - previouslyDone; - if (tailSize > UIntPtr.Zero) { - Util.MemCopy(objDesc.secondBase + previouslyDone, - objDesc.objectBase + previouslyDone, - tailSize); - } - } - - internal void VisitReferenceFields(Object srcObject, - Object dstObject) - { - int postHeaderSize = PostHeader.Size; - ObjectDescriptor objDesc = - new ObjectDescriptor(srcObject.vtable, - Magic.addressOf(srcObject), - Magic.addressOf(dstObject), - (UIntPtr) postHeaderSize); - UIntPtr objectSize = VisitReferenceFieldsTemplate(ref objDesc); - int preHeaderSize = PreHeader.Size; - UIntPtr limitSize = objectSize - preHeaderSize; - UIntPtr previouslyDone = objDesc.extra; - UIntPtr tailSize = limitSize - previouslyDone; - if (tailSize > UIntPtr.Zero) { - Util.MemCopy(objDesc.secondBase + previouslyDone, - objDesc.objectBase + previouslyDone, - tailSize); - } - } - - // Partial array copy - internal void VisitReferenceFields(VTable vtable, - UIntPtr srcElementPtr, - UIntPtr dstElementPtr, - int length) - { - ObjectDescriptor objDesc = - new ObjectDescriptor(vtable, srcElementPtr, dstElementPtr); - VisitReferenceFieldsTemplate(ref objDesc, length); - UIntPtr srcLimitAddr = - srcElementPtr + length * vtable.arrayElementSize; - UIntPtr previouslyDone = objDesc.objectBase + objDesc.extra; - UIntPtr tailSize = srcLimitAddr - previouslyDone; - if (tailSize > UIntPtr.Zero) { - Util.MemCopy(objDesc.secondBase + objDesc.extra, - previouslyDone, tailSize); - } - } - - internal override void FieldOffset(UIntPtr offset, - ref ObjectDescriptor objDesc) - { - UIntPtr previouslyDone = objDesc.extra; - objDesc.extra = offset + UIntPtr.Size; - UIntPtr norefSize = offset - previouslyDone; - if (norefSize > UIntPtr.Zero) { - Util.MemCopy(objDesc.secondBase + previouslyDone, - objDesc.objectBase + previouslyDone, - norefSize); - } - UIntPtr *srcAddr = (UIntPtr *) (objDesc.objectBase + offset); - UIntPtr *dstAddr = (UIntPtr *) (objDesc.secondBase + offset); - Object fieldValue = Magic.fromAddress(*srcAddr); - WriteBarrier.WriteReference(dstAddr, fieldValue); - } - - } - - private class ZeroFieldsVisitor : OffsetReferenceVisitor - { - - internal void VisitReferenceFields(VTable vtable, - UIntPtr elementAddr, - int length) - { - int postHeaderSize = PostHeader.Size; - ObjectDescriptor objDesc = - new ObjectDescriptor(vtable, elementAddr); - VisitReferenceFieldsTemplate(ref objDesc, length); - UIntPtr limitAddr = - elementAddr + length * vtable.arrayElementSize; - UIntPtr previouslyDone = objDesc.objectBase + objDesc.extra; - UIntPtr tailSize = limitAddr - previouslyDone; - if (tailSize > UIntPtr.Zero) { - Buffer.ZeroMemory((byte *) previouslyDone, tailSize); - } - } - - internal override void FieldOffset(UIntPtr offset, - ref ObjectDescriptor objDesc) - { - int postHeaderSize = PostHeader.Size; - UIntPtr previouslyDone = objDesc.extra; - objDesc.extra = offset + UIntPtr.Size; - UIntPtr norefSize = offset - previouslyDone; - if (norefSize > UIntPtr.Zero) { - Util.MemClear(objDesc.objectBase + previouslyDone, - norefSize); - } - UIntPtr *fieldAddr = (UIntPtr *) (objDesc.objectBase + offset); - WriteBarrier.WriteReference(fieldAddr, null); - } - - } - - } - -} diff --git a/base/Kernel/Bartok/GCs/WriteBarrierCMS.cs b/base/Kernel/Bartok/GCs/WriteBarrierCMS.cs deleted file mode 100644 index 021c4dd..0000000 --- a/base/Kernel/Bartok/GCs/WriteBarrierCMS.cs +++ /dev/null @@ -1,148 +0,0 @@ -/*******************************************************************/ -/* WARNING */ -/* This file should be identical in the Bartok and Singularity */ -/* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ -/*******************************************************************/ - -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -namespace System.GCs { - - using Microsoft.Bartok.Runtime; - using System.Threading; - using System.Runtime.CompilerServices; - - internal unsafe class WriteBarrierCMS : UniversalWriteBarrier - { - - internal static WriteBarrierCMS instance; - - [PreInitRefCounts] - internal static new void Initialize() { - WriteBarrierCMS.instance = (WriteBarrierCMS) - BootstrapMemory.Allocate(typeof(WriteBarrierCMS)); - } - - [Inline] - protected override Object AtomicSwapImpl(ref Object reference, - Object value) - { - ReferenceCheck(Magic.toPointer(ref reference), value); - UIntPtr resultAddr = - Interlocked.Exchange(Magic.toPointer(ref reference), - Magic.addressOf(value)); - return Magic.fromAddress(resultAddr); - } - - [Inline] - protected override - Object AtomicCompareAndSwapImpl(ref Object reference, - Object newValue, - Object comparand) - { - ReferenceCheck(Magic.toPointer(ref reference), newValue); - UIntPtr resultAddr = - Interlocked.CompareExchange(Magic.toPointer(ref reference), - Magic.addressOf(newValue), - Magic.addressOf(comparand)); - return Magic.fromAddress(resultAddr); - } - - [Inline] - protected override void CloneImpl(Object srcObject, Object dstObject) - { - // There is no need to keep track of initial writes, so do nothing! - CloneNoBarrier(srcObject, dstObject); - } - - [Inline] - protected override void WriteReferenceImpl(UIntPtr *location, - Object value) - { - ReferenceCheck(location, value); - *location = Magic.addressOf(value); - } - - /// - /// In the sliding views phase, where some threads may have - /// scanned their roots and others have not, we need to ensure - /// that both old and new values will be marked and scanned. - /// In the tracing phase we only need to ensure that the old - /// values are traced and marked, as the old values may be the - /// only references to a part of the snapshot reachable object - /// graph from the untraced part of the object graph. - /// - /// The memory location being modified - /// The reference value to be written into - /// the "addr" location - [Inline] - private static void ReferenceCheck(UIntPtr *addr, Object value) - { -#if !SINGULARITY || CONCURRENT_MS_COLLECTOR - if (ConcurrentMSCollector.CurrentMarkingPhase == - ConcurrentMSCollector.MarkingPhase.ComputingRoots) { - UIntPtr oldValue = *addr; - MarkIfNecessary(oldValue); - MarkIfNecessary(Magic.addressOf(value)); - } else if (ConcurrentMSCollector.CurrentMarkingPhase == - ConcurrentMSCollector.MarkingPhase.Tracing) { - UIntPtr oldValue = *addr; - MarkIfNecessary(oldValue); - } -#endif // CONCURRENT_MS_COLLECTOR - } - - /// - /// Ensures that a reference value is going to be marked and - /// scanned. - /// - /// The reference value that may need to - /// be marked - [RequiredByBartok] - private static void MarkIfNecessary(UIntPtr value) { -#if !SINGULARITY || CONCURRENT_MS_COLLECTOR - if (PageTable.IsGcPage(PageTable.Page(value)) && - (ThreadHeaderQueue.GcMark(Magic.fromAddress(value)) != - ConcurrentMSCollector.markedColor)) { - VTable.Assert(PageTable.IsMyPage(PageTable.Page(value))); - Thread thread = Thread.CurrentThread; - ThreadHeaderQueue.Push(thread, - value, - ConcurrentMSCollector.markedColor, - ConcurrentMSCollector.unmarkedColor); - } -#endif // CONCURRENT_MS_COLLECTOR - } - - internal static void EnterSnoopingPhase() { - WriteBarrierCMS.isSnooping = true; - } - - internal static void LeaveSnoopingPhase() { - WriteBarrierCMS.isSnooping = false; - } - - internal static bool InSnoopingPhase { - get { - return WriteBarrierCMS.isSnooping; - } - } - - /// - /// Ensures that an object is going to be marked and scanned. - /// - /// The object that may need to be marked - internal static void MarkObject(Object obj) { -#if !SINGULARITY || CONCURRENT_MS_COLLECTOR - MarkIfNecessary(Magic.addressOf(obj)); -#endif // CONCURRENT_MS_COLLECTOR - } - - private static bool isSnooping; - - } - -} diff --git a/base/Kernel/Bartok/Headers.cs b/base/Kernel/Bartok/Headers.cs deleted file mode 100644 index 2081949..0000000 --- a/base/Kernel/Bartok/Headers.cs +++ /dev/null @@ -1,46 +0,0 @@ -/*******************************************************************/ -/* WARNING */ -/* This file should be identical in the Bartok and Singularity */ -/* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ -/*******************************************************************/ - -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -namespace Microsoft.Bartok.Runtime { - - using System; - using System.Runtime.CompilerServices; - - [RequiredByBartok] - internal struct PreHeader { - - internal MultiUseWord muw; - - [Intrinsic] - internal static int Size; - - } - - [RequiredByBartok] - internal struct PostHeader { - -#if (REFERENCE_COUNTING_GC || DEFERRED_REFERENCE_COUNTING_GC) - internal uint refState; -#endif - -#if SINGULARITY - [AccessedByRuntime("accessed from halexn.cpp")] -#else - [RequiredByBartok] -#endif - internal VTable vtableObject; - - [Intrinsic] - internal static int Size; - - } - -} diff --git a/base/Kernel/Bartok/Ilhelpers.il b/base/Kernel/Bartok/Ilhelpers.il deleted file mode 100644 index 3cdee7b..0000000 --- a/base/Kernel/Bartok/Ilhelpers.il +++ /dev/null @@ -1,102 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -/*******************************************************************/ -/* WARNING */ -/* This file should be identical in the Bartok and Singularity */ -/* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ -/*******************************************************************/ - -.assembly extern mscorlib -{ - .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. - .ver 1:0:5000:0 -} - -.assembly ILHelpers -{ -} -.module ILHelpers.dll -.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) -.imagebase 0x00400000 -.subsystem 0x00000003 -.file alignment 4096 -.corflags 0x00000001 -// Image base: 0x02c00000 - -// ============== CLASS STRUCTURE DECLARATION ================== - -.namespace System -{ - .class public auto ansi serializable beforefieldinit sealed ILHelpers - extends [mscorlib]System.Object - { - } // end of class ILHelpers - -} // end of namespace System - -// =============== CLASS MEMBERS DECLARATION =================== -// note that class flags, 'extends' and 'implements' clauses -// are provided here for information only - -.namespace System -{ - .class public auto ansi serializable beforefieldinit sealed ILHelpers - extends [mscorlib]System.Object - { - .method public hidebysig specialname static - void SetAt(object obj, - native unsigned int offset, - native unsigned int data) cil managed - { - //.maxstack 8 - .locals ([0] native unsigned int& dataPtr) - - // dataPtr = (native unsigned int&) obj - ldarg.0 - stloc.0 - - // dataPtr += offset - ldloc.0 - ldarg.1 - add - stloc.0 - - // *dataPtr = data - ldloc.0 - ldarg.2 - stind.i - - ret - } // end of method ILHelpers::SetAt - - .method public hidebysig specialname static - void SetAt(object obj, - native unsigned int offset, - object data) cil managed - { - //.maxstack 8 - .locals ([0] object& dataPtr) - - // dataPtr = (object&) obj - ldarg.0 - stloc.0 - - // dataPtr += offset - ldloc.0 - ldarg.1 - add - stloc.0 - - // *dataPtr = data - ldloc.0 - ldarg.2 - stind.ref - - ret - } // end of method ILHelpers::SetAt - } -} diff --git a/base/Kernel/Bartok/Magic.cs b/base/Kernel/Bartok/Magic.cs deleted file mode 100644 index b651786..0000000 --- a/base/Kernel/Bartok/Magic.cs +++ /dev/null @@ -1,92 +0,0 @@ -/*******************************************************************/ -/* WARNING */ -/* This file should be identical in the Bartok and Singularity */ -/* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ -/*******************************************************************/ - -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -namespace Microsoft.Bartok.Runtime { - - using System; - using System.Runtime.CompilerServices; - using System.Threading; - - internal sealed class Magic { - - internal static extern UIntPtr OffsetOfVTable { - [NoHeapAllocation] - [Intrinsic] - get; - } - - [Intrinsic] - [NoHeapAllocation] - internal static extern UIntPtr addressOf(Object o); - - [Intrinsic] - [NoHeapAllocation] - internal static unsafe extern UIntPtr *toPointer(ref Object o); - - [Intrinsic] - [NoHeapAllocation] - internal static unsafe extern UIntPtr *toPointer(ref VTable o); - - [Intrinsic] - [NoHeapAllocation] - internal static extern Object fromAddress(UIntPtr v); - - [Intrinsic] - [NoHeapAllocation] - internal static extern Thread toThread(Object o); - - [Intrinsic] - [NoHeapAllocation] - internal static extern Monitor toMonitor(Object o); - - [Intrinsic] - [NoHeapAllocation] - internal static extern EMU toEMU(Object o); - - [Intrinsic] - [NoHeapAllocation] - internal static extern VTable toVTable(Object o); - - [Intrinsic] - [NoHeapAllocation] - internal static extern Array toArray(Object o); - - [Intrinsic] - [NoHeapAllocation] - internal static extern String toString(Object o); - - [Intrinsic] - [NoHeapAllocation] - internal static extern RuntimeType toRuntimeType(Object o); - - [Intrinsic] - [NoHeapAllocation] - internal static extern Type toType(Object o); - - [Intrinsic] - [NoHeapAllocation] - internal static extern uint[] toUIntArray(Object o); - - [Intrinsic] - internal static extern WeakReference toWeakReference(Object o); - - [Intrinsic] - internal static extern void calli(System.UIntPtr p); - - [Intrinsic] - internal static extern void calli(System.UIntPtr p, System.UIntPtr v); - - [Intrinsic] - internal static extern void callFinalizer(Object o); - - } - -} diff --git a/base/Kernel/Bartok/Shared.cs b/base/Kernel/Bartok/Shared.cs deleted file mode 100644 index 8f3fc04..0000000 --- a/base/Kernel/Bartok/Shared.cs +++ /dev/null @@ -1,283 +0,0 @@ -/*******************************************************************/ -/* WARNING */ -/* This file should be identical in the Bartok and Singularity */ -/* depots. Master copy resides in Bartok Depot. Changes should be */ -/* made to Bartok Depot and propagated to Singularity Depot. */ -/*******************************************************************/ - -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// This code is used in both mscorlibOverride and for building applications -// (in particular Bartok). This makes sure the constant used in Bartok -// compiler and mscorlibOverride are the same - -using System; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; - -namespace Microsoft.Bartok.Runtime { - public enum StructuralType { - None = 0x00, -#if SINGULARITY - [AccessedByRuntime("Referenced from C++")] -#endif - Reference = 0x01, - UntracedPointer = 0x02, - Struct = 0x03, -#if SINGULARITY - [AccessedByRuntime("Referenced from C++")] -#endif - Bool = 0x04, -#if SINGULARITY - [AccessedByRuntime("Referenced from C++")] -#endif - Char = 0x05, -#if SINGULARITY - [AccessedByRuntime("Referenced from C++")] -#endif - Int8 = 0x06, -#if SINGULARITY - [AccessedByRuntime("Referenced from C++")] -#endif - Int16 = 0x07, -#if SINGULARITY - [AccessedByRuntime("Referenced from C++")] -#endif - Int32 = 0x08, -#if SINGULARITY - [AccessedByRuntime("Referenced from C++")] -#endif - Int64 = 0x09, -#if SINGULARITY - [AccessedByRuntime("Referenced from C++")] -#endif - UInt8 = 0x0a, -#if SINGULARITY - [AccessedByRuntime("Referenced from C++")] -#endif - UInt16 = 0x0b, -#if SINGULARITY - [AccessedByRuntime("Referenced from C++")] -#endif - UInt32 = 0x0c, -#if SINGULARITY - [AccessedByRuntime("Referenced from C++")] -#endif - UInt64 = 0x0d, -#if SINGULARITY - [AccessedByRuntime("Referenced from C++")] -#endif - Float32 = 0x0e, -#if SINGULARITY - [AccessedByRuntime("Referenced from C++")] -#endif - Float64 = 0x0f, -#if SINGULARITY - [AccessedByRuntime("Referenced from C++")] -#endif - IntPtr = 0x10, -#if SINGULARITY - [AccessedByRuntime("Referenced from C++")] -#endif - UIntPtr = 0x11, - Void = 0x12, - }; - - public enum GCType{ - AdaptiveCopyingCollector = 0x00, - MarkSweepCollector = 0x01, - SemispaceCollector = 0x02, - SlidingCollector = 0x03, - ReferenceCountingCollector = 0x04, - ConcurrentMSCollector = 0x05, - DeferredReferenceCountingCollector = 0x06, - NullCollector = 0x07, - AtomicRCCollector = 0x08, - TableMarkSweepCollector = 0x09, - }; - - public enum PTType{ - CentralPT = 0, - CentralPTHimem, - FlatDistributedPT, - FlatDistributedPTTest - }; - - public enum WBType { - noWB = 0, - Generational = 1, - CMS = 2, - ARC = 3, - AllCards = 4, - }; - - public enum RemSetType { - noRemSet = 0, - SSB = 1, - Cards = 2, - }; - - public enum CopyScanType { - noCopyScan = 0, - CheneyScan = 1, - HierarchicalScan = 2, - NestedHierarchicalScan = 3, - }; - - public class Constants { - public const int TypeTestDisplaySize = 6; - public const int TypeTestDisplayPosCache = TypeTestDisplaySize + 1; - public const bool TypeTestDisplayIncludesObject = false; - public const int TypeTestDisplayObjectOffset = - TypeTestDisplayIncludesObject ? 1 : 0; - public const int LargeObjectBits = 16; - - // constants used by GC activation descriptor table entry - public const int InbetweenSlotsNoFP = 1; - public const int InbetweenSlotsFP = 2; - - // shared by compact record - public const int CompactEntryMaskStart = 6; - public const int CompactArgMaskStart = 2; - public const int CompactArgMask = 0xf; - - // shared by full record - public const int FullEntryMaskStart = 1; - public const int FullRecordMask = 0x7; -#if X86 - public const int LastBitPos = 31; - // Compact record Frame pointer omitted - public const int CompactStackBitMaskStartNoFP = 24; - public const int CompactEntryMaskNoFP = 0x1f; - public const int CompactCalleeSaveUseStartNoFP = 11; - public const int CompactCalleeSaveUseMaskNoFP = 0xff; - public const int CompactFrameSizeStartNoFP = 19; - public const int CompactFrameSizeMaskNoFP = 0x1f; - - // Compact record Use frame pointer - public const int CompactStackBitMaskStartFP = 16; - public const int CompactEntryMaskFP = 0xf; - public const int CompactCalleeSaveUseStartFP = 10; - public const int CompactCalleeSaveUseMaskFP = 0x3f; - - // Full record Frame pointer omitted. - public const int FullEntryMaskNoFP = 0x1f; - public const int FullPinnedPosNoFP = 17; - public const int FullPinnedStartNoFP = 18; - public const int FullCalleeSaveUseStartNoFP = 6; - public const int FullCalleeSaveUseMaskNoFP = 0xff; - public const int FullRecordSizePosNoFP = 14; - public const int FullFrameSizeStartNoFP = 22; - - // Full record Use Frame pointer - public const int FullEntryMaskFP = 0xf; - public const int FullPinnedPosFP = 14; - public const int FullPinnedStartFP = 15; - public const int FullCalleeSaveUseStartFP = 5; - public const int FullCalleeSaveUseMaskFP = 0x3f; - public const int FullRecordSizePosFP = 11; -#elif AMD64 - public const int LastBitPos = 63; - // Compact record Frame pointer omitted - public const int CompactStackBitMaskStartNoFP = 36; - public const int CompactEntryMaskNoFP = 0x1ff; - public const int CompactCalleeSaveUseStartNoFP = 15; - public const int CompactCalleeSaveUseMaskNoFP = 0xffff; - public const int CompactFrameSizeStartNoFP =31; - public const int CompactFrameSizeMaskNoFP = 0x1f; - - // Compact record Use frame pointer - public const int CompactStackBitMaskStartFP = 28; - public const int CompactEntryMaskFP = 0xff; - public const int CompactCalleeSaveUseStartFP = 14; - public const int CompactCalleeSaveUseMaskFP = 0x3fff; - - // Full record Frame pointer omitted - public const int FullEntryMaskNoFP = 0x1ff; - public const int FullPinnedPosNoFP = 29; - public const int FullPinnedStartNoFP = 30; - public const int FullCalleeSaveUseStartNoFP = 10; - public const int FullCalleeSaveUseMaskNoFP = 0xffff; - public const int FullRecordSizePosNoFP = 26; - public const int FullFrameSizeStartNoFP = 38; - - // Full record Use Frame pointer - public const int FullEntryMaskFP = 0xff; - public const int FullPinnedPosFP = 26; - public const int FullPinnedStartFP = 27; - public const int FullCalleeSaveUseStartFP = 9; - public const int FullCalleeSaveUseMaskFP = 0x3fff; - public const int FullRecordSizePosFP = 23; -#endif - - // Workaround for lack of enum printing in Bartok -#if SINGULARITY - [AccessedByRuntime("Referenced from C++")] -#endif - public static string[] StructuralTypeNames = { - "None", - "Reference", - "UntracedPointer", - "Struct", - "Bool", - "Char", - "Int8", - "Int16", - "Int32", - "Int64", - "UnsignedInt8", - "UnsignedInt16", - "UnsignedInt32", - "UnsignedInt64", - "Float32", - "Float64", - "IntPtr", - "UIntPtr", - "Void", - }; - - // BUGBUG: what about entry for structs? -#if SINGULARITY - [AccessedByRuntime("Referenced from C++")] -#endif - public int[] arrayOfStride = { - 0, - 4, - 4, - 0, - 1, - 2, - 1, - 2, - 4, - 8, - 1, - 2, - 4, - 8, - 4, - 8, - 4, - 4, - 4, - }; - }; - -#if !BARTOK_SYSTEM_EXTENSION - [RequiredByBartok] -#endif - public enum TypeInitState { - Ready = 0, - Running = 1, - Failed = 2, - Completed = 3 - }; - - public enum StageControlOption { - TryAllSupport = 0, - InstrumentVirtualCalls = 1, - PInvoke = 2, - }; -} - diff --git a/base/Kernel/GC.targets b/base/Kernel/GC.targets index 9a790f8..84f5ff7 100644 --- a/base/Kernel/GC.targets +++ b/base/Kernel/GC.targets @@ -30,57 +30,69 @@ passing 'GCDefine' to the appropriate preprocessor. - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -91,18 +103,18 @@ passing 'GCDefine' to the appropriate preprocessor. /DWRITEBARRIER_SSB=1 - - - - - - - - - - - - + + + + + + + + + + + + @@ -113,10 +125,11 @@ passing 'GCDefine' to the appropriate preprocessor. /DWRITEBARRIER_CMS=1 - - - - + + + + + @@ -127,9 +140,21 @@ passing 'GCDefine' to the appropriate preprocessor. - - - + + + + + + + + NULL_COLLECTOR + /NullGC + NULL_COLLECTOR + + + + + @@ -140,16 +165,17 @@ passing 'GCDefine' to the appropriate preprocessor. /DWRITEBARRIER_SSB=1 - - - - - - - - - - + + + + + + + + + + + @@ -160,15 +186,15 @@ passing 'GCDefine' to the appropriate preprocessor. /DWRITEBARRIER_SSB=1 - - - - - - - - - + + + + + + + + + @@ -183,5 +209,5 @@ passing 'GCDefine' to the appropriate preprocessor. $(DefineConstants); $(DefineConstants)$(GCDefine) - + diff --git a/base/Kernel/Hal.Common.targets b/base/Kernel/Hal.Common.targets index fff10e3..ea57331 100644 --- a/base/Kernel/Hal.Common.targets +++ b/base/Kernel/Hal.Common.targets @@ -1,6 +1,14 @@ + + - + @@ -15,21 +23,32 @@ - - - - + + + + + + + + + + + + Microsoft.Singularity.Hal.Acpi.AmlParserUnions + + + + + + - + - + - - - diff --git a/base/Kernel/ILHelpers.proj b/base/Kernel/ILHelpers.proj deleted file mode 100644 index 3836d75..0000000 --- a/base/Kernel/ILHelpers.proj +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - ILHelpers - Library - $(KERNEL_IL_DIR) - - - - $(OutputPath)\$(AssemblyName).dll - - - - - - - - - - - - - - - - - - - - diff --git a/base/Kernel/Kernel.Corlib.csproj b/base/Kernel/Kernel.Corlib.csproj index 2f70f14..a9b04a5 100644 --- a/base/Kernel/Kernel.Corlib.csproj +++ b/base/Kernel/Kernel.Corlib.csproj @@ -1,248 +1,281 @@ - - + kernel Exe C# true - 169,649 + 169,649,436 2 SINGULARITY_KERNEL;ENDPOINT_STRUCT;_NEW_CLASSLOADER - false $(KERNEL_IL_DIR) true true {FA3325A6-5C21-475E-A600-51AD6163DFAA} + + $(SINGULARITY_ROOT)\Applications\Runtime\Full - - - - $(DefineConstants);PAGING - - - - - - - $(DefineConstants);AFFINITY_SCHEDULER - - - + + + $(DefineConstants);PAGING + + + + $(DefineConstants);SINGULARITY_LINKED_STACKS + $(DefineConstants);SINGULARITY_STACK_CHECKS + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + @@ -253,17 +286,31 @@ - + + + + + + + + - + + + + + + + + @@ -271,17 +318,13 @@ + + - - - - - - - - - + + + @@ -291,26 +334,9 @@ - - - - - - - - - - - - + - - - - - - @@ -325,42 +351,77 @@ + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + @@ -368,7 +429,6 @@ - @@ -378,19 +438,30 @@ + + + + + + + + + + + + - - + + - - + $(COLLECTOR_KERNEL) diff --git a/base/Kernel/Kernel.proj b/base/Kernel/Kernel.proj index e3017c2..5dbe7b2 100644 --- a/base/Kernel/Kernel.proj +++ b/base/Kernel/Kernel.proj @@ -1,50 +1,93 @@ - + + + + + + undrpl sdizepdb $(AFLAGS) - $(CFLAGS) /Gr /Oi /I..\boot\include /I..\Windows\Inc /I$(KERNEL_NATIVE_DIR) /I$(SINGULARITY_ROOT)\boot\include -UDEBUG - $(ACFLAGS) /DSINGULARITY=1 /DSINGULARITY_KERNEL=1 /I$(KERNEL_NATIVE_DIR) + $(CFLAGS) /Oi /I"$(KERNEL_NATIVE_DIR)" /I"$(SINGULARITY_ROOT)\boot\include" + $(ACFLAGS) /DSINGULARITY=1 /DSINGULARITY_KERNEL=1 /I"$(KERNEL_NATIVE_DIR)" $(ACFLAGS) /DSINGULARITY_MP=1 $(ACFLAGS) /DPAGING $(LINKFLAGS) /nodefaultlib /subsystem:native /OPT:REF /RELEASE /DLL - $(KERNEL_NATIVE_DIR)\obj - >$(KERNEL_NATIVE_DIR)\kernel.log - /fixed /map /base:0x310000 - /entry:Hal + /entry:HalEntryPoint + >"$(KERNEL_NATIVE_DIR)\kernel.log" + /fixed:no /map $(COLLECTOR_KERNEL) - $(BUILDDIR)\SyscallBuilder.exe + "$(BUILDDIR)\SyscallBuilder.exe" $(DEFAULT_SYSCALL_BUILDER) + + kernel.$(Machine).manifest + + + + $(CFLAGS) /Gr + $(LBASEFLAGS) /base:0xC10000 + + + + $(BartokFlags) /PhoenixMirOpts=false + + $(BartokFlags) /MinOpt + $(BartokFlags) /nohiropt + $(BartokFlags) /PhoenixMirOpts=false + + + + $(CFLAGS) /QRarch5 /QRimplicit-import- + $(LBASEFLAGS) /base:0x80100000 /driver + + + + $(KERNEL_NATIVE_DIR)\halclass.h + $(KERNEL_NATIVE_DIR)\halclass.inc + $(KERNEL_NATIVE_DIR)\_halclass.h + $(KERNEL_NATIVE_DIR)\_halclass.inc + $(BartokFlags) /Singularity - $(BartokFlags) /verbosity:silence - $(BartokFlags) /LinkedStacksRequireExternalBound=true - $(BartokFlags) /LinkedStacksDumpBounds=true - $(BartokFlags) /BackEndComments=true - $(BartokFlags) /GCInlineAllocations=false + $(BartokFlags) /verbosity:perphase + $(BartokFlags) /GCInlineArrayAllocations=false + $(BartokFlags) /GCInlineFixedAllocations=false + $(BartokFlags) /GCIntrinsicFixedAllocations=false $(BartokFlags) /GCInlineWriteBarrier=false - $(BartokFlags) /GenAsmHeader="$(KERNEL_NATIVE_DIR)\halclass.inc" - $(BartokFlags) /GenCppHeader="$(KERNEL_NATIVE_DIR)\halclass.h" + $(BartokFlags) /GenAsmHeader="$(HalClassTmpIncFile)" + $(BartokFlags) /GenCppHeader="$(HalClassTmpHFile)" $(BartokFlags) /OmitFramePointer=false $(BartokFlags) /DebugInline=true $(BartokFlags) /UnnameTracedPtrs=true $(BartokFlags) /Warnings=true $(BartokFlags) /WholeProgram=true - $(BartokFlags) /GenCoffLineNumber=false + $(BartokFlags) /UseSegmentRegister=false + - $(BartokFlags) /IrSimpleInliner=false - + + $(BartokFlags) /IrSimpleInliner=false + $(BartokFlags) /CoalesceExceptionThrow=false + $(BartokFlags) /OptimizeLocals=false + + + + @@ -73,9 +116,14 @@ $(SINGULARITY_ROOT)\Kernel\Singularity.Hal.ApicMP.csproj - + - $(SINGULARITY_ROOT)\Kernel\Singularity.Hal.LegacyPC.csproj + $(SINGULARITY_ROOT)\Kernel\Singularity.Hal.Apic64.csproj + + + + + $(SINGULARITY_ROOT)\Kernel\Singularity.Hal.Omap3430.csproj @@ -85,15 +133,46 @@ - - - - $(BartokFlags) /SymbolicDebug=true - $(BartokFlags) /LinkedStacks=true - Native - - - + + + $(BartokFlags) /SymbolicDebug=true + $(BartokFlags) /StackOverflowChecks=true + $(BartokFlags) /LinkedStacks=true + + $(BartokFlags) /LinkedStacksDumpBounds=true + $(ACFLAGS) /DISA_IX=1 /DISA_IX86=1 /DPTR_SIZE_32=1 + $(AFLAGS) /I"$(SINGULARITY_ROOT)\kernel\native\ix" + $(AFLAGS) /I"$(SINGULARITY_ROOT)\kernel\native\ix86" + $(UFLAGS) + + + + $(BartokFlags) /SymbolicDebug=true + $(BartokFlags) /StackOverflowChecks=true + $(BartokFlags) /LinkedStacks=true + $(BartokFlags) /LinkedStacksRequireExternalBound=true + $(BartokFlags) /LinkedStacksDumpBounds=true + $(BartokFlags) /x64 + $(ACFLAGS) /DISA_IX=1 /DISA_IX64=1 /DPTR_SIZE_64=1 + $(AFLAGS) /I"$(SINGULARITY_ROOT)\kernel\native\ix" + $(AFLAGS) /I"$(SINGULARITY_ROOT)\kernel\native\ix64" + $(UFLAGS) -r:SI:SA -r:PA:PEA + + + + $(BartokFlags) /SymbolicDebug=true + + + + + $(BartokFlags) /TargetArch=ARM + $(BartokFlags) /EnableIrExposeAllocationCall=true + $(ACFLAGS) /DISA_ARM=1 /DPTR_SIZE_32=1 + $(AFLAGS) /I"$(SINGULARITY_ROOT)\kernel\native\ARM" + $(UFLAGS) -r:SI:SA + @@ -110,7 +189,6 @@ BuildDependentProjects; BuildKernelManifest; LinkKernel; - BuildKernelDmp; LinkTestApp; BuildTestPeManifest; $(BuildDependsOn); @@ -121,9 +199,9 @@ - + @@ -132,62 +210,63 @@ + + - + - - - - + - + + + + - + - + - + DependsOnTargets="CompileKernelObj;BuildNativeIncludes;BuildNativeLib"> - + + DependsOnTargets="CompileKernelObj;BuildNativeIncludes;BuildNativeLib;BuildAbiLib"> - - - + + + - - - - - + + + + @@ -207,71 +286,50 @@ Inputs="$(KERNEL_NATIVE_DIR)\sysentry.cpp;$(KERNEL_NATIVE_DIR)\syscalls.h" Outputs="$(KERNEL_NATIVE_DIR)\sysentry.obj" DependsOnTargets="CreateSyscallsSources"> - + - - - - - - - + + - + - - + - - - + Inputs="$(KERNEL_NATIVE_DIR)\SingularityStub.V1.dumpbin;Singularity\BspAbiStub.cs;$(KERNEL_NATIVE_DIR)\MpSyscalls.$(Machine)" + Outputs=""> + - + - - - - - + + + @@ -280,25 +338,25 @@ genabistub: $(KERNEL_NATIVE_DIR)\SingularityStub.V1.dumpbin \ Inputs="$(KERNEL_NATIVE_DIR)\SingularityStub.V1.dumpbin" Outputs="$(KERNEL_NATIVE_DIR)\MpSyscalls.h;$(KERNEL_NATIVE_DIR)\MpSyscalls.cpp;$(KERNEL_NATIVE_DIR)\MpSyscalls.obj" DependsOnTargets="BuildAbiDumpBin"> - - - + + + - + @@ -315,68 +373,53 @@ $(KERNEL_NATIVE_DIR)\MpSyscalls.x86: $(KERNEL_NATIVE_DIR)\MpSyscalls.obj $(KERNE > - + + + + DependsOnTargets="CompileKernelObj;BuildNativeIncludes;PreprocessAbiDef;BuildNativeLib;$(LinkKernelDependsOn)"> - - - + + - - - - - - - - - - - + - + - - - - - - + Inputs="$(KERNEL_NATIVE_DIR)\$(SING_DEF_FILE)" + Outputs="$(KERNEL_NATIVE_DIR)\Singularity.V1.$(Runtime).lib" + DependsOnTargets="PreprocessAbiDef"> + + + testpe.manifest + $(KERNEL_NATIVE_DIR)\testpe.$(Machine).manifest + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Native\ + Native\ + Native\ + Native\ + Native\ + Native\ + Native\ + Native\ + Native\ + Native\ + Native\ + Native\ + Native\ + Native\ + Native\ + Native\ + Native\ + Native\ + Native\ + Native\ix64\ + Native\ix86\ + Singularity\Isal\ + + + + BartokSharedArch\ + BartokSingularityArch\ + BartokSingularityArch\ + + + + + + + Native\ + + Native\ix\ + Native\ix\ + Native\ix\ + + Singularity\Isal\ix\ + Singularity\Isal\ix\ + Singularity\Isal\ix\ + + + + + + Native\ix64\ + Native\ix64\ + Native\ix64\ + Native\ix64\ + Native\ix64\ + Native\ix64\ + Native\ix64\ + + + + + + Native\ix86\ + Native\ix86\ + Native\ix86\ + Native\ix86\ + Native\ix86\ + Native\ix86\ + Native\ix86\ + Native\ix86\ + Native\ix86\ + Native\ix86\ + Native\ix86\ + Native\ix86\ + Native\ix86\ + + + + + + + Native\arm\ + Native\arm\ + Native\arm\ + Native\arm\ + Native\arm\ + Native\arm\ + Singularity\Isal\arm\ + Singularity\Isal\arm\ + Singularity\Isal\arm\ + Singularity\Isal\arm\ + Singularity\Isal\arm\ + + + + + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + Native\Arm\Crt\ + + + + + Native\ + Native\arm\ - + + Outputs="$(KERNEL_NATIVE_DIR)\%(NativeSource.OutputRelativeDir)\%(NativeSource.filename).obj" + DependsOnTargets="CompileKernelObj;BuildNativeIncludes;CreateNativeItems;MakeDirs"> + + Command="$(CC) $(CFLAGS) %(NativeSource.CFlags) $(ACFLAGS) $(GC_ML_DEFS) /I"$(KERNEL_NATIVE_DIR)" /INative /INative/$(Machine) /FAsc /Fa"$(KERNEL_NATIVE_DIR)\%(NativeSource.filename).lst" /Fd"$(KERNEL_NATIVE_DIR)\native.pdb" /Fo"$(KERNEL_NATIVE_DIR)\%(NativeSource.OutputRelativeDir)\%(NativeSource.filename).obj" /c "%(NativeSource.identity)""/> + Command="$(AS) $(AFLAGS) $(ACFLAGS) $(GC_ML_DEFS) /I"$(KERNEL_NATIVE_DIR)" /INative /INative/$(Machine) /I"$(BartokSrcDir)\runtime\Singularity\native\arch\$(BARTOK_MACHINE)" /Fl"$(KERNEL_NATIVE_DIR)\%(NativeSource.OutputRelativeDir)\%(NativeSource.filename).lst" /Fo"$(KERNEL_NATIVE_DIR)\%(NativeSource.OutputRelativeDir)\%(NativeSource.filename).obj" /c "%(NativeSource.identity)""/> - + + + + + Outputs="$(KERNEL_NATIVE_DIR)\%(NativeSource.OutputRelativeDir)\%(NativeSource.filename).i" + DependsOnTargets="CompileKernelObj;BuildNativeIncludes;CreateNativeItems;MakeDirs"> + Command="$(CC) /EP >"$(KERNEL_NATIVE_DIR)\%(NativeSource.OutputRelativeDir)\%(NativeSource.filename).i" $(CFLAGS) $(ACFLAGS) $(GC_ML_DEFS) /I"$(KERNEL_NATIVE_DIR)" /INative /FAsc /Fa"$(KERNEL_NATIVE_DIR)\%(NativeSource.OutputRelativeDir)\%(NativeSource.filename).lst" /Fd"$(KERNEL_NATIVE_DIR)\native.pdb" /Fo"$(KERNEL_NATIVE_DIR)\%(NativeSource.OutputRelativeDir)\%(NativeSource.filename).obj" /c "%(NativeSource.identity)""/> + Command="$(AS) /EP >"$(KERNEL_NATIVE_DIR)\%(NativeSource.OutputRelativeDir)\%(NativeSource.filename).i" $(AFLAGS) $(ACFLAGS) $(GC_ML_DEFS) /I"$(KERNEL_NATIVE_DIR)" /INative /Fl"$(KERNEL_NATIVE_DIR)\%(NativeSource.OutputRelativeDir)\%(NativeSource.filename).lst" /Fo"$(KERNEL_NATIVE_DIR)\%(NativeSource.OutputRelativeDir)\%(NativeSource.filename).obj" /c "%(NativeSource.identity)""/> - + + + + + + + + + + + + + + + + + + + - + + + + Command="$(BARTOK) $(BARTOK_COLLECTOR) $(BartokFlags) /out: "$(KernelObjPath)" @(Reference->'"%(identity)"',' ') $(KERNEL_BARTOK_LOG)"/> + + + + diff --git a/base/Kernel/Native/BootInfo.cpp b/base/Kernel/Native/BootInfo.cpp deleted file mode 100644 index 7d97074..0000000 --- a/base/Kernel/Native/BootInfo.cpp +++ /dev/null @@ -1,31 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: BootInfo.cpp -// -// Note: -// - -#include "hal.h" - -#if SINGULARITY_KERNEL - -////////////////////////////////////////////////////////////////////////////// -// -const Struct_Microsoft_Singularity_BootInfo *g_pBootInfo; - -////////////////////////////////////////////////////////////////////////////// -// -Struct_Microsoft_Singularity_BootInfo * -Struct_Microsoft_Singularity_BootInfo::g_HalGetBootInfo() -{ - return (Struct_Microsoft_Singularity_BootInfo *)g_pBootInfo; -} - -#endif // SINGULARITY_KERNEL - -// -///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/Buffer.cpp b/base/Kernel/Native/Buffer.cpp index 5ad18f4..f05249b 100644 --- a/base/Kernel/Native/Buffer.cpp +++ b/base/Kernel/Native/Buffer.cpp @@ -6,11 +6,12 @@ // // File: Buffer.cpp // -// Note: +// Note: Kernel & Process // #include "hal.h" +#if ISA_IX86 || ISA_IX64 void Class_System_Buffer::g_MoveMemory(uint8* dmem, uint8* smem, int size) { if (size % 64 == 0 && size >= 64 && (uintptr)dmem % 64 == 0 && (uintptr)smem % 64 == 0) { @@ -165,6 +166,7 @@ void Class_System_Buffer::g_ZeroMemory(uint8* dmem, int size) *dmem++ = 0; } } +#endif // ///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/DebugStub.cpp b/base/Kernel/Native/DebugStub.cpp index 6391baf..1407661 100644 --- a/base/Kernel/Native/DebugStub.cpp +++ b/base/Kernel/Native/DebugStub.cpp @@ -1,26 +1,17 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // -// DebugStub.cpp: runtime support for debugging +// File: DebugStub.cpp: runtime support for debugging // +// Note: Process Only // + #include "hal.h" -#if SINGULARITY_PROCESS -#if PAGING -int ccc; -extern "C" void __cdecl _pushStackMark(); -__declspec(naked) void Class_Microsoft_Singularity_DebugStub::g_Foo() -{ - __asm - { - ret - } -// ccc = *((int *)_pushStackMark); -} -#endif void Class_Microsoft_Singularity_DebugStub::g_Break() { - __asm int 3; + __debugbreak(); } -#endif diff --git a/base/Kernel/Native/Decimal.cpp b/base/Kernel/Native/Decimal.cpp index 982d09f..efc5613 100644 --- a/base/Kernel/Native/Decimal.cpp +++ b/base/Kernel/Native/Decimal.cpp @@ -6,7 +6,7 @@ // // File: Number.cpp // -// Note: +// Note: Kernel & Process // #include "hal.h" diff --git a/base/Kernel/Native/EventController.cpp b/base/Kernel/Native/EventController.cpp new file mode 100644 index 0000000..4d0cd64 --- /dev/null +++ b/base/Kernel/Native/EventController.cpp @@ -0,0 +1,570 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: EventController.cpp +// +// Note: Kernel & Process +// + +#include "hal.h" +#include "eventing.h" + +bool GetTracingHandles(UIntPtr * storageHandle, + UIntPtr * sourceHandle, + UIntPtr * eventTypeHandle); + +bool GetMonitoringHandles(UIntPtr * storageHandle, + UIntPtr * sourceHandle, + UIntPtr * eventTypeHandle); + +SOURCE_CONTROLLER SourceController = {NULL, NULL, NULL, NULL, NULL}; + +char * ConvertToChars(char * dst, bartok_char *src, int32 length) +{ + if (src != NULL) { + bartok_char *end = src + length; + + while (src < end) { + *dst++ = (uint8)*src++; + } + } + *dst++ = '\0'; + return dst; +} + +void ConvertToChars(char * dst, Class_System_String *arg) +{ + ConvertToChars(dst, &arg->m_firstChar, arg->m_stringLength); +} + +void +RegisterRepositoryStorage(PMEMORY_STORAGE storage) +{ + EV_ASSERT(SourceController.SourceRepository == NULL); + SourceController.SourceRepository = storage; + RegisterNativeTypes(); +} + +bool +RegisterStorage(PMEMORY_STORAGE storage) +{ + // Note the caller of this function needs to assure mutual exclusion + + if (SourceController.SourceRepository == NULL) { + + // The first storage registered needs to be the type repository + + RegisterRepositoryStorage(storage); + + } else { + + storage->Link = SourceController.StorageList; + SourceController.StorageList = storage; + } + + return true; +} + + +void +UnRegisterStorage(PMEMORY_STORAGE storage) +{ + // Note the caller of this function needs to assure mutual exclusion + + PMEMORY_STORAGE tmpStorage = SourceController.StorageList; + + EV_ASSERT(tmpStorage != NULL); + + if (tmpStorage == storage) { + + SourceController.StorageList = tmpStorage->Link; + + } else { + + while (tmpStorage->Link != storage) { + + tmpStorage = tmpStorage->Link; + } + + if (tmpStorage->Link == storage) { + + tmpStorage->Link = storage->Link; + } + } +} + +void +RegisterExternalController(PEXTERNAL_CONTROLLER_DESCRIPTOR controller) +{ + // Note the caller of this function needs to assure mutual exclusion + + controller->Link = SourceController.ExternalControllers; + SourceController.ExternalControllers = controller; +} + +void +UnRegisterExternalController(PEXTERNAL_CONTROLLER_DESCRIPTOR controller) +{ + // Note the caller of this function needs to assure mutual exclusion + + PEXTERNAL_CONTROLLER_DESCRIPTOR tmpController = SourceController.ExternalControllers; + + EV_ASSERT(tmpController != NULL); + + if (tmpController == controller) { + + SourceController.ExternalControllers = tmpController->Link; + + } else { + + while (tmpController->Link != controller) { + + tmpController = tmpController->Link; + } + + if (tmpController->Link == controller) { + + tmpController->Link = controller->Link; + } + } + + // Insert it to the free llist + + controller->Link = SourceController.FreeControllerList; + SourceController.FreeControllerList = controller; + +} + +void +RegisterQueryView(PQUERY_VIEW queryView) +{ + // Note the caller of this function needs to assure mutula exclusion + + queryView->Link = SourceController.QueryViews; + SourceController.QueryViews = queryView; +} + +void +UnRegisterQueryView(PQUERY_VIEW queryView) +{ + // Note the caller of this function needs to assure mutual exclusion + + PQUERY_VIEW tmpQueryView = SourceController.QueryViews; + + EV_ASSERT(tmpQueryView != NULL); + + if (tmpQueryView == queryView) { + + SourceController.QueryViews = tmpQueryView->Link; + + } else { + + while (tmpQueryView->Link != queryView) { + + tmpQueryView = tmpQueryView->Link; + } + + if (tmpQueryView->Link == queryView) { + + tmpQueryView->Link = queryView->Link; + } + } + + // Insert it to the free llist + + queryView->Link = SourceController.QueryViews; + SourceController.FreeQueryViews = queryView; + +} + +PQUERY_VIEW AllocateQueryView( ) +{ + if (SourceController.FreeQueryViews != NULL) { + + PQUERY_VIEW queryView = SourceController.FreeQueryViews; + SourceController.FreeQueryViews = queryView->Link; + RegisterQueryView(queryView); + return queryView; + } + + QUERY_VIEW source; + + PMEMORY_HEADER Entry = InternalLogFixedRecord( GetLocalRepositoryHandle(), + RECORD_EVENT_CONTROLLER, + 0, + &source, + sizeof(source)); + + if (Entry == NULL) { + return NULL; + } + + PQUERY_VIEW newSource = (PQUERY_VIEW)GetUserRecordStructure(Entry); + RegisterQueryView(newSource); + return newSource; +} + + + +bool +RegisterSource(PSOURCE_DESCRIPTOR newSource) +{ + // Note the caller of this function needs to assure mutual exclusion + + newSource->Link = (UIntPtr)SourceController.SourceDescriptors; + SourceController.SourceDescriptors = newSource; + + return true; +} + + +// +// ABI entries +// + +UIntPtr Class_Microsoft_Singularity_Eventing_LocalController:: + g_FetchLocalStorage() +{ + return (UIntPtr)GetLocalRepository(); +} + +bool Class_Microsoft_Singularity_Eventing_LocalController::g_SetRepositoryStorage(UIntPtr storageHandle) +{ + if (SourceController.SourceRepository != NULL) { + return false; + } + + RegisterRepositoryStorage(HANDLE_TO_STORAGE(storageHandle)); + return true; +} + + +UIntPtr Class_Microsoft_Singularity_Eventing_LocalController:: + g_RegisterEventDescriptorInternal(Class_System_String * eventName, + Class_System_String * eventDescription) +{ + return (UIntPtr)RegisterEventDescriptorImplementation(Class_Microsoft_Singularity_Eventing_DataType___string, + &eventName->m_firstChar, + eventName->m_stringLength, + &eventDescription->m_firstChar, + eventDescription->m_stringLength); +} + +UIntPtr Class_Microsoft_Singularity_Eventing_LocalController:: + g_RegisterEventFieldInternal(UIntPtr eventHandle, + Class_System_String * fieldName, + uint16 offset, + uint16 type) +{ + return (UIntPtr) RegisterFieldDescriptorImplementation( + Class_Microsoft_Singularity_Eventing_DataType___string, + HANDLE_TO_HEADER(eventHandle), + &fieldName->m_firstChar, + fieldName->m_stringLength, + offset, + type); +} + +UIntPtr Class_Microsoft_Singularity_Eventing_LocalController:: + g_RegisterEventGenericFieldInternal(UIntPtr eventHandle, + Class_System_String * fieldName, + uint16 offset, + uint16 size, + UIntPtr typeFieldDescriptor) +{ + return (UIntPtr)RegisterGenericFieldDescriptorImplementation( + Class_Microsoft_Singularity_Eventing_DataType___string, + HANDLE_TO_HEADER(eventHandle), + &fieldName->m_firstChar, + fieldName->m_stringLength, + offset, + size, + typeFieldDescriptor); +} + +UIntPtr Class_Microsoft_Singularity_Eventing_LocalController:: + g_RegisterEnumDescriptorInternal(Class_System_String * name, + uint16 type) +{ + return (UIntPtr)RegisterEnumDescriptorImplementation( + Class_Microsoft_Singularity_Eventing_DataType___string, + &name->m_firstChar, + name->m_stringLength, + type); +} + +UIntPtr Class_Microsoft_Singularity_Eventing_LocalController:: + g_RegisterValueDescriptorInternal(UIntPtr eventHandle, + Class_System_String * name, + uint64 value, + uint8 flagLetter) +{ + return (UIntPtr)RegisterValueDescriptorImplementation( + Class_Microsoft_Singularity_Eventing_DataType___string, + HANDLE_TO_HEADER(eventHandle), + &name->m_firstChar, + name->m_stringLength, + value, + flagLetter); +} + + +bool +Class_Microsoft_Singularity_Eventing_LocalController::g_RegisterStorageImpl(UIntPtr storageHandle) +{ + return RegisterStorage(HANDLE_TO_STORAGE(storageHandle)); +} +void +Class_Microsoft_Singularity_Eventing_LocalController::g_UnRegisterStorageImpl(UIntPtr storageHandle) +{ + UnRegisterStorage(HANDLE_TO_STORAGE(storageHandle)); +} + +UIntPtr +Class_Microsoft_Singularity_Eventing_LocalController::g_AllocateSourceHandleImpl( + Class_System_String * sourceName) +{ + SOURCE_DESCRIPTOR source = {NULL, 0}; + PVOID ExtendedBuffer; + + PMEMORY_HEADER Entry = InternalLogRecord( GetLocalRepositoryHandle(), + RECORD_EVENT_SOURCE, + 0, + &source, + sizeof(source), + &ExtendedBuffer, + sourceName->m_stringLength + 1); + + if (Entry != NULL) { + + PSOURCE_DESCRIPTOR newSource = (PSOURCE_DESCRIPTOR)GetUserRecordStructure(Entry); + + ConvertToChars((char *)ExtendedBuffer, sourceName); + RegisterSource(newSource); + CommitEventEntry(Entry); + } + + return (UIntPtr)Entry; +} + +PSOURCE_DESCRIPTOR +GetSourceFromHandle(UIntPtr sourceHandle) +{ + if (sourceHandle == 0) { + + return NULL; + } + + PMEMORY_HEADER entry = HANDLE_TO_HEADER(sourceHandle); + + if (entry->Flags == RECORD_EVENT_SOURCE) { + + return (PSOURCE_DESCRIPTOR)GetUserRecordStructure(entry); + } + return NULL; +} + +UIntPtr +AllocateNativeSourceHandle(char * sourceName) +{ + SOURCE_DESCRIPTOR source = {0,NULL,0,0,0,0,0,0}; + + PVOID ExtendedBuffer; + uint16 sourceLen = strlen(sourceName) + 1; + + PMEMORY_HEADER Entry = InternalLogRecord( GetLocalRepositoryHandle(), + RECORD_EVENT_SOURCE, + 0, + &source, + sizeof(source), + &ExtendedBuffer, + sourceLen); + + if (Entry != NULL) { + + PSOURCE_DESCRIPTOR newSource = (PSOURCE_DESCRIPTOR)GetUserRecordStructure(Entry); + memcpy(ExtendedBuffer, sourceName, sourceLen); + + RegisterSource(newSource); + CommitEventEntry(Entry); + } + + return (UIntPtr)Entry; +} + +UIntPtr +RegisterNativeSource(char * sourceName, UIntPtr storageHandle, uint32 controlFlags) +{ + UIntPtr sourceHandle = AllocateNativeSourceHandle(sourceName); + + if (sourceHandle != 0) { + + Class_Microsoft_Singularity_Eventing_LocalController::g_RegisterSourceStorageImpl( + sourceHandle, storageHandle, controlFlags); + + if (storageHandle != 0) { + + RegisterStorage(HANDLE_TO_STORAGE(storageHandle)); + } + + } + + return sourceHandle; +} + +void +Class_Microsoft_Singularity_Eventing_LocalController::g_RegisterSourceStorageImpl( + UIntPtr sourceHandle, + UIntPtr storageHandle, + uint32 controlFlags) +{ + PSOURCE_DESCRIPTOR Source; + + Source = (PSOURCE_DESCRIPTOR)GetUserRecordStructure(HANDLE_TO_HEADER(sourceHandle)); + + if (Source) { + Source->StorageHandle = storageHandle; + Source->EventTypeHandle = 0; + Source->DebuggerBufferAddress = 0; + Source->Count = 0; + Source->EntrySize = 0; + Source->ControlFlags = controlFlags; + } +} + +void +Class_Microsoft_Singularity_Eventing_LocalController::g_RegisterActiveSourceImpl( + UIntPtr sourceHandle, + UIntPtr eventTypeHandle, + UIntPtr debuggerBufferAddress, + uint16 count, + uint16 entrySize) +{ + PSOURCE_DESCRIPTOR Source; + + Source = (PSOURCE_DESCRIPTOR)GetUserRecordStructure(HANDLE_TO_HEADER(sourceHandle)); + + if (Source) { + + Source->StorageHandle = 0; + Source->EventTypeHandle = eventTypeHandle; + Source->DebuggerBufferAddress = debuggerBufferAddress; + Source->Count = count; + Source->EntrySize = entrySize; + Source->ControlFlags = 0xFFFF0000; + } +} + +void +Class_Microsoft_Singularity_Eventing_LocalController::g_UnRegisterSourceImpl( + UIntPtr sourceHandle) +{ + PSOURCE_DESCRIPTOR Source; + + Source = (PSOURCE_DESCRIPTOR)GetUserRecordStructure(HANDLE_TO_HEADER(sourceHandle)); + + if (Source) { + + EV_ASSERT(Source->StorageHandle == 0); + Source->StorageHandle = 0; + } +} + + +bool Class_Microsoft_Singularity_Eventing_LocalController:: + g_GetControllerHandle(UIntPtr *storageHandle, UIntPtr *contextHandle) +{ + // ????? Some correct values are needed that would allow switching to the right context in debugger + // Note these values are not being accessed programatically. Correct contracts should be established + // to query entries cross SIPs + + *storageHandle = (UIntPtr)&SourceController; + *contextHandle = 0; + return true; +} + + +bool Class_Microsoft_Singularity_Eventing_LocalController:: +g_QueryNativeSourceInfo(UIntPtr sourceHandle, + UIntPtr * storageHandle, + bartok_char * bufferName, + uint16 bufferSize) +{ + PMEMORY_HEADER Entry = HANDLE_TO_HEADER(sourceHandle); + PSOURCE_DESCRIPTOR source = (PSOURCE_DESCRIPTOR)GetUserRecordStructure(Entry); + + *storageHandle = source->StorageHandle; + + char * src = (char *)(source + 1); + + if ((bufferName != NULL) && (bufferSize != 0)) { + + while ((bufferSize != 0) && (*src)) { + + *bufferName++ = *src++; + bufferSize -= sizeof(bartok_char); + } + + if (bufferSize == 0) { + + // Move back one position to insert the null terminator. + // We have at least this character in the buffer due to the test two levels above + + bufferSize -= sizeof(bartok_char); + } + + *bufferName = 0; + } + + return true; +} + + +int Class_Microsoft_Singularity_Eventing_LocalController:: +g_QuerySystemSources(UIntPtr * sourceHandles, + uint16 arraySize) +{ + + int totalSources = 0; + + PSOURCE_DESCRIPTOR source = SourceController.SourceDescriptors; + + while (source != NULL) { + + if (totalSources < arraySize) { + + sourceHandles[totalSources] = (UIntPtr)((PMEMORY_HEADER)source - 1); + } + + source = (PSOURCE_DESCRIPTOR)source->Link; + totalSources += 1; + } + + return totalSources; +} + +bool Class_Microsoft_Singularity_Eventing_Controller:: + g_GetSharedSourceHandlesInternal(uint32 infoId, + UIntPtr * storageHandle, + UIntPtr * sourceHandle, + UIntPtr * eventTypeHandle) +{ + switch (infoId) { + case Class_Microsoft_Singularity_Eventing_Controller_TracingInfo: + return GetTracingHandles(storageHandle, sourceHandle, eventTypeHandle); + + case Class_Microsoft_Singularity_Eventing_Controller_MonitoringInfo: + return GetMonitoringHandles(storageHandle, sourceHandle, eventTypeHandle); + } + + return false; +} + + + +// +///////////////////////////////////////////////////////////////// End of File. + diff --git a/base/Kernel/Native/EventDescriptor.cpp b/base/Kernel/Native/EventDescriptor.cpp new file mode 100644 index 0000000..7a15e65 --- /dev/null +++ b/base/Kernel/Native/EventDescriptor.cpp @@ -0,0 +1,537 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: EventDescriptor.cpp +// +// Note: Kernel & Process +// + +#include "hal.h" +#include "eventing.h" + +#ifdef SINGULARITY_KERNEL +extern UIntPtr MonitoringTypeHandle; +extern UIntPtr TracingTypeHandle; +#endif + + +#define _LOGGING_TO_REPOSITORY + +#define DECLARE_STRUCTURE_BEGIN(x,d) UIntPtr Handle_##x; +#define DECLARE_ENUM_BEGIN(x,sz) UIntPtr Handle_##x; + + #include "SystemEvents.inl" + +#undef _LOGGING_TO_REPOSITORY + +uint16 GetFieldSize(uint16 type) +{ + if (type & EVENT_FIELD_TYPE_arrayType) { + + return sizeof(uint16); + } + + switch (type) { + + case EVENT_FIELD_TYPE_int8: + case EVENT_FIELD_TYPE_uint8: + return sizeof(uint8); + + case EVENT_FIELD_TYPE_int16: + case EVENT_FIELD_TYPE_uint16: + return sizeof(uint16); + + case EVENT_FIELD_TYPE_int32: + case EVENT_FIELD_TYPE_uint32: + return sizeof(uint32); + + case EVENT_FIELD_TYPE_int64: + case EVENT_FIELD_TYPE_uint64: + return sizeof(uint64); + + case EVENT_FIELD_TYPE_IntPtr: + case EVENT_FIELD_TYPE_UIntPtr: + return sizeof(UIntPtr); + } + + return 0; +} + + +uint16 GetFixedTypeSize(PEVENT_DESCRIPTOR typeEntry) +{ + PEVENT_FIELD_DESCRIPTOR field; + uint16 size = 0; + + for (field = typeEntry->fieldsLink; field != NULL; field = field->fieldsLink) { + + uint16 fieldSize = GetFieldSize(field->Type); + + if (size < (field->Offset + fieldSize)) { + + size = field->Offset + fieldSize; + } + } + + return size; +} + +char * GetExtendedString(UIntPtr EntryHandle, int index) +{ + PMEMORY_HEADER entry = HANDLE_TO_HEADER(EntryHandle); + char * endPtr = (char *)(entry) + entry->Size; + + int entrySize; + + switch (entry->Flags) { + + case RECORD_EVENT_TYPE: + entrySize = sizeof(EVENT_DESCRIPTOR); + break; + + case RECORD_EVENT_FIELD: + entrySize = sizeof(EVENT_FIELD_DESCRIPTOR); + break; + + case RECORD_EVENT_GENERIC_FIELD: + entrySize = sizeof(EVENT_GENERIC_TYPE_DESCRIPTOR); + break; + + case RECORD_EVENT_VALUE: + entrySize = sizeof(EVENT_VALUE_DESCRIPTOR); + break; + + case RECORD_EVENT_ENUM: + entrySize = sizeof(ENUM_DESCRIPTOR); + break; + + default : { + + PEVENT_DESCRIPTOR typeEntry = HANDLE_TO_TYPE(entry->Type); + entrySize = GetFixedTypeSize(typeEntry); + } + } + + + void * base = GetUserRecordStructure(entry); + + char * crtPtr = (char *)base + entrySize; + + crtPtr = (char *)ROUND_UP_TO_POWER2(crtPtr, sizeof(int)); + if (crtPtr >= endPtr) { + return NULL; + } + + for (int i = 1; i < index; i++) { + + uint16 length; + + // copy the structure localy since it may not be aligned. + + memcpy(&length, crtPtr, sizeof(uint16)); + crtPtr += length + sizeof(uint16); + if (crtPtr >= endPtr) { + return NULL; + } + } + + char * ptr = crtPtr + sizeof(uint16); // skip also the size of the string + if (ptr >= endPtr) { + return NULL; + } + return ptr; +} + + + +// +// Shared routines to handle both unicode and ascii names for eventing +// + +PMEMORY_HEADER +RegisterEventDescriptorImplementation(int stringType, + PVOID name, + uint16 nameLength, + PVOID description, + uint16 descriptionLength) +{ + EVENT_DESCRIPTOR Event = {NULL, 0, 1, 2}; + + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {nameLength, + sizeof(char), + stringType, + name}, + {descriptionLength, + sizeof(char), + stringType, + description}}; + + if (descriptionLength == 0) { + + Event.Description = 0; + } + + PMEMORY_HEADER Entry = InternalLogVariableRecord( true, + GetLocalRepositoryHandle(), + RECORD_EVENT_TYPE, + 0, + &Event, + sizeof(Event), + (descriptionLength) ? 2 : 1, + array); + + return Entry; + +} + +PMEMORY_HEADER +RegisterFieldDescriptorImplementation(int stringType, + PMEMORY_HEADER Descriptor, + PVOID name, + uint32 nameLength, + uint16 offset, + uint16 type ) +{ + PEVENT_DESCRIPTOR eventDescriptor = (PEVENT_DESCRIPTOR)GetUserRecordStructure(Descriptor); + int fieldSize = GetFieldSize(type); + + if (offset == 0) { + + offset = eventDescriptor->Size; + + // + // Align naturaly the field + // + + offset = (uint16)ROUND_UP_TO_POWER2(offset, fieldSize); + } + + EV_ASSERT(offset == ROUND_UP_TO_POWER2(offset, fieldSize)); + EV_ASSERT(offset >= eventDescriptor->Size); + + EVENT_FIELD_DESCRIPTOR Field = {NULL, offset, type, NULL}; + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {nameLength, + sizeof(char), + stringType, + name}}; + + PMEMORY_HEADER Entry = InternalLogVariableRecord( false, + GetLocalRepositoryHandle(), + RECORD_EVENT_FIELD, + (UIntPtr)Descriptor, + &Field, + sizeof(Field), + 1, + array); + + if (Entry != NULL) { + + PEVENT_FIELD_DESCRIPTOR newField = (PEVENT_FIELD_DESCRIPTOR)GetUserRecordStructure(Entry); + + newField->fieldsLink = eventDescriptor->fieldsLink; + eventDescriptor->fieldsLink = newField; + eventDescriptor->Size = offset + fieldSize; + + CommitEventEntry(Entry); + } + + return Entry; +} + +PMEMORY_HEADER +RegisterGenericFieldDescriptorImplementation( int stringType, + PMEMORY_HEADER event, + PVOID name, + uint32 nameLength, + uint16 offset, + uint16 size, + UIntPtr typeFieldDescriptor) +{ + PEVENT_DESCRIPTOR eventDescriptor = (PEVENT_DESCRIPTOR)GetUserRecordStructure(event); + + if (offset == 0) { + + offset = eventDescriptor->Size; + + // + // Align naturaly the field + // + + offset = (uint16)ROUND_UP_TO_POWER2(offset, size); + } + + EV_ASSERT(offset == ROUND_UP_TO_POWER2(offset, size)); + EV_ASSERT(offset >= eventDescriptor->Size); + + EVENT_GENERIC_TYPE_DESCRIPTOR Field = {NULL, + offset, + GENERIC_TYPE_SIGNATURE, + size, + typeFieldDescriptor, + 1}; + + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {nameLength, + sizeof(char), + stringType, + name}}; + + PMEMORY_HEADER Entry = InternalLogVariableRecord( false, + GetLocalRepositoryHandle(), + RECORD_EVENT_GENERIC_FIELD, + (UIntPtr)event, + &Field, + sizeof(Field), + 1, + array); + + if (Entry != NULL) { + + PEVENT_FIELD_DESCRIPTOR newField = (PEVENT_FIELD_DESCRIPTOR)GetUserRecordStructure(Entry); + + newField->fieldsLink = eventDescriptor->fieldsLink; + eventDescriptor->fieldsLink = newField; + eventDescriptor->Size = offset + size; + + CommitEventEntry(Entry); + } + + return Entry; +} + +PMEMORY_HEADER +RegisterEnumDescriptorImplementation(int stringType, + PVOID name, + uint16 nameLength, + uint16 type) +{ + ENUM_DESCRIPTOR Event = {NULL, type, 0, 1, 2}; + + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {nameLength, + sizeof(char), + stringType, + name}}; + + PMEMORY_HEADER Entry = InternalLogVariableRecord( true, + GetLocalRepositoryHandle(), + RECORD_EVENT_ENUM, + 0, + &Event, + sizeof(Event), + 1, + array); + + return Entry; + +} + +PMEMORY_HEADER +RegisterValueDescriptorImplementation(int stringType, + PMEMORY_HEADER descriptor, + PVOID name, + uint32 nameLength, + uint64 value, + uint8 flagLetter) +{ + EVENT_VALUE_DESCRIPTOR Field = {NULL, flagLetter, value, NULL }; + + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {nameLength, + sizeof(char), + stringType, + name}}; + + PMEMORY_HEADER Entry = InternalLogVariableRecord( false, + GetLocalRepositoryHandle(), + RECORD_EVENT_VALUE, + (UIntPtr)descriptor, + &Field, + sizeof(Field), + 1, + array); + if (Entry != NULL) { + + PENUM_DESCRIPTOR eventDescriptor = (PENUM_DESCRIPTOR)GetUserRecordStructure(descriptor); + PEVENT_VALUE_DESCRIPTOR newField = (PEVENT_VALUE_DESCRIPTOR)GetUserRecordStructure(Entry); + + newField->fieldsLink = eventDescriptor->fieldsLink; + eventDescriptor->fieldsLink = newField; + + if (flagLetter) { + + eventDescriptor->FlagsMask |= value; + } + + CommitEventEntry(Entry); + } + + return Entry; +} + + + +PMEMORY_HEADER +RegisterEventDescriptor(PCHAR name, + uint16 nameLength, + PCHAR description, + uint16 descriptionLength) +{ + return RegisterEventDescriptorImplementation(EVENT_FIELD_TYPE_szChar, + name, + nameLength, + description, + descriptionLength); +} + +PMEMORY_HEADER +RegisterFieldDescriptor(PMEMORY_HEADER Descriptor, + PCHAR name, + uint32 nameLength, + uint16 offset, + uint16 type ) +{ + return RegisterFieldDescriptorImplementation( + EVENT_FIELD_TYPE_szChar, + Descriptor, + name, + nameLength, + offset, + type); +} + +PMEMORY_HEADER +RegisterGenericFieldDescriptor(PMEMORY_HEADER event, + PCHAR name, + uint32 nameLength, + uint16 offset, + uint16 size, + UIntPtr typeFieldDescriptor) +{ + return RegisterGenericFieldDescriptorImplementation( EVENT_FIELD_TYPE_szChar, + event, + name, + nameLength, + offset, + size, + typeFieldDescriptor); +} + +PMEMORY_HEADER +RegisterEnumDescriptor(PCHAR name, + uint16 nameLength, + uint16 type) +{ + return RegisterEnumDescriptorImplementation(EVENT_FIELD_TYPE_szChar, + name, + nameLength, + type); +} + +PMEMORY_HEADER +RegisterValueDescriptor(PMEMORY_HEADER descriptor, + PCHAR name, + uint32 nameLength, + uint64 value, + uint8 flagLetter) +{ + + return RegisterValueDescriptorImplementation(EVENT_FIELD_TYPE_szChar, + descriptor, + name, + nameLength, + value, + flagLetter); +} + + +// Declare the system event structures + +void +InitializeRegistrationSystem(void * buffer, size_t size) +{ + + // Create a bufffer which does not recycle memory + + UIntPtr LocalStorageHandle = Class_Microsoft_Singularity_Eventing_MemoryStorage:: + g_MemoryStorageCreateImpl(MEMORY_STORAGE_FLAGS_PERMANENT, (uint8 *)buffer, (uint32)size, 0); + + RegisterRepositoryStorage(HANDLE_TO_STORAGE(LocalStorageHandle)); +} + +void +RegisterNativeTypes() +{ +// +// Use this opportunity to validate the assumptions around the basic definition +// layouts +// + +#define _VALIDATE_INVARIANTS + +#define _LOGGING_TO_REPOSITORY + + +#define DECLARE_STRUCTURE_BEGIN(x,d) \ +{ \ + PMEMORY_HEADER __Event; \ + __Event = RegisterEventDescriptor("System."#x, sizeof("System."#x) - 1, d, sizeof(d)); \ + if (__Event != NULL) { \ + PMEMORY_HEADER __Field; \ + Handle_##x = (UIntPtr)__Event; + +#define DECLARE_ENUM_BEGIN(x,t) \ +{ \ + PMEMORY_HEADER __Event; \ + __Event = RegisterEnumDescriptor("System."#x, sizeof("System."#x) - 1, EVENT_FIELD_##t); \ + if (__Event != NULL) { \ + PMEMORY_HEADER __Field; \ + Handle_##x = (UIntPtr)__Event; + +#define DECLARE_STRUCTURE_END(x) }} + +#define DECLARE_ENUM_END DECLARE_STRUCTURE_END + +#define DECLARE_FIELD(s,t,n) \ + __Field = RegisterFieldDescriptor(__Event, #n, sizeof(#n), offsetof(s, n), EVENT_FIELD_##t); + +#define DECLARE_SPECIAL_FIELD(s,t,n) \ + __Field = RegisterFieldDescriptor(__Event, #n, sizeof(#n), offsetof(s, n), \ + EVENT_FIELD_TYPE_UIntPtr); + +#define DECLARE_EXTENDED_ARRAY_FIELD(s,t,n) \ + __Field = RegisterFieldDescriptor(__Event, #n, sizeof(#n), offsetof(s, n), \ + EVENT_FIELD_TYPE_arrayType | EVENT_FIELD_##t); + +#define DECLARE_VALUE(s,v,f,n) \ + __Field = RegisterValueDescriptor(__Event, #n, sizeof(#n), v, f); + +#define DECLARE_GENERIC_FIELD(s,t,sz,t1,n) \ + __Field = RegisterGenericFieldDescriptor(__Event, #n, sizeof(#n), offsetof(s, n), sz, Handle_##t1); + + #include "SystemEvents.inl" + +#undef _LOGGING_TO_REPOSITORY + +#ifdef SINGULARITY_KERNEL + + // + // For apps, discard the use of handles that have been registered, since these shared + // events are going to use the system handles + // + + MonitoringTypeHandle = Handle_MONITORING_ENTRY; + TracingTypeHandle = Handle_LEGACY_LOG_ENTRY; + +#endif +} + + +// +///////////////////////////////////////////////////////////////// End of File. + diff --git a/base/Kernel/Native/EventingKernel.cpp b/base/Kernel/Native/EventingKernel.cpp new file mode 100644 index 0000000..ab450a9 --- /dev/null +++ b/base/Kernel/Native/EventingKernel.cpp @@ -0,0 +1,431 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: EventController.cpp +// +// Note: Kernel & Process +// + +#include "hal.h" +#include "eventing.h" + +extern SOURCE_CONTROLLER SourceController; + +#include "csformat.inc" + +extern int strformat(void (*pfOutput)(void *pContext, char c), void *pContext, + const char * pszFmt, va_list args); + + +struct SNPRINT_CONTEXT { + + char ** CrtPos; + char * EndBuffer; +}; + +static void snprintfout(void *pContext, char c) +{ + SNPRINT_CONTEXT * context = (SNPRINT_CONTEXT *)pContext; + + if (*(context->CrtPos) < context->EndBuffer) { + + *(*context->CrtPos)++ = c; + } +} + +int snprintf(char *pszOut, int outSize, const char *pszFmt, ...) +{ + int nOut; + va_list args; + SNPRINT_CONTEXT context = {&pszOut, pszOut + outSize - 1}; + + va_start(args, pszFmt); + nOut = strformat(snprintfout, &context, pszFmt, args); + va_end(args); + + *pszOut = '\0'; + + return nOut; +} + +extern void kdprints(const char * pszFmt); + +PEVENT_FIELD_DESCRIPTOR GetDescriptorField(PEVENT_DESCRIPTOR typeEntry, int fieldIndex) +{ + PEVENT_FIELD_DESCRIPTOR field; + + // + // TODO: This needs to be optimized caching the total number of fields + // + + int numFields = 0; + + for (field = typeEntry->fieldsLink; (field != NULL); field = field->fieldsLink) { + + numFields++; + } + + fieldIndex = numFields - fieldIndex - 1; + + for (field = typeEntry->fieldsLink; (field != NULL) && (fieldIndex > 0); field = field->fieldsLink) { + + fieldIndex--; + } + + return field; +} + + +PEVENT_FIELD_DESCRIPTOR FindFieldDescriptor(PEVENT_DESCRIPTOR typeEntry, int offset) +{ + PEVENT_FIELD_DESCRIPTOR field; + + for (field = typeEntry->fieldsLink; field != NULL; field = field->fieldsLink) { + + if (field->Offset == offset) { + + return field; + } + } + return NULL; +} + + +uint64 ReadValueAs(void * baseAddress, int offset, int type) +{ + void * memLocation = (char *)baseAddress + offset; + + if (type & EVENT_FIELD_TYPE_arrayType) { + + return *(uint16 *)memLocation; + } + + switch (type) { + + case EVENT_FIELD_TYPE_int8: + case EVENT_FIELD_TYPE_uint8: + return *(uint8 *)memLocation; + + case EVENT_FIELD_TYPE_int16: + case EVENT_FIELD_TYPE_uint16: + return *(uint16 *)memLocation; + + case EVENT_FIELD_TYPE_int32: + case EVENT_FIELD_TYPE_uint32: + return *(uint32 *)memLocation; + + case EVENT_FIELD_TYPE_int64: + case EVENT_FIELD_TYPE_uint64: + return *(uint64 *)memLocation; + + case EVENT_FIELD_TYPE_IntPtr: + case EVENT_FIELD_TYPE_UIntPtr: + return (uint64)(*(UIntPtr *)memLocation); + } + + return 0; +} + +uint64 GetFieldValue(UIntPtr EntryHandle, int fieldIndex) +{ + PMEMORY_HEADER entry = HANDLE_TO_HEADER(EntryHandle); + PEVENT_DESCRIPTOR typeEntry = HANDLE_TO_TYPE(entry->Type); + PEVENT_FIELD_DESCRIPTOR field = GetDescriptorField(typeEntry, fieldIndex); + + void * base = GetUserRecordStructure(entry); + + if (field != NULL) { + + return ReadValueAs(GetUserRecordStructure(entry), field->Offset, field->Type); + } + + return 0; +} + +struct PRINT_EVENT_CONTEXT { + + UIntPtr EntryHandle; + int ArgOffset; +}; + +int WriteSymbolicValue(char *pszOut, int bufferSize, PENUM_DESCRIPTOR enumDescriptor, uint64 value) +{ + PEVENT_VALUE_DESCRIPTOR field; + int retValue = 0; + + uint64 flags = value & enumDescriptor->FlagsMask; + uint64 item = value & ~enumDescriptor->FlagsMask; + + for (field = enumDescriptor->fieldsLink; field != NULL; field = field->fieldsLink) { + + int ret = 0; + + if ((field->FlagLetter != 0)) { + + // + // This needs to be elaborated further with using a string for the false case too + // + + char symbol = ((field->Value & flags) == field->Value) ? field->FlagLetter : '.'; + + ret = snprintf(pszOut, bufferSize, "%c", symbol); + + } else if (field->Value == item) { + + char * strValue = GetExtendedString((UIntPtr)((PMEMORY_HEADER)field - 1), 1); + + ret = snprintf(pszOut, bufferSize, "%s", strValue); + + } + + // + // Advance the output buffer, updating the current number of bytes written + // + + retValue += ret; + pszOut += ret; + bufferSize -= ret; + } + + return retValue; + +} + +char * GetStringField(void * context, int argIdx) +{ + PRINT_EVENT_CONTEXT * eventContext = (PRINT_EVENT_CONTEXT*) context; + + argIdx += eventContext->ArgOffset; + + PMEMORY_HEADER entry = HANDLE_TO_HEADER(eventContext->EntryHandle); + PEVENT_DESCRIPTOR typeEntry = HANDLE_TO_TYPE(entry->Type); + + PEVENT_FIELD_DESCRIPTOR field = GetDescriptorField(typeEntry, argIdx); + + if (field->Type & (Class_Microsoft_Singularity_Eventing_DataType___string | + Class_Microsoft_Singularity_Eventing_DataType___szChar)) { + + int extendedIndex = (int)GetFieldValue(eventContext->EntryHandle, argIdx); + + if (extendedIndex > 0) { + + char * str = GetExtendedString(eventContext->EntryHandle, extendedIndex); + + // + // From this point all formating is relative to this string + // + + eventContext->ArgOffset = argIdx + 1; + + return str; + } + } + + return NULL; +} + +int PrintEventField(void * context, char *pszOut, int bufferSize, int aln, int wid, char fmt, int argIdx) +{ + PRINT_EVENT_CONTEXT * eventContext = (PRINT_EVENT_CONTEXT*) context; + argIdx += eventContext->ArgOffset; + + PMEMORY_HEADER entry = HANDLE_TO_HEADER(eventContext->EntryHandle); + PEVENT_DESCRIPTOR typeEntry = HANDLE_TO_TYPE(entry->Type); + + PEVENT_FIELD_DESCRIPTOR field = GetDescriptorField(typeEntry, argIdx); + + if (field == NULL) return 0; + + int retValue = 0; + char * str = NULL; + + if (field->Type == GENERIC_TYPE_SIGNATURE) { + + // + // This might be a nested structure or an enum. The process of retrieving the + // actual value becomes a bit more complicated. The existing entry will be placed + // at a known offset in the parent structure, but we cannot interpret the value unless + // we find out the descriptor for this field. + // + + if (((PMEMORY_HEADER)field - 1)->Flags == RECORD_EVENT_GENERIC_FIELD) { + + // + // After validating the descriptor version agains the nested type, we can safely + // cast to a generic field + // + + PEVENT_GENERIC_TYPE_DESCRIPTOR genericType = (PEVENT_GENERIC_TYPE_DESCRIPTOR)field; + UIntPtr typeHandle = genericType->GenericTypeHandle; + PEVENT_DESCRIPTOR descriptor = (PEVENT_DESCRIPTOR)GetUserRecordStructure(HANDLE_TO_HEADER(typeHandle)); + + if (((PMEMORY_HEADER)descriptor - 1)->Flags == RECORD_EVENT_ENUM) { + + // + // The field is an enum. Handle here the symbolic + // + + PENUM_DESCRIPTOR enumDescriptor = (PENUM_DESCRIPTOR)descriptor; + uint16 valueBasicType = enumDescriptor->Type; + uint64 value = ReadValueAs(GetUserRecordStructure(entry), field->Offset, valueBasicType); + + retValue = WriteSymbolicValue(pszOut, bufferSize, enumDescriptor, value); + + } else { + + retValue = snprintf(pszOut, bufferSize, "(unsupported field)"); + } + + } else { + + retValue = snprintf(pszOut, bufferSize, "(unknown)"); + } + + } else if (field->Type & + (Class_Microsoft_Singularity_Eventing_DataType___string | + Class_Microsoft_Singularity_Eventing_DataType___szChar)) { + + int extendedIndex = (int)GetFieldValue(eventContext->EntryHandle, argIdx); + + if (extendedIndex > 0) { + + char * str = GetExtendedString(eventContext->EntryHandle, extendedIndex); + + if (str != NULL) { + + retValue = snprintf(pszOut, bufferSize, "%s", str); + } else { + + retValue = snprintf(pszOut, bufferSize, "(invalid string index)"); + } + } + + } else { + + uint64 value = GetFieldValue(eventContext->EntryHandle, argIdx); + + if (aln < wid) { + aln = wid; + } + + if (fmt == 'x') { + if (wid > 0) { + retValue = snprintf(pszOut, bufferSize, "%0x", value); + } + else { + retValue = snprintf(pszOut, bufferSize, "%x", value); + } + } + else { + retValue = snprintf(pszOut, bufferSize, "%d", value); + } + + } + + return retValue; +} + +void DebugPrintEvent(UIntPtr eventHandle) +{ + char msg[256]; + PRINT_EVENT_CONTEXT printContext = {eventHandle, 0}; + + PMEMORY_HEADER entry = HANDLE_TO_HEADER(eventHandle); + char * frmt = GetExtendedString(entry->Type, 2); + + if (frmt != NULL) { + + FormatCSOutput(&printContext, frmt, msg, sizeof(msg),PrintEventField, GetStringField); + } + + bartok_char wmsg[256]; + int lMsg = strlen(msg); + + if (lMsg < (sizeof(msg) - 1)) { + + msg[lMsg] = '\n'; + lMsg += 1; + msg[lMsg] = 0; + } + + for (int i = 0; i < lMsg; i++) { + + wmsg[i] = msg[i]; + } + Class_Microsoft_Singularity_DebugStub::g_Print(wmsg, lMsg); + //kdprints(msg); +} + + +bool Class_Microsoft_Singularity_Eventing_KernelController:: + g_DebugPrintLogEntry(UIntPtr controllerHandle, UIntPtr entryHandle) +{ + // + // TODO: Handle the paging case with apporpriate access as + // + + DebugPrintEvent(entryHandle); + return true; +} + +bool Class_Microsoft_Singularity_Eventing_KernelController:: + g_RegisterExternalController(UIntPtr storageHandle, UIntPtr contextHandle) +{ + // The external caller is responsible with the synchronization + + EXTERNAL_CONTROLLER_DESCRIPTOR source = {NULL, storageHandle, contextHandle}; + + if (SourceController.FreeControllerList != NULL) { + + PEXTERNAL_CONTROLLER_DESCRIPTOR controller = SourceController.FreeControllerList; + SourceController.FreeControllerList = controller->Link; + *controller = source; + RegisterExternalController(controller); + + } else { + + PMEMORY_HEADER Entry = InternalLogFixedRecord( GetLocalRepositoryHandle(), + RECORD_EVENT_CONTROLLER, + 0, + &source, + sizeof(source)); + + if (Entry == NULL) { + return false; + } + + PEXTERNAL_CONTROLLER_DESCRIPTOR newSource = (PEXTERNAL_CONTROLLER_DESCRIPTOR)GetUserRecordStructure(Entry); + RegisterExternalController(newSource); + } + + return true; +} + +void Class_Microsoft_Singularity_Eventing_KernelController:: + g_UnRegisterExternalController(UIntPtr controllerHandle, UIntPtr contextHandle) +{ + // Note the caller of this function needs to assure mutual exclusion + + PEXTERNAL_CONTROLLER_DESCRIPTOR tmpController = SourceController.ExternalControllers; + + for (tmpController = SourceController.ExternalControllers; + tmpController != NULL; + tmpController = tmpController->Link) { + + if ((tmpController->ContextHandle == contextHandle) && + (tmpController->ControllerHandle == controllerHandle)){ + + UnRegisterExternalController(tmpController); + + return; + } + } +} + +// +///////////////////////////////////////////////////////////////// End of File. + + diff --git a/base/Kernel/Native/GCTracing.cpp b/base/Kernel/Native/GCTracing.cpp new file mode 100644 index 0000000..fb8083a --- /dev/null +++ b/base/Kernel/Native/GCTracing.cpp @@ -0,0 +1,148 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: GSTracing.cpp +// +// Note: Kernel & Process +// + +#include "hal.h" +#include "eventing.h" + +void Class_Microsoft_Singularity_GCProfilerLogger_ProfilerBuffer:: +g_LogFunction(uint32 funcNo, UIntPtr eip) +{ + GC_FUNCTION entry = {funcNo, eip}; + InternalLogFixedRecord((UIntPtr)Class_Microsoft_Singularity_GCProfilerLogger::c_TypeStorageHandle, + 0, Handle_GC_FUNCTION, &entry, sizeof(entry)); +} + + +void Class_Microsoft_Singularity_GCProfilerLogger_ProfilerBuffer:: +g_LogAllocation(int32 threadId, UIntPtr objectAddress, uint32 stkNo) +{ + GC_ALLOCATION entry = {threadId, stkNo, objectAddress}; + InternalLogFixedRecord((UIntPtr)Class_Microsoft_Singularity_GCProfilerLogger::c_StorageHandle, + 0, Handle_GC_ALLOCATION, &entry, sizeof(entry)); +} + +void Class_Microsoft_Singularity_GCProfilerLogger_ProfilerBuffer:: +g_LogStack(uint32 stackNo, uint32 typeNo, UIntPtr size, uint32 stackSize, uint32 * funcIDs) +{ + GC_STACK entry = {stackNo, typeNo, (uint32)size, 1}; + + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {(uint16)(stackSize * sizeof(uint32)), + sizeof(uint32), + Class_Microsoft_Singularity_Eventing_DataType___arrayType, + funcIDs}}; + + InternalLogVariableRecord(true, + (UIntPtr)Class_Microsoft_Singularity_GCProfilerLogger::c_TypeStorageHandle, + 0, + Handle_GC_STACK, + &entry, + sizeof(entry), + sizeof(array)/sizeof(array[0]), + array); +} + +void Class_Microsoft_Singularity_GCProfilerLogger_ProfilerBuffer:: +g_LogInterval(uint64 Delta) +{ + GC_INTERVAL entry = {Delta}; + InternalLogFixedRecord((UIntPtr)Class_Microsoft_Singularity_GCProfilerLogger::c_StorageHandle, + 0, Handle_GC_INTERVAL, &entry, sizeof(entry)); +} + + +void Class_Microsoft_Singularity_GCProfilerLogger_ProfilerBuffer:: +g_LogType(uint32 typeId, Class_System_String * typeName) +{ + GC_TYPE entry = {typeId, 1}; + + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {typeName->m_stringLength, + sizeof(char), + Class_Microsoft_Singularity_Eventing_DataType___string, + &typeName->m_firstChar}}; + + InternalLogVariableRecord(true, + (UIntPtr)Class_Microsoft_Singularity_GCProfilerLogger::c_TypeStorageHandle, + 0, + Handle_GC_TYPE, + &entry, + sizeof(entry), + sizeof(array)/sizeof(array[0]), + array); +} + +void Class_Microsoft_Singularity_GCProfilerLogger_ProfilerBuffer:: +g_LogObject(uint32 ArraySize, UIntPtr * objectParameters) +{ + GC_OBJECT entry = {1}; + + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {(uint16)(ArraySize * sizeof(UIntPtr)), + sizeof(UIntPtr), + Class_Microsoft_Singularity_Eventing_DataType___arrayType, + objectParameters}}; + + InternalLogVariableRecord(true, + (UIntPtr)Class_Microsoft_Singularity_GCProfilerLogger::c_StorageHandle, + 0, + Handle_GC_OBJECT, + &entry, + sizeof(entry), + sizeof(array)/sizeof(array[0]), + array); +} + +void Class_Microsoft_Singularity_GCProfilerLogger_ProfilerBuffer:: +g_LogGenerations(int maxGeneration, int * generations) +{ + GC_GENERATIONS entry = {1}; + + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {(uint16)(maxGeneration * sizeof(int32)), + sizeof(int32), + Class_Microsoft_Singularity_Eventing_DataType___arrayType, + generations}}; + + InternalLogVariableRecord(true, + (UIntPtr)Class_Microsoft_Singularity_GCProfilerLogger::c_StorageHandle, + 0, + Handle_GC_GENERATIONS, + &entry, + sizeof(entry), + sizeof(array)/sizeof(array[0]), + array); +} + +void Class_Microsoft_Singularity_GCProfilerLogger_ProfilerBuffer:: +g_LogRoots(uint32 ArraySize, UIntPtr * objectRoots) +{ + GC_ROOTS entry = {1}; + + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {(uint16)(ArraySize * sizeof(UIntPtr)), + sizeof(UIntPtr), + Class_Microsoft_Singularity_Eventing_DataType___arrayType, + objectRoots}}; + + InternalLogVariableRecord(true, + (UIntPtr)Class_Microsoft_Singularity_GCProfilerLogger::c_StorageHandle, + 0, + Handle_GC_ROOTS, + &entry, + sizeof(entry), + sizeof(array)/sizeof(array[0]), + array); +} + + +// +///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/HalKd1394.cpp b/base/Kernel/Native/HalKd1394.cpp index 0b206b6..5efdd32 100644 --- a/base/Kernel/Native/HalKd1394.cpp +++ b/base/Kernel/Native/HalKd1394.cpp @@ -1,7 +1,10 @@ +////////////////////////////////////////////////////////////////////////////// // -// Copyright (c) Microsoft Corporation. All rights reserved. +// Microsoft Research Singularity // -// halkd1394.cpp: runtime support for debugging over 1394. +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: halkd1394.cpp: runtime support for debugging over 1394. // // For more information see: // \nt\base\ntos\kd64 @@ -10,13 +13,13 @@ // \nt\base\boot\kdusb2 // \nt\sdktools\debuggers\ntsd64 // +// Note: Kernel Only +// + #include "hal.h" #include "halkd.h" #include "halkd1394.h" -extern "C" void * __cdecl memcpy(void *, const void *, size_t); -extern "C" void * __cdecl memset(void *, int, size_t); - // // Debugger Debugging // @@ -33,17 +36,17 @@ extern "C" void * __cdecl memset(void *, int, size_t); #define DEBUG_1394_CONFIG_TAG 0xBABABABA struct DEBUG_1394_SEND_PACKET { - ULONG TransferStatus; - ULONG PacketHeader[4]; - ULONG Length; + UINT32 TransferStatus; + UINT32 PacketHeader[4]; + UINT32 Length; UCHAR Packet[DEBUG_BUS1394_MAX_PACKET_SIZE]; UCHAR Padding[72]; }; STATIC_ASSERT(sizeof(DEBUG_1394_SEND_PACKET) == 4096); struct DEBUG_1394_RECEIVE_PACKET { - ULONG TransferStatus; - ULONG Length; + UINT32 TransferStatus; + UINT32 Length; UCHAR Packet[DEBUG_BUS1394_MAX_PACKET_SIZE]; UCHAR Padding[88]; }; @@ -51,19 +54,19 @@ STATIC_ASSERT(sizeof(DEBUG_1394_RECEIVE_PACKET) == 4096); // exists on target, host reads it to match for id. struct DEBUG_1394_CONFIG { - ULONG Tag; - USHORT MajorVersion; - USHORT MinorVersion; - ULONG Id; - ULONG BusPresent; - ULONG64 SendPacket; - ULONG64 ReceivePacket; + UINT32 Tag; + UINT16 MajorVersion; + UINT16 MinorVersion; + UINT32 Id; + UINT32 BusPresent; + UINT64 SendPacket; + UINT64 ReceivePacket; }; STATIC_ASSERT(sizeof(DEBUG_1394_CONFIG) == 32); struct DEBUG_1394_DATA { // POHCI_REGISTER_MAP BaseAddress; // our OHCI register map - ULONG CromBuffer[248]; // our config ROM - must be 1k aligned + UINT32 CromBuffer[248]; // our config ROM - must be 1k aligned DEBUG_1394_CONFIG Config; // our config for this session DEBUG_1394_SEND_PACKET SendPacket; // our send packet (isoch packet) DEBUG_1394_RECEIVE_PACKET ReceivePacket; // our receive packet @@ -89,13 +92,15 @@ static ULONG_PTR MmGetPhysicalAddress(PVOID pv) } // ?? need to look into this -static ULONG Dbg1394_StallExecution(ULONG LoopCount) +static UINT32 Dbg1394_StallExecution(UINT32 LoopCount) { - ULONG b = 1; + volatile UINT32 b = 1; - for (ULONG k = 0; k < LoopCount; k++) { - for (ULONG i = 1; i < 100000; i++) { + for (volatile UINT32 k = 0; k < LoopCount; k++) { + for (volatile UINT32 i = 1; i < 100000; i++) { +#ifdef ISA_IX86 __asm pause; +#endif b = b * (i>>k); } } @@ -104,7 +109,7 @@ static ULONG Dbg1394_StallExecution(ULONG LoopCount) } // Exchange byte pairs 0:3 and 1:2 of Source and returns the resulting ULONG. -static inline ULONG Dbg1394_ByteSwap(ULONG Source) +static inline UINT32 Dbg1394_ByteSwap(UINT32 Source) { return (((Source) << (8 * 3)) | ((Source & 0x0000FF00) << (8 * 1)) | @@ -114,14 +119,14 @@ static inline ULONG Dbg1394_ByteSwap(ULONG Source) // Derive the 16 bit CRC as defined by IEEE 1212 clause 8.1.5. // (ISO/IEC 13213) First edition 1994-10-05. -// data - ULONG data to derive CRC from. +// data - UINT32 data to derive CRC from. // check - check value. -static ULONG Dbg1394_Crc16(ULONG data, ULONG check) +static UINT32 Dbg1394_Crc16(UINT32 data, UINT32 check) { - ULONG next = check; + UINT32 next = check; - for (LONG shift = 28; shift >= 0; shift -= 4) { - ULONG sum = ((next >> 12) ^ (data >> shift)) & 0xf; + for (INT32 shift = 28; shift >= 0; shift -= 4) { + UINT32 sum = ((next >> 12) ^ (data >> shift)) & 0xf; next = (next << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum); } return (next & 0xFFFF); @@ -130,77 +135,75 @@ static ULONG Dbg1394_Crc16(ULONG data, ULONG check) // Calculate a CRC for the pointer to the Quadlet data. // Quadlet - Pointer to data to CRC // length - length of data to CRC -static USHORT Dbg1394_CalculateCrc(PULONG Quadlet, ULONG length) +static UINT16 Dbg1394_CalculateCrc(UINT32 *Quadlet, UINT32 length) { - ULONG temp = 0; + UINT32 temp = 0; - for (ULONG index = 0; index < length; index++) { + for (UINT32 index = 0; index < length; index++) { temp = Dbg1394_Crc16(Quadlet[index], temp); } - return (USHORT)temp; + return (UINT16)temp; } ////////////////////////////////////////////////// Initialize the controller! // -bool Kdp1394Init(Struct_Microsoft_Singularity_BootInfo *bi) +bool Kdp1394Init(UINT16 Channel, ULONG_PTR Base, ULONG_PTR BufferAddr32, UINT32 BufferSize32) { - if (bi->Ohci1394Base == 0 || - bi->Ohci1394BufferAddr32 == 0 || - bi->Ohci1394BufferSize32 < sizeof(DEBUG_1394_DATA)) { + if (Base == 0 || + BufferAddr32 == 0 || + BufferSize32 < sizeof(DEBUG_1394_DATA)) { - bi->Ohci1394Base = 0; - bi->Ohci1394BufferAddr32 = 0; - bi->Ohci1394BufferSize32 = 0; - return FALSE; + return false; } // Note: Kd1394Data must be in the low 32-bits of address space due to // limits on valid 1394 DMA addresses. It must also be contiguous. - Kd1394Data = (DEBUG_1394_DATA *)bi->Ohci1394BufferAddr32; + + Kd1394Data = (DEBUG_1394_DATA *)BufferAddr32; memset(Kd1394Data, 0, sizeof(*Kd1394Data)); // get our base address - KdRegisters = (volatile OHCI_REGISTER_MAP *)bi->Ohci1394Base; + KdRegisters = (volatile OHCI_REGISTER_MAP *)Base; // initialize our config info for host debugger to read. Kd1394Data->Config.Tag = DEBUG_1394_CONFIG_TAG; Kd1394Data->Config.MajorVersion = DEBUG_1394_MAJOR_VERSION; Kd1394Data->Config.MinorVersion = DEBUG_1394_MINOR_VERSION; - Kd1394Data->Config.Id = (bi->DebugBasePort < 0x100) ? bi->DebugBasePort : 0; - Kd1394Data->Config.BusPresent = FALSE; + Kd1394Data->Config.Id = (Channel < 0x100) ? Channel : 0; + Kd1394Data->Config.BusPresent = false; Kd1394Data->Config.SendPacket = MmGetPhysicalAddress(&Kd1394Data->SendPacket); Kd1394Data->Config.ReceivePacket = MmGetPhysicalAddress(&Kd1394Data->ReceivePacket); // get our version - ULONG ulVersion = KdRegisters->Version.all; + UINT32 ulVersion = KdRegisters->Version.all; UCHAR MajorVersion = (UCHAR)(ulVersion >> 16); // make sure we have a valid version if (MajorVersion != 1) { // INVESTIGATE kdprintf("1394: MajorVersion != 1\n"); - return FALSE; + return false; } // soft reset to initialize the controller HC_CONTROL_REGISTER HCControl; HCControl.all = 0; - HCControl.SoftReset = TRUE; + HCControl.SoftReset = true; KdRegisters->HCControlSet.all = HCControl.all; // wait until reset complete - ?? - ULONG retry = 1000; // ?? + UINT32 retry = 1000; // ?? do { HCControl.all = KdRegisters->HCControlSet.all; Dbg1394_StallExecution(1); } while ((HCControl.SoftReset) && (--retry)); if (retry == 0) { kdprintf("1394: Reset failed\n"); - return FALSE; + return false; } // enable link to phy communication. HCControl.all = 0; - HCControl.Lps = TRUE; + HCControl.Lps = true; KdRegisters->HCControlSet.all = HCControl.all; Dbg1394_StallExecution(20); @@ -208,32 +211,32 @@ bool Kdp1394Init(Struct_Microsoft_Singularity_BootInfo *bi) // initialize HCControl register // send data in little-endian order (i.e. do byte swap). HCControl.all = 0; - HCControl.NoByteSwapData = TRUE; + HCControl.NoByteSwapData = true; KdRegisters->HCControlClear.all = HCControl.all; // enable posted writes. HCControl.all = 0; - HCControl.PostedWriteEnable = TRUE; + HCControl.PostedWriteEnable = true; KdRegisters->HCControlSet.all = HCControl.all; // setup the link control LINK_CONTROL_REGISTER LinkControl; LinkControl.all = 0; - LinkControl.CycleTimerEnable = TRUE; - LinkControl.CycleMaster = TRUE; - LinkControl.RcvPhyPkt = TRUE; - LinkControl.RcvSelfId = TRUE; + LinkControl.CycleTimerEnable = true; + LinkControl.CycleMaster = true; + LinkControl.RcvPhyPkt = true; + LinkControl.RcvSelfId = true; KdRegisters->LinkControlClear.all = LinkControl.all; LinkControl.all = 0; - LinkControl.CycleTimerEnable = TRUE; - LinkControl.CycleMaster = TRUE; + LinkControl.CycleTimerEnable = true; + LinkControl.CycleMaster = true; KdRegisters->LinkControlSet.all = LinkControl.all; // set the bus number (hardcoded to 0x3FF) - ??? what about node id?? NODE_ID_REGISTER NodeId; NodeId.all = 0; - NodeId.BusId = (USHORT)0x3FF; + NodeId.BusId = (UINT16)0x3FF; KdRegisters->NodeId.all = NodeId.all; // do something with the crom... @@ -244,11 +247,11 @@ bool Kdp1394Init(Struct_Microsoft_Singularity_BootInfo *bi) // 0xf0000408 - bus options register BUS_OPTIONS_REGISTER BusOptions; BusOptions.all = Dbg1394_ByteSwap(KdRegisters->BusOptions.all); - BusOptions.Pmc = FALSE; - BusOptions.Bmc = FALSE; - BusOptions.Isc = FALSE; - BusOptions.Cmc = FALSE; - BusOptions.Irmc = FALSE; + BusOptions.Pmc = false; + BusOptions.Bmc = false; + BusOptions.Isc = false; + BusOptions.Cmc = false; + BusOptions.Irmc = false; BusOptions.g = 1; Kd1394Data->CromBuffer[2] = Dbg1394_ByteSwap(BusOptions.all); @@ -276,7 +279,7 @@ bool Kdp1394Init(Struct_Microsoft_Singularity_BootInfo *bi) // 0xf0000428 - debug value (for KD) IMMEDIATE_ENTRY CromEntry; - CromEntry.all = MmGetPhysicalAddress(&Kd1394Data->Config); + CromEntry.all = (UINT32)MmGetPhysicalAddress(&Kd1394Data->Config); CromEntry.IE_Key = 0x1E; Kd1394Data->CromBuffer[10] = Dbg1394_ByteSwap(CromEntry.all); @@ -296,14 +299,14 @@ bool Kdp1394Init(Struct_Microsoft_Singularity_BootInfo *bi) KdRegisters->GuidLo = Kd1394Data->CromBuffer[4]; // set our crom - KdRegisters->ConfigRomMap = MmGetPhysicalAddress(&Kd1394Data->CromBuffer); + KdRegisters->ConfigRomMap = (UINT32)MmGetPhysicalAddress(&Kd1394Data->CromBuffer); // disable all interrupts. wdm driver will enable them later - ?? KdRegisters->IntMaskClear.all = 0xFFFFFFFF; // enable the link HCControl.all = 0; - HCControl.LinkEnable = TRUE; + HCControl.LinkEnable = true; KdRegisters->HCControlSet.all = HCControl.all; Dbg1394_StallExecution(1000); @@ -317,7 +320,7 @@ bool Kdp1394Init(Struct_Microsoft_Singularity_BootInfo *bi) // hard reset on the bus (so KD will look for us) PHY_CONTROL_REGISTER PhyControl; PhyControl.all = 0; - PhyControl.RdReg = TRUE; + PhyControl.RdReg = true; PhyControl.RegAddr = 1; KdRegisters->PhyControl.all = PhyControl.all; @@ -327,13 +330,13 @@ bool Kdp1394Init(Struct_Microsoft_Singularity_BootInfo *bi) } while ((!PhyControl.RdDone) && --retry); if (retry == 0) { kdprintf("1394: Read on bus failed\n"); - return FALSE; + return false; } UCHAR Data = ((UCHAR)PhyControl.RdData | PHY_INITIATE_BUS_RESET); PhyControl.all = 0; - PhyControl.WrReg = TRUE; + PhyControl.WrReg = true; PhyControl.RegAddr = 1; PhyControl.WrData = Data; @@ -346,11 +349,11 @@ bool Kdp1394Init(Struct_Microsoft_Singularity_BootInfo *bi) } while (PhyControl.WrReg && --retry); if (retry == 0) { kdprintf("1394: Hard reset on bus failed\n"); - return FALSE; + return false; } kdprintf("1394: Init Succeeded\n"); - return TRUE; + return true; } static void Dbg1394_EnablePhysicalAccess() @@ -391,7 +394,7 @@ Dbg1394_ReadPacket( // KDP_PACKET_TIMEOUT - if timeout. = 1 = CP_GET_NODATA // KDP_PACKET_RECEIVED - if packet received. = 0 = CP_GET_SUCCESS { - ULONG timeoutLimit = 0; + UINT32 timeoutLimit = 0; do { // make sure our link is enabled.. @@ -399,7 +402,7 @@ Dbg1394_ReadPacket( KdpSpin(); if (Kd1394Data->ReceivePacket.TransferStatus == STATUS_PENDING) { - KdDebuggerNotPresent = FALSE; + KdDebuggerNotPresent = false; memcpy(PacketHeader, &Kd1394Data->ReceivePacket.Packet[0], @@ -417,7 +420,7 @@ Dbg1394_ReadPacket( &Kd1394Data->ReceivePacket.Packet[sizeof(KD_PACKET)], MessageHeader->MaximumLength); - if (Kd1394Data->ReceivePacket.Length <= (USHORT)(sizeof(KD_PACKET)+MessageHeader->MaximumLength)) { + if (Kd1394Data->ReceivePacket.Length <= (UINT16)(sizeof(KD_PACKET)+MessageHeader->MaximumLength)) { Kd1394Data->ReceivePacket.TransferStatus = STATUS_SUCCESS; return(KDP_PACKET_RECEIVED); } @@ -435,7 +438,7 @@ Dbg1394_ReadPacket( timeoutLimit++; - if (Wait == FALSE) { + if (Wait == false) { return(KDP_PACKET_RESEND); } @@ -450,7 +453,7 @@ Dbg1394_ReadPacket( // // PacketType - Supplies the type of packet to send. // -static void KdpSendControlPacket(IN USHORT PacketType) +static void KdpSendControlPacket(IN UINT16 PacketType) { KD_PACKET PacketHeader; @@ -475,10 +478,10 @@ static void KdpSendControlPacket(IN USHORT PacketType) KDP_STATUS Kdp1394ReceivePacket( - IN ULONG PacketType, + IN UINT32 PacketType, OUT PSTRING MessageHeader, OUT PSTRING MessageData, - OUT PULONG DataLength, + OUT UINT32 * DataLength, IN OUT PKD_CONTEXT KdContext ) // Routine Description: @@ -496,7 +499,7 @@ Kdp1394ReceivePacket( // MessageHeader - Supplies a pointer to a string descriptor for the input // message. // MessageData - Supplies a pointer to a string descriptor for the input data. - // DataLength - Supplies pointer to ULONG to receive length of recv. data. + // DataLength - Supplies pointer to UINT32 to receive length of recv. data. // KdContext - Supplies a pointer to the kernel debugger context. // // Return Value: @@ -504,10 +507,10 @@ Kdp1394ReceivePacket( // KDP_PACKET_TIMEOUT - if timeout. = 1 = CP_GET_NODATA // KDP_PACKET_RECEIVED - if packet received. = 0 = CP_GET_SUCCESS { - ULONG MessageLength; + UINT32 MessageLength; KD_PACKET PacketHeader; KDP_STATUS ReturnCode; - ULONG Checksum; + UINT32 Checksum; KDDBG2("Kdp1394ReceivePacket\n"); @@ -520,7 +523,7 @@ WaitForPacketLeader: ReturnCode = Dbg1394_ReadPacket(&PacketHeader, MessageHeader, MessageData, - TRUE); + true); // // If we can successfully read packet leader, it has high possibility that // kernel debugger is alive. So reset count. @@ -532,7 +535,7 @@ WaitForPacketLeader: if (ReturnCode != KDP_PACKET_RECEIVED) { // see if it's a breakin packet... if ((PacketHeader.PacketLeader & 0xFF) == BREAKIN_PACKET_BYTE) { - KdContext->KdpControlCPending = TRUE; + KdContext->KdpControlCPending = true; return(KDP_PACKET_RESEND); } return(ReturnCode); @@ -552,14 +555,14 @@ WaitForPacketLeader: // MessageLength = MessageHeader->MaximumLength; - if ((PacketHeader.ByteCount > (USHORT)PACKET_MAX_SIZE) || - (PacketHeader.ByteCount < (USHORT)MessageLength)) { + if ((PacketHeader.ByteCount > (UINT16)PACKET_MAX_SIZE) || + (PacketHeader.ByteCount < (UINT16)MessageLength)) { goto SendResendPacket; } *DataLength = PacketHeader.ByteCount - MessageLength; - MessageData->Length = (USHORT)*DataLength; - MessageHeader->Length = (USHORT)MessageLength; + MessageData->Length = (UINT16)*DataLength; + MessageHeader->Length = (UINT16)MessageLength; // // Check PacketType is what we are waiting for. @@ -588,7 +591,7 @@ SendResendPacket: void Kdp1394SendPacket( - IN ULONG PacketType, + IN UINT32 PacketType, IN PSTRING MessageHeader, IN PSTRING MessageData OPTIONAL, IN OUT PKD_CONTEXT KdContext @@ -609,9 +612,9 @@ Kdp1394SendPacket( // None. { KD_PACKET PacketHeader; - ULONG MessageDataLength; - ULONG ReturnCode; - bool bException = FALSE; + UINT32 MessageDataLength; + UINT32 ReturnCode; + bool bException = false; KDDBG2("Kdp1394SendPacket\n"); PacketHeader.Checksum = 0; @@ -631,8 +634,8 @@ Kdp1394SendPacket( // Initialize and send the packet header. // PacketHeader.PacketLeader = PACKET_LEADER; - PacketHeader.ByteCount = (USHORT)(MessageHeader->Length + MessageDataLength); - PacketHeader.PacketType = (USHORT)PacketType; + PacketHeader.ByteCount = (UINT16)(MessageHeader->Length + MessageDataLength); + PacketHeader.PacketType = (UINT16)PacketType; PacketHeader.PacketId = KdPacketId; KdPacketId++; @@ -673,12 +676,13 @@ Kdp1394SendPacket( // need to do this to fix pre-logging (eventually). #if 0 UCHAR uDummy; - DWORD dwDrained = 0; + uint32 dwDrained = 0; KdCompNextPacketIdToSend |= SYNC_PACKET_ID; - KdStateChange64Sent = TRUE; + KdStateChange64Sent = true; - while (KdCompGetByte(&uDummy) == CP_GET_SUCCESS) + while (KdCompGetByte(&uDummy) == CP_GET_SUCCESS) { dwDrained++; + } #endif // 0 } @@ -687,7 +691,7 @@ Kdp1394SendPacket( KdCompNumberRetries, KdCompRetryCount, &Kd1394Data->SendPacket, Kd1394Data->SendPacket.TransferStatus, - *(ULONG*)&Kd1394Data->SendPacket.PacketHeader, + *(UINT32*)&Kd1394Data->SendPacket.PacketHeader, Kd1394Data->SendPacket.Length, Kd1394Data->SendPacket.Packet[0], Kd1394Data->SendPacket.Packet[1], @@ -707,7 +711,7 @@ Kdp1394SendPacket( = (PDBGKD_DEBUG_IO)MessageHeader->Buffer; if (DebugIo->ApiNumber == DbgKdPrintStringApi) { - KdDebuggerNotPresent = TRUE; + KdDebuggerNotPresent = true; Kd1394Data->SendPacket.TransferStatus = STATUS_SUCCESS; return; } @@ -716,31 +720,29 @@ Kdp1394SendPacket( PDBGKD_ANY_WAIT_STATE_CHANGE StateChange = (PDBGKD_ANY_WAIT_STATE_CHANGE)MessageHeader->Buffer; if (StateChange->NewState == DbgKdLoadSymbolsStateChange) { - KdDebuggerNotPresent = TRUE; + KdDebuggerNotPresent = true; Kd1394Data->SendPacket.TransferStatus = STATUS_SUCCESS; return; } } -#if 0 else if (PacketType == PACKET_TYPE_KD_FILE_IO) { PDBGKD_FILE_IO FileIo = (PDBGKD_FILE_IO)MessageHeader->Buffer; if (FileIo->ApiNumber == DbgKdCreateFileApi) { - KdDebuggerNotPresent = TRUE; + KdDebuggerNotPresent = true; Kd1394Data->SendPacket.TransferStatus = STATUS_SUCCESS; return; } } -#endif else { - bException = TRUE; + bException = true; } } ReturnCode = KDP_PACKET_TIMEOUT; { - ULONG count = 0; + UINT32 count = 0; volatile NTSTATUS *pStatus; pStatus = &Kd1394Data->ReceivePacket.TransferStatus; @@ -782,9 +784,9 @@ Kdp1394SendPacket( KdCompRetryCount = KdContext->KdpDefaultRetries; } -// Returns TRUE if a breakin packet is pending. +// Returns true if a breakin packet is pending. // A packet is present if: There is a valid character which matches BREAK_CHAR. -bool Kdp1394PollBreakIn(VOID) +bool Kdp1394PollBreakIn(void) { KDDBG2("Kdp1394PollBreakIn\n"); @@ -795,7 +797,7 @@ bool Kdp1394PollBreakIn(VOID) if ((Kd1394Data->ReceivePacket.TransferStatus == STATUS_PENDING) && (Kd1394Data->ReceivePacket.Packet[0] == BREAKIN_PACKET_BYTE)) { - KdDebuggerNotPresent = FALSE; + KdDebuggerNotPresent = false; // we have a breakin packet Kd1394Data->ReceivePacket.TransferStatus = STATUS_SUCCESS; diff --git a/base/Kernel/Native/HalKd1394.h b/base/Kernel/Native/HalKd1394.h index db6b6fd..2337cdc 100644 --- a/base/Kernel/Native/HalKd1394.h +++ b/base/Kernel/Native/HalKd1394.h @@ -1,8 +1,7 @@ ////////////////////////////////////////////////////////////////////////////// // -// Copyright (c) Microsoft Corporation. All rights reserved. -// // kd1394.h - 1394 Kernel Debugger DLL +// // // Various OHCI definitions @@ -13,302 +12,302 @@ // union VERSION_REGISTER { struct { - ULONG Revision:8; // bits 0-7 - ULONG Reserved:8; // bits 8-15 - ULONG Version:8; // bits 16-23 - ULONG GUID_ROM:1; // bit 24 - ULONG Reserved1:7; // bits 25-31 + UINT32 Revision:8; // bits 0-7 + UINT32 Reserved:8; // bits 8-15 + UINT32 Version:8; // bits 16-23 + UINT32 GUID_ROM:1; // bit 24 + UINT32 Reserved1:7; // bits 25-31 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(VERSION_REGISTER) == 4); union VENDOR_ID_REGISTER { struct { - ULONG VendorCompanyId:24; // bits 0-23 - ULONG VendorUnique:8; // bits 24-31 + UINT32 VendorCompanyId:24; // bits 0-23 + UINT32 VendorUnique:8; // bits 24-31 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(VENDOR_ID_REGISTER) == 4); union GUID_ROM_REGISTER { struct { - ULONG Reserved0:16; // bits 0-15 - ULONG RdData:8; // bits 16-23 - ULONG Reserved1:1; // bit 24 - ULONG RdStart:1; // bit 25 - ULONG Reserved2:5; // bits 26-30 - ULONG AddrReset:1; // bits 31 + UINT32 Reserved0:16; // bits 0-15 + UINT32 RdData:8; // bits 16-23 + UINT32 Reserved1:1; // bit 24 + UINT32 RdStart:1; // bit 25 + UINT32 Reserved2:5; // bits 26-30 + UINT32 AddrReset:1; // bits 31 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(GUID_ROM_REGISTER) == 4); union AT_RETRIES_REGISTER { struct { - ULONG MaxATReqRetries:4; // bits 0-3 - ULONG MaxATRespRetries:4; // bits 4-7 - ULONG MaxPhysRespRetries:4; // bits 8-11 - ULONG Reserved:4; // bits 12-15 - ULONG CycleLimit:13; // bits 16-28 - ULONG SecondLimit:3; // bits 29-31 + UINT32 MaxATReqRetries:4; // bits 0-3 + UINT32 MaxATRespRetries:4; // bits 4-7 + UINT32 MaxPhysRespRetries:4; // bits 8-11 + UINT32 Reserved:4; // bits 12-15 + UINT32 CycleLimit:13; // bits 16-28 + UINT32 SecondLimit:3; // bits 29-31 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(AT_RETRIES_REGISTER) == 4); union CSR_CONTROL_REGISTER { struct { - ULONG CsrSel:2; // bits 0-1 - ULONG Reserved:29; // bits 2-30 - ULONG CsrDone:1; // bit 31 + UINT32 CsrSel:2; // bits 0-1 + UINT32 Reserved:29; // bits 2-30 + UINT32 CsrDone:1; // bit 31 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(CSR_CONTROL_REGISTER) == 4); union CONFIG_ROM_HEADER_REGISTER { struct { - ULONG Rom_crc_value:16; // bits 0-15 - ULONG Crc_length:8; // bits 16-23 - ULONG Info_length:8; // bits 24-31 + UINT32 Rom_crc_value:16; // bits 0-15 + UINT32 Crc_length:8; // bits 16-23 + UINT32 Info_length:8; // bits 24-31 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(CONFIG_ROM_HEADER_REGISTER) == 4); union BUS_OPTIONS_REGISTER { struct { - ULONG Link_spd:3; // bits 0-2 - ULONG Reserved0:3; // bits 3-5 - ULONG g:2; // bits 6-7 - ULONG Reserved1:4; // bits 8-11 - ULONG Max_rec:4; // bits 12-15 - ULONG Cyc_clk_acc:8; // bits 16-23 - ULONG Reserved2:3; // bits 24-26 - ULONG Pmc:1; // bit 27 - ULONG Bmc:1; // bit 28 - ULONG Isc:1; // bit 29 - ULONG Cmc:1; // bit 30 - ULONG Irmc:1; // bit 31 + UINT32 Link_spd:3; // bits 0-2 + UINT32 Reserved0:3; // bits 3-5 + UINT32 g:2; // bits 6-7 + UINT32 Reserved1:4; // bits 8-11 + UINT32 Max_rec:4; // bits 12-15 + UINT32 Cyc_clk_acc:8; // bits 16-23 + UINT32 Reserved2:3; // bits 24-26 + UINT32 Pmc:1; // bit 27 + UINT32 Bmc:1; // bit 28 + UINT32 Isc:1; // bit 29 + UINT32 Cmc:1; // bit 30 + UINT32 Irmc:1; // bit 31 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(BUS_OPTIONS_REGISTER) == 4); union HC_CONTROL_REGISTER { struct { - ULONG Reserved:16; // bits 0-15 - ULONG SoftReset:1; // bit 16 - ULONG LinkEnable:1; // bit 17 - ULONG PostedWriteEnable:1; // bit 18 - ULONG Lps:1; // bit 19 - ULONG Reserved2:2; // bits 20-21 - ULONG APhyEnhanceEnable:1; // bit 22 - ULONG ProgramPhyEnable:1; // bit 23 - ULONG Reserved3:6; // bits 24-29 - ULONG NoByteSwapData:1; // bit 30 - ULONG Reserved4:1; // bit 31 + UINT32 Reserved:16; // bits 0-15 + UINT32 SoftReset:1; // bit 16 + UINT32 LinkEnable:1; // bit 17 + UINT32 PostedWriteEnable:1; // bit 18 + UINT32 Lps:1; // bit 19 + UINT32 Reserved2:2; // bits 20-21 + UINT32 APhyEnhanceEnable:1; // bit 22 + UINT32 ProgramPhyEnable:1; // bit 23 + UINT32 Reserved3:6; // bits 24-29 + UINT32 NoByteSwapData:1; // bit 30 + UINT32 Reserved4:1; // bit 31 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(HC_CONTROL_REGISTER) == 4); union FAIRNESS_CONTROL_REGISTER { struct { - ULONG Pri_req:8; // bits 0-7 - ULONG Reserved0:24; // bits 8-31 + UINT32 Pri_req:8; // bits 0-7 + UINT32 Reserved0:24; // bits 8-31 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(FAIRNESS_CONTROL_REGISTER) == 4); union LINK_CONTROL_REGISTER { struct { - ULONG Reserved0:4; // bits 0-3 - ULONG CycleSyncLReqEnable:1; // bit 4 - ULONG Reserved1:4; // bits 5-8 - ULONG RcvSelfId:1; // bit 9 - ULONG RcvPhyPkt:1; // bit 10 - ULONG Reserved2:9; // bits 11-19 - ULONG CycleTimerEnable:1; // bit 20 - ULONG CycleMaster:1; // bit 21 - ULONG CycleSource:1; // bit 22 - ULONG Reserved3:9; // bits 23-31 + UINT32 Reserved0:4; // bits 0-3 + UINT32 CycleSyncLReqEnable:1; // bit 4 + UINT32 Reserved1:4; // bits 5-8 + UINT32 RcvSelfId:1; // bit 9 + UINT32 RcvPhyPkt:1; // bit 10 + UINT32 Reserved2:9; // bits 11-19 + UINT32 CycleTimerEnable:1; // bit 20 + UINT32 CycleMaster:1; // bit 21 + UINT32 CycleSource:1; // bit 22 + UINT32 Reserved3:9; // bits 23-31 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(LINK_CONTROL_REGISTER) == 4); union NODE_ID_REGISTER { struct { - ULONG NodeId:6; // bits 0-5 - ULONG BusId:10; // bits 6-15 - ULONG Reserved1:11; // bits 16-26 - ULONG Cps:1; // bit 27 - ULONG Reserved2:2; // bits 28-29 - ULONG Root:1; // bit 30 - ULONG IdValid:1; // bit 31 + UINT32 NodeId:6; // bits 0-5 + UINT32 BusId:10; // bits 6-15 + UINT32 Reserved1:11; // bits 16-26 + UINT32 Cps:1; // bit 27 + UINT32 Reserved2:2; // bits 28-29 + UINT32 Root:1; // bit 30 + UINT32 IdValid:1; // bit 31 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(NODE_ID_REGISTER) == 4); union SELF_ID_BUFFER_REGISTER { - ULONG SelfIdBufferPointer; + UINT32 SelfIdBufferPointer; struct { - ULONG Reserved0:11; // bits 0-10 - ULONG SelfIdBuffer:21; // bits 11-32 + UINT32 Reserved0:11; // bits 0-10 + UINT32 SelfIdBuffer:21; // bits 11-32 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(SELF_ID_BUFFER_REGISTER) == 4); union SELF_ID_COUNT_REGISTER { struct { - ULONG Reserved0:2; // bits 0-1 - ULONG SelfIdSize:11; // bits 2-12 - ULONG Reserved1:3; // bits 13-15 - ULONG SelfIdGeneration:8; // bits 16-23 - ULONG Reserved2:7; // bits 24-30 - ULONG SelfIdError:1; // bit 31 + UINT32 Reserved0:2; // bits 0-1 + UINT32 SelfIdSize:11; // bits 2-12 + UINT32 Reserved1:3; // bits 13-15 + UINT32 SelfIdGeneration:8; // bits 16-23 + UINT32 Reserved2:7; // bits 24-30 + UINT32 SelfIdError:1; // bit 31 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(SELF_ID_COUNT_REGISTER) == 4); union PHY_CONTROL_REGISTER { struct { - ULONG WrData:8; // bits 0-7 - ULONG RegAddr:4; // bits 8-11 - ULONG Reserved0:2; // bits 12-13 - ULONG WrReg:1; // bit 14 - ULONG RdReg:1; // bit 15 - ULONG RdData:8; // bits 16-23 - ULONG RdAddr:4; // bits 24-27 - ULONG Reserved1:3; // bits 28-30 - ULONG RdDone:1; // bit 31 + UINT32 WrData:8; // bits 0-7 + UINT32 RegAddr:4; // bits 8-11 + UINT32 Reserved0:2; // bits 12-13 + UINT32 WrReg:1; // bit 14 + UINT32 RdReg:1; // bit 15 + UINT32 RdData:8; // bits 16-23 + UINT32 RdAddr:4; // bits 24-27 + UINT32 Reserved1:3; // bits 28-30 + UINT32 RdDone:1; // bit 31 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(PHY_CONTROL_REGISTER) == 4); union ISOCH_CYCLE_TIMER_REGISTER { struct { - ULONG CycleOffset:12; // bits 0-11 - ULONG CycleCount:13; // bits 12-24 - ULONG CycleSeconds:7; // bits 25-31 + UINT32 CycleOffset:12; // bits 0-11 + UINT32 CycleCount:13; // bits 12-24 + UINT32 CycleSeconds:7; // bits 25-31 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(ISOCH_CYCLE_TIMER_REGISTER) == 4); union INT_EVENT_MASK_REGISTER { struct { - ULONG ReqTxComplete:1; // bit 0 - ULONG RspTxComplete:1; // bit 1 - ULONG ARRQ:1; // bit 2 - ULONG ARRS:1; // bit 3 - ULONG RQPkt:1; // bit 4 - ULONG RSPPkt:1; // bit 5 - ULONG IsochTx:1; // bit 6 - ULONG IsochRx:1; // bit 7 - ULONG PostedWriteErr:1; // bit 8 - ULONG LockRespErr:1; // bit 9 - ULONG Reserved0:6; // bits 10-15 - ULONG SelfIdComplete:1; // bit 16 - ULONG BusReset:1; // bit 17 - ULONG Reserved1:1; // bit 18 - ULONG Phy:1; // bit 19 - ULONG CycleSynch:1; // bit 20 - ULONG Cycle64Secs:1; // bit 21 - ULONG CycleLost:1; // bit 22 - ULONG CycleInconsistent:1; // bit 23 - ULONG UnrecoverableError:1; // bit 24 - ULONG CycleTooLong:1; // bit 25 - ULONG PhyRegRcvd:1; // bit 26 - ULONG Reserved2:3; // bits 27-29 - ULONG VendorSpecific:1; // bit 30 - ULONG MasterIntEnable:1; // bit 31 + UINT32 ReqTxComplete:1; // bit 0 + UINT32 RspTxComplete:1; // bit 1 + UINT32 ARRQ:1; // bit 2 + UINT32 ARRS:1; // bit 3 + UINT32 RQPkt:1; // bit 4 + UINT32 RSPPkt:1; // bit 5 + UINT32 IsochTx:1; // bit 6 + UINT32 IsochRx:1; // bit 7 + UINT32 PostedWriteErr:1; // bit 8 + UINT32 LockRespErr:1; // bit 9 + UINT32 Reserved0:6; // bits 10-15 + UINT32 SelfIdComplete:1; // bit 16 + UINT32 BusReset:1; // bit 17 + UINT32 Reserved1:1; // bit 18 + UINT32 Phy:1; // bit 19 + UINT32 CycleSynch:1; // bit 20 + UINT32 Cycle64Secs:1; // bit 21 + UINT32 CycleLost:1; // bit 22 + UINT32 CycleInconsistent:1; // bit 23 + UINT32 UnrecoverableError:1; // bit 24 + UINT32 CycleTooLong:1; // bit 25 + UINT32 PhyRegRcvd:1; // bit 26 + UINT32 Reserved2:3; // bits 27-29 + UINT32 VendorSpecific:1; // bit 30 + UINT32 MasterIntEnable:1; // bit 31 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(INT_EVENT_MASK_REGISTER) == 4); union COMMAND_POINTER_REGISTER { struct { - ULONG Z:4; // bits 0-3 - ULONG DescriptorAddr:28; // bits 4-31 + UINT32 Z:4; // bits 0-3 + UINT32 DescriptorAddr:28; // bits 4-31 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(COMMAND_POINTER_REGISTER) == 4); union CONTEXT_CONTROL_REGISTER { struct { - ULONG EventCode:5; // bits 0-4 - ULONG Spd:3; // bits 5-7 - ULONG Reserved0:2; // bits 8-9 - ULONG Active:1; // bit 10 - ULONG Dead:1; // bit 11 - ULONG Wake:1; // bit 12 - ULONG Reserved1:2; // bits 13-14 - ULONG Run:1; // bit 15 - ULONG Reserved2:16; // bits 16-31 + UINT32 EventCode:5; // bits 0-4 + UINT32 Spd:3; // bits 5-7 + UINT32 Reserved0:2; // bits 8-9 + UINT32 Active:1; // bit 10 + UINT32 Dead:1; // bit 11 + UINT32 Wake:1; // bit 12 + UINT32 Reserved1:2; // bits 13-14 + UINT32 Run:1; // bit 15 + UINT32 Reserved2:16; // bits 16-31 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(CONTEXT_CONTROL_REGISTER) == 4); union IT_CONTEXT_CONTROL_REGISTER { struct { - ULONG EventCode:5; // bits 0-4 - ULONG Spd:3; // bits 5-7 - ULONG Reserved0:2; // bits 8-9 - ULONG Active:1; // bit 10 - ULONG Dead:1; // bit 11 - ULONG Wake:1; // bit 12 - ULONG Reserved1:2; // bits 13-14 - ULONG Run:1; // bit 15 - ULONG CycleMatch:15; // bits 16-30 - ULONG CycleMatchEnable:1; // bit 31 + UINT32 EventCode:5; // bits 0-4 + UINT32 Spd:3; // bits 5-7 + UINT32 Reserved0:2; // bits 8-9 + UINT32 Active:1; // bit 10 + UINT32 Dead:1; // bit 11 + UINT32 Wake:1; // bit 12 + UINT32 Reserved1:2; // bits 13-14 + UINT32 Run:1; // bit 15 + UINT32 CycleMatch:15; // bits 16-30 + UINT32 CycleMatchEnable:1; // bit 31 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(IT_CONTEXT_CONTROL_REGISTER) == 4); union IR_CONTEXT_CONTROL_REGISTER { struct { - ULONG EventCode:5; // bits 0-4 - ULONG Spd:3; // bits 5-7 - ULONG Reserved0:2; // bits 8-9 - ULONG Active:1; // bit 10 - ULONG Dead:1; // bit 11 - ULONG Wake:1; // bit 12 - ULONG Reserved1:2; // bits 13-14 - ULONG Run:1; // bit 15 - ULONG CycleMatch:12; // bits 16-27 - ULONG MultiChanMode:1; // bit 28 - ULONG CycleMatchEnable:1; // bit 29 - ULONG IsochHeader:1; // bit 30 - ULONG BufferFill:1; // bit 31 + UINT32 EventCode:5; // bits 0-4 + UINT32 Spd:3; // bits 5-7 + UINT32 Reserved0:2; // bits 8-9 + UINT32 Active:1; // bit 10 + UINT32 Dead:1; // bit 11 + UINT32 Wake:1; // bit 12 + UINT32 Reserved1:2; // bits 13-14 + UINT32 Run:1; // bit 15 + UINT32 CycleMatch:12; // bits 16-27 + UINT32 MultiChanMode:1; // bit 28 + UINT32 CycleMatchEnable:1; // bit 29 + UINT32 IsochHeader:1; // bit 30 + UINT32 BufferFill:1; // bit 31 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(IR_CONTEXT_CONTROL_REGISTER) == 4); union CONTEXT_MATCH_REGISTER { struct { - ULONG ChannelNumber:6; // bits 0-5 - ULONG Reserved:1; // bit 6 - ULONG Tag1SyncFilter:1; // bit 7 - ULONG Sync:4; // bits 8-11 - ULONG CycleMatch:13; // bits 12-24 - ULONG Reserved1:3; // bits 25-27 - ULONG Tag:4; // bit 28-31 + UINT32 ChannelNumber:6; // bits 0-5 + UINT32 Reserved:1; // bit 6 + UINT32 Tag1SyncFilter:1; // bit 7 + UINT32 Sync:4; // bits 8-11 + UINT32 CycleMatch:13; // bits 12-24 + UINT32 Reserved1:3; // bits 25-27 + UINT32 Tag:4; // bit 28-31 }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(CONTEXT_MATCH_REGISTER) == 4); @@ -317,24 +316,24 @@ STATIC_ASSERT(sizeof(CONTEXT_MATCH_REGISTER) == 4); struct DMA_CONTEXT_REGISTERS { CONTEXT_CONTROL_REGISTER ContextControlSet; CONTEXT_CONTROL_REGISTER ContextControlClear; - ULONG Reserved0[1]; + UINT32 Reserved0[1]; COMMAND_POINTER_REGISTER CommandPtr; - ULONG Reserved1[4]; + UINT32 Reserved1[4]; }; struct DMA_ISOCH_RCV_CONTEXT_REGISTERS { IR_CONTEXT_CONTROL_REGISTER ContextControlSet; IR_CONTEXT_CONTROL_REGISTER ContextControlClear; - ULONG Reserved0[1]; + UINT32 Reserved0[1]; COMMAND_POINTER_REGISTER CommandPtr; CONTEXT_MATCH_REGISTER ContextMatch; - ULONG Reserved1[3]; + UINT32 Reserved1[3]; }; struct DMA_ISOCH_XMIT_CONTEXT_REGISTERS { IT_CONTEXT_CONTROL_REGISTER ContextControlSet; IT_CONTEXT_CONTROL_REGISTER ContextControlClear; - ULONG Reserved0[1]; + UINT32 Reserved0[1]; COMMAND_POINTER_REGISTER CommandPtr; }; @@ -342,37 +341,37 @@ struct OHCI_REGISTER_MAP { VERSION_REGISTER Version; // @ 0 GUID_ROM_REGISTER GUID_ROM; // @ 4 AT_RETRIES_REGISTER ATRetries; // @ 8 - ULONG CsrData; // @ C - ULONG CsrCompare; // @ 10 + UINT32 CsrData; // @ C + UINT32 CsrCompare; // @ 10 CSR_CONTROL_REGISTER CsrControl; // @ 14 CONFIG_ROM_HEADER_REGISTER ConfigRomHeader; // @ 18 - ULONG BusId; // @ 1C + UINT32 BusId; // @ 1C BUS_OPTIONS_REGISTER BusOptions; // @ 20 - ULONG GuidHi; // @ 24 - ULONG GuidLo; // @ 28 - ULONG Reserved0[2]; // @ 2C - ULONG ConfigRomMap; // @ 34 + UINT32 GuidHi; // @ 24 + UINT32 GuidLo; // @ 28 + UINT32 Reserved0[2]; // @ 2C + UINT32 ConfigRomMap; // @ 34 - ULONG PostedWriteAddressLo; // @ 38 - ULONG PostedWriteAddressHi; // @ 3C + UINT32 PostedWriteAddressLo; // @ 38 + UINT32 PostedWriteAddressHi; // @ 3C VENDOR_ID_REGISTER VendorId; // @ 40 - ULONG Reserved1[3]; // @ 44 + UINT32 Reserved1[3]; // @ 44 HC_CONTROL_REGISTER HCControlSet; // @ 50 HC_CONTROL_REGISTER HCControlClear; // @ 54 - ULONG Reserved2[3]; // @ 58 + UINT32 Reserved2[3]; // @ 58 SELF_ID_BUFFER_REGISTER SelfIdBufferPtr; // @ 64 SELF_ID_COUNT_REGISTER SelfIdCount; // @ 68 - ULONG Reserved3[1]; // @ 6C + UINT32 Reserved3[1]; // @ 6C - ULONG IRChannelMaskHiSet; // @ 70 - ULONG IRChannelMaskHiClear; // @ 74 - ULONG IRChannelMaskLoSet; // @ 78 - ULONG IRChannelMaskLoClear; // @ 7C + UINT32 IRChannelMaskHiSet; // @ 70 + UINT32 IRChannelMaskHiClear; // @ 74 + UINT32 IRChannelMaskLoSet; // @ 78 + UINT32 IRChannelMaskLoClear; // @ 7C INT_EVENT_MASK_REGISTER IntEventSet; // @ 80 INT_EVENT_MASK_REGISTER IntEventClear; // @ 84 @@ -380,19 +379,19 @@ struct OHCI_REGISTER_MAP { INT_EVENT_MASK_REGISTER IntMaskSet; // @ 88 INT_EVENT_MASK_REGISTER IntMaskClear; // @ 8C - ULONG IsoXmitIntEventSet; // @ 90 - ULONG IsoXmitIntEventClear; // @ 94 + UINT32 IsoXmitIntEventSet; // @ 90 + UINT32 IsoXmitIntEventClear; // @ 94 - ULONG IsoXmitIntMaskSet; // @ 98 - ULONG IsoXmitIntMaskClear; // @ 9C + UINT32 IsoXmitIntMaskSet; // @ 98 + UINT32 IsoXmitIntMaskClear; // @ 9C - ULONG IsoRecvIntEventSet; // @ A0 - ULONG IsoRecvIntEventClear; // @ A4 + UINT32 IsoRecvIntEventSet; // @ A0 + UINT32 IsoRecvIntEventClear; // @ A4 - ULONG IsoRecvIntMaskSet; // @ A8 - ULONG IsoRecvIntMaskClear; // @ AC + UINT32 IsoRecvIntMaskSet; // @ A8 + UINT32 IsoRecvIntMaskClear; // @ AC - ULONG Reserved4[11]; // @ B0 + UINT32 Reserved4[11]; // @ B0 FAIRNESS_CONTROL_REGISTER FairnessControl; // @ DC @@ -404,22 +403,22 @@ struct OHCI_REGISTER_MAP { ISOCH_CYCLE_TIMER_REGISTER IsochCycleTimer; // @ F0 - ULONG Reserved5[3]; // @ F4 + UINT32 Reserved5[3]; // @ F4 - ULONG AsynchReqFilterHiSet; // @ 100 - ULONG AsynchReqFilterHiClear; // @ 104 + UINT32 AsynchReqFilterHiSet; // @ 100 + UINT32 AsynchReqFilterHiClear; // @ 104 - ULONG AsynchReqFilterLoSet; // @ 108 - ULONG AsynchReqFilterLoClear; // @ 10C + UINT32 AsynchReqFilterLoSet; // @ 108 + UINT32 AsynchReqFilterLoClear; // @ 10C - ULONG PhyReqFilterHiSet; // @ 110 - ULONG PhyReqFilterHiClear; // @ 114 + UINT32 PhyReqFilterHiSet; // @ 110 + UINT32 PhyReqFilterHiClear; // @ 114 - ULONG PhyReqFilterLoSet; // @ 118 - ULONG PhyReqFilterLoClear; // @ 11C + UINT32 PhyReqFilterLoSet; // @ 118 + UINT32 PhyReqFilterLoClear; // @ 11C - ULONG PhysicalUpperBound; // @ 120 - ULONG Reserved6[23]; // @ 124 + UINT32 PhysicalUpperBound; // @ 120 + UINT32 Reserved6[23]; // @ 124 DMA_CONTEXT_REGISTERS AsynchContext[4]; // @ 180 // ATRsp_Context; // @ 1A0 @@ -438,16 +437,16 @@ STATIC_ASSERT(sizeof(OHCI_REGISTER_MAP) == 2048); union CONFIG_ROM_INFO { struct { union { - USHORT CRI_CRC_Value:16; + UINT16 CRI_CRC_Value:16; struct { - UCHAR CRI_Saved_Info_Length; - UCHAR CRI_Saved_CRC_Length; + UINT8 CRI_Saved_Info_Length; + UINT8 CRI_Saved_CRC_Length; } Saved; }; - UCHAR CRI_CRC_Length; - UCHAR CRI_Info_Length; + UINT8 CRI_CRC_Length; + UINT8 CRI_Info_Length; }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(CONFIG_ROM_INFO) == 4); @@ -456,10 +455,10 @@ STATIC_ASSERT(sizeof(CONFIG_ROM_INFO) == 4); // union IMMEDIATE_ENTRY { struct { - ULONG IE_Value:24; - ULONG IE_Key:8; + UINT32 IE_Value:24; + UINT32 IE_Key:8; }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(IMMEDIATE_ENTRY) == 4); @@ -469,12 +468,12 @@ STATIC_ASSERT(sizeof(IMMEDIATE_ENTRY) == 4); union DIRECTORY_INFO { struct { union { - USHORT DI_CRC; - USHORT DI_Saved_Length; + UINT16 DI_CRC; + UINT16 DI_Saved_Length; }; - USHORT DI_Length; + UINT16 DI_Length; }; - ULONG all; + UINT32 all; }; STATIC_ASSERT(sizeof(DIRECTORY_INFO) == 4); diff --git a/base/Kernel/Native/HalKdCom.cpp b/base/Kernel/Native/HalKdCom.cpp index 5ebdc8f..d3fb2d1 100644 --- a/base/Kernel/Native/HalKdCom.cpp +++ b/base/Kernel/Native/HalKdCom.cpp @@ -1,32 +1,23 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // -// halkd.cpp: runtime support for debugging +// File: halkdcom.cpp: pc-compatible com port serial transport. // -// For more information see: -// \nt\base\ntos\kd64 -// \nt\base\boot\kdcom -// \nt\base\boot\kd1394 -// \nt\base\boot\kdusb2 -// \nt\sdktools\debuggers\ntsd64 +// Note: Kernel Only // + #include "hal.h" #include "halkd.h" -extern "C" void * __cdecl memcpy(void *, const void *, size_t); -extern "C" void * __cdecl memset(void *, int, size_t); - // // Debugger Debugging // #define KDDBG if (0) kdprintf #define KDDBG2 if (0) kdprintf -// -#define CP_GET_SUCCESS 0 -#define CP_GET_NODATA 1 -#define CP_GET_ERROR 2 - ////////////////////////////////////////////////////////// COM PORT Constants. // #define COM1_PORT 0x03f8 @@ -69,42 +60,28 @@ const UINT16 BaudRate = 1; // 115200 bps // Globals // static UINT16 KdBasePort = COM2_PORT; -static ULONG KdCompPacketIdExpected = 0; -static ULONG KdCompNextPacketIdToSend = 0; -static BOOL KdStateChange64Sent = FALSE; ////////////////////////////////////////////////// Serial Port Input & Output. // + static UINT8 KdReadInt8(UINT16 port) { - __asm { - mov eax,0; - mov dx,port; - in al,dx; - } + return __inbyte(port); } static void KdWriteInt8(UINT16 port, UINT8 value) { - __asm { - mov dx,port; - mov al,value; - out dx,al; - } + __outbyte(port, value); } -// http://byterunner.com/16550.html - -bool KdpComInit(Struct_Microsoft_Singularity_BootInfo *bi) +bool KdpSerialInit(Class_Microsoft_Singularity_Hal_Platform *nbi) // Initializes the communication port (baud rate, parity etc.) { - KdBasePort = bi->DebugBasePort; + KdBasePort = (UINT16)nbi->DebugBasePort; if (KdBasePort < 0x100) { KdBasePort = 0; - return FALSE; + return false; } - KdCompNextPacketIdToSend = INITIAL_PACKET_ID | SYNC_PACKET_ID; - KdCompPacketIdExpected = INITIAL_PACKET_ID; // turn off interrupts KdWriteInt8(KdBasePort + COM_LCR, 0x00); @@ -138,15 +115,15 @@ bool KdpComInit(Struct_Microsoft_Singularity_BootInfo *bi) // // Define wait timeout value. // -#define TIMEOUT_COUNT 1024 * 10 +#define TIMEOUT_COUNT 1024 * 1000 // #define TIMEOUT_COUNT 1024 * 200 //#define TIMEOUT_COUNT 15 -static KDP_STATUS CpGetByte(OUT PUCHAR Input, BOOL WaitForByte) +KDP_STATUS KdpSerialGetByte(OUT PUCHAR Input, BOOL WaitForByte) { UCHAR lsr; UCHAR value; - ULONG limitcount = WaitForByte ? TIMEOUT_COUNT : 1; + UINT32 limitcount = WaitForByte ? TIMEOUT_COUNT : 1; UCHAR msr; msr = KdReadInt8(KdBasePort + COM_MSR); @@ -166,770 +143,16 @@ static KDP_STATUS CpGetByte(OUT PUCHAR Input, BOOL WaitForByte) return KDP_PACKET_TIMEOUT; } -// Fetch a byte from the debug port and return it. -// N.B. It is assumed that necessary multiprocessor synchronization has been -// performed before this routine is called. -// -static KDP_STATUS KdCompGetByte(OUT PUCHAR Input) -{ - KDP_STATUS stat; - KDDBG2("KdCompGetByte\n"); - stat = CpGetByte(Input, TRUE); - KDDBG2("KdCompGetByte status %d\n", stat); - return stat; -} - -// Write a byte to the debug port. -// N.B. It is assumed that necessary multiprocessor synchronization has been -// performed before this routine is called. -// -static VOID KdCompPutByte(IN UCHAR Output) +void KdpSerialPutByte(IN UCHAR Output) { - KDDBG2("KdCompPutByte %02x\n", Output); // wait for the com port to be ready - while ((KdReadInt8( KdBasePort + COM_LSR ) & COM_OUTRDY) == 0); - - KDDBG2("KdCompPutByte ready\n"); + while ((KdReadInt8( KdBasePort + COM_LSR ) & COM_OUTRDY) == 0) { + // nop; + } // write a single char KdWriteInt8(KdBasePort + COM_DAT, Output); - KDDBG2("KdCompPutByte done\n"); -} - - -// Fetch a byte from the debug port and return it if one is available. -// N.B. It is assumed that necessary multiprocessor synchronization has been -// performed before this routine is called. -// -static KDP_STATUS KdCompPollByte(OUT PUCHAR Input) -{ - KDDBG2("KdCompPollByte\n"); - KDP_STATUS status = CpGetByte(Input, FALSE); - KDDBG2("KdCompPollByte %d\n", status); - return status; -} - -// Wait for a packet header leader (receive it into PacketLeader ULONG). -// -static -KDP_STATUS -KdCompReceivePacketLeader( - OUT PULONG PacketLeader, - IN OUT PKD_CONTEXT KdContext - ) -{ - - UCHAR Input; - UCHAR PreviousByte = 0; - ULONG PacketId = 0; - ULONG Index; - KDP_STATUS ReturnCode; - BOOLEAN BreakinDetected = FALSE; - - KDDBG2("KdCompReceivePacketLeader\n"); - // - // NOTE - With all the interrupts being off, it is very hard - // to implement the actual timeout code. (Maybe, by reading the CMOS.) - // Here we use a loop count to wait about 3 seconds. The CpGetByte - // will return with error code = KDP_PACKET_TIMEOUT if it cannot find data - // byte within 1 second. Kernel debugger's timeout period is 5 seconds. - // - - Index = 0; - do { - ReturnCode = KdCompGetByte(&Input); - if (ReturnCode == KDP_PACKET_TIMEOUT) { - if (BreakinDetected) { - KdContext->KdpControlCPending = TRUE; - return KDP_PACKET_RESEND; - } else { - KDDBG2("KdCompReceivePackerLeader returning KDP_PACKET_TIMEOUT\n"); - return KDP_PACKET_TIMEOUT; - } - } else if (ReturnCode == KDP_PACKET_RESEND) { - Index = 0; - continue; - } else { // if (ReturnCode == KDP_PACKET_RECEIVED) - if ( Input == PACKET_LEADER_BYTE || - Input == CONTROL_PACKET_LEADER_BYTE ) { - if ( Index == 0 ) { - PreviousByte = Input; - Index++; - } else if (Input == PreviousByte ) { - Index++; - } else { - PreviousByte = Input; - Index = 1; - } - } else { - - // - // If we detect breakin character, we need to verify it - // validity. (It is possible that we missed a packet leader - // and the breakin character is simply a data byte in the - // packet.) - // Since kernel debugger send out breakin character ONLY - // when it is waiting for State Change packet. The breakin - // character should not be followed by any other character - // except packet leader byte. - // - - if ( Input == BREAKIN_PACKET_BYTE ) { - BreakinDetected = TRUE; - } - else { - - // - // The following statement is ABSOLUTELY necessary. - // - - BreakinDetected = FALSE; - } - Index = 0; - } - } - } while ( Index < 4 ); - - if (BreakinDetected) { - KdContext->KdpControlCPending = TRUE; - } - - // - // return the packet leader and FALSE to indicate no resend is needed. - // - - if ( Input == PACKET_LEADER_BYTE ) { - *PacketLeader = PACKET_LEADER; - } - else { - *PacketLeader = CONTROL_PACKET_LEADER; - } - KdDebuggerNotPresent = FALSE; -#if 0 - SharedUserData->KdDebuggerEnabled |= 0x00000002; -#endif - - return KDP_PACKET_RECEIVED; -} - - -static -VOID -KdpSendString( - IN PCHAR Source, - IN ULONG Length - ) - // Routine Description: - // This routine writes a string to the kernel debugger port. - // - // Arguments: - // Source - Supplies a pointer to the output string. - // Length - Supplies the length of the string to be written. - // - // Return Value: - // None. -{ - - UCHAR Output; - - KDDBG2("KdpSendString len %d\n", Length); - - // - // Write bytes to the kernel debugger port. - // - - while (Length > 0) { - Output = *Source++; - KdCompPutByte(Output); - Length -= 1; - } - KDDBG2("KdpSendString done\n"); - return; -} - -static -KDP_STATUS -KdpReceiveString( - OUT PCHAR Destination, - IN ULONG Length - ) - // Routine Description: - // This routine reads a string from the kernel debugger port. - // - // Arguments: - // Destination - Supplies a pointer to the input string. - // Length - Supplies the length of the string to be read. -{ - - UCHAR Input; - KDP_STATUS ReturnCode; - KDDBG2("KdpReceiveString len %d\n", Length); - - // - // Read bytes until either an error is encountered or the entire string - // has been read. - // - while (Length > 0) { - KdpSpin(); - - ReturnCode = KdCompGetByte(&Input); - if (ReturnCode != KDP_PACKET_RECEIVED) { - KDDBG("KdpReceiveString return %d\n", ReturnCode); - return ReturnCode; - } - else { - *Destination++ = Input; - Length -= 1; - } - } - KDDBG2("KdpReceiveString return %d\n", KDP_PACKET_RECEIVED); - return KDP_PACKET_RECEIVED; -} - -static -VOID -KdpSendControlPacket( - IN USHORT PacketType, - IN ULONG PacketId OPTIONAL - ) - // Routine Description: - // This routine sends a control packet to the host machine that is running the - // kernel debugger and waits for an ACK. - // - // Arguments: - // PacketType - Supplies the type of packet to send. - // PacketId - Supplies packet id, optionally. - // - // Return Value: - // None. -{ - - KD_PACKET PacketHeader; - - // - // Initialize and send the packet header. - // - - PacketHeader.PacketLeader = CONTROL_PACKET_LEADER; - if (PacketId != 0) { - PacketHeader.PacketId = PacketId; - } - PacketHeader.ByteCount = 0; - PacketHeader.Checksum = 0; - PacketHeader.PacketType = PacketType; - KdpSendString((PCHAR)&PacketHeader, sizeof(KD_PACKET)); - - return; -} - -////////////////////////////////////////////////////////////////////////////// -// -void -KdpComSendPacket( - IN ULONG PacketType, - IN PSTRING MessageHeader, - IN PSTRING MessageData OPTIONAL, - IN OUT PKD_CONTEXT KdContext - ) - // Routine Description: - // This routine sends a packet to the host machine that is running the - // kernel debugger and waits for an ACK. - // - // Arguments: - // PacketType - Supplies the type of packet to send. - // MessageHeader - Supplies a pointer to a string descriptor that describes - // the message information. - // MessageData - Supplies a pointer to a string descriptor that describes - // the optional message data. - // KdContext - Supplies a pointer to the kernel debugger context. - // - // Return Value: - // None. -{ - - KD_PACKET PacketHeader; - ULONG MessageDataLength; - KDP_STATUS ReturnCode; - KDDBG2("KdpComSendPacket %d\n", PacketType); - - if (MessageData != NULL) { - MessageDataLength = MessageData->Length; - PacketHeader.Checksum = KdpComputeChecksum(MessageData->Buffer, - MessageData->Length); - } - else { - MessageDataLength = 0; - PacketHeader.Checksum = 0; - } - PacketHeader.Checksum += KdpComputeChecksum(MessageHeader->Buffer, - MessageHeader->Length); - - // - // Initialize and send the packet header. - // - - PacketHeader.PacketLeader = PACKET_LEADER; - PacketHeader.ByteCount = (USHORT)(MessageHeader->Length + MessageDataLength); - PacketHeader.PacketType = (USHORT)PacketType; - - KdCompNumberRetries = KdCompRetryCount; - - // - // We sync on first STATE_CHANGE64 message like NT. If this - // is the first such message, drain receive pipe as nothing - // said before this instant is interesting (and any buffered - // packets may interact badly with SendWaitContinue). - // - if (PacketType == PACKET_TYPE_KD_STATE_CHANGE64 && !KdStateChange64Sent) { - // - UCHAR uDummy; - DWORD dwDrained = 0; - KdCompNextPacketIdToSend |= SYNC_PACKET_ID; - KdStateChange64Sent = TRUE; - - while (KdCompGetByte(&uDummy) == KDP_PACKET_RECEIVED) - dwDrained++; - } - - do { - KDDBG2("LOOP %d/%d\n", KdCompNumberRetries, KdCompRetryCount); - if (KdCompNumberRetries == 0) { - KDDBG("KdCompNumberRetries == 0\n"); - // - // If the packet is not for reporting exception, we give up - // and declare debugger not present. - // - if (PacketType == PACKET_TYPE_KD_STATE_CHANGE64) { - PDBGKD_ANY_WAIT_STATE_CHANGE StateChange - = (PDBGKD_ANY_WAIT_STATE_CHANGE)MessageHeader->Buffer; - if (StateChange->NewState == DbgKdLoadSymbolsStateChange) { - KdDebuggerNotPresent = TRUE; - //SharedUserData->KdDebuggerEnabled &= ~0x00000002; - KdCompNextPacketIdToSend = INITIAL_PACKET_ID | SYNC_PACKET_ID; - KdCompPacketIdExpected = INITIAL_PACKET_ID; - return; - } - } - else if (PacketType == PACKET_TYPE_KD_DEBUG_IO) { - PDBGKD_DEBUG_IO DebugIo - = (PDBGKD_DEBUG_IO)MessageHeader->Buffer; - if (DebugIo->ApiNumber == DbgKdPrintStringApi) { - KdDebuggerNotPresent = TRUE; - //SharedUserData->KdDebuggerEnabled &= ~0x00000002; - KdCompNextPacketIdToSend = INITIAL_PACKET_ID | SYNC_PACKET_ID; - KdCompPacketIdExpected = INITIAL_PACKET_ID; - return; - } - } -#if 0 - else if (PacketType == PACKET_TYPE_KD_FILE_IO) { - PDBGKD_FILE_IO FileIo; - - FileIo = (PDBGKD_FILE_IO)MessageHeader->Buffer; - if (FileIo->ApiNumber == DbgKdCreateFileApi) { - KdDebuggerNotPresent = TRUE; - //SharedUserData->KdDebuggerEnabled &= ~0x00000002; - KdCompNextPacketIdToSend = INITIAL_PACKET_ID | SYNC_PACKET_ID; - KdCompPacketIdExpected = INITIAL_PACKET_ID; - return; - } - } -#endif - } - // - // Setting PacketId has to be in the do loop in case Packet Id was - // reset. - // - - PacketHeader.PacketId = KdCompNextPacketIdToSend; - KdpSendString((PCHAR)&PacketHeader, sizeof(KD_PACKET)); - - // - // Output message header. - // - - KdpSendString(MessageHeader->Buffer, MessageHeader->Length); - - // - // Output message data. - // - - if ( MessageDataLength ) { - KdpSendString(MessageData->Buffer, MessageData->Length); - } - - // - // Output a packet trailing byte - // - - KdCompPutByte(PACKET_TRAILING_BYTE); - - // - // Wait for the Ack Packet - // - - ReturnCode = KdpComReceivePacket( - PACKET_TYPE_KD_ACKNOWLEDGE, - NULL, - NULL, - NULL, - KdContext - ); - if (ReturnCode == KDP_PACKET_TIMEOUT) { - KDDBG2("TIMEOUT\n"); - KdCompNumberRetries--; - } - KdpSpin(); - } while (ReturnCode != KDP_PACKET_RECEIVED); - - KDDBG2("KD: PACKET_RECEIVED\n"); - // - // Reset Sync bit in packet id. The packet we sent may have Sync bit set - // - - KdCompNextPacketIdToSend &= ~SYNC_PACKET_ID; - - // - // Since we are able to talk to debugger, the retrycount is set to - // maximum value. - // - - KdCompRetryCount = KdContext->KdpDefaultRetries; - - KDDBG2("KdpComSendPacket %d done\n", PacketType); -} - -////////////////////////////////////////////////////////////////////////////// -// -KDP_STATUS -KdpComReceivePacket( - IN ULONG PacketType, - OUT PSTRING MessageHeader, - OUT PSTRING MessageData, - OUT PULONG DataLength, - IN OUT PKD_CONTEXT KdContext - ) - // Routine Description: - // This routine receives a packet from the host machine that is running - // the kernel debugger UI. This routine is ALWAYS called after packet being - // sent by caller. It first waits for ACK packet for the packet sent and - // then waits for the packet desired. - // - // N.B. If caller is KdPrintString, the parameter PacketType is - // PACKET_TYPE_KD_ACKNOWLEDGE. In this case, this routine will return - // right after the ack packet is received. - // - // Arguments: - // PacketType - Supplies the type of packet that is excepted. - // MessageHeader - Supplies a pointer to a string descriptor for the input - // message. - // MessageData - Supplies a pointer to a string descriptor for the input data. - // DataLength - Supplies pointer to ULONG to receive length of recv. data. - // KdContext - Supplies a pointer to the kernel debugger context. - // - // Return Value: - // KDP_PACKET_RESEND - if resend is required. - // KDP_PAKCET_TIMEOUT - if timeout. - // KDP_PACKET_RECEIVED - if packet received. -{ - - UCHAR Input; - ULONG MessageLength; - KD_PACKET PacketHeader; - KDP_STATUS ReturnCode; - ULONG Checksum; - - KDDBG2("KdpComReceivePacket %d\n", PacketType); - - WaitForPacketLeader: - - KdpSpin(); - - // - // Read Packet Leader - // - ReturnCode = KdCompReceivePacketLeader(&PacketHeader.PacketLeader, KdContext); - KDDBG2("KdCompReceivePacketLeader returned %d\n", ReturnCode); - - // - // If we can successfully read packet leader, it has high possibility that - // kernel debugger is alive. So reset count. - // - if (ReturnCode != KDP_PACKET_TIMEOUT) { - KdCompNumberRetries = KdCompRetryCount; - } - if (ReturnCode != KDP_PACKET_RECEIVED) { - return ReturnCode; - } - - // - // Read packet type. - // - ReturnCode = KdpReceiveString((PCHAR)&PacketHeader.PacketType, - sizeof(PacketHeader.PacketType)); - if (ReturnCode == KDP_PACKET_TIMEOUT) { - return KDP_PACKET_TIMEOUT; - } - else if (ReturnCode == KDP_PACKET_RESEND) { - if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER) { - // - // If read error and it is for a control packet, simply - // pretend that we have not seen this packet. Hopefully - // we will receive the packet we desire which automatically acks - // the packet we just sent. - // - goto WaitForPacketLeader; - } - else { - // - // if read error while reading data packet, we have to ask - // kernel debugger to resend us the packet. - // - goto SendResendPacket; - } - } - - // - // if the packet we received is a resend request, we return true and - // let caller resend the packet. - // - if ( PacketHeader.PacketLeader == CONTROL_PACKET_LEADER && - PacketHeader.PacketType == PACKET_TYPE_KD_RESEND ) { - return KDP_PACKET_RESEND; - } - - // - // Read data length. - // - ReturnCode = KdpReceiveString((PCHAR)&PacketHeader.ByteCount, - sizeof(PacketHeader.ByteCount)); - if (ReturnCode == KDP_PACKET_TIMEOUT) { - return KDP_PACKET_TIMEOUT; - } - else if (ReturnCode == KDP_PACKET_RESEND) { - if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER) { - goto WaitForPacketLeader; - } - else { - goto SendResendPacket; - } - } - - // - // Read Packet Id. - // - ReturnCode = KdpReceiveString((PCHAR)&PacketHeader.PacketId, - sizeof(PacketHeader.PacketId)); - - if (ReturnCode == KDP_PACKET_TIMEOUT) { - return KDP_PACKET_TIMEOUT; - } - else if (ReturnCode == KDP_PACKET_RESEND) { - if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER) { - goto WaitForPacketLeader; - } - else { - goto SendResendPacket; - } - } - - // - // Read packet checksum. - // - ReturnCode = KdpReceiveString((PCHAR)&PacketHeader.Checksum, - sizeof(PacketHeader.Checksum)); - if (ReturnCode == KDP_PACKET_TIMEOUT) { - return KDP_PACKET_TIMEOUT; - } - else if (ReturnCode == KDP_PACKET_RESEND) { - if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER) { - goto WaitForPacketLeader; - } - else { - goto SendResendPacket; - } - } - - // - // A complete packet header is received. Check its validity and - // perform appropriate action depending on packet type. - // - if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER ) { - if (PacketHeader.PacketType == PACKET_TYPE_KD_ACKNOWLEDGE ) { - // - // If we received an expected ACK packet and we are not - // waiting for any new packet, update outgoing packet id - // and return. If we are NOT waiting for ACK packet - // we will keep on waiting. If the ACK packet - // is not for the packet we send, ignore it and keep on waiting. - // - if (PacketHeader.PacketId != - (KdCompNextPacketIdToSend & ~SYNC_PACKET_ID)) { - goto WaitForPacketLeader; - } - else if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) { - KdCompNextPacketIdToSend ^= 1; - return KDP_PACKET_RECEIVED; - } else { - goto WaitForPacketLeader; - } - } - else if (PacketHeader.PacketType == PACKET_TYPE_KD_RESET) { - // - // if we received Reset packet, reset the packet control variables - // and resend earlier packet. - // - KdCompNextPacketIdToSend = INITIAL_PACKET_ID; - KdCompPacketIdExpected = INITIAL_PACKET_ID; - KdpSendControlPacket(PACKET_TYPE_KD_RESET, 0L); - return KDP_PACKET_RESEND; - } - else if (PacketHeader.PacketType == PACKET_TYPE_KD_RESEND) { - return KDP_PACKET_RESEND; - } - else { - // - // Invalid packet header, ignore it. - // - goto WaitForPacketLeader; - } - - // - // The packet header is for data packet (not control packet). - // - - } - else if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) { - // - // if we are waiting for ACK packet ONLY - // and we receive a data packet header, check if the packet id - // is what we expected. If yes, assume the acknowledge is lost (but - // sent), ask sender to resend and return with PACKET_RECEIVED. - // - if (PacketHeader.PacketId == KdCompPacketIdExpected) { - KdpSendControlPacket(PACKET_TYPE_KD_RESEND, 0L); - KdCompNextPacketIdToSend ^= 1; - return KDP_PACKET_RECEIVED; - } - else { - KdpSendControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE, - PacketHeader.PacketId); - goto WaitForPacketLeader; - } - } - - // - // we are waiting for data packet and we received the packet header - // for data packet. Perform the following checks to make sure - // it is the packet we are waiting for. - // - - // - // Check ByteCount received is valid - // - MessageLength = MessageHeader->MaximumLength; - if ((PacketHeader.ByteCount > (USHORT)PACKET_MAX_SIZE) || - (PacketHeader.ByteCount < (USHORT)MessageLength)) { - goto SendResendPacket; - } - *DataLength = PacketHeader.ByteCount - MessageLength; - - // - // Read the message header. - // - ReturnCode = KdpReceiveString(MessageHeader->Buffer, MessageLength); - if (ReturnCode != KDP_PACKET_RECEIVED) { - goto SendResendPacket; - } - MessageHeader->Length = (USHORT)MessageLength; - - // - // Read the message data. - // - ReturnCode = KdpReceiveString(MessageData->Buffer, *DataLength); - if (ReturnCode != KDP_PACKET_RECEIVED) { - goto SendResendPacket; - } - MessageData->Length = (USHORT)*DataLength; - - // - // Read packet trailing byte - // - ReturnCode = KdCompGetByte(&Input); - if (ReturnCode != KDP_PACKET_RECEIVED || Input != PACKET_TRAILING_BYTE) { - goto SendResendPacket; - } - - // - // Check PacketType is what we are waiting for. - // - if (PacketType != PacketHeader.PacketType) { - KdpSendControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE, - PacketHeader.PacketId); - goto WaitForPacketLeader; - } - - // - // Check PacketId is valid. - // - if (PacketHeader.PacketId == INITIAL_PACKET_ID || - PacketHeader.PacketId == (INITIAL_PACKET_ID ^ 1)) { - if (PacketHeader.PacketId != KdCompPacketIdExpected) { - KdpSendControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE, - PacketHeader.PacketId); - goto WaitForPacketLeader; - } - } - else { - goto SendResendPacket; - } - - // - // Check checksum is valid. - // - Checksum = KdpComputeChecksum(MessageHeader->Buffer, - MessageHeader->Length); - Checksum += KdpComputeChecksum(MessageData->Buffer, - MessageData->Length); - if (Checksum != PacketHeader.Checksum) { - goto SendResendPacket; - } - - // - // Send Acknowledge byte and the Id of the packet received. - // Then, update the ExpectId for next incoming packet. - // - KdpSendControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE, - PacketHeader.PacketId); - - // - // We have successfully received the packet so update the - // packet control variables and return success. - // - KdCompPacketIdExpected ^= 1; - KDDBG2("KdpComReceivePacket - got one!\n"); - return KDP_PACKET_RECEIVED; - - SendResendPacket: - KdpSendControlPacket(PACKET_TYPE_KD_RESEND, 0L); - goto WaitForPacketLeader; -} - -// Returns TRUE if a breakin packet is pending. -// A packet is present if: There is a valid character which matches BREAK_CHAR. -bool KdpComPollBreakIn() -{ - KDDBG2("KdpComPollBreakIn\n"); - UCHAR Input; - ULONG Status = KdCompPollByte(&Input); - KDDBG2("KdCompPollByte STATUS %d Input %02x\n", Status, Input); - if ((Status == KDP_PACKET_RECEIVED) && (Input == BREAKIN_PACKET_BYTE)) { - KDDBG("KDP_PACKET_RECEIVED\n"); - KdDebuggerNotPresent = FALSE; - return true; - } - return false; } // ///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/HalKdSerial.cpp b/base/Kernel/Native/HalKdSerial.cpp new file mode 100644 index 0000000..36ffc23 --- /dev/null +++ b/base/Kernel/Native/HalKdSerial.cpp @@ -0,0 +1,791 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: halkdserial.cpp: Serial-port transport routines. +// +// Note: Kernel Only +// + +#include "hal.h" +#include "halkd.h" + +// +// Debugger Debugging +// +#define KDDBG if (0) kdprintf +#define KDDBG2 if (0) kdprintf + +// +// Globals +// +static UINT32 KdCompPacketIdExpected = INITIAL_PACKET_ID; +static UINT32 KdCompNextPacketIdToSend = INITIAL_PACKET_ID | SYNC_PACKET_ID; +static BOOL KdStateChange64Sent = false; + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// +// PLATFORM LAYER - this is the cut point for abstracting the serial port +// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + + +KDP_STATUS KdpSerialGetByte(OUT PUCHAR Input, BOOL WaitForByte); +void KdpSerialPutByte(IN UCHAR Output); + +//++ +// +//Routine Description: +// +// This routine reads a string from the kernel debugger port. +// +//Arguments: +// +// Destination - Supplies a pointer to the input string. +// +// Length - Supplies the length of the string to be read. +//-- + +static KDP_STATUS KdpSerialReceiveString(OUT PUCHAR Destination, IN UINT32 Length, IN BOOL WaitForInput = true) +{ + KDDBG2("KdpSerialReceiveString len %d\n", Length); + + UCHAR Input; + UINT32 ReqLength = Length; + UINT32 ReturnCode; + + // + // Read bytes until either a error is encountered or the entire string + // has been read. + // + while (Length > 0) { + KdpSpin(); + + ReturnCode = KdpSerialGetByte(&Input, WaitForInput); + if (ReturnCode != KDP_PACKET_RECEIVED) { + break; + } + else { + *Destination++ = Input; + Length -= 1; + } + } + + KDDBG2("KdpSerialReceiveString left %d\n", Length); + + return (Length == 0) ? KDP_PACKET_RECEIVED : KDP_PACKET_TIMEOUT; +} + +//++ +// +//Routine Description: +// +// This routine writes a string to the kernel debugger port. +// +//Arguments: +// +// Source - Supplies a pointer to the output string. +// +// Length - Supplies the length of the string to be written. +// +//Return Value: +// +// None. +// +//-- + +static void KdpSerialSendString(IN PUCHAR Source, IN UINT32 Length) +{ + + KDDBG2("KdpSerialSendString len %d\n", Length); + + UINT32 ReqLength = (UINT32)Length; + + // + // Write bytes to the kernel debugger port. + // + + UCHAR Output; + + while (Length > 0) { + Output = *Source++; + KdpSerialPutByte(Output); + Length -= 1; + } + + KDDBG2("KdpSerialSendString done\n"); +} + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// +// CHANNEL LAYER - all this code should be neutral to the actual channel +// (i.e. nothing specific to serial port, only reading and writing bytes) +// +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +// Wait for a packet header leader (receive it into PacketLeader ULONG). +// +static +KDP_STATUS +KdCompReceivePacketLeader( + OUT UINT32 *PacketLeader, + IN OUT PKD_CONTEXT KdContext + ) +{ + + UCHAR Input; + UCHAR PreviousByte = 0; + UINT32 PacketId = 0; + UINT32 Index; + KDP_STATUS ReturnCode; + BOOLEAN BreakinDetected = false; + + KDDBG2("KdCompReceivePacketLeader\n"); + // + // NOTE - With all the interrupts being off, it is very hard + // to implement the actual timeout code. (Maybe, by reading the CMOS.) + // Here we use a loop count to wait about 3 seconds. The CpGetByte + // will return with error code = KDP_PACKET_TIMEOUT if it cannot find data + // byte within 1 second. Kernel debugger's timeout period is 5 seconds. + // + + Index = 0; + do { + ReturnCode = KdpSerialReceiveString(&Input, 1); + if (ReturnCode == KDP_PACKET_TIMEOUT) { + if (BreakinDetected) { + KdContext->KdpControlCPending = true; + return KDP_PACKET_RESEND; + } + else { + KDDBG2("KdCompReceivePackerLeader returning KDP_PACKET_TIMEOUT\n"); + return KDP_PACKET_TIMEOUT; + } + } + else if (ReturnCode == KDP_PACKET_RESEND) { + Index = 0; + continue; + } + else { // if (ReturnCode == KDP_PACKET_RECEIVED) + if ( Input == PACKET_LEADER_BYTE || + Input == CONTROL_PACKET_LEADER_BYTE ) { + if ( Index == 0 ) { + PreviousByte = Input; + Index++; + } + else if (Input == PreviousByte) { + Index++; + } + else { + PreviousByte = Input; + Index = 1; + } + } + else { + + // + // If we detect breakin character, we need to verify it + // validity. (It is possible that we missed a packet leader + // and the breakin character is simply a data byte in the + // packet.) + // Since kernel debugger send out breakin character ONLY + // when it is waiting for State Change packet. The breakin + // character should not be followed by any other character + // except packet leader byte. + // + + if ( Input == BREAKIN_PACKET_BYTE ) { + BreakinDetected = true; + } + else { + + // + // The following statement is ABSOLUTELY necessary. + // + + BreakinDetected = false; + } + Index = 0; + } + } + } while ( Index < 4 ); + + if (BreakinDetected) { + KdContext->KdpControlCPending = true; + } + + // + // return the packet leader and false to indicate no resend is needed. + // + + if ( Input == PACKET_LEADER_BYTE ) { + *PacketLeader = PACKET_LEADER; + } + else { + *PacketLeader = CONTROL_PACKET_LEADER; + } + KdDebuggerNotPresent = false; +#if 0 + SharedUserData->KdDebuggerEnabled |= 0x00000002; +#endif + + return KDP_PACKET_RECEIVED; +} + + +static +void +KdpSendControlPacket( + IN UINT16 PacketType, + IN UINT32 PacketId OPTIONAL + ) + // Routine Description: + // This routine sends a control packet to the host machine that is running the + // kernel debugger and waits for an ACK. + // + // Arguments: + // PacketType - Supplies the type of packet to send. + // PacketId - Supplies packet id, optionally. + // + // Return Value: + // None. +{ + + KD_PACKET PacketHeader; + + // + // Initialize and send the packet header. + // + + PacketHeader.PacketLeader = CONTROL_PACKET_LEADER; + if (PacketId != 0) { + PacketHeader.PacketId = PacketId; + } + PacketHeader.ByteCount = 0; + PacketHeader.Checksum = 0; + PacketHeader.PacketType = PacketType; + KdpSerialSendString((PUCHAR)&PacketHeader, sizeof(KD_PACKET)); + + return; +} + +////////////////////////////////////////////////////////////////////////////// +// +void +KdpSerialSendPacket( + IN UINT32 PacketType, + IN PSTRING MessageHeader, + IN PSTRING MessageData OPTIONAL, + IN OUT PKD_CONTEXT KdContext + ) + // Routine Description: + // This routine sends a packet to the host machine that is running the + // kernel debugger and waits for an ACK. + // + // Arguments: + // PacketType - Supplies the type of packet to send. + // MessageHeader - Supplies a pointer to a string descriptor that describes + // the message information. + // MessageData - Supplies a pointer to a string descriptor that describes + // the optional message data. + // KdContext - Supplies a pointer to the kernel debugger context. + // + // Return Value: + // None. +{ + + KD_PACKET PacketHeader; + UINT32 MessageDataLength; + KDP_STATUS ReturnCode; + KDDBG2("KdpSerialSendPacket %d\n", PacketType); + + if (MessageData != NULL) { + MessageDataLength = MessageData->Length; + PacketHeader.Checksum = KdpComputeChecksum(MessageData->Buffer, + MessageData->Length); + } + else { + MessageDataLength = 0; + PacketHeader.Checksum = 0; + } + PacketHeader.Checksum += KdpComputeChecksum(MessageHeader->Buffer, + MessageHeader->Length); + + // + // Initialize and send the packet header. + // + + PacketHeader.PacketLeader = PACKET_LEADER; + PacketHeader.ByteCount = (UINT16)(MessageHeader->Length + MessageDataLength); + PacketHeader.PacketType = (UINT16)PacketType; + + KdCompNumberRetries = KdCompRetryCount; + + // + // We sync on first STATE_CHANGE64 message like NT. If this + // is the first such message, drain receive pipe as nothing + // said before this instant is interesting (and any buffered + // packets may interact badly with SendWaitContinue). + // + if (PacketType == PACKET_TYPE_KD_STATE_CHANGE64 && !KdStateChange64Sent) { + // + UCHAR uDummy; + uint32 dwDrained = 0; + KdCompNextPacketIdToSend |= SYNC_PACKET_ID; + KdStateChange64Sent = true; + + while (KdpSerialReceiveString(&uDummy, 1, false) == KDP_PACKET_RECEIVED) { + dwDrained++; + } + } + + do { + KDDBG2("LOOP %d/%d\n", KdCompNumberRetries, KdCompRetryCount); + if (KdCompNumberRetries == 0) { + KDDBG("KdCompNumberRetries == 0\n"); + // + // If the packet is not for reporting exception, we give up + // and declare debugger not present. + // + if (PacketType == PACKET_TYPE_KD_STATE_CHANGE64) { + PDBGKD_ANY_WAIT_STATE_CHANGE StateChange + = (PDBGKD_ANY_WAIT_STATE_CHANGE)MessageHeader->Buffer; + if (StateChange->NewState == DbgKdLoadSymbolsStateChange) { + KdDebuggerNotPresent = true; + //SharedUserData->KdDebuggerEnabled &= ~0x00000002; + KdCompNextPacketIdToSend = INITIAL_PACKET_ID | SYNC_PACKET_ID; + KdCompPacketIdExpected = INITIAL_PACKET_ID; + return; + } + } + else if (PacketType == PACKET_TYPE_KD_DEBUG_IO) { + PDBGKD_DEBUG_IO DebugIo + = (PDBGKD_DEBUG_IO)MessageHeader->Buffer; + if (DebugIo->ApiNumber == DbgKdPrintStringApi) { + KdDebuggerNotPresent = true; + //SharedUserData->KdDebuggerEnabled &= ~0x00000002; + KdCompNextPacketIdToSend = INITIAL_PACKET_ID | SYNC_PACKET_ID; + KdCompPacketIdExpected = INITIAL_PACKET_ID; + return; + } + } + else if (PacketType == PACKET_TYPE_KD_FILE_IO) { + PDBGKD_FILE_IO FileIo; + + FileIo = (PDBGKD_FILE_IO)MessageHeader->Buffer; + if (FileIo->ApiNumber == DbgKdCreateFileApi) { + KdDebuggerNotPresent = true; + //SharedUserData->KdDebuggerEnabled &= ~0x00000002; + KdCompNextPacketIdToSend = INITIAL_PACKET_ID | SYNC_PACKET_ID; + KdCompPacketIdExpected = INITIAL_PACKET_ID; + return; + } + } + } + // + // Setting PacketId has to be in the do loop in case Packet Id was + // reset. + // + + PacketHeader.PacketId = KdCompNextPacketIdToSend; + KdpSerialSendString((PUCHAR)&PacketHeader, sizeof(KD_PACKET)); + + // + // Output message header. + // + + KdpSerialSendString((PUCHAR)MessageHeader->Buffer, MessageHeader->Length); + + // + // Output message data. + // + + if ( MessageDataLength ) { + KdpSerialSendString((PUCHAR)MessageData->Buffer, MessageData->Length); + } + + // + // Output a packet trailing byte + // + + { + UCHAR b = PACKET_TRAILING_BYTE; + KdpSerialSendString(&b, 1); + } + + // + // Wait for the Ack Packet + // + + ReturnCode = KdpSerialReceivePacket( + PACKET_TYPE_KD_ACKNOWLEDGE, + NULL, + NULL, + NULL, + KdContext + ); + if (ReturnCode == KDP_PACKET_TIMEOUT) { + KDDBG2("TIMEOUT\n"); + KdCompNumberRetries--; + } + } while (ReturnCode != KDP_PACKET_RECEIVED); + + KDDBG2("KD: PACKET_RECEIVED\n"); + // + // Reset Sync bit in packet id. The packet we sent may have Sync bit set + // + + KdCompNextPacketIdToSend &= ~SYNC_PACKET_ID; + + // + // Since we are able to talk to debugger, the retrycount is set to + // maximum value. + // + + KdCompRetryCount = KdContext->KdpDefaultRetries; + + KDDBG2("KdpSerialSendPacket %d done\n", PacketType); +} + +////////////////////////////////////////////////////////////////////////////// +// +KDP_STATUS +KdpSerialReceivePacket( + IN UINT32 PacketType, + OUT PSTRING MessageHeader, + OUT PSTRING MessageData, + OUT UINT32 *DataLength, + IN OUT PKD_CONTEXT KdContext + ) + // Routine Description: + // This routine receives a packet from the host machine that is running + // the kernel debugger UI. This routine is ALWAYS called after packet being + // sent by caller. It first waits for ACK packet for the packet sent and + // then waits for the packet desired. + // + // N.B. If caller is KdPrintString, the parameter PacketType is + // PACKET_TYPE_KD_ACKNOWLEDGE. In this case, this routine will return + // right after the ack packet is received. + // + // Arguments: + // PacketType - Supplies the type of packet that is excepted. + // MessageHeader - Supplies a pointer to a string descriptor for the input + // message. + // MessageData - Supplies a pointer to a string descriptor for the input data. + // DataLength - Supplies pointer to UINT32 to receive length of recv. data. + // KdContext - Supplies a pointer to the kernel debugger context. + // + // Return Value: + // KDP_PACKET_RESEND - if resend is required. + // KDP_PAKCET_TIMEOUT - if timeout. + // KDP_PACKET_RECEIVED - if packet received. +{ + + UCHAR Input; + UINT32 MessageLength; + KD_PACKET PacketHeader; + KDP_STATUS ReturnCode; + UINT32 Checksum; + + KDDBG2("KdpSerialReceivePacket %d\n", PacketType); + + WaitForPacketLeader: + + // + // Read Packet Leader + // + ReturnCode = KdCompReceivePacketLeader(&PacketHeader.PacketLeader, KdContext); + KDDBG2("KdCompReceivePacketLeader returned %d\n", ReturnCode); + + // + // If we can successfully read packet leader, it has high possibility that + // kernel debugger is alive. So reset count. + // + if (ReturnCode != KDP_PACKET_TIMEOUT) { + KdCompNumberRetries = KdCompRetryCount; + } + if (ReturnCode != KDP_PACKET_RECEIVED) { + return ReturnCode; + } + + // + // Read packet type. + // + + ReturnCode = KdpSerialReceiveString((PUCHAR)&PacketHeader.PacketType, + sizeof(PacketHeader.PacketType)); + + if (ReturnCode == KDP_PACKET_TIMEOUT) { + return KDP_PACKET_TIMEOUT; + } + else if (ReturnCode == KDP_PACKET_RESEND) { + if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER) { + // + // If read error and it is for a control packet, simply + // pretend that we have not seen this packet. Hopefully + // we will receive the packet we desire which automatically acks + // the packet we just sent. + // + goto WaitForPacketLeader; + } + else { + // + // if read error while reading data packet, we have to ask + // kernel debugger to resend us the packet. + // + goto SendResendPacket; + } + } + + // + // if the packet we received is a resend request, we return true and + // let caller resend the packet. + // + if ( PacketHeader.PacketLeader == CONTROL_PACKET_LEADER && + PacketHeader.PacketType == PACKET_TYPE_KD_RESEND ) { + return KDP_PACKET_RESEND; + } + + // + // Read data length. + // + ReturnCode = KdpSerialReceiveString((PUCHAR)&PacketHeader.ByteCount, + sizeof(PacketHeader.ByteCount)); + if (ReturnCode == KDP_PACKET_TIMEOUT) { + return KDP_PACKET_TIMEOUT; + } + else if (ReturnCode == KDP_PACKET_RESEND) { + if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER) { + goto WaitForPacketLeader; + } + else { + goto SendResendPacket; + } + } + + // + // Read Packet Id. + // + ReturnCode = KdpSerialReceiveString((PUCHAR)&PacketHeader.PacketId, + sizeof(PacketHeader.PacketId)); + + if (ReturnCode == KDP_PACKET_TIMEOUT) { + return KDP_PACKET_TIMEOUT; + } + else if (ReturnCode == KDP_PACKET_RESEND) { + if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER) { + goto WaitForPacketLeader; + } + else { + goto SendResendPacket; + } + } + + // + // Read packet checksum. + // + ReturnCode = KdpSerialReceiveString((PUCHAR)&PacketHeader.Checksum, + sizeof(PacketHeader.Checksum)); + if (ReturnCode == KDP_PACKET_TIMEOUT) { + return KDP_PACKET_TIMEOUT; + } + else if (ReturnCode == KDP_PACKET_RESEND) { + if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER) { + goto WaitForPacketLeader; + } + else { + goto SendResendPacket; + } + } + + // + // A complete packet header is received. Check its validity and + // perform appropriate action depending on packet type. + // + if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER ) { + if (PacketHeader.PacketType == PACKET_TYPE_KD_ACKNOWLEDGE ) { + // + // If we received an expected ACK packet and we are not + // waiting for any new packet, update outgoing packet id + // and return. If we are NOT waiting for ACK packet + // we will keep on waiting. If the ACK packet + // is not for the packet we send, ignore it and keep on waiting. + // + if (PacketHeader.PacketId != + (KdCompNextPacketIdToSend & ~SYNC_PACKET_ID)) { + goto WaitForPacketLeader; + } + else if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) { + KdCompNextPacketIdToSend ^= 1; + return KDP_PACKET_RECEIVED; + } else { + goto WaitForPacketLeader; + } + } + else if (PacketHeader.PacketType == PACKET_TYPE_KD_RESET) { + // + // if we received Reset packet, reset the packet control variables + // and resend earlier packet. + // + KdCompNextPacketIdToSend = INITIAL_PACKET_ID; + KdCompPacketIdExpected = INITIAL_PACKET_ID; + KdpSendControlPacket(PACKET_TYPE_KD_RESET, 0L); + return KDP_PACKET_RESEND; + } + else if (PacketHeader.PacketType == PACKET_TYPE_KD_RESEND) { + return KDP_PACKET_RESEND; + } + else { + // + // Invalid packet header, ignore it. + // + goto WaitForPacketLeader; + } + + // + // The packet header is for data packet (not control packet). + // + + } + else if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) { + // + // if we are waiting for ACK packet ONLY + // and we receive a data packet header, check if the packet id + // is what we expected. If yes, assume the acknowledge is lost (but + // sent), ask sender to resend and return with PACKET_RECEIVED. + // + if (PacketHeader.PacketId == KdCompPacketIdExpected) { + KdpSendControlPacket(PACKET_TYPE_KD_RESEND, 0L); + KdCompNextPacketIdToSend ^= 1; + return KDP_PACKET_RECEIVED; + } + else { + KdpSendControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE, + PacketHeader.PacketId); + goto WaitForPacketLeader; + } + } + + // + // we are waiting for data packet and we received the packet header + // for data packet. Perform the following checks to make sure + // it is the packet we are waiting for. + // + + // + // Check ByteCount received is valid + // + MessageLength = MessageHeader->MaximumLength; + if ((PacketHeader.ByteCount > (UINT16)PACKET_MAX_SIZE) || + (PacketHeader.ByteCount < (UINT16)MessageLength)) { + goto SendResendPacket; + } + *DataLength = PacketHeader.ByteCount - MessageLength; + + // + // Read the message header. + // + + ReturnCode = KdpSerialReceiveString((PUCHAR)MessageHeader->Buffer, MessageLength); + if (ReturnCode != KDP_PACKET_RECEIVED) { + goto SendResendPacket; + } + MessageHeader->Length = (UINT16)MessageLength; + + // + // Read the message data. + // + + ReturnCode = KdpSerialReceiveString((PUCHAR)MessageData->Buffer, *DataLength); + if (ReturnCode != KDP_PACKET_RECEIVED) { + goto SendResendPacket; + } + MessageData->Length = (UINT16)*DataLength; + + // + // Read packet trailing byte + // + + ReturnCode = KdpSerialReceiveString(&Input, 1); + if (ReturnCode != KDP_PACKET_RECEIVED || Input != PACKET_TRAILING_BYTE) { + goto SendResendPacket; + } + + // + // Check PacketType is what we are waiting for. + // + if (PacketType != PacketHeader.PacketType) { + KdpSendControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE, + PacketHeader.PacketId); + goto WaitForPacketLeader; + } + + // + // Check PacketId is valid. + // + if (PacketHeader.PacketId == INITIAL_PACKET_ID || + PacketHeader.PacketId == (INITIAL_PACKET_ID ^ 1)) { + if (PacketHeader.PacketId != KdCompPacketIdExpected) { + KdpSendControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE, + PacketHeader.PacketId); + goto WaitForPacketLeader; + } + } + else { + goto SendResendPacket; + } + + // + // Check checksum is valid. + // + Checksum = KdpComputeChecksum(MessageHeader->Buffer, + MessageHeader->Length); + Checksum += KdpComputeChecksum(MessageData->Buffer, + MessageData->Length); + if (Checksum != PacketHeader.Checksum) { + goto SendResendPacket; + } + + // + // Send Acknowledge byte and the Id of the packet received. + // Then, update the ExpectId for next incoming packet. + // + KdpSendControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE, + PacketHeader.PacketId); + + // + // We have successfully received the packet so update the + // packet control variables and return success. + // + KdCompPacketIdExpected ^= 1; + KDDBG2("KdpSerialReceivePacket - got one!\n"); + return KDP_PACKET_RECEIVED; + + SendResendPacket: + KdpSendControlPacket(PACKET_TYPE_KD_RESEND, 0L); + goto WaitForPacketLeader; +} + +// Returns true if a breakin packet is pending. +// A packet is present if: There is a valid character which matches BREAK_CHAR. +bool KdpSerialPollBreakIn() +{ + KDDBG2("KdpSerialPollBreakIn\n"); + UCHAR Input; + UINT32 Status = KdpSerialReceiveString(&Input, 1, false); + KDDBG2("KdpSerialPollByte STATUS %d Input %02x\n", Status, Input); + if ((Status == KDP_PACKET_RECEIVED) && (Input == BREAKIN_PACKET_BYTE)) { + KDDBG("KDP_PACKET_RECEIVED\n"); + KdDebuggerNotPresent = false; + return true; + } + return false; +} +// +///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/HalKdUart.cpp b/base/Kernel/Native/HalKdUart.cpp new file mode 100644 index 0000000..c319142 --- /dev/null +++ b/base/Kernel/Native/HalKdUart.cpp @@ -0,0 +1,222 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: halkduart.cpp: UART serial transport. +// +// Note: Kernel Only +// + +#include "hal.h" +#include "halkd.h" + +// +// Debugger Debugging +// +#define KDDBG if (0) kdprintf +#define KDDBG2 if (0) kdprintf + +///////////////////////////////////////////////////////////////// Serial Port. +// +#define OMAP_CLOCK_RATE 2995200 + +// Define COM Port registers. +#define COM_DAT 0x00 +#define COM_DLL 0x00 // Divisor Latch (LSB). +#define COM_IEN 0x01 // Interrupt enable register +#define COM_DLM 0x01 // Divisor Latch (MSB). +#define COM_FCR 0x02 // FIFO Control Register. +#define COM_LCR 0x03 // Line Control Register. +#define COM_MCR 0x04 // Modem Control Register. +#define COM_LSR 0x05 // Line Status Register. +#define COM_MSR 0x06 // Modem Status Register. +#define COM_SCR 0x07 // Scratch Register. + +#define OMAP_UART_MDR1 0x8 +#define OMAP_UART_IER 0x1 +#define OMAP_UART_EFR 0x2 + +// Define bits in the FIFO Control Register (FCR). +#define FCR_ENABLE 0x01 +#define FCR_CLEAR_RECEIVE 0x02 +#define FCR_CLEAR_TRANSMIT 0x04 + +// Define bits in the Line Control Register (LCR). +#define LCR_DATA_SIZE 0x03 +#define LCR_DLAB 0x80 + +// Define bits in the Modem Control Register (MCR). +#define MCR_DATA_TERMINAL_READY 0x01 +#define MCR_REQUEST_TO_SEND 0x02 +#define MCR_OUT1 0x04 +#define MCR_OUT2 0x08 +#define MCR_LOOPBACK 0x10 +#define MCR_INITIALIZE (MCR_DATA_TERMINAL_READY | MCR_REQUEST_TO_SEND) + +// Define bits in the Line Status Register (LSR). +#define LSR_DATA_AVAILABLE 0x01 +#define LSR_OVERRUN_ERROR 0x02 +#define LSR_PARITY_ERROR 0x04 +#define LSR_FRAMING_ERROR 0x08 +#define LSR_BREAK_SIGNAL 0x10 +#define LSR_THR_EMPTY 0x20 +#define LSR_THR_LINE_IDLE 0x40 + + +// Defined bits in the Modem Status Register (MSR). +#define MSR_DELTA_CLEAR_TO_SEND 0x01 +#define MSR_DELTA_DATA_SET_READY 0x02 +#define MSR_DELTA_RING_INDICATOR 0x04 +#define MSR_DELTA_CARRIER_DETECT 0x08 +#define MSR_CLEAR_TO_SEND 0x10 +#define MSR_DATA_SET_READY 0x20 +#define MSR_RING_INDICATOR 0x40 +#define MSR_CARRIER_DETECT 0x80 + +// +// Globals +// +static const uint32 BaudRate = 115200; +static uint32 * uartBase = 0; + +////////////////////////////////////////////////// Serial Port Input & Output. +// +static inline void WriteReg8(volatile void * addr, uint8 value) +{ + ((volatile uint8 *)addr)[0] = value; +} + +static inline uint8 ReadReg8(volatile void * addr) +{ + return ((volatile uint8 *)addr)[0]; +} + +static void UartSetBaudRate(uint32 * BaseAddress, uint32 BaudRate) +{ + uint32 Divisor = OMAP_CLOCK_RATE / BaudRate; + uint8 Enhanced; + + // Disable UART + WriteReg8(BaseAddress + OMAP_UART_MDR1, 0x7); + + // Set register configuration mode B + WriteReg8(BaseAddress + COM_LCR, 0xBF); + + // Save enhanced mode + Enhanced = ReadReg8(BaseAddress + OMAP_UART_EFR); + WriteReg8(BaseAddress + OMAP_UART_EFR, Enhanced | (1 << 4)); + + // switch to operational mode + WriteReg8(BaseAddress + COM_LCR, 0); + + // clear sleep mode + WriteReg8(BaseAddress + OMAP_UART_IER, 0); + + // Set register configuration mode B + WriteReg8(BaseAddress + COM_LCR, 0xBF); + + // Write the divisor value to DLL and DLM. + WriteReg8(BaseAddress + COM_DLM, (uint8)((Divisor >> 8) & 0xff)); + WriteReg8(BaseAddress + COM_DLL, (uint8)(Divisor & 0xff)); + + // Restore enhanced mode + WriteReg8(BaseAddress + OMAP_UART_EFR, Enhanced); + + // Reset the Line Control Register. + WriteReg8(BaseAddress + COM_LCR, LCR_DATA_SIZE); + + // Enable UART + WriteReg8(BaseAddress + OMAP_UART_MDR1, 0); +} + +bool KdpSerialInit(Class_Microsoft_Singularity_Hal_Platform *nbi) +// Initializes the communication port (baud rate, parity etc.) +{ + if (nbi->DebugBasePort < 0x100) { + return false; + + } + uartBase = (uint32 *)nbi->DebugBasePort; + + // Set the default baudrate. + UartSetBaudRate(uartBase, BaudRate); + + // Set DLAB to zero. DLAB controls the meaning of the first two + // registers. When zero, the first register is used for all byte transfer + // and the second register controls device interrupts. + // + WriteReg8(uartBase + COM_LCR, + ReadReg8(uartBase + COM_LCR) & ~LCR_DLAB); + + // Disable device interrupts. This implementation will handle state + // transitions by request only. + // + WriteReg8(uartBase + COM_IEN, 0); + + // Reset and disable the FIFO queue. + // N.B. FIFO will be reenabled before returning from this routine. + // + WriteReg8(uartBase + COM_FCR, FCR_CLEAR_TRANSMIT | FCR_CLEAR_RECEIVE); + + // Configure the Modem Control Register. Disabled device interrupts, + // turn off loopback. + // + WriteReg8(uartBase + COM_MCR, + ReadReg8(uartBase + COM_MCR) & MCR_INITIALIZE); + + // Initialize the Modem Control Register. Indicate to the device that + // we are able to send and receive data. + // + WriteReg8(uartBase + COM_MCR, MCR_INITIALIZE); + + // Enable the FIFO queues. + WriteReg8(uartBase + COM_FCR, FCR_ENABLE); + + return true; +} + +// +// Define wait timeout value. +// +#define TIMEOUT_COUNT 1024 * 30 +// #define TIMEOUT_COUNT 1024 * 200 +//#define TIMEOUT_COUNT 15 + +KDP_STATUS KdpSerialGetByte(OUT PUCHAR Input, BOOL WaitForByte) +{ + UINT8 lsr; + UINT8 value; + UINT32 limitcount = (WaitForByte) ? TIMEOUT_COUNT : 1; + + UINT8 msr; + msr = ReadReg8(uartBase + COM_MSR); + KDDBG2("MSR %02x\n", msr); + + while (limitcount != 0) { + limitcount--; + + lsr = ReadReg8(uartBase + COM_LSR); + KDDBG2("LSR %02x\n", lsr); + if (lsr & LSR_DATA_AVAILABLE) { + value = ReadReg8(uartBase + COM_DAT); + *Input = (UINT8)(value & 0xff); + return KDP_PACKET_RECEIVED; + } + } + return KDP_PACKET_TIMEOUT; +} + + +void KdpSerialPutByte(IN UCHAR Output) +{ + // Loop until the device is ready for output + while ((ReadReg8(uartBase + COM_LSR) & LSR_THR_EMPTY) == 0) { + } + + // The transmitter regiser is clear and can be written to. + WriteReg8(uartBase + COM_DAT, Output); +} +// +///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/HalKdUartIop348.cpp b/base/Kernel/Native/HalKdUartIop348.cpp new file mode 100644 index 0000000..c3faac3 --- /dev/null +++ b/base/Kernel/Native/HalKdUartIop348.cpp @@ -0,0 +1,240 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// HalKdUartIop348.cpp: UART serial transport for IOP348 +// +// Note: Kernel Only +// + +#include "hal.h" +#include "halkd.h" + +// +// Debugger Debugging +// +#define KDDBG if (0) kdprintf +#define KDDBG2 if (0) kdprintf + +// +// Status Constants for reading data from comport +// + +#define CP_GET_SUCCESS 0 +#define CP_GET_NODATA 1 +#define CP_GET_ERROR 2 + + +#define IOP348_UART1_BASE 0xffd82300 +#define IOP348_UART2_BASE 0xffd82340 + +///////////////////////////////////////////////////////////////// Serial Port. +// +#define IOP348_CLOCK_RATE 2083375 + +#define IOP348_CONTROL_PADCONF_UART1_TX (0x17c) +#define IOP348_CONTROL_PADCONF_UART1_CTS (0x180) +#define IOP348_CONTROL_PADCONF_UART2_TX (0x178) +#define IOP348_CONTROL_PADCONF_UART2_CTS (0x174) + +// Define COM Port registers. +#define COM_DAT 0x00 +#define COM_DLL 0x00 // Divisor Latch (LSB). +#define COM_IEN 0x01 // Interrupt enable register +#define COM_DLM 0x01 // Divisor Latch (MSB). +#define COM_FCR 0x02 // FIFO Control Register. +#define COM_LCR 0x03 // Line Control Register. +#define COM_MCR 0x04 // Modem Control Register. +#define COM_LSR 0x05 // Line Status Register. +#define COM_MSR 0x06 // Modem Status Register. +#define COM_SCR 0x07 // Scratch Register. + +#define IOP348_UART_MDR1 0x8 +#define IOP348_UART_IER 0x1 +#define IOP348_UART_EFR 0x2 + +// Define bits in the Interrupt enable register +#define IEN_UNIT_ENABLE 0x40 + +// Define bits in the FIFO Control Register (FCR). +#define FCR_ENABLE 0x01 +#define FCR_CLEAR_RECEIVE 0x02 +#define FCR_CLEAR_TRANSMIT 0x04 + +// Define bits in the Line Control Register (LCR). +#define LCR_DATA_SIZE 0x03 +#define LCR_DLAB 0x80 + +// Define bits in the Modem Control Register (MCR). +#define MCR_DATA_TERMINAL_READY 0x01 +#define MCR_REQUEST_TO_SEND 0x02 +#define MCR_OUT1 0x04 +#define MCR_OUT2 0x08 +#define MCR_LOOPBACK 0x10 +#define MCR_INITIALIZE (MCR_DATA_TERMINAL_READY | MCR_REQUEST_TO_SEND) + +// Define bits in the Line Status Register (LSR). +#define LSR_DATA_AVAILABLE 0x01 +#define LSR_OVERRUN_ERROR 0x02 +#define LSR_PARITY_ERROR 0x04 +#define LSR_FRAMING_ERROR 0x08 +#define LSR_BREAK_SIGNAL 0x10 +#define LSR_THR_EMPTY 0x20 +#define LSR_THR_LINE_IDLE 0x40 + + +// Defined bits in the Modem Status Register (MSR). +#define MSR_DELTA_CLEAR_TO_SEND 0x01 +#define MSR_DELTA_DATA_SET_READY 0x02 +#define MSR_DELTA_RING_INDICATOR 0x04 +#define MSR_DELTA_CARRIER_DETECT 0x08 +#define MSR_CLEAR_TO_SEND 0x10 +#define MSR_DATA_SET_READY 0x20 +#define MSR_RING_INDICATOR 0x40 +#define MSR_CARRIER_DETECT 0x80 + +// +// Communication functions. +// + +static const uint32 BaudRate = 115200; +static uint32 *uartBase = 0; + + +////////////////////////////////////////////////// Serial Port Input & Output. +// +static inline void WriteReg8(volatile void * addr, uint8 value) +{ + ((volatile uint8 *)addr)[0] = value; +} + +static inline uint8 ReadReg8(volatile void * addr) +{ + return ((volatile uint8 *)addr)[0]; +} + +static void UartSetBaudRate(uint32 * BaseAddress, uint32 BaudRate) +{ + UINT32 Divisor = 18; // IOP348_CLOCK_RATE / BaudRate; + + // Disable UART + WriteReg8(BaseAddress + IOP348_UART_MDR1, 0x7); + + // Set register configuration mode B + WriteReg8(BaseAddress + COM_LCR, 0xBF); + + // Save enhanced mode + UINT8 Enhanced = ReadReg8(BaseAddress + IOP348_UART_EFR); + WriteReg8(BaseAddress + IOP348_UART_EFR, Enhanced | (1 << 4)); + + // switch to operational mode + WriteReg8(BaseAddress + COM_LCR, 0); + + // clear sleep mode + WriteReg8(BaseAddress + IOP348_UART_IER, 0); + + // Set register configuration mode B + WriteReg8(BaseAddress + COM_LCR, 0xBF); + + // Write the divisor value to DLL and DLM. + WriteReg8(BaseAddress + COM_DLM, (UINT8)((Divisor >> 8) & 0xff)); + WriteReg8(BaseAddress + COM_DLL, (UINT8)(Divisor & 0xff)); + + // Restore enhanced mode + WriteReg8(BaseAddress + IOP348_UART_EFR, Enhanced); + + // Reset the Line Control Register. + WriteReg8(BaseAddress + COM_LCR, LCR_DATA_SIZE); + // Enable UART + WriteReg8(BaseAddress + IOP348_UART_MDR1, 0); +} + +bool KdpSerialInit(Class_Microsoft_Singularity_Hal_Platform *nbi) +{ + if (nbi->DebugBasePort < 0x100) { + return false; + + } + uartBase = (uint32 *)nbi->DebugBasePort; + + // Set the default baudrate. + UartSetBaudRate(uartBase, BaudRate); + + // Set DLAB to zero. DLAB controls the meaning of the first two + // registers. When zero, the first register is used for all byte transfer + // and the second register controls device interrupts. + // + WriteReg8(uartBase + COM_LCR, + ReadReg8(uartBase + COM_LCR) & ~LCR_DLAB); + + // Disable device interrupts. This implementation will handle state + // transitions by request only. + WriteReg8(uartBase + COM_IEN, 0); + + // Reset and disable the FIFO queue. + // N.B. FIFO will be reenabled before returning from this routine. + // + WriteReg8(uartBase + COM_FCR, FCR_CLEAR_TRANSMIT | FCR_CLEAR_RECEIVE); + + // Configure the Modem Control Register. Disabled device interrupts, + // turn off loopback. + // + WriteReg8(uartBase + COM_MCR, + ReadReg8(uartBase + COM_MCR) & MCR_INITIALIZE); + + // Initialize the Modem Control Register. Indicate to the device that + // we are able to send and receive data. + // + WriteReg8(uartBase + COM_MCR, MCR_INITIALIZE); + + // Enable the FIFO queues. + WriteReg8(uartBase + COM_FCR, FCR_ENABLE); + + // Enable the UART by enabling the UUE bit + WriteReg8(uartBase + COM_IEN, IEN_UNIT_ENABLE); + + return true; +} + +// +// Define wait timeout value. +// +#define TIMEOUT_COUNT 30 * 1024 +// #define TIMEOUT_COUNT 1024 * 200 +//#define TIMEOUT_COUNT 15 + +KDP_STATUS KdpSerialGetByte(OUT PUCHAR Input, BOOL WaitForByte) +{ + UINT8 lsr; + UINT8 value; + UINT32 limitcount = WaitForByte ? TIMEOUT_COUNT : 1; + + UINT8 msr; + msr = ReadReg8(uartBase + COM_MSR); + + while (limitcount != 0) { + limitcount--; + + lsr = ReadReg8(uartBase + COM_LSR); + if (lsr & LSR_DATA_AVAILABLE) { + value = ReadReg8(uartBase + COM_DAT); + *Input = (UINT8)(value & 0xff); + return KDP_PACKET_RECEIVED; + } + } + return KDP_PACKET_TIMEOUT; +} + +void KdpSerialPutByte(IN UCHAR Output) +{ + // Loop until the device is ready for output + while ((ReadReg8(uartBase + COM_LSR) & LSR_THR_EMPTY) == 0) { + } + + // The transmitter regiser is clear and can be written to. + WriteReg8(uartBase + COM_DAT, Output); +} + +// End of File. diff --git a/base/Kernel/Native/Hypervisor.cpp b/base/Kernel/Native/Hypervisor.cpp deleted file mode 100644 index 0cfb3d7..0000000 --- a/base/Kernel/Native/Hypervisor.cpp +++ /dev/null @@ -1,71 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: hyper.cpp -// -// Note: -// Hypervisor interaction -// - -#include "hal.h" - -#if SINGULARITY_KERNEL - -uint16 Class_Microsoft_Singularity_Hypervisor::g_DispatchRepHypercall(uint16 callCode, - uint16 *reps, uint16 start, - UIntPtr inputBuffer, UIntPtr outputBuffer) -{ - uint32 hi = ((uint32)*reps) | (((uint32)start) << 16); - uint32 lo = (uint32) callCode; - - uint32 result, complete; - - uint32 dest = Struct_Microsoft_Singularity_BootInfo_HYPERCALL_PAGE; - - __asm { - mov edx, hi; - mov eax, lo; - xor ebx, ebx; - mov ecx, inputBuffer; - xor edi, edi; - mov esi, outputBuffer; - - call dest; - - mov result, eax; - mov complete, edx; - } - - *reps = (complete&0x3F); - return (result&0xff); -} - -uint16 Class_Microsoft_Singularity_Hypervisor::g_DispatchHypercall(uint16 callCode, - UIntPtr inputBuffer, UIntPtr outputBuffer) -{ - uint32 lo = (uint32) callCode; - - uint32 result; - - uint32 dest = Struct_Microsoft_Singularity_BootInfo_HYPERCALL_PAGE; - - __asm { - xor edx, edx; - mov eax, lo; - xor ebx, ebx; - mov ecx, inputBuffer; - xor edi, edi; - mov esi, outputBuffer; - - call dest; - - mov result, eax; - } - - return (result&0xff); -} - -#endif // SINGULARITY_KERNEL diff --git a/base/Kernel/Native/IoPort.cpp b/base/Kernel/Native/IoPort.cpp index 76268e9..b72b10a 100644 --- a/base/Kernel/Native/IoPort.cpp +++ b/base/Kernel/Native/IoPort.cpp @@ -6,99 +6,104 @@ // // File: IoPort.cpp // -// Note: +// Note: Kernel & Process // #include "hal.h" -#if SINGULARITY - ///////////////////////////////////////////////////////////// I/O Port Access. // +// Note: eventually we need to fix the IoPort class for ARM. +// For now, though, we conditionalize this code since we don't have +// io port operations +// + uint8 Class_Microsoft_Singularity_Io_IoPort::g_HalReadInt8(uint32 port) { - __asm { - mov eax,0; - mov edx,port; - in al,dx; - } +#if ISA_IX86 || ISA_IX64 + return __inbyte(port); +#else + __debugbreak(); + return 0; +#endif } uint16 Class_Microsoft_Singularity_Io_IoPort::g_HalReadInt16(uint32 port) { - __asm { - mov eax,0; - mov edx,port; - in ax,dx; - } +#if ISA_IX86 || ISA_IX64 + return __inword(port); +#else + __debugbreak(); + return 0; +#endif } uint32 Class_Microsoft_Singularity_Io_IoPort::g_HalReadInt32(uint32 port) { - __asm { - mov edx,port; - in eax,dx; - } +#if ISA_IX86 || ISA_IX64 + return __indword(port); +#else + __debugbreak(); + return 0; +#endif } void Class_Microsoft_Singularity_Io_IoPort::g_HalWriteInt8(uint32 port, uint8 value) { - __asm { - mov edx,port; - mov al,value; - out dx,al; - } +#if ISA_IX86 || ISA_IX64 + __outbyte(port, value); +#else + __debugbreak(); +#endif } void Class_Microsoft_Singularity_Io_IoPort::g_HalWriteInt16(uint32 port, uint16 value) { - __asm { - mov edx,port; - mov ax,value; - out dx,ax; - } +#if ISA_IX86 || ISA_IX64 + __outword(port, value); +#else + __debugbreak(); +#endif } void Class_Microsoft_Singularity_Io_IoPort::g_HalWriteInt32(uint32 port, uint32 value) { - __asm { - mov edx,port; - mov eax,value; - out dx,eax; - } +#if ISA_IX86 || ISA_IX64 + __outdword(port, value); +#else + __debugbreak(); +#endif } #if DO_UNSAFE_CODE_IN_IO + void Class_Microsoft_Singularity_Io_IoPort::g_HalReadFifo16(uint32 port, uint16 *buffer, uint32 count) { - __asm { - mov edx,port; - mov edi,buffer; - mov ecx,count; - rep insw; - } +#if ISA_IX86 || ISA_IX64 + __inwordstring(port, buffer, count); +#else + __debugbreak(); +#endif } void Class_Microsoft_Singularity_Io_IoPort::g_HalWriteFifo16(uint32 port, uint16 *buffer, uint32 count) { - __asm { - mov edx,port; - mov esi,buffer; - mov ecx,count; - rep outsw; - } +#if ISA_IX86 || ISA_IX64 + __outwordstring(port, buffer, count); +#else + __debugbreak(); +#endif } -#endif // DO_UNSAFE_CODE_IN_IO -#endif // SINGULARITY_KERNEL +#endif // DO_UNSAFE_CODE_IN_IO // ///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/Math.cpp b/base/Kernel/Native/Math.cpp deleted file mode 100644 index 50fda5b..0000000 --- a/base/Kernel/Native/Math.cpp +++ /dev/null @@ -1,946 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Math.cpp -// -// Note: -// - -#define LITTL_ENDIAN - -#include "hal.h" - -#pragma warning(disable: 4725) - -////////////////////////////////////////////////////////////////////////////// -// -#define IMCW_EM 0x003f // interrupt Exception Masks -#define IEM_INVALID 0x0001 // invalid -#define IEM_DENORMAL 0x0002 // denormal -#define IEM_ZERODIVIDE 0x0004 // zero divide -#define IEM_OVERFLOW 0x0008 // overflow -#define IEM_UNDERFLOW 0x0010 // underflow -#define IEM_PRECISION 0x0020 // precision - -#define IMCW_RC 0x0c00 // Rounding Control -#define IRC_CHOP 0x0c00 // chop -#define IRC_UP 0x0800 // up -#define IRC_DOWN 0x0400 // down -#define IRC_NEAR 0x0000 // near - -#define ISW_INVALID 0x0001 // invalid -#define ISW_DENORMAL 0x0002 // denormal -#define ISW_ZERODIVIDE 0x0004 // zero divide -#define ISW_OVERFLOW 0x0008 // overflow -#define ISW_UNDERFLOW 0x0010 // underflow -#define ISW_PRECISION 0x0020 // inexact - -#define IMCW_PC 0x0300 // Precision Control -#define IPC_24 0x0000 // 24 bits -#define IPC_53 0x0200 // 53 bits -#define IPC_64 0x0300 // 64 bits - -////////////////////////////////////////////////////////////////////////////// -// -#ifdef BIG_ENDIAN -// big endian -#define D_EXP(x) ((unsigned short *)&(x)) -#define D_HI(x) ((unsigned long *)&(x)) -#define D_LO(x) ((unsigned long *)&(x)+1) -#else -#define D_EXP(x) ((unsigned short *)&(x)+3) -#define D_HI(x) ((unsigned long *)&(x)+1) -#define D_LO(x) ((unsigned long *)&(x)) -#endif - -#define D_BIASM1 0x3fe // off by one to compensate for the implied bit -#define MAXEXP 1024 -#define MINEXP -1021 - -// return the int representation of the exponent -// if x = .f * 2^n, 0.5<=f<1, return n (unbiased) -// e.g. INTEXP(3.0) == 2 -#define INTEXP(x) ((signed short)((*D_EXP(x) & 0x7ff0) >> 4) - D_BIASM1) - - -// check for infinity, NAN -#define D_ISINF(x) ((*D_HI(x) & 0x7fffffff) == 0x7ff00000 && *D_LO(x) == 0) -#define IS_D_SPECIAL(x) ((*D_EXP(x) & 0x7ff0) == 0x7ff0) -#define IS_D_NAN(x) (IS_D_SPECIAL(x) && !D_ISINF(x)) - -#define IS_D_QNAN(x) ((*D_EXP(x) & 0x7ff8) == 0x7ff8) -#define IS_D_SNAN(x) ((*D_EXP(x) & 0x7ff8) == 0x7ff0 && \ - (*D_HI(x) << 13 || *D_LO(x))) - -#define IS_D_DENORM(x) ((*D_EXP(x) & 0x7ff0) == 0 && \ - (*D_HI(x) << 12 || *D_LO(x))) - -#define IS_D_INF(x) (*D_HI(x) == 0x7ff00000 && *D_LO(x) == 0) -#define IS_D_MINF(x) (*D_HI(x) == 0xfff00000 && *D_LO(x) == 0) - -/////////////////////////////////////////////////////// Define special values. -// -typedef union { - uint64 lng; - float64 dbl; -} _dbl; - -static _dbl _d_pos_inf = { 0x7ff0000000000000 }; // positive infinity -static _dbl _d_neg_inf = { 0xfff0000000000000 }; // negative infinity -static _dbl _d_ind = { 0xfff8000000000000 }; // real indefinite -static _dbl _d_neg_zer = { 0x8000000000000000 }; // negative zero - -////////////////////////////////////////////////////////////////////////////// -// - -__declspec(naked) uint16 __fastcall g_ClearFp() -{ - __asm { - push ebp - mov ebp, esp - - // Save the old SW - fnstsw [esp-4]; - fnclex - mov eax, [esp-4]; - and eax, 0xffff; - - pop ebp; - ret; // Returns result in EAX - } -} - -// Doesn't alter any additional registers -__declspec(naked) uint16 __fastcall g_ControlFp(uint16 newctrl, uint16 mask) -{ - (void)newctrl; (void)mask; // accessed directly via ecx, edx respectively. - __asm { - push ebp - mov ebp, esp - - // Save the old CW - fstcw [esp-4]; - mov eax, [esp-4]; - and eax, 0xffff; - - // Load the new CW - and ecx,edx; - not edx; - and edx,eax; - or edx,ecx; - mov [esp-4], edx; - fldcw [esp-4]; - - pop ebp - ret; - } -} - -// Doesn't alter any additional registers -__declspec(naked) void __fastcall g_RestoreFp(uint16 newctrl) -{ - (void)newctrl; // accessed directly via ecx. - __asm { - push ebp - mov ebp, esp - - // Load the new CW - and ecx, 0xffff; - mov [esp-4], ecx; - fldcw [esp-4]; - - pop ebp - ret; - } -} - -////////////////////////////////////////////////////////////////////////////// -// -static float64 __fastcall _frnd(float64 v) -{ - _asm { - fld v; - frndint; - } -} - -static int32 __fastcall _ftoi(float64 v) -{ - int32 intval; - int32 oldcw; - int32 newcw; - _asm { - fstcw [oldcw]; // get control word - - mov eax, [oldcw]; // round mode saved - or eax, IRC_CHOP; // set chop rounding mode - mov [newcw], eax; // back to memory - - fldcw [newcw]; // reset rounding - fld v; - fistp [intval]; // store chopped integer - fwait; - fldcw [oldcw]; // restore rounding - } - return intval; -} - -static float64 _abs(float64 x) -{ - (*(uint64 *)&x) &= 0x7fffffffffffffff; - return x; -} - -static float64 _set_exp(float64 x, int exp) // does not check validity of exp -{ - float64 retval = x; - int biased_exp = exp + D_BIASM1; - *D_EXP(retval) = (unsigned short) (*D_EXP(x) & 0x800f | (biased_exp << 4)); - return retval; -} - -int _get_exp(float64 x) -{ - signed short exp; - exp = (signed short)((*D_EXP(x) & 0x7ff0) >> 4); - exp -= D_BIASM1; //unbias - return (int) exp; -} - - -// Provide the mantissa and the exponent of e^x -// -// Entry: -// x : a (non special) float64 precision number -// -// Exit: -// *newexp: the exponent of e^x -// return value: the mantissa m of e^x scaled by a factor -// (the value of this factor has no significance. -// The mantissa can be obtained with _set_exp(m, 0). -// -// _set_exp(m, *pnewexp) may be used for constructing the final -// result, if it is within the representable range. -// -static inline float64 r_exp_p(float64 z) -{ - static float64 const p0 = 0.249999999999999993e+0; - static float64 const p1 = 0.694360001511792852e-2; - static float64 const p2 = 0.165203300268279130e-4; - - return ( (p2 * (z) + p1) * (z) + p0 ); -} - -static inline float64 r_exp_q(float64 z) -{ - static float64 const q0 = 0.500000000000000000e+0; - static float64 const q1 = 0.555538666969001188e-1; - static float64 const q2 = 0.495862884905441294e-3; - - return ( (q2 * (z) + q1) * (z) + q0 ); -} - -float64 _exphlp(float64 x, int * pnewexp) -{ - static float64 const LN2INV = 1.442695040889634074; // 1/ln(2) - static float64 const C1 = 0.693359375000000000; - static float64 const C2 = -2.1219444005469058277e-4; - float64 xn; - float64 g,z,gpz,qz,rg; - int n; - - xn = _frnd(x * LN2INV); - n = _ftoi(xn); - - // assume guard digit is present - g = (x - xn * C1) - xn * C2; - z = g*g; - gpz = g * r_exp_p(z); - qz = r_exp_q(z); - rg = 0.5 + gpz/(qz-gpz); - - n++; - - *pnewexp = _get_exp(rg) + n; - return rg; -} - -// -// Decompose a number to a normalized mantissa and exponent. -// -static float64 _decomp(float64 x, int *pexp) -{ - int exp; - float64 man; - - if (x == 0.0) { - man = 0.0; - exp = 0; - } - else if (IS_D_DENORM(x)) { - int neg; - - exp = 1 - D_BIASM1; - neg = x < 0.0; - while((*D_EXP(x) & 0x0010) == 0) { - // shift mantissa to the left until bit 52 is 1 - (*D_HI(x)) <<= 1; - if (*D_LO(x) & 0x80000000) - (*D_HI(x)) |= 0x1; - (*D_LO(x)) <<= 1; - exp--; - } - (*D_EXP(x)) &= 0xffef; // clear bit 52 - if (neg) { - (*D_EXP(x)) |= 0x8000; // set sign bit - } - man = _set_exp(x,0); - } - else { - man = _set_exp(x,0); - exp = INTEXP(x); - } - - *pexp = exp; - return man; -} - -static bool is_odd_integer(float64 y) -{ - int exp = INTEXP(y); - if (exp < 1 || exp > 63) { - return false; - } - return(((*(uint64*)&y) | 0x10000000000000u) << (10 + exp) == 0x8000000000000000); -} - -static bool is_even_integer(float64 y) -{ - int exp = INTEXP(y); - if (exp < 1 || exp > 63) { - return false; - } - return (((*(uint64*)&y) | 0x10000000000000u) << (10 + exp) == 0); -} - -////////////////////////////////////////////////////////////////////////////// - -float64 Class_System_Math::g_Round(float64 v) -{ - return _frnd(v); -} - -float64 Class_System_Math::g_Sin(float64 v) -{ - __asm { - fld v; - fsin; // Returns result in ST(0) - } -} - -float64 Class_System_Math::g_Cos(float64 v) -{ - __asm { - fld v; - fcos; // Returns result in ST(0) - } -} - -float64 Class_System_Math::g_Tan(float64 v) -{ - __asm { - fld v; - fptan; - fstp ST(0); // Returns result in ST(0) - } -} - -float64 Class_System_Math::g_Atan(float64 v) -{ - __asm { - fld v; - fld1; - fpatan; - } -} - -float64 Class_System_Math::g_Atan2(float64 v, float64 w) -{ - __asm { - fld v; - fld w; - fpatan; - } -} - -float64 Class_System_Math::g_Abs(float64 v) -{ - __asm { - fld v; - fabs; - } -} - -float64 Class_System_Math::g_Sqrt(float64 v) -{ - __asm { - fld v; - fsqrt; - } -} - -float64 Class_System_Math::g_Log(float64 v) -{ - __asm { - fld v; - fldln2; - fxch ST(1); - fyl2x; // Returns result in ST(0) - } -} - -float64 Class_System_Math::g_Log10(float64 v) -{ - __asm { - fld v; - fldlg2; - fxch ST(1); - fyl2x; // Returns result in ST(0) - } -} - -float64 Class_System_Math::g_Exp(float64 v) -{ - __asm { - fldl2e; - fmul v; - fld ST(0); - frndint; - fxch ST(1); - fsub ST(0), ST(1); - f2xm1; - fld1; - faddp ST(1), ST(0); - fscale; - fstp ST(1); // Returns result in ST(0) - } -} - -// constants for the rational approximation -static inline float64 r_sinh_p(float64 f) -{ - static float64 const p0 = -0.35181283430177117881e+6; - static float64 const p1 = -0.11563521196851768270e+5; - static float64 const p2 = -0.16375798202630751372e+3; - static float64 const p3 = -0.78966127417357099479e+0; - - return (((p3 * (f) + p2) * (f) + p1) * (f) + p0); -} - -static inline float64 r_sinh_q(float64 f) -{ - static float64 const q0 = -0.21108770058106271242e+7; - static float64 const q1 = 0.36162723109421836460e+5; - static float64 const q2 = -0.27773523119650701667e+3; - // q3 = 1 is not used (avoid multiplication by 1) - - return ((((f) + q2) * (f) + q1) * (f) + q0); -} - -// Compute the hyperbolic sine of a number. -// The algorithm (reduction / rational approximation) is -// taken from Cody & Waite. -float64 Class_System_Math::g_Sinh(float64 v) -{ - static float64 const EPS = 5.16987882845642297e-26; // 2^(-53) / 2 - // exp(YBAR) should be close to but less than XMAX - // and 1/exp(YBAR) should not underflow - static float64 const YBAR = 7.00e2; - - // WMAX=ln(OVFX)+0.69 (Cody & Waite),omitted LNV, used OVFX instead of BIGX - - static float64 const WMAX = 1.77514678223345998953e+003; - - float64 result; - - if (IS_D_SPECIAL(v)) { - if (IS_D_INF(v) || IS_D_MINF(v)) { - } - else if (IS_D_QNAN(v)) { - // should throw a soft exception. - } - else if (IS_D_SNAN(v)) { - // should throw a hard exception. - } - result = v; - } - else if (v == 0.0) { - // no precision exception - result = v; - } - else { - bool neg = (v < 0.0); - float64 y = _abs(v); - - if (y > 1.0) { - int newexp; - if (y > YBAR) { - if (y > WMAX) { - // result too large, even after scaling - result = v * _d_pos_inf.dbl; - // should through hard exception. - goto exit; - } - - // - // result = exp(y)/2 - // - - result = _exphlp(y, &newexp); - newexp --; //divide by 2 - if (newexp > MAXEXP) { - result = 0.0; //result = _set_exp(result, newexp-IEEE_ADJUST); - // should through hard exception. - goto exit; - } - else { - result = _set_exp(result, newexp); - } - - } - else { - float64 z = _exphlp(y, &newexp); - z = _set_exp(z, newexp); - result = (z - 1.0/z) / 2.0; - } - - if (neg) { - result = -result; - } - } - else { - if (y < EPS) { - result = v; - if (IS_D_DENORM(result)) { - result = 0.0; // result = _add_exp(result, IEEE_ADJUST); - // should through hard exception. - goto exit; - } - } - else { - float64 f = v * v; - float64 r = f * (r_sinh_p(f) / r_sinh_q(f)); - result = v + v * r; - } - } - } - - exit: - return result; -} - -// Compute the hyperbolic cosine of a number. -// The algorithm (reduction / rational approximation) is -// taken from Cody & Waite. -// -float64 Class_System_Math::g_Cosh(float64 v) -{ - // exp(YBAR) should be close to but less than XMAX - // and 1/exp(YBAR) should not underflow - static float64 const YBAR = 7.00e2; - - // WMAX=ln(OVFX)+0.69 (Cody & Waite),omitted LNV, used OVFX instead of BIGX - static float64 const WMAX = 1.77514678223345998953e+003; - - float64 result; - - if (IS_D_SPECIAL(v)) { - if (IS_D_INF(v) || IS_D_MINF(v)) { - result = _d_pos_inf.dbl; - - } - else if (IS_D_QNAN(v)) { - // should throw a soft exception. - result = v; - } - else { - // should throw a hard exception. - result = v; - } - } - else if (v == 0.0) { - result = 1.0; - } - else { - float64 y = _abs(v); - if (y > YBAR) { - if (y > WMAX) { - // should throw a hard exception. - result = v; - goto exit; - } - - // - // result = exp(y)/2 - // - - int newexp; - result = _exphlp(y, &newexp); - newexp --; //divide by 2 - if (newexp > MAXEXP) { - // should throw a hard exception. - result = 0.0; //result = _set_exp(result, newexp-IEEE_ADJUST); - goto exit; - } - else { - result = _set_exp(result, newexp); - } - } - else { - int newexp; - float64 z = _exphlp(y, &newexp); - z = _set_exp(z, newexp); - result = (z + 1.0/z) / 2.0; - } - // should throw a hard exception if exactness is required. - } - - exit: - return result; -} - -// Compute the hyperbolic tangent of a number. -// The algorithm (reduction / rational approximation) is -// taken from Cody & Waite. - -static inline float64 r_tanh(float64 g) -{ - // constants for rational approximation - static float64 const p0 = -0.16134119023996228053e+4; - static float64 const p1 = -0.99225929672236083313e+2; - static float64 const p2 = -0.96437492777225469787e+0; - static float64 const q0 = 0.48402357071988688686e+4; - static float64 const q1 = 0.22337720718962312926e+4; - static float64 const q2 = 0.11274474380534949335e+3; - static float64 const q3 = 0.10000000000000000000e+1; - - return ((((p2 * (g) + p1) * (g) + p0) * (g)) / ((((g) + q2) * (g) + q1) * (g) + q0)); -} - -float64 Class_System_Math::g_Tanh(float64 v) -{ - // constants - static float64 const EPS = 5.16987882845642297e-26; // 2^(-53) / 2 - static float64 const XBIG = 1.90615474653984960096e+001; // ln(2)(53+2)/2 - static float64 const C0 = 0.54930614433405484570; // ln(3)/2 - - if (IS_D_SPECIAL(v)) { - if (IS_D_INF(v)) { - v = 1.0; - } - else if (IS_D_MINF(v)) { - v = -1.0; - } - else if (IS_D_QNAN(v)) { - // should throw a soft exception. - } - else if (IS_D_SNAN(v)) { - // should throw a hard exception. - } - } - else if (v == 0.0) { - // no precision exception - } - else { - bool neg = false; - if (v < 0.0) { - neg = true; - v = -v; - } - - if (v > XBIG) { - v = 1; - } - else if (v > C0) { - v = 0.5 - 1.0 / (g_Exp(v+v) + 1.0); - v = v + v; - } - else if (v < EPS) { - if (IS_D_DENORM(v)) { - // should throw a heard exception. - } - } - else { - v = v + v * r_tanh(v * v); - } - if (neg) { - v = -v; - } - } - return v; -} - -float64 Class_System_Math::g_Acos(float64 v) -{ - __asm { - fld v; - fld1; // load 1.0 - fadd st, st(1); // 1+x - fld1; // load 1.0 - fsub st, st(2); // 1-x - fmul; // (1+x)(1-x) - fsqrt; // sqrt((1+x)(1-x)) - fxch; - fpatan; // fpatan(x,sqrt((1+x)(1-x))) - } -} - -float64 Class_System_Math::g_Asin(float64 v) -{ - __asm { - fld v; - fld1; // load 1.0 - fadd st, st(1); // 1+x - fld1; // load 1.0 - fsub st, st(2); // 1-x - fmul; // (1+x)(1-x) - fsqrt; // sqrt((1+x)(1-x)) - fpatan; // fpatan(x,sqrt((1+x)(1-x))) - } -} - -static float64 _fastpow(float64 v, float64 w) -{ - __asm { - fld w; // neither v or w can be a boundary cases. - fld v; - fyl2x; // compute y*log2(x) - fld st(0); // duplicate stack top - frndint; // N = round(y) - fsubr st(1), st; - fxch; - fchs; // g = y - N where abs(g) < 1 - f2xm1; // 2**g - 1 - fld1; - fadd; // 2**g - fscale; // (2**g) * (2**N) - gives 2**y - fstp st(1); // pop extra stuff from fp stack - } -} - -float64 Class_System_Math::g_Pow(float64 v, float64 w) -{ - float64 result = 0.0; - - // check for infinity or NAN - if (IS_D_SPECIAL(w) || IS_D_SPECIAL(v)) { - if (IS_D_INF(w)) { - float64 absv = _abs(v); - if (absv > 1.0) { - result = _d_pos_inf.dbl; - } - else if (absv < 1.0) { - result = 0.0; - } - else { - result = _d_ind.dbl; - // Should throw a hard exception. - goto exit; - } - } - else if (IS_D_MINF(w)) { - float64 absv = _abs(v); - if (absv > 1.0) { - result = 0.0; - } - else if (absv < 1.0) { - result = _d_pos_inf.dbl; - } - else { - result = _d_ind.dbl; - // Should throw a hard exception. - goto exit; - } - } - else if (IS_D_INF(v)) { - if (w > 0.0) { - result = _d_pos_inf.dbl; - } - else if (w < 0.0) { - result = 0.0; - } - else { - result = 1.0; - } - } - else if (IS_D_MINF(v)) { - if (w > 0.0) { - result = is_odd_integer(w) ? _d_neg_inf.dbl : _d_pos_inf.dbl; - } - else if (w < 0.0) { - result = is_odd_integer(w) ? _d_neg_zer.dbl : 0.0; - } - else { - result = 1.0; - } - } - } - else if (w == 0.0) { - result = 1.0; - } - else if (v == 0.0) { - if (w < 0.0) { - // Should throw a hard exception. - result = is_odd_integer(v) ? _d_neg_inf.dbl : _d_pos_inf.dbl; - } - else { - result = is_odd_integer(v) ? w : 0.0; - } - } - else if (v < 0.0) { - if (is_odd_integer(w)) { - result = - _fastpow(-v, w); - } - else if (is_even_integer(w)) { - result = _fastpow(-v, w); - } - else { - // Should throw a hard exception. - result = v; - } - } - else { - result = _fastpow(v, w); - } - - exit: - return result; -} - -float64 Class_System_Math::g_Mod(float64 x, float64 y) -{ - float64 result = 0.0; - - // check for infinity or NAN - if (IS_D_SPECIAL(y) || IS_D_SPECIAL(x)) { - if (IS_D_SNAN(y) || IS_D_SNAN(x)) { - // Should throw a hard exception. - } - else if (IS_D_QNAN(y) || IS_D_QNAN(x)) { - // Should throw a soft exception. - result = x; - } - else if (IS_D_INF(x) || IS_D_MINF(x)) { - // Should throw a hard exception. - } - } - else if (y == 0.0) { - // Should throw a hard exception. - } - else if (x == 0.0) { - result = x; - } - else { - const int SCALE = 53; - bool neg = false; - bool denorm = false; - float64 d,ty,fx,fy; - int nx, ny, nexp; - - if (x < 0.0) { - result = -x; - neg = 1; - } - else { - result = x; - } - - ty = _abs(y); - - while (result >= ty) { - fx = _decomp(result, &nx); - fy = _decomp(ty, &ny); - - if (nx < MINEXP) { - // result is a denormalized number - denorm = true; - nx += SCALE; - ny += SCALE; - result = _set_exp(fx, nx); - ty = _set_exp(fy, ny); - } - - - if (fx >= fy) { - nexp = nx ; - } - else { - nexp = nx - 1; - } - d = _set_exp(fy, nexp); - result -= d; - } - if (denorm) { - // should raise only FP_U exception. - } - if (neg) { - result = -result; - } - } - - return result; -} - -float64 Class_System_Math::g_Floor(float64 v) -{ - float64 result; - - // save user fp control word, and set round down. - uint16 oldcw = g_ControlFp(IRC_DOWN, IMCW_RC); - - if (IS_D_SPECIAL(v)) { - if (IS_D_QNAN(v)) { - // should throw a soft exception. - } - else if (IS_D_SNAN(v)) { - // should throw a hard exception. - } - result = v; - } - else { - result = _frnd(v); // round according to the current rounding mode. - } - - g_RestoreFp(oldcw); - return result; -} - -float64 Class_System_Math::g_Ceiling(float64 v) -{ - float64 result; - - // save user fp control word, and set round up. - uint16 oldcw = g_ControlFp(IRC_UP, IMCW_RC); - if (IS_D_SPECIAL(v)) { - if (IS_D_QNAN(v)) { - // should throw a soft exception. - } - else if (IS_D_SNAN(v)) { - // should throw a hard exception. - } - result = v; - } - else { - result = _frnd(v); // round according to the current rounding mode. - } - - g_RestoreFp(oldcw); - return result; -} - -// -///////////////////////////////////////////////////////////////// End of File. - diff --git a/base/Kernel/Native/MemoryStorage.cpp b/base/Kernel/Native/MemoryStorage.cpp new file mode 100644 index 0000000..4bb82d7 --- /dev/null +++ b/base/Kernel/Native/MemoryStorage.cpp @@ -0,0 +1,893 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: MemoryStorage.cpp +// +// Note: Kernel & Process +// + +#include "hal.h" +#include "eventing.h" + +#ifndef SINGULARITY_KERNEL +void DebugPrintEvent(UIntPtr eventHandle) +{ + Class_Microsoft_Singularity_Eventing_SystemControllerProxy::g_DebugPrintLogEntry(eventHandle); +} +#endif + +uint32 +MemoryStorageGetNextGeneration(PMEMORY_STORAGE Storage) +{ + return (uint32)InterlockedIncrement((volatile INT32* )&Storage->Generation); +} + +PMEMORY_HEADER +MemoryStorageAdvanceCursor(PMEMORY_STORAGE Storage, + PMEMORY_ZONE CurrentCursor, + uint16 size) +{ + PMEMORY_ZONE NewCursor; + PMEMORY_ZONE CapturedCursor = Storage->ZoneCursor; + BOOL Reycled = false; + + NewCursor = CurrentCursor; + + if (NewCursor == NULL) { + + NewCursor = Storage->MemoryZoneLink; + } + + for (NewCursor = NewCursor->Link; NewCursor != CurrentCursor; NewCursor = NewCursor->Link) { + + if (NewCursor == NULL) { + + // Restart from the begining of the storage + // Make sure the policy allows this + + if (Storage->Flags & MEMORY_STORAGE_FLAGS_RECYCLE_MEMORY) { + + if (Reycled) { + + // Every zone has been tried for this allocation + // return back the failure. + + return NULL; + } + + if (Storage->Flags & MEMORY_STORAGE_FLAGS_BREAK_ON_RECYCLE) { + + // If we trap here, there is a request to stop before over-writing + // any buffer contents. That is about to happen, so this is the + // last chance to retrieve those contents before they are lost. + // + // Then proceed from this breakpoint. + + __debugbreak(); + } + + Reycled = true; + + NewCursor = Storage->MemoryZoneLink; + + } else { + + // We must return here failure, as recycling the memory for older events + // is not permited + + return NULL; + } + + } + + if (IsZoneCompleted(NewCursor)) { + + RecycleZone(NewCursor); + } + + PMEMORY_HEADER Event = AllocateEventEntry(NewCursor, size); + if (Event != NULL) { + + // we were successfuly in using this zone, set it as current cursor + // It is possible some other thread already did it, or even moved far ahead + // But this should not be a problem as there are no guarantees wrt. ordering + // in the storage + + InterlockedCompareExchangePointer((PVOID *)&Storage->ZoneCursor, + (PVOID)NewCursor, + (PVOID)CapturedCursor); + return Event; + } + } + + return NULL; +} + + +uint32 +CaptureStackTrace(UIntPtr * StackArray, UINT MaxStackSize) +{ + + uint32 Index; + UIntPtr CurrentFrame = Class_Microsoft_Singularity_Isal_Isa::g_GetFramePointer(); + UIntPtr CallerFrame = Class_Microsoft_Singularity_Isal_Isa::g_GetFrameCallerFrame(CurrentFrame); + + Struct_Microsoft_Singularity_ProcessorContext *processorContext = + Class_Microsoft_Singularity_Processor::g_GetCurrentProcessorContext(); + + if ((CurrentFrame <= (UIntPtr)processorContext->cpuRecord.interruptStackBegin) && + (CurrentFrame > (UIntPtr)processorContext->cpuRecord.interruptStackLimit)) { + + // Capture the interrupte stack. Always check against the limits + // before fetching the next frame + + for (Index = 0; (CallerFrame != 0) && Index < MaxStackSize; Index += 1) { + + if ((CallerFrame > (UIntPtr)processorContext->cpuRecord.interruptStackBegin) || + (CallerFrame < (UIntPtr)processorContext->cpuRecord.interruptStackLimit)) { + + return Index; + } + StackArray[Index] = Class_Microsoft_Singularity_Isal_Isa::g_GetFrameReturnAddress(CallerFrame); + CallerFrame = Class_Microsoft_Singularity_Isal_Isa::g_GetFrameCallerFrame(CallerFrame); + } + return Index; + } + + + for (Index = 0; (CallerFrame != 0) && Index < MaxStackSize; Index += 1) { + + StackArray[Index] = Class_Microsoft_Singularity_Isal_Isa::g_GetFrameReturnAddress(CallerFrame); + CallerFrame = Class_Microsoft_Singularity_Isal_Isa::g_GetFrameCallerFrame(CallerFrame); + } + return Index; +} + + + +PMEMORY_HEADER +InternalLogRecord(UIntPtr StorageHandle, + uint32 Flags, + UIntPtr eventType, + PVOID Buffer, + uint32 size, + PVOID * ExtendedBuffer, + uint32 ExtendedSize ) +{ + if (StorageHandle == 0) return NULL; + + PMEMORY_STORAGE Storage = (PMEMORY_STORAGE)StorageHandle; + + UIntPtr Stacks[RECORD_MAXSTACKSIZE]; + uint32 StackSize = 0; + uint32 allocSize = (uint32)ROUND_UP_TO_POWER2(size, sizeof(UIntPtr)); + + if (Flags & RECORD_STACK_TRACES) { + + StackSize = CaptureStackTrace(Stacks, RECORD_MAXSTACKSIZE); + + if (StackSize) { + + // There is no point to save the eip of the current function in the database + // since is redundant information. Reuse instead that slot to keep the stack size + + Stacks[0] = (UIntPtr)(StackSize - 1); + + // Convert to memory usage + + StackSize *= sizeof(UIntPtr); + } else { + + // clear the flag as there is no stack available + Flags &= ~RECORD_STACK_TRACES; + } + } + + PMEMORY_ZONE Zone = Storage->ZoneCursor; + + if (Zone == NULL) { + + Zone = Storage->MemoryZoneLink; + } + + UINT32 EntrySize = GetRecordHeaderSize (Flags, StackSize); + + PMEMORY_HEADER Event = AllocateEventEntry(Zone, + (uint16)(EntrySize + allocSize + ExtendedSize - sizeof(MEMORY_HEADER))); + + if (Event == NULL) { + + // The zone is filled up, advance the cursor to the next zone + + Event = MemoryStorageAdvanceCursor(Storage, Zone, + (uint16)(EntrySize + allocSize + ExtendedSize - sizeof(MEMORY_HEADER))); + } + + if (Event != NULL) { + + // Copy the entry flags, which also include the layout description + + Event->Flags = (uint16)Flags; + Event->Type = eventType; + // Handle the stack traces, if present + + UCHAR * Dest = (UCHAR *)GetRecordInternalStructure(Event, RECORD_STACK_TRACES); + + if (Dest != NULL) { + + memcpy(Dest, Stacks, StackSize); + } + + // Copy the remaining portion provided by the user to the buffer + + if (Buffer) { + + memcpy((char *)Event + EntrySize, Buffer, size); + } + + if (ExtendedSize != 0) { + + *ExtendedBuffer = (char *)Event + EntrySize + allocSize; + } + + } + + return Event; +} + +PMEMORY_HEADER +InternalLogFixedRecord(UIntPtr StorageHandle, + uint32 Flags, + UIntPtr eventType, + PVOID Buffer, + uint32 size) +{ + PMEMORY_HEADER entry = InternalLogRecord(StorageHandle, Flags, eventType, Buffer, size, NULL, 0); + + if (entry != NULL) { + + if (Flags & Class_Microsoft_Singularity_Eventing_EventSource_CAPTURE_DEBUG_PRINT){ + + DebugPrintEvent((UIntPtr)entry); + } + + CommitEventEntry(entry); + } + + return entry; +} + +PMEMORY_HEADER +InternalLogVariableRecord( + bool doCommit, + UIntPtr StorageHandle, + uint32 Flags, + UIntPtr eventType, + PVOID Buffer, + uint32 size, + int32 variableItemsCount, + Struct_Microsoft_Singularity_Eventing_ArrayType * variableItems) +{ + if (StorageHandle == 0) return NULL; + + PVOID ExtendedBuffer; + int i; + int arrayDescriptorSize = variableItemsCount * sizeof(uint16); + + int32 extendedSize = arrayDescriptorSize; + + for (i = 0; i < variableItemsCount; i++) { + + extendedSize += (int32)variableItems[i].Length; + + if ((variableItems[i].Type == EVENT_FIELD_TYPE_string) + || + (variableItems[i].Type == EVENT_FIELD_TYPE_szChar)) { + + // Account for the null terminator that ConvertToChars automatically + // inserts to the string. For szChar data, the string will also have one + // null character at the end, that has not been encounted for + + extendedSize += 1; + } + } + + PMEMORY_HEADER Entry = InternalLogRecord(StorageHandle, + Flags, + eventType, + Buffer, + size, + &ExtendedBuffer, + extendedSize); + + if (Entry == NULL) { + return Entry; + } + + if (extendedSize > 0) { + + // Note the test for extendedSize. This is the only case where we are + // allowed to add something after calling InternalLogRecord. We are also required + // explicitely commit the entry when we're done + + for (i = 0; i < variableItemsCount; i++) { + + // + // For strings, the mechanism wil automatically extend the buffer with one + // extra character to include the null terminator. The length in the field + // should be however consistent and report the correct length of the buffer + // + + if (variableItems[i].Type == EVENT_FIELD_TYPE_string) { + + // Account for the null terminator that ConvertToChars automatically + // inserts to the string. + + unsigned short adjustedLength = variableItems[i].Length + 1; + + C_ASSERT(sizeof(adjustedLength) == sizeof(variableItems[i].Length)); + memcpy(ExtendedBuffer, &adjustedLength, sizeof(adjustedLength)); + ExtendedBuffer = (char *)ExtendedBuffer + sizeof(variableItems[i].Length); + + + // If this was a bartok string, convert it to ascii + + ExtendedBuffer = ConvertToChars((char *)ExtendedBuffer, + (bartok_char *)variableItems[i].Buffer, + (int32)variableItems[i].Length); + + + EV_ASSERT(ExtendedBuffer <= (PVOID)((char*)Entry + Entry->Size )); + + } else if (variableItems[i].Type == EVENT_FIELD_TYPE_szChar) { + + // Account for the null terminator that is automatically + // inserted to the string. + + unsigned short adjustedLength = variableItems[i].Length + 1; + + // + // Save the total size in bytes and save it to the entry. Move the pointer forward + // + + C_ASSERT(sizeof(adjustedLength) == sizeof(variableItems[i].Length)); + memcpy(ExtendedBuffer, &adjustedLength, sizeof(adjustedLength)); + ExtendedBuffer = (char *)ExtendedBuffer + sizeof(adjustedLength); + + // + // Copy now the actual content of the buffer + // + + memcpy(ExtendedBuffer, variableItems[i].Buffer, (int32)variableItems[i].Length); + ExtendedBuffer = (char *)ExtendedBuffer + variableItems[i].Length; + *(char *)ExtendedBuffer = (char)0; + ExtendedBuffer = (char *)ExtendedBuffer + 1; + + } else { + + // + // Save the length of the data and advance the pointer + // + + memcpy(ExtendedBuffer, &variableItems[i].Length, sizeof(variableItems[i].Length)); + ExtendedBuffer = (char *)ExtendedBuffer + sizeof(variableItems[i].Length); + + // nothing else to do here, just copy the content and advance the pointer + + memcpy(ExtendedBuffer, variableItems[i].Buffer, (int32)variableItems[i].Length); + ExtendedBuffer = (char *)ExtendedBuffer + variableItems[i].Length; + } + + EV_ASSERT(ExtendedBuffer <= (PVOID)((char*)Entry + Entry->Size )); + + } + + } + + if (Flags & Class_Microsoft_Singularity_Eventing_EventSource_CAPTURE_DEBUG_PRINT){ + + DebugPrintEvent((UIntPtr)Entry); + } + + if (doCommit) { + + CommitEventEntry(Entry); + } + + + return Entry; +} + + +UIntPtr +OpenLoggingStorage(UIntPtr sourceHandle, uint32 * flags) +{ + if (sourceHandle == 0) return 0; + + PSOURCE_DESCRIPTOR source = HANDLE_TO_SOURCE(sourceHandle); + if (((source->ControlFlags & (*flags)) >> 16) == 0) return 0; + + *flags |= source->ControlFlags; + return source->StorageHandle; +} + + +PMEMORY_ZONE +GetNextZone(PQUERY_VIEW view) +{ + if (view->StartZone == NULL) { + return NULL; + } + + for (;;) { + + if (view->Forward) { + + view->CurrentZone = view->CurrentZone->Link; + + if (view->CurrentZone == NULL) { + + view->CurrentZone = view->Storage->MemoryZoneLink; + } + } else { + + view->CurrentZone = view->CurrentZone->BkLink; + + if (view->CurrentZone == NULL) { + + view->CurrentZone = view->Storage->BkLink; + } + } + + if (view->CurrentZone == view->StartZone) { + + return NULL; + } + + // ??? handle the generation wrap + + if (view->CurrentZone->Generation <= view->QueryGeneration) { + + view->CurrentEntryIndex = 0; + view->ZoneGeneration = view->CurrentZone->Generation; + return view->CurrentZone; + } + + } +} + +PQUERY_VIEW +CreateQuery(UIntPtr storageHandle, bool forward) +{ + PQUERY_VIEW queryView = AllocateQueryView( ); + + if (queryView) { + + queryView->Storage = HANDLE_TO_STORAGE(storageHandle); + queryView->Forward = forward; + queryView->QueryGeneration = queryView->Storage->Generation; + + queryView->CurrentZone = NULL; + queryView->ZoneGeneration = 0; + queryView->CurrentEntry = NULL; + queryView->CurrentEntryIndex = 0; + queryView->EndOfBuffer = false; + + if (forward) { + + queryView->StartZone = queryView->Storage->ZoneCursor; + + if (queryView->StartZone != NULL) { + + queryView->StartZone = queryView->StartZone->Link; + } + + if (queryView->StartZone == NULL) { + + queryView->StartZone = queryView->Storage->MemoryZoneLink; + } + + } else { + + queryView->StartZone = queryView->Storage->ZoneCursor; + + if (queryView->StartZone == NULL) { + + queryView->StartZone = queryView->Storage->BkLink; + } + } + + queryView->CurrentZone = queryView->StartZone; + queryView->QueryReset = true; + + } + + return queryView; +} + +PMEMORY_HEADER +GetNextStorageEntry(PQUERY_VIEW queryView) +{ + PMEMORY_HEADER Entry; + + if (queryView->EndOfBuffer) { + + return NULL; + } + + if (queryView->CurrentEntry) { + + Entry = GetNextEntry(queryView); + + if (Entry) { + + return Entry; + } + } + + PMEMORY_ZONE zone; + if (queryView->QueryReset) { + + zone = queryView->StartZone; + queryView->QueryReset = false; + queryView->ZoneGeneration = zone->Generation; + queryView->CurrentEntryIndex = 0; + + } else { + + zone = GetNextZone(queryView); + } + + while (zone != NULL) { + + queryView->CurrentEntry = GetFirstEntry(zone, queryView->Forward); + + if (queryView->CurrentEntry) { + return queryView->CurrentEntry; + } + + zone = GetNextZone(queryView); + } + + queryView->EndOfBuffer = true; + + return NULL; +} + +// +// ABI calls for Eventing.MemoryStorage +// + +UIntPtr Class_Microsoft_Singularity_Eventing_MemoryStorage:: +g_MemoryStorageCreateImpl(uint32 Flags, UCHAR * InitialBuffer, uint32 BufferSize, uint32 ZoneSize) +{ + if (BufferSize <= sizeof(MEMORY_STORAGE)) { + + return 0; + } + + PMEMORY_STORAGE Storage = (PMEMORY_STORAGE)InitialBuffer; + Storage->StorageSize = 0; + Storage->MemoryZoneLink = NULL; + Storage->ZoneCursor = NULL; + Storage->Flags = Flags; + Storage->BkLink = NULL; + Storage->Generation = 0; + + ZoneSize = (uint32)ROUND_UP_TO_POWER2(ZoneSize, EV_ZONE_ALIGNMENT); + + if (((Flags & 0xF) == MEMORY_STORAGE_FLAGS_PERMANENT) + || + ((Flags & 0xF) == MEMORY_STORAGE_FLAGS_ACTIVE_STORAGE) ) { + + ZoneSize = BufferSize; + } + + if (ZoneSize < sizeof(MEMORY_ZONE)) { + + ZoneSize = BufferSize / EV_DEFAULT_ZONE_BUFFER_RATIO; + ZoneSize = (uint32)ROUND_UP_TO_POWER2(ZoneSize, EV_ZONE_ALIGNMENT); + } + + if (ZoneSize > EV_MAXIMUM_ZONE_SIZE) { + Storage->DefaultZoneSize = EV_MAXIMUM_ZONE_SIZE; + + } else { + + Storage->DefaultZoneSize = ZoneSize; + } + + Storage->ZoneCount = 0; + InitialBuffer = (UCHAR *)(Storage + 1); + InitialBuffer = (UCHAR *)ROUND_UP_TO_POWER2(InitialBuffer, EV_ZONE_ALIGNMENT); + + Class_Microsoft_Singularity_Eventing_MemoryStorage::g_MemoryStorageRegisterBufferImpl( + (UIntPtr)Storage, + InitialBuffer, + (uint32)(BufferSize - ((uint8 *)InitialBuffer - (uint8 *)Storage))); + + Storage->ZoneCursor = Storage->MemoryZoneLink; + + return (UIntPtr)Storage; +} + +void +Class_Microsoft_Singularity_Eventing_MemoryStorage:: +g_MemoryStorageRegisterBufferImpl(UIntPtr StorageHandle, UCHAR * Buffer, uint32 BufferSize) +{ + + PMEMORY_STORAGE Storage = (PMEMORY_STORAGE)StorageHandle; + PMEMORY_ZONE LastZone = NULL; + PMEMORY_ZONE ZoneChain = NULL; + PMEMORY_ZONE TempZone; + PMEMORY_ZONE LastExistingZone = Storage->BkLink; + + InterlockedExchangeAdd((volatile INT32 *)&Storage->StorageSize, BufferSize); + + if ((Storage->DefaultZoneSize != 0) && (BufferSize > Storage->DefaultZoneSize)) { + + // We have to split the larger chunk in multiple zones + + uint32 MaxZone = BufferSize / Storage->DefaultZoneSize; + + for (uint32 i = 0; i < MaxZone; i++) { + + TempZone = InitializeMemoryZone(Buffer, Storage->DefaultZoneSize, StorageHandle); + EV_ASSERT(TempZone != NULL); + + InterlockedIncrement((volatile INT32* )&Storage->ZoneCount); + + if (ZoneChain == NULL) { + + ZoneChain = TempZone; + } + + // Link the zones togeather. Note the chain is not yet published outside + // until is pushed to the memory storage list eventually + + if (LastZone) { + + LastZone->Link = TempZone; + } + + TempZone->BkLink = LastZone; + LastZone = TempZone; + + Buffer = (UCHAR *)Buffer + Storage->DefaultZoneSize; + BufferSize -= Storage->DefaultZoneSize; + + } + + } + + TempZone = InitializeMemoryZone(Buffer, BufferSize, StorageHandle); + + if (TempZone != NULL) { + + InterlockedIncrement((volatile INT32 *)&Storage->ZoneCount); + + // Here the remining portion might be too small for a whole zone. + + if (ZoneChain == NULL) { + + ZoneChain = TempZone; + } + + // Link the zones togeather. Note the chain is not yet published outside + // until is pushed to the memory storage list eventually + + if (LastZone) { + + LastZone->Link = TempZone; + } + + TempZone->BkLink = LastZone; + LastZone = TempZone; + } + + if (LastZone != NULL) { + + LastZone->Link = NULL; + + if (LastExistingZone == NULL) { + + // The existing list is empty. Insert the new one to the head + + ZoneChain->BkLink = NULL; + Storage->BkLink = LastZone; + Storage->MemoryZoneLink = ZoneChain; + + } else { + + LastExistingZone->Link = ZoneChain; + ZoneChain->BkLink = LastExistingZone; + + } + + Storage->BkLink = LastZone; + } + +} + + +UIntPtr Class_Microsoft_Singularity_Eventing_EventSource:: +g_LogSourceEntryImpl(UIntPtr sourceHandle, + uint32 flags, + UIntPtr eventType, + uint8 * Buffer, + int32 size) +{ + UIntPtr storageHandle = OpenLoggingStorage(sourceHandle, &flags); + return (UIntPtr)InternalLogFixedRecord(storageHandle, + flags, + eventType, + Buffer, + size); +} + +UIntPtr Class_Microsoft_Singularity_Eventing_EventSource:: + g_LogSourceEntryImpl(UIntPtr sourceHandle, + uint32 flags, + UIntPtr eventType, + uint8 * Buffer, + int32 size, + int32 arraysCount, + Struct_Microsoft_Singularity_Eventing_ArrayType * arrays) +{ + UIntPtr storageHandle = OpenLoggingStorage(sourceHandle, &flags); + return (UIntPtr)InternalLogVariableRecord(true, + storageHandle, + flags, + eventType, + Buffer, + size, + arraysCount, + arrays); +} + + +uint32 Class_Microsoft_Singularity_Eventing_MemoryStorage:: +g_GetMemoryStorageOveheadImpl() +{ + // Determine the overhead in the pesimistic case + + return sizeof(MEMORY_STORAGE) + + (sizeof(MEMORY_ZONE) + EV_ZONE_ALIGNMENT) * EV_DEFAULT_ZONE_BUFFER_RATIO + + sizeof(MEMORY_HEADER) * EV_DEFAULT_ZONE_BUFFER_RATIO; +} + +UIntPtr Class_Microsoft_Singularity_Eventing_MemoryStorage:: +g_CreateQueryViewImpl(UIntPtr storageHandle, bool forward) +{ + return (UIntPtr)CreateQuery(storageHandle, forward); +} + +void Class_Microsoft_Singularity_Eventing_MemoryStorage:: +g_DeleteQueryViewImpl(UIntPtr queryHandle) +{ + UnRegisterQueryView((PQUERY_VIEW)queryHandle); +} + +UIntPtr Class_Microsoft_Singularity_Eventing_MemoryStorage:: +g_GetNextEntryImpl(UIntPtr queryHandle, + UIntPtr * typeHandle, + uint32 * userOffset, + uint8 * buffer, + uint16 bufferSize) +{ + PQUERY_VIEW view = (PQUERY_VIEW)queryHandle; + + PMEMORY_HEADER entry = GetNextStorageEntry(view); + + if (entry != NULL) { + + *typeHandle = entry->Type; + + void * src = GetUserRecordStructure(entry); + + if (entry->Size < bufferSize) { + + bufferSize = entry->Size; + } + + memcpy(buffer, entry , bufferSize); + *userOffset = (uint32)((ULONG_PTR)src - (ULONG_PTR)entry); + } + + return (UIntPtr) entry; +} + +UIntPtr Class_Microsoft_Singularity_Eventing_MemoryStorage:: +g_WalkEventDescriptorImpl(UIntPtr eventHandle, + UIntPtr currentField, + uint16 * offset, + uint16 * type, + bartok_char * bufferName, + uint16 bufferSize) +{ + if (eventHandle == 0) { + + eventHandle = Handle_MEMORY_HEADER; + } + + PMEMORY_HEADER Entry = HANDLE_TO_HEADER(eventHandle); + + EV_ASSERT(Entry->Flags == RECORD_EVENT_TYPE); + + PEVENT_DESCRIPTOR eventDescriptor = (PEVENT_DESCRIPTOR)GetUserRecordStructure(Entry); + PEVENT_FIELD_DESCRIPTOR field = NULL; + char * src; + + if (currentField == 0) { + + src = GetExtendedString(eventHandle, 1); + + } else if (currentField == eventHandle) { + + // start now with the fields inside the event + + field = eventDescriptor->fieldsLink; + + } else { + + field = (PEVENT_FIELD_DESCRIPTOR)(currentField); + field = field->fieldsLink; + + if (field == NULL) { + + return 0; + } + } + + if (field == NULL) { + + // This is refers the event descriptor itself. + // The type returned will be zero, the only meaningful information + // will be the event name + + *offset = 0; + *type = 0; + + } else { + + *offset = field->Offset; + *type = field->Type; + + src = GetExtendedString((UIntPtr)((PMEMORY_HEADER)field - 1), 1); + } + + if ((bufferName != NULL) && (bufferSize != 0)) { + + + while ((bufferSize != 0) && (*src)) { + + *bufferName++ = *src++; + bufferSize -= sizeof(bartok_char); + } + + if (bufferSize == 0) { + + // Move back one position to insert the null terminator. + // We have at least this character in the buffer due to the test two levels above + + bufferSize -= sizeof(bartok_char); + } + + *bufferName = 0; + } + + if (field == NULL) { + + return eventHandle; + } + + return (UIntPtr)field; +} + + + +// +///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/Monitoring.cpp b/base/Kernel/Native/Monitoring.cpp index b937dff..29f7dc4 100644 --- a/base/Kernel/Native/Monitoring.cpp +++ b/base/Kernel/Native/Monitoring.cpp @@ -6,877 +6,178 @@ // // File: Monitoring.cpp // -// Note: +// Note: Kernel & Process // -// This stuff in here provides a ring-buffer like way to organize monitoring -// buffer entries. Interrupts are *never* disabled, all synchronization is -// done in a non-blocking fashion. -// -// You should *never* get incorrect values from the basic events, the worst -// thing that should happen is that a value is not available anymore, due to -// buffer wrap around. Entries should never be corrupted. -// -// This buffer should be thread and MP safe. Therefore only one buffer is -// currently used for the whole system. -// -// All events may have an additional pointer to a variably sized string in a -// second buffer. Guarantees for data in this buffer are weaker. You might -// not be able to retrieve data in it, if there was a buffer wrap-around or -// an very old mid-preempted entry-writing threads gets scheduled again and -// corrupts the area you just acquired. That is, you might not be able to -// retrieve certain string, but older ones might still be accessible. -// In very rare cases you could also see corrupted data in the strings! -// -// These words of warning spoken: Such data loss should be negligible if you -// make the buffer structures large enough, and read often enough from them. - #include "hal.h" - -//////////////////////////////////////////////////////////////// Image Loader. -// - -#define min(a, b) ((a < b) ? a : b) - -struct indexEntry { - union { - volatile UINT64 data; - struct { - volatile UINT32 lo; - volatile UINT32 hi; - } hi_lo; - struct { - // use similar types to really get a packed bitfield - // (circumvent compiler limitation) - volatile UINT64 index:16; // index (lo. 16 bits) \_ 'data' grows - volatile UINT64 counter:48; // counter (hi. 48 bits) / monotonically - } val; - }; -}; - -struct buffer_t { - indexEntry head; // head (most current) index entry - indexEntry tail; // tail (oldest valid) index entry - UINT32 flags; // 0 == monitoring active, 1 == mon. inactive - UINT32 entries; // number of elements in the following arrays - indexEntry * index; // index array - Struct_Microsoft_Singularity_Monitoring_LogEntry * logs; // entry array - uint8 * txt; // start of text area - uint8 * txtLimit; // end of text area - // fixme: we need a version number here for ABA problem - uint8 * txtCurrent; // pointer to current position in text area - UINT32 magic; // just for padding to 48 byte size -}; - -static unsigned char cas64(void *dest, void *comp, void *exch) -{ - unsigned char retval; - __asm { - mov esi, [comp]; - mov eax, [esi + 0]; - mov edx, [esi + 4]; - mov esi, [exch]; - mov ebx, [esi + 0]; - mov ecx, [esi + 4]; - mov esi, [dest]; - lock cmpxchg8b [esi]; - sete retval; - } - return retval; -} - -static unsigned char cas(void *dest, void *comp, void *exch) -{ - unsigned char retval; - __asm { - mov ecx, dest; - mov edx, exch; - mov eax, comp; - lock cmpxchg [ecx], edx; - sete retval; - } - return retval; -} - -// debug version that puts a seqno in _eip. -// #define GET_EIP() \ -// { \ -// uintptr old_seq; \ -// uintptr new_seq; \ -// UINT32 *valp = &(((buffer_t*)c_buffer)->magic); \ -// do \ -// { \ -// old_seq = *valp; \ -// new_seq = old_seq + 1; \ -// } while (!cas(valp, (void*)old_seq, (void*)new_seq)); \ -// _eip = new_seq; \ -// } - +#include "eventing.h" #define MONITORING_BUFFER_SIZE (6 * 1024 * 1024) #define MONITORING_TEXT_SIZE (2 * 1024 * 1024) +UIntPtr MonitoringStorageHandle = 0; +PSOURCE_DESCRIPTOR MonitoringSource = NULL; +UIntPtr MonitoringTypeHandle = 0; + +const UINT32 Monitoring_ControlFlag_Active = 0x00010000; + void Class_Microsoft_Singularity_Monitoring:: g_Initialize() { + #if SINGULARITY_KERNEL - // Init. all data only in the kernel - //Propagate information to userland to init. a (simpler) copy object there - // layout the header structure at the start of KERNEL_MONREC_BEGIN g_InitPages((UIntPtr)(MONITORING_BUFFER_SIZE)); - if (c_buffer == NULL) { - printf("Monitoring: Error getting Memory for buffer!!!\n"); - // fixme: halt system here - return; - } - else { - printf("Monitoring: Got Memory for buffer: %p\n", c_buffer); - } - // text stuff - ((buffer_t *)c_buffer)->txt = - (uint8 *) g_InitText((UIntPtr)(MONITORING_TEXT_SIZE)); - if (((buffer_t *)c_buffer)->txt == NULL) { - printf("Monitoring: Error getting Memory for text!!!\n"); - // fixme: halt system here - return; - } - else { - printf("Monitoring: Got Memory for text: %p\n", - ((buffer_t *)c_buffer)->txt); - } - ((buffer_t *)c_buffer)->txtLimit = ((buffer_t *)c_buffer)->txt + - MONITORING_TEXT_SIZE; - ((buffer_t *)c_buffer)->txtCurrent = ((buffer_t *)c_buffer)->txt; + if (c_buffer != NULL) { - ((buffer_t *)c_buffer)->head.data = 0; - ((buffer_t *)c_buffer)->tail.data = 0; - ((buffer_t *)c_buffer)->flags = 0; - ((buffer_t *)c_buffer)->index = (indexEntry *)(((buffer_t *)c_buffer) + 1); - // compute the number of available events - // fixme: one should really have ssize_t here - int memSize = MONITORING_BUFFER_SIZE - sizeof(buffer_t); - if (memSize < sizeof(indexEntry) + sizeof(buffer_t)) { - printf("Error: Not enough memory for monitoring events!\n"); - // fixme: stop machine here or something - ((buffer_t *)c_buffer)->entries = 0; - } - else { - ((buffer_t *)c_buffer)->entries = - min(0xffff, memSize / (sizeof(indexEntry) + sizeof(buffer_t))); - printf("Info: Monitoring will use %u entries at %p, memSize = %d.\n", - ((buffer_t *)c_buffer)->entries, c_buffer, memSize); - } - ((buffer_t *)c_buffer)->logs = - (Struct_Microsoft_Singularity_Monitoring_LogEntry *) - (((buffer_t *)c_buffer)->index + ((buffer_t *)c_buffer)->entries); + MonitoringStorageHandle = Class_Microsoft_Singularity_Eventing_MemoryStorage:: + g_MemoryStorageCreateImpl(MEMORY_STORAGE_FLAGS_RECYCLE_MEMORY, + (uint8 *)c_buffer, + (uint32)MONITORING_BUFFER_SIZE, + 0); - // init. elements - for (UINT16 i = 0; i < ((buffer_t *)c_buffer)->entries; i++) { - // clean up indices - ((buffer_t *)c_buffer)->index[i].val.index = i; - ((buffer_t *)c_buffer)->index[i].val.counter = i; // fixme: 0 or i - // cleanup timestamps in real entries as this is used to detect a - // valid entry - ((buffer_t *)c_buffer)->logs[i].cycleCount = 0ULL; - } - // init. head and tail indices - // fixme: get this right - ((buffer_t *)c_buffer)->head.val.index = 0; - ((buffer_t *)c_buffer)->head.val.counter = ((buffer_t *)c_buffer)->entries; - ((buffer_t *)c_buffer)->tail.val.index = 0; - ((buffer_t *)c_buffer)->tail.val.counter = 0; + if (MonitoringStorageHandle) { - // last magic values, mostly used for alignment - ((buffer_t *)c_buffer)->magic = 0x12345678; + UIntPtr sourceHandle = RegisterNativeSource("Monitoring", + MonitoringStorageHandle, + Monitoring_ControlFlag_Active); + + MonitoringSource = GetSourceFromHandle(sourceHandle); + } + } #elif SINGULARITY_PROCESS - // get the address from the kernel and init. our local object - uint8 * _buffer; Struct_Microsoft_Singularity_V1_Services_ProcessService:: - g_GetMonitoringHeaders(&_buffer); + g_GetSharedSourceHandles(Class_Microsoft_Singularity_Eventing_Controller_MonitoringInfo, + &MonitoringStorageHandle, + (UIntPtr *)&MonitoringSource, + &MonitoringTypeHandle); - c_buffer = _buffer; +#else +#error "File should be compiled with SINGULARITY_KERNEL or SINGULARITY_PROCESS" #endif } -// Get Pointer to (hopefully dequeued) LogEntry for index into the log array -// as returned by dequeue. -Struct_Microsoft_Singularity_Monitoring_LogEntry * -Class_Microsoft_Singularity_Monitoring:: -g_IndexToPointer(UINT16 index) +bool GetMonitoringHandles(UIntPtr * storageHandle, + UIntPtr * sourceHandle, + UIntPtr * eventTypeHandle) { - return &((buffer_t *)c_buffer)->logs[index]; + *storageHandle = MonitoringStorageHandle; + *sourceHandle = (UIntPtr)MonitoringSource; + *eventTypeHandle = MonitoringTypeHandle; + return true; } -// fixme: care for failing dequeuing due to missing elements -UINT16 -Class_Microsoft_Singularity_Monitoring:: -g_Dequeue() + +bool __inline IsMonitoringEnabled() { - indexEntry tail; - indexEntry new_tail; - UINT16 index; + return ((MonitoringSource != NULL) && + (MonitoringSource->ControlFlags & Monitoring_ControlFlag_Active)); +} - // comments are from the paper where this buffer is described: - // "A Generalized Approach to Runtime Monitoring for Real-Time Systems", - // Torvald Riegel, 2005, Technische Universität Dresden, Germany - // - // 1. The tail position is read. The technique explained in the previous - // subsection (reading the higher, the lower, and again the higher word) can - // be reused if the index part is not monotonically increasing because the - // element counter part is strictly monotonically increasing and located in - // the upper 48 bits of 64 bit wide tail position. If the first and the - // second read of the higher word returned different values, this step is - // executed again. - for (;;) { - // read atomically - tail.hi_lo.hi = ((buffer_t *)c_buffer)->tail.hi_lo.hi; - tail.hi_lo.lo = ((buffer_t *)c_buffer)->tail.hi_lo.lo; - if (tail.hi_lo.hi != ((buffer_t *)c_buffer)->tail.hi_lo.hi) { - continue; // try again - } - new_tail.data = tail.data; +void Class_Microsoft_Singularity_Monitoring:: +g_Log(uint16 provider, uint16 type) +{ + if (IsMonitoringEnabled()) { - // 2. The element counter part and the index part of the local copy of - // the read position are advanced. - // - new_tail.val.counter++; - new_tail.val.index++; - if (new_tail.val.index >= ((buffer_t *)c_buffer)->entries) { - new_tail.val.index = 0; - } + Struct_Microsoft_Singularity_ThreadContext *threadContext = + Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext(); - index = tail.val.index; - - // 3. Using a compare-and-exchange instruction, it is tried to - // exchange the tail position in the output buffer with the - // modified local copy. If the exchange fails because the value - // in the buffer has been modified, the algorithm is restarted at - // step one. If the operation succeeds, the index that was read - // in step one returned. It points to the dequeued and thus - // allocated output element. - // - if (cas64(&(((buffer_t *)c_buffer)->tail), - (void *)&tail.data, (void *)&new_tail.data)) { - return index; - } + MONITORING_ENTRY entry = {threadContext->processId, provider,type,0,0,0,0,0,0}; + InternalLogFixedRecord(MonitoringStorageHandle, + MonitoringSource->ControlFlags, + MonitoringTypeHandle, + &entry, + sizeof(entry)); } } -void -Class_Microsoft_Singularity_Monitoring:: -g_Enqueue(UINT16 newElement) -{ - indexEntry head; - indexEntry new_head; - indexEntry index; - indexEntry new_index; - UINT32 i; - int enqueued; - - do { - // step 1 - // read atomically - head.hi_lo.hi = ((buffer_t *)c_buffer)->head.hi_lo.hi; - head.hi_lo.lo = ((buffer_t *)c_buffer)->head.hi_lo.lo; - if (head.hi_lo.hi != ((buffer_t *)c_buffer)->head.hi_lo.hi) { - continue; // try again - } - - i = head.val.index; - - // step 2 - index.hi_lo.hi = ((buffer_t *)c_buffer)->index[i].hi_lo.hi; - index.hi_lo.lo = ((buffer_t *)c_buffer)->index[i].hi_lo.lo; - if ((index.hi_lo.hi != ((buffer_t *)c_buffer)->index[i].hi_lo.hi) - || (index.val.counter > head.val.counter)) { - continue; // try again - } - - if (index.val.counter != head.val.counter) { - // step 3 - ((buffer_t *)c_buffer)->logs[newElement].cycleCount = RDTSC(); - // step 4 - new_index.val.counter = head.val.counter; - new_index.val.index = newElement; - enqueued = cas64(&(((buffer_t *)c_buffer)->index[i]), - (void *)&index.data, (void *)&new_index.data); - } - // step 5 - new_head.val.counter = head.val.counter + 1; - new_head.val.index = head.val.index + 1; - if (new_head.val.index >= ((buffer_t *)c_buffer)->entries) { - new_head.val.index = 0; - } - - cas64(&(((buffer_t *)c_buffer)->head), (void *)&head.data, - (void *)&new_head.data); - } while (!enqueued); -} - -void -Class_Microsoft_Singularity_Monitoring:: -g_Enqueue(UINT16 newElement, UINT64 * ptr) -{ - indexEntry head; - indexEntry new_head; - indexEntry index; - indexEntry new_index; - UINT32 i; - int enqueued; - - do { - // step 1 - // read atomically - head.hi_lo.hi = ((buffer_t *)c_buffer)->head.hi_lo.hi; - head.hi_lo.lo = ((buffer_t *)c_buffer)->head.hi_lo.lo; - if (head.hi_lo.hi != ((buffer_t *)c_buffer)->head.hi_lo.hi) { - continue; // try again - } - - i = head.val.index; - - // step 2 - index.hi_lo.hi = ((buffer_t *)c_buffer)->index[i].hi_lo.hi; - index.hi_lo.lo = ((buffer_t *)c_buffer)->index[i].hi_lo.lo; - if ((index.hi_lo.hi != ((buffer_t *)c_buffer)->index[i].hi_lo.hi) - || (index.val.counter > head.val.counter)) { - continue; // try again - } - - if (index.val.counter != head.val.counter) { - // step 3 - *ptr = RDTSC(); - ((buffer_t *)c_buffer)->logs[newElement].cycleCount = *ptr; - // step 4 - new_index.val.counter = head.val.counter; - new_index.val.index = newElement; - enqueued = cas64(&(((buffer_t *)c_buffer)->index[i]), - (void *)&index.data, (void *)&new_index.data); - } - - // step 5 - new_head.val.counter = head.val.counter + 1; - new_head.val.index = head.val.index + 1; - if (new_head.val.index >= ((buffer_t *)c_buffer)->entries) { - new_head.val.index = 0; - } - - cas64(&(((buffer_t *)c_buffer)->head), (void *)&head.data, - (void *)&new_head.data); - } while (!enqueued); -} - - -void -Class_Microsoft_Singularity_Monitoring:: -g_Finalize() -{ -} - -uint8 * -Class_Microsoft_Singularity_Monitoring:: -g_CompareExchange(uint8 **dest, uint8 *exch, uint8 *comp) -{ - uint8 *val; - __asm { - mov ecx, dest; - mov edx, exch; - mov eax, comp; - lock cmpxchg [ecx], edx; - mov val, eax; - } - return val; -} - - -Struct_Microsoft_Singularity_Monitoring_LogEntry * -Class_Microsoft_Singularity_Monitoring:: -g_CompareExchange(Struct_Microsoft_Singularity_Monitoring_LogEntry **dest, - Struct_Microsoft_Singularity_Monitoring_LogEntry *exch, - Struct_Microsoft_Singularity_Monitoring_LogEntry *comp) -{ - Struct_Microsoft_Singularity_Monitoring_LogEntry * val; - __asm { - mov ecx, dest; - mov edx, exch; - mov eax, comp; - lock cmpxchg [ecx], edx; - mov val, eax; - } - return val; -} - void Class_Microsoft_Singularity_Monitoring:: g_Log(uint16 provider, uint16 type, uint16 version, uint32 a0, uint32 a1, uint32 a2, uint32 a3, uint32 a4) { - // fixme: should we check for c_buffer != 0 ??? + if (IsMonitoringEnabled()) { - if (! g_isActive()) { - return; + Struct_Microsoft_Singularity_ThreadContext *threadContext = + Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext(); + + MONITORING_ENTRY entry = {threadContext->processId, provider,type,version,a0,a1,a2,a3,a4}; + InternalLogFixedRecord(MonitoringStorageHandle, + MonitoringSource->ControlFlags, + MonitoringTypeHandle, + &entry, + sizeof(entry)); } - - uintptr _eip; - __asm { - mov eax, [ebp+4]; - mov _eip, eax; - } - - UINT16 index; - Struct_Microsoft_Singularity_Monitoring_LogEntry * p; - - Struct_Microsoft_Singularity_X86_ThreadContext *threadContext = - Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext(); - - int cpuid = Class_Microsoft_Singularity_Processor:: - g_GetCurrentProcessorContext()->cpuId; - - // get buffer - index = g_Dequeue(); - p = g_IndexToPointer(index); - - // fill it - p->eip = _eip; - p->provider = provider; - p->type = type; - p->text = NULL; - p->cpu = cpuid; - p->version = version; - p->arg0 = a0; - p->arg1 = a1; - p->arg2 = a2; - p->arg3 = a3; - p->arg4 = a4; - p->processId = threadContext->processId; -#ifdef SINGULARITY_KERNEL - p->threadId = threadContext->threadIndex; -#else - p->threadId = threadContext->kernelThreadIndex; -#endif - - // return buffer - g_Enqueue(index); - -// UINT32 d_ret; -// d_ret = g_ConsistencyCheck(); -// if (d_ret) { -// Class_Microsoft_Singularity_DebugStub::g_Break(); -// } -} - -void Class_Microsoft_Singularity_Monitoring:: -g_Log(uint16 provider, uint16 type) -{ - // fixme: should we check for c_buffer != 0 ??? Right now we assume to - // only get called after init. - - if (! g_isActive()) { - return; - } - - uintptr _eip; - __asm { - mov eax, [ebp+4]; - mov _eip, eax; - } - - UINT16 index; - Struct_Microsoft_Singularity_Monitoring_LogEntry * p; - - Struct_Microsoft_Singularity_X86_ThreadContext *threadContext = - Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext(); - - int cpuid = Class_Microsoft_Singularity_Processor:: - g_GetCurrentProcessorContext()->cpuId; - - // get buffer - index = g_Dequeue(); - p = g_IndexToPointer(index); - - // fill it - p->eip = _eip; - p->provider = provider; - p->type = type; - p->text = NULL; - p->cpu = cpuid; - p->version = 0; - p->arg0 = 0; - p->arg1 = 0; - p->arg2 = 0; - p->arg3 = 0; - p->arg4 = 0; - p->processId = threadContext->processId; -#ifdef SINGULARITY_KERNEL - p->threadId = threadContext->threadIndex; -#else - p->threadId = threadContext->kernelThreadIndex; -#endif - - // return buffer - g_Enqueue(index); - -// UINT32 d_ret; -// d_ret = g_ConsistencyCheck(); -// if (d_ret) { -// Class_Microsoft_Singularity_DebugStub::g_Break(); -// } } void Class_Microsoft_Singularity_Monitoring:: g_Log(uint16 provider, uint16 type, Class_System_String *s) { - // fixme: should we check for c_buffer != 0 ??? Right now we assume to - // only get called after init. + if (IsMonitoringEnabled()) { - if (! g_isActive()) { - return; + Struct_Microsoft_Singularity_ThreadContext *threadContext = + Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext(); + + MONITORING_ENTRY entry = {threadContext->processId, provider,type,0,0,0,0,0,0,1}; + + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {s->m_stringLength, + sizeof(char), + Class_Microsoft_Singularity_Eventing_DataType___string, + &s->m_firstChar}}; + + InternalLogVariableRecord(true, + MonitoringStorageHandle, + MonitoringSource->ControlFlags, + MonitoringTypeHandle, + &entry, + sizeof(entry), + sizeof(array)/sizeof(array[0]), + array); } +} - uintptr _eip; - __asm { - mov eax, [ebp+4]; - mov _eip, eax; - } - UINT16 index; - Struct_Microsoft_Singularity_Monitoring_LogEntry * p; +bool Class_Microsoft_Singularity_Monitoring:: +g_isActive() +{ + return IsMonitoringEnabled(); +} - Struct_Microsoft_Singularity_X86_ThreadContext *threadContext = - Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext(); - - int cpuid = Class_Microsoft_Singularity_Processor:: - g_GetCurrentProcessorContext()->cpuId; - - // get buffer - index = g_Dequeue(); - p = g_IndexToPointer(index); - - // fill it - p->eip = _eip; - p->provider = provider; - p->type = type; - p->cpu = cpuid; - p->version = 0; - p->arg0 = 0; - p->arg1 = 0; - p->arg2 = 0; - p->arg3 = 0; - p->arg4 = 0; - p->processId = threadContext->processId; -#ifdef SINGULARITY_KERNEL - p->threadId = threadContext->threadIndex; -#else - p->threadId = threadContext->kernelThreadIndex; -#endif - // care for the string now ... - - bool enabled = Class_Microsoft_Singularity_Processor::g_DisableInterrupts(); - // reserve space for one 64 bit counter, 32 bit for the string length and - // the string itself - uint8 * newTxt; - uint8 * oldTxt; - -// Class_Microsoft_Singularity_DebugStub::g_Break(); - - // update txtCurrent, align to 8 byte - do { - oldTxt = ((buffer_t *)c_buffer)->txtCurrent; - // well, this is ugly but /W3 seems to prevent bitmasking (uint8 *) ... - newTxt = (uint8 *)((unsigned) - ((oldTxt + sizeof(UINT64) + sizeof(UINT32) + - s->m_stringLength + 7)) & ((unsigned)(-1) - 7)); - if (newTxt >= ((buffer_t *)c_buffer)->txtLimit) { - newTxt = (uint8 *)((unsigned) - (((buffer_t *)c_buffer)->txt + sizeof(UINT64) + - sizeof(UINT32) + s->m_stringLength + 7) & - ((unsigned)(-1) - 7)); +void Class_Microsoft_Singularity_Monitoring:: +g_setActive(bool active) +{ + if (MonitoringSource != NULL) { + if (active) { + MonitoringSource->ControlFlags |= Monitoring_ControlFlag_Active; + } else { + MonitoringSource->ControlFlags &= ~Monitoring_ControlFlag_Active; } - } while (! cas(&((buffer_t *)c_buffer)->txtCurrent, oldTxt, newTxt)); - - *(oldTxt + sizeof(UINT64)) = s->m_stringLength; // write length - g_AddText(oldTxt + sizeof(UINT64) + sizeof(UINT32), s); // write string - p->text = oldTxt; // string ptr -> event - - // return buffer - // also sets counter in string, *before* releasing event - g_Enqueue(index, (UINT64 *)oldTxt); - Class_Microsoft_Singularity_Processor::g_RestoreInterrupts(enabled); - -// UINT32 d_ret; -// d_ret = g_ConsistencyCheck(); -// if (d_ret) { -// Class_Microsoft_Singularity_DebugStub::g_Break(); -// } + } } int Class_Microsoft_Singularity_Monitoring:: g_FillLogEntry(Struct_Microsoft_Singularity_Monitoring_LogEntry * log, UINT64 * min_counter) { - // fixme: should we check for c_buffer != 0 ???, probably not necessary, - // Initialize() should have been called long before first request - // here, right? - - indexEntry tail; - indexEntry head; - indexEntry ie; - UINT16 index; - -// UINT32 d_ret; -// d_ret = g_ConsistencyCheck(); -// if (d_ret) { -// Class_Microsoft_Singularity_DebugStub::g_Break(); -// } - - // get tail pointer first - do { - tail.hi_lo.hi = ((buffer_t *)c_buffer)->tail.hi_lo.hi; - tail.hi_lo.lo = ((buffer_t *)c_buffer)->tail.hi_lo.lo; - } while (tail.hi_lo.hi != ((buffer_t *)c_buffer)->tail.hi_lo.hi); - - for (;;) { - - // check if user wants valid entry, no? give him the oldest valid one - if (*min_counter < tail.val.counter) { - *min_counter = tail.val.counter; - } - - // check for too fast request polling - do { - head.hi_lo.hi = ((buffer_t *)c_buffer)->head.hi_lo.hi; - head.hi_lo.lo = ((buffer_t *)c_buffer)->head.hi_lo.lo; - } while (head.hi_lo.hi != ((buffer_t *)c_buffer)->head.hi_lo.hi); - // consumer requests counter in the future - if (*min_counter >= head.val.counter) { - return -1; - } - - // now get the index entry atomically - index = (UINT16)(*min_counter % ((buffer_t *)c_buffer)->entries); - do { - ie.hi_lo.hi = ((buffer_t *)c_buffer)->index[index].hi_lo.hi; - ie.hi_lo.lo = ((buffer_t *)c_buffer)->index[index].hi_lo.lo; - } while (ie.hi_lo.hi != ((buffer_t *)c_buffer)->index[index].hi_lo.hi); - - // if index is newer than expected, try again - if (ie.val.counter != *min_counter) { - continue; - } - - // now copy the whole thing - *log = ((buffer_t *)c_buffer)->logs[ie.val.index]; - - // get tail pointer again - do { - tail.hi_lo.hi = ((buffer_t *)c_buffer)->tail.hi_lo.hi; - tail.hi_lo.lo = ((buffer_t *)c_buffer)->tail.hi_lo.lo; - } while (tail.hi_lo.hi != ((buffer_t *)c_buffer)->tail.hi_lo.hi); - - // check if we were overtaken - if (tail.val.counter <= *min_counter) { - break; // finally, we got it ... - } - } - -// d_ret = g_ConsistencyCheck(); -// if (d_ret) { -// Class_Microsoft_Singularity_DebugStub::g_Break(); -// } - + // + // TODO: impplement it to fix monnet + // return 0; } int Class_Microsoft_Singularity_Monitoring:: g_FillTextEntry(uint8 * src, UINT64 counter, uint8 * dst, int max_size) { - uint8 * old_src = src; + // + // TODO: impplement it to fix monnet + // - if ((src < ((buffer_t *)c_buffer)->txt) || - (src + sizeof(UINT64) + sizeof(UINT32) > - ((buffer_t *)c_buffer)->txtLimit)) { - return -2; // memory bounds error - } - - // 1. get size - // fixme: make another bounds check here - int len = *(UINT32 *)(src + sizeof(UINT64)); - - // fixme: create a memory barrier here - - // 2. check TSC, -> bail out if wrong - volatile UINT64 _counter = *(UINT64 *)(src); - if (_counter != counter) { - return -1; // corrupt data - } - - src += sizeof(UINT64) + sizeof(UINT32); - - // 3. copy string - int i; - for (i = 0; i < max_size && i < len; i++) { - *dst++ = *src++; - } - - // 4. check TSC again, -> bail out if wrong - _counter = *(UINT64 *)(old_src); - if (_counter != counter) { - return -3; // corrupt data - } - return i; -} - -// Copies a given string to a memory area by stripping every second byte, -// UTF16 -> ASCII conversion, assuming there where no control characters in the -// UTF16 string ... -uint8 * Class_Microsoft_Singularity_Monitoring:: -g_AddText(uint8 *dst, Class_System_String *arg) -{ - bartok_char *src = &arg->m_firstChar; - bartok_char *end = src + arg->m_stringLength; - - while (src < end) { - *dst++ = (uint8)*src++; - } -// *dst++ = '\0'; - - return dst; -} - -bool Class_Microsoft_Singularity_Monitoring:: -g_isActive() -{ - return ((buffer_t *)c_buffer)->flags == 0; -} - -void Class_Microsoft_Singularity_Monitoring:: -g_setActive(bool active) -{ - if (active) { - ((buffer_t *)c_buffer)->flags = 0; - } - else { - ((buffer_t *)c_buffer)->flags = 1; - } -} - - -void Class_Microsoft_Singularity_Monitoring:: -g_DebugTest(UINT64 * h_ts, UINT64 * t_ts, UINT64 *min, UINT64 * max) -{ - // output head and tail counters -#if 0 -#if SINGULARITY_KERNEL - printf("Head (%llu, %llu), Tail (%llu, %llu)\n", - ((buffer_t *)c_buffer)->head.val.index, ((buffer_t *)c_buffer)->head.val.counter, - ((buffer_t *)c_buffer)->tail.val.index, ((buffer_t *)c_buffer)->tail.val.counter); -#else - Class_Microsoft_Singularity_DebugStub::Print("Head (" + ((buffer_t *)c_buffer)->head.val.index + - ", " + ((buffer_t *)c_buffer)->head.val.counter + - "), Tail (" + ((buffer_t *)c_buffer)->tail.val.index + - ", " + ((buffer_t *)c_buffer)->tail.val.counter) + ")\n"); - -#endif -#endif - // output head and tail elements - UINT64 ih, it; - UINT64 ih2, it2; - - ih = (((buffer_t *)c_buffer)->head.val.counter - 1) % ((buffer_t *)c_buffer)->entries; - it = (((buffer_t *)c_buffer)->tail.val.counter) % ((buffer_t *)c_buffer)->entries; - - ih2 = ((buffer_t *)c_buffer)->index[ih].val.index; - it2 = ((buffer_t *)c_buffer)->index[it].val.index; - -#if 0 -#if SINGULARITY_KERNEL - printf("TS: Head-1 (%llu), Tail (%llu)\n", - ((buffer_t *)c_buffer)->logs[ih2].cycleCount, - ((buffer_t *)c_buffer)->logs[it2].cycleCount); -#else - Class_Microsoft_Singularity_DebugStub::Print("TS: Head-1 (" + ((buffer_t *)c_buffer)->logs[ih2].cycleCount + - "), Tail (" + ((buffer_t *)c_buffer)->logs[it2].cycleCount + ")\n"); -#endif -#endif - - *h_ts = ((buffer_t *)c_buffer)->logs[ih2].cycleCount; - *t_ts = ((buffer_t *)c_buffer)->logs[it2].cycleCount; - - // show min and max ts + its index - UINT32 i, j; - UINT64 min_ts = 100000000000ULL, max_ts = 0, ts; - for (i = ((buffer_t *)c_buffer)->tail.val.counter; - i < ((buffer_t *)c_buffer)->head.val.counter;) { - j = i % ((buffer_t *)c_buffer)->entries; - ts = ((buffer_t *)c_buffer)->logs[((buffer_t *)c_buffer)->index[j].val.index].cycleCount; - - if (ts < min_ts) { - min_ts = ts; - } - if (ts > max_ts) { - max_ts = ts; - } - - // advance i - i++; - if (i >= ((buffer_t *)c_buffer)->entries) { - i = 0; - } - } -#if 0 -#if SINGULARITY_KERNEL - printf("TS: Min (%llu), Max (%llu)\n", min_ts, max_ts); -#else - Class_Microsoft_Singularity_DebugStub::Print("TS: Min (" + min_ts + "), Max (" +max_ts + ")\n"); -#endif -#endif - *min = min_ts; - *max = max_ts; - -} - -uint32 Class_Microsoft_Singularity_Monitoring:: -g_ConsistencyCheck() -{ - bool id; - - id = Class_Microsoft_Singularity_Processor::g_DisableInterrupts(); - - // run through all indices and check the TSs of the log entries for - // ascending order - uint64 i; - uint32 j; - uint32 k = 0; - uint64 old_ts = 0; - uint64 ts; - for (i = ((buffer_t *)c_buffer)->tail.val.counter; - i < ((buffer_t *)c_buffer)->head.val.counter; - i++, k++) { - j = (UINT32)(i % ((buffer_t *)c_buffer)->entries); - ts = ((buffer_t *)c_buffer)->logs[((buffer_t *)c_buffer)->index[j].val.index].cycleCount; - - if (ts < old_ts && old_ts != 0 && ts != 0) { - ((buffer_t *)c_buffer)->magic = (UINT32)i; - Class_Microsoft_Singularity_DebugStub::g_Break(); - Class_Microsoft_Singularity_Processor::g_RestoreInterrupts(id); - return (UINT32)i; - } - old_ts = ts; - if (k > 100000) { - Class_Microsoft_Singularity_Processor::g_RestoreInterrupts(id); - Class_Microsoft_Singularity_DebugStub::g_Break(); - return 0; - } - } - - Class_Microsoft_Singularity_Processor::g_RestoreInterrupts(id); - - return 0; // everything is fine! + return 0; } // diff --git a/base/Kernel/Native/MpBootInfo.cpp b/base/Kernel/Native/MpBootInfo.cpp deleted file mode 100644 index 4be5fbc..0000000 --- a/base/Kernel/Native/MpBootInfo.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -#include "hal.h" - -#if SINGULARITY_KERNEL - -Struct_Microsoft_Singularity_MpBootInfo* -Struct_Microsoft_Singularity_MpBootInfo::g_HalGetMpBootInfo() -{ - Struct_Microsoft_Singularity_BootInfo* bi = Struct_Microsoft_Singularity_BootInfo::g_HalGetBootInfo(); - uint32 addr = (uint32) bi->MpBootInfo32; - return (Struct_Microsoft_Singularity_MpBootInfo*)((uint8*) addr); -} - -void -Struct_Microsoft_Singularity_MpBootInfo::g_HalReleaseMpStartupLock() -{ - Struct_Microsoft_Singularity_BootInfo* bi = Struct_Microsoft_Singularity_BootInfo::g_HalGetBootInfo(); - uint32 lockAddr = (uint32) bi->MpStartupLock32; - if (lockAddr != 0) - { - *((uint16*) lockAddr) = 0; - } -} - -#endif // SINGULARITY_KERNEL diff --git a/base/Kernel/Native/Number.cpp b/base/Kernel/Native/Number.cpp index 3414a67..bf97a22 100644 --- a/base/Kernel/Native/Number.cpp +++ b/base/Kernel/Native/Number.cpp @@ -6,11 +6,15 @@ // // File: Number.cpp // -// Note: +// Note: Kernel & Process // #include "hal.h" +////////////////////////////////////////////////////////////////////////////// +// +extern "C" int _fltused = 0x9875; + ////////////////////////////////////////////////////////////////////////////// // #define LITTLE_ENDIAN @@ -796,7 +800,7 @@ bool Class_System_Number::g_ecvt(double value, (*p)++; } else { - // we probably don't want to truncate all of the zeros! + // We probably don't want to truncate all of the zeros! for (; p >= man && *p == '0'; p--) { // remove extra zeros; } diff --git a/base/Kernel/Native/PEImage.cpp b/base/Kernel/Native/PEImage.cpp index 871b476..f340b1c 100644 --- a/base/Kernel/Native/PEImage.cpp +++ b/base/Kernel/Native/PEImage.cpp @@ -6,13 +6,11 @@ // // File: PEImage.cpp // -// Note: +// Note: Kernel Only // #include "hal.h" -#if SINGULARITY_KERNEL - //////////////////////////////////////////////////////////////// Image Loader. // #if PAGING @@ -34,7 +32,7 @@ int FakeSyscallBegin(int abiIndex) // TODO: these cannot be functions; we cannot trust the user stack uintptr PushAbiStack(uintptr esp) { - Struct_Microsoft_Singularity_X86_ThreadContext * context = + Struct_Microsoft_Singularity_ThreadContext * context = Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext(); AbiStackHead *head = (AbiStackHead *)(context->abiStackHead); head->prevBegin = context->stackBegin; @@ -47,7 +45,7 @@ uintptr PushAbiStack(uintptr esp) uintptr PopAbiStack(AbiStackHead *head) { - Struct_Microsoft_Singularity_X86_ThreadContext * context = + Struct_Microsoft_Singularity_ThreadContext * context = Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext(); // HACK: if the ABI call changed stackBegin/stackLimit, keep the changes // (otherwise, switch back to original stack) @@ -58,10 +56,11 @@ uintptr PopAbiStack(AbiStackHead *head) return head->esp; } -#define SEGMENT_SELECTOR(s) \ - (uint16)(offsetof(Struct_Microsoft_Singularity_CpuInfo,s) \ - - offsetof(Struct_Microsoft_Singularity_CpuInfo,GdtNull)) -const uint16 ourFs = SEGMENT_SELECTOR(GdtPF); +//#define SEGMENT_SELECTOR(s) \ +// (uint16)(offsetof(Struct_Microsoft_Singularity_CpuInfo,s) \ +// - offsetof(Struct_Microsoft_Singularity_CpuInfo,GdtNull)) +//const uint16 ourFs = SEGMENT_SELECTOR(GdtPF); + __declspec(naked) void FakeSyscall() { __asm @@ -115,7 +114,7 @@ __declspec(naked) void FakeSyscall() pop eax // pointer to arg1 // user stack = ret arg1 arg2 ret arg3 ... argn ... call edx - call Class_Microsoft_Singularity_Processor::g_EnterRing3 + call Class_Microsoft_Singularity_Isal_Isa::g_EnterUserMode // Any changes here must be reflected in EnterRing0 mov ecx, esp @@ -156,13 +155,13 @@ int32 Class_Microsoft_Singularity_Loader_PEImage:: g_HalCallEntryPoint(UIntPtr entry, int threadIndex, bool runAtRing3) { if (entry == 0) { - printf("Class_Microsoft_Singularity_Loader_PEImage::g_HalCallPeEntry called with null entry.\n"); + __debugbreak(); return -1; } #if PAGING if (runAtRing3) { - Class_Microsoft_Singularity_Processor::g_EnterRing3(); + Class_Microsoft_Singularity_Isal_Isa::g_EnterUserMode(); } #else Assert(!runAtRing3); @@ -173,7 +172,7 @@ g_HalCallEntryPoint(UIntPtr entry, int threadIndex, bool runAtRing3) #if PAGING if (runAtRing3) { // We should still be running at ring-3 when the process unwinds - Assert(!Class_Microsoft_Singularity_Processor::g_AtKernelPrivilege()); + Assert(Class_Microsoft_Singularity_Isal_Isa::g_IsInUserMode()); // Call ABI zero, which drops us to ring-0. Why is this not // a gaping security hole? The kernel should verify that this @@ -194,7 +193,6 @@ done: return retval; } -#endif // SINGULARITY_KERNEL // ///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/Platform.cpp b/base/Kernel/Native/Platform.cpp new file mode 100644 index 0000000..d333334 --- /dev/null +++ b/base/Kernel/Native/Platform.cpp @@ -0,0 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Platform.cpp +// +// Note: Kernel Only +// + +#include "hal.h" + +void Class_Microsoft_Singularity_Hal_Platform::g_NativeKill(int32 action) +{ + Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->KillAction = action; + + void (__cdecl *pfKill)(void) = (void (__cdecl *)(void))Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->Kill32; + + pfKill(); + __debugbreak(); +} diff --git a/base/Kernel/Native/ProcessEntry.cpp b/base/Kernel/Native/ProcessEntry.cpp new file mode 100644 index 0000000..528fa87 --- /dev/null +++ b/base/Kernel/Native/ProcessEntry.cpp @@ -0,0 +1,112 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ProcessEntry.cpp +// +// Note: Process Only +// + +#include "hal.h" + +int __cdecl _cinit(void); + +////////////////////////////////////////////////////////////////////////////// +// debugging code. Put it here for now. + +void fail_assert(Class_System_String *message) +{ + Struct_Microsoft_Singularity_V1_Services_DebugService::g_Print((bartok_char*)&message->m_firstChar, + message->m_stringLength); + //Struct_Microsoft_Singularity_V1_Services_DebugService::g_Break(); + Class_Microsoft_Singularity_DebugStub::g_Break(); +} + +////////////////////////////////////////////////////////////////////////////// +// + +BOOL KdDebuggerNotPresent; +extern Class_System_RuntimeType * brtmainClass; +extern int (*brtmain)(ClassVector_Class_System_String *args); +extern int brtmainReturnsInt; + +#if ISA_IX86 + +// Note: CallMain's return value is only meaningful if brtmainReturnsInt is true. +// Example: +// int ret = CallMain(args); +// if (!MainReturnsInt()) ret = 0; +__declspec(naked) int Class_Microsoft_Singularity_AppRuntime:: +g_CallMain(ClassVector_Class_System_String *args) +{ + // To avoid creating an unmanaged stack frame, jmp directly to the main function: + __asm { + mov eax, brtmain; + jmp eax; + } +} + +#endif + +bool Class_Microsoft_Singularity_AppRuntime::g_MainReturnsInt() +{ + return (brtmainReturnsInt != 0); +} + +void Class_Microsoft_Singularity_AppRuntime::g_SetDebuggerPresence(bool debuggerPresent) +{ + KdDebuggerNotPresent = !debuggerPresent; +} + +////////////////////////////////////////////////////////////////////////////// +// +// These are the first instruction executed in a process. +// +extern "C" int32 __fastcall RuntimeEntryPoint(int threadIndex) +{ + int32 ret = 0; + + if (threadIndex == -1) { + // WARNING: When initializing an SIP, before making any other calls + // into the HAL we need to make sure the HAL is initialized using + // the following API. + + Class_Microsoft_Singularity_Isal_Isa::c_currentThreadOffset + = Struct_Microsoft_Singularity_V1_Services_PlatformService::g_GetThreadContextOffset(); + Class_Microsoft_Singularity_Isal_Isa::c_currentCpuOffset + = Struct_Microsoft_Singularity_V1_Services_PlatformService::g_GetProcessorContextOffset(); + } + + Struct_Microsoft_Singularity_ThreadContext * context = + Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext(); + + if (!Struct_Microsoft_Singularity_ThreadContext::m_IsInKernelMode( + context)) { + // fail assertion in uninitialized process mode: + __debugbreak(); + } + + Struct_Microsoft_Singularity_ThreadContext::m_SetProcessMode(context); + + if (threadIndex == -1) { + _cinit(); + + Class_Microsoft_Singularity_Tracing::g_Initialize(); +// Class_Microsoft_Singularity_Tracing::g_Log(0, "RuntimeEntryPoint:Main"); +#ifndef NO_MONITORING + Class_Microsoft_Singularity_Monitoring::g_Initialize(); +#endif + + ret = Class_Microsoft_Singularity_AppRuntime::g_AppStart(brtmainClass); + } + else { +// Class_Microsoft_Singularity_Tracing::g_Log(0, "RuntimeEntryPoint:Thread"); + Class_System_Threading_Thread::g_ThreadStub(threadIndex); + } + + Struct_Microsoft_Singularity_ThreadContext::m_SetKernelMode(context); + + return ret; +} diff --git a/base/Kernel/Native/Processor.cpp b/base/Kernel/Native/Processor.cpp deleted file mode 100644 index a6fe58a..0000000 --- a/base/Kernel/Native/Processor.cpp +++ /dev/null @@ -1,826 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Processor.cpp -// -// Note: -// - -#include "hal.h" - -#if SINGULARITY_KERNEL -#include "halkd.h" -#endif // SINGULARITY_KERNEL - -/////////////////////////////////////////////////////////// Segment Selectors. -// -#define SEGMENT_SELECTOR(s) \ - (uint16)(offsetof(Struct_Microsoft_Singularity_CpuInfo,s) \ - - offsetof(Struct_Microsoft_Singularity_CpuInfo,GdtNull)) - -/////////////////////////////////////////////////////////// Processor Context. -// -Class_Microsoft_Singularity_Processor * -Struct_Microsoft_Singularity_X86_ProcessorContext:: -m_GetProcessor(Struct_Microsoft_Singularity_X86_ProcessorContext *self) -{ - return self->_processor; -} - -#if SINGULARITY_KERNEL -void -Struct_Microsoft_Singularity_X86_ProcessorContext:: -m_UpdateAfterGC(Struct_Microsoft_Singularity_X86_ProcessorContext * self, - Class_Microsoft_Singularity_Processor *processor) -{ - self->_processor = processor; -} -#endif // SINGULARITY_KERNEL - -/////////////////////////////////////////////////////////// Processor Methods. -// -#if SINGULARITY_KERNEL - -static int g_nIgnoredHardwareInterrupts = 0; - -static __declspec(align(8)) Struct_Microsoft_Singularity_X86_IDTP g_idt; -static __declspec(align(8)) Struct_Microsoft_Singularity_X86_IDTE g_idtEntries[256]; -void (__fastcall *c_exceptionHandler)(int exception, - Struct_Microsoft_Singularity_X86_ThreadContext *context); -void (__fastcall *c_interruptHandler)(int exception, - Struct_Microsoft_Singularity_X86_ThreadContext *context); - -__declspec(naked) -void Class_Microsoft_Singularity_Processor::g_HaltUntilInterruptNative() -{ - __asm { - hlt; - ret; - } -} - -__declspec(naked) -void Class_Microsoft_Singularity_Processor::g_InitFpu(void) -{ - __asm { - finit; - mov eax, 0x37e; - push eax; - fldcw [esp]; - pop eax; - ret; - } -} - -__declspec(naked) -uint32 Class_Microsoft_Singularity_Processor::g_ReadFpuStatus(void) -{ - __asm { - xor eax,eax; - push eax; - fnstsw [esp]; - pop eax; - ret; - } -} - -__declspec(naked) -void Class_Microsoft_Singularity_Processor::g_ClearFpuStatus(void) -{ - __asm { - fnclex; - ret; - } -} - -__declspec(naked) -Class_Microsoft_Singularity_Processor * -Class_Microsoft_Singularity_Processor::g_GetCurrentProcessor() -{ - __asm { - mov eax, fs:[0]Struct_Microsoft_Singularity_X86_ProcessorContext._processor; - ret; - } -} - -#endif // SINGULARITY_KERNEL - -__declspec(naked) -uint64 Class_Microsoft_Singularity_Processor::g_ReadMsr(uint32 counter) -{ - __asm { - // ECX = msr - rdmsr; - ret; - } -} - -void Class_Microsoft_Singularity_Processor::g_WriteMsr(uint32 msr, uint64 value) -{ - uint32 lo = *(((uint32*)&value) + 0); - uint32 hi = *(((uint32*)&value) + 1); - - __asm { - mov ecx, msr; - mov eax, lo; - mov edx, hi; - wrmsr; - } -} - -void Class_Microsoft_Singularity_Processor::g_ReadCpuid(uint32 feature, - uint32 *p0, - uint32 *p1, - uint32 *p2, - uint32 *p3) -{ - uint32 v0; - uint32 v1; - uint32 v2; - uint32 v3; - - __asm { - mov eax, feature; - cpuid; - mov v0, eax; - mov v1, ebx; - mov v2, ecx; - mov v3, edx; - } - *p0 = v0; - *p1 = v1; - *p2 = v2; - *p3 = v3; -} - -__declspec(naked) -uint64 Class_Microsoft_Singularity_Processor::g_ReadPmc(uint32 counter) -{ - __asm { - // ECX = counter - rdpmc; - ret; - } -} - -__declspec(naked) -uint64 Class_Microsoft_Singularity_Processor::g_GetCycleCount() -{ - __asm { - rdtsc; - ret; - } -} - -#ifndef ZERO_RUNTIME -UIntPtr Class_Microsoft_Singularity_Processor::g_GetFrameEip(UIntPtr ebp) -{ - if (ebp < (UIntPtr)0x10000) { - return 0; - } - return ((UIntPtr*)ebp)[1]; -} - -UIntPtr Class_Microsoft_Singularity_Processor::g_GetFrameEbp(UIntPtr ebp) -{ - if (ebp < (UIntPtr)0x10000) { - return 0; - } - return ((UIntPtr*)ebp)[0]; -} - -__declspec(naked) -UIntPtr Class_Microsoft_Singularity_Processor::g_GetStackPointer() -{ - __asm { - mov eax, esp; - ret; - } -} - -__declspec(naked) -UIntPtr Class_Microsoft_Singularity_Processor::g_GetFramePointer() -{ - __asm { - mov eax, ebp; - ret; - } -} - -__declspec(naked) -Struct_Microsoft_Singularity_X86_ProcessorContext * -Class_Microsoft_Singularity_Processor::g_GetCurrentProcessorContext() -{ - __asm { - mov eax, fs:[0]Struct_Microsoft_Singularity_X86_ProcessorContext.processorContext; - ret; - } -} -#endif // !ZERO_RUNTIME - -__declspec(naked) -Struct_Microsoft_Singularity_X86_ThreadContext * -Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext() -{ - __asm { - mov eax, fs:[0]Struct_Microsoft_Singularity_X86_ProcessorContext.threadContext; - ret; - } -} - -__declspec(naked) -Class_System_Threading_Thread * -Class_Microsoft_Singularity_Processor::g_GetCurrentThread() -{ - __asm { - mov edx, fs:[0]Struct_Microsoft_Singularity_X86_ProcessorContext.threadContext; - mov eax, [edx]Struct_Microsoft_Singularity_X86_ThreadContext._thread; - ret; - } -#if 0 - Struct_Microsoft_Singularity_X86_ProcessorContext * proc; - __asm { - mov eax, fs:[0]Struct_Microsoft_Singularity_X86_ProcessorContext.processorContext; - mov proc, eax; - } - return proc->threadContext->_thread; -#endif -} - -#if SINGULARITY_KERNEL - -__declspec(naked) -void Class_Microsoft_Singularity_Processor:: -g_SetCurrentThreadContext(Struct_Microsoft_Singularity_X86_ThreadContext * context) -{ - __asm { - mov fs:[0]Struct_Microsoft_Singularity_X86_ProcessorContext.threadContext, ecx; - ret; - } -} - -#endif // SINGULARITY_KERNEL - -#if SINGULARITY_KERNEL -#if DO_FXSAVE_TEST -#pragma warning(disable:4733) // We'll touch fs:[0] if we want to. -int Class_Microsoft_Singularity_Processor::g_TestFxsave() -{ - uint64 beg; - uint64 end; - int loops = 1000000; - - Struct_Microsoft_Singularity_X86_ThreadContext * context = - g_GetCurrentThreadContext(); - Struct_Microsoft_Singularity_X86_MmxContext *fxregs = &context->mmx; - int fs0; - - bool fEnabled = g_DisableInterrupts(); - - beg = RDTSC(); - while (loops-- > 0) { - __asm { - mov eax, fxregs; - fxsave [eax]; - fxrstor [eax]; - } - } - end = RDTSC(); - - g_RestoreInterrupts(fEnabled); - - printf("value of fs:[0]: %08x\n", fs0); - printf("elapsed: %d\n", (int)(end - beg)); - return ((int)((end - beg) / 1000000)); -} -#endif DO_FS0_TEST - -#if DO_FS0_TEST -#pragma warning(disable:4733) // We'll touch fs:[0] if we want to. -int Class_Microsoft_Singularity_Processor::g_TestFs0() -{ - uint64 beg; - uint64 end; - int loops = 1000000; - void * stackLimit = (void *)0x800000; - void * threadContext = &stackLimit; - void * processorContextThread = &threadContext; - int fs0; - - bool fEnabled = g_DisableInterrupts(); - - __asm { - mov eax, processorContextThread; - mov fs:[0], eax; - } - - beg = RDTSC(); - while (loops-- > 0) { - __asm { - mov eax, fs:[0]; - mov eax, [eax]; - add eax, 0x100; - cmp eax, esp; - mov fs0, eax; - } - } - end = RDTSC(); - - g_RestoreInterrupts(fEnabled); - - printf("value of fs:[0]: %08x\n", fs0); - printf("elapsed: %d\n", (int)(end - beg)); - return ((int)((end - beg) / 1000000)); -} -#endif DO_FS0_TEST - -#if DO_CLI_STI_TEST -int Class_Microsoft_Singularity_Processor::g_TestCliSti() -{ - uint64 beg; - uint64 end; - int loops = 1000000; - - bool fEnabled = g_DisableInterrupts(); - - g_nIgnoredHardwareInterrupts = 0; - - __asm sti; - beg = RDTSC(); - while (loops-- > 0) { - __asm { - cli; // 1 - sti; - cli; // 2 - sti; - cli; // 3 - sti; - cli; // 4 - sti; - cli; // 5 - sti; - cli; // 6 - sti; - cli; // 7 - sti; - cli; // 8 - sti; - cli; // 9 - sti; - cli; // 10 - sti; - } - } - end = RDTSC(); - __asm cli; - - printf("Ignored %d hardware interrupts.\n", g_nIgnoredHardwareInterrupts); - - g_RestoreInterrupts(fEnabled); - - return ((int)((end - beg) / 1000000)) / 10; -} -#endif // DO_CLI_STI_TEST -#endif // SINGULARITY_KERNEL - -#if SLOW_INTERRUPT_SAVE_RESTORE - -static uint32 dstack[8]; -static uint64 disabled = 0; -static uint64 restored = 0; - -bool Class_Microsoft_Singularity_Processor::g_DisableInterrupts() -{ - uint32 eflags; - - __asm { - pushfd; - pop eax; - mov eflags, eax; - cli; - } -#if 0 - if ((eflags & Struct_Microsoft_Singularity_X86_EFlags_IF) != 0) { - uint32 _ebp; - __asm { - mov _ebp, ebp; - } - for (int i = 0; i < arrayof(dstack); i++) { - if (_ebp != 0) { - dstack[i] = ((uint32*)_ebp)[1]; - _ebp = ((uint32*)_ebp)[0]; - } - else { - dstack[i] = 0; - } - } - disabled = g_GetCycleCount(); - } -#endif // 0 - return (eflags & Struct_Microsoft_Singularity_X86_EFlags_IF) != 0; -} - -void Class_Microsoft_Singularity_Processor::g_RestoreInterrupts(bool enabled) -{ - if (enabled) { -#if 0 - restored = g_GetCycleCount(); - if (restored - disabled > 10000000000 && disabled != 0) { - __asm int 3; - } -#endif // 0 - __asm sti; - } -} - -#else // SLOW_INTERRUPT_SAVE_RESTORE - -__declspec(naked) -bool Class_Microsoft_Singularity_Processor::g_DisableInterrupts() -{ - __asm { - pushfd; - pop eax; - test eax, Struct_Microsoft_Singularity_X86_EFlags_IF; - setnz al; - cli; - ret; - } -} - -__declspec(naked) -void Class_Microsoft_Singularity_Processor::g_RestoreInterrupts(bool enabled) -{ - __asm { - test cl, cl; - je done; - sti; - done: - ret; - } -} - -#endif // SLOW_INTERRUPT_SAVE_RESTORE - -__declspec(naked) -bool Class_Microsoft_Singularity_Processor::g_InterruptsDisabled() -{ - __asm { - pushfd; - pop eax; - test eax, Struct_Microsoft_Singularity_X86_EFlags_IF; - setz al; - ret; - } -} - -__declspec(naked) void Class_Microsoft_Singularity_Processor::g_EnterRing3() -{ -// int uc3 = SEGMENT_SELECTOR(GdtUC) + 3; -// int ud3 = SEGMENT_SELECTOR(GdtUD) + 3; -// int uf3 = SEGMENT_SELECTOR(GdtPF) + 3; // for the moment, share UF and PF - -// TODO: get rid of hexadecimal constants below - - // warning: preserve return values eax, edx (for returning from ABI) - __asm { - push edx - mov ecx, esp - mov edx, ring3 - _emit 0x0f; - _emit 0x35; //sysexit - ring3: - pop edx - mov cx, ss - mov ds, cx - mov es, cx - mov ecx, 0x38 + 3 // SEGMENT_SELECTOR(GdtPF) + 3 - mov fs, cx - ret - } -} - -#if DONT_TRY_THIS_YET -__declspec(naked) bool Class_Microsoft_Singularity_Processor::g_AtKernelPrivilege() -{ - // The bottom two bits of the CS selector are the RPL - // (requested privilege level) of the selector. If - // this is zero, we're running at ring0. Otherwise, - // we're less privileged. - __asm { - mov ax, cs; - test ax, 3; - setnz al; // or setz al; - ret; - } -} -#else -bool Class_Microsoft_Singularity_Processor::g_AtKernelPrivilege() -{ - uint16 _cs; - - __asm{ - mov _cs, cs - } - - // The bottom two bits of the CS selector are the RPL - // (requested privilege level) of the selector. If - // this is zero, we're running at ring0. Otherwise, - // we're less privileged. - return (_cs & 0x3) == 0; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -#if SINGULARITY_KERNEL -void DumpContext(Struct_Microsoft_Singularity_X86_ThreadContext *context) -{ - printf(" thr=%8x, prv=%8x, nxt=%8x, ctx=%08x\n", - context->_thread, context->prev, context->next, context); - printf(" num=%8x, err=%8x, cr2=%8x\n", - context->num, context->err, context->cr2); - printf(" eax=%8x, ebx=%8x, ecx=%8x, edx=%8x\n", - context->eax, context->ebx, context->ecx, context->edx); - printf(" esp=%8x, ebp=%8x, esi=%8x, edi=%8x\n", - context->esp, context->ebp, context->esi, context->edi); - printf(" efl=%8x, eip=%8x, beg=%8x, lim=%8x\n", - context->efl, context->eip, context->stackBegin, context->stackLimit); - printf(" cs0=%8x\n", context->cs0); - -#if 0 - uint32 _cs; - uint32 _ss; - uint32 _ds; - uint32 _es; - uint32 _fs; - uint32 _gs; - uint32 _esp; - __asm { - push cs; - pop _cs; - push ss; - pop _ss; - push ds; - pop _ds; - push es; - pop _es; - push fs; - pop _fs; - push gs; - pop _gs; - push esp; - pop _esp; - } - - printf(" cs=%04x, ss=%04x, ds=%04x, es=%04x, fs=%04x, gs=%04x, esp=%08x\n", - _cs, _ss, _ds, _es, _fs, _gs, _esp); -#endif -} - -void LimitedDispatchException(int interrupt, Struct_Microsoft_Singularity_X86_ThreadContext *context) -{ -#if 0 - Struct_Microsoft_Singularity_X86_ProcessorContext * proc; - __asm { - mov eax, fs:[0]Struct_Microsoft_Singularity_X86_ProcessorContext.processorContext; - mov proc, eax; - } -#endif - if (context->num == 0x2E) { - printf("SYSCALL!!\n"); - DumpContext(context); - return; - } - if (context->num == 0x2F) { - printf("SYSENTER!!\n"); - DumpContext(context); - return; - } - if (context->num > 0x20) { - g_nIgnoredHardwareInterrupts++; - return; - } - -#if 1 - printf("-- Exception 0x%02x -------------------------------------------\n", - interrupt); - DumpContext(context); -#endif // 1 - - printf("Entering debugger stub...\n"); - printf("[= Exception: %02x eip=%08x efl=%08x ==\n", - interrupt, context->eip, context->efl); - - Class_Microsoft_Singularity_DebugStub::g_Trap(context, false); -} - -void Class_Microsoft_Singularity_Processor::g_ClearIdtTable() -{ - printf("Clearing C# IDT Table.\n"); - c_exceptionHandler = LimitedDispatchException; - c_interruptHandler = LimitedDispatchException; -} - -void Class_Microsoft_Singularity_Processor::g_SetIdtTable() -{ - printf("Setting C# IDT Table.\n"); - c_exceptionHandler = Class_Microsoft_Singularity_Processor::g_DispatchException; - c_interruptHandler = Class_Microsoft_Singularity_Processor::g_DispatchInterrupt; -} - -extern "C" void __cdecl EdtEnter0(void); -extern "C" void __cdecl EdtEnter1(void); -extern "C" void __cdecl IdtEnter20(void); -extern "C" void __cdecl IdtEnter21(void); -extern "C" void __cdecl SysEnter(void); -extern void FakeSyscall(); - -void IdtInitialize() -{ - // Configure the simplest IDT Handler. - Class_Microsoft_Singularity_Processor::g_ClearIdtTable(); - - // Create the IDT Entry Table, first set the exception entries. - uint32 entry = (uint32)EdtEnter0; - uint32 offset = ((uint32)EdtEnter1) - ((uint32)EdtEnter0); - - for (int i = 0; i < 0x20; i++) { -#if DOUBLE_FAULT_HANDLER - if (i == Struct_Microsoft_Singularity_X86_EVectors_DoubleFault) { - // The double fault handler uses a task gate to set up stack. - - g_idtEntries[i].offset_0_15 = 0; - g_idtEntries[i].offset_16_31 = 0; - g_idtEntries[i].selector = SEGMENT_SELECTOR(GdtDF); - g_idtEntries[i].access = - (Struct_Microsoft_Singularity_X86_IDTE_PRESENT | - Struct_Microsoft_Singularity_X86_IDTE_DPL_RING0 | // RING0 for hardware ints. - Struct_Microsoft_Singularity_X86_IDTE_TASK_GATE); - - - uint32 * pi = (uint32 *)&g_idtEntries[i]; - printf("idt[0x%02x] = %08x %08x\n", i, pi[0], pi[1]); - } - else { -#endif - g_idtEntries[i].offset_0_15 = (uint16)entry; - g_idtEntries[i].selector = SEGMENT_SELECTOR(GdtPC); - g_idtEntries[i].access = - (Struct_Microsoft_Singularity_X86_IDTE_PRESENT | - Struct_Microsoft_Singularity_X86_IDTE_DPL_RING3 | // RING0 for hardware ints. - Struct_Microsoft_Singularity_X86_IDTE_INT_GATE); - g_idtEntries[i].offset_16_31 = (uint16)(entry >> 16); -#if DOUBLE_FAULT_HANDLER - } -#endif - entry += offset; - } - - // Now set the interrupt entries. - entry = (uint32)IdtEnter20; - offset = ((uint32)IdtEnter21) - ((uint32)IdtEnter20); - - for (int i = 0x20; i < arrayof(g_idtEntries); i++) { - g_idtEntries[i].offset_0_15 = (uint16)entry; - g_idtEntries[i].selector = SEGMENT_SELECTOR(GdtPC); - g_idtEntries[i].access = - (Struct_Microsoft_Singularity_X86_IDTE_PRESENT | - Struct_Microsoft_Singularity_X86_IDTE_DPL_RING3 | - Struct_Microsoft_Singularity_X86_IDTE_INT_GATE); - g_idtEntries[i].offset_16_31 = (uint16)(entry >> 16); - entry += offset; - } - - g_idt.limit = sizeof(g_idtEntries); - g_idt.addr = (uintptr)g_idtEntries; -} - -void IdtLoad() -{ - __asm { - lidt g_idt.limit; - } -} - -void ProcessorInitialize(const Struct_Microsoft_Singularity_CpuInfo *pCpuInfo, - int cpuId) -{ - Struct_Microsoft_Singularity_X86_ProcessorContext *proc - = (Struct_Microsoft_Singularity_X86_ProcessorContext *)pCpuInfo->Fs32; - - proc->cpuId = cpuId; - // Set up a default environment. - proc->processorContext = proc; - proc->threadContext = &proc->thirdContext; // Insure valid context early in boot. - proc->threadContext->_thread = (Class_System_Threading_Thread *)pCpuInfo->Gs32; - proc->threadContext->stackLimit = 0; - proc->threadContext->stackBegin = 0; - - // Make sure we have a viable exception stack for early debugging. - // Note that this stack is only used for early debugging (i.e before Kernel.cs runs). - proc->exceptionStackBegin - = Struct_Microsoft_Singularity_BootInfo_KERNEL_STACK_BEGIN + 0x2000; - proc->exceptionStackLimit - = Struct_Microsoft_Singularity_BootInfo_KERNEL_STACK_BEGIN; - -#if PAGING - // XXX Set up MSRs for SYSENTER/SYSEXIT - Class_Microsoft_Singularity_Processor::g_WriteMsr(0x174, SEGMENT_SELECTOR(GdtPC)); - Class_Microsoft_Singularity_Processor::g_WriteMsr(0x175, proc->exceptionStackBegin + 0x2000); -#if !PAGING - Class_Microsoft_Singularity_Processor::g_WriteMsr(0x176, (UINT64)SysEnter); -#else - Class_Microsoft_Singularity_Processor::g_WriteMsr(0x176, (UINT64)FakeSyscall); -#endif -#endif -} - -void Class_Microsoft_Singularity_Processor::g_PrivateEnablePaging(uint32 pdpt) -{ - __asm { - // use the pdpt argument directly as the cr3 value. This leaves - // top-level write-through and cache-disable turned off. - mov eax, pdpt; - mov cr3, eax; - - // Turn on paging and write protection - mov eax, cr0; - or eax, Struct_Microsoft_Singularity_X86_CR0_PG + Struct_Microsoft_Singularity_X86_CR0_WP; - mov cr0, eax; - - jmp reload_tlb; - ALIGN 0x010; - -reload_tlb: - } -} - -void Class_Microsoft_Singularity_Processor::g_PrivateChangeAddressSpace(uint32 pdpt) -{ - __asm { - // use the pdpt argument directly as the cr3 value. This leaves - // top-level write-through and cache-disable turned off. - mov eax, pdpt; - mov cr3, eax; - - jmp reload_tlb; - ALIGN 0x010; - -reload_tlb: - } -} - -__declspec(naked) -void Class_Microsoft_Singularity_Processor::g_DisablePaging() -{ - __asm { - // Turn off paging. - mov eax, cr0; - and eax, NOT (Struct_Microsoft_Singularity_X86_CR0_PG + Struct_Microsoft_Singularity_X86_CR0_WP); - mov cr0, eax; - - // Flush and reset the TLB. - mov eax,0; - mov cr3,eax; - - jmp reload_tlb; - ALIGN 0x010; - -reload_tlb: - } -} - -void Class_Microsoft_Singularity_Processor::g_PrivateInvalidateTLBEntry(UIntPtr pageAddr) -{ - __asm { - mov eax, pageAddr; - invlpg [eax]; - } -} - -__declspec(naked) -uint32 Class_Microsoft_Singularity_Processor::g_GetCr3() -{ - __asm { - mov eax, cr3; - ret; - } -} - -// haryadi -void Class_Microsoft_Singularity_Processor::g_MpCallEntryPoint(UIntPtr entry) -{ - __asm { - mov eax, entry; - call eax; - } -} - -#endif // SINGULARITY_KERNEL - -// -///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/Stacks.cpp b/base/Kernel/Native/Stacks.cpp deleted file mode 100644 index 61c4bca..0000000 --- a/base/Kernel/Native/Stacks.cpp +++ /dev/null @@ -1,34 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Stacks.cpp -// -// Note: -// - -#include "hal.h" - -// #define TEST_STACK_LINKING -#ifdef TEST_STACK_LINKING -void Class_Microsoft_Singularity_Memory_Stacks::g_TestStackLink1024() -{ -} - -void Class_Microsoft_Singularity_Memory_Stacks::g_TestStackLink2048() -{ -} - -void Class_Microsoft_Singularity_Memory_Stacks::g_TestStackLink4096() -{ -} - -void Class_Microsoft_Singularity_Memory_Stacks::g_TestStackLink8192() -{ -} -#endif // TEST_STACK_LINKING - -// -///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/SystemEvents.inl b/base/Kernel/Native/SystemEvents.inl new file mode 100644 index 0000000..659512e --- /dev/null +++ b/base/Kernel/Native/SystemEvents.inl @@ -0,0 +1,328 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: SystemEvents.inl +// +// Note: +// + + +// Fill in the missing macro with default empty definitions +// This is helpful for easier maintanance of the code and macros +// as the consummers only need to provide the definitions that they would like to handle +// +// NOTE: at the end of the file, all definitions are cleared +// + +#ifndef DECLARE_STRUCTURE_BEGIN +#define DECLARE_STRUCTURE_BEGIN(x,d) +#endif + +#ifndef DECLARE_STRUCTURE_END +#define DECLARE_STRUCTURE_END(x) +#endif + +#ifndef DECLARE_FIELD +#define DECLARE_FIELD(s,t,n) +#endif + +#ifndef DECLARE_SPECIAL_FIELD +#define DECLARE_SPECIAL_FIELD(s,t,n) +#endif + +#ifndef DECLARE_EXTENDED_ARRAY_FIELD +#define DECLARE_EXTENDED_ARRAY_FIELD(s,t,n) +#endif + +#ifndef DECLARE_VALUE +#define DECLARE_VALUE(s,v,f,n) +#endif + +#ifndef DECLARE_GENERIC_FIELD +#define DECLARE_GENERIC_FIELD(s,t,sz,t1,n) +#endif + +#ifndef DECLARE_ENUM_BEGIN +#define DECLARE_ENUM_BEGIN(x,sz) +#endif + +#ifndef DECLARE_ENUM_END +#define DECLARE_ENUM_END(x) +#endif + +#ifdef _CONSTANT_DEFINITIONS + + #define RECORD_EVENT_METADATA_FLAG 0x8000 + #define RECORD_EVENT_TYPE_ARRAY_FLAG 0x4000 + + #define RECORD_EVENT_TYPE (RECORD_EVENT_METADATA_FLAG | 0x0100) + #define RECORD_EVENT_FIELD (RECORD_EVENT_METADATA_FLAG | 0x0200) + #define RECORD_EVENT_SOURCE (RECORD_EVENT_METADATA_FLAG | 0x0300) + #define RECORD_EVENT_CONTROLLER (RECORD_EVENT_METADATA_FLAG | 0x0400) + #define RECORD_EVENT_VALUE (RECORD_EVENT_METADATA_FLAG | 0x0800) + #define RECORD_EVENT_GENERIC_FIELD (RECORD_EVENT_METADATA_FLAG | 0x1000) + #define RECORD_EVENT_ENUM (RECORD_EVENT_METADATA_FLAG | 0x2000) + + #define GENERIC_TYPE_SIGNATURE 0x0001 + +#endif // _CONSTANT_DEFINITIONS + +DECLARE_STRUCTURE_BEGIN(MEMORY_HEADER, "") + + DECLARE_FIELD(MEMORY_HEADER, TYPE_uint16, Size) + DECLARE_FIELD(MEMORY_HEADER, TYPE_uint16, Link) + DECLARE_FIELD(MEMORY_HEADER, TYPE_uint16, Offset) + DECLARE_FIELD(MEMORY_HEADER, TYPE_uint16, Flags) + DECLARE_FIELD(MEMORY_HEADER, TYPE_uint64, Timestamp) + DECLARE_FIELD(MEMORY_HEADER, TYPE_UIntPtr, Type) + DECLARE_FIELD(MEMORY_HEADER, TYPE_uint16, TID) + DECLARE_FIELD(MEMORY_HEADER, TYPE_uint16, Cpu) + +DECLARE_STRUCTURE_END(MEMORY_HEADER) + +DECLARE_ENUM_BEGIN(LOG_SEVERITY,TYPE_uint8) + DECLARE_VALUE(LOG_SEVERITY, 0xe, 0, Fatal) + DECLARE_VALUE(LOG_SEVERITY, 0xc, 0, Error) + DECLARE_VALUE(LOG_SEVERITY, 0xa, 0, Warning) + DECLARE_VALUE(LOG_SEVERITY, 0x8, 0, Notice) + DECLARE_VALUE(LOG_SEVERITY, 0x6, 0, Trace) + DECLARE_VALUE(LOG_SEVERITY, 0x4, 0, Audit) + DECLARE_VALUE(LOG_SEVERITY, 0x2, 0, Debug) +DECLARE_ENUM_END(LOG_SEVERITY) + +DECLARE_STRUCTURE_BEGIN(LOG_EXCEPTION, "Throw {0}, Handler {1}") + + DECLARE_FIELD(LOG_EXCEPTION, TYPE_UIntPtr, ThrowFrom) + DECLARE_FIELD(LOG_EXCEPTION, TYPE_UIntPtr, Handler) + + DECLARE_EXTENDED_ARRAY_FIELD(LOG_EXCEPTION, TYPE_string, ExceptionName) + +DECLARE_STRUCTURE_END(LOG_EXCEPTION) + +// +// GC profiler structures +// + +DECLARE_STRUCTURE_BEGIN(GC_FUNCTION, "") + + DECLARE_FIELD(GC_FUNCTION, TYPE_uint32, FnIdx) + DECLARE_FIELD(GC_FUNCTION, TYPE_UIntPtr, IP) + +DECLARE_STRUCTURE_END(GC_FUNCTION) + +DECLARE_STRUCTURE_BEGIN(GC_ALLOCATION, "") + + DECLARE_FIELD(GC_ALLOCATION, TYPE_uint32, TID) + DECLARE_FIELD(GC_ALLOCATION, TYPE_uint32, StkNo) + DECLARE_FIELD(GC_ALLOCATION, TYPE_UIntPtr, Object) + +DECLARE_STRUCTURE_END(GC_ALLOCATION) + +DECLARE_STRUCTURE_BEGIN(GC_INTERVAL, "") + + DECLARE_FIELD(GC_INTERVAL, TYPE_uint64, Delta) + +DECLARE_STRUCTURE_END(GC_INTERVAL) + +DECLARE_STRUCTURE_BEGIN(GC_TYPE, "") + + DECLARE_FIELD(GC_TYPE, TYPE_uint32, TypeId) + DECLARE_EXTENDED_ARRAY_FIELD(GC_TYPE, TYPE_string, Name) + +DECLARE_STRUCTURE_END(GC_TYPE) + +DECLARE_STRUCTURE_BEGIN(GC_STACK, "") + + DECLARE_FIELD(GC_STACK, TYPE_uint32, StkNo) + DECLARE_FIELD(GC_STACK, TYPE_uint32, TypeId) + DECLARE_FIELD(GC_STACK, TYPE_uint32, Size) + DECLARE_EXTENDED_ARRAY_FIELD(GC_STACK, TYPE_uint32, fncList) + +DECLARE_STRUCTURE_END(GC_STACK) + +DECLARE_STRUCTURE_BEGIN(GC_OBJECT, "") + + DECLARE_EXTENDED_ARRAY_FIELD(GC_OBJECT, TYPE_UIntPtr, ParameterList) + +DECLARE_STRUCTURE_END(GC_OBJECT) + +DECLARE_STRUCTURE_BEGIN(GC_GENERATIONS, "") + + DECLARE_EXTENDED_ARRAY_FIELD(GC_GENERATIONS, TYPE_int32, Genrations) + +DECLARE_STRUCTURE_END(GC_GENERATIONS) + +DECLARE_STRUCTURE_BEGIN(GC_ROOTS, "") + + DECLARE_EXTENDED_ARRAY_FIELD(GC_ROOTS, TYPE_UIntPtr, Roots) + +DECLARE_STRUCTURE_END(GC_ROOTS) + +DECLARE_STRUCTURE_BEGIN(LEGACY_LOG_ENTRY, "Log {0}:{1} {3*}") + + DECLARE_GENERIC_FIELD(LEGACY_LOG_ENTRY, TYPE_uint8, sizeof(uint8), LOG_SEVERITY, severity) + DECLARE_FIELD(LEGACY_LOG_ENTRY, TYPE_uint16, PID) + DECLARE_FIELD(LEGACY_LOG_ENTRY, TYPE_UIntPtr, IP) + DECLARE_EXTENDED_ARRAY_FIELD(LEGACY_LOG_ENTRY, TYPE_string, Msg) + DECLARE_FIELD(LEGACY_LOG_ENTRY, TYPE_UIntPtr, arg0) + DECLARE_FIELD(LEGACY_LOG_ENTRY, TYPE_UIntPtr, arg1) + DECLARE_FIELD(LEGACY_LOG_ENTRY, TYPE_UIntPtr, arg2) + DECLARE_FIELD(LEGACY_LOG_ENTRY, TYPE_UIntPtr, arg3) + DECLARE_FIELD(LEGACY_LOG_ENTRY, TYPE_UIntPtr, arg4) + DECLARE_FIELD(LEGACY_LOG_ENTRY, TYPE_UIntPtr, arg5) + + DECLARE_EXTENDED_ARRAY_FIELD(LEGACY_LOG_ENTRY, TYPE_string, StrArg0) + DECLARE_EXTENDED_ARRAY_FIELD(LEGACY_LOG_ENTRY, TYPE_string, StrArg1) + +DECLARE_STRUCTURE_END(LEGACY_LOG_ENTRY) + +DECLARE_STRUCTURE_BEGIN(MONITORING_ENTRY, "{0}:{1}:{2}:{3} - ({4},{5},{6},{7},{8}) {9}") + + DECLARE_FIELD(MONITORING_ENTRY, TYPE_uint16, PID) + DECLARE_FIELD(MONITORING_ENTRY, TYPE_uint16, Provider) + DECLARE_FIELD(MONITORING_ENTRY, TYPE_uint16, Type) + DECLARE_FIELD(MONITORING_ENTRY, TYPE_uint16, version) + DECLARE_FIELD(MONITORING_ENTRY, TYPE_uint32, arg0) + DECLARE_FIELD(MONITORING_ENTRY, TYPE_uint32, arg1) + DECLARE_FIELD(MONITORING_ENTRY, TYPE_uint32, arg2) + DECLARE_FIELD(MONITORING_ENTRY, TYPE_uint32, arg3) + DECLARE_FIELD(MONITORING_ENTRY, TYPE_uint32, arg4) + + DECLARE_EXTENDED_ARRAY_FIELD(MONITORING_ENTRY, TYPE_string, Text) + +DECLARE_STRUCTURE_END(MONITORING_ENTRY) + +#ifndef _LOGGING_TO_REPOSITORY + +DECLARE_STRUCTURE_BEGIN(EVENT_FIELD_DESCRIPTOR, "") + DECLARE_SPECIAL_FIELD(EVENT_FIELD_DESCRIPTOR, struct _EVENT_FIELD_DESCRIPTOR *, fieldsLink) + DECLARE_FIELD(EVENT_FIELD_DESCRIPTOR, TYPE_uint16, Offset) + DECLARE_FIELD(EVENT_FIELD_DESCRIPTOR, TYPE_uint16, Type) + DECLARE_EXTENDED_ARRAY_FIELD(EVENT_FIELD_DESCRIPTOR, TYPE_string, Name) +DECLARE_STRUCTURE_END(EVENT_FIELD_DESCRIPTOR) + +DECLARE_STRUCTURE_BEGIN(EVENT_GENERIC_TYPE_DESCRIPTOR, "") + DECLARE_SPECIAL_FIELD(EVENT_GENERIC_TYPE_DESCRIPTOR, struct _EVENT_FIELD_DESCRIPTOR *, fieldsLink) + DECLARE_FIELD(EVENT_GENERIC_TYPE_DESCRIPTOR, TYPE_uint16, Offset) + DECLARE_FIELD(EVENT_GENERIC_TYPE_DESCRIPTOR, TYPE_uint16, Type) + DECLARE_FIELD(EVENT_GENERIC_TYPE_DESCRIPTOR, TYPE_uint16, Size) + DECLARE_FIELD(EVENT_GENERIC_TYPE_DESCRIPTOR, TYPE_UIntPtr, GenericTypeHandle) + DECLARE_EXTENDED_ARRAY_FIELD(EVENT_GENERIC_TYPE_DESCRIPTOR, TYPE_string, Name) +DECLARE_STRUCTURE_END(EVENT_GENERIC_TYPE_DESCRIPTOR) + +DECLARE_STRUCTURE_BEGIN(EVENT_DESCRIPTOR, "") + DECLARE_SPECIAL_FIELD(EVENT_DESCRIPTOR, struct _EVENT_FIELD_DESCRIPTOR *, fieldsLink) + DECLARE_FIELD(EVENT_DESCRIPTOR, TYPE_uint16, Size) + DECLARE_EXTENDED_ARRAY_FIELD(EVENT_DESCRIPTOR, TYPE_string, Name) + DECLARE_EXTENDED_ARRAY_FIELD(EVENT_DESCRIPTOR, TYPE_string, Description) +DECLARE_STRUCTURE_END(EVENT_DESCRIPTOR) + +DECLARE_STRUCTURE_BEGIN(EVENT_VALUE_DESCRIPTOR, "") + DECLARE_SPECIAL_FIELD(EVENT_VALUE_DESCRIPTOR, struct _EVENT_VALUE_DESCRIPTOR *, fieldsLink) + DECLARE_FIELD(EVENT_VALUE_DESCRIPTOR, TYPE_uint8, FlagLetter) + DECLARE_FIELD(EVENT_VALUE_DESCRIPTOR, TYPE_uint64, Value) + DECLARE_EXTENDED_ARRAY_FIELD(EVENT_VALUE_DESCRIPTOR, TYPE_string, Name) +DECLARE_STRUCTURE_END(EVENT_VALUE_DESCRIPTOR) + +DECLARE_STRUCTURE_BEGIN(ENUM_DESCRIPTOR, "") + DECLARE_SPECIAL_FIELD(ENUM_DESCRIPTOR, struct _EVENT_VALUE_DESCRIPTOR *, fieldsLink) + DECLARE_FIELD(ENUM_DESCRIPTOR, TYPE_uint16, Type) + DECLARE_FIELD(ENUM_DESCRIPTOR, TYPE_uint64, FlagsMask) + DECLARE_EXTENDED_ARRAY_FIELD(ENUM_DESCRIPTOR, TYPE_string, Name) + DECLARE_EXTENDED_ARRAY_FIELD(ENUM_DESCRIPTOR, TYPE_string, Description) +DECLARE_STRUCTURE_END(ENUM_DESCRIPTOR) + +DECLARE_STRUCTURE_BEGIN(MEMORY_ZONE, "") + DECLARE_SPECIAL_FIELD(MEMORY_ZONE, struct _MEMORY_ZONE *, Link) + DECLARE_SPECIAL_FIELD(MEMORY_ZONE, struct _MEMORY_ZONE *, BkLink) + DECLARE_SPECIAL_FIELD(MEMORY_ZONE, volatile TYPE_uint32, ZoneSize) + DECLARE_SPECIAL_FIELD(MEMORY_ZONE, ZONE_ALLOCATION_POINTER, Allocation) + DECLARE_SPECIAL_FIELD(MEMORY_ZONE, ZONE_READY_LIST, ReadyList) + DECLARE_FIELD(MEMORY_ZONE, TYPE_UIntPtr, StorageHandle) + DECLARE_SPECIAL_FIELD(MEMORY_ZONE, TYPE_uint32, Generation) + DECLARE_SPECIAL_FIELD(MEMORY_ZONE, TYPE_uint16, LastSyncPoint) + DECLARE_SPECIAL_FIELD(MEMORY_ZONE, TYPE_uint16, Reserved) +DECLARE_STRUCTURE_END(MEMORY_ZONE) + +DECLARE_STRUCTURE_BEGIN(MEMORY_STORAGE, "") + DECLARE_SPECIAL_FIELD(MEMORY_STORAGE, struct _MEMORY_STORAGE *, Link) + DECLARE_FIELD(MEMORY_STORAGE, TYPE_uint32, StorageSize) + DECLARE_FIELD(MEMORY_STORAGE, TYPE_uint32, ZoneCount) + DECLARE_FIELD(MEMORY_STORAGE, TYPE_uint16, DefaultZoneSize) + DECLARE_FIELD(MEMORY_STORAGE, TYPE_uint32, Flags) + DECLARE_SPECIAL_FIELD(MEMORY_STORAGE, PMEMORY_ZONE, MemoryZoneLink) + DECLARE_SPECIAL_FIELD(MEMORY_STORAGE, PMEMORY_ZONE, BkLink) + DECLARE_SPECIAL_FIELD(MEMORY_STORAGE, PMEMORY_ZONE, ZoneCursor) + DECLARE_SPECIAL_FIELD(MEMORY_ZONE, TYPE_uint32, Generation) +DECLARE_STRUCTURE_END(MEMORY_STORAGE) + +DECLARE_STRUCTURE_BEGIN(SOURCE_DESCRIPTOR, "") + DECLARE_FIELD(SOURCE_DESCRIPTOR, TYPE_UIntPtr, Link) + DECLARE_FIELD(SOURCE_DESCRIPTOR, TYPE_PCHAR, Name) + DECLARE_FIELD(SOURCE_DESCRIPTOR, TYPE_UIntPtr, StorageHandle) + DECLARE_FIELD(SOURCE_DESCRIPTOR, TYPE_UIntPtr, EventTypeHandle) + DECLARE_FIELD(SOURCE_DESCRIPTOR, TYPE_UIntPtr, DebuggerBufferAddress) + DECLARE_FIELD(SOURCE_DESCRIPTOR, TYPE_uint32, ControlFlags) + DECLARE_FIELD(SOURCE_DESCRIPTOR, TYPE_uint16, Count) + DECLARE_FIELD(SOURCE_DESCRIPTOR, TYPE_uint16, EntrySize) +DECLARE_STRUCTURE_END(SOURCE_DESCRIPTOR) + +DECLARE_STRUCTURE_BEGIN(EXTERNAL_CONTROLLER_DESCRIPTOR, "") + DECLARE_SPECIAL_FIELD(EXTERNAL_CONTROLLER_DESCRIPTOR, struct _EXTERNAL_CONTROLLER_DESCRIPTOR *, Link) + DECLARE_FIELD(EXTERNAL_CONTROLLER_DESCRIPTOR, TYPE_UIntPtr, ControllerHandle) + DECLARE_FIELD(EXTERNAL_CONTROLLER_DESCRIPTOR, TYPE_UIntPtr, ContextHandle) +DECLARE_STRUCTURE_END(EXTERNAL_CONTROLLER_DESCRIPTOR) + +DECLARE_STRUCTURE_BEGIN(QUERY_VIEW, "") + DECLARE_SPECIAL_FIELD(QUERY_VIEW, struct _QUERY_VIEW *, Link) + DECLARE_SPECIAL_FIELD(QUERY_VIEW, PMEMORY_STORAGE, Storage) + DECLARE_SPECIAL_FIELD(QUERY_VIEW, PMEMORY_ZONE, StartZone) + DECLARE_SPECIAL_FIELD(QUERY_VIEW, PMEMORY_ZONE, CurrentZone) + DECLARE_SPECIAL_FIELD(QUERY_VIEW, PMEMORY_HEADER, CurrentEntry) + DECLARE_FIELD(QUERY_VIEW, TYPE_uint32, QueryGeneration) + DECLARE_FIELD(QUERY_VIEW, TYPE_uint32, ZoneGeneration) + DECLARE_FIELD(QUERY_VIEW, TYPE_uint32, CurrentEntryIndex) + DECLARE_FIELD(QUERY_VIEW, TYPE_BOOL, Forward) + DECLARE_FIELD(QUERY_VIEW, TYPE_BOOL, QueryReset) + DECLARE_FIELD(QUERY_VIEW, TYPE_BOOL, EndOfBuffer) +DECLARE_STRUCTURE_END(QUERY_VIEW) + +DECLARE_STRUCTURE_BEGIN(SOURCE_CONTROLLER, "") + DECLARE_SPECIAL_FIELD(SOURCE_CONTROLLER, PMEMORY_STORAGE, SourceRepository) + DECLARE_SPECIAL_FIELD(SOURCE_CONTROLLER, PMEMORY_STORAGE, StorageList) + DECLARE_SPECIAL_FIELD(SOURCE_CONTROLLER, PSOURCE_DESCRIPTOR, SourceDescriptors) + DECLARE_SPECIAL_FIELD(SOURCE_CONTROLLER, PEXTERNAL_CONTROLLER_DESCRIPTOR, ExternalControllers) + DECLARE_SPECIAL_FIELD(SOURCE_CONTROLLER, PEXTERNAL_CONTROLLER_DESCRIPTOR, FreeControllerList) + DECLARE_SPECIAL_FIELD(SOURCE_CONTROLLER, PQUERY_VIEW, QueryViews) + DECLARE_SPECIAL_FIELD(SOURCE_CONTROLLER, PQUERY_VIEW, FreeQueryViews) +DECLARE_STRUCTURE_END(SOURCE_CONTROLLER) + +#endif // #_LOGGING_TO_REPOSITORY + +#ifdef _VALIDATE_INVARIANTS + + C_ASSERT(offsetof(EVENT_FIELD_DESCRIPTOR, fieldsLink) == offsetof(EVENT_GENERIC_TYPE_DESCRIPTOR,fieldsLink)); + C_ASSERT(offsetof(EVENT_FIELD_DESCRIPTOR, Offset) == offsetof(EVENT_GENERIC_TYPE_DESCRIPTOR,Offset)); + C_ASSERT(offsetof(EVENT_FIELD_DESCRIPTOR, Type) == offsetof(EVENT_GENERIC_TYPE_DESCRIPTOR,Type)); + + C_ASSERT(offsetof(EVENT_DESCRIPTOR, fieldsLink) == offsetof(ENUM_DESCRIPTOR,fieldsLink)); + +#endif + +// +// Reset all definitions +// + +#undef DECLARE_EXTENDED_ARRAY_FIELD +#undef DECLARE_SPECIAL_FIELD +#undef DECLARE_STRUCTURE_BEGIN +#undef DECLARE_STRUCTURE_END +#undef DECLARE_FIELD +#undef DECLARE_VALUE +#undef DECLARE_GENERIC_FIELD +#undef DECLARE_ENUM_BEGIN +#undef DECLARE_ENUM_END + diff --git a/base/Kernel/Native/Thread.cpp b/base/Kernel/Native/Thread.cpp deleted file mode 100644 index 8559eba..0000000 --- a/base/Kernel/Native/Thread.cpp +++ /dev/null @@ -1,80 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Thread.cpp -// -// Note: -// - -#include "hal.h" - -////////////////////////////////////////////////////////////// Thread Context. -// -void -Struct_Microsoft_Singularity_X86_ThreadContext:: -m_UpdateAfterGC(Struct_Microsoft_Singularity_X86_ThreadContext * self, - Class_System_Threading_Thread *thread) -{ - self->_thread = thread; -} - -Class_System_Threading_Thread * -Struct_Microsoft_Singularity_X86_ThreadContext:: -m_GetThread(Struct_Microsoft_Singularity_X86_ThreadContext * self) -{ - return self->_thread; -} - -#if SINGULARITY_KERNEL -void -Struct_Microsoft_Singularity_X86_ThreadContext:: -m_Initialize(Struct_Microsoft_Singularity_X86_ThreadContext * self, - int threadIndex, - UIntPtr stack, - uint32 cr3) -{ - uintptr *esp = (uintptr*)stack; - *--esp = 0; - *--esp = 0; - self->ebp = (uintptr)esp; - self->esp = (uintptr)esp; - self->eip = (uintptr)Class_System_Threading_Thread::g_ThreadStub; - self->cs0 = (uintptr)(offsetof(Struct_Microsoft_Singularity_CpuInfo,GdtPC) - - offsetof(Struct_Microsoft_Singularity_CpuInfo,GdtNull)); - self->ecx = threadIndex; - self->cr3 = cr3; - self->efl = Struct_Microsoft_Singularity_X86_EFlags_IF | Struct_Microsoft_Singularity_X86_EFlags_IOPL; // TODO: get rid of IOPL - self->mmx.fcw = 0x037f; - self->mmx.ftw = 0; - // self->mmx.mxcsr = 0x1f80; -} - -void -Struct_Microsoft_Singularity_X86_ThreadContext:: -m_InitializeIdle(Struct_Microsoft_Singularity_X86_ThreadContext * self, - int threadIndex, - UIntPtr stack, - uint32 cr3) -{ - uintptr *esp = (uintptr*)stack; - *--esp = 0; - *--esp = 0; - self->ebp = (uintptr)esp; - self->esp = (uintptr)esp; - self->eip = (uintptr)Class_System_Threading_Thread::g_ThreadIdleStub; - self->cs0 = (uintptr)(offsetof(Struct_Microsoft_Singularity_CpuInfo,GdtPC) - - offsetof(Struct_Microsoft_Singularity_CpuInfo,GdtNull)); - self->ecx = threadIndex; - self->cr3 = cr3; - self->efl = Struct_Microsoft_Singularity_X86_EFlags_IF | Struct_Microsoft_Singularity_X86_EFlags_IOPL; // TODO: get rid of IOPL - self->mmx.fcw = 0x037f; - self->mmx.ftw = 0; - // self->mmx.mxcsr = 0x1f80; -} -#endif // SINGULARITY_KERNEL - -// -///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/Tracing.cpp b/base/Kernel/Native/Tracing.cpp index b4c4a5a..b2ddc62 100644 --- a/base/Kernel/Native/Tracing.cpp +++ b/base/Kernel/Native/Tracing.cpp @@ -6,239 +6,133 @@ // // File: Tracing.cpp // -// Note: +// Note: Kernel & Process // #include "hal.h" +#include "eventing.h" -#if SINGULARITY_KERNEL -static int64 tscOffsets[8]; -#endif +UIntPtr TracingStorageHandle = 0; +const UINT32 Tracing_ControlFlag_Active = 0x00010000; +PSOURCE_DESCRIPTOR TracingSource = NULL; +UIntPtr TracingTypeHandle = 0; -//////////////////////////////////////////////////////////////// Image Loader. -// +uint16 +inline +GetCurrentProcessId() +{ + return Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext()->processId; +} void Class_Microsoft_Singularity_Tracing:: g_Initialize() { #if SINGULARITY_KERNEL - c_txtBegin = (uint8 *)Struct_Microsoft_Singularity_BootInfo_KERNEL_LOGTXT_BEGIN; - c_txtLimit = (uint8 *)Struct_Microsoft_Singularity_BootInfo_KERNEL_LOGTXT_LIMIT; - c_txtHead = c_txtBegin; - c_ptxtHead = &c_txtHead; - int cnt = (Struct_Microsoft_Singularity_BootInfo_KERNEL_LOGREC_LIMIT - - Struct_Microsoft_Singularity_BootInfo_KERNEL_LOGREC_BEGIN) - / sizeof(Struct_Microsoft_Singularity_Tracing_LogEntry); + Class_Microsoft_Singularity_Hal_Platform *platform = Class_Microsoft_Singularity_Hal_Platform::c_thePlatform; + + InitializeRegistrationSystem((void *)platform->LogTextBuffer, + platform->LogTextSize); + + TracingStorageHandle = Class_Microsoft_Singularity_Eventing_MemoryStorage:: + g_MemoryStorageCreateImpl(MEMORY_STORAGE_FLAGS_RECYCLE_MEMORY, + (uint8 *)platform->LogRecordBuffer, + (uint32)platform->LogRecordSize, + 0); + if (TracingStorageHandle) { + + UIntPtr sourceHandle = RegisterNativeSource("LegacyTracing", + TracingStorageHandle, + Tracing_ControlFlag_Active); + TracingSource = GetSourceFromHandle(sourceHandle); + } + - c_logBegin = (Struct_Microsoft_Singularity_Tracing_LogEntry *) - Struct_Microsoft_Singularity_BootInfo_KERNEL_LOGREC_BEGIN; - c_logLimit = c_logBegin + cnt; - c_logHead = c_logBegin; - c_plogHead = &c_logHead; - c_tscOffsets = tscOffsets; #elif SINGULARITY_PROCESS - Struct_Microsoft_Singularity_Tracing_LogEntry * logBegin; - Struct_Microsoft_Singularity_Tracing_LogEntry * logLimit; - Struct_Microsoft_Singularity_Tracing_LogEntry ** plogHead; - uint8 * txtBegin; - uint8 * txtLimit; - uint8 ** ptxtHead; Struct_Microsoft_Singularity_V1_Services_ProcessService:: - g_GetTracingHeaders((Struct_Microsoft_Singularity_V1_Services_LogEntry **)&logBegin, - (Struct_Microsoft_Singularity_V1_Services_LogEntry **)&logLimit, - (Struct_Microsoft_Singularity_V1_Services_LogEntry ***)&plogHead, - &txtBegin, - &txtLimit, - &ptxtHead); + g_GetSharedSourceHandles(Class_Microsoft_Singularity_Eventing_Controller_TracingInfo, + &TracingStorageHandle, + (UIntPtr *)&TracingSource, + &TracingTypeHandle); - c_logBegin = logBegin; - c_logLimit = logLimit; - c_plogHead = plogHead; - - c_txtBegin = txtBegin; - c_txtLimit = txtLimit; - c_ptxtHead = ptxtHead; #else #error "File should be compiled with SINGULARITY_KERNEL or SINGULARITY_PROCESS" #endif } -void -Class_Microsoft_Singularity_Tracing:: -g_Finalize() +bool __inline IsTracingEnabled() { + return ((TracingSource != NULL) && + (TracingSource->ControlFlags & Tracing_ControlFlag_Active)); } -#if SINGULARITY_KERNEL -void -Class_Microsoft_Singularity_Tracing:: -g_SetTscOffset(int64 theTscOffset) + + +bool GetTracingHandles(UIntPtr * storageHandle, + UIntPtr * sourceHandle, + UIntPtr * eventTypeHandle) { - Struct_Microsoft_Singularity_X86_ProcessorContext *processorContext = - Class_Microsoft_Singularity_Processor::g_GetCurrentProcessorContext(); - int cpuId = processorContext->cpuId; - if (cpuId < 0 || cpuId >= sizeof(tscOffsets)/sizeof(tscOffsets[0])) { - __asm int 3 - } - tscOffsets[cpuId] = theTscOffset; + *storageHandle = TracingStorageHandle; + *sourceHandle = (UIntPtr)TracingSource; + *eventTypeHandle = TracingTypeHandle; + return true; } -#endif // SINGULARITY_KERNEL uint8 * CompareExchange(uint8 **dest, uint8 *exch, uint8 *comp) { - uint8 *val; - __asm { - mov ecx, dest; - mov edx, exch; - mov eax, comp; - lock cmpxchg [ecx], edx; - mov val, eax; - } - return val; + return (uint8 *) InterlockedCompareExchangePointer((PVOID *) dest, + (PVOID) exch, + (PVOID) comp); } -Struct_Microsoft_Singularity_Tracing_LogEntry * -CompareExchange(Struct_Microsoft_Singularity_Tracing_LogEntry **dest, - Struct_Microsoft_Singularity_Tracing_LogEntry *exch, - Struct_Microsoft_Singularity_Tracing_LogEntry *comp) -{ - Struct_Microsoft_Singularity_Tracing_LogEntry * val; - __asm { - mov ecx, dest; - mov edx, exch; - mov eax, comp; - lock cmpxchg [ecx], edx; - mov val, eax; - } - return val; -} - -Struct_Microsoft_Singularity_Tracing_LogEntry * +UIntPtr Class_Microsoft_Singularity_Tracing:: -g_CreateLog(uint8 severity, UIntPtr eip, int chars, uint8 **buf) +g_GetSystemTracingStorageHandle() { - Struct_Microsoft_Singularity_Tracing_LogEntry * log; - Struct_Microsoft_Singularity_Tracing_LogEntry * logWas; - Struct_Microsoft_Singularity_Tracing_LogEntry * logBec; - - do { - log = *c_plogHead; - logWas = log; - logBec = logWas + 1; - if (logBec >= c_logLimit) { - logBec = c_logBegin; - } - } while (logWas != CompareExchange(c_plogHead, logBec, logWas)); - - uint8 *str; - uint8 *txtWas; - uint8 *txtBec; - - do { - str = *c_ptxtHead; - txtWas = str; - txtBec = str + chars; - - if (txtBec > c_txtLimit) { - str = c_txtBegin; - txtBec = str + chars; - } - } while (txtWas != CompareExchange(c_ptxtHead, txtBec, txtWas)); - -#if SINGULARITY_KERNEL || !PAGING - bool enabled = Class_Microsoft_Singularity_Processor::g_DisableInterrupts(); -#endif - - // We need acquiring a log entry to be - // wait-free since this routine can be called from the - // kernel or a userland application. We cannot simply - // disable interrupts as there may be multiple hardware - // threads and it might result in a userland thread blocking - // a kernel thread. Callers may be context switched acquiring - // the log entry. This means there is finite chance that - // the log entries do not have monotonically increasing tsc - // values. This is sometimes visible in the !log output. - UINT64 tsc = RDTSC(); - - Struct_Microsoft_Singularity_X86_ThreadContext *threadContext = - Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext(); - Struct_Microsoft_Singularity_X86_ProcessorContext *processorContext = - Class_Microsoft_Singularity_Processor::g_GetCurrentProcessorContext(); - - log->processId = threadContext->processId; - -#if SINGULARITY_KERNEL - log->threadId = threadContext->threadIndex; -#else - log->threadId = threadContext->kernelThreadIndex; -#endif - - log->eip = (uintptr)eip; //(uintptr)g_GetCallerEip(3); - log->text = str; - log->severity = (uint8)severity; - log->strings = 0; - log->tag = 0; - log->cycleCount = tsc; - log->cpuId = processorContext->cpuId; - - *str++ = (uint8)log->cycleCount; - *str = '\0'; - -#if SINGULARITY_KERNEL || !PAGING - Class_Microsoft_Singularity_Processor::g_RestoreInterrupts(enabled); -#endif - - *buf = str; - - return log; + return TracingStorageHandle; } -uint8 * Class_Microsoft_Singularity_Tracing:: -g_AddText(uint8 *dst, Class_System_String *arg) -{ - if (arg != NULL) { - bartok_char *src = &arg->m_firstChar; - bartok_char *end = src + arg->m_stringLength; - - while (src < end) { - *dst++ = (uint8)*src++; - } - } - *dst++ = '\0'; - - return dst; -} void Class_Microsoft_Singularity_Tracing:: g_Log(uint8 severity) { - UIntPtr _eip; - __asm { - mov eax, [ebp+4]; - mov _eip, eax; - } - Struct_Microsoft_Singularity_Tracing_LogEntry * log; - uint8 *text; - log = g_CreateLog(severity, _eip, 2, &text); + if (!IsTracingEnabled()) return; + + LEGACY_LOG_ENTRY entry = {severity, GetCurrentProcessId(), (UIntPtr)_ReturnAddress(), + 0,0,0,0,0,0}; + InternalLogFixedRecord(TracingStorageHandle, + TracingSource->ControlFlags, + TracingTypeHandle, + &entry, + sizeof(entry)); } void Class_Microsoft_Singularity_Tracing:: g_Log(uint8 severity, Class_System_String *msg) { - UIntPtr _eip; - __asm { - mov eax, [ebp+4]; - mov _eip, eax; - } - Struct_Microsoft_Singularity_Tracing_LogEntry * log; - uint8 *text; - int chars = 1 + msg->m_stringLength + 1; - log = g_CreateLog(severity, _eip, chars, &text); - text = g_AddText(text, msg); + if (!IsTracingEnabled()) return; + + LEGACY_LOG_ENTRY entry = {severity, GetCurrentProcessId(), (UIntPtr)_ReturnAddress(), + 1,0,0,0,0,0,0,0,0}; + + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {msg->m_stringLength, + sizeof(char), + Class_Microsoft_Singularity_Eventing_DataType___string, + &msg->m_firstChar}}; + + InternalLogVariableRecord(true, + TracingStorageHandle, + TracingSource->ControlFlags, + TracingTypeHandle, + &entry, + sizeof(entry), + sizeof(array)/sizeof(array[0]), + array); + } void Class_Microsoft_Singularity_Tracing:: @@ -246,17 +140,24 @@ g_Log(uint8 severity, Class_System_String *msg, UIntPtr arg0) { - UIntPtr _eip; - __asm { - mov eax, [ebp+4]; - mov _eip, eax; - } - Struct_Microsoft_Singularity_Tracing_LogEntry * log; - uint8 *text; - int chars = 1 + msg->m_stringLength + 1; - log = g_CreateLog(severity, _eip, chars, &text); - log->arg0 = (uintptr)arg0; - text = g_AddText(text, msg); + if (!IsTracingEnabled()) return; + + LEGACY_LOG_ENTRY entry = {severity, GetCurrentProcessId(), (UIntPtr)_ReturnAddress(), + 1, arg0,0,0,0,0,0,0,0}; + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {msg->m_stringLength, + sizeof(char), + Class_Microsoft_Singularity_Eventing_DataType___string, + &msg->m_firstChar}}; + + InternalLogVariableRecord(true, + TracingStorageHandle, + TracingSource->ControlFlags, + TracingTypeHandle, + &entry, + sizeof(entry), + sizeof(array)/sizeof(array[0]), + array); } void Class_Microsoft_Singularity_Tracing:: @@ -264,18 +165,24 @@ g_Log(uint8 severity, Class_System_String *msg, UIntPtr arg0, UIntPtr arg1) { - UIntPtr _eip; - __asm { - mov eax, [ebp+4]; - mov _eip, eax; - } - Struct_Microsoft_Singularity_Tracing_LogEntry * log; - uint8 *text; - int chars = 1 + msg->m_stringLength + 1; - log = g_CreateLog(severity, _eip, chars, &text); - log->arg0 = (uintptr)arg0; - log->arg1 = (uintptr)arg1; - text = g_AddText(text, msg); + if (!IsTracingEnabled()) return; + + LEGACY_LOG_ENTRY entry = {severity, GetCurrentProcessId(), (UIntPtr)_ReturnAddress(), + 1, arg0,arg1,0,0,0,0, 0,0}; + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {msg->m_stringLength, + sizeof(char), + Class_Microsoft_Singularity_Eventing_DataType___string, + &msg->m_firstChar}}; + + InternalLogVariableRecord(true, + TracingStorageHandle, + TracingSource->ControlFlags, + TracingTypeHandle, + &entry, + sizeof(entry), + sizeof(array)/sizeof(array[0]), + array); } void Class_Microsoft_Singularity_Tracing:: @@ -283,19 +190,24 @@ g_Log(uint8 severity, Class_System_String *msg, UIntPtr arg0, UIntPtr arg1, UIntPtr arg2) { - UIntPtr _eip; - __asm { - mov eax, [ebp+4]; - mov _eip, eax; - } - Struct_Microsoft_Singularity_Tracing_LogEntry * log; - uint8 *text; - int chars = 1 + msg->m_stringLength + 1; - log = g_CreateLog(severity, _eip, chars, &text); - log->arg0 = (uintptr)arg0; - log->arg1 = (uintptr)arg1; - log->arg2 = (uintptr)arg2; - text = g_AddText(text, msg); + if (!IsTracingEnabled()) return; + + LEGACY_LOG_ENTRY entry = {severity,GetCurrentProcessId(), (UIntPtr)_ReturnAddress(), + 1,arg0,arg1,arg2,0,0,0,0,0}; + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {msg->m_stringLength, + sizeof(char), + Class_Microsoft_Singularity_Eventing_DataType___string, + &msg->m_firstChar}}; + + InternalLogVariableRecord(true, + TracingStorageHandle, + TracingSource->ControlFlags, + TracingTypeHandle, + &entry, + sizeof(entry), + sizeof(array)/sizeof(array[0]), + array); } void Class_Microsoft_Singularity_Tracing:: @@ -304,20 +216,24 @@ g_Log(uint8 severity, UIntPtr arg0, UIntPtr arg1, UIntPtr arg2, UIntPtr arg3) { - UIntPtr _eip; - __asm { - mov eax, [ebp+4]; - mov _eip, eax; - } - Struct_Microsoft_Singularity_Tracing_LogEntry * log; - uint8 *text; - int chars = 1 + msg->m_stringLength + 1; - log = g_CreateLog(severity, _eip, chars, &text); - log->arg0 = (uintptr)arg0; - log->arg1 = (uintptr)arg1; - log->arg2 = (uintptr)arg2; - log->arg3 = (uintptr)arg3; - text = g_AddText(text, msg); + if (!IsTracingEnabled()) return; + + LEGACY_LOG_ENTRY entry = {severity,GetCurrentProcessId(), (UIntPtr)_ReturnAddress(), + 1,arg0,arg1,arg2,arg3,0,0,0,0}; + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {msg->m_stringLength, + sizeof(char), + Class_Microsoft_Singularity_Eventing_DataType___string, + &msg->m_firstChar}}; + + InternalLogVariableRecord(true, + TracingStorageHandle, + TracingSource->ControlFlags, + TracingTypeHandle, + &entry, + sizeof(entry), + sizeof(array)/sizeof(array[0]), + array); } void Class_Microsoft_Singularity_Tracing:: @@ -326,21 +242,24 @@ g_Log(uint8 severity, UIntPtr arg0, UIntPtr arg1, UIntPtr arg2, UIntPtr arg3, UIntPtr arg4) { - UIntPtr _eip; - __asm { - mov eax, [ebp+4]; - mov _eip, eax; - } - Struct_Microsoft_Singularity_Tracing_LogEntry * log; - uint8 *text; - int chars = 1 + msg->m_stringLength + 1; - log = g_CreateLog(severity, _eip, chars, &text); - log->arg0 = (uintptr)arg0; - log->arg1 = (uintptr)arg1; - log->arg2 = (uintptr)arg2; - log->arg3 = (uintptr)arg3; - log->arg4 = (uintptr)arg4; - text = g_AddText(text, msg); + if (!IsTracingEnabled()) return; + + LEGACY_LOG_ENTRY entry = {severity,GetCurrentProcessId(), (UIntPtr)_ReturnAddress(), + 1,arg0,arg1,arg2,arg3,arg4,0,0,0}; + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {msg->m_stringLength, + sizeof(char), + Class_Microsoft_Singularity_Eventing_DataType___string, + &msg->m_firstChar}}; + + InternalLogVariableRecord(true, + TracingStorageHandle, + TracingSource->ControlFlags, + TracingTypeHandle, + &entry, + sizeof(entry), + sizeof(array)/sizeof(array[0]), + array); } void Class_Microsoft_Singularity_Tracing:: @@ -349,22 +268,24 @@ g_Log(uint8 severity, UIntPtr arg0, UIntPtr arg1, UIntPtr arg2, UIntPtr arg3, UIntPtr arg4, UIntPtr arg5) { - UIntPtr _eip; - __asm { - mov eax, [ebp+4]; - mov _eip, eax; - } - Struct_Microsoft_Singularity_Tracing_LogEntry * log; - uint8 *text; - int chars = 1 + msg->m_stringLength + 1; - log = g_CreateLog(severity, _eip, chars, &text); - log->arg0 = (uintptr)arg0; - log->arg1 = (uintptr)arg1; - log->arg2 = (uintptr)arg2; - log->arg3 = (uintptr)arg3; - log->arg4 = (uintptr)arg4; - log->arg5 = (uintptr)arg5; - text = g_AddText(text, msg); + if (!IsTracingEnabled()) return; + + LEGACY_LOG_ENTRY entry = {severity,GetCurrentProcessId(), (UIntPtr)_ReturnAddress(), + 1,arg0,arg1,arg2,arg3,arg4,arg5,0,0}; + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {msg->m_stringLength, + sizeof(char), + Class_Microsoft_Singularity_Eventing_DataType___string, + &msg->m_firstChar}}; + + InternalLogVariableRecord(true, + TracingStorageHandle, + TracingSource->ControlFlags, + TracingTypeHandle, + &entry, + sizeof(entry), + sizeof(array)/sizeof(array[0]), + array); } void Class_Microsoft_Singularity_Tracing:: @@ -372,19 +293,30 @@ g_Log(uint8 severity, Class_System_String *msg, Class_System_String *arg0) { - UIntPtr _eip; - __asm { - mov eax, [ebp+4]; - mov _eip, eax; - } - Struct_Microsoft_Singularity_Tracing_LogEntry * log; - uint8 *text; - int chars = 1 + msg->m_stringLength + 1 + - (arg0 != NULL ? arg0->m_stringLength : 0) + 1; - log = g_CreateLog(severity, _eip, chars, &text); - log->strings = Struct_Microsoft_Singularity_Tracing_Strings_String0; - text = g_AddText(text, msg); - text = g_AddText(text, arg0); + if (!IsTracingEnabled()) return; + + LEGACY_LOG_ENTRY entry = {severity,GetCurrentProcessId(), (UIntPtr)_ReturnAddress(), + 1,0,0,0,0,0,0,2,0}; + + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {msg->m_stringLength, + sizeof(char), + Class_Microsoft_Singularity_Eventing_DataType___string, + &msg->m_firstChar}, + {arg0->m_stringLength, + sizeof(char), + Class_Microsoft_Singularity_Eventing_DataType___string, + &arg0->m_firstChar}}; + + InternalLogVariableRecord(true, + TracingStorageHandle, + TracingSource->ControlFlags, + TracingTypeHandle, + &entry, + sizeof(entry), + sizeof(array)/sizeof(array[0]), + array); + } void Class_Microsoft_Singularity_Tracing:: @@ -393,20 +325,30 @@ g_Log(uint8 severity, Class_System_String *arg0, UIntPtr arg1) { - UIntPtr _eip; - __asm { - mov eax, [ebp+4]; - mov _eip, eax; - } - Struct_Microsoft_Singularity_Tracing_LogEntry * log; - uint8 *text; - int chars = 1 + msg->m_stringLength + 1 + - (arg0 != NULL ? arg0->m_stringLength : 0) + 1; - log = g_CreateLog(severity, _eip, chars, &text); - log->strings = Struct_Microsoft_Singularity_Tracing_Strings_String0; - log->arg1 = (uintptr)arg1; - text = g_AddText(text, msg); - text = g_AddText(text, arg0); + if (!IsTracingEnabled()) return; + + LEGACY_LOG_ENTRY entry = {severity,GetCurrentProcessId(), (UIntPtr)_ReturnAddress(), + 1,arg1,0,0,0,0,0,2,0}; + + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {msg->m_stringLength, + sizeof(char), + Class_Microsoft_Singularity_Eventing_DataType___string, + &msg->m_firstChar}, + {arg0->m_stringLength, + sizeof(char), + Class_Microsoft_Singularity_Eventing_DataType___string, + &arg0->m_firstChar}}; + + InternalLogVariableRecord(true, + TracingStorageHandle, + TracingSource->ControlFlags, + TracingTypeHandle, + &entry, + sizeof(entry), + sizeof(array)/sizeof(array[0]), + array); + } void Class_Microsoft_Singularity_Tracing:: @@ -415,45 +357,30 @@ g_Log(uint8 severity, Class_System_String *arg0, UIntPtr arg1, UIntPtr arg2) { - UIntPtr _eip; - __asm { - mov eax, [ebp+4]; - mov _eip, eax; - } - Struct_Microsoft_Singularity_Tracing_LogEntry * log; - uint8 *text; - int chars = 1 + msg->m_stringLength + 1 + - (arg0 != NULL ? arg0->m_stringLength : 0) + 1; - log = g_CreateLog(severity, _eip, chars, &text); - log->strings = Struct_Microsoft_Singularity_Tracing_Strings_String0; - log->arg1 = (uintptr)arg1; - log->arg2 = (uintptr)arg2; - text = g_AddText(text, msg); - text = g_AddText(text, arg0); -} + if (!IsTracingEnabled()) return; + + LEGACY_LOG_ENTRY entry = {severity,GetCurrentProcessId(), (UIntPtr)_ReturnAddress(), + 1,arg1,arg2,0,0,0,0,2,0}; + + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {msg->m_stringLength, + sizeof(char), + Class_Microsoft_Singularity_Eventing_DataType___string, + &msg->m_firstChar}, + {arg0->m_stringLength, + sizeof(char), + Class_Microsoft_Singularity_Eventing_DataType___string, + &arg0->m_firstChar}}; + + InternalLogVariableRecord(true, + TracingStorageHandle, + TracingSource->ControlFlags, + TracingTypeHandle, + &entry, + sizeof(entry), + sizeof(array)/sizeof(array[0]), + array); -void Class_Microsoft_Singularity_Tracing:: -g_Log(uint8 severity, - Class_System_String *msg, - Class_System_String *arg0, - UIntPtr arg1, UIntPtr arg2, UIntPtr arg3) -{ - UIntPtr _eip; - __asm { - mov eax, [ebp+4]; - mov _eip, eax; - } - Struct_Microsoft_Singularity_Tracing_LogEntry * log; - uint8 *text; - int chars = 1 + msg->m_stringLength + 1 + - (arg0 != NULL ? arg0->m_stringLength : 0) + 1; - log = g_CreateLog(severity, _eip, chars, &text); - log->strings = Struct_Microsoft_Singularity_Tracing_Strings_String0; - log->arg1 = (uintptr)arg1; - log->arg2 = (uintptr)arg2; - log->arg3 = (uintptr)arg3; - text = g_AddText(text, msg); - text = g_AddText(text, arg0); } void Class_Microsoft_Singularity_Tracing:: @@ -462,107 +389,58 @@ g_Log(uint8 severity, Class_System_String *arg0, Class_System_String *arg1) { - UIntPtr _eip; - __asm { - mov eax, [ebp+4]; - mov _eip, eax; - } - Struct_Microsoft_Singularity_Tracing_LogEntry * log; - uint8 *text; + if (!IsTracingEnabled()) return; - int chars = 1 + msg->m_stringLength + 1 - + (arg0 != NULL ? arg0->m_stringLength : 0) + 1 - + (arg1 != NULL ? arg0->m_stringLength : 0) + 1; - log = g_CreateLog(severity, _eip, chars, &text); - log->strings = (Struct_Microsoft_Singularity_Tracing_Strings_String0 | - Struct_Microsoft_Singularity_Tracing_Strings_String1); - text = g_AddText(text, msg); - text = g_AddText(text, arg0); - text = g_AddText(text, arg1); + LEGACY_LOG_ENTRY entry = {severity,GetCurrentProcessId(), (UIntPtr)_ReturnAddress(), + 1,0,0,0,0,0,0,2,3}; + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {msg->m_stringLength, + sizeof(char), + Class_Microsoft_Singularity_Eventing_DataType___string, + &msg->m_firstChar}, + {arg0->m_stringLength, + sizeof(char), + Class_Microsoft_Singularity_Eventing_DataType___string, + &arg0->m_firstChar}, + {arg1->m_stringLength, + sizeof(char), + Class_Microsoft_Singularity_Eventing_DataType___string, + &arg1->m_firstChar}}; + + InternalLogVariableRecord(true, + TracingStorageHandle, + TracingSource->ControlFlags, + TracingTypeHandle, + &entry, + sizeof(entry), + sizeof(array)/sizeof(array[0]), + array); } -void Class_Microsoft_Singularity_Tracing:: -g_Log(uint8 severity, - char * msg) + +void LogExceptionInfo(UIntPtr throwFrom, UIntPtr handler, Class_System_String * exceptionName) { - UIntPtr _eip; - __asm { - mov eax, [ebp+4]; - mov _eip, eax; - } - char *txt = msg; - while (*txt) { - txt++; - } - Struct_Microsoft_Singularity_Tracing_LogEntry * log; - uint8 *text; - int chars = 1 + txt - msg + 1; - log = g_CreateLog(severity, _eip, chars, &text); - while (*msg != '\0') { - *text++ = *msg++; - } - *text++ = '\0'; + if (!IsTracingEnabled()) return; + + LOG_EXCEPTION entry = {throwFrom, handler, 1}; + + Struct_Microsoft_Singularity_Eventing_ArrayType array[] = { + {exceptionName->m_stringLength, + sizeof(char), + Class_Microsoft_Singularity_Eventing_DataType___string, + &exceptionName->m_firstChar}}; + + InternalLogVariableRecord(true, + TracingStorageHandle, + TracingSource->ControlFlags, + TracingTypeHandle, + &entry, + sizeof(entry), + sizeof(array)/sizeof(array[0]), + array); } -void Class_Microsoft_Singularity_Tracing:: -g_Log(uint8 severity, - char * msg, - Class_System_String *arg0, - UIntPtr arg1 - ) -{ - UIntPtr _eip; - __asm { - mov eax, [ebp+4]; - mov _eip, eax; - } - char *txt = msg; - while (*txt) { - txt++; - } - Struct_Microsoft_Singularity_Tracing_LogEntry * log; - uint8 *text; - int chars = 1 + txt - msg + 1 + (arg0 != NULL ? arg0->m_stringLength : 0) + 1; - log = g_CreateLog(severity, _eip, chars, &text); - log->strings = Struct_Microsoft_Singularity_Tracing_Strings_String0; - while (*msg != '\0') { - *text++ = *msg++; - } - *text++ = '\0'; - text = g_AddText(text, arg0); - log->arg1 = (uintptr)arg1; -} -void Class_Microsoft_Singularity_Tracing:: -g_Log(uint8 severity, - char * msg, - Class_System_String *arg0, - UIntPtr arg1, - UIntPtr arg2 - ) -{ - UIntPtr _eip; - __asm { - mov eax, [ebp+4]; - mov _eip, eax; - } - char *txt = msg; - while (*txt) { - txt++; - } - Struct_Microsoft_Singularity_Tracing_LogEntry * log; - uint8 *text; - int chars = 1 + txt - msg + 1 + (arg0 != NULL ? arg0->m_stringLength : 0) + 1; - log = g_CreateLog(severity, _eip, chars, &text); - log->strings = Struct_Microsoft_Singularity_Tracing_Strings_String0; - while (*msg != '\0') { - *text++ = *msg++; - } - *text++ = '\0'; - text = g_AddText(text, arg0); - log->arg1 = (uintptr)arg1; - log->arg2 = (uintptr)arg2; -} // ///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/ZoneAllocation.cpp b/base/Kernel/Native/ZoneAllocation.cpp new file mode 100644 index 0000000..75c8e7a --- /dev/null +++ b/base/Kernel/Native/ZoneAllocation.cpp @@ -0,0 +1,491 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ZoneAllocation.cpp +// +// Note: Non-blocking zone allocation for variable size +// elements for eventing and tracing infrastructure. +// + +#include "hal.h" +#include "eventing.h" + +// +// Utility routines for buffer validation +// + +#ifdef BUFFER_VALIDATION + +#define BOB_SIGNATURE 0xabababab // begining of buffer signature +#define EOB_SIGNATURE 0xcdcdcdcd // end of buffer signature + +void SetEndOfBuffer(PMEMORY_ZONE Zone) +{ + uint32 * ptr = (uint32 *)((ULONG_PTR)Zone + Zone->ZoneSize); + *ptr = EOB_SIGNATURE; + + ptr = (uint32 *)((ULONG_PTR)Zone) - 1; + *ptr = BOB_SIGNATURE; +} + +void CheckEndOfBuffer(PMEMORY_ZONE Zone) +{ + uint32 * ptr = (uint32 *)((ULONG_PTR)Zone + Zone->ZoneSize); + EV_ASSERT(*ptr == EOB_SIGNATURE); + + ptr = (uint32 *)((ULONG_PTR)Zone) - 1; + EV_ASSERT(*ptr == BOB_SIGNATURE); +} + +#else // !BUFFER_VALIDATION + +#define SetEndOfBuffer(x) +#define CheckEndOfBuffer(x) + +#endif // BUFFER_VALIDATION + + +PMEMORY_ZONE +InitializeMemoryZone(void * Buffer, uint16 Size, UIntPtr storageHandle) +{ + PMEMORY_ZONE Zone; + +#ifdef BUFFER_VALIDATION + + // In buffer validation mode, add extra space for the end signature + // to detect potential buffer overruns + + Size = Size - 2 * sizeof(uint32); + Buffer = (void *)((ULONG_PTR)Buffer + sizeof(uint32)); + +#endif // BUFFER_VALIDATION + + if (Size <= sizeof(MEMORY_ZONE)) { + return NULL; + } + + Zone = (PMEMORY_ZONE)Buffer; + + Zone->ZoneSize = Size; + Zone->Link = NULL; + Zone->StorageHandle = storageHandle; + Zone->LastSyncPoint = 0; + + Zone->Allocation.AtomicValue32 = 0; + Zone->ReadyList.AtomicValue32 = 0; + + Zone->Allocation.FreeOffset = sizeof(MEMORY_ZONE); + + // Assert the assumptions regarding the bit values that have been initialized + // with the dword write above + + EV_ASSERT(Zone->Allocation.Filled == 0); + EV_ASSERT(Zone->Allocation.Committed == 0); + Zone->Generation = 0; + + SetEndOfBuffer(Zone); + return Zone; +} + +bool +RecycleZone(PMEMORY_ZONE Zone) +{ + CheckEndOfBuffer(Zone); + + ZONE_ALLOCATION_POINTER CapturedValue; + ZONE_ALLOCATION_POINTER NextValue; + + do { + + CapturedValue.AtomicValue32 = Zone->Allocation.AtomicValue32; + + if (CapturedValue.Recycling) { + + // Someone is already recycling this zone. Nothing left to do, just return + + return false; + } + + // Also test whether the zone has been already recycled and in is in use + + if ((CapturedValue.Filled == 0) || (CapturedValue.Committed == 0)) { + + return false; + } + + NextValue.AtomicValue32 = CapturedValue.AtomicValue32; + NextValue.Recycling = 1; + + // Atomically also set the recycling flag to prevent other concurrent recycling + + } while (InterlockedCompareExchange(&Zone->Allocation.AtomicValue32, + NextValue.AtomicValue32, + CapturedValue.AtomicValue32) != CapturedValue.AtomicValue32); + + // We sucessfully too ownership on recycling phase. We can now reinitialize the fields + // The last operation should be atomically updating the flags and offset in + // the allocation field + + EV_ASSERT(IsZoneCompleted(Zone)); + EV_ASSERT(Zone->Allocation.Count == Zone->ReadyList.Count); + + ZONE_ALLOCATION_POINTER Allocation; + Allocation.AtomicValue32 = 0; + Allocation.FreeOffset = sizeof(MEMORY_ZONE); + Zone->LastSyncPoint = Allocation.FreeOffset; + + Zone->Generation = MemoryStorageGetNextGeneration(HANDLE_TO_STORAGE(Zone->StorageHandle)); + + InterlockedExchange(&Zone->ReadyList.AtomicValue32, 0); + InterlockedExchange(&Zone->Allocation.AtomicValue32, Allocation.AtomicValue32); + + return true; +} + + +void +MarkZoneFull(PMEMORY_ZONE Zone) +{ + ZONE_ALLOCATION_POINTER CapturedOffset; + ZONE_ALLOCATION_POINTER NextValueOffset; + + CheckEndOfBuffer(Zone); + + do { + + CapturedOffset.AtomicValue32 = Zone->Allocation.AtomicValue32; + + if (CapturedOffset.Filled) { + + // Someone already did it. We are done here. + + return; + } + + NextValueOffset.AtomicValue32 = CapturedOffset.AtomicValue32; + NextValueOffset.Filled = 1; + + // + // Atomically also set the committed flag if all entries have been commited + // + + if (Zone->ReadyList.Count == CapturedOffset.Count) { + + NextValueOffset.Committed = 1; + } + + } while (InterlockedCompareExchange(&Zone->Allocation.AtomicValue32, + NextValueOffset.AtomicValue32, + CapturedOffset.AtomicValue32) != CapturedOffset.AtomicValue32); + +} + +void +MarkZoneCommited(PMEMORY_ZONE Zone) +{ + ZONE_ALLOCATION_POINTER CapturedValue; + ZONE_ALLOCATION_POINTER NextValue; + + CheckEndOfBuffer(Zone); + + do { + + CapturedValue.AtomicValue32 = Zone->Allocation.AtomicValue32; + + if ((CapturedValue.Committed == 1) + || + (CapturedValue.Recycling == 1) + || + (CapturedValue.Filled == 0) + || + (Zone->ReadyList.Count != CapturedValue.Count)) { + + return; + } + + NextValue.AtomicValue32 = CapturedValue.AtomicValue32; + NextValue.Committed = 1; + + } while (InterlockedCompareExchange(&Zone->Allocation.AtomicValue32, + NextValue.AtomicValue32, + CapturedValue.AtomicValue32) != CapturedValue.AtomicValue32); + +} + + +PMEMORY_HEADER +AllocateEventEntry(PMEMORY_ZONE Zone, uint16 size) +{ + PMEMORY_HEADER ReturnBuffer; + ZONE_ALLOCATION_POINTER CapturedOffset; + ZONE_ALLOCATION_POINTER NextValueOffset; + uint16 Reqsize = size; + + CheckEndOfBuffer(Zone); + + size += sizeof(MEMORY_HEADER); + size = (uint16)ROUND_UP_TO_POWER2(size, sizeof(uint64)); + + do { + + CapturedOffset.AtomicValue32 = Zone->Allocation.AtomicValue32; + ReturnBuffer = GetMemoryHeader(Zone, CapturedOffset.FreeOffset); + + if (CapturedOffset.Filled) { + + return NULL; + } + + NextValueOffset.AtomicValue32 = CapturedOffset.AtomicValue32; + NextValueOffset.FreeOffset += size; + NextValueOffset.Count += 1; + + if ((NextValueOffset.FreeOffset >= Zone->ZoneSize) + || + (NextValueOffset.FreeOffset < CapturedOffset.FreeOffset )) { + + MarkZoneFull(Zone); + + // After that call this zone might have been recycled, no operations + + return NULL; + } + } while (InterlockedCompareExchange(&Zone->Allocation.AtomicValue32, + NextValueOffset.AtomicValue32, + CapturedOffset.AtomicValue32) != CapturedOffset.AtomicValue32); + + EV_ASSERT(((ULONG_PTR)ReturnBuffer + size) <= ((ULONG_PTR)Zone + Zone->ZoneSize)); + EV_ASSERT((ULONG_PTR)ReturnBuffer >= ((ULONG_PTR)(Zone + 1))); +#ifdef BUFFER_VALIDATION + ReturnBuffer->Link = 0xffff; +#endif + + ReturnBuffer->Size = (uint16)size; + ReturnBuffer->Offset = CapturedOffset.FreeOffset; + ReturnBuffer->Flags = 0; + + + Struct_Microsoft_Singularity_ThreadContext *threadContext = + Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext(); + Struct_Microsoft_Singularity_ProcessorContext *processorContext = + Class_Microsoft_Singularity_Processor::g_GetCurrentProcessorContext(); + +#if SINGULARITY_KERNEL + ReturnBuffer->TID = threadContext->threadIndex; +#else + ReturnBuffer->TID = threadContext->kernelThreadIndex; +#endif + ReturnBuffer->Cpu = processorContext->cpuRecord.id; + +#if ISA_XSCALE + ReturnBuffer->Timestamp = Class_Microsoft_Singularity_Isal_Isa::g_GetCycleCount(); +#else // ISA_XSCALE + // Guarantee strict ordering across all CPUs (which RDTSC does not provide) + static long s_timestamp; + ReturnBuffer->Timestamp = ::InterlockedIncrement(&s_timestamp); +#endif // ISA_XSCALE + + CheckEndOfBuffer(Zone); + + return ReturnBuffer; +} + +void +CommitEventEntry(PMEMORY_HEADER Entry) +{ + PMEMORY_ZONE Zone = (PMEMORY_ZONE)((ULONG_PTR)Entry - Entry->Offset); + ZONE_ALLOCATION_POINTER CapturedAllocationInfo; + ZONE_READY_LIST CapturedReadyList; + ZONE_READY_LIST NextReadyList; + + EV_ASSERT(Entry->Link == 0xffff); + CheckEndOfBuffer(Zone); + + do { + + CapturedReadyList.AtomicValue32 = Zone->ReadyList.AtomicValue32; + CapturedAllocationInfo.AtomicValue32 = Zone->Allocation.AtomicValue32; + + EV_ASSERT(CapturedAllocationInfo.Committed == 0); + EV_ASSERT(CapturedAllocationInfo.Count > CapturedReadyList.Count); + + Entry->Link = CapturedReadyList.ReadyList; + + NextReadyList.Count = CapturedReadyList.Count + 1; + NextReadyList.ReadyList = Entry->Offset; + + if (NextReadyList.Count == CapturedAllocationInfo.Count) { + + // Remember the new high watermark for forward zone walking + + Zone->LastSyncPoint = CapturedAllocationInfo.FreeOffset; + } + + } while (InterlockedCompareExchange(&Zone->ReadyList.AtomicValue32, + NextReadyList.AtomicValue32, + CapturedReadyList.AtomicValue32) != CapturedReadyList.AtomicValue32); + + if (Zone->Allocation.Filled) { + + // The thread that took the last entry from the zone, did not had + // a chance to see this last commit. We need to update also the + // commit flag + + MarkZoneCommited(Zone); + } + + +} + + +bool +IsZoneCompleted(PMEMORY_ZONE Zone) +{ + ZONE_ALLOCATION_POINTER CapturedValue; + + CapturedValue.AtomicValue32 = Zone->Allocation.AtomicValue32; + return ((CapturedValue.Filled != 0) && (CapturedValue.Committed != 0)); +} + +bool +IsEntryCommited(PMEMORY_ZONE Zone, PMEMORY_HEADER Entry) +{ + // This function assumes the zone is locked for read so it does not get + // recycled during this test + + uint16 offsetKey = Entry->Offset; + + if (offsetKey < Zone->LastSyncPoint) { + + return true; + } + + uint16 crtOffset = Zone->ReadyList.ReadyList; + + while (crtOffset) { + + if (crtOffset == offsetKey) { + + return true; + } + + Entry = (PMEMORY_HEADER)((ULONG_PTR)Zone + crtOffset); + crtOffset = Entry->Link; + } + + return false; +} + +PMEMORY_HEADER +GetFirstReadyEntry(PMEMORY_ZONE Zone, uint16 offset) +{ + PMEMORY_HEADER Entry; + + // This function assumes the zone is locked for read so it does not get + // recycled during this test + + uint16 offsetFound = Zone->ZoneSize; + uint16 crtOffset = Zone->ReadyList.ReadyList; + + while (crtOffset) { + + if ((crtOffset > offset) && (crtOffset < offsetFound)) { + + offsetFound = crtOffset; + } + + Entry = (PMEMORY_HEADER)((ULONG_PTR)Zone + crtOffset); + crtOffset = Entry->Link; + } + + if (offsetFound != Zone->ZoneSize) { + + return (PMEMORY_HEADER)((ULONG_PTR)Zone + offsetFound); + + } + return NULL; +} + + +PMEMORY_HEADER +GetFirstEntry(PMEMORY_ZONE Zone, bool forward) +{ + if (forward) { + + return GetFirstReadyEntry(Zone, 0); + } + + uint16 crtOffset = Zone->ReadyList.ReadyList; + + if (crtOffset == 0) { + + return NULL; + } + + return (PMEMORY_HEADER)((ULONG_PTR)Zone + crtOffset); +} + + + +PMEMORY_HEADER +GetNextEntry(PQUERY_VIEW view) +{ + PMEMORY_ZONE Zone = view->CurrentZone; + PMEMORY_HEADER Entry = view->CurrentEntry; + + if (!Zone->Allocation.Filled) { + + // The Zone is during allocations + + if (view->ZoneGeneration != Zone->Generation) { + + // We lost the context as the zone has been recycled + + return NULL; + } + } + + if (view->Forward) { + + view->CurrentEntryIndex += 1; + + if (view->CurrentEntryIndex >= Zone->Allocation.Count) { + + return NULL; + } + + view->CurrentEntry = (PMEMORY_HEADER)((ULONG_PTR)Entry + Entry->Size); + + if ((Zone->Allocation.Filled != 0) || IsEntryCommited(Zone, Entry)) { + + // Valid entry + + Entry = view->CurrentEntry; + + } else { + + view->CurrentEntry = GetFirstReadyEntry(Zone, Entry->Offset); + Entry = view->CurrentEntry; + } + + } else { + + if (Entry->Link == 0) { + + // We finished to walk the chain backwards + + return NULL; + } + + view->CurrentEntry = (PMEMORY_HEADER)((ULONG_PTR)Zone + Entry->Link); + Entry = view->CurrentEntry; + } + + return Entry; +} + +// +///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/_llshl.asm b/base/Kernel/Native/_llshl.asm deleted file mode 100644 index 8ade80b..0000000 --- a/base/Kernel/Native/_llshl.asm +++ /dev/null @@ -1,84 +0,0 @@ -;*** -;llshl.asm - long shift left -; -; Copyright (c) Microsoft Corporation. All rights reserved. -; -;Purpose: -; define long shift left routine (signed and unsigned are same) -; __allshl -; -;******************************************************************************* - -.686p -.mmx -.xmm -.model flat,C -.code - -assume ds:flat -assume es:flat -assume ss:flat -assume fs:nothing -assume gs:nothing - -include hal.inc - -;*** -;llshl - long shift left -; -;Purpose: -; Does a Long Shift Left (signed and unsigned are identical) -; Shifts a long left any number of bits. -; -;Entry: -; EDX:EAX - long value to be shifted -; CL - number of bits to shift by -; -;Exit: -; EDX:EAX - shifted value -; -;Uses: -; CL is destroyed. -; -;Exceptions: -; -;******************************************************************************* - -_allshl PROC NEAR - -; -; Handle shifts of 64 or more bits (all get 0) -; - cmp cl, 64 - jae short RETZERO - -; -; Handle shifts of between 0 and 31 bits -; - cmp cl, 32 - jae short MORE32 - shld edx,eax,cl - shl eax,cl - ret - -; -; Handle shifts of between 32 and 63 bits -; -MORE32: - mov edx,eax - xor eax,eax - and cl,31 - shl edx,cl - ret - -; -; return 0 in edx:eax -; -RETZERO: - xor eax,eax - xor edx,edx - ret - -_allshl ENDP - - end diff --git a/base/Kernel/Native/_llshr.asm b/base/Kernel/Native/_llshr.asm deleted file mode 100644 index a4e0a12..0000000 --- a/base/Kernel/Native/_llshr.asm +++ /dev/null @@ -1,85 +0,0 @@ -;*** -;llshr.asm - long shift right -; -; Copyright (c) Microsoft Corporation. All rights reserved. -; -;Purpose: -; define signed long shift right routine -; __allshr -; -;******************************************************************************* - -.686p -.mmx -.xmm -.model flat,C -.code - -assume ds:flat -assume es:flat -assume ss:flat -assume fs:nothing -assume gs:nothing - -include hal.inc - -;*** -;llshr - long shift right -; -;Purpose: -; Does a signed Long Shift Right -; Shifts a long right any number of bits. -; -;Entry: -; EDX:EAX - long value to be shifted -; CL - number of bits to shift by -; -;Exit: -; EDX:EAX - shifted value -; -;Uses: -; CL is destroyed. -; -;Exceptions: -; -;******************************************************************************* - -_allshr PROC NEAR - -; -; Handle shifts of 64 bits or more (if shifting 64 bits or more, the result -; depends only on the high order bit of edx). -; - cmp cl,64 - jae short RETSIGN - -; -; Handle shifts of between 0 and 31 bits -; - cmp cl, 32 - jae short MORE32 - shrd eax,edx,cl - sar edx,cl - ret - -; -; Handle shifts of between 32 and 63 bits -; -MORE32: - mov eax,edx - sar edx,31 - and cl,31 - sar eax,cl - ret - -; -; Return double precision 0 or -1, depending on the sign of edx -; -RETSIGN: - sar edx,31 - mov eax,edx - ret - -_allshr ENDP - - end diff --git a/base/Kernel/Native/_memcpy.asm b/base/Kernel/Native/_memcpy.asm deleted file mode 100644 index 78eceee..0000000 --- a/base/Kernel/Native/_memcpy.asm +++ /dev/null @@ -1,590 +0,0 @@ -;******************************************************************************* -;memcpy.asm - contains memcpy and memmove routines -; -; Copyright (c) Microsoft Corporation. All rights reserved. -; -;Purpose: -; memcpy() copies a source memory buffer to a destination buffer. -; Overlapping buffers are not treated specially, so propogation may occur. -; memmove() copies a source memory buffer to a destination buffer. -; Overlapping buffers are treated specially, to avoid propogation. -; -;******************************************************************************* - -.686p -.mmx -.xmm -.model flat,C -.code - -assume ds:flat -assume es:flat -assume ss:flat -assume fs:nothing -assume gs:nothing - -include hal.inc - -;*** -;memcpy - Copy source buffer to destination buffer -; -;Purpose: -; memcpy() copies a source memory buffer to a destination memory buffer. -; This routine does NOT recognize overlapping buffers, and thus can lead -; to propogation. -; For cases where propogation must be avoided, memmove() must be used. -; -; Algorithm: -; -; void * memcpy(void * dst, void * src, size_t count) -; { -; void * ret = dst; -; -; /* -; * copy from lower addresses to higher addresses -; */ -; while (count--) -; *dst++ = *src++; -; -; return(ret); -; } -; -;memmove - Copy source buffer to destination buffer -; -;Purpose: -; memmove() copies a source memory buffer to a destination memory buffer. -; This routine recognize overlapping buffers to avoid propogation. -; For cases where propogation is not a problem, memcpy() can be used. -; -; Algorithm: -; -; void * memmove(void * dst, void * src, size_t count) -; { -; void * ret = dst; -; -; if (dst <= src || dst >= (src + count)) { -; /* -; * Non-Overlapping Buffers -; * copy from lower addresses to higher addresses -; */ -; while (count--) -; *dst++ = *src++; -; } -; else { -; /* -; * Overlapping Buffers -; * copy from higher addresses to lower addresses -; */ -; dst += count - 1; -; src += count - 1; -; -; while (count--) -; *dst-- = *src--; -; } -; -; return(ret); -; } -; -; -;Entry: -; void *dst = pointer to destination buffer -; const void *src = pointer to source buffer -; size_t count = number of bytes to copy -; -;Exit: -; Returns a pointer to the destination buffer in AX/DX:AX -; -;Uses: -; CX, DX -; -;Exceptions: -;******************************************************************************* - -% public memcpy -memcpy proc \ - dst:ptr byte, \ - src:ptr byte, \ - count:DWORD - - ; destination pointer - ; source pointer - ; number of bytes to copy - -; push ebp ;U - save old frame pointer -; mov ebp, esp ;V - set new frame pointer - - push edi ;U - save edi - push esi ;V - save esi - - mov esi,[src] ;U - esi = source - mov ecx,[count] ;V - ecx = number of bytes to move - - mov edi,[dst] ;U - edi = dest - -; -; Check for overlapping buffers: -; If (dst <= src) Or (dst >= src + Count) Then -; Do normal (Upwards) Copy -; Else -; Do Downwards Copy to avoid propagation -; - - mov eax,ecx ;V - eax = byte count... - - mov edx,ecx ;U - edx = byte count... - add eax,esi ;V - eax = point past source end - - cmp edi,esi ;U - dst <= src ? - jbe short CopyUp ;V - yes, copy toward higher addresses - - cmp edi,eax ;U - dst < (src + count) ? - jb CopyDown ;V - yes, copy toward lower addresses - -; -; Copy toward higher addresses. -; -; -; The algorithm for forward moves is to align the destination to a dword -; boundary and so we can move dwords with an aligned destination. This -; occurs in 3 steps. -; -; - move x = ((4 - Dest & 3) & 3) bytes -; - move y = ((L-x) >> 2) dwords -; - move (L - x - y*4) bytes -; - -CopyUp: - test edi,11b ;U - destination dword aligned? - jnz short CopyLeadUp ;V - if we are not dword aligned already, align - - shr ecx,2 ;U - shift down to dword count - and edx,11b ;V - trailing byte count - - cmp ecx,8 ;U - test if small enough for unwind copy - jb short CopyUnwindUp ;V - if so, then jump - - rep movsd ;N - move all of our dwords - - jmp dword ptr TrailUpVec[edx*4] ;N - process trailing bytes - -; -; Code to do optimal memory copies for non-dword-aligned destinations. -; - -; The following length check is done for two reasons: -; -; 1. to ensure that the actual move length is greater than any possiale -; alignment move, and -; -; 2. to skip the multiple move logic for small moves where it would -; be faster to move the bytes with one instruction. -; - - align @WordSize -CopyLeadUp: - - mov eax,edi ;U - get destination offset - mov edx,11b ;V - prepare for mask - - sub ecx,4 ;U - check for really short string - sub for adjust - jb short ByteCopyUp ;V - branch to just copy bytes - - and eax,11b ;U - get offset within first dword - add ecx,eax ;V - update size after leading bytes copied - - jmp dword ptr LeadUpVec[eax*4-4] ;N - process leading bytes - - align @WordSize -ByteCopyUp: - jmp dword ptr TrailUpVec[ecx*4+16] ;N - process just bytes - - align @WordSize -CopyUnwindUp: - jmp dword ptr UnwindUpVec[ecx*4] ;N - unwind dword copy - - align @WordSize -LeadUpVec dd LeadUp1, LeadUp2, LeadUp3 - - align @WordSize -LeadUp1: - and edx,ecx ;U - trailing byte count - mov al,[esi] ;V - get first byte from source - - mov [edi],al ;U - write second byte to destination - mov al,[esi+1] ;V - get second byte from source - - mov [edi+1],al ;U - write second byte to destination - mov al,[esi+2] ;V - get third byte from source - - shr ecx,2 ;U - shift down to dword count - mov [edi+2],al ;V - write third byte to destination - - add esi,3 ;U - advance source pointer - add edi,3 ;V - advance destination pointer - - cmp ecx,8 ;U - test if small enough for unwind copy - jb short CopyUnwindUp ;V - if so, then jump - - rep movsd ;N - move all of our dwords - - jmp dword ptr TrailUpVec[edx*4] ;N - process trailing bytes - - align @WordSize -LeadUp2: - and edx,ecx ;U - trailing byte count - mov al,[esi] ;V - get first byte from source - - mov [edi],al ;U - write second byte to destination - mov al,[esi+1] ;V - get second byte from source - - shr ecx,2 ;U - shift down to dword count - mov [edi+1],al ;V - write second byte to destination - - add esi,2 ;U - advance source pointer - add edi,2 ;V - advance destination pointer - - cmp ecx,8 ;U - test if small enough for unwind copy - jb short CopyUnwindUp ;V - if so, then jump - - rep movsd ;N - move all of our dwords - - jmp dword ptr TrailUpVec[edx*4] ;N - process trailing bytes - - align @WordSize -LeadUp3: - and edx,ecx ;U - trailing byte count - mov al,[esi] ;V - get first byte from source - - mov [edi],al ;U - write second byte to destination - add esi,1 ;V - advance source pointer - - shr ecx,2 ;U - shift down to dword count - add edi,1 ;V - advance destination pointer - - cmp ecx,8 ;U - test if small enough for unwind copy - jb short CopyUnwindUp ;V - if so, then jump - - rep movsd ;N - move all of our dwords - - jmp dword ptr TrailUpVec[edx*4] ;N - process trailing bytes - - align @WordSize -UnwindUpVec dd UnwindUp0, UnwindUp1, UnwindUp2, UnwindUp3 - dd UnwindUp4, UnwindUp5, UnwindUp6, UnwindUp7 - -UnwindUp7: - mov eax,[esi+ecx*4-28] ;U - get dword from source - ;V - spare - mov [edi+ecx*4-28],eax ;U - put dword into destination -UnwindUp6: - mov eax,[esi+ecx*4-24] ;U(entry)/V(not) - get dword from source - ;V(entry) - spare - mov [edi+ecx*4-24],eax ;U - put dword into destination -UnwindUp5: - mov eax,[esi+ecx*4-20] ;U(entry)/V(not) - get dword from source - ;V(entry) - spare - mov [edi+ecx*4-20],eax ;U - put dword into destination -UnwindUp4: - mov eax,[esi+ecx*4-16] ;U(entry)/V(not) - get dword from source - ;V(entry) - spare - mov [edi+ecx*4-16],eax ;U - put dword into destination -UnwindUp3: - mov eax,[esi+ecx*4-12] ;U(entry)/V(not) - get dword from source - ;V(entry) - spare - mov [edi+ecx*4-12],eax ;U - put dword into destination -UnwindUp2: - mov eax,[esi+ecx*4-8] ;U(entry)/V(not) - get dword from source - ;V(entry) - spare - mov [edi+ecx*4-8],eax ;U - put dword into destination -UnwindUp1: - mov eax,[esi+ecx*4-4] ;U(entry)/V(not) - get dword from source - ;V(entry) - spare - mov [edi+ecx*4-4],eax ;U - put dword into destination - - lea eax,[ecx*4] ;V - compute update for pointer - - add esi,eax ;U - update source pointer - add edi,eax ;V - update destination pointer -UnwindUp0: - jmp dword ptr TrailUpVec[edx*4] ;N - process trailing bytes - -;----------------------------------------------------------------------------- - - align @WordSize -TrailUpVec dd TrailUp0, TrailUp1, TrailUp2, TrailUp3 - - align @WordSize -TrailUp0: - mov eax,[dst] ;U - return pointer to destination - pop esi ;V - restore esi - pop edi ;U - restore edi - ;V - spare - ret - - align @WordSize -TrailUp1: - mov al,[esi] ;U - get byte from source - ;V - spare - mov [edi],al ;U - put byte in destination - mov eax,[dst] ;V - return pointer to destination - pop esi ;U - restore esi - pop edi ;V - restore edi - ret - - align @WordSize -TrailUp2: - mov al,[esi] ;U - get first byte from source - ;V - spare - mov [edi],al ;U - put first byte into destination - mov al,[esi+1] ;V - get second byte from source - mov [edi+1],al ;U - put second byte into destination - mov eax,[dst] ;V - return pointer to destination - pop esi ;U - restore esi - pop edi ;V - restore edi - ret - - align @WordSize -TrailUp3: - mov al,[esi] ;U - get first byte from source - ;V - spare - mov [edi],al ;U - put first byte into destination - mov al,[esi+1] ;V - get second byte from source - mov [edi+1],al ;U - put second byte into destination - mov al,[esi+2] ;V - get third byte from source - mov [edi+2],al ;U - put third byte into destination - mov eax,[dst] ;V - return pointer to destination - pop esi ;U - restore esi - pop edi ;V - restore edi - ret - -;----------------------------------------------------------------------------- -;----------------------------------------------------------------------------- -;----------------------------------------------------------------------------- - -; -; Copy down to avoid propogation in overlapping buffers. -; - align @WordSize -CopyDown: - lea esi,[esi+ecx-4] ;U - point to 4 bytes before src buffer end - lea edi,[edi+ecx-4] ;V - point to 4 bytes before dest buffer end -; -; See if the destination start is dword aligned -; - - test edi,11b ;U - test if dword aligned - jnz short CopyLeadDown ;V - if not, jump - - shr ecx,2 ;U - shift down to dword count - and edx,11b ;V - trailing byte count - - cmp ecx,8 ;U - test if small enough for unwind copy - jb short CopyUnwindDown ;V - if so, then jump - - std ;N - set direction flag - rep movsd ;N - move all of our dwords - cld ;N - clear direction flag back - - jmp dword ptr TrailDownVec[edx*4] ;N - process trailing bytes - - align @WordSize -CopyUnwindDown: - neg ecx ;U - negate dword count for table merging - ;V - spare - - jmp dword ptr UnwindDownVec[ecx*4+28] ;N - unwind copy - - align @WordSize -CopyLeadDown: - - mov eax,edi ;U - get destination offset - mov edx,11b ;V - prepare for mask - - cmp ecx,4 ;U - check for really short string - jb short ByteCopyDown ;V - branch to just copy bytes - - and eax,11b ;U - get offset within first dword - sub ecx,eax ;U - to update size after lead copied - - jmp dword ptr LeadDownVec[eax*4-4] ;N - process leading bytes - - align @WordSize -ByteCopyDown: - jmp dword ptr TrailDownVec[ecx*4] ;N - process just bytes - - align @WordSize -LeadDownVec dd LeadDown1, LeadDown2, LeadDown3 - - align @WordSize -LeadDown1: - mov al,[esi+3] ;U - load first byte - and edx,ecx ;V - trailing byte count - - mov [edi+3],al ;U - write out first byte - sub esi,1 ;V - point to last src dword - - shr ecx,2 ;U - shift down to dword count - sub edi,1 ;V - point to last dest dword - - cmp ecx,8 ;U - test if small enough for unwind copy - jb short CopyUnwindDown ;V - if so, then jump - - std ;N - set direction flag - rep movsd ;N - move all of our dwords - cld ;N - clear direction flag - - jmp dword ptr TrailDownVec[edx*4] ;N - process trailing bytes - - align @WordSize -LeadDown2: - mov al,[esi+3] ;U - load first byte - and edx,ecx ;V - trailing byte count - - mov [edi+3],al ;U - write out first byte - mov al,[esi+2] ;V - get second byte from source - - shr ecx,2 ;U - shift down to dword count - mov [edi+2],al ;V - write second byte to destination - - sub esi,2 ;U - point to last src dword - sub edi,2 ;V - point to last dest dword - - cmp ecx,8 ;U - test if small enough for unwind copy - jb short CopyUnwindDown ;V - if so, then jump - - std ;N - set direction flag - rep movsd ;N - move all of our dwords - cld ;N - clear direction flag - - jmp dword ptr TrailDownVec[edx*4] ;N - process trailing bytes - - align @WordSize -LeadDown3: - mov al,[esi+3] ;U - load first byte - and edx,ecx ;V - trailing byte count - - mov [edi+3],al ;U - write out first byte - mov al,[esi+2] ;V - get second byte from source - - mov [edi+2],al ;U - write second byte to destination - mov al,[esi+1] ;V - get third byte from source - - shr ecx,2 ;U - shift down to dword count - mov [edi+1],al ;V - write third byte to destination - - sub esi,3 ;U - point to last src dword - sub edi,3 ;V - point to last dest dword - - cmp ecx,8 ;U - test if small enough for unwind copy - jb CopyUnwindDown ;V - if so, then jump - - std ;N - set direction flag - rep movsd ;N - move all of our dwords - cld ;N - clear direction flag - - jmp dword ptr TrailDownVec[edx*4] ;N - process trailing bytes - -;------------------------------------------------------------------ - - align @WordSize -UnwindDownVec dd UnwindDown7, UnwindDown6, UnwindDown5, UnwindDown4 - dd UnwindDown3, UnwindDown2, UnwindDown1, UnwindDown0 - -UnwindDown7: - mov eax,[esi+ecx*4+28] ;U - get dword from source - ;V - spare - mov [edi+ecx*4+28],eax ;U - put dword into destination -UnwindDown6: - mov eax,[esi+ecx*4+24] ;U(entry)/V(not) - get dword from source - ;V(entry) - spare - mov [edi+ecx*4+24],eax ;U - put dword into destination -UnwindDown5: - mov eax,[esi+ecx*4+20] ;U(entry)/V(not) - get dword from source - ;V(entry) - spare - mov [edi+ecx*4+20],eax ;U - put dword into destination -UnwindDown4: - mov eax,[esi+ecx*4+16] ;U(entry)/V(not) - get dword from source - ;V(entry) - spare - mov [edi+ecx*4+16],eax ;U - put dword into destination -UnwindDown3: - mov eax,[esi+ecx*4+12] ;U(entry)/V(not) - get dword from source - ;V(entry) - spare - mov [edi+ecx*4+12],eax ;U - put dword into destination -UnwindDown2: - mov eax,[esi+ecx*4+8] ;U(entry)/V(not) - get dword from source - ;V(entry) - spare - mov [edi+ecx*4+8],eax ;U - put dword into destination -UnwindDown1: - mov eax,[esi+ecx*4+4] ;U(entry)/V(not) - get dword from source - ;V(entry) - spare - mov [edi+ecx*4+4],eax ;U - put dword into destination - - lea eax,[ecx*4] ;V - compute update for pointer - - add esi,eax ;U - update source pointer - add edi,eax ;V - update destination pointer -UnwindDown0: - jmp dword ptr TrailDownVec[edx*4] ;N - process trailing bytes - -;----------------------------------------------------------------------------- - - align @WordSize -TrailDownVec dd TrailDown0, TrailDown1, TrailDown2, TrailDown3 - - align @WordSize -TrailDown0: - mov eax,[dst] ;U - return pointer to destination - ;V - spare - pop esi ;U - restore esi - pop edi ;V - restore edi - ret - - align @WordSize -TrailDown1: - mov al,[esi+3] ;U - get byte from source - ;V - spare - mov [edi+3],al ;U - put byte in destination - mov eax,[dst] ;V - return pointer to destination - pop esi ;U - restore esi - pop edi ;V - restore edi - ret - - align @WordSize -TrailDown2: - mov al,[esi+3] ;U - get first byte from source - ;V - spare - mov [edi+3],al ;U - put first byte into destination - mov al,[esi+2] ;V - get second byte from source - mov [edi+2],al ;U - put second byte into destination - mov eax,[dst] ;V - return pointer to destination - pop esi ;U - restore esi - pop edi ;V - restore edi - ret - - align @WordSize -TrailDown3: - mov al,[esi+3] ;U - get first byte from source - ;V - spare - mov [edi+3],al ;U - put first byte into destination - mov al,[esi+2] ;V - get second byte from source - mov [edi+2],al ;U - put second byte into destination - mov al,[esi+1] ;V - get third byte from source - mov [edi+1],al ;U - put third byte into destination - mov eax,[dst] ;V - return pointer to destination - pop esi ;U - restore esi - pop edi ;V - restore edi - ret - -memcpy endp - - -memmove proc \ - dst:ptr byte, \ - src:ptr byte, \ - count:DWORD - - jmp memcpy - -memmove endp - end - diff --git a/base/Kernel/Native/_memset.asm b/base/Kernel/Native/_memset.asm deleted file mode 100644 index 960a8c9..0000000 --- a/base/Kernel/Native/_memset.asm +++ /dev/null @@ -1,140 +0,0 @@ -;******************************************************************************* -;memset.asm - set a section of memory to all one byte -; -; Copyright (c) Microsoft Corporation. All rights reserved. -; -;Purpose: -; contains the memset() routine -; -;******************************************************************************* - -.686p -.mmx -.xmm -.model flat,C -.code - -assume ds:flat -assume es:flat -assume ss:flat -assume fs:nothing -assume gs:nothing - -include hal.inc - -;*** -;char *memset(dst, value, count) - sets "count" bytes at "dst" to "value" -; -;Purpose: -; Sets the first "count" bytes of the memory starting -; at "dst" to the character value "value". -; -; Algorithm: -; char * -; memset (dst, value, count) -; char *dst; -; char value; -; unsigned int count; -; { -; char *start = dst; -; -; while (count--) -; *dst++ = value; -; return(start); -; } -; -;Entry: -; char *dst - pointer to memory to fill with value -; char value - value to put in dst bytes -; int count - number of bytes of dst to fill -; -;Exit: -; returns dst, with filled bytes -; -;Uses: -; -;Exceptions: -; -;******************************************************************************* - - public memset -memset proc - - .FPO ( 0, 3, 0, 0, 0, 0 ) - - mov edx,[esp + 0ch] ; edx = "count" - mov ecx,[esp + 4] ; ecx points to "dst" - - test edx,edx ; 0? - jz short toend ; if so, nothing to do - - xor eax,eax - mov al,[esp + 8] ; the byte "value" to be stored - - -; Align address on dword boundary - - push edi ; preserve edi - mov edi,ecx ; edi = dest pointer - - cmp edx,4 ; if it's less then 4 bytes - jb tail ; tail needs edi and edx to be initialized - - neg ecx - and ecx,3 ; ecx = # bytes before dword boundary - jz short dwords ; jump if address already aligned - - sub edx,ecx ; edx = adjusted count (for later) -adjust_loop: - mov [edi],al - add edi,1 - sub ecx,1 - jnz adjust_loop - -dwords: -; set all 4 bytes of eax to [value] - mov ecx,eax ; ecx=0/0/0/value - shl eax,8 ; eax=0/0/value/0 - - add eax,ecx ; eax=0/0val/val - - mov ecx,eax ; ecx=0/0/val/val - - shl eax,10h ; eax=val/val/0/0 - - add eax,ecx ; eax = all 4 bytes = [value] - -; Set dword-sized blocks - mov ecx,edx ; move original count to ecx - and edx,3 ; prepare in edx byte count (for tail loop) - shr ecx,2 ; adjust ecx to be dword count - jz tail ; jump if it was less then 4 bytes - - rep stosd -main_loop_tail: - test edx,edx ; if there is no tail bytes, - jz finish ; we finish, and it's time to leave -; Set remaining bytes - -tail: - mov [edi],al ; set remaining bytes - add edi,1 - - sub edx,1 ; if there is some more bytes - jnz tail ; continue to fill them - -; Done -finish: - mov eax,[esp + 8] ; return dest pointer - pop edi ; restore edi - - ret - -toend: - mov eax,[esp + 4] ; return dest pointer - - ret - -memset endp - - end diff --git a/base/Kernel/Native/_ulldvrm.asm b/base/Kernel/Native/_ulldvrm.asm deleted file mode 100644 index fb81fbd..0000000 --- a/base/Kernel/Native/_ulldvrm.asm +++ /dev/null @@ -1,193 +0,0 @@ -;******************************************************************************* -;ulldvrm.asm - unsigned long divide and remainder routine -; -; Copyright (c) Microsoft Corporation. All rights reserved. -; -;Purpose: -; defines the unsigned long divide and remainder routine -; __aulldvrm -; -;Revision History: -; 10-06-98 SMK Initial version. -; -;******************************************************************************* - -.686p -.mmx -.xmm -.model flat,C -.code - -assume ds:flat -assume es:flat -assume ss:flat -assume fs:nothing -assume gs:nothing - -include hal.inc - -;*** -;ulldvrm - unsigned long divide and remainder -; -;Purpose: -; Does a unsigned long divide and remainder of the arguments. Arguments -; are not changed. -; -;Entry: -; Arguments are passed on the stack: -; 1st pushed: divisor (QWORD) -; 2nd pushed: dividend (QWORD) -; -;Exit: -; EDX:EAX contains the quotient (dividend/divisor) -; EBX:ECX contains the remainder (divided % divisor) -; NOTE: this routine removes the parameters from the stack. -; -;Uses: -; ECX -; -;Exceptions: -; -;******************************************************************************* - -_aulldvrm PROC NEAR - - push esi - -; Set up the local stack and save the index registers. When this is done -; the stack frame will look as follows (assuming that the expression a/b will -; generate a call to aulldvrm(a, b)): -; -; ----------------- -; | | -; |---------------| -; | | -; |--divisor (b)--| -; | | -; |---------------| -; | | -; |--dividend (a)-| -; | | -; |---------------| -; | return addr** | -; |---------------| -; ESP---->| ESI | -; ----------------- -; - -DVND equ [esp + 8] ; stack address of dividend (a) -DVSR equ [esp + 16] ; stack address of divisor (b) - -; -; Now do the divide. First look to see if the divisor is less than 4194304K. -; If so, then we can use a simple algorithm with word divides, otherwise -; things get a little more complex. -; - - mov eax,HIWORD(DVSR) ; check to see if divisor < 4194304K - or eax,eax - jnz short L1 ; nope, gotta do this the hard way - mov ecx,LOWORD(DVSR) ; load divisor - mov eax,HIWORD(DVND) ; load high word of dividend - xor edx,edx - div ecx ; get high order bits of quotient - mov ebx,eax ; save high bits of quotient - mov eax,LOWORD(DVND) ; edx:eax <- remainder:lo word of dividend - div ecx ; get low order bits of quotient - mov esi,eax ; ebx:esi <- quotient - -; -; Now we need to do a multiply so that we can compute the remainder. -; - mov eax,ebx ; set up high word of quotient - mul dword ptr LOWORD(DVSR) ; HIWORD(QUOT) * DVSR - mov ecx,eax ; save the result in ecx - mov eax,esi ; set up low word of quotient - mul dword ptr LOWORD(DVSR) ; LOWORD(QUOT) * DVSR - add edx,ecx ; EDX:EAX = QUOT * DVSR - jmp short L2 ; complete remainder calculation - -; -; Here we do it the hard way. Remember, eax contains DVSRHI -; - -L1: - mov ecx,eax ; ecx:ebx <- divisor - mov ebx,LOWORD(DVSR) - mov edx,HIWORD(DVND) ; edx:eax <- dividend - mov eax,LOWORD(DVND) -L3: - shr ecx,1 ; shift divisor right one bit; hi bit <- 0 - rcr ebx,1 - shr edx,1 ; shift dividend right one bit; hi bit <- 0 - rcr eax,1 - or ecx,ecx - jnz short L3 ; loop until divisor < 4194304K - div ebx ; now divide, ignore remainder - mov esi,eax ; save quotient - -; -; We may be off by one, so to check, we will multiply the quotient -; by the divisor and check the result against the orignal dividend -; Note that we must also check for overflow, which can occur if the -; dividend is close to 2**64 and the quotient is off by 1. -; - - mul dword ptr HIWORD(DVSR) ; QUOT * HIWORD(DVSR) - mov ecx,eax - mov eax,LOWORD(DVSR) - mul esi ; QUOT * LOWORD(DVSR) - add edx,ecx ; EDX:EAX = QUOT * DVSR - jc short L4 ; carry means Quotient is off by 1 - -; -; do long compare here between original dividend and the result of the -; multiply in edx:eax. If original is larger or equal, we are ok, otherwise -; subtract one (1) from the quotient. -; - - cmp edx,HIWORD(DVND) ; compare hi words of result and original - ja short L4 ; if result > original, do subtract - jb short L5 ; if result < original, we are ok - cmp eax,LOWORD(DVND) ; hi words are equal, compare lo words - jbe short L5 ; if less or equal we are ok, else subtract -L4: - dec esi ; subtract 1 from quotient - sub eax,LOWORD(DVSR) ; subtract divisor from result - sbb edx,HIWORD(DVSR) -L5: - xor ebx,ebx ; ebx:esi <- quotient - -L2: -; -; Calculate remainder by subtracting the result from the original dividend. -; Since the result is already in a register, we will do the subtract in the -; opposite direction and negate the result. -; - - sub eax,LOWORD(DVND) ; subtract dividend from result - sbb edx,HIWORD(DVND) - neg edx ; otherwise, negate the result - neg eax - sbb edx,0 - -; -; Now we need to get the quotient into edx:eax and the remainder into ebx:ecx. -; - mov ecx,edx - mov edx,ebx - mov ebx,ecx - mov ecx,eax - mov eax,esi -; -; Just the cleanup left to do. edx:eax contains the quotient. -; Restore the saved registers and return. -; - - pop esi - - ret 16 - -_aulldvrm ENDP - - end diff --git a/base/Kernel/Native/arm/Crt/__div.asm b/base/Kernel/Native/arm/Crt/__div.asm new file mode 100644 index 0000000..33af2f2 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/__div.asm @@ -0,0 +1,139 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; Signed divide of r1 by r0 +; Return: +; r0 - quotient +; r1 - remainder + + OPT 2 ; disable listing + INCLUDE kxarm.inc + OPT 1 ; reenable listing + + IMPORT __rt_div0 + + IF Thumbing + AREA |.text|, CODE, READONLY, THUMB + ELSE + AREA |.text|, CODE, READONLY + ENDIF + + EXPORT |__rt_sdiv| [FUNC] + +|__rt_sdiv| + + IF Thumbing + ; Switch from Thumb mode to ARM mode + DCW 0x4778 ; bx pc + DCW 0x46C0 ; nop + ENDIF + + ANDS a4, r0, #&80000000 + RSBMI r0, r0, #0 + EORS ip, a4, r1, ASR #32 + + RSBCS r1, r1, #0 + + MOVS a3, r0 + BEQ DivideByZero + +while + CMP a3, r1, LSR #8 + MOVLS a3, a3, LSL #8 + BLO while + + CMP a3, r1, LSR #1 + BHI goto7 + CMP a3, r1, LSR #2 + BHI goto6 + CMP a3, r1, LSR #3 + BHI goto5 + CMP a3, r1, LSR #4 + BHI goto4 + CMP a3, r1, LSR #5 + BHI goto3 + CMP a3, r1, LSR #6 + BHI goto2 + CMP a3, r1, LSR #7 + BHI goto1 + +loop + MOVHI a3, a3, LSR #8 + + CMP r1, a3, LSL #7 + ADC a4, a4, a4 + SUBCS r1, r1, a3, LSL #7 + CMP r1, a3, LSL #6 +goto1 + ADC a4, a4, a4 + SUBCS r1, r1, a3, LSL #6 + CMP r1, a3, LSL #5 + +goto2 + ADC a4, a4, a4 + SUBCS r1, r1, a3, LSL #5 + CMP r1, a3, LSL #4 + +goto3 + ADC a4, a4, a4 + SUBCS r1, r1, a3, LSL #4 + CMP r1, a3, LSL #3 + +goto4 + ADC a4, a4, a4 + SUBCS r1, r1, a3, LSL #3 + CMP r1, a3, LSL #2 + +goto5 + ADC a4, a4, a4 + SUBCS r1, r1, a3, LSL #2 + CMP r1, a3, LSL #1 + +goto6 + ADC a4, a4, a4 + SUBCS r1, r1, a3, LSL #1 + +goto7 + CMP r1, a3 + ADC a4, a4, a4 + SUBCS r1, r1, a3 + CMP a3, r0 + BNE loop + MOV r0, a4 + + MOVS ip, ip, ASL #1 + RSBCS r0, r0, #0 + RSBMI r1, r1, #0 + +end + IF Interworking :LOR: Thumbing + BX lr + ELSE + MOV pc, lr + ENDIF + +; +; Divide by zero has occurred. Raise an exception +; call RaiseException(STATUS_INTEGER_DIVIDE_BY_ZERO, 0, 0, NULL) +; + +DivideByZero + + ldr r12, =__rt_div0 + ldr r0, =0xC0000094 + mov r1, #0 + mov r2, #0 + mov r3, #0 + IF Thumbing + bx r12 + ELSE + mov pc, r12 + ENDIF + + END diff --git a/base/Kernel/Native/arm/Crt/__div0.cpp b/base/Kernel/Native/arm/Crt/__div0.cpp new file mode 100644 index 0000000..1827683 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/__div0.cpp @@ -0,0 +1,15 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// This file contains ARM-specific FP code. +// + +extern "C" int __rt_div0() +{ + // RaiseException(STATUS_INTEGER_DIVIDE_BY_ZERO, 0, 0, NULL); + __debugbreak(); + return -1; +} diff --git a/base/Kernel/Native/arm/Crt/__udiv.asm b/base/Kernel/Native/arm/Crt/__udiv.asm new file mode 100644 index 0000000..2195ab4 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/__udiv.asm @@ -0,0 +1,130 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; Unsigned divide of r1 by r0: returns quotient in r0, remainder in r1 +; Destroys a3, a4 + + OPT 2 ; disable listing + INCLUDE kxarm.inc + OPT 1 ; reenable listing + + IMPORT __rt_div0 + + IF Thumbing + AREA |.text|, CODE, READONLY, THUMB + ELSE + AREA |.text|, CODE, READONLY + ENDIF + + EXPORT |__rt_udiv| [FUNC] + +|__rt_udiv| + + IF Thumbing + ; Switch from Thumb mode to ARM mode + DCW 0x4778 ; bx pc + DCW 0x46C0 ; nop + ENDIF + + MOV a4, #0 + MOVS a3, r0 + BEQ DivideByZero + +while + CMP a3, r1, LSR #8 + MOVLS a3, a3, LSL #8 + BLO while + + CMP a3, r1, LSR #1 + BHI goto7 + CMP a3, r1, LSR #2 + BHI goto6 + CMP a3, r1, LSR #3 + BHI goto5 + CMP a3, r1, LSR #4 + BHI goto4 + CMP a3, r1, LSR #5 + BHI goto3 + CMP a3, r1, LSR #6 + BHI goto2 + CMP a3, r1, LSR #7 + BHI goto1 +loop + MOVHI a3, a3, LSR #8 + + CMP r1, a3, LSL #7 + ADC a4, a4, a4 + SUBCS r1, r1, a3, LSL #7 + CMP r1, a3, LSL #6 + +goto1 + ADC a4, a4, a4 + SUBCS r1, r1, a3, LSL #6 + CMP r1, a3, LSL #5 + +goto2 + ADC a4, a4, a4 + SUBCS r1, r1, a3, LSL #5 + CMP r1, a3, LSL #4 + +goto3 + ADC a4, a4, a4 + SUBCS r1, r1, a3, LSL #4 + CMP r1, a3, LSL #3 + +goto4 + ADC a4, a4, a4 + SUBCS r1, r1, a3, LSL #3 + CMP r1, a3, LSL #2 + +goto5 + ADC a4, a4, a4 + SUBCS r1, r1, a3, LSL #2 + CMP r1, a3, LSL #1 + +goto6 + ADC a4, a4, a4 + SUBCS r1, r1, a3, LSL #1 + +goto7 + CMP r1, a3 + ADC a4, a4, a4 + SUBCS r1, r1, a3 + CMP a3, r0 + BNE loop + MOV r0, a4 + +end + + IF Interworking :LOR: Thumbing + BX lr + ELSE + MOV pc, lr + ENDIF + + +; +; Divide by zero has occurred. Raise an exception +; call RaiseException(STATUS_INTEGER_DIVIDE_BY_ZERO, 0, 0, NULL) +; + +DivideByZero + + ldr r12, =__rt_div0 + ldr r0, =0xC0000094 + mov r1, #0 + mov r2, #0 + mov r3, #0 + IF Thumbing + bx r12 + ELSE + mov pc, r12 + ENDIF + + END diff --git a/base/Kernel/Native/arm/Crt/add.asm b/base/Kernel/Native/arm/Crt/add.asm new file mode 100644 index 0000000..6507746 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/add.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL addsub_s + + GET veneer.asm + + END diff --git a/base/Kernel/Native/arm/Crt/arith.asm b/base/Kernel/Native/arm/Crt/arith.asm new file mode 100644 index 0000000..23c16c5 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/arith.asm @@ -0,0 +1,5613 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; arith.s +; +; Copyright (C) Advanced RISC Machines Limited, 1994. All rights reserved. +; +; RCS Revision: 1 +; Checkin Date: 2007/06/29 02:59:16 +; Revising Author + +; > coresrc.s.arith +; +; Assembler source for FPA support code and emulator +; ================================================== +; Routines to do arithmetic. +; + +; These routines work on numbers in the standard internal format. + +;=========================================================================== + + GBLS NormaliseOp1_str + GBLS NormaliseOp1Neg_str + GBLS NormaliseOp2_str + GBLS NormDenormOp1_str + GBLS NormDenormOp2_str + + GBLS ConvertNaNs_str + GBLS ConvertNaN1_str + GBLS ConvertNaN1Of2_str + GBLS ConvertNaN2Of2_str + + GBLL FPLibWanted + + [ FPEWanted :LOR: FPASCWanted + +NormaliseOp1_str SETS "NormaliseOp1" +NormaliseOp1Neg_str SETS "NormaliseOp1Neg" +NormaliseOp2_str SETS "NormaliseOp2" +NormDenormOp1_str SETS "NormDenormOp1" +NormDenormOp2_str SETS "NormDenormOp2" + +ConvertNaNs_str SETS "ConvertNaNs" +ConvertNaN1_str SETS "ConvertNaN1" +ConvertNaN1Of2_str SETS "ConvertNaN1Of2" +ConvertNaN2Of2_str SETS "ConvertNaN2Of2" + +FPLibWanted SETL {FALSE} + + | + +NormaliseOp1_str SETS "__fp_normalise_op1" +NormaliseOp1Neg_str SETS "__fp_normalise_op1neg" +NormaliseOp2_str SETS "__fp_normalise_op2" +NormDenormOp1_str SETS "__fp_norm_denorm_op1" +NormDenormOp2_str SETS "__fp_norm_denorm_op2" + +ConvertNaNs_str SETS "__fp_convert_NaNs" +ConvertNaN1_str SETS "__fp_convert_NaN1" +ConvertNaN1Of2_str SETS "__fp_convert_NaN_1Of2" +ConvertNaN2Of2_str SETS "__fp_convert_NaN_2Of2" + +FPLibWanted SETL {TRUE} + + [ :LNOT: :DEF: normalise_s + + IMPORT $NormaliseOp1_str + IMPORT $NormaliseOp1Neg_str + IMPORT $NormaliseOp2_str + IMPORT $NormDenormOp1_str + IMPORT $NormDenormOp2_str + + IMPORT $ConvertNaNs_str + IMPORT $ConvertNaN1_str + IMPORT $ConvertNaN1Of2_str + IMPORT $ConvertNaN2Of2_str + ] + + ] + + [ :DEF: normalise_s :LOR: FPEWanted :LOR: FPASCWanted + +; Many of these routines use some standard entry and exit conventions. There +; are two such sets of conventions: +; +; STANDARD MONADIC OPERATION ENTRY AND EXIT +; ----------------------------------------- +; +; Entry: OP1sue = Operand sign, uncommon, exponent; +; OP1mhi = Operand mantissa, high word; +; OP1mlo = Operand mantissa, low word; +; Rfpsr = FPSR; +; Rins = instruction (may be needed to determine the exact +; operation and/or for traps); +; Rwp, Rfp, Rsp hold their usual values; +; R14 = return link. +; Exit: OP1sue = the result's sign and uncommon bit; the remaining bits are +; zero if the uncommon bit is 0, and set correctly for the final +; result if the uncommon bit is 1; +; OP1mhi, OP1mlo = the result's mantissa; +; RNDexp (= OP2sue) = if the uncommon bit is 0, the result exponent, +; which may be negative; otherwise corrupt; +; Rarith is corrupt if the uncommon bit is 1; otherwise, if the +; destination precision is extended, it holds the round bit (in bit +; 31) and the sticky bit (in bits 30:0), and if the destination +; precision is single or double, it holds part of the sticky bit +; (the remainder of which is held in bits below the round bit in +; OP1mhi and OP1mlo); +; OP2mhi, OP2mlo, Rtmp, Rtmp2 and R14 may be corrupt; +; Rfpsr may be updated; +; All other registers preserved. +; +; STANDARD DYADIC OPERATION ENTRY AND EXIT +; ---------------------------------------- +; +; Entry: OP1sue = First operand sign, uncommon, exponent; +; OP1mhi = First operand mantissa, high word; +; OP1mlo = First operand mantissa, low word; +; OP2sue = Second operand sign, uncommon, exponent; +; OP2mhi = Second operand mantissa, high word; +; OP2mlo = Second operand mantissa, low word; +; Rfpsr = FPSR; +; Rins = instruction (may be needed to determine the exact +; operation and/or for traps); +; Rwp, Rfp, Rsp hold their usual values; +; R14 = return link. +; Exit: OP1sue = the result's sign and uncommon bit; the remaining bits are +; zero if the uncommon bit is 0, and set correctly for the final +; result if the uncommon bit is 1; +; OP1mhi, OP1mlo = the result's mantissa; +; RNDexp (= OP2sue) = if the uncommon bit is 0, the result exponent, +; which may be negative; otherwise corrupt; +; Rarith is corrupt if the uncommon bit is 1; otherwise, if the +; destination precision is extended, it holds the round bit (in bit +; 31) and the sticky bit (in bits 30:0), and if the destination +; precision is single or double, it holds part of the sticky bit +; (the remainder of which is held in bits below the round bit in +; OP1mhi and OP1mlo); +; OP2mhi, OP2mlo, Rtmp, Rtmp2 and R14 may be corrupt; +; Rfpsr may be updated; +; All other registers preserved. +; +; In both sets of conventions, the routine called is free to produce an +; incorrect result mantissa and rounding information, as long as it knows +; that it will in fact be rounded to the correct value. + +;=========================================================================== + +; Routine to normalise the first or only operand. The biased exponent won't +; be taken below 0: instead, the number will be denormalised if normalising +; it would cause this to happen. Note that the result will never be marked +; as uncommon: any caller of this routine must deal with this itself if +; necessary. +; Entry: OP1sue = First operand sign, remaining bits junk; +; OP1mhi, OP1mlo = First operand mantissa; +; Rarith = First operand exponent, shifted to be left aligned in the +; word; +; Rwp, Rfp, Rsp contain their usual values; +; R14 is the return link. +; Exit: OP1sue = First operand sign and exponent (uncommon is always 0); +; OP1mhi, OP1mlo updated; +; Rarith, Rtmp, Rtmp2 and R14 may be corrupt; +; All other registers preserved. + +$NormDenormOp1_str + +; Clear out the junk bits in OP1sue. + + AND OP1sue,OP1sue,#Sign_bit + +; Do we have to normalise by 32 bits or more? + + TEQ OP1mhi,#0 + BEQ NormDenormOp1_LongShift + +; If not, find out how much we do have to shift by. + + MOV Rtmp,#0 ;Accumulate shift amount in Rtmp + MOVS Rtmp2,OP1mhi,LSR #16 + MOVEQ OP1mhi,OP1mhi,LSL #16 + ADDEQ Rtmp,Rtmp,#16 + MOVS Rtmp2,OP1mhi,LSR #24 + MOVEQ OP1mhi,OP1mhi,LSL #8 + ADDEQ Rtmp,Rtmp,#8 + MOVS Rtmp2,OP1mhi,LSR #28 + MOVEQ OP1mhi,OP1mhi,LSL #4 + ADDEQ Rtmp,Rtmp,#4 + MOVS Rtmp2,OP1mhi,LSR #30 + MOVEQ OP1mhi,OP1mhi,LSL #2 + ADDEQ Rtmp,Rtmp,#2 + MOVS Rtmp2,OP1mhi,LSR #31 + MOVEQ OP1mhi,OP1mhi,LSL #1 + ADDEQ Rtmp,Rtmp,#1 + +; Have we shifted too far? - i.e. by more than the exponent? If so, go back +; the excess distance. Then complete the shift - i.e. convert the single +; word shift into a two word shift - adjust the exponent if the exponent was +; greater than the shift amount (otherwise we leave it zero) and return. + + SUBS Rtmp2,Rtmp,Rarith,LSR #32-EIExp_len ;Shift amt. - exp. + MOVHI OP1mhi,OP1mhi,LSR Rtmp2 + MOVHI Rtmp,Rarith,LSR #32-EIExp_len + RSB Rarith,Rtmp,#32 + ORR OP1mhi,OP1mhi,OP1mlo,LSR Rarith + MOV OP1mlo,OP1mlo,LSL Rtmp + SUBLO OP1sue,OP1sue,Rtmp2,LSL #EIExp_pos ;ADD exp.-shift amt. + + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC, LR + ENDIF + +NormDenormOp1_LongShift + +; The top word is zero, so we need to shift by 32 bits or more. Or do we? - +; if the exponent is less than 32, we simply need to shift by the exponent. + + CMP Rarith,#32:SHL:(32-EIExp_len) + BLO NormDenormOp1_ByExponent + +; Now check the bottom word: if it is also zero, we simply need to +; denormalise to exponent 0. + + MOVS OP1mhi,OP1mlo + IF Interworking :LOR: Thumbing + BXEQ LR + ELSE + MOVEQ PC,LR ;OP1sue/mhi/mlo are all already correct! + ENDIF + + MOV OP1mlo,#0 + +; The bottom word is non-zero, so we have a shift amount in the range 32-63. + + MOV Rtmp,#32 + MOVS Rtmp2,OP1mhi,LSR #16 + MOVEQ OP1mhi,OP1mhi,LSL #16 + ADDEQ Rtmp,Rtmp,#16 + MOVS Rtmp2,OP1mhi,LSR #24 + MOVEQ OP1mhi,OP1mhi,LSL #8 + ADDEQ Rtmp,Rtmp,#8 + MOVS Rtmp2,OP1mhi,LSR #28 + MOVEQ OP1mhi,OP1mhi,LSL #4 + ADDEQ Rtmp,Rtmp,#4 + MOVS Rtmp2,OP1mhi,LSR #30 + MOVEQ OP1mhi,OP1mhi,LSL #2 + ADDEQ Rtmp,Rtmp,#2 + MOVS Rtmp2,OP1mhi,LSR #31 + MOVEQ OP1mhi,OP1mhi,LSL #1 + ADDEQ Rtmp,Rtmp,#1 + +; Have we shifted too far? - i.e. by more than the exponent? If so, go back +; the excess distance. Note that this cannot require us to undo the shift +; from the bottom word to the top word, since we know the exponent was at +; least 32. +; So we need to backshift if shift amount > exponent, and create a +; non-zero exponent if shift amount < exponent. + + SUBS Rtmp2,Rtmp,Rarith,LSR #32-EIExp_len ;Shift amt. - exp. + MOVHI OP1mhi,OP1mhi,LSR Rtmp2 + SUBLO OP1sue,OP1sue,Rtmp2,LSL #EIExp_pos ;ADD exp.-shift amt. + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + +NormDenormOp1_ByExponent + +; We need to shift the mantissa left by the exponent, which is guaranteed to +; be less than 32, and to return a zero exponent (note that OP1sue is +; already set up for this). + + MOV Rtmp,Rarith,LSR #32-EIExp_len + RSB Rtmp2,Rtmp,#32 + MOV OP1mhi,OP1mhi,LSL Rtmp + ORR OP1mhi,OP1mhi,OP1mlo,LSR Rtmp2 + MOV OP1mlo,OP1mlo,LSL Rtmp + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +;=========================================================================== + +; Routine to normalise the second operand. The biased exponent won't be +; taken below 0: instead, the number will be denormalised if normalising it +; would cause this to happen. Note that the result will never be marked +; as uncommon: any caller of this routine must deal with this itself if +; necessary. +; Entry: OP2sue = Second operand sign, remaining bits junk; +; OP2mhi, OP2mlo = Second operand mantissa; +; Rarith = Second operand exponent, shifted to be left aligned in the +; word; +; Rwp, Rfp, Rsp contain their usual values; +; R14 is the return link. +; Exit: OP2sue = Second operand sign and exponent (uncommon is always 0); +; OP2mhi, OP2mlo updated; +; Rarith, Rtmp, Rtmp2 and R14 may be corrupt; +; All other registers preserved. + +$NormDenormOp2_str + +; Clear out the junk bits in OP2sue. + + AND OP2sue,OP2sue,#Sign_bit + +; Do we have to normalise by 32 bits or more? + + TEQ OP2mhi,#0 + BEQ NormDenormOp2_LongShift + +; If not, find out how much we do have to shift by. + + MOV Rtmp,#0 ;Accumulate shift amount in Rtmp + MOVS Rtmp2,OP2mhi,LSR #16 + MOVEQ OP2mhi,OP2mhi,LSL #16 + ADDEQ Rtmp,Rtmp,#16 + MOVS Rtmp2,OP2mhi,LSR #24 + MOVEQ OP2mhi,OP2mhi,LSL #8 + ADDEQ Rtmp,Rtmp,#8 + MOVS Rtmp2,OP2mhi,LSR #28 + MOVEQ OP2mhi,OP2mhi,LSL #4 + ADDEQ Rtmp,Rtmp,#4 + MOVS Rtmp2,OP2mhi,LSR #30 + MOVEQ OP2mhi,OP2mhi,LSL #2 + ADDEQ Rtmp,Rtmp,#2 + MOVS Rtmp2,OP2mhi,LSR #31 + MOVEQ OP2mhi,OP2mhi,LSL #1 + ADDEQ Rtmp,Rtmp,#1 + +; Have we shifted too far? - i.e. by more than the exponent? If so, go back +; the excess distance. Then complete the shift - i.e. convert the single +; word shift into a two word shift - adjust the exponent if the exponent was +; greater than the shift amount (otherwise we leave it zero) and return. + + SUBS Rtmp2,Rtmp,Rarith,LSR #32-EIExp_len ;Shift amt. - exp. + MOVHI OP2mhi,OP2mhi,LSR Rtmp2 + MOVHI Rtmp,Rarith,LSR #32-EIExp_len + RSB Rarith,Rtmp,#32 + ORR OP2mhi,OP2mhi,OP2mlo,LSR Rarith + MOV OP2mlo,OP2mlo,LSL Rtmp + SUBLO OP2sue,OP2sue,Rtmp2,LSL #EIExp_pos ;ADD exp.-shift amt. + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +NormDenormOp2_LongShift + +; The top word is zero, so we need to shift by 32 bits or more. Or do we? - +; if the exponent is less than 32, we simply need to shift by the exponent. + + CMP Rarith,#32:SHL:(32-EIExp_len) + BLO NormDenormOp2_ByExponent + +; Now check the bottom word: if it is also zero, we simply need to +; denormalise to exponent 0. + + MOVS OP2mhi,OP2mlo + IF Interworking :LOR: Thumbing + BXEQ LR + ELSE + MOVEQ PC,LR ;OP2sue/mhi/mlo are all already correct! + ENDIF + MOV OP2mlo,#0 + +; The bottom word is non-zero, so we have a shift amount in the range 32-63. + + MOV Rtmp,#32 + MOVS Rtmp2,OP2mhi,LSR #16 + MOVEQ OP2mhi,OP2mhi,LSL #16 + ADDEQ Rtmp,Rtmp,#16 + MOVS Rtmp2,OP2mhi,LSR #24 + MOVEQ OP2mhi,OP2mhi,LSL #8 + ADDEQ Rtmp,Rtmp,#8 + MOVS Rtmp2,OP2mhi,LSR #28 + MOVEQ OP2mhi,OP2mhi,LSL #4 + ADDEQ Rtmp,Rtmp,#4 + MOVS Rtmp2,OP2mhi,LSR #30 + MOVEQ OP2mhi,OP2mhi,LSL #2 + ADDEQ Rtmp,Rtmp,#2 + MOVS Rtmp2,OP2mhi,LSR #31 + MOVEQ OP2mhi,OP2mhi,LSL #1 + ADDEQ Rtmp,Rtmp,#1 + +; Have we shifted too far? - i.e. by more than the exponent? If so, go back +; the excess distance. Note that this cannot require us to undo the shift +; from the bottom word to the top word, since we know the exponent was at +; least 32. +; So we need to backshift if shift amount > exponent, and create a +; non-zero exponent if shift amount < exponent. + + SUBS Rtmp2,Rtmp,Rarith,LSR #32-EIExp_len ;Shift amt. - exp. + MOVHI OP2mhi,OP2mhi,LSR Rtmp2 + SUBLO OP2sue,OP2sue,Rtmp2,LSL #EIExp_pos ;ADD exp.-shift amt. + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +NormDenormOp2_ByExponent + +; We need to shift the mantissa left by the exponent, which is guaranteed to +; be less than 32, and to return a zero exponent (note that OP2sue is +; already set up for this). + + MOV Rtmp,Rarith,LSR #32-EIExp_len + RSB Rtmp2,Rtmp,#32 + MOV OP2mhi,OP2mhi,LSL Rtmp + ORR OP2mhi,OP2mhi,OP2mlo,LSR Rtmp2 + MOV OP2mlo,OP2mlo,LSL Rtmp + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +;=========================================================================== + +; Routine to float an integer. To fit in with the usual conventions, the +; entry point is given two labels, namely "FltFPE" and "FltFPASC". +; The value returned is always a numeric value plus associated rounding +; information, with the uncommon bit clear. +; Entry: Rarith = integer; +; Rfpsr = FPSR; +; Rins = instruction (needed for traps); +; Rwp, Rfp, Rsp hold their usual values; +; R14 = return link. +; Exit: OP1sue = the result's sign, with the remaining bits zero; +; OP1mhi, OP1mlo = the result's mantissa; +; RNDexp (= OP2sue) = the result exponent; +; Rarith = 0 (i.e. the appropriate round and sticky information for +; extended precision); +; OP2mhi, OP2mlo, Rtmp, Rtmp2 and R14 may be corrupt; +; Rfpsr may be updated; +; All other registers preserved. + + [ FPEWanted +FltFPE + ] + + [ FPASCWanted +FltFPASC + ] + + CDebug1 3,"FltFPE/FPASC: operand =",Rarith + +; Extract the sign and produce an unnormalised mantissa. In the process, +; detect the special case of a zero operand. + + MOV OP1mlo,#0 ;Mantissa low word is always zero + ANDS OP1sue,Rarith,#Sign_bit ;Extract sign + ASSERT Sign_pos = 31 + RSBNE OP1mhi,Rarith,#0 ;If -ve, 2's complement the integer + MOVEQS OP1mhi,Rarith ;If +ve, copy and check for zero + MOVEQ RNDexp,#0 ;If zero, result exponent is zero + IF Interworking :LOR: Thumbing + BXEQ LR + ELSE + MOVEQ PC,LR ; and return (Rarith is already 0) + ENDIF + +; If non-zero, set the approriate exponent and rounding information, then +; fall through into NormaliseOp1 to complete the job. + + MOV RNDexp,#(EIExp_bias+31):AND:&FF00 + ORR RNDexp,RNDexp,#(EIExp_bias+31):AND:&FF + ASSERT (EIExp_bias+31) <= &FFFF + MOV Rarith,#0 + +; Fall through to NormaliseOp1 + +;=========================================================================== + +; NB it is possible to fall through into this routine. + +; Routine to normalise the result or first operand. Unlike the two routines +; above, this routine will normalise the exponent to a value less than zero +; if necessary, and it won't put the exponent back into OP1sue. Note that +; the result will never be marked as uncommon: any caller of this routine +; must deal with this itself if necessary. +; Entry: OP1mhi, OP1mlo = Result/first operand mantissa, which must not be +; all zero; +; RNDexp = Result/first operand exponent (in normal position in +; word); +; Rwp, Rfp, Rsp contain their usual values; +; R14 is the return link. +; Exit: OP1mhi, OP1mlo and RNDexp updated; +; Rtmp, Rtmp2 and R14 may be corrupt; +; All other registers preserved; +; NE condition is true. + +$NormaliseOp1_str + TEQ OP1mhi,#0 ;Do full word shift if + MOVEQ OP1mhi,OP1mlo ; necessary + MOVEQ OP1mlo,#0 + SUBEQ RNDexp,RNDexp,#32 + MOV Rtmp,#0 ;Counter for rest of shift + MOVS Rtmp2,OP1mhi,LSR #16 ;Shift top word by 16 if + MOVEQ OP1mhi,OP1mhi,LSL #16 ; necessary + ADDEQ Rtmp,Rtmp,#16 + MOVS Rtmp2,OP1mhi,LSR #24 ;Shift top word by 8 if + MOVEQ OP1mhi,OP1mhi,LSL #8 ; necessary + ADDEQ Rtmp,Rtmp,#8 + MOVS Rtmp2,OP1mhi,LSR #28 ;Shift top word by 4 if + MOVEQ OP1mhi,OP1mhi,LSL #4 ; necessary + ADDEQ Rtmp,Rtmp,#4 + MOVS Rtmp2,OP1mhi,LSR #30 ;Shift top word by 2 if + MOVEQ OP1mhi,OP1mhi,LSL #2 ; necessary + ADDEQ Rtmp,Rtmp,#2 + MOVS Rtmp2,OP1mhi,LSR #31 ;Shift top word by 1 if + MOVEQ OP1mhi,OP1mhi,LSL #1 ; necessary + ADDEQ Rtmp,Rtmp,#1 + RSBS Rtmp2,Rtmp,#32 ;Shift the bottom word by + ORR OP1mhi,OP1mhi,OP1mlo,LSR Rtmp2 ; the same amount and set NE + MOV OP1mlo,OP1mlo,LSL Rtmp + SUB RNDexp,RNDexp,Rtmp ;Adjust exponent by shift + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR ; amount and return + ENDIF + +;=========================================================================== + +; Routine to normalise the second operand. Unlike the two routines above, +; this routine will normalise the exponent to a value less than zero if +; necessary, and it won't put the exponent back into OP1sue. Note that the +; result will never be marked as uncommon: any caller of this routine must +; deal with this itself if necessary. +; Entry: OP2mhi, OP2mlo = Second operand mantissa, which must not be all +; zero; +; RNDexp = Second operand exponent (in normal position in word); +; Rwp, Rfp, Rsp contain their usual values; +; R14 is the return link. +; Exit: OP2mhi, OP2mlo and RNDexp updated; +; Rtmp, Rtmp2 and R14 may be corrupt; +; All other registers preserved; +; NE condition is true. + +$NormaliseOp2_str + TEQ OP2mhi,#0 ;Do full word shift if + MOVEQ OP2mhi,OP2mlo ; necessary + MOVEQ OP2mlo,#0 + SUBEQ RNDexp,RNDexp,#32 + MOV Rtmp,#0 ;Counter for rest of shift + MOVS Rtmp2,OP2mhi,LSR #16 ;Shift top word by 16 if + MOVEQ OP2mhi,OP2mhi,LSL #16 ; necessary + ADDEQ Rtmp,Rtmp,#16 + MOVS Rtmp2,OP2mhi,LSR #24 ;Shift top word by 8 if + MOVEQ OP2mhi,OP2mhi,LSL #8 ; necessary + ADDEQ Rtmp,Rtmp,#8 + MOVS Rtmp2,OP2mhi,LSR #28 ;Shift top word by 4 if + MOVEQ OP2mhi,OP2mhi,LSL #4 ; necessary + ADDEQ Rtmp,Rtmp,#4 + MOVS Rtmp2,OP2mhi,LSR #30 ;Shift top word by 2 if + MOVEQ OP2mhi,OP2mhi,LSL #2 ; necessary + ADDEQ Rtmp,Rtmp,#2 + MOVS Rtmp2,OP2mhi,LSR #31 ;Shift top word by 1 if + MOVEQ OP2mhi,OP2mhi,LSL #1 ; necessary + ADDEQ Rtmp,Rtmp,#1 + RSBS Rtmp2,Rtmp,#32 ;Shift the bottom word by + ORR OP2mhi,OP2mhi,OP2mlo,LSR Rtmp2 ; the same amount and set NE + MOV OP2mlo,OP2mlo,LSL Rtmp + SUB RNDexp,RNDexp,Rtmp ;Adjust exponent by shift + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR ; amount and return + ENDIF + +;=========================================================================== + +; Routine to normalise the first operand. Like "NormaliseOp1", except that +; it increments the exponent in RNDexp by the shift amount, rather than +; decrementing it. +; Entry: OP1mhi, OP1mlo = Second operand mantissa, which must not be all +; zero; +; RNDexp = Exponent (in normal position in word); +; Rwp, Rfp, Rsp contain their usual values; +; R14 is the return link. +; Exit: OP1mhi, OP1mlo and RNDexp updated; +; Rtmp, Rtmp2 and R14 may be corrupt; +; All other registers preserved; +; NE condition is true. + +$NormaliseOp1Neg_str + TEQ OP1mhi,#0 ;Do full word shift if + MOVEQ OP1mhi,OP1mlo ; necessary + MOVEQ OP1mlo,#0 + ADDEQ RNDexp,RNDexp,#32 + MOV Rtmp,#0 ;Counter for rest of shift + MOVS Rtmp2,OP1mhi,LSR #16 ;Shift top word by 16 if + MOVEQ OP1mhi,OP1mhi,LSL #16 ; necessary + ADDEQ Rtmp,Rtmp,#16 + MOVS Rtmp2,OP1mhi,LSR #24 ;Shift top word by 8 if + MOVEQ OP1mhi,OP1mhi,LSL #8 ; necessary + ADDEQ Rtmp,Rtmp,#8 + MOVS Rtmp2,OP1mhi,LSR #28 ;Shift top word by 4 if + MOVEQ OP1mhi,OP1mhi,LSL #4 ; necessary + ADDEQ Rtmp,Rtmp,#4 + MOVS Rtmp2,OP1mhi,LSR #30 ;Shift top word by 2 if + MOVEQ OP1mhi,OP1mhi,LSL #2 ; necessary + ADDEQ Rtmp,Rtmp,#2 + MOVS Rtmp2,OP1mhi,LSR #31 ;Shift top word by 1 if + MOVEQ OP1mhi,OP1mhi,LSL #1 ; necessary + ADDEQ Rtmp,Rtmp,#1 + RSBS Rtmp2,Rtmp,#32 ;Shift the bottom word by + ORR OP1mhi,OP1mhi,OP1mlo,LSR Rtmp2 ; the same amount and set NE + MOV OP1mlo,OP1mlo,LSL Rtmp + ADD RNDexp,RNDexp,Rtmp ;Adjust exponent by shift + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR ; amount and return + ENDIF + + ] + +;=========================================================================== + + [ :DEF: addsub_s :LOR: FPEWanted :LOR: FPASCWanted + +; Routine to add, subtract or reverse subtract two internal format floating +; point numbers. It has two entry points: "AddSubFPE", which has an +; optimised fast track for both operands being common, and "AddSubFPASC", +; which avoids the test for this optimised fast track - since it should +; never happen. The second entry point lies a long way down in the source +; to avoid addressing constraints. +; The value returned is either a numeric value plus associated rounding +; information, with the uncommon bit clear, or an infinity or NaN, with the +; uncommon bit set. +; This routine will not work correctly with inputs which are unnormalised +; URD results, or with invalid internal format numbers. +; +; Uses standard dyadic operation entry and exit conventions - see top of +; this file. + + ASSERT RNDexp = OP2sue ;We swap over from the use of OP2sue to that + ; of RNDexp partway through this routine. + + [ FPEWanted + +AddSubFPE + + CDebug3 3,"AddSubFPE: op1 =",OP1sue,OP1mhi,OP1mlo + CDebug3 3," op2 =",OP2sue,OP2mhi,OP2mlo + +; Start by detecting the "fast track" case of both operands being common. + + TST OP1sue,#Uncommon_bit + TSTEQ OP2sue,#Uncommon_bit + BNE AddSub_Uncommon + + ] + + [ FPLibWanted +__fp_addsub_common + ] + +AddSub_Common + + STMFD Rsp!,{LR} ;Register needed, and we may get a + ; subroutine call + + CDebug3 4,"AddSub_Common: op1 =",OP1sue,OP1mhi,OP1mlo + CDebug3 4," op2 =",OP2sue,OP2mhi,OP2mlo + +; Both operands are zeros or normalised numbers. We can distinguish between +; them on the basis of the units bit. However, note that the standard +; algorithm for adding/subtracting floating point numbers (i.e. do an +; alignment shift on the one with the smaller exponent, add or subtract the +; mantissas, then do a normalisation shift if necessary) works equally well +; on all of these. + +; This entry point is also called from AddSub_Uncommon to add or subtract +; operands which are zeros, normalised numbers or extended denormalised +; numbers. It works perfectly well on such numbers, provided it is +; recognised that the result mantissa may be unnormalised and non-zero. +; Note that we know that the invalid operation and divide-by-zero +; exceptions won't occur - i.e. we don't need to preserve the operands. So +; we start by modifying the signs of the operands for SUF and RSF +; instructions. + + [ :LNOT: :DEF: addsub_s + + TST Rins,#SubNotAdd_bit ;Is it SUF/RSF, not ADF? + EORNE OP2sue,OP2sue,#Sign_bit ;If so, change op2 sign (assuming SUF) + TST Rins,#RSF_bit ;Is it RSF, not ADF/SUF? + EORNE OP2sue,OP2sue,#Sign_bit ;If so, we shouldn't have changed op2 + EORNE OP1sue,OP1sue,#Sign_bit ; sign and should have changed op1 sign + + ] + +; We can consider this to be an addition from now on. Next, we'll deal with +; the basic exponent and sign calculation: the results of this may get +; modified later on. +; This section will leave the prospective sign for the result in OP1sue, +; R14 containing the exclusive-OR of the signs (which determines later +; whether we do a magnitude addition or subtraction), RNDexp equal to the +; first operand exponent and Rarith equal to the exponent difference. + + ExpDiff Rtmp,Rarith,OP1sue,OP2sue ;Get difference and op1 exp. + EOR R14,OP1sue,OP2sue ;Make EOR of signs + AND OP1sue,OP1sue,#Sign_bit ;Isolate prospective result sign + MOV RNDexp,Rarith,LSR #32-EIExp_len ;Right-align operand 1 exponent + BHI AddSub_Op2Shift + MOVEQ Rtmp2,Rtmp ;If EQ, Rtmp = Rtmp2 = 0 + BEQ AddSub_ShiftDone ; = correct guard/round/sticky + +AddSub_Op1Shift + +; Operand 1 needs shifting, and so operand 2's exponent is used for the +; result. Rarith currently contains exp1-exp2 = -(shift amount), +; left-aligned. + + RSB Rarith,Rtmp,#0 ;Get shift amount = exp2 - exp1 + MOV Rarith,Rarith,LSR #32-EIExp_len ;Right-align exponent difference + ADD RNDexp,RNDexp,Rarith ;Resurrect operand 2 exponent + +; Now denormalise (OP1mhi,OP1mlo) with a shift amount of Rarith, putting +; op1 guard/round/sticky bits into Rtmp, op2 guard/round/sticky bits into +; Rtmp2. + + Denorm OP1mhi,OP1mlo,Rtmp,Rarith,Rtmp2,Rarith + MOV Rtmp2,#0 ;Operand 2 guard/round/sticky + B AddSub_ShiftDone + +AddSub_Op2Shift + +; Operand 2 needs shifting, and so we've already selected the correct result +; exponent. Furthermore, Rtmp currently contains exp1-exp2 = shift amount, +; left-aligned. So denormalise (OP2mhi,OP2mlo) with a shift amount of Rtmp, +; putting op1 guard/round/sticky bits into Rtmp, op2 guard/round/sticky bits +; into Rtmp2. + + MOV Rarith,Rtmp,LSR #32-EIExp_len ;Right-align exponent difference + Denorm OP2mhi,OP2mlo,Rtmp2,Rarith,Rtmp,Rarith + MOV Rtmp,#0 ;Operand 1 guard/round/sticky + +AddSub_ShiftDone + +; We now have: +; OP1sue: Prospective result sign (= operand 1 sign); +; OP1mhi/OP1mlo: Operand 1 mantissa, possibly shifted; +; RNDexp: Prospective result exponent (= MAX(operand exponents)); +; OP2mhi/OP2mlo: Operand 2 mantissa, possibly shifted; +; Rarith: Free; +; Rfpsr: FPSR; +; Rtmp: Operand 1 guard, round and sticky bits; +; Rins: Instruction; +; Rtmp2: Operand 2 guard, round and sticky bits; +; Rwp,Rfp,Rsp: Standard values; +; R14: Sign bit indicates magnitude subtraction/NOT addition; +; Now we need to split according to whether we need to do a magnitude +; addition or a magnitude subtraction. + + TST R14,#Sign_bit + BNE AddSub_MagSub + +AddSub_MagAdd + +; Perform the magnitude addition. Note first that we have no need for a +; guard bit in this case, so we are going to regard the guard/round/sticky +; bits in Rtmp[31/30/29:0] and Rtmp2[31/30/29:0] as simply being +; round/sticky bits in Rtmp[31/30:0] and Rtmp2[31/30:0]. Secondly, note that +; since we know that at least one of Rtmp and Rtmp2 is zero, we can simply +; add these round/sticky bit representations to get the result round/sticky +; representation. + + ADDS Rarith,Rtmp,Rtmp2 ;Will not in fact generate C=1 + ADCS OP1mlo,OP1mlo,OP2mlo + ADCS OP1mhi,OP1mhi,OP2mhi + +; If C=0, we're done. Otherwise, we've got to adjust the exponent, mantissa, +; round and sticky bits. + + IF Interworking :LOR: Thumbing + LDMCCFD Rsp!,{LR} + BXCC LR + ELSE + LDMCCFD Rsp!,{PC} + ENDIF + ADD RNDexp,RNDexp,#1 + MOVS OP1mhi,OP1mhi,RRX + MOVS OP1mlo,OP1mlo,RRX + ORR Rarith,Rarith,Rarith,LSL #1 ;Sticky receives all of old + MOV Rarith,Rarith,RRX ; round/sticky; round is new + IF Interworking :LOR: Thumbing + LDMFD Rsp!,{LR} + BX LR + ELSE + LDMFD Rsp!,{PC} + ENDIF + + +AddSub_MagSub + +; We need to do a magnitude subtraction of OP2mhi/OP2mlo/Rtmp2 from +; OP1mhi/OP1mlo/Rtmp. The prospective result exponent in RNDexp has been +; made right already, but if the subtraction comes out negative, we will +; have to change the sign of the result. Note we can subtract the +; guard/round/sticky representations in Rtmp and Rtmp2, because we know one +; of them is entirely zero. + + SUBS Rarith,Rtmp,Rtmp2 + SBCS OP1mlo,OP1mlo,OP2mlo + SBCS OP1mhi,OP1mhi,OP2mhi + +; If the subtraction (which was of unsigned numbers) came out negative, we +; need to reverse the sign of the result and 2's complement the mantissa - +; again including the guard/round/sticky part. + + BCS AddSub_MagSub_Normalise + EOR OP1sue,OP1sue,#Sign_bit + RSBS Rarith,Rarith,#0 + RSCS OP1mlo,OP1mlo,#0 + RSC OP1mhi,OP1mhi,#0 + +AddSub_MagSub_Normalise + +; Now we need to normalise the result. This is slightly tricky, because in +; the case of subtracting the largest possible number with one exponent from +; the smallest number of the next exponent (e.g. 1-(1-2^(-64))), the leading +; bit of the result is actually the round bit. We can divide into two cases: +; +; (a) The exponent difference was 0 or 1: in this case, the number may be +; normalised by up to 64 bits, but the current round and sticky bits +; are guaranteed to be 0 - this ensures that the eventual sticky bit +; is guaranteed to be zero, and that the round bit is also zero if a +; non-zero normalisation shift is required; +; +; (b) The exponent difference was 2 or more: in this case, the number can +; be normalised by at most one bit, but the eventual sticky bit may be +; non-zero. +; +; So we will first try to normalise by 1 bit, bringing the guard bit into the +; mantissa if necessary. + + TST OP1mhi,#EIUnits_bit ;Already normalised? + IF Interworking :LOR: Thumbing + LDMNEFD Rsp!,{LR} ;Return if so + BXNE LR + ELSE + LDMNEFD Rsp!,{PC} ;Return if so + ENDIF + ADDS Rarith,Rarith,Rarith ;Shift mhi/mlo/guard/round/sticky + ADCS OP1mlo,OP1mlo,OP1mlo ; left by one bit to form new + ADC OP1mhi,OP1mhi,OP1mhi ; mhi/mlo/round/sticky + SUB RNDexp,RNDexp,#1 + +; If the result is normalised now, we're done. Otherwise, we know that a +; normalisation shift of 1-63 is still required, that the exponent +; difference was 0 or 1, and thus that the new round and sticky bits are +; both zero. +; However, at this point, we need to look out for the case of a magnitude +; subtraction of two equal numbers - for which we need to apply the special +; IEEE sign rule (i.e. -0 if rounding to -infinity, otherwise +0). + + TST OP1mhi,#EIUnits_bit ;Normalised now? + IF Interworking :LOR: Thumbing + LDMNEFD Rsp!,{LR} ;Return if so + BXNE LR + ELSE + LDMNEFD Rsp!,{PC} ;Return if so + ENDIF + + ORRS LR,OP1mhi,OP1mlo ;Is result zero? + BLNE $NormaliseOp1_str ;If not, complete normalisation + IF Interworking :LOR: Thumbing + LDMNEFD Rsp!,{LR} ; and return (note NormaliseOp1 + BXNE LR + ELSE + LDMNEFD Rsp!,{PC} ; and return (note NormaliseOp1 + ENDIF + + +; We know the result is a zero, with sign determined by the rounding mode. +; Everything except the sign and exponent has been correctly set already, +; so we test the rounding mode, set the sign and exponent, and return. + + [ :DEF: addsub_s + MOV dOPh, #0 + MOV dOPl, #0 + ASSERT dOPh = fOP :LOR: dOPl = fOP + ; ADD sp,sp,#4 ; Pop link register off the stack + ; VReturn + IF Interworking :LOR: Thumbing + LDMFD Rsp!,{LR} + BX LR + ELSE + LDMFD Rsp!,{PC} + ENDIF + + | + AND Rtmp,Rins,#RM_mask + TEQ Rtmp,#RM_MinusInf + MOVEQ OP1sue,#Sign_bit + MOVNE OP1sue,#0 + MOV RNDexp,#0 + + IF Interworking :LOR: Thumbing + LDMFD Rsp!,{LR} + BX LR + ELSE + LDMFD Rsp!,{PC} + ENDIF + + + ] + + ] ; Conditional assembly of AddSub + +;=========================================================================== + + [ :DEF: mul_s :LOR: FPEWanted :LOR: FPASCWanted + +; Routine to multiply or fast-multiply two internal format floating point +; numbers. It has two entry points: "MultFPE", which has an optimised fast +; track for both operands being common, and "MultFPASC", which avoids the +; test for this optimised fast track - since it should never happen. The +; second entry point lies a long way down in the source to avoid addressing +; constraints. +; The value returned is either a numeric value plus associated rounding +; information, with the uncommon bit clear, or an infinity or NaN, with the +; uncommon bit set. +; This routine will not work correctly with inputs which are unnormalised +; URD results, or with invalid internal format numbers. +; +; Uses standard dyadic operation entry and exit conventions - see top of +; this file. + + ASSERT RNDexp = OP2sue ;We swap over from the use of OP2sue to that + ; of RNDexp partway through this routine. + + [ FPEWanted + +MultFPE + + CDebug3 3,"MultFPE: op1 =",OP1sue,OP1mhi,OP1mlo + CDebug3 3," op2 =",OP2sue,OP2mhi,OP2mlo + +; Start by detecting the "fast track" case of both operands being common. + + TST OP1sue,#Uncommon_bit + TSTEQ OP2sue,#Uncommon_bit + BNE Mult_Uncommon + +; If either operand is a zero, the product is a zero. Because the numbers +; are common and assumed not to be unnormalised URD results, we can check +; for zeros by means of the units bits. + + ANDS Rtmp,OP1mhi,OP2mhi + ASSERT EIUnits_pos = 31 + BPL Mult_Zero + +; Both operands may now be assumed to be normalised numbers. Produce the +; result sign and the prospective result exponent. + + ] + + [ :DEF: mul_s :LOR: FPEWanted + + [ FPLibWanted +__fp_mult_common + ] + + AND Rtmp,OP1sue,#ToExp_mask + AND Rtmp2,OP2sue,#ToExp_mask + EOR OP1sue,OP1sue,OP2sue ;Produce result sign + AND OP1sue,OP1sue,#Sign_bit + ADD RNDexp,Rtmp,Rtmp2 + SUB RNDexp,RNDexp,#(EIExp_bias-1):AND:&FF00 + SUB RNDexp,RNDexp,#(EIExp_bias-1):AND:&FF + ASSERT (EIExp_bias-1) < &10000 ;Result exponent if mantissa + ; overflow is exp1+exp2-bias+1 + + ] + +; This subsidiary entry point deals with multiplying two normalised +; mantissas together and adjusting the exponent if necessary. +; Entry: OP1sue = the result's sign, with an uncommon bit of 0 - the +; remaining bits are zero; +; OP1mhi = First operand mantissa, high word; +; OP1mlo = First operand mantissa, low word; +; RNDexp = Prospective result exponent, which may be negative; this +; needs to be decremented if mantissa overflow doesn't occur; +; OP2mhi = Second operand mantissa, high word; +; OP2mlo = Second operand mantissa, low word; +; Rins = instruction (may be needed to discriminate between MUF and +; FML); +; Rwp, Rfp, Rsp hold their usual values; +; R14 = return link. +; Exit: OP1sue = the result's sign, with an uncommon bit of 0; the +; remaining bits are zero; +; OP1mhi, OP1mlo = the result's mantissa; +; RNDexp = the result exponent, which may be negative; +; Rarith holds the round bit (in bit 31) and the sticky bit (in bits +; 30:0) if the destination precision is extended; if the +; destination precision is single or double, it holds part of the +; sticky bit (the remainder of which is held in bits below the +; round bit in OP1mhi and OP1mlo); +; OP2mhi, OP2mlo, Rtmp, Rtmp2 and R14 may be corrupt; +; All other registers preserved. + +Mult_Mantissas + +; We will split into various lines, depending on the operands: +; +; if ((OP1mlo = 0) AND (OP2mlo = 0)) +; do 32x32->64 multiplication of OP1mhi by OP2mhi; +; if ((OP1mlo = 0) AND (OP2mlo != 0)) +; do 32x64->96 multiplication of OP1mhi by (OP2mhi,OP2mlo); +; if ((OP1mlo != 0) AND (OP2mlo = 0)) +; do 64x32->96 multiplication of (OP1mhi,OP1mlo) by OP2mhi; +; if ((OP1mlo != 0) AND (OP2mlo != 0)) +; do 64x32->128 multiplication of (OP1mhi,OP1mlo) by (OP2mhi,OP2mlo); +; +; In each case, this is then followed by code to deal with the case of no +; mantissa overflow (i.e. the top bit of the product was zero) and to create +; the round and sticky bits. +; +; This is all designed to make multiplications involving single precision +; numbers, immediate constants and/or FLTed integers as efficient as +; possible. +; +; If the instruction is an FML, we simply assume that both mantissa low +; words are zero. + + [ FPEWanted + + TST Rins,#Fast_bit + BNE Mult_32x32 + + ] + + TEQ OP1mlo,#0 + BEQ Mult_32xX + +Mult_64xX + + TEQ OP2mlo,#0 + BEQ Mult_64x32 + +Mult_64x64 + + STMFD Rsp!,{OP1sue,Rfpsr,Rins,LR} + +; We do this multiplication by applying the trick (described in Knuth +; section 4.3.3) for reducing the obvious algorithm involving four 32x32 +; multiplications to just three plus some additions and sign manipulations, +; by means of the formula: +; +; (a1*2^32 + a0) * (b1*2^32 + b0) +; = a1*b1*(2^64+2^32) + (a1-a0)*(b0-b1)*2^32 + a0*b0*(2^32+1) +; +; This has to be done carefully: the a1*b1 and a0*b0 multiplications are +; straightforward 32x32 multiplications, but each of a1-a0 and b0-b1 is in +; the range -2^32+1 < x < 2^32-1. To see what effect this has, we need to +; look at what we will get if we simply do the a1-a0 and b0-b1 subtractions, +; then multiply the results as unsigned numbers: +; +; (A) If a1-a0 >= 0, b0-b1 >= 0: +; product obtained = (a1-a0)*(b0-b1) +; +; (B) If a1-a0 >= 0, b0-b1 < 0: +; product obtained = (a1-a0)*(b0-b1+2^32) +; = (a1-a0)*(b0-b1) + (a1-a0)*2^32 +; +; (C) If a1-a0 < 0, b0-b1 >= 0: +; product obtained = (a1-a0+2^32)*(b0-b1) +; = (a1-a0)*(b0-b1) + (b0-b1)*2^32 +; +; (D) If a1-a0 < 0, b0-b1 < 0: +; product obtained = (a1-a0+2^32)*(b0-b1+2^32) +; = (a1-a0)*(b0-b1) + ((a1-a0)+(b0-b1))*2^32 + 2^64 +; = (a1-a0)*(b0-b1) +; + ((a1-a0+2^32) + (b0-b1+2^32))*2^32 - 2^64 +; +; So to get the real value of (a1-a0)*(b0-b1), we must look at the signs of +; a1-a0 and b0-b1: if a1-a0 is in fact negative, we must subtract the +; calculated value of b0-b1 from the high word of the calculated product; if +; b0-b1 is in fact negative, we must subtract the calculated value of a1-a0 +; from the high word of the calculated product; and finally we must add 2^64 +; if both were negative. +; +; This last step is awkward. However, note that (a1-a0)*(b0-b1) is actually +; guaranteed to lie in the range -2^64 < x < 2^64, which means that it is +; sufficient to calculate its value modulo 2^64 (i.e. disregarding carries +; out of the high word and the possible addition of 2^64), provided we take +; care to get the sign word right. +; +; We do the 32x32 multiplications by means of standard macros. First +; multiply a1*b1 = OP1mhi*OP2mhi into (OP1sue,Rfpsr). + + Split16 OP1sue,Rfpsr,OP1mhi + Mul64 OP1sue,Rfpsr,OP1sue,Rfpsr,OP2mhi,,,Rarith,Rtmp,Rtmp2 + +; Multiply a0*b0 = OP1mlo*OP2mlo into (Rins,R14). + + Split16 Rins,R14,OP1mlo + Mul64 Rins,R14,Rins,R14,OP2mlo,,,Rarith,Rtmp,Rtmp2 + +; Next, we need to calculate a1*b1*(2^64+2^32) + a0*b0*(2^32+1) +; +; = (2^32+1) * (a1*b1*2^32 + a0*b0) +; +; Note that a1*b1*2^32 + a0*b0 <= (2^32-1)*(2^32-1)*(2^32+1) +; = (2^32-1)*(2^64-1) < 2^96 and that (2^32+1) * (a1*b1*2^32 + a0*b0) +; <= (2^32+1)*(2^32-1)*(2^32-1)*(2^32+1) = (2^64-1)^2 < 2^128, so the +; calculations can be done respectively in 3- and 4-word unsigned +; arithmetic. + + ADDS Rfpsr,Rfpsr,Rins ;Put a1*b1*2^32 + a0*b0 into + ADC OP1sue,OP1sue,#0 ; (OP1sue,Rfpsr,R14) + ADDS Rins,Rfpsr,R14 ;Then multiply by 2^32+1, putting + ADCS Rfpsr,Rfpsr,OP1sue ; result in (OP1sue,Rfpsr,Rins,R14) + ADC OP1sue,OP1sue,#0 + +; Calculate a1-a0 = OP1mhi-OP1mlo into Rtmp, +; b0-b1 = OP2mlo-OP2mhi into Rtmp2, +; addend to high word of calculated (a1-a0)*(b0-b1) product into +; Rarith, and +; correct sign of (a1-a0)*(b0-b1) product into OP1mhi. +; The sign word is 0 for a positive or zero result, &FFFFFFFF for a negative +; result - i.e. it is the word which, when prefixed to the 64-bit product +; calculated otherwise, gives us the true result as a 96-bit signed number. +; Getting this right is slightly tricky, because of the possibilities of +; a1-a0 and b0-b1 being zero and thus invalidating the usual EOR rule about +; the sign. The key to the code below is that if Rtmp = a1-a0 comes out as +; 0, OP1mhi and OP1mlo come out as zero and Rtmp2 never gets set - but this +; last doesn't matter, since zero times anything is zero! +; Note also that we don't care about carries out of the addend, since they +; go into the sign word, which we are getting right by other means. + + SUBS Rtmp,OP1mhi,OP1mlo ;Rtmp := a1-a0 + MOV OP1mhi,#0 ;Sign if a1-a0,b0-b1 both +ve + MOV Rarith,#0 ;Addend if both +ve + MVNLO OP1mhi,OP1mhi ;If a1-a0 -ve, adjust sign and + SUBLO Rarith,OP2mhi,OP2mlo ; addend = -(b0-b1) = b1-b0 + SUBNES Rtmp2,OP2mlo,OP2mhi ;Rtmp2 := b0-b1 + MOVEQ OP1mhi,#0 ;Override sign if b0-b1 = 0 + MVNLO OP1mhi,OP1mhi ;If b0-b1 -ve, adjust sign and + SUBLO Rarith,Rarith,Rtmp ; addend += -(a1-a0) + +; Finish calculating the real value of (a1-a0)*(b0-b1) into +; (OP1mhi,OP1mlo,Rarith). I.e. multiply Rtmp by Rtmp2, adding OP1mlo into the +; high word and putting the result in (OP1mlo,Rarith). OP1mhi is already OK. + + Split16 OP2mhi,OP2mlo,Rtmp + Mul64 OP1mlo,Rarith,OP2mhi,OP2mlo,Rtmp2,Rarith,,Rtmp,Rtmp2,OP1mlo + +; Now add a1*b1*(2^64+2^32) + a0*b0*(2^32+1) and (a1-a0)*(b0-b1)*2^32 +; together, putting the result in (OP1mhi,OP1mlo,Rarith,R14). Note the low +; word is in R14 already. + + ADDS Rarith,Rins,Rarith + ADCS OP1mlo,Rfpsr,OP1mlo + ADCS OP1mhi,OP1sue,OP1mhi + +; Transfer R14 into the sticky bit, without affecting flags. Also make +; certain we don't affect the guard or round bits. + + ORR R14,R14,R14,LSL #2 + ORR Rarith,Rarith,R14,LSR #2 + +; If result is normalised, return. Otherwise normalise by shifting left one +; bit. + + IF Interworking :LOR: Thumbing + LDMMIFD Rsp!,{OP1sue,Rfpsr,Rins,LR} + BXMI LR + ELSE + LDMMIFD Rsp!,{OP1sue,Rfpsr,Rins,PC} + ENDIF + + ADDS Rarith,Rarith,Rarith + ADCS OP1mlo,OP1mlo,OP1mlo + ADC OP1mhi,OP1mhi,OP1mhi + SUB RNDexp,RNDexp,#1 + IF Interworking :LOR: Thumbing + LDMFD Rsp!,{OP1sue,Rfpsr,Rins,LR} + BX LR + ELSE + LDMFD Rsp!,{OP1sue,Rfpsr,Rins,PC} + ENDIF + + +Mult_64x32 + +; To perform this multiplication, we do two 32x32 multiplications, then add +; the results together. We use the standard macros for the purpose. + + Split16 OP2mlo,Rarith,OP2mhi + Mul64 OP2mhi,OP1mhi,OP2mlo,Rarith,OP1mhi,,,Rtmp,Rtmp2,OP2mhi + Mul64 OP2mlo,Rarith,OP2mlo,Rarith,OP1mlo,,,Rtmp,Rtmp2,OP1mlo + ADDS OP1mlo,OP2mlo,OP1mhi + ADCS OP1mhi,OP2mhi,#0 + +; If the top bit was clear, we need to shift the product, round and sticky +; bits left by one bit and decrement the exponent. Otherwise, everything is +; ready for the return. + + IF Interworking :LOR: Thumbing + BXMI LR + ELSE + MOVMI PC,LR + ENDIF + + ADDS Rarith,Rarith,Rarith + ADCS OP1mlo,OP1mlo,OP1mlo + ADC OP1mhi,OP1mhi,OP1mhi + SUB RNDexp,RNDexp,#1 + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + +Mult_32xX + + TEQ OP2mlo,#0 + BEQ Mult_32x32 + +Mult_32x64 + +; To perform this multiplication, we do two 32x32 multiplications, then add +; the results together. We use the standard macros for the purpose. + + Split16 OP1mlo,Rarith,OP1mhi + Mul64 OP1mhi,OP2mhi,OP1mlo,Rarith,OP2mhi,,,Rtmp,Rtmp2,OP1mhi + Mul64 OP1mlo,Rarith,OP1mlo,Rarith,OP2mlo,,,Rtmp,Rtmp2,OP2mlo + ADDS OP1mlo,OP1mlo,OP2mhi + ADCS OP1mhi,OP1mhi,#0 + +; If the top bit was clear, we need to shift the product, round and sticky +; bits left by one bit and decrement the exponent. Otherwise, everything is +; ready for the return. + + IF Interworking :LOR: Thumbing + BXMI LR + ELSE + MOVMI PC,LR + ENDIF + + ADDS Rarith,Rarith,Rarith + ADCS OP1mlo,OP1mlo,OP1mlo + ADC OP1mhi,OP1mhi,OP1mhi + SUB RNDexp,RNDexp,#1 + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + [ FPLibWanted + KEEP |$F__fp_mult_fast_common| +|$F__fp_mult_fast_common| +__fp_mult_fast_common +; This code duplicated from about for the fast case. + AND Rtmp,OP1sue,#ToExp_mask + AND Rtmp2,OP2sue,#ToExp_mask + EOR OP1sue,OP1sue,OP2sue ;Produce result sign + AND OP1sue,OP1sue,#Sign_bit + ADD RNDexp,Rtmp,Rtmp2 + SUB RNDexp,RNDexp,#(EIExp_bias-1):AND:&FF00 + SUB RNDexp,RNDexp,#(EIExp_bias-1):AND:&FF + ASSERT (EIExp_bias-1) < &10000 ;Result exponent if mantissa + ; overflow is exp1+exp2-bias+1 + + ] + +Mult_32x32 + +; Only the high words of the operand mantissas need to be multiplied +; together. Use the standard macros for this purpose. + + Split16 OP2mlo,Rarith,OP2mhi + Mul64 OP1mhi,OP1mlo,OP2mlo,Rarith,OP1mhi,,S,Rtmp,Rtmp2,OP1mhi + +; The round and sticky bits are always going to be zero. + + MOV Rarith,#0 + +; If the top bit was clear, we need to shift the product left one bit and +; decrement the exponent. Otherwise we're done. + + IF Interworking :LOR: Thumbing + BXMI LR + ELSE + MOVMI PC,LR + ENDIF + + ADDS OP1mlo,OP1mlo,OP1mlo + ADC OP1mhi,OP1mhi,OP1mhi + SUB RNDexp,RNDexp,#1 + + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + ] ; Conditional compilation of Mult + +;=========================================================================== + + [ :DEF: div_s :LOR: FPEWanted :LOR: FPASCWanted + +; Routine to divide, reverse-divide, fast-divide or fast-reverse-divide two +; internal format floating point numbers. It has two entry points: "DivFPE", +; which has an optimised fast track for both operands being common, and +; "DivFPASC", which avoids the test for this optimised fast track - since it +; should rarely happen. The second entry point lies a long way down in the +; source to avoid addressing constraints. +; The value returned is either a numeric value plus associated rounding +; information, with the uncommon bit clear, or an infinity or NaN, with the +; uncommon bit set. +; This routine will not work correctly with inputs which are unnormalised +; URD results, or with invalid internal format numbers. +; +; Uses standard dyadic operation entry and exit conventions - see top of +; this file. + + ASSERT RNDexp = OP2sue ;We swap over from the use of OP2sue to that + ; of RNDexp partway through this routine. + + [ FPEWanted + +DivFPE + + CDebug3 3,"DivFPE: op1 =",OP1sue,OP1mhi,OP1mlo + CDebug3 3," op2 =",OP2sue,OP2mhi,OP2mlo + +; Start by detecting the "fast track" case of both operands being common. + + TST OP1sue,#Uncommon_bit + TSTEQ OP2sue,#Uncommon_bit + BNE Div_Uncommon + +; If either operand is a zero, we need to take special action. Because the +; numbers are common and assumed not to be unnormalised URD results, we can +; check for zeros by means of the units bits. + + ANDS Rtmp,OP1mhi,OP2mhi + ASSERT EIUnits_pos = 31 + BPL Div_Zero + +; Both operands may now be assumed to be normalised numbers. We now know +; that we are not going to need to know the operands for trap purposes, so +; we can swap them if this is a normal division rather than a reverse +; division. + + TST Rins,#RevDiv_bit + BNE Div_Common_Swapped + + ] + + [ FPLibWanted +__fp_div_common + ] + + MOV Rtmp,OP1sue + MOV OP1sue,OP2sue + MOV OP2sue,Rtmp + MOV Rtmp,OP1mhi + MOV OP1mhi,OP2mhi + MOV OP2mhi,Rtmp + MOV Rtmp,OP1mlo + MOV OP1mlo,OP2mlo + MOV OP2mlo,Rtmp + + [ FPLibWanted + KEEP |$F__fp_rdv_common| +|$F__fp_rdv_common| +__fp_rdv_common + ] + +Div_Common_Swapped + +; Produce the result sign and the prospective result exponent. + + AND Rtmp,OP1sue,#ToExp_mask + AND Rtmp2,OP2sue,#ToExp_mask + EOR OP1sue,OP1sue,OP2sue ;Produce result sign + AND OP1sue,OP1sue,#Sign_bit + SUB RNDexp,Rtmp2,Rtmp + ADD RNDexp,RNDexp,#EIExp_bias:AND:&FF00 + ADD RNDexp,RNDexp,#EIExp_bias:AND:&FF + ASSERT EIExp_bias < &10000 ;Result exponent if no mantissa + ; underflow is exp1-exp2+bias + +; This subsidiary entry point deals with dividing a normalised mantissa by +; another and adjusting the exponent if necessary. +; Entry: OP1sue = the result's sign, with an uncommon bit of 0 - the +; remaining bits are zero; +; OP1mhi = Divisor mantissa, high word; +; OP1mlo = Divisor mantissa, low word; +; RNDexp = Prospective result exponent, which may be negative; this +; needs to be decremented if mantissa underflow occurs; +; OP2mhi = Dividend mantissa, high word; +; OP2mlo = Dividend mantissa, low word; +; Rins = instruction (needed to determine precision; may be needed +; to discriminate between normal and fast divisions); +; Rwp, Rfp, Rsp hold their usual values; +; R14 = return link. +; Exit: OP1sue = the result's sign, with an uncommon bit of 0; the +; remaining bits are zero; +; OP1mhi, OP1mlo = the result's mantissa; +; RNDexp = the result exponent, which may be negative; +; Rarith holds the round bit (in bit 31) and the sticky bit (in bits +; 30:0) if the destination precision is extended; if the +; destination precision is single or double, it holds part of the +; sticky bit (the remainder of which is held in bits below the +; round bit in OP1mhi and OP1mlo); +; OP2mhi, OP2mlo, Rtmp, Rtmp2 and R14 may be corrupt; +; All other registers preserved. + +Div_Mantissas + + STMFD Rsp!,{OP1sue,OP2sue,Rfpsr,Rins,LR} + + CDebug2 4,"Div_Mantissas: dividend =",OP2mhi,OP2mlo + CDebug2 4," divisor =",OP1mhi,OP1mlo + CDebug1 4," exponent =",RNDexp + +; We will do the mantissa division by an algorithm which is a hybrid between +; Newton-Raphson approximation and ordinary long division: this results in +; division being done to IEEE accuracy, yet more than 50% faster than the +; straightforward long division technique. A summary of the algorithm is: +; +; (a) Use table look-up to get an initial approximation to the reciprocal +; of the divisor; +; +; (b) Use two iterations of Newton-Raphson to improve the reciprocal +; approximation to one with about 15 bits accuracy; +; +; (c) Do long division base 2^13, using the reciprocal approximation to +; determine the result "digits" - which are in fact fixed point +; numbers with 13 bits before the binary point and 3 after it; +; +; (d) Resolve the exact values of the last three bits by ordinary long +; division; +; +; (e) Adjust the exponent and shift the mantissa if mantissa underflow +; occurs, and create the sticky bit. +; +; Exact details of the algorithm appear in comments next to the relevant +; parts of the code below. +; +; The long division is performed for 2 steps for single precision, 4 steps +; for double precision and 5 steps for extended precision, producing 2*13+3 +; = 29, 4*13+3 = 55 and 5*13+3 = 68 bits respectively, plus a sticky bit in +; each case. +; +; Note that this algorithm has been specifically tailored to the software +; environment - e.g. the availability of 32x32->32 bit multiplication and +; the fact that negative partial remainders during the long division will +; cause problems. This leads to some apparently strange bits of code below - +; e.g. getting less accuracy from a Newton-Raphson iteration than might +; appear to be available, in order to preserve knowledge of the sign of the +; error. +; +; In what follows, we will refer to the true mathematical value of the +; dividend mantissa as P, that of the divisor as D, that of the reciprocal +; of the divisor as R and that of the quotient as Q. So Q = P/D = P*R are +; exact mathematical relationships. Also, we have P = (2^32*OP1mhi + +; OP1mlo)*2^(-63), D = (2^32*OP2mhi + OP2mlo)*2^(-63). + +; First step: initialise by breaking the divisor up into 16-bit chunks, +; held in (OP1sue,Rfpsr,Rins,R14). + + Split16 OP1sue,Rfpsr,OP1mhi + Split16 Rins,R14,OP1mlo + +; Second step: use table look-up to get an approximation to R. Specifically, +; we load Rarith with an 8-bit value such that we know: +; +; R <= Rarith*2^(-7) < R + 2^(-6) + + [ CoreDebugging = 0 + ADR Rarith,Recip_Table-128 ;-128 to cancel units bit + | + ADRL Rarith,Recip_Table-128 ;-128 to cancel units bit + ] + LDRB Rarith,[Rarith,OP1sue,LSR #8] + + CDebug1 5,"Table look-up approx'n is",Rarith + +; Third step: use a Newton-Raphson iteration to improve this to an 11-bit +; value in Rarith such that: +; +; R < Rarith*2^(-10) < R + 2^(-9) +; +; Details: Let W be the current value of Rarith, so we have: +; +; R <= W*2^(-7) < R + 2^(-6) +; +; Let X be the first 16 bits of D (i.e. OP1sue), incremented by 1. This has +; the property that: +; +; D < X*2^(-15) <= D + 2^(-15) +; +; Suppose further that W*2^(-7) = R+e, with 0 <= e < 2^(-6), and X*2^(-15) = +; D+f, with 0 < f <= 2^(-15). +; +; Now let Y = W * (2^23 - X*W), which is a calculation that can be performed +; without overflowing a word. This is equivalent to: +; +; Y*2^(-29) = W*2^(-7) * (2 - X*2^(-15) * W*2^(-7)) +; +; = (R+e) * (2 - (D+f)*(R+e)) +; +; = (R+e) * (2 - (1 + D*e + R*f + e*f)), since D*R=1 exactly, +; +; = (R+e) * (1 - D*e - R*f - e*f) +; +; = R + e - e - D*e*e - R*R*f - R*e*f - R*e*f - e*e*f, since D*R=1, +; +; = R - D*e*e - R*R*f - 2*R*e*f - e*e*f +; +; Since R > 0, D > 0, e >= 0 and f > 0, this is clearly less than R. On the +; other hand, we know that R <= 1, D < 2, e < 2^(-6) and f <= 2^(-15). So: +; +; R > Y*2^(-29) +; > R - 2^(-11) - 2^(-15) - 2^(-20) - 2^(-27) +; +; Now let Z be Y shifted right 19 bits. This gives us: +; +; Y*2^(-29) - 2^(-10) < Z*2^(-10) <= Y*2^(-29) +; +; Combining the inequalities, we get: +; +; R - 2^(-9) < R - 2^(-11) - 2^(-15) - 2^(-20) - 2^(-27) - 2^(-10) +; < Y*2^(-29) - 2^(-10) +; < Z*2^(-10) +; <= Y*2^(-29) +; < R +; +; So if we put Rarith = Z+2, we get: +; +; R < Rarith*2^(-10) < R + 2^(-9), +; +; as desired. + + MLA Rtmp,OP1sue,Rarith,Rarith ;Rtmp := (X-1)*W + W = X*W + RSB Rtmp,Rtmp,#1:SHL:23 ;Rtmp := 2^23 - X*W + MUL Rarith,Rtmp,Rarith ;Rarith := W*(2^23 - X*W) = Y + MOV Rarith,Rarith,LSR #19 ;Shift right 19 bits and add + ADD Rarith,Rarith,#2 ; 2 to get new approximation + + CDebug1 5,"First N-R approx'n is",Rarith + +; Fourth step: use a Newton-Raphson iteration to improve this to a 16-bit +; value in Rarith such that: +; +; R - 2^(-15) < Rarith*2^(-16) < R +; +; Details: Let W be the current value of Rarith, so we have: +; +; R < W*2^(-10) < R + 2^(-9) +; +; Let X be the first 19 bits of D (i.e. the top 19 bits of OP1mhi), +; incremented by 1. This has the property that: +; +; D < X*2^(-18) <= D + 2^(-18) +; +; Suppose further that W*2^(-10) = R+e, with 0 < e < 2^(-9), and X*2^(-18) +; = D+f, with 0 < f <= 2^(-18). +; +; Now let Y = W * (2^29 - X*W): part of this calculation will require 2-word +; arithmetic. This is equivalent to: +; +; Y*2^(-38) = W*2^(-10) * (2 - X*2^(-18) * W*2^(-10)) +; +; = (R+e) * (2 - (D+f)*(R+e)) +; +; = R - D*e*e - R*R*f - 2*R*e*f - e*e*f, as in the third step. +; +; Since R > 0, D > 0, e >= 0 and f > 0, this is clearly less than R. On the +; other hand, we know that R <= 1, D < 2, e < 2^(-9) and f <= 2^(-18). So: +; +; R > Y*2^(-38) +; > R - 2^(-17) - 2^(-18) - 2^(-26) - 2^(-36) +; +; Now let Z be Y shifted right 22 bits. This gives us: +; +; Y*2^(-38) - 2^(-16) < Z*2^(-16) <= Y*2^(-38) +; +; Combining the inequalities, we get: +; +; R - 2^(-15) < R - 2^(-17) - 2^(-18) - 2^(-26) - 2^(-36) - 2^(-16) +; < Y*2^(-38) - 2^(-16) +; < Z*2^(-16) +; <= Y*2^(-38) +; < R +; +; So if we put Rarith = Z, we get the desired inequality. + + MOV Rtmp,OP1mhi,LSR #13 ;Rtmp := X-1 + MLA Rtmp2,Rtmp,Rarith,Rarith ;Rtmp2 := (X-1)*W + W = X*W + RSB Rtmp2,Rtmp2,#1:SHL:29 ;Rtmp2 := 2^29 - X*W + Split16 Rtmp,Rtmp2,Rtmp2 ;Rtmp/Rtmp2 := top/bottom half + MUL OP1mlo,Rtmp2,Rarith ;OP1mhi, OP1mlo := two + MUL OP1mhi,Rtmp,Rarith ; parts of product with W + ADD Rarith,OP1mhi,OP1mlo,LSR #16 ;Rarith := Y >> 16 + MOV Rarith,Rarith,LSR #6 ;Rarith := Y >> 22 + + CDebug1 5,"Second N-R approx'n is",Rarith + +; Fifth step: initialise the partial remainder - its binary point lies to +; the right of bit 30 of its top word to line up well with the results of +; later multiplications. + + MOVS OP2mhi,OP2mhi,LSR #1 + MOVS OP2mlo,OP2mlo,RRX + MOVCC OP2sue,#0 + MOVCS OP2sue,#TopBit + +; Sixth step: do the first iteration of the long division process. The +; register allocation during this is: +; +; OP1sue, Rfpsr, Rins, R14: Divisor, in 16-bit chunks; its binary point is +; considered to lie to the right of bit 15 of +; OP1sue; +; OP1mhi, OP1mlo: Quotient so far (Rarith joins into this near the +; end of the calculation); its binary point is +; considered to lie to the right of bit 31 of +; OP1mhi; +; OP2mhi, OP2mlo, OP2sue: Partial remainder; its binary point is +; considered to lie to the right of bit 30 of +; OP2mhi; +; Rarith: 16-bit reciprocal approximation, until near the +; end of the calculation; its binary point lies to +; the *left* of bit 15; +; Rtmp, Rtmp2: Temporaries. +; +; Some of these registers (OP1mhi and OP1mlo) only become set some way into +; the calculation: until they do become set, they should be regarded as +; being 0. +; +; The details of iteration N (for N=0 to 4) of the long division process +; are: +; +; Let D be the divisor represented by (OP1sue,Rfpsr,Rins,R14), and let R = +; 1/D be its reciprocal. Let A be the reciprocal approximation represented +; by Rarith from now until near the end of the calculation - i.e. A = +; Rarith*2^(-16). We know that: +; +; 1 <= D < 2; +; 0.5 < R <= 1; +; R-2^(-15) < A < R +; +; Let Q[N] be the quotient represented by those of OP1mhi, OP1mlo and Rarith +; that have become set at the end of iteration N-1/start of iteration N - +; i.e.: +; +; Q[0] = 0; +; Q[1],Q[2] = (OP1mhi at appropriate time) * 2^(-31); +; Q[3],Q[4] = (OP1mhi at appropriate time) * 2^(-31) +; + (OP1mlo at appropriate time) * 2^(-63); +; Q[5] = (OP1mhi at appropriate time) * 2^(-31) +; + (OP1mlo at appropriate time) * 2^(-63) +; + (Rarith at appropriate time) * 2^(-95); +; +; Let P[N] be the partial remainder represented by those of OP2mhi, OP2mlo +; and OP2sue that have become set at the end of iteration N-1/start of +; iteration N - i.e.: +; +; P[i] = (OP2mhi at appropriate time) * 2^(-30) +; + (OP2mlo at appropriate time) * 2^(-62) +; + (OP2sue at appropriate time) * 2^(-94); +; +; Finally, let P be the original dividend - i.e. P is the current value of +; OP2mhi*2^(-31) + OP2mlo*2^(-63). +; +; For i=0, we can clearly make the following three statements: +; +; (a) Q[i] is a multiple of 2^(-13*i-2); +; +; (b) P[i] is a multiple of 2^(-65); +; +; (c) P = Q[i]*D + P[i]*2^(-13*i); +; +; (d) 0 < P[i] < 2; +; +; since Q[0] = 0 and P[0] = P. The algorithm will result in the same +; statements being true for i = 1, 2, 3, 4 and 5 as well. +; +; Iteration i of the algorithm is: +; +; Papprox = P[i], rounded down to a multiple of 2^(-15); +; digit = Papprox * A, rounded down to a multiple of 2^(-15); +; P[i+1] = (P[i] - digit*D) * 2^13 +; Q[i+1] = Q[i] + digit*2^(-13*i) +; +; Proof that the three statements above are true for all i: we will do this +; by induction. We already know that they are true for i=0. So suppose they +; are true for i=N. Then: +; +; (a) Q[i+1] = Q[i] + digit*2^(-13*i) +; = (multiple of 2^(-13*i-2)) + (multiple of 2^(-15))*2^(-13*i) +; = multiple of 2^(-13*i-15) +; = multiple of 2^(-13*(i+1)-2). +; +; (b) P[i+1] = (P[i] - digit*D) * 2^13 +; = 2^13 * (multiple of 2^(-65) +; - (multiple of 2^(-15)) * (multiple of 2^(-63))) +; = multiple of 2^(-65). +; +; (c) P = Q[i]*D + P[i]*2^(-13*i) +; = (Q[i+1] - digit*2^(-13*i)) * D +; + (P[i+1]*2^(-13) + digit*D) * 2^(-13*i) +; = Q[i+1]*D + P[i+1]*2^(-13*i-13) +; = Q[i+1]*D + P[i+1]*2^(-13*(i+1)). +; +; (d) First, since Papprox = P[i] rounded down to a multiple of 2^(-15) and +; R-2^(-15) < A < R, we have Papprox = P[i]-e and A = R-f, where 0 <= e +; < 2^(-15) and 0 < f < 2^(-15). Then, since digit = Papprox * A rounded +; down to a multiple of 2^(-15), we have digit = Papprox * A - g, where +; 0 <= g < 2^(-15). Putting these together, we have: +; +; digit = (P[i]-e)*(R-f) - g +; = P[i]*R - P[i]*f - e*R + e*f - g +; +; Since everything is non-negative, 'digit' is clearly at most P[i]*R. +; Conversely, since P[i] < 2, R <= 1, e < 2^(-15), f < 2^(-15) and g < +; 2^(-15), we have: +; +; P[i]*R > digit +; > P[i]*R - 2*2^(-15) - 2^(-15)*1 - 2^(-15) +; = P[i]*R - 2^(-13) +; +; Or: +; +; 0 < P[i]*R - digit < 2^(-13) +; +; Multiplying by D, which is known to satisfy 1 <= D < 2: +; +; 0 < P[i] - digit*D < 2^(-12) +; +; Multiplying by 2^(13): +; +; 0 < P[i+1] < 2 +; +; Notes: +; +; (1) The subtraction to create P[i] is done by subtracting the four 16x16 +; products formed from the digit and the 16-bit chunks of the divisor +; from the partial remainder. Two of these 32-bit products are aligned +; with the partial remainder and thus don't cause any problems. The +; other two are both mis-aligned by 16 bits. One way to subtract them +; would be to do a double word shift on them and subtract the results +; from the partial remainder: this takes 2 instructions to form the +; central shifted word and 3 for the subtraction (two of which are +; "shift and subtracts"). However, this makes use of one register more +; than we have. So the code below makes use of a trick, based on the +; fact that if we subtract the top 16 bits and the bottom 16 bits of the +; central shifted word separately, only one of the subtractions can +; cause a borrow. So if we've got a borrow after the first one, we do +; the second one without setting the condition codes, knowing that it +; won't cause a borrow; if we don't, we set the condition codes on the +; result of the second subtraction. +; +; (2) The multiplication operands are generally ordered to maximise the +; chance of early termination. This means that all but the top chunk of +; the divisor are good second operands to the multiplication, the digit +; is next best, and the top chunk of the divisor is the least good. +; +; (3) The above is in fact not exactly true, due to the fact that it saves +; some cycles not to shift P[1] and P[3] left by 13 bits, but to wait +; until P[2] and P[4] are generated, then shift them left 26 bits. + + MOV Rtmp,OP2mhi,LSR #15 ;Rtmp := Papprox + MUL Rtmp2,Rarith,Rtmp ;Rtmp2 := Papprox * A + MOV Rtmp2,Rtmp2,LSR #16 ;Rtmp2 := digit + MUL Rtmp,Rtmp2,Rins ;Subtract digit*D from P[0] to + SUBS OP2mlo,OP2mlo,Rtmp ; form P[1]*2^(-13) - this requires + MUL Rtmp,OP1sue,Rtmp2 ; 4 multiplications and subtractions + SBC OP2mhi,OP2mhi,Rtmp ; at various alignments + MUL Rtmp,Rtmp2,R14 + SUBS OP2sue,OP2sue,Rtmp,LSL #16 + SBCS OP2mlo,OP2mlo,Rtmp,LSR #16 + MUL Rtmp,Rtmp2,Rfpsr + SUBCC OP2mlo,OP2mlo,Rtmp,LSL #16 ;Already got a borrow + SUBCSS OP2mlo,OP2mlo,Rtmp,LSL #16 ;No borrow yet - try for one + SBC OP2mhi,OP2mhi,Rtmp,LSR #16 + MOV OP1mhi,Rtmp2,LSL #16 ;OP1mhi := Q[1] + + CDebug1 5,"1st iter'n: quotient so far =",OP1mhi + CDebug3 5," partial remainder =",OP2mhi,OP2mlo,OP2sue + +; Seventh step: second iteration. At the end of this step, we check whether +; the multiplication is single precision and branch out to termination code +; if so. + + MOV Rtmp,OP2mhi,LSR #2 ;Rtmp := Papprox + MUL Rtmp2,Rarith,Rtmp ;Rtmp2 := Papprox * A + MOV Rtmp2,Rtmp2,LSR #16 ;Rtmp2 := digit + MUL Rtmp,Rtmp2,Rins ;Subtract digit*D from P[1]*2^(-13) + SUBS OP2sue,OP2sue,Rtmp,LSL #19 ; to form P[2]*2^(-26) - this + SBCS OP2mlo,OP2mlo,Rtmp,LSR #13 ; requires 4 multiplications and + MUL Rtmp,OP1sue,Rtmp2 ; subtractions at various alignments + SUBCC OP2mlo,OP2mlo,Rtmp,LSL #19 ;Already got a borrow + SUBCSS OP2mlo,OP2mlo,Rtmp,LSL #19 ;No borrow yet - try for one + SBC OP2mhi,OP2mhi,Rtmp,LSR #13 + MUL Rtmp,Rtmp2,R14 + SUBS OP2sue,OP2sue,Rtmp,LSL #3 + SBCS OP2mlo,OP2mlo,Rtmp,LSR #29 + MUL Rtmp,Rtmp2,Rfpsr + SUBCC OP2mlo,OP2mlo,Rtmp,LSL #3 ;Already got a borrow + SUBCSS OP2mlo,OP2mlo,Rtmp,LSL #3 ;No borrow yet - try for one + SBC OP2mhi,OP2mhi,Rtmp,LSR #29 + MOV OP2mhi,OP2mhi,LSL #26 ;Shift by 26 bits to form P[2] + ORR OP2mhi,OP2mhi,OP2mlo,LSR #6 + MOV OP2mlo,OP2mlo,LSL #26 + ORR OP2mlo,OP2mlo,OP2sue,LSR #6 + MOV OP2sue,OP2sue,LSL #26 + ADD OP1mhi,OP1mhi,Rtmp2,LSL #3 ;OP1mhi := Q[2] + + CDebug1 5,"2nd iter'n: quotient so far =",OP1mhi + CDebug3 5," partial remainder =",OP2mhi,OP2mlo,OP2sue + + LDR Rtmp,[Rsp,#12] ;Recover instruction + + [ FPEWanted :LOR: FPASCWanted + + TST Rtmp,#Pr1_mask ;Check for single precision + TSTEQ Rtmp,#Pr2_mask + BEQ Div_Single + + | + + TST Rtmp,#Single_mask ;Use a simpler encoding + BNE Div_Single + + ] + +; Eighth step: third iteration. + + MOV Rtmp,OP2mhi,LSR #15 ;Rtmp := Papprox + MUL Rtmp2,Rarith,Rtmp ;Rtmp2 := Papprox * A + MOV Rtmp2,Rtmp2,LSR #16 ;Rtmp2 := digit + MUL Rtmp,Rtmp2,Rins ;Subtract digit*D from P[2] to + SUBS OP2mlo,OP2mlo,Rtmp ; form P[3]*2^(-13) - this requires + MUL Rtmp,OP1sue,Rtmp2 ; 4 multiplications and subtractions + SBC OP2mhi,OP2mhi,Rtmp ; at various alignments + MUL Rtmp,Rtmp2,R14 + SUBS OP2sue,OP2sue,Rtmp,LSL #16 + SBCS OP2mlo,OP2mlo,Rtmp,LSR #16 + MUL Rtmp,Rtmp2,Rfpsr + SUBCC OP2mlo,OP2mlo,Rtmp,LSL #16 ;Already got a borrow + SUBCSS OP2mlo,OP2mlo,Rtmp,LSL #16 ;No borrow yet - try for one + SBC OP2mhi,OP2mhi,Rtmp,LSR #16 + MOV OP1mlo,Rtmp2,LSL #22 ;(OP1mhi,OP1mlo) := Q[3] + ADD OP1mhi,OP1mhi,Rtmp2,LSR #10 + + CDebug2 5,"3rd iter'n: quotient so far =",OP1mhi,OP1mlo + CDebug3 5," partial remainder =",OP2mhi,OP2mlo,OP2sue + +; Ninth step: fourth iteration. At the end of this step, we check whether +; the multiplication is double precision and branch out to termination code +; if so. + + MOV Rtmp,OP2mhi,LSR #2 ;Rtmp := Papprox + MUL Rtmp2,Rarith,Rtmp ;Rtmp2 := Papprox * A + MOV Rtmp2,Rtmp2,LSR #16 ;Rtmp2 := digit + MUL Rtmp,Rtmp2,Rins ;Subtract digit*D from P[3]*2^(-13) + SUBS OP2sue,OP2sue,Rtmp,LSL #19 ; to form P[4]*2^(-26) - this + SBCS OP2mlo,OP2mlo,Rtmp,LSR #13 ; requires 4 multiplications and + MUL Rtmp,OP1sue,Rtmp2 ; subtractions at various alignments + SUBCC OP2mlo,OP2mlo,Rtmp,LSL #19 ;Already got a borrow + SUBCSS OP2mlo,OP2mlo,Rtmp,LSL #19 ;No borrow yet - try for one + SBC OP2mhi,OP2mhi,Rtmp,LSR #13 + MUL Rtmp,Rtmp2,R14 + SUBS OP2sue,OP2sue,Rtmp,LSL #3 + SBCS OP2mlo,OP2mlo,Rtmp,LSR #29 + MUL Rtmp,Rtmp2,Rfpsr + SUBCC OP2mlo,OP2mlo,Rtmp,LSL #3 ;Already got a borrow + SUBCSS OP2mlo,OP2mlo,Rtmp,LSL #3 ;No borrow yet - try for one + SBC OP2mhi,OP2mhi,Rtmp,LSR #29 + MOV OP2mhi,OP2mhi,LSL #26 ;Shift by 26 bits to form P[4] + ORR OP2mhi,OP2mhi,OP2mlo,LSR #6 + MOV OP2mlo,OP2mlo,LSL #26 + ORR OP2mlo,OP2mlo,OP2sue,LSR #6 + MOV OP2sue,OP2sue,LSL #26 + ADDS OP1mlo,OP1mlo,Rtmp2,LSL #9 ;(OP1mhi,OP1mlo) := Q[4] + ADC OP1mhi,OP1mhi,#0 + + CDebug2 5,"4th iter'n: quotient so far =",OP1mhi,OP1mlo + CDebug3 5," partial remainder =",OP2mhi,OP2mlo,OP2sue + + LDR Rtmp,[Rsp,#12] ;Recover instruction + + [ FPEWanted :LOR: FPASCWanted + + TST Rtmp,#Pr1_mask ;Check for double precision + BEQ Div_Double + + | + + TST Rtmp,#Double_mask + BNE Div_Double + + ] + +; Tenth step: fifth iteration. We can enter the extended precision +; termination code at the end of this iteration, since we know it must be an +; extended precision division. + + MOV Rtmp,OP2mhi,LSR #15 ;Rtmp := Papprox + MUL Rtmp2,Rarith,Rtmp ;Rtmp2 := Papprox * A + MOV Rtmp2,Rtmp2,LSR #16 ;Rtmp2 := digit + MUL Rtmp,Rtmp2,Rins ;Subtract digit*D from P[4] to + SUBS OP2mlo,OP2mlo,Rtmp ; form P[5]*2^(-13) - this requires + MUL Rtmp,OP1sue,Rtmp2 ; 4 multiplications and subtractions + SBC OP2mhi,OP2mhi,Rtmp ; at various alignments + MUL Rtmp,Rtmp2,R14 + SUBS OP2sue,OP2sue,Rtmp,LSL #16 + SBCS OP2mlo,OP2mlo,Rtmp,LSR #16 + MUL Rtmp,Rtmp2,Rfpsr + SUBCC OP2mlo,OP2mlo,Rtmp,LSL #16 ;Already got a borrow + SUBCSS OP2mlo,OP2mlo,Rtmp,LSL #16 ;No borrow yet - try for one + SBC OP2mhi,OP2mhi,Rtmp,LSR #16 + MOV OP2mhi,OP2mhi,LSL #14 ;Shift by 14 bits to form 2*P[5] + ORR OP2mhi,OP2mhi,OP2mlo,LSR #18 + MOV OP2mlo,OP2mlo,LSL #14 + ORR OP2mlo,OP2mlo,OP2sue,LSR #18 + MOV OP2sue,OP2sue,LSL #14 + MOV Rarith,Rtmp2,LSL #28 ;(OP1mhi,OP1mlo,Rarith) := Q[5] + ADDS OP1mlo,OP1mlo,Rtmp2,LSR #4 + ADC OP1mhi,OP1mhi,#0 + + CDebug3 5,"5th iter'n: quotient so far =",OP1mhi,OP1mlo,Rarith + CDebug3 5," partial remainder =",OP2mhi,OP2mlo,OP2sue + +Div_Extended + +; We've completed the main work for an extended precision division. We've +; now got the divisor D in (OP1sue,Rfpsr,Rins,R14), the quotient Q[5] in +; (OP1mhi,OP1mlo,Rarith) and twice the partial remainder P[5] in +; (OP2mhi,OP2mlo,OP2sue) such that: +; +; (a) Q[5] is a multiple of 2^(-67); +; +; (b) P[5] is a multiple of 2^(-65); +; +; (c) P = Q[5]*D + P[5]*2^(-65); +; +; (d) 0 < P[5] < 2; +; +; The main problem with this is that P[5]*2^(-65) may be almost 2^(-64), +; while Q[5] is a multiple of 2^(-67). To know the correct IEEE answer, we +; have to make the partial remainder be less than the "quantum" in the +; quotient - i.e. less than 2^(-67) in this case. Without doing this, we +; can't calculate the sticky bit accurately: we know that a non-zero partial +; remainder at this point represents a string of quotient bits which are not +; all zero, but if they overlap the quotient bits we've already calculated, +; we don't know whether adding the bits together in the area of overlap +; would result in a string of all zero bits and thus a sticky bit of 0. +; +; We deal with this by doing three bits worth of ordinary long division. To +; save on multi-word additions and problems about carry flag use, we put the +; bits calculated into R14 and only add them into the quotient once at the +; end. +; +; Note that generating twice P[5] above with the binary point to the right +; of bit 30 of OP2mhi is equivalent to generating P[5] with the binary point +; to the right of bit 31 - i.e. to generating it in the position we want it +; to be for the code that follows. This is a trick we only use for extended +; precision, since for the other precisions, we need to be ready for another +; iteration of the algorithm above as well as for the termination code. + + ORR OP1sue,Rfpsr,OP1sue,LSL #16 ;Reform divisor + ORR Rfpsr,R14,Rins,LSL #16 + + MOV R14,#0 ;Initialise extra bits + + SUBS Rtmp2,OP2mlo,Rfpsr ;First extra bit: trial subtraction + SBCS Rtmp,OP2mhi,OP1sue ; of divisor from partial remainder + MOVCS OP2mlo,Rtmp2 ;If bit is 1, really do subtraction + MOVCS OP2mhi,Rtmp + ADC R14,R14,R14 ;Accumulate bit + + MOV Rins,#0 ;Initialise overflow word + ADDS OP2sue,OP2sue,OP2sue ;Second extra bit: shift partial + ADCS OP2mlo,OP2mlo,OP2mlo ; remainder + ADCS OP2mhi,OP2mhi,OP2mhi + ADC Rins,Rins,Rins + SUBS Rtmp2,OP2mlo,Rfpsr ;Trial subtraction of divisor + SBCS Rtmp,OP2mhi,OP1sue ; from partial remainder + SBCS Rins,Rins,#0 + MOVCS OP2mlo,Rtmp2 ;If bit is 1, really do subtraction + MOVCS OP2mhi,Rtmp + ADC R14,R14,R14 ;Accumulate bit + + MOV Rins,#0 ;Initialise overflow word + ADDS OP2sue,OP2sue,OP2sue ;Third extra bit: shift partial + ADCS OP2mlo,OP2mlo,OP2mlo ; remainder + ADCS OP2mhi,OP2mhi,OP2mhi + ADC Rins,Rins,Rins + SUBS Rtmp2,OP2mlo,Rfpsr ;Trial subtraction of divisor + SBCS Rtmp,OP2mhi,OP1sue ; from partial remainder + SBCS Rins,Rins,#0 + MOVCS OP2mlo,Rtmp2 ;If bit is 1, really do subtraction + MOVCS OP2mhi,Rtmp + ADC R14,R14,R14 ;Accumulate bit + + CDebug1 5,"Extra bits to add in are",R14 + +; (OP1mhi,OP1mlo,Rarith) now contains 68 bits of quotient, R14 three extra +; bits that need to be added into its low end and (OP2mhi,OP2mlo) the final +; partial remainder. (We've shifted all the extra bits out of OP2sue, and the +; overflow word Rins must be zero at this point.) +; This is enough bits to provide guard and round bits, plus 2 bits +; contributing to the sticky bit and enough information to complete +; generating it. We will finish generating it by setting bit 0 of Rarith if +; the partial remainder is non-zero. + + ORRS Rtmp,OP2mhi,OP2mlo + ORRNE Rarith,Rarith,#1 + +; Now add the three extra bits into the quotient and test for mantissa +; underflow. + + ADDS Rarith,Rarith,R14,LSL #28 ;Add extra bits into quotient + ADCS OP1mlo,OP1mlo,#0 + ADCS OP1mhi,OP1mhi,#0 + +; If no mantissa underflow, we're ready to return. Otherwise, we must +; recover the spilled registers (to get hold of the result exponent), shift +; the mantissa left one bit, decrement the exponent and return. + + IF Interworking :LOR: Thumbing + LDMMIFD Rsp!,{OP1sue,OP2sue,Rfpsr,Rins,LR} + BXMI LR + ELSE + LDMMIFD Rsp!,{OP1sue,OP2sue,Rfpsr,Rins,PC} + ENDIF + + LDMFD Rsp!,{OP1sue,OP2sue,Rfpsr,Rins,LR} + ADDS Rarith,Rarith,Rarith + ADCS OP1mlo,OP1mlo,OP1mlo + ADC OP1mhi,OP1mhi,OP1mhi + SUB OP2sue,OP2sue,#1 + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +Div_Double + +; We've completed the main work for a double precision division. We've now +; got the divisor D in (OP1sue,Rfpsr,Rins,R14), the quotient Q[4] in +; (OP1mhi,OP1mlo,Rarith) and the partial remainder P[4] in +; (OP2mhi,OP2mlo,OP2sue) such that: +; +; (a) Q[4] is a multiple of 2^(-54); +; +; (b) P[4] is a multiple of 2^(-65); +; +; (c) P = Q[4]*D + P[4]*2^(-52); +; +; (d) 0 < P[4] < 2; +; +; The main problem with this is that P[4]*2^(-52) may be almost 2^(-51), +; while Q[4] is a multiple of 2^(-54). To know the correct IEEE answer, we +; have to make the partial remainder be less than the "quantum" in the +; quotient - i.e. less than 2^(-54) in this case. Without doing this, we +; can't calculate the sticky bit accurately: we know that a non-zero partial +; remainder at this point represents a string of quotient bits which are not +; all zero, but if they overlap the quotient bits we've already calculated, +; we don't know whether adding the bits together in the area of overlap +; would result in a string of all zero bits and thus a sticky bit of 0. +; +; We deal with this by doing three bits worth of ordinary long division. To +; save on multi-word additions and problems about carry flag use, we put the +; bits calculated into R14 and only add them into the quotient once at the +; end. + + ORR OP1sue,Rfpsr,OP1sue,LSL #16 ;Reform divisor + ORR Rfpsr,R14,Rins,LSL #16 + + MOV R14,#0 ;Initialise extra bits + + ADDS OP2sue,OP2sue,OP2sue ;First extra bit: shift partial + ADCS OP2mlo,OP2mlo,OP2mlo ; remainder + ADC OP2mhi,OP2mhi,OP2mhi + SUBS Rtmp2,OP2mlo,Rfpsr ;Trial subtraction of divisor from + SBCS Rtmp,OP2mhi,OP1sue ; partial remainder + MOVCS OP2mlo,Rtmp2 ;If bit is 1, really do subtraction + MOVCS OP2mhi,Rtmp + ADC R14,R14,R14 ;Accumulate bit + + MOV Rins,#0 ;Initialise overflow word + ADDS OP2sue,OP2sue,OP2sue ;Second extra bit: shift partial + ADCS OP2mlo,OP2mlo,OP2mlo ; remainder + ADCS OP2mhi,OP2mhi,OP2mhi + ADC Rins,Rins,Rins + SUBS Rtmp2,OP2mlo,Rfpsr ;Trial subtraction of divisor + SBCS Rtmp,OP2mhi,OP1sue ; from partial remainder + SBCS Rins,Rins,#0 + MOVCS OP2mlo,Rtmp2 ;If bit is 1, really do subtraction + MOVCS OP2mhi,Rtmp + ADC R14,R14,R14 ;Accumulate bit + + MOV Rins,#0 ;Initialise overflow word + ADDS OP2sue,OP2sue,OP2sue ;Third extra bit: shift partial + ADCS OP2mlo,OP2mlo,OP2mlo ; remainder + ADCS OP2mhi,OP2mhi,OP2mhi + ADC Rins,Rins,Rins + SUBS Rtmp2,OP2mlo,Rfpsr ;Trial subtraction of divisor + SBCS Rtmp,OP2mhi,OP1sue ; from partial remainder + SBCS Rins,Rins,#0 + MOVCS OP2mlo,Rtmp2 ;If bit is 1, really do subtraction + MOVCS OP2mhi,Rtmp + ADC R14,R14,R14 ;Accumulate bit + + CDebug1 5,"Extra bits to add in are",R14 + +; (OP1mhi,OP1mlo) now contains 55 bits of quotient, R14 three extra bits +; that need to be added into its low end and (OP2mhi,OP2mlo) the final +; partial remainder. (We've shifted all the extra bits out of OP2sue, and +; the overflow word Rins must be zero at this point.) +; This is enough bits to provide guard and round bits, plus enough +; information to generate the sticky bit. We do this by setting Rarith to +; zero if the partial remainder is zero, non-zero if the partial remainder +; is non-zero. Note that since we know rounding will take place to double +; precision, we don't mind having the sticky bit overflow into the extended +; precision round bit. + + ORR Rarith,OP2mhi,OP2mlo + +; Now add the three extra bits into the quotient and test for mantissa +; underflow. + + ADDS OP1mlo,OP1mlo,R14,LSL #9 ;Add extra bits into quotient + ADCS OP1mhi,OP1mhi,#0 + +; If no mantissa underflow, we're ready to return. Otherwise, we must +; recover the spilled registers (to get hold of the result exponent), shift +; the mantissa left one bit, decrement the exponent and return. + + IF Interworking :LOR: Thumbing + LDMMIFD Rsp!,{OP1sue,OP2sue,Rfpsr,Rins,LR} + BXMI LR + ELSE + LDMMIFD Rsp!,{OP1sue,OP2sue,Rfpsr,Rins,PC} + ENDIF + + LDMFD Rsp!,{OP1sue,OP2sue,Rfpsr,Rins,LR} + ADDS OP1mlo,OP1mlo,OP1mlo + ADC OP1mhi,OP1mhi,OP1mhi + SUB OP2sue,OP2sue,#1 + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +Div_Single + +; We've completed the main work for a single precision division. We've now +; got the divisor D in (OP1sue,Rfpsr,Rins,R14), the quotient Q[2] in +; (OP1mhi,OP1mlo,Rarith) and the partial remainder P[2] in +; (OP2mhi,OP2mlo,OP2sue) such that: +; +; (a) Q[2] is a multiple of 2^(-28); +; +; (b) P[2] is a multiple of 2^(-65); +; +; (c) P = Q[2]*D + P[2]*2^(-26); +; +; (d) 0 < P[2] < 2; +; +; The main problem with this is that P[2]*2^(-26) may be almost 2^(-25), +; while Q[2] is a multiple of 2^(-28). To know the correct IEEE answer, we +; have to make the partial remainder be less than the "quantum" in the +; quotient - i.e. less than 2^(-28) in this case. Without doing this, we +; can't calculate the sticky bit accurately: we know that a non-zero partial +; remainder at this point represents a string of quotient bits which are not +; all zero, but if they overlap the quotient bits we've already calculated, +; we don't know whether adding the bits together in the area of overlap +; would result in a string of all zero bits and thus a sticky bit of 0. +; +; We deal with this by doing three bits worth of ordinary long division. + + ORR OP1sue,Rfpsr,OP1sue,LSL #16 ;Reform divisor + ORR Rfpsr,R14,Rins,LSL #16 + + ADDS OP2sue,OP2sue,OP2sue ;First extra bit: shift partial + ADCS OP2mlo,OP2mlo,OP2mlo ; remainder + ADC OP2mhi,OP2mhi,OP2mhi + SUBS Rtmp2,OP2mlo,Rfpsr ;Trial subtraction of divisor from + SBCS Rtmp,OP2mhi,OP1sue ; partial remainder + MOVCS OP2mlo,Rtmp2 ;If bit is 1, really do subtraction + MOVCS OP2mhi,Rtmp + ADDCS OP1mhi,OP1mhi,#1:SHL:5 ;Add bit to quotient + + MOV Rins,#0 ;Initialise overflow word + ADDS OP2sue,OP2sue,OP2sue ;Second extra bit: shift partial + ADCS OP2mlo,OP2mlo,OP2mlo ; remainder + ADCS OP2mhi,OP2mhi,OP2mhi + ADC Rins,Rins,Rins + SUBS Rtmp2,OP2mlo,Rfpsr ;Trial subtraction of divisor + SBCS Rtmp,OP2mhi,OP1sue ; from partial remainder + SBCS Rins,Rins,#0 + MOVCS OP2mlo,Rtmp2 ;If bit is 1, really do subtraction + MOVCS OP2mhi,Rtmp + ADDCS OP1mhi,OP1mhi,#1:SHL:4 ;Add bit to quotient + + MOV Rins,#0 ;Initialise overflow word + ADDS OP2sue,OP2sue,OP2sue ;Third extra bit: shift partial + ADCS OP2mlo,OP2mlo,OP2mlo ; remainder + ADCS OP2mhi,OP2mhi,OP2mhi + ADC Rins,Rins,Rins + SUBS Rtmp2,OP2mlo,Rfpsr ;Trial subtraction of divisor + SBCS Rtmp,OP2mhi,OP1sue ; from partial remainder + SBCS Rins,Rins,#0 + MOVCS OP2mlo,Rtmp2 ;If bit is 1, really do subtraction + MOVCS OP2mhi,Rtmp + ADDCS OP1mhi,OP1mhi,#1:SHL:3 ;Add bit to quotient + + CDebug1 5,"Quotient after adding in extra bits is",R14 + +; (OP1mhi,OP1mlo,Rarith) now contains 29 bits of quotient and (OP2mhi,OP2mlo) +; the final partial remainder. (We've shifted all the extra bits out of +; OP2sue, and the overflow word Rins must be zero at this point.) +; This is enough bits to provide guard and round bits, plus 3 bits +; contributing to the sticky bit and enough information to complete +; generating it. We will finish generating it by setting Rarith to zero if +; the partial remainder zero, non-zero if the partial remainder is non-zero. +; We must also set the low word of the result mantissa to 0. + + ORR Rarith,OP2mhi,OP2mlo + MOV OP1mlo,#0 + +; Now test for mantissa underflow. If no mantissa underflow, we're ready to +; return. Otherwise, we must recover the spilled registers (to get hold of +; the result exponent), shift the mantissa left one bit, decrement the +; exponent and return. + + TEQ OP1mhi,#0 + + IF Interworking :LOR: Thumbing + LDMMIFD Rsp!,{OP1sue,OP2sue,Rfpsr,Rins,LR} + BXMI LR + ELSE + LDMMIFD Rsp!,{OP1sue,OP2sue,Rfpsr,Rins,PC} + ENDIF + + LDMFD Rsp!,{OP1sue,OP2sue,Rfpsr,Rins,LR} + MOV OP1mhi,OP1mhi,LSL #1 + SUB OP2sue,OP2sue,#1 + + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +;=========================================================================== + +; Reciprocal approximation table +; ------------------------------ +; +; This table contains 128 entries, indexed by the first 7 fractional bits of +; a normalised divisor mantissa D. The value Rapprox obtained has the +; property that: +; +; 1/D <= Rapprox*2^(-7) < 1/D + 2^(-6) +; +; In fact, entry N in the table is calculated by the formula: +; +; Entry(N) = 2^14 divided by (128+N), rounded up to an integer. +; +; Proof that this is correct: if the first 7 fractional bits of D are N, we +; know that: +; +; (128+N)*2^(-7) <= D < (129+N)*2^(-7) +; +; This gives us: +; (2^7)/(129+N) < 1/D <= (2^7)/(128+N) +; +; Next, we have: +; 1/(128+N) - 1/(129+N) = 1/((128+N)*(129+N)) +; < 1/(128*128) +; = 2^(-14) +; +; Multiplying by 2^7 and rearranging: +; (2^7)/(128+N) - 2^(-7) < (2^7)/(129+N) +; +; So: +; (2^7)/(128+N) - 2^(-7) < 1/D <= (2^7)/(128+N) +; +; Or: +; 1/D <= (2^7)/(128+N) < 1/D + 2^(-7) +; +; If we round (2^7)/(128+N) up to a multiple of 2^(-7), we increase it by +; less than 2^(-7), giving us: +; +; 1/D <= (2^7)/(128+N) rounded up to a multiple of 2^(-7) < 1/D + 2^(-64) +; +; But (2^7)/(128+N) rounded up to a multiple of 2^(-7) is Entry(N)*2^(-7), +; giving us the desired property. + +Recip_Table BytesStart + + GBLA Rec_tmp +Rec_tmp SETA 0 + WHILE Rec_tmp < 128 + DCB (16384+127+Rec_tmp)/(128+Rec_tmp) +Rec_tmp SETA Rec_tmp+1 + WEND + + BytesEnd + + ] ; Conditional assembly of Div + +;=========================================================================== + + [ :DEF: fmod_s :LOR: FPEWanted :LOR: FPASCWanted + +; Routine to perform the IEEE remainder function. It has the usual two +; labels on its entry point. +; The value returned is either a numeric value plus associated rounding +; information, with the uncommon bit clear, or an infinity or NaN, with the +; uncommon bit set. +; This routine will not work correctly with inputs which are unnormalised +; URD results, or with invalid internal format numbers. +; +; Uses standard dyadic operation entry and exit conventions - see top of +; this file. + + ASSERT RNDexp = OP2sue ;We swap over from the use of OP2sue to that + ; of RNDexp partway through this routine. + + [ FPEWanted +RemFPE + ] + + [ FPASCWanted +RemFPASC + ] + + CDebug3 3,"RemFPASC/FPE: op1 =",OP1sue,OP1mhi,OP1mlo + CDebug3 3," op2 =",OP2sue,OP2mhi,OP2mlo + + [ FPEWanted :LOR: FPASCWanted + +; Start by detecting the "fast track" case of both operands being common. + + TST OP1sue,#Uncommon_bit + TSTEQ OP2sue,#Uncommon_bit + BNE Rem_Uncommon + +; If the second operand is a zero, we've got an invalid operation. +; Otherwise, if the first operand is a zero, the result is equal to the +; first operand. + + ORRS Rarith,OP2mhi,OP2mlo + MOVEQ Rtmp,#InvReas_XRem0 + BEQ InvalidOperation2ForSDE + + ORRS Rarith,OP1mhi,OP1mlo + BEQ Rem_FirstOperand_Zero + + ] + +; Both operands may now be assumed to be normalised numbers - now to deal +; with signs and exponents. +; +; We're going to generate the remainder by a long-division-like algorithm, +; which can be summarised as follows: +; +; partial remainder = ABS(op1); sign = SIGN(op1); +; FOR I = (op1 exponent) TO ((op2 exponent)-1) STEP -1 +; Trial subtract (partial remainder) from (op2 mantissa)*2^I; +; IF strictly negative THEN +; partial remainder := 2*(op2 mantissa)*2^I - (partial remainder); +; sign := NOT(sign); +; NEXT +; IF (partial remainder) = 0 +; THEN result = 0, with sign SIGN(op1); +; ELSE result = (-1)^(sign) * (partial remainder); +; +; We're clearly going to keep both the current sign and the original sign +; around: we'll do this in the top two bits of OP1sue. We'll also need to +; know the prospective result exponent (in OP2sue = RNDexp) and the number +; of iterations of the loop (in Rarith). However, note that if the +; calculated number of iterations is 0 or less, this means that the result +; is equal to the first operand. So we'll take care to calculate this number +; before disturbing the first operand in any way. +; +; Note also that the sign of the second operand is totally irrelevant, now +; that we've got past the stage of there being any potential invalid operation +; or divide-by-zero exceptions. + +Rem_Common + + STMFD Rsp!,{LR} ;Because we'll need the register, we + ; may well call NormaliseOp1, and to + ; match the Rem_Uncommon path. + + AND RNDexp,OP2sue,#ToExp_mask ;Second operand exponent + SUB RNDexp,RNDexp,#1 ;Prospective result exponent + AND Rarith,OP1sue,#ToExp_mask ;First operand exponent + SUBS Rarith,Rarith,RNDexp ;Number of iterations - 1 + +Rem_ExponentsDone + + AND OP1sue,OP1sue,#Sign_bit ;All cases want this + ADDLT RNDexp,Rarith,RNDexp ;Recover first operand exp. + MOVLT Rarith,#0 ;And return first operand + IF Interworking :LOR: Thumbing + BXLT LR + ELSE + MOVLT PC,LR ; exactly + ENDIF + + +; Prepare for the main loop and branch into it. + + MOV OP1sue,OP1sue,ASR #1 ;Make a copy of the sign, in + ; case the result is zero + MOV LR,#0 ;Top word of the partial + ; remainder + + CDebug2 4,"Entering RMF loop: Rarith, LR",Rarith,LR + CDebug3 4," op1",OP1sue,OP1mhi,OP1mlo + CDebug3 4," op2",RNDexp,OP2mhi,OP2mlo + + B Rem_Loop_Entry + +Rem_Loop_Shift + +; Shift the partial remainder left by 1 bit, using a bit of trickery to do +; each word in 1 cycle. + + MOV LR,OP1mhi,LSR #31 + ADDS OP1mlo,OP1mlo,OP1mlo + ADC OP1mhi,OP1mhi,OP1mhi + +Rem_Loop_Entry + +; Do the trial subtraction of divisor - partial remainder; if it comes out +; non-negative, keep the previous partial remainder. + + RSBS Rtmp,OP1mlo,OP2mlo + RSCS Rtmp2,OP1mhi,OP2mhi + RSCS LR,LR,#0 + BCS Rem_Loop_End + +; Otherwise, use the trial division result to form a new partial remainder +; equal to 2*divisor minus old partial remainder, and note that the sign of +; the partial remainder has changed. + + ADDS OP1mlo,Rtmp,OP2mlo + ADC OP1mhi,Rtmp2,OP2mhi + EOR OP1sue,OP1sue,#Sign_bit + +Rem_Loop_End + +; Loop until finished. Note the partial remainder is completely contained in +; OP2mhi and OP2mlo at this point. + + SUBS Rarith,Rarith,#1 + BGE Rem_Loop_Shift + +; The result will always be exact. + + MOV Rarith,#0 + +; If we've now got a partial remainder of exactly zero, the result is zero, +; with sign equal to that of the original first operand. Otherwise, we've +; got to normalise the result. + + ORRS Rtmp,OP1mhi,OP1mlo + MOVEQ OP1sue,OP1sue,LSL #1 ;Recover copy of original sign + MOVEQ RNDexp,#0 + ANDNE OP1sue,OP1sue,#Sign_bit + BLNE $NormaliseOp1_str + +; And return. + + IF Interworking :LOR: Thumbing + LDMFD Rsp!,{LR} + BX LR + ELSE + LDMFD Rsp!,{PC} + ENDIF + + + ] ; Conditional assembly of Rem/mod + +;=========================================================================== + + [ :DEF: sqrt_s :LOR: FPEWanted :LOR: FPASCWanted + +; Routine to take the square root of an internal format floating point +; number. Unlike the dyadic arithmetic instructions, only one entry point is +; required: we do however give it two labels for the sake of consistent +; naming. +; The value returned is either a numeric value plus associated rounding +; information, with the uncommon bit clear, or an infinity or NaN, with the +; uncommon bit set. +; This routine will not work correctly with an input which is an +; unnormalised URD result, or an invalid internal format number. +; +; Uses standard monadic operation entry and exit conventions - see top of +; this file. + + [ FPEWanted +SqrtFPE + ] + + [ FPASCWanted +SqrtFPASC + ] + + [ :LNOT: :DEF: sqrt_s + + CDebug3 3,"SqrtFPE/FPASC: operand =",OP1sue,OP1mhi,OP1mlo + +; Start by splitting according to whether the operand is common or uncommon. +; The code to deal with uncommon operands lies a long way down in the +; source, to avoid addressability problems. + + TST OP1sue,#Uncommon_bit + BNE Sqrt_Uncommon + +; If the operand is a zero, the product is the same zero. Because the +; operand is common and assumed not to be an unnormalised URD result, we can +; check for zeros by means of the units bit. + + TST OP1mhi,#EIUnits_bit + BEQ Sqrt_Zero + +; The operand may now be assumed to be a normalised number. If it is +; negative, we have an invalid operation exception. Otherwise, the result +; sign is positive (equal to the operand sign) and we need to produce the +; result exponent. +; We produce the result exponent by adding the exponent bias to the +; already biased exponent, producing (unbiased exponent) + 2*bias, then +; shifting right by one bit, producing ((unbiased exponent) DIV 2) + bias. +; We set the condition codes on this last instruction in order to transfer +; the least significant bit of the unbiased exponent into C. + + ] + + [ FPLibWanted +__fp_sqrt_common + ] + +Sqrt_Common + + AND RNDexp,OP1sue,#ToExp_mask ;Extract operand exponent + + [ FPEWanted :LOR: FPASCWanted + + ANDS OP1sue,OP1sue,#Sign_bit ;Isolate sign bit & check positive + MOVNE Rtmp,#InvReas_SqrtNeg + BNE InvalidOperation1ForSDE + + | + + ANDS OP1sue,OP1sue,#Sign_bit ;Isolate sign bit + ORRNE OP1sue,OP1sue,#IVO_bits + IF Interworking :LOR: Thumbing + BXNE LR + ELSE + MOVNE PC,LR + ENDIF + + ] + + ADD RNDexp,RNDexp,#EIExp_bias:AND:&FF00 + ADD RNDexp,RNDexp,#EIExp_bias:AND:&FF + ASSERT (EIExp_bias-1) < &10000 ;Result exponent if mantissa + ; overflow is (exp+bias) DIV 2 + MOVS RNDexp,RNDexp,LSR #1 + +; This subsidiary entry point deals with taking the square root of a +; normalised mantissa. +; Entry: OP1sue = the result's sign, with an uncommon bit of 0 - the +; remaining bits are zero; +; OP1mhi = Operand mantissa, high word; +; OP1mlo = Operand mantissa, low word; +; RNDexp = Prospective result exponent; +; Rins = instruction (needed to determine the precision); +; Rwp, Rfp, Rsp hold their usual values; +; R14 = return link; +; C = least significant bit of operand's unbiased exponent. +; Exit: OP1sue = the result's sign (always positive), with an uncommon bit +; of 0; the remaining bits are zero; +; OP1mhi, OP1mlo = the result's mantissa; +; RNDexp = the result exponent; +; Rarith holds the round bit (in bit 31) and the sticky bit (in bits +; 30:0) if the destination precision is extended; if the +; destination precision is single or double, it holds part of the +; sticky bit (the remainder of which is held in bits below the +; round bit in OP1mhi and OP1mlo); +; OP2mhi, OP2mlo, Rtmp, Rtmp2 and R14 may be corrupt; +; All other registers preserved. +; +; Note that the result exponent is in fact always equal to the prospective +; result exponent: the process of taking the square root always results in a +; normalised mantissa. (Subsequent rounding may of course lead to mantissa +; overflow, but the raw unrounded result mantissa is always normalised.) + +Sqrt_Mantissa + + CDebug2 4,"SqrtFPE/FPASC: mantissa =",OP1mhi,OP1mlo + CDebug1 4," sign =",OP1sue + CDebug1 4," exponent =",RNDexp + +; We do the square root by the standard "long square root" algorithm. (There +; is an optimisation possibility here, of doing square roots by +; Newton-Raphson followed by a final correction. This only applies to the +; FPASC, since the FPE's division is too slow for there to be any +; possibility of this making a profit - even the FPA's division will have to +; be used very carefully for it to have a hope of working.) +; +; A description of the long square root algorithm follows: +; +; The problem is to take the square root of a mantissa M in the range 1 <= M +; < 4. An initial approximation R[0]=1 to the root has the property that it +; is the rounded-down root to 0 places after the binary point - i.e. that +; R[0] is a multiple of 2^(-0) and R[0] <= Sqrt(M) < R[0] + 2^(-0). We will +; evaluate successive approximations R[i] to the root such that R[i] is the +; correct rounded-down root to i places after the binary point - i.e. that +; R[i] is a multiple of 2^(-i) and R[i] <= Sqrt(M) < R[i] + 2^(-i). If we +; know R[24], R[53] or R[64] respectively for single, double or extended +; precision, and in addition know whether the result is exact (i.e. whether +; R[i] = Sqrt(M) exactly), we have enough information to provide all the +; required fractional bits and the round and sticky bits, and so to +; calculate the correct IEEE square root. (Note that a guard bit is not +; required: the infinite precision square root of M will not suffer mantissa +; overflow or underflow, and so its finite precision approximations can only +; suffer mantissa overflow during rounding, not prior to rounding.) +; +; So we will use a partial remainder P[i] = M - R[i]^2; initially, P[0] = +; M-1. Next, we know that R[i+1] is either equal to R[i] or to R[i] + +; 2^(-i-1), depending on whether the next bit of the root is 0 or 1. To +; determine which, we need to know whether R[i] + 2^(-i-1) <= Sqrt(M): if it +; is, the next bit of the root is 1; if it isn't, the next bit of the root +; is 0. +; +; This is equivalent to asking whether (R[i] + 2^(-i-1))^2 <= M, i.e. to +; whether: +; +; R[i]^2 + R[i]*2^(-i) + 2^(-2*i-2) <= M +; +; or to whether: +; +; R[i]*2^(-i) + 2^(-2*i-2) <= P[i] +; +; If it is, then R[i+1] = R[i] - 2^(-i-1) and: +; +; P[i+1] = M - R[i+1]^2 +; = M - (R[i] + 2^(-i-1))^2 +; = M - R[i]^2 - R[i]*2^(-i) - 2^(-2*i-2) +; = P[i] - R[i]*2^(-i) - 2^(-2*i-2) +; +; If it isn't, then R[i+1] = R[i] and P[i+1] = M - R[i+1]^2 = M - R[i]^2 = +; P[i]. +; +; So the long square root algorithm can be stated as follows, where N=24, 53 +; or 64 respectively for single, double or extended precision: +; +; (1) Initialise: R[0] = 1, P[0] = M-1; +; +; (2) For i=0 to N-1: +; Do a trial subtraction of R[i]*2^(-i) + 2^(-2*i-2) from P[i]; +; If result >= 0, put R[i+1] = R[i] + 2^(-i-1), P[i+1] = result of +; trial subtraction; +; Else put R[i+1] = R[i], P[i+1] = P[i]; +; +; (3) The units, fractional and round bits of the result are in R[N], while +; the sticky bit is 0 if P[N] = 0, 1 if P[N] > 0. +; +; Note that P[i] = M - R[i]^2 +; < M - (Sqrt(M) - 2^(-i))^2 +; = M - M + Sqrt(M)*2^(-i+1) - 2^(-2*i) +; = Sqrt(M)*2^(-i+1) - 2^(-2*i) +; < 2^(-i+2) +; +; So P[i] decreases greatly in magnitude during the long square root +; process. If we use it straightforwardly, this will result in a lot of +; spurious subtractions of bits known to be zero from other bits known to be +; zero during the algorithm. So instead, let us define Q[i] = P[i]*2^(i-1) +; and recast the algorithm in terms of Q[i]: +; +; (1) Initialise: R[0] = 1, Q[0] = (M-1)/2; +; +; (2) For i=0 to N-1: +; Do a trial subtraction of R[i] + 2^(-i-2) from 2*Q[i]; +; If result >= 0, put R[i+1] = R[i] + 2^(-i-1), Q[i+1] = result of +; trial subtraction; +; Else put R[i+1] = R[i], Q[i+1] = 2*Q[i]; +; +; (3) The units, fractional and round bits of the result are in R[N], while +; the sticky bit is 0 if Q[N] = 0, 1 if Q[N] > 0. +; +; Introducing a travelling bit variable T[i] to represent 2^(-i-2) and +; rephrasing in terms of shifts: +; +; (1) Initialise: R[0] = 1, Q[0] = (M-1)/2, T[0] = 2^(-2); +; +; (2) For i=0 to N-1: +; Do a trial subtraction of R[i] + T[i] from Q[i] << 1; +; If result >= 0, put R[i+1] = R[i] + (T[i] << 1), +; Q[i+1] = (Q[i] << 1) - (R[i]+T[i]); +; Else put R[i+1] = R[i], Q[i+1] = Q[i] << 1; +; +; (3) The units, fractional and round bits of the result are in R[N], while +; the sticky bit is 0 if Q[N] = 0, 1 if Q[N] > 0. +; +; This is more-or-less the algorithm we use, though we split into different +; sections depending on how far the travelling bit has been shifted down so +; far, to avoid doing multi-word arithmetic until we have to. +; +; One thing we do have to look at is the precision required for Q[i]. We +; know that 0 < Q[i] = P[i]*2^(i-1) < 2^(-i+2)*2^(i-1) = 2, so one place +; before the binary point is enough. Initially, Q[0] = (M-1)/2 is a multiple +; of 2^(-64), requiring 64 places after the binary point, or 65 bits in +; total - one bit more than 2 words. This is highly inconvenient, but we can +; get around it by noticing that if M < 2, then the first two bits of the +; result are definitely 1.0, and we have R[1] = 1.0, Q[1] = M-1 and T[0] = +; 2^(-2). So Q[1] is a multiple of 2^(-63) and can be represented in two +; words. On the other hand, if M >= 2, then Q[0] = (M-1)/2 is a multiple of +; 2^(-63) and can also be represented by two words. This transforms the +; algorithm to: +; +; IF M < 1 THEN +; +; (1) Initialise: R[1] = 1.0, Q[1] = M-1, T[1] = 2^(-3); +; +; (2) For i=1 to N-1: +; Do a trial subtraction of R[i] + T[i] from Q[i] << 1; +; If result >= 0, put R[i+1] = R[i] + (T[i] << 1), +; Q[i+1] = (Q[i] << 1) - (R[i]+T[i]); +; Else put R[i+1] = R[i], Q[i+1] = Q[i] << 1; +; +; (3) The units, fractional and round bits of the result are in R[N], while +; the sticky bit is 0 if Q[N] = 0, 1 if Q[N] > 0. +; +; ELSE +; +; (1') Initialise: R[0] = 1, Q[0] = (M-1)/2, T[0] = 2^(-2); +; +; (2') For i=0 to N-1: +; Do a trial subtraction of R[i] + T[i] from Q[i] << 1; +; If result >= 0, put R[i+1] = R[i] + (T[i] << 1), +; Q[i+1] = (Q[i] << 1) - (R[i]+T[i]); +; Else put R[i+1] = R[i], Q[i+1] = Q[i] << 1; +; +; (3') The units, fractional and round bits of the result are in R[N], while +; the sticky bit is 0 if Q[N] = 0, 1 if Q[N] > 0. +; +; ENDIF +; +; Now Q[i] can be represented in two words up to the point where the trial +; subtraction produces results that overflow two words. We have the +; following situation at various iterations, remembering that T[i] = 2^(-i-1): +; +; For i < 30: R[i] and T[i] can be represented by 1 word, with the binary +; point to the right of bit 31; Q[i+1] requires two words, with the trial +; subtraction being performed on the top word only. +; +; For 30 <= i < 62: R[i] can be represented by 2 words, with the binary point +; to the right of bit 31 of the top word (strictly, the low word isn't +; required for R[30]); T[i] can be represented by 1 word, now with an +; implicit word of zeros above it and the binary point to the right of bit +; 31 of this implicit word; Q[i+1] still requires two words, with the trial +; subtraction occurring on both words; +; +; For i=62: R[i] can be represented by 2 words, with the binary point to the +; right of bit 31 of the top word; T[i] can be represented by 1 word, now +; with two implicit words of zeros above it and the binary point to the +; right of bit 31 of the more significant of the two words; Q[i+1] still +; contains two words, but a third word is required for the trial +; subtraction. +; +; For i=63: R[i] now requires 3 words, with the binary point to the right of +; bit 31 of the most significant word; T[i] can be represented by 1 word, +; now with two implicit words of zeros above it and the binary point to +; the right of bit 31 of the more significant of the two words; Q[i+1] will +; require 3 words to represent it, with the trial subtraction occurring on +; all three words. +; +; So we will actually perform the square root in 5 stages: +; +; (A) Initialisation and iterations with 0 <= i < 30. Terminated after i=23 +; for single precision. +; (B) Iterations with 30 <= i < 62. Terminated after i=52 for double +; precision, not done at all for single precision. +; (C) Iteration with i=62. Only done for extended precision. +; (D) Iteration with i=63. Only done for extended precision. +; (E) Sticky bit construction. Done separately for single/double and +; extended precisions. +; +; Register usage: +; OP1mhi, OP1mlo: R[i] (the root so far); Rarith is also involved in this +; at the end of the i=63 iteration. +; OP2mhi, OP2mlo: Q[i] (the shifted partial remainder). +; Rarith: temporary register. +; Rtmp: T[i] (the travelling bit); +; Rtmp2: loop counter. + +; Initialise remainder (Q[0] for odd exponent, Q[1] for even exponent) + + SUBCC OP2mhi,OP1mhi,#TopBit ;Subtract 1 for even exponent + SUBCS OP2mhi,OP1mhi,#TopBit:SHR:1 ;Shift left, subtract 1 and shift + ; right for odd exponent + MOV OP2mlo,OP1mlo ;Bottom word is unaffected either way + +; Initialise travelling bit. Due to the loop unwinding below, we actually +; want T[0] for an odd exponent, T[1] << 1 for an even exponent: both of +; these are 2^(-2). + + MOV Rtmp,#TopBit:SHR:2 + +; Initialise result - both R[1] = 1.0 for even exponents and R[0] = 1 for +; odd exponents require the same bit pattern. + + MOV OP1mhi,#TopBit + MOV OP1mlo,#0 + +; Initialise the loop counter. This is a bit esoteric: it contains minus the +; number of times the first loop below is executed in its top four bits, +; plus the number of times the second loop is exceuted in its bottom 4 bits. +; The idea is that the first loop adds 1 << 28 to it until it becomes +; positive, then the second subtracts one from it until it becomes zero. +; This is the only time we actually need to look at the precision bits in +; the instruction! +; Note that we must take great care not to change the C flag in this code. + + [ FPEWanted :LOR: FPASCWanted + + MOV Rtmp2,#((-5):SHL:28) + 8 ;Correct value for extended + [ Pr1_mask < &100 ;I.e. if immediate won't set C + TST Rins,#Pr1_mask ;Z := 1 if single/double + | + MOV Rarith,Rins,LSR #Pr1_pos + TST Rarith,#(Pr1_mask:SHR:Pr1_pos) + ] + MOVEQ Rtmp2,#((-5):SHL:28) + 6 ;Correct value for double + [ Pr2_mask < &100 ;I.e. if immediate won't set C + TSTEQ Rins,#Pr2_mask ;Z := 1 if single + | + MOVEQ Rarith,Rins,LSR #Pr2_pos + TSTEQ Rarith,#(Pr2_mask:SHR:Pr2_pos) + ] + MOVEQ Rtmp2,#((-4):SHL:28) + 0 ;Correct value for single + + | + +; Single precision square root is not allowed. Extended is though. + + [ Double_mask < &100 + TST Rins,#Double_mask + | + MOV Rarith,Rins,LSR #Double_pos + TST Rarith,#(Double_mask:SHR:Double_pos) + ] + MOVEQ Rtmp2,#((-5):SHL:28) + 8 + MOVNE Rtmp2,#((-5):SHL:28) + 6 + ] + +; We now require the iterations with 0 <= i < 30 to be done - i.e.: +; +; 23 iterations for single precision, even exponent (1<=i<=23); +; 24 iterations for single precision, odd exponent (0<=i<=23); +; 29 iterations for double/extended precision, even exponent (1<=i<=29); +; 30 iterations for double/extended precision, odd exponent (0<=i<=29). +; +; We unwind this loop to produce 6 copies of the code, and branch in after +; the first one for even exponents. + + BCC Sqrt_Loop1A + +Sqrt_Loop1 + +; First copy of code + + ADDS OP2mlo,OP2mlo,OP2mlo ;Get Q[i] << 1 - note top bit goes + ADCS OP2mhi,OP2mhi,OP2mhi ; into C + + ORR Rarith,OP1mhi,Rtmp ;And R[i] + T[i] - note no overlap + CMPCC OP2mhi,Rarith ;Trial subtraction - always works + ; if (Q[i] << 1) >= 2. + SUBCS OP2mhi,OP2mhi,Rarith ;Do real subtraction if trial works + ORRCS OP1mhi,OP1mhi,Rtmp,LSL #1 ;Put 1 in result if trial works + +Sqrt_Loop1A + +; Second copy of code - similar to first copy except we use Rtmp >> 1 +; instead of Rtmp. + + ADDS OP2mlo,OP2mlo,OP2mlo + ADCS OP2mhi,OP2mhi,OP2mhi + ORR Rarith,OP1mhi,Rtmp,LSR #1 + CMPCC OP2mhi,Rarith + SUBCS OP2mhi,OP2mhi,Rarith + ORRCS OP1mhi,OP1mhi,Rtmp + +; Third copy of code - similar to first copy except we use Rtmp >> 2 +; instead of Rtmp. + + ADDS OP2mlo,OP2mlo,OP2mlo + ADCS OP2mhi,OP2mhi,OP2mhi + ORR Rarith,OP1mhi,Rtmp,LSR #2 + CMPCC OP2mhi,Rarith + SUBCS OP2mhi,OP2mhi,Rarith + ORRCS OP1mhi,OP1mhi,Rtmp,LSR #1 + +; Fourth copy of code - similar to first copy except we use Rtmp >> 3 +; instead of Rtmp. + + ADDS OP2mlo,OP2mlo,OP2mlo + ADCS OP2mhi,OP2mhi,OP2mhi + ORR Rarith,OP1mhi,Rtmp,LSR #3 + CMPCC OP2mhi,Rarith + SUBCS OP2mhi,OP2mhi,Rarith + ORRCS OP1mhi,OP1mhi,Rtmp,LSR #2 + +; Fifth copy of code - similar to first copy except we use Rtmp >> 4 +; instead of Rtmp. + + ADDS OP2mlo,OP2mlo,OP2mlo + ADCS OP2mhi,OP2mhi,OP2mhi + ORR Rarith,OP1mhi,Rtmp,LSR #4 + CMPCC OP2mhi,Rarith + SUBCS OP2mhi,OP2mhi,Rarith + ORRCS OP1mhi,OP1mhi,Rtmp,LSR #3 + +; Sixth copy of code - similar to first copy except we use Rtmp >> 5 +; instead of Rtmp. + + ADDS OP2mlo,OP2mlo,OP2mlo + ADCS OP2mhi,OP2mhi,OP2mhi + ORR Rarith,OP1mhi,Rtmp,LSR #5 + CMPCC OP2mhi,Rarith + SUBCS OP2mhi,OP2mhi,Rarith + ORRCS OP1mhi,OP1mhi,Rtmp,LSR #4 + +; Now update the travelling bit and loop counter, then loop if required. + + ADDS Rtmp2,Rtmp2,#1:SHL:28 ;Increment loop counter + MOV Rtmp,Rtmp,ROR #6 ;ROR rather than LSR to set up + BLT Sqrt_Loop1 ; for next loop. + +; If the result is exact at this point, we can obviously return with all the +; remaining fractional bits, the round bit and the sticky bit equal to 0. If +; the result is not exact but the precision is single, we can return with a +; sticky bit of 1. We only continue if the result is inexact and the +; precision is double or extended. + + ORRS Rarith,OP2mhi,OP2mlo + CMPNE Rtmp,#TopBit:SHR:26 ;Will be EQ for single, NE for + IF Interworking :LOR: Thumbing + BXEQ LR + ELSE + MOVEQ PC,LR ; double or extended + ENDIF + + +; Next, we need to do the iterations with 30 <= i < 62 - i.e.: +; +; 32 iterations for extended precision (30<=i<=61); +; 23 iterations for double precision (30<=i<=52). +; +; This is a bit awkward from the point of view of unwinding the loop, so we +; will instead do 24 iterations for double precision and unwind the loop to +; produce 4 copies of the code. The extra iteration for double precision is +; wasted work but does no harm. + + STMFD Rsp!,{Rfpsr,Rins,LR} ;We need a few more registers + +Sqrt_Loop2 + + ADDS OP2mlo,OP2mlo,OP2mlo ;Get Q[i] << 1, + ADCS OP2mhi,OP2mhi,OP2mhi + ADC LR,LR,LR ; putting overflow bit into LR[0] + + ORR Rarith,OP1mlo,Rtmp ;(OP1mhi,Rarith) := R[i] + T[i] + SUBS Rins,OP2mlo,Rarith ;Do trial subtraction, which + SBCS Rfpsr,OP2mhi,OP1mhi + MOVCCS LR,LR,LSR #1 ; always works if (Q[i] << 1) >= 2. + + MOVCS OP2mlo,Rins ;Use subtraction result if + MOVCS OP2mhi,Rfpsr ; successful + ORRCS OP1mlo,OP1mlo,Rtmp,LSL #1 ;And put a 1 in the result + ORRCS OP1mhi,OP1mhi,Rtmp,LSR #31 ;(NB Rtmp may be &80000000) + +; Second copy of code - similar to first copy except we use Rtmp >> 1 in +; place of Rtmp, and don't need to worry about putting the 1 into OP1mhi. + + ADDS OP2mlo,OP2mlo,OP2mlo + ADCS OP2mhi,OP2mhi,OP2mhi + ADC LR,LR,LR + ORR Rarith,OP1mlo,Rtmp,LSR #1 + SUBS Rins,OP2mlo,Rarith + SBCS Rfpsr,OP2mhi,OP1mhi + MOVCCS LR,LR,LSR #1 + MOVCS OP2mlo,Rins + MOVCS OP2mhi,Rfpsr + ORRCS OP1mlo,OP1mlo,Rtmp + +; Third copy of code - similar to first copy except we use Rtmp >> 2 in +; place of Rtmp, and don't need to worry about putting the 1 into OP1mhi. + + ADDS OP2mlo,OP2mlo,OP2mlo + ADCS OP2mhi,OP2mhi,OP2mhi + ADC LR,LR,LR + ORR Rarith,OP1mlo,Rtmp,LSR #2 + SUBS Rins,OP2mlo,Rarith + SBCS Rfpsr,OP2mhi,OP1mhi + MOVCCS LR,LR,LSR #1 + MOVCS OP2mlo,Rins + MOVCS OP2mhi,Rfpsr + ORRCS OP1mlo,OP1mlo,Rtmp,LSR #1 + +; Fourth copy of code - similar to first copy except we use Rtmp >> 3 in +; place of Rtmp, and don't need to worry about putting the 1 into OP1mhi. + + ADDS OP2mlo,OP2mlo,OP2mlo + ADCS OP2mhi,OP2mhi,OP2mhi + ADC LR,LR,LR + ORR Rarith,OP1mlo,Rtmp,LSR #3 + SUBS Rins,OP2mlo,Rarith + SBCS Rfpsr,OP2mhi,OP1mhi + MOVCCS LR,LR,LSR #1 + MOVCS OP2mlo,Rins + MOVCS OP2mhi,Rfpsr + ORRCS OP1mlo,OP1mlo,Rtmp,LSR #2 + +; Now update the travelling bit and loop counter, then loop if required. + + SUBS Rtmp2,Rtmp2,#1 ;Decrement loop counter + MOV Rtmp,Rtmp,ROR #4 ;ROR rather than LSR to set up + BNE Sqrt_Loop2 ; for last couple of iterations. + +; If the remainder is zero at this point, we've got an exact result: the +; last fractional bit, the round bit and the sticky bit must all be zero. +; Otherwise, we know that the result will *not* be exact, since each of +; the last two iterations either doesn't change the partial remainder (thus +; leaving it non-zero) or subtracts a value with a 1 in a less significant +; bit than the lowest bit currently in the partial remainder, which must +; leave it non-zero. +; So we can now return if either the result is currently exact or if it is +; inexact and the precision is double, taking care to make Rarith zero in +; the first case and non-zero in the second. We only need to perform the +; rest of the division if the precision is extended and the result is +; currently inexact - which implies that it will also ultimately be inexact +; and thus that the sticky bit is 1. + + ORRS Rarith,OP2mhi,OP2mlo + CMPNE Rtmp,#TopBit:SHR:24 ;Will be EQ for double, NE for + IF Interworking :LOR: Thumbing + LDMEQFD Rsp!,{Rfpsr,Rins,LR} ; extended + BXEQ LR + ELSE + LDMEQFD Rsp!,{Rfpsr,Rins,PC} ; extended + ENDIF + + +; Now we need to get the last fractional bit. + + ADDS OP2mlo,OP2mlo,OP2mlo ;Get Q[i] << 1, + ADCS OP2mhi,OP2mhi,OP2mhi + ADC LR,LR,LR ; putting overflow bit into LR[0] + + RSBS Rtmp,Rtmp,#0 ;Do trial subtraction, which + RSCS Rins,OP1mlo,OP2mlo + RSCS Rfpsr,OP1mhi,OP2mhi + MOVCCS LR,LR,LSR #1 ; always works if (Q[i] << 1) >= 2. + + MOVCS OP2mlo,Rins ;Use subtraction result if + MOVCS OP2mhi,Rfpsr ; successful + MOVCC Rtmp,#0 ;And forget it if not + ORRCS OP1mlo,OP1mlo,#1 ;And put a 1 in the result + +; And the round bit. + + MOV Rarith,#TopBit+1 ;We know sticky bit is 1 - assume + ; round bit is also 1 + + ADDS Rtmp,Rtmp,Rtmp ;Get Q[i] << 1. + ADCS OP2mlo,OP2mlo,OP2mlo + ADCS OP2mhi,OP2mhi,OP2mhi + IF Interworking :LOR: Thumbing + LDMCSFD Rsp!,{Rfpsr,Rins,LR} ;If >= 2, round bit must be 1 + BXCS LR + ELSE + LDMCSFD Rsp!,{Rfpsr,Rins,PC} ;If >= 2, round bit must be 1 + ENDIF + + ;Omit low word of trial subtraction + ; - we know it will borrow and thus + ; leave C=0. But C=0 here anyway! + SBCS Rins,OP2mlo,OP1mlo ;Do rest of trial subtraction + SBCS Rins,OP2mhi,OP1mhi + MOVCC Rarith,#1 ;If it fails, round=0, sticky=1 + IF Interworking :LOR: Thumbing + LDMFD Rsp!,{Rfpsr,Rins,LR} + BX LR + ELSE + LDMFD Rsp!,{Rfpsr,Rins,PC} + ENDIF + + ] ; Conditional compilation of sqrt + +;=========================================================================== + + [ FPEWanted :LOR: FPASCWanted + +; Routine to do a move/move negated/absolute value of an internal format +; floating point number. It has the usual pair of entry points, one +; optimised for the FPASC, the other for the FPE. +; The value returned is either a numeric value plus associated rounding +; information, with the uncommon bit clear, or an infinity or NaN, with the +; uncommon bit set. +; This routine will not work correctly with an input which is an +; unnormalised URD result, or an invalid internal format number. +; +; Uses standard monadic operation entry and exit conventions - see top of +; this file. +; +; Note that these operations are usually very simple: +; * Numeric values need their sign bits modified, then to be set up for +; rounding; note that in the process, uncommon numeric values need to be +; converted to zeros or normalised numbers to ensure that the rounding +; works; +; * Infinities and quiet NaNs need their sign bits modified; +; * Signalling NaNs just need their sign bits modified if no change of +; format is involved (what this means depends on the state of the FPSR +; NE bit); if a change of format is required, they should generate the +; usual invalid operation exception. + + [ FPEWanted + +MoveFPE + + CDebug3 3,"MoveFPE: operand =",OP1sue,OP1mhi,OP1mlo + +; If the value is common, it's a numeric value and there's no problem. + + TST OP1sue,#Uncommon_bit + BNE Move_Uncommon + +; Split out the exponent. + + AND RNDexp,OP1sue,#ToExp_mask + + ] + +Move_Numeric + +; Isolate sign bit and clear uncommon bit. Also set Rarith to 0, since all +; rounding information is completely contained in OP1mhi and OP1mlo. + + AND OP1sue,OP1sue,#Sign_bit + MOV Rarith,#0 + +Move_DoSigns + +; Do the sign manipulations and return. + + TST Rins,#MNF_bit + EORNE OP1sue,OP1sue,#Sign_bit + TST Rins,#ABS_bit + BICNE OP1sue,OP1sue,#Sign_bit + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + ] ; Conditional assembly of Move + +;=========================================================================== + + [ FPEWanted :LOR: FPASCWanted + +; Routine to do a NRM instruction on an internal format floating point +; number. It has the usual pair of entry points, one optimised for the +; FPASC, the other for the FPE. +; The value returned is either a numeric value plus associated rounding +; information, with the uncommon bit clear, or an infinity or NaN, with the +; uncommon bit set. +; +; Uses standard monadic operation entry and exit conventions - see top of +; this file. +; +; This operation is very similar to MVF, except that we have to cater for +; unnormalised values with the uncommon bit equal to zero - i.e. an URD +; result. + + [ FPEWanted + +NormFPE + + CDebug3 3,"NormFPE: operand =",OP1sue,OP1mhi,OP1mlo + +; Split according to whether the value is common or uncommon. + + TST OP1sue,#Uncommon_bit + BNE Norm_Uncommon + +; Split out the exponent. + + AND RNDexp,OP1sue,#ToExp_mask + +; If the units bit is clear, it's either a URD result or a zero. URD results +; can be treated just like extended unnormalised numbers and zeros. + + TST OP1mhi,#EIUnits_bit + BNE Norm_Numeric + + ] + +Norm_ZeroUnnormOrDenorm + +; The value is an uncommon numeric value - i.e. a denormalised number, an +; extended unnormalised number or an extended unnormalised zero - or a +; proper zero or a URD result, which may be treated like an extended +; unnormalised number or zero. If it's any sort of zero, change it to a real +; zero and treat it as a numeric. + + ORRS Rtmp,OP1mhi,OP1mlo + MOVEQ RNDexp,#0 + BEQ Norm_Numeric + +; The operand is now a denormalised number or extended unnormalised non-zero +; number. We will change it into the corresponding normalised number +; (possibly with a negative biased exponent), then treat it as a numeric. +; The types of numbers that require converting are extended unnormalised +; numbers and denormalised numbers of all precisions. In the case of the +; extended denormalised and unnormalised numbers, this just requires us to +; normalise them; in the case of the single and double denormalised numbers, +; we need to clear their units bits and add 1 to their exponents before we +; normalise them. +; At this stage, we can recognise that the numbers are single or double +; denormalised numbers simply by the fact that they have uncommon = units = +; 1: all other numbers with this property are NaNs or infinities and have +; been dealt with already. + + STMFD Rsp!,{LR} ;We will have subroutine calls below + + ANDS Rarith,OP1mhi,OP1sue,LSL #EIUnits_pos-Uncommon_pos + ASSERT EIUnits_pos = 31 + BICMI OP1mhi,OP1mhi,#EIUnits_bit + ADDMI RNDexp,RNDexp,#1 + + BL $NormaliseOp1_str ;NB must be necessary, so no + ; point in checking whether + ; normalised + + LDMFD Rsp!,{LR} + +Norm_Numeric + +; Isolate sign bit and clear uncommon bit. Also set Rarith to 0, since all +; rounding information is completely contained in OP1mhi and OP1mlo. + + AND OP1sue,OP1sue,#Sign_bit + MOV Rarith,#0 + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + ] ; Conditional assembly of Norm + +;=========================================================================== + + [ FPEWanted :LOR: FPASCWanted + +; Routine to do a URD instruction on an internal format floating point +; number. There are the usual two entry points. +; This routine will not work correctly with inputs which are unnormalised +; URD results, or with invalid internal format numbers. +; +; Uses standard monadic operation entry and exit conventions - see top of +; this file. + + [ FPEWanted + +UrdFPE + + CDebug3 3,"UrdFPE: operand =",OP1sue,OP1mhi,OP1mlo + +; Start by splitting between common and uncommon operands. + + TST OP1sue,#Uncommon_bit + BNE Urd_Uncommon + + ] + +Urd_Common + +; The operand is common. Split OP1sue into sign and biased exponent. + + AND Rarith,OP1sue,#ToExp_mask + AND OP1sue,OP1sue,#Sign_bit + +Urd_Numeric + +; Calculate shift amount to denormalise the number to put the true binary +; point at the rounding boundary - i.e. to give it an effective unbiased +; exponent of 23, 52 or 63 depending on whether the precision of the +; instruction is single, double or extended. + + MOV RNDexp,#((EIExp_bias+23):AND:&FF) + TST Rins,#Pr2_mask + MOVNE RNDexp,#((EIExp_bias+52):AND:&FF) + TST Rins,#Pr1_mask + MOVNE RNDexp,#((EIExp_bias+63):AND:&FF) + ORR RNDexp,RNDexp,#((EIExp_bias+63):AND:&FF00) + ASSERT ((EIExp_bias+63):AND:&FF00) = ((EIExp_bias+52):AND:&FF00) + ASSERT ((EIExp_bias+63):AND:&FF00) = ((EIExp_bias+23):AND:&FF00) + + SUBS Rtmp,RNDexp,Rarith + BLS Urd_Big + +; Denormalise the number to have this unbiased exponent and return. + + Denorm OP1mhi,OP1mlo,Rarith,Rtmp,Rtmp2,Rtmp + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +Urd_Big + +; We just need to return the number itself, with rounding bits equal to +; zero. + + MOV RNDexp,Rarith + MOV Rarith,#0 + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + ] ; Conditional assembly of Urd + +;=========================================================================== + + [ FPEWanted :LOR: FPASCWanted + +; Routine to do a RND instruction on an internal format floating point +; number. There are the usual two entry points. +; This routine will not work correctly with inputs which are unnormalised +; URD results, or with invalid internal format numbers. +; +; Uses standard monadic operation entry and exit conventions - see top of +; this file. + + [ FPEWanted +RndFPE + ] + + [ FPASCWanted +RndFPASC + ] + + CDebug3 3,"RndFPASC/FPE: operand =",OP1sue,OP1mhi,OP1mlo + +; Start by splitting between common and uncommon operands. + + TST OP1sue,#Uncommon_bit + BNE Rnd_Uncommon + +Rnd_Common + +; The operand is common. Split OP1sue into sign and biased exponent. + + AND RNDexp,OP1sue,#ToExp_mask + AND OP1sue,OP1sue,#Sign_bit + +; If the number is a zero, we're done. + + TST OP1mhi,#EIUnits_bit + BEQ Rnd_Exact + +Rnd_Numeric + +; Find the position of the real binary point. + + MOVNE Rarith,#((EIExp_bias+63):AND:&FF) + ORR Rarith,Rarith,#((EIExp_bias+63):AND:&FF00) + ASSERT (EIExp_bias + 63) < &10000 + + SUBS Rtmp,Rarith,RNDexp + BLE Rnd_Exact + +; The rounding position for an integer - i.e. the real binary point - is now +; Rtmp bits above the bottom of the mantissa. Split according to whether +; this puts the round bit in the low word of the mantissa, the high word of +; the mantissa or above the high word of the mantissa. + + RSBS Rtmp2,Rtmp,#32 + BLT Rnd_AboveLowWord + +Rnd_LowWord + +; Branch out if rounding is exact. + + MOVS Rtmp,OP1mlo,LSL Rtmp2 + BEQ Rnd_Exact + +; We now know we want to round down if we're rounding to zero, or if we're +; rounding to minus infinity and the number is positive, or if we're +; rounding to plus infinity and the number is negative. + + MOVS Rtmp,OP1sue,LSL #32-Sign_pos + TSTCS Rins,#1:SHL:RM_pos + TSTCC Rins,#1:SHL:(RM_pos+1) + ASSERT RM_pos < 7 ;So that constants don't disturb C + BNE Rnd_LowWord_RoundDown + +; If we're not rounding to nearest, we must now be rounding up. + + TST Rins,#RM_mask + BNE Rnd_LowWord_RoundUp + ASSERT RM_Nearest = 0 + +; We're rounding to nearest. Produce the round and sticky bits, then work +; out which way we're rounding. + + ADD Rtmp,Rtmp2,#1 + MOVS Rtmp,OP1mlo,LSL Rtmp ;C<-round, Z<-NOT(sticky) + BNE Rnd_LowWord_GotDir ;Branch if not halfway case + + MOVS Rtmp,OP1mhi,LSR #1 ;C<-least significant bit, from + MOVS Rtmp,OP1mlo,LSL Rtmp2 ; low word unless Rtmp2 is 0. + +Rnd_LowWord_GotDir + + BCS Rnd_LowWord_RoundUp + +Rnd_LowWord_RoundDown + + RSB Rtmp2,Rtmp2,#32 ;Clear all bits below rounding + MOV OP1mlo,OP1mlo,LSR Rtmp2 ; boundary + MOV OP1mlo,OP1mlo,LSL Rtmp2 + MOV Rarith,#&40000000 ;And set round=0, sticky=1 + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +Rnd_LowWord_RoundUp + + RSB Rtmp2,Rtmp2,#32 ;Set all bits below rounding + MVN OP1mlo,OP1mlo,LSR Rtmp2 ; boundary + MVN OP1mlo,OP1mlo,LSL Rtmp2 + MOV Rarith,#&C0000000 ;And set round=1, sticky=1 + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +Rnd_AboveLowWord + + RSBS Rtmp2,Rtmp,#64 + BLT Rnd_AboveMantissa + +Rnd_HighWord + +; Branch out if rounding is exact. + + ORRS Rtmp,OP1mlo,OP1mhi,LSL Rtmp2 + BEQ Rnd_Exact + +; We now know we want to round down if we're rounding to zero, or if we're +; rounding to minus infinity and the number is positive, or if we're +; rounding to plus infinity and the number is negative. + + MOVS Rtmp,OP1sue,LSL #32-Sign_pos + TSTCS Rins,#1:SHL:RM_pos + TSTCC Rins,#1:SHL:(RM_pos+1) + ASSERT RM_pos < 7 ;So that constants don't disturb C + BNE Rnd_HighWord_RoundDown + +; If we're not rounding to nearest, we must now be rounding up. + + TST Rins,#RM_mask + BNE Rnd_HighWord_RoundUp + ASSERT RM_Nearest = 0 + +; We're rounding to nearest. Produce the round and sticky bits, then work +; out which way we're rounding. + + ADD Rtmp,Rtmp2,#1 + ORRS Rtmp,OP1mlo,OP1mhi,LSL Rtmp ;C<-round, Z<-NOT(sticky) + BNE Rnd_HighWord_GotDir ;Branch if not halfway case + + CMP Rtmp2,#1 ;C<-least significant bit, from + MOVCSS Rtmp,OP1mhi,LSL Rtmp2 ; high word unless Rtmp2 is 0. + +Rnd_HighWord_GotDir + + BCS Rnd_HighWord_RoundUp + +Rnd_HighWord_RoundDown + + RSB Rtmp2,Rtmp2,#32 ;Clear all bits below rounding + MOV OP1mhi,OP1mhi,LSR Rtmp2 ; boundary + MOVS OP1mhi,OP1mhi,LSL Rtmp2 + MOV OP1mlo,#0 + MOVEQ RNDexp,#0 ;Exponent must change for 0 result + MOV Rarith,#&40000000 ;And set round=0, sticky=1 + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +Rnd_HighWord_RoundUp + + RSB Rtmp2,Rtmp2,#32 ;Set all bits below rounding + MVN OP1mhi,OP1mhi,LSR Rtmp2 ; boundary + MVN OP1mhi,OP1mhi,LSL Rtmp2 + MOV OP1mlo,#&FFFFFFFF + MOV Rarith,#&C0000000 ;And set round=1, sticky=1 + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +Rnd_AboveMantissa + +; The rounding cannot possibly be exact - we must either be rounding down to +; zero or up to one. Furthermore, we know that the round bit is 0 and the +; sticky bit is 1. So we can only be rounding up if we're rounding to plus +; or minus infinity, and the result must be of the correct sign as well. + + EOR Rtmp,OP1sue,Rins,LSL #31-RM_pos ;Somewhat tricky code to + EOR Rtmp2,OP1sue,Rins,LSL #30-RM_pos ; establish the above + BICS Rtmp,Rtmp,Rtmp2 + BMI Rnd_UpToOne + +Rnd_DownToZero + + MOV OP1mhi,#0 + MOV OP1mlo,#0 + MOV RNDexp,#0 + MOV Rarith,#&40000000 + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +Rnd_UpToOne + + MOV OP1mhi,#&FFFFFFFF + MOV OP1mlo,#&FFFFFFFF + MOV RNDexp,#(EIExp_bias-1):AND:&FF00 + ORR RNDexp,RNDexp,#(EIExp_bias-1):AND:&FF + ASSERT (EIExp_bias-1) < &10000 + MOV Rarith,#&C0000000 + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +Rnd_Exact + +; We just need to return the number itself, with rounding bits equal to +; zero. + + MOV Rarith,#0 + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + ] ; Conditional assembly of Rnd + +;=========================================================================== + + [ :DEF: compare_s :LOR: FPEWanted :LOR: FPASCWanted + +; Routine to compare two internal format floating point numbers. It has two +; entry points: "CompareFPE", which has an optimised fast track for common +; vs. common comparisons, and "CompareFPASC", which avoids the test for this +; optimised fast track - since it should never happen. The second entry +; point lies a long way down in the source to avoid addressing constraints. +; This routine will not work correctly with inputs which are unnormalised +; URD results, or with invalid internal format numbers. +; Entry: OP1sue = First operand sign, uncommon, exponent; +; OP1mhi = First operand mantissa, high word; +; OP1mlo = First operand mantissa, low word; +; OP2sue = Second operand sign, uncommon, exponent; +; OP2mhi = Second operand mantissa, high word; +; OP2mlo = Second operand mantissa, low word; +; Rfpsr = FPSR; +; Rins = instruction (needed to discriminate between +; CMF/CMFE/CNF/CNFE and for traps); +; Rwp, Rfp, Rsp hold their usual values; +; R14 = return link. +; Exit: Rarith = result NZCV in bits 31:28; other bits zero; +; OP1sue, OP1mhi, OP1mlo, OP2sue, OP2mhi, OP2mlo, Rtmp, Rtmp2 and R14 +; may be corrupt. +; Rfpsr may be updated. +; All other registers preserved. + + [ FPEWanted :LOR: FPLibWanted + +CompareFPE + + [ FPLibWanted +__fp_compare + ] + + CDebug3 3,"CompareFPE: op1 =",OP1sue,OP1mhi,OP1mlo + CDebug3 3," op2 =",OP2sue,OP2mhi,OP2mlo + +; Start by detecting the "fast track" case of both operands being common. + + TST OP1sue,#Uncommon_bit + TSTEQ OP2sue,#Uncommon_bit + BNE Compare_Uncommon + + ] + +Compare_Common + +; Start by changing the sign of the second operand if the operation is +; CMF(E). (CNF(E) is easier than CMF(E), basically because addition is +; commutative and subtraction isn't.) + + [ FPEWanted :LOR: FPASCWanted + TST Rins,#CompNeg_bit + EOREQ OP2sue,OP2sue,#Sign_bit + | + EOR OP2sue,OP2sue,#Sign_bit + ] + +; Both operands are common. We start with a magnitude comparison - life is +; fairly easy if (as is likely) it comes out not equal. In this case, the +; results are: +; +; Magnitude Operand 1 Operand 2 | Result for +; comparison sign sign | CNF(E) +; ------------------------------------+------------ +; > + X | > +; > - X | < +; < X + | > +; < X - | < + + ExpComp Rtmp,OP1sue,OP2sue,Rtmp2 ;Rtmp := left-aligned op1 exp. + CMPEQ OP1mhi,OP2mhi + CMPEQ OP1mlo,OP2mlo + BEQ Compare_EqualMag + TEQCS OP1sue,#0 ;NB does not affect C + TEQCC OP2sue,#0 + ASSERT Sign_pos = 31 + MOVPL Rarith,#Comp_GT + MOVMI Rarith,#Comp_LT + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +Compare_EqualMag + +; If the operands are equal magnitude, then if they're both zero, the +; results is equality. Otherwise, the result is given by the following +; table: +; +; Operand 1 Operand 2 | Result for +; sign sign | CNF(E) +; -----------------------+------------ +; + + | > +; + - | = +; - + | = +; - - | < +; +; Of course, since they're equal magnitude, they're both zero if the first +; one is. Note Rtmp still contains a left-aligned operand 1 exponent. + + EORS Rtmp2,OP1sue,OP2sue ;Are signs opposite or the same? + ASSERT Sign_pos = 31 + MOV Rarith,#Comp_EQ ;Result if signs opposite + IF Interworking :LOR: Thumbing + BXMI LR + ELSE + MOVMI PC,LR + ENDIF + ORR Rtmp,Rtmp,OP1mhi ;Otherwise, are they both zero? + ORRS Rtmp,Rtmp,OP1mlo + IF Interworking :LOR: Thumbing + BXEQ LR + ELSE + MOVEQ PC,LR + ENDIF + TST OP1sue,#Sign_bit + MOVEQ Rarith,#Comp_GT + MOVNE Rarith,#Comp_LT + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + ] ; Conditional assembly of Compare + +;=========================================================================== + + [ FPEWanted :LOR: FPASCWanted :LOR: :DEF: fix_s :LOR: :DEF: fixu_s + +; Routine to FIX an internal format floating point number. There are the +; usual two entry points. +; This routine will not work correctly with inputs which are unnormalised +; URD results, or with invalid internal format numbers. +; Entry: OP1sue = Operand sign, uncommon, exponent; +; OP1mhi = Operand mantissa, high word; +; OP1mlo = Operand mantissa, low word; +; Rfpsr = FPSR; +; Rins = instruction (needed for rounding information and traps); +; Rwp, Rfp, Rsp hold their usual values; +; R14 = return link. +; Exit: Rarith = result value; +; OP1sue, OP1mhi, OP1mlo, OP2sue, OP2mhi, OP2mlo, Rtmp, Rtmp2 and R14 +; may be corrupt. +; Rfpsr may be updated. +; All other registers preserved. + + [ FPEWanted + +FixFPE + + CDebug3 3,"FixFPE: operand =",OP1sue,OP1mhi,OP1mlo + +; Start by splitting between common and uncommon operands. + + TST OP1sue,#Uncommon_bit + BNE Fix_Uncommon + + ] + + [ :DEF: fix_s +__fp_fix_common + ] + [ :DEF: fixu_s +__fp_fixu_common + ] + +Fix_Common + +; The operand is common. Split OP1sue into sign and biased exponent. + + AND Rarith,OP1sue,#ToExp_mask + [ :LNOT: :DEF: fixu_s + AND OP1sue,OP1sue,#Sign_bit + ] + +Fix_Numeric + +; Calculate shift amount to denormalise the number to have effective +; unbiased exponent 63 - i.e. to put the true binary point at the rounding +; boundary. + + STMFD Rsp!,{LR} ;There may be a subroutine call below + + MOV RNDexp,#((EIExp_bias+63):AND:&FF00) + ORR RNDexp,RNDexp,#((EIExp_bias+63):AND:&FF) + ASSERT (EIExp_bias+63) <= &FFFF + SUBS Rtmp,RNDexp,Rarith + BLS Fix_OutOfRange ;Deal with massively out of range values + +; Now denormalise the number to have this unbiased exponent. + + Denorm OP1mhi,OP1mlo,Rarith,Rtmp,Rtmp2,Rtmp + +; Next, we need to round the result to extended precision. + + [ FPEWanted :LOR: FPASCWanted + + AND RNDprm,Rins,#RM_mask + ORR RNDprm,RNDprm,#2:SHL:(RM_pos+2) + MOV RNDdir,#0 ;Result has not been rounded so far + BL RoundNum_Extended + + | + +; Expanded out rounding code + + MOVS Rtmp,Rarith,LSL #1 ;C<-round, Z<-"tied case" + BCC Fix_NoRounding ;Skip all rounding code... + MOVEQS Rtmp,OP1mlo,LSR #1 ; If "tied" C<-round + ADDCSS OP1mlo,OP1mlo,#1 ;Increment low word + ADDCSS OP1mlo,OP1mlo,#1 ;If carry out, increment high word + MOVCS OP1mhi,#EIUnits_bit ;If mantissa overflow, adjust + ADDCS RNDexp,RNDexp,#1 ; mantissa and exponent + +Fix_NoRounding + + ] + + [ :LNOT: :DEF: fixu_s + +; Produce the potential result, checking for an out-of-range value. +; We know at this point that (OP1mhi,OP1mlo) contains the unsigned integer +; result, which is in the range 0 to 2^63, *both ends included*, and that +; OP1sue contains the sign of the result. We first need to apply the sign to +; this value - this is done by some slightly tricky code to avoid branches. +; Note we cannot tell the difference between a result of +2^63 and -2^63 +; after this. This doesn't matter, though - they're both well out of range! + + MOVS Rtmp,OP1sue,LSL #32-Sign_pos ;CS if -ve, CC if +ve + MVNCS OP1mhi,OP1mhi ;If -ve, 1's compl't high + RSBCSS OP1mlo,OP1mlo,#0 ; word, 2's compl't low word + ADDCS OP1mhi,OP1mhi,#1 ; and do carry if needed + + ] + +; The result is now in (OP1mhi,OP1mlo). Check for it being out of range - +; i.e. for its top 33 bits not being all identical. + + TEQ OP1mhi,OP1mlo,ASR #31 + BNE Fix_OutOfRange + + [ FPEWanted :LOR: FPASCWanted + + MOV Rarith,OP1mlo + +; The only remaining exception that could occur at this point is an inexact +; result. +; If the result is exact, we don't want to do anything about the inexact +; exception. If it's inexact and the inexact trap is disabled, we want to +; set the inexact cumulative bit in the FPSR. If it's inexact and the +; inexact trap is enabled, we want to call the trap. We use some tricky +; code to distinguish the three cases in-line. + + CMP RNDdir,#0 ;Leaves CS/EQ if exact, NE if inexact + MOVNES Rtmp,Rfpsr,LSR #IXE_pos+1 + ;Now CS/EQ if exact, CS/NE if inexact & + ; trap enabled, CC/NE if inexact & trap + ASSERT SysID_FPA <> 0 ; disabled (since SysID non-zero & not + ASSERT SysID_FPE <> 0 ; shifted out) + ASSERT SysID_pos > IXE_pos + ORRCC Rfpsr,Rfpsr,#IXC_bit + BLHI InexactTrapForI ;Works because HI = CS/NE + + | + + MOV OP1sue,#0 ;Signal no error + + ] + + IF Interworking :LOR: Thumbing + LDMFD Rsp!,{LR} + BX LR + ELSE + LDMFD Rsp!,{PC} + ENDIF + +Fix_OutOfRange + +; An out of range FIX produces an invalid operation, with a potential result +; of &7FFFFFFF or &80000000, depending on the sign of the operand. + + [ FPEWanted :LOR: FPASCWanted + + LDMFD Rsp!,{LR} + MOV Rarith,#:NOT:TopBit ;Make &7FFFFFFF + EOR Rarith,Rarith,OP1sue,ASR #31 ;Convert to &80000000 if -ve + MOV Rtmp,#InvReas_FixRange + B InvalidOperation1ForI + + | + + ORR OP1sue,OP1sue,#IVO_bits + IF Interworking :LOR: Thumbing + LDMFD Rsp!,{LR} + BX LR + ELSE + LDMFD Rsp!,{PC} + ENDIF + + ] + + ] ; Conditional assembly of Fix + +;=========================================================================== + + [ :DEF: addsub_s :LOR: FPEWanted :LOR: FPASCWanted + +; The second entry point to the addition/subtraction routine, meant for use +; by the FPASC and without a fast track for common operands. +; The value returned is either a numeric value plus associated rounding +; information, with the uncommon bit clear, or an infinity or NaN, with the +; uncommon bit set. +; This routine will not work correctly with inputs which are unnormalised +; URD results, or with invalid internal format numbers. +; +; Uses standard dyadic operation entry and exit conventions - see top of +; this file. + + [ FPASCWanted + +AddSubFPASC + + CDebug3 3,"AddSubFPASC: op1 =",OP1sue,OP1mhi,OP1mlo + CDebug3 3," op2 =",OP2sue,OP2mhi,OP2mlo + + ] + + [ FPLibWanted +__fp_addsub_uncommon + ] + +AddSub_Uncommon + +; We have to do a full addition/subtraction, since either or both of the +; operands may be uncommon. What we will do is: +; +; (a) Check for NaNs. If found, produce an invalid operation exception and +; suitable NaN result. +; +; (b) Check for infinities. If found, the infinity effectively becomes the +; result, unless both operands are infinities and (after taking +; account of whether an addition or subtraction is involved) they are +; effectively of opposite signs. +; +; (c) If no NaNs or infinities, adjust the operands by replacing all +; effectively unnormalised numbers by the corresponding normalised or +; extended denormalised number. Then call AddSub_Common, which will +; work correctly on zeros, normalised numbers and extended +; denormalised numbers. +; +; So the first thing we do is check for NaNs and infinities - if we find +; one, we'll generate the result by special case code. Note that we check +; for them together, since they have similar bit patterns. + + TNaNInf Rtmp2,OP2sue,OP2mhi ;Rtmp2[31] := (op2 is NaN/inf) + TNaNInf Rtmp,OP1sue,OP1mhi ;Rtmp[31] := (op1 is NaN/inf) + BMI AddSub_NaNInf1 + TST Rtmp2,#TopBit ;Operand 2 NaN or infinity? + BNE AddSub_NaNInf2Only + +; Now we know there are no NaNs or infinities and therefore no Invalid +; Operation or Divide-By-Zero exceptions - which means we no longer need to +; keep track of exactly what the operands are. Next, we will convert the +; remaining types of numbers to zeros, normalised numbers and extended +; denormalised numbers, which can be dealt with by a call to AddSub_Common +; and one to NormaliseOp1. +; The types of numbers that require converting are extended unnormalised +; numbers and zeros, and single and double denormalised numbers. In the case +; of the extended unnormalised numbers and zeros, this just requires us to +; normalise them; in the case of the single and double denormalised numbers, +; we need to clear their units bits and add 1 to their exponents before we +; normalise them. +; At this stage, we can recognise that the numbers are single or double +; denormalised numbers simply by the fact that they have uncommon = units = +; 1: all other numbers with this property are NaNs or infinities and have +; been dealt with already. + + STMFD Rsp!,{LR} ;We will have subroutine calls below + + ANDS Rarith,OP1mhi,OP1sue,LSL #EIUnits_pos-Uncommon_pos + ASSERT EIUnits_pos = 31 + BICMI OP1mhi,OP1mhi,#EIUnits_bit + ADDMI OP1sue,OP1sue,#1:SHL:EIExp_pos + ANDS Rarith,OP2mhi,OP2sue,LSL #EIUnits_pos-Uncommon_pos + ASSERT EIUnits_pos = 31 + BICMI OP2mhi,OP2mhi,#EIUnits_bit + ADDMI OP2sue,OP2sue,#1:SHL:EIExp_pos + +; Now we need to normalise all these types of numbers, which now means all +; uncommon numbers except those with exponent 0 (which are extended +; precision denormalised numbers and should be left alone). + + TST OP1sue,#Uncommon_bit + Exp2Top Rarith,OP1sue,NE,S ;Complete test & set up for call + BLNE $NormDenormOp1_str + TST OP2sue,#Uncommon_bit + Exp2Top Rarith,OP2sue,NE,S ;Complete test & set up for call + BLNE $NormDenormOp2_str + +; Call AddSub_Common to do the addition, then normalise the result if it +; isn't already normalised and isn't zero. (This is necessary because e.g. a +; magnitude sum of two denormalised numbers will only have been shifted 1 +; bit by AddSub_Common.) + + BL AddSub_Common + TST OP1mhi,#EIUnits_bit + IF Interworking :LOR: Thumbing + LDMNEFD Rsp!,{LR} + BXNE LR + ELSE + LDMNEFD Rsp!,{PC} + ENDIF + ORRS LR,OP1mhi,OP1mlo + BLNE $NormaliseOp1_str + IF Interworking :LOR: Thumbing + LDMFD Rsp!,{LR} + BX LR + ELSE + LDMFD Rsp!,{PC} + ENDIF + +AddSub_NaNInf1 + +; The first operand is a NaN or infinity, the second may be (the top bit of +; Rtmp2 indicates whether it is). + + TST Rtmp2,#TopBit + BEQ AddSub_NaNInf1Only + +; Both operands are NaNs or infinities. If both operands are infinities, the +; result is an infinity with their shared sign if they have the same effective +; sign, or an invalid operation if they have opposite effective signs +; ("effective" means after taking ADF/SUF/RSF distinctions into account). +; If either operand is a NaN, the standard exception/NaN propagation rules +; apply. + + ORR Rtmp,OP1mlo,OP1mhi,LSL #1 ;Test if both are infinities + ORR Rtmp,Rtmp,OP2mlo + ORRS Rtmp,Rtmp,OP2mhi,LSL #1 + BNE $ConvertNaNs_str ;If not, use shared code + BiShift EOR,Rtmp,OP2sue,Rins,LSR #SubNotAdd_pos,LSL #Sign_pos + EORS Rtmp,Rtmp,OP1sue ;Check whether signs are + ASSERT Sign_pos = 31 ; effectively same. + ANDPL Rtmp,OP1sue,#Sign_bit ;If so, result is infinity + BPL AddSub_InfShared ; (with op1 sign unless RSF) + + [ FPEWanted :LOR: FPASCWanted + + MOV Rtmp,#InvReas_MagSubInf ;If not, it's an invalid + B InvalidOperation2ForSDE ; operation + + | + + ORR OP1sue,OP1sue,#IVO_bits + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + ] + +AddSub_NaNInf1Only + +; The first operand is a NaN or infinity, the second isn't. The result is: +; * an invalid operation exception if the first operand is a signalling +; NaN; +; * the first operand unchanged if it is a quiet NaN; +; * the standard infinity if the first operand is an infinity, with its +; sign determined by that of the first operand and whether the +; instruction is RSF. + + ORRS Rtmp,OP1mlo,OP1mhi,LSL #1 ;Is operand a NaN? + BNE $ConvertNaN1Of2_str ;Use standard exception/quiet NaN + ; propagation code if so + AND Rtmp,OP1sue,#Sign_bit ;Make standard infinity with right + B AddSub_InfShared ; sign + +AddSub_NaNInf2Only + +; The first operand is not a NaN or infinity, the second is. The result is: +; * an invalid operation exception if the second operand is a signalling +; NaN; +; * the second operand unchanged if it is a quiet NaN; +; * the standard infinity if the second operand is an infinity, with its +; sign determined by that of the second operand and whether the +; instruction is SUF. + + ORRS Rtmp,OP2mlo,OP2mhi,LSL #1 ;Is operand a NaN? + BNE $ConvertNaN2Of2_str ;Use standard exception/quiet NaN + ; propagation code if so + AND Rtmp,OP2sue,#Sign_bit ;Make standard infinity with right + TST Rins,#SubNotAdd_bit ; sign + EORNE Rtmp,Rtmp,#Sign_bit +AddSub_InfShared + TST Rins,#RSF_bit + EORNE Rtmp,Rtmp,#Sign_bit + [ CoreDebugging = 0 + ADR OP1sue,Prototype_Infinity + | + ADRL OP1sue,Prototype_Infinity + ] + LDMIA OP1sue,OP1regs + ORR OP1sue,OP1sue,Rtmp + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + ] ; Conditional assembly of AddSub + +;=========================================================================== + + [ :DEF: mul_s :LOR: FPEWanted :LOR: FPASCWanted + +; The second entry point to the normal/fast multiplication routine, meant +; for use by the FPASC and without a fast track for common operands. +; The value returned is either a numeric value plus associated rounding +; information, with the uncommon bit clear, or an infinity or NaN, with the +; uncommon bit set. +; This routine will not work correctly with inputs which are unnormalised +; URD results, or with invalid internal format numbers. +; +; Uses standard dyadic operation entry and exit conventions - see top of +; this file. + + [ FPASCWanted + +MultFPASC + + CDebug3 3,"MultFPASC: op1 =",OP1sue,OP1mhi,OP1mlo + CDebug3 3," op2 =",OP2sue,OP2mhi,OP2mlo + + ] + + [ FPLibWanted +__fp_mult_uncommon + ] + +Mult_Uncommon + +; We have to do a full multiplication, since either or both of the operands +; may be uncommon. What we will do is: +; +; (a) Check for NaNs. If found, produce an invalid operation exception and +; suitable NaN result. +; +; (b) Check for infinities. If found, the result is an infinity with sign +; equal to the exclusive-OR of the two operand signs, unless the other +; operand is a zero, in which case we have an invalid operation. +; +; (c) Check for zeros. If found, the result is a zero with sign equal to +; the exclusive-OR of the two operand signs. +; +; (d) If no NaNs, infinities or zeros, we can transform the problem into +; that of multiplying together two normalised numbers, though the +; normalised numbers concerned may have unusual exponents. +; +; So the first thing we do is check for NaNs and infinities - if we find +; one, we'll generate the result by special case code. Note that we check +; for them together, since they have similar bit patterns. + + TNaNInf Rtmp2,OP2sue,OP2mhi ;Rtmp2[31] := (op2 is NaN/inf) + TNaNInf Rtmp,OP1sue,OP1mhi ;Rtmp[31] := (op1 is NaN/inf) + BMI Mult_NaNInf1 + TST Rtmp2,#TopBit ;Operand 2 NaN or infinity? + BNE Mult_NaNInf2Only + +; Now if either operand is a zero, the result is zero. We can detect zeros +; by the mantissa being all zero, since only zeros, some unnormalised URD +; results, extended unnormalised zeros and extended infinities have this +; property, we're assuming the operands are not URD results and we've +; already dealt with extended infinities. + + ORRS Rtmp,OP1mhi,OP1mlo + ORRNES Rtmp,OP2mhi,OP2mlo + BEQ Mult_Zero + +; Both operands are now normalised numbers, denormalised numbers or extended +; unnormalised non-zero numbers. The first step is to convert all of these +; to normalised numbers, possibly with a negative biased exponent. After +; doing the exponent and sign calculations, we then call Mult_Mantissas to +; complete the calculation. +; The types of numbers that require converting are extended unnormalised +; numbers and denormalised numbers of all precisions. In the case of the +; extended denormalised and unnormalised numbers, this just requires us to +; normalise them; in the case of the single and double denormalised numbers, +; we need to clear their units bits and add 1 to their exponents before we +; normalise them. +; At this stage, we can recognise that the numbers are single or double +; denormalised numbers simply by the fact that they have uncommon = units = +; 1: all other numbers with this property are NaNs or infinities and have +; been dealt with already. + + ANDS Rarith,OP1mhi,OP1sue,LSL #EIUnits_pos-Uncommon_pos + ASSERT EIUnits_pos = 31 + BICMI OP1mhi,OP1mhi,#EIUnits_bit + ADDMI OP1sue,OP1sue,#1:SHL:EIExp_pos + ANDS Rarith,OP2mhi,OP2sue,LSL #EIUnits_pos-Uncommon_pos + ASSERT EIUnits_pos = 31 + BICMI OP2mhi,OP2mhi,#EIUnits_bit + ADDMI OP2sue,OP2sue,#1:SHL:EIExp_pos + + AND Rtmp,OP1sue,#ToExp_mask + AND Rtmp2,OP2sue,#ToExp_mask + EOR OP1sue,OP1sue,OP2sue ;Produce result sign + AND OP1sue,OP1sue,#Sign_bit + ADD RNDexp,Rtmp,Rtmp2 + SUB RNDexp,RNDexp,#(EIExp_bias-1):AND:&FF00 + SUB RNDexp,RNDexp,#(EIExp_bias-1):AND:&FF + ASSERT (EIExp_bias-1) < &10000 ;Result exponent if mantissa + ; overflow is exp1+exp2-bias+1 + + STMFD Rsp!,{LR} ;We will have subroutine calls below + + TST OP1mhi,#EIUnits_bit + BLEQ $NormaliseOp1_str + TST OP2mhi,#EIUnits_bit + BLEQ $NormaliseOp2_str + + LDMFD Rsp!,{LR} + B Mult_Mantissas + +Mult_Zero + +; The result is zero. + + EOR OP1sue,OP1sue,OP2sue ;Get sign right + AND OP1sue,OP1sue,#Sign_bit + MOV OP1mhi,#0 + MOV OP1mlo,#0 + MOV RNDexp,#0 ;And exponent + MOV Rarith,#0 ;And round/sticky bits + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +Mult_NaNInf1 + +; The first operand is a NaN or infinity, the second may be (the top bit of +; Rtmp2 indicates whether it is). + + TST Rtmp2,#TopBit + BEQ Mult_NaNInf1Only + +; Both operands are NaNs or infinities. If both operands are infinities, the +; result is an infinity with sign determined by those of the two operands. +; If either operand is a NaN, the standard exception/NaN propagation rules +; apply. + + ORR Rtmp,OP1mlo,OP1mhi,LSL #1 ;Test if both are infinities + ORR Rtmp,Rtmp,OP2mlo + ORRS Rtmp,Rtmp,OP2mhi,LSL #1 + BNE $ConvertNaNs_str ;If not, use shared code +Mult_InfShared + EOR Rtmp,OP1sue,OP2sue ;If so, result is infinity + AND Rtmp,Rtmp,#Sign_bit ; with correct sign + ADR OP1sue,Prototype_Infinity + LDMIA OP1sue,OP1regs + ORR OP1sue,OP1sue,Rtmp + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +Mult_NaNInf1Only + +; The first operand is a NaN or infinity, the second isn't. The result is: +; * an invalid operation exception if the first operand is a signalling +; NaN; +; * the first operand unchanged if it is a quiet NaN; +; * an invalid operation exception if the first operand is an infinity and +; the second is a zero; +; * the standard infinity if the first operand is an infinity and the +; second operand is not a zero, with its sign determined by those of the +; two operands. +; Note that we can detect the second operand being zero by its mantissa +; being all zero, since only zeros, some unnormalised URD results, extended +; unnormalised zeros and extended infinities have this property, we're +; assuming the operands are not URD results and we know the second operand +; isn't an extended infinity. + + ORRS Rtmp,OP1mlo,OP1mhi,LSL #1 ;Is first operand a NaN? + BNE $ConvertNaN1Of2_str ;Use standard exception/quiet NaN + ; propagation code if so + ORRS Rtmp,OP2mhi,OP2mlo ;Is second operand a zero? + BNE Mult_InfShared ;If not, result is an infinity + + [ FPEWanted :LOR: FPASCWanted + + MOV Rtmp,#InvReas_InfTimes0 ;Otherwise, an invalid operation + B InvalidOperation2ForSDE + + | + + ORR OP1sue,OP1sue,#IVO_bits + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + ] + +Mult_NaNInf2Only + +; The first operand is not a NaN or infinity, the second is. The result is: +; * an invalid operation exception if the second operand is a signalling +; NaN; +; * the second operand unchanged if it is a quiet NaN; +; * an invalid operation exception if the first operand is a zero and the +; second is an infinity; +; * the standard infinity if the first operand is not a zero and the second +; operand is an infinity, with its sign determined by those of the two +; operands. +; Note that we can detect the first operand being zero by its mantissa being +; all zero, since only zeros, some unnormalised URD results, extended +; unnormalised zeros and extended infinities have this property, we're +; assuming the operands are not URD results and we know it isn't an extended +; infinity. + + ORRS Rtmp,OP2mlo,OP2mhi,LSL #1 ;Is second operand a NaN? + BNE $ConvertNaN2Of2_str ;Use standard exception/quiet NaN + ; propagation code if so + ORRS Rtmp,OP1mhi,OP1mlo ;Is first operand a zero? + BNE Mult_InfShared ;If not, result is an infinity + + [ FPEWanted :LOR: FPASCWanted + + MOV Rtmp,#InvReas_0TimesInf ;Otherwise, an invalid operation + B InvalidOperation2ForSDE + + | + + ORR OP1sue,OP1sue,#IVO_bits + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + ] + + ] + +;=========================================================================== + + [ :DEF: div_s :LOR: FPEWanted :LOR: FPASCWanted + +; The second entry point to the normal/fast division/reverse division +; routine, meant for use by the FPASC and without a fast track for common +; operands. +; The value returned is either a numeric value plus associated rounding +; information, with the uncommon bit clear, or an infinity or NaN, with the +; uncommon bit set. +; This routine will not work correctly with inputs which are unnormalised +; URD results, or with invalid internal format numbers. +; +; Uses standard dyadic operation entry and exit conventions - see top of +; this file. + + [ FPASCWanted + +DivFPASC + + CDebug3 3,"DivFPASC: op1 =",OP1sue,OP1mhi,OP1mlo + CDebug3 3," op2 =",OP2sue,OP2mhi,OP2mlo + + ] + + [ FPLibWanted +__fp_div_uncommon +__fp_rdv_uncommon + ] + +Div_Uncommon + +; We have to do a full division, since either or both of the operands may be +; uncommon. What we will do is: +; +; (a) Check for NaNs. If found, produce an invalid operation exception and +; suitable NaN result. +; +; (b) Check for infinities. If found, the result is: +; * An invalid operation exception if both operands are infinities; +; * An infinite result if the dividend is an infinity and the +; divisor is numeric; +; * A zero result if the dividend is numeric and the divisor is an +; infinity; +; +; (c) Check for zeros. If found, the result is: +; * An invalid operation exception if both operands are zeros; +; * A divide-by-zero exception if the dividend is non-zero and the +; divisor is zero; +; * A zero if the dividend is zero and the divisor is non-zero. +; +; (d) If no NaNs, infinities or zeros, we can transform the problem into +; that of dividing a normalised number by another, though the +; normalised numbers concerned may have unusual exponents. +; +; So the first thing we do is check for NaNs and infinities - if we find +; one, we'll generate the result by special case code. Note that we check +; for them together, since they have similar bit patterns. + + TNaNInf Rtmp2,OP2sue,OP2mhi ;Rtmp2[31] := (op2 is NaN/inf) + TNaNInf Rtmp,OP1sue,OP1mhi ;Rtmp[31] := (op1 is NaN/inf) + BMI Div_NaNInf1 + TST Rtmp2,#TopBit ;Operand 2 NaN or infinity? + BNE Div_NaNInf2Only + +; Now if either operand is a zero, we need to take special action. We can +; detect zeros by the mantissa being all zero, since only zeros, some +; unnormalised URD results, extended unnormalised zeros and extended +; infinities have this property, we're assuming the operands are not URD +; results and we've already dealt with extended infinities. + + [ FPEWanted :LOR: FPASCWanted + + ORRS Rtmp,OP1mhi,OP1mlo + ORRNES Rtmp,OP2mhi,OP2mlo + BEQ Div_Zero + +; Both operands are now going to be converted to normalised numbers. We now +; know that we are not going to need to know the operands for trap purposes, +; so we can swap them if this is a normal (rather than reverse) division. + + TST Rins,#RevDiv_bit + | + TST Rins,#Reverse + ] + BNE Div_Uncommon_Swapped + + MOV Rtmp,OP1sue + MOV OP1sue,OP2sue + MOV OP2sue,Rtmp + MOV Rtmp,OP1mhi + MOV OP1mhi,OP2mhi + MOV OP2mhi,Rtmp + MOV Rtmp,OP1mlo + MOV OP1mlo,OP2mlo + MOV OP2mlo,Rtmp + +Div_Uncommon_Swapped + +; Both operands are now normalised numbers, denormalised numbers or extended +; unnormalised non-zero numbers. The first step is to convert all of these +; to normalised numbers, possibly with a negative biased exponent. After +; doing the exponent and sign calculations, we then call Div_Mantissas to +; complete the calculation. +; The types of numbers that require converting are extended unnormalised +; numbers and denormalised numbers of all precisions. In the case of the +; extended denormalised and unnormalised numbers, this just requires us to +; normalise them; in the case of the single and double denormalised numbers, +; we need to clear their units bits and add 1 to their exponents before we +; normalise them. +; At this stage, we can recognise that the numbers are single or double +; denormalised numbers simply by the fact that they have uncommon = units = +; 1: all other numbers with this property are NaNs or infinities and have +; been dealt with already. + + ANDS Rarith,OP1mhi,OP1sue,LSL #EIUnits_pos-Uncommon_pos + ASSERT EIUnits_pos = 31 + BICMI OP1mhi,OP1mhi,#EIUnits_bit + ADDMI OP1sue,OP1sue,#1:SHL:EIExp_pos + ANDS Rarith,OP2mhi,OP2sue,LSL #EIUnits_pos-Uncommon_pos + ASSERT EIUnits_pos = 31 + BICMI OP2mhi,OP2mhi,#EIUnits_bit + ADDMI OP2sue,OP2sue,#1:SHL:EIExp_pos + + AND Rtmp,OP1sue,#ToExp_mask + AND Rtmp2,OP2sue,#ToExp_mask + EOR OP1sue,OP1sue,OP2sue ;Produce result sign + AND OP1sue,OP1sue,#Sign_bit + SUB RNDexp,Rtmp2,Rtmp + ADD RNDexp,RNDexp,#EIExp_bias:AND:&FF00 + ADD RNDexp,RNDexp,#EIExp_bias:AND:&FF + ASSERT EIExp_bias < &10000 ;Result exponent if no mantissa + ; underflow is exp1-exp2+bias + + STMFD Rsp!,{LR} ;We will have subroutine calls below + + TST OP1mhi,#EIUnits_bit + BLEQ $NormaliseOp1Neg_str + TST OP2mhi,#EIUnits_bit + BLEQ $NormaliseOp2_str + + LDMFD Rsp!,{LR} + B Div_Mantissas + + [ FPEWanted :LOR: FPASCWanted + +Div_Zero + +; One or both operands are zeros, and both are numeric values (i.e. not NaNs +; or infinities). The result is: +; * An invalid operation exception if both operands are zeros; +; * A divide-by-zero exception if the dividend is non-zero and the divisor +; is zero; +; * A zero if the dividend is zero and the divisor is non-zero. +; +; Split according to whether this is a normal or reverse division. + + MOV Rtmp,#InvReas_0Div0 ;The only type of invalid operation + ; that occurs below + TST Rins,#RevDiv_bit + BNE Div_Zero_Reversed + +; It's a normal division - check the three cases above. + + ORRS Rtmp2,OP1mhi,OP1mlo ;Check dividend + BNE DivideByZero2 + ORRS Rtmp2,OP2mhi,OP2mlo ;Check divisor + BEQ InvalidOperation2ForSDE + +Div_ZeroByX + +; The result is zero. + + EOR OP1sue,OP1sue,OP2sue ;Get sign right + AND OP1sue,OP1sue,#Sign_bit ;Uncommon bit is zero + MOV OP1mhi,#0 ;So is mantissa + MOV OP1mlo,#0 + MOV RNDexp,#0 ;And exponent + MOV Rarith,#0 ;And round/sticky bits + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +Div_Zero_Reversed + +; It's a reverse division - check the three cases above. + + ORRS Rtmp2,OP1mhi,OP1mlo ;Check divisor + BNE Div_ZeroByX + ORRS Rtmp2,OP2mhi,OP2mlo ;Check dividend + BNE DivideByZero2 + B InvalidOperation2ForSDE + + ] + +Div_NaNInf1 + +; The first operand is a NaN or infinity, the second may be (the top bit of +; Rtmp2 indicates whether it is). + + TST Rtmp2,#TopBit + BEQ Div_NaNInf1Only + +; Both operands are NaNs or infinities. If both operands are infinities, the +; result is an invalid operation. +; If either operand is a NaN, the standard exception/NaN propagation rules +; apply. + + ORR Rtmp,OP1mlo,OP1mhi,LSL #1 ;Test if both are infinities + ORR Rtmp,Rtmp,OP2mlo + ORRS Rtmp,Rtmp,OP2mhi,LSL #1 + BNE $ConvertNaNs_str ;If not, use shared code + + [ FPEWanted :LOR: FPASCWanted + + MOV Rtmp,#InvReas_InfDivInf + B InvalidOperation2ForSDE + + | + + ORR OP1sue,OP1sue,#IVO_bits + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + ] + +Div_NaNInf1Only + +; The first operand is a NaN or infinity, the second isn't. The result is: +; * an invalid operation exception if the first operand is a signalling +; NaN; +; * the first operand unchanged if it is a quiet NaN; +; * a standard infinity with sign equal to the exclusive-OR of the two +; operand signs if the first operand is an infinity and the instruction +; is a normal division; +; * a zero with sign equal to the exclusive-OR of the two operand signs if +; the first operand is an infinity and the instruction is a reverse +; division. + + ORRS Rtmp,OP1mlo,OP1mhi,LSL #1 ;Is first operand a NaN? + BNE $ConvertNaN1Of2_str ;Use standard exception/quiet NaN + ; propagation code if so + EOR Rtmp,OP1sue,OP2sue + AND Rtmp,Rtmp,#Sign_bit + [ FPASCWanted :LOR: FPEWanted + TST Rins,#RevDiv_bit + | + TST Rins,#Reverse + ] + ADREQ OP1sue,Prototype_Infinity + ADRNE OP1sue,Prototype_Zero + LDMIA OP1sue,OP1regs + ORR OP1sue,OP1sue,Rtmp + MOV RNDexp,#0 ;These two are only needed when + MOV Rarith,#0 ; result is zero + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +Div_NaNInf2Only + +; The first operand is not a NaN or infinity, the second is. The result is: +; * an invalid operation exception if the second operand is a signalling +; NaN; +; * the second operand unchanged if it is a quiet NaN; +; * a standard infinity with sign equal to the exclusive-OR of the two +; operand signs if the first operand is an infinity and the instruction +; is a reverse division; +; * a zero with sign equal to the exclusive-OR of the two operand signs if +; the first operand is an infinity and the instruction is a normal +; division. + + ORRS Rtmp,OP2mlo,OP2mhi,LSL #1 ;Is second operand a NaN? + BNE $ConvertNaN2Of2_str ;Use standard exception/quiet NaN + ; propagation code if so + + EOR Rtmp,OP1sue,OP2sue + AND Rtmp,Rtmp,#Sign_bit + [ FPEWanted :LOR: FPASCWanted + TST Rins,#RevDiv_bit + | + TST Rins,#Reverse + ] + ADRNE OP1sue,Prototype_Infinity + ADREQ OP1sue,Prototype_Zero + LDMIA OP1sue,OP1regs + ORR OP1sue,OP1sue,Rtmp + MOV RNDexp,#0 ;These two are only needed when + MOV Rarith,#0 ; result is zero + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + ] + +;=========================================================================== + + [ :DEF: fmod_s :LOR: FPEWanted :LOR: FPASCWanted + +; The second part of the IEEE remainder function. + +Rem_Uncommon + +; One or both of the operands may be uncommon. What we will do is: +; +; (a) Check for NaNs. If found, produce an invalid operation exception and +; suitable NaN result. +; +; (b) Check for infinities. If found, the result is: +; * An invalid operation exception if the first operand is an +; infinity. +; * Equal to the first operand if the second operand is an infinity +; and the first isn't. +; +; (c) Check for zeros. If found, the result is: +; * An invalid operation exception if the second operand is a zero; +; * Equal to the first operand if the first operand is a zero and +; the second isn't; +; +; (d) If no NaNs, infinities or zeros, we can transform the problem into +; that of doing the remainder of one normalised number by another, +; though the normalised numbers concerned may have unusual exponents. +; +; So the first thing we do is check for NaNs and infinities - if we find +; one, we'll generate the result by special case code. Note that we check +; for them together, since they have similar bit patterns. + + TNaNInf Rtmp2,OP2sue,OP2mhi ;Rtmp2[31] := (op2 is NaN/inf) + TNaNInf Rtmp,OP1sue,OP1mhi ;Rtmp[31] := (op1 is NaN/inf) + BMI Rem_NaNInf1 + TST Rtmp2,#TopBit ;Operand 2 NaN or infinity? + BNE Rem_NaNInf2Only + +; Now if the second operand is a zero, we've got an invalid operation, and +; if it isn't but the first operand is, we've got a result equal to the +; first operand. We can detect zeros by the mantissa being all zero, since +; only zeros, some unnormalised URD results, extended unnormalised zeros and +; extended infinities have this property, we're assuming the operands are +; not URD results and we've already dealt with extended infinities. + + ORRS Rtmp,OP2mhi,OP2mlo + + [ FPEWanted :LOR: FPASCWanted + + MOVEQ Rtmp,#InvReas_XRem0 + BEQ InvalidOperation2ForSDE + + | + + ORREQ OP1sue,OP1sue,#IVO_bits + IF Interworking :LOR: Thumbing + BXEQ LR + ELSE + MOVEQ PC,LR + ENDIF + + ] + + ORRS Rarith,OP1mhi,OP1mlo + BEQ Rem_FirstOperand_Zero + +; Both operands may now be forced to be normalised numbers - after we've +; dealt with signs and exponents, we can rejoin the main code. +; The types of numbers that require converting are extended unnormalised +; numbers and denormalised numbers of all precisions. In the case of the +; extended denormalised and unnormalised numbers, this just requires us to +; normalise them; in the case of the single and double denormalised numbers, +; we need to clear their units bits and add 1 to their exponents before we +; normalise them. +; At this stage, we can recognise that the numbers are single or double +; denormalised numbers simply by the fact that they have uncommon = units = +; 1: all other numbers with this property are NaNs or infinities and have +; been dealt with already. + + ANDS Rarith,OP1mhi,OP1sue,LSL #EIUnits_pos-Uncommon_pos + ASSERT EIUnits_pos = 31 + BICMI OP1mhi,OP1mhi,#EIUnits_bit + ADDMI OP1sue,OP1sue,#1:SHL:EIExp_pos + ANDS Rarith,OP2mhi,OP2sue,LSL #EIUnits_pos-Uncommon_pos + ASSERT EIUnits_pos = 31 + BICMI OP2mhi,OP2mhi,#EIUnits_bit + ADDMI OP2sue,OP2sue,#1:SHL:EIExp_pos + + STMFD Rsp!,{LR} ;We will have subroutine calls below + + AND RNDexp,OP2sue,#ToExp_mask ;Raw second operand exponent + TST OP2mhi,#EIUnits_bit ;Normalise second operand, + BLEQ $NormaliseOp2_str ; then adjust to get + SUB Rtmp2,RNDexp,#1 ; prospective result exp. + + AND RNDexp,OP1sue,#ToExp_mask ;Raw first operand exponent + TST OP2mhi,#EIUnits_bit ;Normalise first operand + BLEQ $NormaliseOp1_str ; then determine the number + SUBS Rarith,RNDexp,Rtmp2 ; of iterations - 1 + MOV RNDexp,Rtmp2 ;Get prospective result exp. + ; back where it's wanted + +; All the special exponent handling is done, so we might as well rejoin the +; main code. + + B Rem_ExponentsDone + +Rem_NaNInf1 + +; The first operand is a NaN or infinity, the second may be (the top bit of +; Rtmp2 indicates whether it is). + + TST Rtmp2,#TopBit + BEQ Rem_NaNInf1Only + +; Both operands are NaNs or infinities. If both operands are infinities, the +; result is an invalid operation. +; If either operand is a NaN, the standard exception/NaN propagation rules +; apply. + + ORR Rtmp,OP1mlo,OP1mhi,LSL #1 ;Test if both are infinities + ORR Rtmp,Rtmp,OP2mlo + ORRS Rtmp,Rtmp,OP2mhi,LSL #1 + BNE $ConvertNaNs_str ;If not, use shared code + + [ FPEWanted :LOR: FPASCWanted + + MOV Rtmp,#InvReas_InfRemX + B InvalidOperation2ForSDE + + | + + ORR OP1sue,OP1sue,#IVO_bits + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + ] + +Rem_NaNInf1Only + +; The first operand is a NaN or infinity, the second isn't. The result is: +; * an invalid operation exception if the first operand is a signalling +; NaN; +; * the first operand unchanged if it is a quiet NaN; +; * an invalid operation if it is an infinity. + + ORRS Rtmp,OP1mlo,OP1mhi,LSL #1 ;Is first operand a NaN? + BNE $ConvertNaN1Of2_str ;Use standard exception/quiet NaN + ; propagation code if so + + [ FPEWanted :LOR: FPASCWanted + + MOV Rtmp,#InvReas_InfRemX + B InvalidOperation2ForSDE + + | + + ORR OP1sue,OP1sue,#IVO_bits + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + ] + +Rem_NaNInf2Only + +; The first operand is not a NaN or infinity, the second is. The result is: +; * an invalid operation exception if the second operand is a signalling +; NaN; +; * the second operand unchanged if it is a quiet NaN; +; * equal to the first operand if the second operand is an infinity. + + ORRS Rtmp,OP2mlo,OP2mhi,LSL #1 ;Is second operand a NaN? + BNE $ConvertNaN2Of2_str ;Use standard exception/quiet NaN + ; propagation code if so + +Rem_FirstOperand + +; If the first operand is common, life is easy. + + TST OP1sue,#Uncommon_bit + ANDEQ RNDexp,OP1sue,#ToExp_mask + ANDEQ OP1sue,OP1sue,#Sign_bit + MOVEQ Rarith,#0 + IF Interworking :LOR: Thumbing + BXEQ LR + ELSE + MOVEQ PC,LR + ENDIF + +; If it's uncommon, life is trickier. First check for zeros. + + ORRS Rarith,OP1mhi,OP1mlo + BEQ Rem_FirstOperand_Zero + +; The operand is now a denormalised number or extended unnormalised non-zero +; number; it needs conversion to an internal precision number. In the case +; of the extended denormalised and unnormalised numbers, this just requires +; us to normalise them; in the case of the single and double denormalised +; numbers, we need to clear their units bits and add 1 to their exponents +; before we normalise them. +; +; At this stage, we can recognise that the numbers are single or double +; denormalised numbers simply by the fact that they have a units bit of 1: +; all other uncommon numbers with this property are NaNs or infinities and +; have been dealt with already. + + AND RNDexp,OP1sue,#ToExp_mask ;Extract operand exponent + AND OP1sue,OP1sue,#Sign_bit ; and its sign + + TST OP1mhi,#EIUnits_bit + BICNE OP1mhi,OP1mhi,#EIUnits_bit + ADDNE RNDexp,RNDexp,#1 + + MOV Rarith,#0 ;Result is exact. + B $NormaliseOp1_str ;NB must be necessary, so no + ; point in checking whether + ; normalised + +Rem_FirstOperand_Zero + + AND OP1sue,OP1sue,#Sign_bit + MOV RNDexp,#0 ;We already know OP1mhi, OP1mlo and + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR ; Rarith are zero + ENDIF + + ] + +;=========================================================================== + +Prototype_Zero + DCD &00000000,&00000000,&00000000 + +Prototype_Infinity + DCD &40007FFF,&00000000,&00000000 + +;=========================================================================== + + [ :DEF: sqrt_s :LOR: FPEWanted :LOR: FPASCWanted + +; The second part of the square root routine, which deals with uncommon +; operands. + + [ FPLibWanted +__fp_sqrt_uncommon + ] + +Sqrt_Uncommon + +; We have to deal with the square root of an uncommon value. The cases are: +; +; * The square root of a signalling NaN is an invalid operation; +; +; * The square root of a quiet NaN is the NaN itself; +; +; * The square root of plus infinity is plus infinity; +; +; * The square root of minus infinity is an invalid operation; +; +; * The square root of an extended unnormalised zero is a zero of the same +; sign; +; +; * The square roots of denormalised numbers and extended unnormalised +; numbers can be determined by transforming them into normalised numbers +; (possibly with an out-of-range exponent), then using the standard +; square root code above. +; +; So the first thing we do is check for NaNs and infinities - if we find +; one, we'll generate the result by special case code. Note that we check +; for them together, since they have similar bit patterns. + + TNaNInf Rtmp,OP1sue,OP1mhi ;Rtmp[31] := (op is NaN/inf) + BMI Sqrt_NaNInf + +; Now if the operand is a zero, the result is a zero of the same sign. We +; can detect zeros by the mantissa being all zero, since only zeros, some +; unnormalised URD results, extended unnormalised zeros and extended +; infinities have this property, we're assuming the operand is not a URD +; result and we've already dealt with extended infinities. + + ORRS Rtmp,OP1mhi,OP1mlo + ANDEQ OP1sue,OP1sue,#Sign_bit + BEQ Sqrt_Zero + +; The operand is now a denormalised number or extended unnormalised non-zero +; number. If it is negative, we've got an invalid operation. Otherwise, we +; know that no invalid operation or divide-by-zero exception is going to +; occur, so we can convert it to a normalised number, possibly with a +; negative biased exponent. After doing the exponent and sign calculations, +; we then call Sqrt_Mantissa to complete the calculation. +; The types of numbers that require converting are extended unnormalised +; numbers and denormalised numbers of all precisions. In the case of the +; extended denormalised and unnormalised numbers, this just requires us to +; normalise them; in the case of the single and double denormalised numbers, +; we need to clear their units bits and add 1 to their exponents before we +; normalise them. +; At this stage, we can recognise that the numbers are single or double +; denormalised numbers simply by the fact that they have a units bit of 1: +; all other numbers with this property are NaNs or infinities and have +; been dealt with already. + + AND RNDexp,OP1sue,#ToExp_mask ;Extract operand exponent + + ANDS OP1sue,OP1sue,#Sign_bit + + [ FPEWanted :LOR: FPASCWanted + + MOVNE Rtmp,#InvReas_SqrtNeg + BNE InvalidOperation1ForSDE + + | + + ORRNE OP1sue,OP1sue,#IVO_bits + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + ] + + STMFD Rsp!,{LR} ;We will have subroutine calls below + + TST OP1mhi,#EIUnits_bit + BICNE OP1mhi,OP1mhi,#EIUnits_bit + ADDNE RNDexp,RNDexp,#1 + + BL $NormaliseOp1_str ;NB must be necessary, so no + ; point in checking whether + ; normalised + + ADD RNDexp,RNDexp,#EIExp_bias:AND:&FF00 + ADD RNDexp,RNDexp,#EIExp_bias:AND:&FF + ASSERT (EIExp_bias-1) < &10000 ;Result exponent if mantissa + ; overflow is (exp+bias) DIV 2 + MOVS RNDexp,RNDexp,LSR #1 + + LDMFD Rsp!,{LR} + B Sqrt_Mantissa + +Sqrt_Zero + +; The result is equal to the operand, which is a zero. + + MOV RNDexp,#0 ;Clear exponent + MOV Rarith,#0 ;And round/sticky bits + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +Sqrt_NaNInf + +; The operand is a NaN or infinity. If it's a NaN, we use the standard +; rules for propagating NaNs. If an infinity, we've got an invalid operation +; if it is negative and a result equal to the standard plus infinity if it +; is positive. + + ORRS Rtmp,OP1mlo,OP1mhi,LSL #1 ;Is operand a NaN? + BNE $ConvertNaN1_str ;Use standard exception/quiet NaN + ; propagation code if so + TST OP1sue,#Sign_bit + + [ FPEWanted :LOR: FPASCWanted + + MOVNE Rtmp,#InvReas_SqrtNeg + BNE InvalidOperation1ForSDE + + ADR OP1sue,Prototype_Infinity + LDMIA OP1sue,OP1regs + + | + + ORRNE OP1sue,OP1sue,#IVO_bits + ADREQ OP1sue,Prototype_Infinity + LDMEQIA OP1sue,OP1regs + + ] + + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + ] + +;=========================================================================== + + [ FPEWanted :LOR: FPASCWanted + +; The second entry point to the move/move negated/absolute value routine, +; meant for use by the FPASC. +; This routine will not work correctly with an input which is an +; unnormalised URD result, or an invalid internal format number. +; +; Uses standard monadic operation entry and exit conventions - see top of +; this file. + + [ FPASCWanted + +MoveFPASC + + CDebug3 3,"MoveFPASC: operand =",OP1sue,OP1mhi,OP1mlo + +; The FPA does not bounce common values in the Prepare stage for these +; instructions, so no need to check the uncommon bit. + + ] + +Move_Uncommon + +; Only uncommon values will get here. First split out NaNs and infinities. + + TNaNInf Rtmp,OP1sue,OP1mhi ;Rtmp[31] := (op is NaN/inf) + BMI Move_NaNInf + +; The value is an uncommon numeric value - i.e. a denormalised number, an +; extended unnormalised number or an extended unnormalised zero. If it's the +; last of these, change it to a real zero and treat it as a numeric. + + ORRS Rtmp,OP1mhi,OP1mlo + MOVEQ RNDexp,#0 + BEQ Move_Numeric + +; The operand is now a denormalised number or extended unnormalised non-zero +; number. We will change it into the corresponding normalised number +; (possibly with a negative biased exponent), then treat it as a numeric. +; The types of numbers that require converting are extended unnormalised +; numbers and denormalised numbers of all precisions. In the case of the +; extended denormalised and unnormalised numbers, this just requires us to +; normalise them; in the case of the single and double denormalised numbers, +; we need to clear their units bits and add 1 to their exponents before we +; normalise them. +; At this stage, we can recognise that the numbers are single or double +; denormalised numbers simply by the fact that they have uncommon = units = +; 1: all other numbers with this property are NaNs or infinities and have +; been dealt with already. + + AND RNDexp,OP1sue,#ToExp_mask + ASSERT EIExp_pos = 0 + + STMFD Rsp!,{LR} ;We will have subroutine calls below + + ANDS Rarith,OP1mhi,OP1sue,LSL #EIUnits_pos-Uncommon_pos + ASSERT EIUnits_pos = 31 + BICMI OP1mhi,OP1mhi,#EIUnits_bit + ADDMI RNDexp,RNDexp,#1 + + BL $NormaliseOp1_str ;NB must be necessary, so no + ; point in checking whether + ; normalised + + LDMFD Rsp!,{LR} + B Move_Numeric + +Move_NaNInf + +; The operand is a NaN or infinity. If it's an infinity, we just want to +; perform the standard sign manipulations on it and return a standard +; infinity. If it's a NaN, we need to pay attention to the implicit IEEE +; format conversion. + + ORRS Rtmp,OP1mlo,OP1mhi,LSL #1 ;Is operand a NaN? + BNE Move_NaN + AND Rtmp,OP1sue,#Sign_bit ;Isolate sign + TST Rins,#MNF_bit ;Do sign manipulations + EORNE Rtmp,Rtmp,#Sign_bit + TST Rins,#ABS_bit + BICNE Rtmp,Rtmp,#Sign_bit + ADR OP1sue,Prototype_Infinity + LDMIA OP1sue,OP1regs + ORR OP1sue,OP1sue,Rtmp + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +Move_NaN + + STMFD Rsp!,{LR} + BL NaNConversionNeeded + TEQ Rarith,#0 ;Conversion needed? + BMI Move_NaN_DoSigns ;Just alter signs if not + BL ConvertNaN1_Special ;Do correct NaN conversion + IF Interworking :LOR: Thumbing + LDMNEFD Rsp!,{LR} ;We're done and must *not* alter + ; signs if an invalid operation trap + ; occurred + BXNE LR + ELSE + LDMNEFD Rsp!,{PC} ;We're done and must *not* alter + ; signs if an invalid operation trap + ; occurred + ENDIF + +Move_NaN_DoSigns + +; Do the sign manipulations and return. + + TST Rins,#MNF_bit + EORNE OP1sue,OP1sue,#Sign_bit + TST Rins,#ABS_bit + BICNE OP1sue,OP1sue,#Sign_bit + IF Interworking :LOR: Thumbing + LDMFD Rsp!,{LR} + BX LR + ELSE + LDMFD Rsp!,{PC} + ENDIF + + ] + +;=========================================================================== + + [ FPEWanted :LOR: FPASCWanted + +; The second entry point to the NRM routine, intended for use by the FPASC. +; +; Uses standard monadic operation entry and exit conventions - see top of +; this file. + + [ FPASCWanted + +NormFPASC + + CDebug3 3,"NormFPASC: operand =",OP1sue,OP1mhi,OP1mlo + +; The FPA does not bounce common values in the Prepare stage for these +; instructions, so no need to check the uncommon bit. + + ] + +Norm_Uncommon + +; Only uncommon values will get here. First split out all but NaNs and +; infinities. + + TNaNInf Rtmp,OP1sue,OP1mhi ;Rtmp[31] := (op is NaN/inf) + ANDPL RNDexp,OP1sue,#ToExp_mask + BPL Norm_ZeroUnnormOrDenorm + +NormUrd_NaNInf + +; The operand is a NaN or infinity. If it's an infinity, we just want to +; return a standard infinity. If it's a NaN, we use the standard NaN +; propagation code. + + ORRS Rtmp,OP1mlo,OP1mhi,LSL #1 ;Check for NaNs + BNE $ConvertNaN1_str + AND Rtmp,OP1sue,#Sign_bit ;Isolate sign + ADR OP1sue,Prototype_Infinity + LDMIA OP1sue,OP1regs + ORR OP1sue,OP1sue,Rtmp + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + ] + +;=========================================================================== + + [ FPEWanted :LOR: FPASCWanted + +; The second entry point to the URD routine, meant for use by the FPASC and +; optimised for uncommon operands. +; This routine will not work correctly with inputs which are unnormalised +; URD results, or with invalid internal format numbers. +; +; Uses standard monadic operation entry and exit conventions - see top of +; this file. + + [ FPASCWanted + +UrdFPASC + + CDebug3 3,"UrdFPASC: operand =",OP1sue,OP1mhi,OP1mlo + +; The FPA does not bounce common values in the Prepare stage for these +; instructions, so no need to check the uncommon bit. + + ] + +Urd_Uncommon + +; Split out NaNs and infinities, which are dealt with in exactly the same +; way as by the NRM instruction. + + TNaNInf Rtmp,OP1sue,OP1mhi ;Rtmp[31] := (op1 is NaN/inf) + BMI NormUrd_NaNInf + +; The operand is now known to be a denormalised number or an extended +; precision unnormalised number or zero. We have to take a little care about +; single and double precision denormalised numbers, since their exponents +; and mantissas need correcting. Otherwise, we can just use the standard +; Urd_Numeric routine on them once we have separated the sign and the +; exponent from each other. We can recognise the single and double +; denormalised numbers by the fact that they are the only remaining cases +; with a units bit of 1. + + AND Rarith,OP1sue,#ToExp_mask ;Extract operand exponent + AND OP1sue,OP1sue,#Sign_bit ; and sign + + TST OP1mhi,#EIUnits_bit + BICNE OP1mhi,OP1mhi,#EIUnits_bit + ADDNE Rarith,Rarith,#1 + + B Urd_Numeric + + ] + +;=========================================================================== + + [ FPEWanted :LOR: FPASCWanted + +; The second part of the RND routine, which deals with uncommon operands. + +Rnd_Uncommon + +; Split out NaNs and infinities, which are dealt with in exactly the same +; way as by the NRM instruction. + + TNaNInf Rtmp,OP1sue,OP1mhi ;Rtmp[31] := (op1 is NaN/inf) + BMI NormUrd_NaNInf + +; The value is an uncommon numeric value - i.e. a denormalised number, an +; extended unnormalised number or an extended unnormalised zero. If it's the +; last of these, change it to a real zero and treat it as a numeric. + + ORRS RNDexp,OP1mhi,OP1mlo + ANDEQ OP1sue,OP1sue,#Sign_bit + BEQ Rnd_Exact + +; The operand is now a denormalised number or extended unnormalised non-zero +; number. We will change it into the corresponding normalised number +; (possibly with a negative biased exponent), then treat it as a numeric. +; The types of numbers that require converting are extended unnormalised +; numbers and denormalised numbers of all precisions. In the case of the +; extended denormalised and unnormalised numbers, this just requires us to +; normalise them; in the case of the single and double denormalised numbers, +; we need to clear their units bits and add 1 to their exponents before we +; normalise them. +; At this stage, we can recognise that the numbers are single or double +; denormalised numbers simply by the fact that they have uncommon = units = +; 1: all other numbers with this property are NaNs or infinities and have +; been dealt with already. + + AND RNDexp,OP1sue,#ToExp_mask + AND OP1sue,OP1sue,#Sign_bit + ASSERT EIExp_pos = 0 + + STMFD Rsp!,{LR} ;We will have subroutine calls below + + TST OP1mhi,#EIUnits_bit + BICNE OP1mhi,OP1mhi,#EIUnits_bit + ADDNE RNDexp,RNDexp,#1 + + BL $NormaliseOp1_str ;NB must be necessary, so no + ; point in checking whether + ; normalised + + LDMFD Rsp!,{LR} + B Rnd_Numeric + + ] + +;=========================================================================== + + [ :DEF: compare_s :LOR: FPEWanted :LOR: FPASCWanted + +; The second entry point to the comparison routine, meant for use by the +; FPASC and without a fast track for common operands. +; This routine will not work correctly with inputs which are unnormalised +; URD results, or with invalid internal format numbers. +; +; Has the same entry and exit conventions as "CompareFPE" above. + + [ FPASCWanted + +CompareFPASC + + CDebug3 3,"CompareFPASC: op1 =",OP1sue,OP1mhi,OP1mlo + CDebug3 3," op2 =",OP2sue,OP2mhi,OP2mlo + + ] + +Compare_Uncommon + +; We have to do a full comparison, since either or both of the operands may +; be uncommon. What we will do is: +; +; (a) Check for NaNs. If found, produce a trap if appropriate, or a result +; of "unordered" otherwise. +; +; (b) If no NaNs, adjust the operands by replacing all infinities by the +; standard extended infinity, and all effectively unnormalised numbers +; by the corresponding normalised or denormalised number. Then call +; Compare_Common, which will work correctly on zeros, denormalised +; numbers, normalised numbers and extended infinities. +; +; So the first thing we do is check for NaNs. This is done by first testing +; for a NaN or infinity (they have similar bit patterns) by a standard +; technique, then checking whether the fraction is non-zero. + + TNaNInf Rtmp,OP1sue,OP1mhi ;Rtmp[31] := (op1 is NaN/inf) + TNaNInf Rtmp2,OP2sue,OP2mhi ;Rtmp2[31] := (op2 is NaN/inf) + TST Rtmp,#TopBit ;Operand 1 NaN or infinity? + ORRNES Rarith,OP1mlo,OP1mhi,LSL #1 ;If so, is it a NaN? + BNE Compare_Unordered + TST Rtmp2,#TopBit ;Operand 2 NaN or infinity? + ORRNES Rarith,OP2mlo,OP2mhi,LSL #1 ;If so, is it a NaN? + BNE Compare_Unordered + +; Now we know there are no NaNs and therefore no exceptions - which means we +; no longer need to keep track of exactly what the operands are. We are +; going to massage the operands into a form where we can use the +; Compare_Common routine on them - note that it already works for zeros, +; normalised numbers, extended denormalised numbers and normal extended +; precision infinities. The remaining numbers are the other infinities, the +; extended unnormalised numbers and zeros, and the single and double +; precision denormalised numbers. +; We will first convert all the infinities to a standard extended +; precision infinity, to ensure that they compare equal with each other. Or +; rather, an almost standard one - we will mark the result as common to +; avoid mistaking it for an unnormalised or denormalised number later on. + + STMFD Rsp!,{LR} ;We're likely to make subroutine calls + + TST Rtmp,#TopBit + ANDNE OP1sue,OP1sue,#Sign_bit + ORRNE OP1sue,OP1sue,#&FF + ORRNE OP1sue,OP1sue,#&7F00 + BICNE OP1mhi,OP1mhi,#EIUnits_bit + TST Rtmp2,#TopBit + ANDNE OP2sue,OP2sue,#Sign_bit + ORRNE OP2sue,OP2sue,#&FF + ORRNE OP2sue,OP2sue,#&7F00 + BICNE OP2mhi,OP2mhi,#EIUnits_bit + +; Now we need to deal with the extended unnormalised numbers and zeros, and +; the single and double denormalised numbers. These basically need +; converting to extended precision normalised or denormalised numbers. In +; the case of the extended unnormalised numbers and zeros, this just +; requires us to normalise them; in the case of the single and double +; denormalised numbers, we need to clear their units bits and add 1 to their +; exponents before we normalise them. +; At this stage, we can recognise that the numbers are single or double +; denormalised numbers simply by the fact that they have uncommon = units = +; 1: all other numbers with this property are NaNs or infinities and have +; been dealt with already. + + ANDS Rarith,OP1mhi,OP1sue,LSL #EIUnits_pos-Uncommon_pos + ASSERT EIUnits_pos = 31 + BICMI OP1mhi,OP1mhi,#EIUnits_bit + ADDMI OP1sue,OP1sue,#1:SHL:EIExp_pos + ANDS Rarith,OP2mhi,OP2sue,LSL #EIUnits_pos-Uncommon_pos + ASSERT EIUnits_pos = 31 + BICMI OP2mhi,OP2mhi,#EIUnits_bit + ADDMI OP2sue,OP2sue,#1:SHL:EIExp_pos + +; Now we need to normalise all these types of numbers, which now means all +; uncommon numbers except those with exponent 0 (which are extended +; precision denormalised numbers and should be left alone). + + TST OP1sue,#Uncommon_bit + Exp2Top Rarith,OP1sue,NE,S ;Complete test & set up for call + BLNE $NormDenormOp1_str + TST OP2sue,#Uncommon_bit + Exp2Top Rarith,OP2sue,NE,S ;Complete test & set up for call + BLNE $NormDenormOp2_str + +; And now we can compare the results as though they were common numbers. + + LDMFD Rsp!,{LR} + B Compare_Common + +Compare_Unordered + +; The result is definitely unordered. We need to choose the correct result. + + TST Rfpsr,#AC_bit + MOVEQ Rarith,#Comp_Un_Orig + MOVNE Rarith,#Comp_Un_Alt + +; Now we need to know whether there's an IEEE exception - there is one if +; either operand is a signalling NaN, or if the instruction is CMFE or CNFE. +; Note that the top bits of Rtmp and Rtmp2 are still NaN/infinity flags for +; the two operands. + + TST Rtmp,#TopBit ;Is operand 1 a NaN? + ORRNES Rtmp,OP1mlo,OP1mhi,LSL #1 + BEQ Compare_Unordered_Op1NotNaN ;If not, operand 2 must be + ANDS Rtmp,OP1mhi,#EIFracTop_bit ;If so, is it signalling? + [ FPLibWanted + MOVEQ Rarith,#IVO_bits + IF Interworking :LOR: Thumbing + BXEQ LR + ELSE + MOVEQ PC,LR + ENDIF + | + BEQ InvalidOperation2ForI ; (invalid operation if so) + ASSERT InvReas_SigNaN = 0 + ] + + TST Rtmp2,#TopBit ;Is operand 2 a NaN? + ORRNES Rtmp,OP2mlo,OP2mhi,LSL #1 + [ FPEWanted :LOR: FPASCWanted + BEQ Compare_Unordered_Op2NotNaN ;Branch if not + | + IF Interworking :LOR: Thumbing + BXEQ LR + ELSE + MOVEQ PC,LR + ENDIF + ] +Compare_Unordered_Op1NotNaN + ANDS Rtmp,OP2mhi,#EIFracTop_bit ;If so, is it signalling? + [ FPLibWanted + MOVEQ Rarith,#IVO_bits + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + | + BEQ InvalidOperation2ForI ; (invalid operation if so) + ASSERT InvReas_SigNaN = 0 + ] + + [ FPEWanted :LOR: FPASCWanted +Compare_Unordered_Op2NotNaN + TST Rins,#CompExc_bit ;Is instruction CMFE/CNFE? + IF Interworking :LOR: Thumbing + BXEQ LR + ELSE + MOVEQ PC,LR ;If not, no exception + ENDIF + MOV Rtmp,#InvReas_CompQNaN ;Otherwise, invalid op + B InvalidOperation2ForI + ] + + ] + +;=========================================================================== + + [ FPEWanted :LOR: FPASCWanted :LOR: :DEF: fix_s :LOR: :DEF: fixu_s + +; The second entry point to the FIX routine, meant for use by the FPASC and +; optimised for uncommon operands. +; This routine will not work correctly with inputs which are unnormalised +; URD results, or with invalid internal format numbers. +; +; Has the same entry and exit conventions as "FixFPE" above. + + [ FPASCWanted + +FixFPASC + + CDebug3 3,"FixFPASC: operand =",OP1sue,OP1mhi,OP1mlo + +; Start by splitting between common and uncommon operands. + + TST OP1sue,#Uncommon_bit + BEQ Fix_Common + + ] + + [ :DEF: fix_s +__fp_fix_uncommon + ] + [ :DEF: fixu_s +__fp_fixu_uncommon + ] + +Fix_Uncommon + +; NaNs and infinities will produce invalid operation exceptions, with the +; precise nature of the exception depending on whether the operand is a +; signalling NaN, a quiet NaN or an infinity. + + TNaNInf Rtmp,OP1sue,OP1mhi ;Rtmp[31] := (op1 is NaN/inf) + BMI Fix_NaNInf + +; The operand is now known to be a denormalised number or an extended +; precision unnormalised number or zero. We have to take a little care about +; single and double precision denormalised numbers, since their exponents +; and mantissas need correcting. Otherwise, we can just use the standard +; Fix_Numeric routine on them once we have separated the sign and the +; exponent from each other. We can recognise the single and double +; denormalised numbers by the fact that they are the only remaining cases +; with a units bit of 1. + + AND Rarith,OP1sue,#ToExp_mask ;Extract operand exponent + [ :LNOT: :DEF: fixu_s + AND OP1sue,OP1sue,#Sign_bit ; and sign + ] + + TST OP1mhi,#EIUnits_bit + BICNE OP1mhi,OP1mhi,#EIUnits_bit + ADDNE Rarith,Rarith,#1 + + B Fix_Numeric + +Fix_NaNInf + +; All of these produce an invalid operation exception, with the reason being +; InvReas_SigNaN for signalling NaNs, InvReas_FixQNaN for quiet NaNs and +; InvReas_FixInf for infinities. + + [ FPEWanted :LOR: FPASCWanted + + TST OP1mhi,#EIFracTop_bit + MOVEQ Rtmp,#InvReas_SigNaN + MOVNE Rtmp,#InvReas_FixQNaN + ORRS Rarith,OP1mlo,OP1mhi,LSL #1 + MOVEQ Rtmp,#InvReas_FixInf + MOV Rarith,#TopBit ;Some sort of integer result + B InvalidOperation1ForI + + | + + MOV OP1sue,#IVO_bits + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + + ] + + ] + +;=========================================================================== + + END diff --git a/base/Kernel/Native/arm/Crt/armdefs.asm b/base/Kernel/Native/arm/Crt/armdefs.asm new file mode 100644 index 0000000..bc13971 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/armdefs.asm @@ -0,0 +1,74 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; Assembler source for FPA support code and emulator +; ================================================== +; Definitions relating to the ARM. Also used by "fplib". +; +; Copyright (C) Advanced RISC Machines Limited, 1992-7. All rights reserved. +; +; RCS Revision: 1 +; Checkin Date: 2007/06/29 02:59:16 +; Revising Author + +;=========================================================================== + +; PSR fields. + +Flags_mask EQU &F0000000 +N_bit EQU &80000000 +Z_bit EQU &40000000 +C_bit EQU &20000000 +V_bit EQU &10000000 + [ {CONFIG}=26 +IntMasks_mask EQU &0C000000 +I_bit EQU &08000000 +F_bit EQU &04000000 +Mode_mask EQU &00000003 +PSR_mask EQU Flags_mask:OR:IntMasks_mask:OR:Mode_mask + ] + [ {CONFIG}=32 +IntMasks_mask EQU &000000C0 +I_bit EQU &00000080 +F_bit EQU &00000040 +Mode_mask EQU &0000001F +Mode_32not26 EQU &00000010 +Mode_USR32 EQU &00000010 +Mode_FIQ32 EQU &00000011 +Mode_IRQ32 EQU &00000012 +Mode_SVC32 EQU &00000013 +Mode_ABT32 EQU &00000017 +Mode_UND32 EQU &0000001B +Mode_SYS32 EQU &0000001F + ] +Mode_USR26 EQU &00000000 +Mode_FIQ26 EQU &00000001 +Mode_IRQ26 EQU &00000002 +Mode_SVC26 EQU &00000003 + +; The ARM vectors. + +Reset_vector EQU &00 +Undef_vector EQU &04 +SWI_vector EQU &08 +Prefetch_vector EQU &0C +Data_vector EQU &10 + [ {CONFIG}=26 +AdrExc_vector EQU &14 + ] +IRQ_vector EQU &18 +FIQ_vector EQU &1C + +; Other ARM constants. + +TopBit EQU &80000000 + +;=========================================================================== + + END diff --git a/base/Kernel/Native/arm/Crt/basic_d.asm b/base/Kernel/Native/arm/Crt/basic_d.asm new file mode 100644 index 0000000..b0ebf9e --- /dev/null +++ b/base/Kernel/Native/arm/Crt/basic_d.asm @@ -0,0 +1,522 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; basic_d.s +; +; Copyright (C) Advanced RISC Machines Limited, 1994. All rights reserved. +; +; RCS Revision: 1 +; Checkin Date: 2007/06/29 02:59:16 +; Revising Author + +; Basic floating point functions +; +; +; Revisions: +; Fixed == and != compares to be IEEE-754 compliant when input QNaNs. +; No exceptions are raised when only QNaNs are the only NaNs input to +; == and !=. Moved NaN detection and exception raising here. +; Removed unnecessary macros for compares that return results in flags. +; Added WindowsCE SEH mechanism support. +; Renamed routines. +; + +; Local storage size and offsets +LOC_SIZE EQU 0x20 +OrgOp2h EQU 0x1C +OrgOp2l EQU 0x18 +OrgOp1h EQU 0x14 +OrgOp1l EQU 0x10 +ExDResl EQU 0x08 +ExOp2h EQU 0x04 +ExOp2l EQU 0x00 +NewResl EQU 0x10 + + + GET fpe.asm + GET kxarm.inc + +;============================================================================== +; Compare +; +; BUGBUG: This documentation is not completely correct. For == and != +; comparisions, only SNANs can raise the invalid operation +; exception. For all other compares, both SNANs and QNANs +; can raise the invalid operation exception and return FALSE +; (they actually compare unordered). When == compares unordered +; (contains 1 or more NANs) it also returns FALSE. When != +; compares unordered, it returns TRUE. See IEEE-754-1985 for +; details. The described behavior is implemented here. +; +; +; +; This isn't as simple as it could be. The problem is that NaNs may cause an +; exception and always compare as FALSE if not signalling. Infinities need to +; be treated as normal numbers, although they look like NaNs. +; Furthermore +0 = -0 needs a special check. +; +; General comparison instruction flow: (this is less than) +; +; OP1 < 0 OR OP2 < 0 +; | +; +--------Y--------------+------------N-------------+ +; | | +; (OP1 OR OP2) NaN? (OP1 OR OP2) NaN? +; | | +; +----N---+---Y------+ +-----Y-------+----N-----+ +; | | | | +; RET OP1 < OP2 OP1 or OP2 inf/NaN? OP1 or OP2 inf/NaN? RET OP1 > OP2 +; | | AND NOT +; +---N--+---Y--+ +---Y--+--N----+ (OP1 = 0 AND OP2 = 0) +; | | | | +; RET OP1 < OP2 (OP1 NaN?) OR (OP2 NaN?) RET OP1 > OP2 +; | | | +; | +--N--+--Y--> exception | +; | | | +; | OP1 < 0 OR OP2 < 0? | +; | | | +; +-----N-------+------------Y-----------+ +; +; The first layer selects between the case where both operands are positive or +; when at least one is negative. The second layer uses a quick test on the +; operands orred together to determine whether they look like a NaN. This check is +; weak: it will get about 4% or 9% 'false hits' for doubles and floats, where +; none of the operands is a NaN. In general false hits occur for very large numbers, +; or for both numbers around 2.0 (one larger, one smaller). +; If the operands are not categorized a NaNs, a normal unsigned compare does the +; actual work. It returns immediately if the highwords of the operands are different. +; Note that the negative case uses a compare with the operands swapped, +; as the order is reversed for negative numbers. The negative case also checks for +; -0 == 0 as a special case. In the NaN code, a more precise check is done, which +; filters out NaNs and infinities, and the normal compare follows otherwise. +; The exception handler raises a Invalid Operation exception if one of the operands +; is a NaN (ignoring the signal bit). +; There are thus 3 different checks on NaNs, with increasing accuracy: +; 1. one of the operands looks like a NaN (but might not be one). +; 2. one of the operands is infinite or NaN. +; 3. one of the operands is a NaN. +; +; The compare routine can either be used as a boolean returning function (dgt, +; dge, dlt, dle) or as a flags returning function (returning < as LO, <= as LS, +; > as HI, >= as HS). +; +; The routine is optimised for the both operands positive which not look like +; NaNs case. It is also assumed the chance that the highwords of the operands are +; equal is less than 50%. Timing: +; Flags: 7/9 (pos), 11/13 (false NaN), 10/12 (neg), 13/15 (false NaN) SA1.1 cycles. +; EQ/NE/HI/HS/LO/LS: 10 / 14 / 13 / 16 +;============================================================================== + + MACRO + CmpReturn $cc + MOV a1, #0 + MOV$cc a1, #1 + ADD sp, sp, #LOC_SIZE + IF Interworking :LOR: Thumbing + LDMFD sp!, {lr} + BX lr + ELSE + LDMFD sp!, {pc} + ENDIF + MEND + + + + MACRO +$lab DoubleCompare $cc, $NaN_lab + + ASSERT "$cc"="LO":LOR:"$cc"="LS":LOR:"$cc"="HS":LOR:"$cc"="HI":LOR:"$cc"="EQ":LOR:"$cc"="NE" + + NESTED_ENTRY $lab + EnterWithLR_16 + STMFD sp!, {lr} ; Save return address + SUB sp, sp, #LOC_SIZE ; Allocate local storage + PROLOG_END + + ORRS tmp, dOP1h, dOP2h + BMI $lab._negative ; one of the operands negative? (MI) + CMN tmp, #0x00100000 ; check whether operands might be infinite/NaN + BMI $lab._check_NaN_pos + CMP dOP1h, dOP2h + CMPEQ dOP1l, dOP2l + CmpReturn $cc + +$lab._check_NaN_pos ; opnd1/2 might be inf/NaNs - do more accurate check + CMN dOP1h, #0x00100000 ; overhead 4 cycles for false hit + CMNPL dOP2h, #0x00100000 + BMI $lab._Inf_or_NaN +$lab._cmp_pos + CMP dOP1h, dOP2h + CMPEQ dOP1l, dOP2l + CmpReturn $cc + +$lab._negative + CMN tmp, #0x00100000 + BPL $lab._check_NaN_neg ; check whether operands might be infinite/NaN + ORRS tmp, dOP1l, dOP1h, LSL #1 ; check for -0 == 0 + ORREQS tmp, dOP2l, dOP2h, LSL #1 + CMPNE dOP2h, dOP1h + CMPEQ dOP2l, dOP1l + CmpReturn $cc + +$lab._check_NaN_neg ; opnd1/2 might be inf/NaNs - do more accurate check + MOV tmp, #0x00200000 ; overhead 3 cycles for false hit + CMN tmp, dOP1h, LSL #1 + CMNCC tmp, dOP2h, LSL #1 + BCS $lab._Inf_or_NaN +$lab._cmp_neg ; -0 == 0 test omitted (cannot give a false hit) + CMP dOP2h, dOP1h + CMPEQ dOP2l, dOP1l + CmpReturn $cc + +$lab._Inf_or_NaN ; one of the operands is infinite or NaN + MOV tmp, #0x00200000 + CMN tmp, dOP1h, LSL #1 + CMPEQ dOP1l, #0 ; HI -> NaN found + CMNLS tmp, dOP2h, LSL #1 ; no NaN, check opnd2 + CMPEQ dOP2l, #0 + BHI $NaN_lab ; NaN found -> exception + ORRS tmp, dOP1h, dOP2h + BPL $lab._cmp_pos + B $lab._cmp_neg + + MEND + + + + + + + + +;============================================================================== +;Invalid Operation checking (NaNs on compares) +;; + + + IMPORT FPE_Raise + + +;; +;; NANs on compares <, >, <=, and >= +;; +;; SNANs and QNANs both raise the invalid operation exception, so we don't +;; care which kind of NAN we get. This is because if we get an SNAN or SNANs, +;; we raise the invalid operation exception. If we get a QNAN or QNANs, we +;; have an unordered compare and must also raise the invalid operation +;; exception. +;; +;; Register usage on entry: +;; r0 - Arg1.low +;; r1 - Arg1.high +;; r2 - Arg2.low +;; r3 - Arg2.high +;; r14 - available for scratch +;; All others have normal usage semantics. +;; + MACRO +$l DCmpNaN $Filter_lab +$l STR r2, [sp, #ExOp2l] ;; Push Arg2.low + STR r3, [sp, #ExOp2h] ;; Push Arg2.high + MOV r3, #_FpCompareUnordered ;; Load default result + STR r3, [sp, #ExDResl] ;; Push default result + MOV r3, r1 ;; Arg1.high + MOV r2, r0 ;; Arg1.low + MOV r1, #_FpCmpD ;; ExInfo: InvalidOp, double compare + ORR r1, r1, #IVO_bit ;; .. + ADD r0, sp, #NewResl ;; Pointer to result + + CALL FPE_Raise ;; Deal with exception information + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #NewResl] ;; Load return value + ADD sp, sp, #LOC_SIZE ;; Restore stack + +;; +;; Register usage: +;; +;; r0 - Result from exception handler +;; +;; We must now examine the result from the exception handler and change it +;; to TRUE or FALSE, depending on the operation. After changing the result, +;; we return to the caller of the FP double compare routine. +;; + B $Filter_lab + MEND + + + + + +;; +;; NANs on compares == and != +;; +;; SNANs and QNANs are treated differently for == and !=. If we get an SNAN +;; or SNANs, we must raise the invalid operation exception. If we only have +;; a QNAN or QNANs, then we simply return false and true for == and !=, +;; respectively. Unordered comparisions for == and != do not raise the +;; invalid operation exception. +;; +;; Register usage on entry: +;; r0 - Arg1.low +;; r1 - Arg1.high +;; r2 - Arg2.low +;; r3 - Arg2.high +;; r14 - available for scratch +;; All others have normal usage semantics. +;; + + MACRO +$l DCmpSNaN $Filter_lab +$l MOV r12, #0x7F0 ;; r12 = Max exponent = 0x7FF + ORR r12, r12, #0x00F ;; ... + MOV r14, r1, LSL #1 ;; Extract exponent from Arg1 + MOV r14, r14, LSR #21 ;; ... + CMP r14, r12 ;; Arg1.exponent == 0x7FF? + BNE $l.checkArg2 ;; Arg1 not a NaN so check Arg2 + MOV r14, r1, LSL #14 ;; r14 = Arg1.Mantissa.High + ORRS r14, r14, r0 ;; Any Arg1.Mantissa bits set? + BEQ $l.checkArg2 ;; Arg1 not a NaN so check Arg2 + TST r1, #dSignalBit ;; Check if SNAN + BEQ $l.SNaN ;; If high mant. bit clear, SNaN + +$l.checkArg2 + MOV r14, r3, LSL #1 ;; Extract exponent from Arg2 + MOV r14, r14, LSR #21 ;; ... + CMP r14, r12 ;; Arg2.exponent == 0x7FF? + BNE $l.cmpUnordered ;; Arg2 not a NaN so Arg1 is a QNaN + MOV r14, r3, LSL #12 ;; r14 = Arg2.Mantissa.High + ORRS r14, r14, r2 ;; Any Arg2.Mantissa bits set? + BEQ $l.cmpUnordered ;; Arg2 not a NaN so Arg1 is a QNaN + TST r3, #dSignalBit ;; Check if SNAN + BEQ $l.SNaN ;; If high mant. bit clear, SNaN + +$l.cmpUnordered + MOV r0, #_FpCompareUnordered ;; Have an unordered compare so + B $Filter_lab ;; don't raise an exception + +$l.SNaN + STR r2, [sp, #ExOp2l] ;; Push Arg2.low + STR r3, [sp, #ExOp2h] ;; Push Arg2.high + MOV r3, #_FpCompareUnordered ;; Load default result + STR r3, [sp, #ExDResl] ;; Push default result + MOV r3, r1 ;; Arg1.high + MOV r2, r0 ;; Arg1.low + MOV r1, #_FpCmpD ;; ExInfo: InvalidOp, double compare + ORR r1, r1, #IVO_bit ;; .. + ADD r0, sp, #NewResl ;; Pointer to result + + CALL FPE_Raise ;; Deal with exception information + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #NewResl] ;; Load return value + +;; +;; Register usage: +;; +;; r0 - Result from exception handler +;; +;; We must now examine the result from the exception handler and change it +;; to TRUE or FALSE, depending on the operation. After changing the result, +;; we return to the caller of the FP double compare routine. +;; + B $Filter_lab + MEND + + + + + +;============================================================================== +;Equality + + [ :DEF: eq_s + + Export __eqd + + AREA |.text|, CODE, READONLY + +__eqd DoubleCompare EQ, __eqd_NaN + +__eqd_NaN DCmpSNaN __eqd_Filter + +__eqd_Filter + CMP r0, #_FpCompareEqual ;; Check if compared == + MOVEQ r0, #1 ;; If did, return true + MOVNE r0, #0 ;; else return false + ADD sp, sp, #LOC_SIZE ;; Restore stack + IF Interworking :LOR: Thumbing + LDMIA sp!, {lr} ;; Return + BX lr + ELSE + LDMIA sp!, {pc} ;; Return + ENDIF + + ENTRY_END __eqd + ] + +;============================================================================== +;Inequality + + [ :DEF: neq_s + + Export __ned + + AREA |.text|, CODE, READONLY + +__ned DoubleCompare NE, __ned_NaN + +__ned_NaN DCmpSNaN __ned_Filter + +__ned_Filter + CMP r0, #_FpCompareEqual ;; Check if compared == + MOVEQ r0, #0 ;; If did, return false + MOVNE r0, #1 ;; else return true + ADD sp, sp, #LOC_SIZE ;; Restore stack + IF Interworking :LOR: Thumbing + LDMIA sp!, {lr} ;; Return + BX lr + ELSE + LDMIA sp!, {pc} ;; Return + ENDIF + + ENTRY_END __ned + ] + + +;============================================================================== +;Less Than + + [ :DEF: ls_s + + Export __ltd + + AREA |.text|, CODE, READONLY + +__ltd DoubleCompare LO, __ltd_NaN + +__ltd_NaN DCmpNaN __ltd_Filter + +__ltd_Filter + CMP r0, #_FpCompareLess ;; Check if compared < + MOVEQ r0, #1 ;; If did, return true + MOVNE r0, #0 ;; else return false + IF Interworking :LOR: Thumbing + LDMIA sp!, {lr} ;; Return + BX lr + ELSE + LDMIA sp!, {pc} ;; Return + ENDIF + + ENTRY_END __ltd + ] + +;============================================================================== +;Less Than or Equal + + [ :DEF: leq_s + + Export __led + + AREA |.text|, CODE, READONLY + +__led DoubleCompare LS, __led_NaN + +__led_NaN DCmpNaN __led_Filter + +__led_Filter + CMP r0, #_FpCompareLess ;; Check if compared < + MOVEQ r0, #1 ;; If did, + BEQ __led_Filter_end ;; return true + CMP r0, #_FpCompareEqual ;; Check if compared == + MOVEQ r0, #1 ;; If did, return true + MOVNE r0, #0 ;; else return false +__led_Filter_end + IF Interworking :LOR: Thumbing + LDMIA sp!, {lr} ;; Return + BX lr + ELSE + LDMIA sp!, {pc} ;; Return + ENDIF + + ENTRY_END __led + ] + +;============================================================================== +;Greater Than + + [ :DEF: gr_s + + Export __gtd + + AREA |.text|, CODE, READONLY + +__gtd DoubleCompare HI, __gtd_NaN + +__gtd_NaN DCmpNaN __gtd_Filter + +__gtd_Filter + CMP r0, #_FpCompareGreater ;; Check if compared > + MOVEQ r0, #1 ;; If did, return true + MOVNE r0, #0 ;; else return false + IF Interworking :LOR: Thumbing + LDMIA sp!, {lr} ;; Return + BX lr + ELSE + LDMIA sp!, {pc} ;; Return + ENDIF + + ENTRY_END __gtd + ] + +;============================================================================== +;Greater Than or Equal + + [ :DEF: geq_s + + Export __ged + + AREA |.text|, CODE, READONLY + +__ged DoubleCompare HS, __ged_NaN + +__ged_NaN DCmpNaN __ged_Filter + +__ged_Filter + CMP r0, #_FpCompareGreater ;; Check if compared > + MOVEQ r0, #1 ;; If did, + BEQ __ged_Filter_end ;; return true + CMP r0, #_FpCompareEqual ;; Check if compared == + MOVEQ r0, #1 ;; If did, return true + MOVNE r0, #0 ;; else return false +__ged_Filter_end + IF Interworking :LOR: Thumbing + LDMIA sp!, {lr} ;; Return + BX lr + ELSE + LDMIA sp!, {pc} ;; Return + ENDIF + + ENTRY_END __ged + ] + + + END diff --git a/base/Kernel/Native/arm/Crt/basic_f.asm b/base/Kernel/Native/arm/Crt/basic_f.asm new file mode 100644 index 0000000..5eab1b1 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/basic_f.asm @@ -0,0 +1,437 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; basic_f.s +; +; Copyright (C) Advanced RISC Machines Limited, 1994. All rights reserved. +; +; RCS Revision: 1 +; Checkin Date: 2007/06/29 02:59:16 +; Revising Author + +; Basic floating point functions +; +; Fixed == and != compares to be IEEE-754 compliant when input QNaNs. +; No exceptions are raised when only QNaNs are the only NaNs input to +; == and !=. Moved NaN detection and exception raising here. +; Removed unnecessary macros for compares that return results in flags. +; Added WindowsCE SEH mechanism support. +; Renamed routines. +; + +LOC_SIZE EQU 0x18 +OrgOp1l EQU 0x14 +OrgOp2l EQU 0x10 +ExDResl EQU 0x08 +ExOp2l EQU 0x00 +NewResl EQU 0x10 + + + GET fpe.asm + GET kxarm.inc + + +;This code is very similar to the "double" versions. The documentation isn't +;extensively repeated. Refer to basic_d.s for further documentation. + + +;============================================================================== +; Compare. +; +; Timing: +; Flags: 7 (pos), 11 (false NaN), 9 (neg), 13 (false NaN) SA1.1 cycles +; Others: 9 / 13 / 11 / 15 +;============================================================================== + + + MACRO + CmpReturn $cc + MOV a1, #0 + MOV$cc a1, #1 + ADD sp, sp, #LOC_SIZE + IF Interworking :LOR: Thumbing + LDMFD sp!, {lr} + BX lr + ELSE + LDMFD sp!, {pc} + ENDIF + MEND + + + + MACRO +$lab FloatCompare $cc, $NaN_lab + + ASSERT "$cc"="LO":LOR:"$cc"="LS":LOR:"$cc"="HS":LOR:"$cc"="HI":LOR:"$cc"="EQ":LOR:"$cc"="NE" + + NESTED_ENTRY $lab + EnterWithLR_16 + STMFD sp!, {lr} ; Save return address + SUB sp, sp, #LOC_SIZE ; Allocate local storage + PROLOG_END + + ORRS tmp, fOP1, fOP2 ; separate into opnd1/2 both positive, or one negative + BMI $lab._negative + CMN tmp, #1 << 23 ; check whether operands might be infinite/NaN + BMI $lab._NaN_check_pos + CMP fOP1, fOP2 + CmpReturn $cc + +$lab._NaN_check_pos ; opnd1/2 might be inf/NaNs - now do the real check + CMN fOP1, #1 << 23 ; these get about 9% false hits - overhead 4 cycles + CMNPL fOP2, #1 << 23 + BMI $lab._Inf_or_NaN_pos +$lab._cmp_pos + CMP fOP1, fOP2 + CmpReturn $cc + +$lab._Inf_or_NaN_pos ; at least one operand infinite or NaN - filter out infinities + MOV tmp, #1 << 24 + CMN tmp, fOP1, LSL #1 + CMNLS tmp, fOP2, LSL #1 + BLS $lab._cmp_pos ; no NaN - continue compare + B $NaN_lab + +$lab._negative ; at least one negative operand + CMN tmp, #1 << 23 + BPL $lab._NaN_check_neg + MOVS tmp, tmp, LSL #1 ; check -0 == 0 (CS & EQ -> HS and LS) + CMPNE fOP2, fOP1 + CmpReturn $cc + +$lab._NaN_check_neg ; opnd1/2 might be inf/NaNs - now do the real check + MOV tmp, #1 << 24 ; these get about 9% false hits - overhead 4 cycles + CMN tmp, fOP1, LSL #1 + CMNLS tmp, fOP2, LSL #1 + BHI $NaN_lab + CMP fOP2, fOP1 ; -0 == 0 check not needed... + CmpReturn $cc + + MEND + + + +;============================================================================== +;Invalid Operation checking (NaNs on compares) +;; + + + IMPORT FPE_Raise + +;; +;; NANs on compares <, >, <=, and >= +;; +;; SNANs and QNANs both raise the invalid operation exception, so we don't +;; care which kind of NAN we get. This is because if we get an SNAN or SNANs, +;; we raise the invalid operation exception. If we get a QNAN or QNANs, we +;; have an unordered compare and must also raise the invalid operation +;; exception. +;; +;; Register usage on entry: +;; r0 - Arg1 +;; r1 - Arg2 +;; r14 - available for scratch +;; All others have normal usage semantics. +;; + MACRO +$l FCmpNaN $Filter_lab +$l STR r1, [sp, #ExOp2l] ;; Push Arg2 + MOV r3, #_FpCompareUnordered ;; Load default result + STR r3, [sp, #ExDResl] ;; Push default result + MOV r2, r0 ;; Arg1 + MOV r1, #IVO_bit ;; ExInfo: InvalidOp, + ORR r1, r1, #_FpCmpS ;; float compare + ADD r0, sp, #NewResl ;; Pointer to result + + CALL FPE_Raise ;; Deal with exception information + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #NewResl] ;; Load return value + ADD sp, sp, #LOC_SIZE ;; Retore stack + +;; +;; Register usage: +;; +;; r0 - Result from exception handler +;; +;; We must now examine the result from the exception handler and change it +;; to TRUE or FALSE, depending on the operation. After changing the result, +;; we return to the caller of the FP double compare routine. +;; + B $Filter_lab + MEND + + + + + +;; +;; NANs on compares == and != +;; +;; SNANs and QNANs are treated differently for == and !=. If we get an SNAN +;; or SNANs, we must raise the invalid operation exception. If we only have +;; a QNAN or QNANs, then we simply return false and true for == and !=, +;; respectively. Unordered comparisions for == and != do not raise the +;; invalid operation exception. +;; +;; Register usage on entry: +;; r0 - Arg1 +;; r1 - Arg2 +;; r14 - available for scratch +;; All others have normal usage semantics. +;; + + MACRO +$l FCmpSNaN $Filter_lab + +$l MOV r14, r0, LSL #1 ;; Extract exponent from Arg1 + MOV r14, r14, LSR #24 ;; ... + CMP r14, #0xFF ;; Arg1.exponent == 0xFF? + BNE $l.checkArg2 ;; Arg1 not a NaN so check Arg2 + MOVS r14, r0, LSL #9 ;; Arg1.Mantissa bits set? + BEQ $l.checkArg2 ;; Arg1 not a NaN so check Arg2 + TST r0, #fSignalBit ;; Check if SNAN + BEQ $l.SNaN ;; If high mant. bit clear, SNaN + +$l.checkArg2 + MOV r14, r1, LSL #1 ;; Extract exponent from Arg2 + MOV r14, r14, LSR #24 ;; ... + CMP r14, #0xFF ;; Arg2.exponent == 0xFF? + BNE $l.cmpUnordered ;; Arg2 not a NaN so Arg1 is a QNaN + MOVS r14, r1, LSL #9 ;; Arg2.Mantissa bits set? + BEQ $l.cmpUnordered ;; Arg2 not a NaN so Arg1 is a QNaN + TST r1, #fSignalBit ;; Check if SNAN + BEQ $l.SNaN ;; If high mant. bit clear, SNaN + +$l.cmpUnordered + MOV r0, #_FpCompareUnordered ;; Have an unordered compare so + B $Filter_lab ;; don't raise an exception + +$l.SNaN + STR r1, [sp, #ExOp2l] ;; Push Arg2 + MOV r3, #_FpCompareUnordered ;; Load default result + STR r3, [sp, #ExDResl] ;; Push default result + MOV r2, r0 ;; Arg1 + MOV r1, #IVO_bit ;; ExInfo: InvalidOp, + ORR r1, r1, #_FpCmpS ;; float compare + ADD r0, sp, #NewResl ;; Pointer to result + + CALL FPE_Raise ;; Deal with exception information + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #NewResl] ;; Load return value + + +;; +;; Register usage: +;; +;; r0 - Result from exception handler +;; +;; We must now examine the result from the exception handler and change it +;; to TRUE or FALSE, depending on the operation. After changing the result, +;; we return to the caller of the FP double compare routine. +;; + B $Filter_lab + MEND + + + + + +;============================================================================== +; Equality + + [ :DEF: eq_s + + Export __eqs + + AREA |.text|, CODE, READONLY + +__eqs FloatCompare EQ, __eqs_NaN + +__eqs_NaN FCmpSNaN __eqs_Filter + +__eqs_Filter + CMP r0, #_FpCompareEqual ;; Check if compared == + MOVEQ r0, #1 ;; If did, return true + MOVNE r0, #0 ;; else return false + ADD sp, sp, #0x18 ;; Pop extra arg passing space + IF Interworking :LOR: Thumbing + LDMIA sp!, {lr} ;; Return + BX lr + ELSE + LDMIA sp!, {pc} ;; Return + ENDIF + + ENTRY_END __eqs + + ] + +;============================================================================== +;Inequality + + [ :DEF: neq_s + + Export __nes + + AREA |.text|, CODE, READONLY + +__nes FloatCompare NE, __nes_NaN + +__nes_NaN FCmpSNaN __nes_Filter + +__nes_Filter + CMP r0, #_FpCompareEqual ;; Check if compared == + MOVEQ r0, #0 ;; If did, return false + MOVNE r0, #1 ;; else return true + ADD sp, sp, #0x18 ;; Pop extra arg passing space + IF Interworking :LOR: Thumbing + LDMIA sp!, {lr} ;; Return + BX lr + ELSE + LDMIA sp!, {pc} ;; Return + ENDIF + + ENTRY_END __nes + + ] + + +;============================================================================== +;Less Than + + [ :DEF: ls_s + + Export __lts + + AREA |.text|, CODE, READONLY + +__lts FloatCompare LO, __lts_NaN + +__lts_NaN FCmpNaN __lts_Filter + +__lts_Filter + CMP r0, #_FpCompareLess ;; Check if compared < + MOVEQ r0, #1 ;; If did, return true + MOVNE r0, #0 ;; else return false + IF Interworking :LOR: Thumbing + LDMIA sp!, {lr} ;; Return + BX lr + ELSE + LDMIA sp!, {pc} ;; Return + ENDIF + + ENTRY_END __lts + ] + +;============================================================================== +;Less Than or Equal + + [ :DEF: leq_s + + Export __les + + AREA |.text|, CODE, READONLY + +__les FloatCompare LS, __les_NaN + +__les_NaN FCmpNaN __les_Filter + +__les_Filter + CMP r0, #_FpCompareLess ;; Check if compared < + MOVEQ r0, #1 ;; If did, + BEQ __les_Filter_end ;; return true + CMP r0, #_FpCompareEqual ;; Check if compared == + MOVEQ r0, #1 ;; If did, return true + MOVNE r0, #0 ;; else return false +__les_Filter_end + IF Interworking :LOR: Thumbing + LDMIA sp!, {lr} ;; Return + BX lr + ELSE + LDMIA sp!, {pc} ;; Return + ENDIF + + ENTRY_END __les + + ] + +;============================================================================== +;Greater Than + + [ :DEF: gr_s + + Export __gts + + AREA |.text|, CODE, READONLY + +__gts FloatCompare HI, __gts_NaN + +__gts_NaN FCmpNaN __gts_Filter + +__gts_Filter + CMP r0, #_FpCompareGreater ;; Check if compared > + MOVEQ r0, #1 ;; If did, return true + MOVNE r0, #0 ;; else return false + IF Interworking :LOR: Thumbing + LDMIA sp!, {lr} ;; Return + BX lr + ELSE + LDMIA sp!, {pc} ;; Return + ENDIF + + ENTRY_END __gts + ] + +;============================================================================== +;Greater Than or Equal + + [ :DEF: geq_s + + Export __ges + + AREA |.text|, CODE, READONLY + +__ges FloatCompare HS, __ges_NaN + +__ges_NaN FCmpNaN __ges_Filter + +__ges_Filter + CMP r0, #_FpCompareGreater ;; Check if compared > + MOVEQ r0, #1 ;; If did, + BEQ __ges_Filter_end ;; return true + CMP r0, #_FpCompareEqual ;; Check if compared == + MOVEQ r0, #1 ;; If did, return true + MOVNE r0, #0 ;; else return false +__ges_Filter_end + IF Interworking :LOR: Thumbing + LDMIA sp!, {lr} ;; Return + BX lr + ELSE + LDMIA sp!, {pc} ;; Return + ENDIF + + ENTRY_END __ges + ] + +;============================================================================== + + END diff --git a/base/Kernel/Native/arm/Crt/callcode.asm b/base/Kernel/Native/arm/Crt/callcode.asm new file mode 100644 index 0000000..ae2f19c --- /dev/null +++ b/base/Kernel/Native/arm/Crt/callcode.asm @@ -0,0 +1,83 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; ************************************************************************** + +; Stolen from armtrap.s + + IF Thumbing + + MACRO + CALL $Fn + ldr r12, =$Fn + mov lr, pc + bx r12 + MEND + + MACRO + CALLEQ $Fn + ldreq r12, =$Fn + moveq lr, pc + bxeq r12 + MEND + + MACRO + RETURN + bx lr + MEND + + MACRO + RETURN_EQ + bxeq lr + MEND + + MACRO + RETURN_NE + bxne lr + MEND + + ELSE + + MACRO + CALL $Fn + bl $Fn + MEND + + MACRO + CALLEQ $Fn + bleq $Fn + MEND + + MACRO + RETURN + mov pc, lr + MEND + + MACRO + RETURN_EQ + moveq pc, lr + MEND + + MACRO + RETURN_NE + movne pc, lr + MEND + + ENDIF + + MACRO + mfc15 $cpureg, $cp15reg + mrc p15,0,$cpureg,$cp15reg,c0,0 + MEND + +; End of stuff stolen from armtrap.s + +; ************************************************************************** + END + diff --git a/base/Kernel/Native/arm/Crt/check.asm b/base/Kernel/Native/arm/Crt/check.asm new file mode 100644 index 0000000..f5e8688 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/check.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL fcheck_s + + GET except.asm + + END diff --git a/base/Kernel/Native/arm/Crt/defaults.asm b/base/Kernel/Native/arm/Crt/defaults.asm new file mode 100644 index 0000000..72038aa --- /dev/null +++ b/base/Kernel/Native/arm/Crt/defaults.asm @@ -0,0 +1,70 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; Assembler source for FPA support code and emulator +; ================================================== +; Definitions and default values of optional optimisations. Also used by +; "fplib". +; +; Copyright (C) Advanced RISC Machines Limited, 1992-7. All rights reserved. +; +; RCS Revision: 1 +; Checkin Date: 2007/06/29 02:59:16 +; Revising Author + +;=========================================================================== + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; No Thumbing or Interworking for now. +; + GBLL Thumbing +Thumbing SETL {FALSE} + GBLL Interworking +Interworking SETL {FALSE} +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; The "traps never return" code size optimisation. + + GBLL TrapsCanReturn +TrapsCanReturn SETL {TRUE} + +; The "FPE context uses 4 words per register" speed optimisation. + + GBLL FPE4WordsPerReg +FPE4WordsPerReg SETL {FALSE} + +; The "do integer powers" optimisation. + + GBLL DoIntegerPowers +DoIntegerPowers SETL {TRUE} + +; The value of 0^0. + + GBLS ZeroToTheZero +ZeroToTheZero SETS "One" + +; The "FPE checks whether next instruction is floating point" optimisation. + + GBLL FPEChecksNextInstr +FPEChecksNextInstr SETL {TRUE} + +; The "no transcendentals" optimisation. + + GBLL NoTranscendentals +NoTranscendentals SETL {FALSE} + +; The "no packed precision" optimisation. + + GBLL NoPacked +NoPacked SETL {FALSE} + +;=========================================================================== + + END diff --git a/base/Kernel/Native/arm/Crt/div.asm b/base/Kernel/Native/arm/Crt/div.asm new file mode 100644 index 0000000..5ca7094 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/div.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL div_s + + GET veneer.asm + + END diff --git a/base/Kernel/Native/arm/Crt/e2d.asm b/base/Kernel/Native/arm/Crt/e2d.asm new file mode 100644 index 0000000..0e2de52 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/e2d.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL e2d_s + + GET format_d.asm + + END diff --git a/base/Kernel/Native/arm/Crt/error.asm b/base/Kernel/Native/arm/Crt/error.asm new file mode 100644 index 0000000..d324bd8 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/error.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL except_s + + GET except.asm + + END diff --git a/base/Kernel/Native/arm/Crt/except.asm b/base/Kernel/Native/arm/Crt/except.asm new file mode 100644 index 0000000..67f79fd --- /dev/null +++ b/base/Kernel/Native/arm/Crt/except.asm @@ -0,0 +1,445 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; except.s +; +; Copyright (C) Advanced RISC Machines Limited, 1994. All rights reserved. +; +; RCS Revision: 1 +; Checkin Date: 2007/06/29 02:59:16 +; Revising Author + +;============================================================================== +;Error generation - from ANSI library + + GET fpe.asm + GET h_errors.asm + + [ :DEF: liberror_s + + AREA |.text|, CODE, READONLY + + Export_32 __fp_edom + Export_32 __fp_erange + +; __fp_edom(ulong sign_bit, boolean huge_val); +; __fp_erange(ulong sign_bit, boolean huge_val); +; +; set errno to EDOM/ERANGE and return sign_bit | (huge_val ? HUGE_VAL : 0) + +__fp_edom EnterWithLR + MOV a3, #EDOM + B skip +__fp_erange EnterWithLR + MOV a3, #ERANGE +skip + AND a4, a1, #Sign_bit + [ :DEF:EMBEDDED_CLIB + IMPORT __rt_errno_addr + LDR a1, =__rt_errno_addr + CMP a1, #0 + BEQ %FT1 + STMDB sp!, {a2-a4, lr} + BL __rt_errno_addr + LDMIA sp!, {a2-a4, lr} + STR a3, [a1] +1 + | + LDR a1, errno + STR a3, [a1] + ] + TEQ a2, #0 + MOVEQ a1, #0 ; generate +/- 0.0 in a1/a2 + LDRNE a1, huge_val + LDMNEIA a1, {a1, a2} ; load HUGE_VAL into a1/a2 + ORR a1, a1, a4 ; add in sign bit + ReturnToLR + + [ :LNOT::DEF:EMBEDDED_CLIB +errno + IMPORT __errno + DCD __errno + ] + +huge_val + IMPORT __huge_val + DCD __huge_val + + ] + +;============================================================================== +;Setting/returning status flags. + + [ :DEF: status_s + + AREA |.text|, CODE, READONLY + + EXPORT __fp_status + +; extern unsigned int __fp_status(unsigned int mask,unsigned int flags) + +__fp_status + + [ :DEF: thumb + + CODE16 + ; mask "mask" to an acceptable value + MOV a4,#&e0 + LSL a3,a1,#32-FPExceptE_pos-Except_len + LSR a3,a3,#32-FPExceptE_pos-Except_len + BIC a3,a4 + LSR a4,#8 + BIC a3,a4 + AND a4,a1 + ORR a3,a1 + AND a2,a3 + ; mask in a3, masked flags in a2 + LDR a4,status_ptr + LDR a1,[a4] + ; **** *WARNING* is ip spare under the TPCS ????? **** + MOV ip,a1 ; store value + BIC a1,a3 + EOR a1,a2 + STR a1,[a4] + MOV a1,ip ; restore value + BX lr + ALIGN + + | + + ; mask "mask" to an acceptable value + MOV a3,a1,LSL #32-FPExceptE_pos-Except_len + BIC a3,a3,#&e0:SHL:(32-FPExceptE_pos-Except_len) + BIC a3,a3,#&e0:SHL:(32-FPExceptE_pos-Except_len+8) + AND a2,a2,a3,LSR #32-FPExceptE_pos-Except_len + ; mask in a3, masked flags in a2. + LDR a4,status_ptr + LDR a1,[a4] ; load old flags + BIC a3,a1,a3,LSR #32-FPExceptE_pos-Except_len + EOR a3,a3,a2 ; clear/set/toggle flags and + STR a3,[a4] ; write back. + IF Interworking :LOR: Thumbing + BX lr + ELSE + MOV pc,lr ; return + ENDIF + + ] + +status_ptr + IMPORT __fp_status_flags + DCD __fp_status_flags + + ] + +;============================================================================== +;Error checking - from fplib code + + [ :DEF: fcheck_s + + AREA |.text|, CODE, READONLY + + [ :DEF: thumb + CODE32 + ] + + EXPORT __fp_fcheck_NaN2 + EXPORT __fp_dcheck_NaN2 + EXPORT __fp_return_NaN + EXPORT __fp_return_Inf + IMPORT __fp_exception + + +; Check fOP1 and fOP2 for signalling NaN, IP contains exception flags. + +__fp_fcheck_NaN2 + MOV a4, #0x01000000 + CMN a4, fOP1, LSL #1 + BLS fcheck_opnd2_NaN +fcheck_opnd1_NaN + TST fOP1, #fSignalBit + BEQ __fp_exception + CMN a4, fOP2, LSL #1 + BLS __fp_return_NaN +fcheck_opnd2_NaN + MOV fOP1, fOP2 + TST fOP1, #fSignalBit + BNE __fp_return_NaN ;; RDCFix: Do we really want to do this? +; BEQ __fp_return_NaN + B __fp_exception + +; Check dOP1 and dOP2 for signalling NaN, IP contains exception flags. + +__fp_dcheck_NaN2 + STMFD sp!, {ip} + MOV tmp, #0x00200000 + CMN tmp, dOP1h, LSL #1 + CMPEQ dOP1l, #0 + BLS dcheck_opnd2_NaN +dcheck_opnd1_NaN + TST dOP1h, #dSignalBit + LDMEQFD sp!, {ip} + BEQ __fp_exception + CMN tmp, dOP2h, LSL #1 + CMPEQ dOP2l, #0 + LDMLSFD sp!, {ip} + BLS __fp_return_NaN +dcheck_opnd2_NaN + LDMFD sp!, {ip} + MOV dOP1h, dOP2h + MOV dOP1l, dOP2l + TST dOP1h, #dSignalBit + BNE __fp_return_NaN ;; RDCFix: Do we really want to do this? +; BEQ __fp_return_NaN + B __fp_exception + +; Return NaN in fOP / dOP, except for non-float returning functions. +; IP contains exception flags. + +__fp_return_NaN + ; test for special cases + +;; +;; RDCFix: Will need to fix this once new defines are in. May want to +;; do some performance enhancements to keep from needing to check +;; each _Fp* operation code. Maybe a jump table. Maybe forget +;; this garbage and make the core emulation routines pack up +;; their proper results instead of hacking them up here. +;; +; AND a4, ip, #Fn_mask +; CMP a4, #CompareFalseFn +; CMPNE a4, #FixZeroFn +; BEQ return_zero +; CMP a4, #CompareTrueFn +; BEQ return_one +; CMP a4, #CmpLessFn +; BEQ return_HI +; CMP a4, #CmpGreaterFn +; BEQ return_LO +; CMP a4, #CmpEqualFn +; BEQ return_NE +; CMP a4, #FixFn +; BEQ return_smaxint +; CMP a4, #FixuFn +; BEQ return_umaxint + ReturnToLR + +return_HI + MOV a1, #1 + CMP a1, #0 + ReturnToLR_flg + +return_LO + MOV a1, #0 + CMP a1, #1 + ReturnToLR_flg + +return_NE + MOVS a1, #1 + ReturnToLR_flg + +return_one + MOV a1, #1 + ReturnToLR + +return_zero + MOV a1, #0 + MOV a2, #0 + ReturnToLR + +return_smaxint + MOV a3, a1 + TST ip, #LongLong_bit + MOVEQ a1, #0x7fffffff + MOVNE a1, #0xffffffff + MOVNE a2, #0x7fffffff + TST a3, #Sign_bit + MVNNE a1, a1 + MVNNE a2, a2 + ReturnToLR + +return_umaxint + MOV a1, #0xffffffff + MOV a2, #0xffffffff + ReturnToLR + +__fp_return_Inf + ; no special cases + ReturnToLR + + ] + +;============================================================================== +;Error generation - from fplib code + + [ :DEF: except_s + + AREA |.text|, CODE, READONLY + +; SWI Names + + [ :DEF: thumb + CODE32 + ] + + EXPORT __fp_veneer_error + EXPORT __fp_nonveneer_error + EXPORT __fp_exception + IMPORT __fp_return_NaN + IMPORT __fp_return_Inf + +__fp_veneer_error ; a4 contains the exception flags + +__fp_nonveneer_error ; a4 contains the exception flags OBSOLETE + MOV ip, a4 + ; fallthrough + +; fp_exception is called when an IEEE exception has occurred + +; a1 contains the sign of the NaN to be returned if the exception is disabled +; ip contains the exception flags (see fpe.s for a list) + +__fp_exception + + IMPORT FPE_Raise + + STMDB sp!,{lr} + + TST ip,#OVF_bit + BNE overflow + TST ip,#UNF_bit + BNE underflow + TST ip,#DVZ_bit + BNE divide_by_zero + TST ip,#INX_bit + BNE divide_by_zero + +invalid_operation + BL return_NaN + B callraise + +overflow + BL return_Inf + B callraise + +underflow + MOV a1, #0 + MOV a2, #0 + B callraise + +inexact + B callraise + +divide_by_zero + BL return_Inf + +callraise + + IF Interworking :LOR: Thumbing + LDMIA sp!, {lr} + BX lr + ELSE + LDMIA sp!, {pc} + ENDIF + +;GenerateError +; IMPORT RaiseException +; [ :DEF:EMBEDDED_CLIB +; STMDB sp!, {r0-r15} +; SUB lr, lr, #4 +; STR lr, [sp, #15*4] +; MOV r1, sp +; | +; LDR r1,ErrBlock +; SUB lr,lr,#4 +; STR lr,[r1,#15*4] +; MOV lr,#&de00 +; ORR lr,lr,#&00ad +; ORR lr,lr,lr,LSL #16 +; STMIA r1,{r0-r14} + +; B RaiseException + +status_ptr + IMPORT __fp_status_flags + DCD __fp_status_flags + +ErrBlock + IMPORT __fp_errblock + DCD __fp_errblock + +trap +; ] + + IMPORT __rt_trap + LDR ip, =__rt_trap + CMP ip, #0 + [ Interworking :LOR:(:DEF:thumb) + BXNE ip + | + MOVNE pc, ip + ] + DCD 0xe6000010 + + ErrorBlock FP_IVO, "Floating Point Exception: Invalid Operation" + ErrorBlock FP_OFL, "Floating Point Exception: Overflow" + ErrorBlock FP_DVZ, "Floating Point Exception: Divide By Zero" + +return_Inf + AND a3,a1,#Sign_bit + TST ip,#Double_bit + ADRNE a1,prototype_double_Inf + LDMNEIA a1,{a1,a2} + LDREQ a1,prototype_single_Inf + ORR a1,a1,a3 + B __fp_return_Inf + +return_NaN + AND a3, a1, #Sign_bit + TST ip, #Double_bit + ADRNE a1,prototype_double_NaN + LDMNEIA a1,{a1,a2} + LDREQ a1,prototype_single_NaN + ORR a1, a1, a3 + B __fp_return_NaN + +prototype_double_Inf + DCD &7ff00000,&00000000 +prototype_single_Inf + DCD &7f800000 +prototype_double_NaN + DCD &7ff00000,&00000001 +prototype_single_NaN + DCD &7f800001 + + ] + +;------------------------------------------------------------------------------ + + [ :DEF: fpdata_s + + AREA |.data|, DATA + + EXPORT __fp_status_flags +__fp_status_flags + ; default - all flags enabled. + DCD (&40:SHL:SysID_pos):OR:(((1:SHL:Except_len)-1):SHL:FPExceptE_pos) + + EXPORT __fp_errblock +__fp_errblock + DCD 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 + + EXPORT __rt_trap +__rt_trap + DCD 0 + ] + +;============================================================================== + + END diff --git a/base/Kernel/Native/arm/Crt/fnorm2.asm b/base/Kernel/Native/arm/Crt/fnorm2.asm new file mode 100644 index 0000000..ceef429 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/fnorm2.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL fnorm2_s + + GET veneer_f.asm + + END diff --git a/base/Kernel/Native/arm/Crt/format.asm b/base/Kernel/Native/arm/Crt/format.asm new file mode 100644 index 0000000..bf4fc36 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/format.asm @@ -0,0 +1,368 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; Copyright (C) Advanced RISC Machines Limited, 1994. All rights reserved. +; +; RCS Revision: 1 +; Checkin Date: 2007/06/29 02:59:16 +; Revising Author + + GET fpe.asm + GET kxarm.inc + + CodeArea |.text| + + IMPORT FPE_Raise + +SNaNInf EQU NaNInfExp_Single - EIExp_bias + SExp_bias +DNaNInf EQU NaNInfExp_Double - EIExp_bias + DExp_bias +DExp_max EQU 2047 +SFrac_len EQU 23 + +exp RN 3 +sign RN 2 +tmp RN 12 + +;============================================================================== +;Format conversions + + [ :DEF: d2f_s + +; Local storage size and offsets +LOC_SIZE EQU 0x18 +OrgOp1h EQU 0x14 +OrgOp1l EQU 0x10 +ExDResl EQU 0x08 +NewResl EQU 0x10 + + ; Ensure the defines are what they should be. Note that there was + ; a problem when we swapped the double halves as the code relied on + ; the low half of the double not being the register where floats are + ; returned. I have put these asserts here so the registers don't + ; change out from under me. We don't really need these conditions + ; to hold true, just the code needs to be checked after register + ; defines are changed. + ASSERT exp = r3 + ASSERT sign = r2 + ASSERT tmp = r12 + ASSERT dOPh = r1 + ASSERT dOPl = r0 + ASSERT fOP = r0 + + Export __dtos + + NESTED_ENTRY __dtos + EnterWithLR_16 + STMFD sp!, {r4, lr} ; Save off non-volatile, lr + SUB sp, sp, #LOC_SIZE ; Allocate stack space + PROLOG_END + + STR r1, [sp, #OrgOp1h] ; Save off arg in case of exception + MOV r4, #_FpDToS ; Set double->float convert + ORRS tmp, dOPl, dOPh, LSL #1 ; Special check for zero + STR r0, [sp, #OrgOp1l] ; Save off arg in case of exception + BEQ __dtos_return_zero ; If zero, return it + AND sign, dOPh, #Sign_bit + MOV tmp, #(DExp_bias - SExp_bias) << (DExp_pos+1) + RSB dOPh, tmp, dOPh, LSL #1 + MOVS exp, dOPh, LSR #DExp_pos+1 + BEQ _d2f_ExpUnderflow + CMP exp, #254 + BHS _d2f_uncommon +_d2f_round + MOVS tmp, dOPl, LSL #3 ; Check for inexact + ORRNE r4, r4, #INX_bit ; Raise inexact if inexact + ORRS tmp, sign, dOPl, LSR #29 + MOV r3, dOPl ; * Need to save dOPl somewhere else + ADC fOP, tmp, dOPh, LSL #2 ; dOPh already shifted 1 bit + BCC __dtos_return + MOVS r3, r3, LSL #4 ; * Was: MOVS dOPl, dOPl, LSL #4 + BNE __dtos_return + BIC fOP, fOP, #1 + B __dtos_return + +_d2f_uncommon ; exp out of range - check for special cases + CMP exp, #(-(DExp_bias - SExp_bias)) :AND: 0x7FF + BHS _d2f_ExpUnderflow + ; if exponent 254 - test for overflow during rounding + CMP exp, #254 + MOVEQ tmp, dOPh, LSL #DExp_len + ORREQ tmp, tmp, dOPl, LSR #DFhi_len + CMNEQ tmp, #1 << 8 ; check if dOP rounds to overflow + BLO _d2f_round ; no - continue (8 clk overhead) + +_d2f_ExpOverflow ; overflow, inf/NaN + ADD exp, exp, #1 + TEQ exp, #DExp_max - DExp_bias + SExp_bias + 1 + MOVNE fOP, sign + BNE __dtos_return_overflow ; overflow +_d2f_Inf_or_NaN ; found inf or NaN + ORRS tmp, dOPl, dOPh, LSL #DExp_len ; infinity if EQ + MOVEQ tmp, #0xFF000000 + ORR fOP, sign, tmp, LSR #1 ; return signed infinity + BEQ __dtos_return + ; sign in fOP + MOVS tmp, dOPh, LSL #DExp_len + ORRPL r4, r4, #IVO_bit ; Set invalid if SNaN + LDR r2, fNaN ; Return quiet NaN + MOV tmp, dOPh, LSL #DExp_len-9 ; insert high mantissa bits + ORR tmp, tmp, dOPl, LSR #29 ; insert low mantissa bits + ORR r0, r2, tmp ; Set exp, high mant bit + B __dtos_return + +fNaN DCD &7FC00000 + +__dtos_return_zero + MOV fOP, dOPh ; dOPh has the correctly signed + B __dtos_return ; float zero, so return it + +_d2f_ExpUnderflow + CMP exp, #0 + SUBNE exp, exp, #0x800 + ; was underflow - return denorm or zero (exp is -X .. 0) + RSB exp, exp, #SExp_len+1 ; right shift for dOPh + RSBS tmp, exp, #33 ; left shift for dOPh rounding bits + MOVLS fOP, sign ; LO: shift larger than 33 -> return signed zero + ORRLS r4, r4, #UNF_bit :OR: INX_bit + BLS __dtos_return + + MOV dOPh, dOPh, LSL #DExp_len-1 ; dOPh shift left 1 bit + ORR dOPh, dOPh, dOPl, LSR #DFhi_len+1 + ORR dOPh, dOPh, #1 << 31 + + MOVS tmp, dOPh, LSL tmp ; CS -> round + ORRNE r4, r4, #UNF_bit :OR: INX_bit + MOV tmp, dOPl ; save dOPl since we may need it + ADC fOP, sign, dOPh, LSR exp + BCC __dtos_return + MOVEQS tmp, tmp, LSL #32 - (DFhi_len+1) ; * Was: MOVEQS dOPl, dOPl, LSL #32 + BICEQ fOP, fOP, #1 + B __dtos_return + + +__dtos_return_overflow + ORR r4, r4, #OVF_bit :OR: INX_bit ; Set exception information + AND r0, r0, #0x80000000 ; Keep sign bit + MOV r1, #0xFF000000 ; Set exponent to max + ORR r0, r0, r1, LSR #1 ; .. + + +__dtos_return + TST r4, #FPECause_mask ; Check for exceptions + MOV r1, r4 ; Move exception info. + ADDEQ sp, sp, #LOC_SIZE ; If none, pop input arg + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {r4, lr} ; restore r4 and return + BXEQ lr + ELSE + LDMEQFD sp!, {r4, pc} ; restore r4 and return + ENDIF + + STR r0, [sp, #ExDResl] ; Store default result + LDR r2, [sp, #OrgOp1l] ; Load original arg + LDR r3, [sp, #OrgOp1h] ; .. + ADD r0, sp, #NewResl ; Pointer to new result + + CALL FPE_Raise ; Deal with exception info. + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #NewResl] ; Load new result + ADD sp, sp, #LOC_SIZE ; Pop exception record, arg + IF Interworking :LOR: Thumbing + LDMFD sp!, {r4, lr} ; Restore r4 and return + BX lr + ELSE + LDMFD sp!, {r4, pc} ; Restore r4 and return + ENDIF + + ENTRY_END __dtos + + ] + +;------------------------------------------------------------------------------ + + [ :DEF: f2d_s + +LOC_SIZE EQU 0x18 +OrgOp1l EQU 0x14 +ExDResh EQU 0x0c +ExDResl EQU 0x08 +NewResh EQU 0x14 +NewResl EQU 0x10 + + Export __stod + IMPORT FPE_Raise + + ; Ensure the defines are what they should be. Note that there was + ; a problem when we swapped the double halves as the code relied on + ; the low half of the double not being the register where floats are + ; passed. I have put these asserts here so the registers don't + ; change out from under me. We don't really need these conditions + ; to hold true, just the code needs to be checked after register + ; defines are changed. + ASSERT exp = r3 + ASSERT sign = r2 + ASSERT tmp = r12 + ASSERT dOPh = r1 + ASSERT dOPl = r0 + ASSERT fOP = r0 + + NESTED_ENTRY __stod + EnterWithLR_16 + STMFD sp!, {lr} ; Save return address + SUB sp, sp, #LOC_SIZE ; Allocate local storage + PROLOG_END + STR r0, [sp, #OrgOp1l] ; Store original arg in case of exception + ADD tmp, fOP, #1 << SExp_pos ; filter out inf/NaN/denorm/zero + TST tmp, #254 << SFrac_len + BEQ _f2d_uncommon + MOV tmp, fOP + MOV dOPl, tmp, LSL #32 - 3 + MOVS dOPh, tmp, ASR #3 + ADD dOPh, dOPh, #(DExp_bias - SExp_bias) << DExp_pos + ADDPL sp, sp, #LOC_SIZE + IF Interworking :LOR: Thumbing + LDMPLFD sp!, {lr} + BXPL lr + ELSE + LDMPLFD sp!, {pc} + ENDIF + SUB dOPh, dOPh, #0x700 << DExp_pos + ADD sp, sp, #LOC_SIZE + IF Interworking :LOR: Thumbing + LDMFD sp!, {lr} + BX lr + ELSE + LDMFD sp!, {pc} + ENDIF + +_f2d_uncommon + TST tmp, #1 << SExp_pos ; inf/NaN -> EQ, zero/denorm ->NE + MOV tmp, fOP + BEQ _f2d_Inf_or_NaN +_f2d_denorm + MOVS dOPl, tmp, LSL #1 ; zero -> EQ + MOVEQ dOPh, tmp + ADDEQ sp, sp, #LOC_SIZE ; dOPl zero, dOPh sign bit + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} + BXEQ lr + ELSE + LDMEQFD sp!, {pc} + ENDIF + + + ; We have a denormal that must be normalized. The exponent and sign + ; are initialized, then the input argument's mantissa is shifted left + ; until the hidden one is left justified. The exponent is adjusted to + ; account for the shifts. Then the hidden one is removed and the + ; final result assembled. + AND exp, tmp, #Sign_bit ; Extract sign + ADD r1, exp, #(DExp_bias-SExp_bias) << DExp_pos + ; Initialize exponent + MOV r0, tmp, LSL #9 ; Extract mantissa and left justify + + MOVS r2, r0, LSR #16 ; Any high 16 bits set? + SUBEQ r1, r1, #16 << DExp_pos ; If not, adjust exponent + MOVEQ r0, r0, LSL #16 ; shift mantissa + + TST r0, #0xFF000000 ; Any high 8 bits set? + SUBEQ r1, r1, #8 << DExp_pos ; If not, adjust exponent + MOVEQ r0, r0, LSL #8 ; shift mantissa + + TST r0, #0xF0000000 ; Any high 4 bits set? + SUBEQ r1, r1, #4 << DExp_pos ; If not, adjust exponent + MOVEQ r0, r0, LSL #4 ; shift mantissa + + TST r0, #0xC0000000 ; Any high 2 bits set? + SUBEQ r1, r1, #2 << DExp_pos ; If not, adjust exponent + MOVEQS r0, r0, LSL #2 ; shift mantissa + + MOVPL r0, r0, LSL #1 ; If high bit clear, adjust exponent + SUBPL r1, r1, #1 << DExp_pos ; shift mantissa + + MOV r0, r0, LSL #1 ; Account for hidden 1 + ORR r1, r1, r0, LSR #12 ; Form sign, exp, high mantissa + MOV r0, r0, LSL #20 ; Form low mantissa + ADD sp, sp, #LOC_SIZE ; Restore stack and + IF Interworking :LOR: Thumbing + LDMFD sp!, {lr} ; return + BX lr + ELSE + LDMFD sp!, {pc} ; return + ENDIF + + +_f2d_Inf_or_NaN ; fOP is NaN/infinity. + MOVS dOPl, tmp, LSL #SExp_len+1 ; EQ -> inf + ORREQ dOPh, tmp, #0x007 << DExp_pos ; tranform float inf to double inf + ADDEQ sp, sp, #LOC_SIZE ; Restore stack and + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; return + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; return + ENDIF + ; MI if quiet NaN - sign in fOP + BPL __stod_snan + ; Have a QNaN so copy the mantissa bits and return the QNaN + MOV dOPh, tmp, ASR #3 ; Load mantissa high, sign + MOV dOPl, tmp, LSL #29 ; Load mantissa low + ORR dOPh, dOPh, #0x70000000 ; Force exp to max + ADD sp, sp, #LOC_SIZE ; Restore stack and + IF Interworking :LOR: Thumbing + LDMFD sp!, {lr} ; return + BX lr + ELSE + LDMFD sp!, {pc} ; return + ENDIF + +__stod_snan ; Got an SNaN so raise exception + LDR r2, [sp, #OrgOp1l] ; Load original operand + MOV r1, r12, ASR #3 ; Load mantissa high, sign + MOV r0, r12, LSL #29 ; Load mantissa low + ORR r1, r1, #0x70000000 ; Force exp to max + ORR r1, r1, #0x00080000 ; Make QNaN + STR r1, [sp, #ExDResh] ; Store default result + STR r0, [sp, #ExDResl] ; .. + ADD r0, sp, #NewResl ; Pointer to return result + MOV r1, #_FpSToD ; Load opcode and exception info. + ORR r1, r1, #IVO_bit ; .. + + CALL FPE_Raise ; Deal with exception info. + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r1, [sp, #NewResh] ; Load new return value + LDR r0, [sp, #NewResl] ; .. + ADD sp, sp, #LOC_SIZE ; Pop space off stack + IF Interworking :LOR: Thumbing + LDMFD sp!, {lr} ; Return + BX lr + ELSE + LDMFD sp!, {pc} ; Return + ENDIF + + ENTRY_END __stod + ] + +;============================================================================== + + END diff --git a/base/Kernel/Native/arm/Crt/format_d.asm b/base/Kernel/Native/arm/Crt/format_d.asm new file mode 100644 index 0000000..76229fc --- /dev/null +++ b/base/Kernel/Native/arm/Crt/format_d.asm @@ -0,0 +1,1429 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; format_d.s +; +; Copyright (C) Advanced RISC Machines Limited, 1994. All rights reserved. +; +; RCS Revision: 1 +; Checkin Date: 2007/06/29 02:59:16 +; Revising Author + + GET fpe.asm + + CodeArea |FPL$$Format| + + IMPORT __fp_nonveneer_error + +; Functions for converting +; double <-> internal, +; double <-> int, +; double <-> longlong +; and for fiddling with the double format (frexp) + + + +DNaNInf EQU NaNInfExp_Double - EIExp_bias + DExp_bias + +exp RN 2 + +;============================================================================== + + [ :DEF: dflt_s :LOR: :DEF: dfltu_s + +;This code needed for the unsigned float too. + + MACRO + DoubleNormalise + +;Now we have the basis of the exponent, we round. + + MOVS utmp1, a1, LSR #16 + MOVEQ a1, a1, LSL #16 + SUBEQ tmp, tmp, #16< 0 + BNE _ll_to_d_shared + | + BEQ %FT02 + +01 + LongDoubleNormalise + +02 + ] + EnterWithStack + BL _dfltu + ORR dOPh, dOPh, #Sign_bit + ReturnToStack + ] + + +;------------------------------------------------------------------------------ + + [ :DEF: ll_uto_d_s + + Export _ll_uto_d + ExportCodeSize _ll_to_d_shared + +_ll_uto_d EnterWithLR_16 + + IMPORT _dfltu + + TEQ a2, #0 + BEQ _dfltu + + [ CodeSize = 2 + MOV tmp, #0 + +_ll_to_d_shared + ORR tmp, tmp, #((DExp_bias + 63) :AND: 0xff00) << DExp_pos + | + MOV tmp, #((DExp_bias + 63) :AND: 0xff00) << DExp_pos + ] + [ CodeSize = 1 +_ll_to_d_shared + ] + ORR tmp, tmp, #((DExp_bias + 63) :AND: 0x00ff) << DExp_pos + + LongDoubleNormalise + + ] + +;=========================================================================== + + [ :DEF: dfix_s + + Export _dfix + + ROUT +_dfix EnterWithLR_16 + MOVS a3, dOPh, LSL #1 ; C<--sign + MOV a3, a3, LSR #DFhi_len+1 + MOV dOPh, dOPh, LSL #DExp_len + ORR a1, dOPh, dOPl, LSR #DFhi_len+1 + ORR a1, a1, #1<<31 + SUB a3, a3, #(DExp_bias:AND:0xff00) + BCS _dfix_neg + SUBS a3, a3, #(DExp_bias:AND:0x00ff) + BMI _dfix_zero + RSBS a3, a3, #31 + ; if resulting shift is -ve, or zero, then the result would + ; overflow (zero because it would leave the units bit + ; unchanged, and this path is for +ve arguments) + MOVGT a1, a1, LSR a3 + ; Don't return -ve results for +ve arguments - will only happen + ; if the shift amount is zero + ReturnToLR GT + + ; a1 contains the mantissa, with units bit at bit 31 (shift + ; did not happen, or was zero) + ; a2 contains the other bits of the mantissa + BIC a1, a1, #Sign_bit ; ensure any untrapped result is +ve + B _dfix_ivo + +_dfix_zero + MOV a1, #0 + ReturnToLR + +_dfix_neg + SUBS a3, a3, #(DExp_bias:AND:0x00ff) + BMI _dfix_zero + RSBS a3, a3, #31 + MOVGT a1, a1, LSR a3 + RSBGT a1, a1, #0 ; don't return +ve for -ve arguments + ReturnToLR GT + +; Special case - INT_MIN will leave a shift of zero (Z set), and +; r0=0x80000000 r1=0x00000000 + ORREQS ip, dOPl, dOPh, LSL #1 ; Z<-special case + ReturnToLR EQ + +; ORR a1, a1, #Sign_bit ; ensure untrapped result is -ve +_dfix_ivo + MOV a4, #IVO_bit :OR: FixFn + [ {TRUE} + ; On some systems, casting NaN returns zero + ; check for maximum exponent + ADD a3, a3, #:NOT:(31-((DExp_mask:SHR:DExp_pos)-DExp_bias)) + CMN a3, #1 + BNE __fp_nonveneer_error + ORRS ip, dOPl, a1, LSL #1 ; ignore prospective sign + MOVNE a4, #IVO_bit :OR: FixZeroFn + ] + B __fp_nonveneer_error + ] + +;--------------------------------------------------------------------------- + + [ :DEF: dfixu_s + + ROUT + Export _dfixu + +_dfixu EnterWithLR_16 + MOVS a3, dOPh, LSL #1 ; C<-sign + BCS _dfixu_neg + [ CodeSize <> 0 +;; This doesn't work at the moment, because dfixu will return UINT_MAX on +;; overflow, whereas for dfix it should return INT_MAX. The same applies +;; to longlong and float versions +_dfix_shared + ] + MOV a3, a3, LSR #DFhi_len+1 ; get exponent into a3 + MOV dOPh, dOPh, LSL #DExp_len + ORR a1, dOPh, dOPl, LSR #DFhi_len+1 ; get mantissa into a1 + ORR a1, a1, #1<<31 ; and add units bit + SUB a3, a3, #(DExp_bias:AND:0xff00) + SUBS a3, a3, #(DExp_bias:AND:0x00ff) + BMI _dfix_zero + RSBS a3, a3, #31 + ; in the unsigned case, a shift of zero is perfectly allowable + MOVPL a1, a1, LSR a3 + ReturnToLR PL + +_dfixu_ivo + MOV a4, #IVO_bit :OR: FixuFn + [ {TRUE} + ; On some systems, casting NaN returns zero + ; check for maximum exponent + ADD a3, a3, #:NOT:(31-((DExp_mask:SHR:DExp_pos)-DExp_bias)) + CMN a3, #1 + BNE __fp_nonveneer_error + ORRS ip, dOPl, a1, LSL #1 ; ignore units bit + MOVNE a4, #IVO_bit :OR: FixZeroFn + ] + B __fp_nonveneer_error + +_dfixu_neg + ORRS a4, dOPl, dOPh, LSL #1 + MOVNE a4, #IVO_bit :OR: FixZeroFn + BNE __fp_nonveneer_error +_dfix_zero + MOV a1, #0 + ReturnToLR + + ] + +;------------------------------------------------------------------------------ + + [ :DEF: ll_sfrom_d_s + + Export _ll_sfrom_d + + ROUT +_ll_sfrom_d EnterWithLR_16 + + MOVS a4, dOPh, LSL #1 ; C<-sign + MOV a4, a4, LSR #DFhi_len+1 ; exp + MOV tmp, dOPh, LSL #DExp_len ; mhi + ORR tmp, tmp, dOPl, LSR #DFhi_len+1 ; add mlo to mhi + ORR tmp, tmp, #1<<31 ; set units bit -- tmp now has top of mantissa + MOV a1, dOPl, LSL #DExp_len ; a1 <- low of mantissa + + ; a1 <- lsw of mantissa + ; a2 <- original low word of mantissa + ; tmp <- msw of mantissa, with units + ; a4 <- exponent, still biased + ; C <- sign + + SUB a4, a4, #(DExp_bias :AND: 0xff00) + + BCS ll_from_d_negative + + SUBS a4, a4, #(DExp_bias :AND: 0x00ff) + BMI _ll_from_d_zero + RSBS a3, a4, #31 ; N <- large exponent + + MOVGT a1, tmp, LSR a3 ; small exponent - 32+a3 shift + MOVGT a2, #0 ; msw is zero + ReturnToLR GT + + ADDS a3, a3, #32 ; large exponent, N <- too large + SUB a4, a4, #31 + MOVGT a1, a1, LSR a3 + ORRGT a1, a1, tmp, LSL a4 + MOVGT a2, tmp, LSR a3 ; ensure result is +ve + ReturnToLR GT + ; fall through + ; tmp/a2 <- mantissa + ; a4 <- unbiased exponent + + BIC a1, a1, #Sign_bit ; prospective result is +ve + B _ll_from_d_ivo + +_ll_from_d_zero + MOV a1, #0 + MOV a2, #0 + ReturnToLR + +ll_from_d_negative + SUBS a4, a4, #(DExp_bias :AND: 0x00ff) + BMI _ll_from_d_zero + RSBS a3, a4, #31 + + MOVPL a1, tmp, LSR a3 + MOVPL a2, #0 + BPL %ft01 + + ADDS a3, a3, #32 + SUB a4, a4, #31 + BLE %f02 + MOV a1, a1, LSR a3 + ORR a1, a1, tmp, LSL a4 + MOV a2, tmp, LSR a3 + +01 RSBS a1, a1, #0 + RSC a2, a2, #0 + + ReturnToLR + +02 +; special case for LLINT_MIN + ORREQS a1, dOPl, ip, LSL #1 + MOVEQ a2, #Sign_bit + ReturnToLR EQ + + ORR a1, a1, #Sign_bit ; ensure result is -ve + ; tmp/a2 <- mantissa + ; a3 <- unbiased exponent +_ll_from_d_ivo + MOV a4, #IVO_bit :OR: FixFn :OR: LongLong_bit + [ {TRUE} + ; On some systems, casting NaN returns zero + ; check for maximum exponent + ADD a3, a3, #:NOT:(63-((DExp_mask:SHR:DExp_pos)-DExp_bias)) + CMN a3, #1 + BNE __fp_nonveneer_error + ORRS ip, dOPl, tmp, LSL #1 ; ignore units bit + MOVNE a4, #IVO_bit :OR: FixZeroFn :OR: LongLong_bit + ] + B __fp_nonveneer_error + ] + +;------------------------------------------------------------------------------ + + [ :DEF: ll_ufrom_d_s + + Export _ll_ufrom_d + + ROUT +_ll_ufrom_d EnterWithLR_16 + + MOVS a3, dOPh, LSL #1 ; C<-sign + BCS _ll_ufrom_d_neg + MOV a3, a3, LSR #DFhi_len+1 ; exp + MOV a4, dOPh, LSL #DExp_len ; mhi + ORR a4, a4, dOPl, LSR #DFhi_len+1 ; add mlo to mhi + ORR a4, a4, #1<<31 ; set units bit -- a4 now has top of mantissa + MOV a1, dOPl, LSL #DExp_len ; a1 <- low of mantissa + MOV a2, a4 ; a2 <- high of mantissa + ; a1 <- lsw of mantissa + ; a2 <- msw of mantissa, with units + + SUB a3, a3, #(DExp_bias :AND: 0xff00) + SUBS a3, a3, #(DExp_bias :AND: 0x00ff) + BMI _ll_ufrom_d_zero + RSBS a4, a3, #31 ; N <- large exponent + + MOVPL a1, a2, LSR a4 ; small exponent - 32+a4 shift + MOVPL a2, #0 ; msw is zero + ReturnToLR PL + + ADDS a4, a4, #32 ; large exponent, N <- too large + SUB a3, a3, #31 + MOVPL a1, a1, LSR a4 + ORRPL a1, a1, a2, LSL a3 + MOVPL a2, a2, LSR a4 + ReturnToLR PL + ; fall through + +_ll_ufrom_d_ivo + ; On some systems, casting NaN returns zero + ; check for maximum exponent + ADD a3, a4, #:NOT:(63-((DExp_mask:SHR:DExp_pos)-DExp_bias)) + CMN a3, #1 + MOV a4, #IVO_bit :OR: FixuFn :OR: LongLong_bit + BNE __fp_nonveneer_error + ORRS ip, a1, dOPl, LSL #1 ; ignore units bit + MOVNE a4, #IVO_bit :OR: FixZeroFn :OR: LongLong_bit + B __fp_nonveneer_error + +_ll_ufrom_d_neg + ORRS a4, dOPl, dOPh, LSL #1 + MOVNE a4, #IVO_bit :OR: FixZeroFn :OR: LongLong_bit + BNE __fp_nonveneer_error +_ll_ufrom_d_zero + MOV a1, #0 + MOV a2, #0 + ReturnToLR + + ] + +;============================================================================== + + [ :DEF: d2e_s + + EXPORT _d2e + +_d2e EnterWithLR + MOVS tmp,dOPh,LSL #1 ;C=sign; Z=exp & frac.top zero + TEQEQ dOPl,#0 ;C unchanged; Z=value is a zero + MOV a4,tmp,LSL #DExp_len-1 ;Frac.top in bits 30:11 of mhi + MOV dOPh,tmp,LSR #DExp_pos ;Exponent in bits 11:1 + MOV OP1mlo,dOPl,LSL #DExp_len ; OP2mhi and 31:11 of OP2mlo + ORR OP1mhi,a4,dOPl,LSR #DFhi_len+1 + ;Fraction in bits 30:0 of + ADDNE dOPh,dOPh,#(EIExp_bias-DExp_bias):SHL:1 + MOV OP1sue,dOPh,RRX ;Recombine sign and exponent + ORRNE OP1mhi,OP1mhi,#EIUnits_bit + + ; Gets here with the *double precision* exponent in the top 11 bits + ; of tmp. (Exponent<<1+DExp_pos.) We use a sign extended shift to + ; spot the "maximum exponent case" - leaves us with -1 in tmp. + MOVS tmp,tmp,ASR #1+DExp_pos + BEQ _d2e_norm_op1 ;Returns to caller + CMP tmp,#&ffffffff + ORREQ OP1sue,OP1sue,#Uncommon_bit + + ReturnToLR + +_d2e_norm_op1 + +; Got to here implies a denormalised number. This needs normalising. The +; units bit is (incorrectly) set. If it *is* unset then this is a zero, +; and we have got here "by accident". + + TST OP1mhi, #EIUnits_bit + ReturnToLR EQ + + BICS OP1mhi,OP1mhi,#Sign_bit + BEQ _d2e_denorm_low + +; The top set bit in the high word, so find out how much to denormalise +; by + + MOVS RNDexp,OP1mhi,LSR #16 + MOVEQ OP1mhi,OP1mhi,LSL #16 + MOVEQ tmp,#16 + MOVNE tmp,#0 + MOVS RNDexp,OP1mhi,LSR #24 + MOVEQ OP1mhi,OP1mhi,LSL #8 + ADDEQ tmp,tmp,#8 + MOVS RNDexp,OP1mhi,LSR #28 + MOVEQ OP1mhi,OP1mhi,LSL #4 + ADDEQ tmp,tmp,#4 + MOVS RNDexp,OP1mhi,LSR #30 + MOVEQ OP1mhi,OP1mhi,LSL #2 + ADDEQ tmp,tmp,#2 + MOVS RNDexp,OP1mhi,LSR #31 + MOVEQ OP1mhi,OP1mhi,LSL #1 + ADDEQ tmp,tmp,#1 + +; tmp now contains the amount to shift by. + + RSB RNDexp,tmp,#32 + ORR OP1mhi,OP1mhi,OP1mlo,LSR RNDexp + MOV OP1mlo,OP1mlo,LSL tmp + + SUB OP1sue,OP1sue,tmp + ADD OP1sue,OP1sue,#1 + ReturnToLR + +_d2e_denorm_low + +; The top bit to be set is in OP1mlo + + MOVS RNDexp,OP1mlo,LSR #16 + MOVEQ OP1mlo,OP1mlo,LSL #16 + MOVEQ tmp,#16 + MOVNE tmp,#0 + MOVS RNDexp,OP1mlo,LSR #24 + MOVEQ OP1mlo,OP1mlo,LSL #8 + ADDEQ tmp,tmp,#8 + MOVS RNDexp,OP1mlo,LSR #28 + MOVEQ OP1mlo,OP1mlo,LSL #4 + ADDEQ tmp,tmp,#4 + MOVS RNDexp,OP1mlo,LSR #30 + MOVEQ OP1mlo,OP1mlo,LSL #2 + ADDEQ tmp,tmp,#2 + MOVS RNDexp,OP1mlo,LSR #31 + MOVEQ OP1mlo,OP1mlo,LSL #1 + ADDEQ tmp,tmp,#1 + +; tmp now contains the amount to shift by. + + MOV OP1mhi,OP1mlo + MOV OP1mlo,#0 + + SUB OP1sue,OP1sue,#31 + SUB OP1sue,OP1sue,tmp + ReturnToLR + + ] + +;============================================================================== + + [ :DEF: e2d_s + + EXPORT _e2d + EXPORT __fp_e2d + + MACRO + E2D_Return $cc=AL + MOV$cc a4, r10 + LDM$cc.FD sp!, {r10} + IF Interworking :LOR: Thumbing + BX$cc lr + ELSE + MOV$cc pc, lr + ENDIF + MEND + +_e2d EnterWithLR + MOV RNDexp, OP1sue, LSL #32-EIExp_len + MOV RNDexp, RNDexp, LSR #32-EIExp_len + BIC OP1sue, OP1sue, RNDexp + +__fp_e2d + STMFD sp!, {r10} ; r10 is used to store exception information + MOV r10, #0 ; Assume no exceptions +;Passed in the result of an operation. That is: +; - the uncommon/sign are in OP1sue +; an error can be flagged as Uncommon, with the Error bit set, and +; a further bit specifying which. fpe.s defines appropriate values +; (e.g. IVO_bits). These are transferred to a4 on exit. +; - the exponent in RNDexp, +; - the fraction in OP1mlo/OP1mhi. +;Return with +; - a result in dOPh/dOPl and possibly some error flags in a4 + + ASSERT Uncommon_bit = 1:SHL:(31-1) + ASSERT EIUnits_bit = 1:SHL:31 + BICS tmp, OP1mhi, OP1sue, LSL #1 + BPL _e2d_SpecialCase + + SUBS RNDexp, RNDexp, #(EIExp_bias-DExp_bias) + BMI _e2d_ExpUnderflow + + ASSERT tmp <> RNDexp + ADDNE tmp, RNDexp, #1 + CMPNE tmp, #DNaNInf+1 + BGE _e2d_ExpOverflow + + MOVS tmp, OP1mlo, LSR #32-DFhi_len-1 ; 1 is for the J bit +;If rounding is needed then a one will have dropped out + BCS _e2d_NeedsRounding + +;Simple enough now then. + + ; Test for inexact + ORRS Rarith, Rarith, OP1mlo, LSL #DFhi_len+1 + ORRNE r10, r10, #INX_bit + + ORR dOPh, OP1sue, RNDexp, LSL #DExp_pos + BIC OP1mhi, OP1mhi, #EIUnits_bit + ORR dOPh, dOPh, OP1mhi, LSR #32-DFhi_len-1 + ORR dOPl, tmp, OP1mhi, LSL #DFhi_len+1 + E2D_Return ; 22S+1N + +;.............................................................................. + +_e2d_SpecialCase + + TST OP1sue, #Uncommon_bit + BNE _e2d_Uncommon + +_e2d_UnitsBit + +;Sign is in OP1sue's top bit. The units bit of OP1mhi is clear indicating a +;zero value (since the denorm case is handled by the uncommon bit). + + AND dOPh, OP1sue, #Sign_bit + MOV dOPl, #0 + E2D_Return ; 9S+2N + +;.............................................................................. + +_e2d_ExpOverflow + +;Sign is in OP1sue's sign bit. May still need rounding. The exponent (RNDexp) +;is out of range for a double precision number, and wasn't signalled as a NaN. + +;The exponent has been re-biased. + +;Might just be the "just underflowing case" (because of a quirk above). In +;that case RNDexp = 0 + + TEQ RNDexp, #0 + BEQ _e2d_ExpUnderflow + MOV r10, #OVF_bit :OR: INX_bit + MOV r0, #0 ; Have an overflow so clear mantissa and + MVN r2, #0 ; force exponent to max, preserving the sign + ANDS r1, r1, r2, LSL #20 ; .. + ORR r1, r1, r2, LSL #20 ; .. + BICPL r1, r1, #0x80000000 ; .. + E2D_Return + +;.............................................................................. + Export _e2d_ExpUnderflow +_e2d_ExpUnderflow + +;Underflow. If the value can be represented as a denorm in the target +;precision, then it should be. For this to be the case the exponent needs +;to lie in the range [&380-23..&380]. Otherwise the result is a suitably +;signed zero. (A zero is, however, a denorm with zero mantissa.) + +;The exponent (RNDexp) has been rebiased (by (EIExp_bias-SExp_bias)) + + ADDS RNDexp, RNDexp, #DFhi_len+32 + +;If the result is zero then we round according to the top bit of the +;fraction. Branch out of line to do this. + + BEQ _e2d_ExpJustUnderflow + +;If this result is negative, nothing can be done, so return a signed zero. + + ANDMI dOPh, OP1sue, #Sign_bit + MOVMI dOPl, #0 + ORRMI r10, r10, #UNF_bits + E2D_Return MI + +;We now have in OP1mhi 1MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +;and in OP1mlo MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +;These need to be converted into a denormalised number. We want the N +;left most bits (N<=52) from this 64-bit value. This splits into three +;cases - [1..20], [21..32] and [33..52]. We actually split it into the +;[1..32] and [33..52] cases. + + SUBS RNDexp, RNDexp, #32 + BEQ _e2d_Underflow32 + BLT _e2d_ShortUnderflow + +;Okay - all the bits we are going to throw away are in the low word. +;Check for rounding. + + SUB tmp, RNDexp, #1 + ORRS tmp, Rarith, OP1mlo, LSL RNDexp + ORRNE r10, r10, #UNF_bits ; Determine inexact + + MOVS tmp, OP1mlo, LSL RNDexp + BMI _e2d_LongUnderflowNeedsRounding + + RSB tmp, RNDexp, #32 + ASSERT dOPh <> OP1mhi + ORR dOPh, OP1sue, OP1mhi, LSR tmp + MOV OP1mlo, OP1mlo, LSR tmp + ORR dOPl, OP1mlo, OP1mhi, LSL RNDexp + + E2D_Return + +_e2d_LongUnderflowNeedsRounding + +;We need to determine whether to just round upwards, or to round to even. + + ORRS tmp, Rarith, tmp, LSL #1 ;C=1, Z=definite round to even. + +;Now we calculate the shift amount and do the shift, probably throwing away +;lots of mantissa bits + + RSB tmp, RNDexp, #32 + +;Merge in the sign bit and add one (C flag). Then undo the rounding if we +;need to round to even and the result isn't even. + + ORR dOPh, OP1sue, OP1mhi, LSR tmp + MOV OP1mlo, OP1mlo, LSR tmp + ORR dOPl, OP1mlo, OP1mhi, LSL RNDexp + TSTEQ dOPl, #1 + E2D_Return EQ + ADDS dOPl, dOPl, #1 + ADDCS dOPh, dOPh, #1 + TST dOPh, #0x00100000 ; Check if rounded up to MinNormal + BICNE r10, r10, #UNF_bit ; If did, no longer underflow + E2D_Return + +;.............................................................................. + +_e2d_Underflow32 + + ; Check for inexact + ORRS tmp, Rarith, OP1mlo + ORRNE r10, r10, #UNF_bits + + + +;We are going to throw away the entire of the bottom word. This makes +;rounding simple, so it is special cased. + + TEQ OP1mlo, #1<<31 + +;Z flag is set if only the top bit is set. The N flag is set to the +;*inverse* of the top bit. + + ASSERT OP1sue = dOPh + ASSERT OP1mhi = dOPl + + E2D_Return MI ;No rounding to be done + + TSTEQ dOPl, #1 ;Z flag now clear if rounding needed + E2D_Return EQ + + ADDS dOPl, dOPl, #1 + ADDCS dOPh, dOPh, #1 + E2D_Return + +;.............................................................................. + +_e2d_ShortUnderflow + +;We are going to throw away all of the bottom word. The amount to shift +;by is in RNDexp, but has had 32 taken away. + + ADD RNDexp, RNDexp, #32 ; Undo the subtraction + + ; Check for inexact + SUB tmp, RNDexp, #0 + ORR tmp, Rarith, OP1mhi, LSL tmp + ORRS tmp, tmp, OP1mlo + ORRNE r10, r10, #UNF_bits + + MOVS tmp, OP1mhi, LSL RNDexp ; test top bit to be thrown + BMI _e2d_ShortUnderflowNeedsRounding + + RSB tmp, RNDexp, #32 + ASSERT dOPh <> OP1mhi + MOV dOPl, OP1mhi, LSR tmp + AND dOPh, OP1sue, #Sign_bit + E2D_Return + +_e2d_ShortUnderflowNeedsRounding + + TEQ tmp, #1<<31 ; any bits other than rounding + ORREQS tmp, OP1mlo, Rarith ; bit? i.e. in lo or in sticky? + + RSB tmp, RNDexp, #32 + ASSERT dOPh = OP1sue + MOV dOPl, OP1mhi, LSR tmp + TSTEQ dOPl, #1 + ADDNE dOPl, dOPl, #1 ; Can't overflow in this case + E2D_Return + +_e2d_ExpJustUnderflow + +;The exponent is just at the limits of underflow - i.e. we may be able +;to represent the number as the float epsilon. + +; MOVS dOPh, dOPh, LSR #20 +; ADC fOP, tmp, #0 + +;But that's not how the FP emulator behaves. It does this: + + TEQ OP1mhi, #1<<31 + TEQEQ OP1mlo, #0 + ASSERT OP1sue = dOPh + MOVEQ dOPl, #0 + MOVNE dOPl, #1 + ORR r10, r10, #UNF_bits + E2D_Return + +_e2d_Uncommon + +;The uncommon bit may signal an error (in which case the error bit will +;be set too) + +; RDCFix: Get rid of this? Take care of NaNs, etc. in special code for each routine? +; TST OP1sue,#Error_bit +; BNE _e2d_Error + + +;The uncommon bit (infinity or a NaN) bit was set. The exponent in RNDexp +;is invalid. An infinity is signalled by mhi=mlo=0. + + TEQ OP1mhi, #0 + MOVNES OP1mhi, OP1mhi, LSL #1 + MOVMI dOPh, #-1 + E2D_Return MI + TEQEQ OP1mlo, #0 + E2D_Return NE + +;An infinity. The sign of the resulting infinity is in tmp. + + AND dOPh, OP1sue, #Sign_bit + ORR dOPh, dOPh,#(DNaNInf:AND:&ff00)<dOPh + STMIA a3,{dOPh,a4} + ASSERT dOP2l=a4 + EOR dOP2h,dOP1h,#Sign_bit + B _dadd + +modf_truncate_top + MOV a4,dOPh,LSR tmp + MOV a4,a4,LSL tmp + MOV tmp,#0 + ASSERT tmp>a4 + STMIA a3,{a4,tmp} + EOR dOP2h,a4,#Sign_bit + MOV dOP2l,#0 + B _dadd + +modf_big + +; The number is so big that it has no fractional part. + + STMIA a3,{dOPh,dOPl} + MOV dOPh,#0 + MOV dOPl,#0 + ReturnToLR + +modf_small + +; The number is so small that it has a zero integer part. + + MOV a4,#0 + MOV tmp,#0 + STMIA a3,{a4,tmp} + ReturnToLR + + ] + +;=========================================================================== + + [ :DEF: floor_s + + EXPORT floor + EXPORT ceil + +mask RN 12 + + ; Floor - Round to an integer towards -Inf + +floor EnterWithLR + MOVS exp, dOPh, LSL #1 + BCS ceil_1 +floor_1 + MOV exp, exp, LSR #21 + RSB exp, exp, #1088 ; 1088 is first 8-bit constant > 1023+53 + RSBS exp, exp, #65 + BLO floor_exp_overflow ; exp > 1088 or exp < 1023 -> overflow + ; exp is number of fraction bits to preserve (0 .. 65) + RSBS exp, exp, #20 ; number of bits to clear in high word + MOV mask, #-1 + ANDGE dOPh, dOPh, mask, LSL exp ; clear exp bits in high word + MOVGE dOPl, #0 + ReturnToLR GE + RSB exp, exp, #0 ; number of bits to preserve in low word + BIC dOPl, dOPl, mask, LSR exp ; preserve exp bits in low word + ReturnToLR + +floor_exp_overflow ; PL if exp too large, MI too small + ANDMI dOPh, dOPh, #Sign_bit ; create signed zero + MOVMI dOPl, #0 + ReturnToLR + + + ; Ceil - Round to an integer towards +Inf + +ceil EnterWithLR + MOVS exp, dOPh, LSL #1 + BCS floor_1 +ceil_1 + MOV exp, exp, LSR #21 + RSB exp, exp, #1088 + RSBS exp, exp, #65 + BLO ceil_exp_overflow ; exp > 1088 or exp < 1023 -> overflow + ; exp is number of fraction bits to preserve (0 .. 65) + MOV mask, #-1 + SUBS exp, exp, #20 ; number of bits to preserve in low word + BGT ceil_low + ADD exp, exp, #32 ; number of bits to preserve in high word + ADDS dOPl, dOPl, #-1 ; round up low word + ADC dOPh, dOPh, mask, LSR exp ; round up high word + BIC dOPh, dOPh, mask, LSR exp ; clear exp bits in high word + MOV dOPl, #0 + ReturnToLR + +ceil_low + ADDS dOPl, dOPl, mask, LSR exp ; round up low word + BIC dOPl, dOPl, mask, LSR exp ; clear exp bits in low word + ReturnToLR CC + ADD dOPh, dOPh, #1 ; round up high word + ReturnToLR + +ceil_exp_overflow ; PL if exp too large, MI too small + ReturnToLR PL + ORRS tmp, dOPl, dOPh, LSL #1 ; zero? + ANDNE dOPh, dOPh, #Sign_bit + ORRNE dOPh, dOPh, #0x30000000 ; no, create +-1.0 + ORRNE dOPh, dOPh, #0x0FF00000 + MOVNE dOPl, #0 + ReturnToLR + + ] + +;=========================================================================== + + END diff --git a/base/Kernel/Native/arm/Crt/format_e.asm b/base/Kernel/Native/arm/Crt/format_e.asm new file mode 100644 index 0000000..212c103 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/format_e.asm @@ -0,0 +1,385 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; format_e.s +; +; Copyright (C) Advanced RISC Machines Limited, 1994. All rights reserved. +; +; RCS Revision: 1 +; Checkin Date: 2007/06/29 02:59:16 +; Revising Author + + GET fpe.asm + + AREA |.text|, CODE, READONLY + +SNaNInf EQU NaNInfExp_Single - EIExp_bias + SExp_bias +DNaNInf EQU NaNInfExp_Double - EIExp_bias + DExp_bias + +;============================================================================== + + [ :DEF: e2e_s + + EXPORT _e2e + +; Convert a five-word internal value to a three word value +; +; Enters with the partly-constructed internal precision value in OP1sue, +; OP1mhi and OP1mlo, the exponent in RNDexp and the guard/sticky bits in +; Rarith. No truncation is needed, only rounding. + + [ :DEF: thumb + CODE32 + ] + +_e2e + TST OP1sue,#Error_bit+Uncommon_bit + BNE _e2e_error_or_uncommon + + MOVS Rtmp,Rarith,LSL #1 ;C<-round, Z<-"tied (or exact) case" + BEQ _e2e_exact_or_tied +_e2e_round_by_C + ADDCSS OP1mlo,OP1mlo,#1 ;Increment low word + ADDCSS OP1mhi,OP1mhi,#1 ;If carry out, increment high word + MOVCS OP1mhi,#EIUnits_bit ;If mantissa overflow, adjust + ADCS RNDexp,RNDexp,#0 ; mantissa and exponent + + BMI _e2e_underflow + +;Check for overflow in the usual way + + ADD tmp,RNDexp,#1 + BIC tmp,tmp,#Uncommon_bit + CMP tmp,#1< RNDexp + ADDNE tmp, RNDexp, #1 + CMPNE tmp, #SNaNInf+1 + BGE _e2f_ExpOverflow + + MOVS tmp, OP1mhi, LSR #32-SFrc_len-1 +;If rounding is needed then a one will have dropped out. + BCS _e2f_NeedsRounding + +; Simple case - just merge in the exponent and sign + + BIC tmp, tmp, #1< round, EQ round to even + ADC a1, exp, a1, ASR #8 ; combine, subtract one from exponent + BCC __itos_return + BNE __itos_return + BIC a1, a1, #1 + +__itos_return + TST r2, #FPECause_mask ; Check if any exceptions + ADDEQ sp, sp, #LOC_SIZE ; If no exceptions, restore stack + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; return + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; return + ENDIF + STR r0, [sp, #ExDResl] ; Store default result + ADD r0, sp, #NewResl ; Pointer to return value + MOV r1, r2 ; Exception information + MOV r2, r12 ; Original arg + + CALL FPE_Raise ; Deal with exception information + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #NewResl] ; Load returned value + ADD sp, sp, #LOC_SIZE ; Restore stack + IF Interworking :LOR: Thumbing + LDMFD sp!, {lr} ; Return + BX lr + ELSE + LDMFD sp!, {pc} ; Return + ENDIF + + ENTRY_END __itos + + ] + +;--------------------------------------------------------------------------- + + [ :DEF: ll_sto_f_s :LOR: :DEF: ll_uto_f_s + + MACRO + LongSingleNormalise + + MOV utmp1, #0 + MOVS utmp2, a2, LSR #16 + MOVEQ a2, a2, LSL #16 + ADDEQ utmp1, utmp1, #16 + MOVS utmp2, a2, LSR #24 + MOVEQ a2, a2, LSL #8 + ADDEQ utmp1, utmp1, #8 + MOVS utmp2, a2, LSR #28 + MOVEQ a2, a2, LSL #4 + ADDEQ utmp1, utmp1, #4 + MOVS utmp2, a2, LSR #30 + MOVEQ a2, a2, LSL #2 + ADDEQ utmp1, utmp1, #2 + MOVS utmp2, a2, LSR #31 + MOVEQ a2, a2, LSL #1 + ADDEQ utmp1, utmp1, #1 + MOV a2, a2, LSL #1 + + SUB tmp, tmp, utmp1, LSL #SExp_pos + + MOV utmp2, a1 + + ; take bits from high word into mantissa + ORR fOP, tmp, a2, LSR #SExp_len+1 + + ; get 32 - (number of bits from other word needed in mantissa) + RSB utmp1, utmp1, #64-SFrc_len-1 + CMP utmp1, #32 + + BHI %ft01 + ORR fOP, fOP, utmp2, LSR utmp1 + RSB utmp1, utmp1, #33 + MOVS utmp2, utmp2, LSL utmp1 ; C <- round, Z <- sticky + ReturnToLR CC + B %ft02 + +01 ; no words from high word - get round/carry from top word + ORRS a2, utmp2, a2, LSL #33-(SExp_len+1) ; C <- round, Z <- sticky + ReturnToLR CC +02 ; test to-even case + TSTEQ fOP, #1 + ADDNE fOP, fOP, #1 + ReturnToLR + + MEND + + ] + +;........................................................................... + + [ :DEF: ll_sto_f_s + + Export _ll_sto_f + ImportCodeSize _ll_to_f_shared + ROUT + +_ll_sto_f EnterWithLR_16 + + IMPORT _ffltu + + TEQ a2, #0 + BEQ _ffltu ; a 'normal' long + + ANDS tmp, a2, #Sign_bit ; Extract sign +; sign in in tmp, denormalised mantissa in a1/a2 + [ CodeSize = 2 + BEQ _ll_to_f_shared + | + ORR tmp, tmp, #((SExp_bias + 63) :AND: 0xff00) << SExp_pos + [ CodeSize = 1 + BEQ _ll_to_f_shared + | + + ORR tmp, tmp, #((SExp_bias + 63) :AND: 0x00ff) << SExp_pos + BEQ %ft01 + ] + ] + RSBS a1, a1, #0 + RSCS a2, a2, #0 + [ CodeSize <> 0 + BNE _ll_to_f_shared + | + BEQ %ft02 + + +01 + LongSingleNormalise + +02 + ] + EnterWithStack + BL _ffltu + ORR fOP, fOP, #Sign_bit + ReturnToStack + + ] + +;........................................................................... + + [ :DEF: ll_uto_f_s + + Export _ll_uto_f + ExportCodeSize _ll_to_f_shared + +_ll_uto_f EnterWithLR_16 + + IMPORT _ffltu + + TEQ a2, #0 + BEQ _ffltu + + [ CodeSize = 2 + MOV tmp, #0 + +_ll_to_f_shared + ORR tmp, tmp, #((SExp_bias + 63) :AND: 0xff00) << SExp_pos + | + MOV tmp, #((SExp_bias + 63) :AND: 0xff00) << SExp_pos + ] + [ CodeSize = 1 +_ll_to_f_shared + ] + ORR tmp, tmp, #((SExp_bias + 63) :AND: 0x00ff) << SExp_pos + + LongSingleNormalise + + ] + + +;=========================================================================== + + [ :DEF: ffix_s + + Export _ffix + IMPORT __fp_exception + +_ffix EnterWithLR_16 + MOVS a3, fOP, ASR #SFrc_len + MOV a1, fOP, LSL #SExp_len + ORR a1, a1, #1 << 31 + BMI _ffix_negative + RSBS a3, a3, #31 + SExp_bias + MOVHI a1, a1, LSR a3 + ReturnToLR HI ; 9 clk + BIC a1, a1, #1 << 31 ; clear high bit (positive) + B _ffix_ivo + +_ffix_negative + AND a3, a3, #255 + RSBS a3, a3, #31 + SExp_bias + MOVHI a1, a1, LSR a3 + RSBHI a1, a1, #0 ; negative result + ReturnToLR HI ; 11 clk + TEQEQ a1, #0x80000000 ; MinInt special case + ReturnToLR EQ + ; a1 negative +_ffix_ivo + CMP a3, #(31 + SExp_bias) - SExp_max ; EQ -> inf or NaN + MOV ip, #IVO_bit :OR: FixFn + BNE __fp_exception + MOVS a2, a1, LSL #1 ; NE -> NaN + MOVNE ip, #IVO_bit :OR: FixZeroFn + B __fp_exception + + ] + +;--------------------------------------------------------------------------- + + [ :DEF: ffixu_s + + Export _ffixu + IMPORT __fp_exception + +_ffixu EnterWithLR_16 + MOVS a3, fOP, ASR #SFrc_len + BMI _ffixu_negative + MOV a1, fOP, LSL #SExp_len + ORR a1, a1, #1 << 31 + RSBS a3, a3, #31 + SExp_bias + MOVHS a1, a1, LSR a3 + ReturnToLR HS + BIC a1, a1, #1 << 31 ; clear high bit (positive) + B _ffixu_ivo + +_ffixu_negative + MOVS a1, fOP, LSL #1 ; don't generate IVO for -0 + ReturnToLR EQ + MOV a1, #0x80000000 ; negative +_ffixu_ivo + CMP a3, #(31 + SExp_bias) - SExp_max ; EQ -> inf or NaN + MOV ip, #IVO_bit :OR: FixuFn + BNE __fp_exception + MOVS a2, a1, LSL #1 ; NE -> NaN + MOVNE ip, #IVO_bit :OR: FixZeroFn + B __fp_exception + + ] + +;--------------------------------------------------------------------------- + + [ :DEF: ll_sfrom_f_s + + Export _ll_sfrom_f + + ROUT +_ll_sfrom_f EnterWithLR_16 + + MOVS a3, fOP, LSL #1 ; C <- sign + MOV a3, a3, LSR #SFrc_len+1 ; Get exponent + MOV a2, fOP, LSL #SExp_len ; mantissa + 1 spare bit + ORR a2, a2, #1<<31 ; add units bit + ; mantissa is in a2 + + BCS ll_from_f_neg + + SUBS a3, a3, #SExp_bias + BMI _ll_from_f_zero + RSBS a4, a3, #31 + + MOVGT a1, a2, LSR a4 + MOVGT a2, #0 + ReturnToLR GT + + ADDS a4, a4, #32 + SUB a3, a3, #31 + MOVGT a1, a2, LSL a3 + MOVGT a2, a2, LSR a4 + ReturnToLR GT + ; fall through + + BIC a1, a1, #Sign_bit ; return +ve value + B _ll_from_f_ivo + +_ll_from_f_zero + MOV a1, #0 + MOV a2, #0 + ReturnToLR + +ll_from_f_neg + SUBS a3, a3, #SExp_bias + BMI _ll_from_f_zero + RSBS a4, a3, #31 + + MOVPL a1, a2, LSR a4 + MOVPL a2, #0 + BPL %ft01 + + ADDS a4, a4, #32 + SUB a3, a3, #31 + BLE %ft02 + MOV a1, a2, LSL a3 + MOV a2, a2, LSR a4 + +01 RSBS a1, a1, #0 + RSC a2, a2, #0 + + ReturnToLR + +02 +; Special case - INT_MIN will leave a shift of zero (Z set), and +; r0=0x80000000 r1=0x00000000 + TEQEQ a2, #Sign_bit + MOVEQ a1, #0 + ReturnToLR EQ + + ORR a1, a1, #Sign_bit ; return -ve value +_ll_from_f_ivo + ; On some systems, casting NaN returns zero + ; check for maximum exponent + ADD a3, a4, #:NOT:(63-((SExp_mask:SHR:SExp_pos)-SExp_bias)) + CMN a3, #1 + MOV a4, #IVO_bit :OR: FixFn :OR: LongLong_bit + BNE __fp_nonveneer_error + MOVS tmp, a2, LSL #1 ; ignore units bit + MOVNE a4, #IVO_bit :OR: FixZeroFn :OR: LongLong_bit + B __fp_nonveneer_error + ] + +;--------------------------------------------------------------------------- + + [ :DEF: ll_ufrom_f_s + + + Export _ll_ufrom_f + ExportCodeSize _ll_from_f_shared + ExportCodeSize _ll_from_f_zero + +_ll_ufrom_f EnterWithLR_16 + + MOVS a3, fOP, LSL #1 ; C <- sign + BHI _ll_ufrom_f_neg + MOV a3, a3, LSR #SFrc_len+1 + MOV a2, fOP, LSL #SExp_len + ORR a2, a2, #1<<31 + + SUBS a3, a3, #SExp_bias + BMI _ll_ufrom_f_zero + RSBS a4, a3, #31 + + MOVPL a1, a2, LSR a4 + MOVPL a2, #0 + ReturnToLR PL + + ADDS a4, a4, #32 + SUB a3, a3, #31 + MOVPL a1, a2, LSL a3 + MOVPL a2, a2, LSR a4 + ReturnToLR PL + ; fall through + +_ll_ufrom_f_ivo + BIC a1, a1, #Sign_bit ; return unsigned result + ; On some systems, casting NaN returns zero + ; check for maximum exponent + ADD a3, a4, #:NOT:(63-((SExp_mask:SHR:SExp_pos)-SExp_bias)) + CMN a3, #1 + MOV a4, #IVO_bit :OR: FixuFn :OR: LongLong_bit + BNE __fp_nonveneer_error + MOVS tmp, a2, LSL #1 ; ignore units bit + MOVNE a4, #IVO_bit :OR: FixZeroFn :OR: LongLong_bit + B __fp_nonveneer_error + +_ll_ufrom_f_neg + MOV a4, #IVO_bit :OR: FixZeroFn :OR: LongLong_bit + B __fp_nonveneer_error + +_ll_ufrom_f_zero + MOV a1, #0 + MOV a2, #0 + ReturnToLR + + ] + +;=========================================================================== + + END diff --git a/base/Kernel/Native/arm/Crt/fpadefs.asm b/base/Kernel/Native/arm/Crt/fpadefs.asm new file mode 100644 index 0000000..624ff21 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/fpadefs.asm @@ -0,0 +1,814 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; Assembler source for FPA support code and emulator +; ================================================== +; Definitions for FPA instructions and registers. Also used by "fplib". +; +; Copyright (C) Advanced RISC Machines Limited, 1992-7. All rights reserved. +; +; RCS Revision: 1 +; Checkin Date: 2007/06/29 02:59:16 +; Revising Author + +;=========================================================================== + +; FPSR fields +; ----------- + +IOC_pos EQU 0 +IOC_bit EQU 1:SHL:IOC_pos +DZC_pos EQU 1 +DZC_bit EQU 1:SHL:DZC_pos +OFC_pos EQU 2 +OFC_bit EQU 1:SHL:OFC_pos +UFC_pos EQU 3 +UFC_bit EQU 1:SHL:UFC_pos +IXC_pos EQU 4 +IXC_bit EQU 1:SHL:IXC_pos +ND_pos EQU 8 +ND_bit EQU 1:SHL:ND_pos +NE_pos EQU 9 +NE_bit EQU 1:SHL:NE_pos +SO_pos EQU 10 +SO_bit EQU 1:SHL:SO_pos +EP_pos EQU 11 +EP_bit EQU 1:SHL:EP_pos +AC_pos EQU 12 +AC_bit EQU 1:SHL:AC_pos +IOE_pos EQU 16 +IOE_bit EQU 1:SHL:IOE_pos +DZE_pos EQU 17 +DZE_bit EQU 1:SHL:DZE_pos +OFE_pos EQU 18 +OFE_bit EQU 1:SHL:OFE_pos +UFE_pos EQU 19 +UFE_bit EQU 1:SHL:UFE_pos +IXE_pos EQU 20 +IXE_bit EQU 1:SHL:IXE_pos +SysID_pos EQU 24 +SysID_mask EQU &FF:SHL:SysID_pos + +SysID_oldFPE EQU 0x00 +SysID_FPE EQU 0x01 +SysID_FPPC EQU 0x80 +; SysID_FPA EQU 0x81 ;Already defined in "optcheck" file + +;=========================================================================== + +; FPCR fields - many also valid for CPDO and CPRT instructions +; ------------------------------------------------------------ + +S2_pos EQU 0 +S2_mask EQU &F:SHL:S2_pos +S2_Ibit EQU &8:SHL:S2_pos +Op3_pos EQU 4 +Op3_mask EQU &1:SHL:Op3_pos +RM_pos EQU 5 +RM_mask EQU &3:SHL:RM_pos +Pr2_pos EQU 7 +Pr2_mask EQU &1:SHL:Pr2_pos +DA_pos EQU 8 +DA_bit EQU 1:SHL:DA_pos +RE_pos EQU 9 +RE_bit EQU 1:SHL:RE_pos +AB_pos EQU 10 +AB_bit EQU 1:SHL:AB_pos +SB_pos EQU 11 +SB_bit EQU 1:SHL:SB_pos +Ds_pos EQU 12 +Ds_mask EQU &7:SHL:Ds_pos +Op2_pos EQU 15 +Op2_mask EQU &1:SHL:Op2_pos +S1_pos EQU 16 +S1_mask EQU &7:SHL:S1_pos +Pr1_pos EQU 19 +Pr1_mask EQU &1:SHL:Pr1_pos +Op1_pos EQU 20 +Op1_mask EQU &F:SHL:Op1_pos +EO_pos EQU 26 +EO_bit EQU 1:SHL:EO_pos +MO_pos EQU 27 +MO_bit EQU 1:SHL:MO_pos +IE_pos EQU 28 +IE_bit EQU 1:SHL:IE_pos +RU_pos EQU 31 +RU_bit EQU 1:SHL:RU_pos + +Pr_mask EQU Pr1_mask+Pr2_mask +Op_mask EQU Op1_mask+Op2_mask+Op3_mask + +; Rounding mode values + +RM_Nearest EQU 0:SHL:RM_pos +RM_PlusInf EQU 1:SHL:RM_pos +RM_MinusInf EQU 2:SHL:RM_pos +RM_Zero EQU 3:SHL:RM_pos + +;=========================================================================== + +; Instruction fields - all coprocessor instructions +; ------------------------------------------------- +; +; The following bit distinguishes CPRTs from CPDOs. + +RTnotDO_pos EQU 4 +RTnotDO_bit EQU 1:SHL:RTnotDO_pos + +; The two recognised coprocessor numbers and the position and mask of the +; field in the instruction. + +Coproc_pos EQU 8 +Coproc_mask EQU &F:SHL:Coproc_pos +Coproc_1 EQU 1:SHL:Coproc_pos +Coproc_2 EQU 2:SHL:Coproc_pos + +; The following bit distinguishes CPDTs from CPRT/CPDOs. + +RTDOnotDT_pos EQU 25 +RTDOnotDT_bit EQU 1:SHL:RTDOnotDT_pos + +; For instructions that take the undefined instruction exception vector, the +; following bit distinguishes coprocessor instructions from genuinely +; undefined instructions. + +NotUndef_pos EQU 27 +NotUndef_bit EQU 1:SHL:NotUndef_pos + +Cond_pos EQU 28 +Cond_mask EQU &F:SHL:Cond_pos +Cond_EQ EQU &0:SHL:Cond_pos +Cond_NE EQU &1:SHL:Cond_pos +Cond_CS EQU &2:SHL:Cond_pos +Cond_CC EQU &3:SHL:Cond_pos +Cond_MI EQU &4:SHL:Cond_pos +Cond_PL EQU &5:SHL:Cond_pos +Cond_VS EQU &6:SHL:Cond_pos +Cond_VC EQU &7:SHL:Cond_pos +Cond_HI EQU &8:SHL:Cond_pos +Cond_LS EQU &9:SHL:Cond_pos +Cond_GE EQU &A:SHL:Cond_pos +Cond_LT EQU &B:SHL:Cond_pos +Cond_GT EQU &C:SHL:Cond_pos +Cond_LE EQU &D:SHL:Cond_pos +Cond_AL EQU &E:SHL:Cond_pos +Cond_NV EQU &F:SHL:Cond_pos + +; CPDT-specific instruction fields +; -------------------------------- + +DT_offset_pos EQU 0 +DT_offset_mask EQU &FF:SHL:DT_offset_pos +DT_src_pos EQU 12 +DT_src_mask EQU &7:SHL:DT_src_pos +DT_dst_pos EQU 12 +DT_dst_mask EQU &7:SHL:DT_dst_pos +DT_pr2_pos EQU 15 +DT_pr2_mask EQU &1:SHL:DT_pr2_pos +DT_ARMreg_pos EQU 16 +DT_ARMreg_mask EQU &F:SHL:DT_ARMreg_pos +DT_LnotS_pos EQU 20 +DT_LnotS_bit EQU 1:SHL:DT_LnotS_pos +DT_wb_pos EQU 21 +DT_wb_bit EQU 1:SHL:DT_wb_pos +DT_pr1_pos EQU 22 +DT_pr1_mask EQU &1:SHL:DT_pr1_pos +DT_UnotD_pos EQU 23 +DT_UnotD_bit EQU 1:SHL:DT_UnotD_pos +DT_PreIndex_pos EQU 24 +DT_PreIndex_bit EQU 1:SHL:DT_PreIndex_pos + +; CPRT-specific instruction fields +; -------------------------------- +; +; The following FPCR fields also apply to CPRTs: S2_pos, S2_mask, S2_Ibit, +; Op3_pos, Op3_mask, RM_pos, RM_mask, Pr2_pos, Pr2_mask, S1_pos, S1_mask, +; Pr1_pos, Pr1_mask, Op1_pos, Op1_mask. (Note Op2_pos and Op2_mask are not +; included in this list.) + +RT_ARMreg_pos EQU 12 +RT_ARMreg_mask EQU &F:SHL:RT_ARMreg_pos +RT_SnotL_pos EQU 20 +RT_SnotL_bit EQU 1:SHL:RT_SnotL_pos + +; The following is the full opcode mask for a CPRT, in which the Pr2_mask +; field is part of an ARM register number rather than of the opcode. It +; includes the Pr3_mask field, whose only purpose is to tell us that this +; *is* a CPRT rather than a CPDO + +OpRT_mask EQU Op1_mask+Op3_mask + +; The following bit distinguishes CMF(E)s from CNF(E)s. + +CompNeg_pos EQU 21 +CompNeg_bit EQU 1:SHL:CompNeg_pos + +; The following bit distinguishes exception-causing comparisons from +; non-exception causing comparisons. + +CompExc_pos EQU 22 +CompExc_bit EQU 1:SHL:CompExc_pos + +; CPDO-specific instruction fields +; -------------------------------- +; +; The following FPCR fields also apply to CPDOs: S2_pos, S2_mask, S2_Ibit, +; Op3_pos, Op3_mask, RM_pos, RM_mask, Pr2_pos, Pr2_mask, Ds_pos, Ds_mask, +; Op2_pos, Op2_mask, S1_pos, S1_mask, Pr1_pos, Pr1_mask, Op1_pos, Op1_mask. + +; The bit to indicate that the operation is monadic and not dyadic. + +DO_monad_pos EQU 15 +DO_monad_bit EQU 1:SHL:DO_monad_pos + +; The bit to indicate that this is a subtraction (SUF or RSF) rather than an +; addition (ADF). + +SubNotAdd_pos EQU 21 +SubNotAdd_bit EQU 1:SHL:SubNotAdd_pos + +; The bit to indicate that this is a reverse subtraction (RSF) rather than +; an addition or ordinary subtraction (ADF or SUF). + +RSF_pos EQU 20 +RSF_bit EQU 1:SHL:RSF_pos + +; The bit to indicate that this is a reverse division (RDF or FRD) rather +; than a normal division (DVF or FDV). + +RevDiv_pos EQU 20 +RevDiv_bit EQU 1:SHL:RevDiv_pos + +; The bit to indicate that this is a "fast" multiplication or division (FML, +; FDV or FRD), rather than the normal version (MUF, DVF or RDF). + +Fast_pos EQU 23 +Fast_bit EQU 1:SHL:Fast_pos + +; The bit to indicate that a move-type instruction (MVF, MNF or ABS) is an +; MNF. + +MNF_pos EQU 20 +MNF_bit EQU 1:SHL:MNF_pos + +; The bit to indicate that a move-type instruction (MVF, MNF or ABS) is an +; ABS. + +ABS_pos EQU 21 +ABS_bit EQU 1:SHL:ABS_pos + +; The bit to indicate that this is a COS instruction rather than a SIN. + +COSnotSIN_pos EQU 20 +COSnotSIN_bit EQU 1:SHL:COSnotSIN_pos + +; The bit to indicate that this is a ACS instruction rather than an ASN. + +ACSnotASN_pos EQU 22 +ACSnotASN_bit EQU 1:SHL:ACSnotASN_pos + +; The bit to indicate that this is a LOG instruction rather than an LGN. + +LOGnotLGN_pos EQU 20 +LOGnotLGN_bit EQU 1:SHL:LOGnotLGN_pos + +; The bit to indicate that this is a RPW instruction rather than a POW. + +RPWnotPOW_pos EQU 20 +RPWnotPOW_bit EQU 1:SHL:RPWnotPOW_pos + +;=========================================================================== + +; +; Floating point formats. +; +; There are four floating point formats: Single, Double, Extended and +; Internal. Single, Double and Extended are memory formats and +; Internal is the floating point register format. (The FPE keeps the +; floating pointer registers in Internal format even through they are +; actually in memory somewhere.) The FPE uses precisely the same +; Internal format as the FPA chip in order to allow the FPASC and FPE +; to share code. STF converts from Internal format to Single, Double +; or Extended format. LDF converts from Single, Double or Extended +; format to Internal format. +; +; All floating point values in memory must be aligned on 4 byte +; boundaries. +; +; The Single and Double formats are specified by the ANSI/IEEE +; standard 754-1985. They look like this: +; +; The Single format is 32 bits wide: +; +; | Sign | Exponent | Fraction | +; +; The sign field is 1 bit wide the exponent field is 8 bits wide and +; the fraction field is 23 bits wide. The exponent field is biased by +; 127 (i.e. an exponent of 1 is encoded as 0x80). The fraction has an +; implicit leading 1 unless the number is denormalised (see table +; below). +; +; The following table summarises the Single format: +; Where: 's' is -1 if the sign bit is set and 1 if the sign bit is clear. +; 'e' is the value of the exponent field interpreted as unsigned. +; 'f' is the fraction field interpreted as binary digits +; +; Exponent Fraction What it is Value +; 0 0 signed zero +0 or -0 +; 0 non-zero denorm. number s * (0.f) * (2**(-126)) +; 255 0 infinity s * infinity +; 255 non-zero NaN +; otherwise normal number s * (1.f) * (2**(e-127)) +; +; The Double format is 64 bits wide: +; +; address: | Sign | Exponent | Fraction high | +; address + 4: | Fraction low | +; +; The sign field is 1 bit wide the exponent field is 11 bits wide and +; the fraction field is 52 bits wide (20 bits in Fraction high and 32 +; in Fraction low). The exponent is biased by 1023. The fraction has +; an implicit leading 1 unless the number is denormalised (see table +; below). +; +; The following table summarises the Double format: +; +; Exponent Fraction What it is Value +; 0 0 signed zero +0 or -0 +; 0 non-zero denorm. number s * (0.f) * (2**(-1022)) +; 2047 0 infinity s * infinity +; 2047 non-zero NaN +; otherwise normal number s * (1.f) * (2**(e-1023)) +; +; Extended format implements the IEEE Double Extended format. People +; wanting an IEEE Single Extended format should either use Double or +; Extended. Since the reasons the standard suggests for wanting to +; have Single Extended as well as Double Extended are upwards +; compatibility and speed, and the former doesn't really apply in our +; world, and some Double precision operations are faster than Extended +; ones (e.g. multiplication), I would actually recommend someone who +; wanted an IEEE Single Extended format to use Double, not Extended. +; +; The Extended format is 96 bits wide: +; +; address: | Sign | Reserved | Exponent | +; address + 4: | Units | Fraction high | +; address + 8: | Fraction low | +; +; The sign field is 1 bit wide, the reserved field is 16 bits wide, +; the exponent field is 15 bits wide, the units field is 1 bit wide +; and the fraction is 63 bits wide (31 bits in Fraction high and 32 in +; Fraction low). The exponent is biased by 16383 = 0x3FFF. The Units +; bit is used instead of having the fraction have an implicit leading +; 1. The Reserved bits are ignored when read and always written as 0. +; +; The floating point registers are kept in Internal format. This is +; the same as Extended format except that the first word contains a +; new field: +; +; The Internal format is 96 bits wide: +; +; address: | Sign | Uncommon | Reserved | Exponent | +; address + 4: | Units | Fraction high | +; address + 8: | Fraction low | +; +; The sign field is 1 bit wide, the uncommon field is 1 bit wide, the +; reserved field is 15 bits wide, the exponent field is 15 bits wide, +; the units field is 1 bit wide and the fraction is 63 bits wide (31 +; bits in Fraction high and 32 in Fraction low). The exponent is +; biased by 16383 = 0x3FFF. The Units bit is used instead of having +; the fraction have an implicit leading 1. The Reserved bits are +; ignored when read and always written as 0. +; +; SFM stores in Internal format. When LFM is loading if the uncommon +; bit is set the memory is loaded unchanged. If the uncommon bit is +; clear LFM will load it as an Extended number possibly setting the +; uncommon bit in the process.Another way of putting this is that LFM +; ORs the uncommon bit from memory with an uncommon bit calculated +; from the other fields of the number in memory. +; +; Extended numbers are interpreted as Internal numbers with the +; uncommon bit clear (which it is). +; +; The following table summarises the Exteded and Internal formats: +; +; Exponent Unit Fraction What it is Value +; 0 0 0 signed zero +0 or -0 +; 0 0 non-zero denorm. number s * (0.f) * (2**(-16383)) +; 32767 0 0 infinity s * infinity +; 32767 1 0 *** Not a valid bit pattern (see note 1) *** +; 32767 x non-zero NaN NaN +; other 0 anything *** Not a valid bit pattern (see note 2) *** +; other 1 anything normal number s * (1.f) * (2**(e-16383)) +; +; +; Notes: 1. This bit pattern is known as an "anomalous infinity" in these +; sources and is actually treated as s * infinity. +; 2. These bit patterns are known as "unnormalised numbers" in these +; sources, and are actually treated as representing the value +; s * (0.f) * (2**(e-16383)). While this behaviour is not part of +; the architectural specification and should not be relied upon by +; programmers, it is important to the correct treatment of the +; intermediate result in the URD/NRM instruction pair in this +; implementation, and so should not be changed. +; +; Uncommon Units Exponent Fraction Notes What it is +; bit bit +; 0 0 0x0000 0 (a) A zero +; 0 0 0x0000 non-zero Should not happen +; 0 0 0x4016 x (b) Result of URD +; 0 0 0x4033 x (b) Result of URD +; 0 0 0x403E x (b) Result of URD +; 0 0 other x Should not happen +; 0 1 0x7FFF x Should not happen +; 0 1 other x (a) Normalised number +; 1 0 0x0000 0 Should not happen +; 1 0 0x0000 non-zero (a) Extended denorm. number +; 1 0 0x7FFF 0 (a) Extended infinity +; 1 0 0x7FFF non-zero (a,d-f) Extended NaN +; 1 0 other 0 (c) Extended unnormalised zero +; 1 0 other non-zero (c) Extended unnorm. number +; 1 1 0x3C00 0 Should not happen +; 1 1 0x3C00 non-zero (a) Double denormalised number +; 1 1 0x3F80 0 Should not happen +; 1 1 0x3F80 non-zero (a) Single denormalised number +; 1 1 0x407F 0 (a) Single infinity +; 1 1 0x407F non-zero (a,e) Single NaN +; 1 1 0x43FF 0 (a) Double infinity +; 1 1 0x43FF non-zero (a,e) Double NaN +; 1 1 0x7FFF 0 (c) Anomalous ext. infinity +; 1 1 0x7FFF non-zero (a,d-f) Extended NaN +; 1 1 other x Should not happen +; +; Notes: +; +; (a) These bit patterns occur normally. +; The original format is kept for NaNs because exceptions are +; generated for signalling NaNs only when converting to a +; different format. The original format is kept for denormalised +; numbers and infinites just because it is convenient. +; +; (b) These bit patterns are the intermediate result of URD before NRM. +; They are treated as unnormalized numbers. URD followed by NRM +; implements RND, or the IEEE "Round a floating point number to +; another floating point number whose value is an integer" +; operation (see section 5.5 "Round Floating-Point Number to +; Integer Value" on page 11 of the standard). If one of these +; numbers is saved (SFM) and restored (LFM) the uncommon bit may +; become set. ("may" rather than "will" because the URD +; instruction generally doesn't change numbers which are +; sufficiently large to already have an integer value). The same +; applies to STFE followed by LDFE (for the benefit of programs +; which use that pair for register preservation & +; restoration). This setting of the uncommon bit is OK because the +; URD results are chosen so that setting the uncommon bit +; transforms them into extended unnormalised numbers which have +; the correct values for the results. +; +; (c) Bit patterns that technically have no meaning. +; These can only be generated by abusing LDFE and LFM. See Notes 1 +; & 2 above. +; +; (d) These are indeed both "Extended NaN": the units bit is effectively a +; "don't care" bit for extended NaNs. This is basically just a +; consequence of extended precision having a units bit, while single and +; double precision don't: the "anomalous extended infinity" is a similar +; consequence. +; +; (e) The general rules about NaN processing are: +; +; * If the top fraction bit is a 1, the NaN is a quiet NaN; if a 0, the +; NaN is a signalling NaN. (N.B. This is the top fraction bit - i.e. +; the second bit of the mantissa - rather than the units bit because +; the units bit does not exist in all formats.) +; +; * A format conversion on a NaN will truncate the fraction at the +; appropriate point for a format narrowing, or extend it with zeros +; for a format widening. If the original NaN was a signalling NaN, an +; invalid operation exception is generated. If this exception is +; untrapped, the result is also "quietened" by having its top fraction +; bit set. (The net result of this is that the result always ends up +; having its top fraction bit set and thus being a NaN rather than an +; infinity, unless the result is generated by a trap handler - in this +; latter case, the trap handler has the responsibility for producing +; the "correct" result.) +; +; * An operation on NaN operand(s) will generate an invalid operation +; exception if any operand is a signalling NaN, as mandated by the +; IEEE standard. If this doesn't happen, or if the exception is +; untrapped, the result is produced by the following rules: +; +; - If the operation is monadic, the result is obtained by +; format-converting the operand NaN to the destination format of the +; operation. (For some versions of the code, the invalid operation +; exception is suppressed if the operation is MVF, MNF, ABS or STF +; and no change of format occurs; for others, this doesn't happen. +; See the discussion under "FPESigNaNCopy_Invalid" in the "main.s" +; source file for more details of this.) +; +; - If the operation is dyadic, the result is obtained by +; format-converting the first operand to the destination format of +; the operation if the first operand is a NaN, and otherwise by +; format-converting the second operand similarly. (Note that this +; rule pays no attention to which NaNs are signalling. In +; particular, if the first operand is a quiet NaN and the second one +; a signalling NaN, the second operand will generate the invalid +; operation exception - but if the exception is untrapped, the first +; operand will generate the result. This is done to match likely +; future hardware behaviour.) +; +; * If an invalid operation exception occurs for other reasons than a +; NaN operand, and the exception is untrapped, the resulting quiet NaN +; has units bit 0, top fraction bit 1, all other fraction bits 0, and +; sign bit equal to what the operation would normally have produced - +; e.g. EOR of the operand signs for infinity * 0; ((rounding mode = +; "to -infinity") ? 1 : 0) for infinity - infinity; etc. (This is +; again to match likely future hardware behaviour.) +; +; (f) The NE bit in the FPSR modifies the rules about extended NaNs when it +; is 0. Basically, these rules cause extended precision to no longer be +; a fully supported IEEE format, but are necessary to allow code that +; uses STFE/LDFE for register preservation and restoration to use +; signalling NaNs effectively. (I.e. basically code that uses the +; "/fpe2" APCS option and some other legacy code.) +; +; The problem for such code is that if extended precision is treated as +; a fully supported IEEE precision, a register-preserving STFE normally +; contains an implicit format conversion and so *must* generate an +; exception. Having a procedure call generate an exception merely +; because the calling procedure happens to have a signalling NaN in a +; register isn't very desirable... +; +; To deal with this, NE=0 provides a "legacy code compatibility" option +; in which an extended precision NaN is regarded as being "really +; single" or "really double", depending on whether the bottom bit of the +; extended precision mantissa is 0 or 1 (respectively). Format +; conversions from single or double precision to extended precision then +; clear or set this bit appropriately in addition to the operations +; described above, and never generate an invalid operation exception. +; Format conversions from a "really single" extended signalling NaN to +; single precision or from a "really double" extended signalling NaN to +; double precision are regarded as not involving a change of precision +; and thus not generating an invalid operation exception; ones from a +; "really single" extended NaN to double precision or a "really double" +; extended NaN to single precision still operate as normal. +; +; NE=1 provides full IEEE compatibility, in which any conversion of a +; signalling NaN from one of single, double or extended precision to +; another will generate an invalid operation exception. +; +; Note also that all this only applies to current systems like the +; FPA+FPASC which don't generate invalid operation exceptions for copies +; of signalling NaNs without change of format. Possible future hardware +; systems which generate invalid operation exceptions on any copy of a +; signalling NaN by an MVF, MNF, ABS or STF instruction will pay no +; attention to the NE bit and only supply the full IEEE compatibility +; option. + +;=========================================================================== + +; Fields in top words of numbers +; ------------------------------ + +; Single precision fields: + +SFrc_pos EQU 0 +SFrc_len EQU 23 +SFrc_mask EQU ((1:SHL:SFrc_len) - 1):SHL:SFrc_len +SExp_pos EQU 23 +SExp_len EQU 8 +SExp_mask EQU ((1:SHL:SExp_len) - 1):SHL:SExp_pos + +; Double precision fields: + +DFhi_pos EQU 0 +DFhi_len EQU 20 +DFhi_mask EQU ((1:SHL:DFhi_len) - 1):SHL:DFhi_len +DExp_pos EQU 20 +DExp_len EQU 11 +DExp_mask EQU ((1:SHL:DExp_len) - 1):SHL:DExp_pos + +; Extended and internal precision fields: + +EIExp_pos EQU 0 +EIExp_len EQU 15 +EIExp_mask EQU ((1:SHL:EIExp_len) - 1):SHL:EIExp_pos +IRsv_pos EQU 15 +IRsv_mask EQU &7FFF:SHL:IRsv_pos +ERsv_pos EQU 15 +ERsv_mask EQU &FFFF:SHL:ERsv_pos +Uncommon_pos EQU 30 +Uncommon_bit EQU 1:SHL:Uncommon_pos + +EIExp_mask_pt1 EQU EIExp_mask:AND:&FF +EIExp_mask_pt2 EQU EIExp_mask:AND:&FF00 + ASSERT EIExp_mask_pt1:OR:EIExp_mask_pt2 = EIExp_mask + ASSERT EIExp_mask_pt1:AND:EIExp_mask_pt2 = 0 + +IRsv_mask_pt1 EQU IRsv_mask:AND:&3FC000 +IRsv_mask_pt2 EQU IRsv_mask:AND:&3FC00000 + ASSERT IRsv_mask_pt1:OR:IRsv_mask_pt2 = IRsv_mask + ASSERT IRsv_mask_pt1:AND:IRsv_mask_pt2 = 0 + +; Shared fields: + +Sign_pos EQU 31 +Sign_bit EQU 1:SHL:Sign_pos + +; The following immediate mask will reduce a sign/uncommon/exponent word to +; just the exponent. + +ToExp_mask EQU &FFFFFFFF - Sign_bit - Uncommon_bit + ASSERT EIExp_pos = 0 ;This mask won't do its job otherwise + +; The minimum and maximum allowed exponents for single, double and extended +; precision numbers held in internal format. + +SMin_Exp EQU &3F81 +SMax_Exp EQU &407E +DMin_Exp EQU &3C01 +DMax_Exp EQU &43FE +EMin_Exp EQU &0000 +EMax_Exp EQU &7FFE + +; The normal exponent bias in a single, a double and an extended or internal +; precision number. In the first two cases, the bias for a denormalised +; number is one smaller. + +SExp_bias EQU &7F +DExp_bias EQU &3FF +EIExp_bias EQU &3FFF + +; The special exponents for NaNs and infinities of various precisions. + +NaNInfExp_Single EQU &407F +NaNInfExp_Double EQU &43FF +NaNInfExp_Extended EQU &7FFF + +; The special exponents for denormalised numbers of various precisions. + +DenormExp_Single EQU &3F80 +DenormExp_Double EQU &3C00 +DenormExp_Extended EQU &0000 + +; The bias adjustments for entry to overflow and underflow trap handlers. + +TrapBiasAdjust_Single EQU &C0 +TrapBiasAdjust_Double EQU &600 +TrapBiasAdjust_Extended EQU &6000 + +;=========================================================================== + +; Fields in other words of numbers +; -------------------------------- + +EIUnits_pos EQU 31 +EIUnits_bit EQU 1:SHL:EIUnits_pos +EIFracTop_pos EQU 30 +EIFracTop_bit EQU 1:SHL:EIFracTop_pos + +;=========================================================================== + +; Comparison results +; ------------------ + +Comp_GT EQU &20000000 +Comp_EQ EQU &60000000 +Comp_LT EQU &80000000 +Comp_Un_Orig EQU &10000000 +Comp_Un_Alt EQU &30000000 + +;=========================================================================== + +; Floating point condition codes +; +; Here are the FPE/ARM condition code maps. The FPE has an FPSR bit +; called AC that determines if the old mapping or new is used: +; +; With AC=0: +; +; Relation Z C N V +; --------------------------- +; EQUAL 1 1 0 0 +; LESS THAN 0 0 1 0 +; GREATER TNAN 0 1 0 0 +; UNORDERED 0 0 0 1 +; +; Code Nm Flags Meaning Floating point meaning +; ------------------------------------------------------------------ +; 0000 EQ Z==1 equal equal +; 0001 NE Z==0 not equal not equal (includes unordered) +; 0010 HS C==1 higher or same greater or equal +; 0011 LO C==0 lower than less than or unordered +; 0100 MI N==1 negative less than +; 0101 PL N==0 nonnegative greater, equal, or unordered +; 0110 VS V==1 overflow unordered +; 0111 VC V==0 not overflow less, greater, or equal +; 1000 HI C==1 && Z==0 higher than greater than +; 1001 LS C==0 || Z==1 lower or same less, equal, or unordered +; 1010 GE N==V greater or equal greater or equal (same as 0010) +; 1011 LT N!=V less than less than or unordered (same as 0011) +; 1100 GT Z==0 && N==V greater than greater than (same as 1000) +; 1101 LE Z==1 || N!=V less or equal less, equal, or unordered (same as 1001) +; 1110 AL don't care always always +; 1111 don't care never never +; +; With AC=1: +; +; Relation Z C N V +; --- ------------------------ +; EQUAL 1 1 0 0 +; LESS THAN 0 0 1 0 +; GREATER TNAN 0 1 0 0 +; UNORDERED 0 1 0 1 +; +; The only difference is the UNORDERED now sets both C and V. +; Now: C <==> greater than or equal or unordered +; +; This changes the condition code table to: +; +; Code Nm Flags Meaning Floating point meaning +; --- --------------------------------------------------------------- +; 0000 EQ Z==1 equal equal +; 0001 NE Z==0 not equal not equal (includes unordered) +; 0010 HS C==1 higher or same greater, equal, or unordered +; 0011 LO C==0 lower than less than +; 0100 MI N==1 negative less than +; 0101 PL N==0 nonnegative greater, equal, or unordered +; 0110 VS V==1 overflow unordered +; 0111 VC V==0 not overflow less, greater, or equal +; 1000 HI C==1 && Z==0 higher than greater or unordered +; 1001 LS C==0 || Z==1 lower or same less or equal +; 1010 GE N==V greater or equal greater or equal +; 1011 LT N!=V less than less than or unordered +; 1100 GT Z==0 && N==V greater than greater than +; 1101 LE Z==1 || N!=V less or equal less, equal, or unordered +; 1110 AL don't care always always +; 1111 don't care never never +; +; Only the entries for 0010, 0011, 1000 and 1001 have changed. +; +; Now all floating point conditionals that can be expressed in C are +; available in one test. +; +; The ARM C compiler does not yet do the right thing for floating +; point comparisons. + +;=========================================================================== + +; Invalid operation reason codes +; ------------------------------ +; These were once used to construct quiet NaNs which would give some +; indication what sort of operation created the NaN. They are now only used +; for some internal purposes, having been removed because it is very +; unlikely that any future hardware which handles NaNs will mimic this +; behaviour. +; InvReas_InitNaN is not an invalid operation reason code; however, like +; all the others, it is related to a NaN that the system can generate. It is +; therefore placed in the same set of numbers. +; InvReas_MsvOverflow and InvReas_MsvUnderflow are similar: NaNs +; (originally constructed from them) are used on entry to overflow and +; underflow trap handlers respectively to indicate that the result is so +; massively out of range that the IEEE-specified exponent bias adjustment +; (&C0 for single, &600 for double and &6000 for extended) is not enough to +; bring it in range. + +InvReas_SigNaN EQU 0 ;An operand is a signalling NaN +InvReas_InitNaN EQU 1 ;(Used to generate initial NaNs) +InvReas_MsvOverflow EQU 2 ;(Used for massive overflow) +InvReas_MsvUnderflow EQU 3 ;(Used for massive underflow) +InvReas_MagSubInf EQU 4 ;Magnitude subtraction of infinities +InvReas_InfTimes0 EQU 5 ;Infinity times zero +InvReas_0TimesInf EQU 6 ;Zero times infinity +InvReas_0Div0 EQU 7 ;Zero divided by zero +InvReas_InfDivInf EQU 8 ;Infinity divided by infinity +InvReas_InfRemX EQU 9 ;RMF with 1st operand infinite +InvReas_XRem0 EQU 10 ;RMF of non-infinite number by zero +InvReas_SqrtNeg EQU 11 ;Square root of a negative number +InvReas_FixQNaN EQU 12 ;FIX applied to a quiet NaN +InvReas_FixInf EQU 13 ;FIX applied to an infinity +InvReas_FixRange EQU 14 ;FIX on an out-of-range number +InvReas_CompQNaN EQU 15 ;CMFE/CNFE on a quiet NaN +InvReas_SinCosRange EQU 16 ;SIN/COS on too big an argument +InvReas_SinCosInf EQU 17 ;SIN/COS on an infinity +InvReas_TanRange EQU 18 ;TAN on too big an argument +InvReas_TanInf EQU 19 ;TAN on an infinity +InvReas_AsnAcsRange EQU 20 ;ASN/ACS on too big an argument +InvReas_AsnAcsInf EQU 21 ;ASN/ACS on an infinity +InvReas_PolZeroZero EQU 22 ;POL on two zeros +InvReas_PolInfInf EQU 23 ;POL on two infinities +InvReas_LgnLogNeg EQU 24 ;LGN/LOG of a negative argument +InvReas_NegPowX EQU 25 ;POW/RPW to do (negative)^X +InvReas_0PowNonpos EQU 26 ;POW/RPW to do (zero)^(non-positive) +InvReas_BadInfPow EQU 27 ;POW/RPW to do 1^(+/-infinity) or + ; (+infinity)^0 + +;=========================================================================== + + END diff --git a/base/Kernel/Native/arm/Crt/fpdata.asm b/base/Kernel/Native/arm/Crt/fpdata.asm new file mode 100644 index 0000000..93fd6cb --- /dev/null +++ b/base/Kernel/Native/arm/Crt/fpdata.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL fpdata_s + + GET except.asm + + END diff --git a/base/Kernel/Native/arm/Crt/fpe.asm b/base/Kernel/Native/arm/Crt/fpe.asm new file mode 100644 index 0000000..57c9b2a --- /dev/null +++ b/base/Kernel/Native/arm/Crt/fpe.asm @@ -0,0 +1,533 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; fpe.s +; +; Copyright (C) Advanced RISC Machines Limited, 1994. All rights reserved. +; +; RCS Revision: 1 +; Checkin Date: 2007/06/29 02:59:16 +; Revising Author + + GET defaults.asm + GET callcode.asm + GBLL FPEWanted + GBLL FPASCWanted + GBLL EnableInterrupts + GBLA CoreDebugging + +FPEWanted SETL {FALSE} +FPASCWanted SETL {FALSE} +EnableInterrupts SETL {FALSE} +CoreDebugging SETA 0 + + +;============================================================================== + +; +; Allow some control over the code/speed of code produced. +; 0 = fastest -> 2 = smallest (overall) +; + + GBLA CodeSize +CodeSize SETA 0 + + MACRO + ImportCodeSize $name + [ CodeSize <> 0 + IMPORT $name + ] + MEND + + MACRO + ExportCodeSize $name + [ CodeSize <> 0 + EXPORT $name + ] + MEND + +;============================================================================== + + MACRO +$label CDebug4 $level,$message,$reg1,$reg2,$reg3,$reg4 + MEND + MACRO +$label CDebug3 $level,$message,$reg1,$reg2,$reg3 + MEND + MACRO +$label CDebug2 $level,$message,$reg1,$reg2 + MEND + MACRO +$label CDebug1 $level,$message,$reg1 + MEND + MACRO +$label CDebug0 $level,$message + MEND + +; File for setting up THUMB macros for entry and exit from the THUMB +; versions of the functions. + + GBLA V_N + + [ :DEF: thumb + + MACRO +$label VEnter_16 ; Like VEnter, but declare __16$label as the + CODE16 ; THUMB entry point +__16$label + BX pc + CODE32 +V_N SETA V_N+1 + LCLS f_lab +f_lab SETS "|":CC:"$F":CC::STR:V_N:CC:"|" + KEEP $f_lab +$f_lab +$label STMFD r13!,veneer_s ; ARM is the declared entry point + MEND + + MACRO +$label VEnter ; Declare the THUMB entry point as the main + CODE16 ; entry point +$label BX pc + CODE32 +V_N SETA V_N+1 + LCLS f_lab +f_lab SETS "|":CC:"$F":CC::STR:V_N:CC:"|" + KEEP $f_lab +$f_lab +__32$label ; Declare a __32$label entry point for ARM + STMFD r13!,veneer_s + MEND + + MACRO +$label VReturn $cc +$label LDM$cc.FD r13!,veneer_s + BX$cc lr + MEND + + MACRO +$label VPull $cc +$label LDM$cc.FD r13!,veneer_s + MEND + + MACRO +$label ReturnToLR $cc +$label BX$cc lr + MEND + + MACRO +$label ReturnToLR_flg $cc +$label BX$cc lr + MEND + + MACRO +$label ReturnToStack $cc +$label LDM$cc.FD sp!,{lr} + BX$cc lr + MEND + + MACRO +$label PullFromStack $cc +$label LDM$cc.FD sp!,{lr} + MEND + + MACRO +$label EnterWithLR_16 + CODE16 +__16$label + BX pc + CODE32 +V_N SETA V_N+1 + LCLS f_lab +f_lab SETS "|":CC:"$F":CC::STR:V_N:CC:"|" + KEEP $f_lab +$f_lab +$label + MEND + + MACRO +$label EnterWithStack_16 + CODE16 +__16$label + BX pc + CODE32 +V_N SETA V_N+1 + LCLS f_lab +f_lab SETS "|":CC:"$F":CC::STR:V_N:CC:"|" + KEEP $f_lab +$f_lab +$label STMFD sp!,{lr} + MEND + + MACRO +$label EnterWithLR + CODE16 +$label BX pc + CODE32 +V_N SETA V_N+1 + LCLS f_lab +f_lab SETS "|":CC:"$F":CC::STR:V_N:CC:"|" + KEEP $f_lab +$f_lab +__32$label + MEND + + MACRO +$label EnterWithStack + CODE16 +$label BX pc + CODE32 +V_N SETA V_N+1 + LCLS f_lab +f_lab SETS "|":CC:"$F":CC::STR:V_N:CC:"|" + KEEP $f_lab +$f_lab +__32$label + STMFD sp!,{lr} + MEND + + MACRO + Export $name + EXPORT $name + EXPORT __16$name + MEND + + MACRO + Export_32 $name + EXPORT __32$name + EXPORT $name + MEND + + MACRO + Import_32 $name + IMPORT __32$name + MEND + + MACRO +$label B_32 $name +$label B __32$name + MEND + + | + +;ARM 32 and 26-bit mode + + MACRO +$label VEnter_16 +$label STMFD r13!,veneer_s + MEND + + MACRO +$label VEnter +$label STMFD r13!,veneer_s + MEND + + MACRO +$label VReturn $cc +$label [ Interworking :LOR: Thumbing + LDM$cc.FD r13!,veneer_s + BX$cc lr + | + [ {CONFIG} = 32 + LDM$cc.FD r13!,veneer_l + ] + [ {CONFIG} = 26 + LDM$cc.FD r13!,veneer_l^ + ] + ] + MEND + + MACRO +$label VPull $cc +$label LDM$cc.FD r13!,veneer_s + MEND + + MACRO +$label ReturnToLR $cc +$label [ Interworking :LOR: Thumbing + BX$cc lr + | + [ {CONFIG} = 32 + MOV$cc pc,lr + ] + [ {CONFIG} = 26 + MOV$cc.S pc,lr + ] + ] + MEND + + MACRO +$label ReturnToLR_flg $cc +$label [ Interworking :LOR: Thumbing + BX$cc lr + | + MOV$cc pc,lr + ] + MEND + + MACRO +$label ReturnToStack $cc +$label [ Interworking :LOR: Thumbing + LDM$cc.FD sp!,{lr} + BX$cc lr + | + [ {CONFIG} = 32 + LDM$cc.FD sp!,{pc} + ] + [ {CONFIG} = 26 + LDM$cc.FD sp!,{pc}^ + ] + ] + MEND + + MACRO +$label PullFromStack $cc +$label LDM$cc.FD sp!,{lr} + MEND + + MACRO +$label EnterWithLR_16 +$label + MEND + + MACRO +$label EnterWithStack_16 +$label STMFD sp!,{lr} + MEND + + MACRO +$label EnterWithLR +$label + MEND + + MACRO +$label EnterWithStack +$label STMFD sp!,{lr} + MEND + + MACRO + Export $name + EXPORT $name + MEND + + MACRO + Export_32 $name + EXPORT $name + MEND + + MACRO + Import_32 $name + IMPORT $name + MEND + + MACRO +$label B_32 $name +$label B $name + MEND + + ] + + MACRO + CodeArea $name +;; $name will be a name for the area. However for a release we'll use +;; C$$code instead + [ Interworking + AREA |.text|, CODE, READONLY + | + AREA |.text|, CODE, READONLY + ] + MEND + + GET regnames.asm + GET armdefs.asm + GET fpadefs.asm + GET macros.asm + +sp RN R13 +lr RN R14 +pc RN R15 + +dOP1h RN R1 ;Double OP1 hi-reg ("First word") - sign,expn,etc. +dOP1l RN R0 ;Double OP1 lo-reg ("Second word") +dOPh RN R1 ;Double OP hi-reg (unary ops) +dOPl RN R0 ;Double OP lo-reg +dOP2h RN R3 ;Double OP2 hi-reg ("First word") +dOP2l RN R2 ;Double OP2 lo-reg ("Second word") + +fOP1 RN R0 ;Float OP1 +fOP RN R0 ;Float OP for unary ops +fOP2 RN R1 ;Float OP2 + +utmp1 RN R2 ;Temporary register fo unary operations +utmp2 RN R3 ; " + +ip RN R12 +tmp RN R12 ;A temporary register + +SignBit EQU &80000000 +fSignalBit EQU &00400000 +dSignalBit EQU &00080000 +w_oSignBit EQU &7fffffff +w_ofSignalBit EQU &ffbfffff +w_odSignalBit EQU &fff7ffff +Internal_mask EQU &00000000 +Single_pos EQU 0 +Double_pos EQU 1 +Single_mask EQU 1:SHL:Single_pos +Double_mask EQU 1:SHL:Double_pos +Reverse EQU 0x4 ; Used to signal a reverse divide + +;;Error flags - an extension to the normal internal format + +Error_pos EQU 29 +Error_bit EQU 1:SHL:Error_pos + +Except_len EQU 5 +Except_pos EQU Error_pos-Except_len + + ASSERT IOC_pos < DZC_pos + ASSERT DZC_pos < OFC_pos + ASSERT OFC_pos < UFC_pos + ASSERT UFC_pos < IXC_pos +FPExceptC_pos EQU IOC_pos + ASSERT IOE_pos < DZE_pos + ASSERT DZE_pos < OFE_pos + ASSERT OFE_pos < UFE_pos + ASSERT UFE_pos < IXE_pos +FPExceptE_pos EQU IOE_pos + + +; Following fields are used to identify the originator function and the return type so that the +; error handler can return the correct value. + +;; +;; All fields and values must agree with C headers and programs. +;; +;; r12 (defined as ip above) is used to pass information about the operation being performed +;; to the exception handler. The bit fields are defined as follows. +;; +;; 31 0 +;; +--------------------------------+ +;; | | +;; +--------------------------------+ +;; +;; Bits Meaning +;; ~~~~ ~~~~~~~ +;; 31 unused +;; 30 Uncommon (possible exceptional case -- requires more checking) +;; 29 Error (exceptional case) +;; 28:25 unused +;; 24:20 Operation code (must agree with enumerated type in fpraise.c) +;; 19:5 unused +;; 4 Exception cause: Inexact (must agree with float.h) +;; 3 Exception cause: Underflow +;; 2 Exception cause: Overflow +;; 1 Exception cause: Zero Divide +;; 0 Exception cause: Invalid Operation +;; + + +;; +;; Operation code defines. These must match with the FP_CODE enumerated +;; type in fpraise.c. +;; +Fn_pos EQU 20 ;; Function (operation) code position +Fn_mask EQU 31 << Fn_pos ;; Mask for the field + +_FpAddD EQU ( 0 << Fn_pos) ;; Add Double +_FpAddS EQU ( 1 << Fn_pos) ;; Add Single +_FpSubD EQU ( 2 << Fn_pos) ;; Subtract Double +_FpSubS EQU ( 3 << Fn_pos) ;; Subtract Single +_FpCmpD EQU ( 4 << Fn_pos) ;; Compare Double +_FpCmpS EQU ( 5 << Fn_pos) ;; Compare Single +_FpDivD EQU ( 6 << Fn_pos) ;; Divide Double +_FpDivS EQU ( 7 << Fn_pos) ;; Divide Single +_FpDToI EQU ( 8 << Fn_pos) ;; Convert Double To int +_FpDToI64 EQU ( 9 << Fn_pos) ;; Convert Double To __int64 +_FpDToS EQU (10 << Fn_pos) ;; Convert Double To Single +_FpDToU EQU (11 << Fn_pos) ;; Convert Double To unsigned int +_FpDToU64 EQU (12 << Fn_pos) ;; Convert Double To unsigned __int64 +_FpIToS EQU (13 << Fn_pos) ;; Convert int To Single +_FpMulD EQU (14 << Fn_pos) ;; Multiply Double +_FpMulS EQU (15 << Fn_pos) ;; Multiply Single +_FpSToD EQU (16 << Fn_pos) ;; Convert Single To Double +_FpSToI EQU (17 << Fn_pos) ;; Convert Single To int +_FpSToI64 EQU (18 << Fn_pos) ;; Convert Single To __int64 +_FpSToU EQU (19 << Fn_pos) ;; Convert Single To unsigned int +_FpSToU64 EQU (20 << Fn_pos) ;; Convert Single To unsigned __int64 +_FpUToS EQU (21 << Fn_pos) ;; Convert unsigned int To Single +_FpI64ToD EQU (22 << Fn_pos) ;; Convert __int64 To Double +_FpI64ToS EQU (23 << Fn_pos) ;; Convert __int64 To Single +_FpU64ToD EQU (24 << Fn_pos) ;; Convert unsigned __int64 To Double +_FpU64ToS EQU (25 << Fn_pos) ;; Convert unsigned __int64 To Single +_FpRndD EQU (26 << Fn_pos) ;; Round to double integer + + +;; FP Exception Causes (must agree with fpraise.c FPExInfo) +;; Note that this is modelled after the ARM 7500FE FPSR Cumulative exception +;; flags byte. The semantics here are slightly different. If a bit is set +;; here, it means that the corresponding exception occurred during the current +;; FP operation. If a bit is clear, it means that the corresponding exception +;; did not occur during the current FP operation. The bits are set regardless +;; of which exceptions are enabled, etc. Raising exceptions, checking enabled +;; exceptions, and updating status and cause bits is done in fpraise.c which +;; maintains the "true" FPSCR. +INX_bit EQU (1 << 4) ;; Inexact +UNF_bit EQU (1 << 3) ;; Underflow +OVF_bit EQU (1 << 2) ;; Overflow +DVZ_bit EQU (1 << 1) ;; Zero Divide +IVO_bit EQU (1 << 0) ;; Invalid Operation +FPECause_mask EQU (INX_bit :OR: \ + UNF_bit :OR: \ + OVF_bit :OR: \ + DVZ_bit :OR: \ + IVO_bit ) + + +INX_bits EQU (INX_bit) +UNF_bits EQU (UNF_bit :OR: INX_bit) +OVF_bits EQU (OVF_bit :OR: INX_bit) +DVZ_bits EQU (DVZ_bit) +IVO_bits EQU (IVO_bit) + + +;; FP compare values from fpieee.h _FPIEEE_COMPARE_RESULT +;; These are used for the exception handler. +_FpCompareEqual EQU 0 +_FpCompareGreater EQU 1 +_FpCompareLess EQU 2 +_FpCompareUnordered EQU 3 + + + +Double_bit EQU ((1 << 18) :AND:0) ;; RDCFix: Get rid of this and fix what it breaks. +LongLong_bit EQU ((1 << 18) :AND:0) ;; RDCFix: Get rid of this and fix what it breaks. + +EDOM EQU 1 ;; RDCFix: Need this? +ERANGE EQU 2 ;; RDCFix: Need this? +ESIGNUM EQU 3 ;; RDCFix: Need this? + +veneer_s RLIST {r4-r9,r11,lr} +veneer_l RLIST {r4-r9,r11,pc} + + +;; Macro to do a load immediate of an arbitrary whole word. + MACRO + LWI $cc, $reg, $imm + MOV$cc $reg, #(($imm) :AND: 0x000000FF) + ORR$cc $reg, $reg, #(($imm) :AND: 0x0000FF00) + ORR$cc $reg, $reg, #(($imm) :AND: 0x00FF0000) + ORR$cc $reg, $reg, #(($imm) :AND: 0xFF000000) + MEND + + + + + + END diff --git a/base/Kernel/Native/arm/Crt/fpraise.cpp b/base/Kernel/Native/arm/Crt/fpraise.cpp new file mode 100644 index 0000000..5b05bfe --- /dev/null +++ b/base/Kernel/Native/arm/Crt/fpraise.cpp @@ -0,0 +1,147 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// This file contains ARM-specific FP code. +// + +// +// FP_CODE +// +// Enumerated type containing all base FP operations that can raise exceptions. +// This must agree with the defines in fpe.asm. +// +typedef enum +{ + _FpAddD, // Add Double + _FpAddS, // Add Single + _FpSubD, // Subtract Double + _FpSubS, // Subtract Single + _FpCmpD, // Compare Double + _FpCmpS, // Compare Single + _FpDivD, // Divide Double + _FpDivS, // Divide Single + _FpDToI, // Convert Double To int + _FpDToI64, // Convert Double To __int64 + _FpDToS, // Convert Double To Single + _FpDToU, // Convert Double To unsigned int + _FpDToU64, // Convert Double To unsigned __int64 + _FpIToS, // Convert int To Single + _FpMulD, // Multiply Double + _FpMulS, // Multiply Single + _FpSToD, // Convert Single To Double + _FpSToI, // Convert Single To int + _FpSToI64, // Convert Single To __int64 + _FpSToU, // Convert Single To unsigned int + _FpSToU64, // Convert Single To unsigned __int64 + _FpUToS, // Convert unsigned int To Single + _FpI64ToD, // Convert __int64 To Double + _FpI64ToS, // Convert __int64 To Single + _FpU64ToD, // Convert unsigned __int64 To Double + _FpU64ToS, // Convert unsigned __int64 To Single + _FpRndD // Round to double integer +} FP_CODE; + + + +// +// FP_VALUE +// +// A floating-point emulation routine supplies an FP_VALUE for each operand and +// the result. The proper alias in the union is determined by the corresponding +// FP_OP. This structure must be exactly 8 bytes big (corresponding to +// sizeof(double)). Note that extended formats such as long double are not +// supported. + +typedef union { + float Fp32Value; // IEEE 32-bit floating-point value + double Fp64Value; // IEEE 64-bit floating-point value + int I32Value; // Signed 32-bit integer value + __int64 I64Value; // Signed 64-bit integer value + unsigned int U32Value; // Unsigned 32-bit integer value + unsigned __int64 U64Value; // Unsigned 64-bit integer value + int CompareValue; // One of _FPIEEE_COMPARE_RESULT enums +} FP_VALUE; + + + +// +// FPExInfo +// +// This must match the defines for exception information in fpe.s. +// + +typedef union +{ + unsigned int Raw; + struct + { + unsigned int CauseInvalid : 1; // Bit 0 + unsigned int CauseZeroDivide : 1; // Bit 1 + unsigned int CauseOverflow : 1; // Bit 2 + unsigned int CauseUnderflow : 1; // Bit 3 + unsigned int CauseInexact : 1; // Bit 4 + unsigned int Reserved2 : 15; // Bits 19:5 + unsigned int Operation : 5; // Bits 24:20 + unsigned int Reserved1 : 7; // Bits 31:25 + } Field; +} FPExInfo; + + + +//++ +// +//Routine Description: +// +// This function accepts basic IEEE floating-point exception information from +// a software emulation routine that detected the exception. All of the +// exception information is packaged into a standard _FPIEEE_RECORD structure +// and passed to RaiseException. On return from RaiseException, the structure +// is unpacked, causing anything that the user changed to be reflected in the +// floating-point status. The calling parameters have been selected and +// arranged for the convenience of the assembly language coded emulation +// routines. +// +// A user typically calls _fpieee_flt, the standard SEH exception filter for +// IEEE floating-point exceptions, to gain access to the _FPIEEE_RECORD +// structure. On a system with hardware floating-point instructions, the +// _fpieee_flt exception filter decodes the results of the floating-point trap +// (often decoding instruction opcodes and other state left by the kernel) and +// builds the _FPIEEE_RECORD structure itself. But since the SH-3 uses +// software floating-point emulation, we use the same reporting mechanism that +// the math library uses. In this case, _fpieee_flt detects that an +// _FPIEEE_RECORD has already been created for the exception and simply passes +// it through. +// +//Arguments: +// +// ExInfo - Supplies the exception information regarding operation and cause. +// +// Operand1 - Supplies the original first operand. +// +// Operand2 - Supplies the original second operand. +// +// Result - Supplies the default result if the user simply continues. +// +//Return Value: +// +// The result, possibly modified by the user's exception handler, after return +// from RaiseException. The software emulation routine substitutes this value +// for the result it returns. +// +//-- + +extern "C" +FP_VALUE +FPE_Raise ( + FPExInfo ExInfo, + FP_VALUE Operand1, + FP_VALUE Operand2, + FP_VALUE Result + ) +{ + __debugbreak(); + return Result; +} diff --git a/base/Kernel/Native/arm/Crt/h_errors.asm b/base/Kernel/Native/arm/Crt/h_errors.asm new file mode 100644 index 0000000..31f523c --- /dev/null +++ b/base/Kernel/Native/arm/Crt/h_errors.asm @@ -0,0 +1,84 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +;;; RCS Revision: 1 +;;; Checkin Date: 2007/06/29 02:59:16 +;;; Revising Author + +;;; h_errors.s +;;; Copyright (C) Advanced RISC Machines Ltd., 1991 + + MACRO + ErrorBlock $name, $string +E_$name + & Error_$name + = "$string", 0 + ALIGN + MEND + +MemoryBase * &8000 ; for rd & wr checks: require +MemoryLimit * &00400000 ; pointer to be in this range + +; Arthur error numbers +Error_NameNotFound * &124 +Error_ValueTooLong * &125 + +Error_IllegalInstruction * &80000000 +Error_PrefetchAbort * &80000001 +Error_DataAbort * &80000002 +Error_AddressException * &80000003 +Error_UnknownIRQ * &80000004 +Error_BranchThroughZero * &80000005 + +Error_FPBase * &80000200 + +Error_FP_IVO * Error_FPBase + 0 +Error_FP_OFL * Error_FPBase + 1 +Error_FP_DVZ * Error_FPBase + 2 +Error_FP_UFL * Error_FPBase + 3 +Error_FP_INX * Error_FPBase + 4 + +Error_FPLimit * &80000300 + +; Arthur errors generated by the library +CLib_Error_Base * &800e80 +CLib_Error_Range * &80 + +Error_BadMemory * &800e80 +Error_UnknownLib * &800e81 +Error_StubCorrupt * &800e82 +Error_StaticSizeWrong * &800e83 +Error_StaticOffsetInconsistent * &800e84 +Error_UnknownSWI * &800e85 + +Error_SharedLibraryNeeded * &800e90 +Error_OldSharedLibrary * &800e91 +Error_NoVeneer * &80800e92 + +Error_ReadFail * &80800ea0 +Error_WriteFail * &80800ea1 + +Error_RecursiveTrap * &800e00 +Error_UncaughtTrap * &800e01 +Error_NoMainProgram * &800e02 +Error_NotAvailable * &800e03 +Error_NoEnvFile * &800e04 +Error_NoRoomForEnv * &800e05 +Error_BadReturnCode * &800e06 +Error_NoStackForTrapHandler * &800e07 +Error_Exit * &800e08 ; in non-user mode +Error_WrongCPU * &800e09 + +Error_ReservedForOverlayManager1 * &800efe +Error_ReservedForOverlayManager2 * &800eff + +Error_DivideByZero * &80000020 +Error_StackOverflow * &80000021 + + END diff --git a/base/Kernel/Native/arm/Crt/macros.asm b/base/Kernel/Native/arm/Crt/macros.asm new file mode 100644 index 0000000..06f5579 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/macros.asm @@ -0,0 +1,927 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; Assembler source for FPA support code and emulator +; ================================================== +; Some useful assembler macros. Also used by "fplib". +; +; Copyright (C) Advanced RISC Machines Limited, 1992-7. All rights reserved. +; +; RCS Revision: 1 +; Checkin Date: 2007/06/29 02:59:16 +; Revising Author + +;=========================================================================== + +; Register names used when isolating the base register of a PC-relative or +; register-relative expression in the macros below. The technique is to set +; a temporary arithmetic variable Base to :BASE:(expression), then refer to +; R$Base. + +R00000000 RN R0 +R00000001 RN R1 +R00000002 RN R2 +R00000003 RN R3 +R00000004 RN R4 +R00000005 RN R5 +R00000006 RN R6 +R00000007 RN R7 +R00000008 RN R8 +R00000009 RN R9 +R0000000A RN R10 +R0000000B RN R11 +R0000000C RN R12 +R0000000D RN R13 +R0000000E RN R14 +R0000000F RN R15 + +;=========================================================================== + +; Two general purpose arithmetic variables. + + GBLA Tempa + GBLA Tempa2 + +;=========================================================================== + +; The following macro is useful for shifting bit fields around when their +; positions are symbolic constants - which makes it unclear to the author +; whether LSR or LSL is needed. + + MACRO +$label BiShift $opc,$Rd,$Rn,$Rm,$rshift,$lshift + [ "$lshift":LEFT:5 <> "LSL #" + ! 4,"Left shift must start exactly 'LSL #'" + | + [ "$rshift":LEFT:5 <> "LSR #" + ! 4,"Right shift must start exactly 'LSR #'" + | + LCLS left + LCLS right +left SETS "$lshift":RIGHT:(:LEN:"$lshift" - 5) +right SETS "$rshift":RIGHT:(:LEN:"$rshift" - 5) + [ "$Rn" = "" + ASSERT (("$opc":LEFT:3) <> "LDR") :LAND: (("$opc":LEFT:3) <> "STR") + [ ($right) > ($left) +$label $opc $Rd,$Rm,LSR #(($right) - ($left)) + | +$label $opc $Rd,$Rm,LSL #(($left) - ($right)) + ] + | + [ ($right) > ($left) + [ (("$opc":LEFT:3) = "LDR") :LOR: (("$opc":LEFT:3) = "STR") +$label $opc $Rd,[$Rn,$Rm,LSR #(($right) - ($left))] + | +$label $opc $Rd,$Rn,$Rm,LSR #(($right) - ($left)) + ] + | + [ (("$opc":LEFT:3) = "LDR") :LOR: (("$opc":LEFT:3) = "STR") +$label $opc $Rd,[$Rn,$Rm,LSL #(($left) - ($right))] + | +$label $opc $Rd,$Rn,$Rm,LSL #(($left) - ($right)) + ] + ] + ] + ] + ] + MEND + +;=========================================================================== + +; The following macro isolates the exponent field from the standard sign/ +; uncommon bit/exponent word, putting it at the top of the destination +; register. + + MACRO +$label Exp2Top $dest,$src,$cond,$s + [ EIExp_pos = 0 +$label MOV$cond$s $dest,$src,LSL #32-EIExp_len + | +$label MOV$cond $dest,$src,LSR #EIExp_pos + MOV$cond$s $dest,$dest,LSL #32-EIExp_len + ] + MEND + +;=========================================================================== + +; The following macros isolate the exponent fields from two standard sign/ +; uncommon bit/exponent words, putting the first one at the top of a +; destination register. ExpDiff puts the difference at the top of another +; register and sets the condition codes on it, while ExpComp simply sets the +; condition codes on the difference. + + MACRO +$label ExpComp $dest,$src1,$src2,$tmp + ASSERT $dest <> $src1 + ASSERT $dest <> $src2 + ASSERT $dest <> $tmp + ASSERT $tmp <> $src1 + ASSERT $tmp <> $src2 + [ EIExp_pos = 0 +$label MOV $dest,$src1,LSL #32-EIExp_len + CMP $dest,$src2,LSL #32-EIExp_len + | +$label MOV $dest,$src1,LSR #EIExp_pos + MOV $dest,$dest,LSL #32-EIExp_len + MOV $tmp,$src2,LSR #EIExp_pos + CMP $dest,$tmp,LSL #32-EIExp_len + ] + MEND + + MACRO +$label ExpDiff $diff,$dest,$src1,$src2 + ASSERT $diff <> $dest + ASSERT $diff <> $src1 + ASSERT $diff <> $src2 + ASSERT $dest <> $src1 + ASSERT $dest <> $src2 + [ EIExp_pos = 0 +$label MOV $dest,$src1,LSL #32-EIExp_len + SUBS $diff,$dest,$src2,LSL #32-EIExp_len + | +$label MOV $dest,$src1,LSR #EIExp_pos + MOV $dest,$dest,LSL #32-EIExp_len + MOV $diff,$src2,LSR #EIExp_pos + SUBS $diff,$dest,$diff,LSL #32-EIExp_len + ] + MEND + +;=========================================================================== + +; The following macro performs the standard test for infinities or NaNs on +; an internal floating point number. It only works on legitimate internal +; precision numbers - i.e. it produces undefined results if the bit pattern +; in the internal precision number is an undefined one. The parameters are: +; $res: On exit, the top bit of this register is set if the number is a +; NaN or infinity, clear if it isn't; +; $sue: Register holding sign, uncommon bit and exponent of number to be +; tested; preserved on exit; +; $mhi: Register containing high word of mantissa of number to be tested; +; preserved on exit; +; $mlo: Register containing low word of mantissa of number to be tested; +; preserved on exit; +; +; In addition, the N flag is set on exit if the number is a NaN or infinity, +; clear if it isn't. +; +; The criterion used for a number to be a NaN or infinity is: +; +; Uncommon bit = 1; and +; Exponent top bit = 1; and +; Exponent = MAX or units bit = 1. +; +; Whether the operand is in fact a NaN or an infinity is then determined by +; seeing whether the fraction is non-zero or zero. + + MACRO +$label TNaNInf $res,$sue,$mhi + ASSERT $res <> $sue + ASSERT $res <> $mhi +$label MOV $res,$sue,LSL #32-(EIExp_pos+EIExp_len) ;Top bit of exponent + CMN $res,#1:SHL:(32-(EIExp_pos+EIExp_len)) ;Is exp. = MAX? If + ANDCC $res,$res,$mhi ; not, use units bit + ANDS $res,$res,$sue,LSL #31-Uncommon_pos ;Use uncommon anyway + MEND + +;=========================================================================== + +; The following macro contains the standard code for denormalising a +; mantissa by a specified amount, producing guard, round and sticky bits in +; the process. The parameters are: +; $mhi: Register containing mantissa high word; updated on exit; +; $mlo: Register containing mantissa low word; updated on exit; +; $grs: Register that will contain the guard bit (in bit 31), the round +; bit (in bit 30) and the sticky bit (in whether bits 29:0 are zero +; or non-zero) on exit; +; $sh: Register containing the shift amount; corrupt on exit; +; $t1,$t2: Registers used as temporaries; corrupt on exit. +; $grs may be null to indicate that the guard, round and sticky information +; isn't wanted. $mlo can be null to indicate that we only need to +; denormalise a single word: in this case, $grs must be null. +; Note that the $grs register may alternatively be interpreted as +; containing a round bit in bit 31 and a sticky bit in bits 30:0, in cases +; when there is no need for a guard bit. Also note that $sh may be the same +; register as either of $grs and $t2; otherwise, the registers must be +; distinct from each other. +; Finally, note that branch instructions are used around a 4 instruction +; sequence and a 5 instruction sequence. This is because statistics show +; that larger shift amounts are less common than smaller ones in general: +; thus these instruction sequences are obeyed less than 50% of the time, +; which makes the code with branches slightly faster. + + MACRO +$label Denorm $mhi,$mlo,$grs,$sh,$t1,$t2 + ASSERT $mhi <> $sh + ASSERT $mhi <> $t1 + ASSERT $mhi <> $t2 + ASSERT $sh <> $t1 + ASSERT $t1 <> $t2 +$label MOV $t1,$sh,LSR #5 ;Number of words to shift by + BIC $t2,$sh,$t1,LSL #5 ;Number of odd bits to shift by + [ "$mlo" = "" + ASSERT "$grs" = "" + CMP $t1,#1 ;At least one word? + MOVLO $mhi,$mhi,LSR $t2 ;Shift by odd bits if not + MOVHS $mhi,#0 ;And clear out completely if so + | + ASSERT $mlo <> $mhi + ASSERT $mlo <> $sh + ASSERT $mlo <> $t1 + ASSERT $mlo <> $t2 + [ "$grs" = "" + CMP $t1,#1 ;HI for 2+ words, EQ for 1, LO for 0 + RSBLS $t1,$t2,#32 ;Shift by the number of odd bits + MOVLS $mlo,$mlo,LSR $t2 + ORRLS $mlo,$mlo,$mhi,LSL $t1 + MOVLS $mhi,$mhi,LSR $t2 + MOVEQ $mlo,$mhi ;Now do full words + MOVHI $mlo,#0 + MOVHS $mhi,#0 + | + ASSERT $grs <> $mhi + ASSERT $grs <> $mlo + ASSERT $grs <> $t1 + ASSERT $grs <> $t2 + CMP $t1,#2 ;CS/NE for 3+ words, CS/EQ for 2, + TEQCC $t1,#0 ; CC/NE for 1 and CC/EQ for 0. + RSB $t1,$t2,#32 ;Shift by the number of odd bits + MOV $grs,$mlo,LSL $t1 + MOV $mlo,$mlo,LSR $t2 + ORR $mlo,$mlo,$mhi,LSL $t1 + MOV $mhi,$mhi,LSR $t2 + BEQ %f90 ;Branch if no 32-bit shift + ORRNE $grs,$grs,$grs,LSL #2 ;Shift by 32 bits, accumulating + ORRNE $grs,$mlo,$grs,LSR #2 ; sticky bit + MOVNE $mlo,$mhi + MOVNE $mhi,#0 +90 + BCC %f99 ;Branch if no 64-bit shift + ORRCS $grs,$grs,$mlo ;Shift by 64 bits, accumulating + ORRCS $grs,$grs,$grs,LSL #2 ; sticky bit + ORRCS $grs,$mhi,$grs,LSR #2 + MOVCS $mlo,#0 + MOVCS $mhi,#0 +99 + ] + ] + MEND + +;=========================================================================== + +; Macro to separate a 32-bit value in a register into its two 16-bit halves. + + MACRO +$label Split16 $resh,$resl,$src + ASSERT $resh <> $src +$label MOV $resh,$src,LSR #16 + BIC $resl,$src,$resh,LSL #16 + MEND + +;=========================================================================== + +; Macro to do a (16,16)x32 -> 64 multiplication. Done by breaking it up into +; four 16x16 multiplications and recombining the pieces. (N.B. The trick +; described in Knuth section 4.3.3 for reducing the four multiplications to +; three plus some additions and sign manipulations is not profitable at this +; size: it only becomes profitable when trying to synthesise a 64x64 +; multiplication out of 32x32 multiplications.) +; Also allows the flags to be set on the high word of the result and an +; optional addend to be added into the high word of the result: however, +; combining these does *not* result in the C flag being set correctly for +; the carry-out from the notional addition of the addend and the high word. +; Only the Z and N flags have meaningful values. +; The operands are: +; $resh,$resl: Registers that will receive the 64-bit product; +; $op1h,$op1l: Registers containing the high and low 16 bits of the first +; 32-bit operand; +; $op2: Register containing the second 32-bit operand; +; $add: If present, register containing the addend; +; $s: "S" to set the condition codes; +; $t1,$t2,$t3: Three temporary registers required during the calculation. +; The restrictions on which registers may be the same are complicated and +; are detailed in the ASSERT statements below. + + MACRO +$label Mul64 $resh,$resl,$op1h,$op1l,$op2,$add,$s,$t1,$t2,$t3 + ASSERT $resh <> $resl + ASSERT $resl <> $op1h + ASSERT $resl <> $t1 + ASSERT $resl <> $t2 + ASSERT $resl <> $t3 + ASSERT $op1h <> $op1l + ASSERT $op1h <> $op2 + ASSERT $op1h <> $t1 + ASSERT $op1h <> $t2 + ASSERT $op1h <> $t3 + ASSERT $op1l <> $op2 + ASSERT $op1l <> $t1 + ASSERT $op1l <> $t2 + ASSERT $op1l <> $t3 + ASSERT $op2 <> $t1 + ASSERT $t1 <> $t2 + ASSERT $t1 <> $t3 + ASSERT $t2 <> $t3 +$label Split16 $t1,$t2,$op2 ;t1 := op2h, t2 := op2l + [ "$add" <> "" + ASSERT $add <> $op1h + ASSERT $add <> $op1l + ASSERT $add <> $op2 + ASSERT $add <> $t1 + ASSERT $add <> $t2 + MLA $t3,$op1h,$t1,$add ;t3 := op1h * op2h + add + | + MUL $t3,$op1h,$t1 ;t3 := op1h * op2h + ] + MUL $t1,$op1l,$t1 ;t1 := op1l * op2h + MUL $resl,$t2,$op1l ;resl := op1l * op2l + ADDS $resl,$resl,$t1,LSL #16 ;Add op1l * op2h into (t3,resl) + ADC $t3,$t3,$t1,LSR #16 + MUL $t2,$op1h,$t2 ;t2 := op1h * op2l + ADDS $resl,$resl,$t2,LSL #16 ;Add op1h * op2l into (t3,resl) + ADC$s $resh,$t3,$t2,LSR #16 ; to produce (resh,resl) + MEND + +;=========================================================================== + +; Macro to transfer the destination register of an instruction to a set of +; registers. Operands are: +; $type: "FPASC" or "FPE"; +; $dest: The destination register list; +; $instr: The instruction whose destination is to be transferred; +; $t: A temporary. + + MACRO +$label GetDst $type,$dest,$instr,$t + LCLA Base + LCLA Offset + ASSERT ("$type" = "FPASC") :LOR: ("$type" = "FPE") + [ ("$type" = "FPASC") +$label TST $instr,#4:SHL:Ds_pos ;Check whether F0-3 or F4-7 + SFMEQFD F0,4,[Rsp]! ;Dump set of 4 registers - + SFMNEFD F4,4,[Rsp]! ; faster than trying to get + ; the correct register only + AND $t,$instr,#3:SHL:Ds_pos ;Get position in dump + ADD $t,$t,$t,LSL #1 ;Convert to number of words + BiShift ADD,$t,Rsp,$t,LSR #Ds_pos,LSL #2 ;Make address of register + LDMIA $t,$dest ; value, then get value + ADD Rsp,Rsp,#48 ;Discard dumped registers + ASSERT Ds_mask = ((4+3):SHL:Ds_pos) + | +$label AND $t,$instr,#Ds_mask + [ :LNOT:FPE4WordsPerReg + ADD $t,$t,$t,LSL #1 + ASSERT Ds_pos <= 27 + ] +Base SETA :BASE:FPE_Regs + [ Base = 15 +Offset SETA FPE_Regs-({PC}+8) + | +Offset SETA :INDEX:FPE_Regs + ] + [ FPE4WordsPerReg + BiShift ADD,$t,R$Base,$t,LSR #Ds_pos,LSL #4 + | + BiShift ADD,$t,R$Base,$t,LSR #Ds_pos,LSL #2 + ] + [ Offset <> 0 + ADD $t,$t,#Offset + ] + LDMIA $t,$dest + ] + MEND + +;=========================================================================== + +; Macro to transfer the destination register of a non-FLT instruction from a +; set of registers. Operands are: +; $type: "FPASC" or "FPE"; +; $source: The source register list; +; $instr: The instruction whose destination is to be transferred; +; $t: A temporary. +; $l: If present, this produces a "long" form of the macro + + MACRO +$label PutDst $type,$source,$instr,$t,$l + ASSERT $t <> $instr + LCLA Base + LCLA Offset + ASSERT ("$type" = "FPASC") :LOR: ("$type" = "FPE") + [ ("$type" = "FPASC") + ALIGN +$label + STMFD Rsp!,$source + AND $t,$instr,#Ds_mask +10 + BiShift ADD,$t,PC,$t,LSR #Ds_pos,LSL #3 + [ "$l"="" + MOV LR,PC + ADD PC,$t,#($type._PutDstRoutines - (%b10+8)) + | + ADD $t,$t,#($type._PutDstRoutines - (%b10+8)):AND:&FF + MOV LR,PC + ADD PC,$t,#($type._PutDstRoutines - (%b10+8)):AND::NOT:&FF + ] + | + ; "$type" = "FPE" +$label AND $t,$instr,#Ds_mask + [ :LNOT:FPE4WordsPerReg + ADD $t,$t,$t,LSL #1 + ASSERT Ds_pos <= 27 + ] +Base SETA :BASE:FPE_Regs + [ Base = 15 +Offset SETA FPE_Regs-({PC}+8) + | +Offset SETA :INDEX:FPE_Regs + ] + [ FPE4WordsPerReg + BiShift ADD,$t,R$Base,$t,LSR #Ds_pos,LSL #4 + | + BiShift ADD,$t,R$Base,$t,LSR #Ds_pos,LSL #2 + ] + [ Offset <> 0 + ADD $t,$t,#Offset + ] + STMIA $t,$source + ] + MEND + +;=========================================================================== + +; Macro to transfer the destination register of a FLT instruction from a set +; of registers. Operands are: +; $type: "FPASC" or "FPE"; +; $source: The source register list; +; $instr: The instruction whose destination is to be transferred; +; $t: A temporary. + + MACRO +$label PutFDst $type,$source,$instr,$t + ASSERT $t <> $instr + LCLA Base + LCLA Offset + ASSERT ("$type" = "FPASC") :LOR: ("$type" = "FPE") + [ ("$type" = "FPASC") + ALIGN +$label + STMFD Rsp!,$source + AND $t,$instr,#S1_mask +10 + BiShift ADD,$t,PC,$t,LSR #S1_pos,LSL #3 + MOV LR,PC + ADD PC,$t,#($type._PutDstRoutines - (%b10+8)) + | + ; "$type" = "FPE" +$label AND $t,$instr,#S1_mask + [ :LNOT:FPE4WordsPerReg + ADD $t,$t,$t,LSL #1 + ASSERT S1_pos <= 27 + ] +Base SETA :BASE:FPE_Regs + [ Base = 15 +Offset SETA FPE_Regs-({PC}+8) + | +Offset SETA :INDEX:FPE_Regs + ] + [ FPE4WordsPerReg + BiShift ADD,$t,R$Base,$t,LSR #S1_pos,LSL #4 + | + BiShift ADD,$t,R$Base,$t,LSR #S1_pos,LSL #2 + ] + [ Offset <> 0 + ADD $t,$t,#Offset + ] + STMIA $t,$source + ] + MEND + +;=========================================================================== + +; Macro to get the first source register of an instruction into three +; registers. Operands are: +; $type: "FPASC" or "FPE"; +; $dest: The destination register list; +; $instr: The instruction whose first source is to be transferred; +; $t: A temporary. + + MACRO +$label GetS1 $type,$dest,$instr,$t + LCLA Base + LCLA Offset + ASSERT ("$type" = "FPASC") :LOR: ("$type" = "FPE") + [ ("$type" = "FPASC") +$label TST $instr,#4:SHL:S1_pos ;Check whether F0-3 or F4-7 + SFMEQFD F0,4,[Rsp]! ;Dump set of 4 registers - + SFMNEFD F4,4,[Rsp]! ; faster than trying to get + ; the correct register only + AND $t,$instr,#3:SHL:S1_pos ;Get position in dump + ADD $t,$t,$t,LSL #1 ;Convert to number of words + BiShift ADD,$t,Rsp,$t,LSR #S1_pos,LSL #2 ;Make address of register + LDMIA $t,$dest ; value, then get value + ADD Rsp,Rsp,#48 ;Discard dumped registers + ASSERT S1_mask = ((4+3):SHL:S1_pos) + | +$label AND $t,$instr,#S1_mask + [ :LNOT:FPE4WordsPerReg + ADD $t,$t,$t,LSL #1 + ASSERT S1_pos <= 27 + ] +Base SETA :BASE:FPE_Regs + [ Base = 15 +Offset SETA FPE_Regs-({PC}+8) + | +Offset SETA :INDEX:FPE_Regs + ] + [ FPE4WordsPerReg + BiShift ADD,$t,R$Base,$t,LSR #S1_pos,LSL #4 + | + BiShift ADD,$t,R$Base,$t,LSR #S1_pos,LSL #2 + ] + [ Offset <> 0 + ADD $t,$t,#Offset + ] + LDMIA $t,$dest + ] + MEND + +;=========================================================================== + +; Macro to get the second source register or constant of an instruction into +; three registers. Operands are: +; $type: "FPASC" or "FPE"; +; $dest: The destination register list; +; $instr: The instruction whose second source is to be transferred; +; $t,$t2: Temporaries. + + MACRO +$label GetS2 $type,$dest,$instr,$t,$t2 + LCLA Base + LCLA Offset + ASSERT ("$type" = "FPASC") :LOR: ("$type" = "FPE") + ASSERT S2_Ibit = 1:SHL:(S2_pos+3) + ASSERT S2_pos = 0 + ASSERT $t <> $t2 +$label MOVS $t2,$instr,LSL #29 ;C:=S2_Ibit, N:=F4-7, not F0-3 + ;$t2 := left-al. reg/const no. + [ ("$type" = "FPASC") +Base SETA :BASE:$type.ConstTable + [ Base = 15 +Offset SETA $type.ConstTable-({PC}+8) + | +Offset SETA :INDEX:$type.ConstTable + ] + ADDCS $t,R$Base,$t2,LSR #25 ;Address the constant if it + [ Offset <> 0 + ADDCS $t,$t,#Offset ; is one + ] + BCS %f10 + SFMPLFD F0,4,[Rsp]! ;Dump set of 4 registers - + SFMMIFD F4,4,[Rsp]! ; faster than trying to get + ; the correct register only + BIC $t2,$t2,#TopBit ;Get position within set + ADD $t,Rsp,$t2,LSR #27 ;Make address of register + ADD $t,$t,$t2,LSR #26 ; value +10 + LDMIA $t,$dest ;Get reg. value or constant + ADDCC Rsp,Rsp,#48 ;If reg, discard dumped regs + | +Base SETA :BASE:FPEConstTable + [ Base = 15 +Offset SETA FPEConstTable-({PC}+8) + | +Offset SETA :INDEX:FPEConstTable + ] + ADDCS $t,R$Base,$t2,LSR #25 + [ Offset <> 0 + ADDCS $t,$t,#Offset + ] +Base SETA :BASE:FPE_Regs + [ Base = 15 +Offset SETA FPE_Regs-({PC}+8) + | +Offset SETA :INDEX:FPE_Regs + ] + [ FPE4WordsPerReg + ADDCC $t,R$Base,$t2,LSR #25 + | + ADDCC $t,R$Base,$t2,LSR #27 + ADDCC $t,$t,$t2,LSR #26 + ] + [ Offset <> 0 + ADDCC $t,$t,#Offset + ] + LDMIA $t,$dest + ] + MEND + +;=========================================================================== + +; Macro to get both source operands of an instruction into two groups of +; three registers. Operands are: +; $type: "FPASC" or "FPE"; +; $dest1: The destination register list for the first operand; +; $dest2: The destination register list for the second operand; +; $instr: The instruction whose second source is to be transferred; +; $t, $t2: Temporaries. + + MACRO +$label GetS12 $type,$dest1,$dest2,$instr,$t,$t2 + LCLA Base + LCLA Offset + ASSERT ("$type" = "FPASC") :LOR: ("$type" = "FPE") + [ ("$type" = "FPASC") + SFMFD F4,4,[Rsp]! ;Dump all registers + SFMFD F0,4,[Rsp]! + AND $t,$instr,#S1_mask ;Get S1 position in dump + ADD $t,$t,$t,LSL #1 ;Convert to number of words + BiShift ADD,$t,Rsp,$t,LSR #S1_pos,LSL #2 ;Make address of register + LDMIA $t,$dest1 ; value, then get value + ASSERT S2_Ibit = 1:SHL:(S2_pos+3) + ASSERT S2_pos = 0 + MOVS $t2,$instr,LSL #29 ;C:=S2_Ibit, N:=F4-7, not F0-3 + ;$t2 := left-al. reg/const no. +Base SETA :BASE:$type.ConstTable + [ Base = 15 +Offset SETA $type.ConstTable-({PC}+8) + | +Offset SETA :INDEX:$type.ConstTable + ] + ADDCS $t,R$Base,$t2,LSR #25 ;Address the constant if it + [ Offset <> 0 + ADDCS $t,$t,#Offset ; is one + ] + ADDCC $t,Rsp,$t2,LSR #27 ;Otherwise address the register + ADDCC $t,$t,$t2,LSR #26 ; value + LDMIA $t,$dest2 + ADD Rsp,Rsp,#96 ;Discard the register dump + | +$label GetS1 $type,$dest1,$instr,$t + GetS2 $type,$dest2,$instr,$t,$t2 + ] + MEND + +;=========================================================================== + +; The standard macro to return to the caller. Note that care has to be taken +; here never to leave Rsp pointing above any useful stack contents, in case +; of a badly-timed interrupt. + + MACRO +$label Return + [ {CONFIG} = 26 +$label MOV Rsp,Rfp ;Discard now-spurious stack contents + ] + [ {CONFIG} = 32 +$label LDMDB Rfp,{Rtmp,Rtmp2} ;Recover the SPSR and CPSR + MSR CPSR_all,Rtmp2 ; (restoring the CPSR re-disables + MSR SPSR_all,Rtmp ; interrupts, so the SPSR isn't ever + ; valid when interrupts are enabled) + MOV Rsp,Rfp ;Discard now-spurious stack contents + ] + LDMIA Rfp,{R0-R14}^ ;Coding rules: cannot use write-back + NOP ; and must protect next instruction + ADD Rsp,Rsp,#15*4 ;Do the write-back + LDMFD Rsp!,{PC}^ ;Restore R13_svr/R13_und, PC and PSR + MEND + +;=========================================================================== + +; Macro to get the processor mode for the caller, with the 26/32 bit +; distinction removed. The flags are set on the result value, so Z indicates +; whether we're in user mode. + + MACRO +$label GetMode $res + [ {CONFIG}=32 +$label LDR $res,[Rfp,#-8] ;Recover original SPSR value + ANDS $res,$res,#Mode_mask-Mode_32not26 + ASSERT (Mode_USR26:AND::NOT:Mode_32not26) = 0 + ASSERT (Mode_USR32:AND::NOT:Mode_32not26) = 0 + ] + [ {CONFIG}=26 +$label LDR $res,[Rfp,#15*4] ;Recover original LR value + ANDS $res,$res,#Mode_mask + ASSERT Mode_USR26 = 0 + ] + MEND + +;=========================================================================== + +; Macro to insert the right amount of padding between a table-driven branch +; instruction (e.g. ADD PC,PC,reg,LSL #2) and the in-line branch table that +; follows it. Made into a macro for documentation purposes, and also just in +; case the user-visible pipeline depth has to change at some point in the +; future. + + MACRO + BranchTablePad + DCD 0 ;Padding before branch table + MEND + +;=========================================================================== + +; Macro to re-enable interrupts if "EnableInterrupts" is {TRUE}. Only +; argument is a temporary register. + + MACRO +$label InterruptEnable $t + [ EnableInterrupts + [ {CONFIG} = 32 +$label MRS $t,CPSR_all + BIC $t,$t,#I_bit + MSR CPSR_all,$t + ] + [ {CONFIG} = 26 +$label MOV $t,PC + BIC $t,$t,#I_bit + TEQP PC,$t + ] + NOP + ] + MEND + +;=========================================================================== + +; Macro to re-disable interrupts if "EnableInterrupts" is {TRUE}. Only +; argument is a temporary register. + + MACRO +$label InterruptDisable $t + [ EnableInterrupts + [ {CONFIG} = 32 +$label MRS $t,CPSR_all + ORR $t,$t,#I_bit + MSR CPSR_all,$t + ] + [ {CONFIG} = 26 +$label MOV $t,PC + ORR $t,$t,#I_bit + TEQP PC,$t + ] + NOP + ] + MEND + +;=========================================================================== + +; Macro to do the standard "enter recursive floating point code" processing. +; The operands are: +; $freeregs: Number of floating point registers to free up, in the range 1 +; to 8; +; $extra: Number of bytes of extra space to be left on the stack above +; the register dump; +; $addr: Register to contain address of extra space - also used to hold +; temporary values during macro, so must be present even if no +; extra space is wanted; +; $nofpsr: If this operand is non-null, it inhibits the FPSR changes +; described below. +; Rsp and Rfpsr are also operands. +; This code is written quite carefully, to avoid the use of floating point +; instructions the FPE won't like (e.g. those which use mode-dependent +; registers). It also disables interrupts (if they were ever enabled) and +; leaves a record of what floating point registers are on the stack, to make +; certain core_abort works. Finally, it clears out the exception enable bits +; and cumulative flags from the real FPSR, since exceptions in recursive +; code must not go out to the user trap handlers. Note that Rfpsr holds the +; real FPSR value and will be written back to the real FPSR before control +; is returned to the user, either via a trap handler or by normal return. + + MACRO +$label EnterRecursive $freeregs,$extra,$addr,$nofpsr + ASSERT ($freeregs) <= 8 + ASSERT ($freeregs) >= 1 +$label SUB Rsp,Rsp,#($freeregs)*12+4+($extra) + MOV $addr,Rfpsr,LSL #8 + ORR $addr,$addr,#(1:SHL:($freeregs))-1 + STR $addr,[Rsp] + InterruptDisable $addr + [ "$nofpsr" = "" + BIC $addr,Rfpsr,#IOE_bit+DZE_bit+OFE_bit+UFE_bit+IXE_bit + BIC $addr,$addr,#IOC_bit+DZC_bit+OFC_bit+UFC_bit+IXC_bit + WFS $addr + ] + ADD $addr,Rsp,#($freeregs)*12+4 + [ ($freeregs) <= 4 + SFM F0,($freeregs),[$addr,#-12*($freeregs)] + | + SFM F0,4,[$addr,#-12*($freeregs)] + SFM F4,($freeregs)-4,[$addr,#-12*($freeregs)+48] + ] + MEND + +;=========================================================================== + +; Macro to do the standard "exit recursive floating point code" processing. +; The operands are: +; $freeregs: Number of floating point registers to recover from the stack, +; in the range 1 to 8; +; $extra: Number of bytes of extra space to recover from the stack; +; $t: A temporary register; +; $fpres: Floating point register that contains the floating point result +; - null if no such result; +; $result: Register list to take 3 word floating point result - null if +; no such result. +; Rsp is also an operand. + + MACRO +$label ExitRecursive $freeregs,$extra,$t,$fpres,$result + ASSERT ($freeregs) <= 8 + ASSERT ($freeregs) >= 1 + ADD $t,Rsp,#($freeregs)*12+4 + [ "$fpres" <> "" + ASSERT ($extra) >= 12 + SFM $fpres,1,[$t] + LDMIA $t,$result + ] + [ ($freeregs) <= 4 + LFM F0,($freeregs),[$t,#-12*($freeregs)] + | + LFM F0,4,[$t,#-12*($freeregs)] + LFM F4,($freeregs)-4,[$t,#-12*($freeregs)+48] + ] + ADD Rsp,Rsp,#($freeregs)*12+4+($extra) + InterruptEnable $t + MEND + +;=========================================================================== + +; Macro to determine whether an exception was precise or imprecise. Implicit +; operands are Rfpsr (for the SO bit and to determine whether it is a +; hardware or software context) and Rins (to determine what type of +; instruction this is). Explicit operands are: +; $dst: Set to zero if precise, non-zero if imprecise; +; $t: A temporary register; +; $tbl_fpa: The address of a bit table used to determine whether FPA +; instructions are capable of producing imprecise exceptions. +; +; The full set of conditions for the operation to be imprecise are: +; +; * The FPA hardware is being used; +; * The SO bit is clear in the FPSR; +; * The instruction is a CPDO or CPRT; +; * The instruction is capable of delivering an imprecise exception (e.g. +; not a purely software-implemented instruction, a FIX or a compare); +; +; Testing this last condition is complicated: it is done via the bit table +; mentioned above. +; +; The optimisation that the whole test is unnecessary for FPE-only code is +; not performed here: it is instead done in the main assembly source and +; this macro is never used. + + MACRO +$label TestImp $dst,$t,$tbl_fpa + ASSERT $dst <> Rins + ASSERT $dst <> Rfpsr + ASSERT $t <> Rins + ASSERT $t <> Rfpsr + ASSERT $t <> $dst +$label + ; Address the table + ADR $t,$tbl_fpa + ; Then look up correct table entry + AND $dst,Rins,#RTnotDO_bit + BiShift LDR,$dst,$t,$dst,LSR #RTnotDO_pos,LSL #2 + TST Rins,#Op2_mask + MOVNE $dst,$dst,LSR #16 + AND $t,Rins,#Op1_mask + MOV $t,$t,LSR #Op1_pos + ; Now incorporate other tests + MOV $dst,$dst,LSR $t ;Bit0 is result so far + AND $dst,$dst,Rfpsr,LSR #31 ;Isolate bit0 and AND + ; with (hardware in use) + BIC $dst,$dst,Rfpsr,LSR #SO_pos ;AND with (SO bit clear) + AND $dst,$dst,Rins,LSR #RTDOnotDT_pos ;AND with (CPRT or CPDO) + MEND + +;=========================================================================== + +; Macros used for byte arrays. + + GBLA ByteArrayCount +ByteArrayCount SETA 0 + + MACRO +$label BytesStart + ALIGN +$label +ByteArray_$ByteArrayCount + MEND + + MACRO +$label BytesEnd + ALIGN +$label +ByteArrayEnd_$ByteArrayCount +ByteArrayCount SETA ByteArrayCount+1 + MEND + +;=========================================================================== + + END diff --git a/base/Kernel/Native/arm/Crt/mdsdiv64.asm b/base/Kernel/Native/arm/Crt/mdsdiv64.asm new file mode 100644 index 0000000..fa30709 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/mdsdiv64.asm @@ -0,0 +1,241 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; +;div64.s - perform __int64 right shift operation +; + OPT 2 ; disable listing + INCLUDE kxarm.inc + OPT 1 ; reenable listing + + IMPORT __rt_div0 + + TEXTAREA + + +;****************************************************************************** + + NESTED_ENTRY __rt_udivrem64by64 + + stmdb sp!, {r4 - r6, r10, r11, lr} + + PROLOG_END + + orrs r11, r2, r3 ; check to see if y is 0 + bne |YIsNotZero| +; +; Divide by zero has occurred. Raise an exception +; call RaiseException(STATUS_INTEGER_DIVIDE_BY_ZERO, 0, 0, NULL) +; + bl __rt_div0 ; __rt_div0 just calls RaiseException with above args. + mvn r0, #0 + mvn r1, #0 + mvn r2, #0 + mvn r3, #0 + b |ReturnQAndMod| +|YIsNotZero| + mov r5, #0 ; r5 and r6 hold __int64 q; + mov r6, #0 + cmp r3, r1 ; Is y>x ? + bhi |ReturnQAndMod| + bcc |YLTEQX_A| + cmp r2, r0 + bhi |ReturnQAndMod| +|YLTEQX_A| + + ands r11, r3, #2, 2 ; check if y's high bit is set + bpl |HighBitNotSetForY| + mov r5, #1 ; r6 already stores 0, q=1 + subs r0, r0, r2 ; subcract y from x, x = x % y + sbc r1, r1, r3 ; + b |ReturnQAndMod| +|HighBitNotSetForY| + mov r10, #1 ; r10 and r4 hold __int64 mod; + mov r4, #0 +|YLTEQX_B| + mov r3, r3, lsl #1 ; y <<= 1; + mov r11, r4, lsl #1 ; mask <<= 1; + orr r3, r3, r2, lsr #31 + orr r4, r11, r10, lsr #31 + mov r10, r10, lsl #1 + mov r2, r2, lsl #1 + movs r11, r3, lsr #31 + bne |HighBitYisSet| ;y is as big as possible + cmp r3, r1 + bcc |YLTEQX_B| ; loop if yhi-xhi < 0 + bhi |ShiftYAndMask|; break if yhi-xhi > 0 + cmp r2, r0 + bls |YLTEQX_B| ; loop ylo - xlo <= 0 + b |ShiftYAndMask| +|YGTX_A| + adds r5, r10, r5 ; q+=mask + adc r6, r4, r6 + subs r0, r0, r2 ; x-=y + sbc r1, r1, r3 +|ShiftYAndMask| + mov r11, r3, lsl #31 ; y>>=1 mask>>=1 + orr r2, r11, r2, lsr #1 + mov r11, r4, lsl #31 + orr r10, r11, r10, lsr #1 + mov r4, r4, lsr #1 + mov r3, r3, lsr #1 +|HighBitYisSet| + orrs r11, r10, r4 ; Is mask 0? + beq |ReturnQAndMod| + cmp r3, r1 + bcc |YGTX_A| ; loop if r1 > r3 or x >= y + bhi |ShiftYAndMask| + cmp r2, r0 + bls |YGTX_A| ; loop y <= x + b |ShiftYAndMask| +|ReturnQAndMod| + mov r2, r0 ; move the remainder to r2 and r3 + mov r3, r1 + mov r0, r5 ; put the quotient in r0, r1 + mov r1, r6 + ldmia sp!, {r4 - r6, r10, r11, lr} ; ldmfd + + IF Interworking + BX lr + ELSE + MOV pc, lr + ENDIF + + ENTRY_END + + +;****************************************************************************** + + NESTED_ENTRY __rt_divrem64by64 + + stmdb sp!, {r10, r11, lr} + + PROLOG_END + + ands r10, r1, #2, 2 ; check if x's sign bit is set + bpl |CompareY| + rsbs r0, r0, #0 ; x=-x; + rsc r1, r1, #0 +|CompareY| + ands r11, r3, #2, 2 ; check if y's sign bit is set + bpl |DoUDiv| + rsbs r2, r2, #0 ; y=-y; + rsc r3, r3, #0 +|DoUDiv| + bl |__rt_udivrem64by64|; + cmp r10, #0 + beq |NumeratorPos| + rsbs r2, r2, #0 ; modulus has same sign as numerator + rsc r3, r3, #0 +|NumeratorPos| + cmp r10, r11 + beq |QPos| + rsbs r0, r0, #0 ; Quotient sign reveral + rsc r1, r1, #0 +|QPos| + ldmia sp!, {r10, r11, lr} + + IF Interworking + BX lr + ELSE + MOV pc, lr + ENDIF + + ENTRY_END + + + +;****************************************************************************** + + NESTED_ENTRY __rt_udiv64by64 + + stmdb sp!, {lr} + + PROLOG_END + + bl __rt_udivrem64by64 + ldmia sp!, {lr} + + IF Interworking + BX lr + ELSE + MOV pc, lr + ENDIF + + ENTRY_END + + +;****************************************************************************** + + NESTED_ENTRY __rt_urem64by64 + + stmdb sp!, {lr} + + PROLOG_END + + bl __rt_udivrem64by64 + mov r0,r2 + mov r1,r3 + ldmia sp!, {lr} + + IF Interworking + BX lr + ELSE + MOV pc, lr + ENDIF + + ENTRY_END + + +;****************************************************************************** + + NESTED_ENTRY __rt_sdiv64by64 + + stmdb sp!, {lr} + + PROLOG_END + + bl __rt_divrem64by64 + ldmia sp!, {lr} + + IF Interworking + BX lr + ELSE + MOV pc, lr + ENDIF + + ENTRY_END + + +;****************************************************************************** + + NESTED_ENTRY __rt_srem64by64 + + stmdb sp!, {lr} + + PROLOG_END + + bl __rt_divrem64by64 + mov r0,r2 + mov r1,r3 + ldmia sp!, {lr} + + IF Interworking + BX lr + ELSE + MOV pc, lr + ENDIF + + ENTRY_END + + + END + + + diff --git a/base/Kernel/Native/arm/Crt/memcpy.asm b/base/Kernel/Native/arm/Crt/memcpy.asm new file mode 100644 index 0000000..e2d4d45 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/memcpy.asm @@ -0,0 +1,1548 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +;********************************************************************** +; void * +; memcpy( void *dest, const void *src, size_t count ); +; The memcpy function copies count bytes of src to dest. +; If the source and destination overlap, this function does +; not ensure that the original source bytes in the overlapping +; region are copied before being overwritten. Use memmove to +; handle overlapping regions. +; +;********************************************************************** + + OPT 2 ; disable listing + INCLUDE kxarm.inc + OPT 1 ; reenable listing + +dest RN R0 +source RN R1 +count RN R2 +temp1 RN R3 +temp2 RN R4 +temp3 RN R5 +temp4 RN R12 + + IF Thumbing + THUMBAREA + ENDIF + + NESTED_ENTRY memcpy + + ROUT + + IF Thumbing +; Switch from Thumb mode to ARM mode + DCW 0x4778 ; bx pc + DCW 0x46C0 ; nop + ENDIF + + ;//Save registers onto the stack + STMDB sp!, {dest,temp2,temp3,lr} ; save registers + + PROLOG_END + +; Use a threshold to determine which code to use: +; +; if destination & source are naturally aligned, then +; threshold = 512 +; else +; threshold = 128 +; +; if copy size > threshold, then +; use memcpybigblk +; else +; use .NET code + + ORR temp1, dest, source + TST temp1, #3 + MOVEQ temp1, #512 + MOVNE temp1, #128 + CMP count, temp1 + BHI UNDO_PROLOG ; revert and continue to memcpybigblk + +; NOTE: UNDO_PROLOG just restores SP, so do NOT modify anything other +; than r3 (temp1) and r12 (temp4) before this point + +;********************************************************************** +; Copy from head to tail to avoid source overwrite because the source +; destination the source +;********************************************************************** +HEAD_TO_TAIL + ;if LT 8 bytes store them and exit + CMP count, #8 ; 2-3 cycles + BLT BYTEMOVE4 + + ;Check alignment of parameters + ANDS temp1, dest, #3 ; 2-3 cycles + BEQ SRCALIGN + + ; destination is at least 1 byte misaligned + ; Read and write (4 - alignment) bytes to align destination. + RSB temp1, temp1, #4 ; 9 cycles + LDRB temp2, [source], #1 + CMP temp1, #2 + STRB temp2, [dest], #1 + LDRGEB temp3, [source], #1 ; >= 2 == at least 2 bytes + LDRGTB temp2, [source], #1 ; > 2 == 3 bytes unaligned + SUB count, count, temp1 + STRGEB temp3, [dest], #1 + STRGTB temp2, [dest], #1 + +SRCALIGN ; 3 - 7 cycles + TST source, #1 ; save alignment of src + BNE UNALIGNED ; src 3 byte unaligned. + TST source, #2 + BNE HWORDMOVE ; src and dst are hword aligned + +; +;word aligned source and destination, move blocks of 32 bytes +;until we have less than 32 bytes left, then divide moves in +;half down to less than 4, where we will move the last 3 or less +;bytes +; +WORDMOVE + SUBS count, count, #32 ; 2-3 cycles + BLT BLK16 + +BLK32 ; 20 cycles/32 bytes + LDMIA source!, {temp1,temp2,temp3,lr} + STMIA dest!, {temp1,temp2,temp3,lr} + LDMIA source!, {temp1,temp2,temp3,lr} + SUBS count, count, #32 + STMIA dest!, {temp1,temp2,temp3,lr} + BGE BLK32 + +BLK16 ; 11-4 cycles/16 bytes + ADDS count, count, #16 + LDMGEIA source!, {temp1, temp2, temp3, lr} + STMGEIA dest!, {temp1, temp2, temp3, lr} + BEQ WORD_BYTES_EXIT + SUBGTS count, count, #16 + +BLK8 ; 6 cycles/8 bytes + ADDS count, count, #8 + LDMGEIA source!, {temp1, temp2} + SUBGE count, count, #8 + STMGEIA dest!, {temp1, temp2} + +BLK4 + ADDS count, count, #4 ; 6-9 cycles/4 bytes + LDRGE temp1, [source], #4 + STRGE temp1, [dest], #4 + +WORD_BYTES + ADDLTS count, count, #4 + BEQ WORD_BYTES_EXIT ; On zero, Return to caller + + LDR temp1, [source], #4 ; 10 cycles/1-3 bytes + CMP count, #2 + STRGEH temp1, [dest], #2 + STRLTB temp1, [dest], #1 + MOVGT temp1, temp1, LSR #16 + STRGTB temp1, [dest], #1 + +WORD_BYTES_EXIT + + IF Interworking :LOR: Thumbing + LDMIA sp!, {dest, temp2, temp3, lr} + BX lr + ELSE + LDMIA sp!, {dest, temp2, temp3, pc} + ENDIF + +; +; half word align source and destination +; +HWORDMOVE ; 2-3 cycles + LDRH temp1, [source], #2 + SUBS count, count, #32 + BLT HWORD8_TST + +HWORD32 ; 35 cycles/32 bytes + LDMIA source!, {temp2,temp3,temp4,lr} + ORR temp1, temp1, temp2, LSL #16 + MOV temp2, temp2, LSR #16 + ORR temp2, temp2, temp3, LSL #16 + MOV temp3, temp3, LSR #16 + ORR temp3, temp3, temp4, LSL #16 + MOV temp4, temp4, LSR #16 + ORR temp4, temp4, lr, LSL #16 + STMIA dest!, {temp1,temp2,temp3,temp4} ; Store bytes 1-16 + MOV temp1, lr, LSR #16 + LDMIA source!, {temp2,temp3,temp4,lr} + ORR temp1, temp1, temp2, LSL #16 + MOV temp2, temp2, LSR #16 + ORR temp2, temp2, temp3, LSL #16 + MOV temp3, temp3, LSR #16 + ORR temp3, temp3, temp4, LSL #16 + MOV temp4, temp4, LSR #16 + ORR temp4, temp4, lr, LSL #16 + STMIA dest!, {temp1,temp2,temp3,temp4} ; Store bytes 17-32 + SUBS count, count, #32 + MOV temp1, lr, LSR #16 + BGE HWORD32 + +HWORD8_TST + ADDS count, count, #24 + BLT HWORD4 + +HWORD8 ; 11 cycles/8 bytes + LDMIA source!, {temp2,temp3} + ORR temp1, temp1, temp2, LSL #16 + MOV temp2, temp2, LSR #16 + ORR temp2, temp2, temp3, LSL #16 + STMIA dest!, {temp1, temp2} + SUBS count, count, #8 + MOV temp1, temp3, LSR #16 + BGE HWORD8 + +HWORD4 ; 3-7 cycles/4 bytes + ADDS count, count, #4 + BLT HWORD_BYTES + LDR temp2, [source], #4 + ORR temp1, temp1, temp2, LSL #16 + STR temp1, [dest], #4 + MOV temp1, temp2, LSR #16 + +HWORD_BYTES ; 5-11 cycles/1-3 bytes + ADDLTS count, count, #4 + BEQ HWORD_BYTES_EXIT ; On zero, Return to caller + CMP count, #2 + STRLTB temp1, [dest], #1 + LDRGTB temp2, [source], #1 + STRGEH temp1, [dest], #2 + STRGTB temp2, [dest], #1 + +HWORD_BYTES_EXIT + + IF Interworking :LOR: Thumbing + LDMIA sp!, {dest, temp2, temp3, lr} + BX lr + ELSE + LDMIA sp!, {dest, temp2, temp3, pc} + ENDIF + +; +; Unaligned Moves +; +UNALIGNED + TST source, #2 + BEQ UNALIGNED1 + +UNALIGNED3 ; 3-4 cycles + LDRB temp1, [source], #1 + SUBS count, count, #32 + BLT OFFTHREE8_TST + +OFFTHREE32 ; 35 cycles/32 bytes + LDMIA source!, {temp2,temp3,temp4,lr} + ORR temp1, temp1, temp2, LSL #8 + MOV temp2, temp2, LSR #24 + ORR temp2, temp2, temp3, LSL #8 + MOV temp3, temp3, LSR #24 + ORR temp3, temp3, temp4, LSL #8 + MOV temp4, temp4, LSR #24 + ORR temp4, temp4, lr, LSL #8 + STMIA dest!, {temp1,temp2,temp3,temp4} ; Store bytes 1-16 + MOV temp1, lr, LSR #24 + LDMIA source!, {temp2,temp3,temp4,lr} + ORR temp1, temp1, temp2, LSL #8 + MOV temp2, temp2, LSR #24 + ORR temp2, temp2, temp3, LSL #8 + MOV temp3, temp3, LSR #24 + ORR temp3, temp3, temp4, LSL #8 + MOV temp4, temp4, LSR #24 + ORR temp4, temp4, lr, LSL #8 + STMIA dest!, {temp1,temp2,temp3,temp4} ; Store bytes 17-32 + SUBS count, count, #32 + MOV temp1, lr, LSR #24 + BGE OFFTHREE32 + +OFFTHREE8_TST + ADDS count, count, #24 + BLT OFFTHREE4 + +OFFTHREE8 ; 11 cycles/8 bytes + LDMIA source!, {temp2,temp3} + ORR temp1, temp1, temp2, LSL #8 + MOV temp2, temp2, LSR #24 + ORR temp2, temp2, temp3, LSL #8 + STMIA dest!, {temp1, temp2} + SUBS count, count, #8 + MOV temp1, temp3, LSR #24 + BGE OFFTHREE8 + +OFFTHREE4 ; 3-7 cycles/4 bytes + ADDS count, count, #4 + BLT OFFTHREE_BYTES + LDR temp2, [source], #4 + ORR temp1, temp1, temp2, LSL #8 + STR temp1, [dest], #4 + MOV temp1, temp2, LSR #24 + +OFFTHREE_BYTES ; 5-12 cycles/ 1-3 bytes + ADDLTS count, count, #4 + BEQ OFFTHREE_EXIT ; On zero, Return to caller + CMP count, #2 + LDRGEH temp2, [source], #2 + STRB temp1, [dest], #1 + STRGEB temp2, [dest], #1 + MOVGT temp2, temp2, LSR #8 + STRGTB temp2, [dest], #1 + +OFFTHREE_EXIT + + IF Interworking :LOR: Thumbing + LDMIA sp!, {dest, temp2, temp3, lr} + BX lr + ELSE + LDMIA sp!, {dest, temp2, temp3, pc} ; On zero, Return to caller + ENDIF + +; +; Source is one byte from word alignment. +; Read a byte & half word then multiple words and a byte. Then +; shift and ORR them into consecutive words for STM writes +UNALIGNED1 ; 5-6 cycles + LDRB temp1, [source], #1 + LDRH temp2, [source], #2 + SUBS count, count, #32 + ORR temp1, temp1, temp2, LSL #8 + BLT OFFONE8_TST + +OFFONE32 ; 35 cycles/32 bytes + LDMIA source!, {temp2, temp3, temp4, lr} + ORR temp1, temp1, temp2, LSL #24 + MOV temp2, temp2, LSR #8 + ORR temp2, temp2, temp3, LSL #24 + MOV temp3, temp3, LSR #8 + ORR temp3, temp3, temp4, LSL #24 + MOV temp4, temp4, LSR #8 + ORR temp4, temp4, lr, LSL #24 + STMIA dest!, {temp1,temp2,temp3,temp4} ; Store bytes 1-16 + MOV temp1, lr, LSR #8 + LDMIA source!, {temp2,temp3,temp4,lr} + ORR temp1, temp1, temp2, LSL #24 + MOV temp2, temp2, LSR #8 + ORR temp2, temp2, temp3, LSL #24 + MOV temp3, temp3, LSR #8 + ORR temp3, temp3, temp4, LSL #24 + MOV temp4, temp4, LSR #8 + ORR temp4, temp4, lr, LSL #24 + STMIA dest!, {temp1,temp2,temp3,temp4} ; Store bytes 17-32 + SUBS count, count, #32 + MOV temp1, lr, LSR #8 + BGE OFFONE32 + +OFFONE8_TST + ADDS count, count, #24 + BLT OFFONE4 + +OFFONE8 ; 11 cycles/8 bytes + LDMIA source!, {temp2,temp3} + ORR temp1, temp1, temp2, LSL #24 + MOV temp2, temp2, LSR #8 + ORR temp2, temp2, temp3, LSL #24 + STMIA dest!, {temp1,temp2} + SUBS count, count, #8 + MOV temp1, temp3, LSR #8 + BGE OFFONE8 + +OFFONE4 ; 3-9 cycles/4 bytes + ADDS count, count, #4 + BLT OFFONE_BYTES + LDR temp2, [source], #4 + ORR temp1, temp1, temp2, LSL #24 + STR temp1, [dest], #4 + BEQ OFFONE_EXIT + MOV temp1, temp2, LSR #8 + +OFFONE_BYTES ; 11 cycles/1-3 bytes + ADDLTS count, count, #4 + BEQ OFFONE_EXIT + CMP count, #2 + STRLTB temp1, [dest], #1 + STRGEH temp1, [dest], #2 + MOVGT temp1, temp1, LSR #16 + STRGTB temp1, [dest], #1 + +OFFONE_EXIT + + IF Interworking :LOR: Thumbing + LDMIA sp!, {dest, temp2, temp3, lr} + BX lr + ELSE + LDMIA sp!, {dest, temp2, temp3, pc} ; Return to caller + ENDIF + +BYTEMOVE4 ; 12 cycles/4 bytes + CMP count, #4 + BLT MMOVEXIT + LDRB temp1, [source], #1 + SUB count, count, #4 + LDRB temp2, [source], #1 + LDRB temp3, [source], #1 + LDRB lr, [source], #1 + STRB temp1, [dest], #1 + STRB temp2, [dest], #1 + STRB temp3, [dest], #1 + STRB lr, [dest], #1 + +MMOVEXIT ; 2-5 cycles + CMP count, #0 + IF Interworking :LOR: Thumbing + LDMEQIA sp!, {dest, temp2, temp3, lr} + BXEQ lr + ELSE + LDMEQIA sp!, {dest, temp2, temp3, pc} ; On zero, Return to caller + ENDIF + +; +; Store last 3 or so bytes and exit +; +BYTEMOVE ; 4-7 cycles/1 byte + LDRB temp1, [source], #1 + CMP count, #2 + STRB temp1, [dest], #1 + BLT BYTEMOVE_EXIT + LDRGEB temp2, [source], #1 ; 8 cycles/1-2 bytes + LDRGTB temp3, [source], #1 + STRGEB temp2, [dest], #1 + STRGTB temp3, [dest], #1 + +BYTEMOVE_EXIT + + IF Interworking :LOR: Thumbing + LDMIA sp!, {dest, temp2, temp3, lr} + BX lr + ELSE + LDMIA sp!, {dest, temp2, temp3, pc} ; Return to caller + ENDIF + + +; THIS IS NOT A RETURN +; The following reverts the stack to its state at the point of entry +; of memcpy. It then falls through to memcpybigblk to perform the +; large copy +UNDO_PROLOG + ADD sp, sp, #0x10 +; +; FALLTHRU +; + ENTRY_END memcpy + + + NESTED_ENTRY memcpybigblk + + ROUT + + ;//Save registers onto the stack + ;//R3 should be OK to destroy. If not, we stack it off too. + stmfd sp!, {r0,r4-r11, lr} + + PROLOG_END + +prefetch_setup +;//Prefetch the source. +;//Have to align source register with word boundary first + mov r5, r1 + and r5, r5, #~0x3 + +;//The PLD instruction just happens to be a Never Execute on ARM V4, +;//so we can in-line the PLD instruction and still maintain V4 compatibility +;// 0x0000000c: f5d5f000 .... PLD [r5,#0] +;// 0x00000010: f5d5f020 ... PLD [r5,#0x20] +;// 0x00000014: f5d5f040 @... PLD [r5,#0x40] + DCD 0xf5d5f000 + DCD 0xf5d5f020 + DCD 0xf5d5f040 + +;//If there are 4 or less bytes to copy, we just jump to the end +;//and do a straight byte copy. + cmp r2, #4 + bls finish + +;//Align the destination to a word boundary. + rsb r4, r0, #0 ;//Figure out how many bytes + ands r4, r4, #0x2 ;//See if we need to do 2 copies + ldrneb r5, [r1], #1 ;//Read the two bytes + ldrneb r6, [r1], #1 + subne r2, r2, #2 ;//Decrement count by 2 + strneb r5, [r0], #1 ;//Now store the two bytes + strneb r6, [r0], #1 ;//Have to do two seperate byte stores + ;//because of possible address misalignment + + ands r4, r0, #0x1 ;//See if we need to do 1 copy + ldrneb r5, [r1], #1 ;//Load the single byte + subne r2, r2, #1 ;//Decrement count by 1 + strneb r5, [r0], #1 ;//Store the single byte + +;//We need to choose which memcpy we use based +;//on how the source is now aligned. If the destination and source +;//are both aligned, then we fall through to the aligned copy + +;//Check the byte alignment of the source +;//We do it in reverse order just because. If most memcopies are +;//expected to be off by a certain #, that should be placed first. + and r3, r1, #3 + cmp r3, #3 ;//If both bits are set, go do case 3, off by 3 bytes + beq memcpyoffby3 ;//Goto case 3 + cmp r3, #2 ;//Check for case 2, off by 2 bytes + beq memcpyoffby2 ;//Goto case 2 + cmp r3, #1 ;//Check for case 1, off by 1 byte + beq memcpyoffby1 ;//Goto case 1 + +;//The source and destination are word aligned. We get an easy job. +memcpyoffby0 + +;//Now we need to align the destination to a cache line boundary +;//We need to figure out how many words are needed to align it. +;//If the number of words to align it are less than the number of words +;//we're asked to copy, just copy the required number of words. + and r4, r0, #0x1C ;//Grab the low bits of the destination + rsb r4, r4, #32 ;//Negate them and + ;//add 32 to the low bits(this is + ;//how many we need to move to get aligned) + and r5, r2, #0x1C ;//Check only the number of words from count + cmp r4, r2 ;//Compare low bits to align against the words from count + movhi r4, r5 ;//If words to align is greater than the count, then + ;//use the words from count instead + + cmp r4, #0 + beq offby0mainloop + +;//r4 now contains the number of times we need to do a word load/store +;//So we need to sortof back-calculate how many of the word load/stores to +;//skip in memcpyoffby0cachelinealignload/store + rsb r3, r4, #32 + and r3, r3, #0x1C +;//r3 now contains the number of *instructions* to skip over. + +;//Deduct words from size + sub r2, r2, r4 + +;//Because the & 0x1C corresponds to words, we don't have to shift anything +;//when we jump into load table +;//Using two jump tables is faster because it gives the processor a chance to load +;//data before we try to store it out. + adr r12, offby0cachelinealignload + add pc, r12, r3 + +offby0cachelinealignload ;//Need to have up to 8 words (1 cache line) + ldr r4, [r1], #4 ;//Could also do load/store pairs, and shift + ldr r5, [r1], #4 ;//r3 left 1 bit to calculate jump address + ldr r6, [r1], #4 + ldr r7, [r1], #4 + ldr r8, [r1], #4 + ldr r9, [r1], #4 + ldr r10,[r1], #4 + ldr r11,[r1], #4 + +;//Now jump into the store table + adr r12, offby0cachelinealignstore + add pc, r12, r3 + +offby0cachelinealignstore + str r4, [r0], #4 + str r5, [r0], #4 + str r6, [r0], #4 + str r7, [r0], #4 + str r8, [r0], #4 + str r9, [r0], #4 + str r10,[r0], #4 + str r11,[r0], #4 + +;//We are now cache line aligned. +;//We loop around doing prefetches and copies based on how far ahead we want to look + +offby0mainloop + cmp r2, #(32*3 + 32) ;//Only keep looking ahead by 4 cache lines + bmi offby0endofmainloop + +;//Preload the data +;// 0x000000f4: f5d1f060 `... PLD [r1,#0x60] +;// 0x000000f8: f5d1f080 .... PLD [r1,#0x80] + + DCD 0xf5d1f060 + DCD 0xf5d1f080 + +;//Here is the main loop that handles pipelining the loads + + ldmia r1!, {r4-r11} + stmia r0!, {r4-r11} + + ldmia r1!, {r4-r11} + stmia r0!, {r4-r11} + + sub r2, r2, #64 ;//Take 64 bytes off of count + + b offby0mainloop + +offby0endofmainloop +;//If we still have more than 32*4 words to move, do one more preload + cmp r2, #32*4 + bls offby0nopreload +;// 0x0000011c: f5d1f080 .... PLD [r1,#0x80] + DCD 0xf5d1f080 + +offby0nopreload + +;//Now we finish up the copy without any preloads. The data should have already +;//been loaded into the caches +;//Copy 32 bytes at a time +offby0finishcachelines + cmp r2, #32 + bmi offby0endoffinishcachelines + + ldmia r1!, {r4-r11} + stmia r0!, {r4-r11} + + sub r2, r2, #32 ;//Take 32 bytes off of count + b offby0finishcachelines + +offby0endoffinishcachelines + +;//Now we need to finish off any partial cache lines that may be left. We do a similar +;//algorithm to the cachelinealign loop above. + ands r3, r2, #0x1C ;//Get number of words left + beq finish ;//If words left==0, then branch to finish + sub r2, r2, r3 ;//Subtract words left from count + rsb r3, r3, #32 ;//Get 32-number of words left + + adr r12, offby0finishload ;//That's the instructions to skip + add pc, r12, r3 + +offby0finishload ;//Need to have up to 8 words (1 cache line) + ldr r4, [r1], #4 ;//Could also do load/store pairs, and shift + ldr r5, [r1], #4 ;//r3 left 1 bit to calculate jump address + ldr r6, [r1], #4 + ldr r7, [r1], #4 + ldr r8, [r1], #4 + ldr r9, [r1], #4 + ldr r10,[r1], #4 + ldr r11,[r1], #4 + +;//Now jump into the store table + adr r12, offby0finishstore + add pc, r12, r3 + +offby0finishstore + str r4, [r0], #4 + str r5, [r0], #4 + str r6, [r0], #4 + str r7, [r0], #4 + str r8, [r0], #4 + str r9, [r0], #4 + str r10,[r0], #4 + str r11,[r0], #4 + +;//Copy the last 4 bytes, if necessary + rsb r2, r2, #4 ;//Find how many bytes to copy (0, 1,2,3, or 4) + adr r12, finishloadby0 + add pc, r12, r2, LSL #2 ;//Need to shift r2 left by 2 bits to jump instructions + +finishloadby0 + ldrb r3, [r1], #1 + ldrb r4, [r1], #1 + ldrb r5, [r1], #1 + ldrb r6, [r1], #1 + + adr r12, finishstoreby0 + add pc, r12, r2, LSL #2 + +finishstoreby0 + strb r3, [r0], #1 + strb r4, [r0], #1 + strb r5, [r0], #1 + strb r6, [r0], #1 + +;//Return to calling function + IF Interworking :LOR: Thumbing + ldmfd sp!, {r0,r4-r11, lr} + bx lr + ELSE + ldmfd sp!, {r0,r4-r11, pc} + ENDIF + + +;//The source and destination are not aligned. We're going to have +;//to load and shift data from a temporary buffer. Stuff needs to be +;//shifted to the right by 8 bits to align properly +memcpyoffby1 + +;//First we need to word align the source + and r3, r1, #~0x3 +;//Load the first value into the holding buffer (lr) + ldr lr, [r3], #4 + mov lr, lr, LSR #8 + +;//Now we need to align the destination to a cache line boundary +;//We need to figure out how many words are needed to align it. +;//If the number of words to align it are less than the number of words +;//we're asked to copy, just copy the required number of words. + and r4, r0, #0x1C ;//Grab the low bits of the destination + rsb r4, r4, #32 ;//Negate them + ;//Add 32 to the low bits(this is + ;//how many we need to move to get aligned) + and r5, r2, #0x1C ;//Check only the number of words from count + cmp r4, r2 ;//Compare low bits to align against the words from count + movhi r4, r5 ;//If words to align is greater than the count, then + ;//use the words from count instead + + cmp r4, #0 + beq offby1mainloop +;//r4 now contains the number of times we need to do a word load/store +;//So we need to sortof back-calculate how many of the word load/stores to +;//skip in memcpyoffby1cachelinealignload + rsb r6, r4, #32 + and r6, r6, #0x1C +;//r3 now contains the number of *words* to skip over. + +;//Deduct words from size + sub r2, r2, r4 + +;//Because the & 0x1C corresponds to words, we DO need to shift this time around +;//when we jump into load table + adr r12, offby1cachelinealignload + add pc, r12, r6, LSL #2 ;//Allows 4 instructions per byteblit + +;//Because there is no convenient way to split the load/store into multiples of 2 +;//unless we keep them together, for misaligned data we leave them together. +offby1cachelinealignload ;//Need to have up to 8 words (1 cache line) + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #24 + str r12,[r0], #4 + mov lr, r4, LSR #8 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #24 + str r12,[r0], #4 + mov lr, r4, LSR #8 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #24 + str r12,[r0], #4 + mov lr, r4, LSR #8 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #24 + str r12,[r0], #4 + mov lr, r4, LSR #8 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #24 + str r12,[r0], #4 + mov lr, r4, LSR #8 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #24 + str r12,[r0], #4 + mov lr, r4, LSR #8 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #24 + str r12,[r0], #4 + mov lr, r4, LSR #8 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #24 + str r12,[r0], #4 + mov lr, r4, LSR #8 + +;//We are now cache line aligned. +;//We loop around doing prefetches and copies based on how far ahead we want to look +offby1mainloop + cmp r2, #(32*4 + 32) ;//Only keep looking ahead by 4 cache lines + bmi offby1endofmainloop + +;//Preload +;// 0x00000264: f5d3f060 `... PLD [r3,#0x60] +;// 0x00000268: f5d3f080 .... PLD [r3,#0x80] + DCD 0xf5d3f060 + DCD 0xf5d3f080 + +;//Here is the main loop that handles pipelining the loads for off by 1 + ldmia r3!, {r4, r5, r6, r7, r8, r9, r10, r11} + + orr r1,lr, r4, LSL #24 + mov lr, r4, LSR #8 + + orr r4, lr, r5, LSL #24 + mov lr, r5, LSR #8 + + orr r5, lr, r6, LSL #24 + mov lr, r6, LSR #8 + + orr r6, lr, r7, LSL #24 + mov lr, r7, LSR #8 + + orr r7, lr, r8, LSL #24 + mov lr, r8, LSR #8 + + orr r8, lr, r9, LSL #24 + mov lr, r9, LSR #8 + + orr r9, lr, r10, LSL #24 + mov lr, r10, LSR #8 + + orr r10, lr, r11, LSL #24 + mov lr, r11, LSR #8 + + stmia r0!, {r1, r4, r5, r6, r7, r8, r9, r10} + + ldmia r3!, {r4, r5, r6, r7, r8, r9, r10, r11} + + orr r1,lr, r4, LSL #24 + mov lr, r4, LSR #8 + + orr r4, lr, r5, LSL #24 + mov lr, r5, LSR #8 + + orr r5, lr, r6, LSL #24 + mov lr, r6, LSR #8 + + orr r6, lr, r7, LSL #24 + mov lr, r7, LSR #8 + + orr r7, lr, r8, LSL #24 + mov lr, r8, LSR #8 + + orr r8, lr, r9, LSL #24 + mov lr, r9, LSR #8 + + orr r9, lr, r10, LSL #24 + mov lr, r10, LSR #8 + + orr r10, lr, r11, LSL #24 + mov lr, r11, LSR #8 + + stmia r0!, {r1, r4, r5, r6, r7, r8, r9, r10} + + sub r2, r2, #64 ;//Take 64 bytes off of count + + b offby1mainloop + +offby1endofmainloop +;//If we still have more than 32*4 words to move, do one more preload + cmp r2, #32*4 + bls offby1nopreload +;// 0x00000338: f5d3f080 .... PLD [r3,#0x80] + DCD 0xf5d3f080 + +offby1nopreload + +;//Now we finish up the copy without any preloads. The data should have alread +;//been loaded into the caches +;//Copy 32 bytes at a time +offby1finishcachelines + cmp r2, #32 + bmi offby1endoffinishcachelines + + ldmia r3!, {r4, r5, r6, r7, r8, r9, r10, r11} + + orr r1,lr, r4, LSL #24 + mov lr, r4, LSR #8 + + orr r4, lr, r5, LSL #24 + mov lr, r5, LSR #8 + + orr r5, lr, r6, LSL #24 + mov lr, r6, LSR #8 + + orr r6, lr, r7, LSL #24 + mov lr, r7, LSR #8 + + orr r7, lr, r8, LSL #24 + mov lr, r8, LSR #8 + + orr r8, lr, r9, LSL #24 + mov lr, r9, LSR #8 + + orr r9, lr, r10, LSL #24 + mov lr, r10, LSR #8 + + orr r10, lr, r11, LSL #24 + mov lr, r11, LSR #8 + + stmia r0!, {r1, r4, r5, r6, r7, r8, r9, r10} + + sub r2, r2, #32 ;//Take 32 bytes off of count + b offby1finishcachelines + +offby1endoffinishcachelines + +;//Now we need to finish off any partial cache lines that may be left. We do a similar +;//algorithm to the cachelinealign loop above. + ands r6, r2, #0x1C ;//Get number of words left + subeq r1, r3, #3 ;//Realign source on exact byte if need to branch + beq finish ;//If words left==0, then branch to finish + sub r2, r2, r6 ;//Subtract words left from count + rsb r6, r6, #32 ;//Get 32-number of words left + + adr r12, offby1finishload ;//That's the copies to skip + add pc, r12, r6, LSL #2 ;//..but need to multiply by 4 to get instructions + +offby1finishload ;//Need to have up to 8 words (1 cache line) + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #24 + str r12,[r0], #4 + mov lr, r4, LSR #8 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #24 + str r12,[r0], #4 + mov lr, r4, LSR #8 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #24 + str r12,[r0], #4 + mov lr, r4, LSR #8 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #24 + str r12,[r0], #4 + mov lr, r4, LSR #8 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #24 + str r12,[r0], #4 + mov lr, r4, LSR #8 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #24 + str r12,[r0], #4 + mov lr, r4, LSR #8 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #24 + str r12,[r0], #4 + mov lr, r4, LSR #8 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #24 + str r12,[r0], #4 + mov lr, r4, LSR #8 + + sub r1, r3, #3 ;//Realign source on exact byte + +;//Copy the last 4 bytes, if necessary + rsb r2, r2, #4 ;//Find how many bytes to copy (1,2,3, or 4) + adr r12, finishloadby1 + add pc, r12, r2, LSL #2 ;//Need to shift r2 left by 2 bits to jump instructions + +finishloadby1 + ldrb r3, [r1], #1 + ldrb r4, [r1], #1 + ldrb r5, [r1], #1 + ldrb r6, [r1], #1 + + adr r12, finishstoreby1 + add pc, r12, r2, LSL #2 + +finishstoreby1 + strb r3, [r0], #1 + strb r4, [r0], #1 + strb r5, [r0], #1 + strb r6, [r0], #1 + +;//Return to calling function + IF Interworking :LOR: Thumbing + ldmfd sp!, {r0,r4-r11, lr} + bx lr + ELSE + ldmfd sp!, {r0,r4-r11, pc} + ENDIF + +;//The source and destination are not aligned. We're going to have to load +;//and shift data from a temporary buffer. Stuff needs to be shifted to the +;//right by 16 bits to align properly +memcpyoffby2 + +;//First we need to word align the source + and r3, r1, #~0x3 +;//Load the first value into the holding buffer (lr) + ldr lr, [r3], #4 + mov lr, lr, LSR #16 + +;//Now we need to align the destination to a cache line boundary +;//We need to figure out how many words are needed to align it. +;//If the number of words to align it are less than the number of words +;//we're asked to copy, just copy the required number of words. + and r4, r0, #0x1C ;//Grab the low bits of the destination + rsb r4, r4, #32 ;//Negate them + ;//Add 32 to the low bits(this is + ;//how many we need to move to get aligned) + and r5, r2, #0x1C ;//Check only the number of words from count + cmp r4, r2 ;//Compare low bits to align against the words from count + movhi r4, r5 ;//If words to align is greater than the count, then + ;//use the words from count instead + + cmp r4, #0 + beq offby2mainloop + +;//r4 now contains the number of times we need to do a word load/store +;//So we need to sortof back-calculate how many of the word load/stores to +;//skip in memcpyoffby2cachelinealignload + rsb r6, r4, #32 + and r6, r6, #0x1C +;//r3 now contains the number of *words* to skip over. + +;//Deduct words from size + sub r2, r2, r4 + +;//Because the & 0x1C corresponds to words, we DO need to shift this time around +;//when we jump into load table + adr r12, offby2cachelinealignload + add pc, r12, r6, LSL #2 ;//Allows 4 instructions per byteblit + +;//Because there is no convenient way to split the load/store into multiples of 2 +;//unless we keep them together, for misaligned data we leave them together. +offby2cachelinealignload ;//Need to have up to 8 words (1 cache line) + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #16 + str r12,[r0], #4 + mov lr, r4, LSR #16 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #16 + str r12,[r0], #4 + mov lr, r4, LSR #16 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #16 + str r12,[r0], #4 + mov lr, r4, LSR #16 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #16 + str r12,[r0], #4 + mov lr, r4, LSR #16 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #16 + str r12,[r0], #4 + mov lr, r4, LSR #16 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #16 + str r12,[r0], #4 + mov lr, r4, LSR #16 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #16 + str r12,[r0], #4 + mov lr, r4, LSR #16 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #16 + str r12,[r0], #4 + mov lr, r4, LSR #16 + +;//So in theory we should now be cache line aligned. +;//We loop around doing prefetches and copies based on how far ahead we want to look +offby2mainloop + cmp r2, #(32*4 + 32) ;//Only keep looking ahead by 4 cache lines + bmi offby2endofmainloop + +;//Preload +;// 0x00000514: f5d3f060 `... PLD [r3,#0x60] +;// 0x00000518: f5d3f080 .... PLD [r3,#0x80] + DCD 0xf5d3f060 + DCD 0xf5d3f080 + +;//Here is the main loop that handles pipelining the loads for off by 2 + ldmia r3!, {r4, r5, r6, r7, r8, r9, r10, r11} + + orr r1,lr, r4, LSL #16 + mov lr, r4, LSR #16 + + orr r4, lr, r5, LSL #16 + mov lr, r5, LSR #16 + + orr r5, lr, r6, LSL #16 + mov lr, r6, LSR #16 + + orr r6, lr, r7, LSL #16 + mov lr, r7, LSR #16 + + orr r7, lr, r8, LSL #16 + mov lr, r8, LSR #16 + + orr r8, lr, r9, LSL #16 + mov lr, r9, LSR #16 + + orr r9, lr, r10, LSL #16 + mov lr, r10, LSR #16 + + orr r10, lr, r11, LSL #16 + mov lr, r11, LSR #16 + + stmia r0!, {r1, r4, r5, r6, r7, r8, r9, r10} + + ldmia r3!, {r4, r5, r6, r7, r8, r9, r10, r11} + + orr r1,lr, r4, LSL #16 + mov lr, r4, LSR #16 + + orr r4, lr, r5, LSL #16 + mov lr, r5, LSR #16 + + orr r5, lr, r6, LSL #16 + mov lr, r6, LSR #16 + + orr r6, lr, r7, LSL #16 + mov lr, r7, LSR #16 + + orr r7, lr, r8, LSL #16 + mov lr, r8, LSR #16 + + orr r8, lr, r9, LSL #16 + mov lr, r9, LSR #16 + + orr r9, lr, r10, LSL #16 + mov lr, r10, LSR #16 + + orr r10, lr, r11, LSL #16 + mov lr, r11, LSR #16 + + stmia r0!, {r1, r4, r5, r6, r7, r8, r9, r10} + + sub r2, r2, #64 ;//Take 64 bytes off of count + b offby2mainloop + +offby2endofmainloop +;//If we still have more than 32*4 words to move, do one more preload + cmp r2, #32*4 + bls offby2nopreload +;// 0x000005e8: f5d3f080 .... PLD [r3,#0x80] + DCD 0xf5d3f080 + +offby2nopreload + +;//Now we finish up the copy without any preloads. The data should have already +;//been loaded into the caches +;//Copy 32 bytes at a time +offby2finishcachelines + cmp r2, #32 + bmi offby2endoffinishcachelines + + ldmia r3!, {r4, r5, r6, r7, r8, r9, r10, r11} + + orr r1,lr, r4, LSL #16 + mov lr, r4, LSR #16 + + orr r4, lr, r5, LSL #16 + mov lr, r5, LSR #16 + + orr r5, lr, r6, LSL #16 + mov lr, r6, LSR #16 + + orr r6, lr, r7, LSL #16 + mov lr, r7, LSR #16 + + orr r7, lr, r8, LSL #16 + mov lr, r8, LSR #16 + + orr r8, lr, r9, LSL #16 + mov lr, r9, LSR #16 + + orr r9, lr, r10, LSL #16 + mov lr, r10, LSR #16 + + orr r10, lr, r11, LSL #16 + mov lr, r11, LSR #16 + + stmia r0!, {r1, r4, r5, r6, r7, r8, r9, r10} + + sub r2, r2, #32 ;//Take 32 bytes off of count + b offby2finishcachelines + +offby2endoffinishcachelines + +;//Now we need to finish off any partial cache lines that may be left. We do a similar +;//algorithm to the cachelinealign loop above. + ands r6, r2, #0x1C ;//Get number of words left + subeq r1, r3, #2 ;//Realign source on exact byte if need to branch + beq finish ;//If words left==0, then branch to finish + sub r2, r2, r6 ;//Subtract words left from count + rsb r6, r6, #32 ;//Get 32-number of words left + + adr r12, offby2finishload ;//That's the copies to skip + add pc, r12, r6, LSL #2 ;//..but need to multiply by 4 to get instructions + +offby2finishload ;//Need to have up to 8 words (1 cache line) + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #16 + str r12,[r0], #4 + mov lr, r4, LSR #16 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #16 + str r12,[r0], #4 + mov lr, r4, LSR #16 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #16 + str r12,[r0], #4 + mov lr, r4, LSR #16 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #16 + str r12,[r0], #4 + mov lr, r4, LSR #16 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #16 + str r12,[r0], #4 + mov lr, r4, LSR #16 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #16 + str r12,[r0], #4 + mov lr, r4, LSR #16 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #16 + str r12,[r0], #4 + mov lr, r4, LSR #16 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #16 + str r12,[r0], #4 + mov lr, r4, LSR #16 + + sub r1, r3, #2 ;//Realign source on exact byte + +;//Copy the last 4 bytes, if necessary + rsb r2, r2, #4 ;//Find how many bytes to copy (1,2,3, or 4) + adr r12, finishloadby2 + add pc, r12, r2, LSL #2 ;//Need to shift r2 left by 2 bits to jump instructions + +finishloadby2 + ldrb r3, [r1], #1 + ldrb r4, [r1], #1 + ldrb r5, [r1], #1 + ldrb r6, [r1], #1 + + adr r12, finishstoreby2 + add pc, r12, r2, LSL #2 + +finishstoreby2 + strb r3, [r0], #1 + strb r4, [r0], #1 + strb r5, [r0], #1 + strb r6, [r0], #1 + +;//Return to calling function + IF Interworking :LOR: Thumbing + ldmfd sp!, {r0,r4-r11, lr} + bx lr + ELSE + ldmfd sp!, {r0,r4-r11, pc} + ENDIF + +;//The source and destination are not aligned. We're going to have to load +;//and shift data from a temporary buffer. Stuff needs to be shifted to the +;//right by 24 bits to align properly +memcpyoffby3 + +;//First we need to word align the source + and r3, r1, #~0x3 +;//Load the first value into the holding buffer (lr) + ldr lr, [r3], #4 + mov lr, lr, LSR #24 + + +;//Now we need to align the destination to a cache line boundary +;//We need to figure out how many words are needed to align it. +;//If the number of words to align it are less than the number of words +;//we're asked to copy, just copy the required number of words. + and r4, r0, #0x1C ;//Grab the low bits of the destination + rsb r4, r4, #32 ;//Negate them + ;//Add 32 to the low bits(this is + ;//how many we need to move to get aligned) + and r5, r2, #0x1C ;//Check only the number of words from count + cmp r4, r2 ;//Compare low bits to align against the words from count + movhi r4, r5 ;//If words to align is greater than the count, then + ;//use the words from count instead + + cmp r4, #0 + beq offby3mainloop + +;//r4 now contains the number of times we need to do a word load/store +;//So we need to sortof back-calculate how many of the word load/stores to +;//skip in memcpyoffby3cachelinealignload + rsb r6, r4, #32 + and r6, r6, #0x1C +;//r3 now contains the number of *words* to skip over. + +;//Deduct words from size + sub r2, r2, r4 + +;//Because the & 0x1C corresponds to words, we DO need to shift this time around +;//when we jump into load table + adr r12, offby3cachelinealignload + add pc, r12, r6, LSL #2 ;//Allows 4 instructions per byteblit + +;//Because there is no convenient way to split the load/store into multiples of 2 +;//unless we keep them together, for misaligned data we leave them together. +offby3cachelinealignload ;//Need to have up to 8 words (1 cache line) + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #8 + str r12,[r0], #4 + mov lr, r4, LSR #24 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #8 + str r12,[r0], #4 + mov lr, r4, LSR #24 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #8 + str r12,[r0], #4 + mov lr, r4, LSR #24 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #8 + str r12,[r0], #4 + mov lr, r4, LSR #24 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #8 + str r12,[r0], #4 + mov lr, r4, LSR #24 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #8 + str r12,[r0], #4 + mov lr, r4, LSR #24 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #8 + str r12,[r0], #4 + mov lr, r4, LSR #24 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #8 + str r12,[r0], #4 + mov lr, r4, LSR #24 + +;//So in theory we should now be cache line aligned. +;//We loop around doing prefetches and copies based on how far ahead we want to look +offby3mainloop + cmp r2, #(32*4 + 32) ;//Only keep looking ahead by 4 cache lines + bmi offby3endofmainloop + +;//Preload +;// 0x000007c4: f5d3f060 `... PLD [r3,#0x60] +;// 0x000007c8: f5d3f080 .... PLD [r3,#0x80] + DCD 0xf5d3f060 + DCD 0xf5d3f080 + +;//Here is the main loop that handles pipelining the loads for off by 1 + ldmia r3!, {r4, r5, r6, r7, r8, r9, r10, r11} + + orr r1,lr, r4, LSL #8 + mov lr, r4, LSR #24 + + orr r4, lr, r5, LSL #8 + mov lr, r5, LSR #24 + + orr r5, lr, r6, LSL #8 + mov lr, r6, LSR #24 + + orr r6, lr, r7, LSL #8 + mov lr, r7, LSR #24 + + orr r7, lr, r8, LSL #8 + mov lr, r8, LSR #24 + + orr r8, lr, r9, LSL #8 + mov lr, r9, LSR #24 + + orr r9, lr, r10, LSL #8 + mov lr, r10, LSR #24 + + orr r10, lr, r11, LSL #8 + mov lr, r11, LSR #24 + + stmia r0!, {r1, r4, r5, r6, r7, r8, r9, r10} + + ldmia r3!, {r4, r5, r6, r7, r8, r9, r10, r11} + + orr r1,lr, r4, LSL #8 + mov lr, r4, LSR #24 + + orr r4, lr, r5, LSL #8 + mov lr, r5, LSR #24 + + orr r5, lr, r6, LSL #8 + mov lr, r6, LSR #24 + + orr r6, lr, r7, LSL #8 + mov lr, r7, LSR #24 + + orr r7, lr, r8, LSL #8 + mov lr, r8, LSR #24 + + orr r8, lr, r9, LSL #8 + mov lr, r9, LSR #24 + + orr r9, lr, r10, LSL #8 + mov lr, r10, LSR #24 + + orr r10, lr, r11, LSL #8 + mov lr, r11, LSR #24 + + stmia r0!, {r1, r4, r5, r6, r7, r8, r9, r10} + + sub r2, r2, #64 ;//Take 64 bytes off of count + b offby3mainloop + +offby3endofmainloop +;//If we still have more than 32*4 words to move, do one more preload + cmp r2, #32*4 + bls offby3nopreload +;// 0x00000898: f5d3f080 .... PLD [r3,#0x80] + DCD 0xf5d3f080 + +offby3nopreload + +;//Now we finish up the copy without any preloads. The data should have alread +;//been loaded into the caches +;//Copy 32 bytes at a time +offby3finishcachelines + cmp r2, #32 + bmi offby3endoffinishcachelines + + ldmia r3!, {r4, r5, r6, r7, r8, r9, r10, r11} + + orr r1,lr, r4, LSL #8 + mov lr, r4, LSR #24 + + orr r4, lr, r5, LSL #8 + mov lr, r5, LSR #24 + + orr r5, lr, r6, LSL #8 + mov lr, r6, LSR #24 + + orr r6, lr, r7, LSL #8 + mov lr, r7, LSR #24 + + orr r7, lr, r8, LSL #8 + mov lr, r8, LSR #24 + + orr r8, lr, r9, LSL #8 + mov lr, r9, LSR #24 + + orr r9, lr, r10, LSL #8 + mov lr, r10, LSR #24 + + orr r10, lr, r11, LSL #8 + mov lr, r11, LSR #24 + + stmia r0!, {r1, r4, r5, r6, r7, r8, r9, r10} + + sub r2, r2, #32 ;//Take 32 bytes off of count + b offby3finishcachelines + +offby3endoffinishcachelines + +;//Now we need to finish off any partial cache lines that may be left. We do a similar +;//algorithm to the cachelinealign loop above. + ands r6, r2, #0x1C ;//Get number of words left + subeq r1, r3, #1 ;//Realign source on exact byte if need to branch + beq finish ;//If words left==0, then branch to finish + sub r2, r2, r6 ;//Subtract words left from count + rsb r6, r6, #32 ;//Get 32-number of words left + + adr r12, offby3finishload ;//That's the copies to skip + add pc, r12, r6, LSL #2 ;//..but need to multiply by 4 to get instructions + +offby3finishload ;//Need to have up to 8 words (1 cache line) + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #8 + str r12,[r0], #4 + mov lr, r4, LSR #24 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #8 + str r12,[r0], #4 + mov lr, r4, LSR #24 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #8 + str r12,[r0], #4 + mov lr, r4, LSR #24 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #8 + str r12,[r0], #4 + mov lr, r4, LSR #24 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #8 + str r12,[r0], #4 + mov lr, r4, LSR #24 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #8 + str r12,[r0], #4 + mov lr, r4, LSR #24 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #8 + str r12,[r0], #4 + mov lr, r4, LSR #24 + + ldr r4, [r3], #4 + orr r12,lr, r4, LSL #8 + str r12,[r0], #4 + mov lr, r4, LSR #24 + + sub r1, r3, #1 ;//Realign source on exact byte + +;// b finish ;//Not needed, just fall through + +;//Copy the last 4 bytes, if necessary +finish ;//This finish also used in < 4 bytes case + rsb r2, r2, #4 ;//Find how many bytes to copy (1,2,3, or 4) + adr r12, finishloadby3 + add pc, r12, r2, LSL #2 ;//Need to shift r2 left by 2 bits to jump instructions + +finishloadby3 + ldrb r3, [r1], #1 + ldrb r4, [r1], #1 + ldrb r5, [r1], #1 + ldrb r6, [r1], #1 + + adr r12, finishstoreby3 + add pc, r12, r2, LSL #2 + +finishstoreby3 + strb r3, [r0], #1 + strb r4, [r0], #1 + strb r5, [r0], #1 + strb r6, [r0], #1 + +;//Return to calling function + IF Interworking :LOR: Thumbing + ldmfd sp!, {r0,r4-r11, lr} + bx lr + ELSE + ldmfd sp!, {r0,r4-r11, pc} + ENDIF + + + ENTRY_END memcpybigblk + + END + diff --git a/base/Kernel/Native/arm/Crt/memmove.asm b/base/Kernel/Native/arm/Crt/memmove.asm new file mode 100644 index 0000000..7088c75 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/memmove.asm @@ -0,0 +1,801 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +;********************************************************************** +; void * +; memmove( void *dest, const void *src, size_t count ); +; +; memmove() copies 'count' bytes from the source buffer to the +; destination buffer and returns a pointer to the destination +; buffer. memmove() guarantees the overlaping buffers will +; be copied successfully. +; +;********************************************************************** +; +; $$$ NOTE $$$: These routines use the LDRH opcode which is not +; supported on ARM 3 or 3T architectures. Hence, these routines +; assume ARM 4 or later architectures. +; +;********************************************************************** + + OPT 2 ; disable listing + INCLUDE kxarm.inc + OPT 1 ; reenable listing + +dest RN R0 +source RN R1 +count RN R2 +temp1 RN R3 +temp2 RN R4 +temp3 RN R5 +temp4 RN R12 + + IF Thumbing + THUMBAREA + ENDIF + + NESTED_ENTRY memmove + + ROUT + + IF Thumbing + ; Switch from Thumb mode to ARM mode + DCW 0x4778 ; bx pc + DCW 0x46C0 ; nop + ENDIF + + STMDB sp!, {dest,temp2,temp3,lr} ; save registers + + PROLOG_END + + ;if source comes before destination copy from tail to head + CMP source, dest + BGE HEAD_TO_TAIL ; if source < dest + +;********************************************************************** +; Copy from tail to head to avoid source overwrite because the source +; precedes the destination +;********************************************************************** +TAILTOHEAD + ;Move pointers to the tails + ADD source, source, count + ADD dest, dest, count + + CMP count, #8 ;if < 8 bytes, byte moves + BLT TTH_BYTEMOVE4 + + ;check if destination is word aligned, if not then align it + ANDS temp1, dest, #3 ; 2-3 cycles + BEQ TTH_CHKSRC_ALIGN +; +;read 1 to 3 bytes until the destination is word aligned, then +;see if the source is word aligned, if it is then go back to +;word length moves, else continue on with single byte moves +; +TTH_ATTEMPTALIGN + LDRB temp2, [source, #-1]! ; 8 cycles/1-3 bytes + CMP temp1, #2 + STRB temp2, [dest, #-1]! + LDRGEB temp3, [source, #-1]! + SUB count, count, temp1 + LDRGTB temp2, [source, #-1]! + STRGEB temp3, [dest, #-1]! + STRGTB temp2, [dest, #-1]! + + ; Check if source is word aligned, if not check for word + ; alignment. +TTH_CHKSRC_ALIGN + TST source, #1 ; 3-7 cycles + BNE TTH_UNALIGNED ; Unaligned moves + TST source, #2 + BNE TTH_HWORD_ALIGNED ; Half Word aligned + +; +; Word aligned source and destination. +; Move blocks of 32 bytes until we have less than 32 bytes left, +; then divide moves in half down to less than 4 then jump to byte +; moves. +; NOTE: Because of the overhead of pushing registers for 32 byte +; moves it is actually more efficient to use 16 byte moves for +; blocks of less than 128 bytes. +; +TTH_REALIGNED + SUBS count, count, #32 ; 2-3 cycles + BLT TTH_BLK16 + +TTH_BLK32 ; 20 cycles/32 bytes + LDMDB source!, {temp1,temp2,temp3,lr} + STMDB dest!, {temp1,temp2,temp3,lr} + LDMDB source!, {temp1,temp2,temp3,lr} + SUBS count, count, #32 + STMDB dest!, {temp1,temp2,temp3,lr} + BGE TTH_BLK32 + +TTH_BLK16 ; 10 cycles/16 bytes + ADDS count, count, #16 + LDMGEDB source!, {temp1, temp2, temp3, lr} + SUBGE count, count, #16 + STMGEDB dest!, {temp1, temp2, temp3, lr} + +TTH_BLK8 ; 6 cycles / 8 bytes + ADDS count, count, #8 + LDMGEDB source!, {temp1, temp2} + SUBGE count, count, #8 + STMGEDB dest!, {temp1, temp2} + +TTH_BLK4 ; 6-9 cycles/4 bytes + ADDS count, count, #4 + LDRGE temp1, [source, #-4]! + STRGE temp1, [dest, #-4]! + +TTH_WORD_BYTES + ADDLTS count, count, #4 + BEQ TTH_WORD_EXIT + LDRB temp1, [source, #-1]! ; 4-7 cycles/1 byte + CMP count, #2 + STRB temp1, [dest, #-1]! + LDRGEB temp2, [source, #-1]! + LDRGTB temp3, [source, #-1]! ; 8 cycles/1-2 bytes + STRGEB temp2, [dest, #-1]! + STRGTB temp3, [dest, #-1]! + + +TTH_WORD_EXIT + + IF Interworking :LOR: Thumbing + LDMIA sp!, {dest, temp2, temp3, lr} + BX lr + ELSE + LDMIA sp!, {dest, temp2, temp3, pc} + ENDIF + +; +; Source and Destination are half word aligned. +; For blocks < 96 bytes it's actually more efficient to jump to +; the 8 byte copy than take the hit for setup time on 32 byte copy. +; +TTH_HWORD_ALIGNED + LDRH lr, [source, #-2]! ; 4-5 cycles + SUBS count, count, #32 + MOV lr, lr, LSL #16 + BLT TTH_HWORD8_TST + +TTH_HWORD32 ; 35 cycles/32 bytes + LDMDB source!, {temp1,temp2,temp3,temp4} + SUBS count, count, #32 + ORR lr, lr, temp4, LSR #16 + MOV temp4, temp4, LSL #16 + ORR temp4, temp4, temp3, LSR #16 + MOV temp3, temp3, LSL #16 + ORR temp3, temp3, temp2, LSR #16 + MOV temp2, temp2, LSL #16 + ORR temp2, temp2, temp1, LSR #16 + STMDB dest!, {temp3,temp4,lr} ; Store bytes 21-32 + STR temp2, [dest, #-4]! ; Store bytes 17-20 + MOV lr, temp1, LSL #16 + LDR temp4, [source, #-4]! + LDMDB source!, {temp1,temp2,temp3} + ORR lr, lr, temp4, LSR #16 + MOV temp4, temp4, LSL #16 + ORR temp4, temp4, temp3, LSR #16 + MOV temp3, temp3, LSL #16 + ORR temp3, temp3, temp2, LSR #16 + MOV temp2, temp2, LSL #16 + ORR temp2, temp2, temp1, LSR #16 + STMDB dest!, {temp3,temp4,lr} ; Store bytes 5-16 + STR temp2, [dest, #-4]! ; Store bytes 1-4 + MOV lr, temp1, LSL #16 + BGE TTH_HWORD32 + +TTH_HWORD8_TST + ADDS count, count, #24 + BLT TTH_HWORD4 + +TTH_HWORD8 ; 11 cycles/8 bytes + LDMDB source!, {temp2, temp3} + SUBS count, count, #8 + ORR lr, lr, temp3, LSR #16 + MOV temp3, temp3, LSL #16 + ORR temp3, temp3, temp2, LSR #16 + STR lr, [dest, #-4]! + STR temp3, [dest, #-4]! + MOV lr, temp2, LSL #16 + BGE TTH_HWORD8 + +TTH_HWORD4 ; 3-12 cycles/4 bytes + ADDS count, count, #4 + BLT TTH_HWORD_BYTES + LDR temp1, [source, #-4]! + ORR lr, lr, temp1, LSR #16 + STR lr, [dest, #-4]! + MOV lr, temp1, LSL #16 + +TTH_HWORD_BYTES + ADDLTS count, count, #4 + BEQ TTH_HWORD_EXIT + + MOV lr, lr, LSR #16 ; 11 cycles/1-3 bytes + CMP count, #2 + MOVLT lr, lr, LSR #8 + STRLTB lr, [dest, #-1]! + LDRGTB temp1, [source, #-1]! + STRGEH lr, [dest, #-2]! + STRGTB temp1, [dest, #-1]! + +TTH_HWORD_EXIT + + IF Interworking :LOR: Thumbing + LDMIA sp!, {dest, temp2, temp3, lr} + BX lr + ELSE + LDMIA sp!, {dest, temp2, temp3, pc} + ENDIF + +TTH_UNALIGNED + TST source, #2 + BEQ TTH_OFFONE +; +; 3 Byte difference between word and source. +; +TTH_OFFTHREE + LDRB temp3, [source, #-1]! ; 5-6 cycles + LDRH lr, [source, #-2]! + SUBS count, count, #32 + ORR lr, lr, temp3, LSL #16 + MOV lr, lr, LSL #8 + BLT TTH_OFFTHREE8_TST + +TTH_OFFTHREE32 ; 35 cycles/32 bytes + LDMDB source!, {temp1,temp2,temp3,temp4} + SUBS count, count, #32 + ORR lr, lr, temp4, LSR #24 + MOV temp4, temp4, LSL #8 + ORR temp4, temp4, temp3, LSR #24 + MOV temp3, temp3, LSL #8 + ORR temp3, temp3, temp2, LSR #24 + MOV temp2, temp2, LSL #8 + ORR temp2, temp2, temp1, LSR #24 + STMDB dest!, {temp3,temp4,lr} ; Store bytes 21-32 + STR temp2, [dest, #-4]! ; Store bytes 17-20 + MOV lr, temp1, LSL #8 + LDR temp4, [source, #-4]! + LDMDB source!, {temp1,temp2,temp3} + ORR lr, lr, temp4, LSR #24 + MOV temp4, temp4, LSL #8 + ORR temp4, temp4, temp3, LSR #24 + MOV temp3, temp3, LSL #8 + ORR temp3, temp3, temp2, LSR #24 + MOV temp2, temp2, LSL #8 + ORR temp2, temp2, temp1, LSR #24 + STMDB dest!, {temp3,temp4,lr} ; Store bytes 5-16 + STR temp2, [dest, #-4]! ; Store bytes 1-4 + MOV lr, temp1, LSL #8 + BGE TTH_OFFTHREE32 + +TTH_OFFTHREE8_TST + ADDS count, count, #24 + BLT TTH_OFFTHREE4 + +TTH_OFFTHREE8 ; 11 cycles/8 bytes + LDMDB source!, {temp1, temp2} + SUBS count, count, #8 + ORR lr, lr, temp2, LSR #24 + MOV temp2, temp2, LSL #8 + ORR temp2, temp2, temp1, LSR #24 + STR lr, [dest, #-4]! ; Store bytes 5-8 + STR temp2, [dest, #-4]! ; Store bytes 1-4 + MOV lr, temp1, LSL #8 + BGE TTH_OFFTHREE8 + +TTH_OFFTHREE4 ; 3-11 cycles/4 bytes + ADDS count, count, #4 + BLT TTH_OFFTHREE_BYTES + LDR temp3, [source, #-4]! + ORR lr, lr, temp3, LSR #24 + STR lr, [dest, #-4]! + MOV lr, temp3, LSL #8 + +TTH_OFFTHREE_BYTES + ADDLTS count, count, #4 + BEQ TTH_OFFTHREE_EXIT + MOV lr, lr, LSR #8 ; 11 cycles/1-3 bytes + CMP count, #2 + MOVLT temp1, lr, LSR #16 + STRLTB temp1, [dest, #-1]! + MOVGE temp1, lr, LSR #8 + STRGEH temp1, [dest, #-2]! + STRGTB lr, [dest, #-1]! + +TTH_OFFTHREE_EXIT + + IF Interworking :LOR: Thumbing + LDMIA sp!, {dest, temp2, temp3, lr} + BX lr + ELSE + LDMIA sp!, {dest, temp2, temp3, pc} + ENDIF + +; +; One Byte difference between word and source. +; +TTH_OFFONE + LDRB lr, [source, #-1]! + SUBS count, count, #32 ; 2-3 cycles + MOV lr, lr, LSL #24 + BLT TTH_OFFONE8_TST + +TTH_OFFONE32 ; 35 cycles/32 bytes + LDMDB source!, {temp1,temp2,temp3,temp4} + SUBS count, count, #32 ; avoid result delay + ORR lr, lr, temp4, LSR #8 + MOV temp4, temp4, LSL #24 + ORR temp4, temp4, temp3, LSR #8 + MOV temp3, temp3, LSL #24 + ORR temp3, temp3, temp2, LSR #8 + MOV temp2, temp2, LSL #24 + ORR temp2, temp2, temp1, LSR #8 + STMDB dest!, {temp3,temp4,lr} ; Store bytes 21-32 + STR temp2, [dest, #-4]! ; STore bytes 17-20 + MOV lr, temp1, LSL #24 + LDR temp4, [source, #-4]! + LDMDB source!, {temp1,temp2,temp3} + ORR lr, lr, temp4, LSR #8 + MOV temp4, temp4, LSL #24 + ORR temp4, temp4, temp3, LSR #8 + MOV temp3, temp3, LSL #24 + ORR temp3, temp3, temp2, LSR #8 + MOV temp2, temp2, LSL #24 + ORR temp2, temp2, temp1, LSR #8 + STMDB dest!, {temp3,temp4,lr} ; Store bytes 5-16 + STR temp2, [dest, #-4]! ; STore bytes 1-4 + MOV lr, temp1, LSL #24 + BGE TTH_OFFONE32 + +TTH_OFFONE8_TST + ADDS count, count, #24 + BLT TTH_OFFONE4 + +TTH_OFFONE8 ; 11 cycles/8 bytes + LDMDB source!, {temp2, temp3} + SUBS count, count, #8 + ORR lr, lr, temp3, LSR #8 + MOV temp3, temp3, LSL #24 + STR lr, [dest, #-4]! + ORR temp3, temp3, temp2, LSR #8 + STR temp3, [dest, #-4]! + MOV lr, temp2, LSL #24 + BGE TTH_OFFONE8 + +TTH_OFFONE4 ; 8-10 cycles/4 bytes + ADDS count, count, #4 + BLT TTH_OFFONE_BYTES + LDR temp3, [source, #-4]! + ORR lr, lr, temp3, LSR #8 + STR lr, [dest, #-4]! + MOV lr, temp3, LSL #24 + +TTH_OFFONE_BYTES ; 13 cycles/1-3 bytes + ADDLTS count, count, #4 + BEQ TTH_OFFONE_EXIT + MOV lr, lr, LSR #24 + CMP count, #2 + STRB lr, [dest, #-1]! + BLT TTH_OFFONE_EXIT + LDRGEB temp1, [source, #-1]! + LDRGTB temp2, [source, #-1]! + STRGEB temp1, [dest, #-1]! + STRGTB temp2, [dest, #-1]! + +TTH_OFFONE_EXIT + + IF Interworking :LOR: Thumbing + LDMIA sp!, {dest, temp2, temp3, lr} + BX lr + ELSE + LDMIA sp!, {dest, temp2, temp3, pc} + ENDIF + +TTH_BYTEMOVE4 ; 12 cycles/4 bytes + CMP count, #4 + BLT TTH_LAST3 + LDRB temp1, [source, #-1]! + LDRB temp2, [source, #-1]! + LDRB temp3, [source, #-1]! + LDRB lr, [source, #-1]! + SUB count, count, #4 + STRB temp1, [dest, #-1]! + STRB temp2, [dest, #-1]! + STRB temp3, [dest, #-1]! + STRB lr, [dest, #-1]! + +; Move the last 0-3 bytes +TTH_LAST3 + CMP count, #0 ; 2 or 5 cycles + BEQ TTH_BYTEMOVE_EXIT +; +;single byte moves +; +TTH_BYTEMOVE ; 11 cycles/1-3 bytes + LDRB temp1, [source, #-1]! + CMP count, #2 + STRB temp1, [dest, #-1]! + BLT TTH_BYTEMOVE_EXIT + LDRGEB temp2, [source, #-1]! + LDRGTB temp3, [source, #-1]! + STRGEB temp2, [dest, #-1]! + STRGTB temp3, [dest, #-1]! + +TTH_BYTEMOVE_EXIT + + IF Interworking :LOR: Thumbing + LDMIA sp!, {dest, temp2, temp3, lr} + BX lr + ELSE + LDMIA sp!, {dest, temp2, temp3, pc} + ENDIF + +;********************************************************************** +; Copy from head to tail to avoid source overwrite because the source +; destination the source +;********************************************************************** +HEAD_TO_TAIL + ;if LT 8 bytes store them and exit + CMP count, #8 ; 2-3 cycles + BLT BYTEMOVE4 + + ;Check alignment of parameters + ANDS temp1, dest, #3 ; 2-3 cycles + BEQ SRCALIGN + + ; destination is at least 1 byte misaligned + ; Read and write (4 - alignment) bytes to align destination. + RSB temp1, temp1, #4 ; 9 cycles + LDRB temp2, [source], #1 + CMP temp1, #2 + STRB temp2, [dest], #1 + LDRGEB temp3, [source], #1 ; >= 2 == at least 2 bytes + LDRGTB temp2, [source], #1 ; > 2 == 3 bytes unaligned + SUB count, count, temp1 + STRGEB temp3, [dest], #1 + STRGTB temp2, [dest], #1 + +SRCALIGN ; 3 - 7 cycles + TST source, #1 ; save alignment of src + BNE UNALIGNED ; src 3 byte unaligned. + TST source, #2 + BNE HWORDMOVE ; src and dst are hword aligned + +; +;word aligned source and destination, move blocks of 32 bytes +;until we have less than 32 bytes left, then divide moves in +;half down to less than 4, where we will move the last 3 or less +;bytes +; +WORDMOVE + SUBS count, count, #32 ; 2-3 cycles + BLT BLK16 + +BLK32 ; 20 cycles/32 bytes + LDMIA source!, {temp1,temp2,temp3,lr} + STMIA dest!, {temp1,temp2,temp3,lr} + LDMIA source!, {temp1,temp2,temp3,lr} + SUBS count, count, #32 + STMIA dest!, {temp1,temp2,temp3,lr} + BGE BLK32 + +BLK16 ; 11-4 cycles/16 bytes + ADDS count, count, #16 + LDMGEIA source!, {temp1, temp2, temp3, lr} + STMGEIA dest!, {temp1, temp2, temp3, lr} + BEQ WORD_BYTES_EXIT + SUBGTS count, count, #16 + +BLK8 ; 6 cycles/8 bytes + ADDS count, count, #8 + LDMGEIA source!, {temp1, temp2} + SUBGE count, count, #8 + STMGEIA dest!, {temp1, temp2} + +BLK4 + ADDS count, count, #4 ; 6-9 cycles/4 bytes + LDRGE temp1, [source], #4 + STRGE temp1, [dest], #4 + +WORD_BYTES + ADDLTS count, count, #4 + BEQ WORD_BYTES_EXIT ; On zero, Return to caller + + LDR temp1, [source], #4 ; 10 cycles/1-3 bytes + CMP count, #2 + STRGEH temp1, [dest], #2 + STRLTB temp1, [dest], #1 + MOVGT temp1, temp1, LSR #16 + STRGTB temp1, [dest], #1 + +WORD_BYTES_EXIT + + IF Interworking :LOR: Thumbing + LDMIA sp!, {dest, temp2, temp3, lr} + BX lr + ELSE + LDMIA sp!, {dest, temp2, temp3, pc} + ENDIF + +; +; half word align source and destination +; +HWORDMOVE ; 2-3 cycles + LDRH temp1, [source], #2 + SUBS count, count, #32 + BLT HWORD8_TST + +HWORD32 ; 35 cycles/32 bytes + LDMIA source!, {temp2,temp3,temp4,lr} + ORR temp1, temp1, temp2, LSL #16 + MOV temp2, temp2, LSR #16 + ORR temp2, temp2, temp3, LSL #16 + MOV temp3, temp3, LSR #16 + ORR temp3, temp3, temp4, LSL #16 + MOV temp4, temp4, LSR #16 + ORR temp4, temp4, lr, LSL #16 + STMIA dest!, {temp1,temp2,temp3,temp4} ; Store bytes 1-16 + MOV temp1, lr, LSR #16 + LDMIA source!, {temp2,temp3,temp4,lr} + ORR temp1, temp1, temp2, LSL #16 + MOV temp2, temp2, LSR #16 + ORR temp2, temp2, temp3, LSL #16 + MOV temp3, temp3, LSR #16 + ORR temp3, temp3, temp4, LSL #16 + MOV temp4, temp4, LSR #16 + ORR temp4, temp4, lr, LSL #16 + STMIA dest!, {temp1,temp2,temp3,temp4} ; Store bytes 17-32 + SUBS count, count, #32 + MOV temp1, lr, LSR #16 + BGE HWORD32 + +HWORD8_TST + ADDS count, count, #24 + BLT HWORD4 + +HWORD8 ; 11 cycles/8 bytes + LDMIA source!, {temp2,temp3} + ORR temp1, temp1, temp2, LSL #16 + MOV temp2, temp2, LSR #16 + ORR temp2, temp2, temp3, LSL #16 + STMIA dest!, {temp1, temp2} + SUBS count, count, #8 + MOV temp1, temp3, LSR #16 + BGE HWORD8 + +HWORD4 ; 3-7 cycles/4 bytes + ADDS count, count, #4 + BLT HWORD_BYTES + LDR temp2, [source], #4 + ORR temp1, temp1, temp2, LSL #16 + STR temp1, [dest], #4 + MOV temp1, temp2, LSR #16 + +HWORD_BYTES ; 5-11 cycles/1-3 bytes + ADDLTS count, count, #4 + BEQ HWORD_BYTES_EXIT ; On zero, Return to caller + CMP count, #2 + STRLTB temp1, [dest], #1 + LDRGTB temp2, [source], #1 + STRGEH temp1, [dest], #2 + STRGTB temp2, [dest], #1 + +HWORD_BYTES_EXIT + + IF Interworking :LOR: Thumbing + LDMIA sp!, {dest, temp2, temp3, lr} + BX lr + ELSE + LDMIA sp!, {dest, temp2, temp3, pc} + ENDIF + +; +; Unaligned Moves +; +UNALIGNED + TST source, #2 + BEQ UNALIGNED1 + +UNALIGNED3 ; 3-4 cycles + LDRB temp1, [source], #1 + SUBS count, count, #32 + BLT OFFTHREE8_TST + +OFFTHREE32 ; 35 cycles/32 bytes + LDMIA source!, {temp2,temp3,temp4,lr} + ORR temp1, temp1, temp2, LSL #8 + MOV temp2, temp2, LSR #24 + ORR temp2, temp2, temp3, LSL #8 + MOV temp3, temp3, LSR #24 + ORR temp3, temp3, temp4, LSL #8 + MOV temp4, temp4, LSR #24 + ORR temp4, temp4, lr, LSL #8 + STMIA dest!, {temp1,temp2,temp3,temp4} ; Store bytes 1-16 + MOV temp1, lr, LSR #24 + LDMIA source!, {temp2,temp3,temp4,lr} + ORR temp1, temp1, temp2, LSL #8 + MOV temp2, temp2, LSR #24 + ORR temp2, temp2, temp3, LSL #8 + MOV temp3, temp3, LSR #24 + ORR temp3, temp3, temp4, LSL #8 + MOV temp4, temp4, LSR #24 + ORR temp4, temp4, lr, LSL #8 + STMIA dest!, {temp1,temp2,temp3,temp4} ; Store bytes 17-32 + SUBS count, count, #32 + MOV temp1, lr, LSR #24 + BGE OFFTHREE32 + +OFFTHREE8_TST + ADDS count, count, #24 + BLT OFFTHREE4 + +OFFTHREE8 ; 11 cycles/8 bytes + LDMIA source!, {temp2,temp3} + ORR temp1, temp1, temp2, LSL #8 + MOV temp2, temp2, LSR #24 + ORR temp2, temp2, temp3, LSL #8 + STMIA dest!, {temp1, temp2} + SUBS count, count, #8 + MOV temp1, temp3, LSR #24 + BGE OFFTHREE8 + +OFFTHREE4 ; 3-7 cycles/4 bytes + ADDS count, count, #4 + BLT OFFTHREE_BYTES + LDR temp2, [source], #4 + ORR temp1, temp1, temp2, LSL #8 + STR temp1, [dest], #4 + MOV temp1, temp2, LSR #24 + +OFFTHREE_BYTES ; 5-12 cycles/ 1-3 bytes + ADDLTS count, count, #4 + BEQ OFFTHREE_EXIT ; On zero, Return to caller + CMP count, #2 + LDRGEH temp2, [source], #2 + STRB temp1, [dest], #1 + STRGEB temp2, [dest], #1 + MOVGT temp2, temp2, LSR #8 + STRGTB temp2, [dest], #1 + +OFFTHREE_EXIT + + IF Interworking :LOR: Thumbing + LDMIA sp!, {dest, temp2, temp3, lr} + BX lr + ELSE + LDMIA sp!, {dest, temp2, temp3, pc} ; On zero, Return to caller + ENDIF + +; +; Source is one byte from word alignment. +; Read a byte & half word then multiple words and a byte. Then +; shift and ORR them into consecutive words for STM writes +UNALIGNED1 ; 5-6 cycles + LDRB temp1, [source], #1 + LDRH temp2, [source], #2 + SUBS count, count, #32 + ORR temp1, temp1, temp2, LSL #8 + BLT OFFONE8_TST + +OFFONE32 ; 35 cycles/32 bytes + LDMIA source!, {temp2, temp3, temp4, lr} + ORR temp1, temp1, temp2, LSL #24 + MOV temp2, temp2, LSR #8 + ORR temp2, temp2, temp3, LSL #24 + MOV temp3, temp3, LSR #8 + ORR temp3, temp3, temp4, LSL #24 + MOV temp4, temp4, LSR #8 + ORR temp4, temp4, lr, LSL #24 + STMIA dest!, {temp1,temp2,temp3,temp4} ; Store bytes 1-16 + MOV temp1, lr, LSR #8 + LDMIA source!, {temp2,temp3,temp4,lr} + ORR temp1, temp1, temp2, LSL #24 + MOV temp2, temp2, LSR #8 + ORR temp2, temp2, temp3, LSL #24 + MOV temp3, temp3, LSR #8 + ORR temp3, temp3, temp4, LSL #24 + MOV temp4, temp4, LSR #8 + ORR temp4, temp4, lr, LSL #24 + STMIA dest!, {temp1,temp2,temp3,temp4} ; Store bytes 17-32 + SUBS count, count, #32 + MOV temp1, lr, LSR #8 + BGE OFFONE32 + +OFFONE8_TST + ADDS count, count, #24 + BLT OFFONE4 + +OFFONE8 ; 11 cycles/8 bytes + LDMIA source!, {temp2,temp3} + ORR temp1, temp1, temp2, LSL #24 + MOV temp2, temp2, LSR #8 + ORR temp2, temp2, temp3, LSL #24 + STMIA dest!, {temp1,temp2} + SUBS count, count, #8 + MOV temp1, temp3, LSR #8 + BGE OFFONE8 + +OFFONE4 ; 3-9 cycles/4 bytes + ADDS count, count, #4 + BLT OFFONE_BYTES + LDR temp2, [source], #4 + ORR temp1, temp1, temp2, LSL #24 + STR temp1, [dest], #4 + BEQ OFFONE_EXIT + MOV temp1, temp2, LSR #8 + +OFFONE_BYTES ; 11 cycles/1-3 bytes + ADDLTS count, count, #4 + BEQ OFFONE_EXIT + CMP count, #2 + STRLTB temp1, [dest], #1 + STRGEH temp1, [dest], #2 + MOVGT temp1, temp1, LSR #16 + STRGTB temp1, [dest], #1 + +OFFONE_EXIT + + IF Interworking :LOR: Thumbing + LDMIA sp!, {dest, temp2, temp3, lr} + BX lr + ELSE + LDMIA sp!, {dest, temp2, temp3, pc} ; Return to caller + ENDIF + +BYTEMOVE4 ; 12 cycles/4 bytes + CMP count, #4 + BLT MMOVEXIT + LDRB temp1, [source], #1 + SUB count, count, #4 + LDRB temp2, [source], #1 + LDRB temp3, [source], #1 + LDRB lr, [source], #1 + STRB temp1, [dest], #1 + STRB temp2, [dest], #1 + STRB temp3, [dest], #1 + STRB lr, [dest], #1 + +MMOVEXIT ; 2-5 cycles + CMP count, #0 + IF Interworking :LOR: Thumbing + LDMEQIA sp!, {dest, temp2, temp3, lr} + BXEQ lr + ELSE + LDMEQIA sp!, {dest, temp2, temp3, pc} ; On zero, Return to caller + ENDIF + +; +; Store last 3 or so bytes and exit +; +BYTEMOVE ; 4-7 cycles/1 byte + LDRB temp1, [source], #1 + CMP count, #2 + STRB temp1, [dest], #1 + BLT BYTEMOVE_EXIT + LDRGEB temp2, [source], #1 ; 8 cycles/1-2 bytes + LDRGTB temp3, [source], #1 + STRGEB temp2, [dest], #1 + STRGTB temp3, [dest], #1 + +BYTEMOVE_EXIT + + IF Interworking :LOR: Thumbing + LDMIA sp!, {dest, temp2, temp3, lr} + BX lr + ELSE + LDMIA sp!, {dest, temp2, temp3, pc} ; Return to caller + ENDIF + + ENTRY_END memmove + + END diff --git a/base/Kernel/Native/arm/Crt/memset.asm b/base/Kernel/Native/arm/Crt/memset.asm new file mode 100644 index 0000000..ac3e772 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/memset.asm @@ -0,0 +1,98 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; +; void * +; memset( void *dest, int c, size_t count ); +; +; The memset function sets the first count bytes of +; dest to the character c (value). +; + + OPT 2 ; disable listing + INCLUDE kxarm.inc + OPT 1 ; reenable listing + +value RN R1 ; int c +count RN R2 +dest RN R3 +temp RN R12 + + IF Thumbing + THUMBAREA + ENDIF + + NESTED_ENTRY memset + PROLOG_END + + IF Thumbing + ; Switch from Thumb mode to ARM mode + DCW 0x4778 ; bx pc + DCW 0x46C0 ; nop + ENDIF + + SUBS count, count, #4 + MOV dest, R0 ;Save R0 for return + BLT BYTESET + + AND value, value, #&FF + ORR value, value, value, LSL #8 +CHECKALIGN ; 2-3 cycles + ANDS temp, dest, #3 ;Check alignment and fix if possible + BNE ALIGN + +BLOCKSET ; 6-7 cycles + ORR value, value, value, LSL #16 + SUBS count, count, #12 + MOV temp, value + BLT BLKSET8 + +BLKSET16 ; 7 cycles/16 bytes + STMIA dest!, {value, temp} + SUBS count, count, #16 + STMIA dest!, {value, temp} + BGE BLKSET16 + +BLKSET8 ; 4 cycles/8 bytes + ADDS count, count, #8 + STMGEIA dest!, {value, temp} + SUBGE count, count, #8 + +BLKSET4 + ADDS count, count, #4 ; 4 cycles/4 bytes + STRGE value, [dest], #4 + +BYTESET + ADDLTS count, count, #4 + BEQ EXIT + + STRB value, [dest], #1 ; 5 cycles/1-3bytes + CMP count, #2 + STRGEB value, [dest], #1 + STRGTB value, [dest], #1 + +EXIT + + IF Interworking :LOR: Thumbing + BX lr + ELSE + MOV pc, lr + ENDIF + +ALIGN ; 8 cycles/1-3 bytes + TST dest, #1 ;Check byte alignment + SUBNE count, count, #1 + STRNEB value, [dest], #1 + TST dest, #2 ;Check Half-word alignment + SUBNE count, count, #2 + STRNEH value, [dest], #2 + B BLOCKSET + + ENTRY_END + END diff --git a/base/Kernel/Native/arm/Crt/mul.asm b/base/Kernel/Native/arm/Crt/mul.asm new file mode 100644 index 0000000..1a2e522 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/mul.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL mul_s + + GET veneer.asm + + END diff --git a/base/Kernel/Native/arm/Crt/nans.asm b/base/Kernel/Native/arm/Crt/nans.asm new file mode 100644 index 0000000..1b1f4ce --- /dev/null +++ b/base/Kernel/Native/arm/Crt/nans.asm @@ -0,0 +1,214 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; nans.s +; +; Copyright (C) Advanced RISC Machines Limited, 1994. All rights reserved. +; +; RCS Revision: 1 +; Checkin Date: 2007/06/29 02:59:16 +; Revising Author + + GET fpe.asm + + [ :DEF: thumb + CODE32 + ] + + AREA |.text|, CODE, READONLY + + EXPORT __fp_convert_NaNs + EXPORT __fp_convert_NaN1 + EXPORT __fp_convert_NaN_1Of2 + EXPORT __fp_convert_NaN_2Of2 + +__fp_convert_NaNs + +; First check for cases where the first operand determines which routine to +; use - i.e. if it is a signalling NaN or not a NaN at all. + + ORRS Rtmp,OP1mlo,OP1mhi,LSL #1 ;First operand a NaN? + BEQ __fp_convert_NaN_2Of2 ;If not, use ConvertNaN2Of2 + TST OP1mhi,#EIFracTop_bit ;First operand a sign.NaN? + BEQ __fp_convert_NaN_1Of2 ;If so, use ConvertNaN1Of2 + +; First operand is a quiet NaN. If second is a signalling NaN, we use +; ConvertNaN2Of2; otherwise, we use ConvertNaN1Of2. + + ORRS Rtmp,OP2mlo,OP2mhi,LSL #1 ;Second operand a NaN? + BEQ __fp_convert_NaN_1Of2 ;If not, use ConvertNaN1Of2 + TST OP2mhi,#EIFracTop_bit ;Second operand a sign.NaN? + BNE __fp_convert_NaN_1Of2 ;If not, use ConvertNaN1Of2 + +__fp_convert_NaN_2Of2 + + CDebug3 4,"ConvertNaN2Of2: NaN =",OP2sue,OP2mhi,OP2mlo + +; We must check for an invalid operation trap *before* we start using shared +; code, because the operands must be left unchanged for the trap handler. + +;; There is always a trap handler, and it doesn't care about the operands + + TST OP2mhi,#EIFracTop_bit ;Signalling NaN? + BEQ ReturnIVO + +; Now we can transfer the NaN to OP1sue/mhi/mlo and use shared code. + + MOV OP1sue,OP2sue + MOV OP1mhi,OP2mhi + MOV OP1mlo,OP2mlo + B ConvertNaN1_NoTrap + +__fp_convert_NaN1 + +; We must check for an invalid operation trap *before* we start using shared +; code, because the operands must be left unchanged for the trap handler. + + CDebug3 4,"ConvertNaN1: NaN =",OP1sue,OP1mhi,OP1mlo + + TST OP1mhi,#EIFracTop_bit ;Signalling NaN? + BEQ ReturnIVO + B ConvertNaN1_NoTrap ; becomes 0 = InvReas_SigNaN + +__fp_convert_NaN_1Of2 + + CDebug3 4,"ConvertNaN1Of2: NaN =",OP1sue,OP1mhi,OP1mlo + +; We must check for an invalid operation trap *before* we start using shared +; code, because the operands must be left unchanged for the trap handler. + + TST OP1mhi,#EIFracTop_bit ;Signalling NaN? + BEQ ReturnIVO + +ConvertNaN1_NoTrap + +; Now we need to perform the actual NaN conversion. The rules here are: +; +; * The exponent field must be converted to the appropriate value for the +; destination precision; +; +; * Fraction bits that are not used by the destination precision must be +; cleared; +; +; * If result precision is single or double, units bit is forced to 1; +; +; * If the NE bit is 0 and we're converting to extended precision, the +; bottom bit of the fraction must be set or cleared to indicate whether +; the NaN is "really" single or double precision respectively. + +; Split according to result precision. + + TST Rins,#Single_mask + BNE ConvertNaN1_ToSingle + TST Rins,#Double_mask + BNE ConvertNaN1_ToDouble + +;------------------------------------------------------------------------------ + +ConvertNaN1_ToExtended + +; If NE=0, we just need to modify the exponent. Otherwise, we need to +; establish the effective precision and set or clear the bottom bit of the +; fraction appropriately. + + TST Rins,#NE_bit + BNE ConvertNaN1_ToExtended_MantissaDone + AND Rtmp,OP1sue,#ToExp_mask ;Isolate exponent field + MOV Rtmp2,#NaNInfExp_Double:AND:&FF + ORR Rtmp2,Rtmp2,#NaNInfExp_Double:AND:&FF00 + ASSERT NaNInfExp_Double <= &FFFF + CMP Rtmp,Rtmp2 ;LO/EQ/HI if single/double/extended + ASSERT NaNInfExp_Single < NaNInfExp_Double + ASSERT NaNInfExp_Double < NaNInfExp_Extended + BICLO OP1mlo,OP1mlo,#1 ;Bottom bit cleared for single NaN + ORREQ OP1mlo,OP1mlo,#1 ;Bottom bit set for double NaN + ;Bottom bit unchanged for extended NaN + +ConvertNaN1_ToExtended_MantissaDone + +; Change exponent and return. + + AND OP1sue,OP1sue,#Sign_bit+Uncommon_bit + ORR OP1sue,OP1sue,#NaNInfExp_Extended:AND:&FF + ORR OP1sue,OP1sue,#NaNInfExp_Extended:AND:&FF00 + ASSERT NaNInfExp_Extended <= &FFFF + + TEQ OP1sue,OP1sue ;Force EQ condition + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +;------------------------------------------------------------------------------ + +ConvertNaN1_ToDouble + +; Change exponent. + + AND OP1sue,OP1sue,#Sign_bit+Uncommon_bit + ORR OP1sue,OP1sue,#NaNInfExp_Double:AND:&FF + ORR OP1sue,OP1sue,#NaNInfExp_Double:AND:&FF00 + ASSERT NaNInfExp_Double <= &FFFF + +; Clear appropriate fraction bits and set the units bit. Note that we know +; this won't change a NaN into an infinity unless someone is misusing +; extended precision with NE=0. + + MOV OP1mlo,OP1mlo,LSR #11 + MOV OP1mlo,OP1mlo,LSL #11 + ORR OP1mhi,OP1mhi,#EIUnits_bit + + TEQ OP1sue,OP1sue ;Force EQ condition + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR + ENDIF + +;------------------------------------------------------------------------------ + +ConvertNaN1_ToSingle + +; Change exponent. + + AND OP1sue,OP1sue,#Sign_bit+Uncommon_bit + ORR OP1sue,OP1sue,#NaNInfExp_Single:AND:&FF + ORR OP1sue,OP1sue,#NaNInfExp_Single:AND:&FF00 + ASSERT NaNInfExp_Single <= &FFFF + +; Clear appropriate fraction bits and set the units bit. Note that we know +; this won't change a NaN into an infinity unless someone is misusing +; extended precision with the NE bit equal to 0. + + MOV OP1mlo,#0 + BIC OP1mhi,OP1mhi,#&FF + ORR OP1mhi,OP1mhi,#EIUnits_bit + IF Interworking :LOR: Thumbing + BX LR + ELSE + MOV PC,LR ;Note we still have EQ from above + ENDIF + +;============================================================================== + +ReturnIVO + +; Return to the veneer with an IVO exception signalled. + + ORR OP1sue,OP1sue,#IVO_bits + IF Interworking :LOR: Thumbing + BX lr + ELSE + MOV pc, lr + ENDIF + +;============================================================================== + + END diff --git a/base/Kernel/Native/arm/Crt/normal.asm b/base/Kernel/Native/arm/Crt/normal.asm new file mode 100644 index 0000000..02a797b --- /dev/null +++ b/base/Kernel/Native/arm/Crt/normal.asm @@ -0,0 +1,57 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; normal.s +; +; Copyright (C) Advanced RISC Machines Limited, 1994. All rights reserved. +; +; RCS Revision: 1 +; Checkin Date: 2007/06/29 02:59:16 +; Revising Author + +;=========================================================================== +;Veneers onto the arith.s functions. +; +;This block should be assembled multiple times, once for each function. +;The possible functions are: +; +; normalise_s Normalisation functions + + GBLL SinglePrecision + GBLL DoublePrecision + GBLL ExtendPrecision + + GET fpe.asm + +;=========================================================================== + + [ :DEF: thumb + CODE32 + ] + + AREA |.text|, CODE, READONLY + + EXPORT __fp_normalise_op1 + EXPORT __fp_normalise_op2 + EXPORT __fp_normalise_op1neg + EXPORT __fp_norm_denorm_op1 + EXPORT __fp_norm_denorm_op2 + + GBLL normalise_s + +SinglePrecision SETL {FALSE} +DoublePrecision SETL {FALSE} +ExtendPrecision SETL {FALSE} +normalise_s SETL {TRUE} + +;=========================================================================== + + GET arith.asm + + END diff --git a/base/Kernel/Native/arm/Crt/normop1.asm b/base/Kernel/Native/arm/Crt/normop1.asm new file mode 100644 index 0000000..9bbb4e5 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/normop1.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL norm_op1_s + + GET format_e.asm + + END diff --git a/base/Kernel/Native/arm/Crt/normop2.asm b/base/Kernel/Native/arm/Crt/normop2.asm new file mode 100644 index 0000000..2b59e50 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/normop2.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL norm_op2_s + + GET format_e.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_addd.asm b/base/Kernel/Native/arm/Crt/r_addd.asm new file mode 100644 index 0000000..1a5c65f --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_addd.asm @@ -0,0 +1,634 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL add_s + + GET veneer_d.asm + + END + + +;;;; THE BELOW ROUTINE SHOULD WORK, BUT THE ARM ROUTINES SHOULD BE FASTER. + + +; +; Translated to ARM from SH3 FP emulation routines. +; +; __addd Double precision floating point addition. +; Input: +; r0 - Arg1.low +; r1 - Arg1.high +; r2 - Arg2.low +; r3 - Arg2.high +; Output: +; r0 - Result.low +; r1 - Result.high +; +; Note: +; If any FP exceptions are enabled, this routine may raise an exception. +; +; +; IEEE DOUBLE FORMAT +; +; 8 BYTES (LONG WORD * 2) +; 63 62 52 51 0 +; +-+-----------+----------------------------------------------------+ +; |s| e(11) | m(52) | +; +-+-----------+----------------------------------------------------+ +; ^ point +; +; INFINITY NUMBER : e = 2047 m = 0 +; ZERO : e = 0 m = 0 +; NaN : e = 2047 m != 0 +; DENORMAL NUMBER : e = 0 m != 0 +; + + + GET fpe.asm + + Export __addd + Export __subd + + IMPORT FPE_Raise + + AREA |.text|, CODE, READONLY + + +CARRY_CHECK EQU 0x01000000 +MSB EQU 0x00800000 +NORMAL EQU 0x00100000 + + +; Note: the SEH prolog below must match the SEH prolog for __addd. + +__subd + + STMFD sp!, {r0-r10, lr} ; Save off args and non-volatiles and lr + + MOV r8, r1 ; Load parameter1 as R8 R0 + MOV r4, r2 ; Load parameter2 as R2 R4 + MOV r2, r3 ; ... + MOV r5, #_FpSubD ; Double add, assume no exceptions + EOR r2, r2, #0x80000000 + ; Toggle sign bit on parameter2 + B add_in ; Then go add + + +; Note: the SEH prolog below must match the SEH prolog for __subd + +__addd + + STMFD sp!, {r0-r10, lr} ; Save off args and non-volatiles and lr + + MOV r8, r1 ; Load parameter1 as R8 R0 + MOV r4, r2 ; Load parameter2 as R2 R4 + MOV r2, r3 ; ... + MOV r5, #_FpAddD ; Double add, assume no exceptions + + +add_in + +; If abs(parameter1) < abs(parameter2) then swap them so that the resulting +; parameter1 has the larger magnitude. This guarantees that only parameter2 +; might need to be shifted right before adding. Because of denormal numbers, +; it's not sufficient to compare only the exponents; the entire mantissa must +; be checked as well. +; +; if ((abs(parameter1).hi < abs(parameter1).hi) || +; ((abs(parameter1.hi == abs(parameter2)) && +; (parameter1.lo < parameter2.lo))) +; swap parameter1 and parameter2 + + MOV r3, r8, LSL #1 ; Extract copies of just the magnitudes + CMP r3, r2, LSL #1 ; of each parameter + CMPEQ r0, r4 ; if ((abs(param1).hi < abs(param2)).hi + ; || + BHS end_swap ; ((abs(param1).hi == abs(param2).hi) + ; && + ; (param1.lo < param2.lo))) + ; .. +swap + MOV r3,r8 ; Swap parameter1 and parameter2 + MOV r8,r2 ; .. + MOV r2,r3 ; .. + MOV r3,r0 ; .. + MOV r0,r4 ; .. + MOV r4,r3 ; .. +end_swap + +; Unpack parameters. +; +; R8 R0: mantissa1 R2 R4: mantissa2 +; R9: exponent1 R1: exponent2 +; R10: sign1 R6: sign2 +; +; R5: Exception flags + + MOV r9, r8, LSL #1 ; Extract exponent1 + MOV r9, r9, LSR #21 ; ... + MOV r1, r2, LSL #1 ; Extract exponent2 + MOV r1, r1, LSR #21 ; ... + MVN r3, #0 ; Set up to extract mantissas + MOV r10, r8 ; Extract sign1 + MOV r6, r2 ; Extract sign2 + AND r8, r8, r3, LSR #12; Extract mantissa1 + AND r2, r2, r3, LSR #12; Extract mantissa2 + + +; Check for exceptional cases. All NaNs, infinities, and 0's are eliminated. +; Denormal numbers return here after normalizing them. After these checks, +; both parameters are normalized numbers. +; +; After potentially swapping the parameters above, it's sufficient to test +; just parameter1 for non-finite values (NaN, inf) to eliminate non-finite +; values in either parameter. Similarly, it's sufficient to test just +; parameter2 for the unnormalized numbers (exponent2 = 0; denormals and 0). +; +; if (exponent1 == 2047) +; exception1; parameter1 is nonfinite, parameter2 might be too +; if (exponent2 == 0) +; exception2; parameter is 0 or denormal, parameter1 might be too + + ADD r3, r9, #1 ; if (exponent1==2047) + CMP r3, #2048 ; ... + BEQ exception1 ; exception1 + CMP r1, #0 ; if (exponent2==0) + BEQ exception2 ; exception2 +exception_return2 + +; Shift the mantissas left 3 bits to make room for guard, round and sticky bits +; (G,R,S). Then set their hidden bits. + + MOV r8, r8, LSL #3 ; Shift mantissa1 left 3 for (G,R,S) + ORR r8, r8, r0, LSR #29; ... + MOV r0, r0, LSL #3 ; ... + + MOV r2, r2, LSL #3 ; Shift mantissa2 left 3 for (G,R,S) + ORR r2, r2, r4, LSR #29; ... + MOV r4, r4, LSL #3 ; ... + + + ORR r8, r8, #0x00800000 ; Set each mantissa's hidden bit + ORR r2, r2, #0x00800000 ; .. + +; Scale parameter2 so that its exponent matches that of parameter1, preparing +; for the addition. Because of the swap earlier, parameter2 always scales by +; shifting right (if it shifts at all). +; +; shift = exponent1 - exponent2 +; if shift <= -55 +; // entire mantissa2 shifts into the sticky bit; just set S +; else +; if (shift <= -32) +; // "shift" by moving high word to low word +; if (shift != 0) +; // shift by dynamic shifting + +scale + SUBS r1, r9, r1 ; shift = exponent2 - exponent1 + BEQ scale_end ; Shift == 0? + CMP r1, #3 ;**; + BLE scale_le_3 ;**; 0 < shift <= 3? .. + CMP r1, #55 ; If shift <= 55 then + BLE scale_le_55 ; .. + MOV R2, #0 ; Else (mantissa2,G,R,S) = 1 + MOV R4, #1 ; .. + B scale_end + +scale_le_3 ;**; No bits are ever lost + MOV r4, r4, LSR r1 ; mantissa2 >>= x where 0 < x <= 3 + RSB r3, r1, #32 + ORR r4, r4, r2, LSL r3 + MOV r2, r2, LSR r1 + B scale_end + +scale_le_55 ; Else shift <= 55 + CMP r1, #31 ; If shift < 32 + BLE scale_le_31 ; .. + CMP r4, #0 ; Else S = mantissa2.l != 0 + SUB r1, r1, #32 ; (32 fewer bits to shift) + MOV r4, r2 ; Shift 32 bits by moving + MOV r2, #0 ; .. + ORRNE r4, r4, #1 ; Set S if shifted out bits + +scale_le_31 + CMP r1, #0 ; If shift != 0 + BEQ scale_end ; .. + RSB r3, r1, #32 ; Get 32 - shift + MOVS r7, r4, LSL r3 ; Extract low mantissa shifted out (Sticky==NE) + MOV r7, r2, LSL r3 ; Extract high mantissa shifted into lower + MOV r2, r2, LSR r1 ; Shift high mantissa into position + MOV r4, r4, LSR r1 ; Shift low mantissa into position + ORR r4, r4, r7 ; Insert bits from high mantissa into low + ORRNE r4, r4, #1 ; Set sticky if shifted out bits + +scale_end + +; Add the mantissas. +; +; if (sign1 == sign2) +; result = mantissa1 + mantissa2 // Same signs => addition +; Scale result right if it carried +; if (result overflowed) +; return properly signed inf +; else if (mantissa1 == mantissa2) +; return +0 // Equal values => result = +0 +; else +; result = mantissa1 - mantissa2 // Opposite signs => subtraction +; Scale result left // High-order bits were lost + + EORS r7, r10, r6 ; If sign1 != sign2 + BMI mantissa_sub ; do subtract + ADDS r0, r0, r4 ; Else result = mantissa1 + mantissa2 + ADC r8, r8, r2 ; .. + CMP r8, #CARRY_CHECK; If the result carried + BLT end_calc ; .. + MOVS r8, r8, LSR #1 ; Then scale right one + MOVS r0, r0, RRX ; .. + ORRCS r0, r0, #1 ; (fold lost bit into S) + ADD r9, r9, #1 ; Add 1 to exponent for shift + ADD r3, r9, #1 ; Add 1 to exponent for compare + CMP r3, #2048 ; EQ if overflow + BLT end_calc ; .. + ; Overflowed so + ORR r5, r5, #OVF_bit :OR: INX_bit + ; set exception flags + MOV r0, #0 ; and return properly signed inf + MOV r8, #0 ; .. + B return_value ; .. + +; Return +0. + +plus_zero + MOV r8, #0 ; Return +0 + MOV r0, #0 ; .. + B return ; .. + +mantissa_sub + CMP r8, r2 ; Else if mantissa1 = mantissa2 + CMPEQ r0, r4 ; .. + BEQ plus_zero ; return +0 +man_sub1 + SUBS r0, r0, r4 ; Else result = mantissa1 - mantissa2 + SBC r8, r8, r2 ; .. +;**; Parameter1 always has the larger magnitude; result is always its sign. + + +; Normalize since high-order bits are lost when subtracting. Do this in +; chunks. + +normalize + CMP r8, #0 ; If mantissa.h = 0 + BNE norm32_end ; .. + MOV r8, r0 ; mantissa <<= 32 by moving + MOV r0, #0 ; .. + SUB r9, r9, #32 ; exponent -= 32 +norm32_end + MVN r3, #0 ; If (mantissa.h & 0xffff0000) = 0 + TST r8, r3, LSL #16 ; .. + BNE norm16_end ; + MOV r8, r8, LSL #16 ; mantissa <<= 16 + ORR r8, r8, r0, LSR #16 + MOV r0, r0, LSL #16 + SUB r9, r9, #16 ; exponent -= 16 +norm16_end + CMP r8, #CARRY_CHECK ; If mantissa is not too far left + BLO overnorm_end ; keep normalizing, otherwise, undo + +over_norm_loop + MOVS r8, r8, LSR #1 ; mantissa1 >>= 1 + MOV r0, r0, RRX ; .. + ADD r9, r9, #1 ; exponent1++ + CMP r8, #CARRY_CHECK ; If mantissa is still too far left + BHS over_norm_loop ; .. + B end_norm ; Done + +overnorm_end + CMP r8, #MSB ; If mantissa is too far right + BGE end_norm ; .. + +norm_loop + MOVS r0, r0, LSL #1 ; mantissa1 <<= 1 + MOV r8, r8, LSL #1 ; .. + ORRCS r8, r8, #1 ; .. + SUB r9, r9, #1 ; exponent1-- + CMP r8, #MSB ; If mantissa is still too far right + BLT norm_loop ; .. + +end_norm +end_calc + +; Denormalize the result if necessary, with no concern for performance. +; Addition (and thus subtraction) can never generate less significant bits than +; those of the original operands. Thus, denormalization never results in lost +; bits to fold into S. + + CMP r9, #0 ; If exponent < 0 + BGT end_denormal ; .. + RSB r9, r9, #0 ; Then shift right exponent1 places + ADD r9, r9, #1 ; +1 for the non-hidden bit +denormal_loop + MOVS r8, r8, LSR #1 ; .. + MOV r0, r0, RRX ; .. + SUBS r9, r9, #1 ; .. + BNE denormal_loop ; .. +end_denormal + +; Round to nearest. If rounding occurs, set inexact and +; mantissa += G & ( L | R | S ). If the rounding carries, then renormalize. +; +; Addition (and thus subtraction) can never generate less significant bits than +; those of the original operands. Thus, rounding can never meet either of the +; IEEE loss of accuracy tests for underflow. Nor can rounding cause MaxDenorm +; to carry to MinNormal. +; +; Test for inexact. + TST r0, #0x7 ; If G|R|S (=> rounding required) + BEQ end_round ; .. + ORR r5, r5, #INX_bit; result is inexact (can't underflow) + +; Round to nearest. + TST r0, #0x4 ; If G && + BEQ end_round ; .. + TST r0, #0xB ; L|R|S + BEQ end_round ; .. + ADDS r0, r0, #0x8 ; Then round the mantissa up + ADC r8, r8, #0 ; + + CMP r8, #CARRY_CHECK; If the rounding carried + BLT end_round ; (mantissa >= 0x01000000) + ADD r9, r9, #2 ; Then renormalize + CMP r9, #2048 ; If rounding caused overflow + SUB r9, r9, #1 + ORREQ r5, r5, #OVF_bit :OR: INX_bit + ; Report overflow (=> inexact) +end_round + +; Pack the result back into IEEE format. + +return_value + MOV r0, r0, LSR #3 ; Shift mantissa right 3 + ORR r0, r0, r8, LSL #29 ; .. + MOV r1, r8, LSR #3 ; .. + BIC r1, r1, #0x0FF00000 + ; Mask away the hidden bit and possibly one bit + ; higher if round incremented mantissa. + ; 0xFF<<20 is probably overkill, but safe. + ORR r1, r1, r9, LSL #20 + ; Merge exponent and mantissa + AND r10, r10, #0x80000000 + ORR r1, r1, r10 ; Merge sign with exponent and mantissa + +; If any trap enable flags are set corresponding to exception flags set, +; set the corresponding cause bits and cause a trap. +; +; if (exception) +; call handler +; extract the possibly updated result +; return + +return + TST r5, #FPECause_mask ; If any exceptions occurred ... + BEQ done + +;; +;; Register usage: +;; r0 - Default result.low +;; r1 - Default result.high +;; r5 - Exception information +;; +;; Stack: +;; 0x10(sp) - up: Saved registers +;; 0xC(sp): Original Arg2.high +;; 0x8(sp): Original Arg2.low +;; 0x4(sp): Original Arg1.high +;; 0x0(sp): Original Arg1.low +;; + LDR r2, [sp, #0x8] ; Load original Arg2.low + LDR r3, [sp, #0xC] ; Load original Arg2.high + SUB sp, sp, #0x8 ; Make room for exception information + STR r2, [sp, #0x0] ; Store original Arg2.low + STR r3, [sp, #0x4] ; Store original Arg2.high + LDR r3, [sp, #0x8] ; Load original Arg1.low + LDR r2, [sp, #0xC] ; Load original Arg1.high + STR r0, [sp, #0x8] ; Store default result.low + STR r1, [sp, #0xC] ; Store default result.high + MOV r1, r5 ; Move exception information + ADD r0, sp, #0x10 ; Pointer for return value + +;; Register Usage: +;; r0 - Address for return value = 0x10(sp) +;; r1 - Exception information +;; r2 - Original arg1.low +;; r3 - Original arg1.high +;; +;; Stack Usage: +;; 0x14(sp): Return result.high +;; 0x10(sp): Return result.low +;; 0xC(sp): Default result.high +;; 0x8(sp): Default result.low +;; 0x4(sp): Original arg2.high +;; 0x0(sp): Original arg2.low + + CALL FPE_Raise ; Deal with exception information + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #0x10] ; Load up returned result + LDR r1, [sp, #0x14] ; ... + ADD sp, sp, #0x8 ; Restore extra arg passing space + + +done + ADD sp, sp, #0x10 ; Pop off original args + IF Interworking :LOR: Thumbing + LDMIA sp!, {r4-r10, lr} + BX lr + ELSE + LDMIA sp!, {r4-r10, pc} + ENDIF + ; Restore off non-volatiles and return + + + +;%%%%%%%%%%%%%%%%%%%%%%%%% +;% Exceptional process % +;%%%%%%%%%%%%%%%%%%%%%%%%% + +; Exception 1: parameter1 is non-finite (exponent1 == 2047). The mantissa has +; not been shifted left for the guard bits yet. The choice of ARM SNaN +; versus QNaN (mantissa<51> = 1 => QNaN) means that abs() > +; abs() > abs(). +; +; exception1: +; if (mantissa1 == 0) +; CheckArg2INF(); // Arg1 is an INF. Must check Arg2 for INF. +; else if (mantissa1[MSb] == 0) +; SignalInvalid(); // Arg1 is an SNaN so signal invalid and return it. +; else +; CheckArg2SNaN(); // Arg1 is a QNaN. Check Arg2 for SNaN. +; +; CheckArg2SNaN: +; if (exponent2 == 2047 && +; mantissa2 != 0 && +; mantissa2[MSb] == 0) +; SignalInvalid(); +; else +; ReturnQNaN(); +; +; CheckArg2INF: +; if (exponent2 == 2047 && +; mantissa2 == 0) +; if (sign1 ^ sign2) +; SignalInvalid(); // Arg1 and Arg2 are opposite INFs. +; else +; ReturnINF(); // Arg1 and Arg2 are same signed INFs. +; else +; ReturnINF(); // Arg1 is INF. Arg2 is not. +; +; SignalInvalid: +; cause |= INVALID_OPERATION; +; ReturnQNaN(); +; +; ReturnQNaN: +; exponent1 = 2047; +; mantissa1[MSb] = 1; +; return(); +; +; ReturnINF +; exponent1 = 2047; +; mantissa1 = 0; +; return(); +; +exception1 + ORRS r3, r8, r0 ; if (mantissa1 == 0) + BEQ CheckArg2INF ; CheckArg2INF + TST r8, #dSignalBit ; else if (mantissa1[MSb] == 0) + BEQ SignalInvalid ; SignalInvalid + ; else + ; CheckArg2SNaN +CheckArg2SNaN + ADD r3, r1, #1 ; if (exponent2 == 2047 && + CMP r3, #2048 ; .. + BNE ReturnQNaN ; .. + ORRS r3, r2, r4 ; mantissa2 != 0 && + BEQ ReturnQNaN ; .. + TST r2, #dSignalBit ; mantissa2[MSb] == 0) + BEQ SignalInvalid ; SignalInvalid + B ReturnQNaN ; else + ; ReturnQNaN +CheckArg2INF + ADD r3, r1, #1 ; if (exponent2 == 2047 && + CMP r3, #2048 ; .. + BNE ReturnINF ; .. + ORRS r3, r2, r4 ; mantissa2 == 0 && + BNE ReturnINF ; .. + EORS r3, r10, r6 ; if (sign1 ^ sign2) + BMI SignalInvalid ; SignalInvalid + ; else + ; ReturnINF + +ReturnINF + AND r1, r10, #0x80000000 ; Get sign bit + ORR r1, r1, r9, LSL #20 ; Insert exponent (exponent == 2047) + B return ; r0 is already 0 so just return + +SignalInvalid + ORR r5, r5, #IVO_bit ; Set invalid operation + +ReturnQNaN + AND r1, r10, #0x80000000 ; Get sign bit + ORR r1, r1, r9, LSL #20 ; Insert exponent (exponent == 2047) + ORR r1, r1, #dSignalBit ; Insert mantissa high bit to ensure QNaN + ORR r1, r1, r8 ; OR in rest of high mantissa bits + B return ; r0 already has the low mantissa bits so + ; just return + + +; Exception 2: parameter1 is finite, parameter2 is not normal (0 or denormal). +; +; if (exponent1 == 0) // parameter1 is not normal +; if (mantissa1 == 0) // parameter1 is 0 +; return properly signed 0 +; else if (mantissa2 == 0) // denormal+denormal +; go normalize both and add +; else // denormal+0 +; return parameter1 +; else if (mantissa != 0) // parameter2 is denormal +; go normalize parameter2 and add +; else // parameter2 is 0 +; return parameter1 + +exception2 + CMP r9, #0 ; if parameter1 is not normal + BNE p1_normal ; .. + ORRS r7, r8, r0 ; if parameter1 is 0 + BNE p1_denormal ; .. +;*** Rounding mode: proper sign is a function of the rounding mode. + AND r10, r10, r6 ; return properly signed 0 + B return_value ; + +p1_denormal + ORRS r7, r2, r4 ; else if parameter2 is denormal + BNE p1_normalize ; go normalize both and add + B return_p1 ; else parameter2 is 0 + ; return parameter1 +p1_normal ; (parameter2 is denormal or 0) + ORRS r7, r2, r4 ; else if parameter2 is denormal + BNE p2_normalize ; go normalize parameter2 and add +return_p1 ; else parameter2 is 0 + MOV r8, r8, LSL #3 ; return parameter1 + ORR r8, r8, r0, LSR #29 + ; .. + MOV r0, r0, LSL #3 ; .. + B return_value ; .. + +; Both parameter1 and parameter2 are denormal. Normalize both then go add. + +p1_normalize ; Stop when we shift into 1.0 bit + MOVS r0, r0, LSL #1 ; Account for the hidden mantissa bit + MOV r8, r8, LSL #1 ; that denormals don't have + ORRCS r8, r8, #1 ; .. + CMP r8, #NORMAL ; While mantissa1 < 1.0 + BGE end_p1_norm ; .. +p1_norm_loop + MOVS r0, r0, LSL #1 ; Scale mantissa1 up by 1 place + MOV r8, r8, LSL #1 ; .. + ORRCS r8, r8, #1 ; .. + SUB r9, r9, #1 ; and exponent1 down by 1 + CMP r8, #NORMAL ; .. + BLT p1_norm_loop ; .. +end_p1_norm + +; parameter1 is (now) normalized, parameter2 is denormal. Normalize +; parameter2 then go add. + +p2_normalize ; Stop when we shift into 1.0 bit + MOVS r4, r4, LSL #1 ; Account for the hidden mantissa bit + MOV r2, r2, LSL #1 ; that denormals don't have + ORRCS r2, r2, #1 ; .. + CMP r2, #NORMAL ; While mantissa2 < 1.0 + BGE end_p2_norm ; .. +p2_norm_loop + MOVS r4, r4, LSL #1 ; Scale mantissa2 up by 1 place + MOV r2, r2, LSL #1 ; .. + ORRCS r2, r2, #1 ; .. + SUB r1, r1, #1 ; and exponent2 down by 1 + CMP r2, #NORMAL ; .. + BLT p2_norm_loop ; .. +end_p2_norm + B exception_return2 + ; Done + + END diff --git a/base/Kernel/Native/arm/Crt/r_adds.asm b/base/Kernel/Native/arm/Crt/r_adds.asm new file mode 100644 index 0000000..c8f2f48 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_adds.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL add_s + + GET veneer_f.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_divd.asm b/base/Kernel/Native/arm/Crt/r_divd.asm new file mode 100644 index 0000000..d668e96 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_divd.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL div_s + + GET veneer_d.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_divs.asm b/base/Kernel/Native/arm/Crt/r_divs.asm new file mode 100644 index 0000000..43215fd --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_divs.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL div_s + + GET veneer_f.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_dtoi.asm b/base/Kernel/Native/arm/Crt/r_dtoi.asm new file mode 100644 index 0000000..4d0aed0 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_dtoi.asm @@ -0,0 +1,172 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; __dtoi double precision floating point to signed integer convert +; +; Input: r1 - Most significant word of the double to be converted +; r0 - Least significant word of the double to be converted +; +; Output: r1 - Converted double in signed integer format +; + +; Local storage size and offsets +LOC_SIZE EQU 0x18 +OrgOp1h EQU 0x14 +OrgOp1l EQU 0x10 +ExDResl EQU 0x08 +NewResl EQU 0x10 + + + GET fpe.asm + GET kxarm.inc + + AREA |.text|, CODE, READONLY + + Export __dtoi + IMPORT FPE_Raise + + NESTED_ENTRY __dtoi + EnterWithLR_16 + STMFD sp!, {lr} ; Save return address + SUB sp, sp, #LOC_SIZE ; Allocate stack space + PROLOG_END + STR r1, [sp, #OrgOp1h] ; Save off original args in case of exception + ORRS r12, r0, r1, LSL #1 ; Check for zero + STR r0, [sp, #OrgOp1l] ; Save off original args in case of exception + ADDEQ sp, sp, #LOC_SIZE ; Restore stack and return + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; if zero (r0 already zero) + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; if zero (r0 already zero) + ENDIF + + MOVS r2, r1, LSL #1 ; Right justify exponent, save sign bit in C + MOV r1, r1, LSL #11 ; Left justify mantissa + ORR r1, r1, r0, LSR #21 ; .. + ; .. + MOV r0, r0, LSL #11 ; .. + ORR r1, r1, #1 << 31 ; Insert hidden one (even for denorms) + + BCS _ffix_negative ; If negative input, separate case + + MOV r3, #DExp_bias+1 ; r3 = 31 + DExp_bias + ADD r3, r3, #30 ; .. + SUBS r2, r3, r2, LSR #21 ; Determine shift amount + BLE shift_left ; Negative shift is a shift left, NaN, + ; or INF; zero shift is an overflow + CMP r2, #32 ; See if shifting all bits out + BGE shift_right_32 ; If shifting all bits out, return zero + +shift_right + RSB r3, r2, #32 ; Check for inexact + ORRS r12, r0, r1, LSL r3 ; .. + MOV r0, r1, LSR r2 ; Shift the result + MOVNE r3, #INX_bit ; Set inexact if inexact + MOVEQ r3, #0 ; Otherwise set to no exceptions + B __dtoi_return ; Return + +shift_left + RSB r2, r2, #0 ; Get left shift amount + CMP r2, #32 ; If >= 32, shift by moving words, and + MOVGE r1, r0 ; adjusting shift amount + MOVGE r0, #0 ; .. + SUBGE r2, r2, #32 ; .. + MOV r1, r1, LSL r2 ; Perform rest of shift + RSB r2, r2, #32 ; .. + ORR r0, r1, r0, LSR r2 ; .. + MOV r3, #IVO_bit ; Overflow so set invalid operation + B __dtoi_return ; Return + +shift_right_32 ; 0.0 < abs(Arg) < 1.0, so losing all bits + BIC r3, r3, #IVO_bit ; Ensure invalid is clear + MOV r3, #INX_bit ; Set inexact + MOV r0, #0 ; Return zero + B __dtoi_return ; Return + +_ffix_negative + MOV r3, #DExp_bias+1 ; r3 = 31 + DExp_bias + ADD r3, r3, #30 ; .. + SUBS r2, r3, r2, LSR #21 ; Determine shift amount + BLT shift_left_neg ; Negative shift is a shift left, NaN, + ; or INF; zero shift is an overflow + BEQ special_int_min ; Special case of INT_MIN + CMP r2, #32 ; See if shifting all bits out + BGE shift_right_32 ; If shifting all bits out, return zero + +shift_right_neg + RSB r3, r2, #32 ; Check for inexact + ORRS r12, r0, r1, LSL r3 ; .. + MOV r0, r1, LSR r2 ; Shift the result + RSB r0, r0, #0 ; Negate result + MOVNE r3, #INX_bit ; Set inexact if inexact + MOVEQ r3, #0 ; Otherwise set to no exceptions + B __dtoi_return ; Return + +shift_left_neg + RSB r2, r2, #0 ; Get left shift amount + CMP r2, #32 ; If >= 32, shift by moving words, and + MOVGE r1, r0 ; adjusting shift amount + MOVGE r0, #0 ; .. + SUBGE r2, r2, #32 ; .. + MOV r1, r1, LSL r2 ; Perform rest of shift + RSB r2, r2, #32 ; .. + ORR r0, r1, r0, LSR r2 ; .. + RSB r0, r0, #0 ; Negate result + MOV r3, #IVO_bit ; Have a negative so invalid + B __dtoi_return ; Return + +special_int_min + TEQ r1, #0x80000000 ; Result == INT_MIN + MOVNE r3, #IVO_bit ; If not, overflow so raise invalid, + RSBNE r0, r1, #0 ; negate and move result into place, + BNE __dtoi_return ; and return + CMP r0, #0 ; If is, okay, check for inexact + MOVNE r3, #INX_bit ; If bits lost in low word, inexact + MOVEQ r3, #0 ; Otherwise, no exceptions + MOV r0, r1 ; Move result into place + + + +__dtoi_return + TST r3, #FPECause_mask ; Any exceptions? + ADDEQ sp, sp, #LOC_SIZE ; If not, pop off saved arg and + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; return + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; return + ENDIF + ORR r1, r3, #_FpDToI ; Exception information + LDR r3, [sp, #OrgOp1h] ; Original operand 1 + LDR r2, [sp, #OrgOp1l] ; .. + STR r0, [sp, #ExDResl] ; Store default result + ADD r0, sp, #NewResl ; Pointer to new result + + CALL FPE_Raise ; Handle exception information + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #NewResl] ; Load new result + ADD sp, sp, #LOC_SIZE ; Pop off exception record and result + IF Interworking :LOR: Thumbing + LDMFD sp!, {lr} ; Return + BX lr + ELSE + LDMFD sp!, {pc} ; Return + ENDIF + + ENTRY_END __dtoi + + END diff --git a/base/Kernel/Native/arm/Crt/r_dtoi64.asm b/base/Kernel/Native/arm/Crt/r_dtoi64.asm new file mode 100644 index 0000000..4ad44aa --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_dtoi64.asm @@ -0,0 +1,207 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; __dtoi64 double precision floating point to signed 64-bit integer convert +; +; Input: r1 - Most significant word of the double to be converted +; r0 - Least significant word of the double to be converted +; +; Output: r1 - Most significant word of the converted double in signed +; 64-bit integer format +; r0 - Least significant word of the converted double in signed +; 64-bit integer format +; + +; Local storage size and offsets +LOC_SIZE EQU 0x18 +OrgOp1h EQU 0x14 +OrgOp1l EQU 0x10 +ExDResh EQU 0x0C +ExDResl EQU 0x08 +NewResh EQU 0x14 +NewResl EQU 0x10 + + + GET fpe.asm + GET kxarm.inc + + AREA |.text|, CODE, READONLY + + Export __dtoi64 + IMPORT FPE_Raise + + NESTED_ENTRY __dtoi64 + EnterWithLR_16 + STMFD sp!, {lr} ; Save return address + SUB sp, sp, #LOC_SIZE ; Allocate local storage + PROLOG_END + + STR r1, [sp, #OrgOp1h] ; Save off original args in case of exception + ORRS r12, r0, r1, LSL #1 ; Check for zero + STR r0, [sp, #OrgOp1l] ; .. + MOVEQ r1, r0 ; return if zero (r0 already zero) + ADDEQ sp, sp, #LOC_SIZE ; restore stack + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; .. + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; .. + ENDIF + + + MOVS r2, r1, LSL #1 ; Right justify exponent, save sign bit in C + MOV r1, r1, LSL #11 ; Left justify mantissa + ORR r1, r1, r0, LSR #21 ; .. + ; .. + MOV r0, r0, LSL #11 ; .. + ORR r1, r1, #1 << 31 ; Insert hidden one (even for denorms) + + BCS _ffix_negative ; If negative input, separate case + + MOV r3, #DExp_bias+1 ; r3 = 63 + DExp_bias + ADD r3, r3, #62 ; .. + SUBS r2, r3, r2, LSR #21 ; Determine shift amount + BLE shift_left ; Negative shift is a shift left, NaN, + ; or INF + CMP r2, #64 ; See if shifting all bits out + BGE shift_right_64 ; If shifting all bits out, return zero + +shift_right + MOV r12, #0 ; Need to clear r12 for inexact check + CMP r2, #32 ; See if shift amount >= 32 + BLT shift_right_31 ; If not, shift right 31 or less + MOV r12, r0 ; If >= 32, save lost bits in temp reg, + MOV r0, r1 ; shift by moving words, and + MOV r1, #0 ; adjust the shift amount + SUB r2, r2, #32 ; .. + +shift_right_31 + RSB r3, r2, #32 ; Check for inexact + ORRS r12, r12, r0, LSL r3 ; .. + MOV r0, r0, LSR r2 ; Shift the result + ORR r0, r0, r1, LSL r3 ; .. + MOV r1, r1, LSR r2 ; .. + MOVNE r3, #INX_bit ; Set inexact if inexact + MOVEQ r3, #0 ; Otherwise set to no exceptions + B __dtoi64_return ; Return + +shift_left + RSB r2, r2, #0 ; Get left shift amount + CMP r2, #32 ; If >= 32, shift by moving words, and + MOVGE r1, r0 ; adjusting shift amount + MOVGE r0, #0 ; .. + SUBGE r2, r2, #32 ; .. + MOV r1, r1, LSL r2 ; Perform rest of shift + RSB r3, r2, #32 ; .. + ORR r1, r1, r0, LSR r3 ; .. + MOV r0, r0, LSL r2 ; .. + MOV r3, #IVO_bit ; Overflow so set invalid operation + B __dtoi64_return ; Return + +shift_right_64 ; 0.0 < abs(Arg) < 1.0, so losing all bits + MOV r3, #INX_bit ; Set inexact + MOV r0, #0 ; Return zero + MOV r1, #0 ; .. + B __dtoi64_return ; Return + +_ffix_negative + MOV r3, #DExp_bias+1 ; r3 = 63 + DExp_bias + ADD r3, r3, #62 ; .. + SUBS r2, r3, r2, LSR #21 ; Determine shift amount + BLT shift_left_neg ; Negative shift is a shift left, NaN, + ; or INF + BEQ special_int64_min ; Special case of 0x8000000000000000 + CMP r2, #64 ; See if shifting all bits out + BGE shift_right_64 ; If shifting all bits out, return zero + +shift_right_neg + MOV r12, #0 ; Need to clear r12 for inexact check + CMP r2, #32 ; See if shift amount >= 32 + BLT shift_right_31_neg ; If not, shift right 31 or less + MOV r12, r0 ; If >= 32, save lost bits in temp reg, + MOV r0, r1 ; shift by moving words, and + MOV r1, #0 ; adjust the shift amount + SUB r2, r2, #32 ; .. + +shift_right_31_neg + RSB r3, r2, #32 ; Check for inexact + ORRS r12, r12, r0, LSL r3 ; .. + MOV r0, r0, LSR r2 ; Shift the result + ORR r0, r0, r1, LSL r3 ; .. + MOV r1, r1, LSR r2 ; .. + MOVNE r3, #INX_bit ; Set inexact if inexact + MOVEQ r3, #0 ; Otherwise set to no exceptions + RSBS r0, r0, #0 ; Negate result + RSC r1, r1, #0 ; .. + B __dtoi64_return ; Return + + +shift_left_neg + RSB r2, r2, #0 ; Get left shift amount + CMP r2, #32 ; If >= 32, shift by moving words, and + MOVGE r1, r0 ; adjusting shift amount + MOVGE r0, #0 ; .. + SUBGE r2, r2, #32 ; .. + MOV r1, r1, LSL r2 ; Perform rest of shift + RSB r3, r2, #32 ; .. + ORR r1, r1, r0, LSR r3 ; .. + MOV r0, r0, LSL r2 ; .. + RSBS r0, r0, #0 ; Negate result + RSC r1, r1, #0 ; .. + MOV r3, #IVO_bit ; Overflow so set invalid operation + B __dtoi64_return ; Return + + +special_int64_min + TEQ r1, #0x80000000 ; If shift amount zero and result is + TEQEQ r0, #0 ; 0x8000000000000000, just leave it + MOVEQ r3, #0 ; .. + MOVNE r3, #IVO_bit ; Otherwise it's invalid (overflow) + RSBS r0, r0, #0 ; Negate result + RSC r1, r1, #0 ; .. + +__dtoi64_return + TST r3, #FPECause_mask ; Any exceptions? + ADDEQ sp, sp, #LOC_SIZE ; If not, pop off saved arg and + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; return + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; return + ENDIF + ORR r12, r3, #_FpDToI64 ; Save exception info + LDR r3, [sp, #OrgOp1h] ; Load original arg + LDR r2, [sp, #OrgOp1l] ; .. + STR r0, [sp, #ExDResl] ; Store default result + STR r1, [sp, #ExDResh] ; .. + MOV r1, r12 ; Exception information + ADD r0, sp, #NewResl ; Pointer to new result + + CALL FPE_Raise ; Handle exception information + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #NewResl] ; Load new result + LDR r1, [sp, #NewResh] ; .. + ADD sp, sp, #LOC_SIZE ; Pop off exception record and result + IF Interworking :LOR: Thumbing + LDMFD sp!, {lr} ; Return + BX lr + ELSE + LDMFD sp!, {pc} ; Return + ENDIF + + ENTRY_END __dtoi64 + + END diff --git a/base/Kernel/Native/arm/Crt/r_dtos.asm b/base/Kernel/Native/arm/Crt/r_dtos.asm new file mode 100644 index 0000000..3b896e5 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_dtos.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL d2f_s + + GET format.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_dtou.asm b/base/Kernel/Native/arm/Crt/r_dtou.asm new file mode 100644 index 0000000..f7a50e3 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_dtou.asm @@ -0,0 +1,156 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; __dtou double precision floating point to unsigned integer convert +; +; Input: r1 - Most significant word of the double to be converted +; r0 - Least significant word of the double to be converted +; +; Output: r0 - converted float in unsigned integer format +; + +; Local storage size and offsets +LOC_SIZE EQU 0x18 +OrgOp1h EQU 0x14 +OrgOp1l EQU 0x10 +ExDResl EQU 0x08 +NewResl EQU 0x10 + + GET fpe.asm + GET kxarm.inc + + AREA |.text|, CODE, READONLY + + Export __dtou + IMPORT FPE_Raise + + NESTED_ENTRY __dtou + EnterWithLR_16 + STMFD sp!, {lr} ; Save return address + SUB sp, sp, #LOC_SIZE ; Allocate stack space + PROLOG_END + + STR r1, [sp, #OrgOp1h] ; Save off original args in case of exception + ORRS r12, r0, r1, LSL #1 ; Check for zero + STR r0, [sp, #OrgOp1l] ; Save off original args in case of exception + ADDEQ sp, sp, #LOC_SIZE ; Restore stack and return + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; if zero (r0 already zero) + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; if zero (r0 already zero) + ENDIF + + MOVS r2, r1, LSL #1 ; Right justify exponent, save sign bit in C + MOV r1, r1, LSL #11 ; Left justify mantissa + ORR r1, r1, r0, LSR #21 ; .. + ; .. + MOV r0, r0, LSL #11 ; .. + ORR r1, r1, #1 << 31 ; Insert hidden one (even for denorms) + + BCS _ffix_negative ; If negative input, separate case + + MOV r3, #DExp_bias+1 ; r3 = 31 + DExp_bias + ADD r3, r3, #30 ; .. + SUBS r2, r3, r2, LSR #21 ; Determine shift amount + BLT shift_left ; Negative shift is a shift left, NaN, + ; or INF + CMP r2, #32 ; See if shifting all bits out + BGE shift_right_32 ; If shifting all bits out, return zero + +shift_right + RSB r3, r2, #32 ; Check for inexact + ORRS r12, r0, r1, LSL r3 ; .. + MOV r0, r1, LSR r2 ; Shift the result + MOVNE r3, #INX_bit ; Set inexact if inexact + MOVEQ r3, #0 ; Otherwise set to no exceptions + B __dtou_return ; Return + +shift_left + RSB r2, r2, #0 ; Get left shift amount + CMP r2, #32 ; If >= 32, shift by moving words, and + MOVGE r1, r0 ; adjusting shift amount + MOVGE r0, #0 ; .. + SUBGE r2, r2, #32 ; .. + MOV r1, r1, LSL r2 ; Perform rest of shift + RSB r2, r2, #32 ; .. + ORR r0, r1, r0, LSR r2 ; .. + MOV r3, #IVO_bit ; Overflow so set invalid operation + B __dtou_return ; Return + +shift_right_32 ; 0.0 < abs(Arg) < 1.0, so losing all bits + BIC r3, r3, #IVO_bit ; Ensure invalid is clear + MOV r3, #INX_bit ; Set inexact + MOV r0, #0 ; Return zero + B __dtou_return ; Return + +_ffix_negative + MOV r3, #DExp_bias+1 ; r3 = 31 + DExp_bias + ADD r3, r3, #30 ; .. + SUBS r2, r3, r2, LSR #21 ; Determine shift amount + BLT shift_left_neg ; Negative shift is a shift left, NaN, + ; or INF + CMP r2, #32 ; See if shifting all bits out + BGE shift_right_32 ; If shifting all bits out, return zero + +shift_right_neg + MOV r0, r1, LSR r2 ; Shift the result + RSB r0, r0, #0 ; Negate result + MOV r3, #IVO_bit ; Have a negative so invalid + B __dtou_return ; Return + +shift_left_neg + RSB r2, r2, #0 ; Get left shift amount + CMP r2, #32 ; If >= 32, shift by moving words, and + MOVGE r1, r0 ; adjusting shift amount + MOVGE r0, #0 ; .. + SUBGE r2, r2, #32 ; .. + MOV r1, r1, LSL r2 ; Perform rest of shift + RSB r2, r2, #32 ; .. + ORR r0, r1, r0, LSR r2 ; .. + RSB r0, r0, #0 ; Negate result + MOV r3, #IVO_bit ; Have a negative so invalid + + +__dtou_return + TST r3, #FPECause_mask ; Any exceptions? + ADDEQ sp, sp, #LOC_SIZE ; If not, pop off saved arg and + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; return + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; return + ENDIF + ORR r1, r3, #_FpDToU ; Exception information + LDR r3, [sp, #OrgOp1h] ; Original operand 1 + LDR r2, [sp, #OrgOp1l] ; .. + STR r0, [sp, #ExDResl] ; Store default result + ADD r0, sp, #NewResl ; Pointer to new result + + CALL FPE_Raise ; Handle exception information + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #NewResl] ; Load new result + ADD sp, sp, #LOC_SIZE ; Pop off exception record and result + IF Interworking :LOR: Thumbing + LDMFD sp!, {lr} ; Return + BX lr + ELSE + LDMFD sp!, {pc} ; Return + ENDIF + + ENTRY_END __dtou + + END diff --git a/base/Kernel/Native/arm/Crt/r_dtou64.asm b/base/Kernel/Native/arm/Crt/r_dtou64.asm new file mode 100644 index 0000000..80f8de3 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_dtou64.asm @@ -0,0 +1,191 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; __dtou64 double precision floating point to unsigned 64-bit integer convert +; +; Input: r1 - Most significant word of the double to be converted +; r0 - Least significant word of the double to be converted +; +; Output: r1 - Most significant word of the converted double in unsigned +; 64-bit integer format +; r0 - Least significant word of the converted double in unsigned +; 64-bit integer format +; + +; Local storage size and offsets +LOC_SIZE EQU 0x18 +OrgOp1h EQU 0x14 +OrgOp1l EQU 0x10 +ExDResh EQU 0x0C +ExDResl EQU 0x08 +NewResh EQU 0x14 +NewResl EQU 0x10 + + + GET fpe.asm + GET kxarm.inc + + AREA |.text|, CODE, READONLY + + Export __dtou64 + IMPORT FPE_Raise + + NESTED_ENTRY __dtou64 + EnterWithLR_16 + STMFD sp!, {lr} ; Save return address + SUB sp, sp, #LOC_SIZE ; Allocate local storage + PROLOG_END + + STR r1, [sp, #OrgOp1h] ; Save off original args in case of exception + ORRS r12, r0, r1, LSL #1 ; Check for zero + STR r0, [sp, #OrgOp1l] ; .. + MOVEQ r1, r0 ; return if zero (r0 already zero) + ADDEQ sp, sp, #LOC_SIZE ; restore stack + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; .. + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; .. + ENDIF + + MOVS r2, r1, LSL #1 ; Right justify exponent, save sign bit in C + MOV r1, r1, LSL #11 ; Left justify mantissa + ORR r1, r1, r0, LSR #21 ; .. + ; .. + MOV r0, r0, LSL #11 ; .. + ORR r1, r1, #1 << 31 ; Insert hidden one (even for denorms) + + BCS _ffix_negative ; If negative input, separate case + + MOV r3, #DExp_bias+1 ; r3 = 63 + DExp_bias + ADD r3, r3, #62 ; .. + SUBS r2, r3, r2, LSR #21 ; Determine shift amount + BLT shift_left ; Negative shift is a shift left, NaN, + ; or INF + CMP r2, #64 ; See if shifting all bits out + BGE shift_right_64 ; If shifting all bits out, return zero + +shift_right + MOV r12, #0 ; Need to clear r12 for inexact check + CMP r2, #32 ; See if shift amount >= 32 + BLT shift_right_31 ; If not, shift right 31 or less + MOV r12, r0 ; If >= 32, save lost bits in temp reg, + MOV r0, r1 ; shift by moving words, and + MOV r1, #0 ; adjust the shift amount + SUB r2, r2, #32 ; .. + +shift_right_31 + RSB r3, r2, #32 ; Check for inexact + ORRS r12, r12, r0, LSL r3 ; .. + MOV r0, r0, LSR r2 ; Shift the result + ORR r0, r0, r1, LSL r3 ; .. + MOV r1, r1, LSR r2 ; .. + MOVNE r3, #INX_bit ; Set inexact if inexact + MOVEQ r3, #0 ; Otherwise set to no exceptions + B __dtou64_return ; Return + +shift_left + RSB r2, r2, #0 ; Get left shift amount + CMP r2, #32 ; If >= 32, shift by moving words, and + MOVGE r1, r0 ; adjusting shift amount + MOVGE r0, #0 ; .. + SUBGE r2, r2, #32 ; .. + MOV r1, r1, LSL r2 ; Perform rest of shift + RSB r3, r2, #32 ; .. + ORR r1, r1, r0, LSR r3 ; .. + MOV r0, r0, LSL r2 ; .. + MOV r3, #IVO_bit ; Overflow so set invalid operation + B __dtou64_return ; Return + +shift_right_64 ; 0.0 < abs(Arg) < 1.0, so losing all bits + MOV r3, #INX_bit ; Set inexact + MOV r0, #0 ; Return zero + MOV r1, #0 ; .. + B __dtou64_return ; Return + +_ffix_negative + MOV r3, #DExp_bias+1 ; r3 = 63 + DExp_bias + ADD r3, r3, #62 ; .. + SUBS r2, r3, r2, LSR #21 ; Determine shift amount + BLT shift_left_neg ; Negative shift is a shift left, NaN, + ; or INF + CMP r2, #64 ; See if shifting all bits out + BGE shift_right_64 ; If shifting all bits out, return zero + +shift_right_neg + CMP r2, #32 ; See if shift amount >= 32 + MOVGE r0, r1 ; If is shift by moving words, and + MOVGE r1, #0 ; adjust the shift amount + SUBGE r2, r2, #32 ; .. + +shift_right_31_neg + RSB r3, r2, #32 ; 32 - right shift amount + MOV r0, r0, LSR r2 ; Shift the result + ORR r0, r0, r1, LSL r3 ; .. + MOV r1, r1, LSR r2 ; .. + RSBS r0, r0, #0 ; Negate result + RSC r1, r1, #0 ; .. + MOV r3, #IVO_bit ; Set invalid operation as negative + B __dtou64_return ; Return + +shift_left_neg + RSB r2, r2, #0 ; Get left shift amount + CMP r2, #32 ; If >= 32, shift by moving words, and + MOVGE r1, r0 ; adjusting shift amount + MOVGE r0, #0 ; .. + SUBGE r2, r2, #32 ; .. + MOV r1, r1, LSL r2 ; Perform rest of shift + RSB r3, r2, #32 ; .. + ORR r1, r1, r0, LSR r3 ; .. + MOV r0, r0, LSL r2 ; .. + RSBS r0, r0, #0 ; Negate result + RSC r1, r1, #0 ; .. + MOV r3, #IVO_bit ; Overflow so set invalid operation + + +__dtou64_return + TST r3, #FPECause_mask ; Any exceptions? + ADDEQ sp, sp, #LOC_SIZE ; If not, pop off saved arg and + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; return + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; return + ENDIF + ORR r12, r3, #_FpDToU64 ; Save exception info + LDR r3, [sp, #OrgOp1h] ; Load original arg + LDR r2, [sp, #OrgOp1l] ; .. + STR r0, [sp, #ExDResl] ; Store default result + STR r1, [sp, #ExDResh] ; .. + MOV r1, r12 ; Exception information + ADD r0, sp, #NewResl ; Pointer to new result + + CALL FPE_Raise ; Handle exception information + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #NewResl] ; Load new result + LDR r1, [sp, #NewResh] ; .. + ADD sp, sp, #LOC_SIZE ; Pop off exception record and result + IF Interworking :LOR: Thumbing + LDMFD sp!, {lr} ; Return + BX lr + ELSE + LDMFD sp!, {pc} ; Return + ENDIF + + ENTRY_END __dtoi64 + + + END diff --git a/base/Kernel/Native/arm/Crt/r_eqd.asm b/base/Kernel/Native/arm/Crt/r_eqd.asm new file mode 100644 index 0000000..c0807df --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_eqd.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL eq_s + + GET basic_d.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_eqs.asm b/base/Kernel/Native/arm/Crt/r_eqs.asm new file mode 100644 index 0000000..8ebe6df --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_eqs.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL eq_s + + GET basic_f.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_ged.asm b/base/Kernel/Native/arm/Crt/r_ged.asm new file mode 100644 index 0000000..a4689de --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_ged.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL geq_s + + GET basic_d.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_ges.asm b/base/Kernel/Native/arm/Crt/r_ges.asm new file mode 100644 index 0000000..c118937 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_ges.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL geq_s + + GET basic_f.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_gtd.asm b/base/Kernel/Native/arm/Crt/r_gtd.asm new file mode 100644 index 0000000..6a2ef76 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_gtd.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL gr_s + + GET basic_d.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_gts.asm b/base/Kernel/Native/arm/Crt/r_gts.asm new file mode 100644 index 0000000..ba9fa01 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_gts.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL gr_s + + GET basic_f.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_i64tod.asm b/base/Kernel/Native/arm/Crt/r_i64tod.asm new file mode 100644 index 0000000..3f5c1ff --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_i64tod.asm @@ -0,0 +1,222 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; __i64tod: __int64 to double conversion routine. +; __u64tod: unsigned __int64 to double conversion routine. +; +; Input: r1 - Most significant word of 64-bit integer to be converted +; r0 - Least significant word of 64-bit integer to be converted +; +; Output: r1 - Most significant word of converted number to floating point +; double precision format. +; r0 - Least significant word of converted number to floating point +; double precision format. + +; Local storage size and offsets +LOC_SIZE EQU 0x18 +OrgOp1h EQU 0x14 +OrgOp1l EQU 0x10 +ExDResh EQU 0x0C +ExDResl EQU 0x08 +NewResh EQU 0x14 +NewResl EQU 0x10 + + GET fpe.asm + GET kxarm.inc + + Export __u64tod + Export __i64tod + IMPORT FPE_Raise + + AREA |.text|, CODE, READONLY + +; Prolog must match __i64tod + NESTED_ENTRY __u64tod + EnterWithLR_16 + STMFD sp!, {lr} ; Save return address + SUB sp, sp, #LOC_SIZE ; Allocate local storage + PROLOG_END + STR r0, [sp, #OrgOp1l] ; Save original arg in case of exception + ORRS r2, r0, r1 ; Check for zero + STR r1, [sp, #OrgOp1h] ; Save original arg in case of exception + ADDEQ sp, sp, #LOC_SIZE ; If zero, restore stack and + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; return zero + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; return zero + ENDIF + MOV r14, #_FpU64ToD ; Initialize opcode, no exceptions + MOV r12, #0 ; Initialize sign to 0 (positive) + B LongSingleNormalize + ENTRY_END __u64tod + + +; Prolog must match __u64tod + NESTED_ENTRY __i64tod + EnterWithLR_16 + STMFD sp!, {lr} ; Save return address + SUB sp, sp, #LOC_SIZE ; Allocate local storage + PROLOG_END + STR r0, [sp, #OrgOp1l] ; Save original arg in case of exception + ORRS r2, r0, r1 ; Check for zero + STR r1, [sp, #OrgOp1h] ; Save original arg in case of exception + ADDEQ sp, sp, #LOC_SIZE ; If zero, restore stack and + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; return zero + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; return zero + ENDIF + MOV r14, #_FpI64ToD ; Initialize opcode, no exceptions + ANDS r12, r1, #Sign_bit ; Extract sign + BEQ LongSingleNormalize ; Positive value so do convert + RSBS r0, r0, #0 ; Else we have a negative number so + RSC r1, r1, #0 ; take the 2's complement inverse + + +; Sign is in r12. +; Mantissa abs(input 64-bit integer) is stored in r1:r0. +; +; Sign +; +-+-------------------------------+ +; r12: |S|0000000000000000000000000000000| +; +-+-------------------------------+ +; +; Unnormalized mantissa +; +--------------------------------+ +; r1: | Most Significant Word | +; +--------------------------------+ +; +; +--------------------------------+ +; r0: | Least Significant Word | +; +--------------------------------+ +; +; Only the high mantissa is shifted. After it is shifted into the correct +; position, the low mantissa is shifted into place. The exception is the +; first shift by 32 which moves the low mantissa into the high. +; +LongSingleNormalize + MOV r2, #0 ; Exponent adjustment/mantissa shift + + CMP r1, #0 ; Any high 32 bits set? + ADDEQ r2, r2, #32 ; If not, adjust exponent + MOVEQ r1, r0 ; and shift the WHOLE mantissa + MOVEQ r0, #0 ; .. + + MOVS r3, r1, LSR #16 ; Any high 16 bits set? + ADDEQ r2, r2, #16 ; If not, adjust exponent + MOVEQ r1, r1, LSL #16 ; and shift high mantissa + + TST r1, #0xFF000000 ; Any high 8 bits set? + ADDEQ r2, r2, #8 ; If not, adjust exponent + MOVEQ r1, r1, LSL #8 ; and shift high mantissa + + TST r1, #0xF0000000 ; Any high 4 bits set? + ADDEQ r2, r2, #4 ; If not, adjust exponent + MOVEQ r1, r1, LSL #4 ; and shift high mantissa + + TST r1, #0xC0000000 ; Any high 2 bits set? + ADDEQ r2, r2, #2 ; If not, adjust exponent + MOVEQS r1, r1, LSL #2 ; and shift high mantissa + + ADDPL r2, r2, #1 ; If high bit clear, adjust exponent + MOVPL r1, r1, LSL #1 ; and shift high mantissa + + MOV r3, #DExp_bias+1 ; Load r3 = DExp_bias+63 + ADD r3, r3, #62 ; .. + RSB r3, r2, r3 ; Calculate exponent + + ORR r12, r12, r3, LSL #DExp_pos + ; Combine sign and exponent + + CMP r2, #32 ; If all low shifted into high ... + MOV r1, r1, LSL #1 ; Hide the hidden one + ADD r2, r2, #1 ; .. + BGE insert_mantissa ; ... just insert the mantissa + + ; R2 contains the amount the mantissa has been shifted left, including + ; the shift for the hidden one. It is in the range 1..32. R1 contains + ; the high portion of the mantissa without any of R0 shifted into it. + ; R0 is unshifted. Note that the case where the entire low word was + ; shifted into the high word has already been taken care of and never + ; reaches this code. + RSB r3, r2, #32 ; Determine amount of low mantissa + ; shifted into high mantissa + + ORR r1, r1, r0, LSR r3 ; Get part of low word shifted in high + MOV r0, r0, LSL r2 ; Shift low word into position + +insert_mantissa + ORR r3, r12, r1, LSR #12 ; Insert high word into result high + MOV r2, r1, LSL #20 ; Insert high word into result low + ORR r2, r2, r0, LSR #12 ; Insert low word into result low + + MOVS r0, r0, LSL #20 ; Check for inexact + ORRNE r14, r14, #INX_bit ; Set inexact if bits are lost + + MOVS r0, r0, LSL #1 ; Round + ; CS -> guard bit + ; MI -> round bit + ; NE -> sticky bit | round bit + + BCC __i64tod_return ; If guard bit clear, leave result + + ; Guard bit is set + MOVS r0, r0, LSR #1 ; Must clear carry bit + ; NE -> sticky bit | round bit + TSTEQ r2, #0x1 ; Check lost bit + ADDNES r2, r2, #1 ; If G & (L | R | S) round up + ADC r3, r3, #0 ; .. + + +__i64tod_return + MOV r1, r3 ; Move result into return regs + MOV r0, r2 ; .. + + TST r14, #FPECause_mask ; Any exceptions? + ADDEQ sp, sp, #LOC_SIZE ; pop off original arg, and + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; return + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; return + ENDIF + + ; Have an exception + LDR r2, [sp, #OrgOp1l] ; Load original operand + LDR r3, [sp, #OrgOp1h] ; .. + STR r0, [sp, #ExDResl] ; Store default result + STR r1, [sp, #ExDResh] ; .. + ADD r0, sp, #NewResl ; Pointer to new result + MOV r1, r14 ; Exception information + + CALL FPE_Raise ; Deal with exception information + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #NewResl] ; Load new result + LDR r1, [sp, #NewResh] ; .. + ADD sp, sp, #LOC_SIZE ; Pop off exception info and orig arg + IF Interworking :LOR: Thumbing + LDMFD sp!, {lr} ; Return + BX lr + ELSE + LDMFD sp!, {pc} ; Return + ENDIF + + ENTRY_END __i64tod + + + END diff --git a/base/Kernel/Native/arm/Crt/r_i64tos.asm b/base/Kernel/Native/arm/Crt/r_i64tos.asm new file mode 100644 index 0000000..4f7febc --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_i64tos.asm @@ -0,0 +1,200 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; __int64 to float and unsigned __int64 to float conversion routine. +; +; Input: r1 - Most significant word of 64-bit integer to be converted +; r0 - Least significant word of 64-bit integer to be converted +; +; Output: r0 - Converted number to floating point single precision format. +; + +; Local storage size and offsets +LOC_SIZE EQU 0x18 +OrgOp1h EQU 0x14 +OrgOp1l EQU 0x10 +ExDResl EQU 0x08 +NewResl EQU 0x10 + + GET fpe.asm + GET kxarm.inc + + Export __u64tos + Export __i64tos + IMPORT FPE_Raise + + AREA |.text|, CODE, READONLY + +; Prolog must match __i64tos + NESTED_ENTRY __u64tos + EnterWithLR_16 + STMFD sp!, {lr} ; Save return address + SUB sp, sp, #LOC_SIZE + PROLOG_END + STR r0, [sp, #OrgOp1l] ; Save original arg in case of exception + ORRS r2, r0, r1 ; Check for zero + STR r1, [sp, #OrgOp1h] ; Save original arg in case of exception + ADDEQ sp, sp, #LOC_SIZE ; If zero, restore stack + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; and return zero + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; and return zero + ENDIF + MOV r14, #_FpU64ToS ; Initialize opcode, no exceptions + MOV r12, #0 + B LongSingleNormalize + ENTRY_END __u64tos + +; Prolog must match __u64tos + NESTED_ENTRY __i64tos + EnterWithLR_16 + STMFD sp!, {lr} ; Save return address + SUB sp, sp, #LOC_SIZE + PROLOG_END + STR r0, [sp, #OrgOp1l] ; Save original arg in case of exception + ORRS r2, r0, r1 ; Check for zero + STR r1, [sp, #OrgOp1h] ; Save original arg in case of exception + ADDEQ sp, sp, #LOC_SIZE ; If zero, restore stack + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; and return zero + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; and return zero + ENDIF + MOV r14, #_FpI64ToS ; Initialize opcode, no exceptions + ANDS r12, r1, #Sign_bit ; Extract sign + BEQ LongSingleNormalize ; Positive value so do convert + RSBS r0, r0, #0 ; Else we have a negative number so + RSC r1, r1, #0 ; take the 2's complement inverse + + +; Sign is in r12. +; Mantissa abs(input 64-bit integer) is stored in r1:r0. +; +; Sign +; +-+-------------------------------+ +; r12: |S|0000000000000000000000000000000| +; +-+-------------------------------+ +; +; Unnormalized mantissa +; +--------------------------------+ +; r1: | Most Significant Word | +; +--------------------------------+ +; +; +--------------------------------+ +; r0: | Least Significant Word | +; +--------------------------------+ +; +; Only the high mantissa is shifted. After it is shifted into the correct +; position, the low mantissa is shifted into place. The exception is the +; first shift by 32 which moves the low mantissa into the high. +; +LongSingleNormalize + MOV r2, #0 ; Exponent adjustment/mantissa shift + + CMP r1, #0 ; Any high 32 bits set? + ADDEQ r2, r2, #32 ; If not, adjust exponent + MOVEQ r1, r0 ; and shift the WHOLE mantissa + MOVEQ r0, #0 ; .. + + MOVS r3, r1, LSR #16 ; Any high 16 bits set? + ADDEQ r2, r2, #16 ; If not, adjust exponent + MOVEQ r1, r1, LSL #16 ; and shift high mantissa + + TST r1, #0xFF000000 ; Any high 8 bits set? + ADDEQ r2, r2, #8 ; If not, adjust exponent + MOVEQ r1, r1, LSL #8 ; and shift high mantissa + + TST r1, #0xF0000000 ; Any high 4 bits set? + ADDEQ r2, r2, #4 ; If not, adjust exponent + MOVEQ r1, r1, LSL #4 ; and shift high mantissa + + TST r1, #0xC0000000 ; Any high 2 bits set? + ADDEQ r2, r2, #2 ; If not, adjust exponent + MOVEQS r1, r1, LSL #2 ; and shift high mantissa + + ADDPL r2, r2, #1 ; If high bit clear, adjust exponent + MOVPL r1, r1, LSL #1 ; and shift high mantissa + + RSB r3, r2, #SExp_bias+63 ; Calculate exponent + ORR r12, r12, r3, LSL #SExp_pos + ; Combine sign and exponent + + CMP r2, #32 ; If all low shifted into high ... + MOV r1, r1, LSL #1 ; Hide the hidden one + ADD r2, r2, #1 ; .. + BGE insert_mantissa ; ... just insert the mantissa + + ; R2 contains the amount the mantissa has been shifted left, including + ; the shift for the hidden one. It is in the range 1..32. R1 contains + ; the high portion of the mantissa without any of R0 shifted into it. + ; R0 is unshifted. Note that the case where the entire low word was + ; shifted into the high word has already been taken care of and never + ; reaches this code. + RSB r3, r2, #32 ; Determine amount of low mantissa + ; shifted into high mantissa + + ORR r1, r1, r0, LSR r3 ; Get part of low word shifted in high + MOVS r0, r0, LSL r2 ; Bits lost in low word? + ORRNE r1, r1, #1 ; If lost bits, set sticky + +insert_mantissa + ORR r0, r12, r1, LSR #9 ; Insert mantissa into result + + MOVS r1, r1, LSL #23 ; Inexact? + ORRNE r14, r14, #INX_bit ; If bits set, set inexact + + MOVS r1, r1, LSL #1 ; Round + ; CS -> guard bit + ; MI -> round bit + ; NE -> sticky bit | round bit + BCC __i64tos_return + TSTEQ r0, #0x1 ; Check lost bit + ADDNE r0, r0, #1 ; If G & (L | R | S) round up + + +__i64tos_return + TST r14, #FPECause_mask ; Any exceptions? + ADDEQ sp, sp, #LOC_SIZE ; pop off original arg, and + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; return + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; return + ENDIF + + ; Have an exception + LDR r2, [sp, #OrgOp1l] ; Load original operand + LDR r3, [sp, #OrgOp1h] ; .. + STR r0, [sp, #ExDResl] ; Store default result + ADD r0, sp, #NewResl ; Pointer to new result + MOV r1, r14 ; Exception information + + CALL FPE_Raise + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #NewResl] ; Load new result + ADD sp, sp, #LOC_SIZE ; Pop off exception info and orig arg + IF Interworking :LOR: Thumbing + LDMFD sp!, {lr} ; Return + BX lr + ELSE + LDMFD sp!, {pc} ; Return + ENDIF + + ENTRY_END __i64tos + + END diff --git a/base/Kernel/Native/arm/Crt/r_itod.asm b/base/Kernel/Native/arm/Crt/r_itod.asm new file mode 100644 index 0000000..5792969 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_itod.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL dflt_s + + GET format_d.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_itos.asm b/base/Kernel/Native/arm/Crt/r_itos.asm new file mode 100644 index 0000000..3558e14 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_itos.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL fflt_s + + GET format_f.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_led.asm b/base/Kernel/Native/arm/Crt/r_led.asm new file mode 100644 index 0000000..a28aeff --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_led.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL leq_s + + GET basic_d.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_les.asm b/base/Kernel/Native/arm/Crt/r_les.asm new file mode 100644 index 0000000..21c0304 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_les.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL leq_s + + GET basic_f.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_ltd.asm b/base/Kernel/Native/arm/Crt/r_ltd.asm new file mode 100644 index 0000000..06c207b --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_ltd.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL ls_s + + GET basic_d.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_lts.asm b/base/Kernel/Native/arm/Crt/r_lts.asm new file mode 100644 index 0000000..ce29e37 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_lts.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL ls_s + + GET basic_f.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_muld.asm b/base/Kernel/Native/arm/Crt/r_muld.asm new file mode 100644 index 0000000..373e9cb --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_muld.asm @@ -0,0 +1,569 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL mul_s + + GET veneer_d.asm + + END + + +;;;; THE BELOW ROUTINE SHOULD WORK, BUT THE ARM ROUTINES SHOULD BE FASTER. + + +; +; Translated to ARM from SH3 FP emulation routines. +; +; __muld Double precision floating point multiplication. +; Input: +; r0 - Arg1.low +; r1 - Arg1.high +; r2 - Arg2.low +; r3 - Arg2.high +; Output: +; r0 - Result.low +; r1 - Result.high +; +; Note: +; If any FP exceptions are enabled, this routine may raise an exception. +; +; +; IEEE DOUBLE FORMAT +; +; 8 BYTES (LONG WORD * 2) +; 63 62 52 51 0 +; +-+-----------+----------------------------------------------------+ +; |s| e(11) | m(52) | +; +-+-----------+----------------------------------------------------+ +; ^ point +; +; INFINITY NUMBER : e = 2047 m = 0 +; ZERO : e = 0 m = 0 +; NaN : e = 2047 m != 0 +; DENORMAL NUMBER : e = 0 m != 0 +; + GET fpe.asm + + Export __muld + + IMPORT FPE_Raise + + AREA |.text|, CODE, READONLY + + +CARRY_CHECK EQU 0x01000000 +MSB EQU 0x00100000 + + +__muld + + STMFD sp!, {r0-r9, lr} ; Save off args and non-volatiles and lr + + MOV r8, r1 ; Load parameter1 as R8 R0 + MOV r4, r2 ; Load parameter2 as R2 R4 + MOV r2, r3 ; ... + MOV r5, #_FpMulD ; Double multiply, assume no exceptions + + +; Unpack parameters. +; +; R8 R0: mantissa1 R2 R4: mantissa2 +; R9: exponent1 R1: exponent2 +; R7: sign = sign1 XOR sign2 +; +; R5: Exception flags + + MOV r9, r8, LSL #1 ; Extract exponent1 + MOV r9, r9, LSR #21 ; ... + MOV r1, r2, LSL #1 ; Extract exponent2 + MOV r1, r1, LSR #21 ; ... + MVN r3, #0 ; Set up to extract mantissas + EOR r7, r8, r2 ; Compute sign of result + AND r8, r8, r3, LSR #12; Extract mantissa1 + AND r2, r2, r3, LSR #12; Extract mantissa2 + + +; Check for exceptional cases. All NaNs, infinities, and 0's are eliminated. +; Denormal numbers return here after normalizing them. After these checks, +; both parameters are normalized numbers. +; +; if (exponent1 == 2047) +; exception1; parameter1 is nonfinite +; if (exponent2 == 2047) +; exception2; parameter1 is finite, parameter2 is nonfinite +; if (exponent1 == 0) +; exception3; parameter1 is 0 or denormal, parameter2 is finite +; if (exponent2 == 0) +; exception4; parameter1 is normalized, parameter2 is 0 or denormal + + ADD r3, r9, #1 ; if (exponent1==2047) + CMP r3, #2048 ; .. + BEQ exception1 ; exception1 + + ADD r3, r1, #1 ; if (exponent2==2047) + CMP r3, #2048 ; .. + BEQ exception2 ; exception2 + + CMP r9, #0 ; if (exponent1==0) + BEQ exception3 ; exception3 +exception_return3 + + CMP r1, #0 ; if (exponent2==0) + BEQ exception4 ; exception4 +exception_return4 + +; Multiply the 53-bit mantissa1 and mantissa2 to produce a 106-bit product. +; +; Mantissas: +; +; 63 53 51 32 31 0 +; 31 21 19 0 31 0 +; +-----------+-+---------------------+-----------------------------------+ +; |<--- 0 --->|1| m1h, m2h | m1l, m2l | +; +-----------+-+---------------------+-----------------------------------+ +; ^ Binary point +; +; Partial product terms: +; +; m1l*m2l.h m1l*m2l.l +; m1h*m2l.h < C + m1h*m2l.l +; + m1l*m2h.h < C + m1l*m2h.l +; + m1h*m2h.h < C + m1h*m2h.l +; ----------- ----------- ----------- ----------- +; res3 res2 res1 res0 +; +; Intermediate result: +; +; 127 106 104 103 96 95 64 63 32 31 0 +; 31 10 9 8 7 0 31 0 31 0 31 0 +; +----------------------+-+-+--------+----/\/----+----/\/----+----/\/----+ +; |<-------- 0 --------->|?|?|R3: res3| R8: res2 | R0: res1 | R6: res0 | +; +----------------------+-+-+--------+----/\/----+----/\/----+----/\/----+ +; ^ Binary point + + ADD r9, r9, r1 ; Compute exponent of result ... + + UMULL r6, r1, r0, r4 ; Compute m1l * m2l + ; r6 = m1l*m2l.l, res0 + ; r1 = m1l*m2l.h + + ORR r8, r8, #MSB ; Set mantissa1's hidden bit + ORR r2, r2, #MSB ; Set mantissa2's hidden bit + + UMULL r4, r3, r8,r4 ; Compute m1h * m2l + ; r4 = m1h*m2l.l + ; r3 = m1h*m2l.h + + ADDS r4, r1, r4 ; Add 1st 2 terms of res1 + + SUB r9, r9, #0x400 ; ... compute exponent of result + ADD r9, r9, #0x1 ; ... compute exponent of result + + UMULL r0, r1, r2, r0 ; Compute m1l * m2h + ; r0 = m1l*m2h.l + ; r1 = m1l*m2h.h + + ADCS r1, r1, r3 ; Add 1st 2 terms of res2, no carry out + ADCS r0, r0, r4 ; Add 3rd term of res1, no carry in + + UMULL r8, r3, r2, r8 ; Compute m1h * m2h + ; r8 = m1h*m2h.l + ; r3 = m1h*m2h.h + + ADCS r8, r8, r1 ; Add 3rd term of res2 + ADC r3, r3, #0 ; Add res2's carry to res3 + + +; Shift the intermediate result right 17 bits, and 1 more if the product took +; 2 bits to the left of the binary point. Fold all dropped bits from the right +; into the sticky bit S. This leaves the result in standardized form for +; rounding. +; +; Result: +; 63 56 54 32 31 3 2 0 +; 31 24 22 0 31 3 2 0 +; +---------+-+-----------------------+-----------------------------------+ +; |<-- 0 -->|1| R8 | R0 L|GRS| +; +---------+-+-----------------------+-----------------------------------+ +; ^ Binary point + +normalize + CMP r6, #0 ; Fold bits we're about to lose into a + ORRNE r0, r0, #1 ; sticky bit + MOV r6, r6, LSR #17 ; Shift intermediate result right 17 + ORR r6, r6, r0, LSL #15 ; .. + MOV r0, r0, LSR #17 ; .. + ORR r0, r0, r8, LSL #15 ; .. + MOV r8, r8, LSR #17 ; .. + ORR r8, r8, r3, LSL #15 ; .. + TST r8, #CARRY_CHECK ; If product has 2 bits to the left of the + BEQ end_normalize ; binary point + MOVS r8, r8, LSR #1 ; Then normalize by scaling right 1 + MOVS r0, r0, RRX ; more bit + MOV r6, r6, RRX ; .. + ADD r9, r9, #1 ; .. +end_normalize + +; There are still 17 or 18 guard bits on the left of R6 that need to be folded +; into the sticky bit S. It's safe to check the right ones over again because +; we're only concerned with stickiness. + + CMP r6,#0 ; If any guard bits below S are set + ORRNE r0, r0, #1 ; fold them into S + +; Denormalize the result if necessary, with no concern for performance. + + CMP r9, #0 ; If exponent <= 0 + BGT end_denormal ; .. + RSB r9, r9, #0 ; Then shift right exponent1 places + ADD r9, r9, #1 ; +1 for the non-hidden bit +denormal_loop + MOVS r8, r8, LSR #1 ; .. + MOVS r0, r0, RRX ; .. + ORRCS r0, r0, #1 ; Fold the lost bit into the sticky bit + SUBS r9, r9, #1 ; .. + BNE denormal_loop ; .. +end_denormal + +; Round to nearest. If rounding occurs, set inexact and +; mantissa += G & ( L | R | S ). If the rounding carries, then renormalize. + +; Test for inexact. + TST r0, #0x7 ; If G|R|S (=> rounding required) + BEQ end_round ; .. + ORR r5, r5, #INX_bit ; result is inexact + +; Round to nearest. + TST r0, #0x4 ; If G && + BEQ end_round ; .. + TST r0, #0xB ; L|R|S + BEQ end_round ; .. + ADDS r0, r0, #0x8 ; Then round the mantissa up + ADC r8, r8, #0 ; .. + + CMP r8, #CARRY_CHECK ; If the rounding carried + BLT no_normal_carry ; (mantissa >= 0x01000000) + + MOVS r8, r8, LSR #1 ; Then renormalize + MOV r0, r0, RRX ; .. + ADD r9, r9, #1 ; .. + B end_round ; .. + +no_normal_carry + CMP r9, #0 ; Else if (exponent == 0) + BNE end_round ; .. + CMP r8, #CARRY_CHECK>>1; && (mantissa >= 0x00800000) + MOVGE r9, #1 ; Then rounded MaxDenorm to MinNormal + +end_round + +; Test for overflow. Do this after rounding in case rounding caused overflow. + ADD r3, r9, #1 ; If (exponent >= 2047) + CMP r3, #2048 ; .. + BGE return_overflow ; return overflow exception + +; Test tininess after rounding. + TST r5, #INX_bit ; If already inexact + BEQ end_check_underflow1; .. + CMP r9, #0 ; and if exponent = 0 + ORREQ r5, r5, #UNF_bit ; result has underflowed too +end_check_underflow1 + +; Pack the result back into IEEE format. + +return_value + + MOV r0, r0, LSR #3 ; Shift mantissa right 3 to remove GRS + ORR r0, r0, r8, LSL #29 ; .. + MOV r8, r8, LSR #3 ; .. + MVN r3, #0 ; Mask away the hidden bit + AND r8, r8, r3, LSR #12 ; .. + ORR r1, r8, r9, LSL #20 ; Merge exponent and mantissa + MOVS r7, r7 ; Merge sign with exponent and mantissa + ORRMI r1, r1, #0x80000000 ; .. + +; If any trap enable flags are set corresponding to exception flags set, +; set the corresponding cause bits and cause a trap. +; +; if (exception) +; call handler +; extract the possibly updated result +; return + +return + + TST r5, #FPECause_mask ; If any exceptions occurred ... + BEQ done ; .. + +cause_trap +;; +;; Register usage: +;; r0 - Default result.low +;; r1 - Default result.high +;; r5 - Exception information +;; +;; Stack: +;; 0x10(sp) - up: Saved registers +;; 0xC(sp): Original Arg2.high +;; 0x8(sp): Original Arg2.low +;; 0x4(sp): Original Arg1.high +;; 0x0(sp): Original Arg1.low +;; + LDR r2, [sp, #0x8] ; Load original Arg2.low + LDR r3, [sp, #0xC] ; Load original Arg2.high + SUB sp, sp, #0x8 ; Make room for exception information + STR r2, [sp, #0x0] ; Store original Arg2.low + STR r3, [sp, #0x4] ; Store original Arg2.high + LDR r3, [sp, #0x8] ; Load original Arg1.low + LDR r2, [sp, #0xC] ; Load original Arg1.high + STR r0, [sp, #0x8] ; Store default result.low + STR r1, [sp, #0xC] ; Store default result.high + MOV r1, r5 ; Move exception information + ADD r0, sp, #0x10 ; Pointer for return value + +;; Register Usage: +;; r0 - Address for return value = 0x10(sp) +;; r1 - Exception information +;; r2 - Original arg1.low +;; r3 - Original arg1.high +;; +;; Stack Usage: +;; 0x14(sp): Return result.high +;; 0x10(sp): Return result.low +;; 0xC(sp): Default result.high +;; 0x8(sp): Default result.low +;; 0x4(sp): Original arg2.high +;; 0x0(sp): Original arg2.low + CALL FPE_Raise ; Deal with exception information + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #0x10] ; Load up returned result + LDR r1, [sp, #0x14] ; ... + ADD sp, sp, #0x8 ; Restore extra arg passing space + +done + ADD sp, sp, #0x10 ; Pop off original args + IF Interworking :LOR: Thumbing + LDMIA sp!, {r4-r9, lr} + BX lr + ELSE + LDMIA sp!, {r4-r9, pc} + ENDIF + ; Restore off non-volatiles and return + + +;%%%%%%%%%%%%%%%%%%%%%%%%% +;% Exceptional process % +;%%%%%%%%%%%%%%%%%%%%%%%%% + +; Exception 1: parameter1 is non-finite (exponent1 == 2047); it's either a +; NaN or inf. The mantissa has not been shifted left for the guard bits yet. +; +; If either parameter is an SNaN, return an invalid op exception with a QNaN. +; Otherwise, if either parameter is a QNaN, silently return a QNaN. Otherwise, +; parameter1 is inf. Return an invalid op exception with a QNaN for inf*0, or +; inf for inf*inf or inf*. +; +; if (mantissa1<51> == 0 & // parameter1 is an SNaN +; mantissa1 != 0) // .. +; return invalid op exception +; else if (exponent2 == 2047 & // parameter2 is an SNaN +; mantissa2<51> == 0 & // .. +; mantissa2 != 0) // .. +; return invalid op exception +; else if (mantissa1 != 0) // parameter1 is a QNaN +; return QNaN +; else if (exponent2 != 2047) // parameter2 is finite +; if (parameter2 != 0) // inf* +; return inf +; else // inf*0 +; return invalid op exception +; return inf +; else if (mantissa2 != 0) // parameter2 is a QNaN +; return QNaN +; else // inf*inf +; return inf + +exception1 + ORRS r3, r8, r0 ; if (mantissa1 !=0 && + BEQ e1_p2_snan_check ; .. + TST r8, #dSignalBit ; mantissa1[MSb] == 0) + BEQ return_invalid ; return invalid operation + +e1_p2_snan_check + ADD r3, r1, #1 ; else if (exponent2 == 2047 && + CMP r3, #2048 ; .. + BNE e1_p2_not_snan ; .. + ORRS r3, r2, r4 ; mantissa2 != 0 && + BEQ e1_p2_not_snan ; .. + TST r2, #dSignalBit ; mantissa2[MSb] == 0) + MOVEQ r8, r2 ; copy mantissa2 to mantissa1 + MOVEQ r0, r4 ; .. + BEQ return_invalid ; return invalid operation + +e1_p2_not_snan + ORRS r3, r8, r0 ; else if (mantissa1 != 0) + BNE return_QNaN ; return QNaN + +e1_p1_is_INF + ADD r3, r1, #1 ; else if (exponent2 != 2047) + CMP r3, #2048 ; .. + BEQ e1_p2_INF_NaN ; .. + CMP r1, #0 ; if (parameter2 != 0) + ORREQS r3, r2, r4 ; .. + BNE return_inf ; return INF + MOV r8, #0 ; else + MOV r0, #0 ; zero out mantissa1 for QNaN + B return_invalid ; return invalid operation + +e1_p2_INF_NaN + ORRS r3, r2, r4 ; else if (mantissa2 != 0) + MOV r8, r2 ; copy mantissa2 to mantissa1 + MOV r0, r4 ; .. + BNE return_QNaN ; return QNaN + B return_inf ; else + ; return INF + + +; Exception 2: parameter1 is finite. parameter2 is non-finite (exponent2 == +; 2047); it's either a NaN or inf. The mantissa has not been shifted left +; for the guard bits yet. +; +; If parameter2 is an SNaN, return an invalid op exception with a QNaN. +; Otherwise, if it's a QNaN, silently return a QNaN. Otherwise it's finite*inf +; so return an invalid op exception with a QNaN for 0*inf, or +; *inf. +; +; if (mantissa2 != 0 & +; mantissa2<51> == 1) // parameter2 is an SNaN +; return invalid op exception +; else if (mantissa2 != 0) // parameter2 is a QNaN +; return QNaN +; else if (parameter1 != 0) // parameter1 is non-0 finite +; return inf +; else // it's 0*inf +; return invalid op exception + +exception2 + ORRS r3, r2, r4 ; if (mantissa2 != 0 && + BEQ e2_p2_is_inf ; .. + TST r2, #dSignalBit ; mantissa2[MSb] == 0) + BEQ return_invalid ; return invalid operation + MOV r8, r2 ; else if (mantissa2 != 0) + MOV r0, r4 ; copy mantissa2 into mantissa1 for QNaN + B return_QNaN ; return QNaN + +e2_p2_is_inf + ORRS r3, r8, r0 ; else if (parameter1 != 0) + CMPEQ r9, #0 ; .. + MOVEQ r8, r2 ; copy mantissa2 to mantissa1 for QNaN + MOVEQ r0, r4 ; .. + BEQ return_invalid ; .. + B return_inf ; return INF + + + +; Exception 3: parameter1 is 0 or denormal (exponent1 = 0), parameter2 is +; finite. +; +; if (mantissa1 == 0) +; return zero +; else normalize parameter1 + +exception3 + ORRS r3, r8, r0 ; if (mantissa1 == 0) + BEQ return_zero ; return zero +p1_norm ; Normalize parameter1 stop when shift into 1.0 bit + MOVS r0, r0, LSL #1 ; Account for the hidden mantissa bit + MOV r8, r8, LSL #1 ; that denormals don't have + ORRCS r8, r8, #0x1 ; .. + CMP r8, #MSB ; While mantissa1 < 1.0 + BGE end_p1_norm ; .. +p1_norm_loop + MOVS r0, r0, LSL #1 ; Scale mantissa1 up by 1 place + MOV r8, r8, LSL #1 ; .. + ORRCS r8, r8, #0x1 ; .. + SUB r9, r9, #1 ; and exponent1 down by 1 + CMP r8, #MSB ; + BLT p1_norm_loop ; +end_p1_norm + B exception_return3 + ; Done + + +; Exception 4: parameter1 is finite and (now) normalized, parameter2 is 0 or +; denormal (exponent2 = 0). +; +; if (mantissa2 == 0) +; return zero +; else normalize parameter2 +exception4 + ORRS r3, r2, r4 ; if (mantissa2 == 0) + BEQ return_zero ; return zero +p2_norm ; Normalize parameter2 stop when shift into 1.0 bit + MOVS r4, r4, LSL #1 ; Account for the hidden mantissa bit + MOV r2, r2, LSL #1 ; that denormals don't have + ORRCS r2, r2, #0x1 ; .. + CMP r2, #MSB ; While mantissa2 < 1.0 + BGE end_p2_norm ; .. +p2_norm_loop + MOVS r4, r4, LSL #1 ; Scale mantissa2 up by 1 place + MOV r2, r2, LSL #1 ; .. + ORRCS r2, r2, #0x1 ; .. + SUB r1, r1, #1 ; and exponent2 down by 1 + CMP r2, #MSB ; + BLT p2_norm_loop ; +end_p2_norm + B exception_return4 + ; Done + + + +; Cause an overflow exception (=> inexact) and return properly signed inf. +return_overflow + ORR r5, r5, #OVF_bit :OR: INX_bit + ; Report overflow (=> inexact) + ; Fall thru to return inf + +; Return properly signed inf. +return_inf + MVN r9, #0 ; exponent1 = 2047 + MOV r9, r9, LSR #21 ; .. + MOV r8, #0 ; mantissa1 = 0 + MOV r0, #0 ; .. + B return_value + + +; Return 0. +return_zero + MOV r1, r7, LSR #31 ; Apply the sign to zero + MOV r1, r1, LSL #31 ; .. + MOV r0, #0 ; Zero low mantissa + B return ; Done + +; Cause an invalid operation exception and return a QNaN. +return_invalid + ORR r5, r5, #IVO_bit; Report invalid op exception + ; Fall thru to return a QNaN + +; Return a QNaN. +return_QNaN + ORR r1, r8, #0x7F000000 + ; Return a QNaN + ORR r1, r1, #0x00F80000 + ; .. + B return ; .. + + END diff --git a/base/Kernel/Native/arm/Crt/r_muls.asm b/base/Kernel/Native/arm/Crt/r_muls.asm new file mode 100644 index 0000000..fc8ddb4 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_muls.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL mul_s + + GET veneer_f.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_ned.asm b/base/Kernel/Native/arm/Crt/r_ned.asm new file mode 100644 index 0000000..f3f9d1b --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_ned.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL neq_s + + GET basic_d.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_negd.cpp b/base/Kernel/Native/arm/Crt/r_negd.cpp new file mode 100644 index 0000000..593cc08 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_negd.cpp @@ -0,0 +1,56 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// This file contains ARM-specific FP code. +// + +typedef union _FP64Form +{ + struct + { + unsigned int MantLo : 28; + unsigned int MantHi0 : 4; + unsigned int MantHi1 : 20; + unsigned int Exponent : 11; + unsigned int SignBit : 1; + }; + struct + { + unsigned int ul0; + unsigned int ul1; + }; + double fp; + unsigned __int64 ull; +} FP64Form, *PFP64Form; + +extern "C" +double +__negd( + double d1 + ) +//++ +// +//Routine Description: +// +// negate the given double precision floating point value +// +//Arguments: +// +// d1 - double precision values to be negated +// +//Return Value: +// +// The negation of d1. +// +//-- +{ + FP64Form FpConv; + + FpConv.fp = d1; + FpConv.SignBit ^= 1; + + return( FpConv.fp ); +} diff --git a/base/Kernel/Native/arm/Crt/r_nes.asm b/base/Kernel/Native/arm/Crt/r_nes.asm new file mode 100644 index 0000000..9af0e47 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_nes.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL neq_s + + GET basic_f.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_stod.asm b/base/Kernel/Native/arm/Crt/r_stod.asm new file mode 100644 index 0000000..791506a --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_stod.asm @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + + GBLL f2d_s + + GET format.asm + + END diff --git a/base/Kernel/Native/arm/Crt/r_stoi.asm b/base/Kernel/Native/arm/Crt/r_stoi.asm new file mode 100644 index 0000000..e8301c6 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_stoi.asm @@ -0,0 +1,135 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; __stoi single precision floating point to signed integer convert +; +; Input: r0 - float to be converted +; Output: r0 - converted float in integer format +; + +; Local stack size and offsets +LOC_SIZE EQU 0x18 +OrgOp1l EQU 0x14 +ExDResl EQU 0x08 +NewResl EQU 0x10 + + + GET fpe.asm + GET kxarm.inc + + AREA |.text|, CODE, READONLY + + Export __stoi + IMPORT FPE_Raise + + NESTED_ENTRY __stoi + EnterWithLR_16 + STMFD sp!, {lr} ; Save return address + SUB sp, sp, #LOC_SIZE ; Allocate local storage + PROLOG_END + MOVS r12, r0 ; Save original arg in case of exception + MOV r3, #_FpSToI ; Initialize opcode, no exceptions + + MOVS r2, r0, ASR #SFrc_len ; Right justify exponent, save sign bit + MOV r0, r0, LSL #SExp_len ; Left justify mantissa and + ORR r0, r0, #1 << 31 ; insert hidden one (even for denorms) + BMI _ffix_negative ; If negative input, separate case + + RSBS r2, r2, #31 + SExp_bias ; Determine shift amount + BLE shift_left ; Negative shift is a shift left, NaN, + ; or INF; zero shift is an overflow + CMP r2, #32 ; See if shifting all bits out + BGE shift_right_32 ; If shifting all bits out, return zero + +shift_right + RSB r1, r2, #32 ; Check for inexact + MOVS r1, r0, LSL r1 ; .. + ORRNE r3, r3, #INX_bit ; .. + MOV r0, r0, LSR r2 ; Shift the result + B __stoi_return ; Return + +shift_left + ORR r3, r3, #IVO_bit ; Overflow so set invalid operation + RSB r2, r2, #0 ; Get left shift amount + MOV r0, r0, LSL r2 ; Perform shift + B __stoi_return ; Return + +shift_right_32 ; abs(Arg) < 1.0, so losing all bits + MOV r0, #0 ; Return zero + MOVS r1, r12, LSL #1 ; Check for inexact + ORRNE r3, r3, #INX_bit ; If bits being lost, set inexact + B __stoi_return ; Return + + +_ffix_negative + AND r2, r2, #0xFF ; Mask off exponent field + RSBS r2, r2, #31 + SExp_bias ; Determine shift amount + BLT shift_left_neg ; Negative shift is a shift left, NaN + ; or INF + BEQ special_min_int ; INT_MIN special case + CMP r2, #32 ; See if shifting all bits out + BGE shift_right_32 ; If shifting all bits out, return zero + +shift_right_neg + RSB r1, r2, #32 ; Check for inexact + MOVS r1, r0, LSL r1 ; .. + ORRNE r3, r3, #INX_bit ; .. + MOV r0, r0, LSR r2 ; Shift the result + RSB r0, r0, #0 ; Negate result + B __stoi_return ; Return + +shift_left_neg + ORR r3, r3, #IVO_bit ; Overflow so set invalid operation + RSB r2, r2, #0 ; Get left shift amount + MOV r0, r0, LSL r2 ; Perform shift + RSB r0, r0, #0 ; Negate result + B __stoi_return ; Return + + +special_min_int + TEQ r0, #0x80000000 ; Special case of INT_MIN + RSB r0, r0, #0 ; Negate result + ORRNE r3, r3, #IVO_bit ; Set invalid if not special case + + +__stoi_return + TST r3, #FPECause_mask ; Any exceptions? + ADDEQ sp, sp, #LOC_SIZE ; If not, restore stack and + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; return + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; return + ENDIF + STR r0, [sp, #ExDResl] ; Store default result + ADD r0, sp, #NewResl ; Pointer to new result + MOV r1, r3 ; Exception information + MOV r2, r12 ; Original arg + + CALL FPE_Raise ; Handle exception information + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #NewResl] ; Load new result + ADD sp, sp, #LOC_SIZE ; Pop off exception record + IF Interworking :LOR: Thumbing + LDMFD sp!, {lr} ; Return + BX lr + ELSE + LDMFD sp!, {pc} ; Return + ENDIF + + ENTRY_END __stoi + + END diff --git a/base/Kernel/Native/arm/Crt/r_stoi64.asm b/base/Kernel/Native/arm/Crt/r_stoi64.asm new file mode 100644 index 0000000..d5e0495 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_stoi64.asm @@ -0,0 +1,172 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; __stoi64 single precision floating point to signed 64-bit integer convert +; +; Input: r0 - float to be converted +; Output: r1 - most significant word of converted float in +; 64-bit integer format +; r0 - least significant word of converted float in +; 64-bit integer format +; + + +; Local storage size and offsets +LOC_SIZE EQU 0x18 +OrgOp1h EQU 0x14 +OrgOp1l EQU 0x10 +ExDResh EQU 0x0C +ExDResl EQU 0x08 +NewResh EQU 0x14 +NewResl EQU 0x10 + + + GET fpe.asm + GET kxarm.inc + + AREA |.text|, CODE, READONLY + + Export __stoi64 + IMPORT FPE_Raise + + NESTED_ENTRY __stoi64 + EnterWithLR_16 + STMFD sp!, {lr} ; Save return address + SUB sp, sp, #LOC_SIZE ; Allocate local storage + PROLOG_END + + MOVS r12, r0 ; Save original arg in case of exception + MOVS r2, r0, ASR #SFrc_len ; Right justify exponent, save sign bit + MOV r0, r0, LSL #SExp_len ; Left justify mantissa and + ORR r1, r0, #1 << 31 ; insert hidden one (even for denorms) + MOV r0, #0 ; clear low 32 bits of result + BMI _ffix_negative ; If negative input, separate case + + RSBS r2, r2, #63 + SExp_bias ; Determine shift amount + BLE shift_left ; Negative shift is a shift left, + ; NaN or INF; all are invalid op + CMP r2, #64 ; See if shifting all bits out + BGE shift_right_64 ; If shifting all bits out, return zero + +shift_right + ; r2 contains the right shift amount. It is on the range of 1..63. + CMP r2, #32 ; Check if we are shifting >= 32 + SUBGE r2, r2, #32 ; If so, do a 32 bit shift by moving + MOVGE r0, r1 ; words and adjusting the shift + MOVGE r1, #0 ; amount + + RSB r2, r2, #32 ; 32 - right shift amount + MOVS r3, r0, LSL r2 ; Check for inexact + RSB r3, r2, #32 ; Right shift amount + MOV r0, r0, LSR r3 ; Shift the result + ORR r0, r0, r1, LSL r2 ; .. + MOV r1, r1, LSR r3 ; .. + MOVNE r3, #INX_bit ; If inexact, set inexact + MOVEQ r3, #0 ; Otherwise set no exceptions + B __stoi64_return ; Return + +shift_left + RSB r2, r2, #0 ; Get positive left shift amount + MOV r3, #IVO_bit ; Set invalid + MOV r1, r1, LSL r2 ; Shift result + B __stoi64_return ; Return + +shift_right_64 ; abs(Arg) < 1.0, so losing all bits + MOV r0, #0 ; Return zero + MOV r1, #0 ; Return zero + MOVS r2, r12, LSL #1 ; Check for inexact + MOVNE r3, #INX_bit ; If bits being lost, set inexact + MOVEQ r3, #0 + B __stoi64_return ; Return + + + +_ffix_negative + AND r2, r2, #0xFF ; Mask off exponent field + RSBS r2, r2, #63 + SExp_bias ; Determine shift amount + BLT shift_left_neg ; Negative shift is a shift left, NaN + ; or INF + BEQ special_min_int64 ; INT_MIN special case + CMP r2, #64 ; See if shifting all bits out + BGE shift_right_64 ; If shifting all bits out, return zero + +shift_right_neg + ; r2 contains the right shift amount. It is on the range of 1..63. + CMP r2, #32 ; Check if we are shifting >= 32 + SUBGE r2, r2, #32 ; If so, do a 32 bit shift by moving + MOVGE r0, r1 ; words and adjusting the shift + MOVGE r1, #0 ; amount + + RSB r2, r2, #32 ; 32 - right shift amount + MOVS r3, r0, LSL r2 ; Check for inexact + RSB r3, r2, #32 ; Right shift amount + MOV r0, r0, LSR r3 ; Shift the result + ORR r0, r0, r1, LSL r2 ; .. + MOV r1, r1, LSR r3 ; .. + MOVNE r3, #INX_bit ; If inexact, set inexact + MOVEQ r3, #0 ; Otherwise set no exceptions + RSBS r0, r0, #0 ; Negate result + RSC r1, r1, #0 ; .. + B __stoi64_return ; Return + + +shift_left_neg + RSB r2, r2, #0 ; Get positive left shift amount + MOV r3, #IVO_bit ; Set invalid + MOV r1, r1, LSL r2 ; Shift result + RSBS r0, r0, #0 ; Negate result + RSC r1, r1, #0 ; .. + B __stoi64_return ; Return + + +special_min_int64 + TEQ r1, #0x80000000 ; Special case of INT64_MIN + MOVNE r3, #IVO_bit ; Set invalid if not special case + MOVEQ r3, #0 + RSBS r0, r0, #0 ; Negate result + RSC r1, r1, #0 ; .. + + +__stoi64_return + TST r3, #FPECause_mask ; Any exceptions? + ADDEQ sp, sp, #LOC_SIZE ; If not, restore stack + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; and return + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; and return + ENDIF + STR r0, [sp, #ExDResl] ; Store default result + STR r1, [sp, #ExDResh] ; .. + ADD r0, sp, #NewResl ; Pointer to new result + ORR r1, r3, #_FpSToI64 ; Exception information + MOV r2, r12 ; Original arg + + CALL FPE_Raise ; Handle exception information + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #NewResl] ; Load new result + LDR r1, [sp, #NewResh] ; .. + ADD sp, sp, #LOC_SIZE ; Pop off exception record + IF Interworking :LOR: Thumbing + LDMFD sp!, {lr} ; Return + BX lr + ELSE + LDMFD sp!, {pc} ; Return + ENDIF + + ENTRY_END __stoi64 + + END diff --git a/base/Kernel/Native/arm/Crt/r_stou.asm b/base/Kernel/Native/arm/Crt/r_stou.asm new file mode 100644 index 0000000..378788e --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_stou.asm @@ -0,0 +1,127 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; __stou single precision floating point to unsigned integer convert +; +; Input: r0 - float to be converted +; Output: r0 - converted float in unsigned integer format +; + +; Local stack size and offsets +LOC_SIZE EQU 0x18 +OrgOp1l EQU 0x14 +ExDResl EQU 0x08 +NewResl EQU 0x10 + + GET fpe.asm + GET kxarm.inc + + AREA |.text|, CODE, READONLY + + Export __stou + IMPORT FPE_Raise + + NESTED_ENTRY __stou + EnterWithLR_16 + STMFD sp!, {lr} ; Save return address + SUB sp, sp, #LOC_SIZE ; Allocate local storage + PROLOG_END + + MOVS r12, r0 ; Save original arg in case of exception + MOV r3, #_FpSToU ; Initialize opcode, no exceptions + + MOVS r2, r0, ASR #SFrc_len ; Right justify exponent, save sign bit + MOV r0, r0, LSL #SExp_len ; Left justify mantissa and + ORR r0, r0, #1 << 31 ; insert hidden one (even for denorms) + BMI _ffix_negative ; If negative input, separate case + + RSBS r2, r2, #31 + SExp_bias ; Determine shift amount + BLT shift_left ; Negative shift is a shift left, NaN, + ; or INF; zero shift is an overflow + CMP r2, #32 ; See if shifting all bits out + BGE shift_right_32 ; If shifting all bits out, return zero + +shift_right + RSB r1, r2, #32 ; Check for inexact + MOVS r1, r0, LSL r1 ; .. + ORRNE r3, r3, #INX_bit ; .. + MOV r0, r0, LSR r2 ; Shift the result + B __stou_return ; Return + +shift_left + ORR r3, r3, #IVO_bit ; Overflow so set invalid operation + RSB r2, r2, #0 ; Get left shift amount + MOV r0, r0, LSL r2 ; Perform shift + B __stou_return ; Return + +shift_right_32 ; abs(Arg) < 1.0, so losing all bits + BIC r3, r3, #IVO_bit ; Ensure invalid is clear + MOV r0, #0 ; Return zero + MOVS r1, r12, LSL #1 ; Check for inexact + ORRNE r3, r3, #INX_bit ; If bits being lost, set inexact + B __stou_return ; Return + +_ffix_negative + ORR r3, r3, #IVO_bit ; Have a negative so invalid + AND r2, r2, #0xFF ; Mask off exponent field + RSBS r2, r2, #31 + SExp_bias ; Determine shift amount + BLT shift_left_neg ; Negative shift is a shift left, NaN + ; or INF + CMP r2, #32 ; See if shifting all bits out + BGE shift_right_32 ; If shifting all bits out, return zero + ; note that this is not an invalid + ; operation exception + +shift_right_neg + MOV r0, r0, LSR r2 ; Shift the result + RSB r0, r0, #0 ; Negate result + B __stou_return ; Return + +shift_left_neg + RSB r2, r2, #0 ; Get left shift amount + MOV r0, r0, LSL r2 ; Perform shift + RSB r0, r0, #0 ; Negate result + B __stou_return ; Return + +__stou_return + TST r3, #FPECause_mask ; Any exceptions? + ADDEQ sp, sp, #LOC_SIZE ; If not, restore stack and + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; return + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; return + ENDIF + STR r0, [sp, #ExDResl] ; Store default result + ADD r0, sp, #NewResl ; Pointer to new result + MOV r1, r3 ; Exception information + MOV r2, r12 ; Original arg + + CALL FPE_Raise ; Handle exception information + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #NewResl] ; Load new result + ADD sp, sp, #LOC_SIZE ; Pop off exception record + IF Interworking :LOR: Thumbing + LDMFD sp!, {lr} ; Return + BX lr + ELSE + LDMFD sp!, {pc} ; Return + ENDIF + + ENTRY_END __stou + + + END diff --git a/base/Kernel/Native/arm/Crt/r_stou64.asm b/base/Kernel/Native/arm/Crt/r_stou64.asm new file mode 100644 index 0000000..973852b --- /dev/null +++ b/base/Kernel/Native/arm/Crt/r_stou64.asm @@ -0,0 +1,159 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; __stou64 single precision floating point to unsigned 64-bit integer convert +; +; Input: r0 - float to be converted +; Output: r1 - most significant word of converted float in +; unsigned 64-bit integer format +; r0 - least significant word of converted float in +; unsigned 64-bit integer format +; + +; Local storage size and offsets +LOC_SIZE EQU 0x18 +OrgOp1h EQU 0x14 +OrgOp1l EQU 0x10 +ExDResh EQU 0x0C +ExDResl EQU 0x08 +NewResh EQU 0x14 +NewResl EQU 0x10 + + + GET fpe.asm + GET kxarm.inc + + AREA |.text|, CODE, READONLY + + Export __stou64 + IMPORT FPE_Raise + + NESTED_ENTRY __stou64 + EnterWithLR_16 + STMFD sp!, {lr} ; Save return address + SUB sp, sp, #LOC_SIZE ; Allocate local storage + PROLOG_END + MOVS r12, r0 ; Save original arg in case of exception + MOVS r2, r0, ASR #SFrc_len ; Right justify exponent, save sign bit + MOV r0, r0, LSL #SExp_len ; Left justify mantissa and + ORR r1, r0, #1 << 31 ; insert hidden one (even for denorms) + MOV r0, #0 ; clear low 32 bits of result + BMI _ffix_negative ; If negative input, separate case + + RSBS r2, r2, #63 + SExp_bias ; Determine shift amount + BLT shift_left ; Negative shift is a shift left, + ; NaN or INF; all are invalid op + CMP r2, #64 ; See if shifting all bits out + BGE shift_right_64 ; If shifting all bits out, return zero + +shift_right + ; r2 contains the right shift amount. It is on the range of 1..63. + CMP r2, #32 ; Check if we are shifting >= 32 + SUBGE r2, r2, #32 ; If so, do a 32 bit shift by moving + MOVGE r0, r1 ; words and adjusting the shift + MOVGE r1, #0 ; amount + + RSB r2, r2, #32 ; 32 - right shift amount + MOVS r3, r0, LSL r2 ; Check for inexact + RSB r3, r2, #32 ; Right shift amount + MOV r0, r0, LSR r3 ; Shift the result + ORR r0, r0, r1, LSL r2 ; .. + MOV r1, r1, LSR r3 ; .. + MOVNE r3, #INX_bit ; If inexact, set inexact + MOVEQ r3, #0 ; Otherwise set no exceptions + B __stou64_return ; Return + +shift_left + RSB r2, r2, #0 ; Get positive left shift amount + MOV r3, #IVO_bit ; Set invalid + MOV r1, r1, LSL r2 ; Shift result + B __stou64_return ; Return + +shift_right_64 ; abs(Arg) < 1.0, so losing all bits + MOV r0, #0 ; Return zero + MOV r1, #0 ; Return zero + MOVS r2, r12, LSL #1 ; Check for inexact + MOVNE r3, #INX_bit ; If bits being lost, set inexact + MOVEQ r3, #0 + B __stou64_return ; Return + + + +_ffix_negative + AND r2, r2, #0xFF ; Mask off exponent field + RSBS r2, r2, #63 + SExp_bias ; Determine shift amount + BLE shift_left_neg ; Negative shift is a shift left, NaN + ; or INF + CMP r2, #64 ; See if shifting all bits out + BGE shift_right_64 ; If shifting all bits out, return zero + +shift_right_neg + ; r2 contains the right shift amount. It is on the range of 1..63. + CMP r2, #32 ; Check if we are shifting >= 32 + SUBGE r2, r2, #32 ; If so, do a 32 bit shift by moving + MOVGE r0, r1 ; words and adjusting the shift + MOVGE r1, #0 ; amount + + RSB r2, r2, #32 ; 32 - right shift amount + RSB r3, r2, #32 ; Right shift amount + MOV r0, r0, LSR r3 ; Shift the result + ORR r0, r0, r1, LSL r2 ; .. + MOV r1, r1, LSR r3 ; .. + RSBS r0, r0, #0 ; Negate result + RSC r1, r1, #0 ; .. + MOV r3, #IVO_bit ; Set invalid + B __stou64_return ; Return + + +shift_left_neg + MOV r3, #IVO_bit ; Set invalid + RSB r2, r2, #0 ; Get positive left shift amount + MOV r1, r1, LSL r2 ; Shift result + RSBS r0, r0, #0 ; Negate result + RSC r1, r1, #0 ; .. + B __stou64_return ; Return + + +__stou64_return + TST r3, #FPECause_mask ; Any exceptions? + ADDEQ sp, sp, #LOC_SIZE ; If not, restore stack + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; and return + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; and return + ENDIF + STR r0, [sp, #ExDResl] ; Store default result + STR r1, [sp, #ExDResh] ; .. + ADD r0, sp, #NewResl ; Pointer to new result + ORR r1, r3, #_FpSToU64 ; Exception information + MOV r2, r12 ; Original arg + + CALL FPE_Raise ; Handle exception information + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #NewResl] ; Load new result + LDR r1, [sp, #NewResh] ; .. + ADD sp, sp, #LOC_SIZE ; Pop off exception record + IF Interworking :LOR: Thumbing + LDMFD sp!, {lr} ; Return + BX lr + ELSE + LDMFD sp!, {pc} ; Return + ENDIF + + ENTRY_END __stou64 + + END diff --git a/base/Kernel/Native/arm/Crt/regnames.asm b/base/Kernel/Native/arm/Crt/regnames.asm new file mode 100644 index 0000000..17f01d7 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/regnames.asm @@ -0,0 +1,89 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; Assembler source for FPA support code and emulator +; ================================================== +; Register allocations. Also used by "fplib". + +; Register usage at top level in undefined instruction handler. + +Rsp RN R13 ;Our stack pointer +Rfp RN R12 ;Our "frame pointer" - points to ARM register dump +Rins RN R11 ;Offending instruction itself +Rwp RN R10 ;The workspace pointer/an entry sequence temporary +Rtmp2 RN R9 ;A temporary +Rtmp RN R8 ;A temporary + +; Register usage in the arithmetic and rounding routines, and the Prepare +; and Round stage exception routines. Rfpsr, Rins, Rwp, Rfp and Rsp are +; also part of this interface. + +OP1sue RN R1 ;The sign, uncommon bit and (sometimes) exponent of + ; the first or only operand +OP1mhi RN R0 ;The mantissa of the first or only operand - high + ; word +OP1mlo RN R2 ;The mantissa of the first or only operand - low + ; word +RNDexp RN R3 ;The exponent of the number being rounded +OP2sue RN R3 ;The sign, uncommon bit and (sometimes) exponent of + ; the second operand +RNDdir RN R4 ;Direction number has already been rounded (0 = exact, + ; positive = rounded up, negative = rounded down) +OP2mhi RN R5 ;The mantissa of the second operand - high word +RNDprm RN R5 ;Precision and rounding mode for rounding, as two + ; adjacent 2 bit fields, lower one at position RM_pos + ; within word, higher one at RM_pos+2. +OP2mlo RN R4 ;The mantissa of the second operand - low word +Rarith RN R6 ;A temporary +Rfpsr RN R7 ;FPSR contents + +; Two more temporaries, used when accessing ARM registers. The requirements +; are (a) that they are not equal to any of the result registers (OP1sue, +; OP1mhi, OP1mlo and Rarith); (b) that they are not equal to Rfpsr, Rins, +; Rwp, Rfp or Rsp; (c) that they are not banked registers; (d) that they +; are not equal to RNDprm. + +Rregno RN R3 +Rregval RN R4 + + ASSERT Rregno <> OP1sue + ASSERT Rregno <> OP1mhi + ASSERT Rregno <> OP1mlo + ASSERT Rregno <> Rfpsr + ASSERT Rregno <> Rins + ASSERT Rregno <> Rwp + ASSERT Rregno <> Rfp + ASSERT Rregno <> Rsp + ASSERT Rregno < R8 + ASSERT Rregno <> RNDprm + + ASSERT Rregval <> OP1sue + ASSERT Rregval <> OP1mhi + ASSERT Rregval <> OP1mlo + ASSERT Rregval <> Rfpsr + ASSERT Rregval <> Rins + ASSERT Rregval <> Rwp + ASSERT Rregval <> Rfp + ASSERT Rregval <> Rsp + ASSERT Rregval < R8 + ASSERT Rregval <> RNDprm + + ASSERT Rregno <> Rregval + +; Some commonly used register lists: + +OP1regs RLIST {OP1mhi,OP1sue,OP1mlo} +; ASSERT OP1sue < OP1mhi + ASSERT OP1mhi < OP1mlo + +OP2regs RLIST {OP2sue,OP2mlo,OP2mhi} + ASSERT OP2sue < OP2mhi +; ASSERT OP2mhi < OP2mlo + + END diff --git a/base/Kernel/Native/arm/Crt/veneer.asm b/base/Kernel/Native/arm/Crt/veneer.asm new file mode 100644 index 0000000..0e33ec7 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/veneer.asm @@ -0,0 +1,118 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; veneer.s +; +; Copyright (C) Advanced RISC Machines Limited, 1994. All rights reserved. +; +; RCS Revision: 1 +; Checkin Date: 2007/06/29 02:59:16 +; Revising Author + +;=========================================================================== +;Veneers onto the arith.asm functions. +; +;This block should be assembled multiple times, once for each function. +;The possible functions are: +; +; addsub_s shared add and subtract +; mul_s shared multiply +; div_s shared divide + + GET fpe.asm + + [ :DEF: thumb + CODE32 + ] + +;=========================================================================== +; Veneer functions + + [ :DEF: addsub_s + + AREA |.text|, CODE, READONLY + + EXPORT __fp_addsub_common + EXPORT __fp_addsub_uncommon + + ] + +;------------------------------------------------------------------------------ + + [ :DEF: mul_s + + AREA |.text|, CODE, READONLY + + EXPORT __fp_mult_common + EXPORT __fp_mult_fast_common + EXPORT __fp_mult_uncommon + + ] + +;--------------------------------------------------------------------------- + + [ :DEF: div_s + + AREA |.text|, CODE, READONLY + + EXPORT __fp_div_common + EXPORT __fp_rdv_common + EXPORT __fp_div_uncommon + EXPORT __fp_rdv_uncommon + + ] + +;--------------------------------------------------------------------------- + + [ :DEF: sqrt_s + + AREA |.text|, CODE, READONLY + + EXPORT __fp_sqrt_common + EXPORT __fp_sqrt_uncommon + + ] + +;--------------------------------------------------------------------------- + + [ :DEF: fix_s + + AREA |.text|, CODE, READONLY + + EXPORT __fp_fix_common + EXPORT __fp_fix_uncommon + + ] + +;--------------------------------------------------------------------------- + + [ :DEF: fixu_s + + AREA |.text|, CODE, READONLY + + EXPORT __fp_fixu_common + EXPORT __fp_fixu_uncommon + + ] + +;=========================================================================== + + [ :DEF: compare_s + + AREA |.text|, CODE, READONLY + + EXPORT __fp_compare + + ] + +;=========================================================================== + + GET arith.asm + + END diff --git a/base/Kernel/Native/arm/Crt/veneer_d.asm b/base/Kernel/Native/arm/Crt/veneer_d.asm new file mode 100644 index 0000000..e5dde78 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/veneer_d.asm @@ -0,0 +1,1115 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; veneer_d.s +; +; Copyright (C) Advanced RISC Machines Limited, 1994. All rights reserved. +; +; RCS Revision: 1 +; Checkin Date: 2007/06/29 02:59:16 +; Revising Author + +;=========================================================================== +;Veneers onto the arith.asm functions. +; +;This block should be assembled multiple times, once for each function. +;The possible functions are: +; +; addsub_s double precision add and subtract +; mul_s double precision multiply +; div_s double precision divide +; fmod_s implementation of math.h's fmod() [REM] +; sqrt_s implementation of math.h's sqrt() [SQRT] + + GET fpe.asm + GET kxarm.inc + +;=========================================================================== + +; When veneering these functions we need to be able to convert from double +; to extended on entry and back again on exit. This macro provides the +; conversion function. + +; *WARNING* If no ulabel is set then the next instruction is skipped in +; the case of a number that needs normalizing. This is INTENTIONAL, since +; this macro leaves the Z flag set in the case of an uncommon case, but +; might also leave it set in the case of a denorm, so the following, +; conditional, instruction is skipped. It might be better if ulabel weren't +; there at all, just to make it explicit. +; The __fp_norm_opx functions should also do the skipping, rather than +; hacking lr in the fast path. This should be fixed, but for the moment +; I'd rather not be so disgustingly vile. + + MACRO +$label DoubleToInternal $op,$zlabel,$ulabel + ASSERT ($op = 1) :LOR: ($op = 2) +$label MOVS tmp,dOP$op.h,LSL #1 ;C:=sign; Z:=exp & frac.top zero + TEQEQ dOP$op.l,#0 ;C unchanged; Z:=value is a zero + [ "$zlabel" <> "" + BEQ $zlabel ;Possible early abort + ] + MOV Rtmp,tmp,LSL #DExp_len-1 ;Frac.top in bits 30:11 of mhi + MOV dOP$op.h,tmp,LSR #DExp_pos ;Exponent in bits 11:1 + MOV OP$op.mlo,dOP$op.l,LSL #DExp_len; OP2mhi and 31:11 of OP2mlo + ORR OP$op.mhi,Rtmp,dOP$op.l,LSR #DFhi_len+1 + ;Fraction in bits 30:0 of + ADDNE dOP$op.h,dOP$op.h,#(EIExp_bias - DExp_bias):SHL:1 + MOV OP$op.sue,dOP$op.h,RRX ;Recombine sign and exponent + ORRNE OP$op.mhi,OP$op.mhi,#EIUnits_bit + + ; Gets here with the *double precision* exponent in the top 11 bits + ; of tmp. (Exponent<<1+DExp_pos.) We use a sign extended shift to + ; spot the "maximum exponent case" - leaves us with -1 in tmp. + MOVS tmp,tmp,ASR #1+DExp_pos + ADDEQ lr,pc,#8 ;Skip two instructions past normalise call + BEQ __fp_norm_op$op + CMP tmp,#&ffffffff + [ "$ulabel" <> "" + BEQ $ulabel + ] + MEND + + MACRO + InternalToDouble + BL __fp_e2d + TST a4,#Error_bit + VReturn EQ + ORR a4,a4,#Double_bit + B __fp_veneer_error + MEND + + MACRO + Double $name + IMPORT __fp_e2d + IMPORT __fp_norm_op1 + IMPORT __fp_norm_op2 + MEND + +;=========================================================================== +; Veneer functions + + [ :DEF: add_s + +; Local stack size and offset defines +LOC_SIZE EQU 0x24 ; Size of local storage on stack +ORG_OP2h EQU 0x20 ; Original Operand 2 high half +ORG_OP2l EQU 0x1C ; Original Operand 2 high low +ORG_OP1h EQU 0x18 ; Original Operand 1 high half +ORG_OP1l EQU 0x14 ; Original Operand 1 high low +OPCODE EQU 0x10 ; Opcode (_FpAddD or _FpSubD) +ExDRESh EQU 0x0C ; Exception record default result high +ExDRESl EQU 0x08 ; Exception record default result low +ExOp2h EQU 0x04 ; Exception record operand 2 high half +ExOp2l EQU 0x00 ; Exception record operand 2 low half +ExNewResh EQU 0x14 ; Exception new result high +ExNewResl EQU 0x10 ; Exception new result low + + + AREA |.text|, CODE, READONLY + Double add + +; This is the veneer onto __addd, _drsb and __subd. + + Export __addd + IMPORT __fp_addsub_common + IMPORT __fp_addsub_uncommon + IMPORT FPE_Raise + + [ :LNOT: :DEF: thumb + + Export __subd + + +; __subd and __addd prologues must be the same. + NESTED_ENTRY __subd + ;VEnter_16 ; Includes extra ARM entry point + STMFD sp!, veneer_s ; Save off non-volatiles + SUB sp, sp, #LOC_SIZE ; Local storage + PROLOG_END + MOV r5, #_FpSubD ; Double precision subtract + STR r3, [sp, #ORG_OP2h] ; Save off original args in case of exception + STR r2, [sp, #ORG_OP2l] + STR r1, [sp, #ORG_OP1h] + STR r0, [sp, #ORG_OP1l] + +;For sub we just invert the sign of operand 2 and branch to add. + + EOR dOP2h, dOP2h, #Sign_bit + B coreadd + ENTRY_END __subd + ] + +; __addd and __subd prologues must be the same. + NESTED_ENTRY __addd + ;VEnter_16 ; Includes extra ARM entry point + STMFD sp!, veneer_s ; Save off non-volatiles + SUB sp, sp, #LOC_SIZE ; Local storage + PROLOG_END + + MOV r5, #_FpAddD ; Double precision addition + STR r3, [sp, #ORG_OP2h] ; Save off original args in case of exception + STR r2, [sp, #ORG_OP2l] + STR r1, [sp, #ORG_OP1h] + STR r0, [sp, #ORG_OP1l] + +coreadd + STR r5, [sp, #OPCODE] ; Save off operation code in case of exception + + ; Catch the NaNs and INFs here. + MOV r4, r1, LSL #1 + MOV r4, r4, LSR #21 + ADD r4, r4, #1 + CMP r4, #2048 + BEQ dadd_arg1_nan_inf ; Arg1 is a NaN/INF + + ; Arg1 is finite + MOV r4, r3, LSL #1 + MOV r4, r4, LSR #21 + ADD r4, r4, #1 + CMP r4, #2048 + BEQ dadd_arg2_nan_inf ; Arg2 is a NaN/INF + + DoubleToInternal 2,,dadd_uncommon1 + DoubleToInternal 1,,dadd_uncommon + + BL __fp_addsub_common + +dadd_return + BL __fp_e2d + +dadd_check_cause + TST r3, #FPECause_mask + ADDEQ sp, sp, #LOC_SIZE + + ; VReturn EQ + LDMEQFD sp!,veneer_s + IF Interworking :LOR: Thumbing + BXEQ lr + ELSE + MOVEQ pc, lr + ENDIF + + LDR r12, [sp, #OPCODE] ; Get saved operation code + STR r0, [sp, #ExDRESl] + STR r1, [sp, #ExDRESh] + LDR r0, [sp, #ORG_OP2l] + LDR r1, [sp, #ORG_OP2h] + STR r0, [sp, #ExOp2l] + STR r1, [sp, #ExOp2h] + LDR r2, [sp, #ORG_OP1l] + ORR r1, r3, r12 + LDR r3, [sp, #ORG_OP1h] + ADD r0, sp, #ExNewResl + + CALL FPE_Raise + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #ExNewResl] + LDR r1, [sp, #ExNewResh] + ADD sp, sp, #LOC_SIZE + LDMFD sp!, veneer_s + IF Interworking :LOR: Thumbing + BX lr + ELSE + MOV pc, lr + ENDIF + + + +dadd_uncommon1 + ORR OP2sue,OP2sue,#Uncommon_bit + DoubleToInternal 1 ; Skips next instruction if denorm +dadd_uncommon + ORREQ OP1sue,OP1sue,#Uncommon_bit + ADR lr,dadd_return + MOV Rins,#Double_mask + B __fp_addsub_uncommon + + + + +; Arg1 is an INF or a NaN. +; If Arg1 is an SNaN, signal invalid and return a QNaN. +; Else if Arg1 is a QNaN, check Arg2 for an SNaN. +; If Arg2 is an SNaN, signal invalid. +; Return the Arg1 QNaN. +; Else it is an INF +; If Arg2 is an SNaN, signal invalid and return a QNaN version of Arg2. +; Else if Arg2 is a QNaN, return it. +; Else if Arg2 is an INF of the same sign, return it. +; Else if Arg2 is an INF of the opposite sign, set invalid and return a QNaN. +; Else return the INF. +dadd_arg1_nan_inf + ORRS r4, r0, r1, LSL #12 ; Mantissa1 == 0? + BEQ dadd_arg1_inf ; Mantissa1 == 0 so is an INF + TST r1, #dSignalBit ; Arg1 == SNaN? + ORREQ r5, r5, #IVO_bit ; If Arg1 == SNaN, signal invalid + BEQ dadd_return_qnan ; and return a QNaN version of it + MOV r4, r3, LSL #1 ; Extract exponent2 + MOV r4, r4, LSR #21 ; .. + ADD r4, r4, #1 ; Exponent2 == 2047? + CMP r4, #2048 ; .. + BNE dadd_return_qnan ; If !=, cannot be an SNaN + ORRS r4, r2, r3, LSL #12 ; Mantissa2 == 0? + BEQ dadd_return_qnan ; If == 0, cannot be an SNaN + TST r3, #dSignalBit ; Arg2 is a NaN. Is it an SNaN? + ORREQ r5, r5, #IVO_bit ; If SNaN, set invalid + B dadd_return_qnan ; Return Arg1 QNaN + + +dadd_arg1_inf + MOVS r4, r3, LSL #1 ; Extract exponent2 + MOV r4, r4, LSR #21 ; .. + ADD r4, r4, #1 ; Exponent2 == 2047? + CMP r4, #2048 ; .. + MOVNE r3, r5 ; Copy exception information + BNE dadd_check_cause ; and return the INF + ORRS r4, r2, r3, LSL #12 ; Mantissa2 == 0? + BEQ dadd_check_inf ; If == 0, have an INF + MOV r1, r3 ; Arg2 is a NaN, so we need to copy the + MOV r0, r2 ; mantissa bits to return in the QNaN. + TST r3, #dSignalBit ; Is Arg2 an SNaN? + ORREQ r5, r5, #IVO_bit ; If SNaN, set invalid + B dadd_return_qnan ; Return Arg2 QNaN + +dadd_check_inf + EORS r4, r1, r3 ; Check signs + MOVPL r3, r5 ; Copy exception information + BPL dadd_check_cause ; Return the INF + ORR r5, r5, #IVO_bit + B dadd_return_qnan ; Return a QNaN + + + +; Arg2 is a NaN or an INF. Arg1 is a finite non-zero number. +; If Arg2 is an INF, return it. +; Else if Arg2 is a QNaN, return it. +; Else if Arg2 is an SNaN, signal invalid and return a QNaN version of it. +dadd_arg2_nan_inf + MOV r1, r3 ; Arg2 is a NaN or INF. We need to copy + MOV r0, r2 ; the bits to the Arg1 registers. + ORRS r4, r2, r3, LSL #12 ; Mantissa2 == 0? + BEQ dadd_check_cause ; .. + TST r3, #dSignalBit ; Is Arg2 an SNaN? + ORREQ r5, r5, #IVO_bit ; If SNaN, set invalid + ; Fall through to return QNaN + + +; Returns a QNaN. R1 and R0 must contain the mantissa portion +; of the QNaN. SNaNs are converted to QNaNs here. +dadd_return_qnan + ORR r1, r1, #0x7F000000 ; Set exponent = 0x7FF + ORR r1, r1, #0x00F80000 ; ... and set mantissa[MSb] = 1 + MOV r3, r5 ; Move exception information + B dadd_check_cause + + ENTRY_END __addd + + ] + +;--------------------------------------------------------------------------- + + [ {FALSE} :LAND: :DEF: sub_s :LAND: :DEF: thumb + + AREA |.text|, CODE, READONLY + Double sub + + Export __subd + + IMPORT __addd + +__subd VEnter_16 + + EOR dOP2h, dOP2h, #Sign_bit + ; Just do a tail call to addd. In the THUMB world, code density is + ; king. (The addition skips the LDM on the __addd entry point, and + ; is dangerous.) + B __addd+4 + + [ {FALSE} + DoubleToInternal 2,,dsub_uncommon1 + DoubleToInternal 1,,dsub_uncommon + + BL __fp_addsub_common + +dsub_return + InternalToDouble + +dsub_uncommon1 + ORR OP2sue,OP2sue,#Uncommon_bit + DoubleToInternal 1 ; Skips next instruction if denorm +dsub_uncommon + ORREQ OP1sue,OP1sue,#Uncommon_bit + ADR lr,dsub_return + MOV Rins,#Double_mask + B __fp_addsub_uncommon + + ] + + ] + +;--------------------------------------------------------------------------- + + [ :DEF: rsb_s :LAND: :DEF: thumb + + CodeArea |FPL$$drsb| + Double rsb + + Export _drsb + IMPORT __addd + +_drsb VEnter_16 + + EOR dOP1h, dOP1h, #Sign_bit + ; Same as above - branch to add code. + B __addd+4 + + [ {FALSE} + + DoubleToInternal 2,,drsb_uncommon1 + DoubleToInternal 1,,drsb_uncommon + + BL __fp_addsub_common + +drsb_return + InternalToDouble + +drsb_uncommon1 + ORR OP2sue,OP2sue,#Uncommon_bit + DoubleToInternal 1 ; Skips next instruction if denorm +drsb_uncommon + ORREQ OP1sue,OP1sue,#Uncommon_bit + ADR lr,drsb_return + MOV Rins,#Double_mask + B __fp_addsub_uncommon + + ] + + ] + +;--------------------------------------------------------------------------- + + [ :DEF: mul_s + +; Local stack size and offset defines +LOC_SIZE EQU 0x20 ; Size of local storage on stack +OrgOP2h EQU 0x1C ; Original Operand 2 high half +OrgOP2l EQU 0x18 ; Original Operand 2 high low +OrgOP1h EQU 0x14 ; Original Operand 1 high half +OrgOP1l EQU 0x10 ; Original Operand 1 high low +ExDRESh EQU 0x0C ; Exception record default result high +ExDRESl EQU 0x08 ; Exception record default result low +ExOp2h EQU 0x04 ; Exception record operand 2 high half +ExOp2l EQU 0x00 ; Exception record operand 2 low half +ExNewResh EQU 0x14 ; Exception new result high +ExNewResl EQU 0x10 ; Exception new result low + + AREA |.text|, CODE, READONLY + Double mul + + Export __muld + IMPORT __fp_mult_common + IMPORT __fp_mult_uncommon + IMPORT FPE_Raise + + NESTED_ENTRY __muld + ; VEnter_16 + STMFD sp!, veneer_s ; Save off non-volatiles + SUB sp, sp, #LOC_SIZE ; Local storage + PROLOG_END + + STR r3, [sp, #OrgOP2h] ; Save off original args in case of exception + STR r2, [sp, #OrgOP2l] + STR r1, [sp, #OrgOP1h] + STR r0, [sp, #OrgOP1l] + + ; Catch the NaNs, INFs, and Zeros here. + MOV r5, #0 ; Exception information initialized to none. + ORRS r4, r0, r1, LSL #1 + BEQ dmul_arg1_zero ; Arg1 is zero + MOV r4, r1, LSL #1 + MOV r4, r4, LSR #21 + ADD r4, r4, #1 + CMP r4, #2048 + BEQ dmul_arg1_nan_inf ; Arg1 is a NaN/INF + + ; Arg1 is non-zero and finite + ORRS r4, r2, r3, LSL #1 + BEQ dmul_return_zero ; Arg2 is zero so just return a zero + MOV r4, r3, LSL #1 + MOV r4, r4, LSR #21 + ADD r4, r4, #1 + CMP r4, #2048 + BEQ dmul_arg2_nan_inf ; Arg2 is a NaN/INF + + + DoubleToInternal 2,,dmul_uncommon1 + DoubleToInternal 1,,dmul_uncommon + + BL __fp_mult_common + +dmul_return + BL __fp_e2d + +dmul_check_cause + TST r3, #FPECause_mask + ADDEQ sp, sp, #LOC_SIZE + + ; VReturn EQ + LDMEQFD r13!,veneer_s + IF Interworking :LOR: Thumbing + BXEQ lr + ELSE + MOVEQ pc, lr + ENDIF + + STR r0, [sp, #ExDRESl] + STR r1, [sp, #ExDRESh] + LDR r0, [sp, #OrgOP2l] + LDR r1, [sp, #OrgOP2h] + STR r0, [sp, #ExOp2l] + STR r1, [sp, #ExOp2h] + LDR r2, [sp, #OrgOP1l] + ORR r1, r3, #_FpMulD + LDR r3, [sp, #OrgOP1h] + ADD r0, sp, #ExNewResl + + CALL FPE_Raise + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #ExNewResl] + LDR r1, [sp, #ExNewResh] + ADD sp, sp, #LOC_SIZE + LDMFD sp!, veneer_s + IF Interworking :LOR: Thumbing + BX lr + ELSE + MOV pc, lr + ENDIF + +dmul_uncommon1 + ORR OP2sue,OP2sue,#Uncommon_bit + DoubleToInternal 1 ; Skips next instruction if denorm +dmul_uncommon + ORREQ OP1sue,OP1sue,#Uncommon_bit + ADR lr,dmul_return + MOV Rins,#Double_mask + B __fp_mult_uncommon + + +; Arg1 is a zero. If Arg2 isn't a NaN or an INF, we return a zero. +; If Arg2 is an INF, we have an invalid operation and return a QNaN. +; If Arg2 is a QNaN, we return the QNaN. +; If Arg2 is an SNaN, we return a QNaN and signal invalid operation. +dmul_arg1_zero + MOV r4, r3, LSL #1 ; Extract exponent2 + MOV r4, r4, LSR #21 ; .. + ADD r4, r4, #1 ; Exponent2 == 2047? + CMP r4, #2048 ; .. + BNE dmul_return_zero ; If != 2047, return 0 + ORRS r4, r2, r3, LSL #12 ; Else if mantissa2==0 + ORREQ r5, r5, #IVO_bit ; If ==, invalid op + BEQ dmul_return_qnan ; return QNaN + MOV r1, r3 ; Else have a NaN so copy mantissas to + MOV r0, r2 ; return QNaN and ... + TST r1, #dSignalBit ; check for an SNaN + ORREQ r5, r5, #IVO_bit ; If clear, have SNaN so invalid operation + B dmul_return_qnan + + +; Arg1 is an INF or a NaN. +; If it is an SNaN, signal invalid and return a QNaN. +; else if it is a QNaN, check Arg2 for an SNaN. +; If Arg2 is an SNaN, signal invalid. +; Return the Arg1 QNaN. +; Else it is an INF +; If Arg2 is an SNaN, signal invalid and return a QNaN version of Arg2. +; Else if Arg2 is a QNaN, return it. +; Else if Arg2 is an INF, return an INF. +; Else if Arg2 is a zero, signal invalid and return a QNaN. +dmul_arg1_nan_inf + ORRS r4, r0, r1, LSL #12 ; Mantissa1 == 0? + BEQ dmul_arg1_inf ; Mantissa1 == 0 so is an INF + TST r1, #dSignalBit ; Arg1 == SNaN? + ORREQ r5, r5, #IVO_bit ; If Arg1 == SNaN, signal invalid + BEQ dmul_return_qnan ; and return a QNaN version of it + MOV r4, r3, LSL #1 ; Extract exponent2 + MOV r4, r4, LSR #21 ; .. + ADD r4, r4, #1 ; Exponent2 == 2047? + CMP r4, #2048 ; .. + BNE dmul_return_qnan ; If !=, cannot be an SNaN + ORRS r4, r2, r3, LSL #12 ; Mantissa2 == 0? + BEQ dmul_return_qnan ; If == 0, cannot be an SNaN + TST r3, #dSignalBit ; Arg2 is a NaN. Is it an SNaN? + ORREQ r5, r5, #IVO_bit ; If SNaN, set invalid + B dmul_return_qnan ; Return Arg1 QNaN + + +dmul_arg1_inf + ORRS r4, r2, r3, LSL #1 ; Arg2 == 0? + ORREQ r5, r5, #IVO_bit ; If == 0, signal invalid, return + BEQ dmul_return_qnan ; return a QNaN + MOVS r4, r3, LSL #1 ; Extract exponent2 + MOV r4, r4, LSR #21 ; .. + ADD r4, r4, #1 ; Exponent2 == 2047? + CMP r4, #2048 ; .. + BNE dmul_return_inf ; If !=, cannot be a NaN or INF + ORRS r4, r2, r3, LSL #12 ; Mantissa2 == 0? + BEQ dmul_return_inf ; If == 0, cannot be a NaN + MOV r1, r3 ; Arg2 is a NaN, so we need to copy the + MOV r0, r2 ; mantissa bits to return in the QNaN. + TST r3, #dSignalBit ; Is Arg2 an SNaN? + ORREQ r5, r5, #IVO_bit ; If SNaN, set invalid + B dmul_return_qnan ; Return Arg2 QNaN + + +; Arg2 is a NaN or an INF. Arg1 is a finite non-zero number. +; If Arg2 is an INF, return it. +; Else if Arg2 is a QNaN, return it. +; Else if Arg2 is an SNaN, signal invalid and return a QNaN version of it. +dmul_arg2_nan_inf + ORRS r4, r2, r3, LSL #12 ; Mantissa2 == 0? + BEQ dmul_return_inf ; If == 0, Arg2 is an INF, so return it. + MOV r1, r3 ; Arg2 is a NaN, so we need to copy the + MOV r0, r2 ; mantissa bits to return in the QNaN. + TST r3, #dSignalBit ; Is Arg2 an SNaN? + ORREQ r5, r5, #IVO_bit ; If SNaN, set invalid + B dmul_return_qnan ; Return Arg2 QNaN + + + +; Returns a QNaN. R1 and R0 must contain the mantissa portion +; of the QNaN. SNaNs are converted to QNaNs here. +dmul_return_qnan + ORR r1, r1, #0x7F000000 ; Set exponent = 0x7FF + ORR r1, r1, #0x00F80000 ; ... and set mantissa[MSb] = 1 + MOV r3, r5 ; Move exception information + B dmul_check_cause + + +; Returns a properly signed INF. r1 and r3 must contain the +; sign bits in the MSb. +dmul_return_inf + EORS r4, r1, r3 ; Check signs of Arg1 and Arg2 + MOV r0, #0 ; Clear mantissa2 ... + MOV r1, #0x7F000000 ; ... and set exponent = 0x7FF + ORR r1, r1, #0x00F00000 ; .. + ORRMI r1, r1, #0x80000000 ; Set sign bit if negative + MOV r3, r5 ; Move exception information + B dmul_check_cause + +; Returns a properly signed zero. r1 and r3 must contain the +; sign bits in the MSb. +dmul_return_zero + EORS r4, r1, r3 ; Check signs of Arg1 and Arg2 + MOV r0, #0 ; Clear sign, exponent, and mantissa + MOV r1, #0 ; .. + ORRMI r1, r1, #0x80000000 ; Set sign bit if negative + MOV r3, r5 ; Move exception information + B dmul_check_cause + + + ENTRY_END __muld + ] + +;--------------------------------------------------------------------------- + + [ :DEF: div_s + +; Local stack size and offset defines +LOC_SIZE EQU 0x20 ; Size of local storage on stack +OrgOP2h EQU 0x1C ; Original Operand 2 high half +OrgOP2l EQU 0x18 ; Original Operand 2 high low +OrgOP1h EQU 0x14 ; Original Operand 1 high half +OrgOP1l EQU 0x10 ; Original Operand 1 high low +ExDRESh EQU 0x0C ; Exception record default result high +ExDRESl EQU 0x08 ; Exception record default result low +ExOp2h EQU 0x04 ; Exception record operand 2 high half +ExOp2l EQU 0x00 ; Exception record operand 2 low half +ExNewResh EQU 0x14 ; Exception new result high +ExNewResl EQU 0x10 ; Exception new result low + + AREA |.text|, CODE, READONLY + Double div + + Export __divd + IMPORT FPE_Raise + IMPORT __fp_div_common + IMPORT __fp_div_uncommon + + IMPORT __fp_veneer_error ; RDCFix: Get rid of this. + + NESTED_ENTRY __divd + ; VEnter_16 + STMFD r13!, veneer_s ; Save off non-volatiles + SUB sp, sp, #LOC_SIZE ; Local storage + PROLOG_END + + STR r3, [sp, #OrgOP2h] ; Save off original args in case of exception + STR r2, [sp, #OrgOP2l] + STR r1, [sp, #OrgOP1h] + STR r0, [sp, #OrgOP1l] + + + ; Catch the NaNs, INFs, and Zeros here. + MOV r5, #0 ; Exception information initialized to none. + ORRS r4, r0, r1, LSL #1 + BEQ ddiv_arg1_zero ; Arg1 is zero + MOV r4, r1, LSL #1 + MOV r4, r4, LSR #21 + ADD r4, r4, #1 + CMP r4, #2048 + BEQ ddiv_arg1_nan_inf ; Arg1 is a NaN/INF + + ; Arg1 is non-zero and finite + ORRS r4, r2, r3, LSL #1 + BEQ ddiv_zero_divide ; Arg2 is zero so have zero divide + MOV r4, r3, LSL #1 + MOV r4, r4, LSR #21 + ADD r4, r4, #1 + CMP r4, #2048 + BEQ ddiv_arg2_nan_inf ; Arg2 is a NaN/INF + + DoubleToInternal 2,ddiv_zero2,ddiv_uncommon1 + DoubleToInternal 1,ddiv_zero1,ddiv_uncommon + + MOV Rins,#Double_mask + BL __fp_div_common + +ddiv_return + BL __fp_e2d + +ddiv_check_cause + TST r3, #FPECause_mask + ADDEQ sp, sp, #LOC_SIZE + + ; VReturn EQ + LDMEQFD r13!,veneer_s + IF Interworking :LOR: Thumbing + BXEQ lr + ELSE + MOVEQ pc, lr + ENDIF + + STR r0, [sp, #ExDRESl] + STR r1, [sp, #ExDRESh] + LDR r0, [sp, #OrgOP2l] + LDR r1, [sp, #OrgOP2h] + STR r0, [sp, #ExOp2l] + STR r1, [sp, #ExOp2h] + LDR r2, [sp, #OrgOP1l] + ORR r1, r3, #_FpDivD + LDR r3, [sp, #OrgOP1h] + ADD r0, sp, #ExNewResl + CALL FPE_Raise + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF + + LDR r0, [sp, #ExNewResl] + LDR r1, [sp, #ExNewResh] + ADD sp, sp, #LOC_SIZE + LDMFD r13!, veneer_s + IF Interworking :LOR: Thumbing + BX lr + ELSE + MOV pc, lr + ENDIF + + + +ddiv_uncommon1 + ORR OP2sue,OP2sue,#Uncommon_bit + DoubleToInternal 1,ddiv_zero3 +ddiv_uncommon + ORREQ OP1sue,OP1sue,#Uncommon_bit + ADR lr,ddiv_return + MOV Rins,#Double_mask + B __fp_div_uncommon + +ddiv_zero3 +; Op1 is a zero, Op2 is an uncommon non-zero. Op2 is in the converted form. +; Op2 is an infinity if all bits are zero (result is a signed zero). Otherwise +; a quiet NaN/exception. + + ORRS tmp,OP2mlo,OP2mhi,LSL #1 + BEQ ddiv_zero1 + MOVS OP2mhi,OP2mhi,LSL #1 + BPL ddiv_ivo + +; return any old quiet NaN + +; RDCFix: Get rid of this stuff. +;ddiv_return_qnan +; MOV dOPh,#-1 +; VReturn + +ddiv_zero2 +; Op2 is a zero. If operand 1 is a zero or a SNaN, this is an invalid +; operation, otherwise it is a divide by zero. + + MOVS tmp, dOP1h, LSL #1 + TEQEQ dOP1l, #0 ; Z <- zero + BEQ ddiv_ivo + + MVNS tmp, tmp, ASR #32-DExp_len-1 ; Z <- QNaN + VReturn EQ ; Return Op1 (QNaN) + ; tmp==1 and mantissa==0 => Inf (Inf) + ; tmp==1 and mantissa!=0 => SNaN (IVO) + TEQ tmp, #1 + BNE ddiv_dvz + ORRS tmp, dOP1l, dOP1h, LSL #DExp_len+1 ; Z <- zero mantissa (Inf) + ; MLS 2890 + ; Infinty/Zero returns appropriately signed infinity. + ; Given the representations of Infinty and Zero, we can get + ; this sign in a single instruction. + EOREQ dOP1h, dOP1h, dOP2h + VReturn EQ ; Return Op1 (Inf) + +ddiv_ivo + MOV a4, #IVO_bit:OR:Double_bit + B __fp_veneer_error + +ddiv_dvz + MOV a4, #DVZ_bit:OR:Double_bit + ; MLS report 2899 + ; division by -0.0 should return inversely signed infinity. + EOR dOP1h, dOP1h, dOP2h + B __fp_veneer_error + +ddiv_zero1 +; Op1 is a zero, Op2 is in the extended form, and can't be an "uncommon". + + EOR dOP1h, dOP1h, OP2sue + AND dOP1h, dOP1h, #Sign_bit + VReturn + + + +; Arg1 is a zero. +; If Arg2 is a zero, set invalid operation and return a QNaN. +; Else if Arg2 is an SNaN, set invalid and return a QNaN version of the SNaN. +; Else if Arg2 is a QNaN, return it. +; Else return a zero. +ddiv_arg1_zero + ORRS r4, r2, r3, LSL #1 ; If Arg2 == 0 + ORREQ r5, r5, #IVO_bit ; set invalid + BEQ ddiv_return_qnan ; return a QNaN + MOV r4, r3, LSL #1 ; Extract exponent2 + MOV r4, r4, LSR #21 ; .. + ADD r4, r4, #1 ; If exponent2 == 2047 + CMP r4, #2048 ; .. + BNE ddiv_return_zero ; Arg2 finite, return a zero + ORRS r4, r2, r3, LSL #12 ; Mantissa2 == 0? + BEQ ddiv_return_zero ; Have an INF, return a zero + TST r3, #dSignalBit ; SNaN? + ORREQ r5, r5, #IVO_bit ; If SNaN, set invalid + MOV r0, r2 ; Copy mantissa2 for QNaN return + MOV r1, r3 ; .. + B ddiv_return_qnan ; Return QNaN + + +; Arg1 is a NaN or an INF. +; If Arg1 is an INF +; If Arg2 is an SNaN, set invalid operation and return a QNaN version of +; the SNaN. +; If Arg2 is a QNaN, return it. +; If Arg2 is an INF, set invalid operation and return a QNaN. +; Else return an INF. +; Else if Arg1 is an SNaN, set invalid operation and return a QNaN version +; of the SNaN. +; Else if Arg1 is a QNaN. +; If Arg2 is an SNaN, set invalid and return the QNaN. +; Else return the QNaN. +ddiv_arg1_nan_inf + ORRS r4, r0, r1, LSL #12 ; Mantissa1 == 0? + BEQ ddiv_arg1_inf ; If ==0, have an INF + TST r1, #dSignalBit ; Check if Arg1 is an SNaN + ORREQ r5, r5, #IVO_bit ; If is an SNaN, signal invalid + BEQ ddiv_return_qnan ; and return a QNaN version of it + MOV r4, r3, LSL #1 ; Extract exponent2 + MOV r4, r4, LSR #21 ; .. + ADD r4, r4, #1 ; If exponent2 == 2047 + CMP r4, #2048 ; .. + BNE ddiv_return_qnan ; If !=, Arg2 finite, so return the QNaN + ORRS r4, r2, r3, LSL #12 ; Mantissa2 == 0? + BEQ ddiv_return_qnan ; If ==, Arg2 is INF, so return the QNaN + TST r3, #dSignalBit ; Check for SNaN + ORREQ r5, r5, #IVO_bit ; If == 0, set invalid operation + B ddiv_return_qnan + +ddiv_arg1_inf + MOV r4, r3, LSL #1 ; Extract exponent2 + MOV r4, r4, LSR #21 ; .. + ADD r4, r4, #1 ; If exponent2 == 2047 + CMP r4, #2048 ; .. + BNE ddiv_return_inf ; If !=, Arg2 is finite, so return an INF + ORRS r4, r2, r3, LSL #12 ; Mantissa2 == 0? + ORREQ r5, r5, #IVO_bit ; If ==, have an INF, so set invalid + BEQ ddiv_return_qnan ; and return a QNaN + MOV r0, r2 ; Copy mantissa2 for QNaN return + MOV r1, r3 ; .. + TST r3, #dSignalBit ; Is Arg2 an SNaN + ORREQ r5, r5, #IVO_bit ; If it is, set invalid operation + B ddiv_return_qnan ; Return QNaN + + + +; Arg2 is a NaN or INF. Arg1 is finite, possibly zero. +; If Arg2 is an INF, return zero. +; Else if Arg2 is an SNaN, set invalid operation and return a QNaN version of the SNaN. +; Else if Arg2 is a QNaN, return it. +ddiv_arg2_nan_inf + ORRS r4, r2, r3, LSL #12 ; If Arg2 == INF + BEQ ddiv_return_zero ; Then return zero + TST r3, #dSignalBit ; Have a NaN, check for SNaN + ORREQ r5, r5, #IVO_bit ; If == 0, set invalid + MOV r1, r3 ; Copy mantissa2 for QNaN return + MOV r0, r2 ; .. + B ddiv_return_qnan ; Return QNaN + + + + +; Returns a QNaN. R1 and R0 must contain the mantissa portion +; of the QNaN. SNaNs are converted to QNaNs here. +ddiv_return_qnan + ORR r1, r1, #0x7F000000 ; Set exponent = 0x7FF + ORR r1, r1, #0x00F80000 ; ... and set mantissa[MSb] = 1 + MOV r3, r5 ; Move exception information + B ddiv_check_cause + + +; Sets the divide-by-zero exception and falls through to return an INF. +ddiv_zero_divide + ORR r5, r5, #DVZ_bit ; Set zero divide + + +; Returns a properly signed INF. r1 and r3 must contain the +; sign bits in the MSb. +ddiv_return_inf + EORS r4, r1, r3 ; Check signs of Arg1 and Arg2 + MOV r0, #0 ; Clear mantissa2 ... + MOV r1, #0x7F000000 ; ... and set exponent = 0x7FF + ORR r1, r1, #0x00F00000 ; .. + ORRMI r1, r1, #0x80000000 ; Set sign bit if negative + MOV r3, r5 ; Move exception information + B ddiv_check_cause + +; Returns a properly signed zero. r1 and r3 must contain the +; sign bits in the MSb. +ddiv_return_zero + EORS r4, r1, r3 ; Check signs of Arg1 and Arg2 + MOV r0, #0 ; Clear sign, exponent, and mantissa + MOV r1, #0 ; .. + ORRMI r1, r1, #0x80000000 ; Set sign bit if negative + MOV r3, r5 ; Move exception information + B ddiv_check_cause + + + ENTRY_END __divd + + ] + +;--------------------------------------------------------------------------- + + [ :DEF: rdv_s + + CodeArea |FPL$$drdv| + Double rdv + + IMPORT __fp_rdv_common + IMPORT __fp_rdv_uncommon + IMPORT __fp_veneer_error + ;Export _drdv + Export _drdiv + +;_drdv +_drdiv VEnter_16 + + DoubleToInternal 2,drdv_zero2,drdv_uncommon1 + DoubleToInternal 1,drdv_dvz,drdv_uncommon + + MOV Rins,#Double_mask :OR: Reverse + BL __fp_rdv_common + +drdv_return + InternalToDouble + +drdv_uncommon1 + ORR OP2sue,OP2sue,#Uncommon_bit + DoubleToInternal 1,drdv_zero1 +drdv_uncommon + ORREQ OP1sue,OP1sue,#Uncommon_bit + ADR lr,drdv_return + MOV Rins,#Double_mask:OR:Reverse + B __fp_rdv_uncommon + +drdv_zero1 +; Op2 is uncommon, but Op1 is a zero. Return Inf for Op2=Inf, IVO for +; Op2=SNaN or a QNaN for Op2=QNaN + + MOVS tmp, dOP2h, LSL #DExp_len+1 ; N <- QNaN + TEQEQ dOP1l, #0 ; Z <- Inf + MOVMIS tmp, #0 ; Z <- N + BNE drdv_ivo + MOV dOP1h, dOP2h ; Return a QNaN/Inf + MOV dOP1l, dOP2l + VReturn + +drdv_zero2 +; Op2 is a zero. If Op1 is a zero or SNaN, this is an invalid operation, +; otherwise it is an appropiately signed zero unless Op1=QNaN + + MOVS tmp, dOP1h, LSL #1 + TEQEQ dOP1l, #0 ; Z <- Op1=0 + BEQ drdv_ivo + MVNS tmp, tmp, ASR #32-DExp_len-1 ; Z <- Op1=QNaN + VReturn EQ ; Return QNaN + ORRS dOP1l, dOP1l, dOP1h, LSL #DExp_len+1 + ; Z <- zero mantissa + BEQ drdv_return_zero + TEQ tmp, #1 ; Z <- SNaN + BEQ drdv_ivo + +drdv_return_zero + EOR dOP1h, dOP1h, dOP2h + AND dOP1h, dOP1h, #Sign_bit + MOV dOP1l, #0 + VReturn + +drdv_dvz + MOV a4,#DVZ_bit:OR:Double_bit + B __fp_veneer_error + +drdv_ivo + MOV a4,#IVO_bit:OR:Double_bit + B __fp_veneer_error + + ] + +;--------------------------------------------------------------------------- + + [ :DEF: fmod_s + + CodeArea |FPL$$dfmod| + Double fmod + + EXPORT fmod + Import_32 __fp_edom + +fmod VEnter + + DoubleToInternal 2,fmod_divide_by_zero,fmod_uncommon1 + DoubleToInternal 1,fmod_Op1Zero,fmod_uncommon + + BL Rem_Common + +fmod_return + InternalToDouble + +fmod_Op1Zero + +; Op1 is zero => result is Op1. Op1h/Op1l hasn't been changed. + + VReturn + +fmod_uncommon1 + ORR OP2sue,OP2sue,#Uncommon_bit + DoubleToInternal 1 +fmod_uncommon + ORREQ OP1sue,OP1sue,#Uncommon_bit + ADR lr,fmod_return + MOV Rins,#Double_mask + B Rem_Uncommon + +fmod_divide_by_zero + ; We return -HUGE_VAL and set errno=EDOM + + VPull + MOV a1, #Sign_bit + MOV a2, #1 ; true + B_32 __fp_edom + + GET arith.asm + + ] + +;--------------------------------------------------------------------------- + + [ :DEF: sqrt_s + + CodeArea |FPL$$dsqrt| + Double sqrt + + EXPORT sqrt + IMPORT __fp_sqrt_common + IMPORT __fp_sqrt_uncommon + +sqrt VEnter + + DoubleToInternal 1,sqrt_Zero,sqrt_uncommon + + MOV Rins,#Double_mask + BL __fp_sqrt_common + +sqrt_return + BL __fp_e2d + + TST a4, #Error_bit + VReturn EQ + +; error - set errno to EDOM and return -HUGE_VAL + + MOV a1, #Sign_bit + MOV a2, #1 ; something non-zero +sqrt_edom + Import_32 __fp_edom +; tail call + VPull + B_32 __fp_edom + +sqrt_Zero + ; C contains the sign bit - if set, record a domain error, + ; but return -0.0 (which is what's in a1/a2 already) + [ :DEF: SqrtMinusZeroGivesEDOM + VReturn CC + B sqrt_edom ; save a few bytes in error case + | + ; Otherwise, just return the zero passed in + VReturn + ] + +sqrt_uncommon + ORR OP1sue,OP1sue,#Uncommon_bit + ADR lr,sqrt_return + MOV Rins,#Double_mask + B __fp_sqrt_uncommon + + ] + +;=========================================================================== + + END diff --git a/base/Kernel/Native/arm/Crt/veneer_f.asm b/base/Kernel/Native/arm/Crt/veneer_f.asm new file mode 100644 index 0000000..1e264b1 --- /dev/null +++ b/base/Kernel/Native/arm/Crt/veneer_f.asm @@ -0,0 +1,959 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +; veneer_f.s - float add/sub/mul/div +; +; Copyright (C) Advanced RISC Machines Limited, 1994. All rights reserved. +; +; RCS Revision: 1 +; Checkin Date: 2007/06/29 02:59:16 +; Revising Author +; + +; Local storage size and offsets +LOC_SIZE EQU 0x18 +OrgOp2l EQU 0x14 +OrgOp1l EQU 0x10 +ExDResl EQU 0x08 +ExOp2l EQU 0x00 +NewResl EQU 0x10 + + + GET fpe.asm + GET kxarm.inc + + +a RN 0 +b RN 1 +tmp RN 12 +mask RN 12 +expa RN 2 +expb RN 3 +exp RN expa +sign RN expb +shift RN expb +res RN expb +guess RN 14 +num RN b +den RN a +div RN 3 + + + +;=============================================================================== +; +; RDCFix: +; BUGBUG: These comments aren't necessarily right anymore. +; +; + + +; __adds/__subs: +; +; Upon entry the signs are checked, and if they are not equal, control is given +; to the inverse routine while negating the second operand. Ie. __adds(+A,-B) -> +; __subs(+A,+B) and __subs(-A,+B) -> __adds(-A,-B). After this check the signs are +; known to be equal. +; +; The operands are sorted to ensure that A >= B. This enables many checks to +; be simplified: (A == 0) || (B == 0) reduces to (B == 0). The calculations +; are also simpler: only operand B needs to be shifted. Unsigned arithmetic +; is used to compare the packed numbers, since we want to have the operand with +; the largest magnitude as operand A. +; +; Special cases, namely zeroes, infinities, denormals and Not-a-Numbers (NaNs) +; are checked for on entry. If one of the operands is special, a jump is made +; to handle these cases out of line to keep overhead for the general case as +; low as possible. Because the operands are sorted, only 2 checks need to be +; made: operand A is checked for NaN, while operand B is checked for zero. +; +; As the signs of the operands are known to be equal and the operands +; are ordered, the sign of the result is the sign of one of the operands. +; Since the exponent can only change a little (one in __adds, and often little +; in __subs), the sign and exponent are not separated. +; +; In __adds, the operands are added with the smallest one shifted right with the +; exponent difference. The fraction might be larger then 1.0, and is renormalised +; if neccesary (max 1 right shift needed). The exponent is adjusted with -1 +; (+ 1 if the fraction was >= 2.0) to counter for the leading bit when the +; fraction and exponent are combined (using an ADD instruction). +; +; In __subs, operand B is subtracted from a, after being shifted right with the +; exponent difference. The result cannot be negative since A >= B, but it can +; result in a unnormalized number (as the high bits of A and B might cancel out). +; The common case results in the exponent being adjusted with +0 or -1, this is +; when the MSB is still set, or when the next bit is set. In the last case +; underflow to a denormalized number is possible. Rounding proceeds as normal. +; When 2 or more leading bits of the result are clear, the result must be +; normalized. If the resulting exponent is smaller than zero, denormalization +; follows. No rounding is necessary (the round bit is zero since we shifted +; left by at least 2 bits). +; +; In the rounding stage, the exponent is recombined with the fraction +; which leading bit is still set (if it is normalized). This causes the +; exponent to increment by one. Therefore, the exponent has been decremented +; in an earlier stage. +; The round-bit is calculated in the result by using more precision than +; necessary. After the result is shifted right to remove thse, the carry +; contains the roundbit. +; The guard bits are in the second operand, which are calculated by +; left shifting. This is only necessary if the roundbit was set. +; Round to even is implemented by always rounding upwards, and +; clearing the LSB in case the guard bits were all zero. Thus an odd value will +; be rounded up, while an even value will not. While rounding, the fraction may +; become too large (>= 2.0), at which time the exponent must be incremented and +; the fraction shifted right. However, this doesn't need extra code, since +; exponent and fraction are already combined: the overflow from the fraction +; increments the exponent. Note that this means a denormalized number might +; become normalized while rounding! +; +; For __adds, overflow is being checked after rounding by adding 1 to the exponent. +; If the result was overflowed, the sign bit inverts (overflowed exponent is 255, +; and 255+1 negates the sign bit). Note that overflow can only occur after +; renormalization, or during rounding, but not in both. +; Overflow cannot occur in __subs. +; +; If one of the operands is an uncommon number, the following happens: +; If the largest operand is a NaN, an Invalid Operation is raised (which +; returns the NaN if it is a quiet NaN). +; For __adds, infinities are returned unaltered (inf + inf = inf), but in __subs a +; Invalid Operation exception is raised for inf - inf. +; If the smallest operand is a zero, the other operand is returned (thus A + 0 +; -> A, A - 0 -> A, but a special case is -0 - -0 -> +0). +; Denormalized numbers are handled by decoding an unnormalized fraction with +; exponent 1. This is to make up for the hidden bit which is clear in +; denormalized numbers. Normal addition or subtraction can now proceed without any +; modification (the algorithms don't rely on the operands being normalized). +; The result can be a denormalized number or a normalized number. +; +; Frsb (B - A) is implemented by negating both signs on input of __subs. Its use +; is mainly intended for code size optimization. +; +;=========================================================================== + + [ :DEF: add_s + + AREA |.text|, CODE, READONLY + + Export __adds + Export __subs + Export __fArithReturn ;; RDCFix: Should move to common area + Export __fArithNaNCheck ;; RDCFix: Should move to common area + Export __flt_underflow ;; RDCFix: Should move to common area + IMPORT FPE_Raise + [ :DEF: thumb + CODE32 + ] + +; Prologues for __adds, __subs, __muls, and __divs must be the same. + NESTED_ENTRY __subs + EnterWithLR_16 + STMFD sp!, {lr} ; Save return address + SUB sp, sp, #LOC_SIZE ; Allocate stack space + PROLOG_END + STR r1, [sp, #OrgOp2l] ; Save original args in case of exception + MOV r14, #_FpSubS ; Initialize no exceptions, float sub + STR r0, [sp, #OrgOp1l] ; Save original args in case of exception + B fsubtract + + ENTRY_END __subs + + + +; Prologues for __adds, __subs, __muls, and __divs must be the same. + NESTED_ENTRY __adds + EnterWithLR_16 + STMFD sp!, {lr} ; Save return address + SUB sp, sp, #LOC_SIZE ; Allocate stack space + PROLOG_END + STR r1, [sp, #OrgOp2l] ; Save original args in case of exception + MOV r14, #_FpAddS ; Initialize no exceptions, float add + STR r0, [sp, #OrgOp1l] ; Save original args in case of exception + B faddition + + + + + +_faddn ; Branch to here from subtract + EOR b, b, #1 << 31 + B _fadd1 + + +faddition + ; if the signs are unequal, it is a subtract + TEQ a, b + BMI _fsubn +_fadd1 + ; swap a and b so that a >= b + SUBS tmp, a, b + SUBLO a, a, tmp + ADDLO b, b, tmp + ; decode exponents, and filter out special cases + MOV exp, a, LSR #23 ; exp = sign<<8 + exponent + SUB shift, exp, b, LSR #23 ; shift = 0..254 (sign bits cancel out) + MOV tmp, #255 << 24 + TST tmp, b, LSL #1 ; check for denorm/zero + TEQNE tmp, exp, LSL #24 ; check for inf/NaN + BEQ fadd_uncommon ; handle zeroes/denorms/infs/NaNs + ; decode fractions and add the leading one + MOV tmp, #1 << 31 + ORR a, tmp, a, LSL #8 ; a = 1.frac_a + ORR b, tmp, b, LSL #8 ; b = 1.frac_b + +fadd_add + ; Check for inexact where all bits lost + CMP shift, #24 ; Shift amount >= 24? + ORRGE r14, r14, #INX_bit ; Set inexact (note b != +/-0) + BGE fadd_add_core + RSB tmp, shift, #24 ; Number of bits lost + MOVS tmp, b, LSL tmp ; Check lower bits of lesser operand + ORRNE r14, r14, #INX_bit ; If bits set, then inexact + + +fadd_add_core ; do the addition and renormalise + ADDS a, a, b, LSR shift ; CS if a >= 2.0 + + BCS fadd_carry + ADD exp, exp, #-1 ; adjust exp for leading bit + MOVS a, a, LSR #8 ; CS -> round up (never EQ) + ADC a, a, exp, LSL #23 ; combine sign, exp & fraction and round + BCC __fArithReturn + RSB shift, shift, #25 + MOVS b, b, LSL shift ; calc guard bits: CS,EQ -> round to even + MOV tmp, a, LSL #1 + CMNNE tmp, #1 << 24 ; check for overflow (if not round to even) + BCC __fArithReturn ; return if NOT(overflow OR round to even) + BICEQ a, a, #1 ; round to even + CMN tmp, #1 << 24 ; check for overflow + BCC __fArithReturn + +fadd_overflow ; sign in a is correct + ORR r14, r14, #OVF_bit :OR: INX_bit + ; Set overflow and inexact + MOVS r0, r0 ; Check sign of result + MOV r1, #0xFF ; Load up a correctly signed INF + MOV r0, r1, LSL #23 ; Move unsigned INF into result + ORRMI r0, r0, #0x80000000 ; Set sign bit if result negative + B __fArithReturn + +fadd_carry + MOV a, a, RRX ; restore leading bit + MOVS tmp, a, ROR #8 ; Check for inexact + ORRMI r14, r14, #INX_bit ; Set inexact if bit set + MOVS a, a, LSR #8 ; CS -> round up (never EQ) + ADC a, a, exp, LSL #23 ; combine sign, exp & fraction and round + MOV tmp, a, LSL #1 + CMNCC tmp, #1 << 24 ; check for overflow (if not round to even) + BCC __fArithReturn + CMN tmp, #1 << 24 + BCS fadd_overflow + RSB shift, shift, #24 + MOVS b, b, LSL shift ; doesn't set carry if shift = 24! + BICEQ a, a, #1 + B __fArithReturn + + +fadd_uncommon + ; handle denorms, infinities and NaNs + TEQ tmp, exp, LSL #24 ; filter out NaN and infinites (EQ) + BEQ fadd_inf_NaN + ; fast check for zeroes + MOVS tmp, b, LSL #1 ; EQ if b is zero + BEQ __fArithReturn ; return A + 0 = A + ; b is denornalized, a might be + MOV a, a, LSL #8 ; a = 0.frac_a + MOV b, b, LSL #8 ; b = 0.frac_b + TST exp, #255 ; a denormalized? (exp == 0 -> EQ) + ORRNE a, a, #1 << 31 ; no denorm, add leading one + SUBNE shift, shift, #1 ; correct shift + ADDEQ exp, exp, #1 ; both denorms - correct exp + B fadd_add + +fadd_inf_NaN + ; handle infinities and NaNs - a is infinite or NaN, b might be + MOVS tmp, a, LSL #9 ; EQ if a inf, NE if a NaN + BEQ __fArithReturn + B __fArithNaNCheck + + +_fsubn ; Branch here from add + EOR b, b, #1 << 31 + B _fsub1 + + +fsubtract + ; if the signs are unequal, it is an addition + TEQ a, b + BMI _faddn +_fsub1 + ; swap a and b so that a >= b + SUBS tmp, a, b + EORLO tmp, tmp, #1 << 31 ; negate both opnds (A - B = -B - -A) + SUBLO a, a, tmp + ADDLO b, b, tmp + ; decode exponents, and filter out special cases + MOV exp, a, LSR #23 ; exp = sign<<8 + exponent + SUB shift, exp, b, LSR #23 ; shift = 0..254 (sign bits cancel out) + MOV tmp, #255 << 24 + TST tmp, b, LSL #1 ; check for denorm/zero + TEQNE tmp, exp, LSL #24 ; check for inf/NaN + BEQ fsub_uncommon ; handle zeroes/denorms/infs/NaNs + ; decode fractions and add the leading one + ORR a, tmp, a, LSL #1 + BIC a, a, #0xFE000000 + ORR b, tmp, b, LSL #1 + + ; Check for inexact + CMP shift, #32 ; Shift amount >= 31? + ORRGE r14, r14, #INX_bit ; Set inexact (note b != +/-0) + BGE fsub_sub_core + RSB tmp, shift, #32 ; Number of bits lost + MOVS tmp, b, LSL tmp ; Check lower bits of lesser operand + ORRNE r14, r14, #INX_bit ; If bits set, then inexact + +fsub_dosub + RSB b, b, #0xFE000000 ; Negate B + +fsub_sub_core + ; do the subtraction and calc number of bits to renormalise (0, 1, >=2) + ADD a, a, b, ASR shift + MOVS tmp, a, LSL #8 ; CS = 10/11, CC,MI = 01, CC,PL = 00 + BCS fsub_renorm_0 ; high bit still set - no renormalisation + BPL fsub_renormalise ; high 2 bits clear - renormalise >= 2 bits + TST expa, #254 ; exp == 1? (cannot be zero) + BEQ fsub_renormalise ; yes -> underflow to denormalized number +fsub_renorm_1 + ; 1 left shift needed, exp -= 1 + MOV a, tmp, ASR #8 ; doesn't set carry - no early exit! +; TST tmp, #0xFF ; RDCFix: Need this? +; ORRNE r14, r14, #INX_bit ; RDCFix: Need this? + RSBS shift, shift, #32+1 ; shift can be <= 0... + MOVLS shift, #1 ; shift 1 -> CS and NE - always roundup + MOVS b, b, LSL shift ; calc rounding (CS) and guard bits (EQ) + ADC a, a, exp, LSL #23 ; recombine sign, exponent and fraction + BCC __fArithReturn + BNE __fArithReturn +; ORR r14, r14, #INX_bit ; RDCFix: Need this? + BICEQ a, a, #1 ; round to even + B __fArithReturn + +fsub_renorm_0 + ; no renormalisation needed + ; RDCFix: Is this right? + MOVS a, tmp, LSL #32-9 ; Check if we're throwing away any bits + ORRNE r14, r14, #INX_bit ; If we are, set inexact + MOVS a, tmp, LSR #9 ; CS -> round up + ADC a, a, exp, LSL #23 ; recombine sign, exponent and fraction + BCC __fArithReturn + RSBS shift, shift, #32-0 ; shift can be <= 0... -> don't round to even + MOVHSS b, b, LSL shift ; EQ -> round to even + BNE __fArithReturn + BICEQ a, a, #1 ; round to even + B __fArithReturn + +fsub_renormalise + ; >= 2 bits renormalisation needed + MOV sign, exp, LSR #8 + TST a, #0x00FF0000 ; bit 16..23 set? + BNE fsub_renorm_small +fsub_renorm_large + ; bit 16..23 clear, >= 8 bits renormalisation + MOVS a, a, LSL #8 + BEQ __fArithReturn ; return +0 if result is zero + SUB exp, exp, #8 + TST a, #0x00FF0000 ; bit 16..23 set? + MOVEQ a, a, LSL #8 + SUBEQ expa, expa, #8 +fsub_renorm_small + ; renormalise A until bit 23 is set + TST a, #0x00F00000 + MOVEQ a, a, LSL #4 + SUBEQ exp, exp, #4 + TST a, #0x00C00000 + MOVEQ a, a, LSL #2 + SUBEQ exp, exp, #2 + CMP a, #1 << 23 + MOVCC a, a, LSL #1 + ADC exp, exp, #-3 + TEQ sign, exp, LSR #8 ; exponent underflow? (signs differ if so) + ADDEQ a, a, exp, LSL #23 ; no rounding necessary + BEQ __fArithReturn + ; underflow to denormalized number + RSB exp, exp, #0 + +;; +;; RDCFix: Move this to a common area (out of the adds/subs routine). +;; +;; Code adapted from except.s __flt_underflow +;; +;; Note that an underflow cannot occur for an add nor a subtract. This is +;; because the Pegasus FP Specification states that underflow happens if the +;; result is denormal (or zero) after rounding and inexact. Since the only +;; way we can get a denormal result from an add or subtract is to add/subtract +;; two denormals, and adding/subtracting two denormals is always exact (no +;; shift occurs as the exponents are equal), it is impossible to generate +;; an underflow condition. Thus, for add and subtract, this code will just +;; generate the correct result. The result will always be exact. +;; +;; For multiply and divide, inexacts must be detected here. An inexact here +;; may or may not also raise underflow in __fArithReturn. It is possible +;; for a normal result to enter here. +;; +;; Register usage: +;; r0 - underflowed number with leading bit set, round and guard bits +;; r2 - shift count for a (0 - exp) +;; r3 - sign in bit 0 (negative if set) +;; +__flt_underflow + ; RDCFix: What part of r2 is valid? Only low byte? + ; I don't completely understand this. + ; Check for inexact + TST r2, #0x000000E0 ; Check for shift >= 32 + ORRNE r14, r14, #INX_bit ; If shift >= 32, lost all bits: inexact + +fp_underflow_calc_result + MOV r3, r3, LSL #31 ; Position sign into sign bit position + ORRS r3, r3, r0, LSR r2 ; Combine sign, exponent, and mantissa + BCS fp_underflow_carry + RSB r2, r2, #32 ; Check for inexact to see if we shifted + MOVS r0, r0, LSL r2 ; any set bits out to the right + ORRNE r14, r14, #INX_bit ; If we did, set inexact + MOV r0, r3 + B __fArithReturn + +fp_underflow_carry + ORR r14, r14, #INX_bit ; RDCFix: Why is inexact guaranteed here? + RSB r2, r2, #33 + MOVS r2, r0, LSL r2 + ADC r0, r3, #0 + BICEQ r0, r0, #1 + B __fArithReturn + + + +fsub_uncommon + TEQ tmp, exp, LSL #24 ; EQ if NaN + BEQ fsub_inf_NaN +fsub_denorm ; here b is denormalized or zero, a might be a normal number + ; check whether a or b is zero - fast case + MOVS tmp, a, LSL #1 + MOVEQ a, #0 ; -0 - -0 = +0 + MOVS b, b, LSL #1 ; EQ if b == 0 or a == 0 + BEQ __fArithReturn ; return a - 0 = a + ; b is denormalized, a might be + TST exp, #255 + BIC a, tmp, #0xFF000000 + ORRNE a, a, #1 << 24 + SUBNE shift, shift, #1 + ADDEQ exp, exp, #1 + + ; Check for inexact + CMP shift, #31 ; Shift amount >= 31? + ORRGE r14, r14, #INX_bit ; Set inexact (note b != +/-0) + BGE fsub_sub_core + RSB tmp, shift, #31 ; Number of bits lost + MOVS tmp, b, LSL tmp ; Check lower bits of lesser operand + ORRNE r14, r14, #INX_bit ; If bits set, then inexact + + RSB b, b, #0 + B fsub_sub_core + + +fsub_inf_NaN + ; handle infinities and NaNs - a is infinite or a NaN, b might be + MOVS tmp, a, LSL #9 ; a NaN? (NE) + BNE __fArithNaNCheck + CMP a, b ; a is infinite, b too? (EQ) + ORREQ r14, r14, #IVO_bit ; Set invalid operation if is + ORREQ r0, r0, #fSignalBit ; Make INF into a QNaN + B __fArithReturn ; yes, a & b infinite -> generate IVO + + +;; +;; _fArithReturn +;; +;; Register Usage: +;; r0 - Default return value +;; r14 - Exception information +;; +;; Stack: +;; | Caller's Frame | +;; | | +;; +----------------+ +;; | Return Address | +;; +----------------+ +;; | Original Arg2 | +;; +----------------+ +;; | Original Arg1 | <-- SP +;; +----------------+ +;; Stack Top +;; +;; +;; Standard return path for single precision arithmetic routines. It checks +;; if any exceptions occurred. If any exceptional conditions occurred, then +;; an FPIEEE exception record is allocated and filled with the approptiate +;; values and the exception handler called. Upon returning, the possibly +;; changed result is loaded from the returned union, the stack space is +;; restored, and control is returned to the caller. If no exceptions occurred, +;; then the default result is returned. +;; +__fArithReturn + TST r14, #FPECause_mask ; Any exceptions? + ADDEQ sp, sp, #LOC_SIZE ; None so pop original args + IF Interworking :LOR: Thumbing + LDMEQFD sp!, {lr} ; and return + BXEQ lr + ELSE + LDMEQFD sp!, {pc} ; and return + ENDIF + ; Else we have an exception + ; Check for underflow (denormal & inexact) + MOV tmp, #0xFF000000 ; Load up exponent mask << 1 + TST r0, tmp, LSR #1 ; See if exponent is zero + BNE no_underflow ; Non-zero exponent so no underflow possible + TST r14, #INX_bit ; See if inexact bit is set + ORRNE r14, r14, #UNF_bit ; If inexact, then underflow +no_underflow + STR r0, [sp, #ExDResl] ; Push default result + LDR r0, [sp, #OrgOp2l] ; Get orig Arg2 off stack + STR r0, [sp, #ExOp2l] ; Push Arg2 + LDR r2, [sp, #OrgOp1l] ; Get orig Arg1 off stack + MOV r1, r14 ; ExInfo + ADD r0, sp, #NewResl ; Pointer to result from ex. handler + ; Note that this clobbers original + CALL FPE_Raise + + IF Thumbing :LAND: :LNOT: Interworking + CODE16 + bx pc ; switch back to ARM mode + nop + CODE32 + ENDIF ; Arg1 and Arg2 on the stack + + LDR r0, [sp, #NewResl] ; Get returned result + ADD sp, sp, #LOC_SIZE ; Pop orig. args and arg passing space + IF Interworking :LOR: Thumbing + LDMFD sp!, {lr} ; Return + BX lr + ELSE + LDMFD sp!, {pc} ; Return + ENDIF + + + +;; __fArithNaNCheck +;; +;; Checks both operands for SNaNs and raises and exception if one is present. +;; If no SNaNs are present, then a QNaN is returned. At least one of Arg1 +;; and Arg2 must be a NaN. +;; +;; Register usage: +;; r0 - Arg1 (must be a NaN if Arg2 is not) +;; r1 - Arg2 (must be a NaN if Arg1 is not) +;; r14 - FP exception information +;; +;; Code adapted from except.s. +__fArithNaNCheck + MOV a4, #0x01000000 + CMN a4, fOP1, LSL #1 + BLS fcheck_opnd2_NaN +fcheck_opnd1_NaN + TST fOP1, #fSignalBit + ORREQ fOP1, fOP1, #fSignalBit + ORREQ r14, r14, #IVO_bit + BEQ __fArithReturn + CMN a4, fOP2, LSL #1 + BLS __fArithReturn +fcheck_opnd2_NaN + MOV fOP1, fOP2 + TST fOP1, #fSignalBit + ORREQ fOP1, fOP1, #fSignalBit + ORREQ r14, r14, #IVO_bit + B __fArithReturn + + ENTRY_END __adds + ] + + +;------------------------------------------------------------------------------ + + [ :DEF: mul_s + + AREA |.text|, CODE, READONLY + + Export __muls + Export fmul_fdiv_overflow + IMPORT __fArithReturn + IMPORT __fArithNaNCheck + IMPORT __flt_normalise2 + IMPORT __flt_underflow + + + MACRO + MULL48 $a, $b, $res, $tmp + ; a = AAAAAA00 + ; b = BBBBBB00 + + UMULL $tmp, $res, $a, $b + SUB exp, exp, #128 << 16 ; subtract bias+1 - 0..253 normal + CMP $tmp, #0 + ORRNE $res, $res, #1 + + MEND + + + + ; Prologues for __adds, __subs, __muls, and __divs must be the same + NESTED_ENTRY __muls + EnterWithLR_16 + STMFD sp!, {lr} ; Save return address + SUB sp, sp, #LOC_SIZE ; Allocate local storage + PROLOG_END + STR r0, [sp, #OrgOp1l] ; Save off args in case of exception + MOV r14, #_FpMulS ; Initialize no exceptions, float multiply + STR r1, [sp, #OrgOp2l] + + MOV mask, #255 << 16 + ANDS expa, mask, a, LSR #7 + ANDNES expb, mask, b, LSR #7 + TEQNE expa, mask + TEQNE expb, mask + BEQ fmul_uncommon + TEQ a, b + ORRMI expa, expa, #1 << 8 + MOV mask, #1 << 31 + ORR a, mask, a, LSL #8 + ORR b, mask, b, LSL #8 +fmul_mul + ADD exp, expa, expb + MULL48 a, b, res, tmp + + ;; r1 now available for scratch + + CMP res, #&80000000 + ORRCS r1, tmp, res, LSL #24 ; Check res low 8 bits, low bits + MOVLO res, res, LSL #1 + ADC exp, exp, exp, ASR #16 ; recombine sign & exp, and adjust exp + ORRS r1, tmp, res, LSL #25 ; check low 7 bits, low bits + ORRNE r14, r14, #INX_bit + +fmul_round + MOVS a, res, LSR #8 ; never EQ (leading bit) + ADC a, a, exp, LSL #23 ; add fraction, and round + TSTCS res, #0x7f ; EQ -> round to even + CMPNE exp, #252 << 16 ; possible overflow? (never EQ) + BLO __fArithReturn ; return if no overflow and no round to even + BICEQ a, a, #1 ; delayed round to even + CMP exp, #252 << 16 + BLO __fArithReturn + BPL fmul_fdiv_overflow + +fmul_underflow ; result may be normalised after rounding + MOV r1, a, LSL #1 + SUB r1, r1, #1 << 24 + CMP r1, #3 << 24 ; result exp in 1..3 -> return + BLO __fArithReturn + MOV a, res + MVN sign, exp, LSR #8 ; correct sign from underflowed exponent + RSB exp, exp, #8 ; calc denormalising shift + B __flt_underflow + +;; +;; fmul_fdiv_overflow is shared between __muls and __divs. +;; +fmul_fdiv_overflow ; result might not be overflowed after all + MOV tmp, a, LSL #1 + ADD tmp, tmp, #1 << 24 + CMP tmp, #254 << 24 ; Check for exp = 253 or 254 + BHS __fArithReturn ; no overflow - 9 cycles overhead + SUBS a, a, exp, LSL #7 ; get correct sign + ORR r14, r14, #OVF_bit :OR: INX_bit ; Set overflow and inexact + MOV a, #0x7F000000 ; Create a properly signed INF + ORR a, a, #0x00800000 ; ... + ORRMI a, a, #0x80000000 ; ... + B __fArithReturn + + + +fmul_uncommon ; a or b denorm/NaN/inf + AND expb, mask, b, LSR #7 + TEQ a, b + ORRMI expa, expa, #1 << 8 + CMP expa, mask + CMPLO expb, mask + BHS fmul_inf_NaN + ; a or b denorm, first check for zero case + MOVS tmp, a, LSL #1 + MOVNES tmp, b, LSL #1 + MOVEQ a, expa, LSL #23 ; return signed zero + BEQ __fArithReturn + ; normalise operands + ADR tmp, fmul_mul + B __flt_normalise2 + + +fmul_inf_NaN ; a or b is a NaN or infinite + MOV tmp, #0x01000000 + CMN tmp, a, LSL #1 + CMNLS tmp, b, LSL #1 + BHI __fArithNaNCheck + ; now a or b is infinite - check that a and b are non-zero + MOVS tmp, a, LSL #1 ; a zero? + MOVNES tmp, b, LSL #1 ; b zero? + ORRNE expa, expa, #255 ; create infinite + MOV a, expa, LSL #23 ; with correct sign + ; If NE: a & b nonzero, return infinite + ORREQ r14, r14, #IVO_bit ; If EQ: inf * 0 signals an exception + ORREQ a, a, #0x7F000000 ; Create a QNaN + ORREQ a, a, #0x00C00000 ; ... + B __fArithReturn + + ENTRY_END __muls + ] + +;--------------------------------------------------------------------------- + + [ :DEF: div_s + + AREA |.text|, CODE, READONLY + + Export __divs + IMPORT __flt_normalise2 + IMPORT __fArithReturn + IMPORT __flt_underflow + IMPORT fmul_fdiv_overflow + IMPORT __fArithNaNCheck + + ; TODO: * halve lookup table size + + + DCB 0, 0, 0, 0 + DCB 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17 + DCB 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 30, 31, 32, 33, 35, 36 + DCB 37, 39, 40, 41, 43, 44, 46, 47, 48, 50, 51, 53, 54, 56, 57, 59 + DCB 60, 62, 63, 65, 66, 68, 70, 71, 73, 74, 76, 78, 80, 81, 83, 85 + DCB 87, 88, 90, 92, 94, 96, 98,100,102,104,106,108,110,112,114,116 + DCB 118,120,122,125,127,129,131,134,136,138,141,143,146,148,151,153 + DCB 156,158,161,164,166,169,172,175,178,180,183,186,189,192,195,199 + DCB 202,205,208,212,215,218,222,225,229,233,236,240,244,248,252,255 +fdiv_tab + + ; Prologues for __adds, __subs, __muls, and __divs must be the same + NESTED_ENTRY __divs + EnterWithLR_16 + STMFD sp!, {lr} ; Save return address + SUB sp, sp, #LOC_SIZE ; Allocate local storage + PROLOG_END + STR r0, [sp, #OrgOp1l] ; Save off args in case of exception + MOV r14, #_FpDivS ; Initialize no exceptions, float divide + ; Note that r14 is also used by "guess" + STR r1, [sp, #OrgOp2l] + MOV mask, #255 << 16 + ANDS expa, mask, a, LSR #7 + ANDNES expb, mask, b, LSR #7 + CMPNE expa, #255 << 16 + CMPNE expb, #255 << 16 + BEQ fdiv_uncommon + TEQ a, b + ADDMI expa, expa, #1 << 8 + ORR tmp, a, #1 << 23 + ORR den, b, #1 << 23 + BIC num, tmp, #0xFF000000 + BIC den, den, #0xFF000000 +fdiv_div + ; calculate exponent and find leading bit of result + SUB exp, expa, expb + CMP num, den + ; this code fills result delay slots + ;MOVLO num, num, LSL #1 ; shift so that div >= 1 << 23 + ;ADD exp, exp, #(127-2) << 16 ; subtract bias (one too small) + ;ADC exp, exp, exp, ASR #16 ; calc exp, combine with sign + + ; lookup guess of 1/den - use rounded inverted tablelookup + ADD tmp, den, #32768 + ((. + 12 - (fdiv_tab + 127)) << 16) + LDRB guess, [pc, -tmp, LSR #16] + RSB den, den, #0 ; result delay - negate den for MLA + ADD guess, guess, #256 + + ; do one Newton-Rhapson iteration to increase precision to 15 bits + MUL tmp, den, guess + MOVLO num, num, LSL #1 ; result delay - shift so that div >= 1 << 23 + MOV tmp, tmp, ASR #4 + MUL div, tmp, guess + MOV guess, guess, LSL #7 + ADD guess, guess, div, ASR #21 + + ; long division - 13 bits + MOV tmp, num, LSR #10 + MUL tmp, guess, tmp + MOV num, num, LSL #12 + MOV div, tmp, LSR #17 + MLA num, den, div, num + ADD exp, exp, #(127-2) << 16 ; result delay - subtract bias (one too small) + + ; long division - 11 bits (can do 12) + MOV tmp, num, LSR #10 + MUL tmp, guess, tmp + MOV num, num, LSL #11 + MOV tmp, tmp, LSR #18 + MLA num, den, tmp, num + ADC exp, exp, exp, ASR #16 ; result delay - calc exp, combine with sign + + ; correct div (may be one too small) + CMN num, den + ADDHS num, num, den ; now num < den + ADC div, tmp, div, LSL #11 + + MOV r14, #_FpDivS ; Reinitialize no exceptions, float divide + ; Note that r14 was used for "guess" + CMP num, #0 ; Check for inexact + ORRNE r14, r14, #INX_bit ; Set inexact if bits lost + CMN den, num, LSL #1 ; CS -> round, EQ -> round to even + ADC a, div, exp, LSL #23 ; recombine exp and fraction - increment exp + CMPNE exp, #252 << 16 ; exp < 252 cannot overflow + BLO __fArithReturn + BICEQ a, a, #1 + CMP exp, #252 << 16 ; exp < 252 cannot overflow + BLO __fArithReturn + BPL fmul_fdiv_overflow + +fdiv_underflow ; result may be normalised after rounding + MOV tmp, a, LSL #1 + SUB tmp, tmp, #1 << 24 + CMP tmp, #3 << 24 ; result exp in 1..3 -> return + BLO __fArithReturn + CMP num, #1 ; num contains implicit guard bits + ADC a, div, div ; add explicit guard bit (1 if num > 0) + MVN sign, exp, LSR #8 ; get correct sign + RSB exp, exp, #1 ; calc 1 - exp + B __flt_underflow + + +fdiv_uncommon + AND expb, mask, b, LSR #7 + TEQ a, b + ORRMI expa, expa, #1 << 8 + CMP expa, mask + CMPLO expb, mask + BHS fdiv_inf_NaN + ; a or b denorm, first check for zero case + MOVS tmp, b, LSL #1 + BEQ fdiv_divbyzero ; a / 0 -> division by zero + MOVS tmp, a, LSL #1 ; 0 / b -> 0 + MOVEQ a, expa, LSL #23 ; return signed zero + BEQ __fArithReturn + ; normalise operands + ADR tmp, fdiv_div1 + B __flt_normalise2 + +fdiv_div1 ; remove... quick hack + MOV tmp, a, LSR #8 + MOV den, b, LSR #8 + MOV num, tmp + B fdiv_div + + +fdiv_inf_NaN ; a or b is a NaN or infinite + MOV tmp, #0x01000000 + CMN tmp, a, LSL #1 + CMNLS tmp, b, LSL #1 + BHI __fArithNaNCheck + ; now a or b is infinite - check that a and b are not both infinite + CMN tmp, a, LSL #1 + CMNEQ tmp, b, LSL #1 + MOVEQ a, expa, LSL #23 + ORREQ r14, r14, #IVO_bit ; Set invalid + ORREQ a, a, #0x7F000000 ; Create QNaN + ORREQ a, a, #0x00C00000 ; ... + BEQ __fArithReturn ; inf / inf -> IVO + CMN tmp, b, LSL #1 ; b inf? (EQ) + MOVEQ a, #0 ; a / inf -> signed zero + BICNE a, a, #1 << 31 ; inf / b = inf (even inf / 0 = inf) + ORR a, a, expa, LSL #23 ; set sign + B __fArithReturn + +fdiv_divbyzero ; b zero + MOVS tmp, a, LSL #1 + ORREQ r14, r14, #IVO_bit ; 0 / 0 -> IVO + ORREQ a, a, #0x7F000000 ; Create QNaN + ORREQ a, a, #0x00C00000 ; ... + ORRNE r14, r14, #DVZ_bit ; A / 0 -> DVZ + MOVNE a, expa, LSL #23 ; set sign of result (returns signed inf) + ORRNE a, a, #0x7F000000 ; Create properly signed INF + ORRNE a, a, #0x00800000 ; ... + B __fArithReturn + + ENTRY_END __divs + ] + +;--------------------------------------------------------------------------- + + [ :DEF: fnorm2_s + + AREA |.text|, CODE, READONLY + + EXPORT __flt_normalise2 + + ; normalise a or b (or both). One operand is denormalised + ; a = x0AAAAAA, bits 0-22 nonzero, bits 23-30 zero + ; normalise such that bit 23 = 1 + ; return to address in tmp + + [ :DEF: thumb + CODE32 + ] +__flt_normalise2 + MOV a, a, LSL #8 + MOV b, b, LSL #8 + TST expa, #255 << 16 + BNE fnorm_b +fnorm_a + CMP a, #1 << 16 + SUBLO expa, expa, #16 << 16 + MOVLO a, a, LSL #16 + TST a, #255 << 24 + SUBEQ expa, expa, #8 << 16 + MOVEQ a, a, LSL #8 + TST a, #15 << 28 + SUBEQ expa, expa, #4 << 16 + MOVEQ a, a, LSL #4 + TST a, #3 << 30 + SUBEQ expa, expa, #2 << 16 + MOVEQS a, a, LSL #2 + MOVPL a, a, LSL #1 + ADDMI expa, expa, #1 << 16 + + TST expb, #255 << 16 + ORRNE b, b, #1 << 31 + MOVNE pc, tmp +fnorm_b + ORR a, a, #1 << 31 + CMP b, #1 << 16 + SUBLO expb, expb, #16 << 16 + MOVLO b, b, LSL #16 + TST b, #255 << 24 + SUBEQ expb, expb, #8 << 16 + MOVEQ b, b, LSL #8 + TST b, #15 << 28 + SUBEQ expb, expb, #4 << 16 + MOVEQ b, b, LSL #4 + TST b, #3 << 30 + SUBEQ expb, expb, #2 << 16 + MOVEQS b, b, LSL #2 + MOVPL b, b, LSL #1 + ADDMI expb, expb, #1 << 16 + MOV pc, tmp + + + ] + +;=========================================================================== + + END diff --git a/base/Kernel/Native/arm/Math.cpp b/base/Kernel/Native/arm/Math.cpp new file mode 100644 index 0000000..8fdff8a --- /dev/null +++ b/base/Kernel/Native/arm/Math.cpp @@ -0,0 +1,847 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Math.cpp +// +// Note: +// + +#define LITTL_ENDIAN + +#include "hal.h" + +#pragma warning(disable: 4725) + +////////////////////////////////////////////////////////////////////////////// +// +#define IMCW_EM 0x003f // interrupt Exception Masks +#define IEM_INVALID 0x0001 // invalid +#define IEM_DENORMAL 0x0002 // denormal +#define IEM_ZERODIVIDE 0x0004 // zero divide +#define IEM_OVERFLOW 0x0008 // overflow +#define IEM_UNDERFLOW 0x0010 // underflow +#define IEM_PRECISION 0x0020 // precision + +#define IMCW_RC 0x0c00 // Rounding Control +#define IRC_CHOP 0x0c00 // chop +#define IRC_UP 0x0800 // up +#define IRC_DOWN 0x0400 // down +#define IRC_NEAR 0x0000 // near + +#define ISW_INVALID 0x0001 // invalid +#define ISW_DENORMAL 0x0002 // denormal +#define ISW_ZERODIVIDE 0x0004 // zero divide +#define ISW_OVERFLOW 0x0008 // overflow +#define ISW_UNDERFLOW 0x0010 // underflow +#define ISW_PRECISION 0x0020 // inexact + +#define IMCW_PC 0x0300 // Precision Control +#define IPC_24 0x0000 // 24 bits +#define IPC_53 0x0200 // 53 bits +#define IPC_64 0x0300 // 64 bits + +////////////////////////////////////////////////////////////////////////////// +// +#ifdef BIG_ENDIAN +// big endian +#define D_EXP(x) ((unsigned short *)&(x)) +#define D_HI(x) ((unsigned long *)&(x)) +#define D_LO(x) ((unsigned long *)&(x)+1) +#else +#define D_EXP(x) ((unsigned short *)&(x)+3) +#define D_HI(x) ((unsigned long *)&(x)+1) +#define D_LO(x) ((unsigned long *)&(x)) +#endif + +#define D_BIASM1 0x3fe // off by one to compensate for the implied bit +#define MAXEXP 1024 +#define MINEXP -1021 + +// return the int representation of the exponent +// if x = .f * 2^n, 0.5<=f<1, return n (unbiased) +// e.g. INTEXP(3.0) == 2 +#define INTEXP(x) ((signed short)((*D_EXP(x) & 0x7ff0) >> 4) - D_BIASM1) + + +// check for infinity, NAN +#define D_ISINF(x) ((*D_HI(x) & 0x7fffffff) == 0x7ff00000 && *D_LO(x) == 0) +#define IS_D_SPECIAL(x) ((*D_EXP(x) & 0x7ff0) == 0x7ff0) +#define IS_D_NAN(x) (IS_D_SPECIAL(x) && !D_ISINF(x)) + +#define IS_D_QNAN(x) ((*D_EXP(x) & 0x7ff8) == 0x7ff8) +#define IS_D_SNAN(x) ((*D_EXP(x) & 0x7ff8) == 0x7ff0 && \ + (*D_HI(x) << 13 || *D_LO(x))) + +#define IS_D_DENORM(x) ((*D_EXP(x) & 0x7ff0) == 0 && \ + (*D_HI(x) << 12 || *D_LO(x))) + +#define IS_D_INF(x) (*D_HI(x) == 0x7ff00000 && *D_LO(x) == 0) +#define IS_D_MINF(x) (*D_HI(x) == 0xfff00000 && *D_LO(x) == 0) + +/////////////////////////////////////////////////////// Define special values. +// +typedef union { + uint64 lng; + float64 dbl; +} _dbl; + +static _dbl _d_pos_inf = { 0x7ff0000000000000 }; // positive infinity +static _dbl _d_neg_inf = { 0xfff0000000000000 }; // negative infinity +static _dbl _d_ind = { 0xfff8000000000000 }; // real indefinite +static _dbl _d_neg_zer = { 0x8000000000000000 }; // negative zero + +////////////////////////////////////////////////////////////////////////////// +// + +uint16 g_ClearFp() +{ + __debugbreak(); + return 0; +} + +// Doesn't alter any additional registers +uint16 g_ControlFp(uint16 newctrl, uint16 mask) +{ + __debugbreak(); + return 0; +} + +// Doesn't alter any additional registers +void g_RestoreFp(uint16 newctrl) +{ + __debugbreak(); +} + +////////////////////////////////////////////////////////////////////////////// +// +static float64 _frnd(float64 v) +{ + __debugbreak(); + return 0; +} + +static int32 _ftoi(float64 v) +{ + __debugbreak(); + return 0; +} + +static float64 _abs(float64 x) +{ + (*(uint64 *)&x) &= 0x7fffffffffffffff; + return x; +} + +static float64 _set_exp(float64 x, int exp) // does not check validity of exp +{ + float64 retval = x; + int biased_exp = exp + D_BIASM1; + *D_EXP(retval) = (unsigned short) (*D_EXP(x) & 0x800f | (biased_exp << 4)); + return retval; +} + +int _get_exp(float64 x) +{ + signed short exp; + exp = (signed short)((*D_EXP(x) & 0x7ff0) >> 4); + exp -= D_BIASM1; //unbias + return (int) exp; +} + + +// Provide the mantissa and the exponent of e^x +// +// Entry: +// x : a (non special) float64 precision number +// +// Exit: +// *newexp: the exponent of e^x +// return value: the mantissa m of e^x scaled by a factor +// (the value of this factor has no significance. +// The mantissa can be obtained with _set_exp(m, 0). +// +// _set_exp(m, *pnewexp) may be used for constructing the final +// result, if it is within the representable range. +// +static inline float64 r_exp_p(float64 z) +{ + static float64 const p0 = 0.249999999999999993e+0; + static float64 const p1 = 0.694360001511792852e-2; + static float64 const p2 = 0.165203300268279130e-4; + + return ( (p2 * (z) + p1) * (z) + p0 ); +} + +static inline float64 r_exp_q(float64 z) +{ + static float64 const q0 = 0.500000000000000000e+0; + static float64 const q1 = 0.555538666969001188e-1; + static float64 const q2 = 0.495862884905441294e-3; + + return ( (q2 * (z) + q1) * (z) + q0 ); +} + +// These are referenced by the ARM compiler compiling the code below - don't know why +// but workaround for now. +extern "C" double __imp___muld(double, double) +{ + __debugbreak(); + return 0; +} + +extern "C" double __imp___divd(double, double) +{ + __debugbreak(); + return 0; +} + +extern "C" double __imp___addd(double, double) +{ + __debugbreak(); + return 0; +} + +extern "C" double __imp___negd(double) +{ + __debugbreak(); + return 0; +} + +float64 _exphlp(float64 x, int * pnewexp) +{ + static float64 const LN2INV = 1.442695040889634074; // 1/ln(2) + static float64 const C1 = 0.693359375000000000; + static float64 const C2 = -2.1219444005469058277e-4; + float64 xn; + float64 g,z,gpz,qz,rg; + int n; + + xn = _frnd(x * LN2INV); + n = _ftoi(xn); + + // assume guard digit is present + g = (x - xn * C1) - xn * C2; + z = g*g; + gpz = g * r_exp_p(z); + qz = r_exp_q(z); + rg = 0.5 + gpz/(qz-gpz); + + n++; + + *pnewexp = _get_exp(rg) + n; + return rg; +} + +// +// Decompose a number to a normalized mantissa and exponent. +// +static float64 _decomp(float64 x, int *pexp) +{ + int exp; + float64 man; + + if (x == 0.0) { + man = 0.0; + exp = 0; + } + else if (IS_D_DENORM(x)) { + int neg; + + exp = 1 - D_BIASM1; + neg = x < 0.0; + while((*D_EXP(x) & 0x0010) == 0) { + // shift mantissa to the left until bit 52 is 1 + (*D_HI(x)) <<= 1; + if (*D_LO(x) & 0x80000000) + (*D_HI(x)) |= 0x1; + (*D_LO(x)) <<= 1; + exp--; + } + (*D_EXP(x)) &= 0xffef; // clear bit 52 + if (neg) { + (*D_EXP(x)) |= 0x8000; // set sign bit + } + man = _set_exp(x,0); + } + else { + man = _set_exp(x,0); + exp = INTEXP(x); + } + + *pexp = exp; + return man; +} + +static bool is_odd_integer(float64 y) +{ + int exp = INTEXP(y); + if (exp < 1 || exp > 63) { + return false; + } + return(((*(uint64*)&y) | 0x10000000000000u) << (10 + exp) == 0x8000000000000000); +} + +static bool is_even_integer(float64 y) +{ + int exp = INTEXP(y); + if (exp < 1 || exp > 63) { + return false; + } + return (((*(uint64*)&y) | 0x10000000000000u) << (10 + exp) == 0); +} + +////////////////////////////////////////////////////////////////////////////// + +float64 Class_System_Math::g_Round(float64 v) +{ + return _frnd(v); +} + +float64 Class_System_Math::g_Sin(float64 v) +{ + __debugbreak(); + return 0; +} + +float64 Class_System_Math::g_Cos(float64 v) +{ + __debugbreak(); + return 0; +} + +float64 Class_System_Math::g_Tan(float64 v) +{ + __debugbreak(); + return 0; +} + +float64 Class_System_Math::g_Atan(float64 v) +{ + __debugbreak(); + return 0; +} + +float64 Class_System_Math::g_Atan2(float64 v, float64 w) +{ + __debugbreak(); + return 0; +} + +float64 Class_System_Math::g_Abs(float64 v) +{ + __debugbreak(); + return 0; +} + +float64 Class_System_Math::g_Sqrt(float64 v) +{ + __debugbreak(); + return 0; +} + +float64 Class_System_Math::g_Log(float64 v) +{ + __debugbreak(); + return 0; +} + +float64 Class_System_Math::g_Log10(float64 v) +{ + __debugbreak(); + return 0; +} + +float64 Class_System_Math::g_Exp(float64 v) +{ + __debugbreak(); + return 0; +} + +// constants for the rational approximation +static inline float64 r_sinh_p(float64 f) +{ + static float64 const p0 = -0.35181283430177117881e+6; + static float64 const p1 = -0.11563521196851768270e+5; + static float64 const p2 = -0.16375798202630751372e+3; + static float64 const p3 = -0.78966127417357099479e+0; + + return (((p3 * (f) + p2) * (f) + p1) * (f) + p0); +} + +static inline float64 r_sinh_q(float64 f) +{ + static float64 const q0 = -0.21108770058106271242e+7; + static float64 const q1 = 0.36162723109421836460e+5; + static float64 const q2 = -0.27773523119650701667e+3; + // q3 = 1 is not used (avoid multiplication by 1) + + return ((((f) + q2) * (f) + q1) * (f) + q0); +} + +// Compute the hyperbolic sine of a number. +// The algorithm (reduction / rational approximation) is +// taken from Cody & Waite. +float64 Class_System_Math::g_Sinh(float64 v) +{ + static float64 const EPS = 5.16987882845642297e-26; // 2^(-53) / 2 + // exp(YBAR) should be close to but less than XMAX + // and 1/exp(YBAR) should not underflow + static float64 const YBAR = 7.00e2; + + // WMAX=ln(OVFX)+0.69 (Cody & Waite),omitted LNV, used OVFX instead of BIGX + + static float64 const WMAX = 1.77514678223345998953e+003; + + float64 result; + + if (IS_D_SPECIAL(v)) { + if (IS_D_INF(v) || IS_D_MINF(v)) { + } + else if (IS_D_QNAN(v)) { + // TODO: should throw a soft exception. + } + else if (IS_D_SNAN(v)) { + // TODO: should throw a hard exception. + } + result = v; + } + else if (v == 0.0) { + // no precision exception + result = v; + } + else { + bool neg = (v < 0.0); + float64 y = _abs(v); + + if (y > 1.0) { + int newexp; + if (y > YBAR) { + if (y > WMAX) { + // result too large, even after scaling + result = v * _d_pos_inf.dbl; + // TODO should throw hard exception. + goto exit; + } + + // + // result = exp(y)/2 + // + + result = _exphlp(y, &newexp); + newexp --; //divide by 2 + if (newexp > MAXEXP) { + result = 0.0; //result = _set_exp(result, newexp-IEEE_ADJUST); + // TODO should throw hard exception. + goto exit; + } + else { + result = _set_exp(result, newexp); + } + + } + else { + float64 z = _exphlp(y, &newexp); + z = _set_exp(z, newexp); + result = (z - 1.0/z) / 2.0; + } + + if (neg) { + result = -result; + } + } + else { + if (y < EPS) { + result = v; + if (IS_D_DENORM(result)) { + result = 0.0; // result = _add_exp(result, IEEE_ADJUST); + // TODO should throw hard exception. + goto exit; + } + } + else { + float64 f = v * v; + float64 r = f * (r_sinh_p(f) / r_sinh_q(f)); + result = v + v * r; + } + } + } + + exit: + return result; +} + +// Compute the hyperbolic cosine of a number. +// The algorithm (reduction / rational approximation) is +// taken from Cody & Waite. +// +float64 Class_System_Math::g_Cosh(float64 v) +{ + // exp(YBAR) should be close to but less than XMAX + // and 1/exp(YBAR) should not underflow + static float64 const YBAR = 7.00e2; + + // WMAX=ln(OVFX)+0.69 (Cody & Waite),omitted LNV, used OVFX instead of BIGX + static float64 const WMAX = 1.77514678223345998953e+003; + + float64 result; + + if (IS_D_SPECIAL(v)) { + if (IS_D_INF(v) || IS_D_MINF(v)) { + result = _d_pos_inf.dbl; + + } + else if (IS_D_QNAN(v)) { + // TODO: should throw a soft exception. + result = v; + } + else { + // TODO: should throw a hard exception. + result = v; + } + } + else if (v == 0.0) { + result = 1.0; + } + else { + float64 y = _abs(v); + if (y > YBAR) { + if (y > WMAX) { + // TODO: should throw a hard exception. + result = v; + goto exit; + } + + // + // result = exp(y)/2 + // + + int newexp; + result = _exphlp(y, &newexp); + newexp --; //divide by 2 + if (newexp > MAXEXP) { + // TODO: should throw a hard exception. + result = 0.0; //result = _set_exp(result, newexp-IEEE_ADJUST); + goto exit; + } + else { + result = _set_exp(result, newexp); + } + } + else { + int newexp; + float64 z = _exphlp(y, &newexp); + z = _set_exp(z, newexp); + result = (z + 1.0/z) / 2.0; + } + // TODO: should throw a hard exception if exactness is required. + } + + exit: + return result; +} + +// Compute the hyperbolic tangent of a number. +// The algorithm (reduction / rational approximation) is +// taken from Cody & Waite. + +static inline float64 r_tanh(float64 g) +{ + // constants for rational approximation + static float64 const p0 = -0.16134119023996228053e+4; + static float64 const p1 = -0.99225929672236083313e+2; + static float64 const p2 = -0.96437492777225469787e+0; + static float64 const q0 = 0.48402357071988688686e+4; + static float64 const q1 = 0.22337720718962312926e+4; + static float64 const q2 = 0.11274474380534949335e+3; + static float64 const q3 = 0.10000000000000000000e+1; + + return ((((p2 * (g) + p1) * (g) + p0) * (g)) / ((((g) + q2) * (g) + q1) * (g) + q0)); +} + +float64 Class_System_Math::g_Tanh(float64 v) +{ + // constants + static float64 const EPS = 5.16987882845642297e-26; // 2^(-53) / 2 + static float64 const XBIG = 1.90615474653984960096e+001; // ln(2)(53+2)/2 + static float64 const C0 = 0.54930614433405484570; // ln(3)/2 + + if (IS_D_SPECIAL(v)) { + if (IS_D_INF(v)) { + v = 1.0; + } + else if (IS_D_MINF(v)) { + v = -1.0; + } + else if (IS_D_QNAN(v)) { + // TODO: should throw a soft exception. + } + else if (IS_D_SNAN(v)) { + // TODO: should throw a hard exception. + } + } + else if (v == 0.0) { + // no precision exception + } + else { + bool neg = false; + if (v < 0.0) { + neg = true; + v = -v; + } + + if (v > XBIG) { + v = 1; + } + else if (v > C0) { + v = 0.5 - 1.0 / (g_Exp(v+v) + 1.0); + v = v + v; + } + else if (v < EPS) { + if (IS_D_DENORM(v)) { + // TODO: should throw a heard exception. + } + } + else { + v = v + v * r_tanh(v * v); + } + if (neg) { + v = -v; + } + } + return v; +} + +float64 Class_System_Math::g_Acos(float64 v) +{ + __debugbreak(); + return 0; +} + +float64 Class_System_Math::g_Asin(float64 v) +{ + __debugbreak(); + return 0; +} + +static float64 _fastpow(float64 v, float64 w) +{ + __debugbreak(); + return 0; +} + +float64 Class_System_Math::g_Pow(float64 v, float64 w) +{ + float64 result = 0.0; + + // check for infinity or NAN + if (IS_D_SPECIAL(w) || IS_D_SPECIAL(v)) { + if (IS_D_INF(w)) { + float64 absv = _abs(v); + if (absv > 1.0) { + result = _d_pos_inf.dbl; + } + else if (absv < 1.0) { + result = 0.0; + } + else { + result = _d_ind.dbl; + // TODO: Should throw a hard exception. + goto exit; + } + } + else if (IS_D_MINF(w)) { + float64 absv = _abs(v); + if (absv > 1.0) { + result = 0.0; + } + else if (absv < 1.0) { + result = _d_pos_inf.dbl; + } + else { + result = _d_ind.dbl; + // TODO: Should throw a hard exception. + goto exit; + } + } + else if (IS_D_INF(v)) { + if (w > 0.0) { + result = _d_pos_inf.dbl; + } + else if (w < 0.0) { + result = 0.0; + } + else { + result = 1.0; + } + } + else if (IS_D_MINF(v)) { + if (w > 0.0) { + result = is_odd_integer(w) ? _d_neg_inf.dbl : _d_pos_inf.dbl; + } + else if (w < 0.0) { + result = is_odd_integer(w) ? _d_neg_zer.dbl : 0.0; + } + else { + result = 1.0; + } + } + } + else if (w == 0.0) { + result = 1.0; + } + else if (v == 0.0) { + if (w < 0.0) { + // TODO: Should throw a hard exception. + result = is_odd_integer(v) ? _d_neg_inf.dbl : _d_pos_inf.dbl; + } + else { + result = is_odd_integer(v) ? w : 0.0; + } + } + else if (v < 0.0) { + if (is_odd_integer(w)) { + result = - _fastpow(-v, w); + } + else if (is_even_integer(w)) { + result = _fastpow(-v, w); + } + else { + // TODO: Should throw a hard exception. + result = v; + } + } + else { + result = _fastpow(v, w); + } + + exit: + return result; +} + +float64 Class_System_Math::g_Mod(float64 x, float64 y) +{ + float64 result = 0.0; + + // check for infinity or NAN + if (IS_D_SPECIAL(y) || IS_D_SPECIAL(x)) { + if (IS_D_SNAN(y) || IS_D_SNAN(x)) { + // TODO: Should throw a hard exception. + } + else if (IS_D_QNAN(y) || IS_D_QNAN(x)) { + // TODO: Should throw a soft exception. + result = x; + } + else if (IS_D_INF(x) || IS_D_MINF(x)) { + // TODO: Should throw a hard exception. + } + } + else if (y == 0.0) { + // TODO: Should throw a hard exception. + } + else if (x == 0.0) { + result = x; + } + else { + const int SCALE = 53; + bool neg = false; + bool denorm = false; + float64 d,ty,fx,fy; + int nx, ny, nexp; + + if (x < 0.0) { + result = -x; + neg = 1; + } + else { + result = x; + } + + ty = _abs(y); + + while (result >= ty) { + fx = _decomp(result, &nx); + fy = _decomp(ty, &ny); + + if (nx < MINEXP) { + // result is a denormalized number + denorm = true; + nx += SCALE; + ny += SCALE; + result = _set_exp(fx, nx); + ty = _set_exp(fy, ny); + } + + + if (fx >= fy) { + nexp = nx ; + } + else { + nexp = nx - 1; + } + d = _set_exp(fy, nexp); + result -= d; + } + if (denorm) { + // TODO: should raise only FP_U exception. + } + if (neg) { + result = -result; + } + } + + return result; +} + +float64 Class_System_Math::g_Floor(float64 v) +{ + float64 result; + + // save user fp control word, and set round down. + uint16 oldcw = g_ControlFp(IRC_DOWN, IMCW_RC); + + if (IS_D_SPECIAL(v)) { + if (IS_D_QNAN(v)) { + // TODO: should throw a soft exception. + } + else if (IS_D_SNAN(v)) { + // TODO: should throw a hard exception. + } + result = v; + } + else { + result = _frnd(v); // round according to the current rounding mode. + } + + g_RestoreFp(oldcw); + return result; +} + +float64 Class_System_Math::g_Ceiling(float64 v) +{ + float64 result; + + // save user fp control word, and set round up. + uint16 oldcw = g_ControlFp(IRC_UP, IMCW_RC); + if (IS_D_SPECIAL(v)) { + if (IS_D_QNAN(v)) { + // TODO: should throw a soft exception. + } + else if (IS_D_SNAN(v)) { + // TODO: should throw a hard exception. + } + result = v; + } + else { + result = _frnd(v); // round according to the current rounding mode. + } + + g_RestoreFp(oldcw); + return result; +} + +// +///////////////////////////////////////////////////////////////// End of File. + diff --git a/base/Kernel/Native/arm/Thread.cpp b/base/Kernel/Native/arm/Thread.cpp new file mode 100644 index 0000000..b467dbf --- /dev/null +++ b/base/Kernel/Native/arm/Thread.cpp @@ -0,0 +1,64 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Thread.cpp +// +// Note: +// + +#include "hal.h" + +////////////////////////////////////////////////////////////// Thread Context. +// +void +Struct_Microsoft_Singularity_ThreadContext:: +m_UpdateAfterGC(Struct_Microsoft_Singularity_ThreadContext * self, + Class_System_Threading_Thread *thread) +{ + self->_thread = thread; +} + +Class_System_Threading_Thread * +Struct_Microsoft_Singularity_ThreadContext:: +m_GetThread(Struct_Microsoft_Singularity_ThreadContext * self) +{ + return (Class_System_Threading_Thread *) self->_thread; +} + +#if SINGULARITY_KERNEL +void +Struct_Microsoft_Singularity_ThreadContext:: +m_Initialize(Struct_Microsoft_Singularity_ThreadContext * self, + int threadIndex, + UIntPtr stackBegin, + uint32 cr3) +{ + Struct_Microsoft_Singularity_Isal_SpillContext::m_Initialize(&self->threadRecord.spill, + stackBegin, + (UIntPtr) self->stackLimit, + (UIntPtr)Class_System_Threading_Thread::g_ThreadStub, + threadIndex, + (UIntPtr) cr3); +} + +void +Struct_Microsoft_Singularity_ThreadContext:: +m_InitializeIdle(Struct_Microsoft_Singularity_ThreadContext * self, + int threadIndex, + UIntPtr stackBegin, + uint32 cr3) +{ + Struct_Microsoft_Singularity_Isal_SpillContext::m_Initialize(&self->threadRecord.spill, + stackBegin, + (UIntPtr) self->stackLimit, + (UIntPtr)Class_System_Threading_Thread::g_DispatcherThreadStub, + threadIndex, + (UIntPtr) cr3); +} +#endif // SINGULARITY_KERNEL + +// +///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/arm/cyclecounter.asm b/base/Kernel/Native/arm/cyclecounter.asm new file mode 100644 index 0000000..6c56165 --- /dev/null +++ b/base/Kernel/Native/arm/cyclecounter.asm @@ -0,0 +1,114 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity ARM Bootstrap +;;; +;;; + +|defining ?g_GetCycleCount@Class_Microsoft_Singularity_Isal_Isa@@SA_KXZ| EQU 1 +|defining ?g_EnableCycleCounter@Class_Microsoft_Singularity_Isal_Isa@@SAXXZ| EQU 1 + + include hal.inc + + TEXTAREA + +;;; +;;; Reset cycle counter +;;; +;;; "void __cdecl ResetCycleCounter(void)" +;;; + LEAF_ENTRY ?ResetCycleCounter@@YAXXZ + mrc p15, 0, r1, c9, c12, 0 ; read PMCR + orr r1, r1, #8 ; set C + mcr p15, 0, r1, c9, c12, 0 ; set PMCR + +; bkpt 0xffff + bx lr + LEAF_END + +;;; +;;; Check for cycle counter wrap +;;; +;;; "bool __cdecl CycleCounterWrapped(void)" +;;; + LEAF_ENTRY ?CycleCounterWrapped@@YA_NXZ + mrc p15, 0, r1, c9, c12, 3 ; read overflow status + mov r1, r1, lsr #31 ; move CC overflow to bit 0 + and r0, r1, #1 ; check bit 0 + +; bkpt 0xffff + bx lr + LEAF_END + +;;; +;;; Clear cycle counter wrap flag +;;; +;;; "void __cdecl ClearCycleCounterWrap(void)" +;;; + LEAF_ENTRY ?ClearCycleCounterWrap@@YAXXZ + mrc p15, 0, r1, c9, c12, 3 ; read overflow status + bic r1, r1, #0x80000000 ; clear CC overflow bit + mcr p15, 0, r1, c9, c12, 3 ; set overflow status + +; bkpt 0xffff + bx lr + LEAF_END + +;;; +;;; Enable cycle counter +;;; +;;; "void __cdecl EnableCycleCounter(void)" +;;; + LEAF_ENTRY ?g_EnableCycleCounter@Class_Microsoft_Singularity_Isal_Isa@@SAXXZ + mrc p15, 0, r1, c9, c12, 0 ; read PMCR + orr r1, r1, #1 ; set E (enable) bit + mcr p15, 0, r1, c9, c12, 0 ; write PMCR + + mrc p15, 0, r1, c9, c12, 1 ; read PM count enable set register + orr r1, r1, #0x80000000 ; set C (CC enable) bit + mcr p15, 0, r1, c9, c12, 1 ; write PM count enable set register + +; bkpt 0xffff + bx lr + LEAF_END + +;;; +;;; Disable cycle counter +;;; +;;; "void __cdecl DisableCycleCounter(void)" +;;; + LEAF_ENTRY ?DisableCycleCounter@@YAXXZ + mrc p15, 0, r1, c9, c12, 1 ; read PM count enable set register + bic r1, r1, #0x80000000 ; clear C (CC enable) bit + mcr p15, 0, r1, c9, c12, 1 ; write PM count enable set register + + mrc p15, 0, r1, c9, c12, 0 ; read PMCR + bic r1, r1, #1 ; clear E (enable) bit + mcr p15, 0, r1, c9, c12, 0 ; write PMCR + +; bkpt 0xffff + bx lr + LEAF_END + +;;; +;;; "public: static unsigned __int64 __cdecl Class_Microsoft_Singularity_Isal_Isa::g_GetCycleCount(void)" +;;; + LEAF_ENTRY ?g_GetCycleCount@Class_Microsoft_Singularity_Isal_Isa@@SA_KXZ + mrc p15, 0, r0, c9, c13, 0 + mov r1, #0 + +; bkpt 0xffff + bx lr + LEAF_END + +;;; +;;; "unsigned __int64 __cdecl RDTSC(void)" +;;; + LEAF_ENTRY ?RDTSC@@YA_KXZ + mrc p15, 0, r0, c9, c13, 0 + mov r1, #0 + +; bkpt 0xffff + bx lr + LEAF_END + + END diff --git a/base/Kernel/Native/arm/cyclecounter_xscale.asm b/base/Kernel/Native/arm/cyclecounter_xscale.asm new file mode 100644 index 0000000..3d1fadd --- /dev/null +++ b/base/Kernel/Native/arm/cyclecounter_xscale.asm @@ -0,0 +1,106 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity ARM Bootstrap +;;; +;;; Xscale varient of the cycle counter routines +;;; + +|defining ?g_GetCycleCount@Class_Microsoft_Singularity_Isal_Isa@@SA_KXZ| EQU 1 +|defining ?g_EnableCycleCounter@Class_Microsoft_Singularity_Isal_Isa@@SAXXZ| EQU 1 + + include hal.inc + + IMPORT |?g_EnableInterrupts@Class_Microsoft_Singularity_Isal_Isa@@SAXXZ| + IMPORT |?g_DisableInterrupts@Class_Microsoft_Singularity_Isal_Isa@@SA_NXZ| + + TEXTAREA + +CycleCountHi DCD 0 ; storage for upper 32--bits of cycle counter (extended) + +;;; +;;; Reset cycle counter +;;; +;;; "void __cdecl ResetCycleCounter(void)" +;;; + LEAF_ENTRY ?ResetCycleCounter@@YAXXZ + mov r1, #0 + mrc p14, 0, r1, c0, c0, 0 ; read PMCR + orr r1, r1, #4 ; set C + mcr p14, 0, r1, c0, c0, 0 ; set PMCR + bx lr + LEAF_END + +;;; +;;; Check for cycle counter wrap and clean +;;; +;;; "bool __cdecl CycleCounterWrapped(void)" +;;; + LEAF_ENTRY ?CycleCounterWrapped@@YA_NXZ + mov r1, #0 + bx lr + LEAF_END + +;;; +;;; Enable cycle counter +;;; +;;; "void __cdecl EnableCycleCounter(void)" +;;; + LEAF_ENTRY ?g_EnableCycleCounter@Class_Microsoft_Singularity_Isal_Isa@@SAXXZ + mov r1, #0 + mrc p14, 0, r1, c0, c1, 0 ; read PMCR + orr r1, r1, #1 ; set E (enable) bit, disable interrupts + mcr p14, 0, r1, c0, c1, 0 ; write PMCR + bx lr + LEAF_END + +;;; +;;; Disable cycle counter +;;; +;;; "void __cdecl DisableCycleCounter(void)" +;;; + LEAF_ENTRY ?DisableCycleCounter@@YAXXZ + mov r1, #0 + mrc p14, 0, r1, c0, c1, 0 ; read PMCR + bic r1, r1, #1 ; clear E (enable) bit + mcr p14, 0, r1, c0, c1, 0 ; write PMCR + bx lr + LEAF_END + +;;; +;;; "public: static unsigned __int64 __cdecl Class_Microsoft_Singularity_Processor::g_GetCycleCount(void)" +;;; + NESTED_ENTRY ?g_GetCycleCount@Class_Microsoft_Singularity_Isal_Isa@@SA_KXZ + + stmfd sp!, {r4,lr} + + PROLOG_END + + ;; Disable interrupts, r0 becomes do_restore + bl |?g_DisableInterrupts@Class_Microsoft_Singularity_Isal_Isa@@SA_NXZ| + mov r2, r0 ; r2 becomes do_restore + + ;; Increment high bits if wrap has occurred + adr r3, CycleCountHi ; r3 is pointer to hi + ldr r0, [r3] ; r0 is cycle count hi + + mrc p14, 0, r1, c5, c1, 0 ; r1 is FLAG + ands r1, r1, #1 ; r1 is CCNT wrapped + + mcrne p14, 0, r1, c5, c1, 0 ; clear bit if set + addne r0, r0, r1 ; increment count hi + strne r0, [r3] ; store count hi + mov r3, r0 ; r3 is cycle count hi + mrc p14, 0, r4, c1, c1, 0 ; r4 becomes cycle cnt + + ;; Restore interrupts + cmp r2, #0 + blne |?g_EnableInterrupts@Class_Microsoft_Singularity_Isal_Isa@@SAXXZ| + + mov r0, r4 ; r0 is cycle count lo + mov r1, r3 ; r1 is cycle count hi + ldmfd sp!, {r4,lr} + + bx lr + NESTED_END + + END diff --git a/base/Kernel/Native/arm/hal.inc b/base/Kernel/Native/arm/hal.inc new file mode 100644 index 0000000..5d9eacb --- /dev/null +++ b/base/Kernel/Native/arm/hal.inc @@ -0,0 +1,157 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; Include file for ARM assembly files. +;;; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +OMAP3430_CPU_ARGS_ADDR equ 0x8000fff0 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; + + include halclass.armfix.inc + include kxarm.inc + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Macros for accessing fields inside ProcessorContext +;;; +;;; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; TPIDRURW ([OMAP3430_CPU_ARGS_ADDR] on older processors) points to the "fs/gs" page. +;;; The CurrentThread and CurrentCpu pointers are stored at offsets on that page. +;;; + MACRO + GET_TPIDRURW $reg + + ldr $reg, =OMAP3430_CPU_ARGS_ADDR + ldr $reg, [$reg] + + MEND + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Get the address of the CurrentThread variable. +;;; + MACRO + GET_THREAD_ADDR $reg, $scratch + + ;; Workaround assembler bug with ldr $reg, =foo + ldr $scratch, [pc] + mov pc, pc ;; skip over dcd value. + dcd |?c_currentThreadOffset@Class_Microsoft_Singularity_Isal_Isa@@2HA| + ldr $scratch, [$scratch] + + GET_TPIDRURW $reg + add $reg, $reg, $scratch + + MEND + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Get the address of the CurrentCpu variable. +;;; + MACRO + GET_CPU_ADDR $reg, $scratch + + ;; Workaround assembler bug with ldr $reg, =foo + ldr $scratch, [pc] + mov pc, pc + dcd |?c_currentCpuOffset@Class_Microsoft_Singularity_Isal_Isa@@2HA| + ldr $scratch, [$scratch] + + GET_TPIDRURW $reg + add $reg, $reg, $scratch + + MEND + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Get the address of a field in the CurrentThread record. +;;; + MACRO + GET_THREAD_FIELD_ADDR $reg, $scratch, $offset + + GET_THREAD_ADDR $reg, $scratch + ldr $reg, [$reg] + add $reg, $reg, $offset + + MEND + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Get the address of a field in the CurrentCpu record. +;;; + MACRO + GET_CPU_FIELD_ADDR $reg, $scratch, $offset + + GET_CPU_ADDR $reg, $scratch + ldr $reg, [$reg] + add $reg, $reg, $offset + + MEND + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Push a register onto the stack. +;;; + MACRO + PUSH $reg + str $reg, [sp, #-4]! + MEND + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Pop a register from the stack. +;;; + MACRO + POP $reg + ldr $reg, [sp], #4 + MEND + + + MACRO + CurrentThreadIndex $reg, $tmpReg + mov $reg, sp, LSR #PAGE_BITS + mov $reg, $reg, LSL #2 + ldr $tmpReg, pageTable + add $reg, $reg, $tmpReg + ldr $reg, [$reg] + ldr $tmpReg, =0xFFF + and $reg, $tmpReg, $reg, LSR #4 + MEND + + EXTERN |?c_halPageDescriptor@Class_System_GCs_PageTable@@2PAIA| + EXTERN |?c_threadTable@Class_System_Threading_Thread@@2PAUClassVector_Class_System_Threading_Thread@@A| +; EXTERN |?c_initialThread@Class_System_Threading_Thread@@2PAU1@A| + +pageTable + DCD |?c_halPageDescriptor@Class_System_GCs_PageTable@@2PAIA| +threadTable + DCD |?c_threadTable@Class_System_Threading_Thread@@2PAUClassVector_Class_System_Threading_Thread@@A| + + MACRO + ThreadFromIndex $reg, $tmpReg + mov $reg, $reg, LSL #2 + add $reg, $reg, #8 + ldr $tmpReg, threadTable + ldr $tmpReg, [$tmpReg] + add $reg, $reg, $tmpReg + ldr $reg, [$reg] + MEND + + MACRO + CurrentThread $reg, $tmpReg + CurrentThreadIndex $reg, $tmpReg + ThreadFromIndex $reg, $tmpReg + MEND + + END diff --git a/base/Kernel/Native/arm/halkdarm.cpp b/base/Kernel/Native/arm/halkdarm.cpp new file mode 100644 index 0000000..8a7917c --- /dev/null +++ b/base/Kernel/Native/arm/halkdarm.cpp @@ -0,0 +1,320 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: halkdarm.cpp - Processor-specific support routines for halkd.cpp. +// +#include "hal.h" +#include "halkd.h" + +#define KDDBG if (0) kdprintf + +extern "C" void * __cdecl memcpy(void *, const void *, size_t); + +void KdpArmSetControlReport(IN OUT PDBGKD_CONTROL_REPORT report); + +//////////////////////////////////////////////// User visible progress beacon. +// +void KdpSpin() +{ +#if 0 + extern uint16 * videoBuffer; + static uint16 values[] = { 0x0000, 0xffff, 0xfc00, 0x03f0, 0x000f }; + + + if (videoBuffer != 0) { + static UINT8 state = 0; + + videoBuffer[0] = values[state]; + videoBuffer[1] = values[state]; + videoBuffer[2] = values[state]; + videoBuffer[3] = values[state]; + videoBuffer[4] = values[state]; + videoBuffer[5] = values[state]; + videoBuffer[6] = values[state]; + videoBuffer[7] = values[state]; + + state++; + if (state >= arrayof(values)) { + state = 0; + } + } +#endif +} + +////////////////////////////////////////////////////////////////////////////// + +void KdpToKdContext(IN CONST Struct_Microsoft_Singularity_Isal_SpillContext *singularity, + OUT CONTEXT *windbg) +{ + windbg->ContextFlags = (CONTEXT_CONTROL | + CONTEXT_INTEGER); + + windbg->Sp = singularity->sp; + windbg->Lr = singularity->lr; + windbg->Pc = singularity->pc; + windbg->R0 = singularity->r0; + windbg->R1 = singularity->r1; + windbg->R2 = singularity->r2; + windbg->R3 = singularity->r3; + windbg->R4 = singularity->r4; + windbg->R5 = singularity->r5; + windbg->R6 = singularity->r6; + windbg->R7 = singularity->r7; + windbg->R8 = singularity->r8; + windbg->R9 = singularity->r9; + windbg->R10 = singularity->r10; + windbg->R11 = singularity->r11; + windbg->R12 = singularity->r12; + windbg->Cpsr = singularity->cpsr; +} + +void KdpFromKdContext(IN CONST CONTEXT *windbg, + OUT Struct_Microsoft_Singularity_Isal_SpillContext *singularity) +{ + singularity->sp = windbg->Sp; + singularity->lr = windbg->Lr; + singularity->pc = windbg->Pc; + singularity->r0 = windbg->R0; + singularity->r1 = windbg->R1; + singularity->r2 = windbg->R2; + singularity->r3 = windbg->R3; + singularity->r4 = windbg->R4; + singularity->r5 = windbg->R5; + singularity->r6 = windbg->R6; + singularity->r7 = windbg->R7; + singularity->r8 = windbg->R8; + singularity->r9 = windbg->R9; + singularity->r10 = windbg->R10; + singularity->r11 = windbg->R11; + singularity->r12 = windbg->R12; + singularity->cpsr = windbg->Cpsr; +} + +void KdpSetControlReport(IN OUT PDBGKD_CONTROL_REPORT report, + IN CONST Struct_Microsoft_Singularity_Isal_SpillContext *singularity) +{ + KdpArmSetControlReport(report); + + report->Cpsr = singularity->cpsr; +} + +void KdpSetControlSet(IN CONST DBGKD_CONTROL_SET * control, + IN OUT Struct_Microsoft_Singularity_Isal_SpillContext *singularity) +{ + // TODO: This needs to be filled in. +} + +////////////////////////////////////////////////////////////////////////////// + +void KdpReadSpecialRegisters(KSPECIAL_REGISTERS *pksp, + IN CONST Struct_Microsoft_Singularity_Isal_SpillContext *singularity) +{ +#if 0 + internal struct ArmKSpecialRegisters { + internal uint Cp15Cr0Id; + internal uint Cp15Cr0Cachetype; + internal uint Cp15Cr1Control; + internal uint Cp15Cr2Ttb; + internal uint Cp15Cr3Dacr; + internal uint Cp15Cr5FaultStatus; + internal uint Cp15Cr5FaultAddress; + internal uint Cp15Cr13ProcessId; + }; +#endif + +#if 0 + pksp->Cp15Cr0Id = _MoveFromCoprocessor(0, 15, 0, 7, 5, 0); +#endif +} + +void KdpWriteSpecialRegisters(CONST KSPECIAL_REGISTERS *pksp) +{ +#if 0 +#endif +} + +void KdpFlushInstCache(void) +{ + _MoveToCoprocessor(0, 15, 0, 7, 5, 0); // ICIALLU: Invalidate all instruction caches. +} + +bool KdpReadMsr(UINT32 msr, UINT32 *plo, UINT32 *phi) +{ + *phi = 0; + + switch (msr) { +#undef VALID_COPROC_REGISTER +#define VALID_COPROC_REGISTER(_Coproc, _OpCode1, _CoReg1, _CoReg2, _OpCode2) \ + case (_Coproc << 16) | (_OpCode1 << 12) | (_CoReg1 << 8) | (_CoReg2 << 4) | _OpCode2 : \ + *plo = _MoveFromCoprocessor(_Coproc, _OpCode1, _CoReg1, _CoReg2, _OpCode2); \ + break; +#include "halkdmsr.h" + + default: + return false; + } + return true; + +} + +bool KdpWriteMsr(UINT32 msr, UINT32 lo, UINT32 hi) +{ + switch (msr) { +#undef VALID_COPROC_REGISTER +#define VALID_COPROC_REGISTER(_Coproc, _OpCode1, _CoReg1, _CoReg2, _OpCode2) \ + case (_Coproc << 16) | (_OpCode1 << 12) | (_CoReg1 << 8) | (_CoReg2 << 4) | _OpCode2 : \ + _MoveToCoprocessor(lo, _Coproc, _OpCode1, _CoReg1, _CoReg2, _OpCode2); \ + break; +#include "halkdmsr.h" + default: + return false; + } + return true; +} + +////////////////////////////////////////////////////////////////////////////// + +KdDebugTrapData * KdpIsDebugTrap(IN CONST Struct_Microsoft_Singularity_Isal_SpillContext *context, + int id) +{ + if ((id == Struct_Microsoft_Singularity_Isal_Arm_ExceptionVector_SoftwareInterrupt) && + (context->instruction == 0xefffff2e)) { + + KDDBG("IsDebugTrap(%x) tag=%p\n", id, ((KdDebugTrapData *)(context->r0))->tag); + return (KdDebugTrapData *)(context->r0); + } + KDDBG("IsDebugTrap(%x) NULL\n", id); + return NULL; +} + +// Convert a trap into a exception record. +void KdpConvertTrapToException(IN OUT EXCEPTION_RECORD64 *per, + IN OUT Struct_Microsoft_Singularity_Isal_SpillContext *context, + int id) +{ + KDDBG("ConvertTrapToExc(%d)\n", id); + KDDBG(" pc=%p [%p]\n", context->pc, *((uint32*)context->pc)); + KDDBG(" lr=%p r0=%p\n", context->lr, context->r0); + + // Breakpoints: + switch (id) { + + case Struct_Microsoft_Singularity_Isal_Arm_ExceptionVector_UndefinedInstruction: { + KDDBG("Illegal Instruction %08x\n", context->instruction); + per->ExceptionCode = STATUS_ILLEGAL_INSTRUCTION; + per->ExceptionAddress = SIGN_EXTEND(context->pc - 4); + per->NumberParameters = 0; + return; + } + + case Struct_Microsoft_Singularity_Isal_Arm_ExceptionVector_PrefetchAbort: { + KDDBG("KD: Prefetch Exception %d\n", id); + per->ExceptionCode = STATUS_ACCESS_VIOLATION; + per->ExceptionAddress = SIGN_EXTEND(context->pc); + per->NumberParameters = 0; + return; + } + + case Struct_Microsoft_Singularity_Isal_Arm_ExceptionVector_DataAbort: { + KDDBG("KD: Data Exception %d\n", id); + per->ExceptionCode = STATUS_ACCESS_VIOLATION; + // Should probably decode instruction to figure out the real faulty address. + per->ExceptionAddress = SIGN_EXTEND(context->pc); + per->NumberParameters = 0; + return; + } + + case Struct_Microsoft_Singularity_Isal_Arm_ExceptionVector_Reset: + case Struct_Microsoft_Singularity_Isal_Arm_ExceptionVector_Irq: + case Struct_Microsoft_Singularity_Isal_Arm_ExceptionVector_Fiq: { + KDDBG("KD: Unexpected IRQ %d\n", id); + per->ExceptionCode = STATUS_UNHANDLED_EXCEPTION; + per->ExceptionAddress = SIGN_EXTEND(context->pc); + per->NumberParameters = 1; + per->ExceptionInformation0 = id; + return; + } + + case Struct_Microsoft_Singularity_Isal_Arm_ExceptionVector_SoftwareInterrupt: { + switch (context->instruction) { + case 0xefffff01: { // aka KDP_BREAKPOINT_VALUE (swi 0xffff01) + KDDBG("KD Single or Bkpt\n"); + per->ExceptionCode = STATUS_SINGLE_STEP; + per->ExceptionAddress = SIGN_EXTEND(context->pc); + return; + } + + case 0xefffff03: { // Hard-coded break (swi 0xffff03) + KDDBG("Breakpoint\n"); + per->ExceptionCode = STATUS_BREAKPOINT; + per->NumberParameters = 1; + per->ExceptionInformation0 = BREAKPOINT_BREAK; + per->ExceptionAddress = SIGN_EXTEND(context->pc - 4); + return; + } + + case 0xefffff2e: { // Debug notify (swi 0xffff2e) + KdDebugTrapData *trapData = (KdDebugTrapData *) (context->r0); + switch (trapData->tag) { + case KdDebugTrapData::FIRST_CHANCE_EXCEPTION: + context->r0 = trapData->firstChanceException.throwAddr; + KDDBG("KD: First chance C# exception\n"); + // per->ExceptionCode = STATUS_CPP_EH_EXCEPTION; //0xe06d7363; + per->ExceptionCode = STATUS_VCPP_EXCEPTION; //0x8000ff1f; + per->ExceptionAddress = SIGN_EXTEND(context->pc - 4); + per->NumberParameters = 1; + per->ExceptionInformation0 = BREAKPOINT_BREAK; + return; + + default: + KDDBG("KD: Bad trap 0x%x\n", id); + per->ExceptionCode = STATUS_UNHANDLED_EXCEPTION; + per->ExceptionAddress = SIGN_EXTEND(context->pc - 4); + per->NumberParameters = 1; + per->ExceptionInformation0 = id; + return; + } + } + + default: { + KDDBG("KD: Unexpected SWI %08x\n", context->instruction); + per->ExceptionCode = STATUS_ILLEGAL_INSTRUCTION; + per->ExceptionAddress = SIGN_EXTEND(context->pc - 4); + return; + } + } + } + + default: + KDDBG("KD: Unexpected interrupt %d\n", id); + per->ExceptionCode = 0x80000000 + id; + per->ExceptionAddress = SIGN_EXTEND(context->pc); + return; + } +} + +// +// Read or Write I/O Space. +// +// Return: Silently does nothing on ARM. Could be implemented +// in the future to read/write from a simulated I/O space +// if an ARM platform has such a mapping. +// +// iowrite == 0: value read from port +// +// iowrite !=0: zero +// +int KdpReadWriteIoSpace( + int size, // 1, 2, 4 + int iowrite, // true if write, false if read + unsigned short addr, + unsigned int value + ) +{ + return 0; +} + + diff --git a/base/Kernel/Native/arm/halkdmsr.h b/base/Kernel/Native/arm/halkdmsr.h new file mode 100644 index 0000000..68d90fe --- /dev/null +++ b/base/Kernel/Native/arm/halkdmsr.h @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: halkdnmsr +// +// This module contain definitions used to define the valid +// coprocessor registers that can be accessed via the read/write msr +// functionality in the debugger. +// +// Because the parameters are encoded in instructions rather than +// carried in registers, an array of valid coprocessor registers would not +// suffice. +// + +// +// This file is included multiple times with different definitions of +// the VALID_COPROC_REGISTER macro. +// + +VALID_COPROC_REGISTER(15, 0, 0, 0, 0) +VALID_COPROC_REGISTER(15, 0, 0, 0, 1) +VALID_COPROC_REGISTER(15, 0, 0, 0, 5) +VALID_COPROC_REGISTER(15, 0, 0, 1, 0) +VALID_COPROC_REGISTER(15, 0, 0, 1, 1) +VALID_COPROC_REGISTER(15, 0, 0, 1, 2) +VALID_COPROC_REGISTER(15, 0, 0, 1, 4) +VALID_COPROC_REGISTER(15, 0, 0, 1, 5) +VALID_COPROC_REGISTER(15, 0, 0, 1, 6) +VALID_COPROC_REGISTER(15, 0, 0, 1, 7) +VALID_COPROC_REGISTER(15, 0, 1, 0, 0) +VALID_COPROC_REGISTER(15, 0, 1, 0, 1) +VALID_COPROC_REGISTER(15, 0, 2, 0, 0) +VALID_COPROC_REGISTER(15, 0, 2, 0, 1) +VALID_COPROC_REGISTER(15, 0, 2, 0, 2) +VALID_COPROC_REGISTER(15, 0, 3, 0, 0) +VALID_COPROC_REGISTER(15, 0, 7, 5, 0) +VALID_COPROC_REGISTER(15, 0, 7, 5, 4) +VALID_COPROC_REGISTER(15, 0, 7, 5, 6) +VALID_COPROC_REGISTER(15, 0, 7, 7, 0) +VALID_COPROC_REGISTER(15, 0, 7, 10, 1) +VALID_COPROC_REGISTER(15, 0, 7, 10, 4) +VALID_COPROC_REGISTER(15, 0, 7, 10, 5) +VALID_COPROC_REGISTER(15, 0, 7, 14, 0) +VALID_COPROC_REGISTER(15, 0, 7, 15, 0) +VALID_COPROC_REGISTER(15, 0, 8, 7, 0) +VALID_COPROC_REGISTER(15, 0, 8, 7, 1) +VALID_COPROC_REGISTER(15, 0, 8, 7, 2) +VALID_COPROC_REGISTER(15, 0, 10, 2, 0) +VALID_COPROC_REGISTER(15, 0, 10, 2, 1) +VALID_COPROC_REGISTER(15, 0, 13, 0, 1) +VALID_COPROC_REGISTER(15, 0, 13, 0, 2) +VALID_COPROC_REGISTER(15, 0, 13, 0, 3) +VALID_COPROC_REGISTER(15, 0, 13, 0, 4) diff --git a/base/Kernel/Native/arm/icu_xscale.asm b/base/Kernel/Native/arm/icu_xscale.asm new file mode 100644 index 0000000..0f56a56 --- /dev/null +++ b/base/Kernel/Native/arm/icu_xscale.asm @@ -0,0 +1,560 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity ARM Bootstrap +;;; +;;; XScale 81348 ICU support routines +;;; + +;;; Definitions taken from: +;;; +;;; Chapter 10, Intel 81348 I/O Processor, September 2006, O/N: 315036-001US +;;; +;;; # Name CRm CRn +;;; INTBASE 2 0 +;;; INTSIZE 2 2 +;;; IINTVEC 2 3 +;;; FINTVEC 2 4 +;;; IPIPNDR 2 8 +;;; +;;; INTPND0 3 0 +;;; INTPND1 3 1 +;;; INTPND2 3 2 +;;; INTPND3 3 3 +;;; +;;; INTCTL0 4 0 +;;; INTCTL1 4 1 +;;; INTCTL2 4 2 +;;; INTCTL3 4 3 +;;; +;;; INTSTR0 5 0 +;;; INTSTR1 5 1 +;;; INTSTR2 5 2 +;;; INTSTR3 5 3 +;;; +;;; IINTSRC0 6 0 +;;; IINTSRC1 6 1 +;;; IINTSRC2 6 2 +;;; IINTSRC3 6 3 +;;; +;;; FINTSRC0 7 0 +;;; FINTSRC1 7 1 +;;; FINTSRC2 7 2 +;;; FINTSRC3 7 3 +;;; +;;; IPR0 8 0 +;;; IPR1 8 1 +;;; IPR2 8 2 +;;; IPR3 8 3 +;;; IPR4 8 4 +;;; IPR5 8 5 +;;; IPR6 8 6 +;;; IPR7 8 7 +;;; +;;; MRC = coprocessor transfer to ARM register +;;; mrc , , , , , +;;; +;;; MCR = ARM register to coprocessor transfer +;;; mcr , , , , , +;;; + + CODE32 + + AREA |.text|, CODE, ARM + + ;;; Register operations for INTBASE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadINTBASE@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadINTBASE@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c0, c2, 0 + bx lr + ENDP + + EXPORT |?g_WriteINTBASE@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteINTBASE@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c0, c2, 0 + bx lr + ENDP + + ;;; Register operations for INTSIZE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadINTSIZE@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadINTSIZE@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c2, c2, 0 + bx lr + ENDP + + EXPORT |?g_WriteINTSIZE@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteINTSIZE@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c2, c2, 0 + bx lr + ENDP + + ;;; Register operations for IINTVEC ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadIINTVEC@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadIINTVEC@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c3, c2, 0 + bx lr + ENDP + + EXPORT |?g_WriteIINTVEC@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteIINTVEC@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c3, c2, 0 + bx lr + ENDP + + ;;; Register operations for FINTVEC ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadFINTVEC@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadFINTVEC@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c4, c2, 0 + bx lr + ENDP + + EXPORT |?g_WriteFINTVEC@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteFINTVEC@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c4, c2, 0 + bx lr + ENDP + + ;;; Register operations for IPIPNDR ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadIPIPNDR@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadIPIPNDR@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c8, c2, 0 + bx lr + ENDP + + EXPORT |?g_WriteIPIPNDR@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteIPIPNDR@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c8, c2, 0 + bx lr + ENDP + + ;;; Register operations for INTPND0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadINTPND0@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadINTPND0@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c0, c3, 0 + bx lr + ENDP + + EXPORT |?g_WriteINTPND0@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteINTPND0@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c0, c3, 0 + bx lr + ENDP + + ;;; Register operations for INTPND1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadINTPND1@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadINTPND1@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c1, c3, 0 + bx lr + ENDP + + EXPORT |?g_WriteINTPND1@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteINTPND1@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c1, c3, 0 + bx lr + ENDP + + ;;; Register operations for INTPND2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadINTPND2@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadINTPND2@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c2, c3, 0 + bx lr + ENDP + + EXPORT |?g_WriteINTPND2@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteINTPND2@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c2, c3, 0 + bx lr + ENDP + + ;;; Register operations for INTPND3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadINTPND3@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadINTPND3@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c3, c3, 0 + bx lr + ENDP + + EXPORT |?g_WriteINTPND3@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteINTPND3@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c3, c3, 0 + bx lr + ENDP + + ;;; Register operations for INTCTL0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadINTCTL0@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadINTCTL0@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c0, c4, 0 + bx lr + ENDP + + EXPORT |?g_WriteINTCTL0@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteINTCTL0@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c0, c4, 0 + bx lr + ENDP + + ;;; Register operations for INTCTL1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadINTCTL1@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadINTCTL1@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c1, c4, 0 + bx lr + ENDP + + EXPORT |?g_WriteINTCTL1@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteINTCTL1@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c1, c4, 0 + bx lr + ENDP + + ;;; Register operations for INTCTL2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadINTCTL2@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadINTCTL2@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c2, c4, 0 + bx lr + ENDP + + EXPORT |?g_WriteINTCTL2@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteINTCTL2@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c2, c4, 0 + bx lr + ENDP + + ;;; Register operations for INTCTL3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadINTCTL3@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadINTCTL3@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c3, c4, 0 + bx lr + ENDP + + EXPORT |?g_WriteINTCTL3@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteINTCTL3@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c3, c4, 0 + bx lr + ENDP + + ;;; Register operations for INTSTR0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadINTSTR0@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadINTSTR0@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c0, c5, 0 + bx lr + ENDP + + EXPORT |?g_WriteINTSTR0@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteINTSTR0@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c0, c5, 0 + bx lr + ENDP + + ;;; Register operations for INTSTR1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadINTSTR1@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadINTSTR1@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c1, c5, 0 + bx lr + ENDP + + EXPORT |?g_WriteINTSTR1@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteINTSTR1@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c1, c5, 0 + bx lr + ENDP + + ;;; Register operations for INTSTR2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadINTSTR2@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadINTSTR2@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c2, c5, 0 + bx lr + ENDP + + EXPORT |?g_WriteINTSTR2@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteINTSTR2@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c2, c5, 0 + bx lr + ENDP + + ;;; Register operations for INTSTR3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadINTSTR3@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadINTSTR3@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c3, c5, 0 + bx lr + ENDP + + EXPORT |?g_WriteINTSTR3@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteINTSTR3@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c3, c5, 0 + bx lr + ENDP + + ;;; Register operations for IINTSRC0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadIINTSRC0@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadIINTSRC0@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c0, c6, 0 + bx lr + ENDP + + EXPORT |?g_WriteIINTSRC0@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteIINTSRC0@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c0, c6, 0 + bx lr + ENDP + + ;;; Register operations for IINTSRC1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadIINTSRC1@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadIINTSRC1@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c1, c6, 0 + bx lr + ENDP + + EXPORT |?g_WriteIINTSRC1@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteIINTSRC1@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c1, c6, 0 + bx lr + ENDP + + ;;; Register operations for IINTSRC2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadIINTSRC2@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadIINTSRC2@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c2, c6, 0 + bx lr + ENDP + + EXPORT |?g_WriteIINTSRC2@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteIINTSRC2@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c2, c6, 0 + bx lr + ENDP + + ;;; Register operations for IINTSRC3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadIINTSRC3@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadIINTSRC3@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c3, c6, 0 + bx lr + ENDP + + EXPORT |?g_WriteIINTSRC3@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteIINTSRC3@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c3, c6, 0 + bx lr + ENDP + + ;;; Register operations for FINTSRC0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadFINTSRC0@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadFINTSRC0@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c0, c7, 0 + bx lr + ENDP + + EXPORT |?g_WriteFINTSRC0@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteFINTSRC0@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c0, c7, 0 + bx lr + ENDP + + ;;; Register operations for FINTSRC1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadFINTSRC1@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadFINTSRC1@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c1, c7, 0 + bx lr + ENDP + + EXPORT |?g_WriteFINTSRC1@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteFINTSRC1@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c1, c7, 0 + bx lr + ENDP + + ;;; Register operations for FINTSRC2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadFINTSRC2@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadFINTSRC2@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c2, c7, 0 + bx lr + ENDP + + EXPORT |?g_WriteFINTSRC2@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteFINTSRC2@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c2, c7, 0 + bx lr + ENDP + + ;;; Register operations for FINTSRC3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadFINTSRC3@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadFINTSRC3@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c3, c7, 0 + bx lr + ENDP + + EXPORT |?g_WriteFINTSRC3@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteFINTSRC3@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c3, c7, 0 + bx lr + ENDP + + ;;; Register operations for IPR0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadIPR0@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadIPR0@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c0, c8, 0 + bx lr + ENDP + + EXPORT |?g_WriteIPR0@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteIPR0@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c0, c8, 0 + bx lr + ENDP + + ;;; Register operations for IPR1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadIPR1@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadIPR1@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c1, c8, 0 + bx lr + ENDP + + EXPORT |?g_WriteIPR1@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteIPR1@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c1, c8, 0 + bx lr + ENDP + + ;;; Register operations for IPR2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadIPR2@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadIPR2@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c2, c8, 0 + bx lr + ENDP + + EXPORT |?g_WriteIPR2@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteIPR2@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c2, c8, 0 + bx lr + ENDP + + ;;; Register operations for IPR3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadIPR3@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadIPR3@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c3, c8, 0 + bx lr + ENDP + + EXPORT |?g_WriteIPR3@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteIPR3@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c3, c8, 0 + bx lr + ENDP + + ;;; Register operations for IPR4 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadIPR4@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadIPR4@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c4, c8, 0 + bx lr + ENDP + + EXPORT |?g_WriteIPR4@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteIPR4@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c4, c8, 0 + bx lr + ENDP + + ;;; Register operations for IPR5 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadIPR5@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadIPR5@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c5, c8, 0 + bx lr + ENDP + + EXPORT |?g_WriteIPR5@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteIPR5@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c5, c8, 0 + bx lr + ENDP + + ;;; Register operations for IPR6 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadIPR6@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadIPR6@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c6, c8, 0 + bx lr + ENDP + + EXPORT |?g_WriteIPR6@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteIPR6@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c6, c8, 0 + bx lr + ENDP + + ;;; Register operations for IPR7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadIPR7@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| +|?g_ReadIPR7@Class_Microsoft_Singularity_Hal_Icu@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c7, c8, 0 + bx lr + ENDP + + EXPORT |?g_WriteIPR7@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| +|?g_WriteIPR7@Class_Microsoft_Singularity_Hal_Icu@@SAXI@Z| PROC + mcr p6, 0, r0, c7, c8, 0 + bx lr + ENDP + + END + diff --git a/base/Kernel/Native/arm/interlocked_v5.asm b/base/Kernel/Native/arm/interlocked_v5.asm new file mode 100644 index 0000000..fd234e4 --- /dev/null +++ b/base/Kernel/Native/arm/interlocked_v5.asm @@ -0,0 +1,321 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +|defining ?g_CompareExchange@Class_System_Threading_Interlocked@@SA?AUStruct_System_Threading_ThreadState@@PAU2@U2@1@Z| EQU 1 +|defining ?g_CompareExchange@Class_System_Threading_Interlocked@@SAHPAHHH@Z| EQU 1 +|defining ?g_CompareExchange@Class_System_Threading_Interlocked@@SAIPAIII@Z| EQU 1 +|defining ?g_CompareExchange@Class_System_Threading_Interlocked@@SAMPAMMM@Z| EQU 1 +|defining ?g_CompareExchange@Class_System_Threading_Interlocked@@SAPAUClass_System_Object@@PAPAU2@PAU2@1@Z| EQU 1 +|defining ?g_CompareExchange@Class_System_Threading_Interlocked@@SAPAUStruct_Microsoft_Singularity_Memory_HandleTable_HandleEntry@@PAPAU2@PAU2@1@Z| EQU 1 +|defining ?g_CompareExchange@Class_System_Threading_Interlocked@@SAPAUStruct_Microsoft_Singularity_Memory_HandleTable_HandlePage@@PAPAU2@PAU2@1@Z| EQU 1 +|defining ?g_CompareExchange@Class_System_Threading_Interlocked@@SAPAUuintPtr@@PAPAU2@PAU2@1@Z| EQU 1 +|defining ?g_CompareExchange@Class_System_Threading_Interlocked@@SAPAUvoid@@PAPAU2@PAU2@1@Z| EQU 1 +|defining ?g_CompareExchange@Class_System_Threading_Interlocked@@SA_JPA_J_J1@Z| EQU 1 +|defining ?g_CompareExchange@Class_System_Threading_Interlocked@@SA_KPA_K_K1@Z| EQU 1 +|defining ?g_Decrement@Class_System_Threading_Interlocked@@SAHPAH@Z| EQU 1 +|defining ?g_Exchange@Class_System_Threading_Interlocked@@SAHPAHH@Z| EQU 1 +|defining ?g_Exchange@Class_System_Threading_Interlocked@@SAIPAII@Z| EQU 1 +|defining ?g_Exchange@Class_System_Threading_Interlocked@@SAMPAMM@Z| EQU 1 +|defining ?g_Exchange@Class_System_Threading_Interlocked@@SAPAUClass_System_Object@@PAPAU2@PAU2@@Z| EQU 1 +|defining ?g_Exchange@Class_System_Threading_Interlocked@@SAPAUuintPtr@@PAPAU2@PAU2@@Z| EQU 1 +|defining ?g_Increment@Class_System_Threading_Interlocked@@SAHPAH@Z| EQU 1 + + include hal.inc + + MACRO + BREAKPOINT + bkpt 0xffff + swi 0xffff03 + MEND + + MACRO + DISABLE_INTERRUPTS $savereg, $tempreg + mrs $savereg, cpsr + orr $tempreg, $savereg, #Struct_Microsoft_Singularity_Isal_Arm_Psr_DisableIrq + msr cpsr_c, $tempreg + MEND + + MACRO + RESTORE_INTERRUPTS $savereg + msr cpsr_c, $savereg + MEND + + TEXTAREA + +;;; uint32 InterlockedCompareExchange(uint32 * dst, uint32 exc, uint32 cmp) + LEAF_ENTRY InterlockedCompareExchange + DISABLE_INTERRUPTS r3, r12 + ldr r12, [r0] + cmp r12, r2 + bne %F1 + str r1, [r0] +1 mov r0, r12 + RESTORE_INTERRUPTS r3 + bx lr + LEAF_END + +;;; uint32 InterlockedExchange(uint32 * dst, uint32 exc, uint32 cmp) + LEAF_ENTRY InterlockedExchange + DISABLE_INTERRUPTS r3, r12 + ldr r12, [r0] + str r1, [r0] + mov r0, r12 + RESTORE_INTERRUPTS r3 + bx lr + LEAF_END + +;;; uint32 InterlockedExchangeAdd(uint32 * dst, uint32 val) + LEAF_ENTRY InterlockedExchangeAdd + DISABLE_INTERRUPTS r3, r12 + ldr r12, [r0] + add r1, r12, r1 + str r1, [r0] + mov r0, r12 + RESTORE_INTERRUPTS r3 + bx lr + LEAF_END + +;;; uint32 InterlockedIncrement(uint32 * dst) + LEAF_ENTRY InterlockedIncrement + DISABLE_INTERRUPTS r3, r12 + ldr r1, [r0] + add r1, r1, #1 + str r1, [r0] + mov r0, r1 + RESTORE_INTERRUPTS r3 + bx lr + LEAF_END + +;;; uint32 InterlockedDecrement(uint32 * dst) + LEAF_ENTRY InterlockedDecrement + DISABLE_INTERRUPTS r3, r12 + ldr r1, [r0] + sub r1, r1, #1 + str r1, [r0] + mov r0, r1 + RESTORE_INTERRUPTS r3 + bx lr + LEAF_END + +;;; +;;; uint64 InterlockedCompareExchange64( +;;; r0 = uint64 *dst, +;;; r1/r2 = uint64 exc, +;;; r3/[sp] = uint64 cmp) +;;; + NESTED_ENTRY InterlockedCompareExchange64 + stmdb sp!, {r4-r7, lr} + PROLOG_END + DISABLE_INTERRUPTS r5, r12 ; save cpsr to r5 + ldr r4, [sp, #0x14] ; load r4 with Comperand.HighPart + + ldr r6, [r0, #0] ; load r6 with *dst.LowPart + ldr r7, [r0, #4] ; load r7 with *dst.HighPart + + cmp r6, r3 ; compare orginal with cmp + bne %F1 + cmp r7, r4 + bne %F1 + + str r1, [r0, #0] ; store exc.LowPart + str r2, [r0, #4] ; store exc.HighPart + +1 mov r0, r6 ; prepare to return original + mov r1, r7 ; + + RESTORE_INTERRUPTS r5 + ldmia sp!, {r4-r7, pc} + NESTED_END + +;;; +;;; int Class_System_Threading_Interlocked::g_CompareExchange( +;;; int * dst, +;;; int exc, +;;; int cmp) +;;; + LEAF_ENTRY ?g_CompareExchange@Class_System_Threading_Interlocked@@SAHPAHHH@Z + b InterlockedCompareExchange + nop + LEAF_END + +;;; +;;; unsigned int Class_System_Threading_Interlocked::g_CompareExchange( +;;; unsigned int *dst, +;;; unsigned int exc, +;;; unsigned int cmp) +;;; + LEAF_ENTRY ?g_CompareExchange@Class_System_Threading_Interlocked@@SAIPAIII@Z + b InterlockedCompareExchange + nop + LEAF_END + +;;; +;;; float Class_System_Threading_Interlocked::g_CompareExchange( +;;; float *dst, +;;; float exc, +;;; float cmp) +;;; + LEAF_ENTRY ?g_CompareExchange@Class_System_Threading_Interlocked@@SAMPAMMM@Z + b InterlockedCompareExchange + nop + LEAF_END + +;;; +;;; struct Class_System_Object * Class_System_Threading_Interlocked::g_CompareExchange( +;;; struct Class_System_Object ** dst, +;;; struct Class_System_Object * exc, +;;; struct Class_System_Object * cmp) +;;; + LEAF_ENTRY ?g_CompareExchange@Class_System_Threading_Interlocked@@SAPAUClass_System_Object@@PAPAU2@PAU2@1@Z + b InterlockedCompareExchange + nop + LEAF_END + +;;; +;;; struct Class_System_Threading_ThreadState Class_System_Threading_Interlocked::g_CompareExchange( +;;; struct Class_System_Threading_ThreadState *dst, +;;; struct Class_System_Threading_ThreadState exc, +;;; struct Class_System_Threading_ThreadState cmp) +;;; + LEAF_ENTRY ?g_CompareExchange@Class_System_Threading_Interlocked@@SA?AUStruct_System_Threading_ThreadState@@PAU2@U2@1@Z + b InterlockedCompareExchange + nop + LEAF_END + +;;; +;;; struct Struct_Microsoft_Singularity_Memory_HandleTable_HandleEntry * Class_System_Threading_Interlocked::g_CompareExchange( +;;; struct Struct_Microsoft_Singularity_Memory_HandleTable_HandleEntry ** dst, +;;; struct Struct_Microsoft_Singularity_Memory_HandleTable_HandleEntry * exc, +;;; struct Struct_Microsoft_Singularity_Memory_HandleTable_HandleEntry * cmp) +;;; + LEAF_ENTRY ?g_CompareExchange@Class_System_Threading_Interlocked@@SAPAUStruct_Microsoft_Singularity_Memory_HandleTable_HandleEntry@@PAPAU2@PAU2@1@Z + b InterlockedCompareExchange + nop + LEAF_END + +;;; +;;; struct Struct_Microsoft_Singularity_Memory_HandleTable_HandlePage * Class_System_Threading_Interlocked::g_CompareExchange( +;;; struct Struct_Microsoft_Singularity_Memory_HandleTable_HandlePage ** dst, +;;; struct Struct_Microsoft_Singularity_Memory_HandleTable_HandlePage * exc, +;;; struct Struct_Microsoft_Singularity_Memory_HandleTable_HandlePage * cmp) +;;; + LEAF_ENTRY ?g_CompareExchange@Class_System_Threading_Interlocked@@SAPAUStruct_Microsoft_Singularity_Memory_HandleTable_HandlePage@@PAPAU2@PAU2@1@Z + b InterlockedCompareExchange + nop + LEAF_END + +;;; +;;; struct uintPtr * Class_System_Threading_Interlocked::g_CompareExchange( +;;; struct uintPtr ** dst, +;;; struct uintPtr * exc, +;;; struct uintPtr * cmp) +;;; + LEAF_ENTRY ?g_CompareExchange@Class_System_Threading_Interlocked@@SAPAUuintPtr@@PAPAU2@PAU2@1@Z + b InterlockedCompareExchange + nop + LEAF_END + +;;; +;;; struct void * Class_System_Threading_Interlocked::g_CompareExchange( +;;; struct void ** dst, +;;; struct void * exc, +;;; struct void * cmp) +;;; + LEAF_ENTRY ?g_CompareExchange@Class_System_Threading_Interlocked@@SAPAUvoid@@PAPAU2@PAU2@1@Z + b InterlockedCompareExchange + nop + LEAF_END + +;;; +;;; __int64 Class_System_Threading_Interlocked::g_CompareExchange( +;;; __int64 *dst, +;;; __int64 exc, +;;; __int64 cmp) +;;; + LEAF_ENTRY ?g_CompareExchange@Class_System_Threading_Interlocked@@SA_JPA_J_J1@Z + b InterlockedCompareExchange64 + nop + LEAF_END + +;;; +;;; unsigned __int64 Class_System_Threading_Interlocked::g_CompareExchange( +;;; unsigned __int64 *dst, +;;; unsigned __int64 exc, +;;; unsigned __int64 cmp) +;;; + LEAF_ENTRY ?g_CompareExchange@Class_System_Threading_Interlocked@@SA_KPA_K_K1@Z + b InterlockedCompareExchange64 + nop + LEAF_END + +;;; +;;; int Class_System_Threading_Interlocked::g_Decrement(int * dst) +;;; + LEAF_ENTRY ?g_Decrement@Class_System_Threading_Interlocked@@SAHPAH@Z + b InterlockedDecrement + nop + LEAF_END + +;;; +;;;- int Class_System_Threading_Interlocked::g_Exchange(int * dst, +;;; int exc) +;;; + LEAF_ENTRY ?g_Exchange@Class_System_Threading_Interlocked@@SAHPAHH@Z + b InterlockedExchange + nop + LEAF_END + +;;; +;;;- unsigned int Class_System_Threading_Interlocked::g_Exchange(unsigned int * dst, +;;; unsigned int exc) +;;; + LEAF_ENTRY ?g_Exchange@Class_System_Threading_Interlocked@@SAIPAII@Z + b InterlockedExchange + nop + LEAF_END + +;;; +;;; float Class_System_Threading_Interlocked::g_Exchange(float * dst, +;;; float exc) +;;; + LEAF_ENTRY ?g_Exchange@Class_System_Threading_Interlocked@@SAMPAMM@Z + b InterlockedExchange + nop + LEAF_END + +;;; +;;;- struct Class_System_Object * Class_System_Threading_Interlocked::g_Exchange( +;;; struct Class_System_Object ** dst, +;;; struct Class_System_Object * exc) +;;; + LEAF_ENTRY ?g_Exchange@Class_System_Threading_Interlocked@@SAPAUClass_System_Object@@PAPAU2@PAU2@@Z + b InterlockedExchange + nop + LEAF_END + +;;; +;;; struct uintPtr * Class_System_Threading_Interlocked::g_Exchange(struct uintPtr ** dst, +;;; struct uintPtr * exc) +;;; + LEAF_ENTRY ?g_Exchange@Class_System_Threading_Interlocked@@SAPAUuintPtr@@PAPAU2@PAU2@@Z + b InterlockedExchange + nop + LEAF_END + +;;; +;;; int Class_System_Threading_Interlocked::g_Increment(int * dst) +;;; + LEAF_ENTRY ?g_Increment@Class_System_Threading_Interlocked@@SAHPAH@Z + b InterlockedIncrement + nop + LEAF_END + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + END diff --git a/base/Kernel/Native/arm/interlocked_v6.asm b/base/Kernel/Native/arm/interlocked_v6.asm new file mode 100644 index 0000000..b795639 --- /dev/null +++ b/base/Kernel/Native/arm/interlocked_v6.asm @@ -0,0 +1,195 @@ +; title "Interlocked Operations" +;++ +; +; Copyright (c) Microsoft Corporation +; +; Module Name: +; +; intrlock.asm +; +; Abstract: +; +; This module implements the 32 and 16 bit interlocked operations for ARMv6 and beyond. +; The instructions used in this module for gaining exclusive access to a memory location +; arent supported in ARMv5 and earlier +; +; The functions aren't optimized because these are brand new instructions that we dont +; understand completely. That should be done after profiling and understanding their behavior. +; +; This whole file will be discarded when the intrinsics come in. +; +; Environment: +; +; Kernel and User mode. +; +;-- + +#include "ksarm.h" + +#if defined(_ARM_WORKAROUND_) +; The LDREXH, STREXH, LDREXD, STREXD and CLREX instructions arent supported +; by the current assembler. Remove this and replace with the actual instructions +; when the assembler supports it +; This is not a generic assembler so I am explicitly defining values only for the +; instructions I am using + + +#define LDREXH_R12_R0 0xE1F0CF9F ;ldrexh r12, [r0] (1110 0001 1111 0000 1100 1111 1001 1111) +#define STREXHEQ_R3_R1_R0 0x01E03F91 ;strexheq r3, r1, [r0] (0000 0001 1110 0000 0011 1111 1001 0001) +#define STREXH_R3_R12_R0 0xE1E03F9C ;strexh r3, r12, [r0] (1110 0001 1110 0000 0011 1111 1001 1100) +#define LDREXD_R4_R0 0xE1B04F9F ;ldrexd r4, [r0] (1110 0001 1011 0000 0100 1111 1001 1111) +#define STREXDEQ_R2_R6_R0 0x01A02F96 ;strexdeq r2, r6, [r0] (0000 0001 1010 0000 0010 1111 1001 0110) + +#define CLREX 0xF57FF01F ; clrex (1111 0101 0111 1111 1111 0000 0001 1111) + +#endif + + + TEXTAREA + + LEAF_ENTRY InterlockedExchange +1 ldrex r12, [r0] ; load from the address and mark it exclusive + strex r3, r1, [r0] ; attempt to store the new value + cmp r3, #1 ; check if the store failed (0=success, 1=failure) + beq %B1 ; restart if the store failed + mov r0, r12 ; return the original value + mov pc, lr + LEAF_END + + LEAF_ENTRY InterlockedCompareExchange +1 ldrex r12, [r0] ; load from the address and mark it exclusive + cmp r12, r2 ; compare the value with the comperand(r2) + strexeq r3, r1, [r0] ; if they were equal, attempt to store the new value (r1) + bne %F2 ; if they were not equal jump to (2) which clears the exclusive tag on the address and returns + cmp r3, #1 ; check the status of the store (returned in r3) + beq %B1 ; go back to the start if it failed (0=success, 1=failure) + bne %F3 ; if it succeeded, jump to (3) and return. there is no need to clrex if strex succeeded +2 dcd CLREX ; clrex +3 mov r0, r12 + mov pc, lr + LEAF_END + + LEAF_ENTRY InterlockedCompareExchange16 ; has the same structure as InterlockedCompareExchange, but uses half word operands +1 dcd LDREXH_R12_R0 + cmp r12, r2 + dcd STREXHEQ_R3_R1_R0 + bne %F2 + cmp r3, #1 + beq %B1 + bne %F3 +2 dcd CLREX ; clrex +3 mov r0, r12 + mov pc, lr + LEAF_END + + LEAF_ENTRY InterlockedIncrement +1 ldrex r12, [r0] ; load from the address and mark it exclusive + add r12, r12, #1 ; increment it + strex r3, r12, [r0] ; attempt to store the new value + cmp r3, #1 ; check if it succeeded (0=success, 1=failure) + beq %B1 ; loop if it failed + mov r0, r12 ; return the increment value if it succeeded + mov pc, lr + LEAF_END + + LEAF_ENTRY InterlockedIncrement16 ; has the same structure as InterlockedIncrement, but uses half word operands +1 dcd LDREXH_R12_R0 + add r12, r12, #1 + dcd STREXH_R3_R12_R0 + cmp r3, #1 + beq %B1 + mov r0, r12 + mov pc, lr + LEAF_END + + LEAF_ENTRY InterlockedDecrement ; has the same structure as InterlockedIncrement +1 ldrex r12, [r0] + sub r12, r12, #1 + strex r3, r12, [r0] + cmp r3, #1 + beq %B1 + mov r0, r12 + mov pc, lr + LEAF_END + + LEAF_ENTRY InterlockedDecrement16 ; has the same structure as InterlockedIncrement +1 dcd LDREXH_R12_R0 + sub r12, r12, #1 + dcd STREXH_R3_R12_R0 + cmp r3, #1 + beq %B1 + mov r0, r12 + mov pc, lr + LEAF_END + + LEAF_ENTRY InterlockedExchangeAdd ; has the same structure as InterlockedIncrement +1 ldrex r12, [r0] + add r2, r12, r1 + strex r3, r2, [r0] + cmp r3, #1 + beq %B1 + mov r0, r12 + mov pc, lr + LEAF_END + + LEAF_ENTRY InterlockedOr ; has the same structure as InterlockedIncrement +1 ldrex r12, [r0] + orr r2, r12, r1 + strex r3, r2, [r0] + cmp r3, #1 + beq %B1 + mov r0, r12 + mov pc, lr + LEAF_END + + LEAF_ENTRY InterlockedAnd ; has the same structure as InterlockedIncrement +1 ldrex r12, [r0] + and r2, r12, r1 + strex r3, r2, [r0] + cmp r3, #1 + beq %B1 + mov r0, r12 + mov pc, lr + LEAF_END + + LEAF_ENTRY InterlockedXor ; has the same structure as InterlockedIncrement +1 ldrex r12, [r0] + eor r2, r12, r1 + strex r3, r2, [r0] + cmp r3, #1 + beq %B1 + mov r0, r12 + mov pc, lr + LEAF_END + + + ; InterlockedCompareExchange64 - + ; Arguments: + ; r0 = Pointer to Destination + ; r1 = Exchange.LowPart + ; r2 = Exchange.HighPart + ; r3 = Comperand.LowPart + ; [sp] = Comperand.HighPart + + NESTED_ENTRY InterlockedCompareExchange64 + stmdb sp!, {r4-r7, lr} + PROLOG_END + ldr r12, [sp, #0x14] ; load r12 with Comperand.HighPart + mov r6, r1 ; move [Exchange.LowPart,Exchange.HighPart] to [r6,r7] because the strex instruction demands that Rm be an even numbered register + mov r7, r2 +1 dcd LDREXD_R4_R0 ; ldrexd r4, [r0] (r4 = Destination.LowPart, r5 = Destination.HighPart) + cmp r4, r3 ; if (Destination.LowPart == Comperand.LowPart) + cmpeq r5, r12 ; && (Destination.HightPart == Comperand.HighPart) + dcd STREXDEQ_R2_R6_R0 ; streqd r2, r6, [r0] (attempt to store the new value if equal. [r0] = r6; [r0 + 4] = r7;) + bne %F2 ; if not equal, branch to end and return + cmp r2, #1 ; check the status of the attempt (0=success, 1=failure) + beq %B1 ; loop if the store failed + bne %F3 +2 dcd CLREX ; clrex +3 mov r0, r4 + mov r1, r5 + ldmia sp!, {r4-r7, pc} + NESTED_END + + END + diff --git a/base/Kernel/Native/arm/kdasm.asm b/base/Kernel/Native/arm/kdasm.asm new file mode 100644 index 0000000..fcadafd --- /dev/null +++ b/base/Kernel/Native/arm/kdasm.asm @@ -0,0 +1,104 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code for the kernel debugger. +;;; + +|defining ?g_Break@Class_Microsoft_Singularity_DebugStub@@SAXXZ| EQU 1 + + include hal.inc + + TEXTAREA + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; KD Support. +;;; + +;;; +;;; "public: static void Class_Microsoft_Singularity_DebugStub::g_Break(void)" +;;; + LEAF_ENTRY ?g_Break@Class_Microsoft_Singularity_DebugStub@@SAXXZ + ; bkpt 0xffff + swi 0xffff03 + bx lr + LEAF_END + +;;; +;;; "void KdpPause(void)" +;;; + LEAF_ENTRY ?KdpPause@@YAXXZ + DCD 0xe320f002 ;; WFE Note: ARMv7 Specific. + bx lr + LEAF_END + +;;; +;;; "bool KdpDisableInterruptsInline(void)" +;;; + LEAF_ENTRY ?KdpDisableInterruptsInline@@YA_NXZ + + mrs r1, cpsr + ;; remember previous bit for later + and r0, r1, #Struct_Microsoft_Singularity_Isal_Arm_Psr_DisableIrq + orr r1, r1, #Struct_Microsoft_Singularity_Isal_Arm_Psr_DisableIrq + msr cpsr_c, r1 + + ;; include a nop so the linker doesn't merge with Target.DisableInterrupts. + nop + + ;; if bit is 1, we return false, so negate the bit with an xor + eors r0, r0, #Struct_Microsoft_Singularity_Isal_Arm_Psr_DisableIrq + ;; normalize true value + movne r0, #1 + + bx lr + LEAF_END + +;;; +;;; "void KdpEnableInterruptsInline(void)" +;;; + LEAF_ENTRY ?KdpEnableInterruptsInline@@SAXXZ + + mrs r0, cpsr + bic r0, r0, #Struct_Microsoft_Singularity_Isal_Arm_Psr_DisableIrq + msr cpsr_c, r0 + + bx lr + LEAF_END + +;;; +;;; "void KdpRestoreInterruptsInline(bool)" +;;; + LEAF_ENTRY ?KdpRestoreInterruptsInline@@YAX_N@Z + cmp r0, #0 + bne |?KdpEnableInterruptsInline@@SAXXZ| + bx lr + LEAF_END + +;;; +;;; "void KdNotifyTrap(KdDebugTrapData *)" +;;; + LEAF_ENTRY ?KdNotifyTrap@@YAXPAUKdDebugTrapData@@@Z + swi 0xffff2e + bx lr + LEAF_END + +;;; +;;; "void KdNotifyException(System_Exception *, unsigned int throwAddr)" +;;; + LEAF_ENTRY ?KdNotifyException@@YAXPAUClass_System_Exception@@I@Z + ;; FIXME!!! + swi 0xffff03 + bx lr + LEAF_END + +;;; +;;; "void KdpArmSetControlReport(Struct_Microsoft_Singularity_Kd_ArmKdControlReport *)" +;;; + LEAF_ENTRY ?KdpArmSetControlReport@@YAXPAUStruct_Microsoft_Singularity_Kd_ArmKdControlReport@@@Z + ;; No support needed yet. + bx lr + LEAF_END + + END diff --git a/base/Kernel/Native/arm/kxarm.inc b/base/Kernel/Native/arm/kxarm.inc new file mode 100644 index 0000000..c37e69a --- /dev/null +++ b/base/Kernel/Native/arm/kxarm.inc @@ -0,0 +1,235 @@ +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; +;++ +; +; +; Module Name: +; +; kxarm.h +; +; Abstract: +; +; This is an implementation of the WINCE/ARM Calling Sequence +; Specification. +; +; Environment: +; String Library +; +; Revision History: +;-- + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; TODO: No Thumbing or Interworking for now. +; + GBLL Thumbing +Thumbing SETL {FALSE} + GBLL Interworking +Interworking SETL {FALSE} +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + GBLS VBar +VBar SETS "|" + GBLL HaveExceptHandler + GBLS AreaName + GBLS FuncName + GBLS PrologName + GBLS FuncEndName + GBLS ExceptHandler + GBLS ExceptData +AreaName SETS "|.text|" +HaveExceptHandler SETL {FALSE} + + MACRO + TEXTAREA + AREA |.text|,ALIGN=2,CODE,READONLY +AreaName SETS "|.text|" + MEND + + MACRO + THUMBAREA + AREA |.text|,ALIGN=2,CODE,READONLY,THUMB +AreaName SETS "|.text|" + MEND + + MACRO + STARTUPTEXT + AREA |.astart|,ALIGN=2,CODE +AreaName SETS "|.astart|" + MEND + + MACRO + START_REGION $NameBegin + LCLS TempName +TempName SETS VBar:CC:"$NameBegin":CC:VBar + EXPORT $TempName[DATA] +$TempName + MEND + + MACRO + END_REGION $NameEnd + LCLS TempName +TempName SETS VBar:CC:"$NameEnd":CC:VBar + EXPORT $TempName[DATA] +$TempName + MEND + +; +; This macro should be used if the assembly function is in ARM +; + + MACRO + NESTED_ARMENTRY $Name +FuncName SETS VBar:CC:"$Name":CC:VBar +PrologName SETS VBar:CC:"$Name":CC:"_Prolog":CC:VBar +FuncEndName SETS VBar:CC:"$Name":CC:"_end":CC:VBar + LCLS ExceptBit + IF HaveExceptHandler +ExceptBit SETS "0x80000000" + ELSE +ExceptBit SETS "0" + ENDIF + + AREA |.pdata|,ALIGN=2,PDATA + DCD $FuncName + DCD (($PrologName-$FuncName)/4) :OR: ((($FuncEndName-$FuncName)/4):SHL:8) :OR: 0x40000000 :OR: $ExceptBit + AREA $AreaName,CODE,READONLY + ALIGN 2 + EXPORT $FuncName [FUNC] + IF HaveExceptHandler + DCD $ExceptHandler + DCD $ExceptData +HaveExceptHandler SETL {FALSE} + ENDIF +$FuncName + ROUT + MEND + +; +; This macro should be used if the assembly function is in THUMB +; + MACRO + NESTED_THUMBENTRY $Name +FuncName SETS VBar:CC:"$Name":CC:VBar +PrologName SETS VBar:CC:"$Name":CC:"_Prolog":CC:VBar +FuncEndName SETS VBar:CC:"$Name":CC:"_end":CC:VBar + LCLS ExceptBit + IF HaveExceptHandler +ExceptBit SETS "0x80000000" + ELSE +ExceptBit SETS "0" + ENDIF + + AREA |.pdata|,ALIGN=2,PDATA + DCD $FuncName + DCD (($PrologName-$FuncName)/2) :OR: ((($FuncEndName-$FuncName)/2):SHL:8) :OR: $ExceptBit + AREA $AreaName,CODE,READONLY + ALIGN 2 + EXPORT $FuncName [FUNC] + IF HaveExceptHandler + DCD $ExceptHandler + DCD $ExceptData +HaveExceptHandler SETL {FALSE} + ENDIF +$FuncName + ROUT + MEND + +; +; This macro should be used if the assembly function is in ARM +; + MACRO + NESTED_ENTRY $Name +FuncName SETS VBar:CC:"$Name":CC:VBar +PrologName SETS VBar:CC:"$Name":CC:"_Prolog":CC:VBar +FuncEndName SETS VBar:CC:"$Name":CC:"_end":CC:VBar + LCLS ExceptBit + IF HaveExceptHandler +ExceptBit SETS "0x80000000" + ELSE +ExceptBit SETS "0" + ENDIF + + AREA |.pdata|,ALIGN=2,PDATA + DCD $FuncName + DCD (($PrologName-$FuncName)/4) :OR: ((($FuncEndName-$FuncName)/4):SHL:8) :OR: 0x40000000 :OR: $ExceptBit + AREA $AreaName,CODE,READONLY + ALIGN 2 + EXPORT $FuncName [FUNC] + IF HaveExceptHandler + DCD $ExceptHandler + DCD $ExceptData +HaveExceptHandler SETL {FALSE} + ENDIF +$FuncName + ROUT + MEND + + MACRO + PROLOG_END +$PrologName + MEND + + MACRO + LEAF_ENTRY $Name +FuncName SETS VBar:CC:"$Name":CC:VBar +PrologName SETS "Invalid Prolog" +FuncEndName SETS VBar:CC:"$Name":CC:"_end":CC:VBar + ALIGN 2 + EXPORT $FuncName [FUNC] +$FuncName + ROUT + MEND + + MACRO + ALTERNATE_ENTRY $Name + LCLS TempName +TempName SETS VBar:CC:"$Name":CC:VBar + EXPORT $TempName [FUNC] +$TempName + MEND + + MACRO + ENTRY_END $Name +$FuncEndName + MEND + + MACRO + NESTED_END $Name +$FuncEndName + MEND + + MACRO + LEAF_END $Name +$FuncEndName + MEND + + MACRO + EXCEPTION_HANDLER $Handler +ExceptHandler SETS VBar:CC:"$Handler":CC:VBar +ExceptData SETS "0" +HaveExceptHandler SETL {TRUE} + MEND + + MACRO + EXCEPTION_HANDLER_DATA $Handler, $HandlerData +ExceptHandler SETS VBar:CC:"$Handler":CC:VBar +ExceptData SETS VBar:CC:"$HandlerData":CC:VBar +HaveExceptHandler SETL {TRUE} + MEND + + MACRO + EXCEPTION_HANDLER_MILLICODE $Handler, $HandlerData +ExceptHandler SETS "$Handler" +ExceptData SETS "$HandlerData" +HaveExceptHandler SETL {TRUE} + MEND + +; MAGIC_ARM_MODE_SWITCH: MS has instead put this macro definition into +; the kernel file winceos\coreos\nk\kernel\arm\intrlock.s. It is also +; defined in corelibc\crtw32\eh\arm\handler.s. In M4, these duplicate +; definitions should be yanked and put here, in this common location. + + END + diff --git a/base/Kernel/Native/arm/lies.asm b/base/Kernel/Native/arm/lies.asm new file mode 100644 index 0000000..2727209 --- /dev/null +++ b/base/Kernel/Native/arm/lies.asm @@ -0,0 +1,587 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved. +;;; +;;; This file contains ARM-specific assembly code. +;;; + +|defining ?g_SetCurrentThreadContext@Class_Microsoft_Singularity_Processor@@SAXPAUStruct_Microsoft_Singularity_X86_ThreadContext@@@Z| EQU 1 +|defining ?g_Initialize@Class_System_VTable@@SAXXZ| EQU 1 +|defining ?g_Halt@Struct_Microsoft_Singularity_Hal_Cpu@@SAXXZ| EQU 1 +|defining ?c_exceptionHandler@@3P6AXHPAUStruct_Microsoft_Singularity_ThreadContext@@@ZA| EQU 1 +|defining ?c_interruptHandler@@3P6AXHPAUStruct_Microsoft_Singularity_ThreadContext@@@ZA| EQU 1 +|defining ?g_DisablePaging@Class_Microsoft_Singularity_Processor@@SAXXZ| EQU 1 +|defining ?g_EnterRing3@Class_Microsoft_Singularity_Processor@@SAXXZ| EQU 1 +|defining ?g_GetFrameEbp@Class_Microsoft_Singularity_Processor@@SAPAUuintPtr@@PAU2@@Z| EQU 1 +|defining ?g_GetStackPointer@Class_Microsoft_Singularity_Processor@@SAPAUuintPtr@@XZ| EQU 1 +|defining ?g_GetFrameEip@Class_Microsoft_Singularity_Processor@@SAPAUuintPtr@@PAU2@@Z| EQU 1 +|defining ?g_GetFramePointer@Class_Microsoft_Singularity_Processor@@SAPAUuintPtr@@XZ| EQU 1 +|defining ?g_ReadMsr@Class_Microsoft_Singularity_Processor@@SA_KI@Z| EQU 1 +|defining ?c_LinkStackFunctionsBegin@Class_Microsoft_Singularity_Memory_Stacks@@2EA| EQU 1 +|defining ?c_LinkStackFunctionsLimit@Class_Microsoft_Singularity_Memory_Stacks@@2EA| EQU 1 +|defining ?c_LinkStackBegin@Class_Microsoft_Singularity_Memory_Stacks@@2EA| EQU 1 +|defining ?c_LinkStackLimit@Class_Microsoft_Singularity_Memory_Stacks@@2EA| EQU 1 +|defining ?c_UnlinkStackBegin@Class_Microsoft_Singularity_Memory_Stacks@@2EA| EQU 1 +|defining ?c_UnlinkStackLimit@Class_Microsoft_Singularity_Memory_Stacks@@2EA| EQU 1 +|defining ?c_LinkStackStubsBegin@Class_Microsoft_Singularity_Memory_Stacks@@2EA| EQU 1 +|defining ?c_LinkStackStubsLimit@Class_Microsoft_Singularity_Memory_Stacks@@2EA| EQU 1 +|defining ?g_GetCr3@Class_Microsoft_Singularity_Processor@@SAIXZ| EQU 1 +|defining ?g_InitFpu@Class_Microsoft_Singularity_Processor@@SAXXZ| EQU 1 +|defining ?g_ClearFpuStatus@Class_Microsoft_Singularity_Processor@@SAXXZ| EQU 1 +|defining ?g_ReadFpuStatus@Class_Microsoft_Singularity_Processor@@SAIXZ| EQU 1 +|defining ?g_LimitedDispatchException@Class_Microsoft_Singularity_Processor@@SAXHPAUStruct_Microsoft_Singularity_X86_ThreadContext@@@Z| EQU 1 +|defining ?g_MpCallEntryPoint@Class_Microsoft_Singularity_Processor@@SAXPAUuintPtr@@@Z| EQU 1 +|defining ?g_PrivateChangeAddressSpace@Class_Microsoft_Singularity_Processor@@SAXI@Z| EQU 1 +|defining ?g_PrivateEnablePaging@Class_Microsoft_Singularity_Processor@@SAXI@Z| EQU 1 +|defining ?g_PrivateInvalidateTLBEntry@Class_Microsoft_Singularity_Processor@@SAXPAUuintPtr@@@Z| EQU 1 +|defining ?g_WriteMsr@Class_Microsoft_Singularity_Processor@@SAXI_K@Z| EQU 1 +;|defining ?g_CollectBodyTransition@Class_System_GC@@SAXPAUClass_System_Threading_Thread@@H@Z| EQU 1 +;|defining ?g_setStopContext@Class_System_Threading_Thread@@SAXPAU1@PAUClass_System_Exception@@@Z| EQU 1 +|defining __throwDispatcherExplicitAddrAfter| EQU 1 +;|defining staticDataPointerBitMap| EQU 1 +|defining ?g_HalGetMpBootInfo@Struct_Microsoft_Singularity_MpBootInfo@@SAPAU1@XZ| EQU 1 +|defining ?g_LinkSharedStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SAXXZ| EQU 1 +|defining ?g_UnlinkSharedStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SAXXZ| EQU 1 +|defining ?g_LinkStack0@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack4@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack8@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack12@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack16@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack20@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack24@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack28@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack32@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack36@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack40@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack44@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack48@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack52@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack56@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack60@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack64@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack68@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack72@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack76@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack80@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack84@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack88@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack92@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack96@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack100@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack104@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack108@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack112@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack116@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack120@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack124@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_LinkStack128@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack0@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack4@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack8@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack12@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack16@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack20@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack24@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack28@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack32@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack36@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack40@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack44@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack48@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack52@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack56@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack60@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack64@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack68@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack72@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack76@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack80@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack84@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack88@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack92@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack96@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack100@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack104@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack108@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack112@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack116@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack120@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack124@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 +|defining ?g_UnlinkStack128@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ| EQU 1 + + include hal.inc + + MACRO + BREAKPOINT + ;; bkpt 0xffff + swi 0xffff03 + MEND + + + TEXTAREA + +;;; +;;; "public: static void __cdecl Class_Microsoft_Singularity_Processor::g_SetCurrentThreadContext(struct Struct_Microsoft_Singularity_X86_ThreadContext *)" +;;; + LEAF_ENTRY ?g_SetCurrentThreadContext@Class_Microsoft_Singularity_Processor@@SAXPAUStruct_Microsoft_Singularity_X86_ThreadContext@@@Z + BREAKPOINT + bx lr + LEAF_END + +;;; +;;; "public: static void __cdecl Class_System_VTable::g_Initialize(void)" +;;; + LEAF_ENTRY ?g_Initialize@Class_System_VTable@@SAXXZ +;;; BREAKPOINT ; we ignore this for now. + bx lr + LEAF_END + +;;; +;;; "public: static void __cdecl Struct_Microsoft_Singularity_Hal_Cpu::g_Halt(void)" +;;; + LEAF_ENTRY ?g_Halt@Struct_Microsoft_Singularity_Hal_Cpu@@SAXXZ + DCD 0xe320f003 ;; WFI Note: ARMv7 Specific. + bx lr + LEAF_END + +;;; +;;; "void(__cdecl* c_exceptionHandler)(int,struct Struct_Microsoft_Singularity_ThreadContext *)" +;;; + LEAF_ENTRY ?c_exceptionHandler@@3P6AXHPAUStruct_Microsoft_Singularity_ThreadContext@@@ZA + BREAKPOINT + bx lr + LEAF_END + +;;; +;;; "void(__cdecl* c_interruptHandler)(int,struct Struct_Microsoft_Singularity_ThreadContext *)" +;;; + LEAF_ENTRY ?c_interruptHandler@@3P6AXHPAUStruct_Microsoft_Singularity_ThreadContext@@@ZA + BREAKPOINT + bx lr + LEAF_END + +;;; +;;; "public: static unsigned char Class_Microsoft_Singularity_Memory_Stacks::c_LinkStackBegin" +;;; + LEAF_ENTRY ?c_LinkStackFunctionsBegin@Class_Microsoft_Singularity_Memory_Stacks@@2EA + BREAKPOINT + bx lr + LEAF_END + +;;; +;;; "public: static unsigned char Class_Microsoft_Singularity_Memory_Stacks::c_LinkStackLimit" +;;; + LEAF_ENTRY ?c_LinkStackFunctionsLimit@Class_Microsoft_Singularity_Memory_Stacks@@2EA + BREAKPOINT + bx lr + LEAF_END + +;;; +;;; "public: static unsigned char Class_Microsoft_Singularity_Memory_Stacks::c_LinkStackBegin" +;;; + LEAF_ENTRY ?c_LinkStackBegin@Class_Microsoft_Singularity_Memory_Stacks@@2EA + BREAKPOINT + bx lr + LEAF_END + +;;; +;;; "public: static unsigned char Class_Microsoft_Singularity_Memory_Stacks::c_LinkStackLimit" +;;; + LEAF_ENTRY ?c_LinkStackLimit@Class_Microsoft_Singularity_Memory_Stacks@@2EA + BREAKPOINT + bx lr + LEAF_END + +;;; +;;; "public: static unsigned char Class_Microsoft_Singularity_Memory_Stacks::c_LinkStackBegin" +;;; + LEAF_ENTRY ?c_UnlinkStackBegin@Class_Microsoft_Singularity_Memory_Stacks@@2EA + BREAKPOINT + bx lr + LEAF_END + +;;; +;;; "public: static unsigned char Class_Microsoft_Singularity_Memory_Stacks::c_LinkStackLimit" +;;; + LEAF_ENTRY ?c_UnlinkStackLimit@Class_Microsoft_Singularity_Memory_Stacks@@2EA + BREAKPOINT + bx lr + LEAF_END + +;;; +;;; "public: static unsigned char Class_Microsoft_Singularity_Memory_Stacks::c_LinkStackBegin" +;;; + LEAF_ENTRY ?c_LinkStackStubsBegin@Class_Microsoft_Singularity_Memory_Stacks@@2EA + BREAKPOINT + bx lr + LEAF_END + +;;; +;;; "public: static unsigned char Class_Microsoft_Singularity_Memory_Stacks::c_LinkStackLimit" +;;; + LEAF_ENTRY ?c_LinkStackStubsLimit@Class_Microsoft_Singularity_Memory_Stacks@@2EA + BREAKPOINT + bx lr + LEAF_END + +;;; +;;; "public: static void __cdecl Class_Microsoft_Singularity_Processor::g_LimitedDispatchException(int,struct Struct_Microsoft_Singularity_X86_ThreadContext *)" +;;; + LEAF_ENTRY ?g_LimitedDispatchException@Class_Microsoft_Singularity_Processor@@SAXHPAUStruct_Microsoft_Singularity_X86_ThreadContext@@@Z + BREAKPOINT + bx lr + LEAF_END + +;;; +;;; "public: static void __cdecl Class_System_GC::g_CollectBodyTransition(struct Class_System_Threading_Thread *,int)" +;;; +; LEAF_ENTRY ?g_CollectBodyTransition@Class_System_GC@@SAXPAUClass_System_Threading_Thread@@H@Z +; BREAKPOINT +; bx lr +; LEAF_END + +;;; +;;; "public: static void __cdecl Class_System_Threading_Thread::g_setStopContext(struct Class_System_Threading_Thread *,struct Class_System_Exception *)" +;;; +; LEAF_ENTRY ?g_setStopContext@Class_System_Threading_Thread@@SAXPAU1@PAUClass_System_Exception@@@Z +; BREAKPOINT +; bx lr +; LEAF_END + +;;; +;;; "__throwDispatcherExplicitAddrAfter" +;;; + LEAF_ENTRY __throwDispatcherExplicitAddrAfter + BREAKPOINT + bx lr + LEAF_END + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ?????????????? +;;; +;;; +;;; "staticDataPointerBitMap" +;;; +; LEAF_ENTRY staticDataPointerBitMap +; BREAKPOINT +; bx lr +; LEAF_END + +;;; +;;; "Win32RaceRegionStart"/"Win32RaceRegionStart" +;;; + EXPORT |Win32RaceRegionStart| +|Win32RaceRegionStart| DCD 0 + EXPORT |Win32RaceRegionEnd| +|Win32RaceRegionEnd| DCD 0 + + LEAF_ENTRY ?g_HalGetMpBootInfo@Struct_Microsoft_Singularity_MpBootInfo@@SAPAU1@XZ + BREAKPOINT + bx lr + LEAF_END + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Linked Stacks. +;;; + LEAF_ENTRY ?g_LinkSharedStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SAXXZ + bkpt 0xffff + bx lr + LEAF_END + + LEAF_ENTRY ?g_UnlinkSharedStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SAXXZ + bkpt 0xffff + bx lr + LEAF_END + + LEAF_ENTRY ?g_LinkStack0@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack4@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack8@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack12@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack16@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack20@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack24@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack28@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack32@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack36@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack40@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack44@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack48@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack52@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack56@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack60@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack64@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack68@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack72@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack76@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack80@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack84@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack88@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack92@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack96@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack100@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack104@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack108@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack112@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack116@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack120@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack124@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_LinkStack128@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack0@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack4@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack8@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack12@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack16@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack20@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack24@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack28@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack32@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack36@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack40@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack44@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack48@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack52@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack56@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack60@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack64@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack68@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack72@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack76@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack80@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack84@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack88@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack92@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack96@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack100@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack104@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack108@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack112@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack116@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack120@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack124@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + + LEAF_ENTRY ?g_UnlinkStack128@Class_Microsoft_Singularity_Memory_Stacks@@SAXXZ + bkpt 0xffff + LEAF_END + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; "activationDescriptorTable" +;;; +; EXPORT |activationDescriptorTable| +;|activationDescriptorTable| DCD 0 + +;;; +;;; "callSetSiteNumberToIndex" +;;; +; EXPORT |callSetSiteNumberToIndex| +;|callSetSiteNumberToIndex| DCD 0 + +;;; +;;; "callSiteSetCount" +;;; +; EXPORT |callSiteSetCount| +;|callSiteSetCount| DCD 0 + +;;; +;;; "returnAddressToCallSiteSetNumbers" +;;; +; EXPORT |returnAddressToCallSiteSetNumbers| +;|returnAddressToCallSiteSetNumbers| DCD 0 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + END diff --git a/base/Kernel/Native/arm/timer_xscale.asm b/base/Kernel/Native/arm/timer_xscale.asm new file mode 100644 index 0000000..ce00303 --- /dev/null +++ b/base/Kernel/Native/arm/timer_xscale.asm @@ -0,0 +1,152 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Microsoft Research Singularity ARM Bootstrap +;;; +;;; XScale timer support routines +;;; +;;; Definitions taken from: +;;; +;;; Chapter 11, Intel 81348 I/O Processor, September 2006, O/N: 315036-001US +;;; + + CODE32 + + AREA |.text|, CODE, ARM + + ;;; Register operations for TMR0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadTMR0@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAIXZ| +|?g_ReadTMR0@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c0, c9, 0 + bx lr + ENDP + + EXPORT |?g_WriteTMR0@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAXI@Z| +|?g_WriteTMR0@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAXI@Z| PROC + mcr p6, 0, r0, c0, c9, 0 + bx lr + ENDP + + ;;; Register operations for TMR1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadTMR1@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAIXZ| +|?g_ReadTMR1@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c1, c9, 0 + bx lr + ENDP + + EXPORT |?g_WriteTMR1@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAXI@Z| +|?g_WriteTMR1@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAXI@Z| PROC + mcr p6, 0, r0, c1, c9, 0 + bx lr + ENDP + + ;;; Register operations for TCR0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadTCR0@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAIXZ| +|?g_ReadTCR0@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c2, c9, 0 + bx lr + ENDP + + EXPORT |?g_WriteTCR0@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAXI@Z| +|?g_WriteTCR0@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAXI@Z| PROC + mcr p6, 0, r0, c2, c9, 0 + bx lr + ENDP + + ;;; Register operations for TCR1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadTCR1@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAIXZ| +|?g_ReadTCR1@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c3, c9, 0 + bx lr + ENDP + + EXPORT |?g_WriteTCR1@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAXI@Z| +|?g_WriteTCR1@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAXI@Z| PROC + mcr p6, 0, r0, c3, c9, 0 + bx lr + ENDP + + ;;; Register operations for TRR0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadTRR0@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAIXZ| +|?g_ReadTRR0@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c4, c9, 0 + bx lr + ENDP + + EXPORT |?g_WriteTRR0@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAXI@Z| +|?g_WriteTRR0@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAXI@Z| PROC + mcr p6, 0, r0, c4, c9, 0 + bx lr + ENDP + + ;;; Register operations for TRR1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadTRR1@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAIXZ| +|?g_ReadTRR1@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c5, c9, 0 + bx lr + ENDP + + EXPORT |?g_WriteTRR1@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAXI@Z| +|?g_WriteTRR1@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAXI@Z| PROC + mcr p6, 0, r0, c5, c9, 0 + bx lr + ENDP + + ;;; Register operations for TISR ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadTISR@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAIXZ| +|?g_ReadTISR@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c6, c9, 0 + bx lr + ENDP + + EXPORT |?g_WriteTISR@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAXI@Z| +|?g_WriteTISR@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAXI@Z| PROC + mcr p6, 0, r0, c6, c9, 0 + bx lr + ENDP + + ;;; Register operations for WDTCR ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadWDTCR@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAIXZ| +|?g_ReadWDTCR@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c7, c9, 0 + bx lr + ENDP + + EXPORT |?g_WriteWDTCR@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAXI@Z| +|?g_WriteWDTCR@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAXI@Z| PROC + mcr p6, 0, r0, c7, c9, 0 + bx lr + ENDP + + ;;; Register operations for WRTSR ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + EXPORT |?g_ReadWRTSR@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAIXZ| +|?g_ReadWRTSR@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAIXZ| PROC + mov r0, #0 + mrc p6, 0, r0, c8, c9, 0 + bx lr + ENDP + + EXPORT |?g_WriteWRTSR@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAXI@Z| +|?g_WriteWRTSR@Class_Microsoft_Singularity_Hal_XScaleTimers@@SAXI@Z| PROC + mcr p6, 0, r0, c8, c9, 0 + bx lr + ENDP + + END + diff --git a/base/Kernel/Native/cinit.cpp b/base/Kernel/Native/cinit.cpp new file mode 100644 index 0000000..e01774e --- /dev/null +++ b/base/Kernel/Native/cinit.cpp @@ -0,0 +1,97 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: cinit.cpp +// +// Note: Kernel & Process +// + +#include "hal.h" + +////////////////////////////////////////////////////////////////////////////// + +// Need to put the following marker variables into the .CRT section. +// The .CRT section contains arrays of function pointers. +// The compiler creates functions and adds pointers to this section +// for things like C++ global constructors. +// +// The XIA, XCA etc are group names with in the section. +// The compiler sorts the contributions by the group name. +// For example, .CRT$XCA followed by .CRT$XCB, ... .CRT$XCZ. +// The marker variables below let us get pointers +// to the beginning/end of the arrays of function pointers. +// +// For example, standard groups are +// XCA used here, for begin marker +// XCC "compiler" inits +// XCL "library" inits +// XCU "user" inits +// XCZ used here, for end marker +// + +typedef void (__cdecl *_PVFV)(void); +// typedef int (__cdecl *_PIFV)(void); +typedef _PVFV _PIFV; + +#pragma comment(linker, "/merge:.CRT=.DATA") + +#pragma data_seg(".CRT$XIA", "DATA") +extern "C" _PIFV __xi_a[] = { NULL }; // C initializers. + +#pragma data_seg(".CRT$XIZ", "DATA") +extern "C" _PIFV __xi_z[] = { NULL }; + +#pragma data_seg(".CRT$XCA", "DATA") +extern "C" _PVFV __xc_a[] = { NULL }; // C++ initializers. + +#pragma data_seg(".CRT$XCZ", "DATA") +extern "C" _PVFV __xc_z[] = { NULL }; + +#pragma data_seg(".CRT$XPA", "DATA") +extern "C" _PVFV __xp_a[] = { NULL }; // C pre-terminators. + +#pragma data_seg(".CRT$XPZ", "DATA") +extern "C" _PVFV __xp_z[] = { NULL }; + +#pragma data_seg(".CRT$XTA", "DATA") +extern "C" _PVFV __xt_a[] = { NULL }; // C terminators. + +#pragma data_seg(".CRT$XTZ", "DATA") +extern "C" _PVFV __xt_z[] = { NULL }; + +#pragma data_seg() + +// Walk an array of function pointers, call non-NULL ones. +static void __cdecl _initterm(_PVFV *pfbegin, _PVFV *pfend) +{ + for (; pfbegin < pfend; pfbegin++) { + if (*pfbegin != NULL) { + (**pfbegin)(); + } + } +} + +// Call all of the C++ static constructors. +// +int __cdecl _cinit(void) +{ + // do C initializations + _initterm( __xi_a, __xi_z ); + + // do C++ initializations + _initterm( __xc_a, __xc_z ); + return 0; +} + +int __cdecl _cfini(void) +{ + // do C initializations + _initterm( __xp_a, __xp_z ); + + // do C++ terminations + _initterm( __xt_a, __xt_z ); + return 0; +} diff --git a/base/Kernel/Native/csformat.inc b/base/Kernel/Native/csformat.inc new file mode 100644 index 0000000..13b2b14 --- /dev/null +++ b/base/Kernel/Native/csformat.inc @@ -0,0 +1,128 @@ +///////////////////////////////////////////////////////////////////////////// +// +// csformat - Shared output format utility between singularity and debugger extension +// +// Copyright Microsoft Corporation. All rights reserved. +// + + +void FormatCSOutput(void * context, + char * format, + char * buffer, + int bufferLen, + int (*pfprintField)(void * context, char *pszOut, int bufferSize, int aln, int wid, char fmt, int argIdx), + char * (*pfStringField)(void * context, int argIdx)) +{ + char * text = format; + char * pmsg = buffer; + char * ptxt = text; + char * endBuffer = buffer + bufferLen - 1; + while (*ptxt != '\0') { + + if (*ptxt == '{') { + char * pbeg = ptxt; + bool bad = false; + ptxt++; + + int ndx = 0; + int aln = 0; + int wid = 0; + char fmt = 'd'; + + if (*ptxt == '{') { + *pmsg++ = *ptxt++; + } + else if (*ptxt >= '0' && *ptxt <= '9') { + + // {index,alignment:type width} + // Get Index + while (*ptxt >= '0' && *ptxt <= '9') { + ndx = ndx * 10 + (*ptxt++ - '0'); + } + + // Get Alignment + if (*ptxt == ',') { + ptxt++; + while (*ptxt >= '0' && *ptxt <= '9') { + aln = aln * 10 + (*ptxt++ - '0'); + } + } + + // Get FormatString + if (*ptxt == ':') { + ptxt++; + if (*ptxt >= 'a' && *ptxt <= 'z') { + fmt = *ptxt++; + } + else if (*ptxt >= 'A' && *ptxt <= 'Z') { + fmt = *ptxt++ - 'A' + 'a'; + } + while (*ptxt >= '0' && *ptxt <= '9') { + wid = wid * 10 + (*ptxt++ - '0'); + } + } + + // indirect formatting + if (*ptxt == '*') { + ptxt++; + + if (pfStringField != NULL) { + + text = pfStringField(context, ndx); + if (text != NULL) { + + ptxt = text; + continue; + } + } + + } + + // Get closing brace. + if (*ptxt == '}') { + ptxt++; + } + else { + bad = true; + } + + int printLength = pfprintField(context, + pmsg, + (int)(endBuffer - pmsg), + aln, + wid, + fmt, + ndx); + + if (printLength >= 0) { + + pmsg += printLength; + + if (pmsg > endBuffer) { + pmsg = endBuffer; + } + } + // If the format was bad, then copy it. + if (bad) { + + while (pbeg < ptxt) { + *pmsg++ = *pbeg++; + } + } + } + } + else if (*ptxt == '}') { + ptxt++; + *pmsg++ = *ptxt++; + } + else if (*ptxt == '\n') { + ptxt++; + } + else { + *pmsg++ = *ptxt++; + } + } + *pmsg++ = '\0'; +} + + diff --git a/base/Kernel/Native/eventing.h b/base/Kernel/Native/eventing.h new file mode 100644 index 0000000..d38ec89 --- /dev/null +++ b/base/Kernel/Native/eventing.h @@ -0,0 +1,416 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: eventing.h +// +// Note: +// + +// Misc. general purpose declarations + +#define ROUND_UP_TO_POWER2( x, n ) (((uintptr)(x) + (uintptr)((n)-1)) & ~((uintptr)(n)-1)) +extern "C" void * __cdecl memcpy(void *, const void *, size_t); + +#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] + +#define BUFFER_VALIDATION 1 + +#ifdef BUFFER_VALIDATION + +#define EV_ASSERT(x) if (!(x)) __debugbreak(); + +#else // !BUFFER_VALIDATION + +#define EV_ASSERT(x) + +#endif // BUFFER_VALIDATION + + +#ifndef PCHAR +#define PCHAR char* +#endif + +inline +uint32 +strlen(char * str) +{ + uint32 length = 0; + while (*str++) length += 1; + return length; +} + + +extern "C" +PVOID +_ReturnAddress( + void + ); + +char * ConvertToChars(char * dst, bartok_char *src, int32 length); + +// Memory zone management + +#define EV_MAXIMUM_ZONE_SIZE ((uint16)0xff80) +#define EV_DEFAULT_ZONE_BUFFER_RATIO 10 +#define EV_ZONE_ALIGNMENT 0x10 + +typedef struct _ZONE_READY_LIST { + + union { + + struct { + + uint16 ReadyList; + uint16 Count; + }; + + volatile INT32 AtomicValue32; + }; + +} ZONE_READY_LIST, *PZONE_READY_LIST; + +typedef struct _ZONE_ALLOCATION_POINTER { + + union { + + struct { + + uint16 FreeOffset; + uint16 Count : 13; + uint16 Recycling : 1; // the entire zone is filled with blocks. + uint16 Filled : 1; // the entire zone is filled with blocks. + uint16 Committed : 1; // The last entry allocated has been committed + }; + + volatile INT32 AtomicValue32; + }; + +} ZONE_ALLOCATION_POINTER, *PZONE_ALLOCATION_POINTER; + +// Declare the system event structures by including the common definition + +// Type definitions for the structure generation macros +// + +#define TYPE_UIntPtr UIntPtr +#define TYPE_uint8 uint8 +#define TYPE_uint16 uint16 +#define TYPE_uint32 uint32 +#define TYPE_uint64 uint64 +#define TYPE_PCHAR PCHAR +#define TYPE_BOOL bool + +#define DECLARE_STRUCTURE_BEGIN(x,d) typedef struct _##x { +#define DECLARE_STRUCTURE_END(x) } ##x, *P##x; +#define DECLARE_FIELD(c,t,n) t n; +#define DECLARE_GENERIC_FIELD(s,t,sz,t1,n) t n; +#define DECLARE_SPECIAL_FIELD DECLARE_FIELD +#define DECLARE_EXTENDED_ARRAY_FIELD(s,t,n) uint16 n; +#define _CONSTANT_DEFINITIONS 1 + #include "SystemEvents.inl" +#undef _CONSTANT_DEFINITIONS + +// Declare the external types that are required by the kernel repository + +#define _LOGGING_TO_REPOSITORY + +#define DECLARE_STRUCTURE_BEGIN(x,d) extern UIntPtr Handle_##x; + + #include "SystemEvents.inl" + +#undef _LOGGING_TO_REPOSITORY + + +#ifdef BUFFER_VALIDATION + +void SetEndOfBuffer(PMEMORY_ZONE Zone); + +void CheckEndOfBuffer(PMEMORY_ZONE Zone); + +#else + +#define SetEndOfBuffer(x) + +#define CheckEndOfBuffer(x) + +#endif // BUFFER_VALIDATION + +#define GetMemoryHeader(p,o) (PMEMORY_HEADER)((ULONG_PTR)(p) + (o)) +#define GetEntryOffset(p,o) (uint32)((ULONG_PTR)(o) - (ULONG_PTR)(p)) +PSOURCE_DESCRIPTOR GetSourceFromHandle(UIntPtr sourceHandle); + +void DebugPrintEvent(UIntPtr eventHandle); + +// +// Record layout (optional extensions) +// + +#define RECORD_STACK_TRACES Class_Microsoft_Singularity_Eventing_EventSource_CAPTURE_STACK_TRACE + +#define RECORD_LAYOUT_FLAGS (RECORD_STACK_TRACES) + +// Stack TrackTraces +// Variable size array, first pointer represents the number of pointers in the array + +#define RECORD_MAXSTACKSIZE 32 + +uint32 +inline +GetRecordHeaderSize ( + uint16 Flags, + uint16 StackSize + ) + +{ + uint16 HeaderSize = sizeof(MEMORY_HEADER); + + if (Flags & RECORD_STACK_TRACES) { + + HeaderSize += StackSize; + } + + return HeaderSize; +} + + +inline +void * +GetRecordInternalStructure ( + PMEMORY_HEADER Record, + uint16 RecordFlag + ) +{ + if (Record->Flags & RecordFlag) { + + uint16 HeaderSize; + + if (RecordFlag & (RecordFlag - 1)) { + + // A single bit at the time can be set to query the internal layout info. + // Return failure otherwise. + + return NULL; + } + + // The structures are allocated in order, as the flags bits decrease + // We can clear the flags + + HeaderSize = GetRecordHeaderSize(Record->Flags & (~((RecordFlag << 1) - 1)), 0); + + return (PVOID)((ULONG_PTR)Record + HeaderSize); + } + + return NULL; +} + + +inline +void * +GetUserRecordStructure ( + PMEMORY_HEADER Record + ) +{ + if (Record->Flags & RECORD_STACK_TRACES) { + + ULONG_PTR * StackSize = (ULONG_PTR *)GetRecordInternalStructure(Record, RECORD_STACK_TRACES); + return (PVOID)((ULONG_PTR)Record + GetRecordHeaderSize(Record->Flags, (uint16)(*StackSize + 1) * sizeof(UIntPtr))); + + } else { + + return (PVOID)((ULONG_PTR)Record + GetRecordHeaderSize(Record->Flags, 0)); + } + + return NULL; +} + +PMEMORY_ZONE +InitializeMemoryZone(void * Buffer, uint16 Size, UIntPtr storageHandle); + +PMEMORY_HEADER +AllocateEventEntry(PMEMORY_ZONE Zone, uint16 size); + +void +CommitEventEntry(PMEMORY_HEADER Entry); + +bool +IsZoneCompleted(PMEMORY_ZONE Zone); + +bool +RecycleZone(PMEMORY_ZONE Zone); + + +#define MEMORY_STORAGE_FLAGS_RECYCLE_MEMORY Struct_Microsoft_Singularity_Eventing_QualityOfService_RecyclableEvents +#define MEMORY_STORAGE_FLAGS_BREAK_ON_RECYCLE Struct_Microsoft_Singularity_Eventing_QualityOfService_OOM_BreakOnRecycle +#define MEMORY_STORAGE_FLAGS_ACTIVE_STORAGE Struct_Microsoft_Singularity_Eventing_QualityOfService_ActiveEvents +#define MEMORY_STORAGE_FLAGS_PERMANENT Struct_Microsoft_Singularity_Eventing_QualityOfService_PermanentEvents + +// Just use the pointers for now in translation. +// More type checking and safety would have to be added +// + +#define HANDLE_TO_STORAGE(x) ((PMEMORY_STORAGE)(x)) +#define HANDLE_TO_HEADER(x) ((PMEMORY_HEADER)(x)) + +#define HANDLE_TO_TYPE(x) ((PEVENT_DESCRIPTOR) (HANDLE_TO_HEADER(x) + 1)) +#define HANDLE_TO_SOURCE(x) ((PSOURCE_DESCRIPTOR)( HANDLE_TO_HEADER(x) + 1)) + +UIntPtr +RegisterNativeSource(char * sourceName, UIntPtr storageHandle, uint32 controlFlags); + +char * GetExtendedString(UIntPtr EntryHandle, int index); + +PMEMORY_HEADER +InternalLogRecord(UIntPtr StorageHandle, + uint32 Flags, + UIntPtr eventType, + PVOID Buffer, + uint32 size, + PVOID * ExtendedBuffer, + uint32 ExtendedSize ); + +PMEMORY_HEADER +InternalLogFixedRecord(UIntPtr StorageHandle, + uint32 Flags, + UIntPtr eventType, + PVOID Buffer, + uint32 size); + +PMEMORY_HEADER +InternalLogVariableRecord(bool doCommit, + UIntPtr StorageHandle, + uint32 Flags, + UIntPtr eventType, + PVOID Buffer, + uint32 size, + int32 stringCount, + Struct_Microsoft_Singularity_Eventing_ArrayType * strings); + +// +// Event fields type +// + + +#define EVENT_FIELD_TYPE_uint8 Class_Microsoft_Singularity_Eventing_DataType___uint8 +#define EVENT_FIELD_TYPE_uint16 Class_Microsoft_Singularity_Eventing_DataType___uint16 +#define EVENT_FIELD_TYPE_uint32 Class_Microsoft_Singularity_Eventing_DataType___uint32 +#define EVENT_FIELD_TYPE_uint64 Class_Microsoft_Singularity_Eventing_DataType___uint64 +#define EVENT_FIELD_TYPE_int8 Class_Microsoft_Singularity_Eventing_DataType___int8 +#define EVENT_FIELD_TYPE_int16 Class_Microsoft_Singularity_Eventing_DataType___int16 +#define EVENT_FIELD_TYPE_int32 Class_Microsoft_Singularity_Eventing_DataType___int32 +#define EVENT_FIELD_TYPE_int64 Class_Microsoft_Singularity_Eventing_DataType___int64 + +#define EVENT_FIELD_TYPE_IntPtr Class_Microsoft_Singularity_Eventing_DataType___IntPtr +#define EVENT_FIELD_TYPE_UIntPtr Class_Microsoft_Singularity_Eventing_DataType___UIntPtr + +#define EVENT_FIELD_TYPE_arrayType Class_Microsoft_Singularity_Eventing_DataType___arrayType +#define EVENT_FIELD_TYPE_string Class_Microsoft_Singularity_Eventing_DataType___string +#define EVENT_FIELD_TYPE_szChar Class_Microsoft_Singularity_Eventing_DataType___szChar + +PMEMORY_HEADER +RegisterEventDescriptorImplementation(int stringType, + PVOID name, + uint16 nameLength, + PVOID description, + uint16 descriptionLength); +PMEMORY_HEADER +RegisterFieldDescriptorImplementation(int stringType, + PMEMORY_HEADER Descriptor, + PVOID name, + uint32 nameLength, + uint16 offset, + uint16 type ); + +PMEMORY_HEADER +RegisterGenericFieldDescriptorImplementation( int stringType, + PMEMORY_HEADER event, + PVOID name, + uint32 nameLength, + uint16 offset, + uint16 size, + UIntPtr typeFieldDescriptor); + +PMEMORY_HEADER +RegisterEnumDescriptorImplementation(int stringType, + PVOID name, + uint16 nameLength, + uint16 type); + +PMEMORY_HEADER +RegisterValueDescriptorImplementation(int stringType, + PMEMORY_HEADER descriptor, + PVOID name, + uint32 nameLength, + uint64 value, + uint8 flagLetter); + +void +InitializeRegistrationSystem(void * buffer, size_t size); + +void +RegisterNativeTypes(); + +extern SOURCE_CONTROLLER SourceController; + +void +RegisterExternalController(PEXTERNAL_CONTROLLER_DESCRIPTOR controller); + +void +UnRegisterExternalController(PEXTERNAL_CONTROLLER_DESCRIPTOR controller); + +PMEMORY_STORAGE +inline +GetLocalRepository() +{ + return SourceController.SourceRepository; +} + +UIntPtr +inline +GetLocalRepositoryHandle() +{ + return (UIntPtr)GetLocalRepository(); +} + +void +RegisterRepositoryStorage(PMEMORY_STORAGE storage); + +bool +RegisterStorage(PMEMORY_STORAGE storage); + +void +UnRegisterStorage(PMEMORY_STORAGE storage); + +uint32 +MemoryStorageGetNextGeneration(PMEMORY_STORAGE Storage); + +PQUERY_VIEW AllocateQueryView( ); + +PMEMORY_HEADER +GetFirstEntry(PMEMORY_ZONE Zone, bool forward); + +PMEMORY_HEADER +GetNextEntry(PQUERY_VIEW view); + +void +EnumerateStorageEntries(UIntPtr storageHandle, bool forward); + +void +UnRegisterQueryView(PQUERY_VIEW queryView); + +// +// Specific logging prototypes for usage in the native size +// + +extern UIntPtr TracingStorageHandle; + +void LogExceptionInfo(UIntPtr throwFrom, UIntPtr handler, Class_System_String * exceptionName); + + +UIntPtr +OpenLoggingStorage(UIntPtr sourceHandle, uint32 * flags); + +// +///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/hal.cpp b/base/Kernel/Native/hal.cpp index 2a76a66..c050309 100644 --- a/base/Kernel/Native/hal.cpp +++ b/base/Kernel/Native/hal.cpp @@ -6,94 +6,32 @@ // // File: Hal.cpp // -// Note: +// Note: Kernel Only // #include "hal.h" - -#if SINGULARITY_KERNEL #include "halkd.h" -#include "printf.cpp" -#endif // SINGULARITY_KERNEL - -////////////////////////////////////////////////////////////////////////////// - -extern "C" int _fltused = 0x9875; - -////////////////////////////////////////////////////////////////////////////// -// debugging code. Put it here for now. - -#if SINGULARITY_KERNEL - -void Halt() -{ - uintptr _ebp; - __asm mov _ebp, ebp; - - for (int i = 0; i < 30 && _ebp >= 0x4000; i++) { - - uintptr next = ((uintptr *)_ebp)[0]; - uintptr code = ((uintptr *)_ebp)[1]; - - printf(" %p: %p %p\n", _ebp, next, code); - _ebp = next; - } - printf("---- Halting. --------------------------------------------------------"); - - __asm int 3; -} - -void Cls() -{ - KdPutChar('\n'); - for (uint16 n = 0; n < 79; n++) { - KdPutChar('-'); - } - KdPutChar('\n'); -} - -void __cdecl PutChar(char cOut) -{ - KdPutChar(cOut); -} - -void fail_assert(const char *expr) -{ - printf("%s\n", expr); - printf("----- Frame --- EBP ---- Code ----------------------------- Assert Failure\n"); - Halt(); - __asm int 3; -} - -#elif SINGULARITY_PROCESS - -void fail_assert(Class_System_String *message) -{ - Struct_Microsoft_Singularity_V1_Services_DebugService::g_Print((bartok_char*)&message->m_firstChar, - message->m_stringLength); - //Struct_Microsoft_Singularity_V1_Services_DebugService::g_Break(); - Class_Microsoft_Singularity_DebugStub::g_Break(); -} -#endif // SINGULARITY_KERNEL + +#include "strformat.cpp" + +int __cdecl _cinit(void); + +#if ISA_ARM +void SerialInit(UINT32 * uartBase); +void SerialOutHex(uint32 value); +void SerialOut(const char * str); +#define ArmSerialInit(x) SerialInit(x) +#define ArmSerialOut(x) SerialOut(x) +#define ArmSerialOutHex(x) SerialOutHex(x) +#else // ISA_ARM +#define ArmSerialInit(x) +#define ArmSerialOut(x) +#define ArmSerialOutHex(x) +#endif // ISA_ARM ////////////////////////////////////////////////////////////////////////////// +// Kernel methods // -extern "C" int __cdecl _cinit(void); -extern "C" int __cdecl _cfini(void); - -#if SINGULARITY_KERNEL - -void Class_Microsoft_Singularity_Kernel::g_Kill(int32 action) -{ - ((Struct_Microsoft_Singularity_BootInfo *)g_pBootInfo)->KillAction = action; - - void (__cdecl *pfKill)(void) = (void (__cdecl *)(void))g_pBootInfo->Kill32; - printf("About to call pfKill(%p) with %08x [g_pBootInfo=%p]\n", - pfKill, g_pBootInfo->KillAction, g_pBootInfo); - pfKill(); - Halt(); -} - extern wchar_t _LinkDate[]; bartok_char * @@ -102,73 +40,60 @@ Class_Microsoft_Singularity_Kernel::g_HalGetLinkDate() return (bartok_char *)_LinkDate; } -// Hoisted this out of checkHinges because VC appears to have changed its name -// mangling strategy for structs in functions. - -struct CheckedHingeEntry { uint32 i; char *a; char *b; }; - -void checkHinges() +void fail_assert(const char *expr) { - // Check for a broken image produce when Bartok compiles through an assembler. - int busted = false; - extern CheckedHingeEntry checkedHingeTable[]; - for (CheckedHingeEntry *che = checkedHingeTable; che->a; che++) { - if (che->a+1 != che->b) { - printf("%06d: 0x%08x 0x%08x\n", che->i, che->a+1, che->b); - busted = true; - } - } - if (busted) { - printf("----- Frame --- EBP ---- Code ------------------------------ Busted Hinges\n"); - Halt(); + // TODO: wire up something useful here. + __debugbreak(); +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +// +Class_System_RuntimeType *CpuRuntimeType = &Class_Microsoft_Singularity_Hal_Cpu::_type; +Class_System_RuntimeType *PlatformRuntimeType = &Class_Microsoft_Singularity_Hal_Platform::_type; + +Class_Microsoft_Singularity_Hal_Cpu *Class_Microsoft_Singularity_Hal_Platform::g_GetCurrentCpu() +{ + return (Class_Microsoft_Singularity_Hal_Cpu *) + Class_Microsoft_Singularity_Processor::g_GetCurrentProcessorContext()->halCpu; +} + +void Struct_Microsoft_Singularity_MpBootInfo::g_HalReleaseMpStartupLock() +{ + uint32 lockAddr = + (uint32)Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->MpStartupLock32; + if (lockAddr != 0) { + *((uint16*) lockAddr) = 0; } } -extern "C" static void __cdecl -HalBspEnter(Struct_Microsoft_Singularity_BootInfo *bi) +void InitCpu(Class_Microsoft_Singularity_Hal_Cpu *pi) { - g_pBootInfo = bi; + ArmSerialOut("InitCpu0\n"); + ArmSerialOutHex(pi->Id); + ArmSerialOut("\n"); - kdprintf("Singularity HAL [%ls]\n", _LinkDate); - _cinit(); + Class_Microsoft_Singularity_Hal_Cpu *hpi = (Class_Microsoft_Singularity_Hal_Cpu*) pi; - kdprintf("DebugPort: %04x\n", bi->DebugBasePort); - KdInitialize(bi); + hpi->postHeader.vtableObject = CpuRuntimeType->classVtable; - Cls(); - printf("Singularity Hardware Abstraction Layer [%ls]\n", _LinkDate); + ArmSerialOut("InitCpu1\n"); - printf("\n"); + Class_Microsoft_Singularity_Hal_Cpu::m_Initialize(hpi); - IdtInitialize(); - IdtLoad(); - ProcessorInitialize(&bi->Cpu0, 0); - checkHinges(); + ArmSerialOut("InitCpu2\n"); - Class_Microsoft_Singularity_Tracing::g_Initialize(); - Class_Microsoft_Singularity_Tracing::g_Log(0); - Class_Microsoft_Singularity_Tracing::g_Log(1); - Class_Microsoft_Singularity_Tracing::g_Log(2); - Class_Microsoft_Singularity_Tracing::g_Log(3); + Class_Microsoft_Singularity_Isal_Isa::g_InitializeCpuDispatchTable(); - printf("----------------------------------------------------------------------\n"); - printf("Calling Kernel.Main:\n"); - Class_Microsoft_Singularity_Kernel::g_Main(); - - // We should not rely on or use any C++ finalizers. - // _cfini(); + ArmSerialOut("InitCpu3\n"); } -extern "C" static void __cdecl -HalApEnter(const Struct_Microsoft_Singularity_BootInfo *bi, int cpu) +void ShutdownCpu(Class_Microsoft_Singularity_Hal_Cpu *pi) { - IdtLoad(); - - const Struct_Microsoft_Singularity_CpuInfo *cpuInfo = &bi->Cpu0 + cpu; - ProcessorInitialize(cpuInfo, cpu); - - Class_Microsoft_Singularity_Kernel::g_MpMain(cpu); +#if ISA_IX86 || ISA_IX64 + // Disable Interrupts and then spin a screen icon waiting to be reset. Class_Microsoft_Singularity_Processor::g_DisableInterrupts(); + + int cpu = pi->Id; for (int i = 0; i != i + 1; i++) { uint16* p = (uint16*)(0xb8000 + (cpu - 1) * 2 * 8); for (int r = 0; r < 8; r++) { @@ -182,175 +107,153 @@ HalApEnter(const Struct_Microsoft_Singularity_BootInfo *bi, int cpu) } *p++ = t; if (Class_Microsoft_Singularity_DebugStub::g_PollForBreak() == true) { - __asm int 3; + __debugbreak(); } for (int i = 0; i < 50000; i++) { - __asm nop; + __nop(); } } } +#endif } -extern "C" void __cdecl -Hal(Struct_Microsoft_Singularity_BootInfo *bi, int cpu) +void InitPlatform(Class_Microsoft_Singularity_Hal_Platform *bi, + Class_Microsoft_Singularity_Hal_Cpu *pi) { - if (cpu == 0) { - HalBspEnter(bi); + ArmSerialOut("InitPlatform0"); + +#ifndef ISA_ARM + Class_Microsoft_Singularity_Isal_Isa::g_InitializeDispatchTable(); +#endif + + InitCpu(pi); + + ArmSerialOut("InitPlatform2"); + + Class_Microsoft_Singularity_Hal_Platform *nbi = (Class_Microsoft_Singularity_Hal_Platform *) bi; + nbi->postHeader.vtableObject = PlatformRuntimeType->classVtable; + + // Call early init routines + + Class_Microsoft_Singularity_Hal_Platform::m_Initialize(nbi, + (Class_Microsoft_Singularity_Hal_Cpu *) pi); + + ArmSerialOut("InitPlatform3"); + + kdprintf("DebugPort: %04x\n", nbi->DebugBasePort); + + ArmSerialOut("InitPlatform4"); + + // Initialize the debugger hardware + + if (KdpSerialInit(nbi)) { + nbi->DebuggerType = Class_Microsoft_Singularity_Hal_Platform_DEBUGGER_SERIAL; } else { - HalApEnter(bi, cpu); + nbi->DebuggerType = Class_Microsoft_Singularity_Hal_Platform_DEBUGGER_NONE; } + + ArmSerialOut("Calling Kernel.Main:\n"); } -#elif SINGULARITY_PROCESS - -BOOL KdDebuggerNotPresent; -extern Class_System_RuntimeType * brtmainClass; -extern int (*brtmain)(ClassVector_Class_System_String *args); -extern int brtmainReturnsInt; - -// Note: CallMain's return value is only meaningful if brtmainReturnsInt is true. -// Example: -// int ret = CallMain(args); -// if (!MainReturnsInt()) ret = 0; -__declspec(naked) int Class_Microsoft_Singularity_AppRuntime:: -g_CallMain(ClassVector_Class_System_String *args) +void ShutdownPlatform(Class_Microsoft_Singularity_Hal_Platform *bi) { - // To avoid creating an unmanaged stack frame, jmp directly to the main function: - __asm { - mov eax, brtmain; - jmp eax; - } } -bool Class_Microsoft_Singularity_AppRuntime:: -g_MainReturnsInt() -{ - return (brtmainReturnsInt != 0); -} - -void Class_Microsoft_Singularity_AppRuntime:: -g_SetDebuggerPresence(bool debuggerPresent) -{ - KdDebuggerNotPresent = !debuggerPresent; -} - -extern "C" int32 __fastcall RuntimeEntryPoint(int threadIndex) -{ - int32 ret = 0; - - Struct_Microsoft_Singularity_X86_ThreadContext * context = - Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext(); - - if (!Struct_Microsoft_Singularity_X86_ThreadContext::m_IsInKernelMode( - context)) { - // fail assertion in uninitialized process mode: - __asm int 3 - } - - Struct_Microsoft_Singularity_X86_ThreadContext::m_SetProcessMode(context); - - if (threadIndex == -1) { - _cinit(); - Class_Microsoft_Singularity_Tracing::g_Initialize(); - Class_Microsoft_Singularity_Tracing::g_Log(0, "RuntimeEntryPoint:Main"); - Class_Microsoft_Singularity_Monitoring::g_Initialize(); - - ret = Class_Microsoft_Singularity_AppRuntime::g_AppStart(brtmainClass); - } - else { - Class_Microsoft_Singularity_Tracing::g_Log(0, "RuntimeEntryPoint:Thread"); - Class_System_Threading_Thread::g_ThreadStub(threadIndex); - } - - Struct_Microsoft_Singularity_X86_ThreadContext::m_SetKernelMode(context); - - return ret; -} - -#endif // SINGULARITY_PROCESS - ////////////////////////////////////////////////////////////////////////////// - -// Need to put the following marker variables into the .CRT section. -// The .CRT section contains arrays of function pointers. -// The compiler creates functions and adds pointers to this section -// for things like C++ global constructors. // -// The XIA, XCA etc are group names with in the section. -// The compiler sorts the contributions by the group name. -// For example, .CRT$XCA followed by .CRT$XCB, ... .CRT$XCZ. -// The marker variables below let us get pointers -// to the beginning/end of the arrays of function pointers. +// These are the first instruction executed in the kernel. // -// For example, standard groups are -// XCA used here, for begin marker -// XCC "compiler" inits -// XCL "library" inits -// XCU "user" inits -// XCZ used here, for end marker -// - -typedef void (__cdecl *_PVFV)(void); -// typedef int (__cdecl *_PIFV)(void); -typedef _PVFV _PIFV; - -#pragma comment(linker, "/merge:.CRT=.DATA") - -#pragma data_seg(".CRT$XIA", "DATA") -extern "C" _PIFV __xi_a[] = { NULL }; // C initializers. - -#pragma data_seg(".CRT$XIZ", "DATA") -extern "C" _PIFV __xi_z[] = { NULL }; - -#pragma data_seg(".CRT$XCA", "DATA") -extern "C" _PVFV __xc_a[] = { NULL }; // C++ initializers. - -#pragma data_seg(".CRT$XCZ", "DATA") -extern "C" _PVFV __xc_z[] = { NULL }; - -#pragma data_seg(".CRT$XPA", "DATA") -extern "C" _PVFV __xp_a[] = { NULL }; // C pre-terminators. - -#pragma data_seg(".CRT$XPZ", "DATA") -extern "C" _PVFV __xp_z[] = { NULL }; - -#pragma data_seg(".CRT$XTA", "DATA") -extern "C" _PVFV __xt_a[] = { NULL }; // C terminators. - -#pragma data_seg(".CRT$XTZ", "DATA") -extern "C" _PVFV __xt_z[] = { NULL }; - -#pragma data_seg() - -// Walk an array of function pointers, call non-NULL ones. -void __cdecl _initterm(_PVFV *pfbegin, _PVFV *pfend) +extern "C" int __fastcall HalEntryPoint( + Class_Microsoft_Singularity_Hal_Platform *bi, + Class_Microsoft_Singularity_Hal_Cpu *pi) { - for (; pfbegin < pfend; pfbegin++) { - if (*pfbegin != NULL) { - (**pfbegin)(); + ArmSerialInit((UINT32 *) 0xffd82300); + ArmSerialOut("HalEntry 1\n"); + + if (bi->Size != sizeof(Class_Microsoft_Singularity_Hal_Platform)) { + __debugbreak(); + return Class_Microsoft_Singularity_Hal_Platform_ERROR_BAD_SIZE; + } + + if (pi->Id == 0) { + + // Static C++ constructors. Ideally, there would be none. + _cinit(); + + ArmSerialOut("HalEntry 2\n"); + + // Initialize the target. + Class_Microsoft_Singularity_Isal_Isa::g_Initialize(bi->CpuRecordPointerOffset, + bi->ThreadRecordPointerOffset); + + Class_Microsoft_Singularity_Isal_Isa::g_InitializeCpu( + (Struct_Microsoft_Singularity_Isal_CpuRecord *)pi->CpuRecordPage, + pi->Id, + (UIntPtr) pi->KernelStackLimit); + + ArmSerialOut("HalEntry 3\n"); + + // Initialize processor context + ProcessorInitialize(pi); + + ArmSerialOut("HalEntry 4\n"); + + // Assert that processor local store is working correctly + if (Class_Microsoft_Singularity_Hal_Platform::g_GetCurrentCpu() != pi) { + __debugbreak(); } + + ArmSerialOut("HalEntry 6\n"); + + // Set up native-only constants + // Initialize platform (in a platform-specific way) + InitPlatform(bi, pi); + ArmSerialOut("HalEntry 7\n"); + + // Initialize tracing + Class_Microsoft_Singularity_Tracing::g_Initialize(); + + ArmSerialOut("HalEntry 8\n"); + + // Initialize debugger + KdInitialize(bi); + + ArmSerialOut("HalEntry 9\n"); + + // Initial breakpoint - uncomment me to do early debugging + //if (bi->DebuggerType != Class_Microsoft_Singularity_Hal_Platform_DEBUGGER_NONE) { + // __debugbreak(); + //} + + Class_Microsoft_Singularity_Kernel::g_Main(); + + ShutdownPlatform(bi); + // Native exits by returning back to the undump/bootloader + return 0; + } + else { + // Initialize the target cpu. + Class_Microsoft_Singularity_Isal_Isa::g_InitializeCpu( + (Struct_Microsoft_Singularity_Isal_CpuRecord *)pi->CpuRecordPage, + pi->Id, + (UIntPtr) pi->KernelStackLimit); + + // Initialize processor context + ProcessorInitialize(pi); + + // Initialize platform processor + InitCpu(pi); + + // Assert that processor local store is working correctly + if (Class_Microsoft_Singularity_Hal_Platform::g_GetCurrentCpu() != pi) { + __debugbreak(); + } + + int result = Class_Microsoft_Singularity_Kernel::g_MpMain(pi->Id); + + ShutdownCpu(pi); + + return Class_Microsoft_Singularity_Hal_Platform_SUCCESS; } } - -// Call all of the C++ static constructors. -// -int __cdecl _cinit(void) -{ - // do C initializations - _initterm( __xi_a, __xi_z ); - - // do C++ initializations - _initterm( __xc_a, __xc_z ); - return 0; -} - -int __cdecl _cfini(void) -{ - // do C initializations - _initterm( __xp_a, __xp_z ); - - // do C++ terminations - _initterm( __xt_a, __xt_z ); - return 0; -} diff --git a/base/Kernel/Native/hal.h b/base/Kernel/Native/hal.h index 1bdcaf2..1b5d433 100644 --- a/base/Kernel/Native/hal.h +++ b/base/Kernel/Native/hal.h @@ -9,16 +9,11 @@ // Note: // -#ifndef __hal_h_ -#define __hal_h_ +#pragma once #define UNICODE #define _UNICODE -////////////////////////////////////////////////////////////////////////////// -// -#define PTR_SIZE_32 1 - /////////////////////////////////////////// Core types used by runtime system. // @@ -98,10 +93,11 @@ typedef unsigned char UCHAR; typedef void *PVOID; typedef uintptr ULONG_PTR; -typedef unsigned long DWORD; typedef uint64 ULARGEST; typedef int64 LARGEST; +typedef int BOOL; + typedef struct { UINT64 _lo; @@ -110,27 +106,40 @@ typedef struct #define NULL 0 +extern "C" { + extern UINT32 ProcessorContextOffset; +}; + + #if SINGULARITY_KERNEL #ifndef _VA_LIST_DEFINED typedef char *va_list; #define _VA_LIST_DEFINED +#if PTR_SIZE_32 + #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) #define va_start(ap,v) ap = (va_list)&v + _INTSIZEOF(v) #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) #define va_end(ap) ap = (va_list)0 -#endif -int printf(const char *pszFmt, ...); +#else // PTR_SIZE_64 -void IdtInitialize(); -void IdtLoad(); +// the following extern "C" code is needed to ensure the +// compiler intrinsic IV_VA_START is invoked. +extern "C" void __cdecl __va_start(va_list *, ...); -struct Struct_Microsoft_Singularity_CpuInfo; -void ProcessorInitialize(const Struct_Microsoft_Singularity_CpuInfo* cpu, - int cpuId); -int GetCurrentProcessorNumber(); +#define va_dcl va_list va_alist; +#define va_start(ap,x) ( __va_start(&ap, x) ) +#define va_arg(ap, t) \ + ( ( sizeof(t) > sizeof(__int64) || ( sizeof(t) & (sizeof(t) - 1) ) != 0 ) \ + ? **(t **)( ( ap += sizeof(__int64) ) - sizeof(__int64) ) \ + : *(t *)( ( ap += sizeof(__int64) ) - sizeof(__int64) ) ) +#define va_end(ap) ( ap = (va_list)0 ) + +#endif // PTR_SIZE_64 +#endif // _VA_LIST_DEFINED #ifndef MAX_CPU #define MAX_CPU 1 @@ -140,19 +149,10 @@ int GetCurrentProcessorNumber(); ////////////////////////////////////////////////////////////////////////////// // -typedef int BOOL; - -#if SINGULARITY_KERNEL - -#include "winctx.h" -#include "minidump.h" - -#elif SINGULARITY_PROCESS +#if SINGULARITY_PROCESS ////////////////////////////////////////////////////////////////////////////// // -typedef int BOOL; - struct Struct_System_ObjectHeader { int32 syncBlockValue; @@ -177,31 +177,16 @@ struct _##s \ {}, \ (Class_System_VTable *)&Class_System_String::_vtable, \ sizeof(v), \ - sizeof(v) - 1, \ - L##v \ + sizeof(v)/sizeof(wchar_t) - 1, \ + v \ } #endif // SINGULARITY_PROCESS ////////////////////////////////////////////////////////////////////////////// -#if DEBUG - -#if SINGULARITY_KERNEL -extern void fail_assert(const char *expr); -#define __fail_assert(expr, file, line) fail_assert("assert(" #expr ") failed at " file ":" #line) -#define Assert(expr) { if (!(expr)) { __fail_assert(expr, __FILE__, __LINE__); } } -#elif SINGULARITY_PROCESS -extern void fail_assert(Class_System_String *message); -#define Assert(expr) { if (!(expr)) { static MAKE_STRING(msg, "assert(" #expr ") failed at " __FILE__ ":" __LINE__); fail_assert(&msg.string); } } -#endif // SINGULARITY_KERNEL - -#else //DEBUG -#define Assert(expr) { 0; } -#endif//DEBUG - #pragma warning(disable: 4201) // Allow nameless struct/union -#pragma warning(disable:4127) // 4127: warning about constant conditional +#pragma warning(disable: 4127) // 4127: warning about constant conditional #define EXCEPTION_ACCESS_VIOLATION 1 #define EXCEPTION_CONTINUE_EXECUTION 2 @@ -244,19 +229,13 @@ struct KdDebugTrapData bool silent; bool ret; } unloadedBinary; - } u; + }; }; ////////////////////////////////////////////////////////////////////////////// // #pragma warning(disable: 4103) -#pragma pack(push, 4) #include "halclass.h" -#pragma pack(pop) - -#if SINGULARITY_KERNEL -extern const Struct_Microsoft_Singularity_BootInfo *g_pBootInfo; -#endif // SINGULARITY_KERNEL ////////////////////////////////////////////////////////////////////////////// // @@ -286,93 +265,32 @@ struct ClassVector_bartok_char : ClassVector bartok_char values[1]; }; - -// Routine to read Pentium time stamp counter -_inline _declspec( naked ) UINT64 RDTSC() -{ - __asm { - rdtsc; - ret; - } -} - -_inline _declspec(naked) UINT32 DisableInterrupts() -{ - __asm { - pushfd; - pop eax; - cli; - ret; - } -} - -_inline _declspec(naked) void EnableInterrupts() -{ - __asm { - sti; - ret; - } -} - -_inline void RestoreInterrupts(UINT32 fd) -{ - if ((fd & Struct_Microsoft_Singularity_X86_EFlags_IF) != 0) { - __asm sti; - } -} - -/////////////////////////////////////////////////////// Interlocked Intrinsics. +////////////////////////////////////////////////////////////// Dynamic Assert. // +#if DEBUG +#if SINGULARITY_KERNEL +extern void fail_assert(const char *expr); +#define __fail_assert(expr, file, line) fail_assert("assert(" #expr ") failed at " file ":" #line) +#define Assert(expr) { if (!(expr)) { __fail_assert(expr, __FILE__, __LINE__); } } +#elif SINGULARITY_PROCESS +extern void fail_assert(Class_System_String *message); +#define Assert(expr) { if (!(expr)) { static MAKE_STRING(msg, L"assert(" L#expr L") failed"); fail_assert(&msg.string); } } +#endif +#else //DEBUG +#define Assert(expr) { 0; } +#endif//DEBUG -#define InterlockedIncrement _InterlockedIncrement -#define InterlockedDecrement _InterlockedDecrement -#define InterlockedExchange _InterlockedExchange -#define InterlockedExchangeAdd _InterlockedExchangeAdd -#define InterlockedCompareExchange _InterlockedCompareExchange +////////////////////////////////////////////////////////////////////////////// -extern "C" -{ +int printf(const char *pszFmt, ...); -INT32 -__cdecl -InterlockedIncrement( - INT32 volatile *lpAddend - ); +void Cls(void); -INT32 -__cdecl -InterlockedDecrement( - INT32 volatile *lpAddend - ); - -INT32 -__cdecl -InterlockedExchange( - INT32 volatile *Target, - INT32 Value - ); - -INT32 -__cdecl -InterlockedExchangeAdd( - INT32 volatile *Addend, - INT32 Value - ); - -INT32 -__cdecl -InterlockedCompareExchange ( - INT32 volatile *Destination, - INT32 ExChange, - INT32 Comperand - ); - -#pragma intrinsic(_InterlockedIncrement) -#pragma intrinsic(_InterlockedDecrement) -#pragma intrinsic(_InterlockedExchange) -#pragma intrinsic(_InterlockedExchangeAdd) -#pragma intrinsic(_InterlockedCompareExchange) -} +#if SINGULARITY_KERNEL +void ProcessorInitialize(Class_Microsoft_Singularity_Hal_Cpu* processor); +#endif +void KdNotifyTrap(KdDebugTrapData *data); +void KdNotifyException(Class_System_Exception *exception, unsigned int throwAddr); ////////////////////////////////////////////////////////////////////////////// @@ -381,7 +299,7 @@ InterlockedCompareExchange ( #pragma warning(disable: 4100) // allow unreferenced formal parameters -#endif // __hal_h_ +// Include standard compiler intrinsics +#include "intrinsics.h" -// ////////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/hal.inc b/base/Kernel/Native/hal.inc deleted file mode 100644 index f0d33c7..0000000 --- a/base/Kernel/Native/hal.inc +++ /dev/null @@ -1,96 +0,0 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; -; Copyright (c) Microsoft Corporation. All rights reserved. -; -; Include file for assembly files. -; - -;ASSUME DS:FLAT, ES:FLAT, SS:FLAT, FS:NOTHING, GS:NOTHING - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -SINGLE_THREADED equ 0 -EXCLUDED equ 0 - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; -; Typedefs & Constants -; -UINT8 TYPEDEF BYTE -UINT16 TYPEDEF WORD -UINT32 TYPEDEF DWORD -UINT64 TYPEDEF QWORD -UINT128 STRUCT 16 - _lo UINT64 ? - _hi UINT64 ? -UINT128 ENDS - -; -X86_EFLAG_IF equ 0200h - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; -; Bartok Types -; - -uint8 TYPEDEF BYTE -uint16 TYPEDEF WORD -uint32 TYPEDEF DWORD -uint64 TYPEDEF QWORD - -int8 TYPEDEF BYTE -int16 TYPEDEF WORD -int32 TYPEDEF DWORD -int64 TYPEDEF QWORD - -bool TYPEDEF BYTE -bartok_char TYPEDEF WORD - -intptr TYPEDEF DWORD -uintptr TYPEDEF DWORD - -uintPtr STRUCT 4 - value uintptr ? -uintPtr ENDS - -intPtr STRUCT 4 - value intptr ? -intPtr ENDS - -PTR_uintptr TYPEDEF PTR uintptr - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; - -include halclass.inc - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; -; Macros -; - -; define CurrentThread() function as a macro. -CurrentThread MACRO reg - mov reg,fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._threadContext] - mov reg,[reg].Struct_Microsoft_Singularity_X86_ThreadContext.__thread - ENDM - -CurrentThreadContext MACRO reg - mov reg,fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._threadContext] - ENDM - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; -; Big/Little Endian Definitions for Long Integers -; - -ifdef bigend ; Big Endian (hi word at low address) -LOWORD equ [4] -HIWORD equ [0] -else ; Little Endian (low word at low address) -LOWORD equ [0] -HIWORD equ [4] -endif - -;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; End of File. diff --git a/base/Kernel/Native/halasm.asm b/base/Kernel/Native/halasm.asm deleted file mode 100644 index 6424ea6..0000000 --- a/base/Kernel/Native/halasm.asm +++ /dev/null @@ -1,1470 +0,0 @@ -; -; Copyright (c) Microsoft Corporation. All rights reserved. -; - -ifdef SINGULARITY -.686p -.mmx -.xmm -else ;; SINGULARITY -.686 -endif ;; SINGULARITY - -.model flat -.code - -ifdef SINGULARITY -assume ds:flat -assume es:flat -assume ss:flat -assume fs:nothing -assume gs:nothing -else ;; SINGULARITY -EXCLUDED equ 0 -SINGLE_THREADED equ 0 -endif ;; SINGULARITY - -include hal.inc - -externdef __throwDispatcher:NEAR -externdef __throwDispatcherExplicitAddrAfter:NEAR -externdef ?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z:NEAR - -ifndef SINGULARITY -externdef ?ResetGuardPage@@YIXXZ:NEAR -endif ;; !SINGULARITY - -ifdef SINGULARITY_KERNEL -externdef ?g_setStopContext@Class_System_Threading_Thread@@SIXPAU1@PAUClass_System_Exception@@@Z:NEAR -externdef __throwBeyondMarker:NEAR -endif ; SINGULARITY_KERNEL - -externdef __throwDivideByZeroException:NEAR -externdef __throwNullPointerException:NEAR -externdef __throwOverflowException:NEAR -externdef __throwStackOverflowException:NEAR - -ifndef SINGULARITY -externdef ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ:NEAR -externdef ?c_CheckCount@Class_System_GCs_StackManager@@2HA:int32 -externdef ?g_GetStackChunk@Class_System_GCs_StackManager@@SIPAUuintPtr@@PAU2@PAUClass_System_Threading_Thread@@_N@Z:NEAR -externdef ?g_ReturnStackChunk@Class_System_GCs_StackManager@@SIXPAUClass_System_Threading_Thread@@_N@Z:NEAR -; -; externals used to construct instances of machine-level exceptions -; - -externdef ?g_AllocateObject@Class_System_GC@@SIPAUClass_System_Object@@PAUClass_System_VTable@@@Z:NEAR -externdef ?m__ctor@Class_System_ArithmeticException@@SIXPAU1@@Z:NEAR -externdef ??_7System_ArithmeticException@@6B@:dword ; vtable -externdef ?m__ctor@Class_System_DivideByZeroException@@SIXPAU1@@Z:NEAR -externdef ??_7System_DivideByZeroException@@6B@:dword ; vtable -externdef ?m__ctor@Class_System_NullReferenceException@@SIXPAU1@@Z:NEAR -externdef ??_7System_NullReferenceException@@6B@:dword ; vtable -externdef ?m__ctor@Class_System_OverflowException@@SIXPAU1@@Z:NEAR -externdef ??_7System_OverflowException@@6B@:dword ; vtable -externdef ?m__ctor@Class_System_StackOverflowException@@SIXPAU1@@Z:NEAR -externdef ??_7System_StackOverflowException@@6B@:PTR_Class_System_VTable - -externdef ?c_allocationInhibitGC@Class_System_GC@@2_NA:dword - -externdef ?g_doubleToInt@Class_System_VTable@@SIHN@Z:NEAR -externdef ?g_doubleToLong@Class_System_VTable@@SI_JN@Z:NEAR -externdef ?g_floatToInt@Class_System_VTable@@SIHM@Z:NEAR -externdef ?g_floatToLong@Class_System_VTable@@SI_JM@Z:NEAR -externdef ?g_checkedDoubleToInt@Class_System_VTable@@SIHN@Z:NEAR -externdef ?g_checkedDoubleToLong@Class_System_VTable@@SI_JN@Z:NEAR -externdef ?g_checkedFloatToInt@Class_System_VTable@@SIHM@Z:NEAR -externdef ?g_checkedFloatToLong@Class_System_VTable@@SI_JM@Z:NEAR -endif ;; !SINGULARITY - -if EXCLUDED -externdef __checkFPStackDepth0:NEAR -externdef __checkFPStackDepth1:NEAR -externdef __checkFPStackDepth2:NEAR -externdef __checkFPStackDepth3:NEAR -externdef __checkFPStackDepth4:NEAR -externdef __checkFPStackDepth5:NEAR -externdef __checkFPStackDepth6:NEAR -externdef __checkFPStackDepth7:NEAR -endif ;; EXCLUDED - - align 8 -$DBLMAXINT DQ 041dfffffffc00000r ; 2.14747e+009 -$DBLMININT DQ 0c1e0000000000000r ; -2.14748e+009 -$MAXLONG DQ 07fffffffffffffffh -$MINLONG DQ 08000000000000000h - -; -; __throwDispatcher(ecx=exception) -; -; Description: this function is called to explicitly throw an exception. -; It assumes that the return address points to the instruction immediately -; after the one where the exception was thrown -; -; Arguments: -; ecx = exception object -; [esp] = the return address -; Effects: -; 1. Create ebp chain entry -; 2. Get return address and uses it to calculate the address of the -; instruction that threw the exception. -; 3. Looks up the appropriate handler and jumps to code to process it. - -align 16 - -__throwDispatcher proc - push ebp ; create ebp chain entry - mov ebp, esp ; set new ebp - mov edx, [ebp+4] ; get return address - push ecx ; save exception - sub edx, 1 ; adjust to point to throw location - call ?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z - pop ecx ; restore exception - pop ebp ; remove ebp chain - add esp, 4 ; remove eip from the stack -; mov edx is already ok - jmp __throwDispatcherHandler -__throwDispatcher endp - -; -; __throwDispatcherExplicitAddr (ecx=exception, edx=throwAddress) -; -; Description: -; This is to be used when the address where the exception occurred -; is passed in as an extra argument -; -; Arguments: -; ecx = exception object -; edx = address where the exception was thrown -; - - -align 16 -__throwDispatcherExplicitAddr proc - push ecx ; save exception - call ?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z - pop ecx ; restore exception -; mov edx is already ok - jmp __throwDispatcherHandler -__throwDispatcherExplicitAddr endp - -; -; __throwDispatcherExplictAddrAfter (ecx=exception, edx=throwAddress) -; -; Description: -; This is to be used when the address of the instruction immediately after -; the one that actually threw the exception is passed as an argument. -; -; Arguments: -; ecx = pointer to exception object being thrown -; edx = address of the instruction following the one where -; the exception was thrown -; -; This is used, for example, in stack unwinding, where edx is the -; return address on the stack for the current procedure. -; -; Stack unwinding occurs when the current procedure does not have a handler -; for the routine. The idea is to pop the stack frame and treat the call -; instruction in the caller as though it threw. We only have the return -; address, though, which points to the instruction *after* the call. -; - -align 16 -__throwDispatcherExplicitAddrAfter proc -ifdef SINGULARITY - push ecx - push edx - mov ecx, edx - call ?g_IsUnlinkStack@Class_System_Exception@@SI_NPAUuintPtr@@@Z - pop edx - pop ecx - test al, al - je normal - mov eax, ecx ; save exception type - mov ecx, edx - mov edx, [ebp+4] ; save the return addr in caller - mov [ebp+4], afterUnlinkStack ; override return addr to instr after - jmp ecx ; unlink stack which saves eax, edx -afterUnlinkStack: - mov ecx, eax ; restore return addr in caller -normal: - ; Have we reached a kernel->process or process->kernel boundary? - ; Call Exception.CheckKernelProcessBoundary(esp, ebp, exception) - push ecx - push edx - push ecx ; arg 3 (exception) - mov ecx, esp ; arg 2 (ebp) - mov edx, ebp ; arg 1 (esp) - call ?g_CheckKernelProcessBoundary@Class_System_Threading_Thread@@SIPAUuintPtr@@PAU2@0PAUClass_System_Exception@@@Z - pop edx - pop ecx - ; A non-zero return value means we reached a boundary. - test eax, eax - jz normal2 -ifdef SINGULARITY_PROCESS - ;; The process's exception reached the kernel's frames. - ; Return gracefully to the kernel (i.e. jump to the kernel's - ; return address), and the kernel's popStackMark - ; will check uncaughtFlag and throw a new exception. - jmp edx -endif ; SINGULARITY_PROCESS -ifdef SINGULARITY_KERNEL -; There are 3 control paths that merge here: -; (1) When Thread.cs stops a process mode thread, it sets eip to point here. -; (2) When Thread.cs stops a blocked kernel thread, it sets eip to point here. -; (3) The stack unwinder falls through to this case when a kernel exception -; reaches a process's frames. -; For control path (3), a finally block puts us in a GC safe state -; just before we reach here. -; We also expect to be in the GC safe state most of the -; time for control paths (1) and (2), but -; because pushStackMark and popStackMark are not atomic operations, -; we cannot be 100% sure that we're in the GC safe state. -; eax: kernel->process or kernel->kernel marker -; ecx: exception -__throwBeyondMarker LABEL NEAR - push ecx - push eax - ; Leave the GC safe state (if we're actually in it) - call ?g_RestoreMutatorControlIfNeeded@Class_System_GCs_Transitions@@SIXXZ - pop eax - pop ecx -; eax: kernel->process or kernel->kernel marker -; ecx: exception - ; Restore state from the marker, skipping over any intermediate frames. - ; Keep the original exception in ecx. - ; Get the return address for the frame beyond the marker into edx: - mov edx, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._stackBottom - mov edx, dword ptr [edx - 4] - ; Free any stack segments that we skipped over: - push eax - push ecx - push edx - mov ecx, eax ; arg 1 (marker) - call ?g_DiscardSkippedStackSegments@Class_System_Threading_Thread@@SIXPAUStruct_System_GCs_CallStack_TransitionRecord@@@Z - pop edx - pop ecx - pop eax - ; Restore the kernel's ebx, edi, esi from the marker: - mov ebx, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._EBX - mov edi, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._EDI - mov esi, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._ESI - ; Restore the kernel's esp, ebp from the kernel->process marker: - mov ebp, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._EBP - mov esp, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._stackBottom -endif ; SINGULARITY_KERNEL -normal2: -endif - push ecx ; save exception - dec edx - call ?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z - pop ecx ; restore exception -; mov edx is already ok - jmp __throwDispatcherHandler -__throwDispatcherExplicitAddrAfter endp - -; __throwDispatcherHandler (eax=frameSetupInfo or exceptionType, -; ecx=exception, -; edx=spillSize,frameSetupSize, or handlerAddress -; -; Description: -; After the exception table entry has been found, the values are passed here -; for processing. This method simply checks for the easy case of an explicit -; handler (known if the exceptionType is given <-- low bit is zero). -; In this case it simply jumps to the handler. Otherwise it passes the -; information along to __throwDispatcherUnwind. -; -; Arguments: -; If low bit of eax is set: -; eax = frame setup information -; ecx = exception object -; edx = spill size excluding callee-save register saves -; or offset from ebp to end of callee-save register saves -; Otherwise: -; eax = exception type (unused) -; ecx = exception object -; edx = handler address - -align 16 -__throwDispatcherHandler proc - test eax, 1 - jne __throwDispatcherUnwind - ;; ecx=exception, edx=handler - jmp edx -__throwDispatcherHandler endp - -; __throwDispatcherUnwind (eax=frame setup info, ecx=exception, edx=spill size -; -; Description: -; This is the global unwind handler. It is used to unwind a single stack -; frame if there are no explicit handlers that catch an exception in a given -; function. -; -; Arguments: -; eax = frame setup information, must have low bit set -; ecx = exception object -; edx = spill size excluding callee-save register saves -; or offset from ebp to end of callee-save register saves -; -; See tables\ExceptionTable.cs for details on these values - -align 16 -__throwDispatcherUnwind proc - ;; eax=frame info - ;; edx=spill size - - ;; obviously ebp isn't useful under frame pointer omission - ;; but less obviously esp may be invalid if ebp is good - ;; (e.g. under varargs we may not have known how many arguments to - ;; pop; this is one reason why varargs turns off frame pointer omission) - test eax, 2h - jne esp_is_good - ;; ebp_is_good - add edx, ebp - mov esp, edx - jmp esp_is_setup -esp_is_good: - ;; pop spill slots - add esp, edx - -esp_is_setup: - - ;; restore callee-saves and pop values from stack - ;; (excludes ebp if used as the frame pointer) - test eax, 4h - je skip_edi_restore - ;; restore edi - pop edi -skip_edi_restore: - test eax, 8h - je skip_esi_restore - ;; restore esi - pop esi -skip_esi_restore: - test eax, 10h - je skip_ebp_restore - ;; restore ebp - pop ebp -skip_ebp_restore: - test eax, 20h - je skip_ebx_restore - ;; restore ebx - pop ebx -skip_ebx_restore: - - test eax, 40h - je skip_jump_transition_record - ;; jump over transition record - add esp, (SIZE Struct_System_GCs_CallStack_TransitionRecord) -skip_jump_transition_record: - - ;; restore ebp if it was used as the frame pointer - test eax, 2h - jne skip_frame_pointer_restore - ;; restore frame pointer (esp == ebp already) - pop ebp -skip_frame_pointer_restore: - - ;; set edx=return address - pop edx - - ;; pop arguments - shr eax, 16 - add esp, eax - - ;; At this point - ;; ecx=exception, edx=return address - ;; esi/edi/ebx/ebp/esp have been restored - ;; eax is scratch - - ;; set up next handler search - jmp __throwDispatcherExplicitAddrAfter -__throwDispatcherUnwind endp - -; -; __throwDivideByZeroException: instantiate an divide-by-zero exception -; and throw it. -; -; Assumes edx points to the address after the one that threw. -; - -align 16 -__throwDivideByZeroException proc - push ebx - push esi - mov ebx,edx ; save address -if SINGLE_THREADED - inc ?c_allocationInhibitGC@Class_System_GC@@2_NA -else ; SINGLE_THREADED - lock inc ?c_allocationInhibitGC@Class_System_GC@@2_NA -endif ; SINGLE_THREADED - mov ecx,offset ??_7System_DivideByZeroException@@6B@ - call ?g_AllocateObject@Class_System_GC@@SIPAUClass_System_Object@@PAUClass_System_VTable@@@Z - mov esi,eax ; save pointer to instance of exception - mov ecx,eax ; initialize instance - call ?m__ctor@Class_System_DivideByZeroException@@SIXPAU1@@Z -if SINGLE_THREADED - dec ?c_allocationInhibitGC@Class_System_GC@@2_NA -else ; SINGLE_THREADED - lock dec ?c_allocationInhibitGC@Class_System_GC@@2_NA -endif ; SINGLE_THREADED - mov ecx,esi - mov edx,ebx - pop esi - pop ebx - jmp __throwDispatcherExplicitAddr -__throwDivideByZeroException endp - -; -; __throwStackOverflowException: instantiate an StackOverflow exception -; and throw it. -; -; Assumes edx points to the address of the instruction that faulted -; - -align 16 -__throwStackOverflowException proc - push ebx - push esi - mov ebx,edx ; save address -if SINGLE_THREADED - inc ?c_allocationInhibitGC@Class_System_GC@@2_NA -else ; SINGLE_THREADED - lock inc ?c_allocationInhibitGC@Class_System_GC@@2_NA -endif ; MULTI_THREADED - mov ecx,offset ??_7System_StackOverflowException@@6B@ - call ?g_AllocateObject@Class_System_GC@@SIPAUClass_System_Object@@PAUClass_System_VTable@@@Z - mov esi,eax ; save pointer to instance of exception - mov ecx,eax ; initialize instance - call ?m__ctor@Class_System_StackOverflowException@@SIXPAU1@@Z -if SINGLE_THREADED - dec ?c_allocationInhibitGC@Class_System_GC@@2_NA -else ; SINGLE_THREADED - lock dec ?c_allocationInhibitGC@Class_System_GC@@2_NA -endif ; MULTI_THREADED - mov ecx,esi - mov edx,ebx - call ?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z -ifndef SINGULARITY - push esi - push edx - call ?ResetGuardPage@@YIXXZ - pop edx - pop eax -endif - pop esi - pop ebx - mov ecx,eax -; mov edx is already ok - jmp edx -__throwStackOverflowException endp - -; -; __throwNullReferenceException: instantiate an NullReference exception -; and throw it. -; -; Assumes edx points to the address of the instruction that faulted -; - -align 16 -__throwNullReferenceException proc - push ebx - push esi - mov ebx,edx ; save address -if SINGLE_THREADED - inc ?c_allocationInhibitGC@Class_System_GC@@2_NA -else ; SINGLE_THREADED - lock inc ?c_allocationInhibitGC@Class_System_GC@@2_NA -endif ; SINGLE_THREADED - mov ecx,offset ??_7System_NullReferenceException@@6B@ - call ?g_AllocateObject@Class_System_GC@@SIPAUClass_System_Object@@PAUClass_System_VTable@@@Z - mov esi,eax ; save pointer to instance of exception - mov ecx,eax ; initialize instance - call ?m__ctor@Class_System_NullReferenceException@@SIXPAU1@@Z -if SINGLE_THREADED - dec ?c_allocationInhibitGC@Class_System_GC@@2_NA -else ; SINGLE_THREADED - lock dec ?c_allocationInhibitGC@Class_System_GC@@2_NA -endif ; SINGLE_THREADED - mov ecx,esi - mov edx,ebx - pop esi - pop ebx - jmp __throwDispatcherExplicitAddr -__throwNullReferenceException endp - -; -; __throwDivideByZeroException: instantiate an divide-by-zero exception -; and throw it. -; -; Assumes edx points to the address of the instruction that faulted -; - -align 16 -__throwOverflowException proc - push ebx - push esi - mov ebx,edx ; save address -if SINGLE_THREADED - inc ?c_allocationInhibitGC@Class_System_GC@@2_NA -else ; SINGLE_THREADED - lock inc ?c_allocationInhibitGC@Class_System_GC@@2_NA -endif ; SINGLE_THREADED - mov ecx,offset ??_7System_OverflowException@@6B@ - call ?g_AllocateObject@Class_System_GC@@SIPAUClass_System_Object@@PAUClass_System_VTable@@@Z - mov esi,eax ; save pointer to instance of exception - mov ecx,eax ; initialize instance - call ?m__ctor@Class_System_OverflowException@@SIXPAU1@@Z -if SINGLE_THREADED - dec ?c_allocationInhibitGC@Class_System_GC@@2_NA -else ; SINGLE_THREADED - lock dec ?c_allocationInhibitGC@Class_System_GC@@2_NA -endif ; SINGLE_THREADED - mov ecx,esi - mov edx,ebx - pop esi - pop ebx - jmp __throwDispatcherExplicitAddr -__throwOverflowException endp - -ifdef SINGULARITY_KERNEL -; -; void Thread.setStopContext(Thread t, Exception exn) -; - -align 16 -?g_setStopContext@Class_System_Threading_Thread@@SIXPAU1@PAUClass_System_Exception@@@Z proc - ; ecx = ecx.context - add ecx, Class_System_Threading_Thread._context - ; context.eip = __throwBeyondMarker - ; context.ecx = processStopException - ; context.eax = context.stackMarkers - mov [ecx].Struct_Microsoft_Singularity_X86_ThreadContext._eip, __throwBeyondMarker - mov [ecx].Struct_Microsoft_Singularity_X86_ThreadContext._ecx, edx - mov eax, [ecx].Struct_Microsoft_Singularity_X86_ThreadContext._stackMarkers - mov [ecx].Struct_Microsoft_Singularity_X86_ThreadContext._eax, eax - ret -?g_setStopContext@Class_System_Threading_Thread@@SIXPAU1@PAUClass_System_Exception@@@Z endp - -endif ; SINGULARITY_KERNEL - -; -; int System.VTable.doubleToInt(double) -; - -align 16 -?g_doubleToInt@Class_System_VTable@@SIHN@Z proc - push ebp - mov ebp,esp - add esp,-8 - fld real8 ptr [ebp+8] - wait - fnstcw word ptr [ebp-2] - wait - mov ax,word ptr [ebp-2] - or eax,0C00h - mov word ptr [ebp-4],ax - fldcw word ptr [ebp-4] - fistp dword ptr [ebp-8] - fldcw word ptr [ebp-2] - mov eax,dword ptr [ebp-8] - - cmp eax,080000000h - je possible_overflow -return: - mov esp,ebp - pop ebp - ret 8 - -possible_overflow: - fld real8 ptr [ebp+8] - fcomp real8 ptr $DBLMAXINT - fnstsw ax - test ah,4 - jne short return_zero - test ah,1 - jne short return_MININT - -return_MAXINT: - mov eax, 07fffffffh - jmp short return - -return_zero: - xor eax, eax - jmp short return - -return_MININT: - mov eax, 080000000h - jmp short return - -?g_doubleToInt@Class_System_VTable@@SIHN@Z endp - -; -; long System.VTable.doubleToLong(double) -; - -align 16 -?g_doubleToLong@Class_System_VTable@@SI_JN@Z proc - push ebp - mov ebp,esp - add esp,-12 - fld real8 ptr [ebp+8] - wait - fnstcw word ptr [ebp-2] - wait - mov ax,word ptr [ebp-2] - or eax,0C00h - mov word ptr [ebp-4],ax - fldcw word ptr [ebp-4] - fistp qword ptr [ebp-12] - fldcw word ptr [ebp-2] - mov eax,dword ptr [ebp-12] - mov edx,dword ptr [ebp-8] - - cmp edx,080000000h - je possible_overflow -return: - mov esp,ebp - pop ebp - ret 8 - -possible_overflow: - mov edx,eax ; save lsw - fld real8 ptr [ebp+8] - fild qword ptr $MAXLONG - fcompp - fnstsw ax - test ah,4 - jne short return_zero - test ah,65 - je short check_MINLONG - -return_MAXLONG: - mov eax, 0ffffffffh - mov edx, 07fffffffh - jmp short return - -return_zero: - xor eax, eax - xor edx, edx - jmp short return - -check_MINLONG: - fld real8 ptr [ebp+8] - fild qword ptr $MINLONG - fcompp - fnstsw ax - test ah,1 - jne short return_original - -return_MINLONG: - xor edx, edx ; zero lsw - -return_original: - mov eax, edx ; restore lsw to eax - mov edx, 080000000h - jmp short return - -?g_doubleToLong@Class_System_VTable@@SI_JN@Z endp - -; -; int System.VTable.floatToInt(float) -; - -align 16 -?g_floatToInt@Class_System_VTable@@SIHM@Z proc - push ebp - mov ebp,esp - add esp,-8 - fld real4 ptr [ebp+8] - wait - fnstcw word ptr [ebp-2] - wait - xor eax,eax - mov ax,word ptr [ebp-2] - or eax,0C00h - mov word ptr [ebp-4],ax - fldcw word ptr [ebp-4] - fistp dword ptr [ebp-8] - fldcw word ptr [ebp-2] - mov eax,dword ptr [ebp-8] - - cmp eax,080000000h - je possible_overflow -return: - mov esp,ebp - pop ebp - ret 4 - -possible_overflow: - fld real4 ptr [ebp+8] - fcomp real8 ptr $DBLMAXINT - fnstsw ax - test ah,4 - jne short return_zero - test ah,1 - jne short return_MININT - mov eax, 07fffffffh - jmp short return - -return_zero: - xor eax, eax - jmp short return - -return_MININT: - mov eax, 080000000h - jmp short return - -?g_floatToInt@Class_System_VTable@@SIHM@Z endp - -; -; long System.VTable.floatToLong(float) -; - -align 16 -?g_floatToLong@Class_System_VTable@@SI_JM@Z proc - push ebp - mov ebp,esp - add esp,-12 - fld real4 ptr [ebp+8] - wait - fnstcw word ptr [ebp-2] - wait - mov ax,word ptr [ebp-2] - or eax,0C00h - mov word ptr [ebp-4],ax - fldcw word ptr [ebp-4] - fistp qword ptr [ebp-12] - fldcw word ptr [ebp-2] - mov eax,dword ptr [ebp-12] - mov edx,dword ptr [ebp-8] - - cmp edx,080000000h - je possible_overflow -return: - mov esp,ebp - pop ebp - ret 4 - -possible_overflow: - mov edx,eax ; save lsw - fld real4 ptr [ebp+8] - fild qword ptr $MAXLONG - fcompp - fnstsw ax - test ah,4 - jne short return_zero - test ah,65 - je short check_MINLONG - -return_MAXLONG: - mov eax, 0ffffffffh - mov edx, 07fffffffh - jmp short return - -return_zero: - xor eax, eax - xor edx, edx - jmp short return - -check_MINLONG: - fld real4 ptr [ebp+8] - fild qword ptr $MINLONG - fcompp - fnstsw ax - test ah,1 - jne short return_original - -return_MINLONG: - xor edx, edx ; zero lsw - -return_original: - mov eax, edx ; restore lsw to eax - mov edx, 080000000h - jmp short return - -?g_floatToLong@Class_System_VTable@@SI_JM@Z endp - -; -; int System.VTable.checkedDoubleToInt(double) -; - -align 16 -?g_checkedDoubleToInt@Class_System_VTable@@SIHN@Z proc - push ebp - mov ebp,esp - add esp,-8 - fld real8 ptr [ebp+8] - wait - fnstcw word ptr [ebp-2] - wait - mov ax,word ptr [ebp-2] - or eax,0C00h - mov word ptr [ebp-4],ax - fldcw word ptr [ebp-4] - fistp dword ptr [ebp-8] - fldcw word ptr [ebp-2] - mov eax,dword ptr [ebp-8] - - cmp eax,080000000h - je possible_overflow -return: - mov esp,ebp - pop ebp - ret 8 - -possible_overflow: - fld real8 ptr [ebp+8] - fcomp real8 ptr $DBLMAXINT - fnstsw ax - test ah,4 ; test for unordered - jne short throw_exception - test ah,1 ; test for <$DBLMAXINT - jne short return_MININT - ; src > $DBLMAXINT - ; throw an overflow exception - jmp short throw_exception - -return_MININT: - ; check against $DBLMININT - fld real8 ptr [ebp+8] - fcomp real8 ptr $DBLMININT - fnstsw ax - test ah, 1 ; test for < $DBLMININT - jne short throw_exception ; throw exception if true - mov eax, 080000000h - jmp short return - -throw_exception: - ; throw an overflow exception - ; set up stack frame so that it looks like a call to throwNewOverflowException - ; from the caller of this function. - mov esp,ebp - pop ebp - pop eax ; grab return address - add esp, 4 ; move esp pass the first parameter. - mov [esp],eax ; overwrite argument - jmp ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ - -?g_checkedDoubleToInt@Class_System_VTable@@SIHN@Z endp - -; -; long System.VTable.checkedDoubleToLong(double) -; - -align 16 -?g_checkedDoubleToLong@Class_System_VTable@@SI_JN@Z proc - push ebp - mov ebp,esp - add esp,-12 - fld real8 ptr [ebp+8] - wait - fnstcw word ptr [ebp-2] - wait - mov ax,word ptr [ebp-2] - or eax,0C00h - mov word ptr [ebp-4],ax - fldcw word ptr [ebp-4] - fistp qword ptr [ebp-12] - fldcw word ptr [ebp-2] - mov eax,dword ptr [ebp-12] - mov edx,dword ptr [ebp-8] - - cmp edx,080000000h - je possible_overflow -return: - mov esp,ebp - pop ebp - ret 8 - -possible_overflow: - mov edx,eax ; save lsw - fld real8 ptr [ebp+8] - fild qword ptr $MAXLONG - fcompp - fnstsw ax - test ah,4 ; test for unordered - jne short return_zero - test ah,65 ; test for <= $MAXLONG - je short check_MINLONG - -return_MAXLONG: - ; src > $MAXLONG - ; throw an exception - jmp short throw_exception - -return_zero: - jmp short throw_exception - -check_MINLONG: - ; src <= $MINLONG - fild qword ptr $MINLONG - fld real8 ptr [ebp+8] - fcompp ; real8 ptr [ebp+8] < $MINLONG - fnstsw ax - test ah,1 - jne short throw_exception - -return_MINLONG: - mov eax, edx ; restore lsw to eax - mov edx, 080000000h - jmp short return - - -throw_exception: - ; throw an overflow exception - ; set up stack frame so that it looks like a call to throwNewOverflowException - ; from the caller of this function. - mov esp,ebp - pop ebp - pop eax ; grab return address - add esp, 4 ; move esp pass the first parameter. - mov [esp],eax ; overwrite argument - jmp ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ - -?g_checkedDoubleToLong@Class_System_VTable@@SI_JN@Z endp - -; -; int System.VTable.checkedFloatToInt(float) -; - -align 16 -?g_checkedFloatToInt@Class_System_VTable@@SIHM@Z proc - push ebp - mov ebp,esp - add esp,-8 - fld real4 ptr [ebp+8] - wait - fnstcw word ptr [ebp-2] - wait - xor eax,eax - mov ax,word ptr [ebp-2] - or eax,0C00h - mov word ptr [ebp-4],ax - fldcw word ptr [ebp-4] - fistp dword ptr [ebp-8] - fldcw word ptr [ebp-2] - mov eax,dword ptr [ebp-8] - - cmp eax,080000000h - je possible_overflow - -return: - mov esp,ebp - pop ebp - ret 4 - -possible_overflow: - fld real4 ptr [ebp+8] - fcomp real8 ptr $DBLMAXINT - fnstsw ax - test ah,4 ; test for unordered - jne short throw_exception - test ah,1 ; test for src < $DBLMAXINT - jne short return_MININT - ; src > $DBLMAXINT - ; throw an overflow exception - jmp short throw_exception - -return_MININT: - ; need to check against $DBLMININT, if it is less than, - ; then throw an overflow exception - fld real4 ptr [ebp+8] - fcomp real8 ptr $DBLMININT - fnstsw ax - test ah,1 ; test for less than - jne short throw_exception - mov eax, 080000000h - jmp short return - -throw_exception: - ; throw an overflow exception - ; set up stack frame so that it looks like a call to throwNewOverflowException - ; from the caller of this function. - mov esp,ebp - pop ebp - pop eax ; grab return address - mov [esp],eax ; overwrite argument - jmp ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ - -?g_checkedFloatToInt@Class_System_VTable@@SIHM@Z endp - -; -; long System.VTable.checkedFloatToLong(float) -; - -align 16 -?g_checkedFloatToLong@Class_System_VTable@@SI_JM@Z proc - push ebp - mov ebp,esp - add esp,-12 - fld real4 ptr [ebp+8] - wait - fnstcw word ptr [ebp-2] - wait - mov ax,word ptr [ebp-2] - or eax,0C00h - mov word ptr [ebp-4],ax - fldcw word ptr [ebp-4] - fistp qword ptr [ebp-12] - fldcw word ptr [ebp-2] - mov eax,dword ptr [ebp-12] - mov edx,dword ptr [ebp-8] - - cmp edx,080000000h - je possible_overflow -return: - mov esp,ebp - pop ebp - ret 4 - -possible_overflow: - mov edx,eax ; save lsw - fld real4 ptr [ebp+8] - fild qword ptr $MAXLONG - fcompp - fnstsw ax - test ah,4 ; test for unordered - jne short return_zero - test ah,65 ; test for <= $MAXLONG - je short check_MINLONG - -return_MAXLONG: - ; src > $MAXLONG - ; throw an exception - jmp short throw_exception - -return_zero: - ; compare with $MAXLONG results in unordered - ; throw an overflow exception - jmp short throw_exception - -check_MINLONG: - ; src <= $MINLONG - fild qword ptr $MINLONG - fld real4 ptr [ebp+8] - fcompp ; real8 ptr [ebp+8] < $MINLONG - fnstsw ax - test ah,1 - jne short throw_exception ; throw an overflow exception when src < $MINLONG -return_MINLONG: - mov eax, edx ; restore lsw - mov edx, 080000000h - jmp short return - -throw_exception: - ; throw an overflow exception - ; set up stack frame so that it looks like a call to throwNewOverflowException - ; from the caller of this function. - mov esp,ebp - pop ebp - pop eax ; grab return address - mov [esp],eax ; overwrite argument - jmp ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ - -?g_checkedFloatToLong@Class_System_VTable@@SI_JM@Z endp - -align 16 -?g_floatRem@Class_System_VTable@@SIMMM@Z proc - fld real4 ptr [esp+8] - fld real4 ptr [esp+4] -fremloop: - fprem - fstsw ax - fwait - sahf - jp fremloop ; Continue while the FPU status bit C2 is set - ffree st(1) - ret 8 -?g_floatRem@Class_System_VTable@@SIMMM@Z endp - -align 16 -?g_doubleRem@Class_System_VTable@@SINNN@Z proc - fld real8 ptr [esp+12] - fld real8 ptr [esp+4] -fremloop: - fprem - fstsw ax - fwait - sahf - jp fremloop ; Continue while the FPU status bit C2 is set - ffree st(1) - ret 16 -?g_doubleRem@Class_System_VTable@@SINNN@Z endp - - -if EXCLUDED -; -; void __checkFPStackDepth0 -; - -align 16 -__checkFPStackDepth0 proc - push ebp - mov ebp,esp - - push eax - pushfd - - xor eax,eax - - wait - fnstsw ax - wait - - shr eax,11 - and eax,7 - cmp eax,0 - je ok -oops: - int 3 -ok: - - popfd - pop eax - - pop ebp - ret 0 -__checkFPStackDepth0 endp - -; -; void __checkFPStackDepth1 -; - -align 16 -__checkFPStackDepth1 proc - push ebp - mov ebp,esp - - push eax - pushfd - - xor eax,eax - - wait - fnstsw ax - wait - - shr eax,11 - and eax,7 - cmp eax,8-1 - je ok -oops: - int 3 -ok: - - popfd - pop eax - - pop ebp - ret 0 -__checkFPStackDepth1 endp - -; -; void __checkFPStackDepth2 -; - -align 16 -__checkFPStackDepth2 proc - push ebp - mov ebp,esp - - push eax - pushfd - - xor eax,eax - - wait - fnstsw ax - wait - - shr eax,11 - and eax,7 - cmp eax,8-2 - je ok -oops: - int 3 -ok: - - popfd - pop eax - - pop ebp - ret 0 -__checkFPStackDepth2 endp - -; -; void __checkFPStackDepth3 -; - -align 16 -__checkFPStackDepth3 proc - push ebp - mov ebp,esp - - push eax - pushfd - - xor eax,eax - - wait - fnstsw ax - wait - - shr eax,11 - and eax,7 - cmp eax,8-3 - je ok -oops: - int 3 -ok: - - popfd - pop eax - - pop ebp - ret 0 -__checkFPStackDepth3 endp - -; -; void __checkFPStackDepth4 -; - -align 16 -__checkFPStackDepth4 proc - push ebp - mov ebp,esp - - push eax - pushfd - - xor eax,eax - - wait - fnstsw ax - wait - - shr eax,11 - and eax,7 - cmp eax,8-4 - je ok -oops: - int 3 -ok: - - popfd - pop eax - - pop ebp - ret 0 -__checkFPStackDepth4 endp - -; -; void __checkFPStackDepth5 -; - -align 16 -__checkFPStackDepth5 proc - push ebp - mov ebp,esp - - push eax - pushfd - - xor eax,eax - - wait - fnstsw ax - wait - - shr eax,11 - and eax,7 - cmp eax,8-5 - je ok -oops: - int 3 -ok: - - popfd - pop eax - - pop ebp - ret 0 -__checkFPStackDepth5 endp - -; -; void __checkFPStackDepth6 -; - -align 16 -__checkFPStackDepth6 proc - push ebp - mov ebp,esp - - push eax - pushfd - - xor eax,eax - - wait - fnstsw ax - wait - - shr eax,11 - and eax,7 - cmp eax,8-6 - je ok -oops: - int 3 -ok: - - popfd - pop eax - - pop ebp - ret 0 -__checkFPStackDepth6 endp - -; -; void __checkFPStackDepth7 -; - -align 16 -__checkFPStackDepth7 proc - push ebp - mov ebp,esp - - push eax - pushfd - - xor eax,eax - - wait - fnstsw ax - wait - - shr eax,11 - and eax,7 - cmp eax,8-7 - je ok -oops: - int 3 -ok: - - popfd - pop eax - - pop ebp - ret 0 -__checkFPStackDepth7 endp -endif ; EXCLUDED - -;;; -;;; -;;; -;;; - -ifdef SINGULARITY -?g_ZeroPages@Class_System_Buffer@@SIXPAEH@Z proc - ;; ECX = dst - ;; EDX = len (bytes) - - push ebp - mov ebp, esp - pxor mm0, mm0 -next: - movntq [ecx + 0], mm0 - movntq [ecx + 8], mm0 - movntq [ecx + 16], mm0 - movntq [ecx + 24], mm0 - movntq [ecx + 32], mm0 - movntq [ecx + 40], mm0 - movntq [ecx + 48], mm0 - movntq [ecx + 56], mm0 - add ecx, 64 - sub edx, 64 - ja next - - sfence - emms - pop ebp - ret -?g_ZeroPages@Class_System_Buffer@@SIXPAEH@Z endp - -?g_CopyPages@Class_System_Buffer@@SIXPAE0H@Z proc - ;; ECX = dst - ;; EDX = src - ;; [ebp+8] len (bytes) - - push ebp - mov ebp, esp - mov eax, [ebp + 8] - - cmp ecx, edx - js down - - ;; destination is lower than source - add ecx, eax - add edx, eax - sub ecx, 64 - sub edx, 64 - -up: - movq mm0, [edx + 0] - movq mm1, [edx + 8] - movq mm2, [edx + 16] - movq mm3, [edx + 24] - movq mm4, [edx + 32] - movq mm5, [edx + 40] - movq mm6, [edx + 48] - movq mm7, [edx + 56] - movntq [ecx + 0], mm0 - movntq [ecx + 8], mm1 - movntq [ecx + 16], mm2 - movntq [ecx + 24], mm3 - movntq [ecx + 32], mm4 - movntq [ecx + 40], mm5 - movntq [ecx + 48], mm6 - movntq [ecx + 56], mm7 - sub ecx, 64 - sub edx, 64 - sub eax, 64 - ja up - - sfence - emms - pop ebp - ret 4 - - ;; destination is higher than source -down: - movq mm0, [edx + 0] - movq mm1, [edx + 8] - movq mm2, [edx + 16] - movq mm3, [edx + 24] - movq mm4, [edx + 32] - movq mm5, [edx + 40] - movq mm6, [edx + 48] - movq mm7, [edx + 56] - movntq [ecx + 0], mm0 - movntq [ecx + 8], mm1 - movntq [ecx + 16], mm2 - movntq [ecx + 24], mm3 - movntq [ecx + 32], mm4 - movntq [ecx + 40], mm5 - movntq [ecx + 48], mm6 - movntq [ecx + 56], mm7 - add ecx, 64 - add edx, 64 - sub eax, 64 - ja down - - sfence - emms - pop ebp - ret 4 -?g_CopyPages@Class_System_Buffer@@SIXPAE0H@Z endp - -endif - -end diff --git a/base/Kernel/Native/halcpu.asm b/base/Kernel/Native/halcpu.asm deleted file mode 100644 index c55db3b..0000000 --- a/base/Kernel/Native/halcpu.asm +++ /dev/null @@ -1,22 +0,0 @@ -; -; Copyright (c) Microsoft Corporation. All rights reserved. -; - -.686p -.model flat -.code - -assume ds:flat -assume es:flat -assume ss:flat -assume fs:nothing -assume gs:nothing - -include hal.inc - -?GetCurrentProcessorNumber@@YIHXZ proc - mov eax, fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._cpuId] - ret -?GetCurrentProcessorNumber@@YIHXZ endp - -end diff --git a/base/Kernel/Native/halexn.cpp b/base/Kernel/Native/halexn.cpp deleted file mode 100644 index 6622396..0000000 --- a/base/Kernel/Native/halexn.cpp +++ /dev/null @@ -1,431 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// -// halexn.cpp - Singularity/Bartok Exception Handling -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// Runtime support for CIL exceptions -// * searching exception table -// * make machine faults raise CIL exceptions -// - -#if SINGULARITY -# include "hal.h" -#else -# include "brt.h" -# include -#endif - -extern "C" void __cdecl _throwDispatcher(); -extern "C" void __cdecl _throwArithmeticException(); -extern "C" void __cdecl _throwDivideByZeroException(); -extern "C" void __cdecl _throwNullReferenceException(); -extern "C" void __cdecl _throwOverflowException(); -extern "C" void __cdecl _throwStackOverflowException(); - -#ifdef SINGULARITY -extern void DumpStack(UIntPtr ebp); -extern BOOL KdDebuggerNotPresent; -#endif - -// See Bartok\tables\ExceptionTable.cs for a description of ExceptionTableEntry - -////////////////////////////////////////////////////////////////////////////// -// -struct ExceptionTableEntry { - uintptr scopeBaseAddr; - union { - struct { - Class_System_Type *exceptionClass; // low bit is zero - uintptr handlerAddr; - }; - struct { - uintptr frameSetupInfo; // low bit is one - uintptr spillSize; - }; - }; -}; - -struct TableEntry { - ExceptionTableEntry * tableBaseAddr; - ExceptionTableEntry * tableEndAddr; -}; - -extern TableEntry TableBase[1]; -extern TableEntry TableBound[1]; - -// This will break down with 64 bit pointers. We will need to revisit the code -// that uses the ExceptionTableLookupReturn result (__throwDispatcher*). -STATIC_ASSERT(sizeof(uint64) == 2 * sizeof(uintptr)); - -union ExceptionTableLookupReturn { - uint64 qword; - struct { - Class_System_Type *exceptionClass; - uintptr handlerAddr; - }; - struct { - uintptr frameSetupInfo; - uintptr spillSize; - }; -}; - -static uintptr LookupTable(uintptr throwAddr, - ExceptionTableEntry **tableBaseEntry, - ExceptionTableEntry **tableEndEntry) { -#if 0 - printf("LookupTable(throwAddr=%p, tableBase=%p, tableEnd=%p)\n", - throwAddr, tableBaseEntry, tableEndEntry); - printf(" TableBase=%p, TableBound=%p, maxIndex = %d\n", - TableBase, TableBound, TableBound - TableBase); - printf(" callSiteTableCount=%d\n", - Class_System_GCs_CallStack::c_callSiteTableCount); - printf(" codeBaseStartTable=%p\n", - Class_System_GCs_CallStack::c_codeBaseStartTable); - printf(" returnAddressToCallSiteSetNumbers=%p\n", - Class_System_GCs_CallStack::c_returnAddressToCallSiteSetNumbers); - printf(" callSiteSetCount=%p\n", - Class_System_GCs_CallStack::c_callSiteSetCount); -#endif - - // search to find which table to use - int maxIndex = TableBound - TableBase; - uintptr codeBase = (uintptr) -1; - uintptr relCodeAddr = 0; - for (int i = 0; i < maxIndex; i++) { - TableEntry *entry = &TableBase[i]; -#if 0 - printf(" TableBase[%d] base=%p end=%p codeBaseStartTable[]=%p\n", - i, entry->tableBaseAddr, entry->tableEndAddr, - Class_System_GCs_CallStack::c_codeBaseStartTable[i]); -#endif - codeBase = - ((uintptr*)Class_System_GCs_CallStack::c_codeBaseStartTable)[i]; - - if (throwAddr < codeBase) { - continue; - } - relCodeAddr = throwAddr - codeBase; - *tableBaseEntry = entry->tableBaseAddr; - *tableEndEntry = entry->tableEndAddr; - -#if 0 - printf(" relCodeAddr = %p\n", relCodeAddr); - printf(" tableBase scopeBaseAddr=%p class=%p handler=%p\n", - (*tableBaseEntry)->scopeBaseAddr, - (*tableBaseEntry)->exceptionClass, - (*tableBaseEntry)->handlerAddr); - printf(" tableEnd scopeBaseAddr=%p class=%p handler=%p\n", - (*tableEndEntry)->scopeBaseAddr, - (*tableEndEntry)->exceptionClass, - (*tableEndEntry)->handlerAddr); -#endif - - if ((relCodeAddr >= (*tableBaseEntry)->scopeBaseAddr) - && (relCodeAddr <= (*tableEndEntry)->scopeBaseAddr)) { - return codeBase; - } - } - - return (uintptr) -1; - //exit(-2); - //__asm int 3; -} - -#if SINGULARITY -Class_System_VTable * getRealVTable(Class_System_VTable * vt) -{ - return (Class_System_VTable *)((uintptr)vt & (~((uintptr)3))); -} -#endif - -// search an exception table - -////////////////////////////////////////////////////////////////////////////// - -// search an exception table -// - Returns the exception in eax. -// - Returns the handler address in edx. -// OR if the shared unwind handler should be used -// - Returns the frameSetupInfo in eax. -// - Returns the spill area size in edx. - -uint64 __fastcall ExceptionTableLookup(Class_System_Exception *exception, - uintptr throwAddr) { -#if 0 - printf("\n"); - printf("ExceptionTableLookup(exception=%p, vtable=%p, throwAddr=%p)\n", - exception, ((uintptr *)exception)[0], throwAddr); -#endif - -#if SINGULARITY - if (exception->_throwAddress == NULL) { - exception->_throwAddress = throwAddr; - } -#endif - - // search for table using throwAddr - ExceptionTableEntry *baseEntry = NULL; - ExceptionTableEntry *endEntry = NULL; - uintptr codeBase = LookupTable(throwAddr, &baseEntry, &endEntry); - -#if 0 - printf(" codeBase=%p baseEntry=%p endEntry=%p\n", - codeBase, baseEntry, endEntry); -#endif - - if (codeBase == (uintptr) -1) { -#if SINGULARITY_KERNEL - printf("Exception outside of any known code regions!\n"); - if (!KdDebuggerNotPresent) { - __asm int 3; - } - Class_System_VTable::g_TerminateByException(exception); - __asm int 3; -#elif SINGULARITY_PROCESS - Assert("Exception outside of any known code regions!\n"); - Class_System_VTable::g_TerminateByException(exception); - __asm int 3; -#else - Class_System_VTable::g_TerminateByException(exception); - exit(-2); -#endif - } - - // bsearch for throwAddr - int minIndex = 0; - int maxIndex = endEntry-baseEntry; - throwAddr -= codeBase; - - if (throwAddr < baseEntry[minIndex].scopeBaseAddr || - throwAddr > baseEntry[maxIndex].scopeBaseAddr) { - // BUGBUG: callback to C# code that may trigger GC -#if SINGULARITY_KERNEL - printf("Exception outside of known code region for %p\n", codeBase); - if (!KdDebuggerNotPresent) { - __asm int 3; - } - Class_System_VTable::g_TerminateByException(exception); - printf("top-level exception handling code failed\n"); - __asm int 3; -#elif SINGULARITY_PROCESS - Assert("Exception outside of known code region for.\n"); - Class_System_VTable::g_TerminateByException(exception); - Assert("top-level exception handling code failed\n"); - __asm int 3; -#else - Class_System_VTable::g_TerminateByException(exception); - fprintf(stderr, "top-level exception handling code failed"); - fflush(stderr); - __asm int 3; - exit(-2); -#endif - } - while (minIndex+1 < maxIndex) { - int midIndex = (minIndex+maxIndex)/2; - uintptr midAddr = baseEntry[midIndex].scopeBaseAddr; - if (throwAddr < midAddr) { - maxIndex = midIndex; - } - else { - minIndex = midIndex; - } - } - ExceptionTableEntry *entry = &baseEntry[minIndex]; - - // back up to first entry containing throwAddr (there may be several) - uintptr baseAddr; - for (baseAddr = entry->scopeBaseAddr; - entry->scopeBaseAddr == baseAddr && entry >= baseEntry; - entry--) { - continue; - } - - // check each of the handlers in turn - - for (entry++; entry->scopeBaseAddr <= throwAddr; entry++) { -#if 0 - printf(" entry=%p[%d] " - "scopeBaseAddr=%p exceptionClass=%p handler=%p\n", - entry, entry - baseEntry, - entry->scopeBaseAddr, entry->exceptionClass, entry->handlerAddr); -#endif - - // 0 now means "no frame pointer omission and no callee save registers - // have been saved to the stack": - // Assert(entry->exceptionClass); - - Assert(((entry->frameSetupInfo & 0x1) != 0) - || (entry->handlerAddr != NULL)); - if (((entry->frameSetupInfo & 0x1) != 0) - || Class_System_VTable::g_IsExceptionHandler(entry->exceptionClass, - exception)) { -#if 0 - printf("Found matching exception entry: %p\n", entry); -#endif - break; - } - } - Assert(entry->scopeBaseAddr == baseAddr); - - ExceptionTableLookupReturn retval; - - if((entry->frameSetupInfo & 0x1) != 0) { - retval.frameSetupInfo = entry->frameSetupInfo; - retval.spillSize = entry->spillSize; -#if SINGULARITY - Class_Microsoft_Singularity_Tracing::g_Log - (9, "Throw {0} from {1:x8} to shared unwind handler", - getRealVTable(exception->postHeader.vtableObject)->vtableType->name, - (UIntPtr)(codeBase + throwAddr)); -#endif - } - else { - retval.exceptionClass = entry->exceptionClass; - retval.handlerAddr = entry->handlerAddr + codeBase; -#if SINGULARITY - Class_Microsoft_Singularity_Tracing::g_Log - (9, "Throw {0} from {1:x8} to {2:x8}", - getRealVTable(exception->postHeader.vtableObject)->vtableType->name, - (UIntPtr)(codeBase + throwAddr), - (UIntPtr)(retval.handlerAddr)); -#endif - } - -#if SINGULARITY_KERNEL || SINGULARITY_PROCESS - if (!exception->_notifiedDebugger) { - exception->_notifiedDebugger = true; - - bool iflag = - Class_Microsoft_Singularity_Processor::g_DisableInterrupts(); - Class_System_VTable * vtable = getRealVTable(exception->postHeader.vtableObject); - if((entry->frameSetupInfo & 0x1) != 0) { - Class_Microsoft_Singularity_Tracing::g_Log - (2, "First chance {0} at {1:x8}. Handler is shared", - vtable->vtableType->name, - (UIntPtr)(codeBase + throwAddr)); - } - else { - Class_Microsoft_Singularity_Tracing::g_Log - (2, "First chance {0} at {1:x8}. Handler is {2:x8}", - vtable->vtableType->name, - (UIntPtr)(codeBase + throwAddr), - (UIntPtr)retval.handlerAddr); - } - if (exception->_message != NULL) { - Class_Microsoft_Singularity_Tracing::g_Log - (0, " Message: {0}", exception->_message, 0); - } - - if (!KdDebuggerNotPresent) { - KdDebugTrapData trapData, *trapDataPtr = &trapData; - trapData.tag = KdDebugTrapData::FIRST_CHANCE_EXCEPTION; - trapData.u.firstChanceException.throwAddr = throwAddr; - __asm { - // int 3; // uncomment this line to cause every exception to break here. - mov eax, exception; - mov fs:[0]Struct_Microsoft_Singularity_X86_ProcessorContext.exception, eax; - mov eax, trapDataPtr; - int 29; // Notify debugging stub of first chance exception. - } - } - Class_Microsoft_Singularity_Processor::g_RestoreInterrupts(iflag); - } -#endif - - return(retval.qword); -} - -#if !SINGULARITY - -void alterContinuation(PCONTEXT contextRecord, DWORD newPC) { - // assign edx location of instruction that faulted - contextRecord->Edx = contextRecord->Eip; - contextRecord->Eip = (DWORD) newPC; -} - -// BartokMachineFaultFilter: -// This filter is invoked when when a machine fault occurs. If the -// machine fault occurred in MSIL code, it modifies the context so -// that the appropriate exception is thrown when the filter returns. -// - -LONG WINAPI BartokMachineFaultFilter(struct _EXCEPTION_POINTERS *exnInfo) { - PEXCEPTION_RECORD exceptionRecord = exnInfo->ExceptionRecord; - PCONTEXT contextRecord = exnInfo->ContextRecord; - -#if 0 - printf("BartokMachineFaultFilter(exnInfo=%p)\n", exnInfo); -#endif - - // check if it is an exception in MSIL code. - - // points at instruction that faulted - uintptr throwAddr = contextRecord->Eip; - - // search for table using throwAddr - ExceptionTableEntry *baseEntry = NULL; - ExceptionTableEntry *endEntry = NULL; - uintptr codeBase = LookupTable(throwAddr, &baseEntry, &endEntry); - - int minIndex = 0; - int maxIndex = endEntry-baseEntry; - throwAddr -= codeBase; - - if (throwAddr < baseEntry[minIndex].scopeBaseAddr || - throwAddr > baseEntry[maxIndex].scopeBaseAddr) { - // nope, it isn't. - return EXCEPTION_CONTINUE_SEARCH; - } - - switch (exceptionRecord->ExceptionCode) { - case EXCEPTION_FLT_DIVIDE_BY_ZERO: - case EXCEPTION_INT_DIVIDE_BY_ZERO: { - alterContinuation(contextRecord, - (DWORD) _throwDivideByZeroException); - return EXCEPTION_CONTINUE_EXECUTION; - } - case EXCEPTION_INT_OVERFLOW: { - alterContinuation(contextRecord,(DWORD) _throwOverflowException); - return EXCEPTION_CONTINUE_EXECUTION; - } - case EXCEPTION_STACK_OVERFLOW: { - alterContinuation(contextRecord, - (DWORD) _throwStackOverflowException); - return EXCEPTION_CONTINUE_EXECUTION; - } - case EXCEPTION_ACCESS_VIOLATION: { - DWORD *exceptionInformation = exceptionRecord->ExceptionInformation; - // BUGBUG: how much memory is protected? NB we must trap negative offsets - // from null because the first access through a null reference may be - // to a header word (e.g. opening the object for read in a memory transaction) - if (exceptionInformation[1] < 4096) { - exceptionInformation[1] >= 0xfffff000) { - alterContinuation(contextRecord, - (DWORD) _throwNullReferenceException); - return EXCEPTION_CONTINUE_EXECUTION; - } - else { - return EXCEPTION_CONTINUE_SEARCH; - } - } - default: { - return EXCEPTION_CONTINUE_SEARCH; - } - } -} - -// If the stack pointer is too close to the bottom of the stack, abort the -// process otherwise reset the guard page. -// This function is called when we handle stack overflow exception. - -void ResetGuardPage(){ - - if (_resetstkoflw() == 0) { - fprintf(stderr, "Reset from stack overflow failed. Abort."); - fflush(stderr); - _exit(-2); - } -} - -#endif // !SINGULARITY diff --git a/base/Kernel/Native/halforgc.asm b/base/Kernel/Native/halforgc.asm deleted file mode 100644 index d17c69d..0000000 --- a/base/Kernel/Native/halforgc.asm +++ /dev/null @@ -1,328 +0,0 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; -; Copyright (c) Microsoft Corporation. All rights reserved. -; - -.686p -.mmx -.xmm -.model flat -.code - -ifdef SINGULARITY -assume ds:flat -assume es:flat -assume ss:flat -assume fs:nothing -assume gs:nothing -endif ; Singularity - -include hal.inc - -PAGE_BITS EQU 12 -MASK_OWNER EQU 03h - -externdef ?g_ssbRecordWriteBarrier@Class_System_VTable@@SIXPAUUntracedPtr_void@@@Z:NEAR -; static void __fastcall VTable.ssbRecordWriteBarrier(void*) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; -; pushStackMark: If a function may be called by C, push a pointer to its -; frame on to a stack at the beginning of the function. -; -; Transition record layout: -; -; (lower addresses) -; -------------------------- -; |Old stack marker record | -; -------------------------- -; |Addr of call instr | -; -------------------------- -; |Bottom of stack frame | -; -------------------------- -; |ebx | -; -------------------------- -; |edi | -; -------------------------- -; |esi | -; -------------------------- -; |ebp | -; -------------------------- -; (higher addresses); -; - -align 16 -__pushStackMark proc - ;; Free up a register - push ecx - ;; Fill the new transition record - lea ecx, [ebp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)] - ;; Stash the callee-save registers - mov [ecx].Struct_System_GCs_CallStack_TransitionRecord._EBX, ebx - mov [ecx].Struct_System_GCs_CallStack_TransitionRecord._EDI, edi - mov [ecx].Struct_System_GCs_CallStack_TransitionRecord._ESI, esi - mov [ecx].Struct_System_GCs_CallStack_TransitionRecord._EBP, ebp - ;; record.callAddr = - mov ebx, [esp+4] - mov [ecx].Struct_System_GCs_CallStack_TransitionRecord._callAddr, ebx - ;; record.stackBottom = - lea ebx, [esp+8] - mov [ecx].Struct_System_GCs_CallStack_TransitionRecord._stackBottom, ebx -ifdef SINGULARITY - ;; Link in new transition record - CurrentThreadContext(ebx) - mov edi, [ebx].Struct_Microsoft_Singularity_X86_ThreadContext._stackMarkers - mov [ecx].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord, edi - ;; The next instruction officially switches modes (process->kernel only). - ;; Make sure that the transition record is complete at this point. - mov [ebx].Struct_Microsoft_Singularity_X86_ThreadContext._stackMarkers, ecx - ;; We are now officially in a different mode. - ;; Allow the GC to work while this thread is out - mov ecx, Class_System_GCs_Transitions_DormantState + Class_System_GCs_Transitions_OtherMutatorState - mov eax, Class_System_GCs_Transitions_MutatorState + Class_System_GCs_Transitions_OtherDormantState - lock cmpxchg [ebx].Struct_Microsoft_Singularity_X86_ThreadContext._gcStates, ecx - jz pushFastPath - ;; Transitions.LeaveManagedSpace(threadContext) - mov ecx, ebx - push eax - push edx - call ?g_LeaveManagedSpace@Class_System_GCs_Transitions@@SIXPAUStruct_Microsoft_Singularity_X86_ThreadContext@@@Z - pop edx - pop eax -pushFastPath: -else ; SINGULARITY - ;; Link in new transition record - CurrentThread(ebx) - mov edi, [ebx].Class_System_Threading_Thread._asmStackMarker - mov [ecx].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord, edi - mov [ebx].Class_System_Threading_Thread._asmStackMarker, ecx - ;; Allow the GC to work while this thread is out - mov ecx, ebx - push eax - push edx - call ?g_LeaveManagedSpace@Class_System_GCs_Transitions@@SIXPAUClass_System_Threading_Thread@@@Z - pop edx - pop eax -endif ; SINGULARITY - pop ecx - ret -__pushStackMark endp - -; -; popStackMark: pop the pointer before returning from the function -; -align 16 -__popStackMark proc - ;; Preserve caller-save registers - push eax -ifdef SINGULARITY - CurrentThreadContext(ebx) - ;; NOTE: replacing the following two instructions with a "test" - ;; instruction has a 1-cycle penalty! - mov edi, [ebx].Struct_Microsoft_Singularity_X86_ThreadContext._gcStates - and edi, Class_System_GCs_Transitions_MutatorState - jnz popFastPath - push ecx - push edx - mov ecx, ebx - call ?g_ReturnToManagedSpace@Class_System_GCs_Transitions@@SIXPAUStruct_Microsoft_Singularity_X86_ThreadContext@@@Z - pop edx - pop ecx -popFastPath: -ifndef SINGULARITY_KERNEL - cmp byte ptr [ebx].Struct_Microsoft_Singularity_X86_ThreadContext._suspendAlert, 0 - je noAlert - push ecx - push edx - call ?g_SuspendBarrier@Struct_Microsoft_Singularity_V1_Processes_ProcessHandle@@SIXXZ - pop edx - pop ecx -noAlert: -endif ; SINGULARITY_KERNEL - ;; Unlink transition record - lea edi, [ebp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)] - mov esi, [edi].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord - ;; The next instruction officially switches modes (process<-kernel only). - mov [ebx].Struct_Microsoft_Singularity_X86_ThreadContext._stackMarkers, esi - ;; We are now officially in a different mode. -ifdef SINGULARITY_KERNEL - ;; Assert(ebx == current context) - ;; Assert(edi == transition record in caller's frame) - ;; If we're returning to the kernel from a process that threw an - ;; exception, throw a new kernel exception: - cmp byte ptr [ebx].Struct_Microsoft_Singularity_X86_ThreadContext._uncaughtFlag, 0 - je noProcessUncaughtException - add esp, 8 ; discard eax, retaddr - ;; push TransitionRecord.callAddr as the new return address - push [edi].Struct_System_GCs_CallStack_TransitionRecord._callAddr - ;; Restore callee-save registers - mov ebx, [edi].Struct_System_GCs_CallStack_TransitionRecord._EBX - mov esi, [edi].Struct_System_GCs_CallStack_TransitionRecord._ESI - mov ebp, [edi].Struct_System_GCs_CallStack_TransitionRecord._EBP - mov edi, [edi].Struct_System_GCs_CallStack_TransitionRecord._EDI - ;; (We have to jump to the code; a "call" would push a return address - ;; that doesn't exist in the exception tables.) - jmp ?g_ThrowProcessUncaughtException@Class_Microsoft_Singularity_ProcessUncaughtException@@SIXXZ -noProcessUncaughtException: -endif ; SINGULARITY_KERNEL -else ; SINGULARITY - push ecx - push edx - CurrentThreadIndex(ecx) ; get current thread - call ?g_ReturnToManagedSpace@Class_System_GCs_Transitions@@SIPAUClass_System_Threading_Thread@@H@Z - pop edx - pop ecx - ;; Unlink transition record - lea edi, [ebp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)] - mov esi, [edi].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord - mov [eax].Class_System_Threading_Thread._asmStackMarker, esi -endif ; SINGULARITY - ;; Restore callee-save registers - mov ebx, [edi].Struct_System_GCs_CallStack_TransitionRecord._EBX - mov esi, [edi].Struct_System_GCs_CallStack_TransitionRecord._ESI - mov ebp, [edi].Struct_System_GCs_CallStack_TransitionRecord._EBP - mov edi, [edi].Struct_System_GCs_CallStack_TransitionRecord._EDI - ;; Restore caller-save registers and return - pop eax - ret -__popStackMark endp - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; -; void CollectBodyTransition(Thread thread, int generation): -; Save the callee-save registers in a transition record and then call -; System.GC.CollectBody(thread, generation) -; - -; -; Note: If you modify the amount of stack that this function uses, -; you MUST make update the StackBound attribute for -; System.GC.CollectBodyTransition. -; -; It is conservatively set to 128 bytes. This includes the frame size -; for the function + 88 bytes for a possible linked stack call in -; CollectBody - -align 16 -?g_CollectBodyTransition@Class_System_GC@@SIXPAUClass_System_Threading_Thread@@H@Z proc -; static void __fastcall GC.CollectBodyTransition(Thread, int) - ;; Prologue - push ebp - mov ebp, esp - sub esp, SIZE Struct_System_GCs_CallStack_TransitionRecord - ;; Fill the new transition record - mov eax, dword ptr [ebp+4] ; return address of this call - mov dword ptr [ebp-24], eax - lea eax, [ebp+8] ; skip pushed PC and SP - mov dword ptr [ebp-20], eax ; bottom of stack frame - mov dword ptr [ebp-16], ebx ; callee-save registers - mov dword ptr [ebp-12], edi - mov dword ptr [ebp-08], esi - mov eax, dword ptr [ebp] - mov dword ptr [ebp-04], eax ; old ebp value - ;; Link in new transition record - lea edi, dword ptr [ebp-28] ; address of new transition record -ifdef SINGULARITY -ifdef SINGULARITY_KERNEL - mov eax, dword ptr [ecx].Class_System_Threading_Thread._context.Struct_Microsoft_Singularity_X86_ThreadContext._stackMarkers - mov dword ptr [ecx].Class_System_Threading_Thread._context.Struct_Microsoft_Singularity_X86_ThreadContext._stackMarkers, edi -else - mov esi, dword ptr [ecx].Class_System_Threading_Thread._context - mov eax, dword ptr [esi].Struct_Microsoft_Singularity_X86_ThreadContext._stackMarkers - mov dword ptr [esi].Struct_Microsoft_Singularity_X86_ThreadContext._stackMarkers, edi -endif ; SINGULARITY_KERNEL -else ; SINGULARITY - mov eax, dword ptr [ecx].Class_System_Threading_Thread._asmStackMarker - mov dword ptr [ecx].Class_System_Threading_Thread._asmStackMarker, edi -endif ; SINGULARITY - mov dword ptr [edi], eax ; add in old transition record chain - ;; Call "Thread GC.CollectBody(Thread, int)" - call ?g_CollectBody@Class_System_GC@@SIPAUClass_System_Threading_Thread@@PAU2@H@Z - ;; Unlink transition record - mov edi, dword ptr [ebp-28] ; get old transition record chain -ifdef SINGULARITY -ifdef SINGULARITY_KERNEL - mov dword ptr [eax].Class_System_Threading_Thread._context.Struct_Microsoft_Singularity_X86_ThreadContext._stackMarkers, edi -else - mov esi, dword ptr [eax].Class_System_Threading_Thread._context - mov dword ptr [esi].Struct_Microsoft_Singularity_X86_ThreadContext._stackMarkers, edi -endif ; SINGULARITY_KERNEL -else ; SINGULARITY - mov dword ptr [eax].Class_System_Threading_Thread._asmStackMarker, edi -endif ; SINGULARITY - ;; Restore callee-save registers - mov ebx, dword ptr [ebp-16] - mov edi, dword ptr [ebp-12] - mov esi, dword ptr [ebp-08] - ;; Epilogue - ;; (note: because of interrupts, be careful not to ever read below esp) - lea esp, [ebp-04] - pop ebp ; restore old ebp - add esp, 4 ; skip FP - ret -?g_CollectBodyTransition@Class_System_GC@@SIXPAUClass_System_Threading_Thread@@H@Z endp - - -ifdef SINGULARITY_KERNEL -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; -; void Processor.SwitchToThreadContext(ref X86_ThreadContext oldContext, -; ref X86_ThreadContext newContext); -; -; precondition: Scheduler.dispatchLock held -; -align 16 -?g_SwitchToThreadContext@Class_Microsoft_Singularity_Processor@@SIXPAUStruct_Microsoft_Singularity_X86_ThreadContext@@0@Z proc - ;; Prologue - push ebp - mov ebp, esp - ;; TransitionRecord record; - ;; - sub esp, SIZE Struct_System_GCs_CallStack_TransitionRecord + 4 - mov [ebp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)-4], ecx - ;; record.oldTransitionRecord = oldContext.stackMarkers - mov eax, [ecx].Struct_Microsoft_Singularity_X86_ThreadContext._stackMarkers - mov [ebp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord, eax - ;; oldContext.stackMarkers = &record - lea eax, [ebp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)] - mov [ecx].Struct_Microsoft_Singularity_X86_ThreadContext._stackMarkers, eax - ;; record.callAddr = - mov ecx, dword ptr [ebp+4] - mov [eax].Struct_System_GCs_CallStack_TransitionRecord._callAddr, ecx - ;; record.stackBottom = - lea ecx, [ebp+8] ; Skip pushed PC and SP - mov [eax].Struct_System_GCs_CallStack_TransitionRecord._stackBottom, ecx - ;; - mov [eax].Struct_System_GCs_CallStack_TransitionRecord._EBX, ebx - mov [eax].Struct_System_GCs_CallStack_TransitionRecord._EDI, edi - mov [eax].Struct_System_GCs_CallStack_TransitionRecord._ESI, esi - mov ecx, dword ptr [ebp] - mov [eax].Struct_System_GCs_CallStack_TransitionRecord._EBP, ecx - ;; Transitions.SuspendThread(oldContext) - mov ecx, [ebp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)-4] - mov edi, edx ; save newContext away in a callee-save reg - call ?g_SuspendThread@Class_System_GCs_Transitions@@SIXPAUStruct_Microsoft_Singularity_X86_ThreadContext@@@Z - ;; Processor.SwitchToThreadContextNoGC(newContext) - mov ecx, edi - call ?g_SwitchToThreadContextNoGC@Class_Microsoft_Singularity_Processor@@SIXPAUStruct_Microsoft_Singularity_X86_ThreadContext@@@Z - ;; Transitions.ReviveThread(oldContext) - mov ecx, [ebp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)-4] - call ?g_ReviveThread@Class_System_GCs_Transitions@@SIXPAUStruct_Microsoft_Singularity_X86_ThreadContext@@@Z - ;; - lea eax, [ebp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)] - mov ebx, [eax].Struct_System_GCs_CallStack_TransitionRecord._EBX - mov edi, [eax].Struct_System_GCs_CallStack_TransitionRecord._EDI - mov esi, [eax].Struct_System_GCs_CallStack_TransitionRecord._ESI - ;; oldContext.stackMarkers = record.oldTransitionRecord - mov ecx, [ebp-(SIZE Struct_System_GCs_CallStack_TransitionRecord)-4] - mov edx, [eax].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord - mov [ecx].Struct_Microsoft_Singularity_X86_ThreadContext._stackMarkers, edx - ;; Epilogue - mov esp, ebp - pop ebp - ret -?g_SwitchToThreadContext@Class_Microsoft_Singularity_Processor@@SIXPAUStruct_Microsoft_Singularity_X86_ThreadContext@@0@Z endp - -endif ; SINGULARITY_KERNEL - -end diff --git a/base/Kernel/Native/halidt.asm b/base/Kernel/Native/halidt.asm deleted file mode 100644 index e4b2408..0000000 --- a/base/Kernel/Native/halidt.asm +++ /dev/null @@ -1,1319 +0,0 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; Microsoft Research Singularity -;; -;; Copyright (c) Microsoft Corporation. All rights reserved. -;; -;; File: halidt.asm -;; -;; Note: -;; - -.686p -.mmx -.xmm -.model flat -.code - -assume ds:flat -assume es:flat -assume ss:flat -assume fs:nothing -assume gs:nothing - -include hal.inc - -DEBUG_INTERRUPTS EQU 0 -ifdef DEBUG -SPINLOCK_RELEASE_SANITY_CHECK EQU 1 -else -SPINLOCK_RELEASE_SANITY_CHECK EQU 0 -endif - -ifdef PAGING -public _SysEnter -endif - -externdef ?c_exceptionHandler@@3P6IXHPAUStruct_Microsoft_Singularity_X86_ThreadContext@@@ZA:uintPtr -; static void __fastcall Class.Microsoft.SingularityIoSystem.DispatchException( -; int interrupt [ECX], ref ThreadContex [EDX])) -; __fastcall: -; Arg0 passed in ECX -; Arg1 passed in EDX -; Others passed right to left on stack. -; EBX, ESI, EDI, and EBP are callee saved. -; EAX receives return value if any. - -public _EdtEnter0 -public _EdtEnter1 -public _EdtEnterBody - -externdef ?c_interruptHandler@@3P6IXHPAUStruct_Microsoft_Singularity_X86_ThreadContext@@@ZA:uintPtr -; static void __fastcall Class.Microsoft.SingularityIoSystem.DispatchInterrupt( -; int interrupt [ECX], ref ThreadContex [EDX])) -; __fastcall: -; Arg0 passed in ECX -; Arg1 passed in EDX -; Others passed right to left on stack. -; EBX, ESI, EDI, and EBP are callee saved. -; EAX receives return value if any. - -public _IdtEnter20 -public _IdtEnter21 -public _IdtEnterBody - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; -;;; The IDT_ENTER building macros insure that each IDT target has -;;; an offset of form (_IdtEnter0 or _EdtEnter0) + 0x10 * interrupt_number. -;;; -IDT_ENTER_IN STRUCT 1 - _esi UINT32 ? - _num UINT32 ? - _err UINT32 ? - _eip UINT32 ? - _cs0 UINT32 ? - _efl UINT32 ? -IDT_ENTER_IN ENDS - -ifdef PAGING -IDT_ENTER_IN_LARGE STRUCT 1 - _esi UINT32 ? - _num UINT32 ? - _err UINT32 ? - _eip UINT32 ? - _cs0 UINT32 ? - _efl UINT32 ? - _esp UINT32 ? - _ss UINT32 ? -IDT_ENTER_IN_LARGE ENDS -endif - -IDT_ENTER_OUT STRUCT 1 - _esi UINT32 ? - _eip UINT32 ? - _cs0 UINT32 ? - _efl UINT32 ? -IDT_ENTER_OUT ENDS - -ifdef PAGING -IDT_ENTER_OUT_LARGE STRUCT 1 - _esi UINT32 ? - _eip UINT32 ? - _cs0 UINT32 ? - _efl UINT32 ? - _esp UINT32 ? - _ss UINT32 ? -IDT_ENTER_OUT_LARGE ENDS -endif - -EDT_ENTER_CLEAN MACRO num - push 0 ; No error - push num - jmp _EdtEnterBody - align 16 -ENDM - -EDT_ENTER_ERR MACRO num - push num - jmp _EdtEnterBody - align 16 -ENDM - -IDT_ENTER_CLEAN MACRO num - push 0 ; No error - push num - jmp _IdtEnterBody - align 16 -ENDM - -IDT_SAVE_CONTEXT MACRO fxregs, error, dregs - ;; Save the processor's thread context. - ;; Mark that the context contains caller-saved registers as well. - ;; Input: - ;; ESI = address of ThreadContext structure. - ;; ESP = bottom of IDT_ENTER_IN context: - ;; ESP[ 0] = esi - ;; ESP[ 4] = num - ;; ESP[ 8] = err - ;; ESP[12] = eip - ;; ESP[16] = cs - ;; ESP[20] = efl - - ;; XXX - if we came from ring 3 then stack also has SS and ESP - ;; ESP[24] = esp - ;; ESP[28] = ss - ;; This code needs to check cs&3 and if non-zero save ss:esp to thread context - - ;; Output: - ;; ESI = address of ThreadContext structure. - ;; ESP = stack w/o IDT_ENTER_IN context - ;; ECX = num - ;; - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._eax, eax - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._ebx, ebx - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._ecx, ecx - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._edx, edx - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._ebp, ebp - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._edi, edi - mov eax, cr3 - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._cr3, eax - - mov eax, [esp].IDT_ENTER_IN._esi - mov ebx, [esp].IDT_ENTER_IN._eip - mov ecx, [esp].IDT_ENTER_IN._cs0 - mov edx, [esp].IDT_ENTER_IN._efl - - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._esi, eax - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._eip, ebx - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._cs0, ecx - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._efl, edx - -if dregs - mov eax, dr0 - mov ebx, dr1 - mov ecx, dr2 - mov edx, dr3 - - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr0, eax - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr1, ebx - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr2, ecx - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr3, edx - - mov eax, dr6 - mov ebx, dr7 - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr6, eax - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr7, ebx - - xor eax, eax - mov dr6, eax -endif - - mov ecx, [esp].IDT_ENTER_IN._num ; this flows through - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._num, cx - -if error - mov ebx, [esp].IDT_ENTER_IN._err - mov eax, cr2 - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._err, ebx - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._cr2, eax -endif - -if fxregs -if dregs - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._regs, 3 -else - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._regs, 1 -endif - fxsave [esi].Struct_Microsoft_Singularity_X86_ThreadContext._mmx - fninit - mov eax, 37eh - push eax - fldcw [esp] - pop eax -else -if dregs - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._regs, 2 -else - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._regs, 0 -endif -endif - -ifdef PAGING - ;; Is it a small or large frame? - mov eax, [esp].IDT_ENTER_IN._cs0 - and eax, 3 - jz s_case_0 - - ;; Case ring-3: IDT_ENTER_IN_LARGE - mov eax, [esp].IDT_ENTER_IN_LARGE._esp - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._esp, eax - add esp, SIZEOF IDT_ENTER_IN_LARGE - jmp s_case_0_3 - -s_case_0: -endif - ;; Case ring-0: IDT_ENTER_IN - add esp, SIZEOF IDT_ENTER_IN - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._esp, esp - -ifdef PAGING -s_case_0_3: -endif - -ENDM - -IDT_LOAD_CONTEXT MACRO dregs - ;; Create the outgoing stack frame. -ifdef PAGING - ;; Is it a small or large frame? - mov eax, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._cs0 - and eax, 3 - jz l_case_0 - - ;; Case ring-3: push an IDT_ENTER_OUT_LARGE - sub esp, SIZEOF IDT_ENTER_OUT_LARGE - mov eax, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._esp - mov [esp].IDT_ENTER_OUT_LARGE._esp, eax - mov eax, Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtUD - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull + 3 - mov [esp].IDT_ENTER_OUT_LARGE._ss, eax - mov es, ax - ;; For the moment, share UF and PF: - mov eax, Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtPF - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull + 3 - mov fs, ax - jmp l_cases_0_3 - -l_case_0: -endif ;; PAGING - ;; Case ring-0: push an IDT_ENTER_OUT - mov esp, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._esp - sub esp, SIZEOF IDT_ENTER_OUT - -ifdef PAGING -l_cases_0_3: - ;; Code common to ring-0 and ring-3 -endif ;; PAGING - - ;; Check if we need to restore the floating-point registers - test [esi].Struct_Microsoft_Singularity_X86_ThreadContext._regs, 1 - jz skip_fxrstor - fxrstor [esi].Struct_Microsoft_Singularity_X86_ThreadContext._mmx -skip_fxrstor: - -if dregs - ;; Check if we need to restore the debug registers - test [esi].Struct_Microsoft_Singularity_X86_ThreadContext._regs, 2 - jz skip_drstor - - mov eax, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr0 - mov ebx, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr1 - mov ecx, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr2 - mov edx, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr3 - - mov dr0, eax - mov dr1, ebx - mov dr2, ecx - mov dr3, edx - - mov eax, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr6 - mov ebx, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr7 - - mov dr6, eax - mov dr7, ebx -skip_drstor: -endif - -ifdef PAGING - ;; Zero for cr3 in the ThreadContext means "don't care" (no paging) - mov eax, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._cr3 - cmp eax, 0 - je skip_cr3 - - ;; Avoid TLB flushes if possible - mov ebx, CR3 - cmp eax, ebx - je skip_cr3 - - mov CR3, eax - -skip_cr3: -endif ;; PAGING - - ;; Restore the registers - mov eax, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._esi - mov ebx, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._eip - mov ecx, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._cs0 - mov edx, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._efl - - mov [esp].IDT_ENTER_OUT._esi, eax - mov [esp].IDT_ENTER_OUT._eip, ebx - mov [esp].IDT_ENTER_OUT._cs0, ecx - mov [esp].IDT_ENTER_OUT._efl, edx - - mov eax, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._eax - mov ebx, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._ebx - mov ecx, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._ecx - mov edx, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._edx - mov ebp, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._ebp - mov edi, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._edi - pop esi -ifdef PAGING - push es - pop ds -endif -ENDM - -DBG_EDX_FROM_EAX MACRO edxval, eaxval -if DEBUG_INTERRUPTS - mov edx, edxval - mov eax, eaxval - mov [edx], eax -endif -ENDM - -DBG_SCREEN_AS_VALUE MACRO edxval, eaxval -if DEBUG_INTERRUPTS - push eax - push edx - DBG_EDX_FROM_EAX edxval, eaxval - pop edx - pop eax -endif -ENDM - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; -ifdef DEBUG -?g_TestSave@Class_Microsoft_Singularity_Processor@@SIXPAUStruct_Microsoft_Singularity_X86_ThreadContext@@@Z proc - pop eax - push eax - pushad - - mov ebx, 0fffffeb0h - mov edx, 0fffffed0h - mov ebp, 0fffffeb1h - mov edi, 0fffffed1h - mov esi, 0fffffef1h - - pushfd ; _efl - push cs ; _cs0 - push eax ; _eip - push 0eeeh ; _err - push 0fffh ; _num - push esi ; _esi - - mov esi, ecx - IDT_SAVE_CONTEXT 1,1,1 - - popad - ret -?g_TestSave@Class_Microsoft_Singularity_Processor@@SIXPAUStruct_Microsoft_Singularity_X86_ThreadContext@@@Z endp - -?g_TestSaveLoad@Class_Microsoft_Singularity_Processor@@SIXPAUStruct_Microsoft_Singularity_X86_ThreadContext@@@Z proc - pop eax - - pushfd ; _efl - push cs ; _cs0 - push eax ; _eip - push 0eeeh ; _err - push 0fffh ; _num - push esi ; _esi - - mov esi, ecx - IDT_SAVE_CONTEXT 1,1,1 - IDT_LOAD_CONTEXT 0 - iretd - -?g_TestSaveLoad@Class_Microsoft_Singularity_Processor@@SIXPAUStruct_Microsoft_Singularity_X86_ThreadContext@@@Z endp -endif - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Exceptions. -;;; - align 16 -_EdtEnter proc -_EdtEnter0:: - EDT_ENTER_CLEAN 000h ; #DE Divide-by-Zero -_EdtEnter1:: - EDT_ENTER_CLEAN 001h ; #DB Debug Exception - EDT_ENTER_CLEAN 002h ; NMI Non-Maskable-Interrupt - EDT_ENTER_CLEAN 003h ; #BP Breakpoint - EDT_ENTER_CLEAN 004h ; #OF OVerflow - EDT_ENTER_CLEAN 005h ; #BR Bound-Range - EDT_ENTER_CLEAN 006h ; #UD Invalid Opcode - EDT_ENTER_CLEAN 007h ; #NM Device Not Available - EDT_ENTER_ERR 008h ; #DF Double Fault - EDT_ENTER_CLEAN 009h ; Unused (was x87 segment except) - EDT_ENTER_ERR 00ah ; #TS Invalid TSS - EDT_ENTER_ERR 00bh ; #NP Sgement Not Present - EDT_ENTER_ERR 00ch ; #SS Stack Exception - EDT_ENTER_ERR 00dh ; #GP General Protection - EDT_ENTER_ERR 00eh ; #PF Page Fault - EDT_ENTER_CLEAN 00fh ; Reserved - EDT_ENTER_CLEAN 010h ; #MF x87 Math Error - EDT_ENTER_ERR 011h ; #AC Alignment Check - EDT_ENTER_CLEAN 012h ; #MC Machine Check - EDT_ENTER_CLEAN 013h ; #XF SIMD Exception - EDT_ENTER_CLEAN 014h ; 014h exception - EDT_ENTER_CLEAN 015h ; 015h exception - EDT_ENTER_CLEAN 016h ; 016h exception - EDT_ENTER_CLEAN 017h ; 017h exception - EDT_ENTER_CLEAN 018h ; 018h exception - EDT_ENTER_CLEAN 019h ; 019h exception - EDT_ENTER_CLEAN 01ah ; 01ah exception - EDT_ENTER_CLEAN 01bh ; 01bh exception - EDT_ENTER_CLEAN 01ch ; 01ch exception - EDT_ENTER_CLEAN 01dh ; 01dh exception - EDT_ENTER_CLEAN 01eh ; 01eh exception - EDT_ENTER_CLEAN 01fh ; 01fh exception - -_EdtEnterBody:: - push esi - -ifdef PAGING - push ss - pop ds ; Copy stack segment selector to DS so we can access memory! - push ss - pop es ; Copy stack segment selector to ES so we can access memory! -endif - - DBG_SCREEN_AS_VALUE 0b8020h, 01f301f40h ; @0 - -ifdef PAGING - ; XXX if we've arrived from ring 3 FS will be invalid - push Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtPF - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull - pop fs -endif - - ;; Exceptions spill to the per-processor exceptionContext. - mov esi, fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._processorContext] - lea esi, [esi].Struct_Microsoft_Singularity_X86_ProcessorContext._exceptionContext - - IDT_SAVE_CONTEXT 1,1,1 ; Save fxregs, error codes, and dregs. - - - ;; Link the per-processor exception to the faulting thread - mov edi, fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._threadContext] - mov eax, [edi].Struct_Microsoft_Singularity_X86_ThreadContext.__thread - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext.__thread, eax - - ;; Link the per-processor thread context to the original thread context. - mov [edi].Struct_Microsoft_Singularity_X86_ThreadContext._next, esi - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._prev, edi - - DBG_EDX_FROM_EAX 0b8024h, 01f311f40h ; @1 - - ;; Switch to the exception stack and adjust the stack limit values. - mov esp, fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._exceptionStackBegin] - mov eax, fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._exceptionStackLimit] - mov edx, [edi].Struct_Microsoft_Singularity_X86_ThreadContext._stackLimit - mov [edi].Struct_Microsoft_Singularity_X86_ThreadContext._stackLimit, eax - mov fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._exceptionStackPreLimit], edx - - DBG_EDX_FROM_EAX 0b8028h, 01f321f40h ; @2 - - ;; Call the exception handler. - mov edx, esi - mov eax, [?c_exceptionHandler@@3P6IXHPAUStruct_Microsoft_Singularity_X86_ThreadContext@@@ZA] - call eax - - DBG_EDX_FROM_EAX 0b802ch, 01f331f40h ; @3 - - ;; mov esi, fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._processorContext] - ;; lea esi, [esi].Struct_Microsoft_Singularity_X86_ProcessorContext._exceptionContext - - ;; Restore the stack limit (edi should still be good) - mov eax, fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._exceptionStackPreLimit] - mov [edi].Struct_Microsoft_Singularity_X86_ThreadContext._stackLimit, eax - - ;; Unlink the per-processor context. - mov edi, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._prev - mov [edi].Struct_Microsoft_Singularity_X86_ThreadContext._next, 0 - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._prev, 0 - - IDT_LOAD_CONTEXT 1 - - iretd - -_EdtEnter endp - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Interrupts. -;;; - - align 16 -_IdtEnter proc -_IdtEnter20:: - IDT_ENTER_CLEAN 020h ; 021h: first interrupt -_IdtEnter21:: - _num = 021h ; 021h to 0ffh - WHILE _num LE 0ffh - IDT_ENTER_CLEAN _num - _num = _num + 1 - ENDM - -_IdtEnterBody:: - push esi - -ifdef PAGING - push ss - pop ds ; Copy stack segment selector to DS so we can access memory! - push ss - pop es ; Copy stack segment selector to ES so we can access memory! -endif - - DBG_SCREEN_AS_VALUE 0b8000h, 01f301f40h ; @0 - -ifdef PAGING - ; XXX if we've arrived from ring 3 FS will be invalid - push Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtPF - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull - pop fs -endif - - ;; Interrupts spill to the thread's context. - mov esi, fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._threadContext] - - IDT_SAVE_CONTEXT 1,0,0 ; Save fxregs, but not error codes or dregs. - - - DBG_EDX_FROM_EAX 0b8004h, 01f311f40h ; @1 - - ;; Switch to the interrupt stack and adjust the stack limit values. - mov edi, fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._threadContext] - mov esp, fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._interruptStackBegin] - mov eax, fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._interruptStackLimit] - mov edx, [edi].Struct_Microsoft_Singularity_X86_ThreadContext._stackLimit - mov [edi].Struct_Microsoft_Singularity_X86_ThreadContext._stackLimit, eax - mov fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._interruptStackPreLimit], edx - - DBG_EDX_FROM_EAX 0b8008h, 01f321f40h ; @2 - - ;; Call the interrupt handler. - mov edx, esi - mov eax, [?c_interruptHandler@@3P6IXHPAUStruct_Microsoft_Singularity_X86_ThreadContext@@@ZA] - call eax - - DBG_EDX_FROM_EAX 0b800ch, 01f331f40h ; @3 - - ;; Restore the stack limit (edi should still be good) - mov eax, fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._interruptStackPreLimit] - mov [edi].Struct_Microsoft_Singularity_X86_ThreadContext._stackLimit, eax - - ;; Select the processor's thread context (may have been changed by scheduler). - mov esi, fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._threadContext] - - IDT_LOAD_CONTEXT 0 - - iretd - -_IdtEnter endp - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Syscall via SYSENTER -;;; -ifdef PAGING - -SYSENTER_IN STRUCT 1 - _esi UINT32 ? - _num UINT32 ? -SYSENTER_IN ENDS - -SYSENTER_SAVE_CONTEXT MACRO fxregs, dregs - ;; Save the processor's thread context. - ;; Input: - ;; ESI = address of ThreadContext structure. - ;; EDX = orig ESP - ;; ECX = orig EIP - ;; Output: - ;; ESI = address of ThreadContext structure. - ;; ESP = stack w/o IDT_ENTER_IN context - ;; ECX = num - ;; - - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._eip, ecx - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._esp, edx - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._cs0, Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtUD - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull + 3 - ;mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._ss, 05bh ; XXX no room at the inn - - pushfd - pop edx - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._efl, edx - - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._eax, eax - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._ebx, ebx - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._ecx, ecx - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._edx, edx - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._ebp, ebp - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._edi, edi - - mov eax, [esp].SYSENTER_IN._esi - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._esi, eax - - mov ecx, [esp].SYSENTER_IN._num ; this flows through - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._num, cx - - ;; XXX Need to save segment registers!! - -if dregs - mov eax, dr0 - mov ebx, dr1 - mov ecx, dr2 - mov edx, dr3 - - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr0, eax - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr1, ebx - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr2, ecx - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr3, edx - - mov eax, dr6 - mov ebx, dr7 - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr6, eax - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr7, ebx - - xor eax, eax - mov dr6, eax -endif - -if fxregs -if dregs - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._regs, 3 -else - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._regs, 1 -endif - fxsave [esi].Struct_Microsoft_Singularity_X86_ThreadContext._mmx - fninit - mov eax, 37eh - push eax - fldcw [esp] - pop eax -else -if dregs - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._regs, 2 -else - mov [esi].Struct_Microsoft_Singularity_X86_ThreadContext._regs, 0 -endif -endif - ;; XXX this is wrong for the ring 3 case... - add esp, SIZEOF SYSENTER_IN - -ENDM - -SYSENTER_LOAD_CONTEXT MACRO dregs - ;; Check if we need to restore the floating-point registers - test [esi].Struct_Microsoft_Singularity_X86_ThreadContext._regs, 1 - jz skip_fxrstor - fxrstor [esi].Struct_Microsoft_Singularity_X86_ThreadContext._mmx -skip_fxrstor: - -if dregs - ;; Check if we need to restore the debug registers - test [esi].Struct_Microsoft_Singularity_X86_ThreadContext._regs, 2 - jz skip_drstor - - mov eax, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr0 - mov ebx, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr1 - mov ecx, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr2 - mov edx, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr3 - - mov dr0, eax - mov dr1, ebx - mov dr2, ecx - mov dr3, edx - - mov eax, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr6 - mov ebx, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._dr7 - - mov dr6, eax - mov dr7, ebx -skip_drstor: -endif - - ;; Restore the registers - mov eax, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._eax - mov ebx, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._ebx - mov ecx, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._ecx ; pointless - mov edx, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._edx ; pointless - mov ebp, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._ebp - mov edi, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._edi - - ;; We restore EIP into edx and ESP into ECX - mov ecx, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._esp - mov edx, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._eip - - - ;; Need to restore segment registers - - ; Restore flags - is this necessary? - push [esi].Struct_Microsoft_Singularity_X86_ThreadContext._efl - mov esi, [esi].Struct_Microsoft_Singularity_X86_ThreadContext._esi - popfd -ENDM - - align 16 -_SysEnter proc - ; SYSENTER doesn't put *anything* on the stack - the only way - ; we can know the return EIP/ESP is if they are passed in registers. - ; On Windows there is only one SYSENTER instruction in ntdll, so sysexit - ; knows where to return to, and the ring 3 stack pointer is passed in edx. - ; - ; Entry state: ss:esp0, cs:eip loaded from processor MSRs - ; - push 0 - push 02Fh - push esi ; This is to make the stack frame look like IDT_ENTER_ expects - - push ss - pop ds ; Copy stack segment selector to DS so we can access memory! - - DBG_SCREEN_AS_VALUE 0b8000h, 01f301f40h ; @0 - - ; XXX if we've arrived from ring 3 FS will be invalid - push Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtPF - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull - pop fs - push ss - pop es ; Copy stack segment selector to ES so we can access memory! - - ;; Interrupts spill to the thread's context. - mov esi, fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._threadContext] - - ;; XXX - Can save some time by not saving caller-save regs - SYSENTER_SAVE_CONTEXT 1,0 ; Save fxregs, but not error codes or dregs. - - DBG_EDX_FROM_EAX 0b8004h, 01f311f40h ; @1 - - ;; Switch to the interrupt stack and adjust the stack limit values. - mov edi, fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._threadContext] - mov esp, fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._interruptStackBegin] - mov eax, fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._interruptStackLimit] - mov edx, [edi].Struct_Microsoft_Singularity_X86_ThreadContext._stackLimit - mov [edi].Struct_Microsoft_Singularity_X86_ThreadContext._stackLimit, eax - mov fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._interruptStackPreLimit], edx - - DBG_EDX_FROM_EAX 0b8008h, 01f321f40h ; @2 - - ;; Call the interrupt handler. - mov edx, esi - mov eax, [?c_interruptHandler@@3P6IXHPAUStruct_Microsoft_Singularity_X86_ThreadContext@@@ZA] - call eax - - DBG_EDX_FROM_EAX 0b800ch, 01f331f40h ; @3 - - ;; Restore the stack limit (edi should still be good) - mov eax, fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._interruptStackPreLimit] - mov [edi].Struct_Microsoft_Singularity_X86_ThreadContext._stackLimit, eax - - ;; Select the processor's thread context (may have been changed by scheduler). - mov esi, fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._threadContext] - - ; - SYSENTER_LOAD_CONTEXT 0 - - ; SYSEXIT does the following - ; EIP <-- EDX - ; ESP <-- ECX - ; CS <-- MSR[IA32_CS_SYSENTER] + 0x18 - sysexit - -_SysEnter endp - -endif - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; -; void Processor.SwitchToThreadContext(ref X86_ThreadContext newContext); -; Saves the current context and load the new context from -; newContext(ecx), effectively switching contexts between threads. -; Always returns executing in the new context. -; The code after _SwitchedInContextSwitch will only run if the context was -; switch out using this routine. -; -; precondition: Scheduler.dispatchLock held -; -align 16 -?g_SwitchToThreadContextNoGC@Class_Microsoft_Singularity_Processor@@SIXPAUStruct_Microsoft_Singularity_X86_ThreadContext@@@Z proc - ;; Save the old processor context. - - pushfd - - ;; From here on we need interrupts disabled. - cli - - mov edx, fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._threadContext] - - ;; On clean switch, no need to save caller-saved registers (eax,ecx,edx,fxsave) -ifdef PAGING - mov [edx].Struct_Microsoft_Singularity_X86_ThreadContext._cs0, Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtPC - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull -endif - mov [edx].Struct_Microsoft_Singularity_X86_ThreadContext._ebx, ebx - - pop eax ; pop flags from stack. - mov [edx].Struct_Microsoft_Singularity_X86_ThreadContext._efl, eax - - mov [edx].Struct_Microsoft_Singularity_X86_ThreadContext._esp, esp - mov [edx].Struct_Microsoft_Singularity_X86_ThreadContext._ebp, ebp - mov [edx].Struct_Microsoft_Singularity_X86_ThreadContext._esi, esi - mov [edx].Struct_Microsoft_Singularity_X86_ThreadContext._edi, edi -ifdef PAGING - mov eax, CR3 - mov [edx].Struct_Microsoft_Singularity_X86_ThreadContext._cr3, eax -endif - - lea eax, _SwitchedInContextSwitch - mov [edx].Struct_Microsoft_Singularity_X86_ThreadContext._eip, eax - -ifdef THREAD_TIME_ACCOUNTING - ;; thread execution time accouting - ;; Get and save current timestamp - mov ebx, edx ; save edx - rdtsc ; -> edx:eax - - push eax ; save timestamp for later use - push edx - - ;; now - old.lastExecutionTimeUpdate - sub eax, dword ptr [ebx ].Struct_Microsoft_Singularity_X86_ThreadContext._lastExecutionTimeUpdate - sbb edx, dword ptr [ebx + 4].Struct_Microsoft_Singularity_X86_ThreadContext._lastExecutionTimeUpdate - - ;; old.executionTime += now - old.lastExecutionTimeUpdate - add dword ptr [ebx ].Struct_Microsoft_Singularity_X86_ThreadContext._executionTime, eax - adc dword ptr [ebx + 4].Struct_Microsoft_Singularity_X86_ThreadContext._executionTime, edx - - pop edx ; restore timestamp - pop eax - - ;; new.lastExecutionTimeUpdate = now - mov dword ptr [ecx ].Struct_Microsoft_Singularity_X86_ThreadContext._lastExecutionTimeUpdate, eax - mov dword ptr [ecx + 4].Struct_Microsoft_Singularity_X86_ThreadContext._lastExecutionTimeUpdate, edx - - mov edx, ebx ; restore edx - ;; End thread execution time accouting -endif - ;; On clean switch, no need to save caller-saved registers - ; fxsave [edx].Struct_Microsoft_Singularity_X86_ThreadContext._mmx - mov [edx].Struct_Microsoft_Singularity_X86_ThreadContext._regs, 0 - - ;; The old context has been saved, so we can now release the dispatch - ;; lock and let other processors load the old context. - ;; (We've released the old stack pointer and haven't yet - ;; set up a new stack pointer, so the following code - ;; does not touch the stack.) - ;; This code duplicates code from SpinLock.Release. - -if SPINLOCK_RELEASE_SANITY_CHECK - ; if (this.lockWord != 1 || this.lockingThreadIndexPlusOne-1 != currentThreadId) throw ... - cmp ?c_dispatchLock@Class_Microsoft_Singularity_Scheduling_Scheduler@@2?AUStruct_System_Threading_SpinLock@@A.Struct_System_Threading_SpinLock._lockWord, 1 - je spinLockOk1 - int 3 - spinLockOk1: - mov eax, ?c_dispatchLock@Class_Microsoft_Singularity_Scheduling_Scheduler@@2?AUStruct_System_Threading_SpinLock@@A.Struct_System_Threading_SpinLock._lockingThreadIndexPlusOne - sub eax, 1 - cmp [edx].Struct_Microsoft_Singularity_X86_ThreadContext._threadIndex, ax - je spinLockOk2 - int 3 - spinLockOk2: -endif ; SPINLOCK_RELEASE_SANITY_CHECK - ; this.lockingThreadIndexPlusOne = 0; - ; this.lockWord = 0; - mov ?c_dispatchLock@Class_Microsoft_Singularity_Scheduling_Scheduler@@2?AUStruct_System_Threading_SpinLock@@A.Struct_System_Threading_SpinLock._lockingThreadIndexPlusOne, 0 - mov ?c_dispatchLock@Class_Microsoft_Singularity_Scheduling_Scheduler@@2?AUStruct_System_Threading_SpinLock@@A.Struct_System_Threading_SpinLock._lockWord, 0 - - ;; Load the new processor context. - - mov fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._threadContext], ecx - mov esi, ecx - - IDT_LOAD_CONTEXT 0 - - iretd - -_SwitchedInContextSwitch: - ret - -?g_SwitchToThreadContextNoGC@Class_Microsoft_Singularity_Processor@@SIXPAUStruct_Microsoft_Singularity_X86_ThreadContext@@@Z endp - -ifdef DOUBLE_FAULT_HANDLER -align 16 -?CallTaskGate@@YIXGI@Z proc -; -; FAR JMP to double-fault task gate. This is hand-coded, because I can't figure out -; how to make MASM do this for me. -; - db 09Ah ; CALL FAR PTR - dd 0 - dw Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtDFG - Struct_Microsoft_Singularity_BootInfo._Cpu0._GdtNull - - ret -?CallTaskGate@@YIXGI@Z endp - -PRINTAL MACRO - mov ah, 0ah;0ah - mov [edx], ax - add edx, 2 -ENDM - -PRINTC MACRO num - mov al, num - mov ah, 02h;0ah - mov [edx], ax - add edx, 2 -ENDM - -; DoubleFault -; Entered in 32-bit protected mode. -PUBLIC ?DoubleFault@@YIXXZ -?DoubleFault@@YIXXZ PROC NEAR - mov edx, 0b8000h - - PRINTC '*' - PRINTC '*' - PRINTC '2' - PRINTC '*' - PRINTC '*' - PRINTC ' ' - - PRINTC 'e' - PRINTC 'b' - PRINTC 'p' - mov ecx, ebp - call printdw - - PRINTC 'e' - PRINTC 'f' - PRINTC 'l' - pushfd - pop ecx - call printdw - - PRINTC 'e' - PRINTC 'f' - PRINTC 'l' - pushfd - pop ecx -; or ecx, 04000h -; push ecx -; popfd - call printdw - - mov edx, 0b8000h + 160 - - PRINTC 'c' - PRINTC 's' - push cs - pop cx - call printw - - PRINTC 's' - PRINTC 's' - mov cx, ss - call printw - - PRINTC 'd' - PRINTC 's' - mov cx, ds - call printw - - PRINTC 'e' - PRINTC 's' - mov cx, es - call printw - - PRINTC 'f' - PRINTC 's' - mov cx, fs - call printw - - PRINTC 'g' - PRINTC 's' - mov cx, gs - call printw - - PRINTC 't' - PRINTC 'r' - str cx - call printw - - mov edx, 0b8000h + 160 * 2 - - PRINTC 'e' - PRINTC 's' - PRINTC 'p' - mov ecx, esp - call printdw - - PRINTC '+' - PRINTC '0' - mov ecx, [esp] - call printdw - - PRINTC '+' - PRINTC '4' - mov ecx, [esp+4] - call printdw - - PRINTC '+' - PRINTC '8' - mov ecx, [esp+8] - call printdw - - PRINTC '+' - PRINTC '1' - PRINTC '2' - mov ecx, [esp+12] - call printdw - - PRINTC '+' - PRINTC '1' - PRINTC '6' - mov ecx, [esp+16] - call printdw - - mov edx, 0b8000h + 160 * 3 - - PRINTC 'e' - PRINTC 's' - PRINTC 'i' - mov ecx, esi - call printdw - - PRINTC 'p' - PRINTC 't' - mov cx, [esi].Struct_Microsoft_Singularity_X86_TSS._previous_tss - call printw - - PRINTC 'e' - PRINTC 's' - PRINTC 'p' - mov ecx, [esi].Struct_Microsoft_Singularity_X86_TSS._esp - call printdw - - PRINTC 'e' - PRINTC 'i' - PRINTC 'p' - mov ecx, [esi].Struct_Microsoft_Singularity_X86_TSS._eip - call printdw - - PRINTC 'e' - PRINTC 'f' - PRINTC 'l' - mov ecx, [esi].Struct_Microsoft_Singularity_X86_TSS._eflags - call printdw - - PRINTC 'c' - PRINTC 'r' - PRINTC '3' - mov ecx, [esi].Struct_Microsoft_Singularity_X86_TSS._cr3 - call printdw - - PRINTC 'u' - mov cx, [esi].Struct_Microsoft_Singularity_X86_TSS._trap_bit - call printw - - mov edx, 0b8000h + 160 * 4 - - PRINTC 'e' - PRINTC 'd' - PRINTC 'i' - mov ecx, edi - call printdw - - PRINTC 'p' - PRINTC 't' - mov cx, [edi].Struct_Microsoft_Singularity_X86_TSS._previous_tss - call printw - - PRINTC 'e' - PRINTC 's' - PRINTC 'p' - mov ecx, [edi].Struct_Microsoft_Singularity_X86_TSS._esp - call printdw - - PRINTC 'e' - PRINTC 'i' - PRINTC 'p' - mov ecx, [edi].Struct_Microsoft_Singularity_X86_TSS._eip - call printdw - - PRINTC 'e' - PRINTC 'f' - PRINTC 'l' - mov ecx, [edi].Struct_Microsoft_Singularity_X86_TSS._eflags - call printdw - - PRINTC 'c' - PRINTC 'r' - PRINTC '3' - mov ecx, [edi].Struct_Microsoft_Singularity_X86_TSS._cr3 - call printdw - - PRINTC 'u' - mov cx, [edi].Struct_Microsoft_Singularity_X86_TSS._trap_bit - call printw - - mov edx, 0b8000h + 160 * 5 - mov ebp, [esi].Struct_Microsoft_Singularity_X86_TSS._esp - - PRINTC 'e' - PRINTC 's' - PRINTC 'p' - mov ecx, ebp - call printdw - - PRINTC '+' - PRINTC '0' - mov ecx, [ebp] - call printdw - - PRINTC '+' - PRINTC '4' - mov ecx, [ebp+4] - call printdw - - PRINTC '+' - PRINTC '8' - mov ecx, [ebp+8] - call printdw - - PRINTC '+' - PRINTC '1' - PRINTC '2' - mov ecx, [ebp+12] - call printdw - - PRINTC '+' - PRINTC '1' - PRINTC '6' - mov ecx, [ebp+16] - call printdw - - - iretd ; // note, should clear CR0.TS -again: - jmp again - -?DoubleFault@@YIXXZ ENDP - -; Print a DWORD to the screen -; [in] ecx = dword to print -; [in] edx = address of screen -; [use] eax = trashed for temporary -; -printdw PROC NEAR - PRINTC ':' - - mov eax, ecx - shr eax, 28 - and eax, 0fh - add eax, 030h - cmp eax, 03ah - jl print28 - add eax, 7h -print28: - PRINTAL - - mov eax, ecx - shr eax, 24 - and eax, 0fh - add eax, 030h - cmp eax, 03ah - jl print24 - add eax, 7h -print24: - PRINTAL - - mov eax, ecx - shr eax, 20 - and eax, 0fh - add eax, 030h - cmp eax, 03ah - jl print20 - add eax, 7h -print20: - PRINTAL - - mov eax, ecx - shr eax, 16 - and eax, 0fh - add eax, 030h - cmp eax, 03ah - jl print16 - add eax, 7h -print16: - PRINTAL - - mov eax, ecx - shr eax, 12 - and eax, 0fh - add eax, 030h - cmp eax, 03ah - jl print12 - add eax, 7h -print12: - PRINTAL - - mov eax, ecx - shr eax, 08 - and eax, 0fh - add eax, 030h - cmp eax, 03ah - jl print08 - add eax, 7h -print08: - PRINTAL - - mov eax, ecx - shr eax, 04 - and eax, 0fh - add eax, 030h - cmp eax, 03ah - jl print04 - add eax, 7h -print04: - PRINTAL - - mov eax, ecx - shr eax, 00 - and eax, 0fh - add eax, 030h - cmp eax, 03ah - jl print00 - add eax, 7h -print00: - PRINTAL - - PRINTC ' ' - - ret -printdw ENDP - -; Print a WORD to the screen -; [in] ecx = word to print (in low 16-bits, high 16-bits ignored) -; [in] edx = address of screen -; [use] eax = trashed for temporary -; -printw PROC NEAR - PRINTC ':' - - mov eax, ecx - shr eax, 12 - and eax, 0fh - add eax, 030h - cmp eax, 03ah - jl print12 - add eax, 7h -print12: - PRINTAL - - mov eax, ecx - shr eax, 08 - and eax, 0fh - add eax, 030h - cmp eax, 03ah - jl print08 - add eax, 7h -print08: - PRINTAL - - mov eax, ecx - shr eax, 04 - and eax, 0fh - add eax, 030h - cmp eax, 03ah - jl print04 - add eax, 7h -print04: - PRINTAL - - mov eax, ecx - shr eax, 00 - and eax, 0fh - add eax, 030h - cmp eax, 03ah - jl print00 - add eax, 7h -print00: - PRINTAL - - PRINTC ' ' - - ret -printw ENDP -endif ;; DOUBLE_FAULT_HANDLER - -END diff --git a/base/Kernel/Native/halkd.cpp b/base/Kernel/Native/halkd.cpp index 42c6a1e..c456084 100644 --- a/base/Kernel/Native/halkd.cpp +++ b/base/Kernel/Native/halkd.cpp @@ -1,7 +1,10 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // -// halkd.cpp: runtime support for debugging +// File: halkd.cpp - runtime support for kernel debugging // // For more information see: // \nt\base\ntos\kd64 @@ -10,33 +13,34 @@ // \nt\base\boot\kdusb2 // \nt\sdktools\debuggers\ntsd64 // +// Note: Kernel Only +// #include "hal.h" #include "halkd.h" extern "C" void * __cdecl memcpy(void *, const void *, size_t); extern "C" void * __cdecl memset(void *, int, size_t); +#define FALSE 0 +#define TRUE 1 + +#define ASSERT(x) +#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0) + // // Debugger Debugging // #define KDDBG if (0) kdprintf #define KDDBG2 if (0) kdprintf - -#if defined(_IA64_) -#define SIGN_EXTEND_PTR(p) p -#else -#define SIGN_EXTEND_PTR(p) ((ULONG_PTR)(LONG_PTR)(LONG)(p)) -#endif +#define KDDBG3 if (0) kdprintf #define KeProcessorLevel 15 // // Globals // -extern const Struct_Microsoft_Singularity_BootInfo *g_pBootInfo; -static Struct_Microsoft_Singularity_X86_IDTP g_idt; - BOOL KdDebuggerNotPresent = FALSE; +BOOL KdAlwaysPrintOutput = FALSE; #define KDP_MESSAGE_BUFFER_SIZE 4096 static CHAR KdpMessageBuffer[KDP_MESSAGE_BUFFER_SIZE]; @@ -45,11 +49,74 @@ static BOOL KdpContextSent; static KPROCESSOR_STATE KdpProcessorState[MAX_CPU]; static KD_CONTEXT KdpContext; static int KeNumberProcessors = 1; -static UINT16 KdpSpinBase = 0x2f00; KDP_BREAKPOINT_TYPE KdpBreakpointInstruction = KDP_BREAKPOINT_VALUE; BREAKPOINT_ENTRY KdpBreakpointTable[BREAKPOINT_TABLE_SIZE] = {0}; +// +// Some quick macros to avoid having to edit too much NT code +// +#define RtlZeroMemory(base, len) memset((base), 0, (len)) +#define KdpQuickMoveMemory(dst, src, len) memcpy((dst), (src), (len)) + +// Read memory from an untrusted pointer into a trusted buffer. +#define KdpCopyFromPtr(Dst, Src, Size, Done) \ + KdpCopyMemoryChunks((ULONG_PTR)(Src), Dst, Size, 0, \ + MMDBG_COPY_UNSAFE, Done) +// Write memory from a trusted buffer through an untrusted pointer. +#define KdpCopyToPtr(Dst, Src, Size, Done) \ + KdpCopyMemoryChunks((ULONG_PTR)(Dst), Src, Size, 0, \ + MMDBG_COPY_WRITE | MMDBG_COPY_UNSAFE, Done) + +#define RtlInitUnicodeString(string, source, length) \ + { (string)->Buffer = (source); (string)->Length = (string)->MaximumLength = (length); } + +#define FORCEINLINE __inline + +void +FORCEINLINE +InitializeListHead( + IN PLIST_ENTRY ListHead + ) +{ + ListHead->Flink = ListHead->Blink = ListHead; +} + +BOOLEAN +FORCEINLINE +RemoveEntryList( + IN PLIST_ENTRY Entry + ) +{ + PLIST_ENTRY Blink; + PLIST_ENTRY Flink; + + Flink = Entry->Flink; + Blink = Entry->Blink; + Blink->Flink = Flink; + Flink->Blink = Blink; + return (BOOLEAN)(Flink == Blink); +} + +void +FORCEINLINE +InsertTailList( + IN PLIST_ENTRY ListHead, + IN PLIST_ENTRY Entry + ) +{ + PLIST_ENTRY Blink; + + Blink = ListHead->Blink; + Entry->Flink = ListHead; + Entry->Blink = Blink; + Blink->Flink = Entry; + ListHead->Blink = Entry; +} + +//#define KdpCopyFromPtr(dst, src, size, done) { memcpy((dst), (src), (size)); *(done)=(size); } +//#define KdpCopyToPtr(dst, src, size, done) { memcpy((dst), (src), (size)); *(done)=(size); } + // // KdpRetryCount controls the number of retries before we give // up and assume kernel debugger is not present. @@ -57,20 +124,41 @@ BREAKPOINT_ENTRY KdpBreakpointTable[BREAKPOINT_TABLE_SIZE] = {0}; // it is set to 5 such that booting NT without debugger won't be // delayed to long. // -ULONG KdCompNumberRetries = 5; -ULONG KdCompRetryCount = 5; -ULONG KdPacketId = 0; +UINT32 KdCompNumberRetries = 5; +UINT32 KdCompRetryCount = 5; +UINT32 KdPacketId = 0; -void (*KdSendPacket)(ULONG PacketType, +static void KdpNulSendPacket(UINT32 PacketType, + IN PSTRING MessageHeader, + IN PSTRING MessageData OPTIONAL, + IN OUT PKD_CONTEXT KdContext) +{ +} + +static KDP_STATUS KdpNulReceivePacket(IN UINT32 PacketType, + OUT PSTRING MessageHeader, + OUT PSTRING MessageData, + OUT UINT32 * DataLength, + IN OUT PKD_CONTEXT KdContext) +{ + return KDP_PACKET_TIMEOUT; +} + +static bool KdpNulPollBreakIn() +{ + return false; +} + +void (*KdSendPacket)(UINT32 PacketType, IN PSTRING MessageHeader, IN PSTRING MessageData OPTIONAL, - IN OUT PKD_CONTEXT KdContext) = NULL; -KDP_STATUS (*KdReceivePacket)(IN ULONG PacketType, + IN OUT PKD_CONTEXT KdContext) = KdpNulSendPacket; +KDP_STATUS (*KdReceivePacket)(IN UINT32 PacketType, OUT PSTRING MessageHeader, OUT PSTRING MessageData, - OUT PULONG DataLength, - IN OUT PKD_CONTEXT KdContext) = NULL; -bool (*KdPollBreakIn)() = NULL; + OUT UINT32 * DataLength, + IN OUT PKD_CONTEXT KdContext) = KdpNulReceivePacket; +bool (*KdPollBreakIn)() = KdpNulPollBreakIn; // // Static data - these are walked by the kernel debugger @@ -84,25 +172,30 @@ struct KLDR_DATA_TABLE_ENTRY_WITH_NAME : KLDR_DATA_TABLE_ENTRY }; static KLDR_DATA_TABLE_ENTRY_WITH_NAME KdModuleKernelEntry[128]; -static ULONG KdModuleKernelUsed = 0; +static UINT32 KdModuleKernelUsed = 0; static LIST_ENTRY PsLoadedModuleList; -static PMINIDUMP_MODULE_LIST KdMinidumpModuleList = NULL; // KdVersionBlock ============================================================= static DBGKD_GET_VERSION64 KdVersionBlock = { - DBGKD_MAJOR_SINGULARITY << 8, // MajorVersion ... this one sort of works + DBGKD_MAJOR_SINGULARITY << 8, // MajorVersion 0, // Minor DBGKD_64BIT_PROTOCOL_VERSION2, // Protocol DBGKD_VERS_FLAG_NOMM, //DBGKD_VERS_FLAG_DATA, // Flags - IMAGE_FILE_MACHINE_I386, // Machine Type +#if ISA_IX86 + IMAGE_FILE_MACHINE_X86, // Machine Type +#elif ISA_IX64 + IMAGE_FILE_MACHINE_X64, // Machine Type +#elif ISA_ARM + IMAGE_FILE_MACHINE_ARM, // Machine Type +#endif PACKET_TYPE_MAX, // Max packet DbgKdMaximumStateChange - DbgKdMinimumStateChange, // MaxStateChange DbgKdSetContextApi /*DbgKdMaximumManipulate*/ - DbgKdMinimumManipulate, // MaxManipulate we support 0, // Simulation 0, // Unused 0, // KernBase - (ULONG64)&PsLoadedModuleList, // PsLoadedModuleList + 0, // PsLoadedModuleList 0 // DebuggerDataList }; @@ -110,57 +203,57 @@ static DBGKD_GET_VERSION64 KdVersionBlock = { // Forward declarations -static void KdpLock(void); -static void KdpUnlock(void); +static void KdpMpLock(void); +static void KdpMpUnlock(void); -static void KdpEnter(void); -static void KdpLeave(void); +static void KdpMpEnter(void); +static void KdpMpLeave(void); -static void KdpFakeOutPsLoadedModuleList(void); +static void KdpUpLock(void); +static void KdpUpUnlock(void); + +static void KdpUpEnter(void); +static void KdpUpLeave(void); + +static void KdpFakeOutPsLoadedModuleList(Class_Microsoft_Singularity_Hal_Platform *); static KCONTINUE_STATUS KdpSendWaitContinue( - IN ULONG OutPacketType, + IN UINT32 OutPacketType, IN PSTRING OutMessageHeader, IN PSTRING OutMessageData OPTIONAL, - IN OUT Struct_Microsoft_Singularity_X86_ThreadContext *x86Context + IN OUT Struct_Microsoft_Singularity_Isal_SpillContext *Context ); -static void LoadedBinary(Struct_Microsoft_Singularity_X86_ThreadContext *context); -static void UnloadedBinary(Struct_Microsoft_Singularity_X86_ThreadContext *context); +static void LoadedBinary(KdDebugTrapData *trapData, + Struct_Microsoft_Singularity_Isal_SpillContext *context); +static void UnloadedBinary(KdDebugTrapData *trapData, + Struct_Microsoft_Singularity_Isal_SpillContext *context); extern int printf(const char *pszFmt, ...); -///////////////////////////////////////// Debugger Unique Interrupts Routines. -// -// NB: Without these, we share routines with the mainline code and we get -// caught in a loop when the debugger inserts a break after the pushfd when -// someone tries to single step through Processor:g_DisableInterrupts! -// -static __declspec(naked) bool KdpDisableInterrupts() +#if ISA_ARM +void (*KdpLock)() = KdpUpLock; +void (*KdpUnlock)() = KdpUpUnlock; +void (*KdpEnter)() = KdpUpEnter; +void (*KdpLeave)() = KdpUpLeave; +#else +void (*KdpLock)() = KdpMpLock; +void (*KdpUnlock)() = KdpMpUnlock; +void (*KdpEnter)() = KdpMpEnter; +void (*KdpLeave)() = KdpMpLeave; +#endif + +// Ack! We cannot replicate the entire pathway through HAL just so single-stepping +// doesn't become nested and confused. +static bool KdpDisableInterrupts(void) { - __asm { - pushfd; - pop eax; - test eax, Struct_Microsoft_Singularity_X86_EFlags_IF; - setnz al; - nop; // required so that the linker doesn't combine with g_Disable - cli; - ret; - } + return KdpDisableInterruptsInline(); } -static __declspec(naked) void KdpRestoreInterrupts(bool enabled) +static void KdpRestoreInterrupts(bool enabled) { - __asm { - nop; - test cl, cl; - je done; - nop; // required so that the linker doesn't combine with g_Restore - sti; - done: - ret; - } + return KdpRestoreInterruptsInline(enabled); } ////////////////////////////////////////////////////////////////////////////// @@ -168,53 +261,77 @@ static __declspec(naked) void KdpRestoreInterrupts(bool enabled) static volatile INT32 KdpInDebugger = 0; static volatile bool KdpInDebuggerIntEnabled = FALSE; +// +// Sending IPI's to other processors may be broken at times, and +// this allows recursive entry to the debugger from breakpoints +// or exceptions in the SendIPI code. +// +static volatile INT32 KdpCpuInDebugger = -1; +static volatile INT32 KdpMpEnterCount = 0; +static volatile INT32 KdpLockEnterCount = 0; + +static CHAR KdpDbgMessageBuffer[KDP_MESSAGE_BUFFER_SIZE]; + #define MAXIMUM_RETRIES 20 -static void KdpNulSendPacket(ULONG PacketType, - IN PSTRING MessageHeader, - IN PSTRING MessageData OPTIONAL, - IN OUT PKD_CONTEXT KdContext) +void KdInitialize(Class_Microsoft_Singularity_Hal_Platform *platform) { -} + kdprintf("KdInitialize! [CpuMaxCount=%d]\n", platform->CpuMaxCount); -static KDP_STATUS KdpNulReceivePacket(IN ULONG PacketType, - OUT PSTRING MessageHeader, - OUT PSTRING MessageData, - OUT PULONG DataLength, - IN OUT PKD_CONTEXT KdContext) -{ - return KDP_PACKET_TIMEOUT; -} + if (platform->CpuMaxCount == 1) { + KdpLock = KdpUpLock; + KdpUnlock = KdpUpUnlock; + KdpEnter = KdpUpEnter; + KdpLeave = KdpUpLeave; + } + else { + KdpLock = KdpMpLock; + KdpUnlock = KdpMpUnlock; + KdpEnter = KdpMpEnter; + KdpLeave = KdpMpLeave; + } -static bool KdpNulPollBreakIn() -{ - return false; -} - -void KdInitialize(Struct_Microsoft_Singularity_BootInfo *bi) -{ KdpLock(); - if (KdpComInit(bi)) { - KdpSpinBase = 0x2f00; - KdSendPacket = KdpComSendPacket; - KdReceivePacket = KdpComReceivePacket; - KdPollBreakIn = KdpComPollBreakIn; - kdprintf("Serial Port (bi->DebugBasePort=%x).\n", bi->DebugBasePort); - } - else if (Kdp1394Init(bi)) { - KdpSpinBase = 0x4f00; - KdSendPacket = Kdp1394SendPacket; - KdReceivePacket = Kdp1394ReceivePacket; - KdPollBreakIn = Kdp1394PollBreakIn; - kdprintf("1394 Port (bi->DebugBasePort=%x).\n", bi->DebugBasePort); - } - else { + // + // Note that this is not quite right - the Hal debugger routines are only called + // if DEBUGGER_SERIAL is configured. The right thing to do is always go through the HAL, + // and then do the switching between serial/1394 inside the hal. + // @todo: Fix this when reconciling native HAL. + // + + switch (platform->DebuggerType) { + + default: + case Class_Microsoft_Singularity_Hal_Platform_DEBUGGER_NONE: kdprintf("No debugger.\n"); + KdSendPacket = KdpNulSendPacket; KdReceivePacket = KdpNulReceivePacket; KdPollBreakIn = KdpNulPollBreakIn; - KdDebuggerNotPresent = true; + + KdDebuggerNotPresent = TRUE; + KdAlwaysPrintOutput = TRUE; // do we really want to do this? + break; + + case Class_Microsoft_Singularity_Hal_Platform_DEBUGGER_SERIAL: + kdprintf("Serial Debugger:\n"); + KdSendPacket = KdpSerialSendPacket; + KdReceivePacket = KdpSerialReceivePacket; + KdPollBreakIn = KdpSerialPollBreakIn; + + KdDebuggerNotPresent = FALSE; + break; + + case Class_Microsoft_Singularity_Hal_Platform_DEBUGGER_1394: + kdprintf("1394 Debugger:\n"); + + KdSendPacket = Kdp1394SendPacket; + KdReceivePacket = Kdp1394ReceivePacket; + KdPollBreakIn = Kdp1394PollBreakIn; + + KdDebuggerNotPresent = FALSE; + break; } // Retries are set to this after boot @@ -222,55 +339,130 @@ void KdInitialize(Struct_Microsoft_Singularity_BootInfo *bi) KdpUnlock(); - KdpFakeOutPsLoadedModuleList(); + KdpFakeOutPsLoadedModuleList(platform); } -static void KdpLock() +// +// Return current CPU ID +// +int KdCurrentCpuId() +{ + return Class_Microsoft_Singularity_Isal_Isa::g_GetCurrentCpu()->id; +} + +// +// MP-safe versions of lock/unlock enter/leave kd functions +// + +static void KdpMpLock() { for (;;) { bool enabled = KdpDisableInterrupts(); if (InterlockedCompareExchange(&KdpInDebugger, 1, 0) == 0) { KdpInDebuggerIntEnabled = enabled; + + KdpCpuInDebugger = KdCurrentCpuId(); + KdpLockEnterCount++; + return; + } + + // Check to see if its already us recursively + if (KdpCpuInDebugger == KdCurrentCpuId()) { + KdpLockEnterCount++; + kdprintf("CPU %d: KdpMpLock: Entered Recursively due to breakpoint or exception, EnterCount %d\n", + KdpCpuInDebugger, KdpLockEnterCount); return; } KdpRestoreInterrupts(enabled); - __asm pause + KdpPause(); } } -static void KdpUnlock() +static void KdpMpUnlock() { + KdpLockEnterCount--; + + // Recursive lock leave + if (KdpLockEnterCount != 0) { + return; + } + + // Must grab the state of interrupts before we release our lock. Fortunately, + // KdpInDebuggerIntEnabled is volatile, so the compiler will not reorder our + // read: + bool intEnabled = KdpInDebuggerIntEnabled; + + KdpCpuInDebugger = -1; + KdpInDebugger = 0; - KdpRestoreInterrupts(KdpInDebuggerIntEnabled); + KdpRestoreInterrupts(intEnabled); } -static void KdpEnter() +static void KdpMpEnter() { - if (KeNumberProcessors > 1) { - Class_Microsoft_Singularity_MpExecution::g_FreezeAllProcessors(); + KdpMpEnterCount++; + + // Only attempt to stop processors on the first entry + if (KdpMpEnterCount == 1) { + if (KeNumberProcessors > 1) { + KDDBG("CPU %d: KdpMpEnter, calling FreezeAllProcessors...\n", KdCurrentCpuId()); + Class_Microsoft_Singularity_MpExecution::g_FreezeAllProcessors(); + KDDBG("CPU %d: KdpMpEnter, return from FreezeAllProcessors...\n", KdCurrentCpuId()); + } } } -static void KdpLeave() +static void KdpMpLeave() { - if (KeNumberProcessors > 1) { - Class_Microsoft_Singularity_MpExecution::g_ThawAllProcessors(); + KdpMpEnterCount--; + Class_Microsoft_Singularity_MpExecution::g_ThawAllProcessors(); +} + +// +// Single processor versions of lock/unlock enter/leave kd functions +// + +static void KdpUpLock() +{ + bool enabled = KdpDisableInterrupts(); + KdpInDebugger++; + + if (KdpInDebugger == 1) { + KdpInDebuggerIntEnabled = enabled; } } +static void KdpUpUnlock() +{ + KdpInDebugger--; + if (KdpInDebugger == 0) { + KdpRestoreInterrupts(KdpInDebuggerIntEnabled); + } +} + +static void KdpUpEnter(void) +{ +} + +static void KdpUpLeave(void) +{ +} + ////////////////////////////////////////////////////////////////////////////// // extern int strformat(void (*pfOutput)(void *pContext, char c), void *pContext, const char * pszFmt, va_list args); -#define KD_LEFT 0 -#define KD_HEIGHT 46 - -static UINT16 kdcurs = KD_LEFT; -static UINT16 kdattr = 0x2f00; static void koutput(void *pContext, char c) { +#if ISA_IX86 || ISA_IX64 +#define KD_LEFT 0 +#define KD_HEIGHT 46 + + static UINT16 kdcurs = KD_LEFT; + static UINT16 kdattr = 0x2f00; + // // Update cursor position // @@ -310,6 +502,9 @@ static void koutput(void *pContext, char c) else if (c == '\f') { kdcurs = 0; } +#elif ISA_ARM + // Do nothing. +#endif } void kdprints(const char * pszFmt) @@ -328,22 +523,101 @@ void kdprintf(const char * pszFmt, ...) va_end(args); } -void KdpSpin() +BOOL +ProbeMemoryRange(UINT64 address, UINT32 length) { - static UINT8 state = 0; + Struct_Microsoft_Singularity_SMAPINFO *sm = + (Struct_Microsoft_Singularity_SMAPINFO *)Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->Smap32; + int smapCount = Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->SmapCount; - *((UINT16 *)0xb809e) = KdpSpinBase + ("+-|*" [state++ & 0x3]); + for (int32 i = 0; i < smapCount; i++) { + if (// (sm[i].type == Struct_Microsoft_Singularity_SMAPINFO_AddressTypeFree) && + (sm[i].addr <= address) && + (sm[i].addr + sm[i].size) >= (address + length)) { + + return true; + } + } + + if ((address >= Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->Smap32) && + ((address + length) <= (Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->Smap32 + 4096))) { + + return true; + } + + if ((address >= Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->LogRecordBuffer) && + ((address + length) <= (Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->LogRecordBuffer + Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->LogRecordSize))) { + + return true; + } + + if ((address >= Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->LogTextBuffer) && + ((address + length) <= (Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->LogTextBuffer + Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->LogTextSize))) { + + return true; + } + + if ((address >= Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->Smap32) && + ((address + length) <= (Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->Smap32 + 4096))) { + + return true; + } + + if ((address >= Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->LogRecordBuffer) && + ((address + length) <= (Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->LogRecordBuffer + Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->LogRecordSize))) { + + return true; + } + + if ((address >= Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->LogTextBuffer) && + ((address + length) <= (Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->LogTextBuffer + Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->LogTextSize))) { + + return true; + } + +#if ISA_IX + // TODO: HACK!HACK!HACK! These I/O ranges + // should not be hardcoded, but probed and put into the SMAP + // as reserved, . + const UINT64 ApicBase = 0xfee00000; + const UINT32 ApicLength = 0x1000; + const UINT64 IoApic0Base = 0xfec00000; + const UINT32 IoApicLength = 0x100; + const UINT64 IoApic1Base = 0xfec80000; + + if (address >= ApicBase && + address <= ApicBase + ApicLength) { + return true; + } + if (address >= IoApic0Base && + address <= IoApic0Base + IoApicLength) { + return true; + } + if (address >= IoApic1Base && + address <= IoApic1Base + IoApicLength) { + return true; + } +#endif + + // + // TODO: revisit this with paging and new MM design + // +#if ISA_ARM + KDDBG("ProbeFailed %p..%p\n", (UIntPtr)address, (UIntPtr)(address + length)); +#endif + + return false; } ////////////////////////////////////////////////////////////////////////////// // -ULONG KdpComputeChecksum(IN PCHAR Buffer, IN ULONG Length) +UINT32 KdpComputeChecksum(IN PCHAR Buffer, IN UINT32 Length) { // Compute the checksum for the string passed in. - ULONG Checksum = 0; + UINT32 Checksum = 0; while (Length > 0) { - Checksum = Checksum + (ULONG)*(PUCHAR)Buffer++; + Checksum = Checksum + (UINT32)*(PUCHAR)Buffer++; Length--; } @@ -352,211 +626,10 @@ ULONG KdpComputeChecksum(IN PCHAR Buffer, IN ULONG Length) ////////////////////////////////////////////////////////////////////////////// // -static -VOID KdSingularityToWindbgContext(IN CONST Struct_Microsoft_Singularity_X86_ThreadContext *singularity, - OUT CONTEXT *windbg) +UINT16 KdpGetCurrentProcessorNumber() { - RtlZeroMemory(windbg, sizeof(*windbg)); - - windbg->ContextFlags = (CONTEXT_CONTROL | - CONTEXT_INTEGER | - CONTEXT_SEGMENTS | - CONTEXT_DEBUG_REGISTERS); - - // CONTEXT_FULL; - windbg->Eax = singularity->eax; - windbg->Ebx = singularity->ebx; - windbg->Ecx = singularity->ecx; - windbg->Edx = singularity->edx; - windbg->Esp = singularity->esp; - windbg->Ebp = singularity->ebp; - windbg->Esi = singularity->esi; - windbg->Edi = singularity->edi; - windbg->Eip = singularity->eip; - windbg->EFlags = singularity->efl; - - // CONTEXT_FLOATING_POINT - if (singularity->regs) { - windbg->ContextFlags |= CONTEXT_FLOATING_POINT; - -#if 0 - PUCHAR pmmx = (PUCHAR)&singularity->mmx; - kdprintf(" MMX=%02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n", - pmmx[0], pmmx[1], pmmx[2], pmmx[3], - pmmx[4], pmmx[5], pmmx[6], pmmx[7], - pmmx[8], pmmx[9], pmmx[10], pmmx[11], - pmmx[12], pmmx[13], pmmx[14], pmmx[15]); - kdprintf(" MMX=%04x %04x %02x %03x %04x:%08x %04x:%08x\n", - singularity->mmx.fcw, - singularity->mmx.fsw, - singularity->mmx.ftw, - singularity->mmx.fop, - singularity->mmx.cs, - singularity->mmx.eip, - singularity->mmx.ds, - singularity->mmx.dp); -#endif - - windbg->FloatSave.ControlWord = singularity->mmx.fcw; - windbg->FloatSave.StatusWord = singularity->mmx.fsw; - windbg->FloatSave.TagWord = singularity->mmx.ftw; - windbg->FloatSave.ErrorOffset = singularity->mmx.eip; - windbg->FloatSave.ErrorSelector = singularity->mmx.cs; - windbg->FloatSave.DataOffset = singularity->mmx.dp; - windbg->FloatSave.DataSelector = singularity->mmx.ds; - - memcpy((uint8*)windbg->FloatSave.RegisterArea+0, &singularity->mmx.st0, 10); - memcpy((uint8*)windbg->FloatSave.RegisterArea+10, &singularity->mmx.st1, 10); - memcpy((uint8*)windbg->FloatSave.RegisterArea+20, &singularity->mmx.st2, 10); - memcpy((uint8*)windbg->FloatSave.RegisterArea+30, &singularity->mmx.st3, 10); - memcpy((uint8*)windbg->FloatSave.RegisterArea+40, &singularity->mmx.st4, 10); - memcpy((uint8*)windbg->FloatSave.RegisterArea+50, &singularity->mmx.st5, 10); - memcpy((uint8*)windbg->FloatSave.RegisterArea+60, &singularity->mmx.st6, 10); - memcpy((uint8*)windbg->FloatSave.RegisterArea+70, &singularity->mmx.st7, 10); - } - - // CONTEXT_EXTENDED_REGISTERS - if (singularity->regs & 1) { - windbg->ContextFlags |= CONTEXT_EXTENDED_REGISTERS; - memcpy(windbg->ExtendedRegisters, &singularity->mmx, 512); - } - - // - // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is - // set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT - // included in CONTEXT_FULL. - // - windbg->Dr0 = singularity->dr0; - windbg->Dr1 = singularity->dr1; - windbg->Dr2 = singularity->dr2; - windbg->Dr3 = singularity->dr3; - windbg->Dr6 = singularity->dr6; - windbg->Dr7 = singularity->dr7; - -#if 0 - __asm { - mov ebx, windbg; - - mov eax, dr0; - mov [ebx]CONTEXT.Dr0, eax; - mov eax, dr1; - mov [ebx]CONTEXT.Dr1, eax; - mov eax, dr2; - mov [ebx]CONTEXT.Dr2, eax; - mov eax, dr3; - mov [ebx]CONTEXT.Dr3, eax; - mov eax, dr6; - mov [ebx]CONTEXT.Dr6, eax; - mov eax, dr7; - mov [ebx]CONTEXT.Dr7, eax; - } -#endif - -#if 1 - // - // This section is specified/returned if the - // ContextFlags word contains the flag CONTEXT_SEGMENTS. - // - __asm { - mov ebx, windbg; - - xor eax, eax; - mov ax, gs; - mov [ebx]CONTEXT.SegGs, eax; - mov ax, fs; - mov [ebx]CONTEXT.SegFs, eax; - mov ax, es; - mov [ebx]CONTEXT.SegEs, eax; - mov ax, ds; - mov [ebx]CONTEXT.SegDs, eax; - } - - // - // This section is specified/returned if the - // ContextFlags word contains the flag CONTEXT_CONTROL. - // - - __asm { - mov ebx, windbg; - - xor eax, eax; - mov ax, ss; - mov [ebx]CONTEXT.SegSs, eax; - } - - windbg->SegCs = singularity->cs0; -#endif -} - -VOID KdWindbgToSingularityContext(IN CONST CONTEXT *windbg, - OUT Struct_Microsoft_Singularity_X86_ThreadContext *singularity) -{ - singularity->eax = windbg->Eax; - singularity->ebx = windbg->Ebx; - singularity->ecx = windbg->Ecx; - singularity->edx = windbg->Edx; - singularity->esp = windbg->Esp; - singularity->ebp = windbg->Ebp; - singularity->esi = windbg->Esi; - singularity->edi = windbg->Edi; - singularity->eip = windbg->Eip; - singularity->efl = windbg->EFlags; - - // CONTEXT_FLOATING_POINT - if (windbg->ContextFlags & CONTEXT_FLOATING_POINT) { - singularity->mmx.fcw = (uint16)windbg->FloatSave.ControlWord; - singularity->mmx.fsw = (uint16)windbg->FloatSave.StatusWord; - singularity->mmx.ftw = (uint16)windbg->FloatSave.TagWord; - singularity->mmx.cs = (uint16)windbg->FloatSave.ErrorSelector; - singularity->mmx.eip = windbg->FloatSave.ErrorOffset; - singularity->mmx.ds = (uint16)windbg->FloatSave.DataSelector; - singularity->mmx.dp = windbg->FloatSave.DataOffset; - memcpy(&singularity->mmx.st0, windbg->FloatSave.RegisterArea+0, 10); - memcpy(&singularity->mmx.st1, windbg->FloatSave.RegisterArea+10, 10); - memcpy(&singularity->mmx.st2, windbg->FloatSave.RegisterArea+20, 10); - memcpy(&singularity->mmx.st3, windbg->FloatSave.RegisterArea+30, 10); - memcpy(&singularity->mmx.st4, windbg->FloatSave.RegisterArea+40, 10); - memcpy(&singularity->mmx.st5, windbg->FloatSave.RegisterArea+50, 10); - memcpy(&singularity->mmx.st6, windbg->FloatSave.RegisterArea+60, 10); - memcpy(&singularity->mmx.st7, windbg->FloatSave.RegisterArea+70, 10); - } - - // CONTEXT_EXTENDED_REGISTERS - if (windbg->ContextFlags & CONTEXT_EXTENDED_REGISTERS) { - memcpy(&singularity->mmx, windbg->ExtendedRegisters, 512); - } - - // - // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is - // set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT - // included in CONTEXT_FULL. - // - if (windbg->ContextFlags & CONTEXT_DEBUG_REGISTERS) { - singularity->dr0 = windbg->Dr0; - singularity->dr1 = windbg->Dr1; - singularity->dr2 = windbg->Dr2; - singularity->dr3 = windbg->Dr3; - singularity->dr6 = windbg->Dr6; - singularity->dr7 = windbg->Dr7; -#if 0 - __asm { - mov ebx, windbg; - mov eax, [ebx]CONTEXT.Dr0; - mov dr0, eax; - mov eax, [ebx]CONTEXT.Dr1; - mov dr1, eax; - mov eax, [ebx]CONTEXT.Dr2; - mov dr2, eax; - mov eax, [ebx]CONTEXT.Dr3; - mov dr3, eax; - mov eax, 0; //CONTEXT.Dr6 : DR6 is reset when changing BPTs. - mov dr6, eax; - mov eax, [ebx]CONTEXT.Dr7; - mov eax, dr7; - } -#endif - } -} + return (UINT16) Class_Microsoft_Singularity_Isal_Isa::g_GetCurrentCpu()->id; +} // KdpGetCurrentProcessorNumber ////////////////////////////////////////////////////////////////////////////// @@ -564,15 +637,14 @@ VOID KdWindbgToSingularityContext(IN CONST CONTEXT *windbg, // Misc KD functions // - NTSTATUS KdpCopyMemoryChunks( - ULONG64 Address, + UINT64 Address, PVOID Buffer, - ULONG TotalSize, - ULONG ChunkSize, - ULONG Flags, - PULONG ActualSize OPTIONAL + UINT32 TotalSize, + UINT32 ChunkSize, + UINT32 Flags, + UINT32 * ActualSize OPTIONAL ) // Routine Description: // Copies memory to/from a buffer to/from a system address. @@ -588,20 +660,14 @@ KdpCopyMemoryChunks( // 0 means choose a default. // Flags - MMDBG_COPY flags for MmDbgCopyMemory. // ActualSize - Number of bytes actually read/written. - // - // Return Value: - // NTSTATUS { - ULONG Length; - ULONG CopyChunk; -// NTSTATUS Status; -#if defined(_IA64_) - ULONG64 AddressStart = Address; -#endif + UINT32 Length; + UINT32 CopyChunk; if (ChunkSize > MMDBG_COPY_MAX_SIZE) { ChunkSize = MMDBG_COPY_MAX_SIZE; - } else if (ChunkSize == 0) { + } + else if (ChunkSize == 0) { // Default to 4 byte chunks as that's // what the previous code did. ChunkSize = 4; @@ -638,15 +704,14 @@ KdpCopyMemoryChunks( CopyChunk >>= 1; } - Address &= 0xffffffff; - if (Address < Struct_Microsoft_Singularity_BootInfo_PHYSICAL_DISABLED) { + if (Address < Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->PhysicalBase) { break; } if (Address == 0) { break; } #if PAGING - ULONG64 RawAddress = Address; + UINT64 RawAddress = Address; if (Flags & MMDBG_COPY_PHYSICAL) { // Temporarily map the physical memory range. // TODO: KernelMapPhysicalMemory tries to acquire a lock -- if @@ -657,7 +722,7 @@ KdpCopyMemoryChunks( Struct_Microsoft_Singularity_Memory_PhysicalAddress::m__ctor( &physical, UIntPtr(Address)); - Address = ULONG64(Class_Microsoft_Singularity_Memory_MemoryManager + Address = UINT64(Class_Microsoft_Singularity_Memory_MemoryManager ::g_KernelMapPhysicalMemory( physical, UIntPtr(CopyChunk))); @@ -671,10 +736,23 @@ KdpCopyMemoryChunks( break; } } + #endif + // + // It is illegal to just touch the memory that the debugger just asked for + // w/o making sure it is valid. Otherwise this will trap inside the debugger + // code, hanging both the system and debugger. We need to ansure the memory + // is valid. For now just check against the smap. + // + + if (!ProbeMemoryRange(Address, CopyChunk)) { + break; + } + if (Flags & MMDBG_COPY_WRITE) { memcpy((void*)Address, Buffer, CopyChunk); - } else { + } + else { memcpy(Buffer, (void*)Address, CopyChunk); } @@ -705,58 +783,49 @@ KdpCopyMemoryChunks( // if ((Flags & MMDBG_COPY_WRITE) && Length < TotalSize) { -#if defined(_IA64_) - // - // KeSweepCurrentIcacheRange requires a valid virtual address. - // It is used because KeSweepCurrentICache does not work until - // the HAL as been initialized. - // - - if (Flags & MMDBG_COPY_PHYSICAL) { - KeSweepCurrentIcache(); - } - else { - KeSweepCurrentIcacheRange((PVOID)AddressStart, TotalSize - Length); - } -#else - __asm wbinvd; - // KeSweepCurrentIcache(); -#endif - + KdpFlushInstCache(); } return Length != 0 ? STATUS_UNSUCCESSFUL : STATUS_SUCCESS; } static -VOID +void KdpSetCommonState( - IN ULONG NewState, - IN Struct_Microsoft_Singularity_X86_ThreadContext *x86Context, + IN UINT32 NewState, + IN Struct_Microsoft_Singularity_Isal_SpillContext *Context, OUT PDBGKD_ANY_WAIT_STATE_CHANGE WaitStateChange ) { - PCHAR PcMemory = (PCHAR)x86Context->eip; - ULONG InstrCount; + UINT32 InstrCount; PUCHAR InstrStream; WaitStateChange->NewState = NewState; WaitStateChange->ProcessorLevel = KeProcessorLevel; - WaitStateChange->Processor = GetCurrentProcessorNumber(); + WaitStateChange->Processor = KdpGetCurrentProcessorNumber(); WaitStateChange->NumberProcessors = KeNumberProcessors; - WaitStateChange->Thread = SIGN_EXTEND_PTR(x86Context->_thread); - WaitStateChange->ProgramCounter = SIGN_EXTEND_PTR(x86Context->eip); + WaitStateChange->Thread + = SIGN_EXTEND(Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext()->_thread); +#if ISA_IX86 + WaitStateChange->ProgramCounter = SIGN_EXTEND(Context->ip); +#elif ISA_IX64 + WaitStateChange->ProgramCounter = Context->ip; +#elif ISA_ARM + WaitStateChange->ProgramCounter = SIGN_EXTEND(Context->pc); +#endif RtlZeroMemory(&WaitStateChange->AnyControlReport, sizeof(WaitStateChange->AnyControlReport)); // // Copy instruction stream immediately following location of event. // + PCHAR PcMemory = (PCHAR)WaitStateChange->ProgramCounter; - InstrStream = WaitStateChange->ControlReport.InstructionStream; - KdpCopyFromPtr(InstrStream, PcMemory, DBGKD_MAXSTREAM, &InstrCount); - WaitStateChange->ControlReport.InstructionCount = (USHORT)InstrCount; + InstrStream = (PUCHAR)&WaitStateChange->ControlReport.InstructionStream; + KdpCopyFromPtr(InstrStream, PcMemory, + sizeof(WaitStateChange->ControlReport.InstructionStream), &InstrCount); + WaitStateChange->ControlReport.InstructionCount = (UINT16)InstrCount; // // Clear breakpoints in copied area. @@ -769,77 +838,9 @@ KdpSetCommonState( // } } -VOID -KdpSetContextState( - IN OUT PDBGKD_ANY_WAIT_STATE_CHANGE WaitStateChange, - IN Struct_Microsoft_Singularity_X86_ThreadContext *x86Context - ) - // Routine Description: - // Fill in the Wait_State_Change message record. - // - // Arguments: - // WaitStateChange - Supplies pointer to record to fill in - // x86Context - Supplies a pointer to a context record. - // - // Return Value: - // None. +UINT32 WcsToStr(WCHAR *pwcsSrc, UINT32 Length, PCHAR pszDst) { -#if 0 - PKPRCB Prcb; - - // - // Special registers for the x86 - // - Prcb = KeGetCurrentPrcb(); - WaitStateChange->ControlReport.Dr6 = Prcb->ProcessorState.SpecialRegisters.KernelDr6; - WaitStateChange->ControlReport.Dr7 = Prcb->ProcessorState.SpecialRegisters.KernelDr7; -#endif - - UINT32 _dr6; - UINT32 _dr7; - UINT16 _cs; - UINT16 _ds; - UINT16 _es; - UINT16 _fs; - - __asm { - mov eax, dr6; - mov _dr6, eax; - mov eax, dr7; - mov _dr7, eax; - mov ax, cs; - mov _cs, ax; - mov ax, ds; - mov _ds, ax; - mov ax, es; - mov _es, ax; - mov ax, fs; - mov _fs, ax; - } - - // I'm not sure we're handling dr6 right here. - KDDBG("KdpSetContextState dr6=%08x dr7=%08x cs=%04x/%04x\n", - _dr6, _dr7, _cs, _ds); - - WaitStateChange->ControlReport.Dr6 = _dr6; - WaitStateChange->ControlReport.Dr7 = _dr7; - WaitStateChange->ControlReport.SegCs = _cs; - WaitStateChange->ControlReport.SegDs = _ds; - WaitStateChange->ControlReport.SegEs = _es; - WaitStateChange->ControlReport.SegFs = _fs; - WaitStateChange->ControlReport.EFlags = x86Context->efl; - WaitStateChange->ControlReport.ReportFlags = X86_REPORT_INCLUDES_SEGS; - -#if !PAGING - // Let the debugger know so that it doesn't have to retrieve the CS descriptor. - WaitStateChange->ControlReport.ReportFlags |= X86_REPORT_STANDARD_CS; -#endif -} - -ULONG -WcsToStr(WCHAR *pwcsSrc, ULONG Length, PCHAR pszDst) -{ - for (ULONG n = 0; n < Length; n++) { + for (UINT32 n = 0; n < Length; n++) { *pszDst++ = (char)*pwcsSrc++; } *pszDst++ = '\0'; @@ -850,13 +851,13 @@ WcsToStr(WCHAR *pwcsSrc, ULONG Length, PCHAR pszDst) bool KdpReportLoadSymbolsStateChange( IN WCHAR *PathName, - IN ULONG PathNameLength, - IN ULONG64 BaseOfDll, - IN ULONG ProcessId, - IN ULONG CheckSum, - IN ULONG SizeOfImage, + IN UINT32 PathNameLength, + IN UINT64 BaseOfDll, + IN UINT32 ProcessId, + IN UINT32 CheckSum, + IN UINT32 SizeOfImage, IN BOOLEAN UnloadSymbols, - IN OUT Struct_Microsoft_Singularity_X86_ThreadContext *x86Context + IN OUT Struct_Microsoft_Singularity_Isal_SpillContext *Context ) // Routine Description: // This routine sends a load symbols state change packet to the kernel @@ -871,10 +872,6 @@ KdpReportLoadSymbolsStateChange( // CheckSum - Checksum from image header. // UnloadSymbol - TRUE if the symbols that were previously loaded for // the named image are to be unloaded from the debugger. - // - // Return Value: - // A value of TRUE is returned if the exception is handled. Otherwise, a - // value of FALSE is returned. { // NB: \nt\sdktools\debuggers\ntsd64\event.cpp // PathNameLength = 0, ProcessId = 0, BaseOfDll = -1 for reboot. @@ -885,32 +882,32 @@ KdpReportLoadSymbolsStateChange( DBGKD_ANY_WAIT_STATE_CHANGE WaitStateChange; KCONTINUE_STATUS Status; - KDDBG("KdpReportLoadSymbolsStateChange %p\n", x86Context); + KDDBG("KdpReportLoadSymbolsStateChange %p\n", Context); do { // // Construct the wait state change message and message descriptor. // - KdpSetCommonState(DbgKdLoadSymbolsStateChange, x86Context, + KdpSetCommonState(DbgKdLoadSymbolsStateChange, Context, &WaitStateChange); - WaitStateChange.u.LoadSymbols.UnloadSymbols = UnloadSymbols; - WaitStateChange.u.LoadSymbols.BaseOfDll = BaseOfDll; - WaitStateChange.u.LoadSymbols.ProcessId = ProcessId; - WaitStateChange.u.LoadSymbols.CheckSum = CheckSum; - WaitStateChange.u.LoadSymbols.SizeOfImage = SizeOfImage; + WaitStateChange.LoadSymbols.UnloadSymbols = UnloadSymbols; + WaitStateChange.LoadSymbols.BaseOfDll = SIGN_EXTEND(BaseOfDll); + WaitStateChange.LoadSymbols.ProcessId = ProcessId; + WaitStateChange.LoadSymbols.CheckSum = CheckSum; + WaitStateChange.LoadSymbols.SizeOfImage = SizeOfImage; if (PathName != NULL) { - WaitStateChange.u.LoadSymbols.PathNameLength = + WaitStateChange.LoadSymbols.PathNameLength = WcsToStr(PathName, PathNameLength, KdpMessageBuffer); } else { - WaitStateChange.u.LoadSymbols.PathNameLength = 0; + WaitStateChange.LoadSymbols.PathNameLength = 0; } MessageData.Buffer = KdpMessageBuffer; - MessageData.Length = (USHORT)WaitStateChange.u.LoadSymbols.PathNameLength; + MessageData.Length = (UINT16)WaitStateChange.LoadSymbols.PathNameLength; - KdpSetContextState(&WaitStateChange, x86Context); + KdpSetControlReport(&WaitStateChange.ControlReport, Context); MessageHeader.Length = sizeof(WaitStateChange); MessageHeader.Buffer = (PCHAR)&WaitStateChange; @@ -919,7 +916,7 @@ KdpReportLoadSymbolsStateChange( PACKET_TYPE_KD_STATE_CHANGE64, &MessageHeader, &MessageData, - x86Context + Context ); } while (Status == ContinueProcessorReselected) ; @@ -930,8 +927,8 @@ KdpReportLoadSymbolsStateChange( static KCONTINUE_STATUS KdpReportExceptionStateChange( - IN PEXCEPTION_RECORD64 ExceptionRecord, - IN OUT Struct_Microsoft_Singularity_X86_ThreadContext *x86Context, + IN EXCEPTION_RECORD64 *ExceptionRecord, + IN OUT Struct_Microsoft_Singularity_Isal_SpillContext *Context, IN BOOLEAN FirstChance ) // Routine Description: @@ -940,7 +937,7 @@ KdpReportExceptionStateChange( // // Arguments: // ExceptionRecord - Supplies a pointer to an exception record. - // x86Context - Supplies a pointer to a context record. + // Context - Supplies a pointer to a context record. // FirstChance - Supplies a boolean value that determines whether this is // the first or second chance for the exception. // @@ -953,7 +950,7 @@ KdpReportExceptionStateChange( DBGKD_ANY_WAIT_STATE_CHANGE WaitStateChange; KCONTINUE_STATUS Status; - KDDBG("KdpReportExceptionStateChange %p\n", x86Context); + KDDBG("KdpReportExceptionStateChange %p\n", Context); do { @@ -961,13 +958,13 @@ KdpReportExceptionStateChange( // Construct the wait state change message and message descriptor. // - KdpSetCommonState(DbgKdExceptionStateChange, x86Context, + KdpSetCommonState(DbgKdExceptionStateChange, Context, &WaitStateChange); - WaitStateChange.u.Exception.ExceptionRecord = *ExceptionRecord; - WaitStateChange.u.Exception.FirstChance = FirstChance; + WaitStateChange.Exception.ExceptionRecord = *ExceptionRecord; + WaitStateChange.Exception.FirstChance = FirstChance; - KdpSetContextState(&WaitStateChange, x86Context); + KdpSetControlReport(&WaitStateChange.ControlReport, Context); MessageHeader.Length = sizeof(WaitStateChange); MessageHeader.Buffer = (PCHAR)&WaitStateChange; @@ -981,7 +978,7 @@ KdpReportExceptionStateChange( PACKET_TYPE_KD_STATE_CHANGE64, &MessageHeader, &MessageData, - x86Context + Context ); } while (Status == ContinueProcessorReselected) ; @@ -990,7 +987,7 @@ KdpReportExceptionStateChange( static -VOID +void KdpReadVirtualMemory( IN PDBGKD_MANIPULATE_STATE64 m, IN PSTRING AdditionalData @@ -1003,19 +1000,15 @@ KdpReadVirtualMemory( // Arguments: // m - Supplies a pointer to the state manipulation message. // AdditionalData - Supplies a pointer to a descriptor for the data to read. - // x86Context - Supplies a pointer to the current context. - // - // Return Value: - // None. { - ULONG Length; + UINT32 Length; STRING MessageHeader; // // Trim the transfer count to fit in a single message. // - Length = m->u.ReadMemory.TransferCount; + Length = m->ReadMemory.TransferCount; if (Length > (PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64))) { Length = PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64); } @@ -1023,31 +1016,22 @@ KdpReadVirtualMemory( // // Move the data to the destination buffer. // - if (m->u.ReadMemory.TargetBaseAddress >= 0x80000000) { - KDDBG(" Read out of range!\n"); - for (UINT i = 0; i < Length; i++) { - AdditionalData->Buffer[i] = 0; - } - Length = 0; - } - else { - m->ReturnStatus = - KdpCopyMemoryChunks(m->u.ReadMemory.TargetBaseAddress, - AdditionalData->Buffer, - Length, - 0, - MMDBG_COPY_UNSAFE, - &Length); - } + m->ReturnStatus = + KdpCopyMemoryChunks((UINT64)(ULONG_PTR)m->ReadMemory.TargetBaseAddress, + AdditionalData->Buffer, + Length, + 0, + MMDBG_COPY_UNSAFE, + &Length); // // Set the actual number of bytes read, initialize the message header, // and send the reply packet to the host debugger. // - AdditionalData->Length = (USHORT)Length; - m->u.ReadMemory.ActualBytesRead = Length; + AdditionalData->Length = (UINT16)Length; + m->ReadMemory.ActualBytesRead = Length; MessageHeader.Length = sizeof(DBGKD_MANIPULATE_STATE64); MessageHeader.Buffer = (PCHAR)m; @@ -1058,9 +1042,9 @@ KdpReadVirtualMemory( return; } - + static -VOID +void KdpWriteVirtualMemory( IN PDBGKD_MANIPULATE_STATE64 m, IN PSTRING AdditionalData @@ -1073,10 +1057,7 @@ KdpWriteVirtualMemory( // Arguments: // m - Supplies a pointer to the state manipulation message. // AdditionalData - Supplies a pointer to a descriptor for the data to write. - // x86Context - Supplies a pointer to the current context. - // - // Return Value: - // None. + // Context - Supplies a pointer to the current context. { STRING MessageHeader; @@ -1086,12 +1067,12 @@ KdpWriteVirtualMemory( // m->ReturnStatus = - KdpCopyMemoryChunks(m->u.WriteMemory.TargetBaseAddress, + KdpCopyMemoryChunks((UINT64)(ULONG_PTR)m->WriteMemory.TargetBaseAddress, AdditionalData->Buffer, AdditionalData->Length, 0, MMDBG_COPY_WRITE | MMDBG_COPY_UNSAFE, - &m->u.WriteMemory.ActualBytesWritten); + &m->WriteMemory.ActualBytesWritten); // // Set the actual number of bytes written, initialize the message header, @@ -1107,9 +1088,9 @@ KdpWriteVirtualMemory( return; } - + static -VOID +void KdpReadPhysicalMemory( IN PDBGKD_MANIPULATE_STATE64 m, IN PSTRING AdditionalData @@ -1122,25 +1103,22 @@ KdpReadPhysicalMemory( // Arguments: // m - Supplies a pointer to the state manipulation message. // AdditionalData - Supplies a pointer to a descriptor for the data to read. - // x86Context - Supplies a pointer to the current context. - // - // Return Value: - // None. + // Context - Supplies a pointer to the current context. { - ULONG Length; + UINT32 Length; STRING MessageHeader; // // Trim the transfer count to fit in a single message. // - Length = m->u.ReadMemory.TransferCount; + Length = m->ReadMemory.TransferCount; if (Length > (PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64))) { Length = PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64); } m->ReturnStatus = - KdpCopyMemoryChunks(m->u.ReadMemory.TargetBaseAddress, + KdpCopyMemoryChunks((UINT64)(ULONG_PTR)m->ReadMemory.TargetBaseAddress, AdditionalData->Buffer, Length, 0, @@ -1152,8 +1130,8 @@ KdpReadPhysicalMemory( // and send the reply packet to the host debugger. // - AdditionalData->Length = (USHORT)Length; - m->u.ReadMemory.ActualBytesRead = Length; + AdditionalData->Length = (UINT16)Length; + m->ReadMemory.ActualBytesRead = Length; MessageHeader.Length = sizeof(DBGKD_MANIPULATE_STATE64); MessageHeader.Buffer = (PCHAR)m; @@ -1164,9 +1142,9 @@ KdpReadPhysicalMemory( return; } - + static -VOID +void KdpWritePhysicalMemory( IN PDBGKD_MANIPULATE_STATE64 m, IN PSTRING AdditionalData @@ -1179,12 +1157,8 @@ KdpWritePhysicalMemory( // Arguments: // m - Supplies a pointer to the state manipulation message. // AdditionalData - Supplies a pointer to a descriptor for the data to write. - // x86Context - Supplies a pointer to the current context. - // - // Return Value: - // None. + // Context - Supplies a pointer to the current context. { - STRING MessageHeader; // @@ -1192,12 +1166,12 @@ KdpWritePhysicalMemory( // m->ReturnStatus = - KdpCopyMemoryChunks(m->u.WriteMemory.TargetBaseAddress, + KdpCopyMemoryChunks((UINT64)(ULONG_PTR)m->WriteMemory.TargetBaseAddress, AdditionalData->Buffer, AdditionalData->Length, 0, MMDBG_COPY_WRITE | MMDBG_COPY_UNSAFE | MMDBG_COPY_PHYSICAL, - &m->u.WriteMemory.ActualBytesWritten); + &m->WriteMemory.ActualBytesWritten); // // Set the actual number of bytes written, initialize the message header, @@ -1213,13 +1187,13 @@ KdpWritePhysicalMemory( return; } - + static -VOID +void KdpReadMachineSpecificRegister( IN PDBGKD_MANIPULATE_STATE64 m, IN PSTRING AdditionalData, - IN Struct_Microsoft_Singularity_X86_ThreadContext *x86Context + IN Struct_Microsoft_Singularity_Isal_SpillContext *Context ) // Routine Description: // This function is called in response of a write physical memory 32-bit @@ -1229,31 +1203,17 @@ KdpReadMachineSpecificRegister( // Arguments: // m - Supplies a pointer to the state manipulation message. // AdditionalData - Supplies a pointer to a descriptor for the data to write. - // x86Context - Supplies a pointer to the current context. - // - // Return Value: - // None. + // Context - Supplies a pointer to the current context. { - STRING MessageHeader; // // Read the MSR // - - UINT32 msr = m->u.ReadWriteMsr.Msr; - UINT32 hi, lo; - - __asm { - mov ecx, msr; - rdmsr; - mov hi, edx; - mov lo, eax; - } - - m->u.ReadWriteMsr.DataValueHigh = hi; - m->u.ReadWriteMsr.DataValueLow = lo; - m->ReturnStatus = STATUS_SUCCESS; + m->ReturnStatus = KdpReadMsr(m->ReadWriteMsr.Msr, + &m->ReadWriteMsr.DataValueLow, + &m->ReadWriteMsr.DataValueHigh) + ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; // // Set the actual number of bytes written, initialize the message header, @@ -1266,16 +1226,14 @@ KdpReadMachineSpecificRegister( &MessageHeader, NULL, &KdpContext); - - return; } - + static -VOID +void KdpWriteMachineSpecificRegister( IN PDBGKD_MANIPULATE_STATE64 m, IN PSTRING AdditionalData, - IN Struct_Microsoft_Singularity_X86_ThreadContext *x86Context + IN Struct_Microsoft_Singularity_Isal_SpillContext *Context ) // Routine Description: // This function is called in response of a write physical memory 32-bit @@ -1285,29 +1243,17 @@ KdpWriteMachineSpecificRegister( // Arguments: // m - Supplies a pointer to the state manipulation message. // AdditionalData - Supplies a pointer to a descriptor for the data to write. - // x86Context - Supplies a pointer to the current context. - // - // Return Value: - // None. + // Context - Supplies a pointer to the current context. { - STRING MessageHeader; // // Write the MSR // - - UINT32 msr = m->u.ReadWriteMsr.Msr; - UINT32 hi = m->u.ReadWriteMsr.DataValueHigh, lo = m->u.ReadWriteMsr.DataValueLow; - - __asm { - mov ecx, msr; - mov edx, hi; - mov eax, lo; - wrmsr; - } - - m->ReturnStatus = STATUS_SUCCESS; + m->ReturnStatus = KdpWriteMsr(m->ReadWriteMsr.Msr, + m->ReadWriteMsr.DataValueLow, + m->ReadWriteMsr.DataValueHigh) + ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; // // Set the actual number of bytes written, initialize the message header, @@ -1323,7 +1269,7 @@ KdpWriteMachineSpecificRegister( return; } - + static int wcslen(WCHAR *pwz) { int len = 0; @@ -1345,83 +1291,41 @@ static WCHAR * trim(WCHAR *pwz) return pwzBeg; } -static void KdpFakeOutPsLoadedModuleList() +KLDR_DATA_TABLE_ENTRY_WITH_NAME KernelEntry; +KLDR_DATA_TABLE_ENTRY_WITH_NAME HalEntry; + +static void KdpFakeOutPsLoadedModuleList(Class_Microsoft_Singularity_Hal_Platform *platform) { - UINT8* pbImage = (UINT8*)g_pBootInfo->DumpAddr32; - PMINIDUMP_HEADER pHeader = (PMINIDUMP_HEADER)(pbImage + 0); + // + // If the debug information has not been filled in by the loader + // we need to reconstruct the loader list from the bootinfo data, + // including the hal and the kernel modules + // + KdVersionBlock.PsLoadedModuleList = SIGN_EXTEND(&PsLoadedModuleList); + KdVersionBlock.KernBase = SIGN_EXTEND(platform->KernelDllBase); -#define VERBOSE 1 -#if VERBOSE - printf("KdpFakeOutPsLoadedModuleList (%p):\n" - " BootInfo at %p MINIDUMP_HEADER at %p\n", - &PsLoadedModuleList, g_pBootInfo, pHeader); + KernelEntry.DllBase = (void *) platform->KernelDllBase; + KernelEntry.CheckSum = 0; + KernelEntry.TimeDateStamp = 0; + KernelEntry.LoadCount = 1; + KernelEntry.SizeOfImage = (uint32)platform->KernelDllSize; + +#if ISA_IX86 + memcpy(KernelEntry.wzName, L"kernel.x86", sizeof(KernelEntry.wzName)); +#elif ISA_IX64 + memcpy(KernelEntry.wzName, L"kernel.x64", sizeof(KernelEntry.wzName)); +#elif ISA_ARM + memcpy(KernelEntry.wzName, L"kernel.arm", sizeof(KernelEntry.wzName)); #endif - // Need to build a data structure which looks like NT's PsLoadedModuleList + RtlInitUnicodeString(&KernelEntry.BaseDllName, KernelEntry.wzName, 20); + RtlInitUnicodeString(&KernelEntry.FullDllName, KernelEntry.wzName, 20); + InitializeListHead(&PsLoadedModuleList); - - PMINIDUMP_DIRECTORY pDir - = (PMINIDUMP_DIRECTORY)(pbImage + pHeader->StreamDirectoryRva); - - for (UINT i = 0; i < pHeader->NumberOfStreams; i++) { - if (pDir[i].StreamType == ModuleListStream) { - KdMinidumpModuleList = (PMINIDUMP_MODULE_LIST)(pbImage + pDir[i].Location.Rva); - break; - } - } - if (KdMinidumpModuleList == NULL) { -#if VERBOSE - printf("Couldn't find module list.\n"); -#endif - return; - } - - KdVersionBlock.KernBase = (ULONG64)KdMinidumpModuleList->Modules[0].BaseOfImage; - -#if VERBOSE - printf("MODULE_LIST: %d entries\n", KdMinidumpModuleList->NumberOfModules); -#endif - -#if 1 - // We manual include these modules to allow debugging very early in the boot cycle. - for (UINT m = 0; m < KdMinidumpModuleList->NumberOfModules && - m < (sizeof(KdModuleKernelEntry)/sizeof(KdModuleKernelEntry[0])); m++) { - - PMINIDUMP_MODULE pModule = &KdMinidumpModuleList->Modules[m]; - KLDR_DATA_TABLE_ENTRY_WITH_NAME *pEntry = &KdModuleKernelEntry[KdModuleKernelUsed++]; - - WCHAR *name = trim((WCHAR *)((pbImage + pModule->ModuleNameRva)+4)); - USHORT nlen = (USHORT)2 * wcslen(name); - if (nlen > sizeof(pEntry->wzName)) { - nlen = sizeof(pEntry->wzName); - } - - pEntry->DllBase = (PVOID *)pModule->BaseOfImage; - pEntry->CheckSum = pModule->CheckSum; - pEntry->TimeDateStamp = pModule->TimeDateStamp; - pEntry->LoadCount = 1; - pEntry->SizeOfImage = pModule->SizeOfImage; - memcpy(pEntry->wzName, name, nlen); - RtlInitUnicodeString(&pEntry->BaseDllName, pEntry->wzName, nlen); - RtlInitUnicodeString(&pEntry->FullDllName, pEntry->wzName, nlen); // Write back on self. - -#if VERBOSE - printf("%4d: BaseOfImage %8lx SizeOfImage %8x ModuleNameRva %8x\n", - m, - pModule->BaseOfImage, - pModule->SizeOfImage, - pModule->ModuleNameRva); - printf(" ModuleName: %ls (%p)\n", - pEntry->BaseDllName.Buffer, - pEntry); -#endif - - InsertTailList(&PsLoadedModuleList, &pEntry->InLoadOrderLinks); - } -#endif + InsertTailList(&PsLoadedModuleList, &KernelEntry.InLoadOrderLinks); } -VOID +void KdpSysGetVersion( PDBGKD_GET_VERSION64 Version ) @@ -1433,16 +1337,13 @@ KdpSysGetVersion( // // Arguments: // Version - Supplies the structure to fill in - // - // Return Value: - // None. { *Version = KdVersionBlock; } static -VOID +void KdpGetVersion( IN PDBGKD_MANIPULATE_STATE64 m ) @@ -1454,16 +1355,13 @@ KdpGetVersion( // // Arguments: // m - Supplies the state manipulation message. - // - // Return Value: - // None. { STRING messageHeader; messageHeader.Length = sizeof(*m); messageHeader.Buffer = (PCHAR)m; - KdpSysGetVersion(&m->u.GetVersion64); + KdpSysGetVersion(&m->GetVersion64); // // the usual stuff @@ -1480,17 +1378,17 @@ KdpGetVersion( return; } // KdGetVersion -static inline ULONG min(ULONG a, ULONG b) +static inline UINT32 min(UINT32 a, UINT32 b) { return a < b ? a : b; } static -VOID +void KdpReadControlSpace( IN PDBGKD_MANIPULATE_STATE64 m, IN PSTRING AdditionalData, - IN Struct_Microsoft_Singularity_X86_ThreadContext * x86Context + IN Struct_Microsoft_Singularity_Isal_SpillContext * Context ) // Routine Description: // This function is called in response of a read control space state @@ -1501,104 +1399,59 @@ KdpReadControlSpace( // m - Supplies the state manipulation message. // AdditionalData - Supplies any additional data for the message. // Context - Supplies the current context. - // - // Return Value: - // None. { - PDBGKD_READ_MEMORY64 a = &m->u.ReadMemory; + PDBGKD_READ_MEMORY64 a = &m->ReadMemory; STRING MessageHeader; - ULONG Length; + PVOID Source = NULL; + UINT32 Length = 0; + UINT32 Actual = 0; + UINT32 Isa = (UINT32)a->TargetBaseAddress; MessageHeader.Length = sizeof(*m); MessageHeader.Buffer = (PCHAR)m; ASSERT(AdditionalData->Length == 0); - Length = min(min(a->TransferCount, - PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64)), - sizeof(KPROCESSOR_STATE) - (ULONG)a->TargetBaseAddress); + KDDBG2(" rctl base=%x, len=%x\n", Isa, a->TransferCount); - if ((a->TargetBaseAddress + Length <= sizeof(KPROCESSOR_STATE)) && - (m->Processor < (ULONG)KeNumberProcessors)) { + if (m->Processor < (UINT32)KeNumberProcessors) { PKPROCESSOR_STATE ProcessorState = &KdpProcessorState[m->Processor]; - if (a->TargetBaseAddress < sizeof(CONTEXT)) { - // Need to update the thread context information. - KdSingularityToWindbgContext(x86Context, &ProcessorState->ContextFrame); + + switch (Isa) { + case X86_DEBUG_CONTROL_SPACE_KSPECIAL: + case DEBUG_CONTROL_SPACE_KSPECIAL: + KdpReadSpecialRegisters(&ProcessorState->SpecialRegisters, Context); + Source = &ProcessorState->SpecialRegisters; + Length = sizeof(ProcessorState->SpecialRegisters); + break; } - if (a->TargetBaseAddress + Length > sizeof(CONTEXT)) { - // Need to update the processor context information. - // ASSERT(m->Processor == 0); // MP support broken. - ProcessorState->SpecialRegisters.Cr2 = x86Context->cr2; - ProcessorState->SpecialRegisters.Cr3 = (ULONG)x86Context->cr3; - KSPECIAL_REGISTERS *pksp = &ProcessorState->SpecialRegisters; - - __asm { - mov ebx, pksp; - - mov eax, cr0; - mov [ebx].Cr0, eax; - mov eax, cr3; - mov [ebx].Cr3, eax; - _emit 0x0f; // mov eax,cr4 - _emit 0x20; - _emit 0xe0; - mov [ebx].Cr4, eax; - - // XXX save TR should save segment regs as well. - str ax; - mov [ebx].Tr, ax; -#if 0 - mov eax, dr0; - mov [ebx].KernelDr0, eax; - mov eax, dr1; - mov [ebx].KernelDr1, eax; - mov eax, dr2; - mov [ebx].KernelDr2, eax; - mov eax, dr3; - mov [ebx].KernelDr3, eax; - mov eax, dr6; - mov [ebx].KernelDr6, eax; - mov eax, dr7; - mov [ebx].KernelDr7, eax; -#endif + if (Source != NULL) { + if (Length > a->TransferCount) { + Length = a->TransferCount; } + m->ReturnStatus = KdpCopyToPtr(AdditionalData->Buffer, Source, Length, &Actual); - const Struct_Microsoft_Singularity_CpuInfo* cpuInfo = &g_pBootInfo->Cpu0 + m->Processor; - ProcessorState->SpecialRegisters.Gdtr.Pad = cpuInfo->GdtPtr.pad; - ProcessorState->SpecialRegisters.Gdtr.Limit = cpuInfo->GdtPtr.limit; - ProcessorState->SpecialRegisters.Gdtr.Base = cpuInfo->GdtPtr.addr; - - ProcessorState->SpecialRegisters.Idtr.Pad = g_idt.pad; - ProcessorState->SpecialRegisters.Idtr.Limit = g_idt.limit; - ProcessorState->SpecialRegisters.Idtr.Base = g_idt.addr; + KDDBG2(" rctl: copy src=%p, len=%x, Actual=%x, return status=%x\n", + Source, Length, Actual, m->ReturnStatus); } - - m->ReturnStatus = KdpCopyToPtr(AdditionalData->Buffer, - ((uint8*)ProcessorState) + a->TargetBaseAddress, - Length, - &Length); } else { + KDDBG("ReadControl: proc %d unknown control type (%d)\n", m->Processor, Isa); m->ReturnStatus = STATUS_UNSUCCESSFUL; - Length = 0; } - AdditionalData->Length = (USHORT)Length; - a->ActualBytesRead = Length; - - KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE, - &MessageHeader, - AdditionalData, - &KdpContext); + AdditionalData->Length = (UINT16)Actual; + a->ActualBytesRead = Actual; + KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE, &MessageHeader, AdditionalData, &KdpContext); } static -VOID +void KdpWriteControlSpace( IN PDBGKD_MANIPULATE_STATE64 m, IN PSTRING AdditionalData, - IN Struct_Microsoft_Singularity_X86_ThreadContext * x86Context + IN Struct_Microsoft_Singularity_Isal_SpillContext * Context ) // Routine Description: // This function is called in response of a write control space state @@ -1608,116 +1461,180 @@ KdpWriteControlSpace( // Arguments: // m - Supplies the state manipulation message. // AdditionalData - Supplies any additional data for the message. - // x86Context - Supplies the current context. - // - // Return Value: - // None. + // Context - Supplies the current context. { - PDBGKD_WRITE_MEMORY64 a = &m->u.WriteMemory; - ULONG Length; + PDBGKD_WRITE_MEMORY64 a = &m->WriteMemory; STRING MessageHeader; + PVOID Dest = NULL; // Pointer to bytes available to target. + UINT32 Length = 0; // # of bytes available to target. + UINT32 Isa = (UINT32)a->TargetBaseAddress; + PKPROCESSOR_STATE ProcessorState = NULL; MessageHeader.Length = sizeof(*m); MessageHeader.Buffer = (PCHAR)m; - Length = AdditionalData->Length; + KDDBG2(" wctl base=%x, len=%x\n", Isa, a->TransferCount); - if (((a->TargetBaseAddress + Length) <= sizeof(KPROCESSOR_STATE)) && - (m->Processor < (ULONG)KeNumberProcessors)) { - PKPROCESSOR_STATE ProcessorState = &KdpProcessorState[m->Processor]; - if (a->TargetBaseAddress < sizeof(CONTEXT)) { - // Need to update the thread context information. - KdWindbgToSingularityContext(&ProcessorState->ContextFrame, x86Context); + if (m->Processor < (UINT32)KeNumberProcessors) { + ProcessorState = &KdpProcessorState[m->Processor]; + + switch (Isa) { + case X86_DEBUG_CONTROL_SPACE_KSPECIAL: + case DEBUG_CONTROL_SPACE_KSPECIAL: + Dest = &ProcessorState->SpecialRegisters; + Length = sizeof(ProcessorState->SpecialRegisters); + break; } - if (a->TargetBaseAddress + Length > sizeof(CONTEXT)) { - // Need to update the processor context information. - KDDBG(" Writing SpecialRegisters.\n"); - ASSERT(m->Processor == 0); // MP support broken. - -#if 0 - // We don't support separate kernel data breakpoints as - // Singularity uses a single address space. - kdprintf(" dr0=%p dr1=%p dr2=%p dr3=%p dr6=%p dr7=%p\n", - (PVOID)(windbg->Dr0), - (PVOID)(windbg->Dr1), - (PVOID)(windbg->Dr2), - (PVOID)(windbg->Dr3), - (PVOID)(windbg->Dr6), - (PVOID)(windbg->Dr7)); - - KSPECIAL_REGISTERS *pksp = &ProcessorState->SpecialRegisters; - kdprintf(" cr0=%p cr2=%p cr3=%p cr4=%p\n", - pksp->Cr0, pksp->Cr2, pksp->Cr3, pksp->Cr4); - - __asm { - mov ebx, pksp; - -#if 0 - mov eax, [ebx].Cr0; - mov cr0, eax; - mov eax, [ebx].Cr2; - mov cr2, eax; - mov eax, [ebx].Cr2; - mov cr3, eax; - mov eax, [ebx].Cr4; - _emit 0x0f; // mov cr4,eax - _emit 0x22; - _emit 0xe0; -#endif - - mov eax, [ebx].KernelDr0; - mov dr0, eax; - mov eax, [ebx].KernelDr1; - mov dr1, eax; - mov eax, [ebx].KernelDr2; - mov dr2, eax; - mov eax, [ebx].KernelDr3; - mov dr3, eax; - mov eax, [ebx].KernelDr6; - mov dr6, eax; - mov eax, [ebx].KernelDr7; - mov dr7, eax; - } - kdprintf(" dr0=%p dr1=%p dr2=%p dr3=%p dr6=%p dr7=%p\n", - (PVOID)(pksp->KernelDr0), - (PVOID)(pksp->KernelDr1), - (PVOID)(pksp->KernelDr2), - (PVOID)(pksp->KernelDr3), - (PVOID)(pksp->KernelDr6), - (PVOID)(pksp->KernelDr7)); - kdprintf(" rr0=%p rr1=%p rr2=%p rr3=%p rr6=%p rr7=%p\n", - (PVOID)(pksp->Reserved[0]), - (PVOID)(pksp->Reserved[1]), - (PVOID)(pksp->Reserved[2]), - (PVOID)(pksp->Reserved[3]), - (PVOID)(pksp->Reserved[4]), - (PVOID)(pksp->Reserved[5])); -#endif - } - - m->ReturnStatus = KdpCopyFromPtr(((uint8*)ProcessorState) + a->TargetBaseAddress, - AdditionalData->Buffer, - Length, - &Length); - } else { - m->ReturnStatus = STATUS_UNSUCCESSFUL; - Length = 0; } - a->ActualBytesWritten = Length; + UINT32 Actual = 0; + if (Dest != NULL) { + if (Length > a->TransferCount) { + Length = a->TransferCount; + } - KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE, - &MessageHeader, - AdditionalData, - &KdpContext); + m->ReturnStatus = KdpCopyToPtr(Dest, AdditionalData->Buffer, Length, &Actual); + + switch (Isa) { + case X86_DEBUG_CONTROL_SPACE_KSPECIAL: + case DEBUG_CONTROL_SPACE_KSPECIAL: + KdpWriteSpecialRegisters(&ProcessorState->SpecialRegisters); + break; + } + + KDDBG2(" wctl: copy dst=%p, len=%x, Actual=%x, return status=%x\n", + Dest, Length, Actual, m->ReturnStatus); + } + else { + KDDBG("WriteControl: proc %d unknown control type (%d)\n", + m->Processor, Isa); + m->ReturnStatus = STATUS_UNSUCCESSFUL; + } + + a->ActualBytesWritten = Actual; + KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE, &MessageHeader, AdditionalData, &KdpContext); } static -VOID +void +KdpReadIoSpace( + IN PDBGKD_MANIPULATE_STATE64 m, + IN PSTRING AdditionalData, + IN Struct_Microsoft_Singularity_Isal_SpillContext * x86Context + ) + // Routine Description: + // This function is called in response of a read I/O space + // manipulation message. Its function is to read the x86 processors + // I/O space and return the result. + // + // Arguments: + // m - Supplies the state manipulation message. + // AdditionalData - Supplies any additional data for the message. + // Context - Supplies the current context. +{ + PDBGKD_READ_WRITE_IO64 a = &m->ReadWriteIo; + STRING MessageHeader; + UINT16 Target = (UINT16)a->IoAddress; + + ASSERT(AdditionalData->Length == 0); + + KDDBG(" read io base=%x, len=%x\n", Target, a->DataSize, AdditionalData->Buffer); + + // + // Zero-fill the entire value so that shorter reads + // do not leave unset bytes. + // + a->DataValue = 0; + + m->ReturnStatus = STATUS_SUCCESS; + + switch (a->DataSize) { + case 1: + a->DataValue = KdpReadWriteIoSpace(a->DataSize, 0, Target, 0); + break; + + case 2: + a->DataValue = KdpReadWriteIoSpace(a->DataSize, 0, Target, 0); + break; + + case 4: + a->DataValue = KdpReadWriteIoSpace(a->DataSize, 0, Target, 0); + break; + + default: + KDDBG("ReadIoSpace: Unrecognized size (%d)\n", a->DataSize); + m->ReturnStatus = STATUS_UNSUCCESSFUL; + break; + } + + // + // Send the response + // + MessageHeader.Length = sizeof(DBGKD_MANIPULATE_STATE64); + MessageHeader.Buffer = (PCHAR)m; + + KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE, &MessageHeader, NULL, &KdpContext); +} + +static +void +KdpWriteIoSpace( + IN PDBGKD_MANIPULATE_STATE64 m, + IN PSTRING AdditionalData, + IN Struct_Microsoft_Singularity_Isal_SpillContext * x86Context + ) + // Routine Description: + // This function is called in response of a write I/O space + // manipulation message. Its function is to write the x86 processors + // I/O space. + // + // Arguments: + // m - Supplies the state manipulation message. + // AdditionalData - Supplies any additional data for the message. + // Context - Supplies the current context. +{ + PDBGKD_READ_WRITE_IO64 a = &m->ReadWriteIo; + STRING MessageHeader; + UINT16 Target = (UINT16)a->IoAddress; + + ASSERT(AdditionalData->Length == 0); + + KDDBG(" write io base=%x, len=%x, value=%x\n", Target, a->DataSize, a->DataValue); + + m->ReturnStatus = STATUS_SUCCESS; + + switch (a->DataSize) { + case 1: + KdpReadWriteIoSpace(a->DataSize, 1, Target, a->DataValue & 0x000000FF); + break; + + case 2: + KdpReadWriteIoSpace(a->DataSize, 1, Target, a->DataValue & 0x0000FFFF); + break; + + case 4: + KdpReadWriteIoSpace(a->DataSize, 1, Target, a->DataValue); + break; + + default: + KDDBG("WriteIoSpace: Unrecognized size (%d)\n", a->DataSize); + m->ReturnStatus = STATUS_UNSUCCESSFUL; + break; + } + + // Send the reply + MessageHeader.Length = sizeof(DBGKD_MANIPULATE_STATE64); + MessageHeader.Buffer = (PCHAR)m; + + KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE, &MessageHeader, NULL, &KdpContext); +} + +static +void KdpSetContext( IN PDBGKD_MANIPULATE_STATE64 m, IN PSTRING AdditionalData, - IN Struct_Microsoft_Singularity_X86_ThreadContext * x86Context + IN Struct_Microsoft_Singularity_Isal_SpillContext * Context ) // Routine Description: // This function is called in response of a set context state @@ -1727,10 +1644,7 @@ KdpSetContext( // Arguments: // m - Supplies the state manipulation message. // AdditionalData - Supplies any additional data for the message. - // x86Context - Supplies the current context. - // - // Return Value: - // None. + // Context - Supplies the current context. { STRING MessageHeader; @@ -1739,15 +1653,12 @@ KdpSetContext( ASSERT(AdditionalData->Length == sizeof(CONTEXT)); - ASSERT(m->Processor == 0); // MP support broken. - - if ((m->Processor >= (USHORT)KeNumberProcessors) || - (KdpContextSent == FALSE)) { + if ((m->Processor >= (UINT16)KeNumberProcessors) || (KdpContextSent == FALSE)) { m->ReturnStatus = STATUS_UNSUCCESSFUL; } else { m->ReturnStatus = STATUS_SUCCESS; - KdWindbgToSingularityContext((CONTEXT*)AdditionalData->Buffer, x86Context); + KdpFromKdContext((CONTEXT*)AdditionalData->Buffer, Context); } KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE, @@ -1757,11 +1668,11 @@ KdpSetContext( } static -VOID +void KdpGetContext( IN PDBGKD_MANIPULATE_STATE64 m, IN PSTRING AdditionalData, - IN Struct_Microsoft_Singularity_X86_ThreadContext *x86Context + IN Struct_Microsoft_Singularity_Isal_SpillContext *Context ) // Routine Description: // This function is called in response of a get context state @@ -1771,10 +1682,7 @@ KdpGetContext( // Arguments: // m - Supplies the state manipulation message. // AdditionalData - Supplies any additional data for the message. - // x86Context - Supplies the current context. - // - // Return Value: - // None. + // Context - Supplies the current context. { STRING MessageHeader; @@ -1783,16 +1691,15 @@ KdpGetContext( ASSERT(AdditionalData->Length == 0); - ASSERT(m->Processor == 0); // MP support broken. - - if (m->Processor >= (USHORT)KeNumberProcessors) { + if (m->Processor >= (UINT16)KeNumberProcessors) { m->ReturnStatus = STATUS_UNSUCCESSFUL; } else { m->ReturnStatus = STATUS_SUCCESS; AdditionalData->Length = sizeof(CONTEXT); - KdSingularityToWindbgContext(x86Context, (CONTEXT*)AdditionalData->Buffer); + RtlZeroMemory(AdditionalData->Buffer, AdditionalData->Length); + KdpToKdContext(Context, (CONTEXT*)AdditionalData->Buffer); KdpContextSent = TRUE; } @@ -1803,7 +1710,7 @@ KdpGetContext( } -ULONG +UINT32 KdpAddBreakpoint( IN PVOID Address ) @@ -1822,14 +1729,16 @@ KdpAddBreakpoint( // not valid. Otherwise, the index of the assigned breakpoint table entry // plus one is returned as the function value. { - ULONG Index; + UINT32 Index; KDP_BREAKPOINT_TYPE Content; BOOL Accessible; KDDBG2("KdpAddBreakpoint(%p)\n", Address); for (Index = 0; Index < BREAKPOINT_TABLE_SIZE; Index++) { - if (KdpBreakpointTable[Index].Flags == 0) break; + if (KdpBreakpointTable[Index].Flags == 0) { + break; + } } if (Index == BREAKPOINT_TABLE_SIZE) { KDDBG("KD: ran out of breakpoints!\n"); @@ -1863,10 +1772,10 @@ KdpAddBreakpoint( BOOLEAN KdpDeleteBreakpoint( - IN ULONG Handle + IN UINT32 Handle ) { - ULONG Index = Handle - 1; + UINT32 Index = Handle - 1; KDDBG2("KD: Delete Breakpoint %d\n", Handle); if ((Handle == 0) || (Handle > BREAKPOINT_TABLE_SIZE)) { @@ -1890,11 +1799,10 @@ KdpDeleteBreakpoint( KdpBreakpointTable[Index].Address); KdpBreakpointTable[Index].Flags = 0; } - return TRUE; } -VOID +void KdpWriteBreakpoint( IN PDBGKD_MANIPULATE_STATE64 m ) @@ -1906,12 +1814,9 @@ KdpWriteBreakpoint( // Arguments: // m - Supplies the state manipulation message. // AdditionalData - Supplies any additional data for the message. - // x86Context - Supplies the current context. - // - // Return Value: - // None. + // Context - Supplies the current context. { - PDBGKD_WRITE_BREAKPOINT64 a = &m->u.WriteBreakPoint; + PDBGKD_WRITE_BREAKPOINT64 a = &m->WriteBreakPoint; STRING MessageHeader; MessageHeader.Length = sizeof(*m); @@ -1932,7 +1837,7 @@ KdpWriteBreakpoint( &KdpContext); } -VOID +void KdpRestoreBreakpoint( IN PDBGKD_MANIPULATE_STATE64 m, IN PSTRING AdditionalData @@ -1946,11 +1851,8 @@ KdpRestoreBreakpoint( // m - Supplies the state manipulation message. // AdditionalData - Supplies any additional data for the message. // Context - Supplies the current context. - // - // Return Value: - // None. { - PDBGKD_RESTORE_BREAKPOINT a = &m->u.RestoreBreakPoint; + PDBGKD_RESTORE_BREAKPOINT a = &m->RestoreBreakPoint; STRING MessageHeader; MessageHeader.Length = sizeof(*m); @@ -1970,25 +1872,19 @@ KdpRestoreBreakpoint( } static -VOID +void KdpGetStateChange( IN PDBGKD_MANIPULATE_STATE64 ManipulateState, - IN Struct_Microsoft_Singularity_X86_ThreadContext *x86Context + IN Struct_Microsoft_Singularity_Isal_SpillContext *Context ) // Routine Description: // Extract continuation control data from Manipulate_State message // // Arguments: // ManipulateState - supplies pointer to Manipulate_State packet - // x86Context - Supplies a pointer to a context record. - // - // Return Value: - // None. + // Context - Supplies a pointer to a context record. { - //PKPRCB Prcb; - //ULONG Processor; - - if (NT_SUCCESS(ManipulateState->u.Continue2.ContinueStatus)) { + if (NT_SUCCESS(ManipulateState->Continue2.ContinueStatus)) { // // If NT_SUCCESS returns TRUE, then the debugger is doing a // continue, and it makes sense to apply control changes. @@ -1996,37 +1892,20 @@ KdpGetStateChange( // to do with this exception, so control values are ignored. // - if (ManipulateState->u.Continue2.ControlSet.TraceFlag == TRUE) { - KDDBG2("KD: Warning - trace flag set\n"); - x86Context->efl |= Struct_Microsoft_Singularity_X86_EFlags_TF; - } - else { - x86Context->efl &= ~Struct_Microsoft_Singularity_X86_EFlags_TF; - } - - UINT32 _dr7 = ManipulateState->u.Continue2.ControlSet.Dr7; - x86Context->dr7 = ManipulateState->u.Continue2.ControlSet.Dr7; + KdpSetControlSet(&ManipulateState->Continue2.ControlSet, Context); #if 0 - __asm { - mov eax, 0; - mov dr6, eax; - mov eax, _dr7; - mov dr7, eax; - } -#endif -#if 0 - for (Processor = 0; Processor < (ULONG)KeNumberProcessors; Processor++) { + for (Processor = 0; Processor < (UINT32)KeNumberProcessors; Processor++) { Prcb = KiProcessorBlock[Processor]; Prcb->ProcessorState.SpecialRegisters.KernelDr7 = - ManipulateState->u.Continue2.ControlSet.Dr7; + ManipulateState->Continue2.ControlSet.Dr7; Prcb->ProcessorState.SpecialRegisters.KernelDr6 = 0L; } - if (ManipulateState->u.Continue2.ControlSet.CurrentSymbolStart != 1) { - KdpCurrentSymbolStart = ManipulateState->u.Continue2.ControlSet.CurrentSymbolStart; - KdpCurrentSymbolEnd = ManipulateState->u.Continue2.ControlSet.CurrentSymbolEnd; + if (ManipulateState->Continue2.ControlSet.CurrentSymbolStart != 1) { + KdpCurrentSymbolStart = ManipulateState->Continue2.ControlSet.CurrentSymbolStart; + KdpCurrentSymbolEnd = ManipulateState->Continue2.ControlSet.CurrentSymbolEnd; } #endif } @@ -2034,10 +1913,10 @@ KdpGetStateChange( KCONTINUE_STATUS KdpSendWaitContinue( - IN ULONG OutPacketType, + IN UINT32 OutPacketType, IN PSTRING OutMessageHeader, IN PSTRING OutMessageData OPTIONAL, - IN OUT Struct_Microsoft_Singularity_X86_ThreadContext *x86Context + IN OUT Struct_Microsoft_Singularity_Isal_SpillContext *Context ) // Routine Description: // This function sends a packet, and then waits for a continue message. @@ -2060,11 +1939,11 @@ KdpSendWaitContinue( // success, Otherwise, a value of FALSE is returned. { - ULONG Length; + UINT32 Length; STRING MessageData; STRING MessageHeader; DBGKD_MANIPULATE_STATE64 ManipulateState; - ULONG ReturnCode; + UINT32 ReturnCode; // NTSTATUS Status; // KCONTINUE_STATUS ContinueStatus; @@ -2107,8 +1986,6 @@ KdpSendWaitContinue( // Wait for State Manipulate Packet without timeout. // - KDDBG("KdpSendWait::\r"); - do { ReturnCode = KdReceivePacket( PACKET_TYPE_KD_STATE_MANIPULATE, @@ -2117,7 +1994,7 @@ KdpSendWaitContinue( &Length, &KdpContext ); - KDDBG2("KdReceivePacket returned %d\n", ReturnCode); + KDDBG3("KdReceivePacket returned %d\n", ReturnCode); if (ReturnCode == KDP_PACKET_RESEND) { goto ResendPacket; } @@ -2132,83 +2009,99 @@ KdpSendWaitContinue( switch (ManipulateState.ApiNumber) { case DbgKdReadVirtualMemoryApi: - KDDBG("KdpSendWait::KdReadVirtualMemory (%8p..%8p) (%d bytes)\n", - (ULONG_PTR)ManipulateState.u.ReadMemory.TargetBaseAddress, - (ULONG_PTR)ManipulateState.u.ReadMemory.TargetBaseAddress + - ManipulateState.u.ReadMemory.TransferCount, - ManipulateState.u.ReadMemory.TransferCount); - KdpReadVirtualMemory(&ManipulateState,&MessageData); + KDDBG("KdReadVirt(%p-%p)\n", + (ULONG_PTR)ManipulateState.ReadMemory.TargetBaseAddress, + (ULONG_PTR)ManipulateState.ReadMemory.TargetBaseAddress + + ManipulateState.ReadMemory.TransferCount); + KdpReadVirtualMemory(&ManipulateState, &MessageData); break; #if 0 case DbgKdReadVirtualMemory64Api: - KdpReadVirtualMemory64(&ManipulateState,&MessageData); + KdpReadVirtualMemory64(&ManipulateState, &MessageData); break; #endif case DbgKdWriteVirtualMemoryApi: - KDDBG("KdpSendWait::KdWriteVirtualMemory(%8p..%8p)\n", - (ULONG_PTR)ManipulateState.u.WriteMemory.TargetBaseAddress, - (ULONG_PTR)ManipulateState.u.WriteMemory.TargetBaseAddress + - ManipulateState.u.WriteMemory.TransferCount); - KdpWriteVirtualMemory(&ManipulateState,&MessageData); + KDDBG("KdWritVirt(%p-%p)\n", + (ULONG_PTR)ManipulateState.WriteMemory.TargetBaseAddress, + (ULONG_PTR)ManipulateState.WriteMemory.TargetBaseAddress + + ManipulateState.WriteMemory.TransferCount); + KdpWriteVirtualMemory(&ManipulateState, &MessageData); break; #if 0 case DbgKdWriteVirtualMemory64Api: - KdpWriteVirtualMemory64(&ManipulateState,&MessageData); + KdpWriteVirtualMemory64(&ManipulateState, &MessageData); break; #endif case DbgKdGetVersionApi: - KDDBG("KdpSendWait::KdGetVersion()\n"); + KDDBG("KdGetVersion()\n"); KdpGetVersion(&ManipulateState); break; case DbgKdGetContextApi: - KDDBG("KdpSendWait::KdGetContext(p=%x)\n", ManipulateState.Processor); - KdpGetContext(&ManipulateState,&MessageData,x86Context); + KDDBG("KdGetContext(p=%x)\n", ManipulateState.Processor); + KdpGetContext(&ManipulateState, &MessageData, Context); break; case DbgKdReadControlSpaceApi: - KDDBG("KdpSendWait::KdReadControlSpace (%8p..%8p p=%x)\n", - (ULONG_PTR)ManipulateState.u.ReadMemory.TargetBaseAddress, - (ULONG_PTR)ManipulateState.u.ReadMemory.TargetBaseAddress + - ManipulateState.u.ReadMemory.TransferCount, + KDDBG("KdReadCtrl(%p-%p p=%x)\n", + (ULONG_PTR)ManipulateState.ReadMemory.TargetBaseAddress, + (ULONG_PTR)ManipulateState.ReadMemory.TargetBaseAddress + + ManipulateState.ReadMemory.TransferCount, (ULONG_PTR)ManipulateState.Processor); - KdpReadControlSpace(&ManipulateState,&MessageData,x86Context); + KdpReadControlSpace(&ManipulateState, &MessageData, Context); break; case DbgKdWriteControlSpaceApi: - KDDBG("KdpSendWait::KdWriteControlSpace (%8p..%8p p=%x)\n", - (ULONG_PTR)ManipulateState.u.WriteMemory.TargetBaseAddress, - (ULONG_PTR)ManipulateState.u.WriteMemory.TargetBaseAddress + - ManipulateState.u.WriteMemory.TransferCount, + KDDBG("KdWriteCtrl(%p-%p p=%x)\n", + (ULONG_PTR)ManipulateState.WriteMemory.TargetBaseAddress, + (ULONG_PTR)ManipulateState.WriteMemory.TargetBaseAddress + + ManipulateState.WriteMemory.TransferCount, (ULONG_PTR)ManipulateState.Processor); - KdpWriteControlSpace(&ManipulateState,&MessageData,x86Context); + KdpWriteControlSpace(&ManipulateState, &MessageData, Context); + break; + + case DbgKdReadIoSpaceApi: + KDDBG("KdReadIoSpace(%p size=%x p=%x)\n", + (ULONG_PTR)ManipulateState.ReadWriteIo.IoAddress, + (ULONG_PTR)ManipulateState.ReadWriteIo.DataSize, + (ULONG_PTR)ManipulateState.Processor); + KdpReadIoSpace(&ManipulateState, &MessageData, Context); + break; + + case DbgKdWriteIoSpaceApi: + KDDBG("KdWriteIoSpace(%p size=%x value=%x, p=%x)\n", + (ULONG_PTR)ManipulateState.ReadWriteIo.IoAddress, + (ULONG_PTR)ManipulateState.ReadWriteIo.DataSize, + (ULONG_PTR)ManipulateState.ReadWriteIo.DataValue, + (ULONG_PTR)ManipulateState.Processor); + KdpWriteIoSpace(&ManipulateState, &MessageData, Context); break; case DbgKdSetContextApi: - KDDBG("KdpSendWait::KdSetContext(p=%x)\n", ManipulateState.Processor); - KdpSetContext(&ManipulateState,&MessageData,x86Context); + KDDBG("KdSetContext(p=%x)\n", ManipulateState.Processor); + KdpSetContext(&ManipulateState, &MessageData, Context); break; case DbgKdWriteBreakPointApi: - KDDBG("KdpSendWait::KdWriteBreakPoint(%p)\n", - ManipulateState.u.WriteBreakPoint.BreakPointAddress); + KDDBG("KdWriteBreak(%p)\n", + ManipulateState.WriteBreakPoint.BreakPointAddress); KdpWriteBreakpoint(&ManipulateState); break; case DbgKdRestoreBreakPointApi: - if (ManipulateState.u.RestoreBreakPoint.BreakPointHandle < 0x8 || - ManipulateState.u.RestoreBreakPoint.BreakPointHandle > 0x1e) { - KDDBG("KdpSendWait::KdRestoreBreakpoint(h=%x)\n", - ManipulateState.u.RestoreBreakPoint.BreakPointHandle); + if (ManipulateState.RestoreBreakPoint.BreakPointHandle < 0x8 || + ManipulateState.RestoreBreakPoint.BreakPointHandle > 0x1e) { + KDDBG("KdRestoreBreak(%x)\n", + ManipulateState.RestoreBreakPoint.BreakPointHandle); } - KdpRestoreBreakpoint(&ManipulateState,&MessageData); + KdpRestoreBreakpoint(&ManipulateState, &MessageData); break; case DbgKdContinueApi: - KDDBG("KdpSendWait::KdContinue(ContinueStatus=%08x)\n", - ManipulateState.u.Continue.ContinueStatus); - if (NT_SUCCESS(ManipulateState.u.Continue.ContinueStatus)) { + KDDBG("KdContinue(%08x)\n", + ManipulateState.Continue.ContinueStatus); + if (NT_SUCCESS(ManipulateState.Continue.ContinueStatus)) { return ContinueSuccess; } else { @@ -2217,34 +2110,34 @@ KdpSendWaitContinue( break; case DbgKdContinueApi2: - KDDBG("KdpSendWait::KdContinue2(ContinueStatus=%08x)\n", - ManipulateState.u.Continue2.ContinueStatus); - if (NT_SUCCESS(ManipulateState.u.Continue2.ContinueStatus)) { - KDDBG("KdpSendWait::KdpGetStateChange()\n"); - KdpGetStateChange(&ManipulateState,x86Context); + KDDBG("KdContinue2(%08x)\n", + ManipulateState.Continue2.ContinueStatus); + if (NT_SUCCESS(ManipulateState.Continue2.ContinueStatus)) { + KDDBG("KdpGetStateChange()\n"); + KdpGetStateChange(&ManipulateState, Context); return ContinueSuccess; } else { - KDDBG("KdpSendWait::ContinueError!\n"); + KDDBG("KdpContinue2 Error!\n"); return ContinueError; } break; case DbgKdReadPhysicalMemoryApi: - KDDBG("KdpSendWait::KdReadPhysicalMemory (%8p..%8p)\n", - (ULONG_PTR)ManipulateState.u.ReadMemory.TargetBaseAddress, - (ULONG_PTR)ManipulateState.u.ReadMemory.TargetBaseAddress + - ManipulateState.u.ReadMemory.TransferCount); - // KdpReadPhysicalMemory(&ManipulateState,&MessageData,x86Context); - KdpReadPhysicalMemory(&ManipulateState,&MessageData); + KDDBG("KdReadPhys(%8p-%8p)\n", + (ULONG_PTR)ManipulateState.ReadMemory.TargetBaseAddress, + (ULONG_PTR)ManipulateState.ReadMemory.TargetBaseAddress + + ManipulateState.ReadMemory.TransferCount); + // KdpReadPhysicalMemory(&ManipulateState, &MessageData, Context); + KdpReadPhysicalMemory(&ManipulateState, &MessageData); break; case DbgKdWritePhysicalMemoryApi: - KdpWritePhysicalMemory(&ManipulateState,&MessageData); - KDDBG("KdpSendWait::KdWritePhysicalMemory (%8p..%8p)\n", - (ULONG_PTR)ManipulateState.u.WriteMemory.TargetBaseAddress, - (ULONG_PTR)ManipulateState.u.WriteMemory.TargetBaseAddress + - ManipulateState.u.ReadMemory.TransferCount); + KdpWritePhysicalMemory(&ManipulateState, &MessageData); + KDDBG("KdWritePhys(%8p-%8p)\n", + (ULONG_PTR)ManipulateState.WriteMemory.TargetBaseAddress, + (ULONG_PTR)ManipulateState.WriteMemory.TargetBaseAddress + + ManipulateState.ReadMemory.TransferCount); break; case DbgKdSwitchProcessor: @@ -2257,119 +2150,15 @@ KdpSendWaitContinue( break; case DbgKdReadMachineSpecificRegister: - KdpReadMachineSpecificRegister(&ManipulateState,&MessageData,x86Context); + KdpReadMachineSpecificRegister(&ManipulateState, &MessageData, Context); break; case DbgKdWriteMachineSpecificRegister: - KdpWriteMachineSpecificRegister(&ManipulateState,&MessageData,x86Context); + KdpWriteMachineSpecificRegister(&ManipulateState, &MessageData, Context); break; -#if 0 // Haven't implemented most of the protocol yet! - case DbgKdCheckLowMemoryApi: - KdpCheckLowMemory (&ManipulateState); - break; - - case DbgKdReadControlSpaceApi: - KdpReadControlSpace(&ManipulateState,&MessageData,x86Context); - break; - - case DbgKdWriteControlSpaceApi: - KdpWriteControlSpace(&ManipulateState,&MessageData,x86Context); - break; - - case DbgKdReadIoSpaceApi: - KdpReadIoSpace(&ManipulateState,&MessageData,x86Context); - break; - - case DbgKdWriteIoSpaceApi: - KdpWriteIoSpace(&ManipulateState,&MessageData,x86Context); - break; - - case DbgKdReadIoSpaceExtendedApi: - KdpReadIoSpaceExtended(&ManipulateState,&MessageData,x86Context); - break; - - case DbgKdWriteIoSpaceExtendedApi: - KdpWriteIoSpaceExtended(&ManipulateState,&MessageData,x86Context); - break; - - case DbgKdGetBusDataApi: - KdpGetBusData(&ManipulateState,&MessageData,x86Context); - break; - - case DbgKdSetBusDataApi: - KdpSetBusData(&ManipulateState,&MessageData,x86Context); - break; - - case DbgKdRebootApi: - HalReturnToFirmware(HalRebootRoutine); - break; - -#if defined(i386) - case DbgKdSetSpecialCallApi: - KdSetSpecialCall(&ManipulateState,x86Context); - break; - - case DbgKdClearSpecialCallsApi: - KdClearSpecialCalls(); - break; - - case DbgKdSetInternalBreakPointApi: - KdSetInternalBreakpoint(&ManipulateState); - break; - - case DbgKdGetInternalBreakPointApi: - KdGetInternalBreakpoint(&ManipulateState); - break; - - case DbgKdClearAllInternalBreakpointsApi: - KdpNumInternalBreakpoints = 0; - break; - -#endif // i386 - - case DbgKdCauseBugCheckApi: - KdpCauseBugCheck(&ManipulateState); - break; - - case DbgKdPageInApi: - KdpNotSupported(&ManipulateState); - break; - - case DbgKdWriteBreakPointExApi: - Status = KdpWriteBreakPointEx(&ManipulateState, - &MessageData, - x86Context); - if (Status) { - ManipulateState.ApiNumber = DbgKdContinueApi; - ManipulateState.u.Continue.ContinueStatus = Status; - return ContinueError; - } - break; - - case DbgKdRestoreBreakPointExApi: - KdpRestoreBreakPointEx(&ManipulateState,&MessageData,x86Context); - break; - - case DbgKdSearchMemoryApi: - KdpSearchMemory(&ManipulateState, &MessageData, x86Context); - break; - - case DbgKdFillMemoryApi: - KdpFillMemory(&ManipulateState, &MessageData, x86Context); - break; - - case DbgKdQueryMemoryApi: - KdpQueryMemory(&ManipulateState, x86Context); - break; - - // - // Invalid message. - // -#endif // XXX Haven't implemented most of the protocol yet! - default: - kdprintf("KdpSendWaitContinue: unrecognized API number 0x%x\n", ManipulateState.ApiNumber); + KDDBG2("Kd Bad API (0x%x)\n", ManipulateState.ApiNumber); MessageData.Length = 0; ManipulateState.ReturnStatus = STATUS_UNSUCCESSFUL; KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE, &MessageHeader, &MessageData, &KdpContext); @@ -2396,7 +2185,7 @@ KdPrintString( // FALSE otherwise. { - ULONG Length; + UINT32 Length; STRING MessageData; STRING MessageHeader; DBGKD_DEBUG_IO DebugIo; @@ -2404,7 +2193,6 @@ KdPrintString( if (KdDebuggerNotPresent) { return FALSE; } - KdpLock(); // Move the output string to the message buffer. @@ -2434,14 +2222,14 @@ KdPrintString( // DebugIo.ApiNumber = DbgKdPrintStringApi; DebugIo.ProcessorLevel = KeProcessorLevel; - DebugIo.Processor = (USHORT)GetCurrentProcessorNumber(); - DebugIo.u.PrintString.LengthOfString = Length; + DebugIo.Processor = (UINT16)KdpGetCurrentProcessorNumber(); + DebugIo.PrintString.LengthOfString = Length; MessageHeader.Length = sizeof(DBGKD_DEBUG_IO); MessageHeader.Buffer = (PCHAR)&DebugIo; // Construct the print string data and data descriptor. // - MessageData.Length = (USHORT)Length; + MessageData.Length = (UINT16)Length; MessageData.Buffer = (PCHAR) KdpMessageBuffer; // Send packet to the kernel debugger on the host machine. @@ -2456,194 +2244,45 @@ KdPrintString( return FALSE; } - -// This should be fixed. -VOID -KdPutChar( - CHAR c -) -{ - static char _KdpDebugStringBuf[KDP_MESSAGE_BUFFER_SIZE]; - static STRING KdpDebugString = { 0, KDP_MESSAGE_BUFFER_SIZE, (PCHAR)&_KdpDebugStringBuf }; - - if (KdpInDebugger) { - koutput(NULL, c); - return; - } - - KdpDebugString.Buffer[KdpDebugString.Length++] = c; - if (c == '\n' || KdpDebugString.Length == KdpDebugString.MaximumLength) { - KdPrintString(&KdpDebugString, FALSE); - KdpDebugString.Length = 0; - } -} - /////////////////////////////////////////////////////// Methods Exposed to C#. // bool Class_Microsoft_Singularity_DebugStub:: -g_Trap(Struct_Microsoft_Singularity_X86_ThreadContext *context, bool firstChance) +g_Trap(Struct_Microsoft_Singularity_Isal_SpillContext *context, int id) { EXCEPTION_RECORD64 er; bool handled; RtlZeroMemory(&er, sizeof(er)); + if (KdDebuggerNotPresent) { + return true; + } + KdpLock(); - // Breakpoints: - switch (context->num) { + KDDBG("CPU %d: In g_Trap num=%d ...", KdCurrentCpuId(), id); - case Struct_Microsoft_Singularity_X86_EVectors_SingleStep: - er.ExceptionCode = STATUS_SINGLE_STEP; - er.ExceptionAddress = (ULONG64)context->eip; - // context->efl &= ~Struct_Microsoft_Singularity_X86_EFlags_TF; - break; + KdDebugTrapData * trapData = KdpIsDebugTrap(context, id); - case Struct_Microsoft_Singularity_X86_EVectors_Breakpoint: - context->eip -= 1; - er.ExceptionCode = STATUS_BREAKPOINT; - er.NumberParameters = 1; - er.ExceptionInformation[0] = BREAKPOINT_BREAK; - er.ExceptionAddress = (ULONG64)context->eip; - break; - - case Struct_Microsoft_Singularity_X86_EVectors_IllegalInstruction: - er.ExceptionCode = STATUS_ILLEGAL_INSTRUCTION; - break; - - case Struct_Microsoft_Singularity_X86_EVectors_PageFault: - KDDBG("KD: 0x0E %d\n", context->num); - er.ExceptionCode = STATUS_ACCESS_VIOLATION; - er.ExceptionAddress = (UINT64)context->eip; - er.NumberParameters = 1; - er.ExceptionInformation[0] = context->cr2; - break; - - case Struct_Microsoft_Singularity_X86_EVectors_FirstChanceException: { - KdDebugTrapData *trapData = (KdDebugTrapData *) (context->eax); - switch(trapData->tag) { - case KdDebugTrapData::FIRST_CHANCE_EXCEPTION: - context->eax = trapData->u.firstChanceException.throwAddr; - KDDBG("KD: First chance C# exception\n"); - // er.ExceptionCode = STATUS_CPP_EH_EXCEPTION; //0xe06d7363; - er.ExceptionCode = STATUS_VCPP_EXCEPTION; //0x8000ff1f; - er.ExceptionAddress = (UINT64)context->eip; - er.NumberParameters = 1; - er.ExceptionInformation[0] = BREAKPOINT_BREAK; - break; - case KdDebugTrapData::LOADED_BINARY: - KDDBG("KD: Loaded binary\n"); - KdpUnlock(); - LoadedBinary(context); - return true; - case KdDebugTrapData::UNLOADED_BINARY: - KDDBG("KD: Unloaded binary\n"); - KdpUnlock(); - UnloadedBinary(context); - return true; - default: - KDDBG("KD: Unexpected interrupt %d\n", context->num); - er.ExceptionCode = 0x80000000 + context->num; - er.ExceptionAddress = (UINT64)context->eip; - break; - } - break; + if (trapData != NULL) { + if (trapData->tag == KdDebugTrapData::LOADED_BINARY) { + KDDBG("KD: Loaded binary %ls\n", trapData->loadedBinary.name); + KdpUnlock(); + LoadedBinary(trapData, context); + return true; + } + else if (trapData->tag == KdDebugTrapData::UNLOADED_BINARY) { + KDDBG("KD: Unloaded binary\n"); + KdpUnlock(); + UnloadedBinary(trapData, context); + return true; } - - case Struct_Microsoft_Singularity_X86_EVectors_SecondChanceException: - KDDBG("KD: Second chance C# exception\n"); - er.ExceptionCode = STATUS_VCPP_EXCEPTION; - er.ExceptionAddress = (UINT64)context->eip; - break; - - case Struct_Microsoft_Singularity_X86_EVectors_DebuggerBreakRequest: - KDDBG("KD: Debugger ctrl-break\n"); - er.ExceptionCode = STATUS_BREAKPOINT; - er.ExceptionInformation[0] = BREAKPOINT_BREAK; - er.ExceptionAddress = (UINT64)context->eip; - break; - - default: - KDDBG("KD: Unexpected interrupt %d\n", context->num); - er.ExceptionCode = 0x80000000 + context->num; - er.ExceptionAddress = (UINT64)context->eip; - break; - } - KDDBG("Trap: Context at %p\n", context); - KDDBG(" CXT=%08x THR=%08x\n", - context, context->_thread); - KDDBG(" EIP=%08x EFL=%08x ERR=%08x CR2=%08x\n", - context->eip, context->efl, context->err, context->cr2); - KDDBG(" EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n", - context->eax, context->ebx, context->ecx, context->edx); - KDDBG(" ESP=%08x EBP=%08x ESI=%08x EDI=%08x\n", - context->esp, context->ebp, context->esi, context->edi); - -#if 0 - kdprintf("Exception EIPs = %08x", er.ExceptionAddress); - uintptr ebp = context->ebp; - for (int i = 0; i < 3 && ebp >= Struct_Microsoft_Singularity_BootInfo_PHYSICAL_DISABLED; i++) { - - uintptr next = ((uintptr *)ebp)[0]; - uintptr code = ((uintptr *)ebp)[1]; - - kdprintf(" %08x", code); - ebp = next; - } - kdprintf("\n"); -#endif + KdpConvertTrapToException(&er, context, id); KdpEnter(); - handled = (KdpReportExceptionStateChange(&er, context, firstChance) == ContinueSuccess); - if (context->num == Struct_Microsoft_Singularity_X86_EVectors_SingleStep && - (context->efl & Struct_Microsoft_Singularity_X86_EFlags_TF) == 0) { -#if 0 - kdprintf("Continuing to eip=%08x [efl=%08x]\n", context->eip, context->efl); -#endif - -#if 0 - if (context->eip != 0 && *(uint8*)(context->eip) == 0x9c) { - // we always override into single-step mode if the next instruction is pushfd. - context->efl |= Struct_Microsoft_Singularity_X86_EFlags_TF; - kdprintf("Continuing to eip=%08x [efl=%08x] ****\n", context->eip, context->efl); -#if 0 - kdprintf(" CXT=%08x THR=%08x REG=%02x\n", - context, context->_thread, context->regs); - kdprintf(" EIP=%08x EFL=%08x ERR=%08x CR2=%08x\n", - context->eip, context->efl, context->err, context->cr2); - kdprintf(" EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n", - context->eax, context->ebx, context->ecx, context->edx); - kdprintf(" ESP=%08x EBP=%08x ESI=%08x EDI=%08x\n", - context->esp, context->ebp, context->esi, context->edi); - kdprintf(" DR0=%08x DR1=%08x DR2=%08x DR3=%08x\n", - context->dr0, context->dr1, context->dr2, context->dr3); - kdprintf(" DR6=%08x DR7=%08x DR2=%08x DR3=%08x\n", - context->dr6, context->dr7, context->dr2, context->dr3); -#endif - } -#endif - } - -#if 0 - if (context->num == Struct_Microsoft_Singularity_X86_EVectors_SingleStep && - context->eip != 0 && *(uint8*)(context->eip) == 0x9c) { - kdprintf(" CXT=%08x THR=%08x REG=%02x\n", - context, context->_thread, context->regs); - kdprintf(" EIP=%08x EFL=%08x ERR=%08x CR2=%08x\n", - context->eip, context->efl, context->err, context->cr2); - kdprintf(" EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n", - context->eax, context->ebx, context->ecx, context->edx); - kdprintf(" ESP=%08x EBP=%08x ESI=%08x EDI=%08x\n", - context->esp, context->ebp, context->esi, context->edi); - kdprintf(" DR0=%08x DR1=%08x DR2=%08x DR3=%08x\n", - context->dr0, context->dr1, context->dr2, context->dr3); - kdprintf(" DR6=%08x DR7=%08x DR2=%08x DR3=%08x\n", - context->dr6, context->dr7, context->dr2, context->dr3); - } - kdprintf("-- Trap num=0x%02x eip=%08x\n", context->num, context->eip); -#endif + handled = (KdpReportExceptionStateChange(&er, context, trapData != NULL) == ContinueSuccess); KdpLeave(); @@ -2653,14 +2292,21 @@ g_Trap(Struct_Microsoft_Singularity_X86_ThreadContext *context, bool firstChance } bool Class_Microsoft_Singularity_DebugStub:: -g_TrapForProcessorSwitch(Struct_Microsoft_Singularity_X86_ThreadContext *context) +g_TrapForProcessorSwitch(Struct_Microsoft_Singularity_Isal_SpillContext *context) { EXCEPTION_RECORD64 er; + KDDBG("CPU %d: TrapForProcessorSwitch:\n", KdCurrentCpuId()); + RtlZeroMemory(&er, sizeof(er)); er.ExceptionCode = STATUS_WAKE_SYSTEM_DEBUGGER; - er.ExceptionRecord = (ULONG64)&er; - er.ExceptionAddress = (ULONG64)context->eip; + er.ExceptionRecord = (UINT64)&er; + +#if ISA_IX86 || ISA_IX64 + er.ExceptionAddress = context->ip; +#elif ISA_ARM + er.ExceptionAddress = context->pc; +#endif // KdSave(FALSE); KCONTINUE_STATUS status = KdpReportExceptionStateChange(&er, context, true); @@ -2671,21 +2317,14 @@ g_TrapForProcessorSwitch(Struct_Microsoft_Singularity_X86_ThreadContext *context void Class_Microsoft_Singularity_DebugStub::g_AddProcessor(int cpuId) { KdpLock(); - KeNumberProcessors = cpuId + 1; - KdpUnlock(); -} - -void Class_Microsoft_Singularity_DebugStub::g_RevertToUniprocessor() -{ - KdpLock(); - KeNumberProcessors = 1; + if (KeNumberProcessors <= cpuId) { + KeNumberProcessors = cpuId + 1; + } KdpUnlock(); } bool Class_Microsoft_Singularity_DebugStub::g_PollForBreak() { - // KdpLock(); - // Don't re-enter debugger if already debugging. if (KdpInDebugger) { return FALSE; @@ -2704,14 +2343,11 @@ bool Class_Microsoft_Singularity_DebugStub::g_PollForBreak() return true; } + KdpLock(); bool success = KdPollBreakIn(); - // KdpUnlock(); - return success; -} + KdpUnlock(); -void Class_Microsoft_Singularity_DebugStub::g_Break() -{ - __asm int 3; + return success; } bool Class_Microsoft_Singularity_DebugStub::g_LoadedBinary(UIntPtr baseAddress, @@ -2736,51 +2372,42 @@ bool Class_Microsoft_Singularity_DebugStub::g_LoadedBinary(UIntPtr baseAddress, uint32 timestamp, bool silent) { - KdDebugTrapData trapData, *trapDataPtr = &trapData; - trapData.tag = KdDebugTrapData::LOADED_BINARY; - trapData.u.loadedBinary.baseAddress = baseAddress; - trapData.u.loadedBinary.bytes = bytes; - trapData.u.loadedBinary.name = name; - trapData.u.loadedBinary.checksum = checksum; - trapData.u.loadedBinary.timestamp = timestamp; - trapData.u.loadedBinary.silent = silent; - - // Call LoadedBinary via an __asm int 29: - __asm { - mov eax, trapDataPtr; - int 29; + if (KdDebuggerNotPresent) { + return true; } - return trapData.u.loadedBinary.ret; + KdDebugTrapData trapData; + trapData.tag = KdDebugTrapData::LOADED_BINARY; + trapData.loadedBinary.baseAddress = baseAddress; + trapData.loadedBinary.bytes = bytes; + trapData.loadedBinary.name = name; + trapData.loadedBinary.checksum = checksum; + trapData.loadedBinary.timestamp = timestamp; + trapData.loadedBinary.silent = silent; + KdNotifyTrap(&trapData); + + return trapData.loadedBinary.ret; } -static void LoadedBinary(Struct_Microsoft_Singularity_X86_ThreadContext *context) +static void LoadedBinary(KdDebugTrapData *trapData, + Struct_Microsoft_Singularity_Isal_SpillContext *context) { - KdDebugTrapData *trapData = (KdDebugTrapData *) (context->eax); - UIntPtr baseAddress = trapData->u.loadedBinary.baseAddress; - UIntPtr bytes = trapData->u.loadedBinary.bytes; - UIntPtr nameof = trapData->u.loadedBinary.name; - uint32 checksum = trapData->u.loadedBinary.checksum; - uint32 timestamp = trapData->u.loadedBinary.timestamp; - bool silent = trapData->u.loadedBinary.silent; + UIntPtr baseAddress = trapData->loadedBinary.baseAddress; + UIntPtr bytes = trapData->loadedBinary.bytes; + UIntPtr nameof = trapData->loadedBinary.name; + uint32 checksum = trapData->loadedBinary.checksum; + uint32 timestamp = trapData->loadedBinary.timestamp; + bool silent = trapData->loadedBinary.silent; KLDR_DATA_TABLE_ENTRY_WITH_NAME *pEntry; bool good = false; WCHAR * name = trim((WCHAR *)nameof); - USHORT nlen = (USHORT)2 * wcslen(name); + UINT16 nlen = (UINT16)2 * wcslen(name); if (nlen > sizeof(pEntry->wzName)) { nlen = sizeof(pEntry->wzName); } KdpLock(); -#if 0 // Debug module names - if (name != NULL) { - kdprintf("LoadedBinary(%08lx: %ls)\n", (uint64)(uintptr)name, name); - } - else { - kdprintf("LoadedBinary(%08lx)\n", (uint64)(uintptr)name); - } -#endif for (int i = 0; i < ARRAYOF(KdModuleKernelEntry); i++) { pEntry = &KdModuleKernelEntry[i]; @@ -2790,21 +2417,11 @@ static void LoadedBinary(Struct_Microsoft_Singularity_X86_ThreadContext *context pEntry->CheckSum = checksum; pEntry->TimeDateStamp = timestamp; pEntry->LoadCount = 1; - pEntry->SizeOfImage = (uintptr)bytes; + pEntry->SizeOfImage = (UINT32)bytes; memcpy(pEntry->wzName, name, nlen); RtlInitUnicodeString(&pEntry->FullDllName, name, nlen); RtlInitUnicodeString(&pEntry->BaseDllName, name, nlen); -#if 0 - printf("----: BaseOfImage %8lx SizeOfImage %8x ModuleName %8x\n", - (uint64)(uintptr)baseAddress, - (uintptr)bytes, - (uintptr)pEntry->BaseDllName.Buffer); - printf(" ModuleName: %ls (%p)\n", - pEntry->BaseDllName.Buffer, - pEntry); -#endif - // We should insert in the right order in the list... InsertTailList(&PsLoadedModuleList, &pEntry->InLoadOrderLinks); good = true; @@ -2816,52 +2433,50 @@ static void LoadedBinary(Struct_Microsoft_Singularity_X86_ThreadContext *context if (good) { KdpReportLoadSymbolsStateChange(pEntry->BaseDllName.Buffer, pEntry->BaseDllName.Length, - (ULONG64)baseAddress, - (ULONG)0, + (UINT64)baseAddress, + (UINT32)0, checksum, - (LONG)bytes, + (INT32)bytes, FALSE, context); } else { KdpReportLoadSymbolsStateChange(NULL, 0, - (ULONG64)baseAddress, - (ULONG)0, + (UINT64)baseAddress, + (UINT32)0, checksum, - (LONG)bytes, + (INT32)bytes, FALSE, context); } } KdpUnlock(); - trapData->u.loadedBinary.ret = good; + trapData->loadedBinary.ret = good; } -bool Class_Microsoft_Singularity_DebugStub:: -g_UnloadedBinary(UIntPtr baseAddress, bool silent) +bool Class_Microsoft_Singularity_DebugStub::g_UnloadedBinary(UIntPtr baseAddress, + bool silent) { - KdDebugTrapData trapData, *trapDataPtr = &trapData; - trapData.tag = KdDebugTrapData::UNLOADED_BINARY; - trapData.u.unloadedBinary.baseAddress = baseAddress; - trapData.u.unloadedBinary.silent = silent; - - // Call UnloadedBinary via an __asm int 29: - __asm { - mov eax, trapDataPtr; - int 29; + if (KdDebuggerNotPresent) { + return true; } - return trapData.u.unloadedBinary.ret; + KdDebugTrapData trapData; + trapData.tag = KdDebugTrapData::UNLOADED_BINARY; + trapData.unloadedBinary.baseAddress = baseAddress; + trapData.unloadedBinary.silent = silent; + KdNotifyTrap(&trapData); + + return trapData.unloadedBinary.ret; } -static void UnloadedBinary(Struct_Microsoft_Singularity_X86_ThreadContext *context) +static void UnloadedBinary(KdDebugTrapData *trapData, + Struct_Microsoft_Singularity_Isal_SpillContext *context) { - KdDebugTrapData *trapData = (KdDebugTrapData *) (context->eax); - UIntPtr baseAddress = trapData->u.unloadedBinary.baseAddress; - bool silent = trapData->u.unloadedBinary.silent; - + UIntPtr baseAddress = trapData->unloadedBinary.baseAddress; + bool silent = trapData->unloadedBinary.silent; bool good = false; KdpLock(); @@ -2884,8 +2499,8 @@ static void UnloadedBinary(Struct_Microsoft_Singularity_X86_ThreadContext *conte KdpReportLoadSymbolsStateChange(pEntry->BaseDllName.Buffer, pEntry->BaseDllName.Length, - (ULONG64)baseAddress, - (ULONG)0, + (UINT64)baseAddress, + (UINT32)0, 0, 0, TRUE, @@ -2895,7 +2510,7 @@ static void UnloadedBinary(Struct_Microsoft_Singularity_X86_ThreadContext *conte } KdpUnlock(); - trapData->u.unloadedBinary.ret = good; + trapData->unloadedBinary.ret = good; } bool Class_Microsoft_Singularity_DebugStub::g_IsDebuggerPresent() @@ -2907,7 +2522,8 @@ bool Class_Microsoft_Singularity_DebugStub::g_IsDebuggerPresent() // Note: Leaves the lock held by the client code, so it had better be trustworthy. void Class_Microsoft_Singularity_DebugStub::g_PrintBegin(WCHAR **buffer, int *length) { - if (KdDebuggerNotPresent) { + if (KdDebuggerNotPresent && !KdAlwaysPrintOutput) { + *buffer = NULL; *length = 0; return; @@ -2923,7 +2539,7 @@ void Class_Microsoft_Singularity_DebugStub::g_PrintBegin(WCHAR **buffer, int *le // Note: Assumes the lock is held, so the client code had better be trustworthy. void Class_Microsoft_Singularity_DebugStub::g_PrintComplete(WCHAR *buffer, int length) { - if (KdDebuggerNotPresent) { + if (KdDebuggerNotPresent && !KdAlwaysPrintOutput) { return; } @@ -2949,8 +2565,8 @@ void Class_Microsoft_Singularity_DebugStub::g_PrintComplete(WCHAR *buffer, int l DBGKD_DEBUG_IO DebugIo; DebugIo.ApiNumber = DbgKdPrintStringApi; DebugIo.ProcessorLevel = KeProcessorLevel; - DebugIo.Processor = (USHORT)GetCurrentProcessorNumber(); - DebugIo.u.PrintString.LengthOfString = length; + DebugIo.Processor = (UINT16)KdpGetCurrentProcessorNumber(); + DebugIo.PrintString.LengthOfString = length; STRING MessageHeader; MessageHeader.Length = sizeof(DBGKD_DEBUG_IO); @@ -2960,7 +2576,7 @@ void Class_Microsoft_Singularity_DebugStub::g_PrintComplete(WCHAR *buffer, int l // Construct the print string data and data descriptor. // STRING MessageData; - MessageData.Length = (USHORT)length; + MessageData.Length = (UINT16)length; MessageData.Buffer = KdpMessageBuffer; // @@ -2976,13 +2592,13 @@ void Class_Microsoft_Singularity_DebugStub::g_PrintComplete(WCHAR *buffer, int l void Class_Microsoft_Singularity_DebugStub::g_Print(WCHAR *buf, int len) { - if (KdDebuggerNotPresent) { - return; - } - WCHAR *buffer; int length; + if (KdDebuggerNotPresent && !KdAlwaysPrintOutput) { + return; + } + g_PrintBegin(&buffer, &length); g_PrintComplete(buf, len); } @@ -2997,5 +2613,90 @@ void Class_Microsoft_Singularity_DebugStub::g_Print(WCHAR *buf) g_Print(buf, len); } +void Class_Microsoft_Singularity_DebugStub::g_RevertToUniprocessor() +{ + KdpLock(); + KeNumberProcessors = 1; + KdpUnlock(); +} + +void Class_Microsoft_Singularity_KernelDebugger_Kd::g_SendPacket( + /*Struct_Microsoft_Singularity_KernelDebugger_KdPacketType*/ uint32 PacketType, + uint8* MessageHeaderBuffer, + int32 MessageHeaderLength, + uint8* MessageDataBuffer, + int32 MessageDataLength) +{ + ASSERT(MessageHeaderLength >= 0); + ASSERT(MessageHeaderLength < 0x10000); + ASSERT(MessageDataLength >= 0); + ASSERT(MessageDataLength < 0x10000); + + STRING MessageHeaderDesc; + MessageHeaderDesc.Buffer = (PCHAR)MessageHeaderBuffer; + MessageHeaderDesc.Length = (uint16)MessageHeaderLength; + MessageHeaderDesc.MaximumLength = (uint16)MessageHeaderLength; + + STRING MessageDataDesc; + MessageDataDesc.Buffer = (PCHAR)MessageDataBuffer; + MessageDataDesc.Length = (uint16)MessageDataLength; + MessageDataDesc.MaximumLength = (uint16)MessageDataLength; + + KdSendPacket( + PacketType, + &MessageHeaderDesc, + &MessageDataDesc, + &KdpContext); +} + +/*Struct_Microsoft_Singularity_KernelDebugger_KdStatus*/ uint32 +Class_Microsoft_Singularity_KernelDebugger_Kd::g_ReceivePacket( + /*Struct_Microsoft_Singularity_KernelDebugger_KdPacketType*/ uint32 PacketType, + uint8* MessageHeaderBuffer, + int32 MessageHeaderLength, + uint8* MessageDataBuffer, + int32 MessageDataBufferLength, + OUT int32* MessageDataLength) +{ + UINT32 DataLength; + + ASSERT(MessageDataLength != NULL); + *MessageDataLength = 0; + + STRING MessageHeaderDesc; + MessageHeaderDesc.Buffer = (PCHAR)MessageHeaderBuffer; + MessageHeaderDesc.Length = 0; + MessageHeaderDesc.MaximumLength = (uint16)MessageHeaderLength; + + STRING MessageDataDesc; + MessageDataDesc.Buffer = (PCHAR)MessageDataBuffer; + MessageDataDesc.Length = 0; + MessageDataDesc.MaximumLength = (uint16)MessageDataBufferLength; + + DataLength = (uint32)MessageDataBufferLength; + + uint32 Status = KdReceivePacket( + PacketType, + &MessageHeaderDesc, + &MessageDataDesc, + &DataLength, + &KdpContext + ); + + *MessageDataLength = (int)DataLength; + + return Status; +} + +void Class_Microsoft_Singularity_KernelDebugger_Kd::g_Lock() +{ + KdpLock(); +} + +void Class_Microsoft_Singularity_KernelDebugger_Kd::g_Unlock() +{ + KdpUnlock(); +} + // ///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/halkd.h b/base/Kernel/Native/halkd.h index a35971d..9957005 100644 --- a/base/Kernel/Native/halkd.h +++ b/base/Kernel/Native/halkd.h @@ -1,147 +1,218 @@ +/////////////////////////////////////////////////////////////////////////////// // -// Copyright (c) Microsoft Corporation. All rights reserved. +// Microsoft Research Singularity // -// halkd.h: runtime support for debugging +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: halkd.h // - - -#define FALSE 0 -#define TRUE 1 #define IN #define OUT #define OPTIONAL #define CONST const -#define ASSERT(x) -#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0) +#if PTR_SIZE_32 +#define SIGN_EXTEND(x) ((UINT64)(INT64)(INT32)(x)) +#elif PTR_SIZE_64 +#define SIGN_EXTEND(x) ((UINT64)(x)) +#endif // NT Compat types typedef BOOL BOOLEAN; -typedef UINT16 USHORT; -typedef INT32 LONG; -typedef UINT32 ULONG, *PULONG; -typedef UINT64 ULONG64; -typedef INT64 LONG64; typedef char *PCHAR; typedef char CHAR; -typedef void VOID; typedef UCHAR *PUCHAR; -typedef WCHAR *LPWSTR, *PWSTR; -typedef /*_W64 */ long LONG_PTR, *PLONG_PTR; -typedef DWORD NTSTATUS; +typedef UINT32 NTSTATUS; + typedef struct _STRING { - USHORT Length; - USHORT MaximumLength; - PCHAR Buffer; + UINT16 Length; + UINT16 MaximumLength; + CHAR * Buffer; } STRING, *PSTRING; + typedef struct _UNICODE_STRING { - USHORT Length; - USHORT MaximumLength; - PWSTR Buffer; + UINT16 Length; + UINT16 MaximumLength; + WCHAR * Buffer; } UNICODE_STRING; + typedef struct _LIST_ENTRY { struct _LIST_ENTRY *Flink; struct _LIST_ENTRY *Blink; } LIST_ENTRY, *PLIST_ENTRY; -// -// Some quick macros to avoid having to edit too much NT code -// -#define RtlZeroMemory(base, len) memset((base), 0, (len)) -#define KdpQuickMoveMemory(dst,src,len) memcpy((dst),(src),(len)) - -// Read memory from an untrusted pointer into a trusted buffer. -#define KdpCopyFromPtr(Dst, Src, Size, Done) \ - KdpCopyMemoryChunks((ULONG_PTR)(Src), Dst, Size, 0, \ - MMDBG_COPY_UNSAFE, Done) -// Write memory from a trusted buffer through an untrusted pointer. -#define KdpCopyToPtr(Dst, Src, Size, Done) \ - KdpCopyMemoryChunks((ULONG_PTR)(Dst), Src, Size, 0, \ - MMDBG_COPY_WRITE | MMDBG_COPY_UNSAFE, Done) - -#define RtlInitUnicodeString(string, source, length) \ - { (string)->Buffer = (source); (string)->Length = (string)->MaximumLength = (length); } - -#define FORCEINLINE __inline - -VOID -FORCEINLINE -InitializeListHead( - IN PLIST_ENTRY ListHead - ) -{ - ListHead->Flink = ListHead->Blink = ListHead; -} - -BOOLEAN -FORCEINLINE -RemoveEntryList( - IN PLIST_ENTRY Entry - ) -{ - PLIST_ENTRY Blink; - PLIST_ENTRY Flink; - - Flink = Entry->Flink; - Blink = Entry->Blink; - Blink->Flink = Flink; - Flink->Blink = Blink; - return (BOOLEAN)(Flink == Blink); -} - -VOID -FORCEINLINE -InsertTailList( - IN PLIST_ENTRY ListHead, - IN PLIST_ENTRY Entry - ) -{ - PLIST_ENTRY Blink; - - Blink = ListHead->Blink; - Entry->Flink = ListHead; - Entry->Blink = Blink; - Blink->Flink = Entry; - ListHead->Blink = Entry; -} - -//#define KdpCopyFromPtr(dst, src, size, done) { memcpy((dst),(src),(size)); *(done)=(size); } -//#define KdpCopyToPtr(dst, src, size, done) { memcpy((dst),(src),(size)); *(done)=(size); } - - - //====================================================================== // Selected structs and defines used by the kernel debugger // -typedef void *PNON_PAGED_DEBUG_INFO; - typedef struct _KLDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; - PVOID __Unused1; - PVOID __Unused2; - PVOID __Unused3; - PNON_PAGED_DEBUG_INFO NonPagedDebugInfo; - PVOID DllBase; - PVOID EntryPoint; - ULONG SizeOfImage; + PVOID __Unused1; + PVOID __Unused2; + PVOID __Unused3; + PVOID NonPagedDebugInfo; + PVOID DllBase; + PVOID EntryPoint; + UINT32 SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; - ULONG Flags; - USHORT LoadCount; - USHORT __Unused5; - PVOID SectionPointer; - ULONG CheckSum; - // ULONG padding on IA64 - ULONG TimeDateStamp; + UINT32 Flags; + UINT16 LoadCount; + UINT16 __Unused5; + PVOID SectionPointer; + UINT32 CheckSum; + // UINT32 padding on IA64 + UINT32 TimeDateStamp; // PVOID LoadedImports; - PVOID __Unused6; + PVOID __Unused6; } KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY; //====================================================================== // Selected structs and defines used by the KD protocol // +///////////////////////////////////////////////////////////////////////// X86. +// +#define X86_REPORT_INCLUDES_SEGS 0x0001 +// Indicates the current CS is a standard 32-bit flat segment. +// This allows the debugger to avoid retrieving the +// CS descriptor to see if it's 16-bit code or not. +// Note that the V86 flag in EFlags must also be checked +// when determining the code type. +#define X86_REPORT_STANDARD_CS 0x0002 + +#define X86_DEBUG_CONTROL_SPACE_KSPECIAL 716 + +//////////////////////////////////////////////////////////////////////// IA64. +// +#define IA64_DBGKD_CONTROL_SET_CONTINUE_NONE 0x0000 +#define IA64_DBGKD_CONTROL_SET_CONTINUE_TRACE_INSTRUCTION 0x0001 +#define IA64_DBGKD_CONTROL_SET_CONTINUE_TRACE_TAKEN_BRANCH 0x0002 + +// +// Aliases to keep old-timers comfortable with new-fangled code. +// +#define ALPHA_DBGKD_CONTROL_REPORT Struct_Microsoft_Singularity_Kd_AlphaKdControlReport +#define ALPHA_DBGKD_CONTROL_SET Struct_Microsoft_Singularity_Kd_AlphaKdControlSet +#define ARM_CONTEXT Struct_Microsoft_Singularity_Kd_ArmContext +#define ARM_DBGKD_CONTROL_REPORT Struct_Microsoft_Singularity_Kd_ArmKdControlReport +#define ARM_DBGKD_CONTROL_SET Struct_Microsoft_Singularity_Kd_ArmKdControlSet +#define ARM_KPROCESSOR_STATE Struct_Microsoft_Singularity_Kd_ArmKProcessorState +#define ARM_KSPECIAL_REGISTERS Struct_Microsoft_Singularity_Kd_ArmKSpecialRegisters +#define ARM_RUNTIME_FUNCTION Struct_Microsoft_Singularity_Kd_ArmRuntimeFunction +#define ARM_RUNTIME_FUNCTION_EXCEPTION Struct_Microsoft_Singularity_Kd_ArmRuntimeFunctionException +#define IA64_DBGKD_CONTROL_REPORT Struct_Microsoft_Singularity_Kd_Ia64KdControlReport +#define IA64_DBGKD_CONTROL_SET Struct_Microsoft_Singularity_Kd_Ia64KdControlSet +#define X64_CONTEXT Struct_Microsoft_Singularity_Kd_X64Context +#define X64_DBGKD_CONTROL_REPORT Struct_Microsoft_Singularity_Kd_X64KdControlReport +#define X64_DBGKD_CONTROL_SET Struct_Microsoft_Singularity_Kd_X64KdControlSet +#define X64_DESCRIPTOR Struct_Microsoft_Singularity_Kd_X64Descriptor +#define X64_KPROCESSOR_STATE Struct_Microsoft_Singularity_Kd_X64KProcessorState +#define X64_KSPECIAL_REGISTERS Struct_Microsoft_Singularity_Kd_X64KSpecialRegisters +#define X64_VECTOR_REGISTERS Struct_Microsoft_Singularity_Kd_X64VectorRegisters +#define X86_387_REGISTER Struct_Microsoft_Singularity_Kd_X86Fp387Register +#define X86_387_SAVE_AREA Struct_Microsoft_Singularity_Kd_X86Fp387SaveArea +#define X86_CONTEXT Struct_Microsoft_Singularity_Kd_X86Context +#define X86_DBGKD_CONTROL_REPORT Struct_Microsoft_Singularity_Kd_X86KdControlReport +#define X86_DBGKD_CONTROL_SET Struct_Microsoft_Singularity_Kd_X86KdControlSet +#define X86_DESCRIPTOR Struct_Microsoft_Singularity_Kd_X86Descriptor +#define X86_KPROCESSOR_STATE Struct_Microsoft_Singularity_Kd_X86KProcessorState +#define X86_KSPECIAL_REGISTERS Struct_Microsoft_Singularity_Kd_X86KSpecialRegisters +#define X86_SEGMENT_REGISTERS Struct_Microsoft_Singularity_Kd_X86SegmentRegisters +#define X86_UNALIGNED_128 Struct_Microsoft_Singularity_Kd_X86Unaligned128 +#define X86_XMM_SAVE_AREA32 Struct_Microsoft_Singularity_Kd_X86XmmSaveArea32 +#define EXCEPTION_RECORD64 Struct_Microsoft_Singularity_Kd_ExceptionRecord64 + +//////////////////////////////////////////////////////////////////// All ISAs. +// + +#define CONTEXT_X86 0x00010000 // i386, etc. +#define CONTEXT_X64 0x00100000 // x64 context record is quite different +#define CONTEXT_ARM 0x00100000 + +#define CONTEXT_EXCEPTION_ACTIVE 0x08000000 +#define CONTEXT_SERVICE_ACTIVE 0x10000000 +#define CONTEXT_EXCEPTION_REQUEST 0x40000000 +#define CONTEXT_EXCEPTION_REPORTING 0x80000000 + +#if ISA_IX86 + +#define CONTEXT_CONTROL (CONTEXT_X86 | 0x1L) // SS:SP, CS:IP, FLAGS, BP +#define CONTEXT_INTEGER (CONTEXT_X86 | 0x2L) // AX, BX, CX, DX, SI, DI +#define CONTEXT_SEGMENTS (CONTEXT_X86 | 0x4L) // DS, ES, FS, GS +#define CONTEXT_FLOATING_POINT (CONTEXT_X86 | 0x8L) // 387 state +#define CONTEXT_DEBUG_REGISTERS (CONTEXT_X86 | 0x10L) // DB 0-3,6,7 +#define CONTEXT_EXTENDED_REGISTERS (CONTEXT_X86 | 0x20L) // cpu specific extensions + +#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS) + +#define CONTEXT_ALL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | CONTEXT_EXTENDED_REGISTERS) + +typedef X86_CONTEXT CONTEXT, *PCONTEXT; +typedef X86_KSPECIAL_REGISTERS KSPECIAL_REGISTERS, *PKSPECIAL_REGISTERS; +typedef X86_KPROCESSOR_STATE KPROCESSOR_STATE, *PKPROCESSOR_STATE; +typedef X86_DBGKD_CONTROL_REPORT DBGKD_CONTROL_REPORT, *PDBGKD_CONTROL_REPORT; +typedef X86_DBGKD_CONTROL_SET DBGKD_CONTROL_SET; + +#define KDP_BREAKPOINT_TYPE uint8 +#define KDP_BREAKPOINT_BUFFER sizeof(uint8) +#define KDP_BREAKPOINT_ALIGN 0 +#define KDP_BREAKPOINT_INSTR_ALIGN 0 +#define KDP_BREAKPOINT_VALUE 0xcc // int 3 + +#elif ISA_IX64 + +#define CONTEXT_CONTROL (CONTEXT_X64 | 0x1L) +#define CONTEXT_INTEGER (CONTEXT_X64 | 0x2L) +#define CONTEXT_SEGMENTS (CONTEXT_X64 | 0x4L) +#define CONTEXT_FLOATING_POINT (CONTEXT_X64 | 0x8L) +#define CONTEXT_DEBUG_REGISTERS (CONTEXT_X64 | 0x10L) + +#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT) + +#define CONTEXT_ALL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS) + +typedef X64_CONTEXT CONTEXT, *PCONTEXT; +typedef X64_KSPECIAL_REGISTERS KSPECIAL_REGISTERS, *PKSPECIAL_REGISTERS; +typedef X64_KPROCESSOR_STATE KPROCESSOR_STATE, *PKPROCESSOR_STATE; +typedef X64_DBGKD_CONTROL_REPORT DBGKD_CONTROL_REPORT, *PDBGKD_CONTROL_REPORT; +typedef X64_DBGKD_CONTROL_SET DBGKD_CONTROL_SET; + +#define KDP_BREAKPOINT_TYPE uint8 +#define KDP_BREAKPOINT_BUFFER sizeof(uint8) +#define KDP_BREAKPOINT_ALIGN 0 +#define KDP_BREAKPOINT_INSTR_ALIGN 0 +#define KDP_BREAKPOINT_VALUE 0xcc // int 3 + +#elif ISA_ARM + +#define CONTEXT_CONTROL (CONTEXT_ARM | 0x1L) +#define CONTEXT_INTEGER (CONTEXT_ARM | 0x2L) +#define CONTEXT_FLOATING_POINT (CONTEXT_ARM | 0x4L) +#define CONTEXT_DEBUG_REGISTERS (CONTEXT_ARM | 0x8L) + +#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT) + +#define CONTEXT_ALL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS) + +typedef ARM_CONTEXT CONTEXT, *PCONTEXT; +typedef ARM_KSPECIAL_REGISTERS KSPECIAL_REGISTERS, *PKSPECIAL_REGISTERS; +typedef ARM_KPROCESSOR_STATE KPROCESSOR_STATE, *PKPROCESSOR_STATE; +typedef ARM_DBGKD_CONTROL_REPORT DBGKD_CONTROL_REPORT, *PDBGKD_CONTROL_REPORT; +typedef ARM_DBGKD_CONTROL_SET DBGKD_CONTROL_SET; + +#define KDP_BREAKPOINT_TYPE UINT32 +#define KDP_BREAKPOINT_BUFFER sizeof(UINT32) +#define KDP_BREAKPOINT_ALIGN 4 +#define KDP_BREAKPOINT_INSTR_ALIGN 4 +#define KDP_BREAKPOINT_VALUE 0xefffff01 // swi 0xffff01 + +#endif + +//-----> From winnt.h#3566 + +//#pragma pack(pop) + // // Values put in ExceptionRecord.ExceptionInformation[0] // First parameter is always in ExceptionInformation[1], @@ -155,73 +226,6 @@ typedef struct _KLDR_DATA_TABLE_ENTRY { #define BREAKPOINT_UNLOAD_SYMBOLS 4 #define BREAKPOINT_COMMAND_STRING 5 - -#define CONTEXT_i386 0x00010000 // this assumes that i386 and -#define CONTEXT_i486 0x00010000 // i486 have identical context records - -// end_wx86 - -#define CONTEXT_CONTROL (CONTEXT_i386 | 0x00000001L) // SS:SP, CS:IP, FLAGS, BP -#define CONTEXT_INTEGER (CONTEXT_i386 | 0x00000002L) // AX, BX, CX, DX, SI, DI -#define CONTEXT_SEGMENTS (CONTEXT_i386 | 0x00000004L) // DS, ES, FS, GS -#define CONTEXT_FLOATING_POINT (CONTEXT_i386 | 0x00000008L) // 387 state -#define CONTEXT_DEBUG_REGISTERS (CONTEXT_i386 | 0x00000010L) // DB 0-3,6,7 -#define CONTEXT_EXTENDED_REGISTERS (CONTEXT_i386 | 0x00000020L) // cpu specific extensions - -#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER |\ - CONTEXT_SEGMENTS) - -#define CONTEXT_ALL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | CONTEXT_EXTENDED_REGISTERS) - -#define CONTEXT_TO_PROGRAM_COUNTER(Context) ((Context)->Eip) - -typedef struct _DESCRIPTOR { - USHORT Pad; - USHORT Limit; - ULONG Base; -} KDESCRIPTOR, *PKDESCRIPTOR; - -typedef struct _KSPECIAL_REGISTERS { - ULONG Cr0; - ULONG Cr2; - ULONG Cr3; - ULONG Cr4; - ULONG KernelDr0; - ULONG KernelDr1; - ULONG KernelDr2; - ULONG KernelDr3; - ULONG KernelDr6; - ULONG KernelDr7; - KDESCRIPTOR Gdtr; - KDESCRIPTOR Idtr; - USHORT Tr; - USHORT Ldtr; - ULONG Reserved[6]; -} KSPECIAL_REGISTERS, *PKSPECIAL_REGISTERS; - -// -// Processor State frame: Before a processor freezes itself, it -// dumps the processor state to the processor state frame for -// debugger to examine. -// - -typedef struct _KPROCESSOR_STATE { - struct _CONTEXT ContextFrame; - struct _KSPECIAL_REGISTERS SpecialRegisters; -} KPROCESSOR_STATE, *PKPROCESSOR_STATE; - -#if 0 // XXX We have one of these already in minidump.h -typedef struct _EXCEPTION_RECORD64 { - DWORD ExceptionCode; - DWORD ExceptionFlags; - DWORD64 ExceptionRecord; - DWORD64 ExceptionAddress; - DWORD NumberParameters; - DWORD __unusedAlignment; - DWORD64 ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; -} EXCEPTION_RECORD64, *PEXCEPTION_RECORD64; -#endif - // Don't care what these point to! typedef void *PKTRAP_FRAME; typedef void *PKEXCEPTION_FRAME; @@ -239,98 +243,47 @@ typedef enum _MODE { typedef struct _DBGKM_EXCEPTION64 { EXCEPTION_RECORD64 ExceptionRecord; - ULONG FirstChance; + UINT32 FirstChance; } DBGKM_EXCEPTION64, *PDBGKM_EXCEPTION64; - -#define DBGKD_MAXSTREAM 16 - -typedef struct _X86_DBGKD_CONTROL_REPORT { - ULONG Dr6; - ULONG Dr7; - USHORT InstructionCount; - USHORT ReportFlags; - UCHAR InstructionStream[DBGKD_MAXSTREAM]; - USHORT SegCs; - USHORT SegDs; - USHORT SegEs; - USHORT SegFs; - ULONG EFlags; -} X86_DBGKD_CONTROL_REPORT, *PX86_DBGKD_CONTROL_REPORT; - -#define X86_REPORT_INCLUDES_SEGS 0x0001 -// Indicates the current CS is a standard 32-bit flat segment. -// This allows the debugger to avoid retrieving the -// CS descriptor to see if it's 16-bit code or not. -// Note that the V86 flag in EFlags must also be checked -// when determining the code type. -#define X86_REPORT_STANDARD_CS 0x0002 - -typedef X86_DBGKD_CONTROL_REPORT DBGKD_CONTROL_REPORT; - -typedef struct _ALPHA_DBGKD_CONTROL_REPORT { - ULONG InstructionCount; - UCHAR InstructionStream[DBGKD_MAXSTREAM]; -} ALPHA_DBGKD_CONTROL_REPORT, *PALPHA_DBGKD_CONTROL_REPORT; - -typedef struct _IA64_DBGKD_CONTROL_REPORT { - ULONG InstructionCount; - UCHAR InstructionStream[DBGKD_MAXSTREAM]; -} IA64_DBGKD_CONTROL_REPORT, *PIA64_DBGKD_CONTROL_REPORT; - -typedef struct _AMD64_DBGKD_CONTROL_REPORT { - ULONG64 Dr6; - ULONG64 Dr7; - ULONG EFlags; - USHORT InstructionCount; - USHORT ReportFlags; - UCHAR InstructionStream[DBGKD_MAXSTREAM]; - USHORT SegCs; - USHORT SegDs; - USHORT SegEs; - USHORT SegFs; -} AMD64_DBGKD_CONTROL_REPORT, *PAMD64_DBGKD_CONTROL_REPORT; - -typedef struct _DBGKD_ANY_CONTROL_REPORT -{ - union - { +typedef struct _DBGKD_ANY_CONTROL_REPORT { + union { X86_DBGKD_CONTROL_REPORT X86ControlReport; - // XXX Though we only care about x86 this union must be the correct size ALPHA_DBGKD_CONTROL_REPORT AlphaControlReport; IA64_DBGKD_CONTROL_REPORT IA64ControlReport; - AMD64_DBGKD_CONTROL_REPORT Amd64ControlReport; + X64_DBGKD_CONTROL_REPORT X64ControlReport; + ARM_DBGKD_CONTROL_REPORT ARMControlReport; }; } DBGKD_ANY_CONTROL_REPORT, *PDBGKD_ANY_CONTROL_REPORT; typedef struct _DBGKD_LOAD_SYMBOLS64 { - ULONG PathNameLength; - ULONG64 BaseOfDll; - ULONG64 ProcessId; - ULONG CheckSum; - ULONG SizeOfImage; + UINT32 PathNameLength; + UINT64 BaseOfDll; + UINT64 ProcessId; + UINT32 CheckSum; + UINT32 SizeOfImage; BOOLEAN UnloadSymbols; } DBGKD_LOAD_SYMBOLS64, *PDBGKD_LOAD_SYMBOLS64; typedef struct _DBGKD_COMMAND_STRING { - ULONG Flags; - ULONG Reserved1; - ULONG64 Reserved2[7]; + UINT32 Flags; + UINT32 Reserved1; + UINT64 Reserved2[7]; } DBGKD_COMMAND_STRING, *PDBGKD_COMMAND_STRING; // Protocol version 6 state change. typedef struct _DBGKD_ANY_WAIT_STATE_CHANGE { - ULONG NewState; - USHORT ProcessorLevel; - USHORT Processor; - ULONG NumberProcessors; - ULONG64 Thread; - ULONG64 ProgramCounter; + UINT32 NewState; + UINT16 ProcessorLevel; + UINT16 Processor; + UINT32 NumberProcessors; + UINT64 Thread; + UINT64 ProgramCounter; union { DBGKM_EXCEPTION64 Exception; DBGKD_LOAD_SYMBOLS64 LoadSymbols; DBGKD_COMMAND_STRING CommandString; - } u; + }; // The ANY control report is unioned here to // ensure that this structure is always large // enough to hold any possible state change. @@ -340,25 +293,32 @@ typedef struct _DBGKD_ANY_WAIT_STATE_CHANGE { }; } DBGKD_ANY_WAIT_STATE_CHANGE, *PDBGKD_ANY_WAIT_STATE_CHANGE; +#define DEBUG_CONTROL_SPACE_KSPECIAL 2 typedef struct _DBGKD_READ_MEMORY64 { - ULONG64 TargetBaseAddress; - ULONG TransferCount; - ULONG ActualBytesRead; + UINT64 TargetBaseAddress; + UINT32 TransferCount; + UINT32 ActualBytesRead; } DBGKD_READ_MEMORY64, *PDBGKD_READ_MEMORY64; typedef struct _DBGKD_WRITE_MEMORY64 { - ULONG64 TargetBaseAddress; - ULONG TransferCount; - ULONG ActualBytesWritten; + UINT64 TargetBaseAddress; + UINT32 TransferCount; + UINT32 ActualBytesWritten; } DBGKD_WRITE_MEMORY64, *PDBGKD_WRITE_MEMORY64; +typedef struct _DBGKD_READ_WRITE_IO64 { + UINT64 IoAddress; + UINT32 DataSize; // 1, 2, 4 + UINT32 DataValue; +} DBGKD_READ_WRITE_IO64, *PDBGKD_READ_WRITE_IO64; + // // Response is a get context message with a full context record following // typedef struct _DBGKD_GET_CONTEXT { - ULONG Unused; + UINT32 Unused; } DBGKD_GET_CONTEXT, *PDBGKD_GET_CONTEXT; // @@ -366,30 +326,22 @@ typedef struct _DBGKD_GET_CONTEXT { // typedef struct _DBGKD_SET_CONTEXT { - ULONG ContextFlags; + UINT32 ContextFlags; } DBGKD_SET_CONTEXT, *PDBGKD_SET_CONTEXT; // // Define breakpoint table entry structure. // - -// XXX Breakpoints are x86 specific -#define KDP_BREAKPOINT_TYPE UCHAR -#define KDP_BREAKPOINT_BUFFER sizeof(UCHAR) -#define KDP_BREAKPOINT_ALIGN 0 -#define KDP_BREAKPOINT_INSTR_ALIGN 0 -#define KDP_BREAKPOINT_VALUE 0xcc - #define KD_BREAKPOINT_IN_USE 0x00000001 #define KD_BREAKPOINT_NEEDS_WRITE 0x00000002 #define KD_BREAKPOINT_SUSPENDED 0x00000004 #define KD_BREAKPOINT_NEEDS_REPLACE 0x00000008 typedef struct _BREAKPOINT_ENTRY { - ULONG Flags; + UINT32 Flags; ULONG_PTR DirectoryTableBase; - PVOID Address; + PVOID Address; KDP_BREAKPOINT_TYPE Content; } BREAKPOINT_ENTRY, *PBREAKPOINT_ENTRY; @@ -397,106 +349,65 @@ typedef struct _BREAKPOINT_ENTRY { typedef struct _DBGKD_WRITE_BREAKPOINT32 { - ULONG BreakPointAddress; - ULONG BreakPointHandle; + UINT32 BreakPointAddress; + UINT32 BreakPointHandle; } DBGKD_WRITE_BREAKPOINT32, *PDBGKD_WRITE_BREAKPOINT32; typedef struct _DBGKD_WRITE_BREAKPOINT64 { - ULONG64 BreakPointAddress; - ULONG BreakPointHandle; + UINT64 BreakPointAddress; + UINT32 BreakPointHandle; } DBGKD_WRITE_BREAKPOINT64, *PDBGKD_WRITE_BREAKPOINT64; typedef struct _DBGKD_RESTORE_BREAKPOINT { - ULONG BreakPointHandle; + UINT32 BreakPointHandle; } DBGKD_RESTORE_BREAKPOINT, *PDBGKD_RESTORE_BREAKPOINT; typedef struct _DBGKD_CONTINUE { NTSTATUS ContinueStatus; } DBGKD_CONTINUE, *PDBGKD_CONTINUE; -// DBGKD_ANY_CONTROL_SET is 32-bit packed with an NTSTATUS in -// DBGKD_CONTINUE2 so start with a 32-bit value to get the 64-bit -// values aligned. - -#pragma pack(push,4) - -typedef struct _X86_DBGKD_CONTROL_SET { - ULONG TraceFlag; - ULONG Dr7; - ULONG CurrentSymbolStart; - ULONG CurrentSymbolEnd; -} X86_DBGKD_CONTROL_SET, *PX86_DBGKD_CONTROL_SET; - -typedef ULONG ALPHA_DBGKD_CONTROL_SET, *PALPHA_DBGKD_CONTROL_SET; - -#define IA64_DBGKD_CONTROL_SET_CONTINUE_NONE 0x0000 -#define IA64_DBGKD_CONTROL_SET_CONTINUE_TRACE_INSTRUCTION 0x0001 -#define IA64_DBGKD_CONTROL_SET_CONTINUE_TRACE_TAKEN_BRANCH 0x0002 - -typedef struct _IA64_DBGKD_CONTROL_SET { - ULONG Continue; - ULONG64 CurrentSymbolStart; - ULONG64 CurrentSymbolEnd; -} IA64_DBGKD_CONTROL_SET, *PIA64_DBGKD_CONTROL_SET; - -typedef struct _AMD64_DBGKD_CONTROL_SET { - ULONG TraceFlag; - ULONG64 Dr7; - ULONG64 CurrentSymbolStart; - ULONG64 CurrentSymbolEnd; -} AMD64_DBGKD_CONTROL_SET, *PAMD64_DBGKD_CONTROL_SET; - -typedef struct _DBGKD_ANY_CONTROL_SET -{ - union - { +typedef struct _DBGKD_ANY_CONTROL_SET { + union { X86_DBGKD_CONTROL_SET X86ControlSet; ALPHA_DBGKD_CONTROL_SET AlphaControlSet; IA64_DBGKD_CONTROL_SET IA64ControlSet; - AMD64_DBGKD_CONTROL_SET Amd64ControlSet; + X64_DBGKD_CONTROL_SET X64ControlSet; + ARM_DBGKD_CONTROL_SET ARMControlSet; }; } DBGKD_ANY_CONTROL_SET, *PDBGKD_ANY_CONTROL_SET; -typedef X86_DBGKD_CONTROL_SET DBGKD_CONTROL_SET; - -#pragma pack(pop) - // This structure must be 32-bit packed for // for compatibility with older, processor-specific // versions of this structure. -#pragma pack(push,4) - typedef struct _DBGKD_CONTINUE2 { - NTSTATUS ContinueStatus; // The ANY control set is unioned here to // ensure that this structure is always large // enough to hold any possible continue. union { + NTSTATUS ContinueStatus; DBGKD_CONTROL_SET ControlSet; DBGKD_ANY_CONTROL_SET AnyControlSet; }; } DBGKD_CONTINUE2, *PDBGKD_CONTINUE2; -#pragma pack(pop) - // // MSR support // typedef struct _DBGKD_READ_WRITE_MSR { - ULONG Msr; - ULONG DataValueLow; - ULONG DataValueHigh; + UINT32 Msr; + UINT32 DataValueLow; + UINT32 DataValueHigh; } DBGKD_READ_WRITE_MSR, *PDBGKD_READ_WRITE_MSR; typedef struct _DBGKD_GET_VERSION64 { - USHORT MajorVersion; - USHORT MinorVersion; - USHORT ProtocolVersion; - USHORT Flags; - USHORT MachineType; + UINT16 MajorVersion; + UINT16 MinorVersion; + UINT16 ProtocolVersion; + UINT16 Flags; + UINT16 MachineType; // // Protocol command support descriptions. @@ -517,10 +428,10 @@ typedef struct _DBGKD_GET_VERSION64 { // by the simulation if one exists. UCHAR Simulation; - USHORT Unused[1]; + UINT16 Unused[1]; - ULONG64 KernBase; - ULONG64 PsLoadedModuleList; + UINT64 KernBase; + UINT64 PsLoadedModuleList; // // Components may register a debug data block for use by @@ -529,15 +440,15 @@ typedef struct _DBGKD_GET_VERSION64 { // There will always be an entry for the debugger. // - ULONG64 DebuggerDataList; + UINT64 DebuggerDataList; } DBGKD_GET_VERSION64, *PDBGKD_GET_VERSION64; typedef struct _DBGKD_MANIPULATE_STATE64 { - ULONG ApiNumber; - USHORT ProcessorLevel; - USHORT Processor; + UINT32 ApiNumber; + UINT16 ProcessorLevel; + UINT16 Processor; NTSTATUS ReturnStatus; union { DBGKD_READ_MEMORY64 ReadMemory; @@ -550,8 +461,8 @@ typedef struct _DBGKD_MANIPULATE_STATE64 { DBGKD_CONTINUE Continue; DBGKD_CONTINUE2 Continue2; DBGKD_READ_WRITE_MSR ReadWriteMsr; -#if 0 DBGKD_READ_WRITE_IO64 ReadWriteIo; +#if 0 DBGKD_READ_WRITE_IO_EXTENDED64 ReadWriteIoExtended; DBGKD_QUERY_SPECIAL_CALLS QuerySpecialCalls; DBGKD_SET_SPECIAL_CALL64 SetSpecialCall; @@ -565,7 +476,7 @@ typedef struct _DBGKD_MANIPULATE_STATE64 { DBGKD_SWITCH_PARTITION SwitchPartition; #endif - } u; + }; } DBGKD_MANIPULATE_STATE64, *PDBGKD_MANIPULATE_STATE64; // @@ -581,7 +492,7 @@ typedef struct _DBGKD_MANIPULATE_STATE64 { // immediately follows the message // typedef struct _DBGKD_PRINT_STRING { - ULONG LengthOfString; + UINT32 LengthOfString; } DBGKD_PRINT_STRING, *PDBGKD_PRINT_STRING; // @@ -593,18 +504,18 @@ typedef struct _DBGKD_PRINT_STRING { // // typedef struct _DBGKD_GET_STRING { - ULONG LengthOfPromptString; - ULONG LengthOfStringRead; + UINT32 LengthOfPromptString; + UINT32 LengthOfStringRead; } DBGKD_GET_STRING, *PDBGKD_GET_STRING; typedef struct _DBGKD_DEBUG_IO { - ULONG ApiNumber; - USHORT ProcessorLevel; - USHORT Processor; + UINT32 ApiNumber; + UINT16 ProcessorLevel; + UINT16 Processor; union { DBGKD_PRINT_STRING PrintString; DBGKD_GET_STRING GetString; - } u; + }; } DBGKD_DEBUG_IO, *PDBGKD_DEBUG_IO; // @@ -642,11 +553,11 @@ typedef struct _DBGKD_DEBUG_IO { typedef struct _KD_PACKET { - ULONG PacketLeader; - USHORT PacketType; - USHORT ByteCount; - ULONG PacketId; - ULONG Checksum; + UINT32 PacketLeader; + UINT16 PacketType; + UINT16 ByteCount; + UINT32 PacketId; + UINT32 Checksum; } KD_PACKET, *PKD_PACKET; #define PACKET_MAX_SIZE 4000 @@ -756,20 +667,70 @@ typedef struct _KD_PACKET { #define DbgKdMaximumManipulate 0x0000315EL - - typedef struct _KD_CONTEXT { - ULONG KdpDefaultRetries; + UINT32 KdpDefaultRetries; BOOLEAN KdpControlCPending; } KD_CONTEXT, *PKD_CONTEXT; typedef enum { - ContinueError = FALSE, - ContinueSuccess = TRUE, + ContinueError = 0, + ContinueSuccess = 1, ContinueProcessorReselected, ContinueNextProcessor } KCONTINUE_STATUS; +// +// If the packet type is PACKET_TYPE_KD_FILE_IO, then +// the format of the packet data is as follows: +// + +#define DbgKdCreateFileApi 0x00003430L +#define DbgKdReadFileApi 0x00003431L +#define DbgKdWriteFileApi 0x00003432L +#define DbgKdCloseFileApi 0x00003433L + +// Unicode filename follows as additional data. +typedef struct _DBGKD_CREATE_FILE { + UINT32 DesiredAccess; + UINT32 FileAttributes; + UINT32 ShareAccess; + UINT32 CreateDisposition; + UINT32 CreateOptions; + // Return values. + UINT64 Handle; + UINT64 Length; +} DBGKD_CREATE_FILE, *PDBGKD_CREATE_FILE; + +// Data is returned as additional data in the response. +typedef struct _DBGKD_READ_FILE { + UINT64 Handle; + UINT64 Offset; + UINT32 Length; +} DBGKD_READ_FILE, *PDBGKD_READ_FILE; + +// Data is given as additional data. +typedef struct _DBGKD_WRITE_FILE { + UINT64 Handle; + UINT64 Offset; + UINT32 Length; +} DBGKD_WRITE_FILE, *PDBGKD_WRITE_FILE; + +typedef struct _DBGKD_CLOSE_FILE { + UINT64 Handle; +} DBGKD_CLOSE_FILE, *PDBGKD_CLOSE_FILE; + +typedef struct _DBGKD_FILE_IO { + UINT32 ApiNumber; + NTSTATUS Status; + union { + UINT64 ReserveSpace[7]; + DBGKD_CREATE_FILE CreateFile; + DBGKD_READ_FILE ReadFile; + DBGKD_WRITE_FILE WriteFile; + DBGKD_CLOSE_FILE CloseFile; + }; +} DBGKD_FILE_IO, *PDBGKD_FILE_IO; + // // status Constants for Packet waiting // @@ -847,13 +808,14 @@ typedef enum { #define DBGKD_VERS_FLAG_HSS 0x0010 // hardware stepping support #define DBGKD_VERS_FLAG_PARTITIONS 0x0020 // multiple OS partitions exist -#define IMAGE_FILE_MACHINE_I386 0x014c // Intel 386. +#define IMAGE_FILE_MACHINE_X86 0x014c // Intel 386, etc. +#define IMAGE_FILE_MACHINE_X64 0x8664 // AMD64 (K8), etc. +#define IMAGE_FILE_MACHINE_ARM 0x01c0 // Little endian ARM w/ Thumb // // KD version MajorVersion high-byte identifiers. // -typedef enum _DBGKD_MAJOR_TYPES -{ +typedef enum _DBGKD_MAJOR_TYPES { DBGKD_MAJOR_NT, DBGKD_MAJOR_XBOX, DBGKD_MAJOR_BIG, @@ -882,46 +844,81 @@ typedef enum _DBGKD_MAJOR_TYPES ////////////////////////////////////////////////////////////////////////////// // -void KdInitialize(Struct_Microsoft_Singularity_BootInfo *bi); +void KdInitialize(Class_Microsoft_Singularity_Hal_Platform *platform); void KdPutChar(char c); void kdprintf(const char * pszFmt, ...); // Low level debug (to screen) only. void kdprints(const char * pszFmt); // Low level debug (to screen) only. void KdpSpin(); -ULONG KdpComputeChecksum(IN PCHAR Buffer, IN ULONG Length); +UINT32 KdpComputeChecksum(IN PCHAR Buffer, IN UINT32 Length); -bool KdpComInit(Struct_Microsoft_Singularity_BootInfo *bi); -void KdpComSendPacket(ULONG PacketType, - IN PSTRING MessageHeader, - IN PSTRING MessageData OPTIONAL, - IN OUT PKD_CONTEXT KdContext); -KDP_STATUS KdpComReceivePacket(IN ULONG PacketType, - OUT PSTRING MessageHeader, - OUT PSTRING MessageData, - OUT PULONG DataLength, - IN OUT PKD_CONTEXT KdContext); -bool KdpComPollBreakIn(); +bool KdpSerialInit(Class_Microsoft_Singularity_Hal_Platform *nbi); +KDP_STATUS KdpSerialGetByte(OUT PUCHAR Input, BOOL WaitForByte); +void KdpSerialPutByte(IN UCHAR Output); -bool Kdp1394Init(Struct_Microsoft_Singularity_BootInfo *bi); -void Kdp1394SendPacket(ULONG PacketType, +void KdpSerialSendPacket(UINT32 PacketType, + IN PSTRING MessageHeader, + IN PSTRING MessageData OPTIONAL, + IN OUT PKD_CONTEXT KdContext); +KDP_STATUS KdpSerialReceivePacket(IN UINT32 PacketType, + OUT PSTRING MessageHeader, + OUT PSTRING MessageData, + OUT UINT32 * DataLength, + IN OUT PKD_CONTEXT KdContext); +bool KdpSerialPollBreakIn(); + +bool Kdp1394Init(UINT16 Channel, ULONG_PTR Base, ULONG_PTR BufferAddr32, UINT32 BufferSize32); + +void Kdp1394SendPacket(UINT32 PacketType, IN PSTRING MessageHeader, IN PSTRING MessageData OPTIONAL, IN OUT PKD_CONTEXT KdContext); -KDP_STATUS Kdp1394ReceivePacket(IN ULONG PacketType, +KDP_STATUS Kdp1394ReceivePacket(IN UINT32 PacketType, OUT PSTRING MessageHeader, OUT PSTRING MessageData, - OUT PULONG DataLength, + OUT UINT32 * DataLength, IN OUT PKD_CONTEXT KdContext); bool Kdp1394PollBreakIn(); +///////////////////////////////////////////////// Processor Specific Routines. +// +bool KdpDisableInterruptsInline(); +void KdpRestoreInterruptsInline(bool enabled); +void KdpPause(); +void KdpFlushInstCache(); +bool KdpReadMsr(UINT32 msr, OUT UINT32 *plo, OUT UINT32 *phi); +bool KdpWriteMsr(UINT32 msr, UINT32 lo, UINT32 hi); + +void KdpToKdContext(IN CONST Struct_Microsoft_Singularity_Isal_SpillContext *singularity, + OUT CONTEXT *windbg); +void KdpFromKdContext(IN CONST CONTEXT *windbg, + OUT Struct_Microsoft_Singularity_Isal_SpillContext *singularity); + +void KdpSetControlReport(IN OUT PDBGKD_CONTROL_REPORT report, + IN CONST Struct_Microsoft_Singularity_Isal_SpillContext *x86Context); +void KdpSetControlSet(IN CONST DBGKD_CONTROL_SET * control, + IN OUT Struct_Microsoft_Singularity_Isal_SpillContext *x86Context); + +void KdpReadSpecialRegisters(OUT KSPECIAL_REGISTERS *pksp, + IN CONST Struct_Microsoft_Singularity_Isal_SpillContext *x86Context); +void KdpWriteSpecialRegisters(IN CONST KSPECIAL_REGISTERS *pksp); + +KdDebugTrapData * KdpIsDebugTrap(IN CONST Struct_Microsoft_Singularity_Isal_SpillContext *context, + int id); + +void KdpConvertTrapToException(IN OUT EXCEPTION_RECORD64 *per, + IN OUT Struct_Microsoft_Singularity_Isal_SpillContext *context, + int id); + +int KdpReadWriteIoSpace(int size, int iowrite, unsigned short addr, unsigned int value); //////////////////////////////////////////////////////////// Shared Variables. // extern BOOL KdDebuggerNotPresent; -extern ULONG KdCompNumberRetries; -extern ULONG KdCompRetryCount; -extern ULONG KdPacketId; +extern UINT32 KdCompNumberRetries; +extern UINT32 KdCompRetryCount; +extern UINT32 KdPacketId; ///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/halstack.asm b/base/Kernel/Native/halstack.asm deleted file mode 100644 index f2bd386..0000000 --- a/base/Kernel/Native/halstack.asm +++ /dev/null @@ -1,278 +0,0 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; Microsoft Research Singularity -;; -;; Copyright (c) Microsoft Corporation. All rights reserved. -;; -;; File: halstack.asm -;; -;; Note: -;; - -.686p -.mmx -.xmm -.model flat -.code - -assume ds:flat -assume es:flat -assume ss:flat -assume fs:nothing -assume gs:nothing - -include hal.inc - -externdef ?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ:NEAR -externdef ?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ:NEAR - -;;; Public symbols -public ?c_LinkedStackBegin@Class_Microsoft_Singularity_Memory_Stacks@@2EA -public ?c_LinkStackBegin@Class_Microsoft_Singularity_Memory_Stacks@@2EA -public ?c_LinkStackLimit@Class_Microsoft_Singularity_Memory_Stacks@@2EA -public ?c_UnlinkStackBegin@Class_Microsoft_Singularity_Memory_Stacks@@2EA -public ?c_UnlinkStackLimit@Class_Microsoft_Singularity_Memory_Stacks@@2EA -public ?c_LinkedStackLimit@Class_Microsoft_Singularity_Memory_Stacks@@2EA - - align 16 - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; -; -; 1) Stack at entry of LinkStackN -; +-----------------------+ -; old base -> | previous esp | 2) Stack on exit from LinkStackN -; +-----------------------+ +------------------------+ <- base -; | previous stackBase | | old esp | -; +-----------------------+ +------------------------+ -; | previous stackLimit | | old base | -; +-----------------------+ +------------------------+ -; | .. | | old limit | -; +-----------------------+ +------------------------+ -; | .. | ==> | .. | -; | args | . | args | -; +-----------------------+ . +------------------------+ -; | return addr in caller | . | return to UnlinkStackN | -; +-----------------------+ . +------------------------+ -; old ebp -> | caller ebp | ==> | old ebp | <- esp & -; +-----------------------+ +------------------------+ ebp -; old esp -> | return addr in callee | | .. | -; +-----------------------+ | .. | <- limit -; * EAX = amount of stack needed. -; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; Implementations of LinkStackNn just push the appropriate number -;; and call the general-purpose LinkStack. - -?c_LinkedStackBegin@Class_Microsoft_Singularity_Memory_Stacks@@2EA byte 0 -?c_LinkStackBegin@Class_Microsoft_Singularity_Memory_Stacks@@2EA byte 0 - -?g_LinkStack0@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - push 0 - push ?g_UnlinkStack0@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ - jmp ?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ - -?g_LinkStack4@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - push 1 - push ?g_UnlinkStack4@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ - jmp ?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ - -?g_LinkStack8@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - push 2 - push ?g_UnlinkStack8@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ - jmp ?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ - -?g_LinkStack12@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - push 3 - push ?g_UnlinkStack12@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ - jmp ?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ - -?g_LinkStack16@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - push 4 - push ?g_UnlinkStack16@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ - jmp ?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ - -?g_LinkStack20@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - push 5 - push ?g_UnlinkStack20@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ - jmp ?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ - -?g_LinkStack24@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - push 6 - push ?g_UnlinkStack24@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ - jmp ?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ - -?g_LinkStack28@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - push 7 - push ?g_UnlinkStack28@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ - jmp ?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ - -?g_LinkStack32@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - push 8 - push ?g_UnlinkStack32@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ - jmp ?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ - -?g_LinkStack36@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - push 9 - push ?g_UnlinkStack36@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ - jmp ?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ - -?g_LinkStack40@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - push 10 - push ?g_UnlinkStack40@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ - jmp ?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ - -?g_LinkStack44@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - push 11 - push ?g_UnlinkStack44@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ - jmp ?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ - -?g_LinkStack48@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - push 12 - push ?g_UnlinkStack48@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ - jmp ?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ - -?g_LinkStack52@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - push 13 - push ?g_UnlinkStack52@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ - jmp ?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ - -?g_LinkStack56@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - push 14 - push ?g_UnlinkStack56@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ - jmp ?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ - -?g_LinkStack60@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - push 15 - push ?g_UnlinkStack60@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ - jmp ?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ - -?g_LinkStack64@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - push 16 - push ?g_UnlinkStack64@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ - jmp ?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; -; -?c_LinkStackLimit@Class_Microsoft_Singularity_Memory_Stacks@@2EA byte 0 - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; -; 1) Stacks on entry to UnlinkStackN: -; old base -> +-----------------------+Organization.tif -; | previous esp | -; +-----------------------+ +------------------------+ <- base -; | previous stackBase | | old esp | -; +-----------------------+ +------------------------+ -; | previous stackLimit | | old base | -; +-----------------------+ +------------------------+ -; | .. | | old limit | <- esp -; +-----------------------+ +------------------------+ -; | .. | | .. | * ebp = -; | args | | .. | old ebp -; +-----------------------+ | .. | -; | return addr in caller | | .. | ecx -; +-----------------------+ | .. | is free -; ebp -> | caller ebp | | .. | -; +-----------------------+ | .. | -; old esp -> | return addr in callee | | .. | -; +-----------------------+ | .. | <- limit -; * EAX/EDX = return value from callee. -; -; 2) Stacks on exit from UnlinkStackN: -; stackBase -> +-----------------------+ -; | previous esp | -; +-----------------------+ -; | previous stackBase | -; +-----------------------+ -; | previous stackLimit | -; +-----------------------+ -; | .. | -; ebp -> | .. | -; | .. | -; esp -> | .. | -; +-----------------------+ * eip = return addr in caller -; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; Implementations of UnlinkStackNn just push the appropriate number -;; and call the general-purpose UnlinkStack. - -?c_UnlinkStackBegin@Class_Microsoft_Singularity_Memory_Stacks@@2EA byte 0 - -?g_UnlinkStack0@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - mov ecx,0 - jmp ?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ -?g_UnlinkStack4@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - mov ecx,4 - jmp ?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ -?g_UnlinkStack8@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - mov ecx,8 - jmp ?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ -?g_UnlinkStack12@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - mov ecx,12 - jmp ?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ -?g_UnlinkStack16@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - mov ecx,16 - jmp ?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ -?g_UnlinkStack20@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - mov ecx,20 - jmp ?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ -?g_UnlinkStack24@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - mov ecx,24 - jmp ?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ -?g_UnlinkStack28@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - mov ecx,28 - jmp ?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ -?g_UnlinkStack32@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - mov ecx,32 - jmp ?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ -?g_UnlinkStack36@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - mov ecx,36 - jmp ?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ -?g_UnlinkStack40@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - mov ecx,40 - jmp ?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ -?g_UnlinkStack44@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - mov ecx,44 - jmp ?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ -?g_UnlinkStack48@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - mov ecx,48 - jmp ?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ -?g_UnlinkStack52@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - mov ecx,52 - jmp ?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ -?g_UnlinkStack56@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - mov ecx,56 - jmp ?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ -?g_UnlinkStack60@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - mov ecx,60 - jmp ?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ -?g_UnlinkStack64@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ:: - mov ecx,64 - jmp ?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -?c_UnlinkStackLimit@Class_Microsoft_Singularity_Memory_Stacks@@2EA byte 0 -?c_LinkedStackLimit@Class_Microsoft_Singularity_Memory_Stacks@@2EA byte 0 - -align 16 -PUBLIC __checkStackLimit -__checkStackLimit PROC - push edx - mov edx, eax - CurrentThreadContext eax - mov eax, [eax][Struct_Microsoft_Singularity_X86_ThreadContext._stackLimit] - cmp edx, eax - jb debugHACK - pop edx - ret -debugHACK: - pop edx - ret -__checkStackLimit ENDP - -end diff --git a/base/Kernel/Native/intrinsics.h b/base/Kernel/Native/intrinsics.h new file mode 100644 index 0000000..6265e8a --- /dev/null +++ b/base/Kernel/Native/intrinsics.h @@ -0,0 +1,350 @@ +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Header file for C++ intrinsics +// + +#pragma once + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Non-portable intrinsics +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// This section contains definitions of compiler intrinsics. This header file provides a +// uniform place where they can be declared if necessary. +// +// The intention here is to give as broad expressibility as possible to C++ code, without +// having to resort to assembly. Thus platform-specific intrinsics are just fine. +// +// These functions were farmed out of the VS compiler header "intern.h", and then trimmed +// to limit dependencies on crt. +// +// Note that there is also a desire to build a platform layer of intrinsic-like functionalty, +// where certain platforms may have to manufacture the functionatity if necessary. +// Rather than do this at the C++ level, instead we do this at the managed level in Isal.Intrinsics. + +extern "C" { + +// All VS platforms + +int __cdecl abs(int); +unsigned short __cdecl _byteswap_ushort(unsigned short value); +unsigned long __cdecl _byteswap_ulong(unsigned long value); +unsigned __int64 __cdecl _byteswap_uint64(unsigned __int64 value); +long __cdecl labs(long); +unsigned long __cdecl _lrotl(unsigned long,int); +unsigned long __cdecl _lrotr(unsigned long,int); +int __cdecl memcmp(const void *,const void *,size_t); +void * __cdecl memcpy(void *,const void *,size_t); +void * __cdecl memset(void *,int,size_t); +unsigned int __cdecl _rotl(unsigned int,int); +unsigned int __cdecl _rotr(unsigned int,int); +char * __cdecl strcat(char *,const char *); +int __cdecl strcmp(const char *,const char *); +char * __cdecl strcpy(char *,const char *); +size_t __cdecl strlen(const char *); +char * __cdecl _strset(char *,int); +char * __cdecl strset(char *,int); +unsigned __int64 __cdecl _rotl64(unsigned __int64,int); +unsigned __int64 __cdecl _rotr64(unsigned __int64,int); +__int64 __cdecl _abs64(__int64); + +long _InterlockedExchange(long volatile *, long); + +#if ISA_IX86 || ISA_IX64 + +// All IA32 platforms + +void __cdecl __debugbreak(void); +void __cdecl _disable(void); +__int64 __emul(int,int); +unsigned __int64 __emulu(unsigned int,unsigned int); +void __cdecl _enable(void); +long __cdecl _InterlockedDecrement(long volatile *); +long _InterlockedExchangeAdd(long volatile *, long); +long _InterlockedCompareExchange (long volatile *, long, long); +__int64 _InterlockedCompareExchange64(__int64 volatile *, __int64, __int64); +long __cdecl _InterlockedIncrement(long volatile *); +int __cdecl _inp(unsigned short); +int __cdecl inp(unsigned short); +unsigned long __cdecl _inpd(unsigned short); +unsigned long __cdecl inpd(unsigned short); +unsigned short __cdecl _inpw(unsigned short); +unsigned short __cdecl inpw(unsigned short); +unsigned __int64 __ll_lshift(unsigned __int64,int); +__int64 __ll_rshift(__int64,int); +int __cdecl _outp(unsigned short,int); +int __cdecl outp(unsigned short,int); +unsigned long __cdecl _outpd(unsigned short,unsigned long); +unsigned long __cdecl outpd(unsigned short,unsigned long); +unsigned short __cdecl _outpw(unsigned short,unsigned short); +unsigned short __cdecl outpw(unsigned short,unsigned short); +void * _ReturnAddress(void); +unsigned __int64 __ull_rshift(unsigned __int64,int); +void * _AddressOfReturnAddress(void); +void _WriteBarrier(void); +void _ReadWriteBarrier(void); +void __wbinvd(void); +void __invlpg(void*); +unsigned __int64 __readmsr(unsigned long); +void __writemsr(unsigned long, unsigned __int64); +unsigned __int64 __rdtsc(void); +void __movsb(unsigned char *, unsigned char const *, size_t); +void __movsw(unsigned short *, unsigned short const *, size_t); +void __movsd(unsigned long *, unsigned long const *, size_t); +unsigned char __inbyte(unsigned short Port); +unsigned short __inword(unsigned short Port); +unsigned long __indword(unsigned short Port); +void __outbyte(unsigned short Port, unsigned char Data); +void __outword(unsigned short Port, unsigned short Data); +void __outdword(unsigned short Port, unsigned long Data); +void __inbytestring(unsigned short Port, unsigned char *Buffer, unsigned long Count); +void __inwordstring(unsigned short Port, unsigned short *Buffer, unsigned long Count); +void __indwordstring(unsigned short Port, unsigned long *Buffer, unsigned long Count); +void __outbytestring(unsigned short Port, unsigned char *Buffer, unsigned long Count); +void __outwordstring(unsigned short Port, unsigned short *Buffer, unsigned long Count); +void __outdwordstring(unsigned short Port, unsigned long *Buffer, unsigned long Count); +unsigned int __getcallerseflags(); +void __vmx_vmptrst(unsigned __int64 *); +void __vmx_vmptrst(unsigned __int64 *); +void __svm_clgi(void); +void __svm_invlpga(void*, int); +void __svm_skinit(int); +void __svm_stgi(void); +void __svm_vmload(size_t); +void __svm_vmrun(size_t); +void __svm_vmsave(size_t); +void __halt(void); +void __sidt(void*); +void __lidt(void*); +void __ud2(void); +void __nop(void); +void __stosb(unsigned char *, unsigned char, size_t); +void __stosw(unsigned short *, unsigned short, size_t); +void __stosd(unsigned long *, unsigned long, size_t); +unsigned char _interlockedbittestandset(long *a, long b); +unsigned char _interlockedbittestandreset(long *a, long b); +void __cpuid(int a[4], int b); +unsigned __int64 __readpmc(unsigned long a); +unsigned long __segmentlimit(unsigned long a); +void __int2c(void); + +// All IA platforms + +long _InterlockedOr(long volatile *, long); +char _InterlockedOr8(char volatile *, char); +short _InterlockedOr16(short volatile *, short); +long _InterlockedXor(long volatile *, long); +char _InterlockedXor8(char volatile *, char); +short _InterlockedXor16(short volatile *, short); +long _InterlockedAnd(long volatile *, long); +char _InterlockedAnd8(char volatile *, char); +short _InterlockedAnd16(short volatile *, short); +unsigned char _bittest(long const *a, long b); +unsigned char _bittestandset(long *a, long b); +unsigned char _bittestandreset(long *a, long b); +unsigned char _bittestandcomplement(long *a, long b); +unsigned char _BitScanForward(unsigned long* Index, unsigned long Mask); +unsigned char _BitScanReverse(unsigned long* Index, unsigned long Mask); +void _ReadBarrier(void); +unsigned char _rotr8(unsigned char value, unsigned char shift); +unsigned short _rotr16(unsigned short value, unsigned char shift); +unsigned char _rotl8(unsigned char value, unsigned char shift); +unsigned short _rotl16(unsigned short value, unsigned char shift); +short _InterlockedIncrement16(short volatile *Addend); +short _InterlockedDecrement16(short volatile *Addend); +short _InterlockedCompareExchange16(short volatile *Destination, short Exchange, short Comparand); +void __nvreg_save_fence(void); +void __nvreg_restore_fence(void); + +#endif // ISA_IX86 || ISA_IX64 + +#if ISA_IX86 + +// x86 only + +long _InterlockedAddLargeStatistic(__int64 volatile *, long); +unsigned long __readcr0(void); +unsigned long __readcr2(void); +unsigned long __readcr3(void); +unsigned long __readcr4(void); +unsigned long __readcr8(void); +void __writecr0(unsigned); +void __writecr3(unsigned); +void __writecr4(unsigned); +void __writecr8(unsigned); +unsigned __readdr(unsigned int); +void __writedr(unsigned int, unsigned); +unsigned __readeflags(void); +void __writeeflags(unsigned); +unsigned char __readfsbyte(unsigned long Offset); +unsigned short __readfsword(unsigned long Offset); +unsigned long __readfsdword(unsigned long Offset); +unsigned __int64 __readfsqword(unsigned long Offset); +void __writefsbyte(unsigned long Offset, unsigned char Data); +void __writefsword(unsigned long Offset, unsigned short Data); +void __writefsdword(unsigned long Offset, unsigned long Data); +void __writefsqword(unsigned long Offset, unsigned __int64 Data); + +#endif // ISA_IX86 + +#if ISA_IX64 + +// x64 only + +__int64 _InterlockedDecrement64(__int64 volatile *); +__int64 _InterlockedExchange64(__int64 volatile *, __int64); +void * _InterlockedExchangePointer(void * volatile *, void *); +__int64 _InterlockedExchangeAdd64(__int64 volatile *, __int64); +__int64 _InterlockedCompare64Exchange128(__int64 volatile *Destination, __int64 ExchangeHigh, __int64 ExchangeLow, __int64 Comparand); +__int64 _InterlockedCompare64Exchange128_acq(__int64 volatile *Destination, __int64 ExchangeHigh, __int64 ExchangeLow, __int64 Comparand); +__int64 _InterlockedCompare64Exchange128_rel(__int64 volatile *Destination, __int64 ExchangeHigh, __int64 ExchangeLow, __int64 Comparand); +void *_InterlockedCompareExchangePointer (void * volatile *, void *, void *); +__int64 _InterlockedIncrement64(__int64 volatile *); +void __faststorefence(void); +__int64 __mulh(__int64,__int64); +unsigned __int64 __umulh(unsigned __int64,unsigned __int64); +unsigned __int64 __readcr0(void); +unsigned __int64 __readcr2(void); +unsigned __int64 __readcr3(void); +unsigned __int64 __readcr4(void); +unsigned __int64 __readcr8(void); +void __writecr0(unsigned __int64); +void __writecr3(unsigned __int64); +void __writecr4(unsigned __int64); +void __writecr8(unsigned __int64); +unsigned __int64 __readdr(unsigned int); +void __writedr(unsigned int, unsigned __int64); +unsigned __int64 __readeflags(void); +void __writeeflags(unsigned __int64); +void __movsq(unsigned long long *, unsigned long long const *, size_t); +unsigned char __readgsbyte(unsigned long Offset); +unsigned short __readgsword(unsigned long Offset); +unsigned long __readgsdword(unsigned long Offset); +unsigned __int64 __readgsqword(unsigned long Offset); +void __writegsbyte(unsigned long Offset, unsigned char Data); +void __writegsword(unsigned long Offset, unsigned short Data); +void __writegsdword(unsigned long Offset, unsigned long Data); +void __writegsqword(unsigned long Offset, unsigned __int64 Data); +unsigned char __vmx_vmclear(unsigned __int64*); +unsigned char __vmx_vmlaunch(void); +unsigned char __vmx_vmptrld(unsigned __int64*); +unsigned char __vmx_vmread(size_t, size_t*); +unsigned char __vmx_vmresume(void); +unsigned char __vmx_vmwrite(size_t, size_t); +unsigned char __vmx_on(unsigned __int64*); +void __stosq(unsigned __int64 *, unsigned __int64, size_t); +unsigned char _interlockedbittestandset64(__int64 *a, __int64 b); +unsigned char _interlockedbittestandreset64(__int64 *a, __int64 b); +short _InterlockedCompareExchange16_np(short volatile *Destination, short Exchange, short Comparand); +long _InterlockedCompareExchange_np (long *, long, long); +__int64 _InterlockedCompareExchange64_np(__int64 *, __int64, __int64); +void *_InterlockedCompareExchangePointer_np (void **, void *, void *); +__int64 _InterlockedCompare64Exchange128_np(__int64 *Destination, __int64 ExchangeHigh, __int64 ExchangeLow, __int64 Comparand); +__int64 _InterlockedCompare64Exchange128_acq_np(__int64 *Destination, __int64 ExchangeHigh, __int64 ExchangeLow, __int64 Comparand); +__int64 _InterlockedCompare64Exchange128_rel_np(__int64 *Destination, __int64 ExchangeHigh, __int64 ExchangeLow, __int64 Comparand); +long _InterlockedAnd_np(long *, long); +char _InterlockedAnd8_np(char *, char); +short _InterlockedAnd16_np(short *, short); +__int64 _InterlockedAnd64_np(__int64 *, __int64); +long _InterlockedOr_np(long *, long); +char _InterlockedOr8_np(char *, char); +short _InterlockedOr16_np(short *, short); +__int64 _InterlockedOr64_np(__int64 *, __int64); +long _InterlockedXor_np(long *, long); +char _InterlockedXor8_np(char *, char); +short _InterlockedXor16_np(short *, short); +__int64 _InterlockedXor64_np(__int64 *, __int64); + +// x64 + IA64 + +__int64 _InterlockedOr64(__int64 volatile *, __int64); +__int64 _InterlockedXor64(__int64 volatile *, __int64); +__int64 _InterlockedXor64(__int64 volatile *, __int64); +__int64 _InterlockedAnd64(__int64 volatile *, __int64); +unsigned char _bittest64(__int64 const *a, __int64 b); +unsigned char _bittestandset64(__int64 *a, __int64 b); +unsigned char _bittestandreset64(__int64 *a, __int64 b); +unsigned char _bittestandcomplement64(__int64 *a, __int64 b); +unsigned char _BitScanForward64(unsigned long* Index, unsigned __int64 Mask); +unsigned char _BitScanReverse64(unsigned long* Index, unsigned __int64 Mask); +unsigned __int64 __shiftleft128(unsigned __int64 LowPart, unsigned __int64 HighPart, unsigned char Shift); +unsigned __int64 __shiftright128(unsigned __int64 LowPart, unsigned __int64 HighPart, unsigned char Shift); +unsigned __int64 _umul128(unsigned __int64 multiplier, unsigned __int64 multiplicand, unsigned __int64 *highproduct); +__int64 _mul128(__int64 multiplier, __int64 multiplicand, __int64 *highproduct); + +#endif // ISA_IX64 + +#if ISA_ARM + +int __cdecl _MoveFromCoprocessor(unsigned int coproc, unsigned int opcode1, unsigned int crn, unsigned int crm, unsigned int opcode2); +int __cdecl _MoveFromCoprocessor2(unsigned int coproc, unsigned int opcode1, unsigned int crn, unsigned int crm, unsigned int opcode2); + +void __cdecl _MoveToCoprocessor(unsigned int value, unsigned int coproc, unsigned int opcode1, unsigned int crn, unsigned int crm, unsigned int opcode2); +void __cdecl _MoveToCoprocessor2(unsigned int value, unsigned int coproc, unsigned int opcode1, unsigned int crn, unsigned int crm, unsigned int opcode2); + +void __cdecl __emit(const unsigned __int32 opcode); +#define __debugbreak() __emit(0xefffff03) + +#endif // ISA_ARM +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Portable intrinsics +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// This section contains standardized "intrinsics" which are portable across all architectures. +// In general we should keep this kind of thing to a minimum - the proper place to build a +// portablity layer at the managed level. + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interlocked operations + +extern "C" long InterlockedDecrement(long volatile *); +extern "C" long InterlockedExchange(long volatile *, long); +extern "C" long InterlockedExchangeAdd(long volatile *, long); +extern "C" long InterlockedCompareExchange (long volatile *, long, long); +extern "C" __int64 InterlockedCompareExchange64(__int64 volatile *, __int64, __int64); +extern "C" long InterlockedIncrement(long volatile *); + +#if ISA_IX64 || ISA_IX86 + +#define InterlockedIncrement _InterlockedIncrement +#define InterlockedDecrement _InterlockedDecrement +#define InterlockedExchange _InterlockedExchange +#define InterlockedExchangeAdd _InterlockedExchangeAdd +#define InterlockedCompareExchange _InterlockedCompareExchange +#define InterlockedCompareExchange64 _InterlockedCompareExchange64 + +#elif ISA_ARM + +#define InterlockedExchange _InterlockedExchange + +#endif + +// Map Pointer version to either 32 or 64 bit version +// (even though we have an explicit pointer version intrinsic on some isas.) + +#if PTR_SIZE_32 +#define InterlockedCompareExchangePointer(a,b,c) \ + ((void*)InterlockedCompareExchange((long volatile *)(a),(long)(b),(long)(c))) +#else +#define InterlockedCompareExchangePointer(a,b,c) \ + ((void*)InterlockedCompareExchange64((__int64 volatile *)(a),(__int64)(b),(__int64)(c))) +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// General-use cheap cpu-relative timestamp + +unsigned __int64 RDTSC(void); + +#if ISA_IX64 || ISA_IX86 + +#define RDTSC __rdtsc + +#endif + diff --git a/base/Kernel/Native/ix/Thread.cpp b/base/Kernel/Native/ix/Thread.cpp new file mode 100644 index 0000000..48bfab2 --- /dev/null +++ b/base/Kernel/Native/ix/Thread.cpp @@ -0,0 +1,64 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Thread.cpp +// +// Note: +// + +#include "hal.h" + +////////////////////////////////////////////////////////////// Thread Context. +// +void +Struct_Microsoft_Singularity_ThreadContext:: +m_UpdateAfterGC(Struct_Microsoft_Singularity_ThreadContext * self, + Class_System_Threading_Thread *thread) +{ + self->_thread = thread; +} + +Class_System_Threading_Thread * +Struct_Microsoft_Singularity_ThreadContext:: +m_GetThread(Struct_Microsoft_Singularity_ThreadContext * self) +{ + return (Class_System_Threading_Thread *) self->_thread; +} + +#if SINGULARITY_KERNEL +void +Struct_Microsoft_Singularity_ThreadContext:: +m_Initialize(Struct_Microsoft_Singularity_ThreadContext * self, + int threadIndex, + UIntPtr stackBegin, + uint32 cr3) +{ + Struct_Microsoft_Singularity_Isal_SpillContext::m_Initialize(&self->threadRecord.spill, + stackBegin, + (UIntPtr) self->stackLimit, + (UIntPtr)Class_System_Threading_Thread::g_ThreadStub, + threadIndex, + (UIntPtr) cr3); +} + +void +Struct_Microsoft_Singularity_ThreadContext:: +m_InitializeIdle(Struct_Microsoft_Singularity_ThreadContext * self, + int threadIndex, + UIntPtr stackBegin, + uint32 cr3) +{ + Struct_Microsoft_Singularity_Isal_SpillContext::m_Initialize(&self->threadRecord.spill, + stackBegin, + (UIntPtr) self->stackLimit, + (UIntPtr)Class_System_Threading_Thread::g_DispatcherThreadStub, + threadIndex, + (UIntPtr) cr3); +} +#endif // SINGULARITY_KERNEL + +// +///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/ix/hal.inc b/base/Kernel/Native/ix/hal.inc new file mode 100644 index 0000000..67f3618 --- /dev/null +++ b/base/Kernel/Native/ix/hal.inc @@ -0,0 +1,116 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; +; Include file for kernel assembly files. +; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Include platform definitions + +include ix.inc + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Include singularity declarations + +include halclass.inc + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; Macros for accessing fields inside ProcessorContext +; + +GET_PROCESSOR_CONTEXT MACRO reg + mov REG32(reg), [?c_currentCpuOffset@Class_Microsoft_Singularity_Isal_Isa@@2HA] + mov reg, PSEG:[REG32(reg)] + ENDM + +GET_THREAD_CONTEXT MACRO reg + mov REG32(reg), [?c_currentThreadOffset@Class_Microsoft_Singularity_Isal_Isa@@2HA] + mov reg, PSEG:[REG32(reg)] + ENDM + +GET_PROCESSOR_CONTEXT_FIELD MACRO reg, field + GET_PROCESSOR_CONTEXT reg + mov reg, [reg].Struct_Microsoft_Singularity_ProcessorContext.field +ENDM + +SET_PROCESSOR_CONTEXT_FIELD MACRO field, reg + push reg + GET_PROCESSOR_CONTEXT reg + pop [reg].Struct_Microsoft_Singularity_ProcessorContext.field + mov reg, [reg].Struct_Microsoft_Singularity_ProcessorContext.field +ENDM + +GET_THREAD_CONTEXT_FIELD MACRO reg, field + GET_THREAD_CONTEXT reg + mov reg, [reg].Struct_Microsoft_Singularity_ThreadContext.field +ENDM + +SET_THREAD_CONTEXT_FIELD MACRO field, reg + push reg + GET_THREAD_CONTEXT reg + pop [reg].Struct_Microsoft_Singularity_ThreadContext.field + mov reg, [reg].Struct_Microsoft_Singularity_ThreadContext.field +ENDM + +GET_CPU_RECORD_FIELD MACRO reg, field + GET_PROCESSOR_CONTEXT reg + mov reg, [reg].Struct_Microsoft_Singularity_Isal_CpuRecord.field +ENDM + +SET_CPU_RECORD_FIELD MACRO field, reg + push reg + GET_PROCESSOR_CONTEXT reg + pop [reg].Struct_Microsoft_Singularity_Isal_CpuRecord.field + mov reg, [reg].Struct_Microsoft_Singularity_Isal_CpuRecord.field +ENDM + +GET_THREAD_RECORD_FIELD MACRO reg, field + GET_THREAD_CONTEXT reg + mov reg, [reg].Struct_Microsoft_Singularity_Isal_ThreadRecord.field +ENDM + +SET_THREAD_RECORD_FIELD MACRO field, reg + push reg + GET_THREAD_CONTEXT reg + pop [reg].Struct_Microsoft_Singularity_Isal_ThreadRecord.field + mov reg, [reg].Struct_Microsoft_Singularity_Isal_ThreadRecord.field + ENDM + +; define CurrentThread() function as a macro. +CurrentThread MACRO reg + GET_THREAD_CONTEXT_FIELD(__thread,reg) + ENDM + +CurrentThreadContext MACRO reg + GET_THREAD_CONTEXT reg + ENDM + +CurrentThreadRecord MACRO reg + GET_THREAD_CONTEXT reg + ENDM + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; Big/Little Endian Definitions for Long Integers +; + +ifdef bigend ; Big Endian (hi word at low address) +LOWORD equ [4] +HIWORD equ [0] +else ; Little Endian (low word at low address) +LOWORD equ [0] +HIWORD equ [4] +endif + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; X64 specific stuff + +ifdef ISA_IX64 + +include halx64.inc + +endif + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; End of File. diff --git a/base/Kernel/Native/ix/halstack.asm b/base/Kernel/Native/ix/halstack.asm new file mode 100644 index 0000000..8a9e797 --- /dev/null +++ b/base/Kernel/Native/ix/halstack.asm @@ -0,0 +1,396 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Microsoft Research Singularity +;; +;; Copyright (c) Microsoft Corporation. All rights reserved. +;; +;; File: halstack.asm +;; +;; Note: +;; + + +include hal.inc + +;; The code in this file runs as part of the priveleged runtime. It is responsible for +;; calling into the ABI to allocate and free new stack segments, and to do all the low +;; level mucking to stitch the stacks together. + + +;; LinkStack is a glue routine to extend the stack. Its job is to: +;; +;; 1 - preserve the calling registers of the function it is being called from +;; 2 - set up arguments and call the appropriate ABI call to grow the stack +;; 3 - copy args and stack frame and switch execution to the resulting new stack segment +;; 4 - tail patch the function return so that Free is called on return +;; +;; Its entry point calling conventions are delicate; essentially +;; - rax contains the amount of new stack needed +;; - all argument registers must be preserved +;; - two values - (# of bytes) and (Unlink) are passed on the stack (from the stubs below) +;; +;; BUGBUG: This code doesn't maintain the required 16 byte alignment when running on 64-bit platforms +;; This is not being fixed at the moment, since we're expecting to deprecate this code. + +LinkFrame struct +ifdef ISA_IX64 + _r9 dp ? ; saved in LinkStack prolog + _r8 dp ? ; saved in LinkStack prolog +endif + _dx dp ? ; saved in LinkStack prolog + _cx dp ? ; saved in LinkStack prolog + _alloc dp ? ; saved in LinkStack prolog from ax (set by grower prolog) + _UnlinkFn dp ? ; pushed by LinkStackN stub + _argStack dp ? ; pushed by LinkStackN stub + ;; Continue address + _continue dp ? ; pushed by call of LinkStack function + ;; EBP frame + _oldBp dp ? ; pushed by grower prolog + ;; Return address + _return dp ? ; pushed by call of grower + ;; Stack Arguments (argStack*sizeof PWORD) +LinkFrame ends + +; +; __checkStackOverflow +; +; This function takes the bottom of the current frame in eax. If we +; detect a stack overflow, we make ourselves a real stack frame (for +; debugging convenience) and then break. +; +; BUGBUG: This code doesn't maintain the required 16 byte alignment when running on 64-bit platforms +; This is not being fixed at the moment, since we're expecting to deprecate this code. + + align 16 +__checkStackOverflow proc + push PDX + GET_THREAD_RECORD_FIELD PDX, _activeStackLimit + cmp PAX, PDX + jb overflowHandler + pop PDX + ret +overflowHandler: + int 3 + pop PDX + ret +__checkStackOverflow endp + + +?c_LinkStackFunctionsBegin@Class_Microsoft_Singularity_Memory_Stacks@@2EA byte 0 + +?c_LinkStackBegin@Class_Microsoft_Singularity_Memory_Stacks@@2EA byte 0 + +;;; NOTE: If you change this function, please ensure that the necessary +;;; changes are made to CallStack.SkipLinkStackFrame + + align 16 + +LinkStack proc + ;; Save allocation amount + push PAX + + ;; Save argument registers + push PCX + push PDX +ifdef ISA_IX64 + push r8 + push r9 +endif + + ;; Call stack allocation ABI - this will update the current thread's stackBegin/Limit + mov PCX, [PSP].LinkFrame._alloc + call SYMFIX(?g_AllocateStackSegment@Struct_Microsoft_Singularity_V1_Services_StackService@@SIPAUuintPtr@@PAU2@@Z) + + ;; Switch over to new stack (keep old stack in PDX) + mov PDX, PSP + mov PSP, PAX + + GET_THREAD_CONTEXT_FIELD PAX, _stackLimit + SET_THREAD_RECORD_FIELD _activeStackLimit, PAX + + ;; Now build up the copied/hijacked stack frame for the grower to continue on + + ;; Copy arguments to new stack + mov PCX, [PDX].LinkFrame._argStack + jecxz noargs + lea PAX, [PDX] + SIZEOF LinkFrame - SIZEOF PWORD +next: + push [PAX+PCX*4] + loop next +noargs: + + ;; Push hijacked return address. This will make the function return to our UnlinkStack routine + ;; rather than its real return address. + push [PDX].LinkFrame._UnlinkFn + + ;; Push old ebp frame + ;; Note that the exception unwind code expects this ebp frame to point to grower's original ebp frame. + push PBP + + ;; Reset ebp to the new stack segment. + mov PBP, PSP + + ;; Get continue address + mov PAX, [PDX].LinkFrame._continue + + ;; Restore arg registers +ifdef ISA_IX64 + mov r8, [PDX].LinkFrame._r8 + mov r9, [PDX].LinkFrame._r9 +endif + mov PCX, [PDX].LinkFrame._cx + mov PDX, [PDX].LinkFrame._dx + + ;; Return. This will resume execution in the prolog of the function which called us. However + ;; we are on the new stack segment, and the function has been tail patched so it will call back to + ;; UnlinkStack when it returns. + jmp PAX + +LinkStack endp + +?c_LinkStackLimit@Class_Microsoft_Singularity_Memory_Stacks@@2EA byte 0 + +?c_UnlinkStackBegin@Class_Microsoft_Singularity_Memory_Stacks@@2EA byte 0 + +;; UnlinkStack is essentially a continuation of LinkStack; it is called from the hijacked return +;; pushed by LinkStack. Because of the unlink stub, we are on the old stack; however we have +;; some cleanup to do. +;; 1. We must call Free on the stack we allocated before, and restore the thread's stack limits. +;; 2. We must update the activeStackLimit on the thread. + +;; The stack state on entry to UnlinkStack is: +;; 1. We are back on the old stack +;; 2. Everything from the grower has been popped except its return address & args +;; 3. We have been called by the UnlinkStackN stub; it will execute the return for us + +;;; NOTE: If you change this function, please ensure that the necessary +;;; changes are made to CallStack.SkipUnlinkStackFrame + + align 16 + +UnlinkStack proc + + ;; Save return value registers + push PDX + push PAX + + ;; HACK: set limit to zero to temporarily disable stack growth + ;; We can get rid of this if we set activeStackLimit in asm code: + ;; ThreadContext *context = Isa.GetCurrentThread(); + ;; StackHead *head = (StackHead *) (context->stackBegin - sizeof(StackHead)); + ;; Isa.StackLimit = head->prevLimit; + xor PAX, PAX + SET_THREAD_RECORD_FIELD _activeStackLimit, PAX + + ;; Call Free routine + call SYMFIX(?g_FreeStackSegment@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ) + + ;; Set active stack limit + GET_THREAD_CONTEXT_FIELD PAX, _stackLimit + SET_THREAD_RECORD_FIELD _activeStackLimit, PAX + + POP PAX + POP PDX + + ;; Return to UnlinkStackN stub, which will execute ret N + ret + +UnlinkStack endp + +?c_UnlinkStackLimit@Class_Microsoft_Singularity_Memory_Stacks@@2EA byte 0 + +LINKNAME MACRO SIZE:REQ + EXITM SYMFIX(?g_LinkStack&SIZE&@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ) + ENDM + +UNLINKNAME MACRO SIZE:REQ + EXITM SYMFIX(?g_UnlinkStack&SIZE&@Class_Microsoft_Singularity_Memory_Stacks@@SIXXZ) + ENDM + +;; Implementations of LinkStackNn: push the appropriate number +;; and call the general-purpose LinkStack. + +ifdef ISA_IX64 + +; x64 does not have a PUSH OFFSET64 instruction, so there are two possible solutions. +; +; (1) LEA into a register and then push it +; (2) Use PUSH reg/mem64 (i.e. FF /6) +; +; Since stack link routines cannot polute any registers, (1) would require save/restore +; overhead as well, making (2) the more desirable solution. + +;;; NOTE: If you change these function, please ensure that the necessary +;;; changes are made to CallStack.SkipLinkStackStubFrame + +LINKSTACKSTUB MACRO SLOTS:REQ + LOCAL unlink, uaddr + align 16 +LINKNAME(%SLOTS*8): + push SLOTS + push [uaddr] + jmp LinkStack +uaddr dq unlink +unlink: + mov rsp, rbp + pop rbp + call UnlinkStack + ret SLOTS*8 + ENDM + +elseifdef ISA_IX86 + +LINKSTACKSTUB MACRO SLOTS:REQ + LOCAL unlink + align 8 +LINKNAME(%SLOTS*4): + push SLOTS + push unlink + jmp LinkStack +unlink: + mov esp, ebp + pop ebp + call UnlinkStack + ret SLOTS*4 + ENDM + +endif + +?c_LinkStackStubsBegin@Class_Microsoft_Singularity_Memory_Stacks@@2EA byte 0 + + LINKSTACKSTUB 0 + LINKSTACKSTUB 1 + LINKSTACKSTUB 2 + LINKSTACKSTUB 3 + LINKSTACKSTUB 4 + LINKSTACKSTUB 5 + LINKSTACKSTUB 6 + LINKSTACKSTUB 7 + LINKSTACKSTUB 8 + LINKSTACKSTUB 9 + LINKSTACKSTUB 10 + LINKSTACKSTUB 11 + LINKSTACKSTUB 12 + LINKSTACKSTUB 13 + LINKSTACKSTUB 14 + LINKSTACKSTUB 15 + LINKSTACKSTUB 16 + LINKSTACKSTUB 17 + LINKSTACKSTUB 18 + LINKSTACKSTUB 19 + LINKSTACKSTUB 20 + LINKSTACKSTUB 21 + LINKSTACKSTUB 22 + LINKSTACKSTUB 23 + LINKSTACKSTUB 24 + LINKSTACKSTUB 25 + LINKSTACKSTUB 26 + LINKSTACKSTUB 27 + LINKSTACKSTUB 28 + LINKSTACKSTUB 29 + LINKSTACKSTUB 30 + LINKSTACKSTUB 31 + LINKSTACKSTUB 32 + +?c_LinkStackStubsLimit@Class_Microsoft_Singularity_Memory_Stacks@@2EA byte 0 + +;; CheckStackLimit is the routine that Bartok-generated code calls to check for +;; stack growth + +align 16 +PUBLIC __checkStackLimit +__checkStackLimit PROC + ;; Note: expected stack growth limit is in PAX + + ;; Bartok doesn't allow quite enough overhead for the stack switch + ;; now that it occurs in managed code. So, we will pretend bartok + ;; asked for more stack until it can be updated. Note that this + ;; is not quite sufficient; there are cases where bartok will not + ;; even probe which we cannot alter. + sub eax, 32*sizeof(PWORD) + + ;; Free up a register + push PDX + +ifdef PARANOID_CHECKS + + ;; Free up another register + push PCX + + ;; See if we are on the interrupt stack + GET_THREAD_RECORD_FIELD PDX, _activeStackLimit + GET_CPU_RECORD_FIELD PCX, _interruptStackLimit + cmp PCX, PDX + jne not_interrupt_stack + + ;; Check for stack underflow + GET_CPU_RECORD_FIELD PCX, _interruptStackBegin + cmp PCX, 0 ; kernel startup? + je stack_ok + cmp PSP, PCX ; underflow? + jl stack_ok + int 3 + + ;; (Note: we will check for overflow below in the normal code) + +not_interrupt_stack: + GET_THREAD_CONTEXT_FIELD PCX, _stackLimit + cmp PCX, PDX + jne not_thread_stack + + ;; Check for stack underflow + GET_THREAD_CONTEXT_FIELD PCX, _stackBegin + cmp PCX, 0 ; kernel startup? + je stack_ok + cmp PSP, PCX ; underflow? + jl stack_ok + int 3 + +not_thread_stack: + ;; Check for kernel startup case + cmp PCX, 0 + je stack_ok + ;; Check for disabled stack limit checks case + cmp PDX, 0 + je stack_ok + ;; activeStackLimit appears to be bogus + int 3 + +stack_ok: + + pop PCX + +endif ; PARANOID_CHECKS + + GET_THREAD_RECORD_FIELD PDX, _activeStackLimit + cmp PAX, PDX + jb grow + pop PDX + ret + +grow: + ;; Since this is a slow path, always do some extra checks here. + + ;; Check for stack overflow + cmp PSP, PDX + jg no_overflow + int 3 + +no_overflow: + ;; Now check to see if we are on the interrupt stack; we shouldn't be growing. + GET_CPU_RECORD_FIELD PAX, _interruptStackLimit + cmp PDX, PAX + jne not_interrupt + int 3 + +not_interrupt: + pop PDX + ;; this sets the status bit checked by "jb" above; our caller will be checking this + stc + ret + + __checkStackLimit ENDP + +?c_LinkStackFunctionsLimit@Class_Microsoft_Singularity_Memory_Stacks@@2EA byte 0 + +end diff --git a/base/Kernel/Native/ix/ix.inc b/base/Kernel/Native/ix/ix.inc new file mode 100644 index 0000000..2d7f06f --- /dev/null +++ b/base/Kernel/Native/ix/ix.inc @@ -0,0 +1,211 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; +; Include file for intel/amd assembly files. +; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Standard x86/x64 asm file setup + +ifdef ISA_IX86 + +.686p +.mmx +.xmm +.model flat + +assume ds:flat +assume es:flat +assume ss:flat +assume fs:nothing +assume gs:nothing + +endif + +.code + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; General purpose utility macros + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; DEFINED is a generic utility macro which tests if an identifier is defined +DEFINED MACRO SYM:REQ + IFDEF SYM + EXITM <-1> + ELSE + EXITM <0> + ENDIF +ENDM + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; REPLACE is a generic utility macro which replaces FIND with REPL in STRING +; (if it occurs) + +REPLACE MACRO STRING:REQ, FIND:REQ, REPL:REQ + LOCAL len, pos, start, end + len = @SizeStr(FIND) + pos = @InStr(,STRING,FIND) + IF pos NE 0 + start TEXTEQU @SubStr(STRING,1,pos-1) + if pos+len-1 LT @SizeStr(STRING) + end TEXTEQU @SubStr(STRING,pos+len) + else + end TEXTEQU <> + endif + EXITM @CatStr(%start, REPL, %end) + else + EXITM + endif +ENDM + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +SINGLE_THREADED equ 0 +EXCLUDED equ 0 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; Typedefs & Constants +; +UINT8 TYPEDEF BYTE +UINT16 TYPEDEF WORD +UINT32 TYPEDEF DWORD +UINT64 TYPEDEF QWORD +UINT128 STRUCT 16 + _lo UINT64 ? + _hi UINT64 ? +UINT128 ENDS + +; +X86_EFLAG_IF equ 0200h + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; X86/X64 portability names (P = pointer) +; + +ifdef ISA_IX86 + +PAX equ eax +PBX equ ebx +PCX equ ecx +PDX equ edx +PSI equ esi +PDI equ edi +PBP equ ebp +PSP equ esp +PIP equ eip + +PSEG equ fs + +dp EQU dd + +PWORD TYPEDEF DWORD + +PUSHFP equ pushfd +IRETP equ iretd + +elseifdef ISA_IX64 + +PAX equ rax +PBX equ rbx +PCX equ rcx +PDX equ rdx +PSI equ rsi +PDI equ rdi +PBP equ rbp +PSP equ rsp +PIP equ rip + +PSEG equ gs + +dp EQU dq + +PWORD TYPEDEF QWORD + +PUSHFP equ pushfq +IRETP equ iretq + +endif + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 32/64 register conversion + +;; Note InStr step in the logic is to deal with possible parens which seem to +;; make their way into the REG argument sometimes. Unfortunately the logic doesn't +;; current work with non --x registers. Oh well. + +REG32 MACRO REG:REQ + EXITM <@CatStr(e, @SubStr(REG,@InStr(,REG,x)+@InStr(,REG,X)-1,2))> + ENDM + +REG64 MACRO REG:REQ + EXITM <@CatStr(r, @SubStr(REG,@InStr(,REG,x)+@InStr(,REG,X)-1,2))> + ENDM + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; SYMFIX fixes mangled symbols to be the 64-bit version if we are compiling 64 bit. +; Specifically this looks for @@SI and replaces with @@SA, and replaces PAU with PEAU (up to twice.) + +SYMFIX MACRO SYM:REQ + LOCAL TEMP +ifdef ISA_IX64 +TEMP TEXTEQU REPLACE(SYM,<@@SI>,<@@SA>) ; __fastcall -> __cdecl +TEMP TEXTEQU REPLACE(%TEMP,<@@YI>,<@@YA>) ; __fastcall -> __cdecl +TEMP TEXTEQU REPLACE(%TEMP,<@@A>,<@@EA>) ; (ptr) -> __ptr64 +TEMP TEXTEQU REPLACE(%TEMP,,) ; * --> * __ptr64 +TEMP TEXTEQU REPLACE(%TEMP,,) ; * --> * __ptr64 +TEMP TEXTEQU REPLACE(%TEMP,,) ; * --> * __ptr64 +TEMP TEXTEQU REPLACE(%TEMP,,) ; * --> * __ptr64 +TEMP TEXTEQU REPLACE(%TEMP,,) ; * --> * __ptr64 + EXITM TEMP +else + EXITM +endif + ENDM + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; SYMEXT fixes an external data symbol + +SYMEXT MACRO SYM:REQ +ifdef ISA_IX64 + EXITM +else + EXITM <@CatStr(<_>,SYM)> +endif + ENDM + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; Bartok Types +; + +uint8 TYPEDEF BYTE +uint16 TYPEDEF WORD +uint32 TYPEDEF DWORD +uint64 TYPEDEF QWORD + +int8 TYPEDEF BYTE +int16 TYPEDEF WORD +int32 TYPEDEF DWORD +int64 TYPEDEF QWORD + +bool TYPEDEF BYTE +bartok_char TYPEDEF WORD + +intptr TYPEDEF PWORD +uintptr TYPEDEF PWORD + +uintPtr STRUCT SIZEOF uintptr + value uintptr ? +uintPtr ENDS + +intPtr STRUCT SIZEOF intptr + value intptr ? +intPtr ENDS + +PTR_uintptr TYPEDEF PTR uintptr + + + diff --git a/base/Kernel/Native/ix/kdnotify.asm b/base/Kernel/Native/ix/kdnotify.asm new file mode 100644 index 0000000..d3921fe --- /dev/null +++ b/base/Kernel/Native/ix/kdnotify.asm @@ -0,0 +1,28 @@ +;; ---------------------------------------------------------------------------- +;; +;; Copyright (c) Microsoft Corporation. All rights reserved. +;; +;; ---------------------------------------------------------------------------- + +include hal.inc + +SYMFIX(?KdNotifyTrap@@YIXPAUKdDebugTrapData@@@Z) proc + + nop + mov PAX, PCX + int 29 + ret + +SYMFIX(?KdNotifyTrap@@YIXPAUKdDebugTrapData@@@Z) endp + + +SYMFIX(?KdNotifyException@@YIXPAUClass_System_Exception@@I@Z) proc + GET_PROCESSOR_CONTEXT PAX + mov [PAX].Struct_Microsoft_Singularity_ProcessorContext._exception, PCX + mov PAX, PDX + int 29 ;Notify debugging stub of first chance exception. + ret +SYMFIX(?KdNotifyException@@YIXPAUClass_System_Exception@@I@Z) endp + +end + diff --git a/base/Kernel/Native/ix64/_memcpy.asm b/base/Kernel/Native/ix64/_memcpy.asm new file mode 100644 index 0000000..68d9b2b --- /dev/null +++ b/base/Kernel/Native/ix64/_memcpy.asm @@ -0,0 +1,613 @@ +;******************************************************************************* +;memcpy.asm - contains memcpy and memmove routines +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; +;Purpose: +; memcpy() copies a source memory buffer to a destination buffer. +; Overlapping buffers are not treated specially, so propogation may occur. +; memmove() copies a source memory buffer to a destination buffer. +; Overlapping buffers are treated specially, to avoid propogation. +; +;******************************************************************************* + +.code + +include hal.inc + +;*** +;memcpy - Copy source buffer to destination buffer +; +;Purpose: +; memcpy() copies a source memory buffer to a destination memory buffer. +; This routine does NOT recognize overlapping buffers, and thus can lead +; to propogation. +; For cases where propogation must be avoided, memmove() must be used. +; +; Algorithm: +; +; void * memcpy(void * dst, void * src, size_t count) +; { +; void * ret = dst; +; +; /* +; * copy from lower addresses to higher addresses +; */ +; while (count--) +; *dst++ = *src++; +; +; return(ret); +; } +; +;memmove - Copy source buffer to destination buffer +; +;Purpose: +; memmove() copies a source memory buffer to a destination memory buffer. +; This routine recognize overlapping buffers to avoid propogation. +; For cases where propogation is not a problem, memcpy() can be used. +; +; Algorithm: +; +; void * memmove(void * dst, void * src, size_t count) +; { +; void * ret = dst; +; +; if (dst <= src || dst >= (src + count)) { +; /* +; * Non-Overlapping Buffers +; * copy from lower addresses to higher addresses +; */ +; while (count--) +; *dst++ = *src++; +; } +; else { +; /* +; * Overlapping Buffers +; * copy from higher addresses to lower addresses +; */ +; dst += count - 1; +; src += count - 1; +; +; while (count--) +; *dst-- = *src--; +; } +; +; return(ret); +; } +; +; +;Entry: +; void *dst = pointer to destination buffer +; const void *src = pointer to source buffer +; size_t count = number of bytes to copy +; +;Exit: +; Returns a pointer to the destination buffer in AX/DX:AX +; +;Uses: +; CX, DX +; +;Exceptions: +;******************************************************************************* + +% public memcpy +;; memcopy (dst,src,count) + +memcpy proc ;;frame + ;;PrologPush rbp ; create ebp chain entry + ;;SetFramePointer rbp ; set new ebp + ;;.endprolog + +;;spin: jmp spin + ;;mov [esp+8], rcx ; spill dest + + push rdi ;U - save rdi + push rsi ;V - save rsi + + mov r9,rcx ; save off dst in scratch reg + + mov rsi,rdx ;U - rsi = source + + mov rdi,rcx ;U - rdi = dest + mov rcx,r8 ;V - rdx = number of bytes to move + + cmp r8,2h + jnz hack +;spin: jmp spin +hack: +; +; Check for overlapping buffers: +; If (dst <= src) Or (dst >= src + Count) Then +; Do normal (Upwards) Copy +; Else +; Do Downwards Copy to avoid propagation +; + + mov rax,r8 ;V - rax = byte count... + + mov rdx,r8 ;U - rdx = byte count... + add rax,rsi ;V - rax = point past source end + + cmp rdi,rsi ;U - dst <= src ? + jbe CopyUp ;V - yes, copy toward higher addresses + + cmp rdi,rax ;U - dst < (src + count) ? + jb CopyDown ;V - yes, copy toward lower addresses + +; +; Copy toward higher addresses. +; +; +; The algorithm for forward moves is to align the destination to a dword +; boundary and so we can move dwords with an aligned destination. This +; occurs in 3 steps. +; +; - move x = ((4 - Dest & 3) & 3) bytes +; - move y = ((L-x) >> 2) dwords +; - move (L - x - y*4) bytes +; + +CopyUp: + test rdi,11b ;U - destination dword aligned? + jnz short CopyLeadUp ;V - if we are not dword aligned already, align + + shr rcx,2 ;U - shift down to dword count + and rdx,11b ;V - trailing byte count + + cmp rcx,8 ;U - test if small enough for unwind copy + jb short CopyUnwindUp ;V - if so, then jump + + rep movsd ;N - move all of our dwords + + lea r10, TrailUpVec + jmp [rdx*8 + r10] ;N - process trailing bytes + +; +; Code to do optimal memory copies for non-dword-aligned destinations. +; + +; The following length check is done for two reasons: +; +; 1. to ensure that the actual move length is greater than any possiale +; alignment move, and +; +; 2. to skip the multiple move logic for small moves where it would +; be faster to move the bytes with one instruction. +; + + align 8 +CopyLeadUp: + + mov rax,rdi ;U - get destination offset + mov rdx,11b ;V - prepare for mask + + sub rcx,4 ;U - check for really short string - sub for adjust + jb ByteCopyUp ;V - branch to just copy bytes + + and rax,11b ;U - get offset within first dword + add rcx,rax ;V - update size after leading bytes copied + + lea r10,LeadUpVec + jmp [rax*8-8 + r10] ;N - process leading bytes + + align 8 +ByteCopyUp: + lea r10, TrailUpVec + jmp [rcx*8+32 + r10] ;N - process just bytes + + align 8 +CopyUnwindUp: + lea r10,UnwindUpVec + jmp [rcx*8 + r10] ;N - unwind dword copy + + align 8 +LeadUpVec dq LeadUp1, LeadUp2, LeadUp3 + + align 8 +LeadUp1: + and rdx,rcx ;U - trailing byte count + mov al,[rsi] ;V - get first byte from source + + mov [rdi],al ;U - write second byte to destination + mov al,[rsi+1] ;V - get second byte from source + + mov [rdi+1],al ;U - write second byte to destination + mov al,[rsi+2] ;V - get third byte from source + + shr rcx,2 ;U - shift down to dword count + mov [rdi+2],al ;V - write third byte to destination + + add rsi,3 ;U - advance source pointer + add rdi,3 ;V - advance destination pointer + + cmp rcx,8 ;U - test if small enough for unwind copy + jb short CopyUnwindUp ;V - if so, then jump + + rep movsd ;N - move all of our dwords + + + ;;jmp TrailUpVec[rdx*4] ;N - process trailing bytes + lea r10, TrailUpVec + jmp [rdx*8 + r10] ;N - process trailing bytes + + align 8 +LeadUp2: + and rdx,rcx ;U - trailing byte count + mov al,[rsi] ;V - get first byte from source + + mov [rdi],al ;U - write second byte to destination + mov al,[rsi+1] ;V - get second byte from source + + shr rcx,2 ;U - shift down to dword count + mov [rdi+1],al ;V - write second byte to destination + + add rsi,2 ;U - advance source pointer + add rdi,2 ;V - advance destination pointer + + cmp rcx,8 ;U - test if small enough for unwind copy + jb CopyUnwindUp ;V - if so, then jump + + rep movsd ;N - move all of our dwords + + ;jmp qword ptr TrailUpVec[rdx*4] ;N - process trailing bytes + lea r10, TrailUpVec + jmp [rdx*8 + r10] ;N - process trailing bytes + + align 8 +LeadUp3: + and rdx,rcx ;U - trailing byte count + mov al,[rsi] ;V - get first byte from source + + mov [rdi],al ;U - write second byte to destination + add rsi,1 ;V - advance source pointer + + shr rcx,2 ;U - shift down to dword count + add rdi,1 ;V - advance destination pointer + + cmp rcx,8 ;U - test if small enough for unwind copy + jb CopyUnwindUp ;V - if so, then jump + + rep movsd ;N - move all of our dwords + + ;jmp qword ptr TrailUpVec[rdx*4] ;N - process trailing bytes + lea r10, TrailUpVec + jmp [rdx*8 + r10] ;N - process trailing bytes + + align 8 +UnwindUpVec dq UnwindUp0, UnwindUp1, UnwindUp2, UnwindUp3 + dq UnwindUp4, UnwindUp5, UnwindUp6, UnwindUp7 + +UnwindUp7: + mov eax,[rsi+rcx*4-28] ;U - get dword from source + ;V - spare + mov [rdi+rcx*4-28],eax ;U - put dword into destination +UnwindUp6: + mov eax,[rsi+rcx*4-24] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [rdi+rcx*4-24],eax ;U - put dword into destination +UnwindUp5: + mov eax,[rsi+rcx*4-20] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [rdi+rcx*4-20],eax ;U - put dword into destination +UnwindUp4: + mov eax,[rsi+rcx*4-16] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [rdi+rcx*4-16],eax ;U - put dword into destination +UnwindUp3: + mov eax,[rsi+rcx*4-12] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [rdi+rcx*4-12],eax ;U - put dword into destination +UnwindUp2: + mov eax,[rsi+rcx*4-8] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [rdi+rcx*4-8],eax ;U - put dword into destination +UnwindUp1: + mov eax,[rsi+rcx*4-4] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [rdi+rcx*4-4],eax ;U - put dword into destination + + lea rax,[rcx*4] ;V - compute update for pointer + + add rsi,rax ;U - update source pointer + add rdi,rax ;V - update destination pointer +UnwindUp0: + ;jmp qword ptr TrailUpVec[rdx*4] ;N - process trailing bytes + lea r10, TrailUpVec + jmp [rdx*8 + r10] ;N - process trailing bytes + +;----------------------------------------------------------------------------- + + align 8 +TrailUpVec dq TrailUp0, TrailUp1, TrailUp2, TrailUp3 + + align 8 +TrailUp0: + mov rax, r9 ;U - return pointer to destination + pop rsi ;V - restore rsi + pop rdi ;U - restore rdi + ;V - spare + ret + + align 8 +TrailUp1: + mov al,[rsi] ;U - get byte from source + ;V - spare + mov [rdi],al ;U - put byte in destination + mov rax,r9 ;V - return pointer to destination + pop rsi ;U - restore rsi + pop rdi ;V - restore rdi + ret + + align 8 +TrailUp2: + mov al,[rsi] ;U - get first byte from source + ;V - spare + mov [rdi],al ;U - put first byte into destination + mov al,[rsi+1] ;V - get second byte from source + mov [rdi+1],al ;U - put second byte into destination + mov rax,r9 ;V - return pointer to destination + pop rsi ;U - restore rsi + pop rdi ;V - restore rdi + ret + + align 8 +TrailUp3: + mov al,[rsi] ;U - get first byte from source + ;V - spare + mov [rdi],al ;U - put first byte into destination + mov al,[rsi+1] ;V - get second byte from source + mov [rdi+1],al ;U - put second byte into destination + mov al,[rsi+2] ;V - get third byte from source + mov [rdi+2],al ;U - put third byte into destination + mov rax,r9 ;V - return pointer to destination + pop rsi ;U - restore rsi + pop rdi ;V - restore rdi + ret + +;----------------------------------------------------------------------------- +;----------------------------------------------------------------------------- +;----------------------------------------------------------------------------- + +; +; Copy down to avoid propogation in overlapping buffers. +; + align 8 +CopyDown: + lea rsi,[rsi+rcx-4] ;U - point to 4 bytes before src buffer end + lea rdi,[rdi+rcx-4] ;V - point to 4 bytes before dest buffer end +; +; See if the destination start is dword aligned +; + + test rdi,11b ;U - test if dword aligned + jnz short CopyLeadDown ;V - if not, jump + + shr rcx,2 ;U - shift down to dword count + and rdx,11b ;V - trailing byte count + + cmp rcx,8 ;U - test if small enough for unwind copy + jb short CopyUnwindDown ;V - if so, then jump + + std ;N - set direction flag + rep movsd ;N - move all of our dwords + cld ;N - clear direction flag back + + ;jmp qword ptr TrailDownVec[rdx*4] ;N - process trailing bytes + lea r10, TrailDownVec + jmp [rdx*8 + r10] ;N - process trailing bytes + + align 8 +CopyUnwindDown: + neg rcx ;U - negate dword count for table merging + ;V - spare + + ;jmp qword ptr UnwindDownVec[rcx*4+28] ;N - unwind copy + lea r10,UnwindDownVec + jmp [rcx*8+56 + r10] ;N - unwind copy + + align 8 +CopyLeadDown: + + mov rax,rdi ;U - get destination offset + mov rdx,11b ;V - prepare for mask + + cmp rcx,4 ;U - check for really short string + jb short ByteCopyDown ;V - branch to just copy bytes + + and rax,11b ;U - get offset within first dword + sub rcx,rax ;U - to update size after lead copied + + ;jmp qword ptr LeadDownVec[rax*8-8] ;N - process leading bytes + lea r10,LeadDownVec + jmp [rax*8-8 + r10] ;N - process leading bytes + + align 8 +ByteCopyDown: + ;jmp qword ptr TrailDownVec[rcx*8] ;N - process just bytes + lea r10, TrailDownVec + jmp [rcx*8 + r10] ;N - process just bytes + + align 8 +LeadDownVec dq LeadDown1, LeadDown2, LeadDown3 + + align 8 +LeadDown1: + mov al,[rsi+3] ;U - load first byte + and rdx,rcx ;V - trailing byte count + + mov [rdi+3],al ;U - write out first byte + sub rsi,1 ;V - point to last src dword + + shr rcx,2 ;U - shift down to dword count + sub rdi,1 ;V - point to last dest dword + + cmp rcx,8 ;U - test if small enough for unwind copy + jb CopyUnwindDown ;V - if so, then jump + + std ;N - set direction flag + rep movsd ;N - move all of our dwords + cld ;N - clear direction flag + + ;jmp qword ptr TrailDownVec[rdx*4] ;N - process trailing bytes + lea r10, TrailDownVec + jmp [rdx*8 + r10] ;N - process trailing bytes + + align 8 +LeadDown2: + mov al,[rsi+3] ;U - load first byte + and rdx,rcx ;V - trailing byte count + + mov [rdi+3],al ;U - write out first byte + mov al,[rsi+2] ;V - get second byte from source + + shr rcx,2 ;U - shift down to dword count + mov [rdi+2],al ;V - write second byte to destination + + sub rsi,2 ;U - point to last src dword + sub rdi,2 ;V - point to last dest dword + + cmp rcx,8 ;U - test if small enough for unwind copy + jb CopyUnwindDown ;V - if so, then jump + + std ;N - set direction flag + rep movsd ;N - move all of our dwords + cld ;N - clear direction flag + + ;jmp qword ptr TrailDownVec[rdx*4] ;N - process trailing bytes + lea r10, TrailDownVec + jmp [rdx*8 + r10] ;N - process trailing bytes + + align 8 +LeadDown3: + mov al,[rsi+3] ;U - load first byte + and rdx,rcx ;V - trailing byte count + + mov [rdi+3],al ;U - write out first byte + mov al,[rsi+2] ;V - get second byte from source + + mov [rdi+2],al ;U - write second byte to destination + mov al,[rsi+1] ;V - get third byte from source + + shr rcx,2 ;U - shift down to dword count + mov [rdi+1],al ;V - write third byte to destination + + sub rsi,3 ;U - point to last src dword + sub rdi,3 ;V - point to last dest dword + + cmp rcx,8 ;U - test if small enough for unwind copy + jb CopyUnwindDown ;V - if so, then jump + + std ;N - set direction flag + rep movsd ;N - move all of our dwords + cld ;N - clear direction flag + + ;jmp qword ptr TrailDownVec[rdx*4] ;N - process trailing bytes + lea r10, TrailDownVec + jmp [rdx*8 + r10] ;N - process trailing bytes + +;------------------------------------------------------------------ + + align 8 +UnwindDownVec dq UnwindDown7, UnwindDown6, UnwindDown5, UnwindDown4 + dq UnwindDown3, UnwindDown2, UnwindDown1, UnwindDown0 + +UnwindDown7: + mov rax,[rsi+rcx*4+28] ;U - get dword from source + ;V - spare + mov [rdi+rcx*4+28],rax ;U - put dword into destination +UnwindDown6: + mov rax,[rsi+rcx*4+24] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [rdi+rcx*4+24],rax ;U - put dword into destination +UnwindDown5: + mov rax,[rsi+rcx*4+20] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [rdi+rcx*4+20],rax ;U - put dword into destination +UnwindDown4: + mov rax,[rsi+rcx*4+16] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [rdi+rcx*4+16],rax ;U - put dword into destination +UnwindDown3: + mov rax,[rsi+rcx*4+12] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [rdi+rcx*4+12],rax ;U - put dword into destination +UnwindDown2: + mov rax,[rsi+rcx*4+8] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [rdi+rcx*4+8],rax ;U - put dword into destination +UnwindDown1: + mov rax,[rsi+rcx*4+4] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [rdi+rcx*4+4],rax ;U - put dword into destination + + lea rax,[rcx*4] ;V - compute update for pointer + + add rsi,rax ;U - update source pointer + add rdi,rax ;V - update destination pointer +UnwindDown0: + ;jmp qword ptr TrailDownVec[rdx*4] ;N - process trailing bytes + lea r10, TrailDownVec + jmp [rdx*8 + r10] ;N - process trailing bytes + +;----------------------------------------------------------------------------- + + align 8 +TrailDownVec dq TrailDown0, TrailDown1, TrailDown2, TrailDown3 + + align 8 +TrailDown0: + mov rax,r9 ;U - return pointer to destination + ;V - spare + pop rsi ;U - restore rsi + pop rdi ;V - restore rdi + ret + + align 8 +TrailDown1: + mov al,[rsi+3] ;U - get byte from source + ;V - spare + mov [rdi+3],al ;U - put byte in destination + mov rax,r9 ;V - return pointer to destination + pop rsi ;U - restore rsi + pop rdi ;V - restore rdi + ret + + align 8 +TrailDown2: + mov al,[rsi+3] ;U - get first byte from source + ;V - spare + mov [rdi+3],al ;U - put first byte into destination + mov al,[rsi+2] ;V - get second byte from source + mov [rdi+2],al ;U - put second byte into destination + mov rax,r9 ;V - return pointer to destination + pop rsi ;U - restore rsi + pop rdi ;V - restore rdi + ret + + align 8 +TrailDown3: + mov al,[rsi+3] ;U - get first byte from source + ;V - spare + mov [rdi+3],al ;U - put first byte into destination + mov al,[rsi+2] ;V - get second byte from source + mov [rdi+2],al ;U - put second byte into destination + mov al,[rsi+1] ;V - get third byte from source + mov [rdi+1],al ;U - put third byte into destination + mov rax,r9 ;V - return pointer to destination + pop rsi ;U - restore rsi + pop rdi ;V - restore rdi + ret + +memcpy endp + + +memmove proc \ + dst:ptr byte, \ + src:ptr byte, \ + count:DWORD + + jmp memcpy + +memmove endp + end + diff --git a/base/Kernel/Native/ix64/_memset.asm b/base/Kernel/Native/ix64/_memset.asm new file mode 100644 index 0000000..6b40f8c --- /dev/null +++ b/base/Kernel/Native/ix64/_memset.asm @@ -0,0 +1,132 @@ +;******************************************************************************* +;memset.asm - set a section of memory to all one byte +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; +;Purpose: +; contains the memset() routine +; +;******************************************************************************* + +.code + +;*** +;char *memset(dst, value, count) - sets "count" bytes at "dst" to "value" +; +;Purpose: +; Sets the first "count" bytes of the memory starting +; at "dst" to the character value "value". +; +; Algorithm: +; char * +; memset (dst, value, count) +; char *dst; +; char value; +; unsigned int count; +; { +; char *start = dst; +; +; while (count--) +; *dst++ = value; +; return(start); +; } +; +;Entry: +; char *dst - pointer to memory to fill with value +; char value - value to put in dst bytes +; int count - number of bytes of dst to fill +; +;Exit: +; returns dst, with filled bytes +; +;Uses: +; +;Exceptions: +; +;******************************************************************************* + + public memset +memset proc +; +; On entry: rcx=dest, rdx=value, r8=count +; + test r8,r8 ; 0? + jz short toend ; if so, nothing to do + + xor rax,rax + mov al,dl ; al = value + mov rdx,r8 ; rdx = "count" + mov r8,rcx ; scroll away dest for return + +; Align address on qword boundary + + push rdi ; preserve rdi + mov rdi,rcx ; rdi = dest pointer + + cmp rdx,8 ; if it's less then 8 bytes + jb tail ; tail needs rdi and rdx to be initialized + + neg rcx + and rcx,7 ; ecx = # bytes before dword boundary + jz short qwords ; jump if address already aligned + + sub rdx,rcx ; edx = adjusted count (for later) +adjust_loop: + mov [rdi],al + add rdi,1 + sub rcx,1 + jnz adjust_loop + +qwords: +; set all 8 bytes of eax to [value] + mov rcx,rax ; rcx=0/0/0/0/0/0/0/value + shl rax,8 ; rax=0/0/0/0/0/0/value/0 + + add rax,rcx ; rax=0/0/0/0/0/0/0/val/val + + mov rcx,rax ; rcx=0/0/0/0/0/0/0/val/val + + shl rax,10h ; rax=0/0/0/0/val/val/0/0 + + add rax,rcx ; rax = all 4 bytes = [value] + + mov rcx,rax ; rcx = 0/0/0/0/val/val/val/val + shl rax,20h ; rax = val/val/val/val/0/0/0/0/ + + add rax,rcx ; rax = val/val/val/val/val/val/val/val + + +; Set qword-sized blocks + mov rcx,rdx ; move original count to ecx + and rdx,7 ; prepare in edx byte count (for tail loop) + shr rcx,3 ; adjust ecx to be dword count + jz tail ; jump if it was less then 4 bytes + + rep stosq +main_loop_tail: + test rdx,rdx ; if there is no tail bytes, + jz finish ; we finish, and it's time to leave +; Set remaining bytes + +tail: + mov [rdi],al ; set remaining bytes + add rdi,1 + + sub rdx,1 ; if there is some more bytes + jnz tail ; continue to fill them + +; Done +finish: + mov rax,r8 ; return dest pointer + pop rdi ; restore edi + + ret + +toend: + mov rax,rcx ; return dest pointer + + ret + +memset endp + + end diff --git a/base/Kernel/Native/ix64/halasm.asm b/base/Kernel/Native/ix64/halasm.asm new file mode 100644 index 0000000..ae60639 --- /dev/null +++ b/base/Kernel/Native/ix64/halasm.asm @@ -0,0 +1,1624 @@ +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; + +.code + +ifdef SINGULARITY +SINGLE_THREADED equ 0 +else ;; SINGULARITY +EXCLUDED equ 0 +SINGLE_THREADED equ 0 +endif ;; SINGULARITY + +include hal.inc + +externdef __throwDispatcher:NEAR +externdef __throwDispatcherExplicitAddrAfter:NEAR +; was externdef ?ExceptionTableLookup@@YI_KPEAUClass_System_Exception@@I@Z:NEAR +; don't understand the I -> _K change yet +;;externdef ?ExceptionTableLookup@@YA?AUPtrPair@@PEAUClass_System_Exception@@_K@Z:NEAR +externdef ?ExceptionTableLookup@@YA?AUPtrPair@@PEAUClass_System_Exception@@_K@Z:NEAR +ifndef SINGULARITY +externdef ?ResetGuardPage@@YAXXZ:NEAR +endif ;; !SINGULARITY + +ifdef SINGULARITY_KERNEL +externdef ?g_setStopContext@Class_System_Threading_Thread@@SAXPEAU1@PEAUClass_System_Exception@@@Z:NEAR +externdef __throwBeyondMarker:NEAR +endif ; SINGULARITY_KERNEL + +externdef __throwDivideByZeroException:NEAR +externdef __throwNullPointerException:NEAR +externdef __throwOverflowException:NEAR +externdef __throwStackOverflowException:NEAR + +ifndef SINGULARITY +externdef ?g_throwNewOverflowException@Class_System_VTable@@SAXXZ:NEAR +externdef ?c_CheckCount@Class_System_GCs_StackManager@@2HA:int32 +externdef ?g_GetStackChunk@Class_System_GCs_StackManager@@SAPEAUuintPtr@@PEAU2@PEAUClass_System_Threading_Thread@@_N@Z:NEAR +externdef ?g_ReturnStackChunk@Class_System_GCs_StackManager@@SAXPEAUClass_System_Threading_Thread@@_N@Z:NEAR +; +; externals used to construct instances of machine-level exceptions +; + +externdef ?g_AllocateObject@Class_System_GC@@SAPEAUClass_System_Object@@PEAUClass_System_VTable@@@Z:NEAR +externdef ?m__ctor@Class_System_ArithmeticException@@SAXPEAU1@@Z:NEAR +externdef ??_7System_ArithmeticException@@6B@:dword ; vtable +externdef ?m__ctor@Class_System_DivideByZeroException@@SAXPEAU1@@Z:NEAR +externdef ??_7System_DivideByZeroException@@6B@:dword ; vtable +externdef ?m__ctor@Class_System_NullReferenceException@@SAXPEAU1@@Z:NEAR +externdef ??_7System_NullReferenceException@@6B@:dword ; vtable +externdef ?m__ctor@Class_System_OverflowException@@SAXPEAU1@@Z:NEAR +externdef ??_7System_OverflowException@@6B@:dword ; vtable +externdef ?m__ctor@Class_System_StackOverflowException@@SAXPEAU1@@Z:NEAR +externdef ??_7System_StackOverflowException@@6B@:PTR_Class_System_VTable + +externdef ?c_allocationInhibitGC@Class_System_GC@@2_NA:dword + +externdef ?g_doubleToInt@Class_System_VTable@@SAHN@Z:NEAR +externdef ?g_doubleToLong@Class_System_VTable@@SA_JN@Z:NEAR +externdef ?g_floatToInt@Class_System_VTable@@SAHM@Z:NEAR +externdef ?g_floatToLong@Class_System_VTable@@SA_JM@Z:NEAR +externdef ?g_checkedDoubleToInt@Class_System_VTable@@SAHN@Z:NEAR +externdef ?g_checkedDoubleToLong@Class_System_VTable@@SA_JN@Z:NEAR +externdef ?g_checkedFloatToInt@Class_System_VTable@@SAHM@Z:NEAR +externdef ?g_checkedFloatToLong@Class_System_VTable@@SA_JM@Z:NEAR + +externdef ?g_Abs@Class_System_Math@@SAMM@Z:NEAR +externdef ?g_Abs@Class_System_Math@@SANN@Z:NEAR +externdef ?g_Atan@Class_System_Math@@SANN@Z:NEAR +externdef ?g_Ceiling@Class_System_Math@@SANN@Z:NEAR +externdef ?g_Cos@Class_System_Math@@SANN@Z:NEAR +externdef ?g_Floor@Class_System_Math@@SANN@Z:NEAR +externdef ?g_Log@Class_System_Math@@SANN@Z:NEAR +externdef ?g_Round@Class_System_Math@@SANN@Z:NEAR +externdef ?g_Sin@Class_System_Math@@SANN@Z:NEAR +externdef ?g_Tan@Class_System_Math@@SANN@Z:NEAR +externdef ?g_atan2@Class_System_Math@@SANNN@Z:NEAR +externdef ?g_exp@Class_System_Math@@SANN@Z:NEAR +endif ;; !SINGULARITY + +if EXCLUDED +externdef __checkFPStackDepth0:NEAR +externdef __checkFPStackDepth1:NEAR +externdef __checkFPStackDepth2:NEAR +externdef __checkFPStackDepth3:NEAR +externdef __checkFPStackDepth4:NEAR +externdef __checkFPStackDepth5:NEAR +externdef __checkFPStackDepth6:NEAR +externdef __checkFPStackDepth7:NEAR +endif ;; EXCLUDED + + align 8 +$DBLMAXINT DQ 041dfffffffc00000r ; 2.14747e+009 +$DBLMININT DQ 0c1e0000000000000r ; -2.14748e+009 +$MAXLONG DQ 07fffffffffffffffh +$MINLONG DQ 08000000000000000h + +; +; __throwDispatcher(ecx=exception) +; +; Description: this function is called to explicitly throw an exception. +; It assumes that the return address points to the instruction immediately +; after the one where the exception was thrown +; +; Arguments: +; ecx = exception object +; [esp] = the return address +; Effects: +; 1. Create ebp chain entry +; 2. Get return address and uses it to calculate the address of the +; instruction that threw the exception. +; 3. Looks up the appropriate handler and jumps to code to process it. + +align 16 + +__throwDispatcher proc frame + PrologPush rbp ; create ebp chain entry + SetFramePointer rbp ; set new ebp + .endprolog + mov rdx, [rbp+8] ; get return address + push rcx ; save exception + sub rdx, 1 ; adjust to point to throw location + ;; set up parameter + mov r8, rdx + mov rdx, rcx + sub rsp, 16 + mov rcx, rsp + ;; set up the frame for parameter + sub rsp, 32 + call ?ExceptionTableLookup@@YA?AUPtrPair@@PEAUClass_System_Exception@@_K@Z + add rsp, 32 + pop rax + pop rdx + pop rcx ; restore exception + pop rbp ; remove ebp chain + add rsp, 8 ; remove eip from the stack +; mov rdx is already ok + jmp __throwDispatcherHandler +__throwDispatcher endp + +; +; __throwDispatcherExplicitAddr (ecx=exception, edx=throwAddress) +; +; Description: +; This is to be used when the address where the exception occurred +; is passed in as an extra argument +; +; Arguments: +; ecx = exception object +; edx = address where the exception was thrown +; + + +align 16 +__throwDispatcherExplicitAddr proc frame + PrologPush rcx ; save exception + ;; set up paramter + mov r8, rdx + mov rdx, rcx + SubRsp 16 + mov rcx, rsp + SubRsp 32 + .endprolog + call ?ExceptionTableLookup@@YA?AUPtrPair@@PEAUClass_System_Exception@@_K@Z + add rsp, 32 + pop rax + pop rdx + pop rcx ; restore exception +; mov rdx is already ok + jmp __throwDispatcherHandler +__throwDispatcherExplicitAddr endp + +; +; __throwDispatcherExplictAddrAfter (ecx=exception, edx=throwAddress) +; +; Description: +; This is to be used when the address of the instruction immediately after +; the one that actually threw the exception is passed as an argument. +; +; Arguments: +; ecx = pointer to exception object being thrown +; edx = address of the instruction following the one where +; the exception was thrown +; +; This is used, for example, in stack unwinding, where edx is the +; return address on the stack for the current procedure. +; +; Stack unwinding occurs when the current procedure does not have a handler +; for the routine. The idea is to pop the stack frame and treat the call +; instruction in the caller as though it threw. We only have the return +; address, though, which points to the instruction *after* the call. +; + +align 16 +__throwDispatcherExplicitAddrAfter proc frame +ifdef SINGULARITY + push rcx + push rdx + mov rcx, rdx + call ?g_IsUnlinkStack@Class_System_Exception@@SA_NPEAUuintPtr@@@Z + pop rdx + pop rcx + test al, al + je normal + mov rax, rcx ; save exception type + mov rcx, rdx + mov rdx, [rbp+8] ; save the return addr in caller + push rax + mov rax, afterUnlinkStack + mov [rbp+8], rax ; override return addr to instr after + pop rax + jmp rcx ; unlink stack which saves eax, edx +afterUnlinkStack: + mov rcx, rax ; restore return addr in caller +normal: +endif + PrologPush rcx ; save exception + dec rdx + ;; set up paramter + mov r8, rdx + mov rdx, rcx + SubRsp 16 + mov rcx, rsp + SubRsp 32 + .endprolog + call ?ExceptionTableLookup@@YA?AUPtrPair@@PEAUClass_System_Exception@@_K@Z + add rsp, 32 + pop rax + pop rdx + pop rcx ; restore exception +; mov edx is already ok + jmp __throwDispatcherHandler +__throwDispatcherExplicitAddrAfter endp + +ifdef SINGULARITY_KERNEL +; There are 3 control paths that merge here: +; (1) When Thread.cs stops a process mode thread, it sets eip to point here. +; (2) When Thread.cs stops a blocked kernel thread, it sets eip to point here. +; (3) The stack unwinder falls through to this case when a kernel exception +; reaches a process's frames. +; For control path (3), a finally block puts us in a gc safe state +; just before we reach here. +; We also expect to be in the gc safe state most of the +; time for control paths (1) and (2), but +; because pushStackMark and popStackMark are not atomic operations, +; we cannot be 100% sure that we're in the gc safe state. +; eax: kernel->process or kernel->kernel marker +; ecx: exception +__throwBeyondMarker LABEL NEAR + push rcx + push rax + ; Leave the gc safe state (if we're actually in it) + call ?g_RestoreMutatorControlIfNeeded@Class_System_GCs_Transitions@@SAXXZ + pop rax + pop rcx +; eax: kernel->process or kernel->kernel marker +; ecx: exception + ; Restore state from the marker, skipping over any intermediate frames. + ; Keep the original exception in ecx. + ; Get the return address for the frame beyond the marker into edx: + mov rdx, qword ptr [rax].Struct_System_GCs_CallStack_TransitionRecord._stackBottom + mov rdx, qword ptr [rdx - 8] + ; Restore the kernel's ebx, edi, esi, ebp from the marker: + mov rbx, qword ptr [rax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBX + mov rdi, qword ptr [rax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EDI + mov rsi, qword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._ESI + mov rbp, qword ptr [rax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBP + ; Keep a copy of transition record (eax) and oldTransitionRecord + push rax + push qword ptr [rax].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord + ; Read _stackBottom (the new esp) from the kernel->process marker: + mov rax, qword ptr [rax].Struct_System_GCs_CallStack_TransitionRecord._stackBottom + ; Save ecx, edx to new stack -- this may overwrite the transition record, + ; so don't read any more fields from the transition record after this point. + mov [rax - 8], ecx ; (exception) + mov [rax - 16], edx ; (return address) + ; Set up the two arguments to DiscardStackSegments + pop rdx ; (oldTransitionRecord) + pop rcx ; (marker) + ; Restore the kernel's esp from the kernel->process marker: + lea rsp, [rax - 16] + ; Free any stack segments that we skipped over: + ;; TODO: are we sure there's enough stack space to call this? + call ?g_DiscardSkippedStackSegments@Class_System_Threading_Thread@@SAXPEAUStruct_System_GCs_CallStack_TransitionRecord@@0@Z + pop rdx ; (return address) + pop rcx ; (exception) +endif ; SINGULARITY_KERNEL + + +; __throwDispatcherHandler (eax=frameSetupInfo or exceptionType, +; ecx=exception, +; edx=spillSize,frameSetupSize, or handlerAddress +; +; Description: +; After the exception table entry has been found, the values are passed here +; for processing. This method simply checks for the easy case of an explicit +; handler (known if the exceptionType is given <-- low bit is zero). +; In this case it simply jumps to the handler. Otherwise it passes the +; information along to __throwDispatcherUnwind. +; +; Arguments: +; If low bit of eax is set: +; eax = frame setup information +; ecx = exception object +; edx = spill size excluding callee-save register saves +; or offset from ebp to end of callee-save register saves +; Otherwise: +; eax = exception type (unused) +; ecx = exception object +; edx = handler address + +align 16 +__throwDispatcherHandler proc ;frame + ;.endprolog + test rax, 1 + jne __throwDispatcherUnwind + ;; ecx=exception, edx=handler + jmp rdx +__throwDispatcherHandler endp + +; __throwDispatcherUnwind (eax=frame setup info, ecx=exception, edx=spill size +; +; Description: +; This is the global unwind handler. It is used to unwind a single stack +; frame if there are no explicit handlers that catch an exception in a given +; function. +; +; Arguments: +; eax = frame setup information, must have low bit set +; ecx = exception object +; edx = spill size excluding callee-save register saves +; or offset from ebp to end of callee-save register saves +; +; See tables\ExceptionTable.cs for details on these values + +align 16 +__throwDispatcherUnwind proc ;frame + ;.endprolog + ;; eax=frame info + ;; edx=spill size + + ;; obviously ebp isn't useful under frame pointer omission + ;; but less obviously esp may be invalid if ebp is good + ;; (e.g. under varargs we may not have known how many arguments to + ;; pop; this is one reason why varargs turns off frame pointer omission) + test rax, 2h + jne esp_is_good + ;; ebp_is_good + add rdx, rbp + mov rsp, rdx + jmp esp_is_setup +esp_is_good: + ;; pop spill slots + add rsp, rdx + +esp_is_setup: + + ;; restore callee-saves and pop values from stack + ;; (excludes ebp if used as the frame pointer) + +; restore float registers + test rax, 100000h + je skip_xmm15_restore + ;; restore xmm15 + movdqa xmm15, [rsp] + add rsp, 16 +skip_xmm15_restore: + test rax, 80000h + je skip_xmm14_restore + ;; restore xmm14 + movdqa xmm14, [rsp] + add rsp, 16 +skip_xmm14_restore: + test rax, 40000h + je skip_xmm13_restore + ;; restore xmm13 + movdqa xmm13, [rsp] + add rsp, 16 +skip_xmm13_restore: + test rax, 20000h + je skip_xmm12_restore + ;; restore xmm12 + movdqa xmm12, [rsp] + add rsp, 16 +skip_xmm12_restore: + test rax, 10000h + je skip_xmm11_restore + ;; restore xmm11 + movdqa xmm11, [rsp] + add rsp, 16 +skip_xmm11_restore: + test rax, 8000h + je skip_xmm10_restore + ;; restore xmm10 + movdqa xmm10, [rsp] + add rsp, 16 +skip_xmm10_restore: + test rax, 4000h + je skip_xmm9_restore + ;; restore xmm9 + movdqa xmm9, [rsp] + add rsp, 16 +skip_xmm9_restore: + test rax, 2000h + je skip_xmm8_restore + ;; restore xmm8 + movdqa xmm8, [rsp] + add rsp, 16 +skip_xmm8_restore: + test rax, 1000h + je skip_xmm7_restore + ;; restore xmm7 + movdqa xmm7, [rsp] + add rsp, 16 +skip_xmm7_restore: + test rax, 800h + je skip_xmm6_restore + ;; restore xmm6 + movdqa xmm6, [rsp] + add rsp, 16 +skip_xmm6_restore: + +; account of alignment + test rax, 200000h + je skip_align_xmm + ;; add 8 to rsp for alignment + add rsp, 8 +skip_align_xmm: + +; restore int registers + test rax, 400h + je skip_r15_restore + ;; restore r15 + pop r15 +skip_r15_restore: + test rax, 200h + je skip_r14_restore + ;; restore r14 + pop r14 +skip_r14_restore: + test rax, 100h + je skip_r13_restore + ;; restore r13 + pop r13 +skip_r13_restore: + test rax, 80h + je skip_r12_restore + ;; restore r12 + pop r12 +skip_r12_restore: + test rax, 4h + je skip_edi_restore + ;; restore edi + pop rdi +skip_edi_restore: + test rax, 8h + je skip_esi_restore + ;; restore esi + pop rsi +skip_esi_restore: + test rax, 10h + je skip_ebp_restore + ;; restore ebp + pop rbp +skip_ebp_restore: + test rax, 20h + je skip_ebx_restore + ;; restore ebx + pop rbx +skip_ebx_restore: + + test rax, 40h + je skip_jump_transition_record + ;; jump over transition record + add rsp, (SIZE Struct_System_GCs_CallStack_TransitionRecord) +skip_jump_transition_record: + + ;; restore ebp if it was used as the frame pointer + test rax, 2h + jne skip_frame_pointer_restore + ;; restore frame pointer (esp == ebp already) + pop rbp +skip_frame_pointer_restore: + + ;; set edx=return address + pop rdx + + ;; no arguments to pop + + ;; At this point + ;; ecx=exception, edx=return address + ;; esi/edi/ebx/ebp/esp have been restored + ;; eax is scratch + + ;; set up next handler search + jmp __throwDispatcherExplicitAddrAfter +__throwDispatcherUnwind endp + +; +; __throwDivideByZeroException: instantiate an divide-by-zero exception +; and throw it. +; +; Assumes rdx points to the address after the one that threw. +; + +align 16 +__throwDivideByZeroException proc frame + PrologPush rbx + PrologPush rsi + .endprolog + mov rbx,rdx ; save address +if SINGLE_THREADED + inc ?c_allocationInhibitGC@Class_System_GC@@2_NA +else ; SINGLE_THREADED + lock inc ?c_allocationInhibitGC@Class_System_GC@@2_NA +endif ; SINGLE_THREADED + mov rcx,offset ??_7System_DivideByZeroException@@6B@ + call ?g_AllocateObject@Class_System_GC@@SAPEAUClass_System_Object@@PEAUClass_System_VTable@@@Z + mov rsi,rax ; save pointer to instance of exception + mov rcx,rax ; initialize instance + call ?m__ctor@Class_System_DivideByZeroException@@SAXPEAU1@@Z +if SINGLE_THREADED + dec ?c_allocationInhibitGC@Class_System_GC@@2_NA +else ; SINGLE_THREADED + lock dec ?c_allocationInhibitGC@Class_System_GC@@2_NA +endif ; SINGLE_THREADED + mov rcx,rsi + mov rdx,rbx + pop rsi + pop rbx + jmp __throwDispatcherExplicitAddr +__throwDivideByZeroException endp + +; +; __throwStackOverflowException: instantiate an StackOverflow exception +; and throw it. +; +; Assumes edx points to the address of the instruction that faulted +; + +align 16 +__throwStackOverflowException proc frame + PrologPush rbx + PrologPush rsi + .endprolog + mov rbx,rdx ; save address +if SINGLE_THREADED + inc ?c_allocationInhibitGC@Class_System_GC@@2_NA +else ; SINGLE_THREADED + lock inc ?c_allocationInhibitGC@Class_System_GC@@2_NA +endif ; MULTI_THREADED + mov rcx,offset ??_7System_StackOverflowException@@6B@ + call ?g_AllocateObject@Class_System_GC@@SAPEAUClass_System_Object@@PEAUClass_System_VTable@@@Z + mov rsi,rax ; save pointer to instance of exception + mov rcx,rax ; initialize instance + call ?m__ctor@Class_System_StackOverflowException@@SAXPEAU1@@Z +if SINGLE_THREADED + dec ?c_allocationInhibitGC@Class_System_GC@@2_NA +else ; SINGLE_THREADED + lock dec ?c_allocationInhibitGC@Class_System_GC@@2_NA +endif ; MULTI_THREADED + ;; set up paramter + mov rdx, rsi + mov r8, rbx + sub rsp, 16 + mov rcx, rsp + sub rsp, 32 + call ?ExceptionTableLookup@@YA?AUPtrPair@@PEAUClass_System_Exception@@_K@Z + add rsp, 32 + pop rax + pop rdx +ifndef SINGULARITY + push rsi + push rdx + call ?ResetGuardPage@@YAXXZ + pop rdx + pop rax +endif + pop rsi + pop rbx + mov rcx,rax +; mov rdx is already ok + jmp rdx +__throwStackOverflowException endp + +; +; __throwNullReferenceException: instantiate an NullReference exception +; and throw it. +; +; Assumes edx points to the address of the instruction that faulted +; + +align 16 +__throwNullReferenceException proc frame + PrologPush rbx + PrologPush rsi + .endprolog + mov rbx,rdx ; save address +if SINGLE_THREADED + inc ?c_allocationInhibitGC@Class_System_GC@@2_NA +else ; SINGLE_THREADED + lock inc ?c_allocationInhibitGC@Class_System_GC@@2_NA +endif ; SINGLE_THREADED + mov rcx,offset ??_7System_NullReferenceException@@6B@ + call ?g_AllocateObject@Class_System_GC@@SAPEAUClass_System_Object@@PEAUClass_System_VTable@@@Z + mov rsi,rax ; save pointer to instance of exception + mov rcx,rax ; initialize instance + call ?m__ctor@Class_System_NullReferenceException@@SAXPEAU1@@Z +if SINGLE_THREADED + dec ?c_allocationInhibitGC@Class_System_GC@@2_NA +else ; SINGLE_THREADED + lock dec ?c_allocationInhibitGC@Class_System_GC@@2_NA +endif ; SINGLE_THREADED + mov rcx,rsi + mov rdx,rbx + pop rsi + pop rbx + jmp __throwDispatcherExplicitAddr +__throwNullReferenceException endp + +; +; __throwDivideByZeroException: instantiate an divide-by-zero exception +; and throw it. +; +; Assumes rdx points to the address of the instruction that faulted +; + +align 16 +__throwOverflowException proc frame + PrologPush rbx + PrologPush rsi + .endprolog + mov rbx,rdx ; save address +if SINGLE_THREADED + inc ?c_allocationInhibitGC@Class_System_GC@@2_NA +else ; SINGLE_THREADED + lock inc ?c_allocationInhibitGC@Class_System_GC@@2_NA +endif ; SINGLE_THREADED + mov rcx,offset ??_7System_OverflowException@@6B@ + call ?g_AllocateObject@Class_System_GC@@SAPEAUClass_System_Object@@PEAUClass_System_VTable@@@Z + mov rsi,rax ; save pointer to instance of exception + mov rcx,rax ; initialize instance + call ?m__ctor@Class_System_OverflowException@@SAXPEAU1@@Z +if SINGLE_THREADED + dec ?c_allocationInhibitGC@Class_System_GC@@2_NA +else ; SINGLE_THREADED + lock dec ?c_allocationInhibitGC@Class_System_GC@@2_NA +endif ; SINGLE_THREADED + mov rcx,rsi + mov rdx,rbx + pop rsi + pop rbx + jmp __throwDispatcherExplicitAddr +__throwOverflowException endp + + +ifdef SINGULARITY_KERNEL +; +; void Thread.setStopContext(Thread t, Exception exn) +; + +align 16 + +?g_setStopContext@Class_System_Threading_Thread@@SAXPEAU1@PEAUClass_System_Exception@@@Z proc frame + PrologPush rbp ; create ebp chain entry + SetFramePointer rbp ; set new ebp + .endprolog + ; rcx = rcx.context + add rcx, Class_System_Threading_Thread._context + ; context.eip = __throwBeyondMarker + ; context.ecx = processStopException + ; context.eax = context.stackMarkers + mov rax, __throwBeyondMarker + mov [rcx].Struct_Microsoft_Singularity_ThreadContext._threadRecord._spill._ip, rax + mov [rcx].Struct_Microsoft_Singularity_ThreadContext._threadRecord._spill._cx, rdx + mov rax, [rcx].Struct_Microsoft_Singularity_ThreadContext._stackMarkers + mov [rcx].Struct_Microsoft_Singularity_ThreadContext._threadRecord._spill._ax, rax + ; Epilogue + mov esp, ebp + pop rbp; + ret +?g_setStopContext@Class_System_Threading_Thread@@SAXPEAU1@PEAUClass_System_Exception@@@Z endp + +endif ; SINGULARITY_KERNEL + + +ifdef SINGULARITY +?g_ZeroPages@Class_System_Buffer@@SAXPEAEH@Z proc frame + ;; ECX = dst + ;; EDX = len (bytes) + PrologPush rbp + SetFramePointer rbp + .endprolog + pxor mm0, mm0 +next: + movntq [rcx + 0], mm0 + movntq [rcx + 8], mm0 + movntq [rcx + 16], mm0 + movntq [rcx + 24], mm0 + movntq [rcx + 32], mm0 + movntq [rcx + 40], mm0 + movntq [rcx + 48], mm0 + movntq [rcx + 56], mm0 + add rcx, 64 + sub rdx, 64 + ja next + + sfence + emms + pop rbp + ret +?g_ZeroPages@Class_System_Buffer@@SAXPEAEH@Z endp + +?g_CopyPages@Class_System_Buffer@@SAXPEAE0H@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + + ;; RCX = dst + ;; RX = src + ;; r8 = len (bytes) + + mov rax, r8 + + cmp rcx, rdx + js down + + ;; destination is lower than source + add rcx, rax + add rdx, rax + sub rcx, 64 + sub rdx, 64 + +up: + movq mm0, [rdx + 0] + movq mm1, [rdx + 8] + movq mm2, [rdx + 16] + movq mm3, [rdx + 24] + movq mm4, [rdx + 32] + movq mm5, [rdx + 40] + movq mm6, [rdx + 48] + movq mm7, [rdx + 56] + movntq [rcx + 0], mm0 + movntq [rcx + 8], mm1 + movntq [rcx + 16], mm2 + movntq [rcx + 24], mm3 + movntq [rcx + 32], mm4 + movntq [rcx + 40], mm5 + movntq [rcx + 48], mm6 + movntq [rcx + 56], mm7 + sub rcx, 64 + sub rdx, 64 + sub rax, 64 + ja up + + sfence + emms + pop rbp + ret + + ;; destination is higher than source +down: + movq mm0, [rdx + 0] + movq mm1, [rdx + 8] + movq mm2, [rdx + 16] + movq mm3, [rdx + 24] + movq mm4, [rdx + 32] + movq mm5, [rdx + 40] + movq mm6, [rdx + 48] + movq mm7, [rdx + 56] + movntq [rcx + 0], mm0 + movntq [rcx + 8], mm1 + movntq [rcx + 16], mm2 + movntq [rcx + 24], mm3 + movntq [rcx + 32], mm4 + movntq [rcx + 40], mm5 + movntq [rcx + 48], mm6 + movntq [rcx + 56], mm7 + add rcx, 64 + add rdx, 64 + sub rax, 64 + ja down + + sfence + emms + pop rbp + ret +?g_CopyPages@Class_System_Buffer@@SAXPEAE0H@Z endp +endif + +; +; int System.VTable.doubleToInt(double) +; + +align 16 +?g_doubleToInt@Class_System_VTable@@SAHN@Z proc frame + PrologPush rbp + SetFramePointer rbp + SubRsp 8 + .endprolog + movsd real8 ptr [rbp+16], xmm0 + fld real8 ptr [rbp+16] + wait + fnstcw word ptr [rbp-2] + wait + mov ax,word ptr [rbp-2] + or eax,0C00h + mov word ptr [rbp-4],ax + fldcw word ptr [rbp-4] + fistp dword ptr [rbp-8] + fldcw word ptr [rbp-2] + mov eax,dword ptr [rbp-8] + + cmp eax,080000000h + je possible_overflow +return: + mov rsp,rbp + pop rbp + ret + +possible_overflow: + fld real8 ptr [rbp+16] + fcomp real8 ptr $DBLMAXINT + fnstsw ax + test ah,4 + jne short return_zero + test ah,1 + jne short return_MININT + +return_MAXINT: + mov eax, 07fffffffh + jmp short return + +return_zero: + xor eax, eax + jmp short return + +return_MININT: + mov eax, 080000000h + jmp short return + +?g_doubleToInt@Class_System_VTable@@SAHN@Z endp + +; +; long System.VTable.doubleToLong(double) +; + +align 16 +?g_doubleToLong@Class_System_VTable@@SA_JN@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + add rsp,-12 + movsd real8 ptr [rbp+16], xmm0 + fld real8 ptr [rbp+16] + wait + fnstcw word ptr [rbp-2] + wait + mov ax,word ptr [rbp-2] + or eax,0C00h + mov word ptr [rbp-4],ax + fldcw word ptr [rbp-4] + fistp qword ptr [rbp-12] + fldcw word ptr [rbp-2] + mov rax,qword ptr [rbp-12] + mov rdx,08000000000000000h + cmp rax,rdx + je possible_overflow +return: + mov rsp,rbp + pop rbp + ret + +possible_overflow: + mov rdx,rax ; save lsw + fld real8 ptr [rbp+16] + fild qword ptr $MAXLONG + fcompp + fnstsw ax + test ah,4 + jne short return_zero + test ah,65 + je short check_MINLONG + +return_MAXLONG: + mov rax, 07fffffffffffffffh + jmp short return + +return_zero: + xor rax, rax + jmp short return + +check_MINLONG: + fld real8 ptr [rbp+16] + fild qword ptr $MINLONG + fcompp + fnstsw ax + test ah,1 + jne short return_original + +return_MINLONG: + mov rax, 08000000000000000h + jmp short return + +return_original: + mov rax, rdx ; restore lsw to eax + mov rdx, 08000000000000000h + and rax, rdx + jmp short return + +?g_doubleToLong@Class_System_VTable@@SA_JN@Z endp + +; +; int System.VTable.floatToInt(float) +; + +align 16 +?g_floatToInt@Class_System_VTable@@SAHM@Z proc frame + PrologPush rbp + SetFramePointer rbp + SubRsp 8 + .endprolog + movss real4 ptr [rbp+16], xmm0 + fld real4 ptr [rbp+16] + wait + fnstcw word ptr [rbp-2] + wait + xor eax,eax + mov ax,word ptr [rbp-2] + or eax,0C00h + mov word ptr [rbp-4],ax + fldcw word ptr [rbp-4] + fistp dword ptr [rbp-8] + fldcw word ptr [rbp-2] + mov eax,dword ptr [rbp-8] + + cmp eax,080000000h + je possible_overflow +return: + mov rsp,rbp + pop rbp + ret + +possible_overflow: + fld real4 ptr [rbp+16] + fcomp real8 ptr $DBLMAXINT + fnstsw ax + test ah,4 + jne short return_zero + test ah,1 + jne short return_MININT + mov eax, 07fffffffh + jmp short return + +return_zero: + xor eax, eax + jmp short return + +return_MININT: + mov eax, 080000000h + jmp short return + +?g_floatToInt@Class_System_VTable@@SAHM@Z endp + +; +; long System.VTable.floatToLong(float) +; + +align 16 +?g_floatToLong@Class_System_VTable@@SA_JM@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + add rsp,-12 + movss real4 ptr [rbp+16], xmm0 + fld real4 ptr [rbp+16] + wait + fnstcw word ptr [rbp-2] + wait + mov ax,word ptr [rbp-2] + or eax,0C00h + mov word ptr [rbp-4],ax + fldcw word ptr [rbp-4] + fistp qword ptr [rbp-12] + fldcw word ptr [rbp-2] + mov rax,qword ptr [rbp-12] + mov rdx,08000000000000000h + cmp rax,rdx + je possible_overflow +return: + mov rsp,rbp + pop rbp + ret + +possible_overflow: + mov rdx,rax ; save lsw + fld real4 ptr [rbp+16] + fild qword ptr $MAXLONG + fcompp + fnstsw ax + test ah,4 + jne short return_zero + test ah,65 + je short check_MINLONG + +return_MAXLONG: + mov rax, 07fffffffffffffffh + jmp short return + +return_zero: + xor rax, rax + jmp short return + +check_MINLONG: + fld real4 ptr [rbp+16] + fild qword ptr $MINLONG + fcompp + fnstsw ax + test ah,1 + jne short return_original + +return_MINLONG: + mov rax, 08000000000000000h + jmp short return + +return_original: + mov rax, rdx ; restore lsw to eax + mov rdx, 08000000000000000h + and rax, rdx + jmp short return + +?g_floatToLong@Class_System_VTable@@SA_JM@Z endp + +; +; int System.VTable.checkedDoubleToInt(double) +; + +align 16 +?g_checkedDoubleToInt@Class_System_VTable@@SAHN@Z proc frame + PrologPush rbp + SetFramePointer rbp + SubRsp 8 + .endprolog + movsd real8 ptr [rbp+16], xmm0 + fld real8 ptr [rbp+16] + wait + fnstcw word ptr [rbp-2] + wait + mov ax,word ptr [rbp-2] + or eax,0C00h + mov word ptr [rbp-4],ax + fldcw word ptr [rbp-4] + fistp dword ptr [rbp-8] + fldcw word ptr [rbp-2] + mov eax,dword ptr [rbp-8] + + cmp eax,080000000h + je possible_overflow +return: + mov rsp,rbp + pop rbp + ret + +possible_overflow: + fld real8 ptr [rbp+16] + fcomp real8 ptr $DBLMAXINT + fnstsw ax + test ah,4 ; test for unordered + jne short throw_exception + test ah,1 ; test for <$DBLMAXINT + jne short return_MININT + ; src > $DBLMAXINT + ; throw an overflow exception + jmp short throw_exception + +return_MININT: + ; check against $DBLMININT + fld real8 ptr [rbp+16] + fcomp real8 ptr $DBLMININT + fnstsw ax + test ah, 1 ; test for < $DBLMININT + jne short throw_exception ; throw exception if true + mov eax, 080000000h + jmp short return + +throw_exception: + ; throw an overflow exception + ; set up stack frame so that it looks like a call to throwNewOverflowException + ; from the caller of this function. + mov rsp,rbp + pop rbp +; pop rax ; grab return address +; add rsp, 8 ; move rsp pass the first parameter. +; mov [rsp],rax ; overwrite argument + jmp ?g_throwNewOverflowException@Class_System_VTable@@SAXXZ + +?g_checkedDoubleToInt@Class_System_VTable@@SAHN@Z endp + +; +; long System.VTable.checkedDoubleToLong(double) +; + +align 16 +?g_checkedDoubleToLong@Class_System_VTable@@SA_JN@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + add rsp,-12 + movsd real8 ptr [rbp+16], xmm0 + fld real8 ptr [rbp+16] + wait + fnstcw word ptr [rbp-2] + wait + mov ax,word ptr [rbp-2] + or eax,0C00h + mov word ptr [rbp-4],ax + fldcw word ptr [rbp-4] + fistp qword ptr [rbp-12] + fldcw word ptr [rbp-2] + mov rax,qword ptr [rbp-12] + mov rdx,08000000000000000h + cmp rax, rdx + je possible_overflow +return: + mov rsp,rbp + pop rbp + ret + +possible_overflow: + mov rdx,rax ; save lsw + fld real8 ptr [rbp+16] + fild qword ptr $MAXLONG + fcompp + fnstsw ax + test ah,4 ; test for unordered + jne short return_zero + test ah,65 ; test for <= $MAXLONG + je short check_MINLONG + +return_MAXLONG: + ; src > $MAXLONG + ; throw an exception + jmp short throw_exception + +return_zero: + jmp short throw_exception + +check_MINLONG: + ; src <= $MINLONG + fild qword ptr $MINLONG + fld real8 ptr [rbp+16] + fcompp ; real8 ptr [rbp+8] < $MINLONG + fnstsw ax + test ah,1 + jne short throw_exception + +return_MINLONG: + mov rax, rdx ; restore lsw to eax + mov rdx, 08000000000000000h + and rax, rdx + jmp short return + + +throw_exception: + ; throw an overflow exception + ; set up stack frame so that it looks like a call to throwNewOverflowException + ; from the caller of this function. + mov rsp,rbp + pop rbp + ;pop rax ; grab return address + ;add rsp, 8 ; move rsp pass the first parameter. + ;mov [rsp],eax ; overwrite argument + jmp ?g_throwNewOverflowException@Class_System_VTable@@SAXXZ + +?g_checkedDoubleToLong@Class_System_VTable@@SA_JN@Z endp + +; +; int System.VTable.checkedFloatToInt(float) +; + +align 16 +?g_checkedFloatToInt@Class_System_VTable@@SAHM@Z proc frame + PrologPush rbp + SetFramePointer rbp + SubRsp 8 + .endprolog + movss real4 ptr [rbp+16], xmm0 + fld real4 ptr [rbp+16] + wait + fnstcw word ptr [rbp-2] + wait + xor eax,eax + mov ax,word ptr [rbp-2] + or eax,0C00h + mov word ptr [rbp-4],ax + fldcw word ptr [rbp-4] + fistp dword ptr [rbp-8] + fldcw word ptr [rbp-2] + mov eax,dword ptr [rbp-8] + + cmp eax,080000000h + je possible_overflow + +return: + mov rsp,rbp + pop rbp + ret + +possible_overflow: + fld real4 ptr [rbp+16] + fcomp real8 ptr $DBLMAXINT + fnstsw ax + test ah,4 ; test for unordered + jne short throw_exception + test ah,1 ; test for src < $DBLMAXINT + jne short return_MININT + ; src > $DBLMAXINT + ; throw an overflow exception + jmp short throw_exception + +return_MININT: + ; need to check against $DBLMININT, if it is less than, + ; then throw an overflow exception + fld real4 ptr [rbp+16] + fcomp real8 ptr $DBLMININT + fnstsw ax + test ah,1 ; test for less than + jne short throw_exception + mov eax, 080000000h + jmp short return + +throw_exception: + ; throw an overflow exception + ; set up stack frame so that it looks like a call to throwNewOverflowException + ; from the caller of this function. + mov rsp,rbp + pop rbp + ;pop rax ; grab return address + ;mov [rsp],eax ; overwrite argument + jmp ?g_throwNewOverflowException@Class_System_VTable@@SAXXZ + +?g_checkedFloatToInt@Class_System_VTable@@SAHM@Z endp + +; +; long System.VTable.checkedFloatToLong(float) +; + +align 16 +?g_checkedFloatToLong@Class_System_VTable@@SA_JM@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + add rsp,-12 + movss real4 ptr [rbp+16], xmm0 + fld real4 ptr [rbp+16] + wait + fnstcw word ptr [rbp-2] + wait + mov ax,word ptr [rbp-2] + or eax,0C00h + mov word ptr [rbp-4],ax + fldcw word ptr [rbp-4] + fistp qword ptr [rbp-12] + fldcw word ptr [rbp-2] + mov rax,qword ptr [rbp-12] + mov rdx,08000000000000000h + cmp rax,rdx + je possible_overflow +return: + mov rsp,rbp + pop rbp + ret + +possible_overflow: + mov rdx,rax ; save lsw + fld real4 ptr [rbp+16] + fild qword ptr $MAXLONG + fcompp + fnstsw ax + test ah,4 ; test for unordered + jne short return_zero + test ah,65 ; test for <= $MAXLONG + je short check_MINLONG + +return_MAXLONG: + ; src > $MAXLONG + ; throw an exception + jmp short throw_exception + +return_zero: + ; compare with $MAXLONG results in unordered + ; throw an overflow exception + jmp short throw_exception + +check_MINLONG: + ; src <= $MINLONG + fild qword ptr $MINLONG + fld real4 ptr [rbp+16] + fcompp ; real8 ptr [rbp+8] < $MINLONG + fnstsw ax + test ah,1 + jne short throw_exception ; throw an overflow exception when src < $MINLONG +return_MINLONG: + mov rax, rdx ; restore lsw + mov rdx, 08000000000000000h + and rax, rdx + jmp short return + +throw_exception: + ; throw an overflow exception + ; set up stack frame so that it looks like a call to throwNewOverflowException + ; from the caller of this function. + mov rsp,rbp + pop rbp + ;pop rax ; grab return address + ;mov [rsp],eax ; overwrite argument + jmp ?g_throwNewOverflowException@Class_System_VTable@@SAXXZ + +?g_checkedFloatToLong@Class_System_VTable@@SA_JM@Z endp + +; +; double System.Math.Sin(double) +; + +align 16 +?g_Sin@Class_System_Math@@SANN@Z proc ;frame + ;.endprolog + add rsp, -8 + movsd real8 ptr [rsp+16], xmm0 + fld real8 ptr [rsp+16] + fsin + fstp real8 ptr [rsp] + movsd xmm0, real8 ptr [rsp] + add rsp, 8 + ret +?g_Sin@Class_System_Math@@SANN@Z endp + +; +; double System.Math.Cos(double) +; + +align 16 +?g_Cos@Class_System_Math@@SANN@Z proc ;frame + ;.endprolog + add rsp, -8 + movsd real8 ptr [rsp+16], xmm0 + fld real8 ptr [rsp+16] + fcos + fstp real8 ptr [rsp] + movsd xmm0, real8 ptr [rsp] + add rsp, 8 + ret +?g_Cos@Class_System_Math@@SANN@Z endp + +; +; double System.Math.Tan(double) +; + +align 16 +?g_Tan@Class_System_Math@@SANN@Z proc ;frame + ;.endprolog + add rsp, -8 + movsd real8 ptr [rsp+16], xmm0 + fld real8 ptr [rsp+16] + fptan + fstp real8 ptr [rsp] + movsd xmm0, real8 ptr [rsp] + add rsp, 8 + ret +?g_Tan@Class_System_Math@@SANN@Z endp + +; +; +; double System.Math.Atan(double) +; + +align 16 +?g_Atan@Class_System_Math@@SANN@Z proc ;frame + ;.endprolog + add rsp, -8 + movsd real8 ptr [rsp+16], xmm0 + fld real8 ptr [rsp+16] + fld1 + fpatan + fstp real8 ptr [rsp] + movsd xmm0, real8 ptr [rsp] + add rsp, 8 + ret +?g_Atan@Class_System_Math@@SANN@Z endp + +; +; double System.Math.atan2(double,double) +; + +align 16 +?g_atan2@Class_System_Math@@SANNN@Z proc ;frame + ;.endprolog + add rsp, -8 + movsd real8 ptr [rsp+16], xmm0 + movsd real8 ptr [rsp+24], xmm1 + fld real8 ptr [rsp+16] + fld real8 ptr [rsp+24] + fpatan + fstp real8 ptr [rsp] + movsd xmm0, real8 ptr [rsp] + add rsp, 8 + ret +?g_atan2@Class_System_Math@@SANNN@Z endp + +; +; double System.Math.exp(double) +; + +align 16 +?g_exp@Class_System_Math@@SANN@Z proc frame + PrologPush rbp + SetFramePointer rbp + SubRsp 8 + .endprolog + + fldl2e + movsd real8 ptr [rbp+16], xmm0 + fmul real8 ptr [rbp+16] + fld st(0) + frndint + fxch st(1) + fsub st(0), st(1) + f2xm1 + fld1 + faddp st(1), st(0) + fscale +;isNaN?? + fstp st(1) + + fstp real8 ptr [rbp-8] + movsd xmm0, real8 ptr [rbp-8] + mov rsp,rbp + pop rbp + ret +?g_exp@Class_System_Math@@SANN@Z endp + +; +; double System.Math.log(double) +; + +align 16 +?g_Log@Class_System_Math@@SANN@Z proc ;frame + ;.endprolog + add rsp, -8 + fldln2 + movsd real8 ptr [rsp+16], xmm0 + fld real8 ptr [rsp+16] + fyl2x + fstp real8 ptr [rsp] + movsd xmm0, real8 ptr [rsp] + add rsp, 8 + ret +?g_Log@Class_System_Math@@SANN@Z endp + +; +; double System.Math.Ceiling(double) +; + +align 16 +?g_Ceiling@Class_System_Math@@SANN@Z proc frame + PrologPush rbp + SetFramePointer rbp + SubRsp 8 + .endprolog + + movsd real8 ptr [rbp+16], xmm0 + fld real8 ptr [rbp+16] + wait + fnstcw word ptr [rbp-2] + wait + mov ax,word ptr [rbp-2] + and ah,0F3h + or ah,008h + mov word ptr [rbp-4],ax + fldcw word ptr [rbp-4] + frndint + fldcw word ptr [rbp-2] + + fstp real8 ptr [rbp-8] + movsd xmm0, real8 ptr [rbp-8] + mov rsp,rbp + pop rbp + ret +?g_Ceiling@Class_System_Math@@SANN@Z endp + +; +; double System.Math.Floor(double) +; + +align 16 +?g_Floor@Class_System_Math@@SANN@Z proc frame + PrologPush rbp + SetFramePointer rbp + SubRsp 8 + .endprolog + + movsd real8 ptr [rsp+16], xmm0 + fld real8 ptr [rbp+16] + wait + fnstcw word ptr [rbp-2] + wait + mov ax,word ptr [rbp-2] + and ah,0F3h + or ah,004h + mov word ptr [rbp-4],ax + fldcw word ptr [rbp-4] + frndint + fldcw word ptr [rbp-2] + + fstp real8 ptr [rbp-8] + movsd xmm0, real8 ptr [rbp-8] + mov rsp,rbp + pop rbp + ret +?g_Floor@Class_System_Math@@SANN@Z endp + +; +; double System.Math.Round(double) +; + +align 16 +?g_Round@Class_System_Math@@SANN@Z proc ;frame + ;.endprolog + add rsp, -8 + movsd real8 ptr [rsp+16], xmm0 + fld real8 ptr [rsp+16] + frndint + fstp real8 ptr [rsp] + movsd xmm0, real8 ptr [rsp] + add rsp, 8 + ret +?g_Round@Class_System_Math@@SANN@Z endp + +; +; float System.Math.Abs(float) +; + +align 16 +?g_abs@Class_System_Math@@SAMM@Z proc ;frame + ;.endprolog + add rsp, -8 + movss real4 ptr [rsp+16], xmm0 + fld real4 ptr [rsp+16] + fabs + fstp real4 ptr [rsp] + movss xmm0, real4 ptr [rsp] + add rsp, 8 + ret +?g_abs@Class_System_Math@@SAMM@Z endp + +; +; double System.Math.Abs(double) +; + +align 16 +?g_abs@Class_System_Math@@SANN@Z proc ;frame + ;.endprolog + add rsp, -8 + movsd real8 ptr [rsp+16], xmm0 + fld real8 ptr [rsp+16] + fabs + fstp real8 ptr [rsp] + movsd xmm0, real8 ptr [rsp] + add rsp, 8 + ret +?g_abs@Class_System_Math@@SANN@Z endp + +align 16 +?g_floatRem@Class_System_VTable@@SAMMM@Z proc ;frame + ;.endprolog + add rsp, -8 + movss real4 ptr [rsp+16], xmm0 + movss real4 ptr [rsp+24], xmm1 + fld real4 ptr [rsp+24] + fld real4 ptr [rsp+16] +fremloop: + fprem + fstsw ax + fwait + sahf + jp fremloop ; Continue while the FPU status bit C2 is set + ffree st(1) + fstp real4 ptr [rsp] + movss xmm0, real4 ptr [rsp] + add rsp, 8 + ret +?g_floatRem@Class_System_VTable@@SAMMM@Z endp + +align 16 +?g_doubleRem@Class_System_VTable@@SANNN@Z proc ;frame + ;.endprolog + add rsp, -8 + movsd real8 ptr [rsp+16], xmm0 + movsd real8 ptr [rsp+24], xmm1 + fld real8 ptr [rsp+24] + fld real8 ptr [rsp+16] +fremloop: + fprem + fstsw ax + fwait + sahf + jp fremloop ; Continue while the FPU status bit C2 is set + ffree st(1) + fstp real8 ptr [rsp] + movsd xmm0, real8 ptr [rsp] + add rsp, 8 + ret +?g_doubleRem@Class_System_VTable@@SANNN@Z endp + +extern ?brtmain@@3P6AHPEAUClassVector_Class_System_String@@@ZEA:qword + +align 16 +?g_CallMain@Class_Microsoft_Singularity_AppRuntime@@SAHPEAUClassVector_Class_System_String@@@Z proc + jmp qword ptr [?brtmain@@3P6AHPEAUClassVector_Class_System_String@@@ZEA] +?g_CallMain@Class_Microsoft_Singularity_AppRuntime@@SAHPEAUClassVector_Class_System_String@@@Z endp + +end + diff --git a/base/Kernel/Native/ix64/halexn.cpp b/base/Kernel/Native/ix64/halexn.cpp new file mode 100644 index 0000000..76fae09 --- /dev/null +++ b/base/Kernel/Native/ix64/halexn.cpp @@ -0,0 +1,334 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +// * searching exception table +// * make machine faults raise CIL exceptions +// +// C code should always invoke the filter BartokMachineFaultFilter +// before it attempts to handle any of the machine faults handled +// below. BartokMachineFaultFilter causes an appropriate exception +// to be thrown if a machine fault occurred in CIL code. + +# include "hal.h" + +extern BOOL KdDebuggerNotPresent; +#ifndef NOEVENTING +#include "eventing.h" +#endif + +// See bartok\tables\ExceptionTable.cs for a description of ExceptionTableEntry + +////////////////////////////////////////////////////////////////////////////// +// +struct ExceptionTableEntry { + uintptr scopeBaseAddr; + union { + struct { + Class_System_Type *exceptionClass; // low bit is zero + uintptr handlerAddr; + }; + struct { + uintptr frameSetupInfo; // low bit is one + uintptr spillSize; + }; + }; +}; + +struct TableEntry { + ExceptionTableEntry * tableBaseAddr; + ExceptionTableEntry * tableEndAddr; +}; + +extern TableEntry TableBase[1]; +extern TableEntry TableBound[1]; + +// TODO: BUGBUG_X64: +// ExceptionTableLookupReturn breaks down with 64-bit pointers. +// We will need to revisit the code +// that uses the ExceptionTableLookupReturn result (__throwDispatcher*). + +#ifdef ISA_IX64 +struct PtrPair { + uintptr a; + uintptr b; +}; +typedef PtrPair lookupReturnType; +#else +typedef uint64 lookupReturnType; +#endif +union ExceptionTableLookupReturn { + lookupReturnType etlReturn; + struct { + Class_System_Type *exceptionClass; + uintptr handlerAddr; + }; + struct { + uintptr frameSetupInfo; + uintptr spillSize; + }; +}; + +static uintptr LookupTable(uintptr throwAddr, + ExceptionTableEntry **tableBaseEntry, + ExceptionTableEntry **tableEndEntry) { +#if 0 + printf("LookupTable(throwAddr=%p, tableBase=%p, tableEnd=%p)\n", + throwAddr, tableBaseEntry, tableEndEntry); + printf(" TableBase=%p, TableBound=%p, maxIndex = %d\n", + TableBase, TableBound, TableBound - TableBase); + printf(" callSiteTableCount=%d\n", + Class_System_GCs_CallStack::c_callSiteTableCount); + printf(" codeBaseStartTable=%p\n", + Class_System_GCs_CallStack::c_codeBaseStartTable); + printf(" returnAddressToCallSiteSetNumbers=%p\n", + Class_System_GCs_CallStack::c_returnAddressToCallSiteSetNumbers); + printf(" callSiteSetCount=%p\n", + Class_System_GCs_CallStack::c_callSiteSetCount); +#endif + + // search to find which table to use + int maxIndex = (int) (TableBound - TableBase); + uintptr codeBase = (uintptr) -1; + uintptr relCodeAddr = 0; + for (int i = 0; i < maxIndex; i++) { + TableEntry *entry = &TableBase[i]; +#if 0 + printf(" TableBase[%d] base=%p end=%p codeBaseStartTable[]=%p\n", + i, entry->tableBaseAddr, entry->tableEndAddr, + Class_System_GCs_CallStack::c_codeBaseStartTable[i]); +#endif + codeBase = + ((uintptr*)Class_System_GCs_CallStack::c_codeBaseStartTable)[i]; + + if (throwAddr < codeBase) { + continue; + } + relCodeAddr = throwAddr - codeBase; + *tableBaseEntry = entry->tableBaseAddr; + *tableEndEntry = entry->tableEndAddr; + +#if 0 + printf(" relCodeAddr = %p\n", relCodeAddr); + printf(" tableBase scopeBaseAddr=%p class=%p handler=%p\n", + (*tableBaseEntry)->scopeBaseAddr, + (*tableBaseEntry)->exceptionClass, + (*tableBaseEntry)->handlerAddr); + printf(" tableEnd scopeBaseAddr=%p class=%p handler=%p\n", + (*tableEndEntry)->scopeBaseAddr, + (*tableEndEntry)->exceptionClass, + (*tableEndEntry)->handlerAddr); +#endif + + if ((relCodeAddr >= (*tableBaseEntry)->scopeBaseAddr) + && (relCodeAddr <= (*tableEndEntry)->scopeBaseAddr)) { + return codeBase; + } + } + + return (uintptr) -1; + //exit(-2); + //__asm int 3; +} + +Class_System_VTable * getRealVTable(Class_System_VTable * vt) +{ + return (Class_System_VTable *)((uintptr)vt & (~((uintptr)3))); +} + +////////////////////////////////////////////////////////////////////////////// + +// ExceptionAssert is called when an unexpected condition happens during +// exception processing + +void ExceptionAssert() +{ +#if SPIN + // Useful for native cases when no debugger is avaiable + while (1) + ; +#endif + + __debugbreak(); +} + +// search an exception table + +////////////////////////////////////////////////////////////////////////////// + +// search an exception table +// - Returns the exception in eax. +// - Returns the handler address in edx. +// OR if the shared unwind handler should be used +// - Returns the frameSetupInfo in eax. +// - Returns the spill area size in edx. + +lookupReturnType __fastcall ExceptionTableLookup(Class_System_Exception *exception, + uintptr throwAddr) { + +#if 0 + printf("\n"); + printf("ExceptionTableLookup(exception=%p, vtable=%p, throwAddr=%p)\n", + exception, ((uintptr *)exception)[0], throwAddr); +#endif + + if (exception->_throwAddress == NULL) { + exception->_throwAddress = throwAddr; + } + + // search for table using throwAddr + ExceptionTableEntry *baseEntry = NULL; + ExceptionTableEntry *endEntry = NULL; + uintptr codeBase = LookupTable(throwAddr, &baseEntry, &endEntry); + +#if SINGULARITY_KERNEL +#if 0 + printf(" codeBase=%p baseEntry=%p endEntry=%p\n", + codeBase, baseEntry, endEntry); +#endif +#endif + + if (codeBase == (uintptr) -1) { +#if SINGULARITY_KERNEL +#if 0 + printf("Exception outside of any known code regions!\n"); +#endif + if (!KdDebuggerNotPresent) { + ExceptionAssert(); + } +#if 0 + printf("Terminating by exception!\n"); +#endif + Class_System_VTable::g_TerminateByException(exception); + ExceptionAssert(); +#elif SINGULARITY_PROCESS + Assert("Exception outside of any known code regions!\n"); + Class_System_VTable::g_TerminateByException(exception); +#endif + } + + // bsearch for throwAddr + int minIndex = 0; + int maxIndex = (int) (endEntry-baseEntry); + throwAddr -= codeBase; + + if (throwAddr < baseEntry[minIndex].scopeBaseAddr || + throwAddr > baseEntry[maxIndex].scopeBaseAddr) { + // BUGBUG: callback to C# code that may trigger GC +#if SINGULARITY_KERNEL +#if 0 + printf("Exception outside of known code region for %p\n", codeBase); +#endif + if (!KdDebuggerNotPresent) { + __debugbreak(); + } + Class_System_VTable::g_TerminateByException(exception); +#if 0 + printf("top-level exception handling code failed\n"); +#endif + __debugbreak(); +#elif SINGULARITY_PROCESS + Assert("Exception outside of known code region for.\n"); + Class_System_VTable::g_TerminateByException(exception); + Assert("top-level exception handling code failed\n"); +#endif + } + while (minIndex+1 < maxIndex) { + int midIndex = (minIndex+maxIndex)/2; + uintptr midAddr = baseEntry[midIndex].scopeBaseAddr; + if (throwAddr < midAddr) { + maxIndex = midIndex; + } else { + minIndex = midIndex; + } + } + ExceptionTableEntry *entry = &baseEntry[minIndex]; + + // back up to first entry containing throwAddr (there may be several) + + uintptr baseAddr; + for (baseAddr = entry->scopeBaseAddr; + entry->scopeBaseAddr == baseAddr && entry >= baseEntry; + entry--) { + continue; + } + + // check each of the handlers in turn + + for (entry++; entry->scopeBaseAddr <= throwAddr; entry++) { +#if 0 + printf(" entry=%p[%d] " + "scopeBaseAddr=%p exceptionClass=%p handler=%p\n", + entry, entry - baseEntry, + entry->scopeBaseAddr, entry->exceptionClass, entry->handlerAddr); +#endif + + // 0 now means "no frame pointer omission and no callee save registers + // have been saved to the stack": + // Assert(entry->exceptionClass); + + Assert(((entry->frameSetupInfo & 0x1) != 0) + || (entry->handlerAddr != NULL)); + if (((entry->frameSetupInfo & 0x1) != 0) + || Class_System_VTable::g_IsExceptionHandler(entry->exceptionClass, + exception)) { +#if 0 + printf("Found matching exception entry: %p\n", entry); +#endif + break; + } + } + Assert(entry->scopeBaseAddr == baseAddr); + + ExceptionTableLookupReturn retval; + + if((entry->frameSetupInfo & 0x1) != 0) { + retval.frameSetupInfo = entry->frameSetupInfo; + retval.spillSize = entry->spillSize; +#ifndef NOEVENTING + LogExceptionInfo((UIntPtr)(codeBase + throwAddr), + 0, + getRealVTable(exception->postHeader.vtableObject)->vtableType->name); +#endif + } else { + retval.exceptionClass = entry->exceptionClass; + retval.handlerAddr = entry->handlerAddr + codeBase; +#ifndef NOEVENTING + LogExceptionInfo((UIntPtr)(codeBase + throwAddr), + (UIntPtr)(retval.handlerAddr), + getRealVTable(exception->postHeader.vtableObject)->vtableType->name); +#endif + } + +#if SINGULARITY_KERNEL + if (!exception->_notifiedDebugger && !KdDebuggerNotPresent) { + exception->_notifiedDebugger = true; + + bool iflag = + Class_Microsoft_Singularity_Processor::g_DisableInterrupts(); + Class_System_VTable * vtable = getRealVTable(exception->postHeader.vtableObject); +#if 0 + if ((entry->frameSetupInfo & 0x1) != 0) { + printf("\n---- First chance %ls at %p. Handler is shared ----\n", + &(vtable->vtableType->name->m_firstChar), + codeBase + throwAddr); + } + else { + printf("\n---- First chance %ls at %p. Handler is %p ----\n", + &(vtable->vtableType->name->m_firstChar), + codeBase + throwAddr, + retval.handlerAddr); + } + if (exception->_message != NULL) { + printf("------ Message: %ls\n", + &(exception->_message->m_firstChar)); + } +#endif + KdNotifyException(exception, (uint32) throwAddr); + Class_Microsoft_Singularity_Processor::g_RestoreInterrupts(iflag); + } +#endif + + return(retval.etlReturn); +} + diff --git a/base/Kernel/Native/ix64/halkdx.cpp b/base/Kernel/Native/ix64/halkdx.cpp new file mode 100644 index 0000000..f2c472f --- /dev/null +++ b/base/Kernel/Native/ix64/halkdx.cpp @@ -0,0 +1,360 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: halkdx64.cpp - Processor-specific support routines for halkd.cpp. +// +#include "hal.h" +#include "halkd.h" + +#define KDDBG if (0) kdprintf + +extern "C" void * __cdecl memcpy(void *, const void *, size_t); + +void KdpX64GetSegmentRegisters(OUT CONTEXT * windbg); +UINT64 KdpX64ReadMsr(UINT32 msr); +void KdpX64WriteMsr(UINT32 msr, UINT64 value); +void KdpX64SetControlReport(IN OUT PDBGKD_CONTROL_REPORT report); +void KdpX64SetControlSet(IN CONST DBGKD_CONTROL_SET *report); +void KdpX64ReadSpecialRegisters(IN OUT KSPECIAL_REGISTERS *pksp); +void KdpX64WriteSpecialRegisters(IN CONST KSPECIAL_REGISTERS *pksp); + +//////////////////////////////////////////////// User visible progress beacon. +// +static UINT16 KdpSpinBase = 0x2f00; +void KdpSpin() +{ + if (KdpSpinBase != 0) { + static UINT8 state = 0; + + // Write the spinner to the screen. + *((UINT16 *)0xb809e) = KdpSpinBase + ("+-|*" [state++ & 0x3]); + } +} + +///////////////////////////////////////// Debugger Unique Interrupts Routines. +// + +// Read a machine specific register (MSR on x86 & x64). +bool KdpReadMsr(UINT32 msr, UINT32 *plo, UINT32 *phi) +{ + UINT64 value = KdpX64ReadMsr(msr); + *phi = (UINT32)(value >> 32); + *plo = (UINT32)(value >> 0); + return true; +} + +// Write a machine specific register (MSR on x86 & x64). +bool KdpWriteMsr(UINT32 msr, UINT32 lo, UINT32 hi) +{ + UINT64 value = (((UINT64)hi) << 32) | (((UINT64)lo) << 0); + KdpX64WriteMsr(msr, value); + return true; +} + +////////////////////////////////////////////////////////////////////////////// + +void KdpToKdContext(IN CONST Struct_Microsoft_Singularity_Isal_SpillContext *singularity, + OUT CONTEXT *windbg) +{ + windbg->ContextFlags = (CONTEXT_CONTROL | + CONTEXT_INTEGER | + CONTEXT_SEGMENTS | + CONTEXT_FLOATING_POINT); + + // CONTEXT_FULL; + windbg->Rax = singularity->ax; + windbg->Rbx = singularity->bx; + windbg->Rcx = singularity->cx; + windbg->Rdx = singularity->dx; + windbg->Rsp = singularity->sp; + windbg->Rbp = singularity->bp; + windbg->Rsi = singularity->si; + windbg->Rdi = singularity->di; + windbg->Rip = singularity->ip; + windbg->R8 = singularity->r8; + windbg->R9 = singularity->r9; + windbg->R10 = singularity->r10; + windbg->R11 = singularity->r11; + windbg->R12 = singularity->r12; + windbg->R13 = singularity->r13; + windbg->R14 = singularity->r14; + windbg->R15 = singularity->r15; + windbg->EFlags = (uint32)singularity->fl; + + windbg->FltSave.ControlWord = singularity->mmx.fcw; + windbg->FltSave.StatusWord = singularity->mmx.fsw; + windbg->FltSave.TagWord = singularity->mmx.ftw; + windbg->FltSave.ErrorAddress = singularity->mmx.ip; + windbg->FltSave.DataAddress = singularity->mmx.dp; + + memcpy(&windbg->FltSave.St0, &singularity->mmx.st0, 16); + memcpy(&windbg->FltSave.St1, &singularity->mmx.st1, 16); + memcpy(&windbg->FltSave.St2, &singularity->mmx.st2, 16); + memcpy(&windbg->FltSave.St3, &singularity->mmx.st3, 16); + memcpy(&windbg->FltSave.St4, &singularity->mmx.st4, 16); + memcpy(&windbg->FltSave.St5, &singularity->mmx.st5, 16); + memcpy(&windbg->FltSave.St6, &singularity->mmx.st6, 16); + memcpy(&windbg->FltSave.St7, &singularity->mmx.st7, 16); + + KdpX64GetSegmentRegisters(windbg); + + // + // This section is specified/returned if the + // ContextFlags word contains the flag CONTEXT_CONTROL. + // + + windbg->SegCs = (uint16)singularity->cs; +} + +void KdpFromKdContext(IN CONST CONTEXT *windbg, + OUT Struct_Microsoft_Singularity_Isal_SpillContext *singularity) +{ + singularity->ax = windbg->Rax; + singularity->bx = windbg->Rbx; + singularity->cx = windbg->Rcx; + singularity->dx = windbg->Rdx; + singularity->sp = windbg->Rsp; + singularity->bp = windbg->Rbp; + singularity->si = windbg->Rsi; + singularity->di = windbg->Rdi; + singularity->ip = windbg->Rip; + singularity->r8 = windbg->R8; + singularity->r9 = windbg->R9; + singularity->r10 = windbg->R10; + singularity->r11 = windbg->R11; + singularity->r12 = windbg->R12; + singularity->r13 = windbg->R13; + singularity->r14 = windbg->R14; + singularity->r15 = windbg->R15; + singularity->fl = windbg->EFlags; + + // CONTEXT_FLOATING_POINT + if (windbg->ContextFlags & CONTEXT_FLOATING_POINT) { + singularity->mmx.fcw = windbg->FltSave.ControlWord; + singularity->mmx.fsw = windbg->FltSave.StatusWord; + singularity->mmx.ftw = windbg->FltSave.TagWord; + singularity->mmx.ip = windbg->FltSave.ErrorAddress; + singularity->mmx.dp = windbg->FltSave.DataAddress; + + memcpy(&singularity->mmx.st0, &windbg->FltSave.St0, 16); + memcpy(&singularity->mmx.st1, &windbg->FltSave.St1, 16); + memcpy(&singularity->mmx.st2, &windbg->FltSave.St2, 16); + memcpy(&singularity->mmx.st3, &windbg->FltSave.St3, 16); + memcpy(&singularity->mmx.st4, &windbg->FltSave.St4, 16); + memcpy(&singularity->mmx.st5, &windbg->FltSave.St5, 16); + memcpy(&singularity->mmx.st6, &windbg->FltSave.St6, 16); + memcpy(&singularity->mmx.st7, &windbg->FltSave.St7, 16); + } +} + +void KdpSetControlReport(IN OUT PDBGKD_CONTROL_REPORT report, + IN CONST Struct_Microsoft_Singularity_Isal_SpillContext *x86Context) + // Routine Description: + // Fill in the DBGKD_CONTROL_REPORT record. + // + // Arguments: + // WaitStateChange - Supplies pointer to record to fill in + // x86Context - Supplies a pointer to a context record. +{ + KdpX64SetControlReport(report); + + // report->EFlags = (uint32)x86Context->efl; + report->ReportFlags = X86_REPORT_INCLUDES_SEGS; + +#if !PAGING + // Let the debugger know so that it doesn't have to retrieve the CS descriptor. + report->ReportFlags |= X86_REPORT_STANDARD_CS; +#endif +} + +void KdpSetControlSet(IN CONST DBGKD_CONTROL_SET * control, + IN OUT Struct_Microsoft_Singularity_Isal_SpillContext *x86Context) +{ + if (control->TraceFlag) { + //KDDBG("KD: Warning - trace flag set prev efl=%x\n",x86Context->efl); + x86Context->fl |= Struct_Microsoft_Singularity_Isal_IX_EFlags_TF; + } + else { + //KDDBG("KD: turning off tracing in efl\n"); + x86Context->fl &= ~Struct_Microsoft_Singularity_Isal_IX_EFlags_TF; + } + + KdpX64SetControlSet(control); +} + +////////////////////////////////////////////////////////////////////////////// + +void KdpReadSpecialRegisters(KSPECIAL_REGISTERS *pksp, + IN CONST Struct_Microsoft_Singularity_Isal_SpillContext *x86Context) +{ + KdpX64ReadSpecialRegisters(pksp); +} + +void KdpWriteSpecialRegisters(CONST KSPECIAL_REGISTERS *pksp) +{ + KdpX64WriteSpecialRegisters(pksp); +} + +////////////////////////////////////////////////////////////////////////////// + +KdDebugTrapData * KdpIsDebugTrap(IN CONST Struct_Microsoft_Singularity_Isal_SpillContext *context, + int id) +{ + if (id == Struct_Microsoft_Singularity_Isal_IX_EVectors_FirstChanceException) { + return (KdDebugTrapData *)(context->ax); + } + return NULL; +} + +// Convert a trap into a exception record. +void KdpConvertTrapToException(IN OUT EXCEPTION_RECORD64 *per, + IN OUT Struct_Microsoft_Singularity_Isal_SpillContext *context, + int id) +{ + // Breakpoints: + switch (id) { + + case Struct_Microsoft_Singularity_Isal_IX_EVectors_SingleStep: + KDDBG("SingleStep\n"); + per->ExceptionCode = STATUS_SINGLE_STEP; + per->ExceptionAddress = (UINT64)context->ip; + // context->efl &= ~Struct_Microsoft_Singularity_Isal_IX_EFlags_TF; + break; + + case Struct_Microsoft_Singularity_Isal_IX_EVectors_Breakpoint: + KDDBG("Breakpoint\n"); + context->ip -= 1; + per->ExceptionCode = STATUS_BREAKPOINT; + per->NumberParameters = 1; + per->ExceptionInformation0 = BREAKPOINT_BREAK; + per->ExceptionAddress = (UINT64)context->ip; + break; + + case Struct_Microsoft_Singularity_Isal_IX_EVectors_IllegalInstruction: + KDDBG("Illegal Instruction\n"); + per->ExceptionCode = STATUS_ILLEGAL_INSTRUCTION; + break; + + case Struct_Microsoft_Singularity_Isal_IX_EVectors_PageFault: + KDDBG("KD: 0x0E %d\n", id); + per->ExceptionCode = STATUS_ACCESS_VIOLATION; + per->ExceptionAddress = (UINT64)context->ip; + per->NumberParameters = 1; + per->ExceptionInformation0 = __readcr2(); + break; + + case Struct_Microsoft_Singularity_Isal_IX_EVectors_FirstChanceException: { + KdDebugTrapData *trapData = (KdDebugTrapData *) (context->ax); + switch (trapData->tag) { + case KdDebugTrapData::FIRST_CHANCE_EXCEPTION: + context->ax = trapData->firstChanceException.throwAddr; + KDDBG("KD: First chance C# exception\n"); + // per->ExceptionCode = STATUS_CPP_EH_EXCEPTION; //0xe06d7363; + per->ExceptionCode = STATUS_VCPP_EXCEPTION; //0x8000ff1f; + per->ExceptionAddress = (UINT64)context->ip; + per->NumberParameters = 1; + per->ExceptionInformation0 = BREAKPOINT_BREAK; + break; + default: + KDDBG("KD: Unexpected interrupt %d\n", id); + per->ExceptionCode = 0x80000000 + id; + per->ExceptionAddress = (UINT64)context->ip; + break; + } + break; + } + + case Struct_Microsoft_Singularity_Isal_IX_EVectors_SecondChanceException: + KDDBG("KD: Second chance C# exception\n"); + per->ExceptionCode = STATUS_VCPP_EXCEPTION; + per->ExceptionAddress = (UINT64)context->ip; + break; + + case Struct_Microsoft_Singularity_Isal_IX_EVectors_DebuggerBreakRequest: + KDDBG("KD: Debugger ctrl-break\n"); + per->ExceptionCode = STATUS_BREAKPOINT; + per->ExceptionInformation0 = BREAKPOINT_BREAK; + per->ExceptionAddress = (UINT64)context->ip; + break; + + case Struct_Microsoft_Singularity_Isal_IX_EVectors_Nmi: + KDDBG("KD: NMI exception\n"); + per->ExceptionCode = STATUS_UNHANDLED_EXCEPTION; + per->ExceptionInformation0 = BREAKPOINT_BREAK; + per->ExceptionAddress = SIGN_EXTEND(context->ip); + break; + + default: + KDDBG("KD: Unexpected interrupt %d\n", id); + per->ExceptionCode = 0x80000000 + id; + per->ExceptionAddress = (UINT64)context->ip; + break; + + } + + KDDBG("Trap: Context at %p\n", context); + KDDBG(" CXT=%08x THR=%08x\n", + context, + Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext()->_thread); + KDDBG(" RIP=%08x EFL=%08x ERR=%08x CR2=%08x\n", + context->ip, context->fl, id, __readcr2()); + KDDBG(" RAX=%08x RBX=%08x RCX=%08x RDX=%08x\n", + context->ax, context->bx, context->cx, context->dx); + KDDBG(" RSP=%08x RBP=%08x RSI=%08x RDI=%08x\n", + context->sp, context->bp, context->si, context->di); +} + + // +// Read or Write I/O Space. +// +// Return: +// +// iowrite == 0: value read from port +// +// iowrite !=0: zero +// +int KdpReadWriteIoSpace( + int size, // 1, 2, 4 + int iowrite, // true if write, false if read + unsigned short addr, + unsigned int value + ) +{ + unsigned int retValue = 0; + + if (iowrite != 0) { + // I/O Write's + + if (size == 1) { + unsigned char byteValue = (unsigned char)value; + __outbyte(addr, value); + } + else if (size == 2) { + unsigned short wordValue = (unsigned short)value; + __outword(addr, value); + } + else if (size == 4) { + __outdword(addr, value); + } + } + else { + // I/O Read's + + if (size == 1) { + retValue = __inbyte(addr); + } + else if (size == 2) { + retValue = __inword(addr); + } + else if (size == 4) { + retValue = __indword(addr); + } + } + + return retValue; +} + + diff --git a/base/Kernel/Native/ix64/halmath.asm b/base/Kernel/Native/ix64/halmath.asm new file mode 100644 index 0000000..12a69b9 --- /dev/null +++ b/base/Kernel/Native/ix64/halmath.asm @@ -0,0 +1,296 @@ +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; + +.code + +include hal.inc +IRC_CHOP equ 0c00h ; chop + +;;;__declspec(naked) uint16 __fastcall g_ClearFp() +?g_ClearFp@@YAGXZ proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + + ;;// Save the old SW + fnstsw [esp-4]; + fnclex + mov eax, [esp-4]; + and eax, 0ffffh; + + pop rbp; + ret; // Returns result in EAX +?g_ClearFp@@YAGXZ endp + +;;;__declspec(naked) uint16 __fastcall g_ControlFp(uint16 newctrl, uint16 mask) +?g_ControlFp@@YAGGG@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + + ;;// Save the old CW + fstcw [esp-4]; + mov eax, [esp-4]; + and eax, 0ffffh; + + ;;// Load the new CW + and ecx,edx; + not edx; + and edx,eax; + or edx,ecx; + mov [esp-4], edx; + fldcw [esp-4]; + + pop rbp + ret; +?g_ControlFp@@YAGGG@Z endp + +;;;__declspec(naked) void __fastcall g_RestoreFp(uint16 newctrl) +?g_RestoreFp@@YAXG@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + + ;;// Load the new CW + and ecx, 0ffffh; + mov [esp-4], ecx; + fldcw [esp-4]; + + pop rbp + ret; +?g_RestoreFp@@YAXG@Z endp + +ifdef GARBAGE + +;;;static float64 __fastcall _frnd(float64 v) +?_frnd@@YANN@Z PROC frame + PrologPush rbp + SetFramePointer rbp + .endprolog + + fld QWORD PTR [rsp +8]; + frndint; + pop rbp; + ret +?_frnd@@YANN@Z endp + +;;;static int32 __fastcall _ftoi(float64 v) +?_ftoi@@YAHN@Z PROC frame + PrologPush rbp + SetFramePointer rbp + .endprolog +ifdef NYI + sub rsp, 24; + ;;int32 intval; +0 + ;;int32 oldcw; +8 + ;;int32 newcw; +16 + + fstcw DWORD PTR [rsp+8] ; // get control word + + mov eax, r9d; // round mode saved + or eax,; // set chop rounding mode + mov r10d, eax; // back to memory + + fldcw r10d; // reset rounding + fld rcx; + fistp r8d; // store chopped integer + fwait; + fldcw r9d; // restore rounding + + add rsp, 24 +endif + or rax,rax + mov eax,r9d + pop rbp; + ret +?_ftoi@@YAHN@Z endp + +;;;float64 Class_System_Math::g_Log(float64 v) +?g_Log@Class_System_Math@@SANN@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + fld QWORD PTR [rsp +8]; + fldln2; + fxch ST(1); + fyl2x; // Returns result in ST(0) + pop rbp; + ret +?g_Log@Class_System_Math@@SANN@Z endp + +;;;float64 Class_System_Math::g_Sin(float64 v) +?g_Sin@Class_System_Math@@SANN@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + fld QWORD PTR [rsp +8]; + fsin; // Returns result in ST(0) + pop rbp; + ret +?g_Sin@Class_System_Math@@SANN@Z endp + +;;;float64 Class_System_Math::g_Cos(float64 v) +?g_Cos@Class_System_Math@@SANN@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + fld QWORD PTR [rsp +8]; + fcos; // Returns result in ST(0) + pop rbp; + ret +?g_Cos@Class_System_Math@@SANN@Z endp + +;;;float64 Class_System_Math::g_Tan(float64 v) +?g_Tan@Class_System_Math@@SANN@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + fld QWORD PTR [rsp +8]; + fptan; + fstp ST(0); // Returns result in ST(0) + pop rbp; + ret +?g_Tan@Class_System_Math@@SANN@Z endp + +;;;float64 Class_System_Math::g_Atan(float64 v) +?g_Atan@Class_System_Math@@SANN@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + fld QWORD PTR [rsp +8]; + fld1; + fpatan; + pop rbp; + ret +?g_Atan@Class_System_Math@@SANN@Z endp + +endif +;;;float64 Class_System_Math::g_Atan2(float64 v, float64 w) +?g_Atan2@Class_System_Math@@SANNN@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + fld QWORD PTR [rsp +8]; + fld QWORD PTR [rsp +16]; + fpatan; + pop rbp; + ret +?g_Atan2@Class_System_Math@@SANNN@Z endp + +;;;float64 Class_System_Math::g_Abs(float64 v) +?g_Abs@Class_System_Math@@SANN@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + fld QWORD PTR [rsp +8]; + fabs; + pop rbp + ret +?g_Abs@Class_System_Math@@SANN@Z endp + +;;;float64 Class_System_Math::g_Sqrt(float64 v) +?g_Sqrt@Class_System_Math@@SANN@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + fld QWORD PTR [rsp +8]; + fsqrt; + pop rbp; + ret +?g_Sqrt@Class_System_Math@@SANN@Z endp + + +;;;float64 Class_System_Math::g_Log10(float64 v) +?g_Log10@Class_System_Math@@SANN@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + fld QWORD PTR [rsp +8]; + fldlg2; + fxch ST(1); + fyl2x; // Returns result in ST(0) + pop rbp; + ret +?g_Log10@Class_System_Math@@SANN@Z endp + +;;;float64 Class_System_Math::g_Exp(float64 v) +?g_Exp@Class_System_Math@@SANN@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + fldl2e; + fmul QWORD PTR [rsp +8]; + fld ST(0); + frndint; + fxch ST(1); + fsub ST(0), ST(1); + f2xm1; + fld1; + faddp ST(1), ST(0); + fscale; + fstp ST(1); // Returns result in ST(0) + pop rbp; + ret +?g_Exp@Class_System_Math@@SANN@Z endp + +;;;float64 Class_System_Math::g_Acos(float64 v) +?g_Acos@Class_System_Math@@SANN@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + fld real8 PTR [rsp+8] ; + fld1; // load 1.0 + fadd st, st(1); // 1+x + fld1; // load 1.0 + fsub st, st(2); // 1-x + fmul; // (1+x)(1-x) + fsqrt; // sqrt((1+x)(1-x)) + fxch; + fpatan; // fpatan(x,sqrt((1+x)(1-x))) + pop rbp; + ret +?g_Acos@Class_System_Math@@SANN@Z endp + +;;;float64 Class_System_Math::g_Asin(float64 v) +?g_Asin@Class_System_Math@@SANN@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + fld real8 PTR [rsp+8] ; + fld1; // load 1.0 + fadd st, st(1); // 1+x + fld1; // load 1.0 + fsub st, st(2); // 1-x + fmul; // (1+x)(1-x) + fsqrt; // sqrt((1+x)(1-x)) + fpatan; // fpatan(x,sqrt((1+x)(1-x))) + pop rbp; + ret +?g_Asin@Class_System_Math@@SANN@Z endp + +;;;static float64 _fastpow(float64 v, float64 w) +?g_Pow@Class_System_Math@@SANNN@Z proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + ;;fld w; // neither v or w can be a boundary cases. + ;;fld v; + fld real8 PTR [rsp+16] ; + fld real8 PTR [rsp+8] ; + fyl2x; // compute y*log2(x) + fld st(0); // duplicate stack top + frndint; // N = round(y) + fsubr st(1), st; + fxch; + fchs; // g = y - N where abs(g) < 1 + f2xm1; // 2**g - 1 + fld1; + fadd; // 2**g + fscale; // (2**g) * (2**N) - gives 2**y + fstp st(1); // pop extra stuff from fp stack + pop rbp; + ret +?g_Pow@Class_System_Math@@SANNN@Z endp + +end \ No newline at end of file diff --git a/base/Kernel/Native/ix64/halprocessor.asm b/base/Kernel/Native/ix64/halprocessor.asm new file mode 100644 index 0000000..bacd75c --- /dev/null +++ b/base/Kernel/Native/ix64/halprocessor.asm @@ -0,0 +1,132 @@ +;; ---------------------------------------------------------------------------- +;; +;; Copyright (c) Microsoft Corporation. All rights reserved. +;; +;; ---------------------------------------------------------------------------- + +;******************************************************************************* +;******************************************************************************* + +;public g_GetCurrentProcessor@Class_Microsoft_Singularity_Processor@@SAPEAU1@XZ +include hal.inc + +.code + +?g_InitFpu@Class_Microsoft_Singularity_Processor@@SAXXZ proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + fninit; + mov rax, 37eh; + push rax; + fldcw [esp]; + pop rax; + ;; Epilogue + mov rsp, rbp + pop rbp; + ret; +?g_InitFpu@Class_Microsoft_Singularity_Processor@@SAXXZ endp + +?g_ClearFpuStatus@Class_Microsoft_Singularity_Processor@@SAXXZ proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + fnclex; + ;; Epilogue + mov rsp, rbp + pop rbp; + ret; + +?g_ClearFpuStatus@Class_Microsoft_Singularity_Processor@@SAXXZ endp + +?g_EnterRing3@Class_Microsoft_Singularity_Processor@@SAXXZ proc +;;// int uc3 = SEGMENT_SELECTOR(GdtUC) + 3; +;;// int ud3 = SEGMENT_SELECTOR(GdtUD) + 3; +;;// int uf3 = SEGMENT_SELECTOR(GdtPF) + 3; // for the moment, share UF and PF +;;// TODO: get rid of hexadecimal constants below + + push rdx + mov rcx, rsp + mov rdx, ring3 + db 0fh; + db 35h; ;;//sysexit +ring3: + pop rdx + mov cx, ss + mov ds, cx + mov es, cx + mov ecx, 38h + 3 ;;// SEGMENT_SELECTOR(GdtPF) + 3 + mov fs, cx + ret + ?g_EnterRing3@Class_Microsoft_Singularity_Processor@@SAXXZ endp + +?g_ReadFpuStatus@Class_Microsoft_Singularity_Processor@@SAIXZ proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + xor rax,rax; + push rax; + fnstsw [rsp]; + pop rax; + ;; Epilogue + mov rsp, rbp + pop rbp; + ret; +?g_ReadFpuStatus@Class_Microsoft_Singularity_Processor@@SAIXZ endp + +?g_GetCS proc + mov rax, cs +?g_GetCS endp + +?g_GetCr3@Class_Microsoft_Singularity_Processor@@SAIXZ proc + mov rax, cr3 + ret +?g_GetCr3@Class_Microsoft_Singularity_Processor@@SAIXZ endp + + +?g_GetCycleCount@Class_Microsoft_Singularity_Processor@@SA_KXZ proc frame + PrologPush rbp + SetFramePointer rbp + .endprolog + rdtsc; reads counter into edx:ecx (32-bits each) + shl rdx, 32 + or rax,rdx ;; make it a 64-bit result + ;; Epilogue + mov rsp, rbp + pop rbp; + ret; +?g_GetCycleCount@Class_Microsoft_Singularity_Processor@@SA_KXZ endp + +?g_GetFramePointer@Class_Microsoft_Singularity_Processor@@SAPEAUuintPtr@@XZ proc + mov rax, rbp + ret +?g_GetFramePointer@Class_Microsoft_Singularity_Processor@@SAPEAUuintPtr@@XZ endp + +?g_MpCallEntryPoint@Class_Microsoft_Singularity_Processor@@SAXPEAUuintPtr@@@Z proc + mov rax, rcx; + call rax; + ret +?g_MpCallEntryPoint@Class_Microsoft_Singularity_Processor@@SAXPEAUuintPtr@@@Z endp + +;__declspec(naked) +;UIntPtr Class_Microsoft_Singularity_Processor::g_GetStackPointer() +?g_GetStackPointer@Class_Microsoft_Singularity_Processor@@SAPEAUuintPtr@@XZ proc + mov rax, rsp; + ret; +?g_GetStackPointer@Class_Microsoft_Singularity_Processor@@SAPEAUuintPtr@@XZ endp + + +; +; void Class_Microsoft_Singularity_Processor::g_Out(uint16 port, int value) +; + +?g_Out@Class_Microsoft_Singularity_Processor@@SAXGH@Z proc + + mov eax, edx + mov dx, cx + out dx, eax + ret + +?g_Out@Class_Microsoft_Singularity_Processor@@SAXGH@Z endp + +end diff --git a/base/Kernel/Native/ix64/halx64.inc b/base/Kernel/Native/ix64/halx64.inc new file mode 100644 index 0000000..878a36f --- /dev/null +++ b/base/Kernel/Native/ix64/halx64.inc @@ -0,0 +1,86 @@ +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; +; Dummy include file for assembly files. + +externdef ?c_halPageDescriptor@Class_System_GCs_PageTable@@2PEAUUntracedPtr_uint32@@EA:NEAR +externdef ?c_threadTable@Class_System_Threading_Thread@@2PEAUClassVector_Class_System_Threading_Thread@@EA:NEAR +externdef ?c_initialThread@Class_System_Threading_Thread@@2PEAU1@EA:NEAR +externdef ?c_segmentAlignMask@Class_System_GCs_DistributedPT@@2PEAUuintPtr@@EA:NEAR +externdef ?c_pageInSegmentAlignMask@Class_System_GCs_DistributedPT@@2PEAUuintPtr@@EA:NEAR + +; reg64 and reg32 may be the same register (e.g. rax and eax) +CurrentThreadIndex MACRO reg64, reg32, tmp64 +; int 3 + mov reg64, rsp + +if DISTRIBUTED_PT + if FLAT_PT + mov tmp64, reg64 + ;get segment part + and tmp64, qword ptr [?c_segmentAlignMask@Class_System_GCs_DistributedPT@@2PEAUuintPtr@@EA] + ;get page number within the segment + and reg64, qword ptr [?c_pageInSegmentAlignMask@Class_System_GCs_DistributedPT@@2PEAUuintPtr@@EA] + ; calculate address of the page entry + shr reg64, PAGE_BITS + lea reg64, dword ptr [reg64*4] + add reg64, tmp64 + add reg64, qword ptr ?c_halPageDescriptor@Class_System_GCs_PageTable@@2PEAUUntracedPtr_uint32@@EA + mov reg32, dword ptr [reg64] + else + ; todo: two level table support + int 3 + endif +else + shr reg64, PAGE_BITS + lea reg64, dword ptr [reg64*4] + add reg64, qword ptr ?c_halPageDescriptor@Class_System_GCs_PageTable@@2PEAUUntracedPtr_uint32@@EA + mov reg32, dword ptr [reg64] +endif + + and reg32, 0FFF0h + shr reg32, 4 + ENDM + +; reg64 and reg32 may be the same register (e.g. rax and eax) +ThreadFromIndex MACRO reg64, reg32 +if REFERENCE_COUNTING_GC + int 3 ;12 below is wrong + lea reg64, qword ptr [reg32*8+12] +elseif DEFERRED_REFERENCE_COUNTING_GC + int 3 ;12 below is wrong + lea reg64, qword ptr [reg32*8+12] +else ; REFERENCE_COUNTING_GC + lea reg64, qword ptr [reg32*8+16] +endif + add reg64, qword ptr ?c_threadTable@Class_System_Threading_Thread@@2PEAUClassVector_Class_System_Threading_Thread@@EA + mov reg64, qword ptr [reg64] + ENDM + +; reg64 and reg32 may be the same register (e.g. rax and eax) +BartokCurrentThread MACRO reg64, reg32, tmp64 +; int 3 + CurrentThreadIndex reg64,reg32, tmp64 + ThreadFromIndex reg64,reg32 + ENDM + +; must use this macro for any push in a prolog +; so that call stack will be correct +PrologPush MACRO reg + push reg + .pushreg reg + ENDM + +; must use this macro for any "mov rbp, rsp" +; in a prolog so that call stack will be correct +SetFramePointer MACRO bpreg + mov bpreg, rsp + .setframe bpreg, 0 + ENDM + +; must use this macro for any "sub rsp, x" +; in a prolog so that call stack will be correct +SubRsp MACRO amount + sub rsp, amount + .allocstack amount + ENDM diff --git a/base/Kernel/Native/ix64/kdasm.asm b/base/Kernel/Native/ix64/kdasm.asm new file mode 100644 index 0000000..456ae63 --- /dev/null +++ b/base/Kernel/Native/ix64/kdasm.asm @@ -0,0 +1,171 @@ +;; ---------------------------------------------------------------------------- +;; +;; Copyright (c) Microsoft Corporation. All rights reserved. +;; +;; ---------------------------------------------------------------------------- + +include hal.inc + +;public: static void __cdecl Class_Microsoft_Singularity_DebugStub::g_Break(void)" +?g_Break@Class_Microsoft_Singularity_DebugStub@@SAXXZ proc + int 3 + ret; +?g_Break@Class_Microsoft_Singularity_DebugStub@@SAXXZ endp + +;void __cdecl KdpPause(void)" +?KdpPause@@YAXXZ proc + pause + ret; +?KdpPause@@YAXXZ endp + +; NB: Without these, we share routines with the mainline code and we get +; caught in a loop when the debugger inserts a break after the pushfd when +; someone tries to single step through Processor:g_DisableInterrupts! +; +;bool __cdecl KdpDisableInterruptsInline(void)" +?KdpDisableInterruptsInline@@YA_NXZ proc + pushfq + pop rax + test rax, Struct_Microsoft_Singularity_Isal_IX_EFlags_IF + setnz al + nop; // required so that the linker doesn't combine with g_Disable + cli; + ret; +?KdpDisableInterruptsInline@@YA_NXZ endp + +;void __cdecl KdpRestoreInterruptsInline(bool)" +?KdpRestoreInterruptsInline@@YAX_N@Z proc + nop; + test cl, cl; + je done; + nop; // required so that the linker doesn't combine with g_Restore + sti; + done: + ret; +?KdpRestoreInterruptsInline@@YAX_N@Z endp + +;void __cdecl KdpFlushInstCache(void)" +?KdpFlushInstCache@@YAXXZ proc + wbinvd; // privileged instruction + ret; +?KdpFlushInstCache@@YAXXZ endp + +; "unsigned __int64 __cdecl KdpX64ReadMsr(unsigned long)" +?KdpX64ReadMsr@@YA_KK@Z proc + rdmsr; + ret; +?KdpX64ReadMsr@@YA_KK@Z endp + +; "void __cdecl KdpX64WriteMsr(unsigned long,unsigned __int64)" +?KdpX64WriteMsr@@YAXK_K@Z proc + mov eax, edx; + shr rdx, 32; + wrmsr; + ret; +?KdpX64WriteMsr@@YAXK_K@Z endp +;?KdpX64ReadMsr@@YA_KK@Z endp + +; "void __cdecl KdpX64GetSegmentRegisters(struct Struct_Microsoft_Singularity_Kd_X64Context *)" +?KdpX64GetSegmentRegisters@@YAXPEAUStruct_Microsoft_Singularity_Kd_X64Context@@@Z proc + mov ax,cs + mov [rcx].Struct_Microsoft_Singularity_Kd_X64Context._SegCs, ax + mov ax,gs + mov [rcx].Struct_Microsoft_Singularity_Kd_X64Context._SegGs, ax + mov ax,fs + mov [rcx].Struct_Microsoft_Singularity_Kd_X64Context._SegFs, ax + mov ax,es + mov [rcx].Struct_Microsoft_Singularity_Kd_X64Context._SegEs, ax + mov ax,ds + mov [rcx].Struct_Microsoft_Singularity_Kd_X64Context._SegDs, ax + mov ax,ss + mov [rcx].Struct_Microsoft_Singularity_Kd_X64Context._SegSs, ax + ret; +?KdpX64GetSegmentRegisters@@YAXPEAUStruct_Microsoft_Singularity_Kd_X64Context@@@Z endp + +; "void __cdecl KdpX64SetControlReport(struct Struct_Microsoft_Singularity_Kd_X64KdControlReport *)" +?KdpX64SetControlReport@@YAXPEAUStruct_Microsoft_Singularity_Kd_X64KdControlReport@@@Z proc + mov rax, dr6; + mov [rcx].Struct_Microsoft_Singularity_Kd_X64KdControlReport._Dr6, rax; + mov rax, dr7; + mov [rcx].Struct_Microsoft_Singularity_Kd_X64KdControlReport._Dr7, rax; + mov ax, cs + mov [rcx].Struct_Microsoft_Singularity_Kd_X64KdControlReport._SegCs, ax + mov ax, es + mov [rcx].Struct_Microsoft_Singularity_Kd_X64KdControlReport._SegEs, ax + mov ax, ds + mov [rcx].Struct_Microsoft_Singularity_Kd_X64KdControlReport._SegDs, ax + mov ax, fs + mov [rcx].Struct_Microsoft_Singularity_Kd_X64KdControlReport._SegFs, ax + pushfq + pop rax + mov [rcx].Struct_Microsoft_Singularity_Kd_X64KdControlReport._EFlags, eax + ret; +?KdpX64SetControlReport@@YAXPEAUStruct_Microsoft_Singularity_Kd_X64KdControlReport@@@Z endp + +; "void __cdecl KdpX64SetControlSet(struct Struct_Microsoft_Singularity_Kd_X64KdControlSet *)" +?KdpX64SetControlSet@@YAXPEBUStruct_Microsoft_Singularity_Kd_X64KdControlSet@@@Z proc + mov rax, [rcx].Struct_Microsoft_Singularity_Kd_X64KdControlSet._Dr7; + mov dr7, rax; + ret; +?KdpX64SetControlSet@@YAXPEBUStruct_Microsoft_Singularity_Kd_X64KdControlSet@@@Z endp + +; "void __cdecl KdpX64WriteSpecialRegisters(struct Struct_Microsoft_Singularity_Kd_X64KSpecialRegisters const *)" +?KdpX64ReadSpecialRegisters@@YAXPEAUStruct_Microsoft_Singularity_Kd_X64KSpecialRegisters@@@Z proc + mov rax, dr0; + mov [rcx].Struct_Microsoft_Singularity_Kd_X64KSpecialRegisters._KernelDr0, rax; + mov rax, dr1; + mov [rcx].Struct_Microsoft_Singularity_Kd_X64KSpecialRegisters._KernelDr1, rax; + mov rax, dr2; + mov [rcx].Struct_Microsoft_Singularity_Kd_X64KSpecialRegisters._KernelDr2, rax; + mov rax, dr3; + mov [rcx].Struct_Microsoft_Singularity_Kd_X64KSpecialRegisters._KernelDr3, rax; + mov rax, dr6; + mov [rcx].Struct_Microsoft_Singularity_Kd_X64KSpecialRegisters._KernelDr6, rax; + mov rax, dr7; + mov [rcx].Struct_Microsoft_Singularity_Kd_X64KSpecialRegisters._KernelDr7, rax; + + sidt [rcx].Struct_Microsoft_Singularity_Kd_X64KSpecialRegisters._Idtr._Limit; + sgdt [rcx].Struct_Microsoft_Singularity_Kd_X64KSpecialRegisters._Gdtr._Limit; + + ;; Should we save the segment regs as well? + str ax; + mov [rcx].Struct_Microsoft_Singularity_Kd_X64KSpecialRegisters._Tr, ax; + ret; +?KdpX64ReadSpecialRegisters@@YAXPEAUStruct_Microsoft_Singularity_Kd_X64KSpecialRegisters@@@Z endp + +; "void __cdecl KdpX64WriteSpecialRegisters(struct Struct_Microsoft_Singularity_Kd_X64KSpecialRegisters const *)" +?KdpX64WriteSpecialRegisters@@YAXPEBUStruct_Microsoft_Singularity_Kd_X64KSpecialRegisters@@@Z proc + mov rax, [rcx].Struct_Microsoft_Singularity_Kd_X64KSpecialRegisters._KernelDr0; + mov dr0, rax; + mov rax, [rcx].Struct_Microsoft_Singularity_Kd_X64KSpecialRegisters._KernelDr1; + mov dr1, rax; + mov rax, [rcx].Struct_Microsoft_Singularity_Kd_X64KSpecialRegisters._KernelDr2; + mov dr2, rax; + mov rax, [rcx].Struct_Microsoft_Singularity_Kd_X64KSpecialRegisters._KernelDr3; + mov dr3, rax; + mov rax, [rcx].Struct_Microsoft_Singularity_Kd_X64KSpecialRegisters._KernelDr6; + mov dr6, rax; + mov rax, [rcx].Struct_Microsoft_Singularity_Kd_X64KSpecialRegisters._KernelDr7; + mov dr7, rax; + ret; +?KdpX64WriteSpecialRegisters@@YAXPEBUStruct_Microsoft_Singularity_Kd_X64KSpecialRegisters@@@Z endp + + + +;static void KdWriteInt8(UINT16 port, UINT8 value) +?KdWriteInt8@@YAXGE@Z proc + mov al,dl + mov dx,cx + out dx, al + ret; +?KdWriteInt8@@YAXGE@Z ENDP + +;static UINT8 KdReadInt8(UINT16 port) +?KdReadInt8@@YAEG@Z proc + mov eax, 0 + mov dx, cx + in al, dx + ret; +?KdReadInt8@@YAEG@Z endp + +end diff --git a/base/Kernel/Native/ix64/math.cpp b/base/Kernel/Native/ix64/math.cpp new file mode 100644 index 0000000..29e115f --- /dev/null +++ b/base/Kernel/Native/ix64/math.cpp @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Math.cpp +// +// Note: +// + +#define LITTL_ENDIAN + +#include "hal.h" + #pragma warning(disable: 4725) + +int32 _ftoi(float64 v) +{ + return 1; +} + +float64 _frnd(float64 v) +{ + return v; +} + +// The following methods are not yet implemented. + +float64 g_Sin(float64 v) +{ + return 0.0; +} + +float64 Class_System_Math::g_Sinh(float64 v) +{ + return 0.0; +} + +float64 Class_System_Math::g_Cosh(float64 v) +{ + return 0.0; +} + +float64 Class_System_Math::g_Tanh(float64 v) +{ + return 0.0; +} + +float64 Class_System_Math::g_Mod(float64 x, float64 y) +{ + return 0.0; +} diff --git a/base/Kernel/Native/ix86/Math.cpp b/base/Kernel/Native/ix86/Math.cpp new file mode 100644 index 0000000..3ad21a7 --- /dev/null +++ b/base/Kernel/Native/ix86/Math.cpp @@ -0,0 +1,850 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Math.cpp +// +// Note: +// + +#define LITTL_ENDIAN + +#include "hal.h" + +#pragma warning(disable: 4725) + +////////////////////////////////////////////////////////////////////////////// +// +#define IMCW_EM 0x003f // interrupt Exception Masks +#define IEM_INVALID 0x0001 // invalid +#define IEM_DENORMAL 0x0002 // denormal +#define IEM_ZERODIVIDE 0x0004 // zero divide +#define IEM_OVERFLOW 0x0008 // overflow +#define IEM_UNDERFLOW 0x0010 // underflow +#define IEM_PRECISION 0x0020 // precision + +#define IMCW_RC 0x0c00 // Rounding Control +#define IRC_CHOP 0x0c00 // chop +#define IRC_UP 0x0800 // up +#define IRC_DOWN 0x0400 // down +#define IRC_NEAR 0x0000 // near + +#define ISW_INVALID 0x0001 // invalid +#define ISW_DENORMAL 0x0002 // denormal +#define ISW_ZERODIVIDE 0x0004 // zero divide +#define ISW_OVERFLOW 0x0008 // overflow +#define ISW_UNDERFLOW 0x0010 // underflow +#define ISW_PRECISION 0x0020 // inexact + +#define IMCW_PC 0x0300 // Precision Control +#define IPC_24 0x0000 // 24 bits +#define IPC_53 0x0200 // 53 bits +#define IPC_64 0x0300 // 64 bits + +////////////////////////////////////////////////////////////////////////////// +// +#ifdef BIG_ENDIAN +// big endian +#define D_EXP(x) ((unsigned short *)&(x)) +#define D_HI(x) ((unsigned long *)&(x)) +#define D_LO(x) ((unsigned long *)&(x)+1) +#else +#define D_EXP(x) ((unsigned short *)&(x)+3) +#define D_HI(x) ((unsigned long *)&(x)+1) +#define D_LO(x) ((unsigned long *)&(x)) +#endif + +#define D_BIASM1 0x3fe // off by one to compensate for the implied bit +#define MAXEXP 1024 +#define MINEXP -1021 + +// return the int representation of the exponent +// if x = .f * 2^n, 0.5<=f<1, return n (unbiased) +// e.g. INTEXP(3.0) == 2 +#define INTEXP(x) ((signed short)((*D_EXP(x) & 0x7ff0) >> 4) - D_BIASM1) + + +// check for infinity, NAN +#define D_ISINF(x) ((*D_HI(x) & 0x7fffffff) == 0x7ff00000 && *D_LO(x) == 0) +#define IS_D_SPECIAL(x) ((*D_EXP(x) & 0x7ff0) == 0x7ff0) +#define IS_D_NAN(x) (IS_D_SPECIAL(x) && !D_ISINF(x)) + +#define IS_D_QNAN(x) ((*D_EXP(x) & 0x7ff8) == 0x7ff8) +#define IS_D_SNAN(x) ((*D_EXP(x) & 0x7ff8) == 0x7ff0 && \ + (*D_HI(x) << 13 || *D_LO(x))) + +#define IS_D_DENORM(x) ((*D_EXP(x) & 0x7ff0) == 0 && \ + (*D_HI(x) << 12 || *D_LO(x))) + +#define IS_D_INF(x) (*D_HI(x) == 0x7ff00000 && *D_LO(x) == 0) +#define IS_D_MINF(x) (*D_HI(x) == 0xfff00000 && *D_LO(x) == 0) + +/////////////////////////////////////////////////////// Define special values. +// +typedef union { + uint64 lng; + float64 dbl; +} _dbl; + +static _dbl _d_pos_inf = { 0x7ff0000000000000 }; // positive infinity +static _dbl _d_neg_inf = { 0xfff0000000000000 }; // negative infinity +static _dbl _d_ind = { 0xfff8000000000000 }; // real indefinite +static _dbl _d_neg_zer = { 0x8000000000000000 }; // negative zero + +////////////////////////////////////////////////////////////////////////////// +// + +__declspec(naked) uint16 __fastcall g_ClearFp() +{ + __asm { + push ebp + mov ebp, esp + + // Save the old SW + fnstsw [esp-4]; + fnclex + mov eax, [esp-4]; + and eax, 0xffff; + + pop ebp; + ret; // Returns result in EAX + } +} + +// Doesn't alter any additional registers +__declspec(naked) uint16 __fastcall g_ControlFp(uint16 newctrl, uint16 mask) +{ + (void)newctrl; (void)mask; // accessed directly via ecx, edx respectively. + __asm { + push ebp + mov ebp, esp + + // Save the old CW + fstcw [esp-4]; + mov eax, [esp-4]; + and eax, 0xffff; + + // Load the new CW + and ecx,edx; + not edx; + and edx,eax; + or edx,ecx; + mov [esp-4], edx; + fldcw [esp-4]; + + pop ebp + ret; + } +} + +// Doesn't alter any additional registers +__declspec(naked) void __fastcall g_RestoreFp(uint16 newctrl) +{ + (void)newctrl; // accessed directly via ecx. + __asm { + push ebp + mov ebp, esp + + // Load the new CW + and ecx, 0xffff; + mov [esp-4], ecx; + fldcw [esp-4]; + + pop ebp + ret; + } +} + +////////////////////////////////////////////////////////////////////////////// +// +static float64 __fastcall _frnd(float64 v) +{ + _asm { + fld v; + frndint; + } +} + +static int32 __fastcall _ftoi(float64 v) +{ + int32 intval; + int32 oldcw; + int32 newcw; + _asm { + fstcw [oldcw]; // get control word + + mov eax, [oldcw]; // round mode saved + or eax, IRC_CHOP; // set chop rounding mode + mov [newcw], eax; // back to memory + + fldcw [newcw]; // reset rounding + fld v; + fistp [intval]; // store chopped integer + fwait; + fldcw [oldcw]; // restore rounding + } + return intval; +} + +static float64 _abs(float64 x) +{ + (*(uint64 *)&x) &= 0x7fffffffffffffff; + return x; +} + +static float64 _set_exp(float64 x, int exp) // does not check validity of exp +{ + float64 retval = x; + int biased_exp = exp + D_BIASM1; + *D_EXP(retval) = (unsigned short) (*D_EXP(x) & 0x800f | (biased_exp << 4)); + return retval; +} + +int _get_exp(float64 x) +{ + signed short exp; + exp = (signed short)((*D_EXP(x) & 0x7ff0) >> 4); + exp -= D_BIASM1; //unbias + return (int) exp; +} + + +// Provide the mantissa and the exponent of e^x +// +// Entry: +// x : a (non special) float64 precision number +// +// Exit: +// *newexp: the exponent of e^x +// return value: the mantissa m of e^x scaled by a factor +// (the value of this factor has no significance. +// The mantissa can be obtained with _set_exp(m, 0). +// +// _set_exp(m, *pnewexp) may be used for constructing the final +// result, if it is within the representable range. +// +static inline float64 r_exp_p(float64 z) +{ + static float64 const p0 = 0.249999999999999993e+0; + static float64 const p1 = 0.694360001511792852e-2; + static float64 const p2 = 0.165203300268279130e-4; + + return ( (p2 * (z) + p1) * (z) + p0 ); +} + +static inline float64 r_exp_q(float64 z) +{ + static float64 const q0 = 0.500000000000000000e+0; + static float64 const q1 = 0.555538666969001188e-1; + static float64 const q2 = 0.495862884905441294e-3; + + return ( (q2 * (z) + q1) * (z) + q0 ); +} + +float64 _exphlp(float64 x, int * pnewexp) +{ + static float64 const LN2INV = 1.442695040889634074; // 1/ln(2) + static float64 const C1 = 0.693359375000000000; + static float64 const C2 = -2.1219444005469058277e-4; + float64 xn; + float64 g,z,gpz,qz,rg; + int n; + + xn = _frnd(x * LN2INV); + n = _ftoi(xn); + + // assume guard digit is present + g = (x - xn * C1) - xn * C2; + z = g*g; + gpz = g * r_exp_p(z); + qz = r_exp_q(z); + rg = 0.5 + gpz/(qz-gpz); + + n++; + + *pnewexp = _get_exp(rg) + n; + return rg; +} + +// +// Decompose a number to a normalized mantissa and exponent. +// +static float64 _decomp(float64 x, int *pexp) +{ + int exp; + float64 man; + + if (x == 0.0) { + man = 0.0; + exp = 0; + } + else if (IS_D_DENORM(x)) { + int neg; + + exp = 1 - D_BIASM1; + neg = x < 0.0; + while((*D_EXP(x) & 0x0010) == 0) { + // shift mantissa to the left until bit 52 is 1 + (*D_HI(x)) <<= 1; + if (*D_LO(x) & 0x80000000) + (*D_HI(x)) |= 0x1; + (*D_LO(x)) <<= 1; + exp--; + } + (*D_EXP(x)) &= 0xffef; // clear bit 52 + if (neg) { + (*D_EXP(x)) |= 0x8000; // set sign bit + } + man = _set_exp(x,0); + } + else { + man = _set_exp(x,0); + exp = INTEXP(x); + } + + *pexp = exp; + return man; +} + +static bool is_odd_integer(float64 y) +{ + int exp = INTEXP(y); + if (exp < 1 || exp > 63) { + return false; + } + return(((*(uint64*)&y) | 0x10000000000000u) << (10 + exp) == 0x8000000000000000); +} + +static bool is_even_integer(float64 y) +{ + int exp = INTEXP(y); + if (exp < 1 || exp > 63) { + return false; + } + return (((*(uint64*)&y) | 0x10000000000000u) << (10 + exp) == 0); +} + +////////////////////////////////////////////////////////////////////////////// + +float64 Class_System_Math::g_Atan2(float64 v, float64 w) +{ + __asm { + fld v; + fld w; + fpatan; + } +} + +float64 Class_System_Math::g_Abs(float64 v) +{ + __asm { + fld v; + fabs; + } +} + +float64 Class_System_Math::g_Sqrt(float64 v) +{ + __asm { + fld v; + fsqrt; + } +} + +float64 Class_System_Math::g_Log10(float64 v) +{ + __asm { + fld v; + fldlg2; + fxch ST(1); + fyl2x; // Returns result in ST(0) + } +} + +float64 Class_System_Math::g_Exp(float64 v) +{ + __asm { + fldl2e; + fmul v; + fld ST(0); + frndint; + fxch ST(1); + fsub ST(0), ST(1); + f2xm1; + fld1; + faddp ST(1), ST(0); + fscale; + fstp ST(1); // Returns result in ST(0) + } +} + +// constants for the rational approximation +static inline float64 r_sinh_p(float64 f) +{ + static float64 const p0 = -0.35181283430177117881e+6; + static float64 const p1 = -0.11563521196851768270e+5; + static float64 const p2 = -0.16375798202630751372e+3; + static float64 const p3 = -0.78966127417357099479e+0; + + return (((p3 * (f) + p2) * (f) + p1) * (f) + p0); +} + +static inline float64 r_sinh_q(float64 f) +{ + static float64 const q0 = -0.21108770058106271242e+7; + static float64 const q1 = 0.36162723109421836460e+5; + static float64 const q2 = -0.27773523119650701667e+3; + // q3 = 1 is not used (avoid multiplication by 1) + + return ((((f) + q2) * (f) + q1) * (f) + q0); +} + +// Compute the hyperbolic sine of a number. +// The algorithm (reduction / rational approximation) is +// taken from Cody & Waite. +float64 Class_System_Math::g_Sinh(float64 v) +{ + static float64 const EPS = 5.16987882845642297e-26; // 2^(-53) / 2 + // exp(YBAR) should be close to but less than XMAX + // and 1/exp(YBAR) should not underflow + static float64 const YBAR = 7.00e2; + + // WMAX=ln(OVFX)+0.69 (Cody & Waite),omitted LNV, used OVFX instead of BIGX + + static float64 const WMAX = 1.77514678223345998953e+003; + + float64 result; + + if (IS_D_SPECIAL(v)) { + if (IS_D_INF(v) || IS_D_MINF(v)) { + } + else if (IS_D_QNAN(v)) { + // TODO: should throw a soft exception. + } + else if (IS_D_SNAN(v)) { + // TODO: should throw a hard exception. + } + result = v; + } + else if (v == 0.0) { + // no precision exception + result = v; + } + else { + bool neg = (v < 0.0); + float64 y = _abs(v); + + if (y > 1.0) { + int newexp; + if (y > YBAR) { + if (y > WMAX) { + // result too large, even after scaling + result = v * _d_pos_inf.dbl; + // TODO: should through hard exception. + goto exit; + } + + // + // result = exp(y)/2 + // + + result = _exphlp(y, &newexp); + newexp --; //divide by 2 + if (newexp > MAXEXP) { + result = 0.0; //result = _set_exp(result, newexp-IEEE_ADJUST); + // TODO: should through hard exception. + goto exit; + } + else { + result = _set_exp(result, newexp); + } + + } + else { + float64 z = _exphlp(y, &newexp); + z = _set_exp(z, newexp); + result = (z - 1.0/z) / 2.0; + } + + if (neg) { + result = -result; + } + } + else { + if (y < EPS) { + result = v; + if (IS_D_DENORM(result)) { + result = 0.0; // result = _add_exp(result, IEEE_ADJUST); + // TODO: should through hard exception. + goto exit; + } + } + else { + float64 f = v * v; + float64 r = f * (r_sinh_p(f) / r_sinh_q(f)); + result = v + v * r; + } + } + } + + exit: + return result; +} + +// Compute the hyperbolic cosine of a number. +// The algorithm (reduction / rational approximation) is +// taken from Cody & Waite. +// +float64 Class_System_Math::g_Cosh(float64 v) +{ + // exp(YBAR) should be close to but less than XMAX + // and 1/exp(YBAR) should not underflow + static float64 const YBAR = 7.00e2; + + // WMAX=ln(OVFX)+0.69 (Cody & Waite),omitted LNV, used OVFX instead of BIGX + static float64 const WMAX = 1.77514678223345998953e+003; + + float64 result; + + if (IS_D_SPECIAL(v)) { + if (IS_D_INF(v) || IS_D_MINF(v)) { + result = _d_pos_inf.dbl; + + } + else if (IS_D_QNAN(v)) { + // TODO: should throw a soft exception. + result = v; + } + else { + // TODO: should throw a hard exception. + result = v; + } + } + else if (v == 0.0) { + result = 1.0; + } + else { + float64 y = _abs(v); + if (y > YBAR) { + if (y > WMAX) { + // TODO: should throw a hard exception. + result = v; + goto exit; + } + + // + // result = exp(y)/2 + // + + int newexp; + result = _exphlp(y, &newexp); + newexp --; //divide by 2 + if (newexp > MAXEXP) { + // TODO: should throw a hard exception. + result = 0.0; //result = _set_exp(result, newexp-IEEE_ADJUST); + goto exit; + } + else { + result = _set_exp(result, newexp); + } + } + else { + int newexp; + float64 z = _exphlp(y, &newexp); + z = _set_exp(z, newexp); + result = (z + 1.0/z) / 2.0; + } + // TODO: should throw a hard exception if exactness is required. + } + + exit: + return result; +} + +// Compute the hyperbolic tangent of a number. +// The algorithm (reduction / rational approximation) is +// taken from Cody & Waite. + +static inline float64 r_tanh(float64 g) +{ + // constants for rational approximation + static float64 const p0 = -0.16134119023996228053e+4; + static float64 const p1 = -0.99225929672236083313e+2; + static float64 const p2 = -0.96437492777225469787e+0; + static float64 const q0 = 0.48402357071988688686e+4; + static float64 const q1 = 0.22337720718962312926e+4; + static float64 const q2 = 0.11274474380534949335e+3; + static float64 const q3 = 0.10000000000000000000e+1; + + return ((((p2 * (g) + p1) * (g) + p0) * (g)) / ((((g) + q2) * (g) + q1) * (g) + q0)); +} + +float64 Class_System_Math::g_Tanh(float64 v) +{ + // constants + static float64 const EPS = 5.16987882845642297e-26; // 2^(-53) / 2 + static float64 const XBIG = 1.90615474653984960096e+001; // ln(2)(53+2)/2 + static float64 const C0 = 0.54930614433405484570; // ln(3)/2 + + if (IS_D_SPECIAL(v)) { + if (IS_D_INF(v)) { + v = 1.0; + } + else if (IS_D_MINF(v)) { + v = -1.0; + } + else if (IS_D_QNAN(v)) { + // TODO: should throw a soft exception. + } + else if (IS_D_SNAN(v)) { + // TODO: should throw a hard exception. + } + } + else if (v == 0.0) { + // no precision exception + } + else { + bool neg = false; + if (v < 0.0) { + neg = true; + v = -v; + } + + if (v > XBIG) { + v = 1; + } + else if (v > C0) { + v = 0.5 - 1.0 / (g_Exp(v+v) + 1.0); + v = v + v; + } + else if (v < EPS) { + if (IS_D_DENORM(v)) { + // TODO: should throw a heard exception. + } + } + else { + v = v + v * r_tanh(v * v); + } + if (neg) { + v = -v; + } + } + return v; +} + +float64 Class_System_Math::g_Acos(float64 v) +{ + __asm { + fld v; + fld1; // load 1.0 + fadd st, st(1); // 1+x + fld1; // load 1.0 + fsub st, st(2); // 1-x + fmul; // (1+x)(1-x) + fsqrt; // sqrt((1+x)(1-x)) + fxch; + fpatan; // fpatan(x,sqrt((1+x)(1-x))) + } +} + +float64 Class_System_Math::g_Asin(float64 v) +{ + __asm { + fld v; + fld1; // load 1.0 + fadd st, st(1); // 1+x + fld1; // load 1.0 + fsub st, st(2); // 1-x + fmul; // (1+x)(1-x) + fsqrt; // sqrt((1+x)(1-x)) + fpatan; // fpatan(x,sqrt((1+x)(1-x))) + } +} + +static float64 _fastpow(float64 v, float64 w) +{ + __asm { + fld w; // neither v or w can be a boundary cases. + fld v; + fyl2x; // compute y*log2(x) + fld st(0); // duplicate stack top + frndint; // N = round(y) + fsubr st(1), st; + fxch; + fchs; // g = y - N where abs(g) < 1 + f2xm1; // 2**g - 1 + fld1; + fadd; // 2**g + fscale; // (2**g) * (2**N) - gives 2**y + fstp st(1); // pop extra stuff from fp stack + } +} + +float64 Class_System_Math::g_Pow(float64 v, float64 w) +{ + float64 result = 0.0; + + // check for infinity or NAN + if (IS_D_SPECIAL(w) || IS_D_SPECIAL(v)) { + if (IS_D_INF(w)) { + float64 absv = _abs(v); + if (absv > 1.0) { + result = _d_pos_inf.dbl; + } + else if (absv < 1.0) { + result = 0.0; + } + else { + result = _d_ind.dbl; + // TODO: Should throw a hard exception. + goto exit; + } + } + else if (IS_D_MINF(w)) { + float64 absv = _abs(v); + if (absv > 1.0) { + result = 0.0; + } + else if (absv < 1.0) { + result = _d_pos_inf.dbl; + } + else { + result = _d_ind.dbl; + // TODO: Should throw a hard exception. + goto exit; + } + } + else if (IS_D_INF(v)) { + if (w > 0.0) { + result = _d_pos_inf.dbl; + } + else if (w < 0.0) { + result = 0.0; + } + else { + result = 1.0; + } + } + else if (IS_D_MINF(v)) { + if (w > 0.0) { + result = is_odd_integer(w) ? _d_neg_inf.dbl : _d_pos_inf.dbl; + } + else if (w < 0.0) { + result = is_odd_integer(w) ? _d_neg_zer.dbl : 0.0; + } + else { + result = 1.0; + } + } + } + else if (w == 0.0) { + result = 1.0; + } + else if (v == 0.0) { + if (w < 0.0) { + // TODO: Should throw a hard exception. + result = is_odd_integer(v) ? _d_neg_inf.dbl : _d_pos_inf.dbl; + } + else { + result = is_odd_integer(v) ? w : 0.0; + } + } + else if (v < 0.0) { + if (is_odd_integer(w)) { + result = - _fastpow(-v, w); + } + else if (is_even_integer(w)) { + result = _fastpow(-v, w); + } + else { + // TODO: Should throw a hard exception. + result = v; + } + } + else { + result = _fastpow(v, w); + } + + exit: + return result; +} + +float64 Class_System_Math::g_Mod(float64 x, float64 y) +{ + float64 result = 0.0; + + // check for infinity or NAN + if (IS_D_SPECIAL(y) || IS_D_SPECIAL(x)) { + if (IS_D_SNAN(y) || IS_D_SNAN(x)) { + // TODO: Should throw a hard exception. + } + else if (IS_D_QNAN(y) || IS_D_QNAN(x)) { + // TODO: Should throw a soft exception. + result = x; + } + else if (IS_D_INF(x) || IS_D_MINF(x)) { + // TODO: Should throw a hard exception. + } + } + else if (y == 0.0) { + // TODO: Should throw a hard exception. + } + else if (x == 0.0) { + result = x; + } + else { + const int SCALE = 53; + bool neg = false; + bool denorm = false; + float64 d,ty,fx,fy; + int nx, ny, nexp; + + if (x < 0.0) { + result = -x; + neg = 1; + } + else { + result = x; + } + + ty = _abs(y); + + while (result >= ty) { + fx = _decomp(result, &nx); + fy = _decomp(ty, &ny); + + if (nx < MINEXP) { + // result is a denormalized number + denorm = true; + nx += SCALE; + ny += SCALE; + result = _set_exp(fx, nx); + ty = _set_exp(fy, ny); + } + + + if (fx >= fy) { + nexp = nx ; + } + else { + nexp = nx - 1; + } + d = _set_exp(fy, nexp); + result -= d; + } + if (denorm) { + // TODO: should raise only FP_U exception. + } + if (neg) { + result = -result; + } + } + + return result; +} + +// +///////////////////////////////////////////////////////////////// End of File. + diff --git a/base/Kernel/Native/ix86/Processor.cpp b/base/Kernel/Native/ix86/Processor.cpp new file mode 100644 index 0000000..eedb074 --- /dev/null +++ b/base/Kernel/Native/ix86/Processor.cpp @@ -0,0 +1,72 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Processor.cpp +// +// Note: +// + +#include "hal.h" + +#if SINGULARITY_KERNEL +#include "halkd.h" +#endif // SINGULARITY_KERNEL + +/////////////////////////////////////////////////////////// Segment Selectors. +// +#define SEGMENT_SELECTOR(s) \ + (uint16)(offsetof(Struct_Microsoft_Singularity_Isal_IX_DescriptorTable_Gdt,s) \ + - offsetof(Struct_Microsoft_Singularity_Isal_IX_DescriptorTable_Gdt,nul)) + +/////////////////////////////////////////////////////////// Processor Context. +// +Class_Microsoft_Singularity_Processor * +Struct_Microsoft_Singularity_ProcessorContext:: +m_GetProcessor(Struct_Microsoft_Singularity_ProcessorContext *self) +{ + return (Class_Microsoft_Singularity_Processor *) self->_processor; +} + +void +Struct_Microsoft_Singularity_ProcessorContext:: +m_UpdateAfterGC(Struct_Microsoft_Singularity_ProcessorContext * self, + Class_Microsoft_Singularity_Processor *processor) +{ + self->_processor = processor; +} + + +////////////////////////////////////////////////////////////////////////////// +// + +#if SINGULARITY_KERNEL + +#if PAGING +extern void FakeSyscall(); +#endif + +void ProcessorInitialize(Class_Microsoft_Singularity_Hal_Cpu *pCpu) +{ + // The ProcessorContext can be found at a published offset in FS. The DS-based address of this + // context is available in the hal processor record + + Struct_Microsoft_Singularity_ProcessorContext *proc + = (Struct_Microsoft_Singularity_ProcessorContext *) pCpu->CpuRecordPage; + + proc->halCpu = pCpu; + +#if PAGING + // XXX PBAR Set up MSRs for SYSENTER/SYSEXIT + Class_Microsoft_Singularity_Isal_Isa::g_WriteMsr(0x174, SEGMENT_SELECTOR(pc)); + Class_Microsoft_Singularity_Isal_Isa::g_WriteMsr(0x175, proc->cpuRecord.interruptStackBegin + 0x2000); + Class_Microsoft_Singularity_Isal_Isa::g_WriteMsr(0x176, (UINT64)FakeSyscall); +#endif +} + +#endif // SINGULARITY_KERNEL + +// +///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Native/_lldiv.asm b/base/Kernel/Native/ix86/_lldiv.asm similarity index 94% rename from base/Kernel/Native/_lldiv.asm rename to base/Kernel/Native/ix86/_lldiv.asm index e5a78b8..8814213 100644 --- a/base/Kernel/Native/_lldiv.asm +++ b/base/Kernel/Native/ix86/_lldiv.asm @@ -9,18 +9,6 @@ ; ;******************************************************************************* -.686p -.mmx -.xmm -.model flat,C -.code - -assume ds:flat -assume es:flat -assume ss:flat -assume fs:nothing -assume gs:nothing - include hal.inc ;*** @@ -46,7 +34,7 @@ include hal.inc ; ;******************************************************************************* -_alldiv PROC NEAR +__alldiv PROC NEAR push edi push esi @@ -202,6 +190,6 @@ L8: ret 16 -_alldiv ENDP +__alldiv ENDP end diff --git a/base/Kernel/Native/_llmul.asm b/base/Kernel/Native/ix86/_llmul.asm similarity index 90% rename from base/Kernel/Native/_llmul.asm rename to base/Kernel/Native/ix86/_llmul.asm index 96ae354..5cddc3c 100644 --- a/base/Kernel/Native/_llmul.asm +++ b/base/Kernel/Native/ix86/_llmul.asm @@ -12,18 +12,6 @@ ; ;******************************************************************************* -.686p -.mmx -.xmm -.model flat,C -.code - -assume ds:flat -assume es:flat -assume ss:flat -assume fs:nothing -assume gs:nothing - include hal.inc ;*** @@ -49,7 +37,7 @@ include hal.inc ; ;******************************************************************************* -_allmul PROC NEAR +__allmul PROC NEAR A EQU [esp + 4] ; stack address of a B EQU [esp + 12] ; stack address of b @@ -98,6 +86,6 @@ B2 EQU [esp + 16] ; stack address of b ret 16 ; callee restores the stack -_allmul ENDP +__allmul ENDP end diff --git a/base/Kernel/Native/_llrem.asm b/base/Kernel/Native/ix86/_llrem.asm similarity index 94% rename from base/Kernel/Native/_llrem.asm rename to base/Kernel/Native/ix86/_llrem.asm index af5bfee..4fb84d4 100644 --- a/base/Kernel/Native/_llrem.asm +++ b/base/Kernel/Native/ix86/_llrem.asm @@ -9,18 +9,6 @@ ; ;******************************************************************************* -.686p -.mmx -.xmm -.model flat,C -.code - -assume ds:flat -assume es:flat -assume ss:flat -assume fs:nothing -assume gs:nothing - include hal.inc ;*** @@ -46,7 +34,7 @@ include hal.inc ; ;******************************************************************************* -_allrem PROC NEAR +__allrem PROC NEAR push ebx push edi @@ -209,6 +197,6 @@ L8: ret 16 -_allrem ENDP +__allrem ENDP end diff --git a/base/Kernel/Native/ix86/_llshl.asm b/base/Kernel/Native/ix86/_llshl.asm new file mode 100644 index 0000000..2f85712 --- /dev/null +++ b/base/Kernel/Native/ix86/_llshl.asm @@ -0,0 +1,72 @@ +;*** +;llshl.asm - long shift left +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; +;Purpose: +; define long shift left routine (signed and unsigned are same) +; __allshl +; +;******************************************************************************* + +include hal.inc + +;*** +;llshl - long shift left +; +;Purpose: +; Does a Long Shift Left (signed and unsigned are identical) +; Shifts a long left any number of bits. +; +;Entry: +; EDX:EAX - long value to be shifted +; CL - number of bits to shift by +; +;Exit: +; EDX:EAX - shifted value +; +;Uses: +; CL is destroyed. +; +;Exceptions: +; +;******************************************************************************* + +__allshl PROC NEAR + +; +; Handle shifts of 64 or more bits (all get 0) +; + cmp cl, 64 + jae short RETZERO + +; +; Handle shifts of between 0 and 31 bits +; + cmp cl, 32 + jae short MORE32 + shld edx,eax,cl + shl eax,cl + ret + +; +; Handle shifts of between 32 and 63 bits +; +MORE32: + mov edx,eax + xor eax,eax + and cl,31 + shl edx,cl + ret + +; +; return 0 in edx:eax +; +RETZERO: + xor eax,eax + xor edx,edx + ret + +__allshl ENDP + + end diff --git a/base/Kernel/Native/ix86/_llshr.asm b/base/Kernel/Native/ix86/_llshr.asm new file mode 100644 index 0000000..ab7b21e --- /dev/null +++ b/base/Kernel/Native/ix86/_llshr.asm @@ -0,0 +1,73 @@ +;*** +;llshr.asm - long shift right +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; +;Purpose: +; define signed long shift right routine +; __allshr +; +;******************************************************************************* + +include hal.inc + +;*** +;llshr - long shift right +; +;Purpose: +; Does a signed Long Shift Right +; Shifts a long right any number of bits. +; +;Entry: +; EDX:EAX - long value to be shifted +; CL - number of bits to shift by +; +;Exit: +; EDX:EAX - shifted value +; +;Uses: +; CL is destroyed. +; +;Exceptions: +; +;******************************************************************************* + +__allshr PROC NEAR + +; +; Handle shifts of 64 bits or more (if shifting 64 bits or more, the result +; depends only on the high order bit of edx). +; + cmp cl,64 + jae short RETSIGN + +; +; Handle shifts of between 0 and 31 bits +; + cmp cl, 32 + jae short MORE32 + shrd eax,edx,cl + sar edx,cl + ret + +; +; Handle shifts of between 32 and 63 bits +; +MORE32: + mov eax,edx + sar edx,31 + and cl,31 + sar eax,cl + ret + +; +; Return double precision 0 or -1, depending on the sign of edx +; +RETSIGN: + sar edx,31 + mov eax,edx + ret + +__allshr ENDP + + end diff --git a/base/Kernel/Native/ix86/_memcpy.asm b/base/Kernel/Native/ix86/_memcpy.asm new file mode 100644 index 0000000..4d70a1c --- /dev/null +++ b/base/Kernel/Native/ix86/_memcpy.asm @@ -0,0 +1,578 @@ +;******************************************************************************* +;memcpy.asm - contains memcpy and memmove routines +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; +;Purpose: +; memcpy() copies a source memory buffer to a destination buffer. +; Overlapping buffers are not treated specially, so propogation may occur. +; memmove() copies a source memory buffer to a destination buffer. +; Overlapping buffers are treated specially, to avoid propogation. +; +;******************************************************************************* + +include hal.inc + +;*** +;memcpy - Copy source buffer to destination buffer +; +;Purpose: +; memcpy() copies a source memory buffer to a destination memory buffer. +; This routine does NOT recognize overlapping buffers, and thus can lead +; to propogation. +; For cases where propogation must be avoided, memmove() must be used. +; +; Algorithm: +; +; void * memcpy(void * dst, void * src, size_t count) +; { +; void * ret = dst; +; +; /* +; * copy from lower addresses to higher addresses +; */ +; while (count--) +; *dst++ = *src++; +; +; return(ret); +; } +; +;memmove - Copy source buffer to destination buffer +; +;Purpose: +; memmove() copies a source memory buffer to a destination memory buffer. +; This routine recognize overlapping buffers to avoid propogation. +; For cases where propogation is not a problem, memcpy() can be used. +; +; Algorithm: +; +; void * memmove(void * dst, void * src, size_t count) +; { +; void * ret = dst; +; +; if (dst <= src || dst >= (src + count)) { +; /* +; * Non-Overlapping Buffers +; * copy from lower addresses to higher addresses +; */ +; while (count--) +; *dst++ = *src++; +; } +; else { +; /* +; * Overlapping Buffers +; * copy from higher addresses to lower addresses +; */ +; dst += count - 1; +; src += count - 1; +; +; while (count--) +; *dst-- = *src--; +; } +; +; return(ret); +; } +; +; +;Entry: +; void *dst = pointer to destination buffer +; const void *src = pointer to source buffer +; size_t count = number of bytes to copy +; +;Exit: +; Returns a pointer to the destination buffer in AX/DX:AX +; +;Uses: +; CX, DX +; +;Exceptions: +;******************************************************************************* + +% public memcpy +memcpy proc NEAR C \ + dst:ptr byte, \ + src:ptr byte, \ + count:DWORD + + ; destination pointer + ; source pointer + ; number of bytes to copy + +; push ebp ;U - save old frame pointer +; mov ebp, esp ;V - set new frame pointer + + push edi ;U - save edi + push esi ;V - save esi + + mov esi,[src] ;U - esi = source + mov ecx,[count] ;V - ecx = number of bytes to move + + mov edi,[dst] ;U - edi = dest + +; +; Check for overlapping buffers: +; If (dst <= src) Or (dst >= src + Count) Then +; Do normal (Upwards) Copy +; Else +; Do Downwards Copy to avoid propagation +; + + mov eax,ecx ;V - eax = byte count... + + mov edx,ecx ;U - edx = byte count... + add eax,esi ;V - eax = point past source end + + cmp edi,esi ;U - dst <= src ? + jbe short CopyUp ;V - yes, copy toward higher addresses + + cmp edi,eax ;U - dst < (src + count) ? + jb CopyDown ;V - yes, copy toward lower addresses + +; +; Copy toward higher addresses. +; +; +; The algorithm for forward moves is to align the destination to a dword +; boundary and so we can move dwords with an aligned destination. This +; occurs in 3 steps. +; +; - move x = ((4 - Dest & 3) & 3) bytes +; - move y = ((L-x) >> 2) dwords +; - move (L - x - y*4) bytes +; + +CopyUp: + test edi,11b ;U - destination dword aligned? + jnz short CopyLeadUp ;V - if we are not dword aligned already, align + + shr ecx,2 ;U - shift down to dword count + and edx,11b ;V - trailing byte count + + cmp ecx,8 ;U - test if small enough for unwind copy + jb short CopyUnwindUp ;V - if so, then jump + + rep movsd ;N - move all of our dwords + + jmp dword ptr TrailUpVec[edx*4] ;N - process trailing bytes + +; +; Code to do optimal memory copies for non-dword-aligned destinations. +; + +; The following length check is done for two reasons: +; +; 1. to ensure that the actual move length is greater than any possiale +; alignment move, and +; +; 2. to skip the multiple move logic for small moves where it would +; be faster to move the bytes with one instruction. +; + + align @WordSize +CopyLeadUp: + + mov eax,edi ;U - get destination offset + mov edx,11b ;V - prepare for mask + + sub ecx,4 ;U - check for really short string - sub for adjust + jb short ByteCopyUp ;V - branch to just copy bytes + + and eax,11b ;U - get offset within first dword + add ecx,eax ;V - update size after leading bytes copied + + jmp dword ptr LeadUpVec[eax*4-4] ;N - process leading bytes + + align @WordSize +ByteCopyUp: + jmp dword ptr TrailUpVec[ecx*4+16] ;N - process just bytes + + align @WordSize +CopyUnwindUp: + jmp dword ptr UnwindUpVec[ecx*4] ;N - unwind dword copy + + align @WordSize +LeadUpVec dd LeadUp1, LeadUp2, LeadUp3 + + align @WordSize +LeadUp1: + and edx,ecx ;U - trailing byte count + mov al,[esi] ;V - get first byte from source + + mov [edi],al ;U - write second byte to destination + mov al,[esi+1] ;V - get second byte from source + + mov [edi+1],al ;U - write second byte to destination + mov al,[esi+2] ;V - get third byte from source + + shr ecx,2 ;U - shift down to dword count + mov [edi+2],al ;V - write third byte to destination + + add esi,3 ;U - advance source pointer + add edi,3 ;V - advance destination pointer + + cmp ecx,8 ;U - test if small enough for unwind copy + jb short CopyUnwindUp ;V - if so, then jump + + rep movsd ;N - move all of our dwords + + jmp dword ptr TrailUpVec[edx*4] ;N - process trailing bytes + + align @WordSize +LeadUp2: + and edx,ecx ;U - trailing byte count + mov al,[esi] ;V - get first byte from source + + mov [edi],al ;U - write second byte to destination + mov al,[esi+1] ;V - get second byte from source + + shr ecx,2 ;U - shift down to dword count + mov [edi+1],al ;V - write second byte to destination + + add esi,2 ;U - advance source pointer + add edi,2 ;V - advance destination pointer + + cmp ecx,8 ;U - test if small enough for unwind copy + jb short CopyUnwindUp ;V - if so, then jump + + rep movsd ;N - move all of our dwords + + jmp dword ptr TrailUpVec[edx*4] ;N - process trailing bytes + + align @WordSize +LeadUp3: + and edx,ecx ;U - trailing byte count + mov al,[esi] ;V - get first byte from source + + mov [edi],al ;U - write second byte to destination + add esi,1 ;V - advance source pointer + + shr ecx,2 ;U - shift down to dword count + add edi,1 ;V - advance destination pointer + + cmp ecx,8 ;U - test if small enough for unwind copy + jb short CopyUnwindUp ;V - if so, then jump + + rep movsd ;N - move all of our dwords + + jmp dword ptr TrailUpVec[edx*4] ;N - process trailing bytes + + align @WordSize +UnwindUpVec dd UnwindUp0, UnwindUp1, UnwindUp2, UnwindUp3 + dd UnwindUp4, UnwindUp5, UnwindUp6, UnwindUp7 + +UnwindUp7: + mov eax,[esi+ecx*4-28] ;U - get dword from source + ;V - spare + mov [edi+ecx*4-28],eax ;U - put dword into destination +UnwindUp6: + mov eax,[esi+ecx*4-24] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [edi+ecx*4-24],eax ;U - put dword into destination +UnwindUp5: + mov eax,[esi+ecx*4-20] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [edi+ecx*4-20],eax ;U - put dword into destination +UnwindUp4: + mov eax,[esi+ecx*4-16] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [edi+ecx*4-16],eax ;U - put dword into destination +UnwindUp3: + mov eax,[esi+ecx*4-12] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [edi+ecx*4-12],eax ;U - put dword into destination +UnwindUp2: + mov eax,[esi+ecx*4-8] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [edi+ecx*4-8],eax ;U - put dword into destination +UnwindUp1: + mov eax,[esi+ecx*4-4] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [edi+ecx*4-4],eax ;U - put dword into destination + + lea eax,[ecx*4] ;V - compute update for pointer + + add esi,eax ;U - update source pointer + add edi,eax ;V - update destination pointer +UnwindUp0: + jmp dword ptr TrailUpVec[edx*4] ;N - process trailing bytes + +;----------------------------------------------------------------------------- + + align @WordSize +TrailUpVec dd TrailUp0, TrailUp1, TrailUp2, TrailUp3 + + align @WordSize +TrailUp0: + mov eax,[dst] ;U - return pointer to destination + pop esi ;V - restore esi + pop edi ;U - restore edi + ;V - spare + ret + + align @WordSize +TrailUp1: + mov al,[esi] ;U - get byte from source + ;V - spare + mov [edi],al ;U - put byte in destination + mov eax,[dst] ;V - return pointer to destination + pop esi ;U - restore esi + pop edi ;V - restore edi + ret + + align @WordSize +TrailUp2: + mov al,[esi] ;U - get first byte from source + ;V - spare + mov [edi],al ;U - put first byte into destination + mov al,[esi+1] ;V - get second byte from source + mov [edi+1],al ;U - put second byte into destination + mov eax,[dst] ;V - return pointer to destination + pop esi ;U - restore esi + pop edi ;V - restore edi + ret + + align @WordSize +TrailUp3: + mov al,[esi] ;U - get first byte from source + ;V - spare + mov [edi],al ;U - put first byte into destination + mov al,[esi+1] ;V - get second byte from source + mov [edi+1],al ;U - put second byte into destination + mov al,[esi+2] ;V - get third byte from source + mov [edi+2],al ;U - put third byte into destination + mov eax,[dst] ;V - return pointer to destination + pop esi ;U - restore esi + pop edi ;V - restore edi + ret + +;----------------------------------------------------------------------------- +;----------------------------------------------------------------------------- +;----------------------------------------------------------------------------- + +; +; Copy down to avoid propogation in overlapping buffers. +; + align @WordSize +CopyDown: + lea esi,[esi+ecx-4] ;U - point to 4 bytes before src buffer end + lea edi,[edi+ecx-4] ;V - point to 4 bytes before dest buffer end +; +; See if the destination start is dword aligned +; + + test edi,11b ;U - test if dword aligned + jnz short CopyLeadDown ;V - if not, jump + + shr ecx,2 ;U - shift down to dword count + and edx,11b ;V - trailing byte count + + cmp ecx,8 ;U - test if small enough for unwind copy + jb short CopyUnwindDown ;V - if so, then jump + + std ;N - set direction flag + rep movsd ;N - move all of our dwords + cld ;N - clear direction flag back + + jmp dword ptr TrailDownVec[edx*4] ;N - process trailing bytes + + align @WordSize +CopyUnwindDown: + neg ecx ;U - negate dword count for table merging + ;V - spare + + jmp dword ptr UnwindDownVec[ecx*4+28] ;N - unwind copy + + align @WordSize +CopyLeadDown: + + mov eax,edi ;U - get destination offset + mov edx,11b ;V - prepare for mask + + cmp ecx,4 ;U - check for really short string + jb short ByteCopyDown ;V - branch to just copy bytes + + and eax,11b ;U - get offset within first dword + sub ecx,eax ;U - to update size after lead copied + + jmp dword ptr LeadDownVec[eax*4-4] ;N - process leading bytes + + align @WordSize +ByteCopyDown: + jmp dword ptr TrailDownVec[ecx*4] ;N - process just bytes + + align @WordSize +LeadDownVec dd LeadDown1, LeadDown2, LeadDown3 + + align @WordSize +LeadDown1: + mov al,[esi+3] ;U - load first byte + and edx,ecx ;V - trailing byte count + + mov [edi+3],al ;U - write out first byte + sub esi,1 ;V - point to last src dword + + shr ecx,2 ;U - shift down to dword count + sub edi,1 ;V - point to last dest dword + + cmp ecx,8 ;U - test if small enough for unwind copy + jb short CopyUnwindDown ;V - if so, then jump + + std ;N - set direction flag + rep movsd ;N - move all of our dwords + cld ;N - clear direction flag + + jmp dword ptr TrailDownVec[edx*4] ;N - process trailing bytes + + align @WordSize +LeadDown2: + mov al,[esi+3] ;U - load first byte + and edx,ecx ;V - trailing byte count + + mov [edi+3],al ;U - write out first byte + mov al,[esi+2] ;V - get second byte from source + + shr ecx,2 ;U - shift down to dword count + mov [edi+2],al ;V - write second byte to destination + + sub esi,2 ;U - point to last src dword + sub edi,2 ;V - point to last dest dword + + cmp ecx,8 ;U - test if small enough for unwind copy + jb short CopyUnwindDown ;V - if so, then jump + + std ;N - set direction flag + rep movsd ;N - move all of our dwords + cld ;N - clear direction flag + + jmp dword ptr TrailDownVec[edx*4] ;N - process trailing bytes + + align @WordSize +LeadDown3: + mov al,[esi+3] ;U - load first byte + and edx,ecx ;V - trailing byte count + + mov [edi+3],al ;U - write out first byte + mov al,[esi+2] ;V - get second byte from source + + mov [edi+2],al ;U - write second byte to destination + mov al,[esi+1] ;V - get third byte from source + + shr ecx,2 ;U - shift down to dword count + mov [edi+1],al ;V - write third byte to destination + + sub esi,3 ;U - point to last src dword + sub edi,3 ;V - point to last dest dword + + cmp ecx,8 ;U - test if small enough for unwind copy + jb CopyUnwindDown ;V - if so, then jump + + std ;N - set direction flag + rep movsd ;N - move all of our dwords + cld ;N - clear direction flag + + jmp dword ptr TrailDownVec[edx*4] ;N - process trailing bytes + +;------------------------------------------------------------------ + + align @WordSize +UnwindDownVec dd UnwindDown7, UnwindDown6, UnwindDown5, UnwindDown4 + dd UnwindDown3, UnwindDown2, UnwindDown1, UnwindDown0 + +UnwindDown7: + mov eax,[esi+ecx*4+28] ;U - get dword from source + ;V - spare + mov [edi+ecx*4+28],eax ;U - put dword into destination +UnwindDown6: + mov eax,[esi+ecx*4+24] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [edi+ecx*4+24],eax ;U - put dword into destination +UnwindDown5: + mov eax,[esi+ecx*4+20] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [edi+ecx*4+20],eax ;U - put dword into destination +UnwindDown4: + mov eax,[esi+ecx*4+16] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [edi+ecx*4+16],eax ;U - put dword into destination +UnwindDown3: + mov eax,[esi+ecx*4+12] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [edi+ecx*4+12],eax ;U - put dword into destination +UnwindDown2: + mov eax,[esi+ecx*4+8] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [edi+ecx*4+8],eax ;U - put dword into destination +UnwindDown1: + mov eax,[esi+ecx*4+4] ;U(entry)/V(not) - get dword from source + ;V(entry) - spare + mov [edi+ecx*4+4],eax ;U - put dword into destination + + lea eax,[ecx*4] ;V - compute update for pointer + + add esi,eax ;U - update source pointer + add edi,eax ;V - update destination pointer +UnwindDown0: + jmp dword ptr TrailDownVec[edx*4] ;N - process trailing bytes + +;----------------------------------------------------------------------------- + + align @WordSize +TrailDownVec dd TrailDown0, TrailDown1, TrailDown2, TrailDown3 + + align @WordSize +TrailDown0: + mov eax,[dst] ;U - return pointer to destination + ;V - spare + pop esi ;U - restore esi + pop edi ;V - restore edi + ret + + align @WordSize +TrailDown1: + mov al,[esi+3] ;U - get byte from source + ;V - spare + mov [edi+3],al ;U - put byte in destination + mov eax,[dst] ;V - return pointer to destination + pop esi ;U - restore esi + pop edi ;V - restore edi + ret + + align @WordSize +TrailDown2: + mov al,[esi+3] ;U - get first byte from source + ;V - spare + mov [edi+3],al ;U - put first byte into destination + mov al,[esi+2] ;V - get second byte from source + mov [edi+2],al ;U - put second byte into destination + mov eax,[dst] ;V - return pointer to destination + pop esi ;U - restore esi + pop edi ;V - restore edi + ret + + align @WordSize +TrailDown3: + mov al,[esi+3] ;U - get first byte from source + ;V - spare + mov [edi+3],al ;U - put first byte into destination + mov al,[esi+2] ;V - get second byte from source + mov [edi+2],al ;U - put second byte into destination + mov al,[esi+1] ;V - get third byte from source + mov [edi+1],al ;U - put third byte into destination + mov eax,[dst] ;V - return pointer to destination + pop esi ;U - restore esi + pop edi ;V - restore edi + ret + +memcpy endp + + +memmove proc NEAR C\ + dst:ptr byte, \ + src:ptr byte, \ + count:DWORD + + jmp memcpy + +memmove endp + end + diff --git a/base/Kernel/Native/ix86/_memset.asm b/base/Kernel/Native/ix86/_memset.asm new file mode 100644 index 0000000..8c0d049 --- /dev/null +++ b/base/Kernel/Native/ix86/_memset.asm @@ -0,0 +1,128 @@ +;******************************************************************************* +;memset.asm - set a section of memory to all one byte +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; +;Purpose: +; contains the memset() routine +; +;******************************************************************************* + +include hal.inc + +;*** +;char *memset(dst, value, count) - sets "count" bytes at "dst" to "value" +; +;Purpose: +; Sets the first "count" bytes of the memory starting +; at "dst" to the character value "value". +; +; Algorithm: +; char * +; memset (dst, value, count) +; char *dst; +; char value; +; unsigned int count; +; { +; char *start = dst; +; +; while (count--) +; *dst++ = value; +; return(start); +; } +; +;Entry: +; char *dst - pointer to memory to fill with value +; char value - value to put in dst bytes +; int count - number of bytes of dst to fill +; +;Exit: +; returns dst, with filled bytes +; +;Uses: +; +;Exceptions: +; +;******************************************************************************* + + public _memset +_memset proc + + .FPO ( 0, 3, 0, 0, 0, 0 ) + + mov edx,[esp + 0ch] ; edx = "count" + mov ecx,[esp + 4] ; ecx points to "dst" + + test edx,edx ; 0? + jz short toend ; if so, nothing to do + + xor eax,eax + mov al,[esp + 8] ; the byte "value" to be stored + + +; Align address on dword boundary + + push edi ; preserve edi + mov edi,ecx ; edi = dest pointer + + cmp edx,4 ; if it's less then 4 bytes + jb tail ; tail needs edi and edx to be initialized + + neg ecx + and ecx,3 ; ecx = # bytes before dword boundary + jz short dwords ; jump if address already aligned + + sub edx,ecx ; edx = adjusted count (for later) +adjust_loop: + mov [edi],al + add edi,1 + sub ecx,1 + jnz adjust_loop + +dwords: +; set all 4 bytes of eax to [value] + mov ecx,eax ; ecx=0/0/0/value + shl eax,8 ; eax=0/0/value/0 + + add eax,ecx ; eax=0/0val/val + + mov ecx,eax ; ecx=0/0/val/val + + shl eax,10h ; eax=val/val/0/0 + + add eax,ecx ; eax = all 4 bytes = [value] + +; Set dword-sized blocks + mov ecx,edx ; move original count to ecx + and edx,3 ; prepare in edx byte count (for tail loop) + shr ecx,2 ; adjust ecx to be dword count + jz tail ; jump if it was less then 4 bytes + + rep stosd +main_loop_tail: + test edx,edx ; if there is no tail bytes, + jz finish ; we finish, and it's time to leave +; Set remaining bytes + +tail: + mov [edi],al ; set remaining bytes + add edi,1 + + sub edx,1 ; if there is some more bytes + jnz tail ; continue to fill them + +; Done +finish: + mov eax,[esp + 8] ; return dest pointer + pop edi ; restore edi + + ret + +toend: + mov eax,[esp + 4] ; return dest pointer + + ret + +_memset endp + + end diff --git a/base/Kernel/Native/_ulldiv.asm b/base/Kernel/Native/ix86/_ulldiv.asm similarity index 93% rename from base/Kernel/Native/_ulldiv.asm rename to base/Kernel/Native/ix86/_ulldiv.asm index 92a255e..267abfe 100644 --- a/base/Kernel/Native/_ulldiv.asm +++ b/base/Kernel/Native/ix86/_ulldiv.asm @@ -9,18 +9,6 @@ ; ;******************************************************************************* -.686p -.mmx -.xmm -.model flat,C -.code - -assume ds:flat -assume es:flat -assume ss:flat -assume fs:nothing -assume gs:nothing - include hal.inc ;*** @@ -46,7 +34,7 @@ include hal.inc ; ;******************************************************************************* -_aulldiv PROC NEAR +__aulldiv PROC NEAR push ebx push esi @@ -158,6 +146,6 @@ L2: ret 16 -_aulldiv ENDP +__aulldiv ENDP end diff --git a/base/Kernel/Native/ix86/_ulldvrm.asm b/base/Kernel/Native/ix86/_ulldvrm.asm new file mode 100644 index 0000000..f7b9120 --- /dev/null +++ b/base/Kernel/Native/ix86/_ulldvrm.asm @@ -0,0 +1,181 @@ +;******************************************************************************* +;ulldvrm.asm - unsigned long divide and remainder routine +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; +;Purpose: +; defines the unsigned long divide and remainder routine +; __aulldvrm +; +;Revision History: +; 10-06-98 SMK Initial version. +; +;******************************************************************************* + +include hal.inc + +;*** +;ulldvrm - unsigned long divide and remainder +; +;Purpose: +; Does a unsigned long divide and remainder of the arguments. Arguments +; are not changed. +; +;Entry: +; Arguments are passed on the stack: +; 1st pushed: divisor (QWORD) +; 2nd pushed: dividend (QWORD) +; +;Exit: +; EDX:EAX contains the quotient (dividend/divisor) +; EBX:ECX contains the remainder (divided % divisor) +; NOTE: this routine removes the parameters from the stack. +; +;Uses: +; ECX +; +;Exceptions: +; +;******************************************************************************* + +__aulldvrm PROC NEAR + + push esi + +; Set up the local stack and save the index registers. When this is done +; the stack frame will look as follows (assuming that the expression a/b will +; generate a call to aulldvrm(a, b)): +; +; ----------------- +; | | +; |---------------| +; | | +; |--divisor (b)--| +; | | +; |---------------| +; | | +; |--dividend (a)-| +; | | +; |---------------| +; | return addr** | +; |---------------| +; ESP---->| ESI | +; ----------------- +; + +DVND equ [esp + 8] ; stack address of dividend (a) +DVSR equ [esp + 16] ; stack address of divisor (b) + +; +; Now do the divide. First look to see if the divisor is less than 4194304K. +; If so, then we can use a simple algorithm with word divides, otherwise +; things get a little more complex. +; + + mov eax,HIWORD(DVSR) ; check to see if divisor < 4194304K + or eax,eax + jnz short L1 ; nope, gotta do this the hard way + mov ecx,LOWORD(DVSR) ; load divisor + mov eax,HIWORD(DVND) ; load high word of dividend + xor edx,edx + div ecx ; get high order bits of quotient + mov ebx,eax ; save high bits of quotient + mov eax,LOWORD(DVND) ; edx:eax <- remainder:lo word of dividend + div ecx ; get low order bits of quotient + mov esi,eax ; ebx:esi <- quotient + +; +; Now we need to do a multiply so that we can compute the remainder. +; + mov eax,ebx ; set up high word of quotient + mul dword ptr LOWORD(DVSR) ; HIWORD(QUOT) * DVSR + mov ecx,eax ; save the result in ecx + mov eax,esi ; set up low word of quotient + mul dword ptr LOWORD(DVSR) ; LOWORD(QUOT) * DVSR + add edx,ecx ; EDX:EAX = QUOT * DVSR + jmp short L2 ; complete remainder calculation + +; +; Here we do it the hard way. Remember, eax contains DVSRHI +; + +L1: + mov ecx,eax ; ecx:ebx <- divisor + mov ebx,LOWORD(DVSR) + mov edx,HIWORD(DVND) ; edx:eax <- dividend + mov eax,LOWORD(DVND) +L3: + shr ecx,1 ; shift divisor right one bit; hi bit <- 0 + rcr ebx,1 + shr edx,1 ; shift dividend right one bit; hi bit <- 0 + rcr eax,1 + or ecx,ecx + jnz short L3 ; loop until divisor < 4194304K + div ebx ; now divide, ignore remainder + mov esi,eax ; save quotient + +; +; We may be off by one, so to check, we will multiply the quotient +; by the divisor and check the result against the orignal dividend +; Note that we must also check for overflow, which can occur if the +; dividend is close to 2**64 and the quotient is off by 1. +; + + mul dword ptr HIWORD(DVSR) ; QUOT * HIWORD(DVSR) + mov ecx,eax + mov eax,LOWORD(DVSR) + mul esi ; QUOT * LOWORD(DVSR) + add edx,ecx ; EDX:EAX = QUOT * DVSR + jc short L4 ; carry means Quotient is off by 1 + +; +; do long compare here between original dividend and the result of the +; multiply in edx:eax. If original is larger or equal, we are ok, otherwise +; subtract one (1) from the quotient. +; + + cmp edx,HIWORD(DVND) ; compare hi words of result and original + ja short L4 ; if result > original, do subtract + jb short L5 ; if result < original, we are ok + cmp eax,LOWORD(DVND) ; hi words are equal, compare lo words + jbe short L5 ; if less or equal we are ok, else subtract +L4: + dec esi ; subtract 1 from quotient + sub eax,LOWORD(DVSR) ; subtract divisor from result + sbb edx,HIWORD(DVSR) +L5: + xor ebx,ebx ; ebx:esi <- quotient + +L2: +; +; Calculate remainder by subtracting the result from the original dividend. +; Since the result is already in a register, we will do the subtract in the +; opposite direction and negate the result. +; + + sub eax,LOWORD(DVND) ; subtract dividend from result + sbb edx,HIWORD(DVND) + neg edx ; otherwise, negate the result + neg eax + sbb edx,0 + +; +; Now we need to get the quotient into edx:eax and the remainder into ebx:ecx. +; + mov ecx,edx + mov edx,ebx + mov ebx,ecx + mov ecx,eax + mov eax,esi +; +; Just the cleanup left to do. edx:eax contains the quotient. +; Restore the saved registers and return. +; + + pop esi + + ret 16 + +__aulldvrm ENDP + + end diff --git a/base/Kernel/Native/_ullrem.asm b/base/Kernel/Native/ix86/_ullrem.asm similarity index 93% rename from base/Kernel/Native/_ullrem.asm rename to base/Kernel/Native/ix86/_ullrem.asm index 9947cb3..3a4fa7b 100644 --- a/base/Kernel/Native/_ullrem.asm +++ b/base/Kernel/Native/ix86/_ullrem.asm @@ -9,18 +9,6 @@ ; ;******************************************************************************* -.686p -.mmx -.xmm -.model flat,C -.code - -assume ds:flat -assume es:flat -assume ss:flat -assume fs:nothing -assume gs:nothing - include hal.inc ;*** @@ -46,7 +34,7 @@ include hal.inc ; ;******************************************************************************* -_aullrem PROC NEAR +__aullrem PROC NEAR push ebx @@ -163,6 +151,6 @@ L2: ret 16 -_aullrem ENDP +__aullrem ENDP end diff --git a/base/Kernel/Native/ix86/_ullshr.asm b/base/Kernel/Native/ix86/_ullshr.asm new file mode 100644 index 0000000..f3add9d --- /dev/null +++ b/base/Kernel/Native/ix86/_ullshr.asm @@ -0,0 +1,74 @@ +;*** +;ullshr.asm - long shift right +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; +;Purpose: +; define unsigned long shift right routine +; __aullshr +; +;******************************************************************************* + +include hal.inc + +;*** +;ullshr - long shift right +; +;Purpose: +; Does a unsigned Long Shift Right +; Shifts a long right any number of bits. +; +;Entry: +; EDX:EAX - long value to be shifted +; CL - number of bits to shift by +; +;Exit: +; EDX:EAX - shifted value +; +;Uses: +; CL is destroyed. +; +;Exceptions: +; +;******************************************************************************* + + +__aullshr PROC NEAR + +; +; Handle shifts of 64 bits or more (if shifting 64 bits or more, the result +; depends only on the high order bit of edx). +; + cmp cl,64 + jae short RETZERO + +; +; Handle shifts of between 0 and 31 bits +; + cmp cl, 32 + jae short MORE32 + shrd eax,edx,cl + shr edx,cl + ret + +; +; Handle shifts of between 32 and 63 bits +; +MORE32: + mov eax,edx + xor edx,edx + and cl,31 + shr eax,cl + ret + +; +; return 0 in edx:eax +; +RETZERO: + xor eax,eax + xor edx,edx + ret + +__aullshr ENDP + + end diff --git a/base/Kernel/Native/ix86/halasm.asm b/base/Kernel/Native/ix86/halasm.asm new file mode 100644 index 0000000..2786543 --- /dev/null +++ b/base/Kernel/Native/ix86/halasm.asm @@ -0,0 +1,1420 @@ +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; + +include hal.inc + +externdef __throwDispatcher:NEAR +externdef __throwDispatcherExplicitAddrAfter:NEAR +externdef ?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z:NEAR + +ifdef SINGULARITY_KERNEL +externdef ?g_setStopContext@Class_System_Threading_Thread@@SIXPAU1@PAUClass_System_Exception@@@Z:NEAR +externdef __throwBeyondMarker:NEAR +endif ; SINGULARITY_KERNEL + +externdef __throwDivideByZeroException:NEAR +externdef __throwNullPointerException:NEAR +externdef __throwOverflowException:NEAR +externdef __throwStackOverflowException:NEAR + +if EXCLUDED +externdef __checkFPStackDepth0:NEAR +externdef __checkFPStackDepth1:NEAR +externdef __checkFPStackDepth2:NEAR +externdef __checkFPStackDepth3:NEAR +externdef __checkFPStackDepth4:NEAR +externdef __checkFPStackDepth5:NEAR +externdef __checkFPStackDepth6:NEAR +externdef __checkFPStackDepth7:NEAR +endif ;; EXCLUDED + + align 8 +$DBLMAXINT DQ 041dfffffffc00000r ; 2.14747e+009 +$DBLMININT DQ 0c1e0000000000000r ; -2.14748e+009 +$MAXLONG DQ 07fffffffffffffffh +$MINLONG DQ 08000000000000000h + +; +; __throwDispatcher(ecx=exception) +; +; Description: this function is called to explicitly throw an exception. +; It assumes that the return address points to the instruction immediately +; after the one where the exception was thrown +; +; Arguments: +; ecx = exception object +; [esp] = the return address +; Effects: +; 1. Create ebp chain entry +; 2. Get return address and uses it to calculate the address of the +; instruction that threw the exception. +; 3. Looks up the appropriate handler and jumps to code to process it. + +align 16 + +__throwDispatcher proc + push ebp ; create ebp chain entry + mov ebp, esp ; set new ebp + mov edx, [ebp+4] ; get return address + push ecx ; save exception + sub edx, 1 ; adjust to point to throw location + call ?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z + pop ecx ; restore exception + pop ebp ; remove ebp chain + add esp, 4 ; remove eip from the stack +; mov edx is already ok + jmp __throwDispatcherHandler +__throwDispatcher endp + +; +; __throwDispatcherExplicitAddr (ecx=exception, edx=throwAddress) +; +; Description: +; This is to be used when the address where the exception occurred +; is passed in as an extra argument +; +; Arguments: +; ecx = exception object +; edx = address where the exception was thrown +; + + +align 16 +__throwDispatcherExplicitAddr proc + push ecx ; save exception + call ?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z + pop ecx ; restore exception +; mov edx is already ok + jmp __throwDispatcherHandler +__throwDispatcherExplicitAddr endp + +; +; __throwDispatcherExplictAddrAfter (ecx=exception, edx=throwAddress) +; +; Description: +; This is to be used when the address of the instruction immediately after +; the one that actually threw the exception is passed as an argument. +; +; Arguments: +; ecx = pointer to exception object being thrown +; edx = address of the instruction following the one where +; the exception was thrown +; +; This is used, for example, in stack unwinding, where edx is the +; return address on the stack for the current procedure. +; +; Stack unwinding occurs when the current procedure does not have a handler +; for the routine. The idea is to pop the stack frame and treat the call +; instruction in the caller as though it threw. We only have the return +; address, though, which points to the instruction *after* the call. +; + +align 16 +__throwDispatcherExplicitAddrAfter proc +ifdef SINGULARITY +; int 3 + push ecx + push edx + mov ecx, edx + call ?g_IsUnlinkStack@Class_System_Exception@@SI_NPAUuintPtr@@@Z + pop edx + pop ecx + test al, al + je normal + mov eax, ecx ; save exception type + mov ecx, edx + mov edx, [ebp+4] ; save the return addr in caller + mov [ebp+4], afterUnlinkStack ; override return addr to instr after + jmp ecx ; unlink stack which saves eax, edx +afterUnlinkStack: + mov ecx, eax ; restore return addr in caller +normal: + ; Have we reached a kernel->process or process->kernel boundary? + ; Call Exception.CheckKernelProcessBoundary(esp, ebp, exception) + push ecx + push edx + push ecx ; arg 3 (exception) + mov ecx, esp ; arg 2 (ebp) + mov edx, ebp ; arg 1 (esp) + call ?g_CheckKernelProcessBoundary@Class_System_Threading_Thread@@SIPAUuintPtr@@PAU2@0PAUClass_System_Exception@@@Z + pop edx + pop ecx + ; A non-zero return value means we reached a boundary. + test eax, eax + jz normal2 +ifdef SINGULARITY_PROCESS + ;; The process's exception reached the kernel's frames. + ; Return gracefully to the kernel (i.e. jump to the kernel's + ; return address), and the kernel's popStackMark + ; will check uncaughtFlag and throw a new exception. + jmp edx +endif ; SINGULARITY_PROCESS +ifdef SINGULARITY_KERNEL +; There are 3 control paths that merge here: +; (1) When Thread.cs stops a process mode thread, it sets eip to point here. +; (2) When Thread.cs stops a blocked kernel thread, it sets eip to point here. +; (3) The stack unwinder falls through to this case when a kernel exception +; reaches a process's frames. +; For control path (3), a finally block puts us in a GC safe state +; just before we reach here. +; We also expect to be in the GC safe state most of the +; time for control paths (1) and (2), but +; because pushStackMark and popStackMark are not atomic operations, +; we cannot be 100% sure that we're in the GC safe state. +; eax: kernel->process or kernel->kernel marker +; ecx: exception +__throwBeyondMarker LABEL NEAR + push ecx + push eax + ; Leave the GC safe state (if we're actually in it) + call ?g_RestoreMutatorControlIfNeeded@Class_System_GCs_Transitions@@SIXXZ + pop eax + pop ecx +; eax: kernel->process or kernel->kernel marker +; ecx: exception + ; Restore state from the marker, skipping over any intermediate frames. + ; Keep the original exception in ecx. + ; Get the return address for the frame beyond the marker into edx: + mov edx, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._stackBottom + mov edx, dword ptr [edx - 4] + ; Restore the kernel's ebx, edi, esi, ebp from the marker: + mov ebx, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBX + mov edi, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EDI + mov esi, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._ESI + mov ebp, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._calleeSaveRegisters._EBP + ; Keep a copy of transition record (eax) and oldTransitionRecord + push eax + push dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._oldTransitionRecord + ; Read _stackBottom (the new esp) from the kernel->process marker: + mov eax, dword ptr [eax].Struct_System_GCs_CallStack_TransitionRecord._stackBottom + ; Save ecx, edx to new stack -- this may overwrite the transition record, + ; so don't read any more fields from the transition record after this point. + mov [eax - 4], ecx ; (exception) + mov [eax - 8], edx ; (return address) + ; Set up the two arguments to DiscardStackSegments + pop edx ; (oldTransitionRecord) + pop ecx ; (marker) + ; Restore the kernel's esp from the kernel->process marker: + lea esp, [eax - 8] + ; Free any stack segments that we skipped over: + ;; TODO: are we sure there's enough stack space to call this? + call ?g_DiscardSkippedStackSegments@Class_System_Threading_Thread@@SIXPAUStruct_System_GCs_CallStack_TransitionRecord@@0@Z + pop edx ; (return address) + pop ecx ; (exception) +endif ; SINGULARITY_KERNEL +normal2: +endif + push ecx ; save exception + dec edx + call ?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z + pop ecx ; restore exception +; mov edx is already ok + jmp __throwDispatcherHandler +__throwDispatcherExplicitAddrAfter endp + +; __throwDispatcherHandler (eax=frameSetupInfo or exceptionType, +; ecx=exception, +; edx=spillSize,frameSetupSize, or handlerAddress +; +; Description: +; After the exception table entry has been found, the values are passed here +; for processing. This method simply checks for the easy case of an explicit +; handler (known if the exceptionType is given <-- low bit is zero). +; In this case it simply jumps to the handler. Otherwise it passes the +; information along to __throwDispatcherUnwind. +; +; Arguments: +; If low bit of eax is set: +; eax = frame setup information +; ecx = exception object +; edx = spill size excluding callee-save register saves +; or offset from ebp to end of callee-save register saves +; Otherwise: +; eax = exception type (unused) +; ecx = exception object +; edx = handler address + +align 16 +__throwDispatcherHandler proc + test eax, 1 + jne __throwDispatcherUnwind + ;; ecx=exception, edx=handler + jmp edx +__throwDispatcherHandler endp + +; __throwDispatcherUnwind (eax=frame setup info, ecx=exception, edx=spill size +; +; Description: +; This is the global unwind handler. It is used to unwind a single stack +; frame if there are no explicit handlers that catch an exception in a given +; function. +; +; Arguments: +; eax = frame setup information, must have low bit set +; ecx = exception object +; edx = spill size excluding callee-save register saves +; or offset from ebp to end of callee-save register saves +; +; See tables\ExceptionTable.cs for details on these values + +align 16 +__throwDispatcherUnwind proc + ;; eax=frame info + ;; edx=spill size + + ;; obviously ebp isn't useful under frame pointer omission + ;; but less obviously esp may be invalid if ebp is good + ;; (e.g. under varargs we may not have known how many arguments to + ;; pop; this is one reason why varargs turns off frame pointer omission) + test eax, 2h + jne esp_is_good + ;; ebp_is_good + add edx, ebp + mov esp, edx + jmp esp_is_setup +esp_is_good: + ;; pop spill slots + add esp, edx + +esp_is_setup: + + ;; restore callee-saves and pop values from stack + ;; (excludes ebp if used as the frame pointer) + test eax, 4h + je skip_edi_restore + ;; restore edi + pop edi +skip_edi_restore: + test eax, 8h + je skip_esi_restore + ;; restore esi + pop esi +skip_esi_restore: + test eax, 10h + je skip_ebp_restore + ;; restore ebp + pop ebp +skip_ebp_restore: + test eax, 20h + je skip_ebx_restore + ;; restore ebx + pop ebx +skip_ebx_restore: + + test eax, 40h + je skip_jump_transition_record + ;; jump over transition record + add esp, (SIZE Struct_System_GCs_CallStack_TransitionRecord) +skip_jump_transition_record: + + ;; restore ebp if it was used as the frame pointer + test eax, 2h + jne skip_frame_pointer_restore + ;; restore frame pointer (esp == ebp already) + pop ebp +skip_frame_pointer_restore: + + ;; set edx=return address + pop edx + + ;; pop arguments + shr eax, 16 + add esp, eax + + ;; At this point + ;; ecx=exception, edx=return address + ;; esi/edi/ebx/ebp/esp have been restored + ;; eax is scratch + + ;; set up next handler search + jmp __throwDispatcherExplicitAddrAfter +__throwDispatcherUnwind endp + +; +; __throwDivideByZeroException: instantiate an divide-by-zero exception +; and throw it. +; +; Assumes edx points to the address after the one that threw. +; + +align 16 +__throwDivideByZeroException proc + push ebx + push esi + mov ebx,edx ; save address +if SINGLE_THREADED + inc ?c_allocationInhibitGC@Class_System_GC@@2_NA +else ; SINGLE_THREADED + lock inc ?c_allocationInhibitGC@Class_System_GC@@2_NA +endif ; SINGLE_THREADED + mov ecx,offset ??_7System_DivideByZeroException@@6B@ + call ?g_AllocateObject@Class_System_GC@@SIPAUClass_System_Object@@PAUClass_System_VTable@@@Z + mov esi,eax ; save pointer to instance of exception + mov ecx,eax ; initialize instance + call ?m__ctor@Class_System_DivideByZeroException@@SIXPAU1@@Z +if SINGLE_THREADED + dec ?c_allocationInhibitGC@Class_System_GC@@2_NA +else ; SINGLE_THREADED + lock dec ?c_allocationInhibitGC@Class_System_GC@@2_NA +endif ; SINGLE_THREADED + mov ecx,esi + mov edx,ebx + pop esi + pop ebx + jmp __throwDispatcherExplicitAddr +__throwDivideByZeroException endp + +; +; __throwStackOverflowException: instantiate an StackOverflow exception +; and throw it. +; +; Assumes edx points to the address of the instruction that faulted +; + +align 16 +__throwStackOverflowException proc + push ebx + push esi + mov ebx,edx ; save address +if SINGLE_THREADED + inc ?c_allocationInhibitGC@Class_System_GC@@2_NA +else ; SINGLE_THREADED + lock inc ?c_allocationInhibitGC@Class_System_GC@@2_NA +endif ; MULTI_THREADED + mov ecx,offset ??_7System_StackOverflowException@@6B@ + call ?g_AllocateObject@Class_System_GC@@SIPAUClass_System_Object@@PAUClass_System_VTable@@@Z + mov esi,eax ; save pointer to instance of exception + mov ecx,eax ; initialize instance + call ?m__ctor@Class_System_StackOverflowException@@SIXPAU1@@Z +if SINGLE_THREADED + dec ?c_allocationInhibitGC@Class_System_GC@@2_NA +else ; SINGLE_THREADED + lock dec ?c_allocationInhibitGC@Class_System_GC@@2_NA +endif ; MULTI_THREADED + mov ecx,esi + mov edx,ebx + call ?ExceptionTableLookup@@YI_KPAUClass_System_Exception@@I@Z +ifndef SINGULARITY + push esi + push edx + call ?ResetGuardPage@@YIXXZ + pop edx + pop eax +endif + pop esi + pop ebx + mov ecx,eax +; mov edx is already ok + jmp edx +__throwStackOverflowException endp + +; +; __throwNullReferenceException: instantiate an NullReference exception +; and throw it. +; +; Assumes edx points to the address of the instruction that faulted +; + +align 16 +__throwNullReferenceException proc + push ebx + push esi + mov ebx,edx ; save address +if SINGLE_THREADED + inc ?c_allocationInhibitGC@Class_System_GC@@2_NA +else ; SINGLE_THREADED + lock inc ?c_allocationInhibitGC@Class_System_GC@@2_NA +endif ; SINGLE_THREADED + mov ecx,offset ??_7System_NullReferenceException@@6B@ + call ?g_AllocateObject@Class_System_GC@@SIPAUClass_System_Object@@PAUClass_System_VTable@@@Z + mov esi,eax ; save pointer to instance of exception + mov ecx,eax ; initialize instance + call ?m__ctor@Class_System_NullReferenceException@@SIXPAU1@@Z +if SINGLE_THREADED + dec ?c_allocationInhibitGC@Class_System_GC@@2_NA +else ; SINGLE_THREADED + lock dec ?c_allocationInhibitGC@Class_System_GC@@2_NA +endif ; SINGLE_THREADED + mov ecx,esi + mov edx,ebx + pop esi + pop ebx + jmp __throwDispatcherExplicitAddr +__throwNullReferenceException endp + +; +; __throwDivideByZeroException: instantiate an divide-by-zero exception +; and throw it. +; +; Assumes edx points to the address of the instruction that faulted +; + +align 16 +__throwOverflowException proc + push ebx + push esi + mov ebx,edx ; save address +if SINGLE_THREADED + inc ?c_allocationInhibitGC@Class_System_GC@@2_NA +else ; SINGLE_THREADED + lock inc ?c_allocationInhibitGC@Class_System_GC@@2_NA +endif ; SINGLE_THREADED + mov ecx,offset ??_7System_OverflowException@@6B@ + call ?g_AllocateObject@Class_System_GC@@SIPAUClass_System_Object@@PAUClass_System_VTable@@@Z + mov esi,eax ; save pointer to instance of exception + mov ecx,eax ; initialize instance + call ?m__ctor@Class_System_OverflowException@@SIXPAU1@@Z +if SINGLE_THREADED + dec ?c_allocationInhibitGC@Class_System_GC@@2_NA +else ; SINGLE_THREADED + lock dec ?c_allocationInhibitGC@Class_System_GC@@2_NA +endif ; SINGLE_THREADED + mov ecx,esi + mov edx,ebx + pop esi + pop ebx + jmp __throwDispatcherExplicitAddr +__throwOverflowException endp + +ifdef SINGULARITY_KERNEL +; +; void Thread.setStopContext(Thread t, Exception exn) +; + +align 16 +?g_setStopContext@Class_System_Threading_Thread@@SIXPAU1@PAUClass_System_Exception@@@Z proc + ; ecx = ecx.context + add ecx, Class_System_Threading_Thread._context + ; context.eip = __throwBeyondMarker + ; context.ecx = processStopException + ; context.eax = context.stackMarkers + mov [ecx].Struct_Microsoft_Singularity_ThreadContext._threadRecord._spill._ip, __throwBeyondMarker + mov [ecx].Struct_Microsoft_Singularity_ThreadContext._threadRecord._spill._cx, edx + mov eax, [ecx].Struct_Microsoft_Singularity_ThreadContext._stackMarkers + mov [ecx].Struct_Microsoft_Singularity_ThreadContext._threadRecord._spill._ax, eax + ret +?g_setStopContext@Class_System_Threading_Thread@@SIXPAU1@PAUClass_System_Exception@@@Z endp + +endif ; SINGULARITY_KERNEL + +; +; int System.VTable.doubleToInt(double) +; + +align 16 +?g_doubleToInt@Class_System_VTable@@SIHN@Z proc + push ebp + mov ebp,esp + add esp,-8 + fld real8 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + mov ax,word ptr [ebp-2] + or eax,0C00h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + fistp dword ptr [ebp-8] + fldcw word ptr [ebp-2] + mov eax,dword ptr [ebp-8] + + cmp eax,080000000h + je possible_overflow +return: + mov esp,ebp + pop ebp + ret 8 + +possible_overflow: + fld real8 ptr [ebp+8] + fcomp real8 ptr $DBLMAXINT + fnstsw ax + test ah,4 + jne short return_zero + test ah,1 + jne short return_MININT + +return_MAXINT: + mov eax, 07fffffffh + jmp short return + +return_zero: + xor eax, eax + jmp short return + +return_MININT: + mov eax, 080000000h + jmp short return + +?g_doubleToInt@Class_System_VTable@@SIHN@Z endp + +; +; long System.VTable.doubleToLong(double) +; + +align 16 +?g_doubleToLong@Class_System_VTable@@SI_JN@Z proc + push ebp + mov ebp,esp + add esp,-12 + fld real8 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + mov ax,word ptr [ebp-2] + or eax,0C00h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + fistp qword ptr [ebp-12] + fldcw word ptr [ebp-2] + mov eax,dword ptr [ebp-12] + mov edx,dword ptr [ebp-8] + + cmp edx,080000000h + je possible_overflow +return: + mov esp,ebp + pop ebp + ret 8 + +possible_overflow: + mov edx,eax ; save lsw + fld real8 ptr [ebp+8] + fild qword ptr $MAXLONG + fcompp + fnstsw ax + test ah,4 + jne short return_zero + test ah,65 + je short check_MINLONG + +return_MAXLONG: + mov eax, 0ffffffffh + mov edx, 07fffffffh + jmp short return + +return_zero: + xor eax, eax + xor edx, edx + jmp short return + +check_MINLONG: + fld real8 ptr [ebp+8] + fild qword ptr $MINLONG + fcompp + fnstsw ax + test ah,1 + jne short return_original + +return_MINLONG: + xor edx, edx ; zero lsw + +return_original: + mov eax, edx ; restore lsw to eax + mov edx, 080000000h + jmp short return + +?g_doubleToLong@Class_System_VTable@@SI_JN@Z endp + +; +; int System.VTable.floatToInt(float) +; + +align 16 +?g_floatToInt@Class_System_VTable@@SIHM@Z proc + push ebp + mov ebp,esp + add esp,-8 + fld real4 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + xor eax,eax + mov ax,word ptr [ebp-2] + or eax,0C00h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + fistp dword ptr [ebp-8] + fldcw word ptr [ebp-2] + mov eax,dword ptr [ebp-8] + + cmp eax,080000000h + je possible_overflow +return: + mov esp,ebp + pop ebp + ret 4 + +possible_overflow: + fld real4 ptr [ebp+8] + fcomp real8 ptr $DBLMAXINT + fnstsw ax + test ah,4 + jne short return_zero + test ah,1 + jne short return_MININT + mov eax, 07fffffffh + jmp short return + +return_zero: + xor eax, eax + jmp short return + +return_MININT: + mov eax, 080000000h + jmp short return + +?g_floatToInt@Class_System_VTable@@SIHM@Z endp + +; +; long System.VTable.floatToLong(float) +; + +align 16 +?g_floatToLong@Class_System_VTable@@SI_JM@Z proc + push ebp + mov ebp,esp + add esp,-12 + fld real4 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + mov ax,word ptr [ebp-2] + or eax,0C00h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + fistp qword ptr [ebp-12] + fldcw word ptr [ebp-2] + mov eax,dword ptr [ebp-12] + mov edx,dword ptr [ebp-8] + + cmp edx,080000000h + je possible_overflow +return: + mov esp,ebp + pop ebp + ret 4 + +possible_overflow: + mov edx,eax ; save lsw + fld real4 ptr [ebp+8] + fild qword ptr $MAXLONG + fcompp + fnstsw ax + test ah,4 + jne short return_zero + test ah,65 + je short check_MINLONG + +return_MAXLONG: + mov eax, 0ffffffffh + mov edx, 07fffffffh + jmp short return + +return_zero: + xor eax, eax + xor edx, edx + jmp short return + +check_MINLONG: + fld real4 ptr [ebp+8] + fild qword ptr $MINLONG + fcompp + fnstsw ax + test ah,1 + jne short return_original + +return_MINLONG: + xor edx, edx ; zero lsw + +return_original: + mov eax, edx ; restore lsw to eax + mov edx, 080000000h + jmp short return + +?g_floatToLong@Class_System_VTable@@SI_JM@Z endp + +; +; int System.VTable.checkedDoubleToInt(double) +; + +align 16 +?g_checkedDoubleToInt@Class_System_VTable@@SIHN@Z proc + push ebp + mov ebp,esp + add esp,-8 + fld real8 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + mov ax,word ptr [ebp-2] + or eax,0C00h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + fistp dword ptr [ebp-8] + fldcw word ptr [ebp-2] + mov eax,dword ptr [ebp-8] + + cmp eax,080000000h + je possible_overflow +return: + mov esp,ebp + pop ebp + ret 8 + +possible_overflow: + fld real8 ptr [ebp+8] + fcomp real8 ptr $DBLMAXINT + fnstsw ax + test ah,4 ; test for unordered + jne short throw_exception + test ah,1 ; test for <$DBLMAXINT + jne short return_MININT + ; src > $DBLMAXINT + ; throw an overflow exception + jmp short throw_exception + +return_MININT: + ; check against $DBLMININT + fld real8 ptr [ebp+8] + fcomp real8 ptr $DBLMININT + fnstsw ax + test ah, 1 ; test for < $DBLMININT + jne short throw_exception ; throw exception if true + mov eax, 080000000h + jmp short return + +throw_exception: + ; throw an overflow exception + ; set up stack frame so that it looks like a call to throwNewOverflowException + ; from the caller of this function. + mov esp,ebp + pop ebp + pop eax ; grab return address + add esp, 4 ; move esp pass the first parameter. + mov [esp],eax ; overwrite argument + jmp ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ + +?g_checkedDoubleToInt@Class_System_VTable@@SIHN@Z endp + +; +; long System.VTable.checkedDoubleToLong(double) +; + +align 16 +?g_checkedDoubleToLong@Class_System_VTable@@SI_JN@Z proc + push ebp + mov ebp,esp + add esp,-12 + fld real8 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + mov ax,word ptr [ebp-2] + or eax,0C00h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + fistp qword ptr [ebp-12] + fldcw word ptr [ebp-2] + mov eax,dword ptr [ebp-12] + mov edx,dword ptr [ebp-8] + + cmp edx,080000000h + je possible_overflow +return: + mov esp,ebp + pop ebp + ret 8 + +possible_overflow: + mov edx,eax ; save lsw + fld real8 ptr [ebp+8] + fild qword ptr $MAXLONG + fcompp + fnstsw ax + test ah,4 ; test for unordered + jne short return_zero + test ah,65 ; test for <= $MAXLONG + je short check_MINLONG + +return_MAXLONG: + ; src > $MAXLONG + ; throw an exception + jmp short throw_exception + +return_zero: + jmp short throw_exception + +check_MINLONG: + ; src <= $MINLONG + fild qword ptr $MINLONG + fld real8 ptr [ebp+8] + fcompp ; real8 ptr [ebp+8] < $MINLONG + fnstsw ax + test ah,1 + jne short throw_exception + +return_MINLONG: + mov eax, edx ; restore lsw to eax + mov edx, 080000000h + jmp short return + + +throw_exception: + ; throw an overflow exception + ; set up stack frame so that it looks like a call to throwNewOverflowException + ; from the caller of this function. + mov esp,ebp + pop ebp + pop eax ; grab return address + add esp, 4 ; move esp pass the first parameter. + mov [esp],eax ; overwrite argument + jmp ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ + +?g_checkedDoubleToLong@Class_System_VTable@@SI_JN@Z endp + +; +; int System.VTable.checkedFloatToInt(float) +; + +align 16 +?g_checkedFloatToInt@Class_System_VTable@@SIHM@Z proc + push ebp + mov ebp,esp + add esp,-8 + fld real4 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + xor eax,eax + mov ax,word ptr [ebp-2] + or eax,0C00h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + fistp dword ptr [ebp-8] + fldcw word ptr [ebp-2] + mov eax,dword ptr [ebp-8] + + cmp eax,080000000h + je possible_overflow + +return: + mov esp,ebp + pop ebp + ret 4 + +possible_overflow: + fld real4 ptr [ebp+8] + fcomp real8 ptr $DBLMAXINT + fnstsw ax + test ah,4 ; test for unordered + jne short throw_exception + test ah,1 ; test for src < $DBLMAXINT + jne short return_MININT + ; src > $DBLMAXINT + ; throw an overflow exception + jmp short throw_exception + +return_MININT: + ; need to check against $DBLMININT, if it is less than, + ; then throw an overflow exception + fld real4 ptr [ebp+8] + fcomp real8 ptr $DBLMININT + fnstsw ax + test ah,1 ; test for less than + jne short throw_exception + mov eax, 080000000h + jmp short return + +throw_exception: + ; throw an overflow exception + ; set up stack frame so that it looks like a call to throwNewOverflowException + ; from the caller of this function. + mov esp,ebp + pop ebp + pop eax ; grab return address + mov [esp],eax ; overwrite argument + jmp ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ + +?g_checkedFloatToInt@Class_System_VTable@@SIHM@Z endp + +; +; long System.VTable.checkedFloatToLong(float) +; + +align 16 +?g_checkedFloatToLong@Class_System_VTable@@SI_JM@Z proc + push ebp + mov ebp,esp + add esp,-12 + fld real4 ptr [ebp+8] + wait + fnstcw word ptr [ebp-2] + wait + mov ax,word ptr [ebp-2] + or eax,0C00h + mov word ptr [ebp-4],ax + fldcw word ptr [ebp-4] + fistp qword ptr [ebp-12] + fldcw word ptr [ebp-2] + mov eax,dword ptr [ebp-12] + mov edx,dword ptr [ebp-8] + + cmp edx,080000000h + je possible_overflow +return: + mov esp,ebp + pop ebp + ret 4 + +possible_overflow: + mov edx,eax ; save lsw + fld real4 ptr [ebp+8] + fild qword ptr $MAXLONG + fcompp + fnstsw ax + test ah,4 ; test for unordered + jne short return_zero + test ah,65 ; test for <= $MAXLONG + je short check_MINLONG + +return_MAXLONG: + ; src > $MAXLONG + ; throw an exception + jmp short throw_exception + +return_zero: + ; compare with $MAXLONG results in unordered + ; throw an overflow exception + jmp short throw_exception + +check_MINLONG: + ; src <= $MINLONG + fild qword ptr $MINLONG + fld real4 ptr [ebp+8] + fcompp ; real8 ptr [ebp+8] < $MINLONG + fnstsw ax + test ah,1 + jne short throw_exception ; throw an overflow exception when src < $MINLONG +return_MINLONG: + mov eax, edx ; restore lsw + mov edx, 080000000h + jmp short return + +throw_exception: + ; throw an overflow exception + ; set up stack frame so that it looks like a call to throwNewOverflowException + ; from the caller of this function. + mov esp,ebp + pop ebp + pop eax ; grab return address + mov [esp],eax ; overwrite argument + jmp ?g_throwNewOverflowException@Class_System_VTable@@SIXXZ + +?g_checkedFloatToLong@Class_System_VTable@@SI_JM@Z endp + +align 16 +?g_floatRem@Class_System_VTable@@SIMMM@Z proc + fld real4 ptr [esp+8] + fld real4 ptr [esp+4] +fremloop: + fprem + fstsw ax + fwait + sahf + jp fremloop ; Continue while the FPU status bit C2 is set + ffree st(1) + ret 8 +?g_floatRem@Class_System_VTable@@SIMMM@Z endp + +align 16 +?g_doubleRem@Class_System_VTable@@SINNN@Z proc + fld real8 ptr [esp+12] + fld real8 ptr [esp+4] +fremloop: + fprem + fstsw ax + fwait + sahf + jp fremloop ; Continue while the FPU status bit C2 is set + ffree st(1) + ret 16 +?g_doubleRem@Class_System_VTable@@SINNN@Z endp + + +if EXCLUDED +; +; void __checkFPStackDepth0 +; + +align 16 +__checkFPStackDepth0 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,0 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth0 endp + +; +; void __checkFPStackDepth1 +; + +align 16 +__checkFPStackDepth1 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-1 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth1 endp + +; +; void __checkFPStackDepth2 +; + +align 16 +__checkFPStackDepth2 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-2 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth2 endp + +; +; void __checkFPStackDepth3 +; + +align 16 +__checkFPStackDepth3 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-3 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth3 endp + +; +; void __checkFPStackDepth4 +; + +align 16 +__checkFPStackDepth4 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-4 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth4 endp + +; +; void __checkFPStackDepth5 +; + +align 16 +__checkFPStackDepth5 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-5 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth5 endp + +; +; void __checkFPStackDepth6 +; + +align 16 +__checkFPStackDepth6 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-6 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth6 endp + +; +; void __checkFPStackDepth7 +; + +align 16 +__checkFPStackDepth7 proc + push ebp + mov ebp,esp + + push eax + pushfd + + xor eax,eax + + wait + fnstsw ax + wait + + shr eax,11 + and eax,7 + cmp eax,8-7 + je ok +oops: + int 3 +ok: + + popfd + pop eax + + pop ebp + ret 0 +__checkFPStackDepth7 endp +endif ; EXCLUDED + +;;; +;;; +;;; +;;; + +ifdef SINGULARITY +?g_ZeroPages@Class_System_Buffer@@SIXPAEH@Z proc + ;; ECX = dst + ;; EDX = len (bytes) + + push ebp + mov ebp, esp + pxor mm0, mm0 +next: + movntq [ecx + 0], mm0 + movntq [ecx + 8], mm0 + movntq [ecx + 16], mm0 + movntq [ecx + 24], mm0 + movntq [ecx + 32], mm0 + movntq [ecx + 40], mm0 + movntq [ecx + 48], mm0 + movntq [ecx + 56], mm0 + add ecx, 64 + sub edx, 64 + ja next + + sfence + emms + pop ebp + ret +?g_ZeroPages@Class_System_Buffer@@SIXPAEH@Z endp + +?g_CopyPages@Class_System_Buffer@@SIXPAE0H@Z proc + ;; ECX = dst + ;; EDX = src + ;; [ebp+8] len (bytes) + + push ebp + mov ebp, esp + mov eax, [ebp + 8] + + cmp ecx, edx + js down + + ;; destination is lower than source + add ecx, eax + add edx, eax + sub ecx, 64 + sub edx, 64 + +up: + movq mm0, [edx + 0] + movq mm1, [edx + 8] + movq mm2, [edx + 16] + movq mm3, [edx + 24] + movq mm4, [edx + 32] + movq mm5, [edx + 40] + movq mm6, [edx + 48] + movq mm7, [edx + 56] + movntq [ecx + 0], mm0 + movntq [ecx + 8], mm1 + movntq [ecx + 16], mm2 + movntq [ecx + 24], mm3 + movntq [ecx + 32], mm4 + movntq [ecx + 40], mm5 + movntq [ecx + 48], mm6 + movntq [ecx + 56], mm7 + sub ecx, 64 + sub edx, 64 + sub eax, 64 + ja up + + sfence + emms + pop ebp + ret 4 + + ;; destination is higher than source +down: + movq mm0, [edx + 0] + movq mm1, [edx + 8] + movq mm2, [edx + 16] + movq mm3, [edx + 24] + movq mm4, [edx + 32] + movq mm5, [edx + 40] + movq mm6, [edx + 48] + movq mm7, [edx + 56] + movntq [ecx + 0], mm0 + movntq [ecx + 8], mm1 + movntq [ecx + 16], mm2 + movntq [ecx + 24], mm3 + movntq [ecx + 32], mm4 + movntq [ecx + 40], mm5 + movntq [ecx + 48], mm6 + movntq [ecx + 56], mm7 + add ecx, 64 + add edx, 64 + sub eax, 64 + ja down + + sfence + emms + pop ebp + ret 4 +?g_CopyPages@Class_System_Buffer@@SIXPAE0H@Z endp + +endif + +end diff --git a/base/Kernel/Native/ix86/halkdx.cpp b/base/Kernel/Native/ix86/halkdx.cpp new file mode 100644 index 0000000..a9d1553 --- /dev/null +++ b/base/Kernel/Native/ix86/halkdx.cpp @@ -0,0 +1,505 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: halkdx86.cpp - Processor-specific support routines for halkd.cpp. +// +#include "hal.h" +#include "halkd.h" + +#define KDDBG if (0) kdprintf + +extern "C" void * __cdecl memcpy(void *, const void *, size_t); + +//////////////////////////////////////////////// User visible progress beacon. +// +static UINT16 KdpSpinBase = 0x2f00; +void KdpSpin() +{ + if (KdpSpinBase != 0) { + static UINT8 state = 0; + + // Write the spinner to the screen. + *((UINT16 *)0xb809e) = KdpSpinBase + ("+-|*" [state++ & 0x3]); + } +} + +///////////////////////////////////////// Debugger Unique Interrupts Routines. +// +// NB: Without these, we share routines with the mainline code and we get +// caught in a loop when the debugger inserts a break after the pushfd when +// someone tries to single step through Processor:g_DisableInterrupts! +// +bool __declspec(naked) KdpDisableInterruptsInline() +{ + __asm { + pushfd; + pop eax; + test eax, Struct_Microsoft_Singularity_Isal_IX_EFlags_IF; + setnz al; + nop; // required so that the linker doesn't combine with g_Disable + cli; + ret; + } +} + +void __declspec(naked) KdpRestoreInterruptsInline(bool enabled) +{ + __asm { + nop; + test cl, cl; + je done; + nop; // required so that the linker doesn't combine with g_Restore + sti; + done: + ret; + } +} + +// Pause processor in deference to other hardware threads on the same core. +void __declspec(naked) KdpPause() +{ + __asm { + pause; + ret; + } +} + +// Flush instruction cache. +void __declspec(naked) KdpFlushInstCache() +{ + __asm { + wbinvd; // privileged instruction + ret; + } +} + +// Break into the debugger. +void __declspec(naked) Class_Microsoft_Singularity_DebugStub::g_Break() +{ + __asm { + int 3; + ret; + } +} + +// Read a machine specific register (MSR on x86 & x64). +bool KdpReadMsr(UINT32 msr, UINT32 *plo, UINT32 *phi) +{ + UINT32 lo; + UINT32 hi; + + __asm { + mov ecx, msr; + rdmsr; + mov hi, edx; + mov lo, eax; + } + + *phi = hi; + *plo = lo; + + return true; +} + +// Write a machine specific register (MSR on x86 & x64). +bool KdpWriteMsr(UINT32 msr, UINT32 lo, UINT32 hi) +{ + __asm { + mov ecx, msr; + mov edx, hi; + mov eax, lo; + wrmsr; + } + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// +void KdpToKdContext(IN CONST Struct_Microsoft_Singularity_Isal_SpillContext *singularity, + OUT CONTEXT *windbg) +{ + windbg->ContextFlags = (CONTEXT_CONTROL | + CONTEXT_INTEGER | + CONTEXT_SEGMENTS | + CONTEXT_FLOATING_POINT | + CONTEXT_EXTENDED_REGISTERS); + + // CONTEXT_FULL; + windbg->Eax = singularity->ax; + windbg->Ebx = singularity->bx; + windbg->Ecx = singularity->cx; + windbg->Edx = singularity->dx; + windbg->Esp = singularity->sp; + windbg->Ebp = singularity->bp; + windbg->Esi = singularity->si; + windbg->Edi = singularity->di; + windbg->Eip = singularity->ip; + windbg->EFlags = singularity->fl; + + windbg->FloatSave.ControlWord = singularity->mmx.fcw; + windbg->FloatSave.StatusWord = singularity->mmx.fsw; + windbg->FloatSave.TagWord = singularity->mmx.ftw; + windbg->FloatSave.ErrorOffset = singularity->mmx.ip; + windbg->FloatSave.ErrorSelector = singularity->mmx.cs; + windbg->FloatSave.DataOffset = singularity->mmx.dp; + windbg->FloatSave.DataSelector = singularity->mmx.ds; + + memcpy(&windbg->FloatSave.St0, &singularity->mmx.st0, 10); + memcpy(&windbg->FloatSave.St1, &singularity->mmx.st1, 10); + memcpy(&windbg->FloatSave.St2, &singularity->mmx.st2, 10); + memcpy(&windbg->FloatSave.St3, &singularity->mmx.st3, 10); + memcpy(&windbg->FloatSave.St4, &singularity->mmx.st4, 10); + memcpy(&windbg->FloatSave.St5, &singularity->mmx.st5, 10); + memcpy(&windbg->FloatSave.St6, &singularity->mmx.st6, 10); + memcpy(&windbg->FloatSave.St7, &singularity->mmx.st7, 10); + + memcpy(&windbg->ExtendedRegisters, &singularity->mmx, 512); + + // Segment registers other than cs don't change so they are live + windbg->SegCs = singularity->cs; + __asm { + mov ebx, windbg; + + xor eax, eax; + mov ax, gs; + mov [ebx]CONTEXT.SegGs, eax; + mov ax, fs; + mov [ebx]CONTEXT.SegFs, eax; + mov ax, es; + mov [ebx]CONTEXT.SegEs, eax; + mov ax, ds; + mov [ebx]CONTEXT.SegDs, eax; + mov ax, ss; + mov [ebx]CONTEXT.SegSs, eax; + } +} + +void KdpFromKdContext(IN CONST CONTEXT *windbg, + OUT Struct_Microsoft_Singularity_Isal_SpillContext *singularity) +{ + singularity->ax = windbg->Eax; + singularity->bx = windbg->Ebx; + singularity->cx = windbg->Ecx; + singularity->dx = windbg->Edx; + singularity->sp = windbg->Esp; + singularity->bp = windbg->Ebp; + singularity->si = windbg->Esi; + singularity->di = windbg->Edi; + singularity->ip = windbg->Eip; + singularity->fl = windbg->EFlags; + + // CONTEXT_FLOATING_POINT + if (windbg->ContextFlags & CONTEXT_FLOATING_POINT) { + singularity->mmx.fcw = windbg->FloatSave.ControlWord; + singularity->mmx.fsw = windbg->FloatSave.StatusWord; + singularity->mmx.ftw = windbg->FloatSave.TagWord; + singularity->mmx.cs = windbg->FloatSave.ErrorSelector; + singularity->mmx.ip = windbg->FloatSave.ErrorOffset; + singularity->mmx.ds = windbg->FloatSave.DataSelector; + singularity->mmx.dp = windbg->FloatSave.DataOffset; + memcpy(&singularity->mmx.st0, &windbg->FloatSave.St0, 10); + memcpy(&singularity->mmx.st1, &windbg->FloatSave.St1, 10); + memcpy(&singularity->mmx.st2, &windbg->FloatSave.St2, 10); + memcpy(&singularity->mmx.st3, &windbg->FloatSave.St3, 10); + memcpy(&singularity->mmx.st4, &windbg->FloatSave.St4, 10); + memcpy(&singularity->mmx.st5, &windbg->FloatSave.St5, 10); + memcpy(&singularity->mmx.st6, &windbg->FloatSave.St6, 10); + memcpy(&singularity->mmx.st7, &windbg->FloatSave.St7, 10); + } + + // CONTEXT_EXTENDED_REGISTERS + if (windbg->ContextFlags & CONTEXT_EXTENDED_REGISTERS) { + memcpy(&singularity->mmx, &windbg->ExtendedRegisters, 512); + } +} + +void KdpSetControlReport(IN OUT PDBGKD_CONTROL_REPORT report, + IN CONST Struct_Microsoft_Singularity_Isal_SpillContext *x86Context) + // Routine Description: + // Fill in the Wait_State_Change message record. + // + // Arguments: + // WaitStateChange - Supplies pointer to record to fill in + // x86Context - Supplies a pointer to a context record. +{ + UINT32 _dr6; + UINT32 _dr7; + UINT16 _cs; + UINT16 _ds; + UINT16 _es; + UINT16 _fs; + + __asm { + mov eax, dr6; + mov _dr6, eax; + mov eax, dr7; + mov _dr7, eax; + + mov ax, cs; + mov _cs, ax; + mov ax, ds; + mov _ds, ax; + mov ax, es; + mov _es, ax; + mov ax, fs; + mov _fs, ax; + } + + report->Dr6 = _dr6; + report->Dr7 = _dr7; + report->SegCs = _cs; + report->SegDs = _ds; + report->SegEs = _es; + report->SegFs = _fs; + report->EFlags = x86Context->fl; + report->ReportFlags = X86_REPORT_INCLUDES_SEGS; + +#if !PAGING + // Let the debugger know so that it doesn't have to retrieve the CS descriptor. + report->ReportFlags |= X86_REPORT_STANDARD_CS; +#endif +} + +void KdpSetControlSet(IN CONST DBGKD_CONTROL_SET * control, + IN OUT Struct_Microsoft_Singularity_Isal_SpillContext *x86Context) +{ + if (control->TraceFlag) { + //KDDBG("KD: Warning - trace flag set prev efl=%x\n",x86Context->efl); + x86Context->fl |= Struct_Microsoft_Singularity_Isal_IX_EFlags_TF; + } + else { + //KDDBG("KD: turning off tracing in efl\n"); + x86Context->fl &= ~Struct_Microsoft_Singularity_Isal_IX_EFlags_TF; + } + + UINT32 _dr7 = control->Dr7; + + __asm { + mov eax, _dr7; + mov dr7, eax + } +} + +////////////////////////////////////////////////////////////////////////////// + +void KdpReadSpecialRegisters(KSPECIAL_REGISTERS *pksp, + IN CONST Struct_Microsoft_Singularity_Isal_SpillContext *x86Context) +{ + Struct_Microsoft_Singularity_Kd_X86Descriptor idtp; + Struct_Microsoft_Singularity_Kd_X86Descriptor gdtp; + + __asm { + mov ebx, pksp; + + sidt idtp.Limit; + sgdt gdtp.Limit; + + mov eax, cr0; + mov [ebx]KSPECIAL_REGISTERS.Cr0, eax; + mov eax, cr2; + mov [ebx]KSPECIAL_REGISTERS.Cr2, eax; + mov eax, cr3; + mov [ebx]KSPECIAL_REGISTERS.Cr3, eax; + _emit 0x0f; // mov eax,cr4 + _emit 0x20; + _emit 0xe0; + mov [ebx]KSPECIAL_REGISTERS.Cr4, eax; + + // Should we save segment regs as well? + str ax; + mov [ebx]KSPECIAL_REGISTERS.Tr, ax; + } + + pksp->Idtr = idtp; + pksp->Gdtr = gdtp; +} + +void KdpWriteSpecialRegisters(CONST KSPECIAL_REGISTERS *pksp) +{ + __asm { + mov ebx, pksp; + + mov eax, [ebx]KSPECIAL_REGISTERS.KernelDr0; + mov dr0, eax; + mov eax, [ebx]KSPECIAL_REGISTERS.KernelDr1; + mov dr1, eax; + mov eax, [ebx]KSPECIAL_REGISTERS.KernelDr2; + mov dr2, eax; + mov eax, [ebx]KSPECIAL_REGISTERS.KernelDr3; + mov dr3, eax; + mov eax, [ebx]KSPECIAL_REGISTERS.KernelDr6; + mov dr6, eax; + mov eax, [ebx]KSPECIAL_REGISTERS.KernelDr7; + mov dr7, eax; + } +} + +////////////////////////////////////////////////////////////////////////////// + +KdDebugTrapData * KdpIsDebugTrap(IN CONST Struct_Microsoft_Singularity_Isal_SpillContext *context, + int id) +{ + if (id == Struct_Microsoft_Singularity_Isal_IX_EVectors_FirstChanceException) { + return (KdDebugTrapData *)(context->ax); + } + return NULL; +} + +// Convert a trap into a exception record. +void KdpConvertTrapToException(IN OUT EXCEPTION_RECORD64 *per, + IN OUT Struct_Microsoft_Singularity_Isal_SpillContext *context, + int id) +{ + // Breakpoints: + switch (id) { + + case Struct_Microsoft_Singularity_Isal_IX_EVectors_SingleStep: + KDDBG("SingleStep\n"); + per->ExceptionCode = STATUS_SINGLE_STEP; + per->ExceptionAddress = SIGN_EXTEND(context->ip); + // context->efl &= ~Struct_Microsoft_Singularity_Isal_IX_EFlags_TF; + break; + + case Struct_Microsoft_Singularity_Isal_IX_EVectors_Breakpoint: + KDDBG("Breakpoint\n"); + context->ip -= 1; + per->ExceptionCode = STATUS_BREAKPOINT; + per->NumberParameters = 1; + per->ExceptionInformation0 = BREAKPOINT_BREAK; + per->ExceptionAddress = SIGN_EXTEND(context->ip); + break; + + case Struct_Microsoft_Singularity_Isal_IX_EVectors_IllegalInstruction: + KDDBG("Illegal Instruction\n"); + per->ExceptionCode = STATUS_ILLEGAL_INSTRUCTION; + break; + + case Struct_Microsoft_Singularity_Isal_IX_EVectors_PageFault: + KDDBG("KD: 0x0E %d\n", id); + per->ExceptionCode = STATUS_ACCESS_VIOLATION; + per->ExceptionAddress = SIGN_EXTEND(context->ip); + per->NumberParameters = 1; + { + UINT32 _cr2; + __asm { + mov eax, cr2; + mov _cr2, eax; + } + per->ExceptionInformation0 = SIGN_EXTEND(_cr2); + } + break; + + case Struct_Microsoft_Singularity_Isal_IX_EVectors_FirstChanceException: { + KdDebugTrapData *trapData = (KdDebugTrapData *) (context->ax); + switch (trapData->tag) { + case KdDebugTrapData::FIRST_CHANCE_EXCEPTION: + context->ax = trapData->firstChanceException.throwAddr; + KDDBG("KD: First chance C# exception\n"); + // per->ExceptionCode = STATUS_CPP_EH_EXCEPTION; //0xe06d7363; + per->ExceptionCode = STATUS_VCPP_EXCEPTION; //0x8000ff1f; + per->ExceptionAddress = SIGN_EXTEND(context->ip); + per->NumberParameters = 1; + per->ExceptionInformation0 = BREAKPOINT_BREAK; + break; + default: + KDDBG("KD: Unexpected interrupt %d\n", id); + per->ExceptionCode = 0x80000000 + id; + per->ExceptionAddress = SIGN_EXTEND(context->ip); + break; + } + break; + } + + case Struct_Microsoft_Singularity_Isal_IX_EVectors_SecondChanceException: + KDDBG("KD: Second chance C# exception\n"); + per->ExceptionCode = STATUS_VCPP_EXCEPTION; + per->ExceptionAddress = SIGN_EXTEND(context->ip); + break; + + case Struct_Microsoft_Singularity_Isal_IX_EVectors_DebuggerBreakRequest: + KDDBG("KD: Debugger ctrl-break\n"); + per->ExceptionCode = STATUS_BREAKPOINT; + per->ExceptionInformation0 = BREAKPOINT_BREAK; + per->ExceptionAddress = SIGN_EXTEND(context->ip); + break; + + case Struct_Microsoft_Singularity_Isal_IX_EVectors_Nmi: + KDDBG("KD: NMI exception\n"); + per->ExceptionCode = STATUS_UNHANDLED_EXCEPTION; + per->ExceptionInformation0 = BREAKPOINT_BREAK; + per->ExceptionAddress = SIGN_EXTEND(context->ip); + break; + + default: + KDDBG("KD: Unexpected interrupt %d\n", id); + per->ExceptionCode = 0x80000000 + id; + per->ExceptionAddress = SIGN_EXTEND(context->ip); + break; + + } + + KDDBG("Trap: Context at %p\n", context); + KDDBG(" CXT=%08x THR=%08x\n", + context, + Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext()->_thread); + KDDBG(" EIP=%08x EFL=%08x\n", + context->ip, context->fl); + KDDBG(" EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n", + context->ax, context->bx, context->cx, context->dx); + KDDBG(" ESP=%08x EBP=%08x ESI=%08x EDI=%08x\n", + context->sp, context->bp, context->si, context->di); +} + +// +// Read or Write I/O Space. +// +// Return: +// +// iowrite == 0: value read from port +// +// iowrite !=0: zero +// +int KdpReadWriteIoSpace( + int size, // 1, 2, 4 + int iowrite, // true if write, false if read + unsigned short addr, + unsigned int value + ) +{ + unsigned int retValue = 0; + + if (iowrite != 0) { + // I/O Write's + + if (size == 1) { + unsigned char byteValue = (unsigned char)value; + __outbyte(addr, value); + } + else if (size == 2) { + unsigned short wordValue = (unsigned short)value; + __outword(addr, value); + } + else if (size == 4) { + __outdword(addr, value); + } + } + else { + // I/O Read's + + if (size == 1) { + retValue = __inbyte(addr); + } + else if (size == 2) { + retValue = __inword(addr); + } + else if (size == 4) { + retValue = __indword(addr); + } + } + + return retValue; +} + diff --git a/base/Kernel/Native/native.h b/base/Kernel/Native/native.h new file mode 100644 index 0000000..f7243ee --- /dev/null +++ b/base/Kernel/Native/native.h @@ -0,0 +1,19 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: native.h +// +// Note: +// + +#include "hal.h" + +#ifdef SINGULARITY_KERNEL + +#include "halkd.h" + +#endif + diff --git a/base/Kernel/Native/ring0_halstack.asm b/base/Kernel/Native/ring0_halstack.asm deleted file mode 100644 index 2ef5dec..0000000 --- a/base/Kernel/Native/ring0_halstack.asm +++ /dev/null @@ -1,180 +0,0 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; Microsoft Research Singularity -;; -;; Copyright (c) Microsoft Corporation. All rights reserved. -;; -;; File: halstack.asm -;; -;; Note: -;; This file contains implementations of the LinkStack and UnlinkStack -;; routines suitable for use by ring-0 code (kernel and ring-0 apps). -;; - -.686p -.mmx -.xmm -.model flat -.code - -ifdef SINGULARITY -assume ds:flat -assume es:flat -assume ss:flat -assume fs:nothing -assume gs:nothing -else ; Singularity -PAGE_BITS EQU 12 -endif ; Singularity - -include hal.inc - - align 16 - -;; Public symbols -public ?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ -public ?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ - -;;; Stack: -;;; arg2.. (args 0 & 1 = ECX & EDX) + 44 -;;; caller (addr) + 40 -;;; caller ebp + 36 <= old EBP -;;; callee (addr) + 32 <= old ESP -;;; argsize + 28 -;;; unlinkN + 24 -;;; efl + 20 -;;; eax + 16 -;;; ecx + 12 -;;; edx + 8 -;;; stackLimit + 4 -;;; stackBegin + 0 -?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ :: - - ;; Disable interrupts - pushfd ; +20 - cli - push eax ; +16 - push ecx ; +12 - push edx ; +8 - - mov edx,fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._threadContext] - push [edx].Struct_Microsoft_Singularity_X86_ThreadContext._stackLimit ; +4 - push [edx].Struct_Microsoft_Singularity_X86_ThreadContext._stackBegin ; +0 - - mov eax,esp - - ;; Change to schedulerStack - mov ecx,fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._schedulerStackLimit] - mov [edx].Struct_Microsoft_Singularity_X86_ThreadContext._stackLimit,ecx - mov ecx,fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._schedulerStackBegin] - mov [edx].Struct_Microsoft_Singularity_X86_ThreadContext._stackBegin,ecx - -ifdef DEBUG - ;; If we're already on the scheduler stack, we're in trouble, - ;; so trap to the debugger: - cmp eax, [edx].Struct_Microsoft_Singularity_X86_ThreadContext._stackBegin - jg stackOk - cmp eax, [edx].Struct_Microsoft_Singularity_X86_ThreadContext._stackLimit - jl stackOk - sti - int 3 -stackOk: -endif ;; DEBUG - - mov esp,ecx - - ;; Save the old ESP (and create a temporary frame head). - lea ecx,[eax+32] - push ecx - push [eax+4] ; stackLimit on old stack - push [eax+0] ; stackBegin on old stack - - ;; Call GetStackSegmentAndCopy(ecx=stack needed, edx=threadContext, *args, args, - ;; esp, begin, limit) - push ecx ; old esp - push [eax+28] ; #args - lea ecx,[eax+44] ; &arg2 - push ecx ; - mov ecx,[eax+16] ; stack size needed - - call ?g_GetStackSegmentAndCopy@Class_Microsoft_Singularity_Memory_Stacks@@SIPAUuintPtr@@PAU2@PAUStruct_Microsoft_Singularity_X86_ThreadContext@@PAII000@Z - - ;; Get back to old ESP, then adjust to pop off EDX and ECX - pop ecx - sub ecx,32 - mov esp,eax - - push [ecx+24] ; unlinkN - push [ecx+20] ; efl - mov eax,[ecx+32] ; callee - mov edx,[ecx+8] ; edx - mov ecx,[ecx+12] ; ecx - - ;; Restore interrupts - popfd - - push ebp ; Create new ebp frame. - mov ebp,esp - jmp eax ; jump to callee code. - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; NB: See halstack.asm for a stack diagram - -?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ:: - - ;; Disable interrupts and remove dead args from old stack - pushfd - cli - - mov esp,ebp ; Find adjusted ebp - add esp,ecx - mov ecx,[ebp+4] ; move eip link up - mov [esp+4],ecx - mov ecx,[ebp+0] ; move ebp link up - mov [esp+0],ecx - mov ebp,esp - - push eax ; save eax to old stack - push edx ; save edx to old stack - - mov ecx,fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._threadContext] - mov eax,[ecx].Struct_Microsoft_Singularity_X86_ThreadContext._stackBegin - mov eax,[eax-16] ; save efl to old stack - push eax - -;;; Stack: -;;; caller (addr) + 16 -;;; caller ebp + 12 -;;; eax + 8 -;;; edx + 4 -;;; efl + 0 - - ;; Prepare scheduler stack segment. - mov eax,esp - mov esp,fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._schedulerStackBegin] - push eax ; save old esp - push [ecx].Struct_Microsoft_Singularity_X86_ThreadContext._stackBegin - push [ecx].Struct_Microsoft_Singularity_X86_ThreadContext._stackLimit - - push [ecx].Struct_Microsoft_Singularity_X86_ThreadContext._stackLimit - mov edx,[ecx].Struct_Microsoft_Singularity_X86_ThreadContext._stackBegin - - ;; Then switch to scheduler stack. - mov eax,fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._schedulerStackLimit] - mov [ecx].Struct_Microsoft_Singularity_X86_ThreadContext._stackLimit,eax - mov eax,fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._schedulerStackBegin] - mov [ecx].Struct_Microsoft_Singularity_X86_ThreadContext._stackBegin,eax - - ;; Call ReturnStackSegmentRaw(ecx=threadContext, edx=begin, limit) - call ?g_ReturnStackSegmentRaw@Class_Microsoft_Singularity_Memory_Stacks@@SIXPAUStruct_Microsoft_Singularity_X86_ThreadContext@@PAUuintPtr@@1@Z - - pop eax ; pop old limit - pop eax ; pop old begin - pop esp ; restore esp - popfd ; restore interrupts - pop edx ; restore eax and edx - pop eax - pop ebp ; pop ebp chain - ret - -end diff --git a/base/Kernel/Native/ring3_halstack.asm b/base/Kernel/Native/ring3_halstack.asm deleted file mode 100644 index 9d12d28..0000000 --- a/base/Kernel/Native/ring3_halstack.asm +++ /dev/null @@ -1,126 +0,0 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; Microsoft Research Singularity -;; -;; Copyright (c) Microsoft Corporation. All rights reserved. -;; -;; File: halstack.asm -;; -;; Note: -;; This file contains implementations of the LinkStack and UnlinkStack -;; routines suitable for use by ring-3 application code. This code is -;; used to satisfy ring-3 applications' import of the LinkStack and -;; UnlinkStack symbols. -;; - -.686p -.mmx -.xmm -.model flat -.code - -assume ds:flat -assume es:flat -assume ss:flat -assume fs:nothing -assume gs:nothing - -include hal.inc - -;;; Public symbols -public ?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ -public ?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ - -;;; Ring-3-specific stack ABIs -externdef ?g_LinkNewStackSegment@Struct_Microsoft_Singularity_V1_Services_StackService@@SIPAUuintPtr@@PAU2@PAII000@Z:NEAR -externdef ?g_ReturnStackSegmentRaw@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXPAUuintPtr@@0@Z:NEAR - - align 16 - -;;; Stack: -;;; arg2.. (args 0 & 1 = ECX & EDX) + 40 -;;; caller (addr) + 36 -;;; caller ebp + 32 <= old EBP -;;; callee (addr) + 28 <= old ESP -;;; argsize + 24 -;;; unlinkN + 20 -;;; eax + 16 -;;; ecx + 12 -;;; edx + 8 -;;; stackLimit + 4 -;;; stackBegin + 0 - -?g_LinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ:: - - push eax ; +16 - push ecx ; +12 - push edx ; +8 - - mov edx,fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._threadContext] - push [edx].Struct_Microsoft_Singularity_X86_ThreadContext._stackLimit ; +4 - push [edx].Struct_Microsoft_Singularity_X86_ThreadContext._stackBegin ; +0 - mov eax,esp - - ;; Save the old ESP (and create a temporary frame head). - lea ecx,[eax+28] - push ecx - - ;; Call LinkNewStackSegment(ecx=stack needed, edx=*args, args, - ;; esp, begin, limit) - push [eax+4] ; stackLimit on old stack - push [eax+0] ; stackBegin on old stack - push ecx ; old esp - push [eax+24] ; #args - lea edx,[eax+40] ; &arg2 - mov ecx,[eax+16] ; stack size needed - - call ?g_LinkNewStackSegment@Struct_Microsoft_Singularity_V1_Services_StackService@@SIPAUuintPtr@@PAU2@PAII000@Z - - ;; Get back to old ESP, then adjust to pop off EDX and ECX - pop ecx - sub ecx,28 - mov esp,eax - - push [ecx+20] ; unlinkN - mov eax,[ecx+28] ; callee - mov edx,[ecx+8] ; edx - mov ecx,[ecx+12] ; ecx - - push ebp ; Create new ebp frame. - mov ebp,esp - jmp eax ; jump to callee code. - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; NB: See halstack.asm for a stack diagram - -?g_UnlinkStack@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXXZ:: - - mov esp,ebp ; Find adjusted ebp - add esp,ecx - mov ecx,[ebp+4] ; move eip link up - mov [esp+4],ecx - mov ecx,[ebp+0] ; move ebp link up - mov [esp+0],ecx - mov ebp,esp - - push eax ; Save eax to old stack - push edx ; save edx to old stack - -;;; Stack: -;;; caller (addr) + 12 -;;; caller ebp + 8 -;;; eax + 4 -;;; edx + 0 - - ;; Call ReturnStackSegmentRaw(ecx=begin, edx=limit) - mov eax,fs:[Struct_Microsoft_Singularity_X86_ProcessorContext._threadContext] - mov edx,[eax].Struct_Microsoft_Singularity_X86_ThreadContext._stackLimit - mov ecx,[eax].Struct_Microsoft_Singularity_X86_ThreadContext._stackBegin - call ?g_ReturnStackSegmentRaw@Struct_Microsoft_Singularity_V1_Services_StackService@@SIXPAUuintPtr@@0@Z - - pop edx ; restore eax and edx - pop eax - pop ebp ; pop ebp chain - ret - -end diff --git a/base/Kernel/Native/x86/Thread.cpp b/base/Kernel/Native/x86/Thread.cpp new file mode 100644 index 0000000..d5771b7 --- /dev/null +++ b/base/Kernel/Native/x86/Thread.cpp @@ -0,0 +1,64 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Thread.cpp +// +// Note: +// + +#include "hal.h" + +////////////////////////////////////////////////////////////// Thread Context. +// +void +Struct_Microsoft_Singularity_ThreadContext:: +m_UpdateAfterGC(Struct_Microsoft_Singularity_ThreadContext * self, + Class_System_Threading_Thread *thread) +{ + self->_thread = thread; +} + +Class_System_Threading_Thread * +Struct_Microsoft_Singularity_ThreadContext:: +m_GetThread(Struct_Microsoft_Singularity_ThreadContext * self) +{ + return (Class_System_Threading_Thread *) self->_thread; +} + +#if SINGULARITY_KERNEL +void +Struct_Microsoft_Singularity_ThreadContext:: +m_Initialize(Struct_Microsoft_Singularity_ThreadContext * self, + int threadIndex, + UIntPtr stackBegin, + uint32 cr3) +{ + Struct_Microsoft_Singularity_Isa_SpillContext::m_Initialize(&self->threadRecord.spill, + stackBegin, + (UIntPtr) self->stackLimit, + (UIntPtr)Class_System_Threading_Thread::g_ThreadStub, + threadIndex, + (UIntPtr) cr3); +} + +void +Struct_Microsoft_Singularity_ThreadContext:: +m_InitializeIdle(Struct_Microsoft_Singularity_ThreadContext * self, + int threadIndex, + UIntPtr stackBegin, + uint32 cr3) +{ + Struct_Microsoft_Singularity_Isa_SpillContext::m_Initialize(&self->threadRecord.spill, + stackBegin, + (UIntPtr) self->stackLimit, + (UIntPtr)Class_System_Threading_Thread::g_DispatcherThreadStub, + threadIndex, + (UIntPtr) cr3); +} +#endif // SINGULARITY_KERNEL + +// +///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/RuntimeNative.Common.targets b/base/Kernel/RuntimeNative.Common.targets index 3a688bc..55b9cf8 100644 --- a/base/Kernel/RuntimeNative.Common.targets +++ b/base/Kernel/RuntimeNative.Common.targets @@ -1,10 +1,16 @@ - + - + - + $(AFLAGS) $(CFLAGS) /Gr /I$(SINGULARITY_ROOT)\boot\include -UDEBUG @@ -15,7 +21,15 @@ $(CFLAGS) $(GC_ML_DEFS) /I$(OutputPath) /I%(rootdir)%(directory) $(AFLAGS) $(GC_ML_DEFS) /I$(OutputPath) /INative - + + + + + $(CFLAGS) /Gr + + + + @@ -32,21 +46,6 @@ - - - - - - - - - - - - - - - diff --git a/base/Kernel/RuntimeNative.Kernel.proj b/base/Kernel/RuntimeNative.Kernel.proj index 04d85b7..0ab2e88 100644 --- a/base/Kernel/RuntimeNative.Kernel.proj +++ b/base/Kernel/RuntimeNative.Kernel.proj @@ -1,3 +1,11 @@ + + @@ -16,11 +24,8 @@ - - - - + diff --git a/base/Kernel/SingSharp.Runtime/AssemblyInfo.cs b/base/Kernel/SingSharp.Runtime/AssemblyInfo.cs index f4fe50b..6cbd7db 100644 --- a/base/Kernel/SingSharp.Runtime/AssemblyInfo.cs +++ b/base/Kernel/SingSharp.Runtime/AssemblyInfo.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; using System.Reflection; diff --git a/base/Kernel/SingSharp.Runtime/Attributes.cs b/base/Kernel/SingSharp.Runtime/Attributes.cs index ef93057..059cd84 100644 --- a/base/Kernel/SingSharp.Runtime/Attributes.cs +++ b/base/Kernel/SingSharp.Runtime/Attributes.cs @@ -1,11 +1,3 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -/////////////////////////////////////////////////////////////////////////////// - using System; using System.Collections; @@ -231,7 +223,7 @@ namespace System.Compiler.TypeExtensions { namespace System.Compiler { /// /// Used to mark Template classes so Bartok can ignore them. - /// If the name or namespace changes, we need to update Bartok. + /// If the name or namespace changes, we need to update bartok. /// public interface ITemplate { } diff --git a/base/Kernel/SingSharp.Runtime/Bitter.sg b/base/Kernel/SingSharp.Runtime/Bitter.sg index a0d30dd..296fe6c 100644 --- a/base/Kernel/SingSharp.Runtime/Bitter.sg +++ b/base/Kernel/SingSharp.Runtime/Bitter.sg @@ -82,9 +82,8 @@ namespace Microsoft.Singularity int length = buffer.Length; fixed (byte *pdst = &retval[0]) { - fixed (byte *psrc = &buffer[0]) { - Buffer.MoveMemory(pdst, psrc, length); - } + byte *psrc = &buffer[0]; + Buffer.MoveMemory(pdst, psrc, length); } return retval; } @@ -96,9 +95,8 @@ namespace Microsoft.Singularity BoundsCheck(array, aoffset, length); fixed (byte *pdst = &array[aoffset]) { - fixed (byte *psrc = &buffer[offset]) { - Buffer.MoveMemory(pdst, psrc, length); - } + byte *psrc = &buffer[offset]; + Buffer.MoveMemory(pdst, psrc, length); } } @@ -112,79 +110,68 @@ namespace Microsoft.Singularity throw new ArgumentOutOfRangeException("ArgumentOutOfRange_Index"); } - fixed (byte * src = &buffer[offset]) { - memory.Write8(aoffset, src, length); - } + byte * src = &buffer[offset]; + memory.Write8(aoffset, src, length); } public static unsafe char ToChar(byte[]! in ExHeap buffer, int offset) { BoundsCheck(buffer, offset, sizeof(char)); - fixed (byte *ptr = &buffer[offset]) { - return *((char *)ptr); - } + byte *ptr = &buffer[offset]; + return *((char *)ptr); } public static unsafe short ToInt16(byte[]! in ExHeap buffer, int offset) { BoundsCheck(buffer, offset, sizeof(short)); - fixed (byte *ptr = &buffer[offset]) { - return *((short *)ptr); - } + byte *ptr = &buffer[offset]; + return *((short *)ptr); } public static unsafe int ToInt32(byte[]! in ExHeap buffer, int offset) { BoundsCheck(buffer, offset, sizeof(int)); - fixed (byte *ptr = &buffer[offset]) { - return *((int *)ptr); - } + byte *ptr = &buffer[offset]; + return *((int *)ptr); } public static unsafe long ToInt64(byte[]! in ExHeap buffer, int offset) { BoundsCheck(buffer, offset, sizeof(long)); - fixed (byte *ptr = &buffer[offset]) { - return *((long *)ptr); - } + byte *ptr = &buffer[offset]; + return *((long *)ptr); } public static unsafe ushort ToUInt16(byte[]! in ExHeap buffer, int offset) { BoundsCheck(buffer, offset, sizeof(ushort)); - fixed (byte *ptr = &buffer[offset]) { - return *((ushort *)ptr); - } + byte *ptr = &buffer[offset]; + return *((ushort *)ptr); } public static unsafe uint ToUInt32(byte[]! in ExHeap buffer, int offset) { BoundsCheck(buffer, offset, sizeof(uint)); - fixed (byte *ptr = &buffer[offset]) { - return *((uint *)ptr); - } + byte *ptr = &buffer[offset]; + return *((uint *)ptr); } public static unsafe ulong ToUInt64(byte[]! in ExHeap buffer, int offset) { BoundsCheck(buffer, offset, sizeof(ulong)); - fixed (byte *ptr = &buffer[offset]) { - return *((ulong *)ptr); - } + byte *ptr = &buffer[offset]; + return *((ulong *)ptr); } public static unsafe float ToSingle(byte[]! in ExHeap buffer, int offset) { BoundsCheck(buffer, offset, sizeof(float)); - fixed (byte *ptr = &buffer[offset]) { - return *((float *)ptr); - } + byte *ptr = &buffer[offset]; + return *((float *)ptr); } public static unsafe double ToDouble(byte[]! in ExHeap buffer, int offset) { BoundsCheck(buffer, offset, sizeof(double)); - fixed (byte *ptr = &buffer[offset]) { - return *((double *)ptr); - } + byte *ptr = &buffer[offset]; + return *((double *)ptr); } public static unsafe bool ToBoolean(byte[]! in ExHeap buffer, int offset) { BoundsCheck(buffer, offset, sizeof(int)); - fixed (byte *ptr = &buffer[offset]) { - return *((int *)ptr) == 0 ? false : true; - } + byte *ptr = &buffer[offset]; + return *((int *)ptr) == 0 ? false : true; } // Converts a character vector to a managed string @@ -201,18 +188,18 @@ namespace Microsoft.Singularity public static unsafe string! ToString2(char[]! in ExHeap chars, int offset, int length) { - if ((length == 0) || (chars.Length == 0)) - { return (!)String.Empty; } // happens before we have the String contract + if ((length == 0) || (chars.Length == 0)) { + return (!)String.Empty; // happens before we have the String contract + } DebugStub.Assert(offset < chars.Length); DebugStub.Assert(offset + length <= chars.Length); - fixed (char *ptr = &chars[0]) { - // unnecessary cast, but because we have to build the - // runtime first before Kernel.Contracts.dll, we don't know - // that StringCTOR always returns non-null value. - return (!)String.StringCTOR(ptr, offset, length); - } + char *ptr = &chars[0]; + // unnecessary cast, but because we have to build the + // runtime first before Kernel.Contracts.dll, we don't know + // that StringCTOR always returns non-null value. + return (!)String.StringCTOR(ptr, offset, length); } public static unsafe string! ToString2(char[]! in ExHeap chars) @@ -232,15 +219,15 @@ namespace Microsoft.Singularity // Converts a character vector to a managed string public static unsafe string! ToString2(byte[]! in ExHeap chars) { - if (chars.Length == 0) - { return (!)String.Empty; } // happens before we have the String contract - - fixed (byte *ptr = &chars[0]) { - // unnecessary cast, but because we have to build the - // runtime first before Kernel.Contracts.dll, we don't know - // that StringCTOR always returns non-null value. - return (!)String.StringCTOR(ptr, 0, chars.Length); + if (chars.Length == 0) { + return (!)String.Empty; // happens before we have the String contract } + + byte *ptr = &chars[0]; + // unnecessary cast, but because we have to build the + // runtime first before Kernel.Contracts.dll, we don't know + // that StringCTOR always returns non-null value. + return (!)String.StringCTOR(ptr, 0, chars.Length); } public static byte[]! in ExHeap FromByteArray(byte[]! buffer) @@ -267,10 +254,9 @@ namespace Microsoft.Singularity BoundsCheck(buffer, offset, length); BoundsCheck(array, aoffset, length); - fixed (byte *pdst = &buffer[offset]) { - fixed (byte *psrc = &array[aoffset]) { - Buffer.MoveMemory(pdst, psrc, length); - } + byte *pdst = &buffer[offset]; + fixed (byte *psrc = &array[aoffset]) { + Buffer.MoveMemory(pdst, psrc, length); } } @@ -284,97 +270,87 @@ namespace Microsoft.Singularity throw new ArgumentOutOfRangeException("ArgumentOutOfRange_Index"); } - fixed (byte * src = &buffer[offset]) { - memory.Read8(aoffset, src, length); - } + byte * src = &buffer[offset]; + memory.Read8(aoffset, src, length); } public static unsafe void FromChar(byte[]! in ExHeap buffer, int offset, char value) { BoundsCheck(buffer, offset, sizeof(char)); - fixed (byte *ptr = &buffer[offset]) { - *((char *)ptr) = value; - } + byte *ptr = &buffer[offset]; + *((char *)ptr) = value; } public static unsafe void FromInt16(byte[]! in ExHeap buffer, int offset, short value) { BoundsCheck(buffer, offset, sizeof(short)); - fixed (byte *ptr = &buffer[offset]) { - *((short *)ptr) = value; - } + byte *ptr = &buffer[offset]; + *((short *)ptr) = value; } public static unsafe void FromInt32(byte[]! in ExHeap buffer, int offset, int value) { BoundsCheck(buffer, offset, sizeof(int)); - fixed (byte *ptr = &buffer[offset]) { - *((int *)ptr) = value; - } + byte *ptr = &buffer[offset]; + *((int *)ptr) = value; } public static unsafe void FromInt64(byte[]! in ExHeap buffer, int offset, long value) { BoundsCheck(buffer, offset, sizeof(long)); - fixed (byte *ptr = &buffer[offset]) { - *((long *)ptr) = value; - } + byte *ptr = &buffer[offset]; + *((long *)ptr) = value; } public static unsafe void FromUInt16(byte[]! in ExHeap buffer, int offset, ushort value) { BoundsCheck(buffer, offset, sizeof(ushort)); - fixed (byte *ptr = &buffer[offset]) { - *((ushort *)ptr) = value; - } + byte *ptr = &buffer[offset]; + *((ushort *)ptr) = value; } public static unsafe void FromUInt32(byte[]! in ExHeap buffer, int offset, uint value) { BoundsCheck(buffer, offset, sizeof(uint)); - fixed (byte *ptr = &buffer[offset]) { - *((uint *)ptr) = value; - } + byte *ptr = &buffer[offset]; + *((uint *)ptr) = value; } public static unsafe void FromUInt64(byte[]! in ExHeap buffer, int offset, ulong value) { BoundsCheck(buffer, offset, sizeof(ulong)); - fixed (byte *ptr = &buffer[offset]) { - *((ulong *)ptr) = value; - } + byte *ptr = &buffer[offset]; + *((ulong *)ptr) = value; } public static unsafe void FromSingle(byte[]! in ExHeap buffer, int offset, float value) { BoundsCheck(buffer, offset, sizeof(float)); - fixed (byte *ptr = &buffer[offset]) { - *((float *)ptr) = value; - } + byte *ptr = &buffer[offset]; + *((float *)ptr) = value; } public static unsafe void FromDouble(byte[]! in ExHeap buffer, int offset, double value) { BoundsCheck(buffer, offset, sizeof(double)); - fixed (byte *ptr = &buffer[offset]) { - *((double *)ptr) = value; - } + byte *ptr = &buffer[offset]; + *((double *)ptr) = value; } public static unsafe void FromBoolean(byte[]! in ExHeap buffer, int offset, bool value) { BoundsCheck(buffer, offset, sizeof(int)); - fixed (byte *ptr = &buffer[offset]) { - *((int *)ptr) = value ? 1 : 0; - } + byte *ptr = &buffer[offset]; + *((int *)ptr) = value ? 1 : 0; } // Converts a managed string to a char vector. public static char[] in ExHeap FromString(string str) { - if (str == null) - { return null; } + if (str == null) { + return null; + } return FromString2(str); } @@ -412,11 +388,9 @@ namespace Microsoft.Singularity BoundsCheck(source, sourceOffset, length); BoundsCheck(dest, destOffset, length); - fixed (byte *pdst = &dest[destOffset]) { - fixed (byte *psrc = &source[sourceOffset]) { - Buffer.MoveMemory(pdst, psrc, length); - } - } + byte *pdst = &dest[destOffset]; + byte *psrc = &source[sourceOffset]; + Buffer.MoveMemory(pdst, psrc, length); } public static unsafe void Copy(char[]! in ExHeap dest, @@ -428,11 +402,9 @@ namespace Microsoft.Singularity BoundsCheck(source, sourceOffset, length); BoundsCheck(dest, destOffset, length); - fixed (char *pdst = &dest[destOffset]) { - fixed (char *psrc = &source[sourceOffset]) { - Buffer.MoveMemory((byte*)pdst, (byte*)psrc, length*sizeof(char)); - } - } + char *pdst = &dest[destOffset]; + char *psrc = &source[sourceOffset]; + Buffer.MoveMemory((byte*)pdst, (byte*)psrc, length*sizeof(char)); } public static unsafe void Zero(byte[]! in ExHeap dest, @@ -440,9 +412,8 @@ namespace Microsoft.Singularity int length) { BoundsCheck(dest, destOffset, length); - fixed (byte *pdst = &dest[destOffset]) { - Buffer.ZeroMemory(pdst, length); - } + byte *pdst = &dest[destOffset]; + Buffer.ZeroMemory(pdst, length); } public static unsafe byte[]! in ExHeap diff --git a/base/Kernel/SingSharp.Runtime/Classes.ssc b/base/Kernel/SingSharp.Runtime/Classes.ssc index 6106770..3899051 100644 --- a/base/Kernel/SingSharp.Runtime/Classes.ssc +++ b/base/Kernel/SingSharp.Runtime/Classes.ssc @@ -1,8 +1,6 @@ using System; using System.Collections; -// Copyright (c) Microsoft Corporation. All rights reserved. - namespace Microsoft.Contracts { /// /// Indicates that all derived classes must override this virtual method since the implementation of the method depends on the runtime type of the "this" object diff --git a/base/Kernel/SingSharp.Runtime/CustomAllocator.sg b/base/Kernel/SingSharp.Runtime/CustomAllocator.sg index c1f04fc..8708d3e 100644 --- a/base/Kernel/SingSharp.Runtime/CustomAllocator.sg +++ b/base/Kernel/SingSharp.Runtime/CustomAllocator.sg @@ -1,11 +1,13 @@ -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // +// ---------------------------------------------------------------------------- using System; -namespace Microsoft.SingSharp { +namespace Microsoft.SingSharp +{ /// /// This is the Base class for all Custom allocators. /// diff --git a/base/Kernel/SingSharp.Runtime/EMap.sg b/base/Kernel/SingSharp.Runtime/EMap.sg index f90e8cb..efd4a98 100644 --- a/base/Kernel/SingSharp.Runtime/EMap.sg +++ b/base/Kernel/SingSharp.Runtime/EMap.sg @@ -14,7 +14,8 @@ using System; using Microsoft.SingSharp; using Microsoft.Contracts; -namespace Microsoft.Singularity.Channels { +namespace Microsoft.Singularity.Channels +{ using Microsoft.Singularity.V1.Threads; @@ -169,6 +170,12 @@ namespace Microsoft.Singularity.Channels { this.listHead.LinkAsNext(ep, data, this); } + public bool IsEmpty { + get { + return this.listHead == this.listHead.next; + } + } + /// /// We have to be careful here. The node in nextScanStart might no longer /// be in the set. @@ -186,6 +193,17 @@ namespace Microsoft.Singularity.Channels { } } + + public int GetCount() { + int count = 0; + Node current = this.listHead.next; + while (current != this.listHead) { + count++; + current = current.next; + } + return count; + } + /// possible set to false when match not possible. Never set to true! public bool HeadMatches(int tag, ref bool possible, ref object setMatch) { @@ -212,12 +230,14 @@ namespace Microsoft.Singularity.Channels { current = current.next; } while (current != scanStart); - if (!setPossible) { possible = false; } + if (!setPossible) { + possible = false; + } return false; } else { // about the map itself - switch(tag) { + switch (tag) { case EMapReceiveTag.Empty: if (this.listHead == null || this.listHead.next == this.listHead) { return true; diff --git a/base/Kernel/SingSharp.Runtime/ESet.sg b/base/Kernel/SingSharp.Runtime/ESet.sg index 217dd75..f1bfeea 100644 --- a/base/Kernel/SingSharp.Runtime/ESet.sg +++ b/base/Kernel/SingSharp.Runtime/ESet.sg @@ -14,7 +14,8 @@ using System; using Microsoft.SingSharp; using Microsoft.Contracts; -namespace Microsoft.Singularity.Channels { +namespace Microsoft.Singularity.Channels +{ using Microsoft.Singularity.V1.Threads; @@ -165,19 +166,22 @@ namespace Microsoft.Singularity.Channels { this.listHead.LinkAsNext(ep, this); } - - public int Count { + public bool IsEmpty { get { - int count = 0; - Node current = this.listHead.next; - while (current != this.listHead) { - count++; - current = current.next; - } - return count; + return this.listHead == this.listHead.next; } } + public int GetCount() { + int count = 0; + Node current = this.listHead.next; + while (current != this.listHead) { + count++; + current = current.next; + } + return count; + } + /// /// We have to be careful here. The node in nextScanStart might no longer /// be in the set. @@ -224,12 +228,14 @@ namespace Microsoft.Singularity.Channels { current = current.next; } while (current != scanStart); - if (!setPossible) { possible = false; } + if (!setPossible) { + possible = false; + } return false; } else { // about the set itself. - switch(tag) { + switch (tag) { case ESetReceiveTag.Empty: if (this.listHead == null || this.listHead.next == this.listHead) { return true; diff --git a/base/Kernel/SingSharp.Runtime/Endpoint.sg b/base/Kernel/SingSharp.Runtime/Endpoint.sg index c9f5829..e0f5b55 100644 --- a/base/Kernel/SingSharp.Runtime/Endpoint.sg +++ b/base/Kernel/SingSharp.Runtime/Endpoint.sg @@ -1,7 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // +// ---------------------------------------------------------------------------- using System; using System.Diagnostics; @@ -12,7 +13,8 @@ using System.Runtime.CompilerServices; using Microsoft.Singularity; using Microsoft.SingSharp; -namespace Microsoft.Singularity.Channels { +namespace Microsoft.Singularity.Channels +{ using Microsoft.Singularity.V1.Threads; using Microsoft.Singularity.V1.Services; @@ -123,7 +125,7 @@ namespace Microsoft.Singularity.Channels { /// public new int ChannelID { [NoHeapAllocation] - get { + get { return EndpointCore.GetChannelID(ref this); } } @@ -133,17 +135,30 @@ namespace Microsoft.Singularity.Channels { /// public new int OwnerProcessID { [NoHeapAllocation] - get { + get { return EndpointCore.GetOwnerProcessID(ref this); } } + unsafe public static void AcceptDelegation(Endpoint*! in ExHeap imp, + Endpoint*! in ExHeap exp, + Endpoint*! in ExHeap ep) + { + EndpointCore.AcceptDelegation((Allocation*)imp, (Allocation*)exp, (Allocation*)ep); + } + + public void EnableDelegation(bool allowMediation) + { + EndpointCore.EnableDelegation(ref this, allowMediation); + } + + /// /// Obtain the principal identifier of the owner. /// public new PrincipalHandle OwnerPrincipalHandle { [NoHeapAllocation] - get { + get { return EndpointCore.GetOwnerPrincipalHandle(ref this); } } @@ -153,7 +168,7 @@ namespace Microsoft.Singularity.Channels { /// public new int PeerProcessID { [NoHeapAllocation] - get { + get { return EndpointCore.GetPeerProcessID(ref this); } } @@ -163,7 +178,7 @@ namespace Microsoft.Singularity.Channels { /// public new PrincipalHandle PeerPrincipalHandle { [NoHeapAllocation] - get { + get { return EndpointCore.GetPeerPrincipalHandle(ref this); } } @@ -178,9 +193,11 @@ namespace Microsoft.Singularity.Channels { if (this.Closed) { throw new ApplicationException("Endpoint already closed"); } +#if MONITORING Monitoring.Log(Monitoring.Provider.Endpoint, (ushort)EndpointEvent.Dispose, 0, (uint)this.ChannelID, 0, 0, 0, 0); +#endif if (!EndpointCore.Dispose(ref this)) { throw new ApplicationException("ChannelService.Dispose returned false"); } @@ -194,19 +211,19 @@ namespace Microsoft.Singularity.Channels { } - unsafe public static SystemType RegisterSystemType(string! name, long lower, long upper, SystemType systemType) + unsafe public static SystemType RegisterSystemType(string! name, long lower, long upper, SystemType systemType) { - char [] typeName = new char[name.Length]; + char [] typeName = new char[name.Length]; if (typeName == null) { - throw new Exception("no more memory!"); + throw new Exception("no more memory!"); } - - for(int i=0; i < name.Length; i++) { - typeName[i] = name[i]; + + for (int i = 0; i < name.Length; i++) { + typeName[i] = name[i]; } fixed (char* start = &typeName[0]) { - return SystemType.Register(start, name.Length, lower, upper, systemType); + return SystemType.Register(start, name.Length, lower, upper, systemType); } } @@ -227,7 +244,22 @@ namespace Microsoft.Singularity.Channels { exp->contractTag = expContractTag; exp->peerContractTag = impContractTag; #endif - EndpointCore.Connect( (Allocation*)imp, (Allocation*)exp); + EndpointCore.Connect( (Allocation*)imp, (Allocation*)exp, null); + } + + unsafe public static void Connect(Endpoint*! in ExHeap imp, + Endpoint*! in ExHeap exp, + Endpoint* in ExHeap ep) + { +#if MONITORING + uint impContractTag = getContractTag(imp); + uint expContractTag = getContractTag(exp); + imp->contractTag = impContractTag; + imp->peerContractTag = expContractTag; + exp->contractTag = expContractTag; + exp->peerContractTag = impContractTag; +#endif + EndpointCore.Connect( (Allocation*)imp, (Allocation*)exp, (Allocation*)ep); } /// @@ -285,7 +317,7 @@ namespace Microsoft.Singularity.Channels { } // Bugfix 167 BY SXIA -- make a consistent statement in previous comments. - // This function (and its twin function below with an extra timeout parameter) + // This function (and its twin function below with an extra timeout parameter) // End Bugfix 167 // is the main interface for receiving on multiple // endpoints and endpoint sets. @@ -318,14 +350,16 @@ namespace Microsoft.Singularity.Channels { { Thread currentThread = (!)Thread.CurrentThread; - try + try // Give back cache (caller should do this, but we do it here for now) // we assume that caller does not use this for any other purpose // true for the generated code, but might not be true for other Select // uses. { +#if MONITORING Monitoring.Log(Monitoring.Provider.Endpoint, (ushort)EndpointEvent.Select); +#endif int numCases = patterns.Length; if (numCases == 0) { setMatch = null; @@ -348,7 +382,7 @@ namespace Microsoft.Singularity.Channels { SyncHandle[] handles = null; bool[] possible = null; - try + try // Give back bool array on all returns { possible = (!)currentThread.PopSelectBools(numCases); @@ -408,12 +442,12 @@ namespace Microsoft.Singularity.Channels { /// /// We compute the index of the row that represents timeout case - /// See Parser.cs: the label for timeout is -2, unsatisfiable is -1 + /// See Parser.cs: the label for timeout is -2, unsatisfiable is -1 /// const int TimeOutIndex = -2; // Bugfix 167 By SXIA -- Add a different version of select here. I keep two versions of select here - // which differs in whether there is an extra timeout parameter. If the only + // which differs in whether there is an extra timeout parameter. If the only // callee of this select is the compilation of switch-receive, then we probably should // delete the older version. // This function is the second version of the main interface for receiving on multiple @@ -447,21 +481,22 @@ namespace Microsoft.Singularity.Channels { // thus the patterns 2nd level array Lengths is the indication how many endpoints are actually used. // - public static int Select(int[][]! patterns, ISelectable[]! endpoints, + public static int Select(int[][]! patterns, ISelectable[]! endpoints, out object setMatch, System.TimeSpan timeOutAfter) { Thread currentThread = (!)Thread.CurrentThread; - try + try // Give back cache (caller should do this, but we do it here for now) // we assume that caller does not use this for any other purpose // true for the generated code, but might not be true for other Select // uses. { - +#if MONITORING Monitoring.Log(Monitoring.Provider.Endpoint, (ushort)EndpointEvent.Select); - +#endif + int numCases = patterns.Length; if (numCases == 0) { setMatch = null; @@ -483,7 +518,7 @@ namespace Microsoft.Singularity.Channels { SyncHandle[] handles = null; bool[] possible = null; - try + try // Give back thread local resources. { possible = (!)currentThread.PopSelectBools(numCases); @@ -498,14 +533,14 @@ namespace Microsoft.Singularity.Channels { setMatch = null; SchedulerTime deadline = (timeOutAfter.Ticks == 0)?SchedulerTime.MinValue:SchedulerTime.Now + timeOutAfter; - + while (numPossible > 0 && ! FindRowMatch(hint, endpoints, patterns, possible, ref numPossible, out match, out setMatch)) { - + // only wait if last FindMatch didn't decrement numPossible to 0. if (numPossible > 0) { @@ -534,13 +569,13 @@ namespace Microsoft.Singularity.Channels { handles[i] = ((!)endpoints[i]).GetWaitHandle(); } } - + hint = WaitAnyEndpoint(handles, numEndpoints, deadline); - + // if the return value is WaitHandle.WaitTimeOut (-1), // then we know it is timeout // currently, we check for all out of range cases. - if (hint <0 || hint > numEndpoints) { + if (hint < 0 || hint > numEndpoints) { setMatch = null; return Endpoint.TimeOutIndex; } @@ -549,8 +584,7 @@ namespace Microsoft.Singularity.Channels { } return match; } - finally - { + finally { // return thread local resources if (possible != null) { currentThread.PushSelectBools(possible); @@ -560,15 +594,14 @@ namespace Microsoft.Singularity.Channels { } } } - finally - { + finally { // return thread local resources - currentThread.PushSelectObjects(endpoints); + currentThread.PushSelectObjects(endpoints); } } // End Bugfix 167 - + /// /// If hint < 0, scan all patterns for a match. Otherwise, hint specifies which /// endpoint changed. Thus only that column has to be reexamined. @@ -593,7 +626,7 @@ namespace Microsoft.Singularity.Channels { return true; } - if ( ! possible[i] ) { + if (!possible[i]) { // i no longer possible possibleCount--; } @@ -606,7 +639,7 @@ namespace Microsoft.Singularity.Channels { else { // hint tells us which column to scan for (int i = 0; i < numRows; i++) { - + if (possible[i]) { int pat = ((!)patterns[i])[hint]; @@ -619,7 +652,7 @@ namespace Microsoft.Singularity.Channels { return true; } } - if ( ! possible[i] ) { + if (!possible[i]) { // i no longer possible possibleCount--; } @@ -644,11 +677,10 @@ namespace Microsoft.Singularity.Channels { setMatch = null; int numEndpoints = conjuncts.Length; - for (int i = 0; i < numEndpoints; i++) - { + for (int i = 0; i < numEndpoints; i++) { int tag = conjuncts[i]; if (tag != 0) { - if (! ((!)endpoints[i]).HeadMatches(tag, ref possible, ref setMatch)) { + if (!((!)endpoints[i]).HeadMatches(tag, ref possible, ref setMatch)) { return false; } } @@ -682,16 +714,16 @@ namespace Microsoft.Singularity.Channels { } } // End Bugfix 167 - - + + /// /// Return a uint tag uniquely identifying the contract /// which endpoint "ep" adheres to. - /// XXX: HACK: austind: this isn't 64-bit clean, nor is the + /// REVIEW: HACK: this isn't 64-bit clean, nor is the /// tag returned particularly stable (it depends on the /// order in which types and handles are allocated). Ideally we /// would use the SystemType's MD5 hash, but we can't access - /// it here; is this correct, MAF? + /// it here; is this correct? /// unsafe private static uint getContractTag(Endpoint*! in ExHeap ep) { @@ -749,33 +781,37 @@ namespace Microsoft.Singularity.Channels { } - new unsafe public void MarshallMessage(byte*! basep, byte*! source, - int*! tagAddress, System.Type! type) + new unsafe public void MarshallMessage(byte*! basep, + byte*! source, + int*! tagAddress, + System.Type! type) + { + + EndpointCore.MarshallMessage(ref this, basep, source, tagAddress, Marshal.StructSize(type)); + } + + new unsafe public void MarshallPointer(byte*! basep, void**! target, System.Type! type, byte* parent, int offset) { // assert type!=null; -#if PAGING - EndpointCore.MarshallMessage(ref this, - basep, source, tagAddress, Marshal.StructSize(type)); -#else - DebugStub.WriteLine("MarshallMessage source offset:{0:x} tagLoc offset:{1:x} type:{2}", - __arglist((uint)source-(uint)basep, - (uint)tagAddress-(uint)basep, - type.FullName)); -#endif - } - - new unsafe public void MarshallPointer(byte*! basep, void**! target, System.Type! type) { - // assert type!=null; if (*target == null) return; -#if PAGING - EndpointCore.MarshallPointer(ref this, - basep, (byte**)target, type.GetSystemType()); -#else - DebugStub.WriteLine("MarshallPointer source offset:{0:x} type:{1}", - __arglist((uint)target-(uint)basep, type.FullName)); +#if false + DebugStub.Print("type is {0} full name is {1}\n", __arglist(type.ToString(), type.FullName)); + unsafe { + EndpointCore ep = this; + DebugStub.Print("base p is {0,8:x} this is {1,8:x}\n", __arglist((uint) basep, (uint) &ep)); + } + if (SystemType.IsSubtype(type.GetSystemType(), typeof(char []).GetSystemType())) { + DebugStub.Print("MarshallPointer: Got endpoint\n"); + DebugStub.Break(); + } + EndpointCore.MarshallEndpoint(ref this, + basep, (byte**)target, type.GetSystemType(), type.ToString(), parent); + return; + } #endif + EndpointCore.MarshallPointer(ref this, + basep, (byte**)target, type.GetSystemType(), parent, offset); } - } } diff --git a/base/Kernel/SingSharp.Runtime/ExHeap.sg b/base/Kernel/SingSharp.Runtime/ExHeap.sg index 49cf801..4808113 100644 --- a/base/Kernel/SingSharp.Runtime/ExHeap.sg +++ b/base/Kernel/SingSharp.Runtime/ExHeap.sg @@ -15,7 +15,8 @@ using System.Runtime.InteropServices; using Microsoft.SingSharp; using Microsoft.Singularity; -namespace Microsoft.Singularity.Channels { +namespace Microsoft.Singularity.Channels +{ using Microsoft.Singularity.V1.Services; using Microsoft.Singularity.V1.Types; @@ -79,6 +80,7 @@ namespace Microsoft.Singularity.Channels { Tracing.Log(Tracing.Debug, "ExHeap.Free: {0:x8}", (UIntPtr)ptr); #endif // SINGULARITY_PROCESS SharedHeapService.Allocation* alloc = (SharedHeapService.Allocation*)ptr; + // DebugStub.WriteLine("ExHeap free ptr {0,8:x}, alloc data {1,8:x}\n", __arglist((uint) ptr, (uint) SharedHeapService.GetData(alloc))); SystemType epType = typeof(Endpoint).GetSystemType(); if (SystemType.IsSubtype(alloc, epType)) { EndpointCore.Free(alloc); @@ -126,7 +128,8 @@ namespace Microsoft.Singularity.Channels { /// /// Specialized entry point for allocating endpoints rather than other exchangeable objects /// - class ChannelHeap : CustomAllocator { + class ChannelHeap : CustomAllocator + { unsafe public static void*! in ExHeap Allocate(Type! type) { diff --git a/base/Kernel/SingSharp.Runtime/ExRef.sg b/base/Kernel/SingSharp.Runtime/ExRef.sg index 366c5ea..b2cd80d 100644 --- a/base/Kernel/SingSharp.Runtime/ExRef.sg +++ b/base/Kernel/SingSharp.Runtime/ExRef.sg @@ -8,6 +8,7 @@ // // Note: File is part of Sing# runtime files and copied into Singularity tree // whenever a new version of Sing# is dropped. +// Coordinate any changes with Sing# team. // using System; diff --git a/base/Kernel/SingSharp.Runtime/Microsoft.SingSharp.Runtime.App.csproj b/base/Kernel/SingSharp.Runtime/Microsoft.SingSharp.Runtime.App.csproj deleted file mode 100644 index 9a5edee..0000000 --- a/base/Kernel/SingSharp.Runtime/Microsoft.SingSharp.Runtime.App.csproj +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - Microsoft.SingSharp.Runtime - Library - true - NOOWNERSHIPCHECK;NODEFAULTLIB - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/base/Kernel/SingSharp.Runtime/Microsoft.SingSharp.Runtime.Kernel.csproj b/base/Kernel/SingSharp.Runtime/Microsoft.SingSharp.Runtime.Kernel.csproj index 5e0de73..fcf7ad6 100644 --- a/base/Kernel/SingSharp.Runtime/Microsoft.SingSharp.Runtime.Kernel.csproj +++ b/base/Kernel/SingSharp.Runtime/Microsoft.SingSharp.Runtime.Kernel.csproj @@ -1,8 +1,6 @@  + + + + + + Library + Channels + true + true + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Kernel/Singularity.Diagnostics/ChannelModule.sg b/base/Kernel/Singularity.Diagnostics/ChannelModule.sg index c845e43..0a17483 100644 --- a/base/Kernel/Singularity.Diagnostics/ChannelModule.sg +++ b/base/Kernel/Singularity.Diagnostics/ChannelModule.sg @@ -4,7 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: ChannelModule.sg // Note: Channel diagnostic module // @@ -36,12 +35,10 @@ namespace Microsoft.Singularity.Diagnostics // receive new client channels. DirectoryServiceContract.Imp! epNS = DirectoryService.NewClientEndpoint(); - try - { + try { epNS.SendRegister(Bitter.FromString2(ChannelContract.ModuleName), nsImp); - switch receive - { + switch receive { case epNS.AckRegister() : // All is well. break; @@ -56,17 +53,14 @@ namespace Microsoft.Singularity.Diagnostics return; } } - finally - { + finally { delete epNS; } // Here is the set of client channels we service ESet epSet = new ESet(); - while(true) - { - switch receive - { + while (true) { + switch receive { // ------------------------------- Requests for new connections case nsExp.Connect(ServiceContract.Exp:Start! newEp) : @@ -74,13 +68,11 @@ namespace Microsoft.Singularity.Diagnostics // We expect people top give us ChannelContract.Exp instances ChannelContract.Exp newDiagEp = newEp as ChannelContract.Exp; - if (newDiagEp == null) - { + if (newDiagEp == null) { // Invalid contract type. Fail. nsExp.SendNackConnect(newEp); } - else - { + else { // Signal ready and start servicing this contract nsExp.SendAckConnect(); newDiagEp.SendReady(); @@ -152,8 +144,7 @@ namespace Microsoft.Singularity.Diagnostics ChannelInfo[]! in ExHeap infoVector = this.infoVector; - if (SystemType.IsSubtype(alloc, endpointType)) - { + if (SystemType.IsSubtype(alloc, endpointType)) { unsafe { // Note that we use Unchecked access here, since we don't // actually own this data. diff --git a/base/Kernel/Singularity.Diagnostics/Diagnostics.csproj b/base/Kernel/Singularity.Diagnostics/Diagnostics.csproj index 5695c87..6108013 100644 --- a/base/Kernel/Singularity.Diagnostics/Diagnostics.csproj +++ b/base/Kernel/Singularity.Diagnostics/Diagnostics.csproj @@ -1,8 +1,6 @@  - - - - - - - - Library - NVidiaDrivers - true - $(PRECOMPILED_NVIDIA_DIR) - - - - - - - - - - - - diff --git a/base/Kernel/Singularity.Drivers/NvPciLpc.cs b/base/Kernel/Singularity.Drivers/NvPciLpc.cs index 111f574..52f7245 100644 --- a/base/Kernel/Singularity.Drivers/NvPciLpc.cs +++ b/base/Kernel/Singularity.Drivers/NvPciLpc.cs @@ -127,8 +127,7 @@ namespace Microsoft.Singularity.Drivers DebugStub.Print("NvPciLpc.Initialize (maxirq={0})\n", __arglist(IoSystem.GetMaximumIrq())); // Reroute NVidia fixed device resources on APIC system - if (IoSystem.GetMaximumIrq() > 15) - { + if (IoSystem.GetMaximumIrq() > 15) { ReroutePciInterrupts(); } } @@ -151,10 +150,8 @@ namespace Microsoft.Singularity.Drivers PciEnumerator enumerator = PciEnumerator.GetEnumerator(null); ICollection! pciConfigs = enumerator.Enumerate().Values; - foreach (PciConfig! pc in pciConfigs) - { - if (pc.VendorId == PCI_VENDOR_NVIDIA) - { + foreach (PciConfig! pc in pciConfigs) { + if (pc.VendorId == PCI_VENDOR_NVIDIA) { // NB - if there are multiple devices on the bus with the // same ID, we still break. Also note that the parameter // "1" indicates that this method wants to renumber the 1st @@ -206,8 +203,7 @@ namespace Microsoft.Singularity.Drivers uint hpetPointer = config.Read32(PCI_HPET_POINTER); - if (hpetPointer != 0) - { + if (hpetPointer != 0) { IoMemoryRange hpetMemory = new IoMemoryRange(hpetPointer, 0x1000, Access.ReadWrite); diff --git a/base/Kernel/Singularity.Drivers/PciBus.cs b/base/Kernel/Singularity.Drivers/PciBus.cs index 7dbb7f7..cbf1445 100644 --- a/base/Kernel/Singularity.Drivers/PciBus.cs +++ b/base/Kernel/Singularity.Drivers/PciBus.cs @@ -9,6 +9,10 @@ // Note: // +#define VERBOSE +//#define DEBUG_PCI_BUS +//#define DONT_USE_PNP_FOR_PCI + using Microsoft.Singularity.Channels; using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; @@ -26,18 +30,24 @@ namespace Microsoft.Singularity.Drivers // create the resource object for CTR to fill in [DriverCategory] [Signature("/pnp/PNP0A03")] + [Signature("/pnp/PNP0A08")] [EnumeratesDevice("pci/...")] internal class PciBusResources : DriverCategoryDeclaration { - [IoFixedPortRange(Base = 0x0cf8, Length = 0x08)] + [IoFixedPortRange(Base = 0x0cf8, Length = 0x08, Shared = true)] IoPortRange configPort; // Provide to unify creation. public static IDevice! DeviceCreate(IoConfig! config, String! instanceName) { Tracing.Log(Tracing.Debug, "Creating PCI Bus"); - - return new PciBus(PciEnumerator.GetEnumerator(config)); + PciBus pciBus = new PciBus(PciEnumerator.GetEnumerator(config)); +#if VERBOSE + DebugStub.WriteLine("Displaying PCI Enumeration:"); + pciBus.Display(); + DebugStub.WriteLine("PCI Enumeration End."); +#endif + return pciBus; } } @@ -72,13 +82,22 @@ namespace Microsoft.Singularity.Drivers public void Display() { - foreach (PciConfig! config in enumerator.Enumerate()) { - config.Print(); + SortedList sl = enumerator.Enumerate(); + for ( int i = 0; i < sl.Count; i++ ) { + object o = sl.GetByIndex(i); + PciConfig config = o as PciConfig; + if (config == null) { + DebugStub.Break(); + } + else { + string str = sl.GetKey(i) as string; + DebugStub.Write("{0}: ",__arglist(str)); + config.Print(); + } } } } - [CLSCompliant(false)] public class PciEnumerator { @@ -90,7 +109,6 @@ namespace Microsoft.Singularity.Drivers private const ushort PCI_ADDRESS_PORT = 0xcf8; private const ushort PCI_DATA_PORT = 0xcfc; - private uint numberOfBuses = 0; private IoPort! addressPort; private IoPort! dataPort; @@ -101,36 +119,37 @@ namespace Microsoft.Singularity.Drivers public static PciEnumerator GetEnumerator(IoConfig config) { - uint buses = Resources.GetPciNumberOfBuses(); - #if DONT_USE_PNP_FOR_PCI - if (config != null && - config.Ranges.Length >= 1 && config.Ranges[0] != null && - config.Ranges[0] is IoPortRange) { - IoPortRange ports = (IoPortRange)config.Ranges[0]; - - IoPort addrPort = ports.PortAtOffset(0, 4, Access.Write); - IoPort dataPort = ports.PortAtOffset(4, 4, Access.ReadWrite); - return new PciEnumerator(addrPort, dataPort, buses); + if (config != null && config.FixedRanges != null && + config.FixedRanges.Length >= 1 && config.FixedRanges[0] != null && + config.FixedRanges[0] is IoPortRange) { + IoPortRange ports = (IoPortRange)config.FixedRanges[0]; + if(ports != null) { + IoPort addrPort = ports.PortAtOffset(0, 4, Access.Write); + IoPort dataPort = ports.PortAtOffset(4, 4, Access.ReadWrite); + return new PciEnumerator(addrPort, dataPort); + } + else { + DebugStub.WriteLine("PciBus: config != null but ports == null\n"); + DebugStub.Break(); + } } else { #endif IoPortRange dev = new IoPortRange(PCI_ADDRESS_PORT, 8, Access.ReadWrite); return new PciEnumerator(dev.PortAtOffset(0, 4, Access.Write), - dev.PortAtOffset(4, 4, Access.ReadWrite), - buses); + dev.PortAtOffset(4, 4, Access.ReadWrite)); #if DONT_USE_PNP_FOR_PCI } #endif } - private PciEnumerator(IoPort! addressPort, IoPort! dataPort, uint buses) + private PciEnumerator(IoPort! addressPort, IoPort! dataPort) { this.addressPort = addressPort; this.dataPort = dataPort; - this.numberOfBuses = buses; } private uint Read32(uint identifier, uint offset) @@ -180,12 +199,9 @@ namespace Microsoft.Singularity.Drivers Tracing.Log(Tracing.Debug, "PCI Bus Enumerate"); SortedList found = new SortedList(); - unchecked { - Tracing.Log(Tracing.Debug, " buses: {0}", - (UIntPtr)(uint)numberOfBuses); - } + for (uint bus = 0; bus < MAX_BUSES; bus++) { + bool hadDevices = false; - for (uint bus = 0; bus < numberOfBuses; bus++) { for (uint device = 0; device < MAX_DEVICES; device++) { PciConfig config; uint identifier = IdentifierFromUnits(bus, device, 0); @@ -238,15 +254,35 @@ namespace Microsoft.Singularity.Drivers } if (config != null) { - found.Add(String.Format("/bus{0,4:x4}/dev{1,4:x4}/func{2,4:x4}", - bus, device, function), - config); + if (((config.VendorId == 0x8086) && (config.DeviceId == 0x269E)) || + ((config.VendorId == 0x8086) && (config.DeviceId == 0x2681))) { + // + // TEMP: Do not enumerate Intel SATA controllers on the Dell Precision 490. + // This needs to be removed when we have real drivers for them. + // + } + else if ((config.VendorId == 0x8086) && + (config.DeviceId >= 0x27C0) && + (config.DeviceId <= 0x27C6)) { + // + // TEMP: Do not enumerate Intel SATA controllers on the Dell Precision 380. + // This needs to be removed when we have real drivers for them. + // + } + else { + found.Add(String.Format("/bus{0,4:x4}/dev{1,4:x4}/func{2,4:x4}", + bus, device, function), + config); + hadDevices = true; + } } } } + if (!hadDevices) { + break; + } } - return found; } } diff --git a/base/Kernel/Singularity.Drivers/PnpBios.cs b/base/Kernel/Singularity.Drivers/PnpBios.cs index c350a65..54af257 100644 --- a/base/Kernel/Singularity.Drivers/PnpBios.cs +++ b/base/Kernel/Singularity.Drivers/PnpBios.cs @@ -539,7 +539,7 @@ namespace Microsoft.Singularity.Drivers #if VERBOSE DebugStub.Print(" {0,8:x8} {1} {2,4:x4}", - __arglist((uint)(region.PhysicalAddress.Value + offset), newId, attr)); + __arglist((uint)(region.PhysicalAddress.Value + (ulong)offset), newId, attr)); String s = ""; if ((attr & 0x0100) != 0) s += " dyn"; diff --git a/base/Kernel/Singularity.Drivers/PseudoBus.cs b/base/Kernel/Singularity.Drivers/PseudoBus.cs index 9db831a..d94e649 100644 --- a/base/Kernel/Singularity.Drivers/PseudoBus.cs +++ b/base/Kernel/Singularity.Drivers/PseudoBus.cs @@ -17,6 +17,14 @@ using System.Collections; namespace Microsoft.Singularity.Drivers { + // create the resource object for CTR to fill in + [DriverCategory] + [Signature("/root/pseudobus0")] + [EnumeratesDevice("pseudo/...")] + internal class PseudoBusResources : DriverCategoryDeclaration + { + } + /// /// Bus for pseudo-devices. A pseudo-device is typically a /// layer in I/O stack that may or may not be associated @@ -30,18 +38,22 @@ namespace Microsoft.Singularity.Drivers private SortedList! pseudoDevices; - private PseudoBus() + internal PseudoBus() { pseudoDevices = new SortedList(); } - internal static PseudoBus! Instance + void IDevice.Initialize() + { + DebugStub.Print("Initializing PseudoBusDevice.\n"); + + instance = this; + + RegisterBootPseudoDevices(); + } + + void IDevice.Finalize() { - get { - if (instance == null) - instance = new PseudoBus(); - return instance; - } } internal void RegisterPseudoDevice(string! name, IoConfig config) @@ -49,23 +61,29 @@ namespace Microsoft.Singularity.Drivers pseudoDevices.Add(name, config); } - public static IDevice! DeviceCreate(IoConfig! configobj) - { - return Instance; - } - SortedList IBusDevice.Enumerate() { return pseudoDevices; } - void IDevice.Initialize() + // + // Register boot time pseudo devices + // + internal void RegisterBootPseudoDevices() { - DebugStub.Print("Initializing PseudoBusDevice.\n"); + // Pseudo Devices have no resources (I/O ports, memory, etc.) + if (instance != null) { + PseudoConfig config = new PseudoConfig("iotest"); + instance.RegisterPseudoDevice("/test/iotest", config); + } } - void IDevice.Finalize() + // Allows us to be registered as a root level driver + public PseudoConfig! ReportConfig() { + PseudoConfig config = new PseudoConfig("root"); + + return config; } } @@ -94,3 +112,4 @@ namespace Microsoft.Singularity.Drivers } } } + diff --git a/base/Kernel/Singularity.Drivers/Register.cs b/base/Kernel/Singularity.Drivers/Register.cs deleted file mode 100644 index 12ce108..0000000 --- a/base/Kernel/Singularity.Drivers/Register.cs +++ /dev/null @@ -1,67 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Register.cs -// -// Note: PnP Device Type Registration and Base Initialization. -// - -using Microsoft.Singularity.Drivers.IDE; -using Microsoft.Singularity.Drivers.Pci; -using Microsoft.Singularity.Io; - -namespace Microsoft.Singularity.Drivers -{ - public sealed class Devices - { - public static void RegisterPnpResources() - { - // : /pnp/PNP0700 : Floppy Controller : PC Standard - // : /pnp/PNP0C01 : RAM : System Board - // : /pnp/PNP0A03 : PCI : PCI Bus - // : /pnp/PNP0501 : Generic Serial : 16550A COM Port - // : /pnp/PNP0501 : Generic Serial : 16550A COM Port - // : /pnp/PNP0400 : AT Parallel : LPT Port - // : /pnp/PNP0000 : ISA 8259 PIC : AT Interrupt Controller - // : /pnp/PNP0200 : ISA 8237 DMA : AT DMA Controller - // : /pnp/PNP0100 : ISA 8254 Timer : AT Timer - // : /pnp/PNP0B00 : ISA RTC Controller : AT RTC - // : /pnp/PNP0800 : Other : ??? - // : /pnp/PNP0C02 : Other : PnP Event Notification - // : /pnp/PNP0C02 : Other : PnP Event Notification - // : /pnp/PNP0C04 : Other : Math Coprocessor - // : /pnp/PNP0303 : Keyboard controller : 101/102 Keyboard - // : /pnp/PNP0F13 : Mouse Controller : Logitech PS/2 Mouse - - PnpBios bios = new PnpBios(Resources.GetPnpBiosInfo()); - - // in order for IoSystem accounting to work, we need to explicitly - // tell it what the IoConfig of the root device is: - IoSystem.AddRootDevice("/pnp0", bios, bios.ReportConfig()); - } - - // Now that we use metadata, this only registers drivers that do not run - // in separate processes. All external processes are registered through - // the IoSystem.Initialize() code. - public static void RegisterInternalDrivers() - { - // PCI Bus - IoSystem.RegisterKernelDriver( - typeof(PciBusResources), - new IoDeviceCreate(PciBusResources.DeviceCreate)); - - // Legacy PC IDE bus - IoSystem.RegisterKernelDriver( - typeof(LegacyIdeBus), - new IoDeviceCreate(LegacyIdeBus.DeviceCreate)); - - // nForce4 IDE bus - IoSystem.RegisterKernelDriver( - typeof(NvIdeBus), - new IoDeviceCreate(NvIdeBus.DeviceCreate)); - } - } -} diff --git a/base/Kernel/Singularity.Drivers/RegisterOmap3430.cs b/base/Kernel/Singularity.Drivers/RegisterOmap3430.cs new file mode 100644 index 0000000..040db59 --- /dev/null +++ b/base/Kernel/Singularity.Drivers/RegisterOmap3430.cs @@ -0,0 +1,310 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Register.cs +// +// Note: PnP Device Type Registration and Base Initialization. +// + +using System; +using System.Collections; + +using Microsoft.Singularity.Hal; +using Microsoft.Singularity.Io; + +namespace Microsoft.Singularity.Drivers +{ + public sealed class Devices + { + public static void RegisterPnpResources() + { + RegisterOmap3430Devices(); + } + + // Now that we use metadata, this only registers drivers that do not run + // in separate processes. All external processes are registered through + // the IoSystem.Initialize() code. + public static void RegisterInternalDrivers() + { + } + + private static void RegisterOmap3430Devices() + { + // Fake that PNP works on the OMAP + AdHocDevices dev = new AdHocDevices(100); + + dev.Add("/arm/ti/3430/INTCPS", + new IoRange[] { + new IoMemoryRange(0x48200000, 0x1000, Access.ReadWrite)}); + + dev.Add("/arm/ti/3430/NMPU", // MPU emulation (EMUINT) + new IoRange[] { + new IoIrqRange(0, 1), // MPU emulation (EMUINT) + new IoIrqRange(1, 1), // MPU emulation (COMMRX) + new IoIrqRange(2, 1), // MPU emulation (COMMTX) + new IoIrqRange(3, 1), // MPU emulation (NMPUIRQ) + }); + dev.Add("/arm/ti/3430/D3D", // 3G coprocessor (stacked modem) + new IoRange[] { + new IoIrqRange(8, 1), // modem reset due to security violation (D2DSEC) + new IoIrqRange(88, 1) // 3G coprocessor (stacked modem) (D2DFRONT) + }); + dev.Add("/arm/ti/3430/GFX", // Display Subsystem module + new IoRange[] { + new IoMemoryRange(((!)Platform.ThePlatform).IsaCsns, + 0x00025800, Access.ReadWrite), + new IoMemoryRange(0x48050400, 0x400, Access.ReadWrite), + new IoIrqRange(21, 1), // GFX 2D/3D graphics module + new IoIrqRange(25, 1) // Display Subsystem module + }); + dev.Add("/arm/ti/3430/FAC", // FAC module (FAC) Pretend Keyboard + new IoRange[] { + new IoIrqRange(85, 1)}); + dev.Add("/arm/ti/3430/FPKA", + new IoRange[] { + new IoIrqRange(50, 1), // FPKA_READY_N - PKA crypto-accelerator + new IoIrqRange(64, 1) // FPKA_RERROR_N - PKA crypto-accelerator + }); + dev.Add("/arm/ti/3430/FSUSB", // Full-Speed USB device controller (FSUSB) + new IoRange[] { + new IoIrqRange(75, 1), // FSUSB_GENI - Full-Speed USB device ctrlr GENI + new IoIrqRange(76, 1), // FSUSB_NONISO - Full-Speed USB device ctrl non-ISO + new IoIrqRange(77, 1), // FSUSB_ISO - Full-Speed USB device ISO + new IoIrqRange(78, 1), // FSUSB_IRQ - Full-Speed USB host controlled + new IoIrqRange(79, 1), // FSUSB_SOF - Full-Speed USB host start-of-frame + new IoIrqRange(80, 1), // FSUSB_OTG - Full-Speed USB OTG + }); + dev.Add("/arm/ti/3430/GPIO_MPU", // GPIO module 1 (GPIO1_MPU) + new IoRange[] { + new IoIrqRange(29, 1)}); + dev.Add("/arm/ti/3430/GPIO_MPU", // GPIO module 2 (GPIO2_MPU) + new IoRange[] { + new IoIrqRange(30, 1)}); + dev.Add("/arm/ti/3430/GPIO_MPU", // GPIO module 3 (GPIO3_MPU) + new IoRange[] { + new IoIrqRange(31, 1)}); + dev.Add("/arm/ti/3430/GPIO_MPU", // GPIO module 4 (GPIO4_MPU) + new IoRange[] { + new IoIrqRange(32, 1)}); + dev.Add("/arm/ti/3430/GPIO_MPU", // GPIO module 5 (GPIO5_MPU) + new IoRange[] { + new IoIrqRange(33, 1)}); + dev.Add("/arm/ti/3430/GPIO_MPU", // GPIO module 6 (GPIO6_MPU) + new IoRange[] { + new IoIrqRange(34, 1)}); + dev.Add("/arm/ti/3430/GPMC", // general-purpose memory controller (GPMC) + new IoRange[] { + new IoIrqRange(20, 1)}); + dev.Add("/arm/ti/3430/GPTIMER1", // general-purpose timer 1 (GPT1) + new IoRange[] { + new IoMemoryRange(0x48318000, 0x1000, Access.ReadWrite), + new IoIrqRange(37, 1)}); + dev.Add("/arm/ti/3430/GPT", // general-purpose timer 2 (GPT2) + new IoRange[] { + new IoIrqRange(38, 1)}); + dev.Add("/arm/ti/3430/GPT", // general-purpose timer 3 (GPT3) + new IoRange[] { + new IoIrqRange(39, 1)}); + dev.Add("/arm/ti/3430/GPT", // general-purpose timer 4 (GPT4) + new IoRange[] { + new IoIrqRange(40, 1)}); + dev.Add("/arm/ti/3430/GPT", // general-purpose timer 5 (GPT5) + new IoRange[] { + new IoIrqRange(41, 1)}); + dev.Add("/arm/ti/3430/GPT", // general-purpose timer 6 (GPT6) + new IoRange[] { + new IoIrqRange(42, 1)}); + dev.Add("/arm/ti/3430/GPT", // general-purpose timer 7 (GPT7) + new IoRange[] { + new IoIrqRange(43, 1)}); + dev.Add("/arm/ti/3430/GPT", // general-purpose timer 8 (GPT8) + new IoRange[] { + new IoIrqRange(44, 1)}); + dev.Add("/arm/ti/3430/GPT", // general-purpose timer 9 (GPT9) + new IoRange[] { + new IoIrqRange(45, 1)}); + dev.Add("/arm/ti/3430/GPT", // general-purpose timer 10 (GPT10) + new IoRange[] { + new IoIrqRange(46, 1)}); + dev.Add("/arm/ti/3430/GPT", // general-purpose timer 11 (GPT11) + new IoRange[] { + new IoIrqRange(47, 1)}); + dev.Add("/arm/ti/3430/GPT", // general-purpose timer 12 (GPT12) + new IoRange[] { + new IoIrqRange(95, 1)}); + dev.Add("/arm/ti/3430/HDQ", // HDQ/One-wire (HDQ) + new IoRange[] { + new IoIrqRange(58, 1)}); + dev.Add("/arm/ti/3430/HSUSB", // High-Speed USB OTG controller (HSUSB) + new IoRange[] { + new IoIrqRange(92, 1), // HSUSB_MC_NINT - High-Speed USB OTG controller + new IoIrqRange(93, 1) // HSUSB_DMA_NINT - High-Speed USB OTG DMA ctrl + }); + dev.Add("/arm/ti/3430/I2C", // I2C module 1 (I2C1) + new IoRange[] { + new IoMemoryRange(0x48070000, 0x80, Access.ReadWrite), + new IoIrqRange(56, 1)}); + dev.Add("/arm/ti/3430/I2C", // I2C module 2 (I2C2) + new IoRange[] { + new IoIrqRange(57, 1)}); + dev.Add("/arm/ti/3430/I2C", // I2C module 3 (I2C3) + new IoRange[] { + new IoIrqRange(61, 1)}); + dev.Add("/arm/ti/3430/IVA2", // IVA2 MMU (IVA2_MMU) + new IoRange[] { + new IoIrqRange(28, 1)}); + dev.Add("/arm/ti/3430/MCBSP", // McBSP module 1 (MCBSP1) + new IoRange[] { + new IoIrqRange(16, 1), // McBSP module 1 (MCBSP1) + new IoIrqRange(59, 1), // McBSP module 1 transmit (MCBSP1_TX) + new IoIrqRange(60, 1) // McBSP module 1 receive (MCBSP1_RX) + }); + dev.Add("/arm/ti/3430/MCBSP;/arm/ti/3430/MCBSP_ST", // McBSP module 2 (MCBSP2) + new IoRange[] { + new IoIrqRange(17, 1), // McBSP module 2 (MCBSP2) + new IoIrqRange(62, 1), // McBSP module 2 transmit (MCBSP2_TX) + new IoIrqRange(63, 1), // McBSP module 2 receive (MCBSP2_RX) + new IoIrqRange(4, 1) // Sidestone MCBSP2 overflow (MCBSP2_ST) + }); + dev.Add("/arm/ti/3430/MCBSP;/arm/ti/3430/MCBSP_ST", // McBSP module 3 (MCBSP3) + new IoRange[] { + new IoIrqRange(22, 1), // McBSP module 3 (MCBSP3) + new IoIrqRange(89, 1), // McBSP module 3 transmit (MCBSP3_TX) + new IoIrqRange(90, 1), // McBSP module 3 receive (MCBSP3_RX) + new IoIrqRange(5, 1) // Sidestone MCBSP3 overflow (MCBSP3_ST) + }); + dev.Add("/arm/ti/3430/MCBSP", // McBSP module 4 (MCBSP4) + new IoRange[] { + new IoIrqRange(23, 1), // McBSP module 4 (MCBSP4) + new IoIrqRange(54, 1), // McBSP module 4 transmit (MCBSP4_TX) + new IoIrqRange(55, 1) // McBSP module 4 receive (MCBSP4_RX) + }); + dev.Add("/arm/ti/3430/MCBSP", // McBSP module 5 (MCBSP5) + new IoRange[] { + new IoIrqRange(27, 1), // McBSP module 5 (MCBSP5) + new IoIrqRange(81, 1), // McBSP module 5 transmit (MCBSP5_TX) + new IoIrqRange(82, 1) // McBSP module 5 receive (MCBSP5_RX) + }); + dev.Add("/arm/ti/3430/MG", // MG function (MG) + new IoRange[] { + new IoIrqRange(53, 1)}); + dev.Add("/arm/ti/3430/MMC", // MMC/SD module 2 (MMC1) + new IoRange[] { + new IoIrqRange(83, 1)}); + dev.Add("/arm/ti/3430/MMC", // MMC/SD module 2 (MMC2) + new IoRange[] { + new IoIrqRange(86, 1)}); + dev.Add("/arm/ti/3430/MPU", // MPU ICR (MPU_ICR) + new IoRange[] { + new IoIrqRange(87, 1)}); + dev.Add("/arm/ti/3430/MS", // MS-PRO module (MS) + new IoRange[] { + new IoIrqRange(84, 1)}); + dev.Add("/arm/ti/3430/Mailbox", // Mailbox user 0 request (Mailbox0) + new IoRange[] { + new IoIrqRange(26, 1)}); + dev.Add("/arm/ti/3430/PRCM_MPU", // PRCM module (PRCM_MPU) + new IoRange[] { + new IoIrqRange(11, 1)}); + dev.Add("/arm/ti/3430/RNG", // RNG module (RNG) + new IoRange[] { + new IoIrqRange(52, 1)}); + dev.Add("/arm/ti/3430/SDMA", // system DMA 0 (SDMA0) + new IoRange[] { + new IoIrqRange(12, 1)}); + dev.Add("/arm/ti/3430/SDMA", // system DMA 1 (SDMA1) + new IoRange[] { + new IoIrqRange(13, 1)}); + dev.Add("/arm/ti/3430/SDMA", // system DMA 2 (SDMA2) + new IoRange[] { + new IoIrqRange(14, 1)}); + dev.Add("/arm/ti/3430/SDMA", // system DMA 3 (SDMA3) + new IoRange[] { + new IoIrqRange(15, 1)}); + dev.Add("/arm/ti/3430/SHA1MD5", // SHA-1/MD5 crypto-accelerator + new IoRange[] { + new IoIrqRange(49, 1), // SHA-1/MD5 crypto-accelerator 2 (SHA1MD52) + new IoIrqRange(51, 1) // SHA-1/MD5 crypto-accelerator 1 (SHA1MD51) + }); + dev.Add("/arm/ti/3430/SMX", // SMX + new IoRange[] { + new IoIrqRange(9, 1), // SMX debug error (SMX_DBG) + new IoIrqRange(10, 1) // SMX application error (SMX_APP) + }); + dev.Add("/arm/ti/3430/SPI", // McSPI module 1 (SPI1) + new IoRange[] { + new IoIrqRange(65, 1)}); + dev.Add("/arm/ti/3430/SPI", // McSPI module 2 (SPI2) + new IoRange[] { + new IoIrqRange(66, 1)}); + dev.Add("/arm/ti/3430/SPI", // McSPI module 3 (SPI3) + new IoRange[] { + new IoIrqRange(91, 1)}); + dev.Add("/arm/ti/3430/SPI", // McSPI module 4 (SPI4) + new IoRange[] { + new IoIrqRange(48, 1)}); + dev.Add("/arm/ti/3430/SR", // SmartReflex 1 (SR1) + new IoRange[] { + new IoIrqRange(18, 1)}); + dev.Add("/arm/ti/3430/SR", // SmartReflex 2 (SR2) + new IoRange[] { + new IoIrqRange(19, 1)}); + dev.Add("/arm/ti/3430/SSI", // Dual SSI (SSI_GDD) + new IoRange[] { + new IoIrqRange(67, 1), // Dual SSI port 1 request 0 (SSI_P1_MPU0) + new IoIrqRange(68, 1), // Dual SSI port 1 request 1 (SSI_P1_MPU1) + new IoIrqRange(69, 1), // Dual SSI port 2 request 0 (SSI_P2_MPU0) + new IoIrqRange(70, 1), // Dual SSI port 2 request 1 (SSI_P2_MPU1) + new IoIrqRange(71, 1) // Dual SSI GDD (SSI_GDD_MPU) + }); + dev.Add("/arm/ti/3430/SSM_ABORT", // MPU subsystem secure state-machine abort (SSM_ABORT) + new IoRange[] { + new IoIrqRange(6, 1)}); + dev.Add("/arm/ti/3430/UART", // UART1 (UART1) + new IoRange[] { + new IoIrqRange(72, 1)}); + dev.Add("/arm/ti/3430/UART", // UART2 (UART2) + new IoRange[] { + new IoIrqRange(73, 1)}); + dev.Add("/arm/ti/3430/UART", // UART3 (UART3) + new IoRange[] { + new IoIrqRange(74, 1)}); + dev.Add("/arm/ti/3430/WDT3", // watchdog timer module 3 overflow (WDT3) + new IoRange[] { + new IoIrqRange(36, 1)}); + dev.Add("/arm/ti/3430/SYSN", // external interrupt line (SYSN) + new IoRange[] { + new IoIrqRange(7, 1)}); + + IoSystem.AddDevicesToTree(dev.List, "/fixed0/", false); + IoSystem.Dump(true); + } + + private class AdHocDevices + { + private SortedList! list; + + internal SortedList List + { + get { + return list; + } + } + + internal AdHocDevices(int capacity) + { + list = new SortedList(capacity); + } + + internal void Add(String signature, IoRange[] ranges) + { + string iter = (!)(list.Count.ToString("d3")); + + list.Add(iter, new PnpConfig(new String[] { signature }, ranges)); + } + } + } +} diff --git a/base/Kernel/Singularity.Drivers/RegisterPc.cs b/base/Kernel/Singularity.Drivers/RegisterPc.cs new file mode 100644 index 0000000..eee758d --- /dev/null +++ b/base/Kernel/Singularity.Drivers/RegisterPc.cs @@ -0,0 +1,120 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Register.cs +// +// Note: PnP Device Type Registration and Base Initialization. +// + +#define ACPI_ENABLED +#define VESA_ENABLED + +using Microsoft.Singularity.Drivers.IDE; +using Microsoft.Singularity.Drivers.Pci; +using Microsoft.Singularity.Hal; +using Microsoft.Singularity.Hal.Acpi; +using Microsoft.Singularity.Io; + +#if VESA_ENABLED +using System; +using System.Collections; +#endif + + +namespace Microsoft.Singularity.Drivers +{ + public sealed class Devices + { + public static void RegisterPnpResources() + { + // Register our pseudo bus driver + PseudoBus pseudo = new PseudoBus(); + IoSystem.AddRootDevice("/pseudobus0", pseudo, pseudo.ReportConfig()); + + // : /pnp/PNP0700 : Floppy Controller : PC Standard + // : /pnp/PNP0C01 : RAM : System Board + // : /pnp/PNP0A03 : PCI : PCI Bus + // : /pnp/PNP0501 : Generic Serial : 16550A COM Port + // : /pnp/PNP0501 : Generic Serial : 16550A COM Port + // : /pnp/PNP0400 : AT Parallel : LPT Port + // : /pnp/PNP0000 : ISA 8259 PIC : AT Interrupt Controller + // : /pnp/PNP0200 : ISA 8237 DMA : AT DMA Controller + // : /pnp/PNP0100 : ISA 8254 Timer : AT Timer + // : /pnp/PNP0B00 : ISA RTC Controller : AT RTC + // : /pnp/PNP0800 : Other : ??? + // : /pnp/PNP0C02 : Other : PnP Event Notification + // : /pnp/PNP0C02 : Other : PnP Event Notification + // : /pnp/PNP0C04 : Other : Math Coprocessor + // : /pnp/PNP0303 : Keyboard controller : 101/102 Keyboard + // : /pnp/PNP0F13 : Mouse Controller : Logitech PS/2 Mouse + AcpiDevice[] deviceInfo = null; + AcpiTables.Parse(); +#if ACPI_ENABLED + deviceInfo = AcpiTables.LoadDevices(); +#endif // ACPI_ENABLED + + + if (deviceInfo != null && deviceInfo.Length > 0) { + AcpiBus! acpi = new AcpiBus(deviceInfo); + IoSystem.AddRootDevice("/acpi0", acpi, acpi.ReportConfig()); + } + else { + PnpBios bios = new PnpBios(Resources.GetPnpBiosInfo()); + + // in order for IoSystem accounting to work, we need to explicitly + // tell it what the IoConfig of the root device is: + IoSystem.AddRootDevice("/pnp0", bios, bios.ReportConfig()); + } + +#if VESA_ENABLED + // Add VESA Device if it exists. + if (((!)Platform.ThePlatform).VesaBuffer != 0) { + SortedList custom = new SortedList(); + + custom.Add("000", + new PnpConfig( + new String[] { "/pnp/vesa" }, + new IoRange[] { + new IoMemoryRange( + Platform.ThePlatform.VesaBuffer, + 0x300000, Access.ReadWrite) })); + IoSystem.AddDevicesToTree(custom, "/vesa0/", false); + } +#endif // VESA_ENABLED + + } + // Now that we use metadata, this only registers drivers that do not run + // in separate processes. All external processes are registered through + // the IoSystem.Initialize() code. + public static void RegisterInternalDrivers() + { + // PCI Bus + IoSystem.RegisterKernelDriver( + typeof(PciBusResources), + new IoDeviceCreate(PciBusResources.DeviceCreate)); + + // nForce4 LPC Bridge + IoSystem.RegisterKernelDriver( + typeof(NvPciLpcResources), + new IoDeviceCreate(NvPciLpcResources.DeviceCreate)); + + // Legacy PC IDE bus + IoSystem.RegisterKernelDriver( + typeof(LegacyIdeBus), + new IoDeviceCreate(LegacyIdeBus.DeviceCreate)); + + // nForce4 IDE bus + IoSystem.RegisterKernelDriver( + typeof(NvIdeBus), + new IoDeviceCreate(NvIdeBus.DeviceCreate)); + + // Tyan motherboard IDE bus + IoSystem.RegisterKernelDriver( + typeof(TyanIdeBus), + new IoDeviceCreate(TyanIdeBus.DeviceCreate)); + } + } +} diff --git a/base/Kernel/Singularity.Drivers/ScsiDevice.cs b/base/Kernel/Singularity.Drivers/ScsiDevice.cs index 64e0f7f..db33587 100644 --- a/base/Kernel/Singularity.Drivers/ScsiDevice.cs +++ b/base/Kernel/Singularity.Drivers/ScsiDevice.cs @@ -4,11 +4,8 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: ScsiDevice.cs -// // Base Classes and interfaces for SCSI Devices // -// // // References used: // "AT Attachment 8 - ATA/ATAPI Command Set (ATA8-ACS)", Revision 0, DRAFT @@ -55,7 +52,7 @@ namespace Microsoft.Singularity.Drivers.SCSI }; public class SPC3 { public static string strDeviceType(PeripheralDeviceType devtype) { - switch(devtype) { + switch (devtype) { case PeripheralDeviceType.BLOCK: return "Block Device"; case PeripheralDeviceType.CDROM: diff --git a/base/Kernel/Singularity.Drivers/Vga.cs b/base/Kernel/Singularity.Drivers/Vga.cs index d2130ba..d080224 100644 --- a/base/Kernel/Singularity.Drivers/Vga.cs +++ b/base/Kernel/Singularity.Drivers/Vga.cs @@ -8,20 +8,18 @@ // // Notes: // -// Based on code originally written by Erick Smith (ErickS) -// // Useful reference URLs: // http://www.xs4all.nl/~ganswijk/chipdir/reg/vga.txt "The VGA Registers" // http://osdev.neopages.net/FreeVGA/vga/graphreg.htm // +using System; + using Microsoft.Singularity.Channels; using Microsoft.Singularity.Hal; using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; -using System; - namespace Microsoft.Singularity.Drivers { // create the resource object for CTR to fill in @@ -87,7 +85,7 @@ namespace Microsoft.Singularity.Drivers textBuffer = IoMemory.AllocateIo(0xb8000, 0x8000, true, true); } - private IHalConsole lastScreen; + private HalConsole lastScreen; // // VGA register definitions @@ -310,7 +308,8 @@ namespace Microsoft.Singularity.Drivers pSrcTemp++; - } else { + } + else { if (((buffer[pSrcTemp] >> 4) & plane) != 0) { Plane[bank] |= Mask; @@ -464,7 +463,8 @@ namespace Microsoft.Singularity.Drivers (byte)(buffer[pSrcTemp] << (8 - shift))); } - } else { + } + else { // // Aligned case. @@ -549,7 +549,8 @@ namespace Microsoft.Singularity.Drivers lDelta = cbScanLine; bih.biHeight = -bih.biHeight; - } else { + } + else { // bottom up bitmap offset += cbScanLine * (bih.biHeight - 1); @@ -574,7 +575,8 @@ namespace Microsoft.Singularity.Drivers BitBlt4(x, y, bih.biWidth, bih.biHeight, buffer, offset, lDelta); } - } else { + } + else { // // We don't support this type of bitmap. diff --git a/base/Kernel/Singularity.Drivers/i8255x.cs b/base/Kernel/Singularity.Drivers/i8255x.cs deleted file mode 100644 index 0183880..0000000 --- a/base/Kernel/Singularity.Drivers/i8255x.cs +++ /dev/null @@ -1,206 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Register.cs -// -// Note: PnP Device Type Registration and Base Initialization. -// - -using Microsoft.Singularity; -using Microsoft.Singularity.Io; -using System; -using System.Threading; -using System.Diagnostics; - -namespace Microsoft.Singularity.Intel8255x -{ - public class Factory { - public static IDevice! DeviceCreate(IoConfig! config, String! instanceName) - { - return new Driver((PciDeviceConfig)config); - } - } - - internal enum SCBStatAck : byte { - CX = 1 << 7, - FR = 1 << 6, - CNA = 1 << 5, - RNR = 1 << 4, - MDI = 1 << 3, - SWI = 1 << 2, - ER = 1 << 1, - FCP = 1 << 0 - } - - internal enum SCBStatus : byte { - RUS = 0x3 << 6, - CUS = 0xf << 2 - } - - internal enum SCBCommand : ushort { - CX = 1 << 15, - FR = 1 << 14, - CNA = 1 << 13, - RNR = 1 << 12, - ER = 1 << 11, - FCP = 1 << 10, - SI = 1 << 9, - M = 1 << 8, - CUC = 0xf << 4, - RUC = 0x7 - } - - public class Driver : IDevice { - public static IDevice! DeviceCreate(IoConfig! config) - { - return new Driver((PciDeviceConfig)config); - } - - private PciDeviceConfig config; - - private IoMemoryRange eepromBase; - private IoMemory eepromMemory; - - private IReadWriteRegister16 scbCommand; - private IReadOnlyRegister8 scbStatus; - private IReadWriteRegister8 scbStatAck; - - private IoIrq irq; - private Thread irqWorker; - private bool irqWorkerStop = false; - private Thread irqMaker; - private bool irqMakerStop = false; - - internal Driver(PciDeviceConfig config) - { - this.config = config; - } - - private void DebugPrint(string format, __arglist) - { - DebugStub.Print(format, __arglist); - } - - private void InitializeMemoryRegisters() - { - IoMemoryRange range = (IoMemoryRange)config.Ranges[0]; - scbStatus = ReadOnlyMemoryRegister8.Create(range, 0); - scbStatAck = ReadWriteMemoryRegister8.Create(range, 1); - scbCommand = ReadWriteMemoryRegister16.Create(range, 2); - } - - private void InitializeIoPortRegisters() - { - IoPortRange range = (IoPortRange)config.Ranges[1]; - scbStatus = ReadOnlyPortRegister8.Create(range, 0); - scbStatAck = ReadWritePortRegister8.Create(range, 1); - scbCommand = ReadWritePortRegister16.Create(range, 2); - } - - public void Initialize() - { - if (config.MemorySpaceEnabled) { - InitializeMemoryRegisters(); - } else { - Debug.Assert(config.IoSpaceEnabled == true); - InitializeIoPortRegisters(); - } - - eepromBase = (IoMemoryRange)config.Ranges[2]; - eepromMemory = eepromBase.MemoryAtOffset(0, 65536, Access.Read); - - DebugPrint("RevisionId {0} SCB Status 0x{1:x2} " + - "Command 0x{2:x4}\n", - __arglist(config.RevisionId, - (byte)GetScbStatus(), (ushort)GetScbCommand())); - - irq = config.GetIrq().IrqAtOffset(0); - irq.RegisterInterrupt(); - irqWorker = new Thread(new ThreadStart(this.IrqWorkerMain), - new Scheduler.Activity() - ); - irqWorker.Start(); - irqWorker.Name = String.Format("i8255x I/O {0} ({0})", - config.ToString()); - - irqMaker = new Thread(new ThreadStart(this.IrqMakerMain), - new Scheduler.Activity() - ); - irqMaker.Start(); - } - - public void Finalize() - { - irqWorkerStop = true; - irqMakerStop = true; - irq.ReleaseInterrupt(); - -#if CAN_JOIN - if (irqMaker != null) { - irqMaker.Join(); - irqMaker = null; - } - if (irqWorker != null) { - irqWorker.Join(); - irqWorker = null; - } -#endif - } - - private SCBStatus GetScbStatus() - { - return (SCBStatus)scbStatus.Read(); - } - - private SCBStatAck GetScbStatAck() - { - return (SCBStatAck)scbStatAck.Read(); - } - - private void SetScbStatAck(SCBStatAck value) - { - scbStatAck.Write((byte)value); - } - - private SCBCommand GetScbCommand() - { - return (SCBCommand)scbCommand.Read(); - } - - private void SetScbCommand(SCBCommand value) - { - scbCommand.Write((ushort)value); - } - - private void IrqMakerMain() - { - for (;;) { - Thread.Sleep(1000); - // Trigger Software Interrupt - scbCommand.Write((ushort)SCBCommand.SI); - } - } - - private void IrqWorkerMain() - { - Tracing.Log(Tracing.Audit, "Start of worker thread."); - uint iters = 0; - for (;;) { - irq.WaitForInterrupt(); - DebugPrint("+ IrqWorkerMain iteration {0}\n", __arglist(iters)); - - SCBStatAck s = GetScbStatAck(); - DebugPrint("Clearing Interupt : StatAck 0x{0:x4}\n", - __arglist((ushort)s)); - SetScbStatAck(s); // Write bits back to clear - scbCommand.Write(0); - irq.AckInterrupt(); - - DebugPrint("- IrqWorkerMain iteration {0}\n", __arglist(iters)); - } - } - } -} diff --git a/base/Kernel/Singularity.Hal.Acpi/AcpiChecksum.cs b/base/Kernel/Singularity.Hal.Acpi/AcpiChecksum.cs index 462bae6..a7f6edf 100644 --- a/base/Kernel/Singularity.Hal.Acpi/AcpiChecksum.cs +++ b/base/Kernel/Singularity.Hal.Acpi/AcpiChecksum.cs @@ -26,8 +26,7 @@ namespace Microsoft.Singularity.Hal.Acpi byte sum = 0; uint stop = offset + length; - for (uint i = offset; i < stop; i++) - { + for (uint i = offset; i < stop; i++) { sum += region.Read8((int) i); } return sum; diff --git a/base/Kernel/Singularity.Hal.Acpi/AcpiTables.cs b/base/Kernel/Singularity.Hal.Acpi/AcpiTables.cs index b16ab33..ba3be48 100644 --- a/base/Kernel/Singularity.Hal.Acpi/AcpiTables.cs +++ b/base/Kernel/Singularity.Hal.Acpi/AcpiTables.cs @@ -9,13 +9,75 @@ // Note: // Based on ACPI 3.0 Spec. +// Define to dump all ACPI tables in uuencoded binary form to the debugger +// at boot time. +//#define DUMP_ACPI_TABLES_UUENCODED + +// Define to dump a trace of all raw read/writes performed by the ACPI +// interpreter to the debugger. This can be processed by a tool +// %SINGULARITY_ROOT%\Windows\ACPI\TestFiles\parse_read_write_trace.pl +// to produce a read/write trace for use by the test harness. +//#define DUMP_RAW_READ_WRITES + +// Define to dump all names in the initial ACPI namespace tree. +//#define DUMP_ACPI_NAMESPACE + namespace Microsoft.Singularity.Hal.Acpi { using System; + using System.Collections; + using System.Text; + using Microsoft.Singularity.Io; + using Microsoft.Singularity.Hal; + + internal sealed class VerboseOut + { + [System.Diagnostics.Conditional("VERBOSE")] + internal static void WriteLine(string format, __arglist) + { + DebugStub.WriteLine(format, new ArgIterator(__arglist)); + } + + [System.Diagnostics.Conditional("VERBOSE")] + internal static void WriteLine(string message) + { + DebugStub.WriteLine(message); + } + + [System.Diagnostics.Conditional("VERBOSE")] + internal static void WriteLine() + { + DebugStub.WriteLine(); + } + + [System.Diagnostics.Conditional("VERBOSE")] + internal static void Write(string format, __arglist) + { + DebugStub.Write(format, new ArgIterator(__arglist)); + } + + [System.Diagnostics.Conditional("VERBOSE")] + internal static void Write(string message) + { + DebugStub.Write(message); + } + + [System.Diagnostics.Conditional("VERBOSE")] + internal static void Print(string format, __arglist) + { + DebugStub.Print(format, new ArgIterator(__arglist)); + } + + [System.Diagnostics.Conditional("VERBOSE")] + internal static void Print(string message) + { + DebugStub.Print(message); + } + } [ CLSCompliant(false) ] - internal class AcpiTables + public class AcpiTables { private static Fadt fadt; private static Madt madt; @@ -25,13 +87,19 @@ namespace Microsoft.Singularity.Hal.Acpi private static Srat srat; + private static SystemTableHeader rsdtHeader; + private static Rsdt rsdt; + private static PMTimer pmTimer; + private static AcpiNamespace acpiNamespace; + private static ReservedObjects reservedObjects; + private static UIntPtr GetRsdpBase() { unsafe { - return BootInfo.GetBootInfo().AcpiRoot32; + return Platform.ThePlatform.AcpiRoot32; } } @@ -55,35 +123,84 @@ namespace Microsoft.Singularity.Hal.Acpi return pmTimer; } + public static AcpiNamespace AcpiNamespace + { + get + { + return acpiNamespace; + } + } + + private class StringSet + { + private SortedList map = new SortedList(); + + public void Add(string s) + { + map.Add(s, null); + } + + public bool Contains(string s) + { + return map.ContainsKey(s); + } + } + public static void Parse() { UIntPtr rsdpBase = GetRsdpBase(); if (rsdpBase == UIntPtr.Zero) { - DebugStub.Print("ACPI RSDP not found\n"); + VerboseOut.Print("ACPI RSDP not found\n"); } - DebugStub.Print("ACPI RSDP address is {0:x8}\n", - __arglist(rsdpBase)); +#if DUMP_ACPI_TABLES_UUENCODED + UuencodeDumpRegion("RSDP.dmp", + IoMemory.MapPhysicalMemory( + rsdpBase, 36u, + true, false)); +#endif Rsdp rsdp = Rsdp.Parse(rsdpBase, 36u); - DebugStub.Print("ACPI RSDP OemId is {0:x8}\n", + VerboseOut.Print("ACPI RSDP OemId is {0:x8}\n", __arglist(rsdp.OemId)); - DebugStub.Print("ACPI RSDP revision is {0:x8}\n", + VerboseOut.Print("ACPI RSDP revision is {0:x8}\n", __arglist(rsdp.Revision)); - DebugStub.Print("ACPI RSDT address is {0:x8}\n", - __arglist(rsdp.RsdtAddress)); - SystemTableHeader rsdtHeader = - SystemTableHeader.Create(rsdp.RsdtAddress); + if (rsdp.Revision == 2) { + rsdtHeader = SystemTableHeader.Create(rsdp.XsdtAddress); +#if DUMP_ACPI_TABLES_UUENCODED + UuencodeDumpRegion("XSDT.dmp", + IoMemory.MapPhysicalMemory( + rsdtHeader.Address, rsdtHeader.FullTableLength, + true, false)); +#endif + rsdt = Xsdt.Create(rsdtHeader); + } + else { + rsdtHeader = SystemTableHeader.Create(rsdp.RsdtAddress); +#if DUMP_ACPI_TABLES_UUENCODED + UuencodeDumpRegion("RSDT.dmp", + IoMemory.MapPhysicalMemory( + rsdtHeader.Address, rsdtHeader.FullTableLength, + true, false)); +#endif + rsdt = Rsdt.Create(rsdtHeader); + } - Rsdt rsdt = Rsdt.Create(rsdtHeader); + VerboseOut.Print("ACPI RSDT/XSDT OemTableId is {0}\n", + __arglist(rsdtHeader.OemTableId)); + VerboseOut.Print("ACPI RSDT/XSDT Revision is {0:x8}\n", + __arglist(rsdtHeader.Revision)); + VerboseOut.Print("ACPI RSDT/XSDT CreatorId is {0:x8}\n", + __arglist(rsdtHeader.CreatorId)); + VerboseOut.Print("ACPI RSDT/XSDT CreatorRevision is {0:x8}\n", + __arglist(rsdtHeader.CreatorRevision)); - - DebugStub.Print("RSDT contains:\n"); + VerboseOut.Print("RSDT contains:\n"); for (int i = 0; i < rsdt.EntryCount; i++) { SystemTableHeader header = rsdt.GetTableHeader(i); - DebugStub.Print(" {0:x8}\n", __arglist(header.Signature)); + VerboseOut.Print(" {0:x8}\n", __arglist(header.Signature)); if (header.Signature == Fadt.Signature) { fadt = Fadt.Create(header); } @@ -93,9 +210,8 @@ namespace Microsoft.Singularity.Hal.Acpi else if (header.Signature == Ssdt.Signature) { ssdt = Ssdt.Create(header); } - // haryadi - Srat, Slit - else if (header.Signature == Srat.Signature) - { + // Srat, Slit + else if (header.Signature == Srat.Signature) { srat = Srat.Create(header); srat.ParseSratStructure(); @@ -106,164 +222,563 @@ namespace Microsoft.Singularity.Hal.Acpi } } + SystemTableHeader dsdtHeader = null; + if (fadt != null) { pmTimer = PMTimer.Create(fadt); - DebugStub.Print("PMTimer Value={0} Width={1}\n", + VerboseOut.Print("PMTimer Value={0} Width={1}\n", __arglist(pmTimer.Value, pmTimer.Width)); uint t0 = pmTimer.Value; uint t1 = pmTimer.Value; uint t2 = pmTimer.Value; uint delta = (t2 >= t1) ? t2 - t1 : ((t1 | 0xff000000) - t2); - DebugStub.Print("Read cost {0} ticks\n", __arglist(delta)); + VerboseOut.Print("Read cost {0} ticks\n", __arglist(delta)); - if (fadt.DSDT != 0) - { - dsdt = Dsdt.Create( - SystemTableHeader.Create(fadt.DSDT) - ); + if (fadt.DSDT != 0) { + dsdtHeader = SystemTableHeader.Create(fadt.DSDT); + dsdt = Dsdt.Create(dsdtHeader); } } - if (dsdt != null) { - DumpRegion("DSDT", dsdt.Region); + + VerboseOut.Print("Parsing and loading AML\n"); + +#if DUMP_ACPI_TABLES_UUENCODED + if (dsdtHeader != null) { + UuencodeDumpRegion("ACPI.DSDT.dmp", + IoMemory.MapPhysicalMemory( + dsdtHeader.Address, dsdtHeader.FullTableLength, + true, false)); } - if (ssdt != null) { - DumpRegion("SSDT", ssdt.Region); + + for (int i = 0; i < rsdt.EntryCount; i++) { + SystemTableHeader header = rsdt.GetTableHeader(i); + UuencodeDumpRegion("ACPI." + header.Signature + "." + header.OemTableId + ".dmp", + IoMemory.MapPhysicalMemory( + header.Address, header.FullTableLength, + true, false)); } +#endif // DUMP_ACPI_TABLES_UUENCODED } - private static char Hex(int v) + public static AcpiDevice[] LoadDevices() { - if (v < 10) { - return (char)('0' + v); + OperationRegionAccessor operationRegionAccessor = new OperationRegionAccessor(); + acpiNamespace = new AcpiNamespace(); + reservedObjects = new ReservedObjects(acpiNamespace); + reservedObjects.CreateReservedObjects(); + + if (dsdt != null) { + if (ParseAndLoadRegion(dsdt.Region, operationRegionAccessor) == AmlParser.ParseSuccess.Failure) { + throw new Exception("AML parser failure while parsing DSDT"); + } + } + + // From the spec: "SSDTs are a continuation of the DSDT. Multiple SSDTs + // can be used as part of a platform description. After the DSDT is loaded + // into the ACPI Namespace, each secondary description table listed in the + // RSDT/XSDT with a unique OEM Table ID is loaded." - section 2.1, General + // ACPI Terminology + StringSet visitedOemTableIds = new StringSet(); + for (int i = 0; i < rsdt.EntryCount; i++) { + SystemTableHeader header = rsdt.GetTableHeader(i); + VerboseOut.Print(" {0:x8}\n", __arglist(header.Signature)); + + string oemTableId = header.OemTableId; + if (!visitedOemTableIds.Contains(oemTableId) && header.Signature == Ssdt.Signature) { + visitedOemTableIds.Add(oemTableId); + ssdt = Ssdt.Create(header); + if (ParseAndLoadRegion(ssdt.Region, operationRegionAccessor) == AmlParser.ParseSuccess.Failure) { + throw new Exception("AML parser failure while parsing SSDT " + oemTableId); + } + } + } + +#if DUMP_ACPI_NAMESPACE + DebugStub.WriteLine("Dumping ACPI namespace tree..."); + acpiNamespace.DumpTree(); +#endif + + return GetDeviceInfo(operationRegionAccessor); + } + +#if DUMP_ACPI_TABLES_UUENCODED + private static void UuencodeDumpRegion(string filename, IoMemory region) + { + DebugStub.Print("\nbegin 777 {0}\n", __arglist(filename)); + + StringBuilder line = new StringBuilder(); + int inputBytesOnLine = 0; + for (int i = 0; i < region.Length; i += 3) { + byte b1 = (byte)0, b2 = (byte)0, b3 = (byte)0; + + b1 = region.Read8(i); + inputBytesOnLine++; + + if (i + 1 < region.Length) { + b2 = region.Read8(i + 1); + inputBytesOnLine++; + } + + if (i + 2 < region.Length) { + b3 = region.Read8(i + 2); + inputBytesOnLine++; + } + + line.Append((char)(32 + (b1 >> 2))); + line.Append((char)(32 + (((b1 << 4) | (b2 >> 4)) & 0x3F))); + line.Append((char)(32 + (((b2 << 2) | (b3 >> 6)) & 0x3F))); + line.Append((char)(32 + (b3 & 0x3F))); + + if (line.Length >= 60 || i + 3 >= region.Length) { + DebugStub.Print("{0}{1}\n", __arglist((char)(inputBytesOnLine + 32), line.ToString())); + line.Remove(0, line.Length); + inputBytesOnLine = 0; + } + } + + DebugStub.Print("end\n\n"); + } +#endif // #if DUMP_ACPI_TABLES_UUENCODED + + private static AmlParser.ParseSuccess ParseAndLoadRegion(IoMemory region, AcpiObject.IOperationRegionAccessor operationRegionAccessor) + { + AmlParser.AMLCode result; + int offset = 0; + AmlParser parser = new AmlParser(new IoMemoryAmlStreamAdapter(region), null, null); + AmlParser.ParseSuccess parseSuccess = + parser.ParseAMLCode(out result, ref offset, region.Length); + + if (parseSuccess == AmlParser.ParseSuccess.Success) { + AmlLoader loader = new AmlLoader(acpiNamespace, operationRegionAccessor); + loader.Load(result); + } + return parseSuccess; + } + + private static AcpiDevice[] GetDeviceInfo(AcpiObject.IOperationRegionAccessor operationRegionAccessor) + { + ArrayList deviceInfo = new ArrayList(); + + AmlInterpreter interpreter = new AmlInterpreter(acpiNamespace, operationRegionAccessor); + + foreach (AcpiNamespace.Node crsNode in acpiNamespace.GetAllNodes()) { + if (crsNode.Name != "_CRS") { + continue; + } + + VerboseOut.Write("Loading resource descriptors for ACPI device "); + foreach (string segment in crsNode.Path.RemoveSegment().NameSegments) { + VerboseOut.Write(segment + "\\"); + } + VerboseOut.WriteLine(); + + AcpiNamespace.Node hidNode = + acpiNamespace.LookupNode(crsNode.Path.RemoveSegmentAbsolute().AddSegmentAbsolute("_HID")); + if (hidNode == null) { + throw new Exception("Found device with _CRS property but no matching _HID property"); + } + + AcpiObject.AcpiObject hidObject = hidNode.Value; + if (hidObject is AcpiObject.BytecodeMethod) { + AmlInterpreterThread thread = + interpreter.InvokeMethodOnNewThread(null, hidNode.Path, new AcpiObject.AcpiObject[] { }); + interpreter.Run(); + hidObject = thread.ExitValue; + } + string deviceId = HidObjectToDeviceId(hidObject); + + AcpiObject.AcpiObject crsObject = crsNode.Value; + if (crsObject is AcpiObject.BytecodeMethod) { + AmlInterpreterThread thread = + interpreter.InvokeMethodOnNewThread(null, crsNode.Path, new AcpiObject.AcpiObject[] { }); + interpreter.Run(); + crsObject = thread.ExitValue; + } + + if (crsObject is AcpiObject.Buffer) { + byte[] crsBuffer = crsObject.GetAsBuffer().Contents; + ResourceDescriptor[] resourceDescriptors = ResourceDescriptorParser.Parse(crsBuffer); + + VerboseOut.WriteLine("Loaded resource descriptor for device " + deviceId); + + deviceInfo.Add(new AcpiDevice(deviceId, resourceDescriptors)); + } + else { + VerboseOut.WriteLine("No resource descriptor for device " + deviceId); + } + } + + return (AcpiDevice[])deviceInfo.ToArray(typeof(AcpiDevice)); + } + + private static string HidObjectToDeviceId(AcpiObject.AcpiObject obj) + { + AcpiObject.AcpiObjectType type = + (AcpiObject.AcpiObjectType)((AcpiObject.Integer)(obj.ObjectType())).Value; + string hid; + + if (type == AcpiObject.AcpiObjectType.Integer) + { + // Swap byte order so that all fields are contiguous + ulong eisaId = ByteOrder.Swap((uint)(((AcpiObject.Integer)obj).Value)); + hid = String.Format("{0}{1}{2}{3:X}{4:X}{5:X}{6:X}", + (char)(((eisaId >> 26) & 0x1F) + '@'), + (char)(((eisaId >> 21) & 0x1F) + '@'), + (char)(((eisaId >> 16) & 0x1F) + '@'), + (eisaId >> 12) & 0xF, + (eisaId >> 8) & 0xF, + (eisaId >> 4) & 0xF, + (eisaId >> 0) & 0xF); + } + else if (type == AcpiObject.AcpiObjectType.String) { + hid = ((AcpiObject.String)obj).Value; } else { - return (char)('A' + v - 10); + throw new ArgumentException("_HID object was not an integer or string as expected"); + } + + if (hid.StartsWith("PNP")) { + return "/pnp/" + hid; + } + else { + return "/acpi/" + hid; } } - private static void DumpRegion(string name, IoMemory region) + public class IoMemoryAmlStreamAdapter : IAmlStream { -#if FALSE - // (new NamespaceWalker(region)).Display(); - DebugStub.Print("-----------------------------------------------------------------------------\n"); - DebugStub.Print("Table {0} dump\n", __arglist(name)); - DebugStub.Print("-----------------------------------------------------------------------------\n"); + private IoMemory region; - const int step = 16; - - byte last = region.Read8(0); - for (int i = 0; i < region.Length - 8; i ++) { - if (region.Read8(i + 0) == (byte)'_' && - (region.Read8(i + 1) == (byte)'C' || - region.Read8(i + 1) == (byte)'H') && - region.Read8(i + 2) == (byte)'I' && - region.Read8(i + 3) == (byte)'D') { - - int o = 1; - byte d0 = region.Read8(i + 4 + o); - byte d1 = region.Read8(i + 5 + o); - byte d2 = region.Read8(i + 6 + o); - byte d3 = region.Read8(i + 7 + o); - char c0 = (char)(((d0 & 0x7f) >> 2) + 0x40); - char c1 = (char)((((d0 & 0x3) << 3) | (d1 >> 5)) + 0x40); - char c2 = (char)((d1 & 0x1f) + 0x40); - char c3 = Hex(d2 >> 4); - char c4 = Hex(d2 & 0xf); - char c5 = Hex(d3 >> 4); - char c6 = Hex(d3 & 0xf); - DebugStub.Print("{0:x8} -> _{8}ID {1}{2}{3}{4}{5}{6}{7}\n", - __arglist(i, c0, c1, c2, - c3, c4, c5, c6, - (char)region.Read8(i + 1))); - } + public IoMemoryAmlStreamAdapter(IoMemory region) + { + this.region = region; } - for (int i = 0; i < region.Length; i += step) { - DebugStub.Print("{0:x8} : ", __arglist(i)); - int n = step; - if (region.Length - i < n) { - n = region.Length - i; + public byte ReadByteData(ref int offset) + { + if (offset + 1 > region.Length) { + throw new EndOfAmlStreamException(); } - for (int j = 0; j < n; j++) { - char c = (char)region.Read8(i + j); - DebugStub.Print("{0:x2} ", __arglist((int)c)); - } - for (int j = 0; j < n; j++) { - char c = (char)region.Read8(i + j); - if (c < 32 || c > 127) - c = '.'; - DebugStub.Print(c.ToString()); - } - DebugStub.Print("\n"); + byte result = region.Read8(offset); + offset++; + return result; + } + + public bool TryReadByteData(ref int offset, out byte result) + { + if (offset + 1 > region.Length) { + result = 0; + return false; + } + result = region.Read8(offset); + offset++; + return true; + } + + public char ReadChar(ref int offset) + { + if (offset + 1 > region.Length) { + throw new EndOfAmlStreamException(); + } + char result = (char)region.Read8(offset); + offset++; + return result; + } + + public byte[] ReadByteDataArray(ref int offset, int length) + { + if (offset + length > region.Length) { + throw new EndOfAmlStreamException(); + } + byte[] result = new byte[length]; + if (length != 0) { + region.Read8(offset, result, 0, length); + } + offset += length; + return result; + } + + public bool TryReadByteDataArray(ref int offset, int length, out byte[] result) + { + if (offset + length > region.Length) { + result = null; + return false; + } + result = new byte[length]; + if (length != 0) { + region.Read8(offset, result, 0, length); + } + offset += length; + return true; + } + + public ushort ReadWordData(ref int offset) + { + if (offset + 2 > region.Length) { + throw new EndOfAmlStreamException(); + } + ushort result = ByteOrder.LittleEndianToHost(region.Read16(offset)); + offset += 2; + return result; + } + + public uint ReadDWordData(ref int offset) + { + if (offset + 4 > region.Length) { + throw new EndOfAmlStreamException(); + } + uint result = ByteOrder.LittleEndianToHost(region.Read32(offset)); + offset += 4; + return result; + } + + public ulong ReadQWordData(ref int offset) + { + if (offset + 8 > region.Length) { + throw new EndOfAmlStreamException(); + } + ulong result = ByteOrder.LittleEndianToHost(region.Read64(offset)); + offset += 8; + return result; } - DebugStub.Print("\n"); -#endif // FALSE } - internal class NamespaceWalker + public class OperationRegionAccessor : AcpiObject.IOperationRegionAccessor { - int cursor = 0; - IoMemory memory; + private const ushort PciAddressPort = 0xcf8; + private const uint PciConfigEnableMask = 1u << 31; + private IoPort pciConfigAddressPort; + private IoPort pciConfigDataPort; - internal NamespaceWalker(IoMemory m) + public OperationRegionAccessor() { - this.memory = m; + IoPortRange pciConfigPorts = new IoPortRange(PciAddressPort, 8, Access.ReadWrite); + pciConfigAddressPort = pciConfigPorts.PortAtOffset(0, 4, Access.Write); + pciConfigDataPort = pciConfigPorts.PortAtOffset(4, 4, Access.ReadWrite); } - byte Read8() + public byte Read8(AcpiObject.RegionSpace regionSpace, ulong offset) { - return this.memory.Read8(cursor++); - } + byte result; + switch (regionSpace) { + case AcpiObject.RegionSpace.SystemMemory: + // TODO: This is a first stab - ideally the AcpiObject.OperationRegion + // ought to be holding onto an IoMemoryRange and passing it in repeatedly. + IoMemory region = IoMemory.MapPhysicalMemory(offset, 1, true/*readable*/, false/*writable*/); + result = region.Read8(0); + break; - ushort Read16() - { - ushort r = this.memory.Read16(cursor); - cursor += 2; - return r; - } + case AcpiObject.RegionSpace.SystemIO: + IoPort port = new IoPort((ushort)offset, 1, Access.Read); + result = port.Read8(); + break; - uint Read32() - { - uint r = this.memory.Read32(cursor++); - cursor += 4; - return r; - } + case AcpiObject.RegionSpace.PCI_Config: + pciConfigAddressPort.Write32(PciConfigEnableMask | (uint)offset); + result = pciConfigDataPort.Read8(); + break; - int GetPackageLength() - { - byte b = Read8(); - int length = (int)b & 0x3f; - int following = ((int)b & 0xc) >> 6; - while (following-- > 0) { - length = length << 8; - length += (int)Read8(); + default: + throw new Exception("Unimplemented operation region type" + regionSpace); } - return length; +#if DUMP_RAW_READ_WRITES + DebugStub.WriteLine("ACPI read: space: " + regionSpace + ", offset: " + offset + ", bytes: " + 1 + ", result: " + result.ToString("X")); +#endif + return result; } - void Reset() + public void Write8(AcpiObject.RegionSpace regionSpace, ulong offset, byte value) { - cursor = 0; - } +#if DUMP_RAW_READ_WRITES + DebugStub.WriteLine("ACPI write: space: " + regionSpace + ", offset: " + offset + ", bytes: " + 1 + ", value: " + value.ToString("X")); +#endif + switch (regionSpace) { + case AcpiObject.RegionSpace.SystemMemory: + IoMemory region = IoMemory.MapPhysicalMemory(offset, 1, true/*readable*/, true/*writable*/); + region.Write8(0, value); + break; - internal void Display() - { - while (cursor < this.memory.Length) { - DisplayPackage(0); + case AcpiObject.RegionSpace.SystemIO: + IoPort port = new IoPort((ushort)offset, 1, Access.ReadWrite); + port.Write8(value); + break; + + case AcpiObject.RegionSpace.PCI_Config: + pciConfigAddressPort.Write32(PciConfigEnableMask | (uint)offset); + pciConfigDataPort.Write8(value); + break; + + default: + throw new Exception("Unimplemented operation region type" + regionSpace); } } - void DisplayPackage(int depth) + public ushort Read16(AcpiObject.RegionSpace regionSpace, ulong offset) { - int length = GetPackageLength(); - int oldCursor = this.cursor; - DebugStub.Print("Package Length {0} Name {1}{2}{3}{4}", - __arglist(length, - (char)Read8(), - (char)Read8(), - (char)Read8(), - (char)Read8())); - cursor = oldCursor + length; + ushort result; + switch (regionSpace) { + case AcpiObject.RegionSpace.SystemMemory: + // TODO: This is a first stab - ideally the AcpiObject.OperationRegion + // ought to be holding onto an IoMemoryRange and passing it in. + IoMemory region = IoMemory.MapPhysicalMemory(offset, 2, true/*readable*/, false/*writable*/); + result = region.Read16(0); + break; + + case AcpiObject.RegionSpace.SystemIO: + IoPort port = new IoPort((ushort)offset, 2, Access.Read); + result = port.Read16(); + break; + + case AcpiObject.RegionSpace.PCI_Config: + pciConfigAddressPort.Write32(PciConfigEnableMask | (uint)offset); + result = pciConfigDataPort.Read16(); + break; + + default: + throw new Exception("Unimplemented operation region type" + regionSpace); + } + +#if DUMP_RAW_READ_WRITES + DebugStub.WriteLine("ACPI read: space: " + regionSpace + ", offset: " + offset + ", bytes: " + 2 + ", result: " + result.ToString("X")); +#endif + return result; + } + + public void Write16(AcpiObject.RegionSpace regionSpace, ulong offset, ushort value) + { +#if DUMP_RAW_READ_WRITES + DebugStub.WriteLine("ACPI write: space: " + regionSpace + ", offset: " + offset + ", bytes: " + 2 + ", value: " + value.ToString("X")); +#endif + switch (regionSpace) { + case AcpiObject.RegionSpace.SystemMemory: + IoMemory region = IoMemory.MapPhysicalMemory(offset, 2, true/*readable*/, true/*writable*/); + region.Write16(0, value); + break; + + case AcpiObject.RegionSpace.SystemIO: + IoPort port = new IoPort((ushort)offset, 2, Access.ReadWrite); + port.Write16(value); + break; + + case AcpiObject.RegionSpace.PCI_Config: + pciConfigAddressPort.Write32(PciConfigEnableMask | (uint)offset); + pciConfigDataPort.Write16(value); + break; + + default: + throw new Exception("Unimplemented operation region type" + regionSpace); + } + } + + public uint Read32(AcpiObject.RegionSpace regionSpace, ulong offset) + { + uint result; + switch (regionSpace) { + case AcpiObject.RegionSpace.SystemMemory: + IoMemory region = IoMemory.MapPhysicalMemory(offset, 4, true/*readable*/, false/*writable*/); + result = region.Read32(0); + break; + + case AcpiObject.RegionSpace.SystemIO: + IoPort port = new IoPort((ushort)offset, 4, Access.Read); + result = port.Read32(); + break; + + case AcpiObject.RegionSpace.PCI_Config: + pciConfigAddressPort.Write32(PciConfigEnableMask | (uint)offset); + result = pciConfigDataPort.Read32(); + break; + + default: + throw new Exception("Unimplemented operation region type" + regionSpace); + } +#if DUMP_RAW_READ_WRITES + DebugStub.WriteLine("ACPI read: space: " + regionSpace + ", offset: " + offset + ", bytes: " + 4 + ", result: " + result.ToString("X")); +#endif + return result; + } + + public void Write32(AcpiObject.RegionSpace regionSpace, ulong offset, uint value) + { +#if DUMP_RAW_READ_WRITES + DebugStub.WriteLine("ACPI write: space: " + regionSpace + ", offset: " + offset + ", bytes: " + 4 + ", value: " + value.ToString("X")); +#endif + switch (regionSpace) { + case AcpiObject.RegionSpace.SystemMemory: + IoMemory region = IoMemory.MapPhysicalMemory(offset, 4, true/*readable*/, true/*writable*/); + region.Write32(0, value); + break; + + case AcpiObject.RegionSpace.SystemIO: + IoPort port = new IoPort((ushort)offset, 4, Access.ReadWrite); + port.Write32(value); + break; + + case AcpiObject.RegionSpace.PCI_Config: + pciConfigAddressPort.Write32(PciConfigEnableMask | (uint)offset); + pciConfigDataPort.Write32(value); + break; + + default: + throw new Exception("Unimplemented operation region type" + regionSpace); + } + } + + public byte[] ReadBytes(AcpiObject.RegionSpace regionSpace, ulong offset, ulong length) + { + byte[] result = new byte[length]; + + switch (regionSpace) { + case AcpiObject.RegionSpace.SystemMemory: + IoMemory region = IoMemory.MapPhysicalMemory(offset, length, true/*readable*/, false/*writable*/); + for (ulong i = 0; i < length; i++) { + result[i] = region.Read8((int)i); + } + break; + + default: + throw new Exception("ReadBytes() only supported for SystemMemory regions"); + } + +#if DUMP_RAW_READ_WRITES + DebugStub.Write("ACPI read: space: " + regionSpace + ", offset: " + offset + ", bytes: " + length + ", result: {"); + for (int i = 0; i < result.Length; i++) { + DebugStub.Write(result[i].ToString("X")); + if (i < result.Length - 1) { + DebugStub.Write(","); + } + } + DebugStub.WriteLine("}"); +#endif + + return result; + } + } + } + + public class AcpiDevice + { + string deviceId; + ResourceDescriptor[] resourceDescriptors; + + public AcpiDevice(string deviceId, ResourceDescriptor[] resourceDescriptors) + { + this.deviceId = deviceId; + this.resourceDescriptors = resourceDescriptors; + } + + public string DeviceId + { + get + { + return deviceId; + } + } + + public ResourceDescriptor[] ResourceDescriptors { + get + { + return resourceDescriptors; } } } diff --git a/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AcpiNamespace.cs b/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AcpiNamespace.cs new file mode 100644 index 0000000..5f2da99 --- /dev/null +++ b/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AcpiNamespace.cs @@ -0,0 +1,483 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Microsoft Research Singularity +// + +using System; +using System.Collections; +using System.Diagnostics; +using System.Text; + +namespace Microsoft.Singularity.Hal.Acpi +{ + public class AcpiNamespace + { + /// + /// The absolute or relative location of a node in the ACPI namespace. + /// + public class NodePath + { + bool isAbsolute; + int numParentPrefixes; + string[] nameSegments; + + public NodePath(bool isAbsolute, int numParentPrefixes, string[] nameSegments) + { + Debug.Assert(numParentPrefixes >= 0, "numParentPrefixes >= 0"); + Debug.Assert(!isAbsolute || numParentPrefixes == 0, + "Absolute path cannot have parent prefixes"); + Debug.Assert(nameSegments.Length >= 0); + + foreach (string name in nameSegments) { + AssertIsValidName(name); + } + + this.isAbsolute = isAbsolute; + this.numParentPrefixes = numParentPrefixes; + this.nameSegments = nameSegments; + } + + public bool IsAbsolute + { + get + { + return isAbsolute; + } + } + + public int NumParentPrefixes + { + get + { + return numParentPrefixes; + } + } + + public string[] NameSegments + { + get + { + return nameSegments; + } + } + + public NodePath AddSegment(string segment) + { + string[] resultNameSegments = new string[this.nameSegments.Length + 1]; + for (int i = 0; i < this.nameSegments.Length; i++) { + resultNameSegments[i] = (string)this.nameSegments[i]; + } + resultNameSegments[resultNameSegments.Length - 1] = segment; + return new NodePath(IsAbsolute, NumParentPrefixes, resultNameSegments); + } + + public NodePath RemoveSegment() + { + string[] resultNameSegments = new string[this.nameSegments.Length - 1]; + for (int i = 0; i < this.nameSegments.Length - 1; i++) { + resultNameSegments[i] = (string)this.nameSegments[i]; + } + return new NodePath(IsAbsolute, NumParentPrefixes, resultNameSegments); + } + + public override string ToString() + { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < nameSegments.Length - 1; i++) { + builder.Append(nameSegments[i]); + builder.Append("\\"); + } + if (nameSegments.Length > 0) { + builder.Append(nameSegments[nameSegments.Length - 1]); + } + return builder.ToString(); + } + } + + /// + /// A restricted type of NodePath that must be statically absolute. + /// + /// Not every absolute path has this type - some are AbsoluteNodePaths + /// and some are NodePaths. The purpose of this subclass is to statically enforce + /// that absolute paths are always used in certain limited contexts. + public class AbsoluteNodePath : NodePath + { + public AbsoluteNodePath(string[] nameSegments) + : base(false, 0, nameSegments) { } + + public static AbsoluteNodePath CreateRoot() + { + return new AbsoluteNodePath(new string[] { }); + } + + public AbsoluteNodePath AddSegmentAbsolute(string segment) + { + return new AbsoluteNodePath(AddSegment(segment).NameSegments); + } + + public AbsoluteNodePath RemoveSegmentAbsolute() + { + return new AbsoluteNodePath(RemoveSegment().NameSegments); + } + } + + /// + /// A node in the ACPI namespace tree. + /// + public class Node + { + private string name; + private Node parentNode; + private IDictionary childByNameMap = null; + private AcpiObject.AcpiObjectCell value; + + /// + /// Create a root node. + /// + public Node() + { + this.name = ""; + this.parentNode = null; + this.value = new AcpiObject.AcpiObjectCell(new AcpiObject.UninitializedObject()); + } + + /// + /// Create a child node with a given name and parent node. + /// + public Node(string name, Node parentNode) + { + AssertIsValidName(name); + Debug.Assert(parentNode != null); + this.name = name; + this.parentNode = parentNode; + this.value = new AcpiObject.AcpiObjectCell(new AcpiObject.UninitializedObject()); + parentNode.AddChild(this); + } + + /// + /// Makes this node refer to the same value as the given node. + /// + /// Aliased names refer to not only the same object but + /// the same object location (cell). Any updates to either node + /// immediately appear in the other. + public void AliasTo(Node n) + { + this.value = n.value; + } + + private void AddChild(Node child) + { + Debug.Assert(child.parentNode == this); + if (childByNameMap == null) { + childByNameMap = new SortedList(); + } + Debug.Assert(!childByNameMap.Contains(child.name)); + childByNameMap.Add(child.name, child); + } + + public Node ParentNode + { + get + { + return parentNode; + } + } + + public string Name + { + get + { + return name; + } + } + + public Node GetChildByName(string name) + { + if (childByNameMap != null && childByNameMap.Contains(name)) { + return (Node)childByNameMap[name]; + } + else { + return null; + } + } + + public AcpiObject.AcpiObject Value + { + get + { + return value.Value; + } + set + { + this.value.Value = value; + } + } + + public void Remove() + { + if (childByNameMap != null) { + foreach (Node child in childByNameMap.Values) { + child.Remove(); + } + } + if (this.parentNode != null) { + this.parentNode.childByNameMap.Remove(name); + } + } + + public AbsoluteNodePath Path + { + get + { + // Measure how deep this node's path is + int level = 0; + Node n = parentNode; + while (n != null) { + level++; + n = n.parentNode; + } + + // Build the path + string[] nameSegments = new string[level]; + n = this; + for(int i = nameSegments.Length - 1; i >= 0; i--) { + nameSegments[i] = n.name; + n = n.parentNode; + } + + return new AbsoluteNodePath(nameSegments); + } + } + + public int CountSubtreeNodes() + { + int count = 1; + if (childByNameMap != null) { + foreach (Node child in childByNameMap.Values) { + count += child.CountSubtreeNodes(); + } + } + return count; + } + + public void GetSubtreeNodes(Node[] result, ref int startIndex) + { + result[startIndex] = this; + startIndex++; + + if (childByNameMap != null) { + foreach (Node child in childByNameMap.Values) { + child.GetSubtreeNodes(result, ref startIndex); + } + } + } + + public Node FindValue(AcpiObject.AcpiObject value) + { + if (this.Value == value) { + return this; + } + else if (childByNameMap != null) { + foreach (Node child in childByNameMap.Values) { + Node result = child.FindValue(value); + if (result != null) { + return result; + } + } + } + return null; + } + + public override string ToString() + { + return Path.ToString(); + } + + public void DumpSubtree() + { +#if SINGULARITY_KERNEL + DebugStub.WriteLine(this.ToString()); +#else + Console.WriteLine(this.ToString()); +#endif + if (childByNameMap != null) { + foreach (Node child in childByNameMap.Values) { + child.DumpSubtree(); + } + } + } + } + + public class NodeAlreadyExistsException : Exception + { + public NodeAlreadyExistsException() + : base("Node already exists") + { + } + + public NodeAlreadyExistsException(string message) + : base("Node already exists: " + message) + { + } + } + + private Node rootNode = new Node(); + + public AcpiNamespace() + { + } + + /// + /// Look up the node at a given absolute node path. + /// + /// The requested node, or null if the node is not found. + public Node LookupNode(AbsoluteNodePath nodePath) + { + return LookupDescendantNode(rootNode, nodePath.NameSegments); + } + + /// + /// Look up the node at a given absolute or relative node path. + /// This may include "single segment name" paths which invoke the + /// search strategy described in section 5.3 of the ACPI spec 3.0b. + /// + /// Absolute path of the current path + /// relative to which any relative node path will be determined. + /// The requested node, or null if the node is not found. + public Node LookupNode(NodePath nodePath, AbsoluteNodePath currentPath) + { + if (nodePath.IsAbsolute) { + return LookupDescendantNode(rootNode, nodePath.NameSegments); + } + else { + Node startNode = LookupNode(currentPath); + Debug.Assert(startNode != null, "LookupNode: Current path did not resolve"); + + if (nodePath.NumParentPrefixes > 0) { + for (int i = 0; i < nodePath.NumParentPrefixes; i++) { + startNode = startNode.ParentNode; + if (startNode == null) { + throw new ArgumentException("Relative path attempts to move above root"); + } + } + return LookupDescendantNode(startNode, nodePath.NameSegments); + } + else { + // The search rules apply - try successively larger scopes + while (startNode != null) { + Node result = LookupDescendantNode(startNode, nodePath.NameSegments); + if (result != null) { + return result; + } + startNode = startNode.ParentNode; + } + return null; + } + } + } + + /// + /// A version of LookupNode that does not ever apply the search rules. + /// + private Node LookupNodeNoSearch(NodePath nodePath, AbsoluteNodePath currentPath) + { + if (nodePath.IsAbsolute) { + return LookupDescendantNode(rootNode, nodePath.NameSegments); + } + else { + Node startNode = LookupNode(currentPath); + Debug.Assert(startNode != null, "LookupNode: Current path did not resolve"); + + for (int i = 0; i < nodePath.NumParentPrefixes; i++) { + startNode = startNode.ParentNode; + if (startNode == null) { + throw new ArgumentException("Relative path attempts to move above root"); + } + } + return LookupDescendantNode(startNode, nodePath.NameSegments); + } + } + + private Node LookupDescendantNode(Node node, string[] nameSegments) + { + Node n = node; + foreach (string name in nameSegments) { + n = n.GetChildByName(name); + if (n == null) { + break; + } + } + return n; + } + + public Node CreateNodeAt(NodePath nodePath, AbsoluteNodePath currentPath) + { + if (LookupNodeNoSearch(nodePath, currentPath) != null) { + throw new NodeAlreadyExistsException(); + } + + // Find deepest existing node on the path + NodePath ancestorNodePath = nodePath; + Node ancestorNode; + int stepsUp = 0; + do { + ancestorNodePath = ancestorNodePath.RemoveSegment(); + stepsUp++; + ancestorNode = LookupNodeNoSearch(ancestorNodePath, currentPath); + } while (ancestorNode == null); + + // Create remaining names to get a node with the desired path + Node newNode = null; + string[] nameSegments = nodePath.NameSegments; + for (int i = nameSegments.Length - stepsUp; i < nameSegments.Length; i++) { + newNode = new Node((string)nameSegments[i], ancestorNode); + ancestorNode = newNode; + } + + return newNode; + } + + public Node FindValue(AcpiObject.AcpiObject value) + { + return rootNode.FindValue(value); + } + + public Node[] GetAllNodes() + { + Node[] result = new Node[rootNode.CountSubtreeNodes()]; + int startIndex = 0; + rootNode.GetSubtreeNodes(result, ref startIndex); + return result; + } + + private static void AssertIsValidName(string name) + { + Debug.Assert(IsValidName(name), "Invalid name for ACPI namespace node"); + } + + public void DumpTree() + { + rootNode.DumpSubtree(); + } + + internal static bool IsValidName(byte[] name) + { + return name.Length == 4 && + (Char.IsUpper((char)name[0]) || (char)name[0] == '_') && + (Char.IsUpper((char)name[1]) || (char)name[1] == '_' || Char.IsDigit((char)name[1])) && + (Char.IsUpper((char)name[2]) || (char)name[2] == '_' || Char.IsDigit((char)name[2])) && + (Char.IsUpper((char)name[3]) || (char)name[3] == '_' || Char.IsDigit((char)name[3])); + } + + internal static bool IsValidName(string name) + { + return name.Length == 4 && + (Char.IsUpper(name[0]) || name[0] == '_') && + (Char.IsUpper(name[1]) || name[1] == '_' || Char.IsDigit(name[1])) && + (Char.IsUpper(name[2]) || name[2] == '_' || Char.IsDigit(name[2])) && + (Char.IsUpper(name[3]) || name[3] == '_' || Char.IsDigit(name[3])); + } + } +} diff --git a/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AcpiObject.cs b/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AcpiObject.cs new file mode 100644 index 0000000..6cd6fe7 --- /dev/null +++ b/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AcpiObject.cs @@ -0,0 +1,1365 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Microsoft Research Singularity +// + +using System; +using System.Collections; +using System.Diagnostics; +using System.Text; + +using TermObj = Microsoft.Singularity.Hal.Acpi.AmlParserUnions.TermObj; +using Microsoft.Singularity.Hal.Acpi.StackIR; +using Microsoft.Singularity.Hal.Acpi.AmlParserUnions; + +namespace Microsoft.Singularity.Hal.Acpi.AcpiObject +{ + // + // Based roughly on Table 17-6 from the ACPI Specification 3.0b, the + // following class hierarchy describes ACPI objects, which constitute + // the values assigned to each named node in the ACPI namespace as well + // as all values assigned to temporary objects, local objects, and so on. + // They are created and used at both load time and run time. + // + + /// + /// Values returned by ObjectType operator as described in section + /// 17.5.86 of the ACPI specification 3.0b. + /// + public enum AcpiObjectType + { + Uninitialized = 0, + Integer = 1, + String = 2, + Buffer = 3, + Package = 4, + FieldUnit = 5, + Device = 6, + Event = 7, + Method = 8, + Mutex = 9, + OperationRegion = 10, + PowerResource = 11, + Processor = 12, + ThermalZone = 13, + BufferField = 14, + DdbHandle = 15, + DebugObject = 16 + } + + public abstract class AcpiObject + { + public abstract Integer ObjectType(); + + public abstract void Write(AcpiObject value); + + /// + /// Get the value referred to by this value, for creating indirection. + /// Returns self for concrete types. + /// + public virtual AcpiObject GetTarget() + { + return this; + } + + public virtual Integer GetAsInt() + { + throw new AmlTypeException(); + } + + public virtual String GetAsString() + { + throw new AmlTypeException(); + } + + public virtual Buffer GetAsBuffer() + { + throw new AmlTypeException(); + } + + public virtual Method GetAsMethod() + { + throw new AmlTypeException(); + } + + public virtual Mutex GetAsMutex() + { + throw new AmlTypeException(); + } + + public virtual Device GetAsDevice() + { + throw new AmlTypeException(); + } + + public virtual ulong Size + { + get + { + throw new AmlTypeException(); + } + } + + public virtual AcpiObject Dereference() + { + throw new AmlTypeException(); + } + + public virtual AcpiObject Index(ulong index) + { + throw new AmlTypeException(); + } + + public virtual AcpiObject[] GetObjects() + { + throw new AmlTypeException(); + } + + public virtual void SetIndex(ulong index, AcpiObject value) + { + throw new AmlTypeException(); + } + } + + /// + /// Contains a reference to an AcpiObject - this added level of indirection + /// is necessary to permit AcpiObject objects to be modified through + /// ObjectReference objects. + /// + public class AcpiObjectCell + { + AcpiObject containedValue; + + public AcpiObjectCell(AcpiObject containedValue) + { + this.containedValue = containedValue; + } + + public AcpiObject Value + { + get + { + return containedValue; + } + set + { + if (containedValue is UninitializedObject) { + containedValue = value; + } + else { + containedValue.Write(value); + } + } + } + } + + /// + /// An uninitialized ACPI object. + /// + /// From Table 17-6: No assigned type or value. This is the type + /// of all control method LocalX variables and unused ArgX variables at + /// the beginning of method execution, as well as all uninitialized + /// Package elements. Uninitialized objects must be initialized (via + /// Store or CopyObject) before they may be used as source operands in + /// ASL expressions. + public class UninitializedObject : AcpiObject + { + public override Integer ObjectType() + { + return new Integer((ulong)AcpiObjectType.Uninitialized); + } + + public override void Write(AcpiObject value) + { + throw new AmlTypeException("Cannot write to uninitialized object"); + } + } + + /// + /// An ACPI buffer object representing an array of bytes. + /// + /// From Table 17-6: An array of bytes. Uninitialized elements + /// are zero by default. + public class Buffer : AcpiObject + { + byte[] contents; + + public Buffer(byte[] contents) + { + this.contents = contents; + } + + public override Integer ObjectType() + { + return new Integer((ulong)AcpiObjectType.Buffer); + } + + public override AcpiObject Index(ulong index) + { + return new Integer(contents[index]); + } + + public override void SetIndex(ulong index, AcpiObject value) + { + contents[index] = (byte)value.GetAsInt().Value; + } + + public byte[] Contents + { + get + { + return (byte[])contents.Clone(); + } + } + + public override Buffer GetAsBuffer() + { + return this; + } + + public override string ToString() + { + StringBuilder result = new StringBuilder(); + result.Append("{"); + bool first = true; + foreach (byte b in contents) { + if (first) { + first = false; + } + else { + result.Append(", "); + } + result.Append(b.ToString("x")); + } + result.Append("}"); + return result.ToString(); + } + + public override void Write(AcpiObject value) + { + throw new AmlTypeException("TODO"); + } + } + + /// + /// An ACPI buffer field object representing a portion of a buffer. + /// + /// From Table 17-6: Portion of a buffer created using + /// CreateBitField, CreateByteField, CreateWordField, CreateQWordField, + /// CreateField, or returned by the Index operator. + public class BufferField : AcpiObject + { + AcpiObject sourceBuffer = null; + ulong startBitIndex; + ulong numBits; + + public BufferField(AcpiObject sourceBuffer, ulong startBitIndex, ulong numBits) + { + if (numBits > sizeof(ulong) * 8) { + throw new AmlTypeException("Tried to create field with more than maximum number of bits"); + } + + this.sourceBuffer = sourceBuffer; + this.startBitIndex = startBitIndex; + this.numBits = numBits; + } + + public override Integer ObjectType() + { + return new Integer((ulong)AcpiObjectType.BufferField); + } + + public override Integer GetAsInt() + { + return (Integer)Dereference(); + } + + public override AcpiObject Dereference() + { + if ((startBitIndex % 8) != 0 || (numBits % 8) != 0) { + throw new AmlTypeException("TODO: Non-byte-aligned buffer fields"); + } + + ulong result = 0; + // Loop from first byte to last + for(ulong idx = startBitIndex/8u; idx < startBitIndex/8u + numBits/8u; idx++) { + result = (result << 8) | sourceBuffer.Index(idx).GetAsInt().Value; + } + return new Integer(result); + } + + public override void Write(AcpiObject valueObj) + { + if ((startBitIndex % 8) != 0 || (numBits % 8) != 0) { + throw new AmlTypeException("TODO: Non-byte-aligned buffer fields"); + } + + ulong value = valueObj.GetAsInt().Value; + + // We ignore high bits above the number of bits fitting in the field. + // Used to check this, but some code actually depends on the behavior of + // truncating high bits, like this from VPC: + + // Method (_CRS, 0, NotSerialized) + // { + // CreateDWordField (CRS, \_SB.SYSM._Y10._BAS, BAS4) + // CreateDWordField (CRS, \_SB.SYSM._Y10._LEN, LEN4) + // Subtract (0x00, BAS4, LEN4) + // } + + // NB write in little endian byte-order + ulong end = startBitIndex/8u + numBits/8u; + for (ulong idx = startBitIndex / 8u; idx < end; idx++) { + sourceBuffer.SetIndex(idx, new Integer(value & 0xFF)); + value >>= 8; + } + } + } + + /// + /// An ACPI DDB handle object referring to a Differentiated Definition Block. + /// + /// From Table 17-6: Definition block handle returned + /// by the Load operator [and passed to the Unload operator]. + public class DdbHandle : AcpiObject + { + // TODO: The representation of this will come from the table loading code + // and depend on the implementation of Load and Unload. + + private DdbHandle() { } // Prevent construction until completed + + public override Integer ObjectType() + { + return new Integer((ulong)AcpiObjectType.DdbHandle); + } + + public override void Write(AcpiObject value) + { + throw new AmlTypeException("TODO"); + } + } + + /// + /// An ACPI debug object used for formatting and printing debug output. + /// + /// From Table 17-6: Debug output object. Formats an object + /// and prints it to the system debug port. Has no effect if debugging + /// is not active. Section 17.5.23: The debug data object is a virtual + /// data object. Writes to this object provide debugging information. + /// On at least debug versions of the interpreter, any writes into this + /// object are appropriately displayed on the system’s native kernel + /// debugger. All writes to the debug object are otherwise benign. If + /// the system is in use without a kernel debugger, then writes to the + /// debug object are ignored. + public class DebugObject : AcpiObject + { + private DebugObject() { } + + public readonly DebugObject Instance = new DebugObject(); + + public override Integer ObjectType() + { + return new Integer((ulong)AcpiObjectType.DebugObject); + } + + public override void Write(AcpiObject value) + { +#if SINGULARITY_KERNEL + DebugStub.Write(value.ToString()); +#else + Console.Write(value.ToString()); +#endif + } + } + + /// + /// An ACPI device object representing a device or bus. + /// + /// From Table 17-6: Device or bus object + public class Device : AcpiObject + { + AcpiNamespace.AbsoluteNodePath path; + + public Device(AcpiNamespace.AbsoluteNodePath path) + { + this.path = path; + } + + public override Integer ObjectType() + { + return new Integer((ulong)AcpiObjectType.Device); + } + + public override void Write(AcpiObject value) + { + throw new AmlTypeException("TODO"); + } + + public AcpiNamespace.AbsoluteNodePath Path + { + get + { + return path; + } + } + + public override Device GetAsDevice() + { + return this; + } + } + + /// + /// An ACPI event synchronization object + /// + /// From Table 17-6: Event synchronization object + public class Event : AcpiObject + { + public Event() + { + // No data - an event is described entirely by its name + } + + public override Integer ObjectType() + { + return new Integer((ulong)AcpiObjectType.Event); + } + + public override void Write(AcpiObject value) + { + throw new AmlTypeException("TODO"); + } + } + + // + // Enums based on FieldFlags rule in AML grammar, + // ACPI specification 3.0b section 18.2.5.2 + // + + public enum AccessType + { + AnyAcc = 0, + ByteAcc = 1, + WordAcc = 2, + DWordAcc = 3, + QWordAcc = 4, + BufferAcc = 5, + Reserved1 = 6, + Reserved2 = 7, + Reserved3 = 8, + Reserved4 = 9, + Reserved5 = 10, + Reserved6 = 11, + Reserved7 = 12, + Reserved8 = 13, + Reserved9 = 14, + Reserved10 = 15 + } + + public enum LockRule + { + NoLock = 0, + Lock = 1 + } + + public enum UpdateRule + { + Preserved = 0, + WriteAsOnes = 1, + WriteAsZeros = 2, + Invalid = 3 + } + + public enum AccessAttrib + { + SMBNone = 0x00, + SMBQuick = 0x02, + SMBSendReceive = 0x04, + SMBByte = 0x06, + SMBWord = 0x08, + SMBBlock = 0x0A, + SMBProcessCall = 0x0C, + SMBBlockProcessCall = 0x0D + } + + /// + /// An ACPI field unit object referring to a portion of an address space. + /// + /// From Table 17-6: (within an Operation Region) Portion of an + /// address space, bit-aligned and of one-bit granularity. Created using + /// Field, BankField, or IndexField. + public class FieldUnit : AcpiObject + { + OperationRegion operationRegion; + int startBitIndex; + int numBits; + AccessType accessType; + AccessAttrib accessAttrib; + LockRule lockRule; + UpdateRule updateRule; + + public FieldUnit(OperationRegion operationRegion, int startBitIndex, int numBits, + AccessType accessType, AccessAttrib accessAttrib, + LockRule lockRule, UpdateRule updateRule) + { + if (startBitIndex < 0 || (startBitIndex + numBits + 7)/8 > (int)operationRegion.Length) { + throw new ArgumentException("Field unit not in bounds of operation region"); + } + + this.operationRegion = operationRegion; + this.startBitIndex = startBitIndex; + this.numBits = numBits; + this.accessType = accessType; + this.accessAttrib = accessAttrib; + this.lockRule = lockRule; + this.updateRule = updateRule; + } + + public override Integer GetAsInt() + { + return new Integer(Read()); + } + + public override Buffer GetAsBuffer() + { + if ((startBitIndex % 8) == 0) { + return new Buffer(operationRegion.ReadBytes((ulong)(startBitIndex / 8), + (ulong)(numBits + 7)/8)); + } + else { + throw new Exception("Unimplemented conversion of unaligned field to buffer"); + } + } + + public ulong Read() + { + if (numBits == 8 && (startBitIndex % 8) == 0) { + return operationRegion.Read8At((ulong)(startBitIndex / 8)); + } + else if (numBits == 16 && (startBitIndex % 8) == 0) { + return operationRegion.Read16At((ulong)(startBitIndex / 8)); + } + else if (numBits == 32 && (startBitIndex % 8) == 0) { + return operationRegion.Read32At((ulong)(startBitIndex / 8)); + } + else { + // Small field or unaligned, do general case + int bitIndex = startBitIndex; + int remainingBits = numBits; + + uint result = 0; + while (remainingBits > 0) { + byte b = operationRegion.Read8At((ulong)(bitIndex / 8)); + for (int bitInWord = bitIndex % 8; + bitInWord < 8 && remainingBits > 0; + bitInWord++, bitIndex++, remainingBits--) { + result = (result << 1) | (uint)((b >> (7 - bitInWord)) & 1); + } + } + + return result; + } + } + + /// + /// This is currently just used by IndexField, will probably become accessible + /// from AML when implementing stores. + /// + public override void Write(AcpiObject valueObj) + { + ulong value = valueObj.GetAsInt().Value; + + Debug.Assert(AcpiObjectUtils.GetNumBits(value) <= (ulong)numBits, "Writing value too large for field"); + + if (numBits == 8 && (startBitIndex % 8) == 0) { + operationRegion.Write8At((ulong)(startBitIndex / 8), (byte)value); + } + else if (numBits == 16 && (startBitIndex % 8) == 0) { + operationRegion.Write16At((ulong)(startBitIndex / 8), (byte)value); + } + else if (numBits == 32 && (startBitIndex % 8) == 0) { + operationRegion.Write32At((ulong)(startBitIndex / 8), (byte)value); + } + else { + throw new Exception("Unimplemented operation region field size"); + } + } + + public override Integer ObjectType() + { + return new Integer((ulong)AcpiObjectType.FieldUnit); + } + + // SortedList will be Dictionary when generics are available + public static SortedList CreateFromFieldList(OperationRegion operationRegionNode, + FieldElement[] fieldList, + AccessType initialAccessType, + AccessAttrib initialAccessAttrib, + LockRule lockRule, + UpdateRule updateRule + ) + { + SortedList result = new SortedList(); // = new Dictionary(); + AccessType accessType = initialAccessType; + AccessAttrib accessAttrib = initialAccessAttrib; + int bitIndex = 0; + + foreach (FieldElement fieldElement in fieldList) { + switch (fieldElement.Tag) { + case FieldElement.TagValue.NamedField: + AmlParser.NamedField namedField = fieldElement.GetAsNamedField(); + result.Add(namedField.nameSeg.data, + new FieldUnit(operationRegionNode, + bitIndex, namedField.bitWidth, + accessType, accessAttrib, lockRule, updateRule)); + bitIndex += namedField.bitWidth; + break; + case FieldElement.TagValue.ReservedField: + AmlParser.ReservedField reservedField = fieldElement.GetAsReservedField(); + bitIndex += reservedField.bitWidth; + break; + case FieldElement.TagValue.AccessField: + AmlParser.AccessField accessField = fieldElement.GetAsAccessField(); + accessType = accessField.accessType; + accessAttrib = accessField.accessAttrib; + break; + default: + throw new LoadException("Unhandled alternative in switch over 'FieldElement'"); + } + } + + return result; + } + } + + /// + /// This operation region accessor accesses internal device registers + /// through an index/data pair (typically a pair of I/O ports inside an + /// I/O operation region). It is used by IndexField definitions, which + /// create FieldUnits inside these internal operation regions. The + /// RegionSpace type is ignored. + /// + public class IndexFieldOperationRegionAccessor : IOperationRegionAccessor + { + FieldUnit index; + FieldUnit data; + + public IndexFieldOperationRegionAccessor(FieldUnit index, FieldUnit data) + { + this.index = index; + this.data = data; + } + + public byte Read8(RegionSpace regionSpace, ulong offset) + { + index.Write(new Integer(offset)); + return (byte)data.Read(); + } + + public void Write8(RegionSpace regionSpace, ulong offset, byte value) + { + index.Write(new Integer(offset)); + data.Write(new Integer(value)); + } + + public ushort Read16(RegionSpace regionSpace, ulong offset) + { + index.Write(new Integer(offset)); + return (ushort)data.Read(); + } + + public void Write16(RegionSpace regionSpace, ulong offset, ushort value) + { + index.Write(new Integer(offset)); + data.Write(new Integer(value)); + } + + public uint Read32(RegionSpace regionSpace, ulong offset) + { + index.Write(new Integer(offset)); + return (uint)data.Read(); + } + + public void Write32(RegionSpace regionSpace, ulong offset, uint value) + { + index.Write(new Integer(offset)); + data.Write(new Integer(value)); + } + + public byte[] ReadBytes(RegionSpace regionSpace, ulong offset, ulong length) + { + throw new Exception("Unimplemented ReadBytes() from index field"); + } + } + + /// + /// An ACPI integer object + /// + /// From Table 17-6: An n-bit little-endian unsigned integer. + /// In ACPI 1.0 this was at least 32 bits. In ACPI 2.0 and later, this + /// is 64 bits. + public class Integer : AcpiObject + { + ulong value; + + public Integer(ulong value) + { + this.value = value; + } + + public ulong Value + { + get + { + return value; + } + } + + public override Integer ObjectType() + { + return new Integer((ulong)AcpiObjectType.Integer); + } + + public override Integer GetAsInt() + { + if (this == IntegerConstant.Zero || + this == IntegerConstant.One || + this == IntegerConstant.Ones) { + // Reading an IntegerConstant does not get the singleton object + // but just the value in it. + return new Integer(value); + } + return this; + } + + public override void Write(AcpiObject value) + { + if (this == IntegerConstant.Zero || + this == IntegerConstant.One || + this == IntegerConstant.Ones) { + throw new AmlTypeException("Cannot write to reserved integer constant objects"); + } + this.value = value.GetAsInt().Value; + } + + public override string ToString() + { + return value.ToString(); + } + } + + /// + /// Wrapper class for some static methods referring to ACPI integer + /// constant built-in integer constants. + /// + /// From Table 17-6: Created by the ASL terms "Zero", "One", + /// "Ones", and "Revision". We did not create a subclass for these + /// because ObjectType has no "IntegerConstant" type, only Integer. + public class IntegerConstant + { + private IntegerConstant() { } // Don't allow construction + + public readonly static Integer Zero = new Integer(0UL); + public readonly static Integer One = new Integer(1UL); + public readonly static Integer Ones = new Integer(~(0UL)); + public readonly static Integer Revision = new Integer(0); // TODO: What's the right value for this? + } + + // + // Enum based on MethodFlags rule in AML grammar, + // ACPI specification 3.0b section 18.2.5.2 + // + + public enum SerializeFlag + { + NotSerialized = 0, + Serialized = 1 + } + + /// + /// An ACPI method object referring to an invokable method. + /// + /// From Table 17-6: Control Method (Executable AML function) + public abstract class Method : AcpiObject + { + byte argCount; + SerializeFlag serializeFlag; + byte syncLevel; + + public Method(byte argCount, SerializeFlag serializeFlag, byte syncLevel) + { + this.argCount = argCount; + this.serializeFlag = serializeFlag; + this.syncLevel = syncLevel; + } + + public byte ArgCount + { + get + { + return argCount; + } + } + + public override Integer ObjectType() + { + return new Integer((ulong)AcpiObjectType.Method); + } + + public override Method GetAsMethod() + { + return this; + } + + public abstract void Invoke(AmlInterpreterThread thread, AcpiObject[] parameters, AcpiNamespace acpiNamespace); + + public override void Write(AcpiObject value) + { + throw new AmlTypeException("Cannot write to method"); + } + } + + /// + /// A method whose body is represented using AML bytecode which can be parsed + /// and interpreted. + /// + public class BytecodeMethod : Method + { + byte[] unparsedTermList; + StackIRNode[] body; + + public BytecodeMethod(byte argCount, SerializeFlag serializeFlag, byte syncLevel, byte[] unparsedTermList) + : base(argCount, serializeFlag, syncLevel) + { + this.unparsedTermList = unparsedTermList; + this.body = null; + } + + public AmlParser.ParseSuccess Parse(AcpiNamespace acpiNamespace, AcpiNamespace.AbsoluteNodePath initialNodePath) + { + int offset = 0; + TermObj[] termList; + if (new AmlParser(new ByteBufferAmlStreamAdapter(unparsedTermList), acpiNamespace, initialNodePath). + ParseTermList(out termList, ref offset, unparsedTermList.Length) == AmlParser.ParseSuccess.Failure) { + return AmlParser.ParseSuccess.Failure; + } + Debug.Assert(offset == unparsedTermList.Length, "offset == unparsedTermList.Length"); + + AmlToStackIRVisitor amlToStackIRVisitor = new AmlToStackIRVisitor(); + amlToStackIRVisitor.VisitSequence(termList); + body = amlToStackIRVisitor.Result; + + return AmlParser.ParseSuccess.Success; + } + + public override void Invoke(AmlInterpreterThread thread, AcpiObject[] parameters, AcpiNamespace acpiNamespace) + { + if (body == null) { + AcpiNamespace.Node node = acpiNamespace.FindValue(this); + if (Parse(acpiNamespace, node.Path) == AmlParser.ParseSuccess.Failure) { + throw new InterpretException("AML parser failure while just-in-time parsing AML method body"); + } + } + thread.PushFrame(new AmlStackFrame(body, parameters)); + } + } + + /// + /// A reserved method implemented directly by the operating system. + /// + public class ReservedMethod : Method + { + public delegate AcpiObject AcpiMethodImpl(AcpiObject[] arguments); + + private AcpiMethodImpl impl; + + public ReservedMethod(byte argCount, SerializeFlag serializeFlag, byte syncLevel, AcpiMethodImpl impl) + : base(argCount, serializeFlag, syncLevel) + { + this.impl = impl; + } + + public override void Invoke(AmlInterpreterThread thread, AcpiObject[] parameters, AcpiNamespace acpiNamespace) + { + thread.Push(new ValueIoLocation(impl(parameters))); + } + } + + /// + /// An ACPI mutex synchronization object + /// + /// From Table 17-6: Mutex synchronization object + public class Mutex : AcpiObject + { + private class AmlInterpreterThreadSet : IEnumerable + { + ArrayList set = new ArrayList(); + + public void Add(AmlInterpreterThread thread) { + set.Add(thread); + } + + public IEnumerator GetEnumerator() { + return set.GetEnumerator(); + } + + public void Clear() { + set.Clear(); + } + } + + byte syncLevel; + AmlInterpreterThread owner = null; + AmlInterpreterThreadSet waitingThreads = new AmlInterpreterThreadSet(); + + public Mutex(byte syncLevel) + { + this.syncLevel = syncLevel; + } + + public void Acquire(AmlInterpreterThread thread) { + if (owner == null) { + owner = thread; + } + else { + waitingThreads.Add(thread); + thread.Block(); + } + } + + public void Release(AmlInterpreterThread thread) { + if (owner == null) { + throw new InterpretException("Attempt to release unlocked mutex"); + } + owner = null; + + foreach (AmlInterpreterThread waitingThread in waitingThreads) { + waitingThread.Notify(); + } + waitingThreads.Clear(); + } + + public override Integer ObjectType() + { + return new Integer((ulong)AcpiObjectType.Mutex); + } + + public override Mutex GetAsMutex() + { + return this; + } + + public override void Write(AcpiObject value) + { + throw new AmlTypeException("Cannot write to mutex"); + } + } + + /// + /// See AmlLoader.LoadTimeEvaluate(AmlParser.UserTermObj userTermObj) + /// for an explanation of the existence of this object type, which is + /// not defined in the specification. + /// + public class NodePathReference : AcpiObject + { + private AcpiNamespace acpiNamespace; + private AcpiNamespace.AbsoluteNodePath nodePath; + + public NodePathReference(AcpiNamespace acpiNamespace, + AcpiNamespace.AbsoluteNodePath nodePath) + { + this.acpiNamespace = acpiNamespace; + this.nodePath = nodePath; + } + + public override Integer ObjectType() + { + return GetTarget().ObjectType(); + } + + public override AcpiObject GetTarget() + { + return acpiNamespace.LookupNode(nodePath).Value; + } + + public override Integer GetAsInt() + { + return GetTarget().GetAsInt(); + } + + public override Device GetAsDevice() + { + return GetTarget().GetAsDevice(); + } + + public override void Write(AcpiObject value) + { + GetTarget().Write(value); + } + } + + /// + /// An ACPI object reference object referring to some other AcpiObject + /// + /// From Table 17-6: Reference to an object created using the + /// RefOf, Index, or CondRefOf operators + public class ObjectReference : AcpiObject + { + AcpiObjectCell target; + + public ObjectReference(AcpiObjectCell target) + { + this.target = target; + } + + public override Integer ObjectType() + { + // From 17.5.86: if this operation is performed on an object + // reference [...] the object type of the base object is returned. + return GetTarget().ObjectType(); + } + + public override AcpiObject GetTarget() + { + return target.Value; + } + + public override Integer GetAsInt() + { + return GetTarget().GetAsInt(); + } + + public override Device GetAsDevice() + { + return GetTarget().GetAsDevice(); + } + + public override void Write(AcpiObject value) + { + GetTarget().Write(value); + } + } + + /// + /// From 17.5.89, describes the built-in region spaces in which an + /// operation region can be created. + /// + public enum RegionSpace + { + SystemMemory = 0, + SystemIO = 1, + PCI_Config = 2, + EmbeddedControl = 3, + SMBus = 4, + CMOS = 5, + PCIBARTarget = 6 + } + + /// + /// Abstracts away reading of operation regions such as arbitrary + /// memory read/writes, I/O, and PCI configuration space access. + /// + public interface IOperationRegionAccessor + { + byte[] ReadBytes(RegionSpace regionSpace, ulong offset, ulong length); + byte Read8(RegionSpace regionSpace, ulong offset); + void Write8(RegionSpace regionSpace, ulong offset, byte value); + ushort Read16(RegionSpace regionSpace, ulong offset); + void Write16(RegionSpace regionSpace, ulong offset, ushort value); + uint Read32(RegionSpace regionSpace, ulong offset); + void Write32(RegionSpace regionSpace, ulong offset, uint value); + } + + /// + /// An ACPI operation region object, representing a region within an + /// address space. + /// + /// From Table 17-6: Reference to an object created using the + /// RefOf, Index, or CondRefOf operators + public class OperationRegion : AcpiObject + { + IOperationRegionAccessor accessor; + RegionSpace regionSpace; + ulong startByteIndex; + ulong numBytes; + + public OperationRegion(IOperationRegionAccessor accessor, + RegionSpace regionSpace, ulong startByteIndex, ulong numBytes) + { + this.accessor = accessor; + this.regionSpace = regionSpace; + this.startByteIndex = startByteIndex; + this.numBytes = numBytes; + } + + public override Integer ObjectType() + { + return new Integer((ulong)AcpiObjectType.OperationRegion); + } + + public ulong Length + { + get + { + return numBytes; + } + } + + // + // These should only be used by FieldUnit. + // + + public byte[] ReadBytes(ulong offset, ulong length) + { + if (offset < 0 || offset + length > numBytes) { + throw new ArgumentOutOfRangeException(); + } + return accessor.ReadBytes(regionSpace, startByteIndex + offset, length); + } + + public byte Read8At(ulong offset) + { + if (offset < 0 || offset >= numBytes) { + throw new ArgumentOutOfRangeException(); + } + return accessor.Read8(regionSpace, startByteIndex + offset); + } + + public uint Read16At(ulong offset) + { + if (offset < 0 || offset >= numBytes) { + throw new ArgumentOutOfRangeException(); + } + return accessor.Read16(regionSpace, startByteIndex + offset); + } + + public uint Read32At(ulong offset) + { + if (offset < 0 || offset >= numBytes) { + throw new ArgumentOutOfRangeException(); + } + return accessor.Read32(regionSpace, startByteIndex + offset); + } + + public void Write8At(ulong offset, byte value) + { + if (offset < 0 || offset >= numBytes) { + throw new ArgumentOutOfRangeException(); + } + accessor.Write8(regionSpace, startByteIndex + offset, value); + } + + public void Write16At(ulong offset, ushort value) + { + if (offset < 0 || offset >= numBytes) { + throw new ArgumentOutOfRangeException(); + } + accessor.Write16(regionSpace, startByteIndex + offset, value); + } + + public void Write32At(ulong offset, uint value) + { + if (offset < 0 || offset >= numBytes) { + throw new ArgumentOutOfRangeException(); + } + accessor.Write32(regionSpace, startByteIndex + offset, value); + } + + public override void Write(AcpiObject value) + { + // Must first create fields inside operation region and then write to those + throw new AmlTypeException("Cannot write directly to operation region"); + } + } + + /// + /// An ACPI package object, a fixed-length list of other AcpiObject objects + /// + /// From Table 17-6: Collection of ASL objects with a fixed + /// number of elements (up to 255). + public class Package : AcpiObject + { + AcpiObjectCell[] objectList; + + public Package(AcpiObjectCell[] objectList) + { + this.objectList = objectList; + } + + public override Integer ObjectType() + { + return new Integer((ulong)AcpiObjectType.Package); + } + + public override AcpiObject Index(ulong index) + { + return objectList[index].Value; + } + + public override AcpiObject[] GetObjects() + { + AcpiObject[] result = new AcpiObject[objectList.Length]; + for (ulong index = 0; index < (ulong) objectList.Length; index++) { + result[index] = this.Index(index); + } + return result; + } + + public override void Write(AcpiObject value) + { + throw new AmlTypeException("Cannot write to package object"); + } + } + + /// + /// An ACPI power resource description object. + /// + /// From Table 17-6: Power Resource description object + public class PowerResource : AcpiObject + { + public byte systemLevel; + public int resourceOrder; + + public PowerResource(byte systemLevel, int resourceOrder) + { + this.systemLevel = systemLevel; + this.resourceOrder = resourceOrder; + } + + public override Integer ObjectType() + { + return new Integer((ulong)AcpiObjectType.PowerResource); + } + + public override void Write(AcpiObject value) + { + throw new AmlTypeException("Cannot write to power resource object"); + } + } + + /// + /// An ACPI processor description object. + /// + /// From Table 17-6: Processor description object. See + /// section 17.5.93. + public class Processor : AcpiObject + { + byte processorId; + uint pblkAddress; + byte pblkLength; + + public Processor(byte processorId, uint pblkAddress, byte pblkLength) + { + this.processorId = processorId; + this.pblkAddress = pblkAddress; + this.pblkLength = pblkLength; + } + + public override Integer ObjectType() + { + return new Integer((ulong)AcpiObjectType.Processor); + } + + public override void Write(AcpiObject value) + { + throw new AmlTypeException("Cannot write to processor object"); + } + } + + /// + /// An ACPI string object. + /// + /// From Table 17-6: Null-terminated ASCII string. + public class String : AcpiObject + { + string contents; + + public String(string contents) + { + for (int i = 0; i < contents.Length; i++) { + if (contents[i] == '\0') { + Debug.Assert(false, "ACPI string contains embedded null"); + } + } + this.contents = contents; + } + + public override Integer ObjectType() + { + return new Integer((ulong)AcpiObjectType.String); + } + + public override ulong Size + { + get + { + return (ulong)contents.Length; + } + } + + public override AcpiObject Index(ulong index) + { + return new Integer(contents[(int)index]); + } + + public string Value + { + get + { + return contents; + } + } + + public override String GetAsString() + { + return this; + } + + public override string ToString() + { + return contents; + } + + public override void Write(AcpiObject value) + { + this.contents = value.GetAsString().Value; + } + } + + /// + /// An ACPI thermal zone description object. + /// + /// From Table 17-6: Thermal Zone description object + public class ThermalZone : AcpiObject + { + public ThermalZone() + { + // No data - a thermal zone is described entirely by its name and children + } + + public override Integer ObjectType() + { + return new Integer((ulong)AcpiObjectType.ThermalZone); + } + + public override void Write(AcpiObject value) + { + throw new AmlTypeException("Cannot write to thermal zone object"); + } + } + + internal class AcpiObjectUtils + { + public static ulong GetNumBits(ulong value) + { + ulong numBits = 0; + while (value != 0) { + value >>= 1; + numBits++; + } + return numBits; + } + } +} diff --git a/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AmlInterpreter.cs b/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AmlInterpreter.cs new file mode 100644 index 0000000..61da108 --- /dev/null +++ b/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AmlInterpreter.cs @@ -0,0 +1,1049 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Microsoft Research Singularity +// + +// Interprets ACPI methods. + +using System; +using System.Collections; +using System.Diagnostics; + +using Node = Microsoft.Singularity.Hal.Acpi.AcpiNamespace.Node; +using NodePath = Microsoft.Singularity.Hal.Acpi.AcpiNamespace.NodePath; +using AbsoluteNodePath = Microsoft.Singularity.Hal.Acpi.AcpiNamespace.AbsoluteNodePath; + +using Microsoft.Singularity.Hal.Acpi; +using Microsoft.Singularity.Hal.Acpi.AcpiObject; +using Microsoft.Singularity.Hal.Acpi.AmlParserUnions; +using Microsoft.Singularity.Hal.Acpi.StackIR; + +namespace Microsoft.Singularity.Hal.Acpi +{ + public class InterpretException : Exception + { + public InterpretException() : base("Error interpreting AML") { } + + public InterpretException(string s) : base("Error interpreting AML: " + s) { } + } + + public class MethodNotFoundInterpretException : Exception + { + public MethodNotFoundInterpretException() : base("Error interpreting AML: Method not found") { } + + public MethodNotFoundInterpretException(string s) : base("Error interpreting AML: Method not found: " + s) { } + } + + public abstract class IoLocation + { + public abstract AcpiObject.AcpiObject Read(); + public abstract void Write(AcpiObject.AcpiObject value); + } + + public class ValueIoLocation : IoLocation + { + private AcpiObject.AcpiObject value; + + public ValueIoLocation() + { + value = new UninitializedObject(); + } + + public ValueIoLocation(AcpiObject.AcpiObject acpiObject) + { + this.value = acpiObject; + } + + public override AcpiObject.AcpiObject Read() + { + return value; + } + + public override void Write(AcpiObject.AcpiObject value) + { + throw new InterpretException("Attempt to store to temporary object"); + } + } + + public class DebugIoLocation : IoLocation + { + public override AcpiObject.AcpiObject Read() + { + throw new InterpretException("Attempt to read from debug object"); + } + + public override void Write(AcpiObject.AcpiObject value) + { +#if SINGULARITY_KERNEL + DebugStub.WriteLine("ACPI DEBUG: " + value.ToString()); +#else + Console.WriteLine("ACPI DEBUG: " + value.ToString()); +#endif + } + } + + public class VariableIoLocation : ValueIoLocation + { + private AcpiObject.AcpiObject value; + + public VariableIoLocation() + { + value = new UninitializedObject(); + } + + public VariableIoLocation(AcpiObject.AcpiObject acpiObject) + { + value = acpiObject; + } + + public override AcpiObject.AcpiObject Read() + { + return value; + } + + public override void Write(AcpiObject.AcpiObject value) + { + this.value = value; + } + } + + public class NodePathIoLocation : ValueIoLocation + { + private AcpiNamespace.Node node; + + public NodePathIoLocation(AcpiNamespace.Node node) + { + if (node == null) { + throw new ArgumentException("node should be non-null"); + } + this.node = node; + } + + public override AcpiObject.AcpiObject Read() + { + return node.Value; + } + + public override void Write(AcpiObject.AcpiObject value) + { + node.Value = value; + } + } + + public class AmlStackFrame : IDisposable + { + // Local variable and argument objects + public const int ReservedLocals = 2; // For use by the AmlStackIR compiler + public const int FirstReservedLocal = 8; + public const int NumLocals = FirstReservedLocal + ReservedLocals; + VariableIoLocation[] locals = new VariableIoLocation[NumLocals]; + public const int NumArgs = 7; + VariableIoLocation[] args = new VariableIoLocation[NumArgs]; + + StackIRNode[] instructions; + int currentInstructionIndex; + + private class AcpiNamespaceNodeSet : IEnumerable + { + private ArrayList nodeSet = new ArrayList(); + + public void Add(AcpiNamespace.Node node) + { + if (!nodeSet.Contains(node)) { + nodeSet.Add(node); + } + } + + public IEnumerator GetEnumerator() + { + return nodeSet.GetEnumerator(); + } + } + + AcpiNamespaceNodeSet methodTemporaryNamespaceObjectSet = new AcpiNamespaceNodeSet(); + + public AmlStackFrame(StackIRNode[] instructions, AcpiObject.AcpiObject[] args) + { + this.instructions = instructions; + currentInstructionIndex = 0; + + int i; + for (i = 0; i < locals.Length; i++) { + locals[i] = new VariableIoLocation(); + } + for (i = 0; i < args.Length; i++) { + this.args[i] = new VariableIoLocation(args[i]); + } + for (; i < this.args.Length; i++) { + this.args[i] = new VariableIoLocation(); + } + } + + public void NotifyCreateNodeAt(NodePath nodePath, Node node) + { + methodTemporaryNamespaceObjectSet.Add(node); + } + + public void Dispose() + { + foreach (Node node in methodTemporaryNamespaceObjectSet) { + node.Remove(); + } + } + + public VariableIoLocation GetArgument(int argNum) + { + return args[argNum]; + } + + public void SetArgument(int argNum, AcpiObject.AcpiObject value) + { + args[argNum].Write(value); + } + + public VariableIoLocation GetLocal(int localNum) + { + return locals[localNum]; + } + + public void SetLocal(int localNum, AcpiObject.AcpiObject value) + { + locals[localNum].Write(value); + } + + public StackIRNode GetNextInstruction() + { + return instructions[currentInstructionIndex]; + } + + public void AdvanceInstruction() + { + currentInstructionIndex++; + } + + public void JumpTo(int index) + { + currentInstructionIndex = index; + } + } + + public delegate void MethodResultCallback(AcpiObject.AcpiObject value); + + public class AmlInterpreterThread + { + private class AmlStack + { + private Stack stack = new Stack(); + + public void Push(AmlStackFrame node) + { + stack.Push(node); + } + + public AmlStackFrame Pop() + { + return (AmlStackFrame)stack.Pop(); + } + + public AmlStackFrame Peek() + { + return (AmlStackFrame)stack.Peek(); + } + + public bool IsEmpty() + { + return stack.Count == 0; + } + } + + private class IoLocationStack + { + private Stack stack = new Stack(); + + public void Push(IoLocation node) + { + stack.Push(node); + } + + public IoLocation Pop() + { + return (IoLocation)stack.Pop(); + } + + public IoLocation Peek() + { + return (IoLocation)stack.Peek(); + } + } + + AcpiNamespace acpiNamespace; + AbsoluteNodePath currentPath; + AmlStack frameStack = new AmlStack(); + bool exited = false; + bool blocked = false; + AcpiObject.AcpiObject exitValue; + IoLocationStack stack = new IoLocationStack(); + MethodResultCallback callback; + IOperationRegionAccessor operationRegionAccessor; + + public AmlInterpreterThread(AcpiNamespace acpiNamespace, MethodResultCallback callback, + IOperationRegionAccessor operationRegionAccessor) + { + this.acpiNamespace = acpiNamespace; + this.currentPath = AbsoluteNodePath.CreateRoot(); + this.callback = callback; + this.operationRegionAccessor = operationRegionAccessor; + } + + public void InvokeMethod(Method method, AcpiObject.AcpiObject[] parameters) + { + method.Invoke(this, parameters, acpiNamespace); + } + + public void InvokeMethod(AbsoluteNodePath nodePath, AcpiObject.AcpiObject[] parameters) + { + Node methodNode = acpiNamespace.LookupNode(nodePath); + if (methodNode == null) { + throw new MethodNotFoundInterpretException(); + } + if (!(methodNode.Value is AcpiObject.BytecodeMethod)) { + throw new AmlTypeException(); + } + this.currentPath = methodNode.Path; + ((AcpiObject.Method)(methodNode.Value)).Invoke(this, parameters, acpiNamespace); + } + + public void PushFrame(AmlStackFrame frame) + { + frameStack.Push(frame); + } + + public void Return(AcpiObject.AcpiObject result) + { + frameStack.Pop().Dispose(); + if (frameStack.IsEmpty()) { + Exit(result); + } + else { + Push(new ValueIoLocation(result)); + } + } + + public AmlStackFrame CurrentFrame + { + get + { + return frameStack.Peek(); + } + } + + public IOperationRegionAccessor OperationRegionAccessor + { + get + { + return operationRegionAccessor; + } + } + + public void Step() + { + if (Exited || Blocked) { + throw new InterpretException("Tried to step exited or blocked thread"); + } + new InterpretStepVisitor(this).Step(); + } + + public bool Exited + { + get + { + return exited; + } + } + + public void Exit(AcpiObject.AcpiObject exitValue) + { + exited = true; + this.exitValue = exitValue; + if (callback != null) { + callback(ExitValue); + } + } + + public AcpiObject.AcpiObject ExitValue + { + get + { + return exitValue; + } + } + + public bool Blocked + { + get + { + return blocked; + } + } + + public void Block() + { + blocked = true; + } + + public void Notify() + { + blocked = false; + } + + public Node LookupNode(NodePath nodePath) + { + return acpiNamespace.LookupNode(nodePath, currentPath); + } + + public Node CreateNodeAt(NodePath nodePath) + { + Node node = acpiNamespace.CreateNodeAt(nodePath, currentPath); + frameStack.Peek().NotifyCreateNodeAt(nodePath, node); + return node; + } + + public void Push(IoLocation location) + { + stack.Push(location); + } + + public IoLocation Pop() + { + return stack.Pop(); + } + } + + public class InterpretStepVisitor : StackIRNodeVisitor + { + AmlInterpreterThread thread; + + public InterpretStepVisitor(AmlInterpreterThread thread) + { + this.thread = thread; + } + + private AmlStackFrame Frame + { + get + { + return thread.CurrentFrame; + } + } + + public void Step() + { + AmlStackFrame frame = Frame; + frame.GetNextInstruction().Accept(this); + if (!thread.Blocked && !thread.Exited) { + frame.AdvanceInstruction(); + } + } + + public override void Visit(Jump node) + { + Frame.JumpTo(node.Target - 1); // Minus one because IP will still advance + } + + public override void Visit(JumpIfNonZero node) + { + AcpiObject.AcpiObject predicate = thread.Pop().Read(); + if (predicate.GetAsInt().Value != 0) { + Frame.JumpTo(node.ThenTarget - 1); // Minus one because IP will still advance + } + } + + public override void Visit(JumpIfNodePathExists node) + { + if (thread.LookupNode(node.NodePath) != null) { + Frame.JumpTo(node.ThenTarget - 1); // Minus one because IP will still advance + } + } + + public override void Visit(PushArgObj node) + { + thread.Push(Frame.GetArgument(node.ArgNum)); + } + + public override void Visit(PushLocalObj node) + { + thread.Push(Frame.GetLocal(node.LocalNum)); + } + + public override void Visit(PushDebugObj node) + { + thread.Push(new DebugIoLocation()); + } + + public override void Visit(PushNodePath node) + { + Node pathNode = thread.LookupNode(node.Value); + thread.Push(new NodePathIoLocation(pathNode)); + if (pathNode.Value is Method && pathNode.Value.GetAsMethod().ArgCount == 0) { + Visit(new MethodCall()); + } + } + + public override void Visit(PushConst node) + { + thread.Push(new ValueIoLocation(node.Value)); + } + + public override void Visit(Discard node) + { + thread.Pop(); + } + + public override void Visit(Store node) + { + IoLocation value = thread.Pop(); + IoLocation destination = thread.Pop(); + destination.Write(value.Read()); + thread.Push(destination); + } + + public override void Visit(MethodCall node) + { + IoLocation methodLocation = thread.Pop(); + Method method = methodLocation.Read().GetAsMethod(); + + AcpiObject.AcpiObject[] args = new AcpiObject.AcpiObject[method.ArgCount]; + for (int i = 0; i < method.ArgCount; i++) { + args[i] = thread.Pop().Read(); + } + + thread.InvokeMethod(method, args); + } + + public override void Visit(Index node) + { + AcpiObject.AcpiObject container = thread.Pop().Read(); + ulong index = thread.Pop().Read().GetAsInt().Value; + thread.Push(new ValueIoLocation(new AcpiObject.BufferField(container, index * 8, 8))); + } + + public override void Visit(ShiftLeft node) + { + ulong left = thread.Pop().Read().GetAsInt().Value; + ulong right = thread.Pop().Read().GetAsInt().Value; + PushInteger(left << (int)right); + } + + public override void Visit(ShiftRight node) + { + ulong left = thread.Pop().Read().GetAsInt().Value; + ulong right = thread.Pop().Read().GetAsInt().Value; + PushInteger(left >> (int)right); + } + + public override void Visit(Concatenate node) + { + AcpiObject.AcpiObject leftObj = thread.Pop().Read(); + AcpiObject.AcpiObject rightObj = thread.Pop().Read(); + + if (leftObj is AcpiObject.String) { + string leftStr = leftObj.GetAsString().Value; + string rightStr = rightObj.GetAsString().Value; + thread.Push(new ValueIoLocation(new AcpiObject.String(leftStr + rightStr))); + } + else { + throw new InterpretException("Concatenate only implemented for strings"); + } + } + + public override void Visit(Add node) + { + ulong left = thread.Pop().Read().GetAsInt().Value; + ulong right = thread.Pop().Read().GetAsInt().Value; + PushInteger(left + right); + } + + public override void Visit(Subtract node) + { + ulong left = thread.Pop().Read().GetAsInt().Value; + ulong right = thread.Pop().Read().GetAsInt().Value; + PushInteger(left - right); + } + + public override void Visit(Multiply node) + { + ulong left = thread.Pop().Read().GetAsInt().Value; + ulong right = thread.Pop().Read().GetAsInt().Value; + PushInteger(left * right); + } + + public override void Visit(Divide node) + { + ulong left = thread.Pop().Read().GetAsInt().Value; + ulong right = thread.Pop().Read().GetAsInt().Value; + PushInteger(left / right); + } + + public override void Visit(Remainder node) + { + ulong left = thread.Pop().Read().GetAsInt().Value; + ulong right = thread.Pop().Read().GetAsInt().Value; + PushInteger(left % right); + } + + public override void Visit(LogicalOp node) + { + ulong left = thread.Pop().Read().GetAsInt().Value; + ulong right = 0; + + if (node.Operator != LogicalOp.Op.Not) { + right = thread.Pop().Read().GetAsInt().Value; + } + + switch (node.Operator) { + case LogicalOp.Op.Less: + PushInteger(left < right ? 1u : 0u); + break; + case LogicalOp.Op.LessEq: + PushInteger(left <= right ? 1u : 0u); + break; + case LogicalOp.Op.Equal: + PushInteger(left == right ? 1u : 0u); + break; + case LogicalOp.Op.Greater: + PushInteger(left > right ? 1u : 0u); + break; + case LogicalOp.Op.GreaterEq: + PushInteger(left >= right ? 1u : 0u); + break; + case LogicalOp.Op.And: + PushInteger(left != 0 && right != 0 ? 1u : 0u); + break; + case LogicalOp.Op.Or: + PushInteger(left != 0 || right != 0 ? 1u : 0u); + break; + case LogicalOp.Op.Not: + PushInteger(left == 0 ? 1u : 0u); + break; + } + } + + public override void Visit(BitOp node) + { + ulong left = thread.Pop().Read().GetAsInt().Value; + ulong right = 0; + + if (node.Operator != BitOp.Op.Not) { + right = thread.Pop().Read().GetAsInt().Value; + } + + switch (node.Operator) { + case BitOp.Op.And: + PushInteger(left & right); + break; + case BitOp.Op.Or: + PushInteger(left | right); + break; + case BitOp.Op.NAnd: + PushInteger(~(left & right)); + break; + case BitOp.Op.NOr: + PushInteger(~(left | right)); + break; + case BitOp.Op.Not: + PushInteger(~left); + break; + case BitOp.Op.XOr: + PushInteger(left ^ right); + break; + } + } + + public override void Visit(Return node) + { + thread.Return(thread.Pop().Read()); + } + + public override void Visit(CreateField node) + { + AcpiObject.Buffer sourceBuff = thread.Pop().Read().GetAsBuffer(); + ulong startBitIndex = thread.Pop().Read().GetAsInt().Value; + ulong numBits = thread.Pop().Read().GetAsInt().Value; + + Node destinationNode = thread.CreateNodeAt(node.NodePath); + destinationNode.Value = new BufferField(sourceBuff, startBitIndex, numBits); + } + + public override void Visit(DefBuffer node) + { + ulong size = thread.Pop().Read().GetAsInt().Value; + byte[] contents = new byte[size]; + byte[] initializer = node.Initializer; + Array.Copy(initializer, contents, initializer.Length); + thread.Push(new ValueIoLocation(new AcpiObject.Buffer(contents))); + } + + public override void Visit(FindSetLeftBit node) + { + // Result: Zero indicates no bit set; k indicates the kth bit from the right is set + ulong value = thread.Pop().Read().GetAsInt().Value; + for (int i = 63; i >= 0; i--) { + if (((value >> i) & 1) != 0) { + PushInteger((ulong)(i + 1)); + return; + } + } + PushInteger(0); + } + + public override void Visit(FindSetRightBit node) + { + // Result: Zero indicates no bit set; k indicates the kth bit from the right is set + ulong value = thread.Pop().Read().GetAsInt().Value; + for (ulong i = 1; i <= 64; i++) { + if ((value & 1) != 0) { + PushInteger(i); + return; + } + value >>= 1; + } + PushInteger(0); + } + + public override void Visit(SizeOf node) + { + PushInteger(thread.Pop().Read().Size); + } + + public override void Visit(DefName node) + { + Node namespaceNode = thread.CreateNodeAt(node.NodePath); + namespaceNode.Value = thread.Pop().Read(); + } + + public override void Visit(Load node) + { + throw new InterpretException("TODO"); + } + + public override void Visit(Stall node) + { + throw new InterpretException("TODO"); + } + + public override void Visit(Match node) + { + throw new InterpretException("TODO"); + } + + public override void Visit(DerefOf node) + { + thread.Push(new ValueIoLocation(thread.Pop().Read().Dereference())); + } + + public override void Visit(StackIR.Package node) + { + throw new InterpretException("TODO"); + } + + private void PushInteger(ulong i) + { + thread.Push(new ValueIoLocation(new AcpiObject.Integer(i))); + } + + public override void Visit(Notify node) + { + throw new InterpretException("TODO"); + } + + public override void Visit(Sleep node) + { + throw new InterpretException("TODO"); + } + + public override void Visit(RefOf node) + { + // TODO: This implementation is not strictly correct, as it + // will not continue to refer to the location if its value is + // replaced (as can happen if it's currently uninitialized). + // But currently the result of RefOf isn't used on any of + // the tested machines, so postponing this. + thread.Push(new ValueIoLocation(new AcpiObject.ObjectReference(new AcpiObjectCell(thread.Pop().Read())))); + } + + public override void Visit(StackIR.OperationRegion node) + { + ulong startIndex = thread.Pop().Read().GetAsInt().Value; + ulong length = thread.Pop().Read().GetAsInt().Value; + + Node namespaceNode = thread.CreateNodeAt(node.NodePath); + namespaceNode.Value = new AcpiObject.OperationRegion(thread.OperationRegionAccessor, + node.OperationSpace, + startIndex, length); + } + + public override void Visit(Field node) + { + Node operationRegionNode = thread.LookupNode(node.NodePath); + SortedList fields = FieldUnit.CreateFromFieldList((AcpiObject.OperationRegion)(operationRegionNode.Value.GetTarget()), + node.FieldElements, + node.FieldFlags.accessType, AccessAttrib.SMBNone, + node.FieldFlags.lockRule, node.FieldFlags.updateRule); + foreach (DictionaryEntry entry in fields) { + Node namespaceNode = thread.CreateNodeAt( + new NodePath(false/*isAbsolute*/, 0, new string[] { (string)entry.Key })); + namespaceNode.Value = (FieldUnit)entry.Value; + } + } + + public override void Visit(Acquire node) + { + Mutex mutex = thread.Pop().Read().GetAsMutex(); + mutex.Acquire(thread); + } + + public override void Visit(Release node) + { + Mutex mutex = thread.Pop().Read().GetAsMutex(); + mutex.Release(thread); + } + + public override void Visit(ToBuffer node) + { + throw new InterpretException("TODO"); + } + + public override void Visit(ToInteger node) + { + throw new InterpretException("TODO"); + } + + public override void Visit(ToString node) + { + throw new InterpretException("TODO"); + } + } + + public class AmlInterpreter + { + private class AmlInterpreterThreadList : IEnumerable + { + Queue queue = new Queue(); + + public void Enqueue(AmlInterpreterThread thread) + { + queue.Enqueue(thread); + } + + public AmlInterpreterThread Dequeue() + { + return (AmlInterpreterThread)queue.Dequeue(); + } + + public IEnumerator GetEnumerator() + { + return queue.GetEnumerator(); + } + + public int Count + { + get + { + return queue.Count; + } + } + } + + AmlInterpreterThreadList threadsReadyToRun = new AmlInterpreterThreadList(); + AmlInterpreterThreadList threadsBlocked = new AmlInterpreterThreadList(); + AcpiNamespace acpiNamespace; + IOperationRegionAccessor operationRegionAccessor; + + public AmlInterpreter(AcpiNamespace acpiNamespace, + IOperationRegionAccessor operationRegionAccessor) + { + this.acpiNamespace = acpiNamespace; + this.operationRegionAccessor = operationRegionAccessor; + } + + public AmlParser.ParseSuccess ParseMethodBodies() + { + foreach (Node node in acpiNamespace.GetAllNodes()) { + if (!(node.Value is AcpiObject.BytecodeMethod)) { + continue; + } + + AcpiObject.BytecodeMethod method = (AcpiObject.BytecodeMethod)node.Value; + +#if SINGULARITY_KERNEL + DebugStub.WriteLine("Parsing method " + node.Path.ToString()); +#else + Console.WriteLine("Parsing method " + node.Path.ToString()); +#endif + + if (method.Parse(acpiNamespace, node.Path) == AmlParser.ParseSuccess.Failure) { + return AmlParser.ParseSuccess.Failure; + } + } + return AmlParser.ParseSuccess.Success; + } + + public AmlInterpreterThread InvokeMethodOnNewThread(MethodResultCallback callback, + AbsoluteNodePath nodePath, AcpiObject.AcpiObject[] parameters) + { + AmlInterpreterThread thread = new AmlInterpreterThread(acpiNamespace, callback, operationRegionAccessor); + thread.InvokeMethod(nodePath, parameters); + threadsReadyToRun.Enqueue(thread); + return thread; + } + + public void Run() + { + while (threadsReadyToRun.Count > 0) { + while (threadsReadyToRun.Count > 0) { + AmlInterpreterThread currentThread = threadsReadyToRun.Dequeue(); + while (!currentThread.Blocked && !currentThread.Exited) { + currentThread.Step(); + } + if (currentThread.Blocked) { + threadsBlocked.Enqueue(currentThread); + } + } + foreach (AmlInterpreterThread thread in threadsBlocked) { + if (!thread.Blocked) { + threadsReadyToRun.Enqueue(thread); + } + } + } + if (threadsBlocked.Count > 0) { + throw new InterpretException("Deadlock between ACPI AML threads detected"); + } + } + } + + internal class ByteBufferAmlStreamAdapter : IAmlStream + { + byte[] buffer; + + public ByteBufferAmlStreamAdapter(byte[] buffer) + { + this.buffer = buffer; + } + + public byte ReadByteData(ref int offset) + { + try { + byte result = buffer[offset]; + offset++; + return result; + } + catch (System.IndexOutOfRangeException) { + throw new EndOfAmlStreamException(); + } + } + + public bool TryReadByteData(ref int offset, out byte result) + { + if (offset >= buffer.Length) { + result = 0; + return false; + } + else { + result = buffer[offset]; + offset++; + return true; + } + } + + public char ReadChar(ref int offset) + { + try { + char result = (char)buffer[offset]; + offset++; + return result; + } + catch (System.IndexOutOfRangeException) { + throw new EndOfAmlStreamException(); + } + } + + public byte[] ReadByteDataArray(ref int offset, int length) + { + try { + byte[] result = new byte[length]; + System.Array.Copy(buffer, offset, result, 0, length); + offset += length; + return result; + } + catch (System.ArgumentException) { + throw new EndOfAmlStreamException(); + } + catch (System.IndexOutOfRangeException) { + throw new EndOfAmlStreamException(); + } + } + + public bool TryReadByteDataArray(ref int offset, int length, out byte[] result) + { + if (offset + length > buffer.Length) { + result = null; + return false; + } + else { + result = new byte[length]; + System.Array.Copy(buffer, offset, result, 0, length); + offset += length; + return true; + } + } + + public UInt16 ReadWordData(ref int offset) + { + try { + UInt16 result = (UInt16)(buffer[offset] + + (((UInt16)buffer[offset + 1]) << 8));; + offset += 2; + return result; + } + catch (System.IndexOutOfRangeException) { + throw new EndOfAmlStreamException(); + } + } + + public UInt32 ReadDWordData(ref int offset) + { + try { + UInt32 result = (UInt32)(buffer[offset] + + (((UInt32)buffer[offset + 1]) << 8) + + (((UInt32)buffer[offset + 2]) << 16) + + (((UInt32)buffer[offset + 3]) << 24)); + offset += 4; + return result; + } + catch (System.IndexOutOfRangeException) { + throw new EndOfAmlStreamException(); + } + } + + public UInt64 ReadQWordData(ref int offset) + { + try { + UInt64 result = (UInt64)(buffer[offset] + + (((UInt64)buffer[offset + 1]) << 8) + + (((UInt64)buffer[offset + 2]) << 16) + + (((UInt64)buffer[offset + 3]) << 24) + + (((UInt64)buffer[offset + 4]) << 32) + + (((UInt64)buffer[offset + 5]) << 40) + + (((UInt64)buffer[offset + 6]) << 48) + + (((UInt64)buffer[offset + 7]) << 56)); + offset += 8; + return result; + } + catch (System.IndexOutOfRangeException) { + throw new EndOfAmlStreamException(); + } + } + } +} \ No newline at end of file diff --git a/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AmlLoader.cs b/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AmlLoader.cs new file mode 100644 index 0000000..6599060 --- /dev/null +++ b/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AmlLoader.cs @@ -0,0 +1,811 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Microsoft Research Singularity +// + +using System; +using System.Collections; +using System.Diagnostics; + +using Microsoft.Singularity.Hal.Acpi.AcpiObject; +using Microsoft.Singularity.Hal.Acpi.AmlParserUnions; +using Node = Microsoft.Singularity.Hal.Acpi.AcpiNamespace.Node; +using NodePath = Microsoft.Singularity.Hal.Acpi.AcpiNamespace.NodePath; +using AbsoluteNodePath = Microsoft.Singularity.Hal.Acpi.AcpiNamespace.AbsoluteNodePath; + +namespace Microsoft.Singularity.Hal.Acpi +{ + public class AmlTypeException : Exception + { + public AmlTypeException() : base("AML type error") { } + + public AmlTypeException(string s) : base("AML type error: " + s) { } + } + + public class LoadException : Exception + { + public LoadException() : base("Error loading AML") { } + + public LoadException(string s) : base("Error loading AML: " + s) { } + } + + public class AmlLoader + { + AcpiNamespace acpiNamespace; + AcpiObject.IOperationRegionAccessor operationRegionAccessor; + + public AmlLoader(AcpiNamespace acpiNamespace, + AcpiObject.IOperationRegionAccessor operationRegionAccessor) + { + this.acpiNamespace = acpiNamespace; + this.operationRegionAccessor = operationRegionAccessor; + } + + public void Load(AmlParser.AMLCode code) + { + // Because of arbitrary forward-references to names in the AML, we + // have to do this in three phases: + // 1. Populate the namespace with all the names; + // 2. Fill in the details of all the object values. + // This allows us to verify that references only reference real + // existing names. + + NamesVisitor namesVisitor = new NamesVisitor(acpiNamespace); + foreach (TermObj termObj in code.termList) + { + termObj.Accept(namesVisitor); + } + + ValuesVisitor valuesVisitor = new ValuesVisitor(this, acpiNamespace); + foreach (TermObj termObj in code.termList) + { + termObj.Accept(valuesVisitor); + } + } + + internal AcpiObject.IOperationRegionAccessor OperationRegionAccessor + { + get + { + return operationRegionAccessor; + } + } + + public class NamesVisitor : AmlParserNodeVisitor + { + AcpiNamespace acpiNamespace; + AbsoluteNodePath currentPath; + + public NamesVisitor(AcpiNamespace acpiNamespace) + { + this.acpiNamespace = acpiNamespace; + this.currentPath = AbsoluteNodePath.CreateRoot(); + } + + public override void UnhandledNodeType(string nodeTypeName) + { + throw new LoadException("Encountered unexpected node type " + + nodeTypeName + " at load-time (names phase)"); + } + + public override void Visit(AmlParser.DefAlias defAlias) + { + acpiNamespace.CreateNodeAt(defAlias.aliasName.nodePath, currentPath); + } + + public override void Visit(AmlParser.DefName defName) + { + acpiNamespace.CreateNodeAt(defName.nameString.nodePath, currentPath); + } + + public override void Visit(AmlParser.DefScope defScope) + { + AbsoluteNodePath oldPath = currentPath; + currentPath = acpiNamespace.LookupNode(defScope.nameString.nodePath, currentPath).Path; + foreach (TermObj termObj in defScope.termList) + { + termObj.Accept(this); + } + currentPath = oldPath; + } + + public override void Visit(AmlParser.DefBankField defBankField) + { + foreach (FieldElement fieldElement in defBankField.fieldList) { + switch (fieldElement.Tag) { + case FieldElement.TagValue.NamedField: + AmlParser.NamedField namedField = fieldElement.GetAsNamedField(); + Node node = acpiNamespace.CreateNodeAt( + new NodePath(false/*isAbsolute*/, 0, new string[] { namedField.nameSeg.data }), + currentPath); + break; + default: + break; + } + } + } + + public override void Visit(AmlParser.DefCreateBitField defCreateBitField) + { + acpiNamespace.CreateNodeAt(defCreateBitField.nameString.nodePath, currentPath); + } + + public override void Visit(AmlParser.DefCreateByteField defCreateByteField) + { + acpiNamespace.CreateNodeAt(defCreateByteField.nameString.nodePath, currentPath); + } + + public override void Visit(AmlParser.DefCreateDWordField defCreateDWordField) + { + acpiNamespace.CreateNodeAt(defCreateDWordField.nameString.nodePath, currentPath); + } + + public override void Visit(AmlParser.DefCreateField defCreateField) + { + acpiNamespace.CreateNodeAt(defCreateField.nameString.nodePath, currentPath); + } + + public override void Visit(AmlParser.DefCreateQWordField defCreateQWordField) + { + acpiNamespace.CreateNodeAt(defCreateQWordField.nameString.nodePath, currentPath); + } + + public override void Visit(AmlParser.DefCreateWordField defCreateWordField) + { + acpiNamespace.CreateNodeAt(defCreateWordField.nameString.nodePath, currentPath); + } + + public override void Visit(AmlParser.DefDataRegion defDataRegion) + { + acpiNamespace.CreateNodeAt(defDataRegion.nameString.nodePath, currentPath); + } + + public override void Visit(AmlParser.DefDevice defDevice) + { + AbsoluteNodePath oldPath = currentPath; + + Node node = acpiNamespace.CreateNodeAt(defDevice.nameString.nodePath, currentPath); + + currentPath = node.Path; + foreach (AmlObject amlObject in defDevice.amlObjectList) + { + amlObject.Accept(this); + } + + currentPath = oldPath; + } + + public override void Visit(AmlParser.DefEvent defEvent) + { + acpiNamespace.CreateNodeAt(defEvent.nameString.nodePath, currentPath); + } + + public override void Visit(AmlParser.DefField defField) + { + foreach (FieldElement fieldElement in defField.fieldList) { + switch (fieldElement.Tag) { + case FieldElement.TagValue.NamedField: + AmlParser.NamedField namedField = fieldElement.GetAsNamedField(); + Node node = acpiNamespace.CreateNodeAt( + new NodePath(false/*isAbsolute*/, 0, new string[] { namedField.nameSeg.data }), + currentPath); + break; + default: + break; + } + } + } + + public override void Visit(AmlParser.DefIndexField defIndexField) + { + foreach (FieldElement fieldElement in defIndexField.fieldList) { + switch (fieldElement.Tag) { + case FieldElement.TagValue.NamedField: + AmlParser.NamedField namedField = fieldElement.GetAsNamedField(); + Node node = acpiNamespace.CreateNodeAt( + new NodePath(false/*isAbsolute*/, 0, new string[] { namedField.nameSeg.data }), + currentPath); + break; + default: + break; + } + } + } + + public override void Visit(AmlParser.DefMethod defMethod) + { + acpiNamespace.CreateNodeAt(defMethod.nameString.nodePath, currentPath); + } + + public override void Visit(AmlParser.DefMutex defMutex) + { + acpiNamespace.CreateNodeAt(defMutex.nameString.nodePath, currentPath); + } + + public override void Visit(AmlParser.DefOpRegion defOpRegion) + { + acpiNamespace.CreateNodeAt(defOpRegion.nameString.nodePath, currentPath); + } + + public override void Visit(AmlParser.DefPowerRes defPowerRes) + { + AbsoluteNodePath oldPath = currentPath; + + Node node = acpiNamespace.CreateNodeAt(defPowerRes.nameString.nodePath, currentPath); + + currentPath = node.Path; + foreach (AmlObject amlObject in defPowerRes.amlObjectList) + { + amlObject.Accept(this); + } + + currentPath = oldPath; + } + + public override void Visit(AmlParser.DefProcessor defProcessor) + { + AbsoluteNodePath oldPath = currentPath; + + Node node = acpiNamespace.CreateNodeAt(defProcessor.nameString.nodePath, currentPath); + + currentPath = node.Path; + foreach (AmlObject amlObject in defProcessor.amlObjectList) + { + amlObject.Accept(this); + } + + currentPath = oldPath; + } + + public override void Visit(AmlParser.DefThermalZone defThermalZone) + { + AbsoluteNodePath oldPath = currentPath; + + Node node = acpiNamespace.CreateNodeAt(defThermalZone.nameString.nodePath, currentPath); + + currentPath = node.Path; + foreach (AmlObject amlObject in defThermalZone.amlObjectList) + { + amlObject.Accept(this); + } + + currentPath = oldPath; + } + + public override void Visit(AmlParser.DefIfElse defIfElse) + { + // + // Load-time Ifs are both rare and tricky, because it makes the + // two-phase model here unworkable: we can't let the NamesVisitor + // visit the if body before the truth value of its predicate is + // known, but this may depend on values filled in by the ValuesVisitor. + // Being an exceptional case, we just have the NamesVisitor always + // visit both branches, which works for the examples I've come across + // like the AMD with Infineon TPM. + // + + foreach (TermObj termObj in defIfElse.termList) { + termObj.Accept(this); + } + if (defIfElse.defElse.termList != null) { + foreach (TermObj termObj in defIfElse.defElse.termList) { + termObj.Accept(this); + } + } + } + } + + public class ValuesVisitor : AmlParserNodeVisitor + { + AcpiNamespace acpiNamespace; + AmlLoader loader; + AbsoluteNodePath currentPath; + + public ValuesVisitor(AmlLoader loader, AcpiNamespace acpiNamespace) + { + this.acpiNamespace = acpiNamespace; + this.currentPath = AbsoluteNodePath.CreateRoot(); + this.loader = loader; + } + + public override void UnhandledNodeType(string nodeTypeName) + { + throw new LoadException("Encountered unexpected node type " + + nodeTypeName + " at load-time (values phase)"); + } + + public override void Visit(AmlParser.DefAlias defAlias) + { + Node sourceNode = acpiNamespace.LookupNode(defAlias.sourceName.nodePath, currentPath); + Node aliasNode = acpiNamespace.LookupNode(defAlias.aliasName.nodePath, currentPath); + aliasNode.AliasTo(sourceNode); + } + + public override void Visit(AmlParser.DefName defName) + { + Node node = acpiNamespace.LookupNode(defName.nameString.nodePath, currentPath); + node.Value = LoadTimeEvaluate(defName.dataRefObject); + } + + public override void Visit(AmlParser.DefScope defScope) + { + AbsoluteNodePath oldPath = currentPath; + currentPath = acpiNamespace.LookupNode(defScope.nameString.nodePath, currentPath).Path; + foreach (TermObj termObj in defScope.termList) + { + termObj.Accept(this); + } + currentPath = oldPath; + } + + public override void Visit(AmlParser.DefBankField defBankField) + { + Node operationRegionNode = acpiNamespace.LookupNode(defBankField.regionName.nodePath, currentPath); + CheckObjectType(operationRegionNode.Value, AcpiObjectType.OperationRegion); + + // TODO: BankFields are not used in the test files and appear to involve some kind of + // "bank selection register". Need to understand this properly to implement it, but for + // leaving unimplemented. Commented out below is some code to use as a starting point. + + throw new LoadException("BankField unimplemented"); + + #if false + + AccessType accessType = defBankField.fieldFlags.accessType; + AccessAttrib accessAttrib = AccessAttrib.SMBNone; + int bitIndex = 0; + foreach (FieldElement fieldElement in defBankField.fieldList) { + switch (fieldElement.Tag) { + case FieldElement.TagValue.NamedField: + AmlParser.NamedField namedField = fieldElement.GetAsNamedField(); + Node node = acpiNamespace.LookupNode( + new NodePath(false/*isAbsolute*/, 0, new string[] { namedField.nameSeg.data }), + currentPath); + node.Value = new FieldUnit((AcpiObject.OperationRegion)operationRegionNode.Value, + bitIndex, namedField.bitWidth, + accessType, accessAttrib, + defBankField.fieldFlags.lockRule, + defBankField.fieldFlags.updateRule); + bitIndex += namedField.bitWidth; + break; + case FieldElement.TagValue.ReservedField: + AmlParser.ReservedField reservedField = fieldElement.GetAsReservedField(); + bitIndex += reservedField.bitWidth; + break; + case FieldElement.TagValue.AccessField: + AmlParser.AccessField accessField = fieldElement.GetAsAccessField(); + accessType = accessField.accessType; + accessAttrib = accessField.accessAttrib; + break; + default: + throw new LoadException("Unhandled alternative in switch over 'FieldElement'"); + } + } + #endif + } + + public override void Visit(AmlParser.DefCreateBitField defCreateBitField) + { + VisitField(defCreateBitField.sourceBuff, + defCreateBitField.bitIndex.integer, 1, 1/*numBits*/, + defCreateBitField.nameString.nodePath); + } + + public override void Visit(AmlParser.DefCreateByteField defCreateByteField) + { + VisitField(defCreateByteField.sourceBuff, + defCreateByteField.byteIndex.integer, 8, 8/*numBits*/, + defCreateByteField.nameString.nodePath); + } + + public override void Visit(AmlParser.DefCreateDWordField defCreateDWordField) + { + VisitField(defCreateDWordField.sourceBuff, + defCreateDWordField.byteIndex.integer, 8, 32/*numBits*/, + defCreateDWordField.nameString.nodePath); + } + + public override void Visit(AmlParser.DefCreateField defCreateField) + { + AcpiObject.AcpiObject sizeObj = LoadTimeEvaluate(defCreateField.numBits.integer); + CheckObjectType(sizeObj, AcpiObjectType.Integer); + VisitField(defCreateField.sourceBuff, + defCreateField.bitIndex.integer, 1, + ((AcpiObject.Integer)(sizeObj.GetTarget())).Value, + defCreateField.nameString.nodePath); + } + + public override void Visit(AmlParser.DefCreateQWordField defCreateQWordField) + { + VisitField(defCreateQWordField.sourceBuff, + defCreateQWordField.byteIndex.integer, 8, 64/*numBits*/, + defCreateQWordField.nameString.nodePath); + } + + public override void Visit(AmlParser.DefCreateWordField defCreateWordField) + { + VisitField(defCreateWordField.sourceBuff, + defCreateWordField.byteIndex.integer, 8, 16/*numBits*/, + defCreateWordField.nameString.nodePath); + } + + public void VisitField(AmlParser.SourceBuff sourceBuff, + TermArg indexTermArg, ulong bitIndexMultiplier, + ulong bitSize, + NodePath fieldName) + { + AcpiObject.AcpiObject indexObj = LoadTimeEvaluate(indexTermArg); + CheckObjectType(indexObj, AcpiObjectType.Integer); + + ulong index = ((AcpiObject.Integer)(indexObj.GetTarget())).Value; + ulong bitIndex = index * bitIndexMultiplier; + + AcpiObject.AcpiObject bufferObj = LoadTimeEvaluate(sourceBuff.buffer); + CheckObjectType(bufferObj, AcpiObjectType.Buffer); + + Node node = acpiNamespace.LookupNode(fieldName, currentPath); + node.Value = new BufferField((AcpiObject.Buffer)(bufferObj.GetTarget()), bitIndex, bitSize); + } + + public override void Visit(AmlParser.DefDataRegion defDataRegion) + { + // TODO: This method will have to go back to the XSDT table + // to look up the designated table and its location so that + // it can produce a corresponding AcpiObject.OperationRegion. + // It is not used in the three test DDBs. + throw new LoadException("TODO Not yet implemented"); + } + + public override void Visit(AmlParser.DefDevice defDevice) + { + AbsoluteNodePath oldPath = currentPath; + + Node node = acpiNamespace.LookupNode(defDevice.nameString.nodePath, currentPath); + + string[] segments = new string[currentPath.NameSegments.Length + + defDevice.nameString.nodePath.NameSegments.Length]; + + int index = 0; + + foreach (string segment in currentPath.NameSegments) + { + segments[index++] = segment; + } + + foreach (string segment in defDevice.nameString.nodePath.NameSegments) + { + segments[index++] = segment; + } + + AbsoluteNodePath devPath = new AbsoluteNodePath(segments); + + node.Value = new AcpiObject.Device(devPath); + + currentPath = node.Path; + foreach (AmlObject amlObject in defDevice.amlObjectList) + { + amlObject.Accept(this); + } + + currentPath = oldPath; + } + + public override void Visit(AmlParser.DefEvent defEvent) + { + Node node = acpiNamespace.LookupNode(defEvent.nameString.nodePath, currentPath); + node.Value = new AcpiObject.Event(); + } + + public override void Visit(AmlParser.DefField defField) + { + Node operationRegionNode = acpiNamespace.LookupNode(defField.nameString.nodePath, currentPath); + CheckObjectType(operationRegionNode.Value, AcpiObjectType.OperationRegion); + SortedList fields = FieldUnit.CreateFromFieldList((AcpiObject.OperationRegion)(operationRegionNode.Value.GetTarget()), + defField.fieldList, + defField.fieldFlags.accessType, AccessAttrib.SMBNone, + defField.fieldFlags.lockRule, defField.fieldFlags.updateRule); + foreach (DictionaryEntry entry in fields) { + Node node = acpiNamespace.LookupNode( + new NodePath(false/*isAbsolute*/, 0, new string[] { (string)entry.Key }), + currentPath); + node.Value = (FieldUnit)entry.Value; + } + } + + public override void Visit(AmlParser.DefIndexField defIndexField) + { + Node indexNode = acpiNamespace.LookupNode(defIndexField.indexName.nodePath, currentPath); + CheckObjectType(indexNode.Value, AcpiObjectType.FieldUnit); + AcpiObject.FieldUnit indexField = (AcpiObject.FieldUnit)(indexNode.Value.GetTarget()); + + Node dataNode = acpiNamespace.LookupNode(defIndexField.dataName.nodePath, currentPath); + CheckObjectType(dataNode.Value, AcpiObjectType.FieldUnit); + AcpiObject.FieldUnit dataField = (AcpiObject.FieldUnit)(dataNode.Value.GetTarget()); + + OperationRegion indexFieldRegion = + new OperationRegion(new IndexFieldOperationRegionAccessor(indexField, dataField), + RegionSpace.SystemIO/*unused*/, 0, 256/*index field never exceeds 8 bits*/); + + AccessType accessType = defIndexField.fieldFlags.accessType; + AccessAttrib accessAttrib = AccessAttrib.SMBNone; + int bitIndex = 0; + foreach (FieldElement fieldElement in defIndexField.fieldList) { + switch (fieldElement.Tag) { + case FieldElement.TagValue.NamedField: + AmlParser.NamedField namedField = fieldElement.GetAsNamedField(); + Node node = acpiNamespace.LookupNode( + new NodePath(false/*isAbsolute*/, 0, new string[] { namedField.nameSeg.data }), + currentPath); + node.Value = new FieldUnit(indexFieldRegion, bitIndex, namedField.bitWidth, + accessType, accessAttrib, + defIndexField.fieldFlags.lockRule, + defIndexField.fieldFlags.updateRule); + bitIndex += namedField.bitWidth; + break; + case FieldElement.TagValue.ReservedField: + AmlParser.ReservedField reservedField = fieldElement.GetAsReservedField(); + bitIndex += reservedField.bitWidth; + break; + case FieldElement.TagValue.AccessField: + AmlParser.AccessField accessField = fieldElement.GetAsAccessField(); + accessType = accessField.accessType; + accessAttrib = accessField.accessAttrib; + break; + default: + throw new LoadException("Unhandled alternative in switch over 'FieldElement'"); + } + } + } + + public override void Visit(AmlParser.DefMethod defMethod) + { + Node node = acpiNamespace.LookupNode(defMethod.nameString.nodePath, currentPath); + node.Value = new AcpiObject.BytecodeMethod(defMethod.methodFlags.argCount, + defMethod.methodFlags.serializeFlag, + defMethod.methodFlags.syncLevel, + defMethod.unparsedTermList); + } + + public override void Visit(AmlParser.DefMutex defMutex) + { + Node node = acpiNamespace.LookupNode(defMutex.nameString.nodePath, currentPath); + node.Value = new AcpiObject.Mutex(defMutex.syncFlags.syncLevel); + } + + public override void Visit(AmlParser.DefOpRegion defOpRegion) + { + AcpiObject.AcpiObject startIndexObj = LoadTimeEvaluate(defOpRegion.regionOffset.integer); + AcpiObject.AcpiObject lengthObj = LoadTimeEvaluate(defOpRegion.regionLen.integer); + CheckObjectType(lengthObj, AcpiObjectType.Integer); + + Node node = acpiNamespace.LookupNode(defOpRegion.nameString.nodePath, currentPath); + node.Value = new AcpiObject.OperationRegion(loader.OperationRegionAccessor, + (RegionSpace)defOpRegion.regionSpace.byteData, + startIndexObj.GetAsInt().Value, + ((AcpiObject.Integer)(lengthObj.GetTarget())).Value); + } + + public override void Visit(AmlParser.DefPowerRes defPowerRes) + { + AbsoluteNodePath oldPath = currentPath; + + Node node = acpiNamespace.LookupNode(defPowerRes.nameString.nodePath, currentPath); + node.Value = new AcpiObject.PowerResource(defPowerRes.systemLevel.byteData, + defPowerRes.resourceOrder.wordData); + + currentPath = node.Path; + foreach (AmlObject amlObject in defPowerRes.amlObjectList) + { + amlObject.Accept(this); + } + + currentPath = oldPath; + } + + public override void Visit(AmlParser.DefProcessor defProcessor) + { + AbsoluteNodePath oldPath = currentPath; + + Node node = acpiNamespace.LookupNode(defProcessor.nameString.nodePath, currentPath); + node.Value = new AcpiObject.Processor(defProcessor.procID.byteData, + defProcessor.pblkAddr.dWordData, + defProcessor.pblkLen.byteData); + + currentPath = node.Path; + foreach (AmlObject amlObject in defProcessor.amlObjectList) + { + amlObject.Accept(this); + } + + currentPath = oldPath; + } + + public override void Visit(AmlParser.DefThermalZone defThermalZone) + { + AbsoluteNodePath oldPath = currentPath; + + Node node = acpiNamespace.LookupNode(defThermalZone.nameString.nodePath, currentPath); + node.Value = new AcpiObject.ThermalZone(); + + currentPath = node.Path; + foreach (AmlObject amlObject in defThermalZone.amlObjectList) + { + amlObject.Accept(this); + } + + currentPath = oldPath; + } + + private AcpiObject.AcpiObject LoadTimeEvaluate(AmlParserNode parserNode) + { + LoadTimeEvaluateVisitor visitor = + new LoadTimeEvaluateVisitor(acpiNamespace, currentPath); + parserNode.Accept(visitor); + return visitor.Result; + } + + public override void Visit(AmlParser.DefIfElse defIfElse) + { + LoadTimeEvaluateVisitor visitor = + new LoadTimeEvaluateVisitor(acpiNamespace, currentPath); + defIfElse.predicate.integer.Accept(visitor); + if (visitor.Result.GetAsInt().Value != 0) { + foreach (TermObj termObj in defIfElse.termList) { + termObj.Accept(this); + } + } + else if (defIfElse.defElse.termList != null) { + foreach (TermObj termObj in defIfElse.defElse.termList) { + termObj.Accept(this); + } + } + } + } + + public class LoadTimeEvaluateVisitor : AmlParserNodeVisitor + { + AcpiNamespace acpiNamespace; + AbsoluteNodePath currentPath; + + public LoadTimeEvaluateVisitor(AcpiNamespace acpiNamespace, AbsoluteNodePath currentPath) + { + this.acpiNamespace = acpiNamespace; + this.currentPath = currentPath; + } + + public override void UnhandledNodeType(string nodeTypeName) + { + throw new LoadException("Encountered unexpected node type " + + nodeTypeName + " at load-time (during evaluation)"); + } + + private AcpiObject.AcpiObject result; + + public AcpiObject.AcpiObject Result + { + get + { + return result; + } + } + + public override void Visit(AmlParser.DefPackage defPackage) + { + VisitPackage(defPackage.numElements.byteData, + defPackage.packageElementList); + } + + public override void Visit(AmlParser.DefVarPackage defVarPackage) + { + defVarPackage.varNumElements.integer.Accept(this); + AcpiObject.AcpiObject numElementsObj = result; + CheckObjectType(numElementsObj, AcpiObjectType.Integer); + VisitPackage((byte)(((AcpiObject.Integer)(numElementsObj.GetTarget())).Value), + defVarPackage.packageElementList); + } + + private void VisitPackage(byte numElements, PackageElement[] packageElementList) + { + AcpiObjectCell[] elements = new AcpiObjectCell[numElements]; + for(int i = 0; i < elements.Length; i++) { + if (i < packageElementList.Length) { + packageElementList[i].Accept(this); + elements[i] = new AcpiObjectCell(result); + } + else { + elements[i] = new AcpiObjectCell(new AcpiObject.UninitializedObject()); + } + } + result = new AcpiObject.Package(elements); + } + + public override void Visit(ComputationalData computationalData) + { + switch (computationalData.Tag) { + case ComputationalData.TagValue.ByteConst: + result = new AcpiObject.Integer(computationalData.GetAsByteConst()); + break; + case ComputationalData.TagValue.WordConst: + result = new AcpiObject.Integer(computationalData.GetAsWordConst()); + break; + case ComputationalData.TagValue.DWordConst: + result = new AcpiObject.Integer(computationalData.GetAsDWordConst()); + break; + case ComputationalData.TagValue.QWordConst: + result = new AcpiObject.Integer(computationalData.GetAsQWordConst()); + break; + case ComputationalData.TagValue.StringConst: + result = new AcpiObject.String(computationalData.GetAsStringConst()); + break; + case ComputationalData.TagValue.RevisionOp: + result = AcpiObject.IntegerConstant.Revision.GetAsInt(); + break; + default: + base.Visit(computationalData); + break; + } + } + + public override void Visit(AmlParser.ConstObj constObj) + { + if (constObj.op == AmlParser.ZeroOp) { + result = AcpiObject.IntegerConstant.Zero.GetAsInt(); + } + else if (constObj.op == AmlParser.OneOp) { + result = AcpiObject.IntegerConstant.One.GetAsInt(); + } + else if (constObj.op == AmlParser.OnesOp) { + result = AcpiObject.IntegerConstant.Ones.GetAsInt(); + } + } + + public override void Visit(AmlParser.DefBuffer defBuffer) + { + defBuffer.bufferSize.integer.Accept(this); + AcpiObject.AcpiObject bufferSizeObj = result; + CheckObjectType(bufferSizeObj, AcpiObjectType.Integer); + + byte[] contents = new byte[((AcpiObject.Integer)(bufferSizeObj.GetTarget())).Value]; + Array.Copy(defBuffer.byteList, contents, defBuffer.byteList.Length); + result = new AcpiObject.Buffer(contents); + } + + public override void Visit(AmlParser.UserTermObj userTermObj) + { + // At load time, it can't have any args, so just look it up in the namespace. + // Note that we can't simply copy the value from the given node, for two reasons: + // + // 1. UserTermObj references can be forwards or backwards, or even circular, as shown + // in this example: + // Name (BAR0, Package (0x1) { BAR1 }) + // Name (BAR1, Package (0x1) { BAR0 }) + // 2. Although the spec doesn't clarify this, I believe a UserTermObj is intended to be + // aliased to the named node so that later changes to it are reflected in the object + // containing the reference. + // + // To deal with we have a special "fake" object type containing an absolute NodePath that + // acts as a direct reference to an object (not the sort handled by AcpiObject.ObjectReference, + // which are different). An alternative would be to attempt to use the same aliasing of nodes + // used by DefAlias, but this would be painful and make it later indistinguishable who is the + // alias and who the original definition, which might be important. + + result = new AcpiObject.NodePathReference(acpiNamespace, + acpiNamespace.LookupNode(userTermObj.nameString.nodePath, currentPath).Path); + } + + public override void Visit(AmlParser.ObjReference objReference) + { + objReference.termArg.Accept(this); + } + } + + private static void CheckObjectType(AcpiObject.AcpiObject obj, AcpiObjectType type) + { + if (obj.ObjectType().Value != (ulong)type) { + throw new AmlTypeException(); + } + } + } +} diff --git a/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AmlParser.cs b/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AmlParser.cs new file mode 100644 index 0000000..cc87b2e --- /dev/null +++ b/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AmlParser.cs @@ -0,0 +1,6727 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Microsoft Research Singularity +// + +//#define DEBUG_AML_PARSER + +using System.Collections; +using System.Diagnostics; +using System.Text; + +using AccessType = Microsoft.Singularity.Hal.Acpi.AcpiObject.AccessType; +using AccessAttrib = Microsoft.Singularity.Hal.Acpi.AcpiObject.AccessAttrib; +using LockRule = Microsoft.Singularity.Hal.Acpi.AcpiObject.LockRule; +using UpdateRule = Microsoft.Singularity.Hal.Acpi.AcpiObject.UpdateRule; +using SerializeFlag = Microsoft.Singularity.Hal.Acpi.AcpiObject.SerializeFlag; +using AbsoluteNodePath = Microsoft.Singularity.Hal.Acpi.AcpiNamespace.AbsoluteNodePath; + +using Microsoft.Singularity.Hal.Acpi.AmlParserUnions; + +// TODO list: +// * Better error handling. Currently just returns Failure if it fails to parse. + +// This file implements a simple recursive descent parser based +// directly on the ACPI specification, Revision 3.0b, section 18.2, in +// order to ensure maximum compliance. Compile-time inlining and +// tail-recursion elimination should make it relatively efficient +// considering the simplicity of the grammar; for this reason, and +// because of annoying snags like the sub-byte encoding of package +// length, I opted for this implementation instead of using a +// full-blown parser generator like netcc on Toolbox. + +// Most rules are taken directly from the spec. I made casing +// consistent and renamed the nonterminals "String" and "Object" to +// avoid keyword conflict, and changed some rules where indicated to +// make the grammar unambiguous and sensible. + +// Note that in some cases identifiers are inconsistent with established +// naming conventions like "AMLCode" instead of "AmlCode". This is for +// consistency with the specification. + +namespace Microsoft.Singularity.Hal.Acpi +{ + // Terminology used by ACPI spec for basic integer types (from section 18.2.3): + // + // ByteData := 0x00 - 0xFF + // WordData := ByteData[0:7] ByteData[8:15] + // // 0x0000-0xFFFF + // DWordData := WordData[0:15] WordData[16:31] + // // 0x00000000-0xFFFFFFFF + // QWordData := DWordData[0:31] DWordData[32:63] + // // 0x0000000000000000-0xFFFFFFFFFFFFFFFF + + using ByteData = System.Byte; + using WordData = System.UInt16; + using DWordData = System.UInt32; + using QWordData = System.UInt64; + + // This interface defines a basic read-only stream interface for the + // AML parser input that is sufficient for the purposes of the parser. + // Any attempt to read past the end of the stream produces the exception + // EndOfAmlStreamException. + public interface IAmlStream + { + // Read 8-bit byte at the given offset, updating the offset past it + ByteData ReadByteData(ref int offset); + + // Try to read 8-bit byte at the given offset, updating the offset past it, + // and returning false and not updating offset if offset is at end of stream. + bool TryReadByteData(ref int offset, out ByteData result); + + // Read 8-bit byte at the given offset as a character, updating the offset past it + char ReadChar(ref int offset); + + // Reads "length" 8-bit bytes at the given offset, updating the offset past them + ByteData[] ReadByteDataArray(ref int offset, int length); + + // Reads "length" 8-bit bytes at the given offset, updating the offset past them, + // and returning false and not updating offset if offset is at end of stream. + bool TryReadByteDataArray(ref int offset, int length, out ByteData[] result); + + // Read 16-bit word at the given offset, updating the offset past it + WordData ReadWordData(ref int offset); + + // Read 32-bit word at the given offset, updating the offset past it + DWordData ReadDWordData(ref int offset); + + // Read 64-bit word at the given offset, updating the offset past it + QWordData ReadQWordData(ref int offset); + } + + public class EndOfAmlStreamException : System.Exception + { + } + + public class AmlMalformattedException : System.Exception + { + public AmlMalformattedException() + : base("Malformatted AML") { } + + public AmlMalformattedException(string reason) + : base("Malformatted AML: " + reason) { } + } + + // Base parser node class + public abstract class AmlParserNode + { + public abstract void Accept(AmlParserNodeVisitor v); + } + +#if !SINGULARITY_KERNEL + public class Debug + { + public static void Assert(bool b) + { + if (!b) { + throw new System.Exception("Failed assertion"); + } + } + + public static void Assert(bool b, string s) + { + if (!b) { + throw new System.Exception("Failed assertion"); + } + } + } +#endif + + // Main parser class + public class AmlParser + { + private IAmlStream stream; + private IDictionary methodNumArgsByName = new SortedList(); + private AcpiNamespace acpiNamespace; + private AcpiNamespace.AbsoluteNodePath currentNodePath; + + public AmlParser(IAmlStream stream, AcpiNamespace acpiNamespace, + AcpiNamespace.AbsoluteNodePath initialNodePath) + { + this.stream = stream; + this.acpiNamespace = acpiNamespace; + this.currentNodePath = initialNodePath; + } + + public enum ParseSuccess + { + Success, + Failure + } + const ParseSuccess Success = ParseSuccess.Success; + const ParseSuccess Failure = ParseSuccess.Failure; + + private void VerifyFormat(bool condition, string malformattedReason) + { + if (!condition) { + throw new AmlMalformattedException(malformattedReason); + } + } + + // + // Section 18.2: AML Grammar Definition + // + + // This section defines the byte values that make up an AML byte stream. + // The AML encoding can be categorized in the following groups: + // * Table and Table Header encoding + // * Name objects encoding + // * Data objects encoding + // * Package length encoding + // * Term objects encoding + // * Miscellaneous objects encoding + + // + // Section 18.2.1: Table and Table Header Encoding + // + + // AMLCode := DefBlockHdr TermList + //[DefBlockHdr is handled by SystemTableHeader under Kernel\Singulary.Acpi, so + // we just make this top-level rule contain a TermList.] + + public class AMLCode : AmlParserNode + { + public TermObj[] termList; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + // Main entry point + public ParseSuccess ParseAMLCode(out AMLCode result, ref int offset, int endOffset) + { + int offset2 = offset; + result = new AMLCode(); + if (ParseTermList(out result.termList, ref offset2, endOffset) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // DefBlockHdr := TableSignature TableLength SpecCompliance CheckSum OemID + // OemTableID OemRevision CreatorID CreatorRevision + //[Handled by SystemTableHeader under Kernel\Singulary.Acpi ] + + // TableSignature := DWordData // As defined in section 5.2.3. + // TableLength := DWordData // Length of the table in bytes including + // // the block header. + // SpecCompliance := ByteData // The revision of the structure. + // CheckSum := ByteData // Byte checksum of the entire table. + // OemID := ByteData(6) // OEM ID of up to 6 characters. If the OEM + // // ID is shorter than 6 characters, it + // // can be terminated with a NULL + // // character. + // OemTableID := ByteData(8) // OEM Table ID of up to 8 characters. If + // the OEM Table ID is shorter than 8 + // characters, it can be terminated with + // a NULL character. + // OemRevision := DWordData // OEM Table Revision. + // CreatorID := DWordData // Vendor ID of the ASL compiler. + // CreatorRevision := DWordData // Revision of the ASL compiler. + + // + // Section 18.2.2: Name Objects Encoding + // + + // [These rules just constrain the characters in NameSegs, + // already described by assertions in AcpiNamespace.AssertIsValidName(). + // LeadNameChar := 'A'-'Z' | '_' + // DigitChar := '0'-'9' + // NameChar := DigitChar | LeadNameChar + + // RootChar := '\' + + private bool IsRootChar(char data) + { + return data == (byte)'\\'; + } + + private ParseSuccess ParseRootChar(ref int offset) + { + int offset2 = offset; + char c = stream.ReadChar(ref offset2); + if (!IsRootChar(c)) { + return Failure; + } + offset = offset2; + return Success; + } + + // ParentPrefixChar := '^' + + private bool IsParentPrefixChar(char data) + { + return data == (byte)'^'; + } + + private ParseSuccess ParseParentPrefixChar(ref int offset) + { + int offset2 = offset; + char c = stream.ReadChar(ref offset2); + if (!IsParentPrefixChar(c)) { + return Failure; + } + offset = offset2; + return Success; + } + + // NameSeg := + // // Notice that NameSegs shorter than 4 characters + // // are filled with trailing underscores ('_'s). + + public class NameSeg : AmlParserNode + { + public string data; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private char[] nameCharsTemp = new char[4]; + + private ParseSuccess ParseNameSeg(out NameSeg result, ref int offset) + { + int offset2 = offset; + result = null; + + // First check first character to avoid spurious allocations + char firstChar = stream.ReadChar(ref offset2); + if (!(System.Char.IsUpper(firstChar) || firstChar == '_')) { + return Failure; + } + + offset2 = offset; // Rewind to first character + byte[] nameBytes; + if (!stream.TryReadByteDataArray(ref offset2, 4, out nameBytes)) { + return Failure; + } + + if (!AcpiNamespace.IsValidName(nameBytes)) { + return Failure; + } + + for (int i = 0; i < nameCharsTemp.Length; i++) { + nameCharsTemp[i] = (char)nameBytes[i]; + } + string name = new string(nameCharsTemp); + + result = new NameSeg(); + result.data = name; + + offset = offset2; + return Success; + } + + // NameString := | + + public class NameString : AmlParserNode + { + public AcpiNamespace.NodePath nodePath; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private string[] NamePathToStringArray(NamePath namePath) + { + string[] result = new string[namePath.segments.Length]; + for(int i = 0; i < result.Length; i++) { + result[i] = namePath.segments[i].data; + } + return result; + } + + private ParseSuccess ParseNameString(out NameString result, ref int offset) + { + result = null; + int offset2 = offset; + + if (ParseRootChar(ref offset2) == Success) { + NamePath namePath; + if (ParseNamePath(out namePath, ref offset2) == Failure) { + return Failure; + } + result = new NameString(); + result.nodePath = new AcpiNamespace.NodePath(true/*isAbsolute*/, 0, NamePathToStringArray(namePath)); + } + else { + PrefixPath prefixPath; + NamePath namePath; + if (ParsePrefixPath(out prefixPath, ref offset2) == Failure || + ParseNamePath(out namePath, ref offset2) == Failure) { + return Failure; + } + result = new NameString(); + result.nodePath = new AcpiNamespace.NodePath(false/*isAbsolute*/, prefixPath.length, NamePathToStringArray(namePath)); + } + + offset = offset2; + return Success; + } + + // PrefixPath := Nothing | <'^' PrefixPath> + // [I use ParentPrefixChar (otherwise unused) for '^'] + + public class PrefixPath : AmlParserNode + { + public int length; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParsePrefixPath(out PrefixPath result, ref int offset) + { + int offset2 = offset; + result = new PrefixPath(); + result.length = 0; + while (ParseParentPrefixChar(ref offset2) != Failure) { + result.length++; + } + offset = offset2; + return Success; + } + + // NamePath := NameSeg | DualNamePath | MultiNamePath | NullName + + public class NamePath : AmlParserNode + { + public NameSeg[] segments; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseNamePath(out NamePath result, ref int offset) + { + int offset2 = offset; + result = new NamePath(); + + if (ParseDualNamePath(out result.segments, ref offset2) == Failure && + ParseMultiNamePath(out result.segments, ref offset2) == Failure && + ParseNullName(out result.segments, ref offset2) == Failure) { + result.segments = new NameSeg[1]; + if (ParseNameSeg(out result.segments[0], ref offset2) == Failure) { + return Failure; + } + } + + offset = offset2; + return Success; + } + + // DualNamePath := DualNamePrefix NameSeg NameSeg + + private ParseSuccess ParseDualNamePath(out NameSeg[] result, ref int offset) + { + int offset2 = offset; + result = null; + + char prefix = stream.ReadChar(ref offset2); + if (prefix != DualNamePrefix) { + return Failure; + } + + result = new NameSeg[2]; + if (ParseNameSeg(out result[0], ref offset2) == Failure || + ParseNameSeg(out result[1], ref offset2) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DualNamePrefix := 0x2E + + private const char DualNamePrefix = '\x002E'; + + // MultiNamePath := MultiNamePrefix SegCount NameSeg(SegCount) + + private ParseSuccess ParseMultiNamePath(out NameSeg[] result, ref int offset) + { + int offset2 = offset; + result = null; + + char prefix = stream.ReadChar(ref offset2); + if (prefix != MultiNamePrefix) { + return Failure; + } + + SegCount segCount; + if (ParseSegCount(out segCount, ref offset2) == Failure) { + return Failure; + } + result = new NameSeg[segCount.data]; + for (int segNum = 0; segNum < segCount.data; segNum++) { + if (ParseNameSeg(out result[segNum], ref offset2) == Failure) { + return Failure; + } + } + + offset = offset2; + return Success; + } + + // MultiNamePrefix := 0x2F + + private const char MultiNamePrefix = '\x002F'; + + // SegCount := ByteData + // Note: SegCount can be from 1 to 255. For example: + // MultiNamePrefix(35) is encoded as 0x2f 0x23 and followed by + // 35 NameSegs. So, the total encoding length will be 1 + 1 + + // 35*4 = 142. Notice that: DualNamePrefix NameSeg NameSeg has + // a smaller encoding than the encoding of: MultiNamePrefix(2) + // NameSeg NameSeg + + public class SegCount : AmlParserNode + { + public ByteData data; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseSegCount(out SegCount result, ref int offset) + { + int offset2 = offset; + result = new SegCount(); + result.data = stream.ReadByteData(ref offset2); + offset = offset2; + return Success; + } + + // SimpleName := NameString | ArgObj | LocalObj + // See AmlParser.csunion + + private ParseSuccess ParseSimpleName(out SimpleName result, ref int offset) + { + int offset2 = offset; + + NameString nameString; + ArgObj argObj; + LocalObj localObj; + if (ParseArgObj(out argObj, ref offset2) == Success) { + result = SimpleName.CreateArgObj(argObj); + } + else if (ParseLocalObj(out localObj, ref offset2) == Success) { + result = SimpleName.CreateLocalObj(localObj); + } + else if (ParseNameString(out nameString, ref offset2) == Success) { + result = SimpleName.CreateNameString(nameString); + } + else { + result = null; + return Failure; + } + + offset = offset2; + return Success; + } + + // SuperName := SimpleName | DebugObj | Type6Opcode + // See AmlParser.csunion + + private ParseSuccess ParseSuperName(out SuperName result, ref int offset) + { + int offset2 = offset; + + // There's an ambiguity here! What to do? + // SuperName -> SimpleName -> NameString + // SuperName -> Type6Opcode -> UserTermObj + // -> NameString TermArgList -> NameString + // For now just doing whatever the parser chooses first. + SimpleName simpleName; + DebugObj debugObj; + Type6Opcode type6Opcode; + if (ParseSimpleName(out simpleName, ref offset2) == Success) { + result = SuperName.CreateSimpleName(simpleName); + } + else if (ParseDebugObj(out debugObj, ref offset2) == Success) { + result = SuperName.CreateDebugObj(debugObj); + } + else if (ParseType6Opcode(out type6Opcode, ref offset2) == Success) { + result = SuperName.CreateType6Opcode(type6Opcode); + } + else { + result = null; + return Failure; + } + + offset = offset2; + return Success; + } + + // NullName := 0x00 + + private ParseSuccess ParseNullName(out NameSeg[] result, ref int offset) + { + int offset2 = offset; + result = null; + + char prefix = stream.ReadChar(ref offset2); + if (prefix != '\0') { + return Failure; + } + + result = new NameSeg[0]; + offset = offset2; + return Success; + } + + // Target := SuperName | NullName + // [NOTE: SuperName can reduce to NullName anyway: + // SuperName -> SimpleName -> NameString -> PrefixPath NamePath + // -> NamePath -> NullName + // Therefore Target is the same thing as SuperName. We wrap it + // for type safety only.] + + public class Target : AmlParserNode + { + public SuperName superName; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseTarget(out Target result, ref int offset) + { + int offset2 = offset; + result = new Target(); + if (ParseSuperName(out result.superName, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // + // Section 18.2.3: Data Objects Encoding + // + + // ComputationalData := ByteConst | WordConst | DWordConst | QWordConst | + // StringConst | ConstObj | RevisionOp | DefBuffer + // See AmlParser.csunion + + private ParseSuccess ParseComputationalData(out ComputationalData result, ref int offset) + { + int offset2 = offset; + + ByteData byteConst; + WordData wordConst; + DWordData dWordConst; + QWordData qWordConst; + string stringConst; + ConstObj constObj; + DefBuffer defBuffer; + if (ParseByteConst(out byteConst, ref offset2) == Success) { + result = ComputationalData.CreateByteConst(byteConst); + } + else if (ParseWordConst(out wordConst, ref offset2) == Success) { + result = ComputationalData.CreateWordConst(wordConst); + } + else if (ParseDWordConst(out dWordConst, ref offset2) == Success) { + result = ComputationalData.CreateDWordConst(dWordConst); + } + else if (ParseQWordConst(out qWordConst, ref offset2) == Success) { + result = ComputationalData.CreateQWordConst(qWordConst); + } + else if (ParseStringConst(out stringConst, ref offset2) == Success) { + result = ComputationalData.CreateStringConst(stringConst); + } + else if (ParseConstObj(out constObj, ref offset2) == Success) { + result = ComputationalData.CreateConstObj(constObj); + } + else if (ParseRevisionOp(ref offset2) == Success) { + result = ComputationalData.CreateRevisionOp(); + } + else if (ParseDefBuffer(out defBuffer, ref offset2) == Success) { + result = ComputationalData.CreateDefBuffer(defBuffer); + } + else { + result = null; + return Failure; + } + + offset = offset2; + return Success; + } + + // DataObject := ComputationalData | DefPackage | DefVarPackage + // See AmlParser.csunion + + private ParseSuccess ParseDataObject(out DataObject result, ref int offset) + { + int offset2 = offset; + + ComputationalData computationalData; + DefPackage defPackage; + DefVarPackage defVarPackage; + if (ParseDefPackage(out defPackage, ref offset2) == Success) { + result = DataObject.CreateDefPackage(defPackage); + } + else if (ParseDefVarPackage(out defVarPackage, ref offset2) == Success) { + result = DataObject.CreateDefVarPackage(defVarPackage); + } + else if (ParseComputationalData(out computationalData, ref offset2) == Success) { + result = DataObject.CreateComputationalData(computationalData); + } + else { + result = null; + return Failure; + } + + offset = offset2; + return Success; + } + + // DataRefObject := DataObject | ObjectReference | DDBHandle + // [Note that here ObjectReference and DDBHandle do not have + // rules; the only reasonable interpretation is that they mean + // "TermArg => ObjectReference" and "DDBHandleObject".] + // See AmlParser.csunion + + private ParseSuccess ParseDataRefObject(out DataRefObject result, ref int offset) + { + int offset2 = offset; + + DataObject dataObject; + TermArg objectReference; + DDBHandleObject ddbHandle; + if (ParseDataObject(out dataObject, ref offset2) == Success) { + result = DataRefObject.CreateDataObject(dataObject); + } + else if (ParseTermArg(out objectReference, ref offset2) == Success) { + //if (!TermArgEvaluatesTo(objectReference, TermArgType.ObjectReference) { + // return Failure; + //} + result = DataRefObject.CreateObjectReference(objectReference); + } + else if (ParseDDBHandleObject(out ddbHandle, ref offset2) == Success) { + result = DataRefObject.CreateDDBHandle(ddbHandle); + } + else { + result = null; + return Failure; + } + + offset = offset2; + return Success; + } + + // ByteConst := BytePrefix ByteData + + private ParseSuccess ParseByteConst(out ByteData result, ref int offset) + { + int offset2 = offset; + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != BytePrefix) { + result = 0; + return Failure; + } + result = stream.ReadByteData(ref offset2); + offset = offset2; + return Success; + } + + // BytePrefix := 0x0A + + const ByteData BytePrefix = 0x0A; + + // WordConst := WordPrefix WordData + + private ParseSuccess ParseWordConst(out WordData result, ref int offset) + { + int offset2 = offset; + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != WordPrefix) { + result = 0; + return Failure; + } + result = stream.ReadWordData(ref offset2); + offset = offset2; + return Success; + } + + // WordPrefix := 0x0B + + const ByteData WordPrefix = 0x0B; + + // DWordConst := DWordPrefix DWordData + + private ParseSuccess ParseDWordConst(out DWordData result, ref int offset) + { + int offset2 = offset; + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != DWordPrefix) { + result = 0; + return Failure; + } + result = stream.ReadDWordData(ref offset2); + offset = offset2; + return Success; + } + + // DWordPrefix := 0x0C + + const ByteData DWordPrefix = 0x0C; + + // QWordConst := QWordPrefix QWordData + + private ParseSuccess ParseQWordConst(out QWordData result, ref int offset) + { + int offset2 = offset; + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != QWordPrefix) { + result = 0; + return Failure; + } + result = stream.ReadQWordData(ref offset2); + offset = offset2; + return Success; + } + + // QWordPrefix := 0x0E + + const ByteData QWordPrefix = 0x0E; + + // StringConst := StringPrefix AsciiCharList NullChar + // AsciiCharList := Nothing | + + private ParseSuccess ParseStringConst(out string result, ref int offset) + { + int offset2 = offset; + result = null; + + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != StringPrefix) { + return Failure; + } + + StringBuilder resultBuilder = new StringBuilder(); + while (true) { + char c = stream.ReadChar(ref offset2); + if (c == NullChar) { + break; + } + if (!IsAsciiChar(c)) { + return Failure; + } + resultBuilder.Append(c); + } + + result = resultBuilder.ToString(); + offset = offset2; + return Success; + } + + // StringPrefix := 0x0D + + const ByteData StringPrefix = 0x0D; + + // ConstObj := ZeroOp | OneOp | OnesOp + + public class ConstObj : AmlParserNode + { + public ByteData op; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseConstObj(out ConstObj result, ref int offset) + { + int offset2 = offset; + result = new ConstObj(); + ByteData op = stream.ReadByteData(ref offset2); + if (op != ZeroOp && + op != OneOp && + op != OnesOp) { + return Failure; + } + result.op = op; + offset = offset2; + return Success; + } + + // ByteList := Nothing | + // [To make this rule make sense, it needs a length + // limit for termination. The rule using it should know this.] + + private ParseSuccess ParseByteList(out ByteData[] result, ref int offset, int endOffset) + { + int offset2 = offset; + result = stream.ReadByteDataArray(ref offset2, endOffset - offset); + offset = offset2; + return Success; + } + + // AsciiChar := 0x01 - 0x7F + + private bool IsAsciiChar(char data) + { + return (byte)data >= 0x01 && (byte)data <= 0x7F; + } + + // NullChar := 0x00 + + char NullChar = '\0'; + + // ZeroOp := 0x00 + + public const ByteData ZeroOp = 0x00; + + // OneOp := 0x01 + + public const ByteData OneOp = 0x01; + + // OnesOp := 0xFF + + public const ByteData OnesOp = 0xFF; + + // RevisionOp := ExtOpPrefix 0x30 + + private ParseSuccess ParseRevisionOp(ref int offset) + { + int offset2 = offset; + ByteData b = stream.ReadByteData(ref offset2); + if (b != ExtOpPrefix) { + return Failure; + } + b = stream.ReadByteData(ref offset2); + if (b != 0x30) { + return Failure; + } + offset = offset2; + return Success; + } + + // ExtOpPrefix := 0x5B + + const ByteData ExtOpPrefix = 0x5B; + + // + // Section 18.2.4: Package Length Encoding + // + + // PkgLength := PkgLeadByte | + // | + // | + // + + // PkgLeadByte := + // + // + + // Note: The high 2 bits of the first byte reveal how many + // follow bytes are in the PkgLength. If the PkgLength has + // only one byte, bit 0 through 5 are used to encode the + // package length (in other words, values 0-63). If the + // package length value is more than 63, more than one byte + // must be used for the encoding in which case bit 4 and 5 of + // the PkgLeadByte are reserved and must be zero. If the + // multiple bytes encoding is used, bits 0-3 of the + // PkgLeadByte become the least significant 4 bits of the + // resulting package length value. The next ByteData will + // become the next least significant 8 bits of the resulting + // value and so on, up to 3 ByteData bytes. Thus, the maximum + // package length is 2**28. + + private ParseSuccess ParsePkgLength(out int result, ref int offset) + { + int offset2 = offset; + int length = 0; // Maximum 2^31 - 1 >= 2^28 + + ByteData pkgLeadByte = stream.ReadByteData(ref offset2); + int followingByteDataCount = pkgLeadByte >> 6; + if (followingByteDataCount == 0) { + // Bits 5-0 contain package length + length = pkgLeadByte & 0x3F; + } + else { + VerifyFormat(((pkgLeadByte >> 4) & 3) == 0, + "Expect bits 5-4 zero when bits 7-6 nonzero in PkgLeadByte"); + length = pkgLeadByte & 0xF; + int shiftAmount = 4; + for (int i = 0; i < followingByteDataCount; i++) { + ByteData b = stream.ReadByteData(ref offset2); + length = length | (((int)b) << shiftAmount); + shiftAmount += 8; + } + } + result = length; + offset = offset2; + return Success; + } + + // [This wrapper converts a PkgLength result to a more useful + // position-independent end offset of the area measured by the + // PkgLength. This offset is exclusive (the byte at that offset + // is not itself in the area).] + private ParseSuccess ParsePkgLengthEndOffset(out int endOffset, ref int offset) + { + int offset2 = offset; + int length; + if (ParsePkgLength(out length, ref offset2) == Failure) { + endOffset = 0; + return Failure; + } + endOffset = offset + length; + offset = offset2; + return Success; + } + + // + // Section 18.2.5: Term Objects Encoding + // + + // TermObj := NameSpaceModifierObj | NamedObj | Type1Opcode | Type2Opcode + // See AmlParser.csunion + + private ParseSuccess ParseTermObj(out TermObj result, ref int offset, int endOffset) + { + int offset2 = offset; + + NameSpaceModifierObj nameSpaceModifierObj; + NamedObj namedObj; + Type1Opcode type1Opcode; + Type2Opcode type2Opcode; + if (ParseNamedObj(out namedObj, ref offset2) == Success) { + result = TermObj.CreateNamedObj(namedObj); + } + else if (ParseType1Opcode(out type1Opcode, ref offset2, endOffset) == Success) { + result = TermObj.CreateType1Opcode(type1Opcode); + } + else if (ParseType2Opcode(out type2Opcode, ref offset2) == Success) { + result = TermObj.CreateType2Opcode(type2Opcode); + } + else if (ParseNameSpaceModifierObj(out nameSpaceModifierObj, ref offset2) == Success) { + result = TermObj.CreateNameSpaceModifierObj(nameSpaceModifierObj); + } + else { + result = null; + return Failure; + } + + offset = offset2; + return Success; + } + + // TermList := Nothing | + // [To make this rule make sense, it needs a length + // limit for termination. The rule using it should know this.] + + private class TermList + { + ArrayList list = new ArrayList(); + + public void Add(TermObj termObj) + { + list.Add(termObj); + } + + public TermObj[] ToArray() + { + return (TermObj[])list.ToArray(typeof(TermObj)); + } + } + + public ParseSuccess ParseTermList(out TermObj[] result, ref int offset, int endOffset) + { + result = null; + TermList termObjs = new TermList(); + int offset2 = offset; + + while (offset2 < endOffset) { +#if DEBUG_AML_PARSER + System.Console.WriteLine(offset2.ToString("x8")); +#endif + TermObj termObj; + if (ParseTermObj(out termObj, ref offset2, endOffset) == Failure) { + return Failure; + } + termObjs.Add(termObj); + } + Debug.Assert(offset2 == endOffset); + + result = termObjs.ToArray(); + offset = offset2; + return Success; + } + + // TermArg := Type2Opcode | DataObject | ArgObj | LocalObj + // See AmlParser.csunion + + private ParseSuccess ParseTermArg(out TermArg result, ref int offset) + { + int offset2 = offset; + + Type2Opcode type2Opcode; + DataObject dataObject; + ArgObj argObj; + LocalObj localObj; + // Type2Opcode must be tried after DataObject, because the constant object + // Zero will otherwise parse as a UserTermObj with NullName for Namestring. + if (ParseDataObject(out dataObject, ref offset2) == Success) { + result = TermArg.CreateDataObject(dataObject); + } + else if (ParseArgObj(out argObj, ref offset2) == Success) { + result = TermArg.CreateArgObj(argObj); + } + else if (ParseLocalObj(out localObj, ref offset2) == Success) { + result = TermArg.CreateLocalObj(localObj); + } + else if (ParseType2Opcode(out type2Opcode, ref offset2) == Success) { + result = TermArg.CreateType2Opcode(type2Opcode); + } + else { + result = null; + return Failure; + } + + offset = offset2; + return Success; + } + + // UserTermObj := NameString TermArgList + // [This rule annoyingly uses a variable-length list that, unlike every other + // such list, is determined not by an obvious encoded PkgLength. According to the engineer + // who worked on the Windows AML parser, it's necessary to look up the method indicated + // by the NameString to determine its number of arguments. To accomplish this we delay + // parsing of the method body until after all methods have been loaded into the namespace.] + + public class UserTermObj : AmlParserNode + { + public NameString nameString; + public TermArg[] termArgList; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseUserTermObj(out UserTermObj result, ref int offset) + { + int offset2 = offset; + result = new UserTermObj(); + + if (ParseNameString (out result.nameString, ref offset2) == Failure) { + return Failure; + } + + int numTermArgs = 0; + AcpiNamespace.Node node = null; + if (acpiNamespace != null) { + node = acpiNamespace.LookupNode(result.nameString.nodePath, currentNodePath); + } + if (node != null && node.Value is AcpiObject.Method) { + numTermArgs = ((AcpiObject.Method)node.Value).ArgCount; + } + else { + numTermArgs = 0; // If we're still loading, it can't be a method call, must have zero args + } + + if (ParseTermArgListN(out result.termArgList, ref offset2, numTermArgs) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // TermArgList := Nothing | + // [To make this rule make sense, it needs a length + // limit for termination. The rule using it should know this.] + + private class TermArgList + { + ArrayList list = new ArrayList(); + + public void Add(TermArg termArg) + { + list.Add(termArg); + } + + public TermArg[] ToArray() + { + return (TermArg[])list.ToArray(typeof(TermArg)); + } + } + + private ParseSuccess ParseTermArgList(out TermArg[] result, ref int offset, int endOffset) + { + result = null; + TermArgList termArgs = new TermArgList(); + int offset2 = offset; + + while (offset2 < endOffset) { + TermArg termArg; + if (ParseTermArg(out termArg, ref offset2) == Failure) { + return Failure; + } + termArgs.Add(termArg); + } + Debug.Assert(offset2 == endOffset); + + result = termArgs.ToArray(); + offset = offset2; + return Success; + } + + // For UserTermObj rule, parses a specified number of TermArgs + private ParseSuccess ParseTermArgListN(out TermArg[] result, ref int offset, int numTermArgs) + { + int offset2 = offset; + result = new TermArg[numTermArgs]; + + for (int i = 0; i < numTermArgs; i++) { + if (ParseTermArg(out result[i], ref offset2) == Failure) { + return Failure; + } + } + + offset = offset2; + return Success; + } + + // AmlObjectList := Nothing | + // [To make this rule make sense, it needs a length + // limit for termination. The rule using it should know this.] + + private class AmlObjectList + { + ArrayList list = new ArrayList(); + + public void Add(AmlObject amlObject) + { + list.Add(amlObject); + } + + public AmlObject[] ToArray() + { + return (AmlObject[])list.ToArray(typeof(AmlObject)); + } + } + + private ParseSuccess ParseAmlObjectList(out AmlObject[] result, ref int offset, int endOffset) + { + result = null; + AmlObjectList amlObjects = new AmlObjectList(); + int offset2 = offset; + + while (offset2 < endOffset) { + AmlObject amlObject; + if (ParseAmlObject(out amlObject, ref offset2) == Failure) { + return Failure; + } + amlObjects.Add(amlObject); + } + Debug.Assert(offset2 == endOffset); + + result = amlObjects.ToArray(); + offset = offset2; + return Success; + } + + // AmlObject := NameSpaceModifierObj | NamedObj + // See AmlParser.csunion + + private ParseSuccess ParseAmlObject(out AmlObject result, ref int offset) + { + int offset2 = offset; + + NameSpaceModifierObj nameSpaceModifierObj; + NamedObj namedObj; + if (ParseNameSpaceModifierObj(out nameSpaceModifierObj, ref offset2) == Success) { + result = AmlObject.CreateNameSpaceModifierObj(nameSpaceModifierObj); + } + else if (ParseNamedObj(out namedObj, ref offset2) == Success) { + result = AmlObject.CreateNamedObj(namedObj); + } + else { + result = null; + return Failure; + } + + offset = offset2; + return Success; + } + + // + // Section 18.2.5.1: Namespace Modifier Objects Encoding + // + + // NameSpaceModifierObj := DefAlias | DefName | DefScope + // See AmlParser.csunion + + private ParseSuccess ParseNameSpaceModifierObj(out NameSpaceModifierObj result, ref int offset) + { + int offset2 = offset; + + DefAlias defAlias; + DefName defName; + DefScope defScope; + if (ParseDefAlias(out defAlias, ref offset2) == Success) { + result = NameSpaceModifierObj.CreateDefAlias(defAlias); + } + else if (ParseDefName(out defName, ref offset2) == Success) { + result = NameSpaceModifierObj.CreateDefName(defName); + } + else if (ParseDefScope(out defScope, ref offset2) == Success) { + result = NameSpaceModifierObj.CreateDefScope(defScope); + } + else { + result = null; + return Failure; + } + + offset = offset2; + return Success; + } + + // DefAlias := AliasOp NameString NameString + + public class DefAlias : AmlParserNode + { + public NameString sourceName; + public NameString aliasName; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefAlias(out DefAlias result, ref int offset) + { + int offset2 = offset; + result = new DefAlias(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != AliasOp) { + return Failure; + } + if (ParseNameString(out result.sourceName, ref offset2) == Failure || + ParseNameString(out result.aliasName, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // AliasOp := 0x06 + + const ByteData AliasOp = 0x06; + + // DefName := NameOp NameString DataRefObject + // [There is no DataRefObject rule. I assume they mean TermArg => DataRefObject as + // in the ArgObject rule.] + + public class DefName : AmlParserNode + { + public NameString nameString; + public TermArg dataRefObject; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefName(out DefName result, ref int offset) + { + int offset2 = offset; + result = new DefName(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != NameOp) { + return Failure; + } + if (ParseNameString(out result.nameString, ref offset2) == Failure || + ParseTermArg(out result.dataRefObject, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.dataRefObject, TermArgType.DataRefObject*/) { + return Failure; + } + offset = offset2; + return Success; + } + + // NameOp := 0x08 + + const ByteData NameOp = 0x08; + + // DefScope := ScopeOp PkgLength NameString TermList + + public class DefScope : AmlParserNode + { + public NameString nameString; + public TermObj[] termList; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefScope(out DefScope result, ref int offset) + { + int offset2 = offset; + result = new DefScope(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != ScopeOp) { + return Failure; + } + + int endOffset; + if (ParsePkgLengthEndOffset(out endOffset, ref offset2) == Failure) { + return Failure; + } + + if (ParseNameString(out result.nameString, ref offset2) == Failure) { + return Failure; + } + + AbsoluteNodePath oldNodePath = currentNodePath; + if (acpiNamespace != null) { + currentNodePath = acpiNamespace.LookupNode(result.nameString.nodePath, currentNodePath).Path; + } + if (ParseTermList(out result.termList, ref offset2, endOffset) == Failure) { + return Failure; + } + currentNodePath = oldNodePath; + + offset = offset2; + return Success; + } + + // ScopeOp := 0x10 + + const ByteData ScopeOp = 0x10; + + // + // Section 18.2.5.2: Named Objects Encoding + // + + // NamedObj := DefBankField | DefCreateBitField | DefCreateByteField | + // DefCreateDWordField | DefCreateField | + // DefCreateQWordField | DefCreateWordField | + // DefDataRegion | DefDevice | DefEvent | DefField | + // DefIndexField | DefMethod | DefMutex | DefOpRegion | + // DefPowerRes | DefProcessor | DefThermalZone + // See AmlParser.csunion + + private ParseSuccess ParseNamedObj(out NamedObj result, ref int offset) + { + int offset2 = offset; + + DefBankField defBankField; + DefCreateBitField defCreateBitField; + DefCreateByteField defCreateByteField; + DefCreateDWordField defCreateDWordField; + DefCreateField defCreateField; + DefCreateQWordField defCreateQWordField; + DefCreateWordField defCreateWordField; + DefDataRegion defDataRegion; + DefDevice defDevice; + DefEvent defEvent; + DefField defField; + DefIndexField defIndexField; + DefMethod defMethod; + DefMutex defMutex; + DefOpRegion defOpRegion; + DefPowerRes defPowerRes; + DefProcessor defProcessor; + DefThermalZone defThermalZone; + if (ParseDefBankField(out defBankField, ref offset2) == Success ) { + result = NamedObj.CreateDefBankField(defBankField); + } + else if (ParseDefCreateBitField(out defCreateBitField, ref offset2) == Success ) { + result = NamedObj.CreateDefCreateBitField(defCreateBitField); + } + else if (ParseDefCreateByteField(out defCreateByteField, ref offset2) == Success ) { + result = NamedObj.CreateDefCreateByteField(defCreateByteField); + } + else if (ParseDefCreateDWordField(out defCreateDWordField, ref offset2) == Success ) { + result = NamedObj.CreateDefCreateDWordField(defCreateDWordField); + } + else if (ParseDefCreateField(out defCreateField, ref offset2) == Success ) { + result = NamedObj.CreateDefCreateField(defCreateField); + } + else if (ParseDefCreateQWordField(out defCreateQWordField, ref offset2) == Success ) { + result = NamedObj.CreateDefCreateQWordField(defCreateQWordField); + } + else if (ParseDefCreateWordField(out defCreateWordField, ref offset2) == Success ) { + result = NamedObj.CreateDefCreateWordField(defCreateWordField); + } + else if (ParseDefDataRegion(out defDataRegion, ref offset2) == Success ) { + result = NamedObj.CreateDefDataRegion(defDataRegion); + } + else if (ParseDefDevice(out defDevice, ref offset2) == Success ) { + result = NamedObj.CreateDefDevice(defDevice); + } + else if (ParseDefEvent(out defEvent, ref offset2) == Success ) { + result = NamedObj.CreateDefEvent(defEvent); + } + else if (ParseDefField(out defField, ref offset2) == Success ) { + result = NamedObj.CreateDefField(defField); + } + else if (ParseDefIndexField(out defIndexField, ref offset2) == Success ) { + result = NamedObj.CreateDefIndexField(defIndexField); + } + else if (ParseDefMethod(out defMethod, ref offset2) == Success ) { + result = NamedObj.CreateDefMethod(defMethod); + } + else if (ParseDefMutex(out defMutex, ref offset2) == Success ) { + result = NamedObj.CreateDefMutex(defMutex); + } + else if (ParseDefOpRegion(out defOpRegion, ref offset2) == Success ) { + result = NamedObj.CreateDefOpRegion(defOpRegion); + } + else if (ParseDefPowerRes(out defPowerRes, ref offset2) == Success ) { + result = NamedObj.CreateDefPowerRes(defPowerRes); + } + else if (ParseDefProcessor(out defProcessor, ref offset2) == Success ) { + result = NamedObj.CreateDefProcessor(defProcessor); + } + else if (ParseDefThermalZone(out defThermalZone, ref offset2) == Success) { + result = NamedObj.CreateDefThermalZone(defThermalZone); + } + else { + result = null; + return Failure; + } + + offset = offset2; + return Success; + } + + // DefBankField := BankFieldOp PkgLength NameString NameString BankValue FieldFlags FieldList + + public class DefBankField : AmlParserNode + { + public NameString regionName; + public NameString bankName; + public BankValue bankValue; + public FieldFlags fieldFlags; + public FieldElement[] fieldList; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefBankField(out DefBankField result, ref int offset) + { + int offset2 = offset; + result = new DefBankField(); + if (CheckTwoBytePrefix(BankFieldOp1, BankFieldOp2, ref offset2) == Failure) { + return Failure; + } + + int endOffset; + if (ParsePkgLengthEndOffset(out endOffset, ref offset2) == Failure) { + return Failure; + } + + if (ParseNameString(out result.regionName, ref offset2) == Failure || + ParseNameString(out result.bankName, ref offset2) == Failure || + ParseBankValue (out result.bankValue, ref offset2) == Failure || + ParseFieldFlags(out result.fieldFlags, ref offset2) == Failure || + ParseFieldList (out result.fieldList, ref offset2, endOffset) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // BankFieldOp := ExtOpPrefix 0x87 + + const ByteData BankFieldOp1 = ExtOpPrefix; + const ByteData BankFieldOp2 = 0x87; + + // BankValue := TermArg => Integer + + public class BankValue : AmlParserNode + { + public TermArg integer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseBankValue(out BankValue result, ref int offset) + { + int offset2 = offset; + result = new BankValue(); + + if (ParseTermArg(out result.integer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.integer, TermArgType.Integer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // FieldFlags := ByteData // bit 0-3: AccessType + // // 0 AnyAcc + // // 1 ByteAcc + // // 2 WordAcc + // // 3 DWordAcc + // // 4 QWordAcc + // // 5 BufferAcc + // // 6 Reserved + // // 7-15 Reserved + // // bit 4: LockRule + // // 0 NoLock + // // 1 Lock + // // bit 5-6: UpdateRule + // // 0 Preserve + // // 1 WriteAsOnes + // // 2 WriteAsZeros + // // bit 7: Reserved (must be 0) + + public class FieldFlags : AmlParserNode + { + public AccessType accessType; + public LockRule lockRule; + public UpdateRule updateRule; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseFieldFlags(out FieldFlags result, ref int offset) + { + int offset2 = offset; + result = new FieldFlags(); + + ByteData b = stream.ReadByteData(ref offset2); + + result.accessType = (AccessType)(b & 0xF); + result.lockRule = (LockRule)((b >> 4) & 1); + result.updateRule = (UpdateRule)((b >> 5) & 3); + if (result.updateRule == UpdateRule.Invalid || ((b >> 7) != 0)) { + return Failure; + } + + offset = offset2; + return Success; + } + + // FieldList := Nothing | + // [To make this rule make sense, it needs a length + // limit for termination. The rule using it should know this.] + + private class FieldList + { + ArrayList list = new ArrayList(); + + public void Add(FieldElement fieldElement) + { + list.Add(fieldElement); + } + + public FieldElement[] ToArray() + { + return (FieldElement[])list.ToArray(typeof(FieldElement)); + } + } + + private ParseSuccess ParseFieldList(out FieldElement[] result, ref int offset, int endOffset) + { + result = null; + FieldList fields = new FieldList(); + int offset2 = offset; + + while (offset2 < endOffset) { + FieldElement fieldElement; + if (ParseFieldElement(out fieldElement, ref offset2) == Failure) { + return Failure; + } + fields.Add(fieldElement); + } + Debug.Assert(offset2 == endOffset); + + result = fields.ToArray(); + offset = offset2; + return Success; + } + + // FieldElement := NamedField | ReservedField | AccessField + // See AmlParser.csunion + + private ParseSuccess ParseFieldElement(out FieldElement result, ref int offset) + { + int offset2 = offset; + + NamedField namedField; + ReservedField reservedField; + AccessField accessField; + if (ParseNamedField(out namedField, ref offset2) == Success) { + result = FieldElement.CreateNamedField(namedField); + } + else if (ParseReservedField(out reservedField, ref offset2) == Success) { + result = FieldElement.CreateReservedField(reservedField); + } + else if (ParseAccessField(out accessField, ref offset2) == Success) { + result = FieldElement.CreateAccessField(accessField); + } + else { + result = null; + return Failure; + } + + offset = offset2; + return Success; + } + + // NamedField := NameSeg PkgLength + + public class NamedField : AmlParserNode + { + public NameSeg nameSeg; + public int bitWidth; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseNamedField(out NamedField result, ref int offset) + { + int offset2 = offset; + result = new NamedField(); + + if (ParseNameSeg (out result.nameSeg, ref offset2) == Failure || + ParsePkgLength(out result.bitWidth, ref offset2) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // ReservedField := 0x00 PkgLength + + public class ReservedField : AmlParserNode + { + public int bitWidth; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseReservedField(out ReservedField result, ref int offset) + { + int offset2 = offset; + result = new ReservedField(); + + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != 0x00) { + return Failure; + } + + if (ParsePkgLength(out result.bitWidth, ref offset2) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // AccessField := 0x01 AccessType AccessAttrib + + public class AccessField : AmlParserNode + { + public AccessType accessType; + public AccessAttrib accessAttrib; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseAccessField(out AccessField result, ref int offset) + { + int offset2 = offset; + result = new AccessField(); + + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != 0x01) { + return Failure; + } + + if (ParseAccessType (out result.accessType, ref offset2) == Failure || + ParseAccessAttrib(out result.accessAttrib, ref offset2) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // AccessType := ByteData // Same as AccessType bits of FieldFlags. + + private ParseSuccess ParseAccessType(out AccessType result, ref int offset) + { + result = AccessType.AnyAcc; + int offset2 = offset; + + ByteData b = stream.ReadByteData(ref offset2); + if (b > 0xF) { + return Failure; + } + result = (AccessType)b; + + offset = offset2; + return Success; + } + + // AccessAttrib := ByteData // If AccessType is BufferAcc for the SMB + // // OpRegion, AccessAttrib can be one of + // // the following values: + // // 0x02 SMBQuick + // // 0x04 SMBSendReceive + // // 0x06 SMBByte + // // 0x08 SMBWord + // // 0x0A SMBBlock + // // 0x0C SMBProcessCall + // // 0x0D SMBBlockProcessCall + + private ParseSuccess ParseAccessAttrib(out AcpiObject.AccessAttrib result, ref int offset) + { + result = AccessAttrib.SMBNone; + int offset2 = offset; + + ByteData b = stream.ReadByteData(ref offset2); + AccessAttrib type = (AccessAttrib)b; + if (type != AccessAttrib.SMBNone && + type != AccessAttrib.SMBQuick && + type != AccessAttrib.SMBSendReceive && + type != AccessAttrib.SMBByte && + type != AccessAttrib.SMBWord && + type != AccessAttrib.SMBBlock && + type != AccessAttrib.SMBProcessCall && + type != AccessAttrib.SMBBlockProcessCall) { + return Failure; + } + + result = type; + offset = offset2; + return Success; + } + + // DefCreateBitField := CreateBitFieldOp SourceBuff BitIndex NameString + + public class DefCreateBitField : AmlParserNode + { + public SourceBuff sourceBuff; + public BitIndex bitIndex; + public NameString nameString; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefCreateBitField(out DefCreateBitField result, ref int offset) + { + int offset2 = offset; + result = new DefCreateBitField(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != CreateBitFieldOp) { + return Failure; + } + if (ParseSourceBuff(out result.sourceBuff, ref offset2) == Failure || + ParseBitIndex (out result.bitIndex, ref offset2) == Failure || + ParseNameString(out result.nameString, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // CreateBitFieldOp := 0x8D + + const ByteData CreateBitFieldOp = 0x8D; + + // SourceBuff := TermArg => Buffer + + public class SourceBuff : AmlParserNode + { + public TermArg buffer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseSourceBuff(out SourceBuff result, ref int offset) + { + int offset2 = offset; + result = new SourceBuff(); + + if (ParseTermArg(out result.buffer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.buffer, TermArgType.Buffer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // BitIndex := TermArg => Integer + + public class BitIndex : AmlParserNode + { + public TermArg integer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseBitIndex(out BitIndex result, ref int offset) + { + int offset2 = offset; + result = new BitIndex(); + + if (ParseTermArg(out result.integer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.integer, TermArgType.Integer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefCreateByteField := CreateByteFieldOp SourceBuff ByteIndex NameString + + public class DefCreateByteField : AmlParserNode + { + public SourceBuff sourceBuff; + public ByteIndex byteIndex; + public NameString nameString; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefCreateByteField(out DefCreateByteField result, ref int offset) + { + int offset2 = offset; + result = new DefCreateByteField(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != CreateByteFieldOp) { + return Failure; + } + if (ParseSourceBuff(out result.sourceBuff, ref offset2) == Failure || + ParseByteIndex (out result.byteIndex, ref offset2) == Failure || + ParseNameString(out result.nameString, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // CreateByteFieldOp := 0x8C + + const ByteData CreateByteFieldOp = 0x8C; + + // ByteIndex := TermArg => Integer + + public class ByteIndex : AmlParserNode + { + public TermArg integer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseByteIndex(out ByteIndex result, ref int offset) + { + int offset2 = offset; + result = new ByteIndex(); + + if (ParseTermArg(out result.integer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.integer, TermArgType.Integer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefCreateDWordField := CreateDWordFieldOp SourceBuff ByteIndex NameString + + public class DefCreateDWordField : AmlParserNode + { + public SourceBuff sourceBuff; + public ByteIndex byteIndex; + public NameString nameString; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefCreateDWordField(out DefCreateDWordField result, ref int offset) + { + int offset2 = offset; + result = new DefCreateDWordField(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != CreateDWordFieldOp) { + return Failure; + } + if (ParseSourceBuff(out result.sourceBuff, ref offset2) == Failure || + ParseByteIndex (out result.byteIndex, ref offset2) == Failure || + ParseNameString(out result.nameString, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // CreateDWordFieldOp := 0x8A + + const ByteData CreateDWordFieldOp = 0x8A; + + // DefCreateField := CreateFieldOp SourceBuff BitIndex NumBits NameString + + public class DefCreateField : AmlParserNode + { + public SourceBuff sourceBuff; + public BitIndex bitIndex; + public NumBits numBits; + public NameString nameString; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefCreateField(out DefCreateField result, ref int offset) + { + int offset2 = offset; + result = new DefCreateField(); + + if (CheckTwoBytePrefix(CreateFieldOp1, CreateFieldOp2, ref offset2) == Failure) { + return Failure; + } + if (ParseSourceBuff(out result.sourceBuff, ref offset2) == Failure || + ParseBitIndex (out result.bitIndex, ref offset2) == Failure || + ParseNumBits (out result.numBits, ref offset2) == Failure || + ParseNameString(out result.nameString, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // CreateFieldOp := ExtOpPrefix 0x13 + + const ByteData CreateFieldOp1 = ExtOpPrefix; + const ByteData CreateFieldOp2 = 0x13; + + // NumBits := TermArg => Integer + + public class NumBits : AmlParserNode + { + public TermArg integer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseNumBits(out NumBits result, ref int offset) + { + int offset2 = offset; + result = new NumBits(); + + if (ParseTermArg(out result.integer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.integer, TermArgType.Integer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefCreateQWordField := CreateQWordFieldOp SourceBuff ByteIndex NameString + + public class DefCreateQWordField : AmlParserNode + { + public SourceBuff sourceBuff; + public ByteIndex byteIndex; + public NameString nameString; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefCreateQWordField(out DefCreateQWordField result, ref int offset) + { + int offset2 = offset; + result = new DefCreateQWordField(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != CreateQWordFieldOp) { + return Failure; + } + if (ParseSourceBuff(out result.sourceBuff, ref offset2) == Failure || + ParseByteIndex (out result.byteIndex, ref offset2) == Failure || + ParseNameString(out result.nameString, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // CreateQWordFieldOp := 0x8F + + const ByteData CreateQWordFieldOp = 0x8F; + + // DefCreateWordField := CreateWordFieldOp SourceBuff ByteIndex NameString + + public class DefCreateWordField : AmlParserNode + { + public SourceBuff sourceBuff; + public ByteIndex byteIndex; + public NameString nameString; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefCreateWordField(out DefCreateWordField result, ref int offset) + { + int offset2 = offset; + result = new DefCreateWordField(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != CreateWordFieldOp) { + return Failure; + } + if (ParseSourceBuff(out result.sourceBuff, ref offset2) == Failure || + ParseByteIndex (out result.byteIndex, ref offset2) == Failure || + ParseNameString(out result.nameString, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // CreateWordFieldOp := 0x8B + + const ByteData CreateWordFieldOp = 0x8B; + + // DefDataRegion := DataRegionOp NameString TermArg TermArg TermArg + + public class DefDataRegion : AmlParserNode + { + public NameString nameString; + public TermArg signatureString; + public TermArg oemIDString; + public TermArg oemTableIDString; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefDataRegion(out DefDataRegion result, ref int offset) + { + int offset2 = offset; + result = new DefDataRegion(); + if (CheckTwoBytePrefix(DataRegionOp1, DataRegionOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseNameString(out result.nameString, ref offset2) == Failure || + ParseTermArg (out result.signatureString, ref offset2) == Failure || + ParseTermArg (out result.oemIDString, ref offset2) == Failure || + ParseTermArg (out result.oemTableIDString, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // DataRegionOp := ExtOpPrefix 0x88 + + const ByteData DataRegionOp1 = ExtOpPrefix; + const ByteData DataRegionOp2 = 0x88; + + // DefDevice := DeviceOp PkgLength NameString AmlObjectList + + public class DefDevice : AmlParserNode + { + public NameString nameString; + public AmlObject[] amlObjectList; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefDevice(out DefDevice result, ref int offset) + { + int offset2 = offset; + result = new DefDevice(); + if (CheckTwoBytePrefix(DeviceOp1, DeviceOp2, ref offset2) == Failure) { + return Failure; + } + + int endOffset; + if (ParsePkgLengthEndOffset(out endOffset, ref offset2) == Failure) { + return Failure; + } + + if (ParseNameString (out result.nameString, ref offset2) == Failure) { + return Failure; + } + + AbsoluteNodePath oldNodePath = currentNodePath; + if (acpiNamespace != null) { + currentNodePath = acpiNamespace.LookupNode(result.nameString.nodePath, currentNodePath).Path; + } + if (ParseAmlObjectList(out result.amlObjectList, ref offset2, endOffset) == Failure) { + return Failure; + } + currentNodePath = oldNodePath; + + offset = offset2; + return Success; + } + + // DeviceOp := ExtOpPrefix 0x82 + + const ByteData DeviceOp1 = ExtOpPrefix; + const ByteData DeviceOp2 = 0x82; + + // DefEvent := EventOp NameString + + public class DefEvent : AmlParserNode + { + public NameString nameString; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefEvent(out DefEvent result, ref int offset) + { + int offset2 = offset; + result = new DefEvent(); + if (CheckTwoBytePrefix(EventOp1, EventOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseNameString(out result.nameString, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // EventOp := ExtOpPrefix 0x02 + + const ByteData EventOp1 = ExtOpPrefix; + const ByteData EventOp2 = 0x02; + + // DefField := FieldOp PkgLength NameString FieldFlags FieldList + + public class DefField : AmlParserNode + { + public NameString nameString; + public FieldFlags fieldFlags; + public FieldElement[] fieldList; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefField(out DefField result, ref int offset) + { + int offset2 = offset; + result = new DefField(); + if (CheckTwoBytePrefix(FieldOp1, FieldOp2, ref offset2) == Failure) { + return Failure; + } + + int endOffset; + if (ParsePkgLengthEndOffset(out endOffset, ref offset2) == Failure) { + return Failure; + } + + if (ParseNameString(out result.nameString, ref offset2) == Failure || + ParseFieldFlags(out result.fieldFlags, ref offset2) == Failure || + ParseFieldList (out result.fieldList, ref offset2, endOffset) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // FieldOp := ExtOpPrefix 0x81 + + const ByteData FieldOp1 = ExtOpPrefix; + const ByteData FieldOp2 = 0x81; + + // DefIndexField := IndexFieldOp PkgLength NameString NameString FieldFlags FieldList + + public class DefIndexField : AmlParserNode + { + public NameString indexName; + public NameString dataName; + public FieldFlags fieldFlags; + public FieldElement[] fieldList; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefIndexField(out DefIndexField result, ref int offset) + { + int offset2 = offset; + result = new DefIndexField(); + if (CheckTwoBytePrefix(IndexFieldOp1, IndexFieldOp2, ref offset2) == Failure) { + return Failure; + } + + int endOffset; + if (ParsePkgLengthEndOffset(out endOffset, ref offset2) == Failure) { + return Failure; + } + + if (ParseNameString(out result.indexName, ref offset2) == Failure || + ParseNameString(out result.dataName, ref offset2) == Failure || + ParseFieldFlags(out result.fieldFlags, ref offset2) == Failure || + ParseFieldList (out result.fieldList, ref offset2, endOffset) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // IndexFieldOp := ExtOpPrefix 0x86 + + const ByteData IndexFieldOp1 = ExtOpPrefix; + const ByteData IndexFieldOp2 = 0x86; + + // DefMethod := MethodOp PkgLength NameString MethodFlags TermList + + public class DefMethod : AmlParserNode + { + public NameString nameString; + public MethodFlags methodFlags; + public byte[] unparsedTermList; + public TermObj[] termList; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefMethod(out DefMethod result, ref int offset) + { + int offset2 = offset; + result = null; + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != MethodOp) { + return Failure; + } + + int endOffset; + if (ParsePkgLengthEndOffset(out endOffset, ref offset2) == Failure) { + return Failure; + } + + NameString nameString; + MethodFlags methodFlags; + + if (ParseNameString (out nameString, ref offset2) == Failure || + ParseMethodFlags(out methodFlags, ref offset2) == Failure) { + return Failure; + } + + result = new DefMethod(); + result.nameString = nameString; + result.methodFlags = methodFlags; + + // We defer processing of the method body until later when all argument info is + // known about methods that it might invoke. + result.unparsedTermList = stream.ReadByteDataArray(ref offset2, endOffset - offset2); + + offset = offset2; + return Success; + } + + // MethodOp := 0x14 + + const ByteData MethodOp = 0x14; + + // MethodFlags := ByteData // bit 0-2: ArgCount (0-7) + // // bit 3: SerializeFlag + // // 0 NotSerialized + // // 1 Serialized + // // bit 4-7: SyncLevel (0x00-0x0f) + + public class MethodFlags : AmlParserNode + { + public ByteData argCount; + public SerializeFlag serializeFlag; + public ByteData syncLevel; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseMethodFlags(out MethodFlags result, ref int offset) + { + int offset2 = offset; + result = new MethodFlags(); + ByteData b = stream.ReadByteData(ref offset2); + + result.argCount = (byte)(b & 7); + result.serializeFlag = (SerializeFlag)((b >> 3) & 1); + result.syncLevel = (byte)(b >> 4); + + offset = offset2; + return Success; + } + + // DefMutex := MutexOp NameString SyncFlags + + public class DefMutex : AmlParserNode + { + public NameString nameString; + public SyncFlags syncFlags; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefMutex(out DefMutex result, ref int offset) + { + int offset2 = offset; + result = new DefMutex(); + if (CheckTwoBytePrefix(MutexOp1, MutexOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseNameString(out result.nameString, ref offset2) == Failure || + ParseSyncFlags (out result.syncFlags, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // MutexOp := ExtOpPrefix 0x01 + + const ByteData MutexOp1 = ExtOpPrefix; + const ByteData MutexOp2 = 0x01; + + // SyncFlags := ByteData // bit 0-3: SyncLevel (0x00-0x0f) + // // bit 4-7: Reserved (must be 0) + + public class SyncFlags : AmlParserNode + { + public ByteData syncLevel; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseSyncFlags(out SyncFlags result, ref int offset) + { + int offset2 = offset; + result = new SyncFlags(); + ByteData b = stream.ReadByteData(ref offset2); + + if ((b >> 4) != 0) { + return Failure; + } + result.syncLevel = (byte)(b & 0x0F); + + offset = offset2; + return Success; + } + + // DefOpRegion := OpRegionOp NameString RegionSpace RegionOffset RegionLen + + public class DefOpRegion : AmlParserNode + { + public NameString nameString; + public RegionSpace regionSpace; + public RegionOffset regionOffset; + public RegionLen regionLen; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefOpRegion(out DefOpRegion result, ref int offset) + { + int offset2 = offset; + result = new DefOpRegion(); + if (CheckTwoBytePrefix(OpRegionOp1, OpRegionOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseNameString (out result.nameString, ref offset2) == Failure || + ParseRegionSpace (out result.regionSpace, ref offset2) == Failure || + ParseRegionOffset(out result.regionOffset, ref offset2) == Failure || + ParseRegionLen (out result.regionLen, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // OpRegionOp := ExtOpPrefix 0x80 + + const ByteData OpRegionOp1 = ExtOpPrefix; + const ByteData OpRegionOp2 = 0x80; + + // RegionSpace := ByteData // 0x00 SystemMemory + // // 0x01 SystemIO + // // 0x02 PCI_Config + // // 0x03 EmbeddedControl + // // 0x04 SMBus + // // 0x05 CMOS + // // 0x06 PciBarTarget + // // 0x80-0xFF: User Defined + + public class RegionSpace : AmlParserNode + { + public ByteData byteData; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseRegionSpace(out RegionSpace result, ref int offset) + { + int offset2 = offset; + result = new RegionSpace(); + result.byteData = stream.ReadByteData(ref offset2); + offset = offset2; + return Success; + } + + // RegionOffset := TermArg => Integer + + public class RegionOffset : AmlParserNode + { + public TermArg integer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseRegionOffset(out RegionOffset result, ref int offset) + { + int offset2 = offset; + result = new RegionOffset(); + + if (ParseTermArg(out result.integer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.integer, TermArgType.Integer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // RegionLen := TermArg => Integer + + public class RegionLen : AmlParserNode + { + public TermArg integer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseRegionLen(out RegionLen result, ref int offset) + { + int offset2 = offset; + result = new RegionLen(); + + if (ParseTermArg(out result.integer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.integer, TermArgType.Integer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefPowerRes := PowerResOp PkgLength NameString SystemLevel ResourceOrder AmlObjectList + + public class DefPowerRes : AmlParserNode + { + public NameString nameString; + public SystemLevel systemLevel; + public ResourceOrder resourceOrder; + public AmlObject[] amlObjectList; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefPowerRes(out DefPowerRes result, ref int offset) + { + int offset2 = offset; + result = new DefPowerRes(); + if (CheckTwoBytePrefix(PowerResOp1, PowerResOp2, ref offset2) == Failure) { + return Failure; + } + + int endOffset; + if (ParsePkgLengthEndOffset(out endOffset, ref offset2) == Failure) { + return Failure; + } + + if (ParseNameString (out result.nameString, ref offset2) == Failure) { + return Failure; + } + + AbsoluteNodePath oldNodePath = currentNodePath; + if (acpiNamespace != null) { + currentNodePath = acpiNamespace.LookupNode(result.nameString.nodePath, currentNodePath).Path; + } + + if (ParseSystemLevel (out result.systemLevel, ref offset2) == Failure || + ParseResourceOrder(out result.resourceOrder, ref offset2) == Failure || + ParseAmlObjectList(out result.amlObjectList, ref offset2, endOffset) == Failure) { + return Failure; + } + + currentNodePath = oldNodePath; + + offset = offset2; + return Success; + } + + // PowerResOp := ExtOpPrefix 0x84 + + const ByteData PowerResOp1 = ExtOpPrefix; + const ByteData PowerResOp2 = 0x84; + + // SystemLevel := ByteData + + public class SystemLevel : AmlParserNode + { + public ByteData byteData; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseSystemLevel(out SystemLevel result, ref int offset) + { + int offset2 = offset; + result = new SystemLevel(); + result.byteData = stream.ReadByteData(ref offset2); + offset = offset2; + return Success; + } + + // ResourceOrder := WordData + + public class ResourceOrder : AmlParserNode + { + public WordData wordData; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseResourceOrder(out ResourceOrder result, ref int offset) + { + int offset2 = offset; + result = new ResourceOrder(); + result.wordData = stream.ReadWordData(ref offset2); + offset = offset2; + return Success; + } + + // DefProcessor := ProcessorOp PkgLength NameString ProcID PblkAddr PblkLen AmlObjectList + + public class DefProcessor : AmlParserNode + { + public NameString nameString; + public ProcID procID; + public PblkAddr pblkAddr; + public PblkLen pblkLen; + public AmlObject[] amlObjectList; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefProcessor(out DefProcessor result, ref int offset) + { + int offset2 = offset; + result = new DefProcessor(); + if (CheckTwoBytePrefix(ProcessorOp1, ProcessorOp2, ref offset2) == Failure) { + return Failure; + } + + int endOffset; + if (ParsePkgLengthEndOffset(out endOffset, ref offset2) == Failure) { + return Failure; + } + + if (ParseNameString (out result.nameString, ref offset2) == Failure) { + return Failure; + } + + AbsoluteNodePath oldNodePath = currentNodePath; + if (acpiNamespace != null) { + currentNodePath = acpiNamespace.LookupNode(result.nameString.nodePath, currentNodePath).Path; + } + + if (ParseProcID (out result.procID, ref offset2) == Failure || + ParsePblkAddr (out result.pblkAddr, ref offset2) == Failure || + ParsePblkLen (out result.pblkLen, ref offset2) == Failure || + ParseAmlObjectList(out result.amlObjectList, ref offset2, endOffset) == Failure) { + return Failure; + } + + currentNodePath = oldNodePath; + + offset = offset2; + return Success; + } + + // ProcessorOp := ExtOpPrefix 0x83 + + const ByteData ProcessorOp1 = ExtOpPrefix; + const ByteData ProcessorOp2 = 0x83; + + // ProcID := ByteData + + public class ProcID : AmlParserNode + { + public ByteData byteData; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseProcID(out ProcID result, ref int offset) + { + int offset2 = offset; + result = new ProcID(); + result.byteData = stream.ReadByteData(ref offset2); + offset = offset2; + return Success; + } + + // PblkAddr := DWordData + + public class PblkAddr : AmlParserNode + { + public DWordData dWordData; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParsePblkAddr(out PblkAddr result, ref int offset) + { + int offset2 = offset; + result = new PblkAddr(); + result.dWordData = stream.ReadDWordData(ref offset2); + offset = offset2; + return Success; + } + + // PblkLen := ByteData + + public class PblkLen : AmlParserNode + { + public ByteData byteData; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParsePblkLen(out PblkLen result, ref int offset) + { + int offset2 = offset; + result = new PblkLen(); + result.byteData = stream.ReadByteData(ref offset2); + offset = offset2; + return Success; + } + + // DefThermalZone := ThermalZoneOp PkgLength NameString AmlObjectList + + public class DefThermalZone : AmlParserNode + { + public NameString nameString; + public AmlObject[] amlObjectList; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefThermalZone(out DefThermalZone result, ref int offset) + { + int offset2 = offset; + result = new DefThermalZone(); + if (CheckTwoBytePrefix(ThermalZoneOp1, ThermalZoneOp2, ref offset2) == Failure) { + return Failure; + } + + int endOffset; + if (ParsePkgLengthEndOffset(out endOffset, ref offset2) == Failure) { + return Failure; + } + + if (ParseNameString (out result.nameString, ref offset2) == Failure) { + return Failure; + } + + AbsoluteNodePath oldNodePath = currentNodePath; + if (acpiNamespace != null) { + currentNodePath = acpiNamespace.LookupNode(result.nameString.nodePath, currentNodePath).Path; + } + + if (ParseAmlObjectList(out result.amlObjectList, ref offset2, endOffset) == Failure) { + return Failure; + } + + currentNodePath = oldNodePath; + + offset = offset2; + return Success; + } + + // ThermalZoneOp := ExtOpPrefix 0x85 + + const ByteData ThermalZoneOp1 = ExtOpPrefix; + const ByteData ThermalZoneOp2 = 0x85; + + // 18.2.5.3 Type 1 Opcodes Encoding + + // [Type 1 opcodes involve mainly control flow and some high-level operations. + // They are only found inside methods.] + // Type1Opcode := DefBreak | DefBreakPoint | DefContinue | DefFatal | + // DefIfElse | DefLoad | DefNoop | DefNotify | + // DefRelease | DefReset | DefReturn | DefSignal | + // DefSleep | DefStall | DefUnload | DefWhile + // See AmlParser.csunion + + private ParseSuccess ParseType1Opcode(out Type1Opcode result, ref int offset, int endOffset) + { + int offset2 = offset; + + DefBreak defBreak; + DefBreakPoint defBreakPoint; + DefContinue defContinue; + DefFatal defFatal; + DefIfElse defIfElse; + DefLoad defLoad; + DefNoop defNoop; + DefNotify defNotify; + DefRelease defRelease; + DefReset defReset; + DefReturn defReturn; + DefSignal defSignal; + DefSleep defSleep; + DefStall defStall; + DefUnload defUnload; + DefWhile defWhile; + if (ParseDefBreak (out defBreak, ref offset2) == Success) { + result = Type1Opcode.CreateDefBreak(defBreak); + } + else if (ParseDefBreakPoint(out defBreakPoint, ref offset2) == Success) { + result = Type1Opcode.CreateDefBreakPoint(defBreakPoint); + } + else if (ParseDefContinue (out defContinue, ref offset2) == Success) { + result = Type1Opcode.CreateDefContinue(defContinue); + } + else if (ParseDefFatal (out defFatal, ref offset2) == Success) { + result = Type1Opcode.CreateDefFatal(defFatal); + } + else if (ParseDefIfElse (out defIfElse, ref offset2, endOffset) == Success) { + result = Type1Opcode.CreateDefIfElse(defIfElse); + } + else if (ParseDefLoad (out defLoad, ref offset2) == Success) { + result = Type1Opcode.CreateDefLoad(defLoad); + } + else if (ParseDefNoop (out defNoop, ref offset2) == Success) { + result = Type1Opcode.CreateDefNoop(defNoop); + } + else if (ParseDefNotify (out defNotify, ref offset2) == Success) { + result = Type1Opcode.CreateDefNotify(defNotify); + } + else if (ParseDefRelease (out defRelease, ref offset2) == Success) { + result = Type1Opcode.CreateDefRelease(defRelease); + } + else if (ParseDefReset (out defReset, ref offset2) == Success) { + result = Type1Opcode.CreateDefReset(defReset); + } + else if (ParseDefReturn (out defReturn, ref offset2) == Success) { + result = Type1Opcode.CreateDefReturn(defReturn); + } + else if (ParseDefSignal (out defSignal, ref offset2) == Success) { + result = Type1Opcode.CreateDefSignal(defSignal); + } + else if (ParseDefSleep (out defSleep, ref offset2) == Success) { + result = Type1Opcode.CreateDefSleep(defSleep); + } + else if (ParseDefStall (out defStall, ref offset2) == Success) { + result = Type1Opcode.CreateDefStall(defStall); + } + else if (ParseDefUnload (out defUnload, ref offset2) == Success) { + result = Type1Opcode.CreateDefUnload(defUnload); + } + else if (ParseDefWhile (out defWhile, ref offset2) == Success) { + result = Type1Opcode.CreateDefWhile(defWhile); + } + else { + result = null; + return Failure; + } + + offset = offset2; + return Success; + } + + // DefBreak := BreakOp + + public class DefBreak : AmlParserNode + { + // No data + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefBreak(out DefBreak result, ref int offset) + { + int offset2 = offset; + result = new DefBreak(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != BreakOp) { + return Failure; + } + offset = offset2; + return Success; + } + + // BreakOp := 0xA5 + + const ByteData BreakOp = 0xA5; + + // DefBreakPoint := BreakPointOp + + public class DefBreakPoint : AmlParserNode + { + // No data + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefBreakPoint(out DefBreakPoint result, ref int offset) + { + int offset2 = offset; + result = new DefBreakPoint(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != BreakPointOp) { + return Failure; + } + offset = offset2; + return Success; + } + + // BreakPointOp := 0xCC + + const ByteData BreakPointOp = 0xCC; + + // DefContinue := ContinueOp + + public class DefContinue : AmlParserNode + { + // No data + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefContinue(out DefContinue result, ref int offset) + { + int offset2 = offset; + result = new DefContinue(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != ContinueOp) { + return Failure; + } + offset = offset2; + return Success; + } + + // ContinueOp := 0x9F + + const ByteData ContinueOp = 0x9F; + + // DefElse := Nothing | + + public class DefElse : AmlParserNode + { + public TermObj[] termList; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefElse(out DefElse result, ref int offset, int endOffset) + { + int offset2 = offset; + result = new DefElse(); + ByteData prefix; + + if (offset >= endOffset) { + return Success; // Empty else clause + } + if (!stream.TryReadByteData(ref offset2, out prefix)) { + return Success; // Sometimes an if with no else is at the end of the stream + } + if (prefix != ElseOp) { + result.termList = null; + return Success; + } + + int endOffsetBody; + if (ParsePkgLengthEndOffset(out endOffsetBody, ref offset2) == Failure) { + return Failure; + } + + if (ParseTermList(out result.termList, ref offset2, endOffsetBody) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // ElseOp := 0xA1 + + const ByteData ElseOp = 0xA1; + + // DefFatal := FatalOp FatalType FatalCode FatalArg + + public class DefFatal : AmlParserNode + { + public FatalType fatalType; + public FatalCode fatalCode; + public FatalArg fatalArg; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefFatal(out DefFatal result, ref int offset) + { + int offset2 = offset; + result = new DefFatal(); + if (CheckTwoBytePrefix(FatalOp1, FatalOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseFatalType(out result.fatalType, ref offset2) == Failure || + ParseFatalCode(out result.fatalCode, ref offset2) == Failure || + ParseFatalArg (out result.fatalArg, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // FatalOp := ExtOpPrefix 0x32 + + const ByteData FatalOp1 = ExtOpPrefix; + const ByteData FatalOp2 = 0x32; + + // FatalType := ByteData + + public class FatalType : AmlParserNode + { + public ByteData byteData; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseFatalType(out FatalType result, ref int offset) + { + int offset2 = offset; + result = new FatalType(); + result.byteData = stream.ReadByteData(ref offset2); + offset = offset2; + return Success; + } + + // FatalCode := DWordData + + public class FatalCode : AmlParserNode + { + public DWordData dWordData; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseFatalCode(out FatalCode result, ref int offset) + { + int offset2 = offset; + result = new FatalCode(); + result.dWordData = stream.ReadDWordData(ref offset2); + offset = offset2; + return Success; + } + + // FatalArg := TermArg => Integer + + public class FatalArg : AmlParserNode + { + public TermArg integer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseFatalArg(out FatalArg result, ref int offset) + { + int offset2 = offset; + result = new FatalArg(); + + if (ParseTermArg(out result.integer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.integer, TermArgType.Integer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefIfElse := IfOp PkgLength Predicate TermList DefElse + + public class DefIfElse : AmlParserNode + { + public Predicate predicate; + public TermObj[] termList; + public DefElse defElse; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefIfElse(out DefIfElse result, ref int offset, int endOffset) + { + int offset2 = offset; + result = new DefIfElse(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != IfOp) { + return Failure; + } + + int endOffsetThen; + if (ParsePkgLengthEndOffset(out endOffsetThen, ref offset2) == Failure) { + return Failure; + } + + // Investigation of ASL compiler output reveals that the PkgLength in this + // rule includes the if body but not the else clause, which is fortunate + // since otherwise ambiguities would arise. + if (ParsePredicate(out result.predicate, ref offset2) == Failure || + ParseTermList(out result.termList, ref offset2, endOffsetThen) == Failure || + ParseDefElse(out result.defElse, ref offset2, endOffset) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // IfOp := 0xA0 + + const ByteData IfOp = 0xA0; + + // Predicate := TermArg => Integer + + public class Predicate : AmlParserNode + { + public TermArg integer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParsePredicate(out Predicate result, ref int offset) + { + int offset2 = offset; + result = new Predicate(); + + if (ParseTermArg(out result.integer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.integer, TermArgType.Integer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefLoad := LoadOp NameString DDBHandleObject + + public class DefLoad : AmlParserNode + { + public NameString nameString; + public DDBHandleObject ddbHandleObject; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefLoad(out DefLoad result, ref int offset) + { + int offset2 = offset; + result = new DefLoad(); + if (CheckTwoBytePrefix(LoadOp1, LoadOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseNameString (out result.nameString, ref offset2) == Failure || + ParseDDBHandleObject(out result.ddbHandleObject, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // LoadOp := ExtOpPrefix 0x20 + + const ByteData LoadOp1 = ExtOpPrefix; + const ByteData LoadOp2 = 0x20; + + // DDBHandleObject := SuperName + + public class DDBHandleObject : AmlParserNode + { + public SuperName superName; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDDBHandleObject(out DDBHandleObject result, ref int offset) + { + int offset2 = offset; + result = new DDBHandleObject(); + + if (ParseSuperName(out result.superName, ref offset2) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefNoop := NoopOp + + public class DefNoop : AmlParserNode + { + // No data + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefNoop(out DefNoop result, ref int offset) + { + int offset2 = offset; + result = new DefNoop(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != NoopOp) { + return Failure; + } + offset = offset2; + return Success; + } + + // NoopOp := 0xA3 + + const ByteData NoopOp = 0xA3; + + // DefNotify := NotifyOp NotifyObject NotifyValue + + public class DefNotify : AmlParserNode + { + public NotifyObject notifyObject; + public NotifyValue notifyValue; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefNotify(out DefNotify result, ref int offset) + { + int offset2 = offset; + result = new DefNotify(); + + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != NotifyOp) { + return Failure; + } + + if (ParseNotifyObject(out result.notifyObject, ref offset2) == Failure || + ParseNotifyValue (out result.notifyValue, ref offset2) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // NotifyOp := 0x86 + + const ByteData NotifyOp = 0x86; + + // NotifyObject := SuperName => ThermalZone | Processor | Device + + public class NotifyObject : AmlParserNode + { + public SuperName superName; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseNotifyObject(out NotifyObject result, ref int offset) + { + int offset2 = offset; + result = new NotifyObject(); + + if (ParseSuperName(out result.superName, ref offset2) == Failure /*|| + (!SuperNameEvaluatesTo(result.superName, SuperNameType.ThermalZone) && + !SuperNameEvaluatesTo(result.superName, SuperNameType.Processor) && + !SuperNameEvaluatesTo(result.superName, SuperNameType.Device)) */) { + return Failure; + } + + offset = offset2; + return Success; + } + + // NotifyValue := TermArg => Integer + + public class NotifyValue : AmlParserNode + { + public TermArg integer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseNotifyValue(out NotifyValue result, ref int offset) + { + int offset2 = offset; + result = new NotifyValue(); + + if (ParseTermArg(out result.integer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.integer, TermArgType.Integer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefRelease := ReleaseOp MutexObject + + public class DefRelease : AmlParserNode + { + public MutexObject mutexObject; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefRelease(out DefRelease result, ref int offset) + { + int offset2 = offset; + result = new DefRelease(); + + if (CheckTwoBytePrefix(ReleaseOp1, ReleaseOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseMutexObject(out result.mutexObject, ref offset2) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // ReleaseOp := ExtOpPrefix 0x27 + + const ByteData ReleaseOp1 = ExtOpPrefix; + const ByteData ReleaseOp2 = 0x27; + + // MutexObject := SuperName + + public class MutexObject : AmlParserNode + { + public SuperName superName; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseMutexObject(out MutexObject result, ref int offset) + { + int offset2 = offset; + result = new MutexObject(); + + if (ParseSuperName(out result.superName, ref offset2) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefReset := ResetOp EventObject + + public class DefReset : AmlParserNode + { + public EventObject eventObject; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefReset(out DefReset result, ref int offset) + { + int offset2 = offset; + result = new DefReset(); + + if (CheckTwoBytePrefix(ResetOp1, ResetOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseEventObject(out result.eventObject, ref offset2) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // ResetOp := ExtOpPrefix 0x26 + + const ByteData ResetOp1 = ExtOpPrefix; + const ByteData ResetOp2 = 0x26; + + // EventObject := SuperName + + public class EventObject : AmlParserNode + { + public SuperName superName; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseEventObject(out EventObject result, ref int offset) + { + int offset2 = offset; + result = new EventObject(); + + if (ParseSuperName(out result.superName, ref offset2) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefReturn := ReturnOp ArgObject + + public class DefReturn : AmlParserNode + { + public ArgObject argObject; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefReturn(out DefReturn result, ref int offset) + { + int offset2 = offset; + result = new DefReturn(); + + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != ReturnOp) { + return Failure; + } + + if (ParseArgObject(out result.argObject, ref offset2) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // ReturnOp := 0xA4 + + const ByteData ReturnOp = 0xA4; + + // ArgObject := TermArg => DataRefObject + + public class ArgObject : AmlParserNode + { + public TermArg dataRefObject; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseArgObject(out ArgObject result, ref int offset) + { + int offset2 = offset; + result = new ArgObject(); + + if (ParseTermArg(out result.dataRefObject, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.dataRefObject, TermArgType.DataRefObject)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefSignal := SignalOp EventObject + + public class DefSignal : AmlParserNode + { + public EventObject eventObject; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefSignal(out DefSignal result, ref int offset) + { + int offset2 = offset; + result = new DefSignal(); + + if (CheckTwoBytePrefix(SignalOp1, SignalOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseEventObject(out result.eventObject, ref offset2) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // SignalOp := ExtOpPrefix 0x24 + + const ByteData SignalOp1 = ExtOpPrefix; + const ByteData SignalOp2 = 0x24; + + // DefSleep := SleepOp MsecTime + + public class DefSleep : AmlParserNode + { + public MsecTime msecTime; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefSleep(out DefSleep result, ref int offset) + { + int offset2 = offset; + result = new DefSleep(); + + if (CheckTwoBytePrefix(SleepOp1, SleepOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseMsecTime(out result.msecTime, ref offset2) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // SleepOp := ExtOpPrefix 0x22 + + const ByteData SleepOp1 = ExtOpPrefix; + const ByteData SleepOp2 = 0x22; + + // MsecTime := TermArg => Integer + + public class MsecTime : AmlParserNode + { + public TermArg integer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseMsecTime(out MsecTime result, ref int offset) + { + int offset2 = offset; + result = new MsecTime(); + + if (ParseTermArg(out result.integer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.integer, TermArgType.Integer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefStall := StallOp UsecTime + + public class DefStall : AmlParserNode + { + public UsecTime usecTime; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefStall(out DefStall result, ref int offset) + { + int offset2 = offset; + result = new DefStall(); + + if (CheckTwoBytePrefix(StallOp1, StallOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseUsecTime(out result.usecTime, ref offset2) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // StallOp := ExtOpPrefix 0x21 + + const ByteData StallOp1 = ExtOpPrefix; + const ByteData StallOp2 = 0x21; + + // UsecTime := TermArg => ByteData + + public class UsecTime : AmlParserNode + { + public TermArg byteData; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseUsecTime(out UsecTime result, ref int offset) + { + int offset2 = offset; + result = new UsecTime(); + + if (ParseTermArg(out result.byteData, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.byteData, TermArgType.ByteData)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefUnload := UnloadOp DDBHandleObject + + public class DefUnload : AmlParserNode + { + public DDBHandleObject ddbHandleObject; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefUnload(out DefUnload result, ref int offset) + { + int offset2 = offset; + result = new DefUnload(); + if (CheckTwoBytePrefix(UnloadOp1, UnloadOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseDDBHandleObject(out result.ddbHandleObject, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // UnloadOp := ExtOpPrefix 0x2A + + const ByteData UnloadOp1 = ExtOpPrefix; + const ByteData UnloadOp2 = 0x2A; + + // DefWhile := WhileOp PkgLength Predicate TermList + + public class DefWhile : AmlParserNode + { + public Predicate predicate; + public TermObj[] termList; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefWhile(out DefWhile result, ref int offset) + { + int offset2 = offset; + result = new DefWhile(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != WhileOp) { + return Failure; + } + + int endOffset; + if (ParsePkgLengthEndOffset(out endOffset, ref offset2) == Failure) { + return Failure; + } + + if (ParsePredicate(out result.predicate, ref offset2) == Failure || + ParseTermList (out result.termList, ref offset2, endOffset) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // WhileOp := 0xA2 + + const ByteData WhileOp = 0xA2; + + // 18.2.5.4 Type 2 Opcodes Encoding + + // [Type 2 opcodes primarily manipulate primitive values (arithmetic, comparison, + // etc.) and can also refer to objects through UserTermObj. They are only found + // inside methods.] + // Type2Opcode := DefAcquire | DefAdd | DefAnd | DefBuffer | + // DefConcat | DefConcatRes | DefCondRefOf | + // DefCopyObject | DefDecrement | DefDerefOf | + // DefDivide | DefFindSetLeftBit | DefFindSetRightBit | + // DefFromBCD | DefIncrement | DefIndex | DefLAnd | + // DefLEqual | DefLGreater | DefLGreaterEqual | + // DefLLess | DefLLessEqual | DefMid | DefLNot | + // DefLNotEqual | DefLoadTable | DefLOr | DefMatch | + // DefMod | DefMultiply | DefNAnd | DefNOr | DefNot | + // DefObjectType | DefOr | DefPackage | DefVarPackage | + // DefRefOf | DefShiftLeft | DefShiftRight | DefSizeOf | + // DefStore | DefSubtract | DefTimer | DefToBCD | + // DefToBuffer | DefToDecimalString | DefToHexString | + // DefToInteger | DefToString | DefWait | DefXOr | + // UserTermObj + // See AmlParser.csunion + + private ParseSuccess ParseType2Opcode(out Type2Opcode result, ref int offset) + { + int offset2 = offset; + + DefAcquire defAcquire; + DefAdd defAdd; + DefAnd defAnd; + DefBuffer defBuffer; + DefConcat defConcat; + DefConcatRes defConcatRes; + DefCondRefOf defCondRefOf; + DefCopyObject defCopyObject; + DefDecrement defDecrement; + DefDerefOf defDerefOf; + DefDivide defDivide; + DefFindSetLeftBit defFindSetLeftBit; + DefFindSetRightBit defFindSetRightBit; + DefFromBCD defFromBCD; + DefIncrement defIncrement; + DefIndex defIndex; + DefLAnd defLAnd; + DefLEqual defLEqual; + DefLGreater defLGreater; + DefLGreaterEqual defLGreaterEqual; + DefLLess defLLess; + DefLLessEqual defLLessEqual; + DefMid defMid; + DefLNot defLNot; + DefLNotEqual defLNotEqual; + DefLoadTable defLoadTable; + DefLOr defLOr; + DefMatch defMatch; + DefMod defMod; + DefMultiply defMultiply; + DefNAnd defNAnd; + DefNOr defNOr; + DefNot defNot; + DefObjectType defObjectType; + DefOr defOr; + DefPackage defPackage; + DefVarPackage defVarPackage; + DefRefOf defRefOf; + DefShiftLeft defShiftLeft; + DefShiftRight defShiftRight; + DefSizeOf defSizeOf; + DefStore defStore; + DefSubtract defSubtract; + DefTimer defTimer; + DefToBCD defToBCD; + DefToBuffer defToBuffer; + DefToDecimalString defToDecimalString; + DefToHexString defToHexString; + DefToInteger defToInteger; + DefToString defToString; + DefWait defWait; + DefXOr defXOr; + UserTermObj userTermObj; + + if (ParseDefAcquire (out defAcquire, ref offset2) == Success) { + result = Type2Opcode.CreateDefAcquire(defAcquire); + } + else if (ParseDefAdd (out defAdd, ref offset2) == Success) { + result = Type2Opcode.CreateDefAdd(defAdd); + } + else if (ParseDefAnd (out defAnd, ref offset2) == Success) { + result = Type2Opcode.CreateDefAnd(defAnd); + } + else if (ParseDefBuffer (out defBuffer, ref offset2) == Success) { + result = Type2Opcode.CreateDefBuffer(defBuffer); + } + else if (ParseDefConcat (out defConcat, ref offset2) == Success) { + result = Type2Opcode.CreateDefConcat(defConcat); + } + else if (ParseDefConcatRes (out defConcatRes, ref offset2) == Success) { + result = Type2Opcode.CreateDefConcatRes(defConcatRes); + } + else if (ParseDefCondRefOf (out defCondRefOf, ref offset2) == Success) { + result = Type2Opcode.CreateDefCondRefOf(defCondRefOf); + } + else if (ParseDefCopyObject (out defCopyObject, ref offset2) == Success) { + result = Type2Opcode.CreateDefCopyObject(defCopyObject); + } + else if (ParseDefDecrement (out defDecrement, ref offset2) == Success) { + result = Type2Opcode.CreateDefDecrement(defDecrement); + } + else if (ParseDefDerefOf (out defDerefOf, ref offset2) == Success) { + result = Type2Opcode.CreateDefDerefOf(defDerefOf); + } + else if (ParseDefDivide (out defDivide, ref offset2) == Success) { + result = Type2Opcode.CreateDefDivide(defDivide); + } + else if (ParseDefFindSetLeftBit (out defFindSetLeftBit, ref offset2) == Success) { + result = Type2Opcode.CreateDefFindSetLeftBit(defFindSetLeftBit); + } + else if (ParseDefFindSetRightBit(out defFindSetRightBit, ref offset2) == Success) { + result = Type2Opcode.CreateDefFindSetRightBit(defFindSetRightBit); + } + else if (ParseDefFromBCD (out defFromBCD, ref offset2) == Success) { + result = Type2Opcode.CreateDefFromBCD(defFromBCD); + } + else if (ParseDefIncrement (out defIncrement, ref offset2) == Success) { + result = Type2Opcode.CreateDefIncrement(defIncrement); + } + else if (ParseDefIndex (out defIndex, ref offset2) == Success) { + result = Type2Opcode.CreateDefIndex(defIndex); + } + else if (ParseDefLAnd (out defLAnd, ref offset2) == Success) { + result = Type2Opcode.CreateDefLAnd(defLAnd); + } + else if (ParseDefLEqual (out defLEqual, ref offset2) == Success) { + result = Type2Opcode.CreateDefLEqual(defLEqual); + } + else if (ParseDefLGreater (out defLGreater, ref offset2) == Success) { + result = Type2Opcode.CreateDefLGreater(defLGreater); + } + else if (ParseDefLGreaterEqual (out defLGreaterEqual, ref offset2) == Success) { + result = Type2Opcode.CreateDefLGreaterEqual(defLGreaterEqual); + } + else if (ParseDefLLess (out defLLess, ref offset2) == Success) { + result = Type2Opcode.CreateDefLLess(defLLess); + } + else if (ParseDefLLessEqual (out defLLessEqual, ref offset2) == Success) { + result = Type2Opcode.CreateDefLLessEqual(defLLessEqual); + } + else if (ParseDefMid (out defMid, ref offset2) == Success) { + result = Type2Opcode.CreateDefMid(defMid); + } + else if (ParseDefLNot (out defLNot, ref offset2) == Success) { + result = Type2Opcode.CreateDefLNot(defLNot); + } + else if (ParseDefLNotEqual (out defLNotEqual, ref offset2) == Success) { + result = Type2Opcode.CreateDefLNotEqual(defLNotEqual); + } + else if (ParseDefLoadTable (out defLoadTable, ref offset2) == Success) { + result = Type2Opcode.CreateDefLoadTable(defLoadTable); + } + else if (ParseDefLOr (out defLOr, ref offset2) == Success) { + result = Type2Opcode.CreateDefLOr(defLOr); + } + else if (ParseDefMatch (out defMatch, ref offset2) == Success) { + result = Type2Opcode.CreateDefMatch(defMatch); + } + else if (ParseDefMod (out defMod, ref offset2) == Success) { + result = Type2Opcode.CreateDefMod(defMod); + } + else if (ParseDefMultiply (out defMultiply, ref offset2) == Success) { + result = Type2Opcode.CreateDefMultiply(defMultiply); + } + else if (ParseDefNAnd (out defNAnd, ref offset2) == Success) { + result = Type2Opcode.CreateDefNAnd(defNAnd); + } + else if (ParseDefNOr (out defNOr, ref offset2) == Success) { + result = Type2Opcode.CreateDefNOr(defNOr); + } + else if (ParseDefNot (out defNot, ref offset2) == Success) { + result = Type2Opcode.CreateDefNot(defNot); + } + else if (ParseDefObjectType (out defObjectType, ref offset2) == Success) { + result = Type2Opcode.CreateDefObjectType(defObjectType); + } + else if (ParseDefOr (out defOr, ref offset2) == Success) { + result = Type2Opcode.CreateDefOr(defOr); + } + else if (ParseDefPackage (out defPackage, ref offset2) == Success) { + result = Type2Opcode.CreateDefPackage(defPackage); + } + else if (ParseDefVarPackage (out defVarPackage, ref offset2) == Success) { + result = Type2Opcode.CreateDefVarPackage(defVarPackage); + } + else if (ParseDefRefOf (out defRefOf, ref offset2) == Success) { + result = Type2Opcode.CreateDefRefOf(defRefOf); + } + else if (ParseDefShiftLeft (out defShiftLeft, ref offset2) == Success) { + result = Type2Opcode.CreateDefShiftLeft(defShiftLeft); + } + else if (ParseDefShiftRight (out defShiftRight, ref offset2) == Success) { + result = Type2Opcode.CreateDefShiftRight(defShiftRight); + } + else if (ParseDefSizeOf (out defSizeOf, ref offset2) == Success) { + result = Type2Opcode.CreateDefSizeOf(defSizeOf); + } + else if (ParseDefStore (out defStore, ref offset2) == Success) { + result = Type2Opcode.CreateDefStore(defStore); + } + else if (ParseDefSubtract (out defSubtract, ref offset2) == Success) { + result = Type2Opcode.CreateDefSubtract(defSubtract); + } + else if (ParseDefTimer (out defTimer, ref offset2) == Success) { + result = Type2Opcode.CreateDefTimer(defTimer); + } + else if (ParseDefToBCD (out defToBCD, ref offset2) == Success) { + result = Type2Opcode.CreateDefToBCD(defToBCD); + } + else if (ParseDefToBuffer (out defToBuffer, ref offset2) == Success) { + result = Type2Opcode.CreateDefToBuffer(defToBuffer); + } + else if (ParseDefToDecimalString(out defToDecimalString, ref offset2) == Success) { + result = Type2Opcode.CreateDefToDecimalString(defToDecimalString); + } + else if (ParseDefToHexString (out defToHexString, ref offset2) == Success) { + result = Type2Opcode.CreateDefToHexString(defToHexString); + } + else if (ParseDefToInteger (out defToInteger, ref offset2) == Success) { + result = Type2Opcode.CreateDefToInteger(defToInteger); + } + else if (ParseDefToString (out defToString, ref offset2) == Success) { + result = Type2Opcode.CreateDefToString(defToString); + } + else if (ParseDefWait (out defWait, ref offset2) == Success) { + result = Type2Opcode.CreateDefWait(defWait); + } + else if (ParseDefXOr (out defXOr, ref offset2) == Success) { + result = Type2Opcode.CreateDefXOr(defXOr); + } + else if (ParseUserTermObj (out userTermObj, ref offset2) == Success) { + result = Type2Opcode.CreateUserTermObj(userTermObj); + } + else { + result = null; + return Failure; + } + + offset = offset2; + return Success; + } + + // Type6Opcode := DefRefOf | DefDerefOf | DefIndex | UserTermObj + + + private ParseSuccess ParseType6Opcode(out Type6Opcode result, ref int offset) + { + int offset2 = offset; + + DefRefOf defRefOf; + DefDerefOf defDerefOf; + DefIndex defIndex; + UserTermObj userTermObj; + + if (ParseDefRefOf (out defRefOf, ref offset2) == Success) { + result = Type6Opcode.CreateDefRefOf(defRefOf); + } + else if (ParseDefDerefOf (out defDerefOf, ref offset2) == Success) { + result = Type6Opcode.CreateDefDerefOf(defDerefOf); + } + else if (ParseDefIndex (out defIndex, ref offset2) == Success) { + result = Type6Opcode.CreateDefIndex(defIndex); + } + else if (ParseUserTermObj(out userTermObj, ref offset2) == Success) { + result = Type6Opcode.CreateUserTermObj(userTermObj); + } + else { + result = null; + return Failure; + } + + offset = offset2; + return Success; + } + + // DefAcquire := AcquireOp MutexObject TimeOut + + public class DefAcquire : AmlParserNode + { + public MutexObject mutexObject; + public TimeOut timeOut; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefAcquire(out DefAcquire result, ref int offset) + { + int offset2 = offset; + result = new DefAcquire(); + if (CheckTwoBytePrefix(AcquireOp1, AcquireOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseMutexObject(out result.mutexObject, ref offset2) == Failure || + ParseTimeOut (out result.timeOut, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // AcquireOp := ExtOpPrefix 0x23 + + const ByteData AcquireOp1 = ExtOpPrefix; + const ByteData AcquireOp2 = 0x23; + + // TimeOut := WordData + + public class TimeOut : AmlParserNode + { + public WordData wordData; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseTimeOut(out TimeOut result, ref int offset) + { + int offset2 = offset; + result = new TimeOut(); + result.wordData = stream.ReadWordData(ref offset2); + offset = offset2; + return Success; + } + + // DefAdd := AddOp Operand Operand Target + + public class DefAdd : AmlParserNode + { + public Operand leftOperand; + public Operand rightOperand; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefAdd(out DefAdd result, ref int offset) + { + int offset2 = offset; + result = new DefAdd(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != AddOp) { + return Failure; + } + if (ParseOperand(out result.leftOperand, ref offset2) == Failure || + ParseOperand(out result.rightOperand, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // AddOp := 0x72 + + const ByteData AddOp = 0x72; + + // Operand := TermArg => Integer + + public class Operand : AmlParserNode + { + public TermArg integer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseOperand(out Operand result, ref int offset) + { + int offset2 = offset; + result = new Operand(); + + if (ParseTermArg(out result.integer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.integer, TermArgType.Integer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefAnd := AndOp Operand Operand Target + + public class DefAnd : AmlParserNode + { + public Operand leftOperand; + public Operand rightOperand; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefAnd(out DefAnd result, ref int offset) + { + int offset2 = offset; + result = new DefAnd(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != AndOp) { + return Failure; + } + if (ParseOperand(out result.leftOperand, ref offset2) == Failure || + ParseOperand(out result.rightOperand, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // AndOp := 0x7B + + const ByteData AndOp = 0x7B; + + // DefBuffer := BufferOp PkgLength BufferSize ByteList + + public class DefBuffer : AmlParserNode + { + public BufferSize bufferSize; + public ByteData[] byteList; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefBuffer(out DefBuffer result, ref int offset) + { + int offset2 = offset; + result = new DefBuffer(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != BufferOp) { + return Failure; + } + + int endOffset; + if (ParsePkgLengthEndOffset(out endOffset, ref offset2) == Failure) { + return Failure; + } + + if (ParseBufferSize(out result.bufferSize, ref offset2) == Failure || + ParseByteList (out result.byteList, ref offset2, endOffset) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // BufferOp := 0x11 + + const ByteData BufferOp = 0x11; + + // BufferSize := TermArg => Integer + + public class BufferSize : AmlParserNode + { + public TermArg integer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseBufferSize(out BufferSize result, ref int offset) + { + int offset2 = offset; + result = new BufferSize(); + + if (ParseTermArg(out result.integer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.integer, TermArgType.Integer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefConcat := ConcatOp Data Data Target + + public class DefConcat : AmlParserNode + { + public Data leftData; + public Data rightData; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefConcat(out DefConcat result, ref int offset) + { + int offset2 = offset; + result = new DefConcat(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != ConcatOp) { + return Failure; + } + if (ParseData(out result.leftData, ref offset2) == Failure || + ParseData(out result.rightData, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // ConcatOp := 0x73 + + const ByteData ConcatOp = 0x73; + + // Data := TermArg => ComputationalData + + public class Data : AmlParserNode + { + public TermArg computationalData; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseData(out Data result, ref int offset) + { + int offset2 = offset; + result = new Data(); + + if (ParseTermArg(out result.computationalData, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.computationalData, TermArgType.ComputationalData)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefConcatRes := ConcatResOp BufData BufData Target + + public class DefConcatRes : AmlParserNode + { + public BufData leftBufData; + public BufData rightBufData; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefConcatRes(out DefConcatRes result, ref int offset) + { + int offset2 = offset; + result = new DefConcatRes(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != ConcatResOp) { + return Failure; + } + if (ParseBufData(out result.leftBufData, ref offset2) == Failure || + ParseBufData(out result.rightBufData, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // ConcatResOp := 0x84 + + const ByteData ConcatResOp = 0x84; + + // BufData := TermArg => Buffer + + public class BufData : AmlParserNode + { + public TermArg buffer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseBufData(out BufData result, ref int offset) + { + int offset2 = offset; + result = new BufData(); + + if (ParseTermArg(out result.buffer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.buffer, TermArgType.Buffer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefCondRefOf := CondRefOfOp SuperName Target + + public class DefCondRefOf : AmlParserNode + { + public SuperName superName; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefCondRefOf(out DefCondRefOf result, ref int offset) + { + int offset2 = offset; + result = new DefCondRefOf(); + if (CheckTwoBytePrefix(CondRefOfOp1, CondRefOfOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseSuperName(out result.superName, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // CondRefOfOp := ExtOpPrefix 0x12 + + const ByteData CondRefOfOp1 = ExtOpPrefix; + const ByteData CondRefOfOp2 = 0x12; + + // DefCopyObject := CopyObjectOp TermArg SimpleName + + public class DefCopyObject : AmlParserNode + { + public TermArg termArg; + public SimpleName simpleName; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefCopyObject(out DefCopyObject result, ref int offset) + { + int offset2 = offset; + result = new DefCopyObject(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != CopyObjectOp) { + return Failure; + } + if (ParseTermArg (out result.termArg, ref offset2) == Failure || + ParseSimpleName(out result.simpleName, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // CopyObjectOp := 0x9D + + const ByteData CopyObjectOp = 0x9D; + + // DefDecrement := DecrementOp SuperName + + public class DefDecrement : AmlParserNode + { + public SuperName superName; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefDecrement(out DefDecrement result, ref int offset) + { + int offset2 = offset; + result = new DefDecrement(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != DecrementOp) { + return Failure; + } + if (ParseSuperName(out result.superName, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // DecrementOp := 0x76 + + const ByteData DecrementOp = 0x76; + + // DefDerefOf := DerefOfOp ObjReference + + public class DefDerefOf : AmlParserNode + { + public ObjReference objReference; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefDerefOf(out DefDerefOf result, ref int offset) + { + int offset2 = offset; + result = new DefDerefOf(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != DerefOfOp) { + return Failure; + } + if (ParseObjReference(out result.objReference, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // DerefOfOp := 0x83 + + const ByteData DerefOfOp = 0x83; + + // ObjReference := TermArg => ObjectReference | StringConst + + public class ObjReference : AmlParserNode + { + public TermArg termArg; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseObjReference(out ObjReference result, ref int offset) + { + int offset2 = offset; + result = new ObjReference(); + + if (ParseTermArg(out result.termArg, ref offset2) == Failure /*|| + (!TermArgEvaluatesTo(result.termArg, TermArgType.ObjectReference) && + !TermArgEvaluatesTo(result.termArg, TermArgType.StringConst))*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefDivide := DivideOp Dividend Divisor Remainder Quotient + + public class DefDivide : AmlParserNode + { + public Dividend dividend; + public Divisor divisor; + public Remainder remainder; + public Quotient quotient; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefDivide(out DefDivide result, ref int offset) + { + int offset2 = offset; + result = new DefDivide(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != DivideOp) { + return Failure; + } + if (ParseDividend (out result.dividend, ref offset2) == Failure || + ParseDivisor (out result.divisor, ref offset2) == Failure || + ParseRemainder(out result.remainder, ref offset2) == Failure || + ParseQuotient (out result.quotient, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // DivideOp := 0x78 + + const ByteData DivideOp = 0x78; + + // Dividend := TermArg => Integer + + public class Dividend : AmlParserNode + { + public TermArg integer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDividend(out Dividend result, ref int offset) + { + int offset2 = offset; + result = new Dividend(); + + if (ParseTermArg(out result.integer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.integer, TermArgType.Integer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // Divisor := TermArg => Integer + + public class Divisor : AmlParserNode + { + public TermArg integer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDivisor(out Divisor result, ref int offset) + { + int offset2 = offset; + result = new Divisor(); + + if (ParseTermArg(out result.integer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.integer, TermArgType.Integer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // Remainder := Target + + public class Remainder : AmlParserNode + { + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseRemainder(out Remainder result, ref int offset) + { + int offset2 = offset; + result = new Remainder(); + + if (ParseTarget(out result.target, ref offset2) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // Quotient := Target + + public class Quotient : AmlParserNode + { + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseQuotient(out Quotient result, ref int offset) + { + int offset2 = offset; + result = new Quotient(); + + if (ParseTarget(out result.target, ref offset2) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefFindSetLeftBit := FindSetLeftBitOp Operand Target + + public class DefFindSetLeftBit : AmlParserNode + { + public Operand operand; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefFindSetLeftBit(out DefFindSetLeftBit result, ref int offset) + { + int offset2 = offset; + result = new DefFindSetLeftBit(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != FindSetLeftBitOp) { + return Failure; + } + if (ParseOperand(out result.operand, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // FindSetLeftBitOp := 0x81 + + const ByteData FindSetLeftBitOp = 0x81; + + // DefFindSetRightBit := FindSetRightBitOp Operand Target + + public class DefFindSetRightBit : AmlParserNode + { + public Operand operand; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefFindSetRightBit(out DefFindSetRightBit result, ref int offset) + { + int offset2 = offset; + result = new DefFindSetRightBit(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != FindSetRightBitOp) { + return Failure; + } + if (ParseOperand(out result.operand, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // FindSetRightBitOp := 0x82 + + const ByteData FindSetRightBitOp = 0x82; + + // DefFromBCD := FromBCDOp BCDValue Target + + public class DefFromBCD : AmlParserNode + { + public BCDValue bcdValue; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefFromBCD(out DefFromBCD result, ref int offset) + { + int offset2 = offset; + result = new DefFromBCD(); + if (CheckTwoBytePrefix(FromBCDOp1, FromBCDOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseBCDValue(out result.bcdValue, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // FromBCDOp := ExtOpPrefix 0x28 + + const ByteData FromBCDOp1 = ExtOpPrefix; + const ByteData FromBCDOp2 = 0x28; + + // BCDValue := TermArg => Integer + + public class BCDValue : AmlParserNode + { + public TermArg integer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseBCDValue(out BCDValue result, ref int offset) + { + int offset2 = offset; + result = new BCDValue(); + + if (ParseTermArg(out result.integer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.integer, TermArgType.Integer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefIncrement := IncrementOp SuperName + + public class DefIncrement : AmlParserNode + { + public SuperName superName; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefIncrement(out DefIncrement result, ref int offset) + { + int offset2 = offset; + result = new DefIncrement(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != IncrementOp) { + return Failure; + } + if (ParseSuperName(out result.superName, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // IncrementOp := 0x75 + + const ByteData IncrementOp = 0x75; + + // DefIndex := IndexOp BuffPkgStrObj IndexValue Target + + public class DefIndex : AmlParserNode + { + public BuffPkgStrObj buffPkgStrObj; + public IndexValue indexValue; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefIndex(out DefIndex result, ref int offset) + { + int offset2 = offset; + result = new DefIndex(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != IndexOp) { + return Failure; + } + if (ParseBuffPkgStrObj(out result.buffPkgStrObj, ref offset2) == Failure || + ParseIndexValue (out result.indexValue, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // IndexOp := 0x88 + + const ByteData IndexOp = 0x88; + + // BuffPkgStrObj := TermArg => Buffer | Package | StringConst + + public class BuffPkgStrObj : AmlParserNode + { + public TermArg termArg; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseBuffPkgStrObj(out BuffPkgStrObj result, ref int offset) + { + int offset2 = offset; + result = new BuffPkgStrObj(); + + if (ParseTermArg(out result.termArg, ref offset2) == Failure /* || + (!TermArgEvaluatesTo(result.termArg, TermArgType.Buffer) && + !TermArgEvaluatesTo(result.termArg, TermArgType.Package) && + !TermArgEvaluatesTo(result.termArg, TermArgType.StringConst)) */) { + return Failure; + } + + offset = offset2; + return Success; + } + + // IndexValue := TermArg => Integer + + public class IndexValue : AmlParserNode + { + public TermArg integer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseIndexValue(out IndexValue result, ref int offset) + { + int offset2 = offset; + result = new IndexValue(); + + if (ParseTermArg(out result.integer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.integer, TermArgType.Integer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefLAnd := LandOp Operand Operand + + public class DefLAnd : AmlParserNode + { + public Operand leftOperand; + public Operand rightOperand; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefLAnd(out DefLAnd result, ref int offset) + { + int offset2 = offset; + result = new DefLAnd(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != LandOp) { + return Failure; + } + if (ParseOperand(out result.leftOperand, ref offset2) == Failure || + ParseOperand(out result.rightOperand, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // LandOp := 0x90 + + const ByteData LandOp = 0x90; + + // DefLEqual := LequalOp Operand Operand + + public class DefLEqual : AmlParserNode + { + public Operand leftOperand; + public Operand rightOperand; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefLEqual(out DefLEqual result, ref int offset) + { + int offset2 = offset; + result = new DefLEqual(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != LequalOp) { + return Failure; + } + if (ParseOperand(out result.leftOperand, ref offset2) == Failure || + ParseOperand(out result.rightOperand, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // LequalOp := 0x93 + + const ByteData LequalOp = 0x93; + + // DefLGreater := LgreaterOp Operand Operand + + public class DefLGreater : AmlParserNode + { + public Operand leftOperand; + public Operand rightOperand; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefLGreater(out DefLGreater result, ref int offset) + { + int offset2 = offset; + result = new DefLGreater(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != LgreaterOp) { + return Failure; + } + if (ParseOperand(out result.leftOperand, ref offset2) == Failure || + ParseOperand(out result.rightOperand, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // LgreaterOp := 0x94 + + const ByteData LgreaterOp = 0x94; + + // DefLGreaterEqual := LgreaterEqualOp Operand Operand + + public class DefLGreaterEqual : AmlParserNode + { + public Operand leftOperand; + public Operand rightOperand; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefLGreaterEqual(out DefLGreaterEqual result, ref int offset) + { + int offset2 = offset; + result = new DefLGreaterEqual(); + if (CheckTwoBytePrefix(LgreaterEqualOp1, LgreaterEqualOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseOperand(out result.leftOperand, ref offset2) == Failure || + ParseOperand(out result.rightOperand, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // LgreaterEqualOp := LnotOp LlessOp + + const ByteData LgreaterEqualOp1 = LnotOp; + const ByteData LgreaterEqualOp2 = LlessOp; + + // DefLLess := LlessOp Operand Operand + + public class DefLLess : AmlParserNode + { + public Operand leftOperand; + public Operand rightOperand; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefLLess(out DefLLess result, ref int offset) + { + int offset2 = offset; + result = new DefLLess(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != LlessOp) { + return Failure; + } + if (ParseOperand(out result.leftOperand, ref offset2) == Failure || + ParseOperand(out result.rightOperand, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // LlessOp := 0x95 + + const ByteData LlessOp = 0x95; + + // DefLLessEqual := LlessEqualOp Operand Operand + + public class DefLLessEqual : AmlParserNode + { + public Operand leftOperand; + public Operand rightOperand; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefLLessEqual(out DefLLessEqual result, ref int offset) + { + int offset2 = offset; + result = new DefLLessEqual(); + if (CheckTwoBytePrefix(LlessEqualOp1, LlessEqualOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseOperand(out result.leftOperand, ref offset2) == Failure || + ParseOperand(out result.rightOperand, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // LlessEqualOp := LnotOp LgreaterOp + + const ByteData LlessEqualOp1 = LnotOp; + const ByteData LlessEqualOp2 = LgreaterOp; + + // DefLNot := LnotOp Operand + + public class DefLNot : AmlParserNode + { + public Operand operand; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefLNot(out DefLNot result, ref int offset) + { + int offset2 = offset; + result = new DefLNot(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != LnotOp) { + return Failure; + } + if (ParseOperand(out result.operand, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // LnotOp := 0x92 + + const ByteData LnotOp = 0x92; + + // DefLNotEqual := LnotEqualOp Operand Operand + + public class DefLNotEqual : AmlParserNode + { + public Operand leftOperand; + public Operand rightOperand; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefLNotEqual(out DefLNotEqual result, ref int offset) + { + int offset2 = offset; + result = new DefLNotEqual(); + if (CheckTwoBytePrefix(LnotEqualOp1, LnotEqualOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseOperand(out result.leftOperand, ref offset2) == Failure || + ParseOperand(out result.rightOperand, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // LnotEqualOp := LnotOp LequalOp + + const ByteData LnotEqualOp1 = LnotOp; + const ByteData LnotEqualOp2 = LequalOp; + + // DefLoadTable := LoadTableOp TermArg TermArg TermArg TermArg TermArg TermArg + + public class DefLoadTable : AmlParserNode + { + public TermArg signatureString; + public TermArg oemIDString; + public TermArg oemTableIDString; + public TermArg rootPathString; + public TermArg parameterPathString; + public TermArg parameterData; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefLoadTable(out DefLoadTable result, ref int offset) + { + int offset2 = offset; + result = new DefLoadTable(); + if (CheckTwoBytePrefix(LoadTableOp1, LoadTableOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseTermArg(out result.signatureString, ref offset2) == Failure || + ParseTermArg(out result.oemIDString, ref offset2) == Failure || + ParseTermArg(out result.oemTableIDString, ref offset2) == Failure || + ParseTermArg(out result.rootPathString, ref offset2) == Failure || + ParseTermArg(out result.parameterPathString, ref offset2) == Failure || + ParseTermArg(out result.parameterData, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // LoadTableOp := ExtOpPrefix 0x1F + + const ByteData LoadTableOp1 = ExtOpPrefix; + const ByteData LoadTableOp2 = 0x1F; + + // DefLOr := LorOp Operand Operand + + public class DefLOr : AmlParserNode + { + public Operand leftOperand; + public Operand rightOperand; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefLOr(out DefLOr result, ref int offset) + { + int offset2 = offset; + result = new DefLOr(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != LorOp) { + return Failure; + } + if (ParseOperand(out result.leftOperand, ref offset2) == Failure || + ParseOperand(out result.rightOperand, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // LorOp := 0x91 + + const ByteData LorOp = 0x91; + + // DefMatch := MatchOp SearchPkg MatchOpcode Operand MatchOpcode Operand StartIndex + + public class DefMatch : AmlParserNode + { + public SearchPkg searchPkg; + public MatchOpcode matchOpcode1; + public Operand operand1; + public MatchOpcode matchOpcode2; + public Operand operand2; + public StartIndex startIndex; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefMatch(out DefMatch result, ref int offset) + { + int offset2 = offset; + result = new DefMatch(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != MatchOp) { + return Failure; + } + if (ParseSearchPkg (out result.searchPkg, ref offset2) == Failure || + ParseMatchOpcode(out result.matchOpcode1, ref offset2) == Failure || + ParseOperand (out result.operand1, ref offset2) == Failure || + ParseMatchOpcode(out result.matchOpcode2, ref offset2) == Failure || + ParseOperand (out result.operand2, ref offset2) == Failure || + ParseStartIndex (out result.startIndex, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // MatchOp := 0x89 + + const ByteData MatchOp = 0x89; + + // SearchPkg := TermArg => Package + + public class SearchPkg : AmlParserNode + { + public TermArg package; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseSearchPkg(out SearchPkg result, ref int offset) + { + int offset2 = offset; + result = new SearchPkg(); + + if (ParseTermArg(out result.package, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.package, TermArgType.Package)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // MatchOpcode := ByteData // 0 MTR + // // 1 MEQ + // // 2 MLE + // // 3 MLT + // // 4 MGE + // // 5 MGT + + public enum MatchOpcode + { + MTR = 0, + MEQ = 1, + MLE = 2, + MLT = 3, + MGE = 4, + MGT = 5, + NumMatchOpcodes = 6 + } + + private ParseSuccess ParseMatchOpcode(out MatchOpcode result, ref int offset) + { + int offset2 = offset; + result = new MatchOpcode(); + + ByteData b = stream.ReadByteData(ref offset2); + if (b >= (byte)MatchOpcode.NumMatchOpcodes) { + return Failure; + } + result = (MatchOpcode)b; + + offset = offset2; + return Success; + } + + // StartIndex := TermArg => Integer + + public class StartIndex : AmlParserNode + { + public TermArg integer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseStartIndex(out StartIndex result, ref int offset) + { + int offset2 = offset; + result = new StartIndex(); + + if (ParseTermArg(out result.integer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.integer, TermArgType.Integer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefMid := MidOp MidObj TermArg TermArg Target + + public class DefMid : AmlParserNode + { + public MidObj midObj; + public TermArg index; + public TermArg length; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefMid(out DefMid result, ref int offset) + { + int offset2 = offset; + result = new DefMid(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != MidOp) { + return Failure; + } + if (ParseMidObj (out result.midObj, ref offset2) == Failure || + ParseTermArg(out result.index, ref offset2) == Failure || + ParseTermArg(out result.length, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // MidOp := 0x9E + + const ByteData MidOp = 0x9E; + + // MidObj := TermArg => Buffer | StringConst + + public class MidObj : AmlParserNode + { + public TermArg termArg; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseMidObj(out MidObj result, ref int offset) + { + int offset2 = offset; + result = new MidObj(); + + if (ParseTermArg(out result.termArg, ref offset2) == Failure /*|| + (!TermArgEvaluatesTo(result.termArg, TermArgType.Buffer) && + !TermArgEvaluatesTo(result.termArg, TermArgType.StringConst))*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefMod := ModOp Dividend Divisor Target + + public class DefMod : AmlParserNode + { + public Dividend dividend; + public Divisor divisor; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefMod(out DefMod result, ref int offset) + { + int offset2 = offset; + result = new DefMod(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != ModOp) { + return Failure; + } + if (ParseDividend (out result.dividend, ref offset2) == Failure || + ParseDivisor (out result.divisor, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // ModOp := 0x85 + + const ByteData ModOp = 0x85; + + // DefMultiply := MultiplyOp Operand Operand Target + + public class DefMultiply : AmlParserNode + { + public Operand leftOperand; + public Operand rightOperand; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefMultiply(out DefMultiply result, ref int offset) + { + int offset2 = offset; + result = new DefMultiply(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != MultiplyOp) { + return Failure; + } + if (ParseOperand (out result.leftOperand, ref offset2) == Failure || + ParseOperand (out result.rightOperand, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // MultiplyOp := 0x77 + + const ByteData MultiplyOp = 0x77; + + // DefNAnd := NandOp Operand Operand Target + + public class DefNAnd : AmlParserNode + { + public Operand leftOperand; + public Operand rightOperand; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefNAnd(out DefNAnd result, ref int offset) + { + int offset2 = offset; + result = new DefNAnd(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != NandOp) { + return Failure; + } + if (ParseOperand (out result.leftOperand, ref offset2) == Failure || + ParseOperand (out result.rightOperand, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // NandOp := 0x7C + + const ByteData NandOp = 0x7C; + + // DefNOr := NorOp Operand Operand Target + + public class DefNOr : AmlParserNode + { + public Operand leftOperand; + public Operand rightOperand; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefNOr(out DefNOr result, ref int offset) + { + int offset2 = offset; + result = new DefNOr(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != NorOp) { + return Failure; + } + if (ParseOperand (out result.leftOperand, ref offset2) == Failure || + ParseOperand (out result.rightOperand, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // NorOp := 0x7E + + const ByteData NorOp = 0x7E; + + // DefNot := NotOp Operand Target + + public class DefNot : AmlParserNode + { + public Operand operand; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefNot(out DefNot result, ref int offset) + { + int offset2 = offset; + result = new DefNot(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != NotOp) { + return Failure; + } + if (ParseOperand (out result.operand, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // NotOp := 0x80 + + const ByteData NotOp = 0x80; + + // DefObjectType := ObjectTypeOp SuperName + + public class DefObjectType : AmlParserNode + { + public SuperName superName; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefObjectType(out DefObjectType result, ref int offset) + { + int offset2 = offset; + result = new DefObjectType(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != ObjectTypeOp) { + return Failure; + } + if (ParseSuperName(out result.superName, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // ObjectTypeOp := 0x8E + + const ByteData ObjectTypeOp = 0x8E; + + // DefOr := OrOp Operand Operand Target + + public class DefOr : AmlParserNode + { + public Operand leftOperand; + public Operand rightOperand; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefOr(out DefOr result, ref int offset) + { + int offset2 = offset; + result = new DefOr(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != OrOp) { + return Failure; + } + if (ParseOperand (out result.leftOperand, ref offset2) == Failure || + ParseOperand (out result.rightOperand, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // OrOp := 0x7D + + const ByteData OrOp = 0x7D; + + // DefPackage := PackageOp PkgLength NumElements PackageElementList + + public class DefPackage : AmlParserNode + { + public NumElements numElements; + public PackageElement[] packageElementList; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefPackage(out DefPackage result, ref int offset) + { + int offset2 = offset; + result = new DefPackage(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != PackageOp) { + return Failure; + } + + int endOffset; + if (ParsePkgLengthEndOffset(out endOffset, ref offset2) == Failure) { + return Failure; + } + + if (ParseNumElements (out result.numElements, ref offset2) == Failure || + ParsePackageElementList(out result.packageElementList, ref offset2, endOffset) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // PackageOp := 0x12 + + const ByteData PackageOp = 0x12; + + // DefVarPackage := VarPackageOp PkgLength VarNumElements PackageElementList + + public class DefVarPackage : AmlParserNode + { + public VarNumElements varNumElements; + public PackageElement[] packageElementList; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefVarPackage(out DefVarPackage result, ref int offset) + { + int offset2 = offset; + result = new DefVarPackage(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != VarPackageOp) { + return Failure; + } + + int endOffset; + if (ParsePkgLengthEndOffset(out endOffset, ref offset2) == Failure) { + return Failure; + } + + if (ParseVarNumElements (out result.varNumElements, ref offset2) == Failure || + ParsePackageElementList(out result.packageElementList, ref offset2, endOffset) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // VarPackageOp := 0x13 + + const ByteData VarPackageOp = 0x13; + + // NumElements := ByteData + + public class NumElements : AmlParserNode + { + public ByteData byteData; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseNumElements(out NumElements result, ref int offset) + { + int offset2 = offset; + result = new NumElements(); + result.byteData = stream.ReadByteData(ref offset2); + offset = offset2; + return Success; + } + + // VarNumElements := TermArg => Integer + + public class VarNumElements : AmlParserNode + { + public TermArg integer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseVarNumElements(out VarNumElements result, ref int offset) + { + int offset2 = offset; + result = new VarNumElements(); + + if (ParseTermArg(out result.integer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.integer, TermArgType.Integer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // PackageElementList := Nothing | + // [To make this rule make sense, it needs a length + // limit for termination. The rule using it should know this.] + + private class PackageElementList + { + ArrayList list = new ArrayList(); + + public void Add(PackageElement packageElement) + { + list.Add(packageElement); + } + + public PackageElement[] ToArray() + { + return (PackageElement[])list.ToArray(typeof(PackageElement)); + } + } + + private ParseSuccess ParsePackageElementList(out PackageElement[] result, ref int offset, int endOffset) + { + result = null; + PackageElementList packageElements = new PackageElementList(); + int offset2 = offset; + + while (offset2 < endOffset) { + PackageElement packageElement; + if (ParsePackageElement(out packageElement, ref offset2) == Failure) { + return Failure; + } + packageElements.Add(packageElement); + } + Debug.Assert(offset2 == endOffset); + + result = packageElements.ToArray(); + offset = offset2; + return Success; + } + + // PackageElement := DataRefObject | NameString + // See AmlParser.csunion + + private ParseSuccess ParsePackageElement(out PackageElement result, ref int offset) + { + int offset2 = offset; + + DataRefObject dataRefObject; + NameString nameString; + + if (ParseDataRefObject(out dataRefObject, ref offset2) == Success) { + result = PackageElement.CreateDataRefObject(dataRefObject); + } + else if (ParseNameString(out nameString, ref offset2) == Success) { + result = PackageElement.CreateNameString(nameString); + } + else { + result = null; + return Failure; + } + + offset = offset2; + return Success; + } + + // DefRefOf := RefOfOp SuperName + + public class DefRefOf : AmlParserNode + { + public SuperName superName; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefRefOf(out DefRefOf result, ref int offset) + { + int offset2 = offset; + result = new DefRefOf(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != RefOfOp) { + return Failure; + } + if (ParseSuperName(out result.superName, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // RefOfOp := 0x71 + + const ByteData RefOfOp = 0x71; + + // DefShiftLeft := ShiftLeftOp Operand ShiftCount Target + + public class DefShiftLeft : AmlParserNode + { + public Operand operand; + public ShiftCount shiftCount; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefShiftLeft(out DefShiftLeft result, ref int offset) + { + int offset2 = offset; + result = new DefShiftLeft(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != ShiftLeftOp) { + return Failure; + } + if (ParseOperand (out result.operand, ref offset2) == Failure || + ParseShiftCount(out result.shiftCount, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // ShiftLeftOp := 0x79 + + const ByteData ShiftLeftOp = 0x79; + + // ShiftCount := TermArg => Integer + + public class ShiftCount : AmlParserNode + { + public TermArg integer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseShiftCount(out ShiftCount result, ref int offset) + { + int offset2 = offset; + result = new ShiftCount(); + + if (ParseTermArg(out result.integer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.integer, TermArgType.Integer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DefShiftRight := ShiftRightOp Operand ShiftCount Target + + public class DefShiftRight : AmlParserNode + { + public Operand operand; + public ShiftCount shiftCount; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefShiftRight(out DefShiftRight result, ref int offset) + { + int offset2 = offset; + result = new DefShiftRight(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != ShiftRightOp) { + return Failure; + } + if (ParseOperand (out result.operand, ref offset2) == Failure || + ParseShiftCount(out result.shiftCount, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // ShiftRightOp := 0x7A + + const ByteData ShiftRightOp = 0x7A; + + // DefSizeOf := SizeOfOp SuperName + + public class DefSizeOf : AmlParserNode + { + public SuperName superName; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefSizeOf(out DefSizeOf result, ref int offset) + { + int offset2 = offset; + result = new DefSizeOf(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != SizeOfOp) { + return Failure; + } + if (ParseSuperName(out result.superName, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // SizeOfOp := 0x87 + + const ByteData SizeOfOp = 0x87; + + // DefStore := StoreOp TermArg SuperName + + public class DefStore : AmlParserNode + { + public TermArg termArg; + public SuperName superName; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefStore(out DefStore result, ref int offset) + { + int offset2 = offset; + result = new DefStore(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != StoreOp) { + return Failure; + } + if (ParseTermArg (out result.termArg, ref offset2) == Failure || + ParseSuperName(out result.superName, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // StoreOp := 0x70 + + const ByteData StoreOp = 0x70; + + // DefSubtract := SubtractOp Operand Operand Target + + public class DefSubtract : AmlParserNode + { + public Operand leftOperand; + public Operand rightOperand; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefSubtract(out DefSubtract result, ref int offset) + { + int offset2 = offset; + result = new DefSubtract(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != SubtractOp) { + return Failure; + } + if (ParseOperand (out result.leftOperand, ref offset2) == Failure || + ParseOperand (out result.rightOperand, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // SubtractOp := 0x74 + + const ByteData SubtractOp = 0x74; + + // DefTimer := TimerOp + + public class DefTimer : AmlParserNode + { + // No data + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefTimer(out DefTimer result, ref int offset) + { + int offset2 = offset; + result = new DefTimer(); + if (CheckTwoBytePrefix(TimerOp1, TimerOp2, ref offset2) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // TimerOp := 0x5B 0x33 + + const ByteData TimerOp1 = 0x5B; + const ByteData TimerOp2 = 0x33; + + // DefToBCD := ToBCDOp Operand Target + + public class DefToBCD : AmlParserNode + { + public Operand operand; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefToBCD(out DefToBCD result, ref int offset) + { + int offset2 = offset; + result = new DefToBCD(); + if (CheckTwoBytePrefix(ToBCDOp1, ToBCDOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseOperand (out result.operand, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // ToBCDOp := ExtOpPrefix 0x29 + + const ByteData ToBCDOp1 = ExtOpPrefix; + const ByteData ToBCDOp2 = 0x29; + + // DefToBuffer := ToBufferOp Operand Target + + public class DefToBuffer : AmlParserNode + { + public Operand operand; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefToBuffer(out DefToBuffer result, ref int offset) + { + int offset2 = offset; + result = new DefToBuffer(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != ToBufferOp) { + return Failure; + } + if (ParseOperand (out result.operand, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // ToBufferOp := 0x96 + + const ByteData ToBufferOp = 0x96; + + // DefToDecimalString := ToDecimalStringOp Operand Target + + public class DefToDecimalString : AmlParserNode + { + public Operand operand; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefToDecimalString(out DefToDecimalString result, ref int offset) + { + int offset2 = offset; + result = new DefToDecimalString(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != ToDecimalStringOp) { + return Failure; + } + if (ParseOperand (out result.operand, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // ToDecimalStringOp := 0x97 + + const ByteData ToDecimalStringOp = 0x97; + + // DefToHexString := ToHexStringOp Operand Target + + public class DefToHexString : AmlParserNode + { + public Operand operand; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefToHexString(out DefToHexString result, ref int offset) + { + int offset2 = offset; + result = new DefToHexString(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != ToHexStringOp) { + return Failure; + } + if (ParseOperand (out result.operand, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // ToHexStringOp := 0x98 + + const ByteData ToHexStringOp = 0x98; + + // DefToInteger := ToIntegerOp Operand Target + + public class DefToInteger : AmlParserNode + { + public Operand operand; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefToInteger(out DefToInteger result, ref int offset) + { + int offset2 = offset; + result = new DefToInteger(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != ToIntegerOp) { + return Failure; + } + if (ParseOperand (out result.operand, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // ToIntegerOp := 0x99 + + const ByteData ToIntegerOp = 0x99; + + // DefToString := ToStringOp TermArg LengthArg Target + + public class DefToString : AmlParserNode + { + public TermArg termArg; + public LengthArg lengthArg; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefToString(out DefToString result, ref int offset) + { + int offset2 = offset; + result = new DefToString(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != ToStringOp) { + return Failure; + } + if (ParseTermArg (out result.termArg, ref offset2) == Failure || + ParseLengthArg(out result.lengthArg, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // LengthArg := TermArg => Integer + + public class LengthArg : AmlParserNode + { + public TermArg integer; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseLengthArg(out LengthArg result, ref int offset) + { + int offset2 = offset; + result = new LengthArg(); + + if (ParseTermArg(out result.integer, ref offset2) == Failure /*|| + !TermArgEvaluatesTo(result.integer, TermArgType.Integer)*/) { + return Failure; + } + + offset = offset2; + return Success; + } + + // ToStringOp := 0x9C + + const ByteData ToStringOp = 0x9C; + + // DefWait := WaitOp EventObject Operand + + public class DefWait : AmlParserNode + { + public EventObject eventObject; + public Operand operand; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefWait(out DefWait result, ref int offset) + { + int offset2 = offset; + result = new DefWait(); + if (CheckTwoBytePrefix(WaitOp1, WaitOp2, ref offset2) == Failure) { + return Failure; + } + + if (ParseEventObject(out result.eventObject, ref offset2) == Failure || + ParseOperand (out result.operand, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // WaitOp := ExtOpPrefix 0x25 + + const ByteData WaitOp1 = ExtOpPrefix; + const ByteData WaitOp2 = 0x25; + + // DefXOr := XorOp Operand Operand Target + + public class DefXOr : AmlParserNode + { + public Operand leftOperand; + public Operand rightOperand; + public Target target; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDefXOr(out DefXOr result, ref int offset) + { + int offset2 = offset; + result = new DefXOr(); + ByteData prefix = stream.ReadByteData(ref offset2); + if (prefix != XorOp) { + return Failure; + } + if (ParseOperand(out result.leftOperand, ref offset2) == Failure || + ParseOperand(out result.rightOperand, ref offset2) == Failure || + ParseTarget (out result.target, ref offset2) == Failure) { + return Failure; + } + offset = offset2; + return Success; + } + + // XorOp := 0x7F + + const ByteData XorOp = 0x7F; + + + // + // Section 18.2.6: Miscellaneous Objects Encoding + // + + // + // Section 18.2.6.1: Arg Objects Encoding + // + + // ArgObj := Arg0Op | Arg1Op | Arg2Op | Arg3Op | Arg4Op | + // Arg5Op | Arg6Op + + public class ArgObj : AmlParserNode + { + public ByteData op; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseArgObj(out ArgObj result, ref int offset) + { + int offset2 = offset; + result = new ArgObj(); + ByteData op = stream.ReadByteData(ref offset2); + if (op != Arg0Op && + op != Arg1Op && + op != Arg2Op && + op != Arg3Op && + op != Arg4Op && + op != Arg5Op && + op != Arg6Op) { + return Failure; + } + result.op = (ByteData)(op - Arg0Op); + offset = offset2; + return Success; + } + + // Arg0Op := 0x68 + + const ByteData Arg0Op = 0x68; + + // Arg1Op := 0x69 + + const ByteData Arg1Op = 0x69; + + // Arg2Op := 0x6A + + const ByteData Arg2Op = 0x6A; + + // Arg3Op := 0x6B + + const ByteData Arg3Op = 0x6B; + + // Arg4Op := 0x6C + + const ByteData Arg4Op = 0x6C; + + // Arg5Op := 0x6D + + const ByteData Arg5Op = 0x6D; + + // Arg6Op := 0x6E + + + const ByteData Arg6Op = 0x6E; + + // + // Section 18.2.6.2: Local Objects Encoding + // + + // LocalObj := Local0Op | Local1Op | Local2Op | Local3Op | Local4Op | + // Local5Op | Local6Op | Local7Op + + public class LocalObj : AmlParserNode + { + public ByteData op; + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseLocalObj(out LocalObj result, ref int offset) + { + int offset2 = offset; + result = new LocalObj(); + ByteData op = stream.ReadByteData(ref offset2); + if (op != Local0Op && + op != Local1Op && + op != Local2Op && + op != Local3Op && + op != Local4Op && + op != Local5Op && + op != Local6Op && + op != Local7Op) { + return Failure; + } + result.op = (ByteData)(op - Local0Op); + offset = offset2; + return Success; + } + + // Local0Op := 0x60 + + const ByteData Local0Op = 0x60; + + // Local1Op := 0x61 + + const ByteData Local1Op = 0x61; + + // Local2Op := 0x62 + + const ByteData Local2Op = 0x62; + + // Local3Op := 0x63 + + const ByteData Local3Op = 0x63; + + // Local4Op := 0x64 + + const ByteData Local4Op = 0x64; + + // Local5Op := 0x65 + + const ByteData Local5Op = 0x65; + + // Local6Op := 0x66 + + const ByteData Local6Op = 0x66; + + // Local7Op := 0x67 + + const ByteData Local7Op = 0x67; + + // + // Section 18.2.6.3: Debug Objects Encoding + // + + // DebugObj := DebugOp + + public class DebugObj : AmlParserNode + { + // No data + + public override void Accept(AmlParserNodeVisitor v) + { + v.Visit(this); + } + } + + private ParseSuccess ParseDebugObj(out DebugObj result, ref int offset) + { + int offset2 = offset; + result = new DebugObj(); + if (CheckTwoBytePrefix(DebugOp1, DebugOp2, ref offset2) == Failure) { + return Failure; + } + + offset = offset2; + return Success; + } + + // DebugOp := ExtOpPrefix 0x31 + + const ByteData DebugOp1 = ExtOpPrefix; + const ByteData DebugOp2 = 0x31; + + // Helpers + + private ParseSuccess CheckTwoBytePrefix(byte op1, byte op2, ref int offset) { + int offset2 = offset; + ByteData prefix1, prefix2; + + prefix1 = stream.ReadByteData(ref offset2); + if (prefix1 != op1) { + return Failure; + } + + prefix2 = stream.ReadByteData(ref offset2); + + if (prefix2 != op2) { + return Failure; + } + + offset = offset2; + return Success; + } + } +} diff --git a/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AmlParser.csunion b/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AmlParser.csunion new file mode 100644 index 0000000..ff11c97 --- /dev/null +++ b/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AmlParser.csunion @@ -0,0 +1,316 @@ +using Microsoft.Singularity.Hal.Acpi; +using ByteData = System.Byte; +using WordData = System.UInt16; +using DWordData = System.UInt32; +using QWordData = System.UInt64; + +%% + +datatype SimpleName : AmlParserNode { + NameString(AmlParser.NameString nameString), + ArgObj(AmlParser.ArgObj argObj), + LocalObj(AmlParser.LocalObj localObj) + %% + public override void Accept(AmlParserNodeVisitor v) { + v.Visit(this); + } + + public void AcceptAlternative(AmlParserNodeVisitor v) { + ((AmlParserNode)value).Accept(v); + } + %% +} + +datatype SuperName : AmlParserNode { + SimpleName(SimpleName simpleName), + DebugObj(AmlParser.DebugObj debugObj), + Type6Opcode(Type6Opcode type6Opcode) + %% + public override void Accept(AmlParserNodeVisitor v) { + v.Visit(this); + } + + public void AcceptAlternative(AmlParserNodeVisitor v) { + ((AmlParserNode)value).Accept(v); + } + %% +} + +datatype ComputationalData : AmlParserNode { + ByteConst(ByteData byteConst), + WordConst(WordData wordConst), + DWordConst(DWordData dWordConst), + QWordConst(QWordData qWordConst), + StringConst(string stringConst), + ConstObj(AmlParser.ConstObj constObj), + RevisionOp, + DefBuffer(AmlParser.DefBuffer defBuffer) + %% + public override void Accept(AmlParserNodeVisitor v) { + v.Visit(this); + } + + public void AcceptAlternative(AmlParserNodeVisitor v) { + ((AmlParserNode)value).Accept(v); + } + %% +} + +datatype DataObject : AmlParserNode { + ComputationalData(ComputationalData computationalData), + DefPackage(AmlParser.DefPackage defPackage), + DefVarPackage(AmlParser.DefVarPackage defVarPackage) + %% + public override void Accept(AmlParserNodeVisitor v) { + v.Visit(this); + } + + public void AcceptAlternative(AmlParserNodeVisitor v) { + ((AmlParserNode)value).Accept(v); + } + %% +} + +datatype DataRefObject : AmlParserNode { + DataObject(DataObject dataObject), + ObjectReference(TermArg objectReference), + DDBHandle(AmlParser.DDBHandleObject ddbHandle) + %% + public override void Accept(AmlParserNodeVisitor v) { + v.Visit(this); + } + + public void AcceptAlternative(AmlParserNodeVisitor v) { + ((AmlParserNode)value).Accept(v); + } + %% +} + +datatype TermObj : AmlParserNode { + NameSpaceModifierObj(NameSpaceModifierObj nameSpaceModifierObj), + NamedObj(NamedObj namedObj), + Type1Opcode(Type1Opcode type1Opcode), + Type2Opcode(Type2Opcode type2Opcode) + %% + public override void Accept(AmlParserNodeVisitor v) { + v.Visit(this); + } + + public void AcceptAlternative(AmlParserNodeVisitor v) { + ((AmlParserNode)value).Accept(v); + } + %% +} + +datatype TermArg : AmlParserNode { + Type2Opcode(Type2Opcode type2Opcode), + DataObject(DataObject dataObject), + ArgObj(AmlParser.ArgObj argObj), + LocalObj(AmlParser.LocalObj localObj) + %% + public override void Accept(AmlParserNodeVisitor v) { + v.Visit(this); + } + + public void AcceptAlternative(AmlParserNodeVisitor v) { + ((AmlParserNode)value).Accept(v); + } + %% +} + +datatype AmlObject : AmlParserNode { + NameSpaceModifierObj(NameSpaceModifierObj nameSpaceModifierObj), + NamedObj(NamedObj namedObj) + %% + public override void Accept(AmlParserNodeVisitor v) { + v.Visit(this); + } + + public void AcceptAlternative(AmlParserNodeVisitor v) { + ((AmlParserNode)value).Accept(v); + } + %% +} + +datatype NameSpaceModifierObj : AmlParserNode { + DefAlias(AmlParser.DefAlias defAlias), + DefName(AmlParser.DefName defName), + DefScope(AmlParser.DefScope defScope) + %% + public override void Accept(AmlParserNodeVisitor v) { + v.Visit(this); + } + + public void AcceptAlternative(AmlParserNodeVisitor v) { + ((AmlParserNode)value).Accept(v); + } + %% +} + +datatype NamedObj : AmlParserNode { + DefBankField(AmlParser.DefBankField defBankField), + DefCreateBitField(AmlParser.DefCreateBitField defCreateBitField), + DefCreateByteField(AmlParser.DefCreateByteField defCreateByteField), + DefCreateDWordField(AmlParser.DefCreateDWordField defCreateDWordField), + DefCreateField(AmlParser.DefCreateField defCreateField), + DefCreateQWordField(AmlParser.DefCreateQWordField defCreateQWordField), + DefCreateWordField(AmlParser.DefCreateWordField defCreateWordField), + DefDataRegion(AmlParser.DefDataRegion defDataRegion), + DefDevice(AmlParser.DefDevice defDevice), + DefEvent(AmlParser.DefEvent defEvent), + DefField(AmlParser.DefField defField), + DefIndexField(AmlParser.DefIndexField defIndexField), + DefMethod(AmlParser.DefMethod defMethod), + DefMutex(AmlParser.DefMutex defMutex), + DefOpRegion(AmlParser.DefOpRegion defOpRegion), + DefPowerRes(AmlParser.DefPowerRes defPowerRes), + DefProcessor(AmlParser.DefProcessor defProcessor), + DefThermalZone(AmlParser.DefThermalZone defThermalZone) + %% + public override void Accept(AmlParserNodeVisitor v) { + v.Visit(this); + } + + public void AcceptAlternative(AmlParserNodeVisitor v) { + ((AmlParserNode)value).Accept(v); + } + %% +} + +datatype FieldElement : AmlParserNode { + NamedField(AmlParser.NamedField namedField), + ReservedField(AmlParser.ReservedField reservedField), + AccessField(AmlParser.AccessField accessField) + %% + public override void Accept(AmlParserNodeVisitor v) { + v.Visit(this); + } + + public void AcceptAlternative(AmlParserNodeVisitor v) { + ((AmlParserNode)value).Accept(v); + } + %% +} + +datatype Type1Opcode : AmlParserNode { + DefBreak (AmlParser.DefBreak defBreak), + DefBreakPoint (AmlParser.DefBreakPoint defBreakPoint), + DefContinue (AmlParser.DefContinue defContinue), + DefFatal (AmlParser.DefFatal defFatal), + DefIfElse (AmlParser.DefIfElse defIfElse), + DefLoad (AmlParser.DefLoad defLoad), + DefNoop (AmlParser.DefNoop defNoop), + DefNotify (AmlParser.DefNotify defNotify), + DefRelease (AmlParser.DefRelease defRelease), + DefReset (AmlParser.DefReset defReset), + DefReturn (AmlParser.DefReturn defReturn), + DefSignal (AmlParser.DefSignal defSignal), + DefSleep (AmlParser.DefSleep defSleep), + DefStall (AmlParser.DefStall defStall), + DefUnload (AmlParser.DefUnload defUnload), + DefWhile (AmlParser.DefWhile defWhile) + %% + public override void Accept(AmlParserNodeVisitor v) { + v.VisitType1Opcode(this); + } + + public void AcceptAlternative(AmlParserNodeVisitor v) { + ((AmlParserNode)value).Accept(v); + } + %% +} + +datatype Type2Opcode : AmlParserNode { + DefAcquire(AmlParser.DefAcquire defAcquire), + DefAdd(AmlParser.DefAdd defAdd), + DefAnd(AmlParser.DefAnd defAnd), + DefBuffer(AmlParser.DefBuffer defBuffer), + DefConcat(AmlParser.DefConcat defConcat), + DefConcatRes(AmlParser.DefConcatRes defConcatRes), + DefCondRefOf(AmlParser.DefCondRefOf defCondRefOf), + DefCopyObject(AmlParser.DefCopyObject defCopyObject), + DefDecrement(AmlParser.DefDecrement defDecrement), + DefDerefOf(AmlParser.DefDerefOf defDerefOf), + DefDivide(AmlParser.DefDivide defDivide), + DefFindSetLeftBit(AmlParser.DefFindSetLeftBit defFindSetLeftBit), + DefFindSetRightBit(AmlParser.DefFindSetRightBit defFindSetRightBit), + DefFromBCD(AmlParser.DefFromBCD defFromBCD), + DefIncrement(AmlParser.DefIncrement defIncrement), + DefIndex(AmlParser.DefIndex defIndex), + DefLAnd(AmlParser.DefLAnd defLAnd), + DefLEqual(AmlParser.DefLEqual defLEqual), + DefLGreater(AmlParser.DefLGreater defLGreater), + DefLGreaterEqual(AmlParser.DefLGreaterEqual defLGreaterEqual), + DefLLess(AmlParser.DefLLess defLLess), + DefLLessEqual(AmlParser.DefLLessEqual defLLessEqual), + DefMid(AmlParser.DefMid defMid), + DefLNot(AmlParser.DefLNot defLNot), + DefLNotEqual(AmlParser.DefLNotEqual defLNotEqual), + DefLoadTable(AmlParser.DefLoadTable defLoadTable), + DefLOr(AmlParser.DefLOr defLOr), + DefMatch(AmlParser.DefMatch defMatch), + DefMod(AmlParser.DefMod defMod), + DefMultiply(AmlParser.DefMultiply defMultiply), + DefNAnd(AmlParser.DefNAnd defNAnd), + DefNOr(AmlParser.DefNOr defNOr), + DefNot(AmlParser.DefNot defNot), + DefObjectType(AmlParser.DefObjectType defObjectType), + DefOr(AmlParser.DefOr defOr), + DefPackage(AmlParser.DefPackage defPackage), + DefVarPackage(AmlParser.DefVarPackage defVarPackage), + DefRefOf(AmlParser.DefRefOf defRefOf), + DefShiftLeft(AmlParser.DefShiftLeft defShiftLeft), + DefShiftRight(AmlParser.DefShiftRight defShiftRight), + DefSizeOf(AmlParser.DefSizeOf defSizeOf), + DefStore(AmlParser.DefStore defStore), + DefSubtract(AmlParser.DefSubtract defSubtract), + DefTimer(AmlParser.DefTimer defTimer), + DefToBCD(AmlParser.DefToBCD defToBCD), + DefToBuffer(AmlParser.DefToBuffer defToBuffer), + DefToDecimalString(AmlParser.DefToDecimalString defToDecimalString), + DefToHexString(AmlParser.DefToHexString defToHexString), + DefToInteger(AmlParser.DefToInteger defToInteger), + DefToString(AmlParser.DefToString defToString), + DefWait(AmlParser.DefWait defWait), + DefXOr(AmlParser.DefXOr defXOr), + UserTermObj(AmlParser.UserTermObj userTermObj) + %% + public override void Accept(AmlParserNodeVisitor v) { + v.VisitType2Opcode(this); + } + + public void AcceptAlternative(AmlParserNodeVisitor v) { + ((AmlParserNode)value).Accept(v); + } + %% +} + +datatype Type6Opcode : AmlParserNode { + DefRefOf(AmlParser.DefRefOf defRefOf), + DefDerefOf(AmlParser.DefDerefOf defDerefOf), + DefIndex(AmlParser.DefIndex defIndex), + UserTermObj(AmlParser.UserTermObj userTermObj) + %% + public override void Accept(AmlParserNodeVisitor v) { + v.VisitType6Opcode(this); + } + + public void AcceptAlternative(AmlParserNodeVisitor v) { + ((AmlParserNode)value).Accept(v); + } + %% +} + +datatype PackageElement : AmlParserNode { + DataRefObject(DataRefObject dataRefObject), + NameString(AmlParser.NameString nameString) + %% + public override void Accept(AmlParserNodeVisitor v) { + v.Visit(this); + } + + public void AcceptAlternative(AmlParserNodeVisitor v) { + ((AmlParserNode)value).Accept(v); + } + %% +} diff --git a/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AmlParserNodeVisitor.cs b/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AmlParserNodeVisitor.cs new file mode 100644 index 0000000..1557108 --- /dev/null +++ b/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AmlParserNodeVisitor.cs @@ -0,0 +1,865 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Microsoft Research Singularity +// + +using System; +using System.Diagnostics; +using System.Text; + +using Microsoft.Singularity.Hal.Acpi.AmlParserUnions; + +namespace Microsoft.Singularity.Hal.Acpi +{ + /// + /// This abstract node visitor class (following the GoF Visitor pattern) enables + /// straightforward structural recursion over an AML parse tree, which is useful in + /// the parser and interpreter. Leaf classes are left abstract and must be implemented. + /// + public abstract class AmlParserNodeVisitor + { + public abstract void UnhandledNodeType(string nodeTypeName); + + public virtual void Visit(SimpleName simpleName) + { + simpleName.AcceptAlternative(this); + } + + public virtual void Visit(SuperName superName) + { + superName.AcceptAlternative(this); + } + + public virtual void Visit(ComputationalData computationalData) + { + computationalData.AcceptAlternative(this); + } + + public virtual void Visit(DataObject dataObject) + { + dataObject.AcceptAlternative(this); + } + + public virtual void Visit(DataRefObject dataRefObject) + { + dataRefObject.AcceptAlternative(this); + } + + public virtual void Visit(TermObj termObj) + { + termObj.AcceptAlternative(this); + } + + public virtual void Visit(TermArg termArg) + { + termArg.AcceptAlternative(this); + } + + public virtual void Visit(AmlObject amlObject) + { + amlObject.AcceptAlternative(this); + } + + public virtual void Visit(NameSpaceModifierObj nameSpaceModifierObj) + { + nameSpaceModifierObj.AcceptAlternative(this); + } + + public virtual void Visit(NamedObj namedObj) + { + namedObj.AcceptAlternative(this); + } + + public virtual void Visit(FieldElement fieldElement) + { + fieldElement.AcceptAlternative(this); + } + + public virtual void VisitType1Opcode(Type1Opcode type1Opcode) + { + type1Opcode.AcceptAlternative(this); + } + + public virtual void VisitType2Opcode(Type2Opcode type2Opcode) + { + type2Opcode.AcceptAlternative(this); + } + + public virtual void VisitType6Opcode(Type6Opcode type6Opcode) + { + type6Opcode.AcceptAlternative(this); + } + + public virtual void Visit(PackageElement packageElement) + { + packageElement.AcceptAlternative(this); + } + + public virtual void Visit(AmlParser.AMLCode amlCode) + { + UnhandledNodeType("AMLCode"); + } + + public virtual void Visit(AmlParser.NameSeg nameSeg) + { + UnhandledNodeType("NameSeg"); + } + + public virtual void Visit(AmlParser.NameString nameString) + { + UnhandledNodeType("NameString"); + } + + public virtual void Visit(AmlParser.PrefixPath prefixPath) + { + UnhandledNodeType("PrefixPath"); + } + + public virtual void Visit(AmlParser.NamePath namePath) + { + UnhandledNodeType("NamePath"); + } + + public virtual void Visit(AmlParser.SegCount segCount) + { + UnhandledNodeType("SegCount"); + } + + public virtual void Visit(AmlParser.Target target) + { + UnhandledNodeType("Target"); + } + + public virtual void Visit(AmlParser.ConstObj constObj) + { + UnhandledNodeType("ConstObj"); + } + + public virtual void Visit(AmlParser.UserTermObj userTermObj) + { + UnhandledNodeType("UserTermObj"); + } + + public virtual void Visit(AmlParser.DefAlias defAlias) + { + UnhandledNodeType("DefAlias"); + } + + public virtual void Visit(AmlParser.DefName defName) + { + UnhandledNodeType("DefName"); + } + + public virtual void Visit(AmlParser.DefScope defScope) + { + UnhandledNodeType("DefScope"); + } + + public virtual void Visit(AmlParser.DefBankField defBankField) + { + UnhandledNodeType("DefBankField"); + } + + public virtual void Visit(AmlParser.BankValue bankValue) + { + UnhandledNodeType("BankValue"); + } + + public virtual void Visit(AmlParser.FieldFlags fieldFlags) + { + UnhandledNodeType("FieldFlags"); + } + + public virtual void Visit(AmlParser.NamedField namedField) + { + UnhandledNodeType("NamedField"); + } + + public virtual void Visit(AmlParser.ReservedField reservedField) + { + UnhandledNodeType("ReservedField"); + } + + public virtual void Visit(AmlParser.AccessField accessField) + { + UnhandledNodeType("AccessField"); + } + + public virtual void Visit(AmlParser.DefCreateBitField defCreateBitField) + { + UnhandledNodeType("DefCreateBitField"); + } + + public virtual void Visit(AmlParser.SourceBuff sourceBuff) + { + UnhandledNodeType("SourceBuff"); + } + + public virtual void Visit(AmlParser.BitIndex bitIndex) + { + UnhandledNodeType("BitIndex"); + } + + public virtual void Visit(AmlParser.DefCreateByteField defCreateByteField) + { + UnhandledNodeType("DefCreateByteField"); + } + + public virtual void Visit(AmlParser.ByteIndex byteIndex) + { + UnhandledNodeType("ByteIndex"); + } + + public virtual void Visit(AmlParser.DefCreateDWordField defCreateDWordField) + { + UnhandledNodeType("DefCreateDWordField"); + } + + public virtual void Visit(AmlParser.DefCreateField defCreateField) + { + UnhandledNodeType("DefCreateField"); + } + + public virtual void Visit(AmlParser.NumBits numBits) + { + UnhandledNodeType("NumBits"); + } + + public virtual void Visit(AmlParser.DefCreateQWordField defCreateQWordField) + { + UnhandledNodeType("DefCreateQWordField"); + } + + public virtual void Visit(AmlParser.DefCreateWordField defCreateWordField) + { + UnhandledNodeType("DefCreateWordField"); + } + + public virtual void Visit(AmlParser.DefDataRegion defDataRegion) + { + UnhandledNodeType("DefDataRegion"); + } + + public virtual void Visit(AmlParser.DefDevice defDevice) + { + UnhandledNodeType("DefDevice"); + } + + public virtual void Visit(AmlParser.DefEvent defEvent) + { + UnhandledNodeType("DefEvent"); + } + + public virtual void Visit(AmlParser.DefField defField) + { + UnhandledNodeType("DefField"); + } + + public virtual void Visit(AmlParser.DefIndexField defIndexField) + { + UnhandledNodeType("DefIndexField"); + } + + public virtual void Visit(AmlParser.DefMethod defMethod) + { + UnhandledNodeType("DefMethod"); + } + + public virtual void Visit(AmlParser.MethodFlags methodFlags) + { + UnhandledNodeType("MethodFlags"); + } + + public virtual void Visit(AmlParser.DefMutex defMutex) + { + UnhandledNodeType("DefMutex"); + } + + public virtual void Visit(AmlParser.SyncFlags syncFlags) + { + UnhandledNodeType("SyncFlags"); + } + + public virtual void Visit(AmlParser.DefOpRegion defOpRegion) + { + UnhandledNodeType("DefOpRegion"); + } + + public virtual void Visit(AmlParser.RegionSpace regionSpace) + { + UnhandledNodeType("RegionSpace"); + } + + public virtual void Visit(AmlParser.RegionOffset regionOffset) + { + UnhandledNodeType("RegionOffset"); + } + + public virtual void Visit(AmlParser.RegionLen regionLen) + { + UnhandledNodeType("RegionLen"); + } + + public virtual void Visit(AmlParser.DefPowerRes defPowerRes) + { + UnhandledNodeType("DefPowerRes"); + } + + public virtual void Visit(AmlParser.SystemLevel systemLevel) + { + UnhandledNodeType("SystemLevel"); + } + + public virtual void Visit(AmlParser.ResourceOrder resourceOrder) + { + UnhandledNodeType("ResourceOrder"); + } + + public virtual void Visit(AmlParser.DefProcessor defProcessor) + { + UnhandledNodeType("DefProcessor"); + } + + public virtual void Visit(AmlParser.ProcID procID) + { + UnhandledNodeType("ProcID"); + } + + public virtual void Visit(AmlParser.PblkAddr pblkAddr) + { + UnhandledNodeType("PblkAddr"); + } + + public virtual void Visit(AmlParser.PblkLen pblkLen) + { + UnhandledNodeType("PblkLen"); + } + + public virtual void Visit(AmlParser.DefThermalZone defThermalZone) + { + UnhandledNodeType("DefThermalZone"); + } + + public virtual void Visit(AmlParser.DefBreak defBreak) + { + UnhandledNodeType("DefBreak"); + } + + public virtual void Visit(AmlParser.DefBreakPoint defBreakPoint) + { + UnhandledNodeType("DefBreakPoint"); + } + + public virtual void Visit(AmlParser.DefContinue defContinue) + { + UnhandledNodeType("DefContinue"); + } + + public virtual void Visit(AmlParser.DefElse defElse) + { + UnhandledNodeType("DefElse"); + } + + public virtual void Visit(AmlParser.DefFatal defFatal) + { + UnhandledNodeType("DefFatal"); + } + + public virtual void Visit(AmlParser.FatalType fatalType) + { + UnhandledNodeType("FatalType"); + } + + public virtual void Visit(AmlParser.FatalCode fatalCode) + { + UnhandledNodeType("FatalCode"); + } + + public virtual void Visit(AmlParser.FatalArg fatalArg) + { + UnhandledNodeType("FatalArg"); + } + + public virtual void Visit(AmlParser.DefIfElse defIfElse) + { + UnhandledNodeType("DefIfElse"); + } + + public virtual void Visit(AmlParser.Predicate predicate) + { + UnhandledNodeType("Predicate"); + } + + public virtual void Visit(AmlParser.DefLoad defLoad) + { + UnhandledNodeType("DefLoad"); + } + + public virtual void Visit(AmlParser.DDBHandleObject dDBHandleObject) + { + UnhandledNodeType("DDBHandleObject"); + } + + public virtual void Visit(AmlParser.DefNoop defNoop) + { + UnhandledNodeType("DefNoop"); + } + + public virtual void Visit(AmlParser.DefNotify defNotify) + { + UnhandledNodeType("DefNotify"); + } + + public virtual void Visit(AmlParser.NotifyObject notifyObject) + { + UnhandledNodeType("NotifyObject"); + } + + public virtual void Visit(AmlParser.NotifyValue notifyValue) + { + UnhandledNodeType("NotifyValue"); + } + + public virtual void Visit(AmlParser.DefRelease defRelease) + { + UnhandledNodeType("DefRelease"); + } + + public virtual void Visit(AmlParser.MutexObject mutexObject) + { + UnhandledNodeType("MutexObject"); + } + + public virtual void Visit(AmlParser.DefReset defReset) + { + UnhandledNodeType("DefReset"); + } + + public virtual void Visit(AmlParser.EventObject eventObject) + { + UnhandledNodeType("EventObject"); + } + + public virtual void Visit(AmlParser.DefReturn defReturn) + { + UnhandledNodeType("DefReturn"); + } + + public virtual void Visit(AmlParser.ArgObject argObject) + { + UnhandledNodeType("ArgObject"); + } + + public virtual void Visit(AmlParser.DefSignal defSignal) + { + UnhandledNodeType("DefSignal"); + } + + public virtual void Visit(AmlParser.DefSleep defSleep) + { + UnhandledNodeType("DefSleep"); + } + + public virtual void Visit(AmlParser.MsecTime msecTime) + { + UnhandledNodeType("MsecTime"); + } + + public virtual void Visit(AmlParser.DefStall defStall) + { + UnhandledNodeType("DefStall"); + } + + public virtual void Visit(AmlParser.UsecTime usecTime) + { + UnhandledNodeType("UsecTime"); + } + + public virtual void Visit(AmlParser.DefUnload defUnload) + { + UnhandledNodeType("DefUnload"); + } + + public virtual void Visit(AmlParser.DefWhile defWhile) + { + UnhandledNodeType("DefWhile"); + } + + public virtual void Visit(AmlParser.DefAcquire defAcquire) + { + UnhandledNodeType("DefAcquire"); + } + + public virtual void Visit(AmlParser.TimeOut timeOut) + { + UnhandledNodeType("TimeOut"); + } + + public virtual void Visit(AmlParser.DefAdd defAdd) + { + UnhandledNodeType("DefAdd"); + } + + public virtual void Visit(AmlParser.Operand operand) + { + UnhandledNodeType("Operand"); + } + + public virtual void Visit(AmlParser.DefAnd defAnd) + { + UnhandledNodeType("DefAnd"); + } + + public virtual void Visit(AmlParser.DefBuffer defBuffer) + { + UnhandledNodeType("DefBuffer"); + } + + public virtual void Visit(AmlParser.BufferSize bufferSize) + { + UnhandledNodeType("BufferSize"); + } + + public virtual void Visit(AmlParser.DefConcat defConcat) + { + UnhandledNodeType("DefConcat"); + } + + public virtual void Visit(AmlParser.Data data) + { + UnhandledNodeType("Data"); + } + + public virtual void Visit(AmlParser.DefConcatRes defConcatRes) + { + UnhandledNodeType("DefConcatRes"); + } + + public virtual void Visit(AmlParser.BufData bufData) + { + UnhandledNodeType("BufData"); + } + + public virtual void Visit(AmlParser.DefCondRefOf defCondRefOf) + { + UnhandledNodeType("DefCondRefOf"); + } + + public virtual void Visit(AmlParser.DefCopyObject defCopyObject) + { + UnhandledNodeType("DefCopyObject"); + } + + public virtual void Visit(AmlParser.DefDecrement defDecrement) + { + UnhandledNodeType("DefDecrement"); + } + + public virtual void Visit(AmlParser.DefDerefOf defDerefOf) + { + UnhandledNodeType("DefDerefOf"); + } + + public virtual void Visit(AmlParser.ObjReference objReference) + { + UnhandledNodeType("ObjReference"); + } + + public virtual void Visit(AmlParser.DefDivide defDivide) + { + UnhandledNodeType("DefDivide"); + } + + public virtual void Visit(AmlParser.Dividend dividend) + { + UnhandledNodeType("Dividend"); + } + + public virtual void Visit(AmlParser.Divisor divisor) + { + UnhandledNodeType("Divisor"); + } + + public virtual void Visit(AmlParser.Remainder remainder) + { + UnhandledNodeType("Remainder"); + } + + public virtual void Visit(AmlParser.Quotient quotient) + { + UnhandledNodeType("Quotient"); + } + + public virtual void Visit(AmlParser.DefFindSetLeftBit defFindSetLeftBit) + { + UnhandledNodeType("DefFindSetLeftBit"); + } + + public virtual void Visit(AmlParser.DefFindSetRightBit defFindSetRightBit) + { + UnhandledNodeType("DefFindSetRightBit"); + } + + public virtual void Visit(AmlParser.DefFromBCD defFromBCD) + { + UnhandledNodeType("DefFromBCD"); + } + + public virtual void Visit(AmlParser.BCDValue bCDValue) + { + UnhandledNodeType("BCDValue"); + } + + public virtual void Visit(AmlParser.DefIncrement defIncrement) + { + UnhandledNodeType("DefIncrement"); + } + + public virtual void Visit(AmlParser.DefIndex defIndex) + { + UnhandledNodeType("DefIndex"); + } + + public virtual void Visit(AmlParser.BuffPkgStrObj buffPkgStrObj) + { + UnhandledNodeType("BuffPkgStrObj"); + } + + public virtual void Visit(AmlParser.IndexValue indexValue) + { + UnhandledNodeType("IndexValue"); + } + + public virtual void Visit(AmlParser.DefLAnd defLAnd) + { + UnhandledNodeType("DefLAnd"); + } + + public virtual void Visit(AmlParser.DefLEqual defLEqual) + { + UnhandledNodeType("DefLEqual"); + } + + public virtual void Visit(AmlParser.DefLGreater defLGreater) + { + UnhandledNodeType("DefLGreater"); + } + + public virtual void Visit(AmlParser.DefLGreaterEqual defLGreaterEqual) + { + UnhandledNodeType("DefLGreaterEqual"); + } + + public virtual void Visit(AmlParser.DefLLess defLLess) + { + UnhandledNodeType("DefLLess"); + } + + public virtual void Visit(AmlParser.DefLLessEqual defLLessEqual) + { + UnhandledNodeType("DefLLessEqual"); + } + + public virtual void Visit(AmlParser.DefLNot defLNot) + { + UnhandledNodeType("DefLNot"); + } + + public virtual void Visit(AmlParser.DefLNotEqual defLNotEqual) + { + UnhandledNodeType("DefLNotEqual"); + } + + public virtual void Visit(AmlParser.DefLoadTable defLoadTable) + { + UnhandledNodeType("DefLoadTable"); + } + + public virtual void Visit(AmlParser.DefLOr defLOr) + { + UnhandledNodeType("DefLOr"); + } + + public virtual void Visit(AmlParser.DefMatch defMatch) + { + UnhandledNodeType("DefMatch"); + } + + public virtual void Visit(AmlParser.SearchPkg searchPkg) + { + UnhandledNodeType("SearchPkg"); + } + + public virtual void Visit(AmlParser.StartIndex startIndex) + { + UnhandledNodeType("StartIndex"); + } + + public virtual void Visit(AmlParser.DefMid defMid) + { + UnhandledNodeType("DefMid"); + } + + public virtual void Visit(AmlParser.MidObj midObj) + { + UnhandledNodeType("MidObj"); + } + + public virtual void Visit(AmlParser.DefMod defMod) + { + UnhandledNodeType("DefMod"); + } + + public virtual void Visit(AmlParser.DefMultiply defMultiply) + { + UnhandledNodeType("DefMultiply"); + } + + public virtual void Visit(AmlParser.DefNAnd defNAnd) + { + UnhandledNodeType("DefNAnd"); + } + + public virtual void Visit(AmlParser.DefNOr defNOr) + { + UnhandledNodeType("DefNOr"); + } + + public virtual void Visit(AmlParser.DefNot defNot) + { + UnhandledNodeType("DefNot"); + } + + public virtual void Visit(AmlParser.DefObjectType defObjectType) + { + UnhandledNodeType("DefObjectType"); + } + + public virtual void Visit(AmlParser.DefOr defOr) + { + UnhandledNodeType("DefOr"); + } + + public virtual void Visit(AmlParser.DefPackage defPackage) + { + UnhandledNodeType("DefPackage"); + } + + public virtual void Visit(AmlParser.DefVarPackage defVarPackage) + { + UnhandledNodeType("DefVarPackage"); + } + + public virtual void Visit(AmlParser.NumElements numElements) + { + UnhandledNodeType("NumElements"); + } + + public virtual void Visit(AmlParser.VarNumElements varNumElements) + { + UnhandledNodeType("VarNumElements"); + } + + public virtual void Visit(AmlParser.DefRefOf defRefOf) + { + UnhandledNodeType("DefRefOf"); + } + + public virtual void Visit(AmlParser.DefShiftLeft defShiftLeft) + { + UnhandledNodeType("DefShiftLeft"); + } + + public virtual void Visit(AmlParser.ShiftCount shiftCount) + { + UnhandledNodeType("ShiftCount"); + } + + public virtual void Visit(AmlParser.DefShiftRight defShiftRight) + { + UnhandledNodeType("DefShiftRight"); + } + + public virtual void Visit(AmlParser.DefSizeOf defSizeOf) + { + UnhandledNodeType("DefSizeOf"); + } + + public virtual void Visit(AmlParser.DefStore defStore) + { + UnhandledNodeType("DefStore"); + } + + public virtual void Visit(AmlParser.DefSubtract defSubtract) + { + UnhandledNodeType("DefSubtract"); + } + + public virtual void Visit(AmlParser.DefTimer defTimer) + { + UnhandledNodeType("DefTimer"); + } + + public virtual void Visit(AmlParser.DefToBCD defToBCD) + { + UnhandledNodeType("DefToBCD"); + } + + public virtual void Visit(AmlParser.DefToBuffer defToBuffer) + { + UnhandledNodeType("DefToBuffer"); + } + + public virtual void Visit(AmlParser.DefToDecimalString defToDecimalString) + { + UnhandledNodeType("DefToDecimalString"); + } + + public virtual void Visit(AmlParser.DefToHexString defToHexString) + { + UnhandledNodeType("DefToHexString"); + } + + public virtual void Visit(AmlParser.DefToInteger defToInteger) + { + UnhandledNodeType("DefToInteger"); + } + + public virtual void Visit(AmlParser.DefToString defToString) + { + UnhandledNodeType("DefToString"); + } + + public virtual void Visit(AmlParser.LengthArg lengthArg) + { + UnhandledNodeType("LengthArg"); + } + + public virtual void Visit(AmlParser.DefWait defWait) + { + UnhandledNodeType("DefWait"); + } + + public virtual void Visit(AmlParser.DefXOr defXOr) + { + UnhandledNodeType("DefXOr"); + } + + public virtual void Visit(AmlParser.ArgObj argObj) + { + UnhandledNodeType("ArgObj"); + } + + public virtual void Visit(AmlParser.LocalObj localObj) + { + UnhandledNodeType("LocalObj"); + } + + public virtual void Visit(AmlParser.DebugObj debugObj) + { + UnhandledNodeType("DebugObj"); + } + } +} diff --git a/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AmlStackIR.cs b/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AmlStackIR.cs new file mode 100644 index 0000000..8729e52 --- /dev/null +++ b/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/AmlStackIR.cs @@ -0,0 +1,1580 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Microsoft Research Singularity +// + +// The AML stack IR is a proprietary linearized representation of an AML method. +// By converting the AML to a linearized IR, we can deal with cooperative +// multithreading more easily, because it's easier to represent and explicitly +// store the current state of the interpreter. This transformation also provides +// an opportunity to eliminate redundancies in the original representation. + +using System; +using System.Collections; +using System.Diagnostics; + +using Node = Microsoft.Singularity.Hal.Acpi.AcpiNamespace.Node; +using NodePath = Microsoft.Singularity.Hal.Acpi.AcpiNamespace.NodePath; +using AbsoluteNodePath = Microsoft.Singularity.Hal.Acpi.AcpiNamespace.AbsoluteNodePath; + +using Microsoft.Singularity.Hal.Acpi.AcpiObject; +using Microsoft.Singularity.Hal.Acpi.StackIR; +using Microsoft.Singularity.Hal.Acpi.AmlParserUnions; + +namespace Microsoft.Singularity.Hal.Acpi.StackIR +{ + public abstract class StackIRNode + { + public abstract void Accept(StackIRNodeVisitor v); + } + + public class Jump : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + + int target; + + public int Target { + get + { + return target; + } + set + { + target = value; + } + } + } + + public class JumpIfNonZero : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + + int thenTarget; + + public int ThenTarget { + get + { + return thenTarget; + } + set + { + thenTarget = value; + } + } + } + + public class PushArgObj : StackIRNode + { + int argNum; + + public PushArgObj(int argNum) + { + this.argNum = argNum; + } + + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + + public int ArgNum + { + get + { + return argNum; + } + } + } + + public class PushLocalObj : StackIRNode + { + int localNum; + + public PushLocalObj(int localNum) + { + this.localNum = localNum; + } + + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + + public int LocalNum + { + get + { + return localNum; + } + } + } + + public class PushDebugObj : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class PushNodePath : StackIRNode + { + NodePath value; + + public PushNodePath(NodePath value) + { + this.value = value; + } + + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + + public NodePath Value + { + get + { + return value; + } + } + } + + public class PushConst : StackIRNode + { + AcpiObject.AcpiObject value; + + public PushConst(AcpiObject.AcpiObject value) + { + this.value = value; + } + + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + + public AcpiObject.AcpiObject Value + { + get + { + return value; + } + } + } + + public class Discard : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + // Store a value in a location *and* push it on the stack + public class Store : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class MethodCall : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class Index : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class ShiftLeft : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class ShiftRight : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class Concatenate : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class Add : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class Subtract : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class Multiply : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class Divide : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class Remainder : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class FindSetLeftBit : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class FindSetRightBit : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class SizeOf : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class LogicalOp : StackIRNode + { + public enum Op + { + Less, + LessEq, + Equal, + Greater, + GreaterEq, + And, + Or, + Not + } + + Op op; + + public LogicalOp(Op op) + { + this.op = op; + } + + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + + public Op Operator + { + get + { + return op; + } + } + } + + public class BitOp : StackIRNode + { + public enum Op + { + And, + Or, + NAnd, + NOr, + Not, + XOr + } + + Op op; + + public BitOp(Op op) + { + this.op = op; + } + + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + + public Op Operator + { + get + { + return op; + } + } + } + + public class Return : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class CreateField : StackIRNode + { + NodePath nodePath; + + public CreateField(NodePath nodePath) + { + this.nodePath = nodePath; + } + + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + + public NodePath NodePath + { + get + { + return nodePath; + } + } + } + + public class DefBuffer : StackIRNode + { + byte[] initializer; + + public DefBuffer(byte[] initializer) + { + this.initializer = initializer; + } + + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + + public byte[] Initializer + { + get + { + return (byte[])initializer.Clone(); + } + } + } + + public class DefName : StackIRNode + { + NodePath nodePath; + + public DefName(NodePath nodePath) + { + this.nodePath = nodePath; + } + + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + + public NodePath NodePath + { + get + { + return nodePath; + } + } + } + + public class Load : StackIRNode + { + NodePath nodePath; + + public Load(NodePath nodePath) + { + this.nodePath = nodePath; + } + + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + + public NodePath NodePath + { + get + { + return nodePath; + } + } + } + + public class Stall : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class Match : StackIRNode + { + AmlParser.MatchOpcode matchOp1, matchOp2; + + public Match(AmlParser.MatchOpcode matchOp1, AmlParser.MatchOpcode matchOp2) + { + this.matchOp1 = matchOp1; + this.matchOp2 = matchOp2; + } + + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class DerefOf : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class Package : StackIRNode + { + int numElements; + + public Package(int numElements) + { + this.numElements = numElements; + } + + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class Notify : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class Sleep : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class RefOf : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class JumpIfNodePathExists : StackIRNode + { + private NodePath nodePath; + + public JumpIfNodePathExists(NodePath nodePath) + { + this.nodePath = nodePath; + } + + public NodePath NodePath + { + get + { + return nodePath; + } + } + + int thenTarget; + + public int ThenTarget { + get + { + return thenTarget; + } + set + { + thenTarget = value; + } + } + + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class OperationRegion : StackIRNode + { + RegionSpace operationSpace; + NodePath nodePath; + + public OperationRegion(RegionSpace operationSpace, NodePath nodePath) + { + this.operationSpace = operationSpace; + this.nodePath = nodePath; + } + + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + + public RegionSpace OperationSpace + { + get + { + return operationSpace; + } + } + + public NodePath NodePath + { + get + { + return nodePath; + } + } + } + + public class Field : StackIRNode + { + AmlParser.FieldFlags fieldFlags; + NodePath nodePath; + FieldElement[] fieldElements; + + public Field(AmlParser.FieldFlags fieldFlags, NodePath nodePath, FieldElement[] fieldElements) + { + this.fieldFlags = fieldFlags; + this.nodePath = nodePath; + this.fieldElements = fieldElements; + } + + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + + public AmlParser.FieldFlags FieldFlags + { + get + { + return fieldFlags; + } + } + + public NodePath NodePath + { + get + { + return nodePath; + } + } + + public FieldElement[] FieldElements + { + get + { + return fieldElements; + } + } + } + + public class Acquire : StackIRNode + { + ushort timeout; + + public Acquire(ushort timeout) + { + this.timeout = timeout; + } + + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class Release : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class ToBuffer : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class ToInteger : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public class ToString : StackIRNode + { + public override void Accept(StackIRNodeVisitor v) + { + v.Visit(this); + } + } + + public abstract class StackIRNodeVisitor + { + public abstract void Visit(Jump node); + public abstract void Visit(JumpIfNonZero node); + public abstract void Visit(JumpIfNodePathExists node); + public abstract void Visit(PushArgObj node); + public abstract void Visit(PushLocalObj node); + public abstract void Visit(PushDebugObj node); + public abstract void Visit(PushNodePath node); + public abstract void Visit(PushConst node); + public abstract void Visit(Discard node); + public abstract void Visit(Store node); + public abstract void Visit(MethodCall node); + public abstract void Visit(Index node); + public abstract void Visit(Add node); + public abstract void Visit(Subtract node); + public abstract void Visit(Multiply node); + public abstract void Visit(Divide node); + public abstract void Visit(Remainder node); + public abstract void Visit(ShiftLeft node); + public abstract void Visit(ShiftRight node); + public abstract void Visit(Concatenate node); + public abstract void Visit(LogicalOp node); + public abstract void Visit(BitOp node); + public abstract void Visit(Return node); + public abstract void Visit(CreateField node); + public abstract void Visit(FindSetLeftBit node); + public abstract void Visit(FindSetRightBit node); + public abstract void Visit(SizeOf node); + public abstract void Visit(DefName node); + public abstract void Visit(Load node); + public abstract void Visit(Stall node); + public abstract void Visit(Match node); + public abstract void Visit(DerefOf node); + public abstract void Visit(Package node); + public abstract void Visit(DefBuffer node); + public abstract void Visit(Notify node); + public abstract void Visit(Sleep node); + public abstract void Visit(RefOf node); + public abstract void Visit(OperationRegion node); + public abstract void Visit(Field node); + public abstract void Visit(Acquire node); + public abstract void Visit(Release node); + public abstract void Visit(ToBuffer node); + public abstract void Visit(ToInteger node); + public abstract void Visit(ToString node); + } +} + +namespace Microsoft.Singularity.Hal.Acpi +{ + public class AmlToStackIRException : Exception + { + // TODO + } + + public class AmlToStackIRVisitor : AmlParserNodeVisitor + { + private class StackIRNodeList + { + ArrayList list = new ArrayList(); + + public void Add(StackIRNode node) { + list.Add(node); + } + + public StackIRNode[] ToArray() { + return (StackIRNode[])list.ToArray(typeof(StackIRNode)); + } + + public StackIRNode Last + { + get + { + return (StackIRNode)list[list.Count - 1]; + } + } + + public int Count + { + get + { + return list.Count; + } + } + } + + StackIRNodeList result = new StackIRNodeList(); + + public StackIRNode[] Result + { + get + { + // Tack on a return of an uninitialized object at the end + // so it doesn't run off the end. + result.Add(new PushConst(new UninitializedObject())); + result.Add(new Return()); + + return result.ToArray(); + } + } + + public override void UnhandledNodeType(string nodeTypeName) + { + throw new AmlToStackIRException(); + } + + public void VisitSequence(AmlParserNode[] nodeSequence) + { + if (nodeSequence == null) { + return; + } + foreach (AmlParserNode node in nodeSequence) { + node.Accept(this); + DiscardResults(); + } + } + + public void DiscardResults() + { + // Certain operations produce results in contexts where we + // don't need them. This throws them away if so. + if (result.Count > 0 && result.Last is Store) { + result.Add(new Discard()); + } + } + + public void VisitSequenceReverse(AmlParserNode[] nodeSequence) + { + for(int i = nodeSequence.Length - 1; i >= 0; i--) { + nodeSequence[i].Accept(this); + } + } + + public override void Visit(AmlParser.DefIfElse defIfElse) + { + // We create a sequence like this: + // (push predicate) + // jump if nonzero to thenBranch + // (else branch instructions) + // jump to end + // elseBranch: + // (then branch instructions) + // end: + + defIfElse.predicate.Accept(this); + JumpIfNonZero thenJump = new JumpIfNonZero(); + result.Add(thenJump); + defIfElse.defElse.Accept(this); + Jump jumpOverThenBrach = new Jump(); + result.Add(jumpOverThenBrach); + thenJump.ThenTarget = result.Count; + VisitSequence(defIfElse.termList); + jumpOverThenBrach.Target = result.Count; + } + + public override void Visit(AmlParser.Predicate predicate) + { + predicate.integer.Accept(this); + } + + public override void Visit(AmlParser.DefElse defElse) + { + VisitSequence(defElse.termList); + } + + public override void Visit(AmlParser.ArgObj argObj) + { + result.Add(new PushArgObj(argObj.op)); + } + + public override void Visit(AmlParser.LocalObj localObj) + { + result.Add(new PushLocalObj(localObj.op)); + } + + public override void Visit(AmlParser.IndexValue indexValue) + { + indexValue.integer.Accept(this); + } + + public override void Visit(AmlParser.ShiftCount shiftCount) + { + shiftCount.integer.Accept(this); + } + + public override void Visit(AmlParser.BuffPkgStrObj buffPkgStrObj) + { + buffPkgStrObj.termArg.Accept(this); + } + + public override void Visit(AmlParser.Target target) + { + target.superName.Accept(this); + } + + public override void Visit(AmlParser.Operand operand) + { + operand.integer.Accept(this); + } + + public override void Visit(AmlParser.ArgObject argObject) + { + argObject.dataRefObject.Accept(this); + } + + public override void Visit(AmlParser.BitIndex bitIndex) + { + bitIndex.integer.Accept(this); + } + + public override void Visit(AmlParser.ByteIndex byteIndex) + { + byteIndex.integer.Accept(this); + } + + public override void Visit(AmlParser.SourceBuff sourceBuff) + { + sourceBuff.buffer.Accept(this); + } + + public override void Visit(AmlParser.StartIndex startIndex) + { + startIndex.integer.Accept(this); + } + + public override void Visit(AmlParser.ObjReference objReference) + { + objReference.termArg.Accept(this); + } + + public override void Visit(AmlParser.SearchPkg searchPkg) + { + searchPkg.package.Accept(this); + } + + public override void Visit(AmlParser.NumBits numBits) + { + numBits.integer.Accept(this); + } + + public override void Visit(AmlParser.NotifyValue notifyValue) + { + notifyValue.integer.Accept(this); + } + + public override void Visit(AmlParser.NotifyObject notifyObject) + { + notifyObject.superName.Accept(this); + } + + public override void Visit(AmlParser.MsecTime msecTime) + { + msecTime.integer.Accept(this); + } + + public override void Visit(AmlParser.RegionLen regionLen) + { + regionLen.integer.Accept(this); + } + + public override void Visit(AmlParser.RegionOffset regionOffset) + { + regionOffset.integer.Accept(this); + } + + public override void Visit(AmlParser.MutexObject mutexObject) + { + mutexObject.superName.Accept(this); + } + + public override void Visit(AmlParser.LengthArg lengthArg) + { + lengthArg.integer.Accept(this); + } + + public override void Visit(AmlParser.DDBHandleObject ddbHandleObject) + { + ddbHandleObject.superName.Accept(this); + } + + public override void Visit(AmlParser.UsecTime usecTime) + { + usecTime.byteData.Accept(this); + } + + public override void Visit(AmlParser.Dividend dividend) + { + dividend.integer.Accept(this); + } + + public override void Visit(AmlParser.Divisor divisor) + { + divisor.integer.Accept(this); + } + + public override void Visit(AmlParser.Remainder remainder) + { + remainder.target.Accept(this); + } + + public override void Visit(AmlParser.Quotient quotient) + { + quotient.target.Accept(this); + } + + public override void Visit(AmlParser.Data data) + { + data.computationalData.Accept(this); + } + + public override void Visit(AmlParser.DefStore defStore) + { + defStore.superName.Accept(this); + defStore.termArg.Accept(this); + result.Add(new Store()); + } + + public override void Visit(AmlParser.NameString nameString) + { + result.Add(new PushNodePath(nameString.nodePath)); + } + + public override void Visit(ComputationalData computationalData) + { + switch (computationalData.Tag) { + case ComputationalData.TagValue.ByteConst: + byte byteConst = computationalData.GetAsByteConst(); + result.Add(new PushConst(new AcpiObject.Integer(byteConst))); + break; + case ComputationalData.TagValue.WordConst: + UInt16 wordConst = computationalData.GetAsWordConst(); + result.Add(new PushConst(new AcpiObject.Integer(wordConst))); + break; + case ComputationalData.TagValue.DWordConst: + UInt32 dWordConst = computationalData.GetAsDWordConst(); + result.Add(new PushConst(new AcpiObject.Integer(dWordConst))); + break; + case ComputationalData.TagValue.QWordConst: + UInt64 qWordConst = computationalData.GetAsQWordConst(); + result.Add(new PushConst(new AcpiObject.Integer(qWordConst))); + break; + case ComputationalData.TagValue.StringConst: + string stringConst = computationalData.GetAsStringConst(); + result.Add(new PushConst(new AcpiObject.String(stringConst))); + break; + case ComputationalData.TagValue.ConstObj: + AmlParser.ConstObj constObj = computationalData.GetAsConstObj(); + switch (constObj.op) { + case AmlParser.ZeroOp: + result.Add(new PushConst(AcpiObject.IntegerConstant.Zero)); + break; + case AmlParser.OneOp: + result.Add(new PushConst(AcpiObject.IntegerConstant.One)); + break; + case AmlParser.OnesOp: + result.Add(new PushConst(AcpiObject.IntegerConstant.Ones)); + break; + } + break; + case ComputationalData.TagValue.RevisionOp: + result.Add(new PushConst(AcpiObject.IntegerConstant.Revision)); + break; + case ComputationalData.TagValue.DefBuffer: + AmlParser.DefBuffer defBuffer = computationalData.GetAsDefBuffer(); + defBuffer.Accept(this); + break; + default: + Debug.Assert(false, "Unhandled alternative in switch over 'ComputationalData'"); + break; + } + } + + public override void Visit(AmlParser.DefName defName) + { + defName.dataRefObject.Accept(this); + result.Add(new DefName(defName.nameString.nodePath)); + } + + public override void Visit(AmlParser.UserTermObj userTermObj) + { + if (userTermObj.termArgList.Length > 0) { + VisitSequenceReverse(userTermObj.termArgList); + result.Add(new PushNodePath(userTermObj.nameString.nodePath)); + result.Add(new MethodCall()); + } + else { + // The interpreter will determine at runtime if this is a method or not + result.Add(new PushNodePath(userTermObj.nameString.nodePath)); + } + } + + public override void Visit(AmlParser.DefCreateField defCreateField) + { + defCreateField.numBits.Accept(this); + defCreateField.bitIndex.Accept(this); + defCreateField.sourceBuff.Accept(this); + result.Add(new CreateField(defCreateField.nameString.nodePath)); + } + + public override void Visit(AmlParser.DefCreateBitField defCreateBitField) + { + result.Add(new PushConst(new AcpiObject.Integer(1))); // number of bits + defCreateBitField.bitIndex.Accept(this); + defCreateBitField.sourceBuff.Accept(this); + result.Add(new CreateField(defCreateBitField.nameString.nodePath)); + } + + public override void Visit(AmlParser.DefCreateByteField defCreateByteField) + { + result.Add(new PushConst(new AcpiObject.Integer(8))); // number of bits + + // Multiply byte index by 8 to get bit index + defCreateByteField.byteIndex.Accept(this); + result.Add(new PushConst(new AcpiObject.Integer(8))); + result.Add(new Multiply()); + + defCreateByteField.sourceBuff.Accept(this); + result.Add(new CreateField(defCreateByteField.nameString.nodePath)); + } + + public override void Visit(AmlParser.DefCreateWordField defCreateWordField) + { + result.Add(new PushConst(new AcpiObject.Integer(16))); // number of bits + + // Multiply byte index by 8 to get bit index + defCreateWordField.byteIndex.Accept(this); + result.Add(new PushConst(new AcpiObject.Integer(8))); + result.Add(new Multiply()); + + defCreateWordField.sourceBuff.Accept(this); + result.Add(new CreateField(defCreateWordField.nameString.nodePath)); + } + + public override void Visit(AmlParser.DefCreateDWordField defCreateDWordField) + { + result.Add(new PushConst(new AcpiObject.Integer(32))); // number of bits + + // Multiply byte index by 8 to get bit index + defCreateDWordField.byteIndex.Accept(this); + result.Add(new PushConst(new AcpiObject.Integer(8))); + result.Add(new Multiply()); + + defCreateDWordField.sourceBuff.Accept(this); + result.Add(new CreateField(defCreateDWordField.nameString.nodePath)); + } + + public override void Visit(AmlParser.DefCreateQWordField defCreateQWordField) + { + result.Add(new PushConst(new AcpiObject.Integer(64))); // number of bits + + // Multiply byte index by 8 to get bit index + defCreateQWordField.byteIndex.Accept(this); + result.Add(new PushConst(new AcpiObject.Integer(8))); + result.Add(new Multiply()); + + defCreateQWordField.sourceBuff.Accept(this); + result.Add(new CreateField(defCreateQWordField.nameString.nodePath)); + } + + public override void Visit(AmlParser.DefBuffer defBuffer) + { + defBuffer.bufferSize.integer.Accept(this); + result.Add(new DefBuffer(defBuffer.byteList)); + } + + public override void Visit(AmlParser.DefDecrement defDecrement) + { + defDecrement.superName.Accept(this); // target + + result.Add(new PushConst(new AcpiObject.Integer(1))); + defDecrement.superName.Accept(this); // left + result.Add(new Subtract()); + + result.Add(new Store()); + } + + public override void Visit(AmlParser.DefIncrement defIncrement) + { + defIncrement.superName.Accept(this); // target + + result.Add(new PushConst(new AcpiObject.Integer(1))); + defIncrement.superName.Accept(this); // left + result.Add(new Add()); + + result.Add(new Store()); + } + + public override void Visit(AmlParser.DefPackage defPackage) + { + VisitSequenceReverse(defPackage.packageElementList); + result.Add(new Microsoft.Singularity.Hal.Acpi.StackIR.Package( + defPackage.numElements.byteData)); + } + + public override void Visit(AmlParser.DefFindSetLeftBit defFindSetLeftBit) + { + VisitUnaryOperator(defFindSetLeftBit.operand, defFindSetLeftBit.target, new FindSetLeftBit()); + } + + public override void Visit(AmlParser.DefFindSetRightBit defFindSetRightBit) + { + VisitUnaryOperator(defFindSetRightBit.operand, defFindSetRightBit.target, new FindSetRightBit()); + } + + public override void Visit(AmlParser.DefSizeOf defSizeOf) + { + defSizeOf.superName.Accept(this); + result.Add(new SizeOf()); + } + + public override void Visit(AmlParser.DefIndex defIndex) + { + VisitBinaryOperator(defIndex.buffPkgStrObj, defIndex.indexValue, defIndex.target, new Index()); + } + + public override void Visit(AmlParser.DefAdd defAdd) + { + VisitBinaryOperator(defAdd.leftOperand, defAdd.rightOperand, defAdd.target, new Add()); + } + + public override void Visit(AmlParser.DefSubtract defSubtract) + { + VisitBinaryOperator(defSubtract.leftOperand, defSubtract.rightOperand, defSubtract.target, new Subtract()); + } + + public override void Visit(AmlParser.DefMultiply defMultiply) + { + VisitBinaryOperator(defMultiply.leftOperand, defMultiply.rightOperand, defMultiply.target, new Multiply()); + } + + public override void Visit(AmlParser.DefDivide defDivide) + { + const int divisorLocal = AmlStackFrame.FirstReservedLocal; + const int dividendLocal = divisorLocal + 1; + + // Push all operands on stack first, since subexpressions may use the reserved locals + if (!IsNullTarget(defDivide.remainder.target)) { + defDivide.remainder.target.Accept(this); + } + if (!IsNullTarget(defDivide.quotient.target)) { + defDivide.quotient.target.Accept(this); + } + result.Add(new PushLocalObj(divisorLocal)); + defDivide.divisor.Accept(this); + result.Add(new PushLocalObj(dividendLocal)); + defDivide.dividend.Accept(this); + + result.Add(new Store()); // Store dividend expression in dividendLocal + result.Add(new Store()); // store divisor expression in divisorLocal + + if (!IsNullTarget(defDivide.quotient.target)) { + result.Add(new PushLocalObj(divisorLocal)); + result.Add(new PushLocalObj(dividendLocal)); + result.Add(new Divide()); + result.Add(new Store()); // Store quotient in quotient.target + } + + if (!IsNullTarget(defDivide.remainder.target)) { + result.Add(new PushLocalObj(divisorLocal)); + result.Add(new PushLocalObj(dividendLocal)); + result.Add(new Remainder()); + result.Add(new Store()); // Store remainder in remainder.target + } + } + + public override void Visit(AmlParser.DefShiftLeft defShiftLeft) + { + VisitBinaryOperator(defShiftLeft.operand, defShiftLeft.shiftCount, defShiftLeft.target, new ShiftLeft()); + } + + public override void Visit(AmlParser.DefShiftRight defShiftRight) + { + VisitBinaryOperator(defShiftRight.operand, defShiftRight.shiftCount, defShiftRight.target, new ShiftRight()); + } + + public override void Visit(AmlParser.DefConcat defConcat) + { + VisitBinaryOperator(defConcat.leftData, defConcat.rightData, defConcat.target, new Concatenate()); + } + + public override void Visit(AmlParser.DebugObj debugObj) + { + result.Add(new PushDebugObj()); + } + + public override void Visit(AmlParser.DefDerefOf defDerefOf) + { + defDerefOf.objReference.Accept(this); + result.Add(new DerefOf()); + } + + public override void Visit(AmlParser.DefMatch defMatch) + { + defMatch.startIndex.Accept(this); + defMatch.operand2.Accept(this); + defMatch.operand1.Accept(this); + defMatch.searchPkg.Accept(this); + result.Add(new Match(defMatch.matchOpcode1, defMatch.matchOpcode2)); + } + + public override void Visit(AmlParser.DefLEqual defLEqual) + { + defLEqual.rightOperand.Accept(this); + defLEqual.leftOperand.Accept(this); + result.Add(new LogicalOp(LogicalOp.Op.Equal)); + } + + public override void Visit(AmlParser.DefLLess defLLess) + { + defLLess.rightOperand.Accept(this); + defLLess.leftOperand.Accept(this); + result.Add(new LogicalOp(LogicalOp.Op.Less)); + } + + public override void Visit(AmlParser.DefLLessEqual defLLessEqual) + { + defLLessEqual.rightOperand.Accept(this); + defLLessEqual.leftOperand.Accept(this); + result.Add(new LogicalOp(LogicalOp.Op.LessEq)); + } + + public override void Visit(AmlParser.DefLGreater defLGreater) + { + defLGreater.rightOperand.Accept(this); + defLGreater.leftOperand.Accept(this); + result.Add(new LogicalOp(LogicalOp.Op.Greater)); + } + + public override void Visit(AmlParser.DefLGreaterEqual defLGreaterEqual) + { + defLGreaterEqual.rightOperand.Accept(this); + defLGreaterEqual.leftOperand.Accept(this); + result.Add(new LogicalOp(LogicalOp.Op.GreaterEq)); + } + + public override void Visit(AmlParser.DefLAnd defLAnd) + { + defLAnd.rightOperand.Accept(this); + defLAnd.leftOperand.Accept(this); + result.Add(new LogicalOp(LogicalOp.Op.And)); + } + + public override void Visit(AmlParser.DefLOr defLOr) + { + defLOr.rightOperand.Accept(this); + defLOr.leftOperand.Accept(this); + result.Add(new LogicalOp(LogicalOp.Op.Or)); + } + + public override void Visit(AmlParser.DefLNot defLNot) + { + defLNot.operand.Accept(this); + result.Add(new LogicalOp(LogicalOp.Op.Not)); + } + + public override void Visit(AmlParser.DefAnd defAnd) + { + VisitBinaryOperator(defAnd.leftOperand, defAnd.rightOperand, defAnd.target, + new BitOp(BitOp.Op.And)); + } + + public override void Visit(AmlParser.DefOr defOr) + { + VisitBinaryOperator(defOr.leftOperand, defOr.rightOperand, defOr.target, + new BitOp(BitOp.Op.Or)); + } + + public override void Visit(AmlParser.DefNAnd defNAnd) + { + VisitBinaryOperator(defNAnd.leftOperand, defNAnd.rightOperand, defNAnd.target, + new BitOp(BitOp.Op.NAnd)); + } + + public override void Visit(AmlParser.DefNOr defNOr) + { + VisitBinaryOperator(defNOr.leftOperand, defNOr.rightOperand, defNOr.target, + new BitOp(BitOp.Op.NOr)); + } + + public override void Visit(AmlParser.DefNot defNot) + { + VisitUnaryOperator(defNot.operand, defNot.target, + new BitOp(BitOp.Op.Not)); + } + + public override void Visit(AmlParser.DefXOr defXOr) + { + VisitBinaryOperator(defXOr.leftOperand, defXOr.rightOperand, defXOr.target, + new BitOp(BitOp.Op.XOr)); + } + + private void VisitBinaryOperator(AmlParserNode left, AmlParserNode right, + AmlParser.Target target, StackIRNode stackNode) + { + if (IsNullTarget(target)) { + right.Accept(this); + left.Accept(this); + result.Add(stackNode); + } + else { + target.Accept(this); + right.Accept(this); + left.Accept(this); + result.Add(stackNode); + result.Add(new Store()); + } + } + + private void VisitUnaryOperator(AmlParserNode operand, + AmlParser.Target target, StackIRNode stackNode) + { + if (IsNullTarget(target)) { + operand.Accept(this); + result.Add(stackNode); + } + else { + target.Accept(this); + operand.Accept(this); + result.Add(stackNode); + result.Add(new Store()); + } + } + + public bool IsNullTarget(AmlParser.Target target) + { + SuperName superName = target.superName; + if (superName.Tag != SuperName.TagValue.SimpleName) { + return false; + } + SimpleName simpleName = superName.GetAsSimpleName(); + if (simpleName.Tag != SimpleName.TagValue.NameString) { + return false; + } + NodePath nodePath = simpleName.GetAsNameString().nodePath; + return (!nodePath.IsAbsolute && + nodePath.NumParentPrefixes == 0 && + nodePath.NameSegments.Length == 0); + } + + public override void Visit(AmlParser.DefReturn defReturn) + { + defReturn.argObject.Accept(this); + result.Add(new Return()); + } + + public override void Visit(AmlParser.DefWhile defWhile) + { + // We create a sequence like this: + // jump to pushPredicate + // loopback: + // (body instructions) + // pushPredicate: + // (push predicate) + // jump if not zero to loopback + + Jump jumpToPushPredicate = new Jump(); + JumpIfNonZero jumpLoopBack = new JumpIfNonZero(); + + result.Add(jumpToPushPredicate); + jumpLoopBack.ThenTarget = result.Count; + VisitSequence(defWhile.termList); + jumpToPushPredicate.Target = result.Count; + defWhile.predicate.Accept(this); + result.Add(jumpLoopBack); + } + + public override void Visit(AmlParser.DefNotify defNotify) + { + defNotify.notifyValue.Accept(this); + defNotify.notifyObject.Accept(this); + result.Add(new Notify()); + } + + public override void Visit(AmlParser.DefSleep defSleep) + { + defSleep.msecTime.Accept(this); + result.Add(new Sleep()); + } + + public override void Visit(AmlParser.DefCondRefOf defCondRefOf) + { + // We create a sequence like this: + // jump if node path exists to existsBranch + // (push 0) + // jump to end + // existsBranch: + // (execute RefOf instruction and store to target) + // (push 1) + // end: + + // The only SuperName that can fail to exist is a node path + if (defCondRefOf.superName.Tag == SuperName.TagValue.SimpleName && + defCondRefOf.superName.GetAsSimpleName().Tag == SimpleName.TagValue.NameString) { + + JumpIfNodePathExists existsJump = new JumpIfNodePathExists( + defCondRefOf.superName.GetAsSimpleName().GetAsNameString().nodePath); + result.Add(existsJump); + result.Add(new PushConst(new Integer(0))); + Jump jumpOverExistsBranch = new Jump(); + result.Add(jumpOverExistsBranch); + existsJump.ThenTarget = result.Count; + + defCondRefOf.target.Accept(this); + defCondRefOf.superName.Accept(this); + result.Add(new RefOf()); + result.Add(new Store()); + result.Add(new Discard()); + result.Add(new PushConst(new Integer(1))); + + jumpOverExistsBranch.Target = result.Count; + } + else { + defCondRefOf.target.Accept(this); + defCondRefOf.superName.Accept(this); + result.Add(new RefOf()); + result.Add(new Store()); + result.Add(new Discard()); + result.Add(new PushConst(new Integer(1))); + } + } + + public override void Visit(AmlParser.DefOpRegion defOpRegion) + { + defOpRegion.regionLen.Accept(this); + defOpRegion.regionOffset.Accept(this); + result.Add(new StackIR.OperationRegion((RegionSpace)defOpRegion.regionSpace.byteData, + defOpRegion.nameString.nodePath)); + } + + public override void Visit(AmlParser.DefField defField) + { + result.Add(new Field(defField.fieldFlags, defField.nameString.nodePath, defField.fieldList)); + } + + public override void Visit(AmlParser.DefAcquire defAcquire) + { + defAcquire.mutexObject.Accept(this); + result.Add(new Acquire(defAcquire.timeOut.wordData)); + } + + public override void Visit(AmlParser.DefRelease defRelease) + { + defRelease.mutexObject.Accept(this); + result.Add(new Release()); + } + + public override void Visit(AmlParser.DefToBuffer defToBuffer) + { + defToBuffer.target.Accept(this); + defToBuffer.operand.Accept(this); + result.Add(new ToBuffer()); + } + + public override void Visit(AmlParser.DefToInteger defToInteger) + { + defToInteger.target.Accept(this); + defToInteger.operand.Accept(this); + result.Add(new ToInteger()); + } + + public override void Visit(AmlParser.DefToString defToString) + { + defToString.target.Accept(this); + defToString.lengthArg.Accept(this); + defToString.termArg.Accept(this); + result.Add(new ToString()); + } + + public override void Visit(AmlParser.DefNoop defNoop) + { + // Do nothing + } + + public override void Visit(AmlParser.DefLoad defLoad) + { + defLoad.ddbHandleObject.Accept(this); + result.Add(new Load(defLoad.nameString.nodePath)); + } + + public override void Visit(AmlParser.DefStall defStall) + { + defStall.usecTime.Accept(this); + result.Add(new Stall()); + } + } +} diff --git a/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/ReservedObjects.cs b/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/ReservedObjects.cs new file mode 100644 index 0000000..a8350fa --- /dev/null +++ b/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/ReservedObjects.cs @@ -0,0 +1,240 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Microsoft Research Singularity +// + +using System.Diagnostics; +using Microsoft.Singularity.Hal.Acpi.AcpiObject; +using Node = Microsoft.Singularity.Hal.Acpi.AcpiNamespace.Node; +using AbsoluteNodePath = Microsoft.Singularity.Hal.Acpi.AcpiNamespace.AbsoluteNodePath; + +namespace Microsoft.Singularity.Hal.Acpi +{ + public class ReservedObjects + { + AcpiNamespace acpiNamespace; + + public ReservedObjects(AcpiNamespace acpiNamespace) + { + this.acpiNamespace = acpiNamespace; + } + + public void CreateReservedObjects() + { + AddNamespace("_GPE"); + AddNamespace("_PR_"); + AddNamespace("_SB_"); + AddNamespace("_SI_"); + AddNamespace("_TZ_"); + + AddMethod("_BCM", 1, _BCM); + AddMethod("_BLT", 3, _BLT); + AddMethod("_BMC", 1, _BMC); + AddMethod("_BTM", 1, _BTM); + AddMethod("_BTP", 1, _BTP); + AddMethod("_DCK", 1, _DCK); + AddMethod("_DDC", 1, _DDC); + AddMethod("_DOS", 1, _DOS); + AddMethod("_DSM", 4, _DSM); + AddMethod("_DSS", 1, _DSS); + AddMethod("_DSW", 3, _DSW); + AddMethod("_FDM", 2, _FDM); + AddMethod("_MSG", 1, _MSG); + AddMethod("_OSC", 4, _OSC); + AddMethod("_OSI", 1, _OSI); + AddMethod("_OST", 1, _OST); + AddMethod("_PDC", 1, _PDC); + AddMethod("_PSW", 1, _PSW); + AddMethod("_REG", 2, _REG); + AddMethod("_ROM", 2, _ROM); + AddMethod("_SCP", 3, _SCP); + AddMethod("_SDD", 1, _SDD); + AddMethod("_SPD", 1, _SPD); + AddMethod("_SRS", 1, _SRS); + AddMethod("_SST", 1, _SST); + AddMethod("_STM", 3, _STM); + AddMethod("_TPT", 1, _TPT); + } + + private void AddNamespace(string name) + { + AbsoluteNodePath root = AbsoluteNodePath.CreateRoot(); + acpiNamespace.CreateNodeAt(new AbsoluteNodePath(new string[] { name } ), root); + } + + private void AddMethod(string name, byte numArgs, ReservedMethod.AcpiMethodImpl impl) + { + AbsoluteNodePath root = AbsoluteNodePath.CreateRoot(); + Node node = acpiNamespace.CreateNodeAt(new AbsoluteNodePath(new string[] { name } ), root); + node.Value = new ReservedMethod(numArgs, SerializeFlag.NotSerialized, 0 /*syncLevel*/, impl);; + } + + private AcpiObject.AcpiObject _OSI(AcpiObject.AcpiObject[] args) + { + if (args.Length > 0 && args[0].GetAsString().Value == "Singularity") { + return new Integer(1); + } + else { + return new Integer(0); + } + } + + private AcpiObject.AcpiObject _OSC(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _SRS(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _OST(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _DCK(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _REG(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _DSW(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _PSW(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _PDC(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _SST(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _MSG(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _BLT(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _STM(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _SDD(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _FDM(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _DSM(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _BTP(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _BTM(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _BMC(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _SCP(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _TPT(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _DOS(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _ROM(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _SPD(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _BCM(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _DDC(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + + private AcpiObject.AcpiObject _DSS(AcpiObject.AcpiObject[] args) + { + Debug.Assert(false, "Not yet implemented"); + return new AcpiObject.UninitializedObject(); + } + } +} diff --git a/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/ResourceDataType.cs b/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/ResourceDataType.cs new file mode 100644 index 0000000..807eb27 --- /dev/null +++ b/base/Kernel/Singularity.Hal.Acpi/AmlInterpreter/ResourceDataType.cs @@ -0,0 +1,824 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Microsoft Research Singularity +// + +using System; +using System.Collections; +using System.Diagnostics; +using System.Text; + +namespace Microsoft.Singularity.Hal.Acpi +{ + // + // Based on section 6.4 of ACPI Specification 3.0b, Resource Data Types for ACPI, + // this class hierarchy describes and parses resource descriptors that can be encoded + // inside buffers returned by the _CRS, _PRS, and _SRS methods of ACPI objects. + // + + public enum ConsumerProducer + { + ProducesAndConsumes = 0, + Consumes = 1 + } + + /// + /// Small item name tag bits as described in table 6-21 of section + /// 6.4.2 of the ACPI specification 3.0b. + /// + public enum SmallResourceItemName + { + IrqFormatDescriptor = 0x04, + DmaFormatDescriptor = 0x05, + StartDependentFunctionsDescriptor = 0x06, + EndDependentFunctionsDescriptor = 0x07, + IoPortDescriptor = 0x08, + FixedLocationIoPortDescriptor = 0x09, + VendorDefinedDescriptor = 0x0E, + EndTagDescriptor = 0x0F + } + + /// + /// Large item name tag bits as described in table 6-33 of section + /// 6.4.2 of the ACPI specification 3.0b. + /// + public enum LargeResourceItemName + { + MemoryRangeDescriptor24Bit = 0x01, + GenericRegisterDescriptor = 0x02, + VendorDefinedDescriptor = 0x04, + MemoryRangeDescriptor32Bit = 0x05, + FixedLocationMemoryRangeDescriptor32Bit = 0x06, + DwordAddressSpaceDescriptor = 0x07, + WordAddressSpaceDescriptor = 0x08, + ExtendedIrqDescriptor = 0x09, + QwordAddressSpaceDescriptor = 0x0A, + ExtendedAddressSpaceDescriptor = 0x0B + } + + public abstract class ResourceDescriptor + { + public abstract void Accept(ResourceDescriptorVisitor v); + } + + public class AddressSpaceDescriptor : ResourceDescriptor + { + public enum ResourceType : byte + { + MemoryRange = 0, + IoRange = 1, + BusNumberRange = 2 + } + + public enum DecodeType + { + BridgePositivelyDecodes = 0, + BridgeSubtractivelyDecodes = 1 + } + + ResourceType resourceType; + ulong minimum; // _MIN + ulong maximum; // _MAX + ulong length; // _LEN + ulong alignment; // _GRA, _ALN + + bool minimumAddressIsFixed; + bool maximumAddressIsFixed; + ulong addressTranslationOffset; // _TRA + DecodeType decodeType; + ConsumerProducer consumerProducer; + + public ulong Minimum + { + get + { + return minimum; + } + } + + public ulong Maximum + { + get + { + return maximum; + } + } + + public ulong Length + { + get + { + return length; + } + } + + public ConsumerProducer ConsumerProducer + { + get + { + return consumerProducer; + } + } + + public AddressSpaceDescriptor(ResourceType resourceType, ulong minimum, ulong maximum, ulong length, ulong alignment) + { + this.resourceType = resourceType; + this.minimum = minimum; + this.maximum = maximum; + this.length = length; + this.alignment = alignment; + + this.minimumAddressIsFixed = false; + this.maximumAddressIsFixed = false; + this.addressTranslationOffset = 0; + this.decodeType = DecodeType.BridgePositivelyDecodes; + this.consumerProducer = ConsumerProducer.Consumes; + } + + public AddressSpaceDescriptor(ResourceType resourceType, ulong minimum, ulong maximum, + ulong length, ulong alignment, ulong addressTranslationOffset, + bool minimumAddressIsFixed, bool maximumAddressIsFixed, + DecodeType decodeType, ConsumerProducer consumerProducer) + { + this.resourceType = resourceType; + this.minimum = minimum; + this.maximum = maximum; + this.length = length; + this.alignment = alignment; + + this.minimumAddressIsFixed = minimumAddressIsFixed; + this.maximumAddressIsFixed = maximumAddressIsFixed; + this.addressTranslationOffset = addressTranslationOffset; + this.decodeType = decodeType; + this.consumerProducer = consumerProducer; + } + + public override void Accept(ResourceDescriptorVisitor v) + { + v.Visit(this); + } + } + + public class MemoryRangeDescriptor : AddressSpaceDescriptor + { + [Flags] + public enum AcpiMemoryFlags + { + None = 0, + ACPI_MEMORY_UC = 0x0000000000000001, + ACPI_MEMORY_WC = 0x0000000000000002, + ACPI_MEMORY_WT = 0x0000000000000004, + ACPI_MEMORY_WB = 0x0000000000000008, + ACPI_MEMORY_UCE = 0x0000000000000010, + ACPI_MEMORY_NV = 0x0000000000008000 + } + + public enum MemoryToIoTranslation + { + TypeStatic = 0, + TypeTranslation = 1 + } + + public enum AddressRangeAttribute + { + Memory = 0, + Reserved = 1, + Acpi = 2, + Nvs = 3 + } + + public enum CacheableAttribute + { + NonCacheable = 0, + Cacheable = 1, + CacheableWithWriteCombining = 2, + CacheableAndPrefetchable = 3 + } + + public enum WriteStatus + { + ReadOnly = 0, + ReadWrite = 1 + } + + AcpiMemoryFlags acpiMemoryFlags; // _ATT + MemoryToIoTranslation memoryToIoTranslation; // _TTP + AddressRangeAttribute addressRangeAttribute; // _MTP + CacheableAttribute cacheableAttribute; // _MEM + WriteStatus writeStatus; // _RW + + public bool Writable + { + get + { + return writeStatus == WriteStatus.ReadWrite; + } + } + + public MemoryRangeDescriptor(ulong minimum, ulong maximum, ulong length, ulong alignment, WriteStatus writeStatus) + : base(AddressSpaceDescriptor.ResourceType.MemoryRange, minimum, maximum, length, alignment) + { + this.acpiMemoryFlags = AcpiMemoryFlags.None; + this.memoryToIoTranslation = MemoryToIoTranslation.TypeStatic; + this.addressRangeAttribute = AddressRangeAttribute.Memory; + this.cacheableAttribute = CacheableAttribute.NonCacheable; + this.writeStatus = writeStatus; + } + + public MemoryRangeDescriptor(ResourceType resourceType, ulong minimum, ulong maximum, + ulong length, ulong alignment, ulong addressTranslationOffset, + bool minimumAddressIsFixed, bool maximumAddressIsFixed, + DecodeType decodeType, ConsumerProducer consumerProducer, + AcpiMemoryFlags acpiMemoryFlags, MemoryToIoTranslation memoryToIoTranslation, + AddressRangeAttribute addressRangeAttribute, + CacheableAttribute cacheableAttribute, WriteStatus writeStatus) + : base(resourceType, minimum, maximum, length, alignment, addressTranslationOffset, + minimumAddressIsFixed, maximumAddressIsFixed, decodeType, consumerProducer) + { + this.acpiMemoryFlags = acpiMemoryFlags; + this.memoryToIoTranslation = memoryToIoTranslation; + this.addressRangeAttribute = addressRangeAttribute; + this.cacheableAttribute = cacheableAttribute; + this.writeStatus = writeStatus; + } + + public override void Accept(ResourceDescriptorVisitor v) + { + v.Visit(this); + } + } + + public class IoRangeDescriptor : AddressSpaceDescriptor + { + public enum IoToMemoryTranslation + { + TypeStatic, + TypeTranslationDense, + TypeTranslationSparse + } + + IoToMemoryTranslation ioToMemoryTranslation; // _TTP, _TRS combined + + public IoRangeDescriptor(ulong minimum, ulong maximum, ulong length, ulong alignment) + : base(AddressSpaceDescriptor.ResourceType.IoRange, minimum, maximum, length, alignment) + { + this.ioToMemoryTranslation = IoToMemoryTranslation.TypeStatic; + } + + public IoRangeDescriptor(ResourceType resourceType, ulong minimum, ulong maximum, + ulong length, ulong alignment, ulong addressTranslationOffset, + bool minimumAddressIsFixed, bool maximumAddressIsFixed, + DecodeType decodeType, ConsumerProducer consumerProducer, + IoToMemoryTranslation ioToMemoryTranslation) + : base(resourceType, minimum, maximum, length, alignment, addressTranslationOffset, + minimumAddressIsFixed, maximumAddressIsFixed, decodeType, consumerProducer) + { + this.ioToMemoryTranslation = ioToMemoryTranslation; + } + + public override void Accept(ResourceDescriptorVisitor v) + { + v.Visit(this); + } + } + + public class IrqDescriptor : ResourceDescriptor + { + public enum Polarity + { + ActiveHigh = 0, + ActiveLow = 1 + } + public enum Mode + { + LevelTriggered = 0, + EdgeTriggered = 1 + } + + int[] interruptNumbers; + bool sharable; // _SHR + Polarity polarity; // _LL + Mode mode; // _HE + + // Extended Interrupt Descriptor-only attributes + ConsumerProducer consumerProducer; + string resourceSource; + byte resourceSourceIndex; + + public IrqDescriptor(int[] interruptNumbers, bool sharable, Polarity polarity, Mode mode) + { + this.interruptNumbers = interruptNumbers; + this.sharable = sharable; + this.polarity = polarity; + this.mode = mode; + } + + public override void Accept(ResourceDescriptorVisitor v) + { + v.Visit(this); + } + + // Make this an ICollection when generics come around + int[] InterruptNumbers + { + get + { + return interruptNumbers; + } + } + } + + public class DmaDescriptor : ResourceDescriptor + { + public enum ChannelSpeed + { + CompatibilityMode = 0, + TypeA = 1, + TypeB = 2, + TypeF = 3 + } + + public enum LogicalDeviceBusMasterStatus + { + IsNotBusMaster = 0, + IsBusMaster = 1 + } + + public enum TransferTypePreference + { + Type8BitOnly = 0, + TypeBoth = 1, + Type16BitOnly = 2 + } + + int[] dmaChannelNumbers; // _DMA + ChannelSpeed channelSpeed; // _TYP + LogicalDeviceBusMasterStatus logicalDeviceBusMasterStatus; // _BM + TransferTypePreference transferTypePreference; // _SIZ + + public DmaDescriptor(int[] dmaChannelNumbers, + ChannelSpeed channelSpeed, + LogicalDeviceBusMasterStatus logicalDeviceBusMasterStatus, + TransferTypePreference transferTypePreference) + { + this.dmaChannelNumbers = dmaChannelNumbers; + this.channelSpeed = channelSpeed; + this.logicalDeviceBusMasterStatus = logicalDeviceBusMasterStatus; + this.transferTypePreference = transferTypePreference; + } + + public override void Accept(ResourceDescriptorVisitor v) + { + v.Visit(this); + } + + public int[] DmaChannelNumbers + { + get + { + return dmaChannelNumbers; + } + } + } + + public class GenericRegisterDescriptor : ResourceDescriptor + { + public enum AddressSpace + { + SystemMemory = 0x00, + SystemIo = 0x01, + PciConfigurationSpace = 0x02, + EmbeddedController = 0x03, + SmBus = 0x04, + FunctionalFixedHardware = 0x7F + } + + public enum AddressSize + { + ByteAccess = 1, + WordAccess = 2, + DwordAccess = 3, + QwordAccess = 4 + } + + AddressSpace addressSpace; // _ASI + byte registerBitWidth; // _RBW + byte registerBitOffset; // _RBO + AddressSize addressSize; // _ASZ + ulong registerAddress; // _ADR + + public GenericRegisterDescriptor(AddressSpace addressSpace, byte registerBitWidth, byte registerBitOffset, + AddressSize addressSize, ulong registerAddress) + { + this.addressSpace = addressSpace; + this.registerBitWidth = registerBitWidth; + this.registerBitOffset = registerBitOffset; + this.addressSize = addressSize; + this.registerAddress = registerAddress; + } + + public override void Accept(ResourceDescriptorVisitor v) + { + v.Visit(this); + } + } + + public class VendorDefinedDescriptor : ResourceDescriptor + { + Guid uuid; + byte subtype; + byte[] data; + + public VendorDefinedDescriptor(byte[] data) + : this(new Guid(), 0, data) { } + + public VendorDefinedDescriptor(Guid uuid, byte subtype, byte[] data) + { + this.uuid = uuid; + this.subtype = subtype; + this.data = data; + } + + public override void Accept(ResourceDescriptorVisitor v) + { + v.Visit(this); + } + } + + public abstract class ResourceDescriptorVisitor + { + public abstract void Visit(AddressSpaceDescriptor descriptor); + public abstract void Visit(MemoryRangeDescriptor descriptor); + public abstract void Visit(IoRangeDescriptor descriptor); + public abstract void Visit(IrqDescriptor descriptor); + public abstract void Visit(DmaDescriptor descriptor); + public abstract void Visit(GenericRegisterDescriptor descriptor); + public abstract void Visit(VendorDefinedDescriptor descriptor); + } + + public class ResourceDescriptionParseException : Exception + { + public ResourceDescriptionParseException() + : base("Failure parsing ACPI resource description") { } + + public ResourceDescriptionParseException(string str) + : base("Failure parsing ACPI resource description: " + str) { } + } + + public class ResourceDescriptorParser + { + private class IntList + { + ArrayList list = new ArrayList(); + + public void Add(int i) { + list.Add(i); + } + + public int[] ToArray() { + return (int[])list.ToArray(typeof(int)); + } + } + + private class ResourceDescriptorList + { + ArrayList list = new ArrayList(); + + public void Add(ResourceDescriptor rd) { + list.Add(rd); + } + + public ResourceDescriptor[] ToArray() { + return (ResourceDescriptor[])list.ToArray(typeof(ResourceDescriptor)); + } + } + + // Expects bytes in little-endian order + private static UInt16 BuildUInt16(byte[] b, int offset) + { + return (ushort)((((ulong)b[offset + 1]) << 8) | + (((ulong)b[offset + 0]) << 0)); + } + + // Expects bytes in little-endian order + private static UInt32 BuildUInt32(byte[] b, int offset) + { + return (uint)((((ulong)b[offset + 3]) << 24) | + (((ulong)b[offset + 2]) << 16) | + (((ulong)b[offset + 1]) << 8) | + (((ulong)b[offset + 0]) << 0)); + } + + // Expects bytes in little-endian order + private static UInt64 BuildUInt64(byte[] b, int offset) + { + return (ulong)((((ulong)b[offset + 7]) << 56) | + (((ulong)b[offset + 6]) << 48) | + (((ulong)b[offset + 5]) << 40) | + (((ulong)b[offset + 4]) << 32) | + (((ulong)b[offset + 3]) << 24) | + (((ulong)b[offset + 2]) << 16) | + (((ulong)b[offset + 1]) << 8) | + (((ulong)b[offset + 0]) << 0)); + } + + public static ResourceDescriptor[] Parse(byte[] resourceBuffer) + { + ResourceDescriptorList result = new ResourceDescriptorList(); + + for (int start = 0; start < resourceBuffer.Length; ) { + if (resourceBuffer.Length < 1) { + throw new ArgumentException("resourceBuffer must have length at least 1"); + } + if ((resourceBuffer[start] & 0x80) == 0) { + // Small item + int lengthBytes = resourceBuffer[start] & 0x7; + + switch ((SmallResourceItemName)((resourceBuffer[start] >> 3) & 0x0F)) { + case SmallResourceItemName.IrqFormatDescriptor: { + Debug.Assert(lengthBytes == 2 || lengthBytes == 3); + IntList interruptNumbers = new IntList(); + for (int i = 0; i < 8; i++) { + if ((resourceBuffer[start + 1] & (1 << i)) != 0) { + interruptNumbers.Add(i); + } + if ((resourceBuffer[start + 2] & (1 << i)) != 0) { + interruptNumbers.Add(i + 8); + } + } + + // From spec: "If byte 3 is not included, High true, + // edge sensitive, non-shareable is assumed." + bool sharable = false; + IrqDescriptor.Polarity polarity = IrqDescriptor.Polarity.ActiveHigh; + IrqDescriptor.Mode mode = IrqDescriptor.Mode.EdgeTriggered; + + if (lengthBytes == 3) { + byte flags = resourceBuffer[start + 3]; + sharable = (flags & (1 << 4)) != 0; + polarity = (IrqDescriptor.Polarity)((flags >> 3) & 1); + mode = (IrqDescriptor.Mode)(flags & 1); + } + + result.Add(new IrqDescriptor(interruptNumbers.ToArray(), + sharable, polarity, mode)); + break; + } + + case SmallResourceItemName.DmaFormatDescriptor: { + Debug.Assert(lengthBytes == 2); + IntList dmaChannelNumbers = new IntList(); + for (int i = 0; i < 8; i++) { + if ((resourceBuffer[start + 1] & (1 << i)) != 0) { + dmaChannelNumbers.Add(i); + } + } + + byte flags = resourceBuffer[start + 2]; + DmaDescriptor.ChannelSpeed channelSpeed = + (DmaDescriptor.ChannelSpeed)((flags >> 5) & 0x3); + DmaDescriptor.LogicalDeviceBusMasterStatus logicalDeviceBusMasterStatus = + (DmaDescriptor.LogicalDeviceBusMasterStatus)((flags >> 2) & 0x1); + DmaDescriptor.TransferTypePreference transferTypePreference = + (DmaDescriptor.TransferTypePreference)(flags & 0x3); + + result.Add(new DmaDescriptor(dmaChannelNumbers.ToArray(), + channelSpeed, logicalDeviceBusMasterStatus, transferTypePreference)); + break; + } + + case SmallResourceItemName.StartDependentFunctionsDescriptor: + throw new ResourceDescriptionParseException("Unimplemented ACPI resource descriptor"); + + case SmallResourceItemName.EndDependentFunctionsDescriptor: + throw new ResourceDescriptionParseException("Unimplemented ACPI resource descriptor"); + + case SmallResourceItemName.IoPortDescriptor: { + UInt16 minimum = BuildUInt16(resourceBuffer, start + 2); + UInt16 maximum = BuildUInt16(resourceBuffer, start + 4); + byte alignment = resourceBuffer[start + 6]; + byte length = resourceBuffer[start + 7]; + result.Add(new IoRangeDescriptor(minimum, maximum, length, alignment)); + break; + } + + case SmallResourceItemName.FixedLocationIoPortDescriptor: { + UInt16 baseAddress = BuildUInt16(resourceBuffer, start + 1); + baseAddress &= 0x3FF; + byte length = resourceBuffer[start + 3]; + result.Add(new IoRangeDescriptor(baseAddress, baseAddress, length, /*alignment*/1)); + break; + } + + case SmallResourceItemName.VendorDefinedDescriptor: { + byte[] data = new byte[7]; + Array.Copy(resourceBuffer, start + 1, data, 0, 7); + result.Add(new VendorDefinedDescriptor(data)); + break; + } + + case SmallResourceItemName.EndTagDescriptor: + // Used to check it was at the end here, but apparently it can occur elsewhere + break; + + default: + // Just ignore it and skip over it + break; + } + + start += 1 + lengthBytes; // 1 for the tag byte 0 + } + else { + // Large item + UInt16 lengthBytes = BuildUInt16(resourceBuffer, start + 1); + LargeResourceItemName itemName = (LargeResourceItemName)(resourceBuffer[start] & 0x7F); + + switch (itemName) { + case LargeResourceItemName.MemoryRangeDescriptor24Bit: { + MemoryRangeDescriptor.WriteStatus writeStatus = + (MemoryRangeDescriptor.WriteStatus)(resourceBuffer[start + 3] & 1); + UInt16 minimum = BuildUInt16(resourceBuffer, start + 3); + minimum *= 256; + UInt16 maximum = BuildUInt16(resourceBuffer, start + 6); + maximum *= 256; + UInt16 alignment = BuildUInt16(resourceBuffer, start + 8); + UInt16 length = BuildUInt16(resourceBuffer, start + 10); + length *= 256; + result.Add(new MemoryRangeDescriptor(minimum, maximum, length, alignment, writeStatus)); + break; + } + + case LargeResourceItemName.GenericRegisterDescriptor: { + GenericRegisterDescriptor.AddressSpace addressSpace = + (GenericRegisterDescriptor.AddressSpace)resourceBuffer[3]; + byte registerBitWidth = resourceBuffer[start + 4]; + byte registerBitOffset = resourceBuffer[start + 5]; + GenericRegisterDescriptor.AddressSize addressSize = + (GenericRegisterDescriptor.AddressSize)resourceBuffer[6]; + ulong registerAddress = BuildUInt64(resourceBuffer, start + 7); + + result.Add(new GenericRegisterDescriptor( + addressSpace, registerBitWidth, registerBitOffset, addressSize, registerAddress)); + break; + } + + case LargeResourceItemName.VendorDefinedDescriptor: { + byte[] guidBytes = new byte[16]; + Array.Copy(resourceBuffer, start + 4, guidBytes, 0, 16); + byte[] data = new byte[lengthBytes - 16 - 1]; + Array.Copy(resourceBuffer, start + 20, data, 0, data.Length); + result.Add(new VendorDefinedDescriptor(new Guid(guidBytes), resourceBuffer[start + 3], data)); + break; + } + + case LargeResourceItemName.MemoryRangeDescriptor32Bit: { + MemoryRangeDescriptor.WriteStatus writeStatus = + (MemoryRangeDescriptor.WriteStatus)(resourceBuffer[start + 3] & 1); + UInt32 minimum = BuildUInt32(resourceBuffer, start + 6); + UInt32 maximum = BuildUInt32(resourceBuffer, start + 10); + UInt32 alignment = BuildUInt32(resourceBuffer, start + 14); + UInt32 length = BuildUInt32(resourceBuffer, start + 18); + result.Add(new MemoryRangeDescriptor(minimum, maximum, length, alignment, writeStatus)); + break; + } + + case LargeResourceItemName.FixedLocationMemoryRangeDescriptor32Bit: { + MemoryRangeDescriptor.WriteStatus writeStatus = + (MemoryRangeDescriptor.WriteStatus)(resourceBuffer[start + 3] & 1); + UInt32 baseAddress = BuildUInt32(resourceBuffer, start + 4); + UInt32 length = BuildUInt32(resourceBuffer, start + 8); + result.Add(new MemoryRangeDescriptor(baseAddress, baseAddress, length, 1, writeStatus)); + break; + } + + case LargeResourceItemName.WordAddressSpaceDescriptor: + case LargeResourceItemName.DwordAddressSpaceDescriptor: + case LargeResourceItemName.QwordAddressSpaceDescriptor: + case LargeResourceItemName.ExtendedAddressSpaceDescriptor: { + AddressSpaceDescriptor.ResourceType resourceType = + (AddressSpaceDescriptor.ResourceType)resourceBuffer[start + 3]; + + byte flags = resourceBuffer[start + 4]; + bool maximumAddressIsFixed = (flags & (1 << 3)) != 0; + bool minimumAddressIsFixed = (flags & (1 << 2)) != 0; + AddressSpaceDescriptor.DecodeType decodeType = + (AddressSpaceDescriptor.DecodeType)((flags >> 1) & 1); + ConsumerProducer consumerProducer = + (ConsumerProducer)(flags & 1); + + // alignment same thing as granularity in spec + ulong minimum, maximum, length, alignment, addressTranslationOffset; + if (itemName == LargeResourceItemName.WordAddressSpaceDescriptor) { + minimum = BuildUInt16(resourceBuffer, start + 8); + maximum = BuildUInt16(resourceBuffer, start + 10); + alignment = BuildUInt16(resourceBuffer, start + 6); + length = BuildUInt16(resourceBuffer, start + 14); + addressTranslationOffset = BuildUInt16(resourceBuffer, start + 12); + } + else if (itemName == LargeResourceItemName.DwordAddressSpaceDescriptor) { + minimum = BuildUInt32(resourceBuffer, start + 10); + maximum = BuildUInt32(resourceBuffer, start + 14); + alignment = BuildUInt32(resourceBuffer, start + 6); + length = BuildUInt32(resourceBuffer, start + 22); + addressTranslationOffset = BuildUInt32(resourceBuffer, start + 18); + } + else if (itemName == LargeResourceItemName.QwordAddressSpaceDescriptor) { + minimum = BuildUInt64(resourceBuffer, start + 14); + maximum = BuildUInt64(resourceBuffer, start + 22); + alignment = BuildUInt64(resourceBuffer, start + 6); + length = BuildUInt64(resourceBuffer, start + 38); + addressTranslationOffset = BuildUInt64(resourceBuffer, start + 30); + } + else /* if (itemName == LargeResourceItemName.ExtendedAddressSpaceDescriptor) */ { + minimum = BuildUInt64(resourceBuffer, start + 16); + maximum = BuildUInt64(resourceBuffer, start + 24); + alignment = BuildUInt64(resourceBuffer, start + 8); + length = BuildUInt64(resourceBuffer, start + 40); + addressTranslationOffset = BuildUInt64(resourceBuffer, start + 32); + } + + byte typeSpecificFlags = resourceBuffer[start + 4]; + if (resourceType == AddressSpaceDescriptor.ResourceType.MemoryRange) { + MemoryRangeDescriptor.AcpiMemoryFlags acpiMemoryFlags = + (itemName == LargeResourceItemName.ExtendedAddressSpaceDescriptor) ? + (MemoryRangeDescriptor.AcpiMemoryFlags)BuildUInt16(resourceBuffer, start + 48) : + MemoryRangeDescriptor.AcpiMemoryFlags.None; + MemoryRangeDescriptor.MemoryToIoTranslation memoryToIoTranslation = + (MemoryRangeDescriptor.MemoryToIoTranslation)((typeSpecificFlags >> 5) & 1); + MemoryRangeDescriptor.AddressRangeAttribute addressRangeAttribute = + (MemoryRangeDescriptor.AddressRangeAttribute)((typeSpecificFlags >> 3) & 3); + MemoryRangeDescriptor.CacheableAttribute cacheableAttribute = + (MemoryRangeDescriptor.CacheableAttribute)((typeSpecificFlags >> 1) & 3); + MemoryRangeDescriptor.WriteStatus writeStatus = + (MemoryRangeDescriptor.WriteStatus)(typeSpecificFlags & 1); + + result.Add(new MemoryRangeDescriptor(resourceType, minimum, maximum, length, + alignment, addressTranslationOffset, + minimumAddressIsFixed, maximumAddressIsFixed, + decodeType, consumerProducer, + acpiMemoryFlags, memoryToIoTranslation, + addressRangeAttribute, cacheableAttribute, + writeStatus)); + } + else if (resourceType == AddressSpaceDescriptor.ResourceType.IoRange) { + bool translation = ((typeSpecificFlags >> 4) & 1) != 0; + bool sparse = ((typeSpecificFlags >> 5) & 1) != 0; + + IoRangeDescriptor.IoToMemoryTranslation ioToMemoryTranslation; + if (!translation) { + ioToMemoryTranslation = IoRangeDescriptor.IoToMemoryTranslation.TypeStatic; + } + else if (sparse) { + ioToMemoryTranslation = IoRangeDescriptor.IoToMemoryTranslation.TypeTranslationSparse; + } + else { + ioToMemoryTranslation = IoRangeDescriptor.IoToMemoryTranslation.TypeTranslationDense; + } + + result.Add(new IoRangeDescriptor(resourceType, minimum, maximum, length, + alignment, addressTranslationOffset, + minimumAddressIsFixed, maximumAddressIsFixed, + decodeType, consumerProducer, + ioToMemoryTranslation)); + } + else { + result.Add(new AddressSpaceDescriptor(resourceType, minimum, maximum, length, + alignment, addressTranslationOffset, + minimumAddressIsFixed, maximumAddressIsFixed, + decodeType, consumerProducer)); + } + break; + } + + case LargeResourceItemName.ExtendedIrqDescriptor: { + byte flags = resourceBuffer[start + 3]; + bool sharable = (flags & (1 << 4)) != 0; + IrqDescriptor.Polarity polarity = (IrqDescriptor.Polarity)((flags >> 3) & 1); + IrqDescriptor.Mode mode = (IrqDescriptor.Mode)(flags & 1); + + int[] interruptTable = new int[resourceBuffer[start + 4]]; + for (int i = 0; i < interruptTable.Length; i++) { + interruptTable[i] = resourceBuffer[start + 5 + i]; + } + + // TODO: Currently we don't extract the Resource Source, if present + + result.Add(new IrqDescriptor(interruptTable, sharable, polarity, mode)); + break; + } + + default: + // Just ignore it and skip over it + break; + } + + start += 1 + 2 + lengthBytes; // 1 for the tag byte, 2 for length bytes + } + } + + return result.ToArray(); + } + } +} diff --git a/base/Kernel/Singularity.Hal.Acpi/Dsdt.cs b/base/Kernel/Singularity.Hal.Acpi/Dsdt.cs index 0f018c1..13774f9 100644 --- a/base/Kernel/Singularity.Hal.Acpi/Dsdt.cs +++ b/base/Kernel/Singularity.Hal.Acpi/Dsdt.cs @@ -36,8 +36,7 @@ namespace Microsoft.Singularity.Hal.Acpi header.PostHeaderLength, true, false); int sum = (header.Checksum + AcpiChecksum.Compute(region)) & 0xff; - if (sum != 0) - { + if (sum != 0) { return null; } return new Dsdt(region, header); diff --git a/base/Kernel/Singularity.Hal.Acpi/Fadt.cs b/base/Kernel/Singularity.Hal.Acpi/Fadt.cs index 2d2d69b..e33a609 100644 --- a/base/Kernel/Singularity.Hal.Acpi/Fadt.cs +++ b/base/Kernel/Singularity.Hal.Acpi/Fadt.cs @@ -135,8 +135,7 @@ namespace Microsoft.Singularity.Hal.Acpi header.PostHeaderLength, true, false); int sum = (header.Checksum + AcpiChecksum.Compute(region)) & 0xff; - if (sum != 0) - { + if (sum != 0) { return null; } return new Fadt(region, header); diff --git a/base/Kernel/Singularity.Hal.Acpi/Madt.cs b/base/Kernel/Singularity.Hal.Acpi/Madt.cs index 75a35f6..76b4193 100644 --- a/base/Kernel/Singularity.Hal.Acpi/Madt.cs +++ b/base/Kernel/Singularity.Hal.Acpi/Madt.cs @@ -63,13 +63,10 @@ namespace Microsoft.Singularity.Hal.Acpi ArrayList l = new ArrayList(); int offset = 8; - while (offset < region.Length) - { - if (region.Read8(offset) == typeId) - { + while (offset < region.Length) { + if (region.Read8(offset) == typeId) { object entry = createMethod(region, offset); - if (entry != null) - { + if (entry != null) { l.Add(entry); } } @@ -146,8 +143,7 @@ namespace Microsoft.Singularity.Hal.Acpi header.PostHeaderLength, true, false); int sum = (header.Checksum + AcpiChecksum.Compute(region)) & 0xff; - if (sum != 0) - { + if (sum != 0) { return null; } return new Madt(region, header); diff --git a/base/Kernel/Singularity.Hal.Acpi/Rsdp.cs b/base/Kernel/Singularity.Hal.Acpi/Rsdp.cs index 494e4bb..30bcf2b 100644 --- a/base/Kernel/Singularity.Hal.Acpi/Rsdp.cs +++ b/base/Kernel/Singularity.Hal.Acpi/Rsdp.cs @@ -45,6 +45,11 @@ namespace Microsoft.Singularity.Hal.Acpi get { return region.Read32(16); } } + public ulong XsdtAddress + { + get { return region.Read64(24); } + } + public uint Length { get { return region.Read32(20); } @@ -53,8 +58,7 @@ namespace Microsoft.Singularity.Hal.Acpi public static Rsdp Parse(UIntPtr regionAddress, uint regionBytes) { - if (regionBytes >= 20) - { + if (regionBytes >= 20) { IoMemory region = IoMemory.MapPhysicalMemory(regionAddress, regionBytes, true, false); diff --git a/base/Kernel/Singularity.Hal.Acpi/Rsdt.cs b/base/Kernel/Singularity.Hal.Acpi/Rsdt.cs index 2d8f2d2..1b45277 100644 --- a/base/Kernel/Singularity.Hal.Acpi/Rsdt.cs +++ b/base/Kernel/Singularity.Hal.Acpi/Rsdt.cs @@ -15,18 +15,18 @@ namespace Microsoft.Singularity.Hal.Acpi using Microsoft.Singularity.Io; public class Rsdt { - private IoMemory region; + protected IoMemory region; private SystemTableHeader header; - public Rsdt(IoMemory region, SystemTableHeader header) + protected Rsdt(IoMemory region, SystemTableHeader header) { this.region = region; this.header = header; } - public int EntryCount { get { return region.Length / 4; } } + public virtual int EntryCount { get { return region.Length / 4; } } - public uint GetEntry(int index) + public virtual ulong GetEntry(int index) { index *= 4; if (index > region.Length) @@ -36,7 +36,7 @@ namespace Microsoft.Singularity.Hal.Acpi public SystemTableHeader GetTableHeader(int index) { - uint address = GetEntry(index); + ulong address = GetEntry(index); if (address == 0) return null; return SystemTableHeader.Create(address); @@ -44,6 +44,7 @@ namespace Microsoft.Singularity.Hal.Acpi public static Rsdt Create(SystemTableHeader header) { + DebugStub.Assert(header.Signature == "RSDT"); return new Rsdt( IoMemory.MapPhysicalMemory( header.PostHeaderAddress, header.PostHeaderLength, @@ -53,4 +54,35 @@ namespace Microsoft.Singularity.Hal.Acpi ); } } + + public class Xsdt : Rsdt + { + private Xsdt(IoMemory region, SystemTableHeader header) + : base(region, header) + { + // Nothing to do + } + + public override int EntryCount { get { return region.Length / 8; } } + + public override ulong GetEntry(int index) + { + index *= 8; + if (index > region.Length) + return 0; + return region.Read64(index); + } + + public new static Xsdt Create(SystemTableHeader header) + { + DebugStub.Assert(header.Signature == "XSDT"); + return new Xsdt( + IoMemory.MapPhysicalMemory( + header.PostHeaderAddress, header.PostHeaderLength, + true, false + ), + header + ); + } + } } diff --git a/base/Kernel/Singularity.Hal.Acpi/Srat.cs b/base/Kernel/Singularity.Hal.Acpi/Srat.cs index eabe014..6da7d36 100644 --- a/base/Kernel/Singularity.Hal.Acpi/Srat.cs +++ b/base/Kernel/Singularity.Hal.Acpi/Srat.cs @@ -4,9 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Srat.cs -// -// // Note: // System Resource Affinity Table (SRAT) // Page 123 of ACPI 3.0 Spec. diff --git a/base/Kernel/Singularity.Hal.Acpi/Ssdt.cs b/base/Kernel/Singularity.Hal.Acpi/Ssdt.cs index 9568241..37d27f5 100644 --- a/base/Kernel/Singularity.Hal.Acpi/Ssdt.cs +++ b/base/Kernel/Singularity.Hal.Acpi/Ssdt.cs @@ -36,8 +36,7 @@ namespace Microsoft.Singularity.Hal.Acpi header.PostHeaderLength, true, false); int sum = (header.Checksum + AcpiChecksum.Compute(region)) & 0xff; - if (sum != 0) - { + if (sum != 0) { return null; } return new Ssdt(region, header); diff --git a/base/Kernel/Singularity.Hal.Acpi/SystemTableHeader.cs b/base/Kernel/Singularity.Hal.Acpi/SystemTableHeader.cs index 71ad3b2..dfed622 100644 --- a/base/Kernel/Singularity.Hal.Acpi/SystemTableHeader.cs +++ b/base/Kernel/Singularity.Hal.Acpi/SystemTableHeader.cs @@ -64,12 +64,12 @@ namespace Microsoft.Singularity.Hal.Acpi public string OemId { - get { return region.ReadAsciiZeroString(10, 16); } + get { return region.ReadAsciiZeroString(10, 6/*max length*/); } } public string OemTableId { - get { return region.ReadAsciiZeroString(16, 24); } + get { return region.ReadAsciiZeroString(16, 8/*max length*/); } } public uint OemRevision @@ -87,7 +87,7 @@ namespace Microsoft.Singularity.Hal.Acpi get { return region.Read32(32); } } - public static SystemTableHeader Create(uint address) + public static SystemTableHeader Create(ulong address) { return new SystemTableHeader( IoMemory.MapPhysicalMemory(new UIntPtr(address), Length, true, false) diff --git a/base/Kernel/Singularity.Hal.Apic64.csproj b/base/Kernel/Singularity.Hal.Apic64.csproj new file mode 100644 index 0000000..3316971 --- /dev/null +++ b/base/Kernel/Singularity.Hal.Apic64.csproj @@ -0,0 +1,49 @@ + + + + + + + Hal + Library + $(KERNEL_IL_DIR)\Apic64 + C# + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Kernel/Singularity.Hal.ApicMP.csproj b/base/Kernel/Singularity.Hal.ApicMP.csproj index 84f8486..cf21723 100644 --- a/base/Kernel/Singularity.Hal.ApicMP.csproj +++ b/base/Kernel/Singularity.Hal.ApicMP.csproj @@ -1,8 +1,6 @@  + + + + + + Hal + Library + $(KERNEL_IL_DIR)\Omap3430 + C# + true + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Kernel/Singularity.Hal.Omap3430/CalibrateTimer.cs b/base/Kernel/Singularity.Hal.Omap3430/CalibrateTimer.cs new file mode 100644 index 0000000..7bb2d53 --- /dev/null +++ b/base/Kernel/Singularity.Hal.Omap3430/CalibrateTimer.cs @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: CalibrateTimers.cs +// +// Note: +// + +// #define VERBOSE + +using System; +using Microsoft.Singularity.Hal; + +namespace Microsoft.Singularity.Hal +{ + [CLSCompliant(false)] + internal sealed class CalibrateTimers + { + internal static void Run(HalClockNull clock, TimerOmap3430 gpTimer1) + { + DebugStub.Print("CLOCK CALIBRATION NOT ATTEMPTED.\n"); + // hacked, should calibrate + Processor.CyclesPerSecond = 687 * 1000 * 1000; // MPU runs @ 687Mhz in OPP1 + gpTimer1.SetTicksPerSecond(32768); + return; + } + } +} diff --git a/base/Kernel/Singularity.Hal.Omap3430/ClockLogger.cs b/base/Kernel/Singularity.Hal.Omap3430/ClockLogger.cs new file mode 100644 index 0000000..4187aed --- /dev/null +++ b/base/Kernel/Singularity.Hal.Omap3430/ClockLogger.cs @@ -0,0 +1,231 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ClockLogger.cs +// +// Useful reference URLs: +// http://developer.intel.com/design/archives/periphrl/index.htm +// http://developer.intel.com/design/archives/periphrl/docs/7203.htm +// http://developer.intel.com/design/archives/periphrl/docs/23124406.htm +// +// The basic ideas for this driver come from the MMOSA code, +// though the implementation differs. This is partly because +// the code needs to run on Virtual PC and it isn't able to do +// a very accurate emulation of the i8254. +// +// There are two source available for timing - the Real-Time +// Clock (RTC) and the programmable interval timer (PIT). The +// standard PC RTC is based on derivatives of the Motorola +// MC146818A. It's able to provide the time with a resolution +// of 1 second and also has a programmable periodic interrupt. +// +// The programmable interrupt timer is based on the i8254. It +// can be programmed in a variety of modes - we use it to +// generate an interrupt at a configurable time in the future +// and then reprogram it each interrupt. The maximum interrupt +// period is 65535 ticks of a 1.193MHz clock. +// +// We use both of the RTC and the programmable interrupt timer to +// maintain our estimate of the current time. The RTC provides granularity +// to with 1/64 seconds and the time is used to get an estimate to within +// 1/1.193 * 10e-6 seconds within each RTC interval. +// +// The key variables are: +// +// upTime - the time the system has been up. This variable gets +// updated during the periodic RTC interrupt handling +// (delta = 1/64Hz). +// +// pitLast - the last value programmed into the PIT. The PIT counts down +// and generates an interrupt at (or shortly after) the instant +// the current PIT value reaches zero. +// +// pitAccum - the accumulated time measured by the PIT since upTime +// was updated. +// +// The current kernel time is always: +// upTime + pitAccum + (pitLast - pitCurrent) +// +// The PIT is always programmed to run, either by the consumer of the timer +// interface or by the timer itself. +// +// Timer::SetNextInterrupt(t) +// pitAccum += (pitLast - pitCurrent) +// // program PIT (not shown) +// pitLast = t +// +// Timer::Interrupt() +// pitAccum += pitLast; +// // But PIT time may accumulate between interrupt dispatch and crossing +// // Zero so. +// if (pitCurrent != 0) +// pitAccum += (MaxPitValue - pitCurrent) +// // Inform user of interrupt +// if (userNotScheduledInterrupt) +// SetNextInterrupt(MaxInterruptInterval) +// +// RTC::Interrupt() +// pitLast = pitNow +// pitAccum = 0 +// upTime += RTCInterruptPeriod +// +// All of these methods are atomic interrupt-wise. +// +// Note: if we want to test the accuracy of the timer over a +// period we can set RTC::Interrupt to just return without +// touching any variables. All of the time accumulated will end +// up in pitAccum. +// +// Conditionals +// +// TIMER_NO_GO - disables timer and scheduling of timer interrupts. +// +// RTC_NO_GO - disable RTC. +// +// DEBUG_TIMER - enable timer debug messages and boot-time diagnostics. +// +// DEBUG_CLOCK - enable clock debug messages +// +// LOG_CLOCK - log adjustments to clock time and dump out later. +// +// LOG_SNI - log calls to SetNextInterrupt to see what's being thrown in. +// +// Tip: When this code does not behave useful things to check +// are the interrupt rate and the rate of calls to +// SetNextInterrupt. +// + +// #define VERBOSE + +using Microsoft.Singularity.Io; + +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Diagnostics; + +namespace Microsoft.Singularity.Hal +{ + [ CLSCompliant(false) ] + internal sealed class ClockLogger + { + public struct Record + { + public int who; + public ulong when; + public ulong currentTime; + public long upTime; + public int pitLastClock; + public int pitNow; + public long cookie; + }; + private static int currentRecord = 0; + private static Record [] entries = null; + private static int ignoreCount = 20000; + + static ClockLogger() + { + InitializeEntries(); +#if LOG_CLOCK + DebugStub.Print("Initializing ClockLogger.\n"); +#endif // LOG_CLOCK + } + + [System.Diagnostics.Conditional("LOG_CLOCK")] + internal static void InitializeEntries() + { + entries = new Record[10000]; + } + + [System.Diagnostics.Conditional("LOG_CLOCK")] + [NoHeapAllocation] + internal static void AddEntry(int who, RtcPitState rps, TimerOmap3430 timer, long cookie) + { + if (ignoreCount != 0) { + ignoreCount--; + return; + } + + if (currentRecord == entries.Length) + return; + + entries[currentRecord].who = who; + entries[currentRecord].cookie = cookie; + entries[currentRecord].when = Processor.CycleCount; +// int pitNow = timer.Timer2Read(); + int pitNow = 0; + entries[currentRecord].currentTime = + (ulong)rps.GetKernelTicks(pitNow); + entries[currentRecord].upTime = rps.upTime; + entries[currentRecord].pitLastClock = rps.pitLastClock; + entries[currentRecord].pitNow = pitNow; + + currentRecord = currentRecord + 1; + + if (currentRecord == entries.Length) { + bool iflag = Processor.DisableInterrupts(); + try { + DumpEntries(); + DebugStub.Assert(false); + } + finally { + Processor.RestoreInterrupts(iflag); + } + } + } + + [System.Diagnostics.Conditional("LOG_CLOCK")] + [NoHeapAllocation] + internal static void AddEntry(int who, RtcPitState rps, TimerOmap3430 timer) + { + AddEntry(who, rps, timer, 0); + } + + [System.Diagnostics.Conditional("LOG_CLOCK")] + [NoHeapAllocation] + internal static void SetCookie(long cookie) + { + if (currentRecord < entries.Length) + entries[currentRecord].cookie = cookie; + } + + [NoHeapAllocation] + static ulong ToMicros(ulong t, ulong t0, ulong tps) + { + return ((t - t0) * 10000000) / tps; + } + + [System.Diagnostics.Conditional("LOG_CLOCK")] + [NoHeapAllocation] + static void DumpEntries() + { + DebugStub.Print("# Clock column 1 who\n"); + DebugStub.Print("# Clock column 2 time in cpu cycles\n"); + DebugStub.Print("# Clock column 3 time from clock\n"); + DebugStub.Print("# Clock column 4 pitLastClock\n"); + DebugStub.Print("# Clock column 5 pitNow\n"); + DebugStub.Print("# Clock column 6 upTime\n"); + DebugStub.Print("# Clock column 7 currentTime\n"); + DebugStub.Print("# Clock column 8 cookie\n"); + DebugStub.Print("# Clock column 9 record number\n"); + + for (int i = 0; i != currentRecord; i++) { + DebugStub.Print("Clock {0:d6} {1:d6} {2} {3} {4} {5} {6} {7}\n", + __arglist( + ToMicros(entries[i].when, + entries[0].when, + Processor.CyclesPerSecond), + ToMicros(entries[i].currentTime, 0, 10000000), + entries[i].pitLastClock, + entries[i].pitNow, + entries[i].upTime, + entries[i].currentTime, + entries[i].cookie, + i)); + } + } + } +} diff --git a/base/Kernel/Singularity.Hal.Omap3430/HalAcpi.cs b/base/Kernel/Singularity.Hal.Omap3430/HalAcpi.cs new file mode 100644 index 0000000..b248212 --- /dev/null +++ b/base/Kernel/Singularity.Hal.Omap3430/HalAcpi.cs @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +// Because ARM compiles out ACPI support, we need to stub out the mandatory +// classes declared in the HAL interface here. + +namespace Microsoft.Singularity.Hal.Acpi +{ + public class AcpiTables + { + public static void Parse() { + } + + public static AcpiDevice[] LoadDevices() { + return new AcpiDevice[0]; + } + } + + public class AcpiDevice + { + public string DeviceId { + get { + return ""; + } + } + + public ResourceDescriptor[] ResourceDescriptors { + get { + return new ResourceDescriptor[0]; + } + } + } + + public enum ConsumerProducer + { + ProducesAndConsumes = 0, + Consumes = 1 + } + + public abstract class ResourceDescriptor + { + } + + public class AddressSpaceDescriptor : ResourceDescriptor + { + public ulong Minimum { + get { + return 0; + } + } + + public ulong Maximum { + get { + return 0; + } + } + + public ulong Length { + get { + return 0; + } + } + + public ConsumerProducer ConsumerProducer { + get { + return ConsumerProducer.Consumes; + } + } + } + + public class MemoryRangeDescriptor : AddressSpaceDescriptor + { + public bool Writable { + get { + return true; + } + } + } + + public class IoRangeDescriptor : AddressSpaceDescriptor + { + } + + public class IrqDescriptor : ResourceDescriptor + { + public int[] InterruptNumbers { + get { + return new int[0]; + } + } + } + + public class DmaDescriptor : ResourceDescriptor + { + public int[] DmaChannelNumbers { + get { + return new int[0]; + } + } + } + + public class GenericRegisterDescriptor : ResourceDescriptor + { + } + + public class VendorDefinedDescriptor : ResourceDescriptor + { + } +} diff --git a/base/Kernel/Singularity.Hal.Omap3430/HalClockNull.cs b/base/Kernel/Singularity.Hal.Omap3430/HalClockNull.cs new file mode 100644 index 0000000..b0fdb5a --- /dev/null +++ b/base/Kernel/Singularity.Hal.Omap3430/HalClockNull.cs @@ -0,0 +1,66 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: HalClockNull.cs +// +// Note: +// +// This file is an pretty bogus implementation of Interfaces/Hal/HalClock.csi +// + +using System; +using System.Runtime.CompilerServices; + +namespace Microsoft.Singularity.Hal +{ + public class HalClockNull : HalClock + { + long ticks; + + public HalClockNull() + { + ticks = 1; + } + + public byte Initialize() + { + ticks = 1; + return 254; + } + + public void Finalize() + { + } + + [NoHeapAllocation] + public override void ClearInterrupt() + { + } + + [NoHeapAllocation] + public override long GetKernelTicks() + { + return ticks; + } + + [NoHeapAllocation] + public override void CpuResumeFromHaltEvent() + { + ticks++; + } + + [NoHeapAllocation] + public override long GetRtcTime() + { + return ticks; + } + + public override void SetRtcTime(long rtcTicks) + { + ticks = rtcTicks; + } + } +} diff --git a/base/Kernel/Singularity.Hal.Omap3430/HalDevices.cs b/base/Kernel/Singularity.Hal.Omap3430/HalDevices.cs new file mode 100644 index 0000000..d80fdd6 --- /dev/null +++ b/base/Kernel/Singularity.Hal.Omap3430/HalDevices.cs @@ -0,0 +1,205 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: HalDevices.cs +// +// Note: This file implements the contract specified in HalDevices.csi which are +// static methods calling the proper platform specific class at runtime. +// +// Many of these methods may be called from C++ or ASM code (PIC functions) +// so they need to be static. +// + +using System; +using System.Collections; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Threading; + +using Microsoft.Singularity; +using Microsoft.Singularity.Hal; +using Microsoft.Singularity.Io; + +namespace Microsoft.Singularity.Hal +{ + // Factory class + [CLSCompliant(false)] + public sealed class HalDevicesFactory + { + public static object Initialize(Processor processor) + { + HalDevicesOmap3430 devices = new HalDevicesOmap3430(); + devices.Initialize(processor); + return devices; + } + } + + [CLSCompliant(false)] + public sealed class HalDevicesOmap3430 : HalDevices + { + private static Pic pic; + private static TimerOmap3430 timer; + private static HalClockNull clock; + private static HalScreenNull halScreen; + private static HalMemoryNull halMemory; + + [CLSCompliant(false)] + public override void Initialize(Processor rootProcessor) + { + DebugStub.Print("HalDevices.Initialize() - ARM\n"); + + // PIC + PnpConfig picConfig + = (PnpConfig)IoSystem.YieldResources("/arm/ti/3430/INTCPS", + typeof(Pic)); + pic = new Pic(picConfig); + pic.Initialize(); + + // Timer + PnpConfig timerConfig + = (PnpConfig)IoSystem.YieldResources("/arm/ti/3430/GPTIMER1", + typeof(TimerOmap3430)); + timer = new TimerOmap3430(timerConfig, pic); + byte timerInterrupt = timer.Initialize(); + + // Real-time clock + clock = new HalClockNull(); + byte clockInterrupt = clock.Initialize(); + bool noisyTimer = false; + + CalibrateTimers.Run(clock, timer); + + SystemClock.Initialize(clock, TimeSpan.FromHours(8).Ticks); + + rootProcessor.AddPic(pic); + + rootProcessor.AddTimer(timerInterrupt, timer); + rootProcessor.AddClock(clockInterrupt, clock); + + // ---------------------------------------------------------- + // Add Srat tables to the Processor + halMemory = new HalMemoryNull(); + ProcessorNode.AddMemory(halMemory); + + timer.Start(); + + halScreen = new HalScreenNull(); + Console.Screen = (HalScreen)halScreen; + } + + public override void ReleaseResources() + { + clock.ReleaseResources(); + clock = null; + + timer.ReleaseResources(); + timer = null; + + pic.ReleaseResources(); + pic = null; + } + + [CLSCompliant(false)] + [NoHeapAllocation] + public override byte GetMaximumIrq() + { + return pic.MaximumIrq; + } + + // + // Adding and removing interrupts from the Pic. + // + [CLSCompliant(false)] + [NoHeapAllocation] + public override void EnableIoInterrupt(byte irq) + { + pic.EnableIrq(irq); + } + + [CLSCompliant(false)] + [NoHeapAllocation] + public override void DisableIoInterrupt(byte irq) + { + pic.DisableIrq(irq); + } + + [CLSCompliant(false)] + [NoHeapAllocation] + public override bool InternalInterrupt(byte interrupt) + { + // Strictly there are no interrupts internal to + // this Hal instance. In practice, some hardware seems + // intent on firing an interrupt even if it is masked. + // + // Return true if interrupt appears to be valid but + // is masked, false otherwise. + byte irq = pic.InterruptToIrq(interrupt); + +#if DEBUG_SPURIOUS + DebugStub.Break(); +#endif + + if (pic.IrqMasked(irq) == true) { + DebugStub.WriteLine("--- Acked spurious Irq={0:x2}", __arglist(irq)); + pic.AckIrq(irq); + return true; + } + return false; + } + + [NoHeapAllocation] + public override int GetProcessorCount() + { + return 1; + } + + public override void StartApProcessors(int cpus) + { + } + + [NoHeapAllocation] + public override void ResetApProcessors() + { + } + + [CLSCompliant(false)] + [NoHeapAllocation] + public override void FreezeProcessors() + { + } + + [CLSCompliant(false)] + [NoHeapAllocation] + public override void SendFixedIPI(byte vector, int from, int to) + { + } + + [CLSCompliant(false)] + [NoHeapAllocation] + public override void BroadcastFixedIPI(byte vector, bool includeSelf) + { + } + + [CLSCompliant(false)] + [NoHeapAllocation] + public override void ClearFixedIPI(int interrupt) + { + } + + public override byte TranslatePciInterrupt(byte currentInterruptLine, + byte pciInterruptPin, + PciPort pciPort) + { + // + // On a non-ACPI PC, there's nothing to translate; + // just return the current interrupt line. + // + return currentInterruptLine; + } + } +} + diff --git a/base/Kernel/Singularity.Hal.Omap3430/HalMemoryNull.cs b/base/Kernel/Singularity.Hal.Omap3430/HalMemoryNull.cs new file mode 100644 index 0000000..7c10f28 --- /dev/null +++ b/base/Kernel/Singularity.Hal.Omap3430/HalMemoryNull.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +namespace Microsoft.Singularity.Hal +{ + using System; + + [CLSCompliant(false)] + internal class HalMemoryNull : HalMemory + { + internal HalMemoryNull() + { + } + + public override ProcessorAffinity[] GetProcessorAffinity() + { + return null; + } + + public override MemoryAffinity[] GetMemoryAffinity() + { + return null; + } + + public override bool PerProcessorAddressSpaceDisabled() + { + return true; + } + } +} diff --git a/base/Kernel/Singularity.Hal.Omap3430/HalScreenNull.cs b/base/Kernel/Singularity.Hal.Omap3430/HalScreenNull.cs new file mode 100644 index 0000000..95c1a6f --- /dev/null +++ b/base/Kernel/Singularity.Hal.Omap3430/HalScreenNull.cs @@ -0,0 +1,101 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: HalScreenNull.cs +// +// +using System; +using System.Runtime.CompilerServices; + +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Configuration; + +[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] +[assembly: System.Reflection.AssemblyKeyFileAttribute(@"..\public.snk")] +[assembly: System.Reflection.AssemblyDelaySignAttribute(true)] + +namespace Microsoft.Singularity.Hal +{ + [CLSCompliant(false)] + public class HalScreenNull : HalScreen + { + public HalScreenNull(IoConfig config) + { + } + + [NoHeapAllocation] + public override void CursorFlash() + { + } + + [NoHeapAllocation] + public override void CursorHide() + { + } + + [NoHeapAllocation] + public override void CursorShow() + { + } + + [NoHeapAllocation] + public override void Clear() + { + } + + [NoHeapAllocation] + public override void GetDisplayDimensions(out int columns, out int rows) + { + columns = 1; + rows = 1; + } + + [NoHeapAllocation] + public override void GetCursorPosition(out int column, out int row) + { + column = 0; + row = 0; + } + + [NoHeapAllocation] + public override void SetCursorSizeLarge() + { + } + + [NoHeapAllocation] + public override void SetCursorSizeSmall() + { + } + + [NoHeapAllocation] + public override bool SetCursorPosition(int column, int row) + { + return true; + } + + [NoHeapAllocation] + public override void ClearCursorToEndOfLine() + { + } + + [NoHeapAllocation] + public override bool PutCharAt(char c, int column, int row) + { + return true; + } + + [NoHeapAllocation] + public override void PutChar(char c) + { + } + + [NoHeapAllocation] + public override void Write(byte[] buffer, int offset, int count) + { + } + } +} // namespace Microsoft.Singularity.Hal diff --git a/base/Kernel/Singularity.Hal.Omap3430/Pic.cs b/base/Kernel/Singularity.Hal.Omap3430/Pic.cs new file mode 100644 index 0000000..9b66196 --- /dev/null +++ b/base/Kernel/Singularity.Hal.Omap3430/Pic.cs @@ -0,0 +1,485 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Pic.cs +// + +#define DEBUG_INTERRUPTS +#define DEBUG_DISPATCH_IO +#define PIC_DEBUG + +using Microsoft.Singularity.Io; + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Isal.Arm; + +namespace Microsoft.Singularity.Hal +{ + // declare resources for the kernel manifest + [DriverCategory] + [Signature("/arm/ti/3430/INTCPS")] + public sealed class PicResources : DriverCategoryDeclaration + { + [IoMemoryRange(0, Default = 0x48200000, Length = 0x1000)] + internal readonly IoMemoryRange registers; + + // CTR will create the rest of this class: + public PicResources(IoConfig config) + { + // dynamic resources + registers = (IoMemoryRange)config.DynamicRanges[0]; + } + } + + [CLSCompliant(false)] + public sealed class Pic : HalPic + { + // size of register block + const int PicRegisterSize = 0x00000280; // size of registers in block + + // registers + const uint INTCPS_REVISION = 0x00000000; // INTCPS block revision + const uint INTCPS_SYSCONFIG = 0x00000010; // configuration + const uint INTCPS_SYSSTATUS = 0x00000014; // status + const uint INTCPS_SIR_IRQ = 0x00000040; // IRQ source interrupt + const uint INTCPS_SIR_FIQ = 0x00000044; // FIQ source interrupt + const uint INTCPS_CONTROL = 0x00000048; // control + const uint INTCPS_PROTECTION = 0x0000004c; // pic access control + const uint INTCPS_IDLE = 0x00000050; // clock idling + const uint INTCPS_IRQ_PRIORITY = 0x00000060; // IRQ priority level + const uint INTCPS_FIQ_PRIORITY = 0x00000064; // FIQ priority level + const uint INTCPS_THRESHHOLD = 0x00000068; // priority threshold control + const uint INTCPS_ITR = 0x00000080; // raw interrupt status vector + const uint INTCPS_MIR = 0x00000084; // interrupt mask vector + const uint INTCPS_MIR_CLEAR = 0x00000088; // clear interrupt mask vector + const uint INTCPS_MIR_SET = 0x0000008c; // set interrupt mask vector + const uint INTCPS_ISR_SET = 0x00000090; // assert software interrupt vector + const uint INTCPS_ISR_CLEAR = 0x00000094; // clear software interrupt vector + const uint INTCPS_PENDING_IRQ = 0x00000098; // pending (masked) IRQ interrupt vector + const uint INTCPS_PENDING_FIQ = 0x0000009c; // pending (masked) FIQ interrupt vector + const uint INTCPS_ILR_Base = 0x00000100; // interrupt priority & fast/slow control + const uint INTCPS_ILR_Offset = 0x00000004; // interrupt priority & fast/slow control + + // source interrupt fields + const uint INTCPS_SIR_IRQ_ACTIVEIRQ_Mask = 0x0000007f; // active IRQ number + const uint INTCPS_SIR_FIQ_ACTIVEFIQ_Mask = 0x0000007f; // active FIQ number + + // interrupt priority fields + const uint INTCPS_IRQ_PRIORITY_Mask = 0x0000003f; // current irq priority + const uint INTCPS_FIQ_PRIORITY_Mask = 0x0000003f; // current fiq priority + + // interrupt level/routing fields + const uint INTCPS_ILR_FIQNIRQ = 0x00000001; // FIQ/IRQ routing + const uint INTCPS_ILR_PRIORITY_Mask = 0x0000000d; // interrupt priority + const int INTCPS_ILR_PRIORITY_Shift = 0x00000002; // interrupt priority + + // registers + private IoMappedPort intcps_revision; // INTCPS block revision + private IoMappedPort intcps_sysconfig; // configuration + private IoMappedPort intcps_sysstatus; // status + private IoMappedPort intcps_sir_irq; // IRQ source interrupt + private IoMappedPort intcps_sir_fiq; // FIQ source interrupt + private IoMappedPort intcps_control; // control + private IoMappedPort intcps_protection; // pic access control + private IoMappedPort intcps_idle; // clock idling + private IoMappedPort intcps_irq_priority; // IRQ priority level + private IoMappedPort intcps_fiq_priority; // FIQ priority level + private IoMappedPort intcps_threshhold; // priority threshold control + private IoMappedPort[] intcps_itr; // raw interrupt status vector + private IoMappedPort[] intcps_mir; // interrupt mask vector + private IoMappedPort[] intcps_mir_clear; // clear interrupt mask vector + private IoMappedPort[] intcps_mir_set; // set interrupt mask vector + private IoMappedPort[] intcps_isr_set; // assert software interrupt vector + private IoMappedPort[] intcps_isr_clear; // clear software interrupt vector + private IoMappedPort[] intcps_pending_irq; // pending (masked) IRQ interrupt vector + private IoMappedPort[] intcps_pending_fiq; // pending (masked) FIQ interrupt vector + private IoMappedPort intcps_ilr; // interrupt level/routing + + // constants for PIC interrupt vectors + const byte INTCPS_Vectors = 0x60; // count of OMAP3430 interrupt vectors + const byte INTCPS_Subvector_Size = 0x20; // size of sub-vectors + + private uint irqMask0; // copy of interrupt mask + private uint irqMask1; // copy of interrupt mask + private uint irqMask2; // copy of interrupt mask + + internal Pic(PnpConfig config) + { + PicResources pr = new PicResources(config); + + IoMemoryRange range = pr.registers; + IoMemory regs = range.MemoryAtOffset(0, PicRegisterSize, Access.ReadWrite); + + intcps_revision = regs.MappedPortAtOffset(INTCPS_REVISION, 4, Access.Read); + intcps_sysconfig = regs.MappedPortAtOffset(INTCPS_SYSCONFIG, 4, Access.ReadWrite); + intcps_sysstatus = regs.MappedPortAtOffset(INTCPS_SYSSTATUS, 4, Access.Read); + intcps_sir_irq = regs.MappedPortAtOffset(INTCPS_SIR_IRQ, 4, Access.Read); + intcps_sir_fiq = regs.MappedPortAtOffset(INTCPS_SIR_FIQ, 4, Access.Read); + intcps_control = regs.MappedPortAtOffset(INTCPS_CONTROL, 4, Access.ReadWrite); + intcps_protection = regs.MappedPortAtOffset(INTCPS_PROTECTION, 4, Access.ReadWrite); + intcps_idle = regs.MappedPortAtOffset(INTCPS_IDLE, 4, Access.ReadWrite); + intcps_irq_priority = regs.MappedPortAtOffset(INTCPS_IRQ_PRIORITY, 4, Access.ReadWrite); + intcps_fiq_priority = regs.MappedPortAtOffset(INTCPS_FIQ_PRIORITY, 4, Access.ReadWrite); + intcps_threshhold = regs.MappedPortAtOffset(INTCPS_THRESHHOLD, 4, Access.ReadWrite); + intcps_itr = CreatePortVector(regs, INTCPS_ITR, Access.Read); + intcps_mir = CreatePortVector(regs, INTCPS_MIR, Access.ReadWrite); + intcps_mir_clear = CreatePortVector(regs, INTCPS_MIR_CLEAR, Access.Write); + intcps_mir_set = CreatePortVector(regs, INTCPS_MIR_SET, Access.Write); + intcps_isr_set = CreatePortVector(regs, INTCPS_ISR_SET, Access.ReadWrite); + intcps_isr_clear = CreatePortVector(regs, INTCPS_ISR_CLEAR, Access.Write); + intcps_pending_irq = CreatePortVector(regs, INTCPS_PENDING_IRQ, Access.Read); + intcps_pending_fiq = CreatePortVector(regs, INTCPS_PENDING_FIQ, Access.Read); + intcps_ilr = regs.MappedPortAtOffset( + INTCPS_ILR_Base, + INTCPS_ILR_Offset * INTCPS_Vectors, + Access.ReadWrite + ); + } + + private static IoMappedPort[] CreatePortVector(IoMemory regs, uint offset, Access access) + { + return new IoMappedPort[3] { + regs.MappedPortAtOffset(offset + 0x00, 4, access), + regs.MappedPortAtOffset(offset + 0x20, 4, access), + regs.MappedPortAtOffset(offset + 0x40, 4, access) + }; + } + + public void Initialize() + { + DebugStub.WriteLine("Pic.Initializing IRQs"); + + // mask all interrupts + MaskAll(); + + // clear all pending interrupts + intcps_isr_clear[0].Write32(0xffffffff); + intcps_isr_clear[1].Write32(0xffffffff); + intcps_isr_clear[2].Write32(0xffffffff); + } + + public void Finalize() + { + UnmaskAll(); + } + + [NoHeapAllocation] + public override byte InterruptToIrq(byte interrupt) + { + DebugStub.WriteLine("Pic.InterruptToIrq({0})", __arglist(interrupt)); + // hack - all h/w interrupts on ARM are routed through a common vector + DebugStub.Assert((interrupt == ExceptionVector.Fiq) || + (interrupt == ExceptionVector.Irq)); + + IoResult result = IoResult.Success; + + if (interrupt == ExceptionVector.Fiq) { + + // read currently active FIQ + ushort sir = intcps_sir_fiq.Read16NoThrow(ref result); + return (byte)(sir & INTCPS_SIR_FIQ_ACTIVEFIQ_Mask); + + } + else { // if (interrupt == ExceptionVector.Irq) + + // read currently active IRQ + ushort sir = intcps_sir_irq.Read16NoThrow(ref result); + return (byte)(sir & INTCPS_SIR_IRQ_ACTIVEIRQ_Mask); + } + } + + [NoHeapAllocation] + public override byte IrqToInterrupt(byte irq) + { + DebugStub.WriteLine("Pic.IrqToInterrupt({0})", __arglist(irq)); + // range check + DebugStub.Assert(irq < INTCPS_Vectors); + + // check where the interrupt is routed: IRQ or FIQ + IoResult result = IoResult.Success; + uint ilr = intcps_ilr.Read32NoThrow(irq * INTCPS_ILR_Offset, ref result); + + if ((ilr & INTCPS_ILR_FIQNIRQ) != 0) { + return (byte)ExceptionVector.Fiq; + } + else { + return (byte)ExceptionVector.Irq; + } + } + + public override byte MaximumIrq + { + [NoHeapAllocation] + get { return INTCPS_Vectors - 1; } + } + + [System.Diagnostics.Conditional("PIC_DEBUG")] + [NoHeapAllocation] + private void DumpBits(String label, IoMappedPort[] regs) + { + IoResult result = IoResult.Success; + uint v0 = regs[0].Read32NoThrow(ref result); + uint v1 = regs[1].Read32NoThrow(ref result); + uint v2 = regs[2].Read32NoThrow(ref result); + + DebugStub.WriteLine(label, __arglist(v0, v1, v2)); + } + + [System.Diagnostics.Conditional("PIC_DEBUG")] + [NoHeapAllocation] + private void DumpRegisters() + { + IoResult result = IoResult.Success; + + ushort currentIrqNumber = (ushort)(intcps_sir_irq.Read16NoThrow(ref result) + & INTCPS_SIR_IRQ_ACTIVEIRQ_Mask); + ushort currentIrqPriority = (ushort)(intcps_irq_priority.Read16NoThrow(ref result) + & (ushort)INTCPS_IRQ_PRIORITY_Mask); + DebugStub.WriteLine("PIC Current IRQ: {0:x4} Priority: {0:x4}", + __arglist(currentIrqNumber, currentIrqPriority)); + + ushort currentFiqNumber = (ushort)(intcps_sir_fiq.Read16NoThrow(ref result) + & INTCPS_SIR_FIQ_ACTIVEFIQ_Mask); + ushort currentFiqPriority = (ushort)(intcps_fiq_priority.Read16NoThrow(ref result) + & INTCPS_FIQ_PRIORITY_Mask); + DebugStub.WriteLine("PIC Current FIQ: {0:x4} Priority: {0:x4}", + __arglist(currentFiqNumber, currentFiqPriority)); + + DumpBits("PIC raw interrupt status: {2:x8}{1:x8}{0:x8}", intcps_itr); + DumpBits("PIC interrupt mask: {2:x8}{1:x8}{0:x8}", intcps_mir); + DumpBits("PIC software interrupts: {2:x8}{1:x8}{0:x8}", intcps_isr_set); + DumpBits("PIC pending IRQs: {2:x8}{1:x8}{0:x8}", intcps_pending_irq); + DumpBits("PIC pending FIQs: {2:x8}{1:x8}{0:x8}", intcps_pending_fiq); + + for (uint i = 0; i < INTCPS_Vectors; ++i) { + + uint ilr = intcps_ilr.Read32NoThrow(i * INTCPS_ILR_Offset, ref result); + + string interruptType = ((ilr & INTCPS_ILR_FIQNIRQ) != 0) ? "fiq" : "irq"; + byte priority = (byte)((ilr & INTCPS_ILR_PRIORITY_Mask) + >> INTCPS_ILR_PRIORITY_Shift); + + DebugStub.WriteLine("PIC ILR[{0}]: {1} Priority: {2:x2}", + __arglist(i, interruptType, priority)); + } + + DebugStub.Assert(IoResult.Success == result); + } + + [NoHeapAllocation] + public override void ClearInterrupt(byte interrupt) + { + byte irq = InterruptToIrq(interrupt); + + Mask(irq); + AckIrq(irq); + Unmask(irq); + +#if DEBUG_INTERRUPTS + DebugStub.WriteLine("pic.ClearInterrupt({2:x8}{1:x8}{0:x8})", + __arglist(irqMask0, irqMask1, irqMask2)); +#endif + } + + [NoHeapAllocation] + public override void AckIrq(byte irq) + { + DebugStub.Assert(Processor.InterruptsDisabled()); +#if PIC_DEBUG + DumpRegisters(); +#endif + + // Mark the IRQ as activated and mask it +#if DEBUG_INTERRUPTS + DebugStub.WriteLine("Int{0:x2} Acked, Mask={3:x8}{2:x8}{1:x8}", + __arglist(irq, irqMask0, irqMask1, irqMask2)); +#endif + + // Quiet the interrupt controller. + IoResult result = IoResult.Success; + uint n = irq / 32u; + uint bit = 1u << (irq % 32); + if (n < intcps_isr_clear.Length) { + intcps_isr_clear[n].Write32NoThrow(bit, ref result); + } + DebugStub.Assert(IoResult.Success == result); + +#if PIC_DEBUG + DumpRegisters(); +#endif + } + + [NoHeapAllocation] + public override void EnableIrq(byte irq) + { + if (irq >= INTCPS_Vectors) { + DebugStub.Break(); + // throw new OverflowException + // (String.Format("irq {0} out of range.", irq)); + } + +#if DEBUG_INTERRUPTS + DebugStub.WriteLine("Int{0:x2} Enable, Mask={3:x8}{2:x8}{1:x8}", + __arglist(irq, irqMask0, irqMask1, irqMask2)); +#endif + + bool saved = Processor.DisableInterrupts(); + try { + Unmask(irq); +#if PIC_DEBUG + DumpRegisters(); +#endif + } + finally { + Processor.RestoreInterrupts(saved); + } + } + + [NoHeapAllocation] + public override void DisableIrq(byte irq) + { + if (irq >= INTCPS_Vectors) { + DebugStub.Break(); + // throw new OverflowException + // (String.Format("irq {0} out of range.", irq)); + } + +#if DEBUG_INTERRUPTS + DebugStub.WriteLine("Int{0:x2} Disable", __arglist(irq)); +#endif + bool saved = Processor.DisableInterrupts(); + try { + Mask(irq); +#if PIC_DEBUG + DumpRegisters(); +#endif + } + finally { + Processor.RestoreInterrupts(saved); + } + } + + ////////////////////////////////////////////////////////////////////// + // + // + [NoHeapAllocation] + private void Mask(byte irq) + { + DebugStub.Assert(Processor.InterruptsDisabled()); + uint n = irq / 32u; + uint bit = 1u << (irq % 32); +#if DEBUG_DISPATCH_IO + DebugStub.WriteLine("PIC.Mask({0}) => {1:x8}", __arglist(irq, bit)); +#endif + + uint mask = GetMaskWord(n); + if ((mask & bit) == 0) { + +#if DEBUG_DISPATCH_IO + DebugStub.WriteLine("-- Mask IRQs [{0}: old={1:x8} new={2:x8}", + __arglist(n, mask, mask | bit)); +#endif + IoResult result = IoResult.Success; + intcps_mir_set[n].Write32NoThrow(bit, ref result); + DebugStub.Assert(IoResult.Success == result); + + SetMaskWord(n, mask | bit); +#if PIC_DEBUG + DumpBits("-- PIC interrupt mask: {2:x8}{1:x8}{0:x8}", intcps_mir); +#endif + } + } + + [NoHeapAllocation] + private void Unmask(byte irq) + { + DebugStub.Assert(Processor.InterruptsDisabled()); + uint n = irq / 32u; + uint bit = 1u << (irq % 32); +#if DEBUG_DISPATCH_IO + DebugStub.WriteLine("PIC.Unmask({0}) => {1:x8}", __arglist(irq, bit)); +#endif + + uint mask = GetMaskWord(n); + if ((mask & bit) != 0) { + +#if DEBUG_DISPATCH_IO + DebugStub.WriteLine("-- Unmask IRQs [{0}: old={1:x8} new={2:x8}", + __arglist(n, mask, mask & ~bit)); +#endif +#if false // Enable this to set interrupt without hardware. + IoResult result = IoResult.Success; + intcps_mir_clear[n].Write32NoThrow(bit, ref result); + intcps_isr_set[n].Write32NoThrow(bit, ref result); + DebugStub.Assert(IoResult.Success == result); +#endif + + SetMaskWord(n, mask & ~bit); +#if PIC_DEBUG + DumpBits("-- PIC interrupt mask: {2:x8}{1:x8}{0:x8}", intcps_mir); +#endif + } + } + + [NoHeapAllocation] + public bool IrqMasked(byte irq) + { + uint n = irq / 32u; + uint bit = 1u << (irq % 32); + + if (irq >= INTCPS_Vectors) { + return true; + } + return ((GetMaskWord(n) & bit) != 0); + } + + [NoHeapAllocation] + private uint GetMaskWord(uint n) + { + switch (n) { + case 0: return irqMask0; + case 1: return irqMask1; + case 2: return irqMask2; + } + return 0; + } + + [NoHeapAllocation] + private void SetMaskWord(uint n, uint value) + { + switch (n) { + case 0: irqMask0 = value; break; + case 1: irqMask1 = value; break; + case 2: irqMask2 = value; break; + } + } + + private void MaskAll() + { + DebugStub.Assert(Processor.InterruptsDisabled()); + + for (int i = 0; i < intcps_mir_set.Length; i++) { + intcps_mir_set[i].Write32(0xffffffff); + } + irqMask0 = irqMask1 = irqMask2 = 0xffffffff; + } + + private void UnmaskAll() + { + DebugStub.Assert(Processor.InterruptsDisabled()); + + for (int i = 0; i < intcps_mir_clear.Length; i++) { + intcps_mir_clear[i].Write32(0xffffffff); + } + irqMask0 = irqMask1 = irqMask2 = 0x0; + } + } +} + diff --git a/base/Kernel/Singularity.Hal.Omap3430/RTClock.cs b/base/Kernel/Singularity.Hal.Omap3430/RTClock.cs new file mode 100644 index 0000000..05690c1 --- /dev/null +++ b/base/Kernel/Singularity.Hal.Omap3430/RTClock.cs @@ -0,0 +1,1115 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: RTClock.cs +// + +// #define VERBOSE + +using Microsoft.Singularity.Io; + +using System; +using System.Threading; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using Microsoft.Singularity.Configuration; + +namespace Microsoft.Singularity.Hal +{ + // + // NB: The RTC is on the Triton2 companion chip and we talk + // to it on the TI 3430 SDP using the 3430's I2C1 @ 0x4b + // + + // declare resources for the kernel manifest + [DriverCategory] + [Signature("/arm/ti/3430/I2C")] + public sealed class I2cResources : DriverCategoryDeclaration + { + // register block + // NB: we may need to share access to i2c1 with peripheral drivers + [IoMemoryRange(0, Default = 0x48070000, Length = 0x80, Shared = true)] + internal readonly IoMemoryRange i2cRegisters; + + // interrupt + // NB: we may need to share access to i2c1 with peripheral drivers + + // i2c1 - interrupt to drive the i2c controller + [IoIrqRange(1, Default = 56, Shared = true)] + internal readonly IoIrqRange i2cIrq; + + // sys_nirq - interrupts directly generated by Triton2 + [IoIrqRange(2, Default = 7, Shared = false)] + internal readonly IoIrqRange tritonIrq; + + // CTR will create the rest of this class: + public I2cResources(IoConfig config) + { + // dynamic resources + i2cRegisters = (IoMemoryRange)config.DynamicRanges[0]; + i2cIrq = (IoIrqRange)config.DynamicRanges[1]; + tritonIrq = (IoIrqRange)config.DynamicRanges[2]; + } + } + + // + // Wrap up control of the I2C bus to allow us to easily access the RTC + // + [CLSCompliant(false)] + internal sealed class I2cOmap3430 + { + // size of registers in block + const int Omap3430_I2C1_RegisterSize = 0x00000058; // size of registers in block + + // clock frequency + const uint Omap3430_TIMER1_Freq = 32768; + + // registers + const uint Omap3430_I2C_REV = 0x00000000; // i2c block revision + const uint Omap3430_I2C_IE = 0x00000004; // interrupt enable + const uint Omap3430_I2C_STAT = 0x00000008; // [interrupt] status + const uint Omap3430_I2C_WE = 0x0000000c; // wakeup enable + const uint Omap3430_I2C_SYSS = 0x00000010; // [non-interrupt] status + const uint Omap3430_I2C_BUF = 0x00000014; // FIFO control + const uint Omap3430_I2C_CNT = 0x00000018; // FIFO data count + const uint Omap3430_I2C_DATA = 0x0000001c; // FIFO data + const uint Omap3430_I2C_SYSC = 0x00000020; // L4 control + const uint Omap3430_I2C_CON = 0x00000024; // control + const uint Omap3430_I2C_OA0 = 0x00000028; // own address 0 + const uint Omap3430_I2C_SA = 0x0000002c; // slave address + const uint Omap3430_I2C_PSC = 0x00000030; // prescale sampling clock + const uint Omap3430_I2C_SCLL = 0x00000034; // SCL low time when master + const uint Omap3430_I2C_SCLH = 0x00000038; // SCL high time when master + const uint Omap3430_I2C_SYSTEST = 0x0000003c; // test-mode register + const uint Omap3430_I2C_BUFSTAT = 0x00000040; // FIFO status + const uint Omap3430_I2C_OA1 = 0x00000044; // own address 1 + const uint Omap3430_I2C_OA2 = 0x00000048; // own address 2 + const uint Omap3430_I2C_OA3 = 0x0000004c; // own address 3 + const uint Omap3430_I2C_ACTOA = 0x00000050; // active own addresses + const uint Omap3430_I2C_SBLOCK = 0x00000054; // slave i2c bus locking + + // revision register fields + const uint Omap3430_I2C_REV_Mask = 0x000000ff; // i2c block revision + const int Omap3430_I2C_REV_Shift = 0; + const uint Omap3430_I2C_REV_Major_Mask = 0x000000f0; // i2c block major revision + const int Omap3430_I2C_REV_Major_Shift = 4; + const uint Omap3430_I2C_REV_Minor_Mask = 0x0000000f; // i2c block minor revision + const int Omap3430_I2C_REV_Minor_Shift = 0; + + // interrupt enable register fields + const uint Omap3430_I2C_IE_XDR_IE = 0x00004000; // transmit draining + const uint Omap3430_I2C_IE_RDR_IE = 0x00002000; // receive draining + const uint Omap3430_I2C_IE_AAS_IE = 0x00000200; // addressed as slave + const uint Omap3430_I2C_IE_BF_IE = 0x00000100; // bus free + const uint Omap3430_I2C_IE_AERR_IE = 0x00000080; // access error + const uint Omap3430_I2C_IE_STC_IE = 0x00000040; // start condition + const uint Omap3430_I2C_IE_GC_IE = 0x00000020; // general call + const uint Omap3430_I2C_IE_XRDY_IE = 0x00000010; // transmit data ready + const uint Omap3430_I2C_IE_RRDY_IE = 0x00000008; // receive data ready + const uint Omap3430_I2C_IE_ARDY_IE = 0x00000004; // register access ready + const uint Omap3430_I2C_IE_NACK_IE = 0x00000002; // no acknowledgment + const uint Omap3430_I2C_IE_AL_IE = 0x00000001; // arbitration lost + + // [interrupt] status register fields + const uint Omap3430_I2C_STAT_XDR = 0x00004000; // transmit draining + const uint Omap3430_I2C_STAT_RDR = 0x00002000; // receive draining + const uint Omap3430_I2C_STAT_BB = 0x00001000; // bus busy + const uint Omap3430_I2C_STAT_ROVR = 0x00000800; // read overrun + const uint Omap3430_I2C_STAT_XUDF = 0x00000400; // transmit underflow + const uint Omap3430_I2C_STAT_AAS = 0x00000200; // addressed as slave + const uint Omap3430_I2C_STAT_BF = 0x00000100; // bus free + const uint Omap3430_I2C_STAT_AERR = 0x00000080; // access error + const uint Omap3430_I2C_STAT_STC = 0x00000040; // start condition + const uint Omap3430_I2C_STAT_GC = 0x00000020; // general call + const uint Omap3430_I2C_STAT_XRDY = 0x00000010; // transmit data ready + const uint Omap3430_I2C_STAT_RRDY = 0x00000008; // receive data ready + const uint Omap3430_I2C_STAT_ARDY = 0x00000004; // register access ready + const uint Omap3430_I2C_STAT_NACK = 0x00000002; // no acknowledgment + const uint Omap3430_I2C_STAT_AL = 0x00000001; // arbitration lost + const uint Omap3430_I2C_STAT_Isr_Mask = 0x000063ff; // ISR mask + + // wakeup enable register fields + const uint Omap3430_I2C_WE_XDR_WE = 0x00004000; // transmit draining + const uint Omap3430_I2C_WE_RDR_WE = 0x00002000; // receive draining + const uint Omap3430_I2C_WE_AAS_WE = 0x00000200; // addressed as slave + const uint Omap3430_I2C_WE_BF_WE = 0x00000100; // bus free + const uint Omap3430_I2C_WE_STC_WE = 0x00000040; // start condition + const uint Omap3430_I2C_WE_GC_WE = 0x00000020; // general call + const uint Omap3430_I2C_WE_DRDY_WE = 0x00000008; // transmit/receive data ready + const uint Omap3430_I2C_WE_ARDY_WE = 0x00000004; // register access ready + const uint Omap3430_I2C_WE_NACK_WE = 0x00000002; // no acknowledgment + const uint Omap3430_I2C_WE_AL_WE = 0x00000001; // arbitration lost + + // [non-interrupt] status register fields + const uint Omap3430_I2C_SSYS_RDONE = 0x00000001; // reset done + + // FIFO control register fields + const uint Omap3430_I2C_BUF_RDMA_EN = 0x00008000; // receive DMA enable + const uint Omap3430_I2C_BUF_RXFIFO_CLR = 0x00004000; // receive FIFO clear + const uint Omap3430_I2C_BUF_RTRSH_Mask = 0x00003f00; // receive threshold + const int Omap3430_I2C_BUF_RTRSH_Shift = 8; + const uint Omap3430_I2C_BUF_XDMA_EN = 0x00000080; // transmit DMA enable + const uint Omap3430_I2C_BUF_TXFIFO_CLR = 0x00000040; // transmit FIFO clear + const uint Omap3430_I2C_BUF_XTRSH_Mask = 0x0000003f; // transmit threshold + const int Omap3430_I2C_BUF_XTRSH_Shift = 0; + + // FIFO data count register fields + const uint Omap3430_I2C_CNT_Mask = 0x0000ffff; // FIFO count + const int Omap3430_I2C_CNT_Shift = 0; + + // FIFO data register fields + const uint Omap3430_I2C_DATA_Mask = 0x000000ff; // FIFO data + const int Omap3430_I2C_DATA_Shift = 0; + + // L4 control register fields + const uint Omap3430_I2C_SYSC_CLOCKACTIVIY_Function = 0x00000200; // function clk on in idle + const uint Omap3430_I2C_SYSC_CLOCKACTIVIY_Interface = 0x00000100; // interface clk on in idle + const uint Omap3430_I2C_SYSC_IDLEMODE_Force = 0x00000000; // force idle mode + const uint Omap3430_I2C_SYSC_IDLEMODE_None = 0x00000080; // never idle + const uint Omap3430_I2C_SYSC_IDLEMODE_Smart = 0x00000100; // smart idling + const uint Omap3430_I2C_SYSC_IDLEMODE_Mask = 0x00000180; // idle mode mask + const uint Omap3430_I2C_SYSC_ENAWAKEUP = 0x00000004; // wakeup enable + const uint Omap3430_I2C_SYSC_SRST = 0x00000002; // software reset + const uint Omap3430_I2C_SYSC_AUTOIDLE = 0x00000001; // auto-idle enable + + // control register fields + const uint Omap3430_I2C_CON_I2C_EN = 0x00008000; // i2c module enable + const uint Omap3430_I2C_CON_OPMODE_Standard_Fast = 0x00000000; // i2c standard/fast mode + const uint Omap3430_I2C_CON_OPMODE_High_Speed = 0x00001000; // i2c high speed mode + const uint Omap3430_I2C_CON_OPMODE_SCCB = 0x00002000; // SCCB mode + const uint Omap3430_I2C_CON_OPMODE_Mask = 0x00003000; // receive FIFO clear + const uint Omap3430_I2C_CON_STB = 0x00000800; // start byte mode (master) + const uint Omap3430_I2C_CON_MST = 0x00000400; // master/slave select + const uint Omap3430_I2C_CON_TRX = 0x00000200; // transmit/receive select + const uint Omap3430_I2C_CON_XSA = 0x00000100; // expanded slave addresses + const uint Omap3430_I2C_CON_XOA0 = 0x00000080; // expand own address 0 + const uint Omap3430_I2C_CON_XOA1 = 0x00000040; // expand own address 1 + const uint Omap3430_I2C_CON_XOA2 = 0x00000020; // expand own address 2 + const uint Omap3430_I2C_CON_XOA3 = 0x00000010; // expand own address 3 + const uint Omap3430_I2C_CON_STP = 0x00000002; // stop condition (master) + const uint Omap3430_I2C_CON_STT = 0x00000001; // start condition (master) + + // own address 0 register fields + const uint Omap3430_I2C_OA0_MCODE_Mask = 0x0000e000; // master code + const int Omap3430_I2C_OA0_MCODE_Shift = 13; + + // own address 1 register fields + // own address 2 register fields + // own address 3 register fields + const uint Omap3430_I2C_OAn_OA_Mask = 0x000003ff; // own address + const int Omap3430_I2C_OAn_OA_Shift = 0; + + // slave address register fields + const uint Omap3430_I2C_SA_SA_Mask = 0x000003ff; // slave address + const int Omap3430_I2C_SA_SA_Shift = 0; + + // prescale sampling clock register fields + const uint Omap3430_I2C_PSC_PSC_Mask = 0x0000000f; // prescale sampling clock + const int Omap3430_I2C_PSC_PSC_Shift = 0; + + // SCL low time when master register fields + const uint Omap3430_I2C_SCLL_HSSCLL_Mask = 0x0000ff00; // SCL low time when master in h/s mode + const int Omap3430_I2C_SCLL_HSSCLL_Shift = 8; + const uint Omap3430_I2C_SCLL_SCLL_Mask = 0x000000ff; // SCL low time when master in standard/fast mode + const int Omap3430_I2C_SCLL_SCLL_Shift = 0; + + // SCL high time when master register fields + const uint Omap3430_I2C_SCLH_HSSCLH_Mask = 0x0000ff00; // SCL high time when master in h/s mode + const int Omap3430_I2C_SCLH_HSSCLH_Shift = 8; + const uint Omap3430_I2C_SCLH_SCLH_Mask = 0x000000ff; // SCL high time when master in standard/fast mode + const int Omap3430_I2C_SCLH_SCLH_Shift = 0; + + // test-mode register register fields + const uint Omap3430_I2C_SYSTEST_ST_EN = 0x00008000; // system test enable + const uint Omap3430_I2C_SYSTEST_FREE = 0x00004000; // free-running mode + const uint Omap3430_I2C_SYSTEST_TMODE_Functional = 0x00000000; // functional test + const uint Omap3430_I2C_SYSTEST_TMODE_Counters = 0x00002000; // counters test + const uint Omap3430_I2C_SYSTEST_TMODE_Loopback_Io = 0x00003000; // loopback / sda/scl io test + const uint Omap3430_I2C_SYSTEST_TMODE_Mask = 0x00003000; // test mode mask + const uint Omap3430_I2C_SYSTEST_SSB = 0x00000800; // set status bits + const uint Omap3430_I2C_SYSTEST_SCCBE_O = 0x00000010; // SCCBE line drive output + const uint Omap3430_I2C_SYSTEST_SCL_I = 0x00000008; // sense SCL line input + const uint Omap3430_I2C_SYSTEST_SCL_O = 0x00000004; // drive SCL line output + const uint Omap3430_I2C_SYSTEST_SDA_I = 0x00000002; // sense SDA line input + const uint Omap3430_I2C_SYSTEST_SDA_O = 0x00000001; // drive SDA line output + + // FIFO status register fields + const uint Omap3430_I2C_BUFSTAT_FIFODEPTH_Mask = 0x0000c000; // lg2(FIFO depth) - 3 + const int Omap3430_I2C_BUFSTAT_FIFODEPTH_Shift = 14; + const uint Omap3430_I2C_BUFSTAT_RXSTAT_Mask = 0x00003f00; // receive count + const int Omap3430_I2C_BUFSTAT_RXSTAT_Shift = 8; + const uint Omap3430_I2C_BUFSTAT_TXSTAT_Mask = 0x0000003f; // transmit count + const int Omap3430_I2C_BUFSTAT_TXSTAT_Shift = 0; + + // active own addresses register fields + const uint Omap3430_I2C_ACTOA_OA3_ACT = 0x00000008; // own addresses 3 active + const uint Omap3430_I2C_ACTOA_OA2_ACT = 0x00000004; // own addresses 2 active + const uint Omap3430_I2C_ACTOA_OA1_ACT = 0x00000002; // own addresses 1 active + const uint Omap3430_I2C_ACTOA_OA0_ACT = 0x00000001; // own addresses 0 active + + // slave i2c bus locking register fields + const uint Omap3430_I2C_SBLOCK_OA3_EN = 0x00000008; // i2c clk blocked for own address 3 + const uint Omap3430_I2C_SBLOCK_OA2_EN = 0x00000004; // i2c clk blocked for own address 2 + const uint Omap3430_I2C_SBLOCK_OA1_EN = 0x00000002; // i2c clk blocked for own address 1 + const uint Omap3430_I2C_SBLOCK_OA0_EN = 0x00000001; // i2c clk blocked for own address 0 + + // resource allocation + private PnpConfig config; + private byte irq; + private Pic pic; + + // registers + private IoMappedPort i2c_rev; // i2c block revision + private IoMappedPort i2c_ie; // interrupt enable + private IoMappedPort i2c_stat; // [interrupt] status + private IoMappedPort i2c_we; // wakeup enable + private IoMappedPort i2c_syss; // [non-interrupt] status + private IoMappedPort i2c_buf; // FIFO control + private IoMappedPort i2c_cnt; // FIFO data count + private IoMappedPort i2c_data; // FIFO data + private IoMappedPort i2c_sysc; // L4 control + private IoMappedPort i2c_con; // control + private IoMappedPort i2c_oa0; // own address 0 + private IoMappedPort i2c_sa; // slave address + private IoMappedPort i2c_psc; // prescale sampling clock + private IoMappedPort i2c_scll; // SCL low time when master + private IoMappedPort i2c_sclh; // SCL high time when master + private IoMappedPort i2c_systest; // test-mode register + private IoMappedPort i2c_bufstat; // FIFO status + private IoMappedPort i2c_oa1; // own address 1 + private IoMappedPort i2c_oa2; // own address 2 + private IoMappedPort i2c_oa3; // own address 3 + private IoMappedPort i2c_actoa; // active own addresses + private IoMappedPort i2c_sblock; // slave i2c bus locking + + // Constructor + internal I2cOmap3430(I2cResources ir, Pic pic) + { + DebugStub.Print("I2cOmap3430: create\n"); + + this.config = config; + this.pic = pic; + this.irq = ir.i2cIrq.Irq; + + IoMemory i2cRegisters = ir.i2cRegisters + .MemoryAtOffset(0, Omap3430_I2C1_RegisterSize, Access.ReadWrite); + + i2c_rev = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_REV, 4, Access.Read); + i2c_ie = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_IE, 4, Access.ReadWrite); + i2c_stat = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_STAT, 4, Access.ReadWrite); + i2c_we = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_WE, 4, Access.ReadWrite); + i2c_syss = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_SYSS, 4, Access.Read); + i2c_buf = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_BUF, 4, Access.ReadWrite); + i2c_cnt = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_CNT, 4, Access.ReadWrite); + i2c_data = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_DATA, 4, Access.ReadWrite); + i2c_sysc = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_SYSC, 4, Access.ReadWrite); + i2c_con = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_CON, 4, Access.ReadWrite); + i2c_oa0 = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_OA0, 4, Access.ReadWrite); + i2c_sa = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_SA, 4, Access.ReadWrite); + i2c_psc = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_PSC, 4, Access.ReadWrite); + i2c_scll = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_SCLL, 4, Access.ReadWrite); + i2c_sclh = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_SCLH, 4, Access.ReadWrite); + i2c_systest = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_SYSTEST, 4, Access.ReadWrite); + i2c_bufstat = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_BUFSTAT, 4, Access.Read); + i2c_oa1 = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_OA1, 4, Access.ReadWrite); + i2c_oa2 = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_OA2, 4, Access.ReadWrite); + i2c_oa3 = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_OA3, 4, Access.ReadWrite); + i2c_actoa = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_ACTOA, 4, Access.Read); + i2c_sblock = i2cRegisters.MappedPortAtOffset(Omap3430_I2C_SBLOCK, 4, Access.ReadWrite); + } + + public byte Initialize() + { +#if VERBOSE + Tracing.Log(Tracing.Debug, "I2COmap3430.Initialize()"); +#endif + + // Take i2c controller into reset + ushort val16 = i2c_con.Read16(); + val16 = (ushort) (val16 & ~((ushort)Omap3430_I2C_CON_I2C_EN)); + IoResult result = i2c_con.Write16NoThrow(val16); + DebugStub.Assert(IoResult.Success == result); + + // Configure internal sampling frequency for 12Mhz. + result = i2c_psc.Write8NoThrow((byte)((0x7 << Omap3430_I2C_PSC_PSC_Shift) & Omap3430_I2C_PSC_PSC_Mask)); + DebugStub.Assert(IoResult.Success == result); + + // Configure SCL high/low times for 100 kb/s + result = i2c_scll.Write8NoThrow((byte)((0x36 << Omap3430_I2C_SCLL_SCLL_Shift) & Omap3430_I2C_SCLL_SCLL_Mask)); + DebugStub.Assert(IoResult.Success == result); + + result = i2c_sclh.Write8NoThrow((byte)((0x38 << Omap3430_I2C_SCLH_SCLH_Shift) & Omap3430_I2C_SCLH_SCLH_Mask)); + DebugStub.Assert(IoResult.Success == result); + + // Set own address address register + result = i2c_oa0.Write16NoThrow((ushort)((0x21 << Omap3430_I2C_OAn_OA_Shift) & Omap3430_I2C_OAn_OA_Mask)); + DebugStub.Assert(IoResult.Success == result); + + // Take i2c controller out of reset + result = i2c_con.Read16NoThrow(out val16); + DebugStub.Assert(IoResult.Success == result); + + val16 |= (ushort)Omap3430_I2C_CON_I2C_EN; + + result = i2c_con.Write16NoThrow(val16); + DebugStub.Assert(IoResult.Success == result); + + // Enable i2c interrupts + pic.EnableIrq(irq); + return pic.IrqToInterrupt(irq); + } + + [NoHeapAllocation] + public bool MasterSend(byte slaveAddress, byte[] data, bool genStart, bool genStop) + { + return MasterCommon(true, slaveAddress, data, genStart, genStop); + } + + [NoHeapAllocation] + public bool MasterReceive(byte slaveAddress, byte[] data, bool genStart, bool genStop) + { + return MasterCommon(false, slaveAddress, data, genStart, genStop); + } + + [NoHeapAllocation] + private bool MasterSetup( + bool xmit, + byte slaveAddress, + ushort length, + bool genStart, + bool genStop + ) + { + ushort control; + byte retry = 100; + + IoResult result = i2c_con.Write16NoThrow((ushort)Omap3430_I2C_CON_I2C_EN); + DebugStub.Assert(IoResult.Success == result); + + // Set slave address + result = i2c_sa.Write8NoThrow(slaveAddress); + DebugStub.Assert(IoResult.Success == result); + + // Set transfer length + result = i2c_cnt.Write16NoThrow(length); + DebugStub.Assert(IoResult.Success == result); + + // Clear status + result = i2c_stat.Write16NoThrow((ushort)(Omap3430_I2C_STAT_ARDY | Omap3430_I2C_STAT_NACK | Omap3430_I2C_STAT_AL)); + DebugStub.Assert(IoResult.Success == result); + + // Read bus status + ushort status; + result = i2c_stat.Read16NoThrow(out status); + DebugStub.Assert(IoResult.Success == result); + + // Wait for free bus + while ((status & Omap3430_I2C_STAT_BB) != 0) { + if (--retry == 0) { + return false; + } + + result = i2c_stat.Read16NoThrow(out status); + DebugStub.Assert(IoResult.Success == result); + } + + control = (ushort)( + Omap3430_I2C_CON_I2C_EN | // enable i2c + Omap3430_I2C_CON_MST | // i2c master + (xmit ? Omap3430_I2C_CON_TRX : 0) | // transmit/receive + (genStart ? Omap3430_I2C_CON_STT : 0) | // start + (genStop ? Omap3430_I2C_CON_STP : 0) // stop + ); + + result = i2c_con.Write16NoThrow(control); + DebugStub.Assert(IoResult.Success == result); + + return true; + } + + [NoHeapAllocation] + private bool MasterTransfer( + bool xmit, + byte[] data + ) + { + ushort length = (ushort)data.Length; + ushort index = 0; + + for (;;) + { + ushort status; + ushort bytesAvailable; + + // Read bus status + IoResult result = i2c_stat.Read16NoThrow(out status); + DebugStub.Assert(IoResult.Success == result); + + if ((status & (Omap3430_I2C_STAT_NACK | Omap3430_I2C_STAT_AL)) != 0) { + return false; + } + + if ((status & (Omap3430_I2C_STAT_ARDY)) != 0) { + DebugStub.Assert(length == 0); + return true; + } + + if (xmit) { + if ((status & Omap3430_I2C_STAT_XDR) != 0) { + ushort bufferStatus; + + // Read FIFO status + result = i2c_bufstat.Read16NoThrow(out bufferStatus); + DebugStub.Assert(IoResult.Success == result); + + bytesAvailable = (ushort)((bufferStatus & Omap3430_I2C_BUFSTAT_TXSTAT_Mask) >> Omap3430_I2C_BUFSTAT_TXSTAT_Shift); + + while (bytesAvailable-- != 0) { + + result = i2c_data.Write8NoThrow(data[index]); + DebugStub.Assert(IoResult.Success == result); + + index++; + length--; + } + } + + if ((status & Omap3430_I2C_STAT_XRDY) != 0) { + ushort bufferControl; + + // Read FIFO threshold + result = i2c_buf.Read16NoThrow(out bufferControl); + DebugStub.Assert(IoResult.Success == result); + + bytesAvailable = (ushort)((bufferControl & Omap3430_I2C_BUF_XTRSH_Mask) >> Omap3430_I2C_BUF_XTRSH_Shift); + bytesAvailable += 1; + + while (bytesAvailable-- != 0) { + + result = i2c_data.Write8NoThrow(data[index]); + DebugStub.Assert(IoResult.Success == result); + + index++; + length--; + } + } + + } else { + + if ((status & Omap3430_I2C_STAT_RDR) != 0) { + ushort bufferStatus; + + // Read FIFO status + result = i2c_bufstat.Read16NoThrow(out bufferStatus); + DebugStub.Assert(IoResult.Success == result); + + bytesAvailable = (ushort)((bufferStatus & Omap3430_I2C_BUFSTAT_RXSTAT_Mask) >> Omap3430_I2C_BUFSTAT_RXSTAT_Shift); + + while (bytesAvailable-- != 0) { + + result = i2c_data.Read8NoThrow(out data[index]); + DebugStub.Assert(IoResult.Success == result); + + index++; + length--; + } + } + + if ((status & Omap3430_I2C_STAT_RRDY) != 0) { + ushort bufferControl; + + // Read FIFO threshold + result = i2c_buf.Read16NoThrow(out bufferControl); + DebugStub.Assert(IoResult.Success == result); + + bytesAvailable = (ushort)((bufferControl & Omap3430_I2C_BUF_RTRSH_Mask) >> Omap3430_I2C_BUF_RTRSH_Shift); + bytesAvailable += 1; + + while (bytesAvailable-- != 0) { + + result = i2c_data.Read8NoThrow(out data[index]); + DebugStub.Assert(IoResult.Success == result); + + index++; + length--; + } + } + } + + result = i2c_stat.Write16NoThrow(status); + DebugStub.Assert(IoResult.Success == result); + } + } + + [NoHeapAllocation] + private bool MasterCommon( + bool xmit, + byte slaveAddress, + byte[] data, + bool genStart, + bool genStop + ) + { + int length = data.Length; + DebugStub.Assert(length <= 0xffffu); + + int retry = 10; + + while (retry-- != 0) { + + MasterSetup(xmit, slaveAddress, (ushort)length, genStart, genStop); + + if (MasterTransfer(xmit, data)) { + return true; + } + } + + return false; + } + + public void Finalize() + { + pic.DisableIrq(irq); + } + } + + /// + /// Class RTClock represents the system Real-Time Clock. + /// + /// RTC chips on ARM are provided by other sources, such as + /// the TWL4030 companion chip. The TWL 4030 RTC provides a + /// timing resolution of 1 second, and can only generate + /// periodic interrupts that frequently. + /// + /// By combining information from the TimerOmap3430 + /// programmable timer, time can be read with a resolution + /// of xxx microseconds. + /// + [CLSCompliant(false)] + internal sealed class RTClockOmap3430 + { + private PnpConfig config; + private Pic pic; + private byte irq; + private TimerOmap3430 timer = null; + private I2cOmap3430 i2c1; + + private byte[] i2cReadBuffer = new byte[1]; + private byte[] i2cWriteBuffer = new byte[2]; + + const int InterruptGapTicks = 10000000; // units of 100ns + + const uint maxAttempts = 1000000; + + private RtcPitState rps = null; + private long rtcBootTime; + private volatile uint irqCount = 0; + + // Variables for fast GetKernelTicks implementation that uses + // TSC when it is deemed safe. + bool noisyTimer; // Running on VPC? Disable optimization + long pmccntrSnapshot; // TSC value at sync point + long ticksSnapshot; // Kernel ticks at sync point + long lastKernelTicks; // Last kernel ticks reported + + bool pmccntrSnapshotValid = false; + bool initedFastTime = false; + + int tickScale; + const int tickRoll = 24; + + // Triton2 i2c bus addresses + const byte TWL4030_ID1_I2c_Address = 0x48; // i2c address for ID1 + const byte TWL4030_ID2_I2c_Address = 0x49; // i2c address for ID2 + const byte TWL4030_ID3_I2c_Address = 0x4a; // i2c address for ID3 + const byte TWL4030_ID4_I2c_Address = 0x4b; // i2c address for ID4 + + // Triton2 register addresses + const byte TWL4030_SECONDS_REG = 0x1c; // BCD seconds, 0 -> 59 + const byte TWL4030_MINUTES_REG = 0x1d; // BCD minutes, 0 -> 59 + const byte TWL4030_HOURS_REG = 0x1e; // BCD hours, 0 -> 23 or 0 -> 11 (am/pm) + const byte TWL4030_DAYS_REG = 0x1f; // BCD days, 0 -> 31 + const byte TWL4030_MONTHS_REG = 0x20; // BCD month, 1 -> 12 + const byte TWL4030_YEARS_REG = 0x21; // BCD year, [20]00 -> [20]99 + const byte TWL4030_WEEKS_REG = 0x22; // BCD day of week, 0 -> 6 + const byte TWL4030_ALARM_SECONDS_REG = 0x23; // Alarm time, BCD seconds, 0 -> 59 + const byte TWL4030_ALARM_MINUTES_REG = 0x24; // Alarm time, BCD minutes, 0 -> 59 + const byte TWL4030_ALARM_HOURS_REG = 0x25; // Alarm time, BCD hours, 0 -> 23 or 0 -> 11 (am/pm) + const byte TWL4030_ALARM_DAYS_REG = 0x26; // Alarm time, BCD days, 0 -> 31 + const byte TWL4030_ALARM_MONTHS_REG = 0x27; // Alarm time, BCD month, 1 -> 12 + const byte TWL4030_ALARM_YEARS_REG = 0x28; // Alarm time, BCD year, [20]00 -> [20]99 + const byte TWL4030_RTC_CTRL_REG = 0x29; // control + const byte TWL4030_RTC_STATUS_REG = 0x2a; // status + const byte TWL4030_RTC_INTERRUPTS_REG = 0x2b; // interrupt status + const byte TWL4030_RTC_COMP_LSB_REG = 0x2c; // clock compensation, comp_reg[15:8], in (1/32768)s + const byte TWL4030_RTC_COMP_MSB_REG = 0x2d; // clock compensation, comp_reg[7:0], in (1/32768)s + + const byte TWL4030_PIH_ISR_P1 = 0x81; // programmable interrupt handler ISR for irq1 + const byte TWL4030_PIH_ISR_P2 = 0x82; // programmable interrupt handler ISR for irq2 + const byte TWL4030_PIH_SIR = 0x83; // software interrupt ISR + + const byte TWL4030_PWR_ISR1 = 0x2e; // power ISR + const byte TWL4030_PWR_IMR1 = 0x2f; // power interrupt mask + const byte TWL4030_PWR_EDR1 = 0x33; // power edge detection + const byte TWL4030_PWR_SIH_CTRL = 0x35; // power secondary interrupt handler control + + // Triton2 register fields + const byte TWL4030_SECONDS_REG_Mask = 0x7f; // BCD seconds, 0 -> 59 + const byte TWL4030_MINUTES_REG_Mask = 0x7f; // BCD minutes, 0 -> 59 + const byte TWL4030_HOURS_REG_Mask = 0x3f; // BCD hours, 0 -> 23 or 0 -> 11 (am/pm) + const byte TWL4030_HOURS_REG_PM_NAM = 0x80; // pm/~am flag (am/pm mode) + const byte TWL4030_DAYS_REG_Mask = 0x3f; // BCD days, 0 -> 31 + const byte TWL4030_MONTHS_REG_Mask = 0x1f; // BCD month, 1 -> 12 + const byte TWL4030_YEARS_REG_Mask = 0xff; // BCD year, [20]00 -> [20]99 + + const byte TWL4030_WEEKS_REG_Mask = 0x07; // BCD day of week, 0 -> 6 + + const byte TWL4030_ALARM_SECONDS_REG_Mask = 0x7f; // Alarm time, BCD seconds, 0 -> 59 + const byte TWL4030_ALARM_MINUTES_REG_Mask = 0x7f; // Alarm time, BCD minutes, 0 -> 59 + const byte TWL4030_ALARM_HOURS_REG_Mask = 0x3f; // Alarm time, BCD hours, 0 -> 23 or 0 -> 11 (am/pm) + const byte TWL4030_ALARM_HOURS_REG_PM_NAM = 0x80; // Alarm time, pm/~am flag (am/pm mode) + const byte TWL4030_ALARM_DAYS_REG_Mask = 0x3f; // Alarm time, BCD days, 0 -> 31 + const byte TWL4030_ALARM_MONTHS_REG_Mask = 0x1f; // Alarm time, BCD month, 1 -> 12 + const byte TWL4030_ALARM_YEARS_REG_Mask = 0xff; // Alarm time, BCD year, [20]00 -> [20]99 + + const byte TWL4030_RTC_CTRL_REG_GET_TIME = 0x40; // latch the time into the alarm registers + const byte TWL4030_RTC_CTRL_REG_SET_32_COUNTER = 0x20; // set the 32kHz counter from comp_reg + const byte TWL4030_RTC_CTRL_REG_TEST_MODE = 0x10; // test mode + const byte TWL4030_RTC_CTRL_REG_MODE_12_24 = 0x08; // enable am/pm mode (when set) + const byte TWL4030_RTC_CTRL_REG_AUTO_COMP = 0x04; // enable auto-compensation, as set in TWL4030_RTC_COMP_*_REG + const byte TWL4030_RTC_CTRL_REG_ROUND_30S = 0x02; // round time to closest minute on the next second + const byte TWL4030_RTC_CTRL_REG_STOP_RTC = 0x01; // ~RTC frozen (frozen when low) + + const byte TWL4030_RTC_STATUS_REG_POWER_UP = 0x80; // RTC reset + const byte TWL4030_RTC_STATUS_REG_ALARM = 0x40; // alarm interrupt + const byte TWL4030_RTC_STATUS_REG_1D_EVENT = 0x20; // day interval + const byte TWL4030_RTC_STATUS_REG_1H_EVENT = 0x10; // hour interval + const byte TWL4030_RTC_STATUS_REG_1M_EVENT = 0x08; // minute interval + const byte TWL4030_RTC_STATUS_REG_1S_EVENT = 0x04; // second interval + const byte TWL4030_RTC_STATUS_REG_RUN = 0x02; // RTC running + + const byte TWL4030_RTC_INTERRUPTS_REG_IT_ALARM = 0x08; // enable alarm interrupt + const byte TWL4030_RTC_INTERRUPTS_REG_IT_TIMER = 0x04; // enable periodic interrupt + const byte TWL4030_RTC_INTERRUPTS_REG_EVERY_Mask = 0x03; // interrupt period mask + const byte TWL4030_RTC_INTERRUPTS_REG_EVERY_Day = 0x03; // daily periodic interrupt + const byte TWL4030_RTC_INTERRUPTS_REG_EVERY_Hour = 0x02; // hourly periodic interrupt + const byte TWL4030_RTC_INTERRUPTS_REG_EVERY_Minute = 0x01; // periodic interrupt every minute + const byte TWL4030_RTC_INTERRUPTS_REG_EVERY_Second = 0x00; // periodic interrupt every second + + const byte TWL4030_PIH_ISR_P1_PIH_ISR5 = 0x81; // power management interrupt + const byte TWL4030_PWR_ISR1_RTC_IT = 0x08; // RTC interrupt + const byte TWL4030_PWR_IMR1_RTC_IT = 0x08; // RTC interrupt mask + const byte TWL4030_PWR_EDR1_RTC_IT_RISING = 0x80; // RTC interrupt rising edge detect + const byte TWL4030_PWR_EDR1_RTC_IT_FALLING = 0x40; // RTC interrupt falling edge detect + const byte TWL4030_PWR_SIH_CTRL_COR = 0x04; // ~clear on read (clear on write when set) + + // Constructor + internal RTClockOmap3430(PnpConfig config, Pic pic, TimerOmap3430 timer) + { + DebugStub.Print("RTClock: create\n"); + + I2cResources ir = new I2cResources(config); + + this.config = config; + this.pic = pic; + this.irq = ir.tritonIrq.Irq; //sys_nirq + this.timer = timer; + + this.i2c1 = new I2cOmap3430(ir, pic); + } + + [NoHeapAllocation] + private bool ReadTritonRegister(byte i2c_addr, byte triton_addr, out byte value) + { + this.i2cReadBuffer[0] = triton_addr; + + bool success = i2c1.MasterSend(i2c_addr, this.i2cReadBuffer, true, false); + + if (success) { + success = i2c1.MasterReceive(i2c_addr, this.i2cReadBuffer, false, true); + } + + // NB: value is trash if we don't succeed. We can't raise an exception, etc. + // as we can't allocate on the heap! + value = this.i2cReadBuffer[0]; + + return success; + } + + [NoHeapAllocation] + private bool WriteTritonRegister(byte i2c_addr, byte triton_addr, byte value) + { + this.i2cWriteBuffer[0] = triton_addr; + this.i2cWriteBuffer[1] = value; + + return i2c1.MasterSend(i2c_addr, this.i2cReadBuffer, true, true); + } + + [NoHeapAllocation] + private byte ReadRtc(byte triton_addr) + { + byte value; + + bool success = ReadTritonRegister(TWL4030_ID4_I2c_Address, triton_addr, out value); + DebugStub.Assert(success); + + return value; + } + + [NoHeapAllocation] + private void WriteRtc(byte triton_addr, byte value) + { + bool success = WriteTritonRegister(TWL4030_ID4_I2c_Address, triton_addr, value); + DebugStub.Assert(success); + } + + [NoHeapAllocation] + private byte ReadPih(byte triton_addr) + { + byte value; + + bool success = ReadTritonRegister(TWL4030_ID2_I2c_Address, triton_addr, out value); + DebugStub.Assert(success); + + return value; + } + + [NoHeapAllocation] + private void WritePih(byte triton_addr, byte value) + { + bool success = WriteTritonRegister(TWL4030_ID2_I2c_Address, triton_addr, value); + DebugStub.Assert(success); + } + + [NoHeapAllocation] + public void ClearInterrupt() + { + byte status = ReadRtc(TWL4030_RTC_STATUS_REG); + + if (((ReadPih(TWL4030_PIH_ISR_P1) & TWL4030_PIH_ISR_P1_PIH_ISR5) == 0) || + ((ReadPih(TWL4030_PWR_ISR1) & TWL4030_PWR_ISR1_RTC_IT) == 0) || + ((status & TWL4030_RTC_STATUS_REG_1S_EVENT) == 0)) { + // Spurious Triton2 interrupt... + pic.AckIrq(irq); + return; + } + + ClockLogger.AddEntry(4, rps, timer); + rps.pitLastClock = 0; // timer.Timer2Read(); + rps.upTime += InterruptGapTicks; + + ClockLogger.AddEntry(5, rps, timer); + irqCount++; + + if (timer.InterruptPending == false) { + // This is to keep time progressing if the user has + // not set an interrupt + //timer.SetKeepAliveInterrupt(); + } + WritePih(TWL4030_PWR_ISR1, TWL4030_PWR_ISR1_RTC_IT); + pic.AckIrq(irq); + + // Invalidate TSC snapshot to force clock synchronization + this.pmccntrSnapshotValid = false; + } + + internal byte Initialize() + { + DebugStub.Print("RTClock: initialize\n"); + + rps = timer.rps; + + // Start the RTC (if not already started) + WriteRtc(TWL4030_RTC_CTRL_REG, TWL4030_RTC_CTRL_REG_STOP_RTC); + + // Disable interrupts for now + WriteRtc(TWL4030_RTC_INTERRUPTS_REG, 0); + + WritePih(TWL4030_PWR_SIH_CTRL, TWL4030_PWR_SIH_CTRL_COR); + WritePih(TWL4030_PWR_IMR1, 0); + + // Capture the boot time + rtcBootTime = PullRtcTime().Ticks; + + // Enable and clear interrupts + pic.EnableIrq(irq); + return pic.IrqToInterrupt(irq); + } + + internal void Start() + { + DebugStub.Print("RTClock::Start()\n"); + + // Enable interrupt every second + WriteRtc(TWL4030_RTC_INTERRUPTS_REG, (TWL4030_RTC_INTERRUPTS_REG_IT_TIMER | TWL4030_RTC_INTERRUPTS_REG_EVERY_Second)); + WritePih(TWL4030_PWR_IMR1, unchecked((byte)~TWL4030_PWR_IMR1_RTC_IT)); + } + + internal void Finalize() + { + pic.DisableIrq(irq); + } + + [NoHeapAllocation] + bool InitializeFastTime() + { + const ulong KernelTicksPerSecond = 10000000; + ulong cps = Processor.CyclesPerSecond; + if (cps >= KernelTicksPerSecond) { + this.pmccntrSnapshot = 0; + this.ticksSnapshot = 0; + this.tickScale = + (int)((1 << tickRoll) * KernelTicksPerSecond / cps); +#if VERBOSE + DebugStub.Print( + "tick scale = {0} (actual {1:d}, fast {2:d})", + __arglist(this.tickScale, + cps, + ((1 << tickRoll) * KernelTicksPerSecond / + (ulong)this.tickScale)) + ); +#endif // VERBOSE + return true; + } + return false; + } + + internal void SetNoisyTimer(bool noisy) + { + this.noisyTimer = noisy; + } + + [NoHeapAllocation] + private long GetKernelTicksFromHW() + { + bool iflag = Processor.DisableInterrupts(); + try { + ClockLogger.AddEntry(100, rps, timer); + long r = rps.upTime; //rps.GetKernelTicks(timer.Timer2Read()); + return r; + } + finally { + Processor.RestoreInterrupts(iflag); + } + } + + [NoHeapAllocation] + private long GetKernelTicksFromPmccntr() + { + long now = unchecked((long)Processor.CycleCount); + long pmccntrDelta = now - this.pmccntrSnapshot; + + long tickDelta = (pmccntrDelta * tickScale) >> tickRoll; + DebugStub.Assert(tickDelta >= 0); + + return ticksSnapshot + tickDelta; + } + + [NoHeapAllocation] + private long InternalGetKernelTicks() + { + if (!initedFastTime) { + initedFastTime = InitializeFastTime(); + } + + if (this.noisyTimer || !initedFastTime) { + // Pull time from the hardware as the PMSCCNTR + // is not being used. + return GetKernelTicksFromHW(); + } + else { + if (!pmccntrSnapshotValid) { + // When waking up from a processor halt + // event or from a clock interrupt, sync + // with the fixed h/w's concept of time. + this.ticksSnapshot = GetKernelTicksFromHW(); + + this.pmccntrSnapshot = unchecked((long)Processor.CycleCount); + this.pmccntrSnapshotValid = true; + + return ticksSnapshot; + } + else { + return GetKernelTicksFromPmccntr(); + } + } + } + + [NoHeapAllocation] + public long GetKernelTicks() + { +#if DEBUG_TIMER + { + long cpuT = GetKernelTicksFromPmccntr(); + long hwT = GetKernelTicksFromHW(); + + if (cpuT - hwT > 5000 || hwT - cpuT < -5000) { + DebugStub.Print("Delta = {0:d} (hw {1:d} cpu {2:d})\n", + __arglist(cpuT - hwT, hwT, cpuT)); + } + } +#endif // DEBUG_TIMER + + // We use the PMCCNTR to provide fast kernel ticks as + // far as possible, and periodically sync against an + // off-chip clock. Because there are different time + // sources and we don't ever want to report time + // going backwards, we need to check that time would + // reported to have advanced or remained the same, + // and that the clock has not wrapped. + long kernelTicks = InternalGetKernelTicks(); + + if (kernelTicks >= lastKernelTicks) { + lastKernelTicks = kernelTicks; + return kernelTicks; + } + else if (kernelTicks < 0 && lastKernelTicks > 0) { + // clock wrap + return kernelTicks; + } +#if DEBUG_TIMER + // Skew from switching between pmccntr and underlying clock + DebugStub.Print("Backwards delta = {0} (HW {1} PMCCNTR {2})\n", + __arglist(kernelTicks - lastKernelTicks, + GetKernelTicksFromHW(), + GetKernelTicksFromPmccntr()) + ); +#endif // DEBUG_TIMER + return lastKernelTicks; + } + + [NoHeapAllocation] + public void CpuResumeFromHaltEvent() + { + pmccntrSnapshotValid = false; + } + + [NoHeapAllocation] + public long GetRtcTime() + { + return rtcBootTime + GetKernelTicks(); + } + + public void SetRtcTime(long rtcTime) + { + long delta = rtcTime - GetRtcTime(); + rtcBootTime = rtcBootTime + delta; + PushRtcTime(new DateTime(rtcTime)); + } + + [NoHeapAllocation] + internal static byte BcdToHex(byte bcd) + { + return (byte) (10 * (bcd >> 4) + (bcd & 0x0f)); + } + + [NoHeapAllocation] + internal static byte HexToBcd(int hex) + { + int h = hex / 10; + return (byte) ((h << 4) | (hex % 10)); + } + + private DateTime PullRtcTime() + { + bool iflag = Processor.DisableInterrupts(); + + // Read out captured time + byte second = 0xff; + byte minute = 0xff; + byte hour = 0xff; + byte day = 0xff; + byte month = 0xff; + byte year = 0xff; + + try { + // Capture current time + byte control = ReadRtc(TWL4030_RTC_CTRL_REG); + control |= TWL4030_RTC_CTRL_REG_GET_TIME; + WriteRtc(TWL4030_RTC_CTRL_REG, control); + + // Read out captured time + second = BcdToHex((byte)(ReadRtc(TWL4030_ALARM_SECONDS_REG) & TWL4030_ALARM_SECONDS_REG_Mask)); + minute = BcdToHex((byte)(ReadRtc(TWL4030_ALARM_MINUTES_REG) & TWL4030_ALARM_MINUTES_REG_Mask)); + hour = BcdToHex((byte)(ReadRtc(TWL4030_ALARM_HOURS_REG) & TWL4030_ALARM_HOURS_REG_Mask)); + day = BcdToHex((byte)(ReadRtc(TWL4030_ALARM_DAYS_REG) & TWL4030_ALARM_DAYS_REG_Mask)); + month = BcdToHex((byte)(ReadRtc(TWL4030_ALARM_MONTHS_REG) & TWL4030_ALARM_MONTHS_REG_Mask)); + year = BcdToHex((byte)(ReadRtc(TWL4030_ALARM_YEARS_REG) & TWL4030_ALARM_YEARS_REG_Mask)); + + DebugStub.Print("PullRtcTime: " + + "({0:d}:{1:d2}:{2:d2} {3:d}/{4:d}/20{5:d2}" + + ")\n", + __arglist(hour, + minute, + second, + day, + month, + year)); + + return new DateTime(2000 + year, + month, day, hour, minute, + second); + } + catch (ArgumentOutOfRangeException e) { + DebugStub.Print("PullRtcTime failed: {0} " + + "({1:d}:{2:d2}:{3:d2} {4:d}/{5:d}/20{6:d2}" + + ")\n", + __arglist(e, + hour, + minute, + second, + day, + month, + year)); + } + finally { + Processor.RestoreInterrupts(iflag); + } + + return new DateTime(2007, 7, 5, 16, 32, 45); + } + + private void PushRtcTime(DateTime dt) + { + bool iflag = Processor.DisableInterrupts(); + byte control = ReadRtc(TWL4030_RTC_CTRL_REG); + + try { + // Freeze RTC + WriteRtc(TWL4030_RTC_CTRL_REG, (byte)(control & ~TWL4030_RTC_CTRL_REG_STOP_RTC)); + + // Write new values + WriteRtc(TWL4030_ALARM_SECONDS_REG, HexToBcd(dt.Second)); + WriteRtc(TWL4030_ALARM_MINUTES_REG, HexToBcd(dt.Minute)); + WriteRtc(TWL4030_ALARM_HOURS_REG, HexToBcd(dt.Hour)); + WriteRtc(TWL4030_ALARM_DAYS_REG, HexToBcd(dt.Day)); + WriteRtc(TWL4030_ALARM_MONTHS_REG, HexToBcd(dt.Month)); + WriteRtc(TWL4030_ALARM_YEARS_REG, HexToBcd(dt.Year % 100)); + + // to mis-quote Ford: + // "you can have any century you like, as long as it's the 21st..." + DebugStub.Print("PushRtcTime: " + + "({0:d}:{1:d2}:{2:d2} {3:d}/{4:d}/20{5:d2}" + + ")\n", + __arglist(dt.Hour, + dt.Minute, + dt.Second, + dt.Day, + dt.Month, + dt.Year % 100)); + if ((dt.Year / 100) != 20) { + DebugStub.Print("PushRtcTime: truncating year {0:d4}->20{1:d2}\n", + __arglist(dt.Year, dt.Year % 100)); + } + } + finally { + // Run RTC again from new value + WriteRtc(TWL4030_RTC_CTRL_REG, control); + Processor.RestoreInterrupts(iflag); + } + } + } +} diff --git a/base/Kernel/Singularity.Hal.Omap3430/RtcPitState.cs b/base/Kernel/Singularity.Hal.Omap3430/RtcPitState.cs new file mode 100644 index 0000000..e6db3f6 --- /dev/null +++ b/base/Kernel/Singularity.Hal.Omap3430/RtcPitState.cs @@ -0,0 +1,165 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: RtcPitState.cs +// +// Useful reference URLs: +// http://developer.intel.com/design/archives/periphrl/index.htm +// http://developer.intel.com/design/archives/periphrl/docs/7203.htm +// http://developer.intel.com/design/archives/periphrl/docs/23124406.htm +// +// The basic ideas for this driver come from the MMOSA code, +// though the implementation differs. This is partly because +// the code needs to run on Virtual PC and it isn't able to do +// a very accurate emulation of the i8254. +// +// There are two source available for timing - the Real-Time +// Clock (RTC) and the programmable interval timer (PIT). The +// standard PC RTC is based on derivatives of the Motorola +// MC146818A. It's able to provide the time with a resolution +// of 1 second and also has a programmable periodic interrupt. +// +// The programmable interrupt timer is based on the i8254. It +// can be programmed in a variety of modes - we use it to +// generate an interrupt at a configurable time in the future +// and then reprogram it each interrupt. The maximum interrupt +// period is 65535 ticks of a 1.193MHz clock. +// +// We use both of the RTC and the programmable interrupt timer to +// maintain our estimate of the current time. The RTC provides granularity +// to with 1/64 seconds and the time is used to get an estimate to within +// 1/1.193 * 10e-6 seconds within each RTC interval. +// +// The key variables are: +// +// upTime - the time the system has been up. This variable gets +// updated during the periodic RTC interrupt handling +// (delta = 1/64Hz). +// +// pitLast - the last value programmed into the PIT. The PIT counts down +// and generates an interrupt at (or shortly after) the instant +// the current PIT value reaches zero. +// +// pitAccum - the accumulated time measured by the PIT since upTime +// was updated. +// +// The current kernel time is always: +// upTime + pitAccum + (pitLast - pitCurrent) +// +// The PIT is always programmed to run, either by the consumer of the timer +// interface or by the timer itself. +// +// Timer::SetNextInterrupt(t) +// pitAccum += (pitLast - pitCurrent) +// // program PIT (not shown) +// pitLast = t +// +// Timer::Interrupt() +// pitAccum += pitLast; +// // But PIT time may accumulate between interrupt dispatch and crossing +// // Zero so. +// if (pitCurrent != 0) +// pitAccum += (MaxPitValue - pitCurrent) +// // Inform user of interrupt +// if (userNotScheduledInterrupt) +// SetNextInterrupt(MaxInterruptInterval) +// +// RTC::Interrupt() +// pitLast = pitNow +// pitAccum = 0 +// upTime += RTCInterruptPeriod +// +// All of these methods are atomic interrupt-wise. +// +// Note: if we want to test the accuracy of the timer over a +// period we can set RTC::Interrupt to just return without +// touching any variables. All of the time accumulated will end +// up in pitAccum. +// +// Conditionals +// +// TIMER_NO_GO - disables timer and scheduling of timer interrupts. +// +// RTC_NO_GO - disable RTC. +// +// DEBUG_TIMER - enable timer debug messages and boot-time diagnostics. +// +// DEBUG_CLOCK - enable clock debug messages +// +// LOG_CLOCK - log adjustments to clock time and dump out later. +// +// LOG_SNI - log calls to SetNextInterrupt to see what's being thrown in. +// +// Tip: When this code does not behave useful things to check +// are the interrupt rate and the rate of calls to +// SetNextInterrupt. +// + +// #define VERBOSE + +using Microsoft.Singularity.Io; + +using System; +using System.Runtime.CompilerServices; +using System.Diagnostics; +using System.Threading; + +namespace Microsoft.Singularity.Hal +{ + // Shared time state between RTClock and Programmable Timer + [ CLSCompliant(false) ] + internal sealed class RtcPitState + { + /// + /// System up time as measured by the RTClock. This value + /// is only updated by the RTClock. + /// + internal long upTime = 0; + + internal volatile int pitLastClock; + + /// + /// Last time returned by GetKernelTicks. Used to check for time + /// going backwards. This can occur in PIT value updates in the + /// scaling between PIT timebase and kernel time base. + /// + internal long lastKernelTicks = 0; + + internal RtcPitState() + { + this.pitLastClock = 0xffff; + this.upTime = 0; + } + + [NoHeapAllocation] + internal static int ComputePitOffset(int pitPrev, int pitNow) + { + if (pitPrev >= pitNow) { + return pitPrev - pitNow; + } + return pitPrev + 0xffff - pitNow; + } + + [NoHeapAllocation] + internal long GetKernelTicks(int pitNow) + { + int pitOffset = ComputePitOffset(this.pitLastClock, pitNow); + long delta = 0; // Timer8254LegacyPC.PitTicksToTimeSpanTicks(pitOffset); + long r = this.upTime + delta; + + if (r < this.lastKernelTicks) { + // This should only be by a few ticks. + // Something to look for if you are ever + // working on this code. + r = this.lastKernelTicks; + } + else { + this.lastKernelTicks = r; + } + return r; + } + } +} diff --git a/base/Kernel/Singularity.Hal.Omap3430/TimerOmap3430.cs b/base/Kernel/Singularity.Hal.Omap3430/TimerOmap3430.cs new file mode 100644 index 0000000..ed426f4 --- /dev/null +++ b/base/Kernel/Singularity.Hal.Omap3430/TimerOmap3430.cs @@ -0,0 +1,433 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: TimerOmap3430.cs +// +// +// The OMAP platform has a plethora of timers to choose from, +// including 12 general-purpose timers, three watchdog timers, +// a 32-kHz synchronised timer and a frame adjustment counter +// for USB applications. +// +// Each of the general-purpose timers are free-running upward +// counters which can be run from either a 32-kHz clock or the +// system clock (12, 13, 19.2, 24, 26, or 38.4 MHz), with the +// option of passing the system clock through a pre-scaler to +// divide it by 2^0 -> 2^8 as desired. +// +// Each of the general purpose timers also provides the option +// of operating in three modes: timer, capture and compare. +// +// The compare mode allows an interrupt and counter value reload +// to happen at an arbitrary counter value. The capture mode +// allows the value of the timer to be captured and an interrupt +// possibly generated the next time the counter reaches that +// value. The timer mode allows standard periodic and one-shot +// timer behaviour, with value reload and interrupts happening +// when the counter value transitions from 0xffffffff -> 0x0. +// +// The 32-bit width of the timer admits a wide degree of +// flexibility in the use of general-purpose timers, and so for +// our present purposes it is sufficient to use the GP timer in +// the basic timer mode with the 32 kHz clock, giving a minimum +// period of 1/32,768 s = 30.5us and a maximum period of +// (2^32 - 1) / 2^15 s ~= 131,072s = 36hrs 24mins 32s. +// +// As the maximum interval is expressed as a long in units of +// 100ns, the maximum interval for a timer which can be +// expressed is comfortably larger than this (0x7fffffffffffffff +// / 365 / 24 / 60 / 60 / 1000 / 1000 / 10) = ~29,247 years +// +#define VERBOSE + +using Microsoft.Singularity.Io; + +using System; +using System.Runtime.CompilerServices; +using System.Diagnostics; +using System.Threading; + +using Microsoft.Singularity.Configuration; + +namespace Microsoft.Singularity.Hal +{ + // declare resources for the kernel manifest + [DriverCategory] + [Signature("/arm/ti/3430/GPTIMER1")] + public sealed class TimerResources : DriverCategoryDeclaration + { + // register block + [IoMemoryRange(0, Default = 0x48318000, Length = 0x1000)] + internal readonly IoMemoryRange registers; + + // interrupt + [IoIrqRange(1, Default = 37)] + internal readonly IoIrqRange irq; + + // CTR will create the rest of this class: + public TimerResources(IoConfig config) + { + // dynamic resources + registers = (IoMemoryRange)config.DynamicRanges[0]; + irq = (IoIrqRange)config.DynamicRanges[1]; + } + } + + [CLSCompliant(false)] + public sealed class TimerOmap3430 : HalTimer + { + // size of registers in block + const int Omap3430_TIMER1_RegisterSize = 0x0000005c; // size of registers in block + + // clock frequency + const uint Omap3430_TIMER1_Freq = 32768; + + // registers + const uint Omap3430_TISR = 0x00000018; // interrupt status + const uint Omap3430_TIER = 0x0000001c; // interrupt enable + const uint Omap3430_TCLR = 0x00000024; // control + const uint Omap3430_TCRR = 0x00000028; // timer counter + const uint Omap3430_TLDR = 0x0000002c; // timer load + + // interrupt status register fields + const uint Omap3430_TISR_TCAR_IT_FLAG = 0x00000001; // capture interrupt + const uint Omap3430_TISR_OVF_IT_FLAG = 0x00000002; // overflow interrupt + const uint Omap3430_TISR_MAT_IT_FLAG = 0x00000004; // match interrupt + + // interrupt enable register fields + const uint Omap3430_TIER_TCAR_IT_ENA = 0x00000001; // capture interrupt + const uint Omap3430_TIER_OVF_IT_ENA = 0x00000002; // overflow interrupt + const uint Omap3430_TIER_MAT_IT_ENA = 0x00000004; // match interrupt + + // timer control register fields + const uint Omap3430_TCLR_ST = 0x00000001; // start timer + const uint Omap3430_TCLR_AR = 0x00000002; // auto-reload + const uint Omap3430_TCLR_PTV_Mask = 0x0000001c; // prescaler trigger value + const uint Omap3430_TCLR_PTV_Shift = 2; // prescaler trigger value + const uint Omap3430_TCLR_PRE = 0x00000020; // prescale enable + const uint Omap3430_TCLR_CE = 0x00000040; // compare enable + const uint Omap3430_TCLR_SCPWM = 0x00000080; // PWM value when stopped + const uint Omap3430_TCLR_TCM_Rising = 0x00000100; // trigger capture mode + const uint Omap3430_TCLR_TCM_Falling = 0x00000200; // trigger capture mode + const uint Omap3430_TCLR_TCM_Both = // trigger capture mode + (Omap3430_TCLR_TCM_Rising | Omap3430_TCLR_TCM_Falling); + const uint Omap3430_TCLR_TRG_Overflow = 0x00000002; // trigger output mode + const uint Omap3430_TCLR_TRG_OvrMatch = 0x00000002; // trigger output mode + const uint Omap3430_TCLR_PT = 0x00000004; // PWM toggle select + const uint Omap3430_TCLR_CAPT_MODE = 0x00000001; // capture mode + const uint Omap3430_TCLR_GPO_CFG = 0x00000002; // PWM config + + // resource allocation + private PnpConfig config; + private Pic pic; + private byte irq; + + // registers + private IoMappedPort tisr; + private IoMappedPort tier; + private IoMappedPort tclr; + private IoMappedPort tcrr; + private IoMappedPort tldr; + + // timer state + private volatile bool interruptPending = false; + internal volatile uint irqCount = 0; + + int ticksPerSecond = 0; + + // Constructor + internal TimerOmap3430(PnpConfig config, Pic pic) + { +#if VERBOSE + DebugStub.WriteLine("TimerOmap3430: create"); +#endif + TimerResources tr = new TimerResources(config); + + this.config = config; + this.pic = pic; + this.irq = tr.irq.Irq; + + IoMemory timerRegisters = tr.registers + .MemoryAtOffset(0, Omap3430_TIMER1_RegisterSize, Access.ReadWrite); + + tisr = timerRegisters.MappedPortAtOffset(Omap3430_TISR, 4, Access.ReadWrite); + tier = timerRegisters.MappedPortAtOffset(Omap3430_TIER, 4, Access.ReadWrite); + tclr = timerRegisters.MappedPortAtOffset(Omap3430_TCLR, 4, Access.ReadWrite); + tcrr = timerRegisters.MappedPortAtOffset(Omap3430_TCRR, 4, Access.ReadWrite); + tldr = timerRegisters.MappedPortAtOffset(Omap3430_TLDR, 4, Access.ReadWrite); + + // _ARM_ERRATA problem with how functional clock is unstuck + tcrr.Write32(0, 0); + } + + public byte Initialize() + { +#if VERBOSE + DebugStub.WriteLine("Timer.Initialize()"); +#endif + + SetOneShot(); + SetInterruptEnabled(false); + Stop(); + + pic.EnableIrq(irq); + return pic.IrqToInterrupt(irq); + } + + public void Finalize() + { + pic.DisableIrq(irq); + Stop(); + } + + internal void SetTicksPerSecond(int count) + { +#if FALSE + ticksPerSecond = count; +#endif +#if VERBOSE + DebugStub.WriteLine("gptimer1 frequency {0} Hz.", __arglist(count)); +#endif + } + + [NoHeapAllocation] + internal uint Read(IoMappedPort register) + { + uint outValue; + IoResult result = register.Read32NoThrow(0, out outValue); + DebugStub.Assert(IoResult.Success == result); + return outValue; + } + + [NoHeapAllocation] + internal void Write(IoMappedPort register, uint value) + { + IoResult result = register.Write32NoThrow(0, value); + DebugStub.Assert(IoResult.Success == result); + } + + [NoHeapAllocation] + internal uint GetCurrentCount() + { + return Read(tcrr); + } + + [NoHeapAllocation] + internal void SetInitialCount(uint value) + { + // Write current counter & reload values + Write(tcrr, value); + Write(tldr, value); + } + + [NoHeapAllocation] + internal void SetInterruptEnabled(bool enabled) + { + uint ie = Read(tier) & ~Omap3430_TIER_OVF_IT_ENA; + + if (enabled) { + ie |= Omap3430_TIER_OVF_IT_ENA; + } + Write(tier, ie); + } + + [NoHeapAllocation] + internal bool InterruptEnabled() + { + return (Read(tier) & Omap3430_TIER_OVF_IT_ENA) != 0; + } + + [NoHeapAllocation] + internal void Start() + { + bool iflag = Processor.DisableInterrupts(); + try { + + // Start counter running + Write(tclr, Omap3430_TCLR_ST); + + } + finally { + Processor.RestoreInterrupts(iflag); + } + } + + [NoHeapAllocation] + internal void Stop() + { + bool iflag = Processor.DisableInterrupts(); + try { + + // Stop counter running + uint val = Read(tclr) & ~Omap3430_TCLR_ST; + Write(tclr, val); + + } + finally { + Processor.RestoreInterrupts(iflag); + } + } + + [NoHeapAllocation] + internal void SetPeriodic() + { + uint val = Read(tclr) | Omap3430_TCLR_AR; + Write(tclr, val); + } + + [NoHeapAllocation] + internal void SetOneShot() + { + uint val = Read(tclr) & ~Omap3430_TCLR_AR; + Write(tclr, val); + } + + private readonly TimeSpan maxInterruptInterval + = new TimeSpan((((1 << 32) - 1) * (1000 * 1000 * 10)) / Omap3430_TIMER1_Freq); // 36.4 hours + private readonly TimeSpan minInterruptInterval + = new TimeSpan(((1000 * 1000 * 10) + Omap3430_TIMER1_Freq) / Omap3430_TIMER1_Freq); // 30.5us, rounded up + private readonly TimeSpan interruptGranularity + = new TimeSpan((1000 * 1000 * 10) / Omap3430_TIMER1_Freq); // 30.5us + + /// + /// Maximum value accepted by SetNextInterrupt (in units of 100ns). + /// + public override TimeSpan MaxInterruptInterval + { + [NoHeapAllocation] + get { return maxInterruptInterval; } + } + + /// + /// Minimum value accepted by SetNextInterrupt (in units of 100ns). + /// + public override TimeSpan MinInterruptInterval + { + [NoHeapAllocation] + get { return minInterruptInterval; } + } + + [NoHeapAllocation] + public override void SetNextInterrupt(TimeSpan delta) + { +#if VERBOSE + DebugStub.WriteLine("Timer.SetNextInterrupt({0})", __arglist(delta.Ticks)); +#endif + DebugStub.Assert(Processor.InterruptsDisabled()); + DebugStub.Assert(delta >= minInterruptInterval); + DebugStub.Assert(delta <= maxInterruptInterval); + + bool iflag = Processor.DisableInterrupts(); + try { + // NB: cast is safe as (delta <= MaxInterruptInterval) + uint timerIntervals = (uint)((delta.Ticks * Omap3430_TIMER1_Freq) + / (1000 * 1000 * 10)); + uint count = (0xffffffff - timerIntervals) + 1; + + Write(tldr, count); + SetPeriodic(); + SetInterruptEnabled(true); + Start(); + + interruptPending = true; + } + finally { + Processor.RestoreInterrupts(iflag); + } + } + + internal bool InterruptPending + { + [NoHeapAllocation] + get { return interruptPending; } + } + + [NoHeapAllocation] + public override void ClearInterrupt() + { +#if VERBOSE + DebugStub.WriteLine("Timer.ClearInterrupt()"); +#endif + + interruptPending = false; + irqCount++; + + Write(tisr, Omap3430_TISR_OVF_IT_FLAG); + } + } +} + + +// +// Definitions for registers in GPTIMERx that we aren't currently using +// + +// // timer register base +// const uint Omap3430_TIMER1_Base = 0x48318000; // GPTIMER1 +// const uint Omap3430_TIMER2_Base = 0x49032000; // GPTIMER2 + +// // registers +// private IoMemory tidr; +// private IoMemory tiocp_cfg; +// private IoMemory tistat; +// private IoMemory tisr; +// private IoMemory tier; +// private IoMemory twer; +// private IoMemory tclr; +// private IoMemory tcrr; +// private IoMemory tldr; +// private IoMemory ttgr; +// private IoMemory twps; +// private IoMemory tmar; +// private IoMemory tcar1; +// private IoMemory tsicr; +// private IoMemory tcar2; +// private IoMemory tpir; +// private IoMemory tnir; +// private IoMemory tcvr; +// private IoMemory tocr; +// private IoMemory towr; + + + // ID register fields + //const uint Omap3430_TIDR_TID_REV = 0x000000ff; // whole id + //const uint Omap3430_TIDR_TID_REV_Major = 0x000000f0; // major revision + //const uint Omap3430_TIDR_TID_REV_Minor = 0x0000000f; // minor revision + + // L4 interface config register fields + //const uint Omap3430_TIOCP_CFG_AUTOIDLE = 0x00000001; // auto L4 clk gating + //const uint Omap3430_TIOCP_CFG_SOFTRESET = 0x00000002; // software reset + //const uint Omap3430_TIOCP_CFG_ENAWAKEUP = 0x00000004; // wake-up enable + //const uint Omap3430_TIOCP_CFG_IDLEMODE_Mask = 0x00000018; // idle mask + //const uint Omap3430_TIOCP_CFG_IDLEMODE_Force = 0x00000000; // force idle + //const uint Omap3430_TIOCP_CFG_IDLEMODE_None = 0x00000008; // deny idle + //const uint Omap3430_TIOCP_CFG_IDLEMODE_Smart = 0x00000010; // smart idle + //const uint Omap3430_TIOCP_CFG_EMUFREE = 0x00000020; // timer is free-running under emulation (debug) + //const uint Omap3430_TIOCP_CFG_CLOCKACTIVITY_Mask = 0x00000300; // wake-up mode clock activity + //const uint Omap3430_TIOCP_CFG_CLOCKACTIVITY_L4 = 0x00000100; // maintain L4 clk in wake-up + //const uint Omap3430_TIOCP_CFG_CLOCKACTIVITY_Func = 0x00000200; // maintain functional clks in wake-up + + // Registers + //const uint Omap3430_TIDR = 0x00000000; // ID + //const uint Omap3430_TIOCP_CFG = 0x00000010; // L4 interface config + //const uint Omap3430_TISTAT = 0x00000014; // timer status + //const uint Omap3430_TISR = 0x00000018; // interrupt status + //const uint Omap3430_TIER = 0x0000001c; // interrupt enable + //const uint Omap3430_TWER = 0x00000020; // wake-up enable + //const uint Omap3430_TCLR = 0x00000024; // control + //const uint Omap3430_TCRR = 0x00000028; // timer counter + //const uint Omap3430_TLDR = 0x0000002c; // timer load + //const uint Omap3430_TTGR = 0x00000030; // timer trigger + //const uint Omap3430_TWPS = 0x00000034; // write-posted status + //const uint Omap3430_TMAR = 0x00000038; // match value + //const uint Omap3430_TCAR1 = 0x0000003c; // counter capture 1 + //const uint Omap3430_TSICR = 0x00000040; // L4 interface sync control + //const uint Omap3430_TCAR2 = 0x00000044; // counter capture 2 + //const uint Omap3430_TPIR = 0x00000048; // +ve increment + //const uint Omap3430_TNIR = 0x0000004c; // -ve increment + //const uint Omap3430_TCVR = 0x00000050; // counter value + //const uint Omap3430_TOCR = 0x00000054; // overflow counter + //const uint Omap3430_TOWR = 0x00000058; // overflow wrapping diff --git a/base/Kernel/Singularity.Hal.Tpm/Tpm.cs b/base/Kernel/Singularity.Hal.Tpm/Tpm.cs new file mode 100644 index 0000000..b5cd086 --- /dev/null +++ b/base/Kernel/Singularity.Hal.Tpm/Tpm.cs @@ -0,0 +1,597 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: TPM.cs +// +// Note: +// +// Useful refs: + +// + +//#define DEBUG_DISPATCH_IO +//#define DEBUG_IO + + +using Microsoft.Singularity.Io; + +using System; +using System.Runtime.CompilerServices; +using System.Diagnostics; +using System.Threading; + +using Microsoft.Singularity.Configuration; + +namespace Microsoft.Singularity.Hal +{ + + // create the resource object for LTR to fill in + [DriverCategory] + [Signature("/pseudobus0/tpm12")] + public sealed class TpmResources : DriverCategoryDeclaration + { + [IoMemoryRange(1, Default = 0xfed40000, Length = 0x4000)] + public IoMemoryRange pcrs; + } + + + ////////////////////////////////////////////////////////////////////////// + // + // Underlying device implementation. + // + internal class Tpm + { + private class TpmRegisterOffsets + { + public const int TpmRegAccess = 0x0; + public const int TpmStatus = 0x18; + public const int TpmBurst = 0x19; + public const int TpmDataFifo = 0x24; + }; + + private class TpmRegAccessMasks + { + public const byte TpmAccessValidStatus = 0x80; + public const byte TpmAccessActiveLocality = 0x20; + public const byte TpmAccessHasBeenSeized = 0x10; + public const byte TpmAccessRequestUse = 0x2; + }; + + private class TpmRegStatusMasks + { + public const byte TpmStatusValid = 0x80; + public const byte TpmStatusCommandReady = 0x40; + public const byte TpmStatusGo = 0x20; + public const byte TpmStatusDataAvailable = 0x10; + public const byte TpmStatusExpect = 0x8; + public const byte TpmStatusRetry = 0x2; + + }; + + // Retries assuming 1 ms wait between retry + // + // TODO: those are the default values, grab the actual ones from the device + + private class Retries + { + public const int MaxStatusRetries = 200; + public const int MaxDataRetries = 400; + + } + + byte[] capability_command = {0, 193, + 0, 0, 0, 18, + 0, 0, 0, 101, + 0, 0, 0, 6, + 0, 0, 0, 0 }; + + private int locality = 0; + + private IoMemory tpmIoMemory; + + private byte[] minResponse; + + //private const int minResponseLen = 6; + private const int minResponseLen = 6; + private const int dataLenOffset = 2; + + + // Constructor + internal Tpm(/*TpmResources! res*/) + { + Tracing.Log(Tracing.Debug, "Tpm: Initialize() called\n"); + + DebugStub.WriteLine("Tpm: Initialize() called"); + + tpmIoMemory = IoMemory.MapPhysicalMemory(new UIntPtr(0xfed40000), + new UIntPtr(0x4000), + true, + true); + + if (tpmIoMemory == null) { + DebugStub.WriteLine("Tpm: Initialize(), MapPhysicalMemory failed"); + + } + + minResponse = new byte[minResponseLen]; + + + + } + + internal void PrintRegisters() + { + byte TpmAccess = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmRegAccess); + byte TpmStatus = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmStatus); + + DebugStub.WriteLine("Tpm: PrintRegisters() Access={0:x2}, Status={1:x2}", __arglist(TpmAccess, TpmStatus)); + + + } + + ushort GetBurst() + { + ushort burst = 0; + for (int j = 0; j < Retries.MaxDataRetries; j++) { + burst = tpmIoMemory.Read16(locality + TpmRegisterOffsets.TpmBurst); + + if (burst != 0) + break; + + Thread.Sleep(1); + } + + return burst; + + + } + + //byte Read8(int byteOffset) + + internal bool WaitOnBit(int address, byte mask, int MaxRetries) + { + for (int i = 0; i < MaxRetries; i++) { + byte data = tpmIoMemory.Read8(address); + + if ((data & mask) > 0) + return true; + + Thread.Sleep(1); + } + return false; + + } + + internal bool Send(byte[] command) + { + bool Status = true; + + DebugStub.WriteLine("Tpm: Send() called"); + PrintRegisters(); + + + byte TpmAccess = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmRegAccess); + + + // + // check the access register + // + + // + // check if the TPM has been disabled + // + + if (TpmAccess == 0xff) { + DebugStub.WriteLine("Tpm: Send() TPM disabled"); + return false; + } + + if ((TpmAccess & TpmRegAccessMasks.TpmAccessHasBeenSeized) > 0) { + DebugStub.WriteLine("Tpm: Send() TPM seized"); + return false; + } + + if ((TpmAccess & TpmRegAccessMasks.TpmAccessValidStatus) == 0) { + DebugStub.WriteLine("Tpm: Send() TPM had invalid status"); + return false; + } + + + // + // request locality use + // + + if ((TpmAccess & TpmRegAccessMasks.TpmAccessActiveLocality) == 0) { + // + TpmAccess |= TpmRegAccessMasks.TpmAccessActiveLocality; + tpmIoMemory.Write8(locality + TpmRegisterOffsets.TpmRegAccess, TpmAccess); + + if (!WaitOnBit(locality + TpmRegisterOffsets.TpmRegAccess, + TpmRegAccessMasks.TpmAccessActiveLocality, + Retries.MaxStatusRetries)) + { + DebugStub.WriteLine("Tpm: Send() could not set locality"); + return false; + + } + + } + + // + // check if the device is ready + // + + byte TpmStatus = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmStatus); + + if ((TpmStatus & TpmRegStatusMasks.TpmStatusCommandReady) == 0) { + TpmStatus |= TpmRegStatusMasks.TpmStatusCommandReady; + tpmIoMemory.Write8(locality + TpmRegisterOffsets.TpmStatus, TpmStatus); + + if (!WaitOnBit(locality + TpmRegisterOffsets.TpmStatus, + TpmRegStatusMasks.TpmStatusCommandReady, + Retries.MaxStatusRetries)) + { + DebugStub.WriteLine("Tpm: Send() device not ready"); + PrintRegisters(); + return false; + + } + + } + + DebugStub.WriteLine("Tpm: Send() command length={0:x2}", __arglist(command.Length)); + + // + // write the data to the data register + // + + for (int i = 0; i < command.Length;) { + ushort burst = 0; + + burst = GetBurst(); + + if (burst == 0) { + DebugStub.WriteLine("Tpm: Send() timed out waiting for burst"); + return false; + } + + while (burst > 0) { + burst--; + + if (i == command.Length) + break; + + // + // last byte, check if we still expect + // + + if (i == command.Length - 1) { + TpmStatus = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmStatus); + + if ((TpmStatus & TpmRegStatusMasks.TpmStatusExpect) == 0) { + // + // something went wrong + // + DebugStub.WriteLine("Tpm: Send() not expecting more data before last byte"); + return false; + + } + DebugStub.WriteLine("Tpm: Send() last byte expected ok"); + + } + + tpmIoMemory.Write8(locality + TpmRegisterOffsets.TpmDataFifo, command[i]); + + + i++; + + } + + + } + + if (!WaitOnBit(locality + TpmRegisterOffsets.TpmStatus, + TpmRegStatusMasks.TpmStatusValid, + Retries.MaxDataRetries)) + { + DebugStub.WriteLine("Tpm: Send(), WaitOnTpmStatusValid timed out after command write"); + PrintRegisters(); + return false; + + } + + TpmStatus = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmStatus); + + if ((TpmStatus & TpmRegStatusMasks.TpmStatusExpect) > 0) { + // + // more data needed + // + DebugStub.WriteLine("Tpm: Send(), more data needed"); + return false; + + } + + + // + // launch the command + // + TpmStatus |= TpmRegStatusMasks.TpmStatusGo; + tpmIoMemory.Write8(locality + TpmRegisterOffsets.TpmStatus, TpmStatus); + + DebugStub.WriteLine("Tpm: Send(), success"); + + return Status; + + } + + private bool ReadBytes(byte[] buffer, int offset, int numBytes, ushort prevBurst, out ushort burstRemainder) + { + byte TpmStatus; + burstRemainder = 0; + // + // read the remaining burst bytes + // + + + if (prevBurst > 0) { + for (int i = 0; i < prevBurst; i++) { + if (numBytes > 0) { + TpmStatus = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmStatus); + + if ((TpmStatus & TpmRegStatusMasks.TpmStatusDataAvailable) == 0) { + DebugStub.WriteLine("Tpm: ReadBytes() no more data avaliable, ={0:d4}", __arglist(numBytes)); + return false; + } + + + } + + if (numBytes <= 0) + return true; + + + buffer[offset] = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmDataFifo); + offset++; + numBytes--; + } + } + + ushort burst = 0; + + while (numBytes > 0) { + //get next burst + + burst = GetBurst(); + + if (burst == 0) { + DebugStub.WriteLine("Tpm: ReadBytes() timed out waiting for burst"); + return false; + } + + while (burst > 0) { + burst--; + if (numBytes == 1) { + TpmStatus = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmStatus); + if ((TpmStatus & TpmRegStatusMasks.TpmStatusDataAvailable) == 0) { + DebugStub.WriteLine("Tpm: ReadBytes() no more data avaliable2"); + return false; + } + } + + buffer[offset] = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmDataFifo); + offset++; + numBytes--; + + if (numBytes == 0) { + burstRemainder = burst; + return true; + } + + + } + + + } + return false; + + } + + bool ReceiveAttempt(out byte[] response) + { + bool Ready = false; + bool DataAvailable = false; + bool Status = true; + response = null; + byte TpmStatus; + + DebugStub.WriteLine("Tpm: ReceiveAttempt() called"); + + for (int i = 0; i < Retries.MaxDataRetries; i++) { + if (WaitOnBit(locality + TpmRegisterOffsets.TpmStatus, + TpmRegStatusMasks.TpmStatusValid, + Retries.MaxDataRetries)) + { + Ready = true; + + } + } + + if (!Ready) { + DebugStub.WriteLine("Tpm: ReceiveAttempt() device not ready"); + return false; + } + + for (int i = 0; i < Retries.MaxDataRetries; i++) { + + TpmStatus = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmStatus); + + if ((TpmStatus & TpmRegStatusMasks.TpmStatusDataAvailable) > 0) { + DataAvailable = true; + break; + + } + Thread.Sleep(1); + } + + if (!DataAvailable) { + DebugStub.WriteLine("Tpm: ReceiveAttempt(), data not available"); + return false; + + } + + // + // read enough bytes to determine the length + // + + ushort burstRemainder; + + Status = ReadBytes(minResponse, 0, minResponseLen, 0, out burstRemainder); + + if (!Status) { + DebugStub.WriteLine("Tpm: ReceiveAttempt(), ReadBytes failed"); + return false; + } + + int dataLen = 0; + + // + // acquire data length + // + + dataLen = (minResponse[dataLenOffset] << 24) | (minResponse[dataLenOffset + 1] << 16) | + (minResponse[dataLenOffset + 2] << 8) | (minResponse[dataLenOffset + 3]); + + DebugStub.WriteLine("Tpm: ReceiveAttempt() data length={0:d4}", __arglist(dataLen)); + + response = new byte[dataLen]; + + for (int i = 0; i < minResponseLen; i++) { + response[i] = minResponse[i]; + + } + + ushort nextBurstRem; + + + Status = ReadBytes(response, minResponseLen, dataLen - minResponseLen, burstRemainder, out nextBurstRem); + + if (!Status) { + DebugStub.WriteLine("Tpm: ReceiveAttempt(), ReadBytes failed2"); + return false; + } + + + if (!WaitOnBit(locality + TpmRegisterOffsets.TpmStatus, + TpmRegStatusMasks.TpmStatusValid, + Retries.MaxDataRetries)) + { + DebugStub.WriteLine("Tpm: ReceiveAttempt(), valid bit not set after done reading"); + //PrintRegisters(); + return false; + + } + + return Status; + + + + } + + + bool Receive(out byte[] response) + { + bool Status; + response = null; + + for (int i = 0; i < Retries.MaxDataRetries; i++) { + Status = ReceiveAttempt(out response); + if (Status) + return Status; + + byte TpmStatus = tpmIoMemory.Read8(locality + TpmRegisterOffsets.TpmStatus); + TpmStatus |= TpmRegStatusMasks.TpmStatusRetry; + tpmIoMemory.Write8(locality + TpmRegisterOffsets.TpmStatus, TpmStatus); + Thread.Sleep(10); + + } + + return false; + + + } + + internal bool SendReceive(byte[] request, out byte[] response) + { + bool Status; + + response = null; + + Status = Send(request); + if (!Status) + return Status; + + Status = Receive(out response); + + if (Status) { + DebugStub.WriteLine("Tpm: SendReceive(), success"); + + } + + return Status; + + } + + // Device methods + public void Initialize() + { + + } +// +// /////////////////////////////////////////////////////////////////////// +// // +// // Register accessors / modifiers / utilities +// // +// +// private uint Read(uint offset) +// { +// return ioMemory.Read32((int) offset); +// } +// +// private void Write(uint offset, uint value) +// { +// ioMemory.Write32((int) offset, value); +// } +// +// private void SetBits(int offset, uint bits) +// { +// ioMemory.Write32(offset, ioMemory.Read32(offset) | bits); +// } +// +// private void ClearBits(int offset, uint bits) +// { +// ioMemory.Write32(offset, ioMemory.Read32(offset) & ~bits); +// } +// + + public void ReleaseResources() + { + Tracing.Log(Tracing.Debug, "Tpm: Finalize() called\n"); + } + + // how do we pass a string back? + + public string Name + { + get { return "TPM 1.2 compliant device"; } + } + + public string Version + { + get { return "0.1"; } + } + + } +} diff --git a/base/Kernel/Singularity.Io/ConsoleOutput.sg b/base/Kernel/Singularity.Io/ConsoleOutput.sg index ce2fe52..d231441 100644 --- a/base/Kernel/Singularity.Io/ConsoleOutput.sg +++ b/base/Kernel/Singularity.Io/ConsoleOutput.sg @@ -161,8 +161,7 @@ namespace Microsoft.Singularity.Io break; case ep.Write(buffer, offset, count) in clients: { - if (buffer != null) - { + if (buffer != null) { if (side.Length < count) { side = new byte [count]; } diff --git a/base/Kernel/Singularity.Io/IoIrq.cs b/base/Kernel/Singularity.Io/IoIrq.cs index d547b5f..d8c69b1 100644 --- a/base/Kernel/Singularity.Io/IoIrq.cs +++ b/base/Kernel/Singularity.Io/IoIrq.cs @@ -20,20 +20,21 @@ namespace Microsoft.Singularity.Io public sealed class IoIrq { private readonly byte irq; - private AutoResetEvent! signal; + private InterruptAwareAutoResetEvent! signal; private IoIrq next; + private bool registered; ////////////////////////////////////////////////////////////////////// // // private const int MaxInterrupts = 256; - private static IoIrq[]! registered; + private static IoIrq[]! irqTable; private static SpinLock regLock; static IoIrq() { - registered = new IoIrq [MaxInterrupts]; - regLock = new SpinLock(); + irqTable = new IoIrq [MaxInterrupts]; + regLock = new SpinLock(SpinLock.Types.IoIrq); } [NoHeapAllocation] @@ -63,8 +64,8 @@ namespace Microsoft.Singularity.Io #if DEBUG_DISPATCH_IO DebugStub.WriteLine("++ SetInterruptEvent: Irq={0:x2}", __arglist(irq)); #endif - for (IoIrq step = registered[irq]; step != null; step = step.next) { - step.signal.Set(); + for (IoIrq step = irqTable[irq]; step != null; step = step.next) { + step.signal.InterruptAwareSet(); } } @@ -72,7 +73,8 @@ namespace Microsoft.Singularity.Io { this.irq = irq; this.next = null; - this.signal = new AutoResetEvent(false); + this.signal = new InterruptAwareAutoResetEvent(false); + this.registered = false; } public byte Irq @@ -93,11 +95,13 @@ namespace Microsoft.Singularity.Io DebugStub.WriteLine("++ Register Irq={0:x2}", __arglist(irq)); #endif - next = registered[irq]; - registered[irq] = this; + this.next = irqTable[irq]; + irqTable[irq] = this; + + this.registered = true; if (next == null) { - HalDevices.EnableIoInterrupt(irq); + Platform.EnableIoInterrupt(irq); return true; } } @@ -114,24 +118,28 @@ namespace Microsoft.Singularity.Io { bool enabled = AcquireLock(); try { + Tracing.Log(Tracing.Debug, "Release Irq={0:x2}", irq); #if DEBUG_DISPATCH_IO DebugStub.WriteLine("++ Release Irq={0:x2}", __arglist(irq)); #endif - if (registered[irq] == this) { - registered[irq] = this.next; + if (irqTable[irq] == this) { + irqTable[irq] = this.next; } else { - IoIrq! prev = (!)registered[irq]; + IoIrq! prev = (!)irqTable[irq]; while (prev.next != this) { prev = prev.next; } prev.next = this.next; } - if (registered[irq] == null) { - HalDevices.DisableIoInterrupt(irq); + this.next = null; + this.registered = false; + + if (irqTable[irq] == null) { + Platform.DisableIoInterrupt(irq); return true; } } @@ -143,23 +151,18 @@ namespace Microsoft.Singularity.Io public bool WaitForInterrupt() { - if (signal != null) { - return signal.WaitOne(); - } - return false; - } + DebugStub.Assert(this.registered); - public bool WaitForInterrupt(TimeSpan timeout) - { if (signal != null) { - return signal.WaitOne(timeout); + signal.InterruptAwareWaitOne(); + return true; } return false; } public void Pulse() { - signal.Set(); + signal.InterruptAwareSet(); } public bool AckInterrupt() @@ -167,7 +170,7 @@ namespace Microsoft.Singularity.Io #if DEBUG_DISPATCH_IO DebugStub.WriteLine("++ AckInterrupt: Irq={0:x2}", __arglist(irq)); #endif - HalDevices.EnableIoInterrupt(irq); + Platform.EnableIoInterrupt(irq); return true; } diff --git a/base/Kernel/Singularity.Io/IoSystem.csproj b/base/Kernel/Singularity.Io/IoSystem.csproj index 38103ed..6a8c9a6 100644 --- a/base/Kernel/Singularity.Io/IoSystem.csproj +++ b/base/Kernel/Singularity.Io/IoSystem.csproj @@ -1,8 +1,6 @@  From winnt.h#2175 + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct InstructionStream { + internal byte InstByte0; + internal byte InstByte1; + internal byte InstByte2; + internal byte InstByte3; + internal byte InstByte4; + internal byte InstByte5; + internal byte InstByte6; + internal byte InstByte7; + internal byte InstByte8; + internal byte InstByte9; + internal byte InstByte10; + internal byte InstByte11; + internal byte InstByte12; + internal byte InstByte13; + internal byte InstByte14; + internal byte InstByte15; + }; + + ///////////////////////////////////////////////////////////////////////// X86. + // + + // + // Format of data for 32-bit fxsave/fxrstor instructions. + // Although the fxsave/fxrstor instructions must operate on a 16-byte aligned + // structure, the KD X86Context structure aligns this field at 12 mod 16. + // So, we must use a 128-bit type made out of 4-byte subtypes. + // + [AccessedByRuntime("referenced from c++")] + [StructLayout(LayoutKind.Sequential)] + internal struct X86Unaligned128 { + internal uint Bits0to31; + internal uint Bits32to63; + internal uint Bits64to95; + internal uint Bits96to128; + }; + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct X86XmmSaveArea { + internal ushort ControlWord; + internal ushort StatusWord; + internal ushort TagWord; + internal ushort ErrorOpcode; + internal uint ErrorOffset; + internal ushort ErrorSelector; + internal ushort reserved0; + internal uint DataOffset; + internal ushort DataSelector; + internal ushort reserved1; + internal uint MxCsr; + internal uint MxCsrMask; + + internal X86Unaligned128 St0; + internal X86Unaligned128 St1; + internal X86Unaligned128 St2; + internal X86Unaligned128 St3; + internal X86Unaligned128 St4; + internal X86Unaligned128 St5; + internal X86Unaligned128 St6; + internal X86Unaligned128 St7; + + internal X86Unaligned128 Xmm0; + internal X86Unaligned128 Xmm1; + internal X86Unaligned128 Xmm2; + internal X86Unaligned128 Xmm3; + internal X86Unaligned128 Xmm4; + internal X86Unaligned128 Xmm5; + internal X86Unaligned128 Xmm6; + internal X86Unaligned128 Xmm7; + + private X86Unaligned128 reserved2; + private X86Unaligned128 reserved3; + private X86Unaligned128 reserved4; + private X86Unaligned128 reserved5; + private X86Unaligned128 reserved6; + private X86Unaligned128 reserved7; + private X86Unaligned128 reserved8; + private X86Unaligned128 reserved9; + + private X86Unaligned128 reservedA; + private X86Unaligned128 reservedB; + private X86Unaligned128 reservedC; + private X86Unaligned128 reservedD; + private X86Unaligned128 reservedE; + private X86Unaligned128 reservedF; + }; + + // 387 floating-point registers are 10 bytes. We must represent them as + // groups of 16-bit words so that the data structure is 16-bit aligned. + [AccessedByRuntime("referenced from c++")] + [StructLayout(LayoutKind.Sequential)] + internal struct X86Fp387Register { + internal ushort word0; + internal ushort word1; + internal ushort word2; + internal ushort word3; + internal ushort word4; + }; + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct X86Fp387SaveArea { + internal uint ControlWord; + internal uint StatusWord; + internal uint TagWord; + internal uint ErrorOffset; + internal uint ErrorSelector; + internal uint DataOffset; + internal uint DataSelector; + + internal X86Fp387Register St0; + internal X86Fp387Register St1; + internal X86Fp387Register St2; + internal X86Fp387Register St3; + internal X86Fp387Register St4; + internal X86Fp387Register St5; + internal X86Fp387Register St6; + internal X86Fp387Register St7; + + internal uint Cr0NpxState; + }; + + // + // Context Frame + // + // This frame has a several purposes: 1) it is used as an argument to + // NtContinue, 2) is is used to construct a call frame for APC delivery, + // and 3) it is used in the user level thread creation routines. + // + // The layout of the record conforms to a standard call frame. + // + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct X86Context { + + // + // The flags values within this flag control the contents of + // a CONTEXT record. + // + // If the context record is used as an input parameter, then + // for each portion of the context record controlled by a flag + // whose value is set, it is assumed that that portion of the + // context record contains valid context. If the context record + // is being used to modify a threads context, then only that + // portion of the threads context will be modified. + // + // If the context record is used as an IN OUT parameter to capture + // the context of a thread, then only those portions of the thread's + // context corresponding to set flags will be returned. + // + // The context record is never used as an OUT only parameter. + // + + internal uint ContextFlags; + + // + // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is + // set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT + // included in CONTEXT_FULL. + // + + internal uint Dr0; + internal uint Dr1; + internal uint Dr2; + internal uint Dr3; + internal uint Dr6; + internal uint Dr7; + + // + // This section is specified/returned if the + // ContextFlags word contains the flag CONTEXT_FLOATING_POINT. + // + + internal X86Fp387SaveArea FloatSave; + + // + // This section is specified/returned if the + // ContextFlags word contains the flag CONTEXT_SEGMENTS. + // + + internal uint SegGs; + internal uint SegFs; + internal uint SegEs; + internal uint SegDs; + + // + // This section is specified/returned if the + // ContextFlags word contains the flag CONTEXT_INTEGER. + // + + internal uint Edi; + internal uint Esi; + internal uint Ebx; + internal uint Edx; + internal uint Ecx; + internal uint Eax; + + // + // This section is specified/returned if the + // ContextFlags word contains the flag CONTEXT_CONTROL. + // + + internal uint Ebp; + internal uint Eip; + internal uint SegCs; // MUST BE SANITIZED + internal uint EFlags; // MUST BE SANITIZED + internal uint Esp; + internal uint SegSs; + + // + // This section is specified/returned if the ContextFlags word + // contains the flag CONTEXT_EXTENDED_REGISTERS. + // The format and contexts are processor specific + // Note that this field is has N mod 16 = 12 alignment. + // + internal X86XmmSaveArea ExtendedRegisters; + }; + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct X86Descriptor { + internal ushort Pad; + internal ushort Limit; + internal uint Base; + }; + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct X86SegmentRegisters { + internal ushort SegCs; + internal ushort SegDs; + internal ushort SegEs; + internal ushort SegFs; + internal ushort SegGs; + internal ushort SegSs; + }; + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct X86KSpecialRegisters { + internal uint Cr0; + internal uint Cr2; + internal uint Cr3; + internal uint Cr4; + internal uint KernelDr0; + internal uint KernelDr1; + internal uint KernelDr2; + internal uint KernelDr3; + internal uint KernelDr6; + internal uint KernelDr7; + internal X86Descriptor Gdtr; + internal X86Descriptor Idtr; + internal ushort Tr; + internal ushort Ldtr; + + private uint Reserved0; + private uint Reserved1; + private uint Reserved2; + private uint Reserved3; + private uint Reserved4; + private uint Reserved5; + }; + + // + // Processor State frame: Before a processor freezes itself, it + // dumps the processor state to the processor state frame for + // debugger to examine. + // + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct X86KProcessorState { + internal X86Context ContextFrame; + internal X86KSpecialRegisters SpecialRegisters; + }; + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct X86KdControlReport { + internal uint Dr6; + internal uint Dr7; + internal ushort InstructionCount; + internal ushort ReportFlags; + internal InstructionStream InstructionStream; + internal ushort SegCs; + internal ushort SegDs; + internal ushort SegEs; + internal ushort SegFs; + internal uint EFlags; + }; + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct X86KdControlSet { + internal uint ContinueStatus; + internal uint TraceFlag; + internal uint Dr7; + internal uint CurrentSymbolStart; + internal uint CurrentSymbolEnd; + }; + + ///////////////////////////////////////////////////////////////////////// X64. + // + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct X64VectorRegisters { + internal UInt128 Vr0; + internal UInt128 Vr1; + internal UInt128 Vr2; + internal UInt128 Vr3; + internal UInt128 Vr4; + internal UInt128 Vr5; + internal UInt128 Vr6; + internal UInt128 Vr7; + internal UInt128 Vr8; + internal UInt128 Vr9; + internal UInt128 Vr10; + internal UInt128 Vr11; + internal UInt128 Vr12; + internal UInt128 Vr13; + internal UInt128 Vr14; + internal UInt128 Vr15; + internal UInt128 Vr16; + internal UInt128 Vr17; + internal UInt128 Vr18; + internal UInt128 Vr19; + internal UInt128 Vr20; + internal UInt128 Vr21; + internal UInt128 Vr22; + internal UInt128 Vr23; + internal UInt128 Vr24; + internal UInt128 Vr25; + }; + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct X64XmmSaveArea { + internal ushort ControlWord; + internal ushort StatusWord; + internal ushort TagWord; + internal ulong ErrorAddress; + internal ulong DataAddress; + internal uint MxCsr; + internal uint MxCsrMask; + + internal X86Unaligned128 St0; + internal X86Unaligned128 St1; + internal X86Unaligned128 St2; + internal X86Unaligned128 St3; + internal X86Unaligned128 St4; + internal X86Unaligned128 St5; + internal X86Unaligned128 St6; + internal X86Unaligned128 St7; + + internal X86Unaligned128 Xmm0; + internal X86Unaligned128 Xmm1; + internal X86Unaligned128 Xmm2; + internal X86Unaligned128 Xmm3; + internal X86Unaligned128 Xmm4; + internal X86Unaligned128 Xmm5; + internal X86Unaligned128 Xmm6; + internal X86Unaligned128 Xmm7; + internal X86Unaligned128 Xmm8; + internal X86Unaligned128 Xmm9; + internal X86Unaligned128 Xmm10; + internal X86Unaligned128 Xmm11; + internal X86Unaligned128 Xmm12; + internal X86Unaligned128 Xmm13; + internal X86Unaligned128 Xmm14; + internal X86Unaligned128 Xmm15; + + private X86Unaligned128 ReservedReg0; + private X86Unaligned128 ReservedReg1; + private X86Unaligned128 ReservedReg2; + private X86Unaligned128 ReservedReg3; + private X86Unaligned128 ReservedReg4; + private X86Unaligned128 ReservedReg5; + }; + + // + // Context Frame + // + // This frame has a several purposes: 1) it is used as an argument to + // NtContinue, 2) is is used to construct a call frame for APC delivery, + // and 3) it is used in the user level thread creation routines. + // + // + // The flags field within this record controls the contents of a CONTEXT + // record. + // + // If the context record is used as an input parameter, then for each + // portion of the context record controlled by a flag whose value is + // set, it is assumed that that portion of the context record contains + // valid context. If the context record is being used to modify a threads + // context, then only that portion of the threads context is modified. + // + // If the context record is used as an output parameter to capture the + // context of a thread, then only those portions of the thread's context + // corresponding to set flags will be returned. + // + // CONTEXT_CONTROL specifies SegSs, Rsp, SegCs, Rip, and EFlags. + // + // CONTEXT_INTEGER specifies Rax, Rcx, Rdx, Rbx, Rbp, Rsi, Rdi, and R8-R15. + // + // CONTEXT_SEGMENTS specifies SegDs, SegEs, SegFs, and SegGs. + // + // CONTEXT_DEBUG_REGISTERS specifies Dr0-Dr3 and Dr6-Dr7. + // + // CONTEXT_MMX_REGISTERS specifies the floating point and extended registers + // Mm0/St0-Mm7/St7 and Xmm0-Xmm15). + // + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct X64Context { + + // + // Register parameter home addresses. + // + // N.B. These fields are for convenience - they could be used to extend the + // context record in the future. + // + + internal ulong P1Home; + internal ulong P2Home; + internal ulong P3Home; + internal ulong P4Home; + internal ulong P5Home; + internal ulong P6Home; + + // + // Control flags. + // + + internal uint ContextFlags; + internal uint MxCsr; + + // + // Segment Registers and processor flags. + // + + internal ushort SegCs; + internal ushort SegDs; + internal ushort SegEs; + internal ushort SegFs; + internal ushort SegGs; + internal ushort SegSs; + internal uint EFlags; + + // + // Debug registers + // + + internal ulong Dr0; + internal ulong Dr1; + internal ulong Dr2; + internal ulong Dr3; + internal ulong Dr6; + internal ulong Dr7; + + // + // Integer registers. + // + + internal ulong Rax; + internal ulong Rcx; + internal ulong Rdx; + internal ulong Rbx; + internal ulong Rsp; + internal ulong Rbp; + internal ulong Rsi; + internal ulong Rdi; + internal ulong R8; + internal ulong R9; + internal ulong R10; + internal ulong R11; + internal ulong R12; + internal ulong R13; + internal ulong R14; + internal ulong R15; + + // + // Program counter. + // + + internal ulong Rip; + + // + // Floating point state. + // + + internal X64XmmSaveArea FltSave; + + // + // Vector registers. + // + + internal X64VectorRegisters VectorRegister; + internal ulong VectorControl; + + // + // Special debug control registers. + // + + internal ulong DebugControl; + internal ulong LastBranchToRip; + internal ulong LastBranchFromRip; + internal ulong LastExceptionToRip; + internal ulong LastExceptionFromRip; + }; + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct X64Descriptor { + internal ushort Pad0; + internal ushort Pad1; + internal ushort Pad2; + internal ushort Limit; + internal ulong Base; + }; + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct X64KSpecialRegisters { + internal ulong Cr0; + internal ulong Cr2; + internal ulong Cr3; + internal ulong Cr4; + internal ulong KernelDr0; + internal ulong KernelDr1; + internal ulong KernelDr2; + internal ulong KernelDr3; + internal ulong KernelDr6; + internal ulong KernelDr7; + internal X64Descriptor Gdtr; + internal X64Descriptor Idtr; + internal ushort Tr; + internal ushort Ldtr; + internal uint MxCsr; + internal ulong DebugControl; + internal ulong LastBranchToRip; + internal ulong LastBranchFromRip; + internal ulong LastExceptionToRip; + internal ulong LastExceptionFromRip; + internal ulong Cr8; + internal ulong MsrGsBase; + internal ulong MsrGsSwap; + internal ulong MsrStar; + internal ulong MsrLStar; + internal ulong MsrCStar; + internal ulong MsrSyscallMask; + }; + + // + // Processor State frame: Before a processor freezes itself, it + // dumps the processor state to the processor state frame for + // debugger to examine. + // + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct X64KProcessorState { + internal X64KSpecialRegisters SpecialRegisters; + private ulong Fill; + X64Context ContextFrame; + }; + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct X64KdControlReport { + internal ulong Dr6; + internal ulong Dr7; + internal uint EFlags; + internal ushort InstructionCount; + internal ushort ReportFlags; + internal InstructionStream InstructionStream; + internal ushort SegCs; + internal ushort SegDs; + internal ushort SegEs; + internal ushort SegFs; + }; + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct X64KdControlSet { + internal uint ContinueStatus; + internal uint TraceFlag; + internal ulong Dr7; + internal ulong CurrentSymbolStart; + internal ulong CurrentSymbolEnd; + }; + + ///////////////////////////////////////////////////////////////////////// ARM. + // + + // + // Context Frame + // + // This frame has a several purposes: 1) it is used as an argument to + // NtContinue, 2) is is used to constuct a call frame for APC delivery, + // and 3) it is used in the user level thread creation routines. + // + // + // The flags field within this record controls the contents of a CONTEXT + // record. + // + // If the context record is used as an input parameter, then for each + // portion of the context record controlled by a flag whose value is + // set, it is assumed that that portion of the context record contains + // valid context. If the context record is being used to modify a threads + // context, then only that portion of the threads context is modified. + // + // If the context record is used as an output parameter to capture the + // context of a thread, then only those portions of the thread's context + // corresponding to set flags will be returned. + // + // _ARM_WORKAROUND_ needs updating + // CONTEXT_CONTROL specifies + // + // CONTEXT_FLOATING_POINT specifies + // + // CONTEXT_SEGMENTS specifies + // + // CONTEXT_DEBUG_REGISTERS + // + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct ArmContext { + + // + // Control flags. + // + + internal uint ContextFlags; + + // + // Integer registers + // + + internal uint R0; + internal uint R1; + internal uint R2; + internal uint R3; + internal uint R4; + internal uint R5; + internal uint R6; + internal uint R7; + internal uint R8; + internal uint R9; + internal uint R10; + internal uint R11; + internal uint R12; + + // + // Control Registers + // + + internal uint Sp; + internal uint Lr; + internal uint Pc; + internal uint Cpsr; + }; + + // + // Define function table entry - a function table entry is generated for + // each frame function. + // + + // _ARM_WORKAROUND_ define RUNTIME_FUNCTION_INDIRECT 0x1 what is this? + + // + // Define function table entry - a function table entry is generated for + // each frame function. + // + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct ArmRuntimeFunction { + internal uint BeginAddress; + internal uint Flags; + // Flags => + // PrologLen : 8; + // FuncLen : 22; + // ThirtyTwoBit : 1; + // ExceptionFlag : 1; + }; + + // + // Define exception data structure that can be found just before the + // start of a function with an exception handler according to ARM + // compiler conventions. each frame function. + // + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct ArmRuntimeFunctionException { + internal uint Handler; // was PVOID + internal uint HandlerData; // was PVOID + }; + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct ArmKSpecialRegisters { + internal uint Cp15Cr0Id; + internal uint Cp15Cr0Cachetype; + internal uint Cp15Cr1Control; + internal uint Cp15Cr2Ttb; + internal uint Cp15Cr3Dacr; + internal uint Cp15Cr5FaultStatus; + internal uint Cp15Cr5FaultAddress; + internal uint Cp15Cr13ProcessId; + }; + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct ArmKProcessorState { + internal ArmKSpecialRegisters SpecialRegisters; + internal ArmContext ContextFrame; + }; + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct ArmKdControlReport { + internal uint Cpsr; + internal uint InstructionCount; + internal InstructionStream InstructionStream; + }; + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct ArmKdControlSet { + internal uint ContinueStatus; + internal uint Continue; + internal uint CurrentSymbolStart; + internal uint CurrentSymbolEnd; + }; + + /////////////////////////////////////////////////////////////////////// Alpha. + // + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct AlphaKdControlReport { + internal uint InstructionCount; + internal InstructionStream InstructionStream; + }; + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct AlphaKdControlSet { + internal uint ContinueStatus; + private uint __padding; + }; + + //////////////////////////////////////////////////////////////////////// IA64. + // + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct Ia64KdControlReport { + internal uint InstructionCount; + internal InstructionStream InstructionStream; + }; + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct Ia64KdControlSet { + internal uint ContinueStatus; + internal uint Continue; + internal ulong CurrentSymbolStart; + internal ulong CurrentSymbolEnd; + }; + + [AccessedByRuntime("referenced from c++", AllFields = true)] + [StructLayout(LayoutKind.Sequential)] + internal struct ExceptionRecord64 { + internal uint ExceptionCode; + internal uint ExceptionFlags; + internal ulong ExceptionRecord; + internal ulong ExceptionAddress; + internal uint NumberParameters; + internal uint __unusedAlignment; + internal ulong ExceptionInformation0; + internal ulong ExceptionInformation1; + internal ulong ExceptionInformation2; + internal ulong ExceptionInformation3; + internal ulong ExceptionInformation4; + internal ulong ExceptionInformation5; + internal ulong ExceptionInformation6; + internal ulong ExceptionInformation7; + internal ulong ExceptionInformation8; + internal ulong ExceptionInformation9; + internal ulong ExceptionInformation10; + internal ulong ExceptionInformation11; + internal ulong ExceptionInformation12; + internal ulong ExceptionInformation13; + internal ulong ExceptionInformation14; + }; +} + +///////////////////////////////////////////////////////////////// End of File. diff --git a/base/Kernel/Singularity/KdFiles.cs b/base/Kernel/Singularity/KdFiles.cs new file mode 100644 index 0000000..3591e19 --- /dev/null +++ b/base/Kernel/Singularity/KdFiles.cs @@ -0,0 +1,667 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Microsoft.Singularity.KernelDebugger +{ + /// + /// This class allows the kernel to send/receive files to/from the attached kernel debugger, + /// if any. Currently, the only use for this is to support the ".kdfiles" command in KD. + /// (Refer to the Windows Debugging Tools documentation (debugger.chm) for info on .kdfiles.) + /// This allows developers to transfer program binaries (and other files) over the debugger + /// port, rather than a filesystem, in order to speed up development. + /// + /// In the comments in this file, 'host' refers to the machine running the kernel debugger. + /// + [NoCCtor] + [CLSCompliant(false)] + public class KernelDebuggerFiles + { + /// + /// This method opens a file on the host. This file handle can be used in calls to ReadHostFile, + /// WriteHostFile (depending on access requested), and CloseHostFile. + /// + /// Returns the file handle. This handle has meaning only to the host. + /// Returns the length of the file. + /// + /// + /// The file to open. Note that KD will not allow the debug target to open arbitrary files; this method + /// can only be used to open files that have been added to the .kdfiles set by the developer controlling KD. + /// For example, a .kdfiles file (say, kdfiles.txt) contains map entries, in this form: + /// + /// map [from] [to] + /// + /// This string identifies the "from" part. The "to" part identifies some file in the namespace of + /// the machine running KD. If the file is opened successfully, then the debug target can read/write + /// the file. + /// + /// + /// + /// The access mask requested, using standard Windows access masks. + /// However, note that KD will only look for the FILE_READ_DATA and FILE_WRITE_DATA bits, which it + /// maps to GENERIC_READ and GENERIC_WRITE. So don't bother specifying any bits outside of those; + /// they will be ignored. + /// + /// + /// Initial file attributes, if creating a file. Most callers should pass 0. + /// + /// + /// The shared access mask, if any. If opening a file for read access, then pass FILE_SHARE_READ. + /// + /// + /// Specifies what to do if the file already exists, or doesn't. + /// Use FILE_OPEN, FILE_CREATE, etc. + /// + /// + /// True if the file was successfully opened. + public static unsafe bool CreateHostFile( + out long FileHandle, + out long FileLength, + string FileName, + uint DesiredAccess, + uint FileAttributes, + uint ShareAccess, + uint CreateDisposition) + { + FileHandle = 0; + FileLength = 0; + + if (!Kd.IsDebuggerPresent()) { + // Think about how silly it would be to call DebugStub.WriteLine here. + return false; + } + + int request_data_size = sizeof(char) * (FileName.Length + 1); + if (request_data_size + sizeof(HostFileIoRequest) > Kd.MaxPacketSize) { + Dbg("CreateHostFile: Filename is too long."); + return false; + } + + // The request data for "create file" consists of the filename, + // in little-endian UTF-16, with a Unicode NUL terminator character. + byte[] request_data = new byte[request_data_size]; + for (int i = 0; i < FileName.Length; i++) { + char c = FileName[i]; + request_data[i * 2] = (byte)(c & 0xff); + request_data[i * 2 + 1] = (byte)((c >> 8) & 0xff); + } + request_data[FileName.Length * 2] = 0; + request_data[FileName.Length * 2 + 1] = 0; + + // + // Send packet to the kernel debugger on the host machine. + // + + HostFileIoRequest request = new HostFileIoRequest(); + request.ApiNumber = (uint)KdApi.CreateFile; + request.CreateFile.DesiredAccess = DesiredAccess; + request.CreateFile.FileAttributes = FileAttributes; + request.CreateFile.ShareAccess = ShareAccess; + request.CreateFile.CreateDisposition = CreateDisposition; + request.CreateFile.CreateOptions = 0; + + HostFileIoResponse response; + + bool succeeded; + int response_data_length; + + fixed (byte* request_data_pinned = &request_data[0]) + { + succeeded = SendFileIoRequestWaitResponse( + request, + request_data_pinned, + request_data_size, + out response, + null, + 0, + out response_data_length); + } + + if (succeeded) { + if (response.Status == 0) { + Dbg("Successfully opened KD file '{0}', handle 0x{1:x} length 0x{2:x}", + FileName, response.CreateFile.FileHandle, response.CreateFile.FileLength); + FileHandle = response.CreateFile.FileHandle; + FileLength = response.CreateFile.FileLength; + return true; + } + else { + Dbg("Failed to open KD file '{0}'. NTSTATUS = 0x{1:x}.", FileName, response.Status); + return false; + } + } + else { + Dbg("Failed to open KD file '{0}'.", FileName); + return false; + } + } + + /// + /// Reads data from a host file that has already been opened using CreateHostFile. + /// + /// The file handle. + /// Offset within the file of the chunk to read. + /// The buffer to store data into. + /// The number of bytes to transfer. + /// On return, the number of bytes actually transferred. + /// True if the transfer succeeded. + public static unsafe bool ReadHostFile( + long RemoteHandle, + long FileOffset, + void* Buffer, + int Length, + out int BytesTransferred) + { + BytesTransferred = 0; + + if (!Kd.IsDebuggerPresent()) + return false; + + Dbg("ReadHostFile: Handle {0:x8} FileOffset {1:x8} Buffer {2:x8} Length {3:x8}", + RemoteHandle, + FileOffset, + (UIntPtr)Buffer, + Length); + + if (Length < 0) { + throw new ArgumentOutOfRangeException("Length"); + } + + int transfer_size = Length; + int MaxTransferSize = Kd.MaxPacketSize - 0x200; // sizeof(HostFileIoRequest); + + if (transfer_size > MaxTransferSize) { + Dbg("ReadHostFile: Length exceeds max transfer size; truncating to 0x{0:x}", MaxTransferSize); + transfer_size = MaxTransferSize; + } + + HostFileIoRequest request = new HostFileIoRequest(); + request.ApiNumber = (uint)KdApi.ReadFile; + request.ReadFile.FileHandle = RemoteHandle; + request.ReadFile.FileOffset = FileOffset; + request.ReadFile.Length = (uint)transfer_size; + + HostFileIoResponse response; + + int response_data_length; + bool succeeded; + + succeeded = SendFileIoRequestWaitResponse( + request, + null, + 0, + out response, + (byte*)Buffer, + transfer_size, + out response_data_length); + + if (succeeded) { + if (response.Status == 0) { + Dbg("Received DBGKD_READ_FILE. Successfully transfered {0} bytes.", response.ReadFile.BytesTransferred); + BytesTransferred = (int)response.ReadFile.BytesTransferred; + return true; + } + else { + Dbg("Received DBGKD_READ_FILE, Status indicates failure. Status = 0x{0:x8}", (uint)response.Status); + DebugStub.Break(); + BytesTransferred = 0; + return false; + } + } + else { + Dbg("Failed to receive response to DBGKD_READ_FILE."); + BytesTransferred = 0; + return false; + } + } + + /// + /// This internal method handles executing a host file I/O request. All requests use the + /// DBGKD_FILEIO structure; using a structure with a different length will greatly confuse + /// the kernel debugger, the kernel, or both. + /// + /// + /// The request to send to the debugger. + /// A buffer containing additional data, if any, to send with the request. + /// The length of the additional data to send, or zero if there is none. + /// On return, contains the response message. + /// A buffer which receives any returned with the response. + /// The maximum length of the data to store in the response buffer. + /// The actual length of the data to store in the response buffer. + /// + static unsafe bool SendFileIoRequestWaitResponse( + HostFileIoRequest Request, + byte* RequestDataBuffer, + int RequestDataLength, + out HostFileIoResponse Response, + byte* ResponseDataBuffer, + int ResponseDataMaximumLength, + out int ResponseDataActualLength) + { + HostFileIoResponse local_response = new HostFileIoResponse(); + + Kd.Lock(); + + // Do NOT use Dbg() or DebugStub.WriteLine() while Kd.Lock() is in effect. + + bool result = Kd.SendRequestWaitResponse( + KdPacketType.FileIo, + (byte*)&Request, + sizeof(HostFileIoRequest), + RequestDataBuffer, + RequestDataLength, + (byte*)&local_response, + sizeof(HostFileIoResponse), + ResponseDataBuffer, + ResponseDataMaximumLength, + out ResponseDataActualLength); + + Kd.Unlock(); + + Response = local_response; + return result; + } + + static bool _dbg_to_kdprint; + + static void Dbg(string line) + { + if (_dbg_to_kdprint) + DebugStub.WriteLine("KdFiles.cs: " + line); + } + + static void Dbg(string format, params object[] args) + { + Dbg(String.Format(format, args)); + } + + /// + /// Writes data to a file open on the host. + /// + /// + /// The host file handle, acquired using CreateHostFile. + /// + /// The offset within the file to begin writing. + /// (TODO: Does this support NT-style always-append semantics? Probably.) + /// + /// The buffer containing the data to write. + /// The number of bytes to write. + /// The actual number of bytes transferred. + /// True if the transfer succeeded. + public static unsafe bool WriteHostFile( + long FileHandle, + long FileOffset, + void* Buffer, + int Length, + out int BytesTransferred) + { + BytesTransferred = 0; + + if (!Kd.IsDebuggerPresent()) + return false; + + if (Length < 0) + throw new ArgumentOutOfRangeException("Length"); + + int transfer_size = Math.Min(Length, Kd.MaxPacketSize - sizeof(HostFileIoRequest)); + + HostFileIoRequest request = new HostFileIoRequest(); + request.ApiNumber = (uint)KdApi.WriteFile; + request.WriteFile.FileHandle = FileHandle; + request.WriteFile.FileOffset = FileOffset; + request.WriteFile.Length = (uint)transfer_size; + + HostFileIoResponse response; + + // + // Send packet to the kernel debugger on the host machine. + // + + int response_data_length; + + bool succeeded = SendFileIoRequestWaitResponse( + request, + (byte*)Buffer, + Length, + out response, + null, + 0, + out response_data_length + ); + + if (succeeded) { + if (response.Status == 0) { + Dbg("Successfully wrote {0} bytes to host file.", response.WriteFile.BytesTransferred); + return true; + } + else { + Dbg("Received response to 'write file' request, but it indicates failure."); + return false; + } + } + else { + Dbg("Received response to 'write file' request, but it indicates failure."); + return false; + } + } + + + /// + /// Closes a file handle that was opened using CreateHostFile. + /// + /// The file handle to close. + public unsafe static void CloseHostFile(long FileHandle) + { + if (!Kd.IsDebuggerPresent()) + return; + + Dbg("Closing file handle 0x{0:x}", FileHandle); + + HostFileIoRequest request = new HostFileIoRequest(); + request.ApiNumber = (uint)KdApi.CloseFile; + request.CloseFile.FileHandle = FileHandle; + + HostFileIoResponse response; + int response_data_length; + bool succeeded = SendFileIoRequestWaitResponse( + request, + null, + 0, + out response, + null, + 0, + out response_data_length); + + if (succeeded) { + if (response.Status == 0) { + Dbg("Successfully closed host file handle."); + } + else { + Dbg("Received response to DBGKD_CLOSE_FILE, but it indicates error. NTSTATUS: 0x{0:x8}", response.Status); + } + } + else { + Dbg("Failed to receive packet for response to DBGKD_CLOSE_FILE"); + } + } + + + #region NT definitions for KDBG_CREATE_FILE + // These values are stored in the KDBG_CREATE_FILE structure, when the debuggee asks + // to open a file on the host (the machine running WinDbg/KD (aka DbgEng)). + // These use the NT values, not Win32 API values. + + // Values for AccessMask. + // The ONLY bits (in the access mask) that DbgEng cares about are FILE_READ_DATA and FILE_WRITE_DATA, + // which internally DbgEng translates into GENERIC_READ and GENERIC_WRITE. DbgEng does not just + // pass these values directly to CreateFile. + + public const uint FILE_READ_DATA = 0x0001; + public const uint FILE_WRITE_DATA = 0x0002; + + // Values for FileShare. Passed unmodified to CreateFile. + public const uint FILE_SHARE_READ = 1; + public const uint FILE_SHARE_WRITE = 2; + public const uint FILE_SHARE_DELETE = 4; + + // Values for CreateDisposition. Translated into Win32 equivalents by DbgEng. + public const uint FILE_SUPERSEDE = 0x00000000; + public const uint FILE_OPEN = 0x00000001; + public const uint FILE_CREATE = 0x00000002; + public const uint FILE_OPEN_IF = 0x00000003; + public const uint FILE_OVERWRITE = 0x00000004; + public const uint FILE_OVERWRITE_IF = 0x00000005; + + #endregion + + /// + /// Transfers a file from the debugger to local memory. See CreateHostFile for more info. + /// + /// + /// The file to download; this filename has meaning only to the host debugger. + /// + /// + /// + public unsafe static bool DownloadHostFile(string filename, out byte[] FileData) + { + FileData = null; + + if (!Kd.IsDebuggerPresent()) + return false; + + long FileHandle; + long FileLength64; + + Dbg("Requesting file '{0}' from debugger", filename); + + bool create_result = CreateHostFile( + out FileHandle, + out FileLength64, + filename, + FILE_READ_DATA, // DesiredAccess + 0, // FileAttributes + FILE_SHARE_READ, // ShareAccess + FILE_OPEN); // CreateDisposition + if (!create_result) { + Dbg("Failed to download file '{0}' from debugger.", filename); + return false; + } + + if (FileLength64 > Int32.MaxValue) { + Dbg("Cannot download ridiculously huge file (>2GiB) over kernel debugger."); + return false; + } + int FileLength = (int)FileLength64; + + try { + byte[] contents = new byte[FileLength]; + + int total_bytes_transferred = 0; + + for (;;) { + + int bytes_remaining = FileLength - total_bytes_transferred; + + if (bytes_remaining == 0) { + Dbg("Received entire file successfully!"); + FileData = contents; + return true; + } + + int bytes_requested = Math.Min(0x800, bytes_remaining); + int bytes_transferred; + bool read_result; + + fixed (byte* contents_pinned = &contents[0]) + { + read_result = ReadHostFile( + FileHandle, + total_bytes_transferred, + &contents_pinned[total_bytes_transferred], + bytes_requested, + out bytes_transferred); + } + + if (!read_result) { + Dbg("FAILED to receive part of file. Read at offset 0x{0:x} failed.", total_bytes_transferred); + FileData = null; + return false; + } + + if (bytes_transferred == 0) { + Dbg("FAILED to receive all bytes of file. Received {0} of {1} bytes.", total_bytes_transferred, FileLength); + FileData = null; + return false; + } + + total_bytes_transferred += bytes_transferred; + } + } + finally { + CloseHostFile(FileHandle); + } + } + } + + #region KD/DbgEng-compatible structures + // These structures define the format of messages exchanged between the kernel and + // the kernel debugger (KD/DbgEng). Explicit layout is used to eliminate any possibility + // of smart compilers helping out. + // + // In KD/NT, there are no request/response structures. Instead, the same structure is used + // for each specific request type. I have split the structures into request/response, and + // used explicit field offsets to make sure that the fields are in the right places. Code + // that builds requests should always use "new XxxRequest()", in order to guarantee that + // memory locations within the structures that are not covered by fields are zero-filled. + + /// + /// Encodes the "Create Host File" request, which allows this OS instance to open a file that + /// resides on the debugger's machine, with the cooperation of the host debugger. + /// + /// This structure is bit-compatible with the Windows DBGKD_CREATE_FILE structure, defined in ntdbg.h, + /// at least when that structure is used as a request. + /// + /// The name of the file to open is carried in the "additional data", and is encoded in UTF-16 (LE), + /// with a Unicode NUL terminator. The name provided has meaning only to KD; KD uses a look-up table, + /// whose contents are set up by the .kdfiles debugger command, to translate these filenames into + /// actual host filenames. + /// + [CLSCompliant(false)] + [StructLayout(LayoutKind.Explicit, Size = 56)] + struct CreateHostFileRequest + { + [FieldOffset(0)] public uint DesiredAccess; + [FieldOffset(4)] public uint FileAttributes; + [FieldOffset(8)] public uint ShareAccess; + [FieldOffset(12)] public uint CreateDisposition; + [FieldOffset(16)] public uint CreateOptions; + } + + /// + /// This structure defines the response to the "Create Host File" request. This structure is bit- + /// compatible with the Windows DBGKD_CREATE_FILE structure, at least with the fields relevant when + /// that structure is used as a response. + /// + /// The field offsets may look strange -- why don't the fields start at 0? The reason is that + /// KD uses the same structure for both the request and (matching) response. So the fields in + /// this structure were originally part of DBGKD_CREATE_FILE / CreateHostFileRequest. Even then, + /// you need to take into account the 8-byte alignment for FileHandle; the compiler inserts 4 + /// byte of padding alignment between CreateOptions and FileHandle. + /// + /// + [StructLayout(LayoutKind.Explicit, Size = 56)] + struct CreateHostFileResponse + { + [FieldOffset(24)] public long FileHandle; + [FieldOffset(32)] public long FileLength; + } + + /// + /// Encodes the "Read Host File" request. This structure is bit-compatible with the Windows + /// DBGKD_READ_FILE structure, defined in ntdbg.h, when used as a request. + /// + /// Data is returned as additional data in the response. + /// + /// This structure is used for both the request and response. + /// + [CLSCompliant(false)] + [StructLayout(LayoutKind.Explicit, Size = 56)] + struct ReadHostFileRequest + { + [FieldOffset(0)] public long FileHandle; + [FieldOffset(8)] public long FileOffset; + [FieldOffset(16)] public uint Length; + } + + /// + /// Encodes the "Read Host File" response. This structure is bit-compatible with the Windows + /// DBGKD_READ_FILE structure, defined in ntdbg.h, when used as a response. + /// + [CLSCompliant(false)] + [StructLayout(LayoutKind.Explicit, Size = 56)] + struct ReadHostFileResponse + { + [FieldOffset(16)] public uint BytesTransferred; + } + + /// + /// Encodes the "Write Host File" request. This structure is bit-compatible with the Windows + /// DBGKD_WRITE_FILE structure, if you exclude the header. + /// + /// The data to write is carried in the "additional data" field. + /// + /// This structure is used for both the request and response. + /// + [CLSCompliant(false)] + [StructLayout(LayoutKind.Explicit, Size = 56)] + struct WriteHostFileRequest + { + [FieldOffset(0)] public long FileHandle; + [FieldOffset(8)] public long FileOffset; + [FieldOffset(16)] public uint Length; + } + + /// + /// Encodes the "Write Host File" response. This structure is bit-compatible with the Windows + /// DBGKD_WRITE_FILE_STRUCTURE, when used as a response. + /// + [CLSCompliant(false)] + [StructLayout(LayoutKind.Explicit, Size = 56)] + struct WriteHostFileResponse + { + [FieldOffset(16)] public uint BytesTransferred; + } + + /// + /// Encodes the "Close Host File" request. This structure is bit-compatible with the Windows + /// DBGKD_CLOSE_FILE structure. There is no matching CloseHostFileResponse structure, because + /// there are no request-specific return fields. + /// + [CLSCompliant(false)] + [StructLayout(LayoutKind.Explicit, Size = 56)] + public struct CloseHostFileRequest + { + [FieldOffset(0)] public long FileHandle; + } + + /// + /// All reqests transmitted from the kernel to the debugger (KD) use this message format. + /// The length is always 64 bytes. + /// + /// This structure is bit-compatible with the Windows DBGKD_FILE_IO structure. + /// + [CLSCompliant(false)] + [StructLayout(LayoutKind.Explicit, Size = 64)] + struct HostFileIoRequest + { + [FieldOffset(0)] public uint ApiNumber; + [FieldOffset(8)] public CreateHostFileRequest CreateFile; + [FieldOffset(8)] public CloseHostFileRequest CloseFile; + [FieldOffset(8)] public ReadHostFileRequest ReadFile; + [FieldOffset(8)] public WriteHostFileRequest WriteFile; + } + + /// + /// All responses transmitted from the debugger (KD) to the kernel use this message format. + /// The length is always 64 bytes. + /// + /// This structure is bit-compatible with the Windows DBGKD_FILE_IO structure. + /// + [CLSCompliant(false)] + [StructLayout(LayoutKind.Explicit, Size = 64)] + struct HostFileIoResponse + { + [FieldOffset(4)] public uint Status; + [FieldOffset(8)] public CreateHostFileResponse CreateFile; + [FieldOffset(8)] public ReadHostFileResponse ReadFile; + [FieldOffset(8)] public WriteHostFileResponse WriteFile; + } + + #endregion +} diff --git a/base/Kernel/Singularity/Kernel.cs b/base/Kernel/Singularity/Kernel.cs index 4354d68..91db406 100644 --- a/base/Kernel/Singularity/Kernel.cs +++ b/base/Kernel/Singularity/Kernel.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Kernel.cs -// // Note: // @@ -20,18 +18,19 @@ using System.Threading; using Microsoft.Bartok.Runtime; using Microsoft.SingSharp; + using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Directory; using Microsoft.Singularity.Drivers; +using Microsoft.Singularity.Eventing; 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; +using Microsoft.Singularity.Xml; [assembly: AssemblyTitle("Microsoft.Singularity")] [assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] @@ -42,15 +41,20 @@ using Microsoft.Singularity.V1.Services; namespace Microsoft.Singularity { + [NoCCtor] [CLSCompliant(false)] + [AccessedByRuntime("referenced from hal.cpp/hal.cpp")] public class Kernel { private static string[] args; private static ManualResetEvent mpEndEvent; private static int bootReturnCode; - #region Waypoints + // sample profiler kernel data + public static uint ProfilerBufferSize; + +#region Waypoints public static long[] Waypoints; public static int[] WaypointSeq; @@ -59,7 +63,41 @@ namespace Microsoft.Singularity 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! + public const int NWP = 2048; // XXX PBAR if this is readonly then it seems to read as 0! + + // This is temporary, until we get a HAL-based plug-and-play system going. + public const byte HalIpiInterrupt = 249; + + /// Flag indicating if kernel has succesfully booted + private static bool hasBooted; + private static Scheduler scheduler; + + /// + /// + /// Property to find out if kernel finished booting + /// + /// + public static bool HasBooted + { + [NoHeapAllocation] + get + { + return hasBooted; + } + } + + /// + /// Get a default kernel scheduler + /// + internal static Scheduler TheScheduler + { + [Inline] + [NoHeapAllocation] + get + { + return scheduler; + } + } [Conditional("WAYPOINTS")] [NoHeapAllocation] @@ -76,7 +114,7 @@ namespace Microsoft.Singularity [NoHeapAllocation] public static void Waypoint(int num) { - if (Waypoints == null || Waypoints[0] == 0 || WaypointNumber > NWP-1) { + if (Waypoints == null || Waypoints[0] == 0 || WaypointNumber > NWP - 1) { return; } @@ -137,18 +175,83 @@ namespace Microsoft.Singularity } WaypointSamples = 0; } - #endregion // Waypoints +#endregion // Waypoints + internal static DateTime BootTime; + + // [System.Diagnostics.Conditional("ISA_ARM")] + private static void ARM_PROGRESS(string msg) + { + DebugStub.WriteLine(msg); + } + + [System.Diagnostics.Conditional("ISA_ARM")] + private static void ARM_SPIN_FOREVER(string msg) + { + DebugStub.Assert(!Processor.InterruptsDisabled()); + Processor p = Processor.CurrentProcessor; + + DebugStub.WriteLine(msg); + DateTime last = DateTime.Now; + + uint n = 0; + + while (true) { + Thread.Sleep(1000); + } + } // Note: This function is called by Hal.cpp. [AccessedByRuntime("referenced from hal.cpp")] internal static int Main() { - bootReturnCode = BootInfo.EXIT_AND_RESTART; + bootReturnCode = Platform.EXIT_AND_RESTART; + InitPreMonitoring(); + try { + InitServices(); + // Consider boot successful at this stage. + bootReturnCode = Platform.EXIT_AND_SHUTDOWN; - DebugStub.WriteLine("Kernel.Main()"); + ARM_SPIN_FOREVER("kernel.arm Spinning forever"); - // Initialize the memory subsystem. This turns on paging! + bootReturnCode = SpawnAndWaitForShell(); + // The shell has exited when we get here + + FinalizeServices(); + } + catch (Exception e) { + System.Console.WriteLine("EXCEPTION:: " + e.Message); + 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)); + FinalizePreMonitoring(); + if (bootReturnCode != Platform.EXIT_AND_WARMBOOT) { + Kill(bootReturnCode); + } + return bootReturnCode; + } + + private static void InitPreMonitoring() + { + Tracing.Log(0); + Tracing.Log(1); + Tracing.Log(2); + Tracing.Log(3); + DebugStub.WriteLine("-------------------------------------------------------------------------------"); + + ARM_PROGRESS("Kernel!001"); + // Indicate that we are not booted yet + hasBooted = false; + + // Rather than mark all bootstrap code with [NoBarriers], perform a mini- + // initialization that gives us a working WriteBarrier. + System.GCs.Barrier.PreInitialize(); + + ARM_PROGRESS("Kernel!002"); + // Initialize the memory subsystem. If enabled this turns on paging MemoryManager.Initialize(); // Note for Monitoring early boot process: @@ -158,311 +261,552 @@ namespace Microsoft.Singularity // copy over all the collected data up to now to the new // dynamically created buffer and continue Monitoring.Initialize(); // uses page memory - + ARM_PROGRESS("Kernel!003"); HandleTable.Initialize(); + } + + private static void InitGCSupport() + { + ARM_PROGRESS("Kernel!004"); + // Initialize the rest of the primitive runtime. + VTable.Initialize((RuntimeType)typeof(Kernel)); + ARM_PROGRESS("Kernel!005"); + // Must occur before MemoryManager.PostGCInitialize() + ProtectionDomain.Initialize(); + ARM_PROGRESS("Kernel!006"); + // Must occur before SharedHeap.Initialize() + MemoryManager.PostGCInitialize(); + ARM_PROGRESS("Kernel!007"); + // Allocates callback objects for stack growth Stacks.Initialize(); + ARM_PROGRESS("Kernel!008"); + // Initialize the HAL parts which require GC allocation + Platform.ThePlatform.InitializeServices(); + ARM_PROGRESS("Kernel!009"); + SharedHeap.Initialize(); + ARM_PROGRESS("Kernel!010"); + // Must occur after SharedHeap.Initialize(); + ProtectionDomain.DefaultDomain.InitHook(); + } - try { - // Initialize the rest of the primitive runtime. - VTable.Initialize((RuntimeType)typeof(Kernel)); + private static void InitServices() + { + InitGCSupport(); + args = GetCommandLine(); + VTable.ParseArgs(args); -#if PAGING - // Must occur before MemoryManager.PostGCInitialize() - ProtectionDomain.Initialize(); -#endif + ARM_PROGRESS("Kernel!011"); + InitSchedulerTypes(); - // Must occur before SharedHeap.Initialize() - MemoryManager.PostGCInitialize(); + ARM_PROGRESS("Kernel!018"); + Controller.InitializeSystem(); + Tracing.InitializeSystem(); - SharedHeap.Initialize(); + ARM_PROGRESS("Kernel!019"); + // Read the profiler settings. The values are assumed in kbytes + // convert them to bytes for direct consumption + ProfilerBufferSize = (uint)GetIntegerArgument("profiler", 0); + ProfilerBufferSize *= 1024; -#if PAGING - // Must occur after SharedHeap.Initialize(); - ProtectionDomain.DefaultDomain.InitHook(); -#endif + ARM_PROGRESS("Kernel!020"); - args = GetCommandLine(); - VTable.ParseArgs(args); + SpinLock.StaticInitialize(); - // Initialize the processor table. - InitType(typeof(Processor)); - InitType(typeof(Scheduler)); - InitType(typeof(MinScheduler)); + int cpusLength; + int cpuCount = GetCpuCount(out cpusLength); - Processor.InitializeProcessorTable(); + Processor.InitializeProcessorTable(cpusLength); + ARM_PROGRESS("Kernel!021"); + Tracing.Log(Tracing.Audit, "processor"); + Processor processor = Processor.EnableProcessor(0); - Tracing.Log(Tracing.Audit, "processor"); - Processor processor = Processor.processorTable[0]; - processor.Initialize(0); + PEImage.Initialize(); + ARM_PROGRESS("Kernel!034"); - PEImage.Initialize(); + // Initialize the sample profiling for the processor + // after the initial breakpoint in kd in the call + // PEImage.Initialize(). This will allow enabling profiling + // from kd, by overwriting the ProfilerBufferSize value + processor.EnableProfiling(); + ARM_PROGRESS("Kernel!035"); + FlatPages.InitializeMemoryMonitoring(); - // initialize endpoints - InitType(typeof(Microsoft.Singularity.Channels.EndpointCore)); + // 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"); + // TODO Bug 59: Currently broken, need to review paging build. +//#if PAGING +// Microsoft.Singularity.Channels.EndpointTrusted.StaticInitialize(); +//#endif + ARM_PROGRESS("Kernel!036"); - PerfCounters.Initialize(); - // need to have processed the manifest before we can call Process initialize - PrincipalImpl.Initialize(initConfig); - Process.Initialize(manifestRoot.GetChild("processConfig")); + // get the system manifest + IoMemory systemManifest = GetSystemManifest(); + ARM_PROGRESS("Kernel!037"); + XmlReader xmlReader = new XmlReader(systemManifest); + XmlNode xmlData = xmlReader.Parse(); + XmlNode manifestRoot = xmlData.GetChild("system"); + XmlNode initConfig = manifestRoot.GetChild("initConfig"); + ARM_PROGRESS("Kernel!038"); + PerfCounters.Initialize(); + // need to have processed the manifest before we can call Process initialize + ARM_PROGRESS("Kernel!039"); + PrincipalImpl.Initialize(initConfig); - // obtain the configuration for the namespace service - // and initialize the namespace service - DirectoryService.Initialize(initConfig); + ARM_PROGRESS("Kernel!040"); + Process.Initialize(manifestRoot.GetChild("processConfig")); - Tracing.Log(Tracing.Audit, "IoSystem"); - IoSystem.Initialize( - manifestRoot.GetChild("drivers")); + InitIO(processor, initConfig, manifestRoot.GetChild("drivers")); - Tracing.Log(Tracing.Audit, "Registering HAL Drivers."); - Devices.RegisterPnpResources(); // add the root device + InitBootTime(); - HalDevices.Initialize(processor); // + ARM_PROGRESS("Kernel!045"); + // From here on, we want lazy type initialization to worry about + // competing threads. + VTable.InitializeForMultipleThread(); -#if SINGULARITY_MP - // haryadi -- at this point we have the SRAT table, - // Create per-processor address space now - MemoryManager.InitializeProcessorAddressSpace(); -#endif + ARM_PROGRESS("Kernel!046"); + Console.WriteLine("Running C# Kernel of {0}", GetLinkDate()); + Console.WriteLine(); - // From here on, we want lazy type initialization to worry about - // competing threads. - VTable.InitializeForMultipleThread(); + // TODO: remove this + Console.WriteLine("Current time: {0}", SystemClock.GetUtcTime().ToString("r")); + ARM_PROGRESS("Kernel!047"); - Console.WriteLine("Running C# Kernel of {0}", GetLinkDate()); + InitScheduling(); - Console.WriteLine(); + DirectoryService.StartNotificationThread(); - Console.WriteLine("Initializing Scheduler"); - MinScheduler.Initialize(Process.idleProcess); + Console.WriteLine("Initializing Shared Heap Walker"); + ProtectionDomain.InitializeSharedHeapWalker(); + ARM_PROGRESS("Kernel!050"); + Console.WriteLine("Initializing Service Thread"); + ServiceThread.Initialize(); + ARM_PROGRESS("Kernel!051"); - DirectoryService.StartNotificationThread(); + GC.EnableHeap(); + GCProfilerLogger.StartProfiling(); + ARM_PROGRESS("Kernel!052"); - Console.WriteLine("Initializing Shared Heap Walker"); - Process.InitializeSharedHeapWalker(); + Tracing.Log(Tracing.Audit, "Waypoints init"); + Waypoints = new long[2048]; + WaypointSeq = new int[2048]; + WaypointThd = new int[2048]; - Console.WriteLine("Initializing Service Thread"); - ServiceThread.Initialize(); + Tracing.Log(Tracing.Audit, "Interrupts ON."); + Processor.RestoreInterrupts(true); + ARM_PROGRESS("Kernel!053"); -#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); +#if ISA_ARM && TEST_GC + for (int i = 0; i < 1000; i++) { + DebugStub.WriteLine("Iteration {0}", __arglist(i)); + ArrayList a = new ArrayList(); + for (int j = 0; j < 128; j++) { + int size = 1024 * 1024; + a.Add(new byte [size]); } - 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); + } +#endif // ISA_ARM + + ARM_PROGRESS("Kernel!054"); + + Tracing.Log(Tracing.Audit, "Binder"); + Binder.Initialize(manifestRoot.GetChild("namingConventions")); + +#if ISA_ARM + DebugStub.WriteLine("Exporting local namespace to BSP\n"); + DirectoryService.ExportArmNamespace(); + DebugStub.WriteLine("Export complete...redirecting binder\n"); + Binder.RedirectRootRef(); + DebugStub.WriteLine("Binder redirect complete\n"); +#endif + +#if false + Tracing.Log(Tracing.Audit, "Starting Security Service channels"); + PrincipalImpl.Export(); + ARM_PROGRESS("Kernel!055"); +#endif + Tracing.Log(Tracing.Audit, "Creating Root Directory."); + + //This can be moved below + IoSystem.InitializeDirectoryService(); + ARM_PROGRESS("Kernel!055"); + +#if false + // Start User space namespace manager + Console.WriteLine("Starting Directory Service SIP"); + DirectoryService.StartUserSpaceDirectoryService(); +#endif + ARM_PROGRESS("Kernel!055.5"); + +#if !ISA_ARM + Tracing.Log(Tracing.Audit, "Starting Security Service channels"); + PrincipalImpl.Export(); +#endif + + ARM_PROGRESS("Kernel!056"); + + Console.WriteLine("Initializing system channels"); + + // starting channels services + DebugStub.Print("Initializing Channel Services\n"); + ChannelDeliveryImplService.Initialize(); + + ARM_PROGRESS("Kernel!057"); + ConsoleOutput.Initialize(); + + ARM_PROGRESS("Kernel!058"); + + // 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"); + + // For ABI to ARM support + MpExecution.Initialize(); + ARM_PROGRESS("Kernel!059"); + + mpEndEvent = new ManualResetEvent(false); + + Tracing.Log(Tracing.Audit, "Initializing Volume Manager."); + +#if !ISA_ARM + IoSystem.InitializeVolumeManager(); +#endif // ISA_ARM + ARM_PROGRESS("Kernel!060"); + + InitDrivers(); + + if (cpuCount > 1) { + unsafe { + Console.WriteLine("Enabling {0} cpus out of {1} real cpus\n", cpuCount, Platform.ThePlatform.CpuRealCount); } + Processor.EnableMoreProcessors(cpuCount); + ARM_PROGRESS("Kernel!064"); + } + + Tracing.Log(Tracing.Audit, "Initializing Service Manager."); + IoSystem.InitializeServiceManager(manifestRoot.GetChild("serviceConfig")); + ARM_PROGRESS("Kernel!065"); + + InitDiagnostics(); + +#if !ISA_ARM + // At this point consider kernel finshed booting + hasBooted = true; +#endif // ISA_ARM + + Processor.StartSampling(); + ARM_PROGRESS("Kernel!069"); + + Microsoft.Singularity.KernelDebugger.KdFilesNamespace.StartNamespaceThread(); + ARM_PROGRESS("Kernel!070"); + + } + + private static void InitSchedulerTypes() + { + InitType(typeof(Processor)); + InitType(typeof(Scheduler)); + InitType(typeof(ProcessorDispatcher)); + // InitType(typeof(AffinityScheduler)); + InitType(typeof(MinScheduler)); + + ARM_PROGRESS("Kernel!013"); + // Create scheduler before initializing processes + scheduler = new MinScheduler(); + + ARM_PROGRESS("Kernel!017"); + // Initialize processor dispatcher + ProcessorDispatcher.StaticInitialize(scheduler); + } + + private static void InitScheduling() + { + Console.WriteLine("Initializing Scheduler"); + + ARM_PROGRESS("Kernel!047"); + // Finish scheduler initialization: + scheduler.Initialize(); + ARM_PROGRESS("Kernel!048"); + + // Initialize initial dispatcher + Processor.InitializeDispatcher(0); + Processor.ActivateTimer(0); + ARM_PROGRESS("Kernel!049"); + } + + private static void InitIO(Processor p, XmlNode initConfig, XmlNode driverConfig) + { + // obtain the configuration for the namespace service + // and initialize the namespace service + ARM_PROGRESS("Kernel!041"); + DirectoryService.Initialize(initConfig); + + Tracing.Log(Tracing.Audit, "IoSystem"); + ARM_PROGRESS("Kernel!042"); + IoSystem.Initialize(driverConfig); + + Tracing.Log(Tracing.Audit, "Registering HAL Drivers."); + + ARM_PROGRESS("Kernel!043"); + + Devices.RegisterPnpResources(); // add the root devices + + ARM_PROGRESS("Kernel!044"); + + Platform.InitializeHal(p); + } + + private static int SpawnAndWaitForShell() + { + +#if ISA_ARM + // spin here for now + while (true) { + Thread.Yield(); + } #else - memory = Binder.LoadImage(Thread.CurrentProcess, "tty.x86", out manifest); + + Tracing.Log(Tracing.Audit, "Creating Shell Process"); + ARM_PROGRESS("Kernel!071"); + int exit = -10000; + Manifest manifest; +#if KERNEL_USE_LOGIN + IoMemory memory; + if (args[0] == "bvt") { + memory = Binder.LoadImage(Thread.CurrentProcess, "tty", 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", out manifest); + } +#else + IoMemory memory = Binder.LoadImage(Thread.CurrentProcess, "tty", out manifest); #endif + ARM_PROGRESS("Kernel!073"); - 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; - } + if (memory == null || memory.Length > 0) { + String[] shellArgs = new String[args.Length + 2]; + shellArgs[0] = "tty"; + shellArgs[1] = "shell"; + for (int i = 0; i < args.Length; i++) { + shellArgs[i + 2] = args[i]; + } + Process process = new Process(Thread.CurrentProcess, memory, null, shellArgs, manifest); + if (process != null) { + PrintBootTime(); + 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)); + ARM_PROGRESS("Kernel!074"); } - catch (Exception e) { - Tracing.Log(Tracing.Fatal, "Caught exception {0}", e.Message); - DebugStub.WriteLine("Caught {0}", __arglist(e.Message)); - bootReturnCode = -1; + return DetermineShutdown(exit); +#endif + } + + private static int DetermineShutdown(int exit) + { + switch (exit) { + case -10000: + Tracing.Log(Tracing.Audit, "Failed to start shell process."); + return Platform.EXIT_AND_RESTART; + case Platform.EXIT_AND_WARMBOOT: + case Platform.EXIT_AND_RESTART: + case Platform.EXIT_AND_SHUTDOWN: + return exit; + default: + DebugStub.WriteLine("Shell process terminated improperly (0x{0:x4})", + __arglist(exit)); + Tracing.Log(Tracing.Audit, "Shell process terminated improperly"); + DebugStub.Break(); + return Platform.EXIT_AND_SHUTDOWN; + } + } + + private static void InitBootTime() + { + if (Platform.ThePlatform.BootYear != 0) { + BootTime = new DateTime((int)Platform.ThePlatform.BootYear, + (int)Platform.ThePlatform.BootMonth, + (int)Platform.ThePlatform.BootDay, + (int)Platform.ThePlatform.BootHour, + (int)Platform.ThePlatform.BootMinute, + (int)Platform.ThePlatform.BootSecond, + 0); + // There is no time zone support in Singularity, the clock assumes GMT + // but the CMOS reports it in local time. Hardcode the correction here for now + BootTime = BootTime.AddHours(8); + } + else { + // Other HALs do not capture the load time. Make the best effort to + // capture now, after the devices are initialized. + BootTime = DateTime.Now; + } + } + + private static void InitDrivers() + { + // 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(); + ARM_PROGRESS("Kernel!061"); + + // register the internal kernel drivers + Devices.RegisterInternalDrivers(); + ARM_PROGRESS("Kernel!062"); +#if DEBUG + // and output the results + IoSystem.Dump(false); +#endif + DebugStub.WriteLine("--- Activating Devices ----------------------------"); + // now do device initialization + IoSystem.ActivateDrivers(); + ARM_PROGRESS("Kernel!063"); +#if DEBUG + // and output the results + // IoSystem.Dump(true); +#endif + } + + private static void InitDiagnostics() + { + // Start up the kernel's diagnostics module + Console.WriteLine("Starting diagnostics module..."); + Diagnostics.DiagnosticsModule.Initialize(); + ARM_PROGRESS("Kernel!066"); + // Start up the kernel's stress test module + Console.WriteLine("Initializing stress module..."); + Stress.StressService.Initialize(); + ARM_PROGRESS("Kernel!067"); + } + + private static IoMemory GetSystemManifest() + { + IoMemory res = Binder.GetSystemManifest(); + if (res == null) { + DebugStub.WriteLine("Failed to load system manifest."); DebugStub.Break(); + throw new Exception("The system manifest could not be loaded."); + } + return res; + } + + private static void FinalizeServices() + { + ARM_PROGRESS("Kernel!075"); + 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"); + for (int i = 0; i < Processor.processorTable.Length; i++) { + Processor p = Processor.processorTable[i]; + if (p != null) { + Console.WriteLine(" cpu {0}: {1} context switches, {2} interrupts", i, p.NumContextSwitches, p.NumInterrupts); + } } - DebugStub.WriteLine("Kernel exiting with 0x{0:x4}", - __arglist(bootReturnCode)); + // Finalize the scheduler + scheduler.Finalize(); + // We should turn off interrupts here! + Platform.ReleaseResources(); + PEImage.Finalize(); + + DebugStub.WriteLine("Kernel Exiting [{0}]", + __arglist(bootReturnCode)); + } + + private static void FinalizePreMonitoring() + { Stacks.Finalize(); HandleTable.Finalize(); -#if !PAGING SharedHeap.Finalize(); -#endif //PAGING - MemoryManager.Finalize(); + } - if (bootReturnCode != BootInfo.EXIT_AND_WARMBOOT) { - Kill(bootReturnCode); + private static int GetCpuCount(out int cpusLength) + { + int cpuReal = Platform.ThePlatform.CpuRealCount; + int cpuLimit = Platform.ThePlatform.CpuMaxCount; + DebugStub.Assert(cpuReal <= cpuLimit); + + // See if the command line argument limits our processor count + int cpuCount = GetIntegerArgument("mp", cpuReal); + cpusLength = cpuReal; + +#if !SINGULARITY_MP + if (cpuCount > 1) { + Console.WriteLine("Limiting processors to 1 due to SP build"); + cpuCount = 1; } - return bootReturnCode; +#endif + return cpuCount; } // Note: This function is entry point to the managed // kernel for CPU's other than the bootstrap processor. - // It is called by Hal.cpp. + // It is called from the Hal or Hal. [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 processor = Processor.EnableProcessor(cpu); + + // Initialize dispatcher + Processor.InitializeDispatcher(cpu); + + // Initialize the HAL processor services + // Note that this must occur after EnableProcessor, as the kernel + // thread does not get bound until then. (It gets bound much + // earlier in the boot processor case -- need to decide if this difference is + // really appropriate.) + Platform.Cpu(cpu).InitializeServices(); + + Platform.InitializeHal(processor); + + Thread.CurrentThread.Affinity = cpu; + + Processor.ActivateTimer(cpu); + +#if DEBUG_SYSTEM_LOCKUP + // Spin one CPU on debugger. For instance with a quad-proc + // box, spin CPU 3. + while (cpu == 3) { + if (DebugStub.PollForBreak() == true) { + DebugStub.Break(); + } + } +#endif // DEBUG_SYSTEM_LOCKUP - 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."); + "Looping in processor's kernel thread."); + //while (cpu > 0) { + // Thread.Yield(); + //} // This probably won't be the // ultimate way of dissociating kernel entry threads // from the kernel. + DebugStub.Print("AP Processor started, about to wait\n"); mpEndEvent.WaitOne(); return 0; @@ -471,8 +815,9 @@ namespace Microsoft.Singularity // 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) + void VisitSpecialData(System.GCs.DirectReferenceVisitor visitor) { + Platform.ThePlatform.VisitSpecialData(visitor); Process.VisitSpecialData(visitor); visitor.VisitReferenceFields(Processor.processorTable); } @@ -484,8 +829,9 @@ namespace Microsoft.Singularity Processor.UpdateAfterGC(currentThread); } + [AccessedByRuntime("defined in HAL.cpp")] [MethodImpl(MethodImplOptions.InternalCall)] - [StackBound(8)] + [StackBound(256)] [NoHeapAllocation] private static unsafe extern char * HalGetLinkDate(); @@ -497,7 +843,8 @@ namespace Microsoft.Singularity internal static unsafe string[] GetCommandLine() { String[] args = - (new String((char *)(BootInfo.HalGetBootInfo()->CmdLine32))).Split(null); + (new String((char *)(Platform.ThePlatform.CommandLine), + 0, Platform.ThePlatform.CommandLineCount)).Split(null); int dst = 0; for (int src = 0; src < args.Length; src++) { if (args[src] != null && args[src].Length > 0) { @@ -514,28 +861,67 @@ namespace Microsoft.Singularity return args; } + // kind the argument "key" and extract the string value following a colon. + internal static string + GetStringArgument(string key, string defaultValue) + { + key = key + ":"; + for (int i = 0; i < args.Length; i++) { + string arg = args[i]; + if ((arg.StartsWith("-") || arg.StartsWith("/")) && + 0 == String.Compare(arg, 1, key, 0, key.Length, true)) { + string result = arg.Substring(key.Length+1); + string[] newargs = new string[args.Length - 1]; + Array.Copy(args, 0, newargs, 0, i); + Array.Copy(args, i + 1, newargs, i, args.Length - i - 1); + args = newargs; + return result; + } + } + return defaultValue; + } + + internal static int GetIntegerArgument(string key, int defaultValue) + { + key = key + ":"; + for (int i = 0; i < args.Length; i++) { + string arg = args[i]; + if ((arg.StartsWith("-") || arg.StartsWith("/")) && + 0 == String.Compare(arg, 1, key, 0, key.Length, true)) { + string result = arg.Substring(key.Length+1); + string[] newargs = new string[args.Length - 1]; + Array.Copy(args, 0, newargs, 0, i); + Array.Copy(args, i + 1, newargs, i, args.Length - i - 1); + args = newargs; + // TODO this should be try-parse + return Int32.Parse(result); + } + } + return defaultValue; + } + [NoHeapAllocation] public static void RequestWarmBoot() { - bootReturnCode = BootInfo.EXIT_AND_WARMBOOT; + bootReturnCode = Platform.EXIT_AND_WARMBOOT; } [NoHeapAllocation] public static void RequestRestart() { - bootReturnCode = BootInfo.EXIT_AND_RESTART; + bootReturnCode = Platform.EXIT_AND_RESTART; } [NoHeapAllocation] public static void RequestShutdown() { - bootReturnCode = BootInfo.EXIT_AND_SHUTDOWN; + bootReturnCode = Platform.EXIT_AND_SHUTDOWN; } - [MethodImpl(MethodImplOptions.InternalCall)] - [StackBound(1024)] - [NoHeapAllocation] - private static extern void Kill(int exitCode); + private static void Kill(int exitCode) + { + Platform.ThePlatform.Kill(exitCode); + } internal static void Shutdown(int exitCode) { @@ -548,10 +934,17 @@ namespace Microsoft.Singularity VTable.Shutdown(exitCode); } + // Rationale for cutting the fact that no stack probes are permitted in this + // tree of calls: + // + // * We are already hosed. + // * Without this cut, places that cannot (transitively) tolerate stack link extensions + // * cannot call Panic. + [NoStackLinkCheckTransCut][IgnoreLockRank] internal static void Panic(string why) { DebugStub.WriteLine("KERNEL PANIC: {0}", __arglist(why)); - Shutdown(BootInfo.EXIT_AND_HALT); + Shutdown(Platform.EXIT_AND_HALT); } ////////////////////////////////////////////////////////////////////// @@ -627,5 +1020,14 @@ namespace Microsoft.Singularity { System.GCs.PageTable.Dump("PageTable"); } + + internal static void PrintBootTime() + { + // Console.WriteLine("Current time: {0}", SystemClock.GetUtcTime().ToString("r")); + // Console.WriteLine("Boot time: {0}", BootTime.ToString("r")); + + TimeSpan delta = SystemClock.GetUtcTime() - BootTime; + DebugStub.WriteLine("Elapsed boot time = {0} msec.", __arglist(delta.TotalMilliseconds)); + } } } diff --git a/base/Kernel/Singularity/KernelDebugger.cs b/base/Kernel/Singularity/KernelDebugger.cs new file mode 100644 index 0000000..875753b --- /dev/null +++ b/base/Kernel/Singularity/KernelDebugger.cs @@ -0,0 +1,309 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Runtime.CompilerServices; + +namespace Microsoft.Singularity.KernelDebugger +{ + /// + /// This class exposes some of the unmanaged kernel debugger functions, mainly + /// KdSendPacket and KdReceivePacket. Eventually, more of the kernel debugger + /// support should be moved out of halkd.cpp and into this class. + /// + /// The main consumer of this class is the KdFiles class. + /// + [NoCCtor] + [CLSCompliant(false)] + [AccessedByRuntime("output to header : methods defined in halkd.cpp")] + public class Kd + { + [AccessedByRuntime("output to header : defined in halkd.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(1024)] + [NoHeapAllocation] + public static unsafe extern void SendPacket( + uint PacketType, + byte* MessageHeaderBuffer, + int MessageHeaderLength, + byte* MessageDataBuffer, + int MessageDataLength + ); + + [AccessedByRuntime("output to header : defined in halkd.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(1024)] + [NoHeapAllocation] + public static unsafe extern /*KdStatus*/ uint ReceivePacket( + uint PacketType, + byte* MessageHeaderBuffer, + int MessageHeaderLength, + byte* MessageDataBuffer, + int MessageDataBufferLength, + out int MessageDataLength + ); + + [AccessedByRuntime("output to header : defined in halkd.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(1024)] + [NoHeapAllocation] + public static extern void Lock(); + + [AccessedByRuntime("output to header : defined in halkd.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(1024)] + [NoHeapAllocation] + public static extern void Unlock(); + + public static unsafe void SendPacket( + KdPacketType PacketType, + byte* MessageHeaderBuffer, + int MessageHeaderLength, + byte* MessageDataBuffer, + int MessageDataLength + ) + { + // DebugStub.WriteLine(String.Format("MessageHeaderBuffer = 0x{0:x}", (UIntPtr)MessageHeaderBuffer)); + // DebugStub.WriteLine("Kd.SendPacket: Sending packet to KD..."); + // Console.WriteLine("Kd.SendPacket: Sending packet to KD..."); + SendPacket( + (uint)PacketType, + MessageHeaderBuffer, + MessageHeaderLength, + MessageDataBuffer, + MessageDataLength); + // DebugStub.WriteLine("Kd.SendPacket: KdSendPacket has returned."); + // Console.WriteLine("Kd.SendPacket: KdSendPacket has returned."); + } + + public static unsafe KdStatus ReceivePacket( + KdPacketType PacketType, + byte* MessageHeaderBuffer, + int MessageHeaderLength, + byte* MessageDataBuffer, + int MessageDataBufferLength, + out int MessageDataLength + ) + { + // DebugStub.WriteLine(String.Format("Kd.ReceivePacket: MessageHeaderBuffer = 0x{0:x}", (UIntPtr)MessageHeaderBuffer)); + // Console.WriteLine("Kd.ReceivePacket: Waiting for packet from KD..."); + + KdStatus status = (KdStatus)ReceivePacket( + (uint)PacketType, + MessageHeaderBuffer, + MessageHeaderLength, + MessageDataBuffer, + MessageDataBufferLength, + out MessageDataLength); + + // Console.WriteLine("Kd.ReceivePacket: KdReceivePacket has returned. Status = " + status); + // DebugStub.Break(); + return status; + } + + public static unsafe bool SendRequestWaitResponse( + KdPacketType PacketType, + byte* RequestHeaderBuffer, + int RequestHeaderLength, + byte* RequestDataBuffer, + int RequestDataLength, + byte* ResponseHeaderBuffer, + int ResponseHeaderLength, + byte* ResponseDataBuffer, + int ResponseDataMaximumLength, + out int ResponseDataActualLength) + { + ResponseDataActualLength = 0; + + send_packet: + if (!IsDebuggerPresent()) + return false; + + SendPacket( + PacketType, + RequestHeaderBuffer, + RequestHeaderLength, + RequestDataBuffer, + RequestDataLength); + + receive_packet: + KdStatus status = (KdStatus)ReceivePacket( + (uint)PacketType, + ResponseHeaderBuffer, + ResponseHeaderLength, + ResponseDataBuffer, + ResponseDataMaximumLength, + out ResponseDataActualLength); + + if (status == KdStatus.PacketReceived) { + Dbg("Received response. Actual data length = {0}", ResponseDataActualLength); + return true; + } + + if (status == KdStatus.PacketTimeout) { + if (IsDebuggerPresent()) { + Dbg("Received PacketTimeout, trying again"); + goto receive_packet; + } + else { + ResponseDataActualLength = 0; + return false; + } + } + + if (status == KdStatus.PacketResend) { + Dbg("Received PacketResend -- resending packet."); + goto send_packet; + } + + Dbg("Received unrecognized response from KdReceivePacket. Return value: {0}", (uint)status); + ResponseDataActualLength = 0; + return false; + } + + public static bool IsDebuggerPresent() + { + return DebugStub.IsDebuggerPresent(); + } + + public const int MaxPacketSize = 4096; + + static bool _debug_to_console; + + static void Dbg(string line) + { + if (_debug_to_console) + Console.WriteLine("KernelDebugger.cs: " + line); + } + + static void Dbg(string format, params object[] args) + { + Dbg(String.Format(format, args)); + } + } + + /// + /// Compatible with PACKET_TYPE_XXX defines in halkd.h. + /// + [CLSCompliant(false)] + [AccessedByRuntime("referenced from halkd.cpp")] + public enum KdPacketType : uint + { + Unused = 0, + StateChange32 = 1, + StateManipulate = 2, + DebugIo = 3, + Acknowledge = 4,// Packet-control type + Resend = 5,// Packet-control type + Reset = 6,// Packet-control type + StateChange64 = 7, + PollBreakin = 8, + TraceIo = 9, + ControlRequest = 10, + [AccessedByRuntime("referenced from halkd.cpp")] FileIo = 11, + MaxValue = 12, + } + + /// + /// These values identify the structure of messages / requests sent between + /// the kernel and KD. Compatible with DbgKdXxxApi values. + /// + [CLSCompliant(false)] + public enum KdApi : uint + { + // State change constants + MinimumStateChange = 0x00003030u, + ExceptionStateChange = 0x00003030u, + LoadSymbolsStateChange = 0x00003031u, + CommandStringStateChange = 0x00003032u, + MaximumStateChange = 0x00003033u, + + // + // If the packet type is PACKET_TYPE_KD_STATE_MANIPULATE, then + // the format of the packet data is as follows: + // + // Api Numbers for state manipulation + // + + MinimumManipulate = 0x00003130u, + + ReadVirtualMemoryApi = 0x00003130u, + WriteVirtualMemoryApi = 0x00003131u, + GetContextApi = 0x00003132u, + SetContextApi = 0x00003133u, + WriteBreakPointApi = 0x00003134u, + RestoreBreakPointApi = 0x00003135u, + ContinueApi = 0x00003136u, + ReadControlSpaceApi = 0x00003137u, + WriteControlSpaceApi = 0x00003138u, + ReadIoSpaceApi = 0x00003139u, + WriteIoSpaceApi = 0x0000313Au, + RebootApi = 0x0000313Bu, + ContinueApi2 = 0x0000313Cu, + ReadPhysicalMemoryApi = 0x0000313Du, + WritePhysicalMemoryApi = 0x0000313Eu, + //QuerySpecialCallsApi = 0x0000313FL + SetSpecialCallApi = 0x00003140u, + ClearSpecialCallsApi = 0x00003141u, + SetInternalBreakPointApi = 0x00003142u, + GetInternalBreakPointApi = 0x00003143u, + ReadIoSpaceExtendedApi = 0x00003144u, + WriteIoSpaceExtendedApi = 0x00003145u, + GetVersionApi = 0x00003146u, + WriteBreakPointExApi = 0x00003147u, + RestoreBreakPointExApi = 0x00003148u, + CauseBugCheckApi = 0x00003149u, + SwitchProcessor = 0x00003150u, + PageInApi = 0x00003151u,// obsolete + ReadMachineSpecificRegister = 0x00003152u, + WriteMachineSpecificRegister = 0x00003153u, + OldVlm1 = 0x00003154u, + OldVlm2 = 0x00003155u, + SearchMemoryApi = 0x00003156u, + GetBusDataApi = 0x00003157u, + SetBusDataApi = 0x00003158u, + CheckLowMemoryApi = 0x00003159u, + ClearAllInternalBreakpointsApi = 0x0000315Au, + FillMemoryApi = 0x0000315Bu, + QueryMemoryApi = 0x0000315Cu, + SwitchPartition = 0x0000315Du, + + MaximumManipulate = 0x0000315Eu, + + + // + // If the packet type is PACKET_TYPE_KD_FILE_IO, then + // the format of the packet data is as follows: + // + + CreateFile = 0x00003430u, + ReadFile = 0x00003431u, + WriteFile = 0x00003432u, + CloseFile = 0x00003433u, + } + + + /// + /// Declares error codes for Kd.ReceivePacket. Values are compatible + /// with unmanaged defines KDP_PACKET_RECEIVED, etc. + /// + [AccessedByRuntime("referenced from halkd.cpp")] + public enum KdStatus + { + [AccessedByRuntime("referenced from halkd.cpp")] + PacketReceived = 0, + + [AccessedByRuntime("referenced from halkd.cpp")] + PacketTimeout = 1, + + [AccessedByRuntime("referenced from halkd.cpp")] + PacketResend = 2, + + } +} + + diff --git a/base/Kernel/Singularity/LimitedTypes.cs b/base/Kernel/Singularity/LimitedTypes.cs new file mode 100644 index 0000000..7cf86e2 --- /dev/null +++ b/base/Kernel/Singularity/LimitedTypes.cs @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; + +public class LimitedTypes +{ + // A list of types that Bartok's VTable class should + // initialize as a limited type. + public static Type[] TypeList { + get { + return new Type[] { + typeof(System.DateTime), + typeof(System.TimeSpan), + typeof(System.SchedulerTime), + + typeof(System.Type), + typeof(System.String), + typeof(System.BitConverter), + typeof(System.Math), + typeof(System.VTable) + }; + } + } +} diff --git a/base/Kernel/Singularity/Loader/BadImageFormatException.cs b/base/Kernel/Singularity/Loader/BadImageFormatException.cs index fbc4881..653d61a 100644 --- a/base/Kernel/Singularity/Loader/BadImageFormatException.cs +++ b/base/Kernel/Singularity/Loader/BadImageFormatException.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Simple PE Loader for Singularity diff --git a/base/Kernel/Singularity/Loader/DirectoryEntry.cs b/base/Kernel/Singularity/Loader/DirectoryEntry.cs index f1e50e0..9faf0a6 100644 --- a/base/Kernel/Singularity/Loader/DirectoryEntry.cs +++ b/base/Kernel/Singularity/Loader/DirectoryEntry.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Simple PE Loader for Singularity diff --git a/base/Kernel/Singularity/Loader/ExportTable.cs b/base/Kernel/Singularity/Loader/ExportTable.cs index d5530a0..228d8af 100644 --- a/base/Kernel/Singularity/Loader/ExportTable.cs +++ b/base/Kernel/Singularity/Loader/ExportTable.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Simple PE Loader for Singularity @@ -115,7 +113,7 @@ namespace Microsoft.Singularity.Loader internal UIntPtr Resolve(ushort hint, string name) { if (names != null) { - // haryadi --- we need put a guard here !!!! because + // we need to put a guard here. because // hint might exceed names.Length The guard in // particular is useful when we want to resolve // imports from MP applications with abi.dll @@ -127,7 +125,7 @@ namespace Microsoft.Singularity.Loader return addresses[hint]; } else { - //[SOSP] we should probably do a binary search. + // TODO: we should probably do a binary search. for (int i = 0; i < names.Length; i++) { if (names[i] == name) { return addresses[i]; diff --git a/base/Kernel/Singularity/Loader/ImportTable.cs b/base/Kernel/Singularity/Loader/ImportTable.cs index 0a6724b..d1935ae 100644 --- a/base/Kernel/Singularity/Loader/ImportTable.cs +++ b/base/Kernel/Singularity/Loader/ImportTable.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Simple PE Loader for Singularity @@ -37,8 +35,7 @@ namespace Microsoft.Singularity.Loader this.mem = mem; this.entry = entry; - if (entry.size == 0) - { + if (entry.size == 0) { DebugStub.WriteLine("// No import table."); return; } @@ -49,8 +46,7 @@ namespace Microsoft.Singularity.Loader public void DumpIAT(String title) { Tracing.Log(Tracing.Debug, "// {0}", title); - if (entry.size == 0) - { + if (entry.size == 0) { Tracing.Log(Tracing.Debug, "// No data."); return; } @@ -61,8 +57,7 @@ namespace Microsoft.Singularity.Loader // There is no indication in the header as to how many descriptors are present. // In the last descriptor all fields will be 0; this code checks just the firstChunk field. // - for (;;) - { + for (;;) { ImportDescriptor importDescriptor = new ImportDescriptor(mem, ref importOffset); @@ -81,8 +76,7 @@ namespace Microsoft.Singularity.Loader Tracing.Log(Tracing.Debug, "//"); int importTableOffset = (int)importDescriptor.characteristics; - for (;;) - { + for (;;) { int importTableID = (int) mem.Read32(importTableOffset); if (importTableID == 0) { break; @@ -110,8 +104,7 @@ namespace Microsoft.Singularity.Loader public void ResolveImports(ExportTable exports) { - if (entry.size == 0) - { + if (entry.size == 0) { Tracing.Log(Tracing.Debug, "// No imports to resolve."); return; } @@ -134,6 +127,7 @@ namespace Microsoft.Singularity.Loader if (importDescriptor.name != 0) { name = mem.ReadAsciiZeroString((int)importDescriptor.name); } + #if verbose Tracing.Log(Tracing.Debug, "// {0}", name); importDescriptor.DumpToStream("// "); @@ -149,6 +143,44 @@ namespace Microsoft.Singularity.Loader } UIntPtr offsetIAT = (UIntPtr)importDescriptor.firstChunk; +#if PTR_SIZE_64 + + // + // AIFIX: Add hint logic. + // + + for (;;) { + + ulong ImportEntry = (ulong) mem.Read64((int) offsetIAT); + + if (ImportEntry == 0) { + + break; + } + + if ((ImportEntry >> 63) != 0) { + + throw new BadImageFormatException("Import Ordinal encountered"); + } + + name = mem.ReadAsciiZeroString(((int) (ImportEntry & 0x7FFFFFFF)) + 2); + + UIntPtr Address; + + Address = exports.Resolve(hint, name); + + if (Address == 0) { + + throw new BadImageFormatException("Import not found"); + } + + mem.Write64((int) offsetIAT, (ulong) Address); + + offsetIAT += 8; + } + +#else + for (;;) { // read elements in the hint array for processing // the hint array terminates when the its content is 0 @@ -169,8 +201,7 @@ namespace Microsoft.Singularity.Loader //Tracing.Log(Tracing.Debug, " function to lookup is {0}", name); UIntPtr addr = exports.Resolve(hint, name); - if (0 != addr) - { + if (0 != addr) { //Overwrite ptr in IAT with address of function in the // IAT thunk table #if verbose @@ -183,10 +214,9 @@ namespace Microsoft.Singularity.Loader #endif mem.Write32((int) offsetIAT,(uint)addr); } - else - { + else { Tracing.Log(Tracing.Debug, " Import not found: {0}", name); - throw new BadImageFormatException("Import Ordinal encountered"); + throw new BadImageFormatException("Import not found"); } } // increment "array indices" @@ -194,6 +224,9 @@ namespace Microsoft.Singularity.Loader offsetHintTable +=4; } //Tracing.Log(Tracing.Debug, ""); + +#endif + } //throw new BadImageFormatException(""); } diff --git a/base/Kernel/Singularity/Loader/PEImage.cs b/base/Kernel/Singularity/Loader/PEImage.cs index 4f29dcc..8b164f3 100644 --- a/base/Kernel/Singularity/Loader/PEImage.cs +++ b/base/Kernel/Singularity/Loader/PEImage.cs @@ -1,18 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Simple PE Loader for Singularity @@ -24,7 +14,6 @@ // #define verbose -// haryadi // #define SINGULARITY_ASMP namespace Microsoft.Singularity.Loader @@ -38,10 +27,13 @@ namespace Microsoft.Singularity.Loader using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Threading; + using Microsoft.Singularity.V1.Services; + using Microsoft.Singularity.Hal; using PageType = System.GCs.PageType; [CLSCompliant(false)] + [AccessedByRuntime("output to header : defined in hal.cpp/peimage.cpp")] internal class PEImage { // PE Signature @@ -54,10 +46,14 @@ namespace Microsoft.Singularity.Loader internal readonly ushort sizeOfOptionalHeader; internal readonly ushort characteristics; +#if PTR_SIZE_64 + internal readonly ulong imageBase; +#else + internal readonly uint imageBase; +#endif // IMAGE_OPTIONAL_HEADER32 internal readonly ushort magic; internal readonly uint addressOfEntryPoint; - internal readonly uint imageBase; internal readonly uint sectionAlignment; internal readonly uint fileAlignment; private readonly uint sizeOfImage; // use loadedSize (this is rnd'd) @@ -78,6 +74,7 @@ namespace Microsoft.Singularity.Loader internal const ushort IMAGE_DOS_SIGNATURE = 0x5A4D; // MZ internal const uint IMAGE_NT_SIGNATURE = 0x00004550; // PE00 internal const ushort IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x010b; + internal const ushort IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x020b; enum Machine : ushort { UNKNOWN = 0, @@ -120,6 +117,8 @@ namespace Microsoft.Singularity.Loader Copyright = 7, // Description String }; + internal UIntPtr VirtualAddress; + // Corresponds to the WinNT IMAGE_NT_HEADERS data structure internal static PEImage kernel = null; internal static ExportTable kernelExports = null; @@ -145,12 +144,11 @@ namespace Microsoft.Singularity.Loader signature = mem.Read32Unchecked(offset + 0); machine = mem.Read16Unchecked(offset + 4); numberOfSections = mem.Read16Unchecked(offset + 6); - timeDateStamp = mem.Read16Unchecked(offset + 8); + timeDateStamp = mem.Read32Unchecked(offset + 8); sizeOfOptionalHeader = mem.Read16Unchecked(offset + 20); characteristics = mem.Read16Unchecked(offset + 22); magic = mem.Read16Unchecked(offset + 24); addressOfEntryPoint = mem.Read32Unchecked(offset + 40); - imageBase = mem.Read32Unchecked(offset + 52); sectionAlignment = mem.Read32Unchecked(offset + 56); fileAlignment = mem.Read32Unchecked(offset + 60); sizeOfImage = mem.Read32Unchecked(offset + 80); @@ -158,6 +156,24 @@ namespace Microsoft.Singularity.Loader checkSum = mem.Read32Unchecked(offset + 88); dllCharacteristics = mem.Read16Unchecked(offset + 94); loaderFlags = mem.Read32Unchecked(offset + 112); + +#if PTR_SIZE_64 + imageBase = mem.Read64Unchecked(offset + 48); + numberOfRvaAndSizes = mem.Read32Unchecked(offset + 132); + offset += 136; + + Kernel.Waypoint(603); + // Verify that we have a valid NT header + DebugStub.WriteLine("size of optional header ={0}", __arglist(sizeOfOptionalHeader)); + if (signature != IMAGE_NT_SIGNATURE || + sizeOfOptionalHeader != 240 || + magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC || + numberOfRvaAndSizes != 16) { + + throw new BadImageFormatException("Invalid PE header"); + } +#else + imageBase = mem.Read32Unchecked(offset + 52); numberOfRvaAndSizes = mem.Read32Unchecked(offset + 116); offset += 120; @@ -167,14 +183,19 @@ namespace Microsoft.Singularity.Loader if (signature != IMAGE_NT_SIGNATURE || sizeOfOptionalHeader != 224 || magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC || - numberOfRvaAndSizes != 16) { + numberOfRvaAndSizes != 16) + { throw new BadImageFormatException("Invalid PE header"); } +#endif + + VirtualAddress = mem.VirtualAddress; Kernel.Waypoint(693); directory = new DirectoryEntry[numberOfRvaAndSizes]; + for (uint i = 0; i < numberOfRvaAndSizes; i++) { directory[i] = new DirectoryEntry(mem, ref offset); } @@ -236,16 +257,27 @@ namespace Microsoft.Singularity.Loader ////////////////////////////////////////////////////// Static Methods. // - public static void Initialize() + public static unsafe void Initialize() { - IoMemory pages = IoMemory.MapPhysicalMemory(BootInfo.KERNEL_BASE, 4096, true, false); + Platform p = Platform.ThePlatform; + IoMemory pages = IoMemory.MapPhysicalMemory((UIntPtr) p.KernelDllFirstPage, + 4096, true, false); kernel = new PEImage(pages); - pages = IoMemory.MapPhysicalMemory(BootInfo.KERNEL_BASE, kernel.loadedSize, true, false); + pages = IoMemory.MapPhysicalMemory((UIntPtr) p.KernelDllBase, + kernel.loadedSize, true, false); + kernelExports = kernel.GetExportTable(pages); + DebugStub.WriteLine("PEImage.Initialize: Notify KD of kernel load."); DebugStub.LoadedBinary(pages.VirtualAddress, kernel.sizeOfImage, +#if ISA_ARM + "kernel.arm", +#elif ISA_IX64 + "kernel.x64", +#elif ISA_IX86 "kernel.x86", +#endif kernel.checkSum, kernel.timeDateStamp, false); @@ -261,15 +293,14 @@ namespace Microsoft.Singularity.Loader #endif } - // haryadi public static void InitializeMpAbi() { // Note: When mapping the abi stub (MpSyscalls.x86), we must // map it with writable flag! as we want to resolve the stub's // imports - IoMemory mpPages = IoMemory.MapPhysicalMemory(BootInfo.MP_ABI_BASE, 4096, true, true); + IoMemory mpPages = IoMemory.MapPhysicalMemory(Platform.MP_ABI_BASE, 4096, true, true); mpAbi = new PEImage(mpPages); - mpPages = IoMemory.MapPhysicalMemory(BootInfo.MP_ABI_BASE, mpAbi.loadedSize, true, true); + mpPages = IoMemory.MapPhysicalMemory(Platform.MP_ABI_BASE, mpAbi.loadedSize, true, true); mpAbiExports = mpAbi.GetExportTable(mpPages); // Resolve imports in abi shim dll with the kernel exports @@ -306,6 +337,8 @@ namespace Microsoft.Singularity.Loader UIntPtr entryPoint = UIntPtr.Zero; loadedMemory = null; + DiagnosisService.DeferedUpdateNotification(); + if (null == rawMemory || 0 == rawMemory.Length) { DebugStub.WriteLine("No PXE image to load!"); return null; @@ -328,7 +361,6 @@ namespace Microsoft.Singularity.Loader #if verbose Tracing.Log(Tracing.Debug, " Loaded Size={0:x8}", image.loadedSize); #endif - Kernel.Waypoint(581); if (0 == image.loadedSize) { throw new BadImageFormatException("Invalid PE, no content"); @@ -339,7 +371,8 @@ namespace Microsoft.Singularity.Loader loadedMemory = IoMemory.AllocateFixed( (UIntPtr)image.loadedSize, process, PageType.System); - } else { + } + else { // Situate the process image in the user range loadedMemory = IoMemory.AllocateUserFixed( (UIntPtr)image.loadedSize, @@ -347,6 +380,8 @@ namespace Microsoft.Singularity.Loader } Kernel.Waypoint(582); + + #if verbose Tracing.Log(Tracing.Debug, " loaded at {0:x8}, Length ={1:x8}", (uint)loadedMemory.VirtualAddress, @@ -358,10 +393,10 @@ namespace Microsoft.Singularity.Loader #if verbose image.DumpLimitedToStream(); #endif + Kernel.Waypoint(583); // load sections into memory where they belong. - for (int i = 0; i < image.numberOfSections; i++) - { + for (int i = 0; i < image.numberOfSections; i++) { if (image.sections[i].IsDiscardable || image.sections[i].sizeOfRawData == 0) { continue; @@ -377,6 +412,23 @@ namespace Microsoft.Singularity.Loader sourceOffset, sourceOffset + rawSize, targetOffset, targetOffset + rawSize); #endif + + if (image.sections[i].virtualSize > image.sections[i].sizeOfRawData) { + + // The memory allocated for the new image is not zeroed, therefore + // we need to clear the remaining region of a section that is not getting + // copied from the source. NOTE BSS sections rely on this to be zeroed + // This is fixing some random bugs with uninitialized variables in unmanaged code + + unsafe { + + byte * dest = (byte *) loadedMemory.VirtualAddress + targetOffset + image.sections[i].sizeOfRawData; + uint length = image.sections[i].virtualSize - image.sections[i].sizeOfRawData; + + Buffer.ZeroMemory(dest, length); + } + } + IoMemory.Copy(rawMemory, sourceOffset, loadedMemory, targetOffset, (int)rawSize); } Kernel.Waypoint(584); @@ -385,7 +437,8 @@ namespace Microsoft.Singularity.Loader // Handle Relocations // int relocationTableOffset = (int)image.GetRelocationsRaw(); - uint diff = (uint)loadedMemory.VirtualAddress - (uint) image.imageBase; + UIntPtr diff = (UIntPtr) loadedMemory.VirtualAddress - (UIntPtr) image.imageBase; + image.VirtualAddress = loadedMemory.VirtualAddress; #if verbose Tracing.Log(Tracing.Debug, " Base loaded={0:x8}, relocated ={1:x8} diff={2:x8}", @@ -394,9 +447,11 @@ namespace Microsoft.Singularity.Loader Tracing.Log(Tracing.Debug, " relocationTableOffset ={0:x8} ", relocationTableOffset); #endif - Relocations.FixupBlocks(rawMemory, (int)relocationTableOffset, - loadedMemory, diff); - // We should probably zero the relocation table. + if (relocationTableOffset > 0) { + Relocations.FixupBlocks(rawMemory, (int)relocationTableOffset, + loadedMemory, diff); + // TODO: We should probably zero the relocation table. + } Kernel.Waypoint(585); @@ -413,7 +468,7 @@ namespace Microsoft.Singularity.Loader #endif //it.DumpIAT("Import directory"); - // haryadi -- if this is loading for remote processor + // if this is loading for remote processor // then solve the imports with abi stub if (isForMp) { it.ResolveImports(mpAbiExports); @@ -437,9 +492,10 @@ namespace Microsoft.Singularity.Loader if (abiStubs != null) { it.ResolveImports(abiStubs); - } else { - it.ResolveImports(kernelExports); } + else { + it.ResolveImports(kernelExports); + } } #endif @@ -464,15 +520,15 @@ namespace Microsoft.Singularity.Loader } finally { if (entryPoint == UIntPtr.Zero && loadedMemory != null) { - // Need to dispose of target Range. + // TODO: Need to dispose of target Range. loadedMemory = null; } } return image; } - // haryadi -- HelloMpLoad skips several procedures to be able to - // load HelloMp.x86 properly + // HelloMpLoad skips several procedures to be able to + // load HelloMp.x86 properly public static PEImage HelloMpLoad(Process process, IoMemory rawMemory, out IoMemory loadedMemory) { @@ -507,8 +563,7 @@ namespace Microsoft.Singularity.Loader (int)image.sizeOfHeaders); Kernel.Waypoint(583); // load sections into memory where they belong. - for (int i = 0; i < image.numberOfSections; i++) - { + for (int i = 0; i < image.numberOfSections; i++) { if (image.sections[i].IsDiscardable || image.sections[i].sizeOfRawData == 0) { continue; @@ -523,14 +578,14 @@ namespace Microsoft.Singularity.Loader Kernel.Waypoint(584); int relocationTableOffset = (int)image.GetRelocationsRaw(); - uint diff = (uint)loadedMemory.VirtualAddress - - (uint) image.imageBase; + UIntPtr diff = (UIntPtr) loadedMemory.VirtualAddress - + (UIntPtr) image.imageBase; entryPoint = (UIntPtr)loadedMemory.VirtualAddress + image.addressOfEntryPoint; } finally { if (entryPoint == UIntPtr.Zero && loadedMemory != null) { - // Need to dispose of target Range. + // TODO: Need to dispose of target Range. loadedMemory = null; } } @@ -546,7 +601,7 @@ namespace Microsoft.Singularity.Loader [AccessedByRuntime("output to header : defined in PEImage.cpp")] [OutsideGCDomain] - [StackBound(64)] + [StackBound(256)] [NoInline] [NoStackLinkCheck] [NoStackOverflowCheck] diff --git a/base/Kernel/Singularity/Loader/Relocations.cs b/base/Kernel/Singularity/Loader/Relocations.cs index 4f0f1ad..f296739 100644 --- a/base/Kernel/Singularity/Loader/Relocations.cs +++ b/base/Kernel/Singularity/Loader/Relocations.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Simple PE Loader for Singularity @@ -16,7 +14,6 @@ // #define verbose -// haryadi //#define SINGULARITY_ASMP namespace Microsoft.Singularity.Loader @@ -38,14 +35,14 @@ namespace Microsoft.Singularity.Loader private const ushort IMAGE_REL_BASED_LOW = 2; private const ushort IMAGE_REL_BASED_HIGHLOW = 3; private const ushort IMAGE_REL_BASED_HIGHADJ = 4; + private const ushort IMAGE_REL_BASED_DIR64 = 10; internal static void FixupBlocks(IoMemory relocationMemory, int relocOffset, - IoMemory codeMemory, uint diff) + IoMemory codeMemory, UIntPtr diff) { uint va = relocationMemory.Read32Unchecked(relocOffset); uint size = relocationMemory.Read32Unchecked(relocOffset+4); - while (0 != va) - { + while (0 != va) { #if verbose DebugStub.WriteLine(" FixUpBlocks: addr={0:x8}, size={1:x8}, roffset={2:x8} ", __arglist(va, size,relocOffset)); @@ -67,7 +64,7 @@ namespace Microsoft.Singularity.Loader } private static void FixupBlock(IoMemory relocationMemory, int relocOffset, - IoMemory codeMemory, uint diff, uint va, int size) + IoMemory codeMemory, UIntPtr diff, uint va, int size) { ushort fixupEntry; @@ -79,16 +76,14 @@ namespace Microsoft.Singularity.Loader #endif int currentSize = size - sizeofBaseRelocation; relocOffset += sizeofBaseRelocation; //skip block header - for (int i = 0; i < numberOfEntries; i++) - { + for (int i = 0; i < numberOfEntries; i++) { fixupEntry = relocationMemory.Read16(relocOffset); relocOffset += 2; currentSize -= 2; ushort type = (ushort) (fixupEntry >>12); //clear out the upper bits uint rVA = (uint)(fixupEntry & 0xfff)+ va; - if (IMAGE_REL_BASED_ABSOLUTE == type) - { + if (IMAGE_REL_BASED_ABSOLUTE == type) { //DebugStub.WriteLine(" type={0:x4} rVA={1:x8}", //__arglist(type, rVA)); // hit an alignment type. there should be no more entries @@ -96,18 +91,31 @@ namespace Microsoft.Singularity.Loader //__arglist(numberOfEntries, i, currentSize)); break; } - else if (IMAGE_REL_BASED_HIGHLOW == type) - { + else if (IMAGE_REL_BASED_HIGHLOW == type) { // get 32-bit word pointed by rVA // mem object representing loaded image is 0 based rVA is sufficient uint fixupItem = codeMemory.Read32Unchecked((int)rVA); - uint newValue = unchecked(diff + (uint) fixupItem); + uint newValue = unchecked((uint) diff + (uint) fixupItem); #if verbose DebugStub.WriteLine(" Fixup: rVA={0:x8} value={1:x8} diff={2:x8}, newValue={3:x8}", __arglist(rVA, fixupItem, diff, newValue)); #endif codeMemory.Write32Unchecked((int) rVA, newValue); } + else if (IMAGE_REL_BASED_DIR64 == type) { + ulong OldValue; + ulong NewValue; + + OldValue = codeMemory.Read64((int) rVA); + + NewValue = unchecked(OldValue + (ulong) diff); + + codeMemory.Write64((int) rVA, NewValue); + } + else { + DebugStub.WriteLine("PE Bad fixup: %x", __arglist(type)); + DebugStub.Break(); + } } //DebugStub.WriteLine(); } diff --git a/base/Kernel/Singularity/Loader/SectionHeader.cs b/base/Kernel/Singularity/Loader/SectionHeader.cs index 24c88e2..5b9bad1 100644 --- a/base/Kernel/Singularity/Loader/SectionHeader.cs +++ b/base/Kernel/Singularity/Loader/SectionHeader.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Simple PE Loader for Singularity diff --git a/base/Kernel/Singularity/Memory/AddressSpace.cs b/base/Kernel/Singularity/Memory/AddressSpace.cs index 37687f7..6196cdc 100644 --- a/base/Kernel/Singularity/Memory/AddressSpace.cs +++ b/base/Kernel/Singularity/Memory/AddressSpace.cs @@ -54,7 +54,8 @@ namespace Microsoft.Singularity.Memory { if (o is AddressSpace) { return this == (AddressSpace)o; - } else { + } + else { return false; } } diff --git a/base/Kernel/Singularity/Memory/FlatPages.cs b/base/Kernel/Singularity/Memory/FlatPages.cs index c88e5e0..55a92ee 100644 --- a/base/Kernel/Singularity/Memory/FlatPages.cs +++ b/base/Kernel/Singularity/Memory/FlatPages.cs @@ -9,8 +9,6 @@ // Note: // -#if !PAGING - //#define TEST //#define VERBOSE //#define MP_VERBOSE @@ -28,8 +26,9 @@ using System.Threading; using System.GCs; using Microsoft.Singularity; -using Microsoft.Singularity.Hal; // for IHal - +using Microsoft.Singularity.Hal; +using Microsoft.Bartok.Runtime; +using Microsoft.Singularity.Eventing; namespace Microsoft.Singularity.Memory { @@ -42,13 +41,13 @@ namespace Microsoft.Singularity.Memory //private const uint PageMask = MemoryManager.PageSize - 1; private const uint SmallSize = MemoryManager.PageSize; - private static UIntPtr addressLimit; - private static UIntPtr pageCount; - private static ulong allocatedBytes; - private static ulong allocatedCount; - private static ulong freedBytes; - private static ulong freedCount; + private static UIntPtr lowerLimit; + private static UIntPtr upperLimit; + private static UIntPtr pageBase; // Lowest page. + private static UIntPtr pageCount; // Number of pages. private static SpinLock pageLock; + private static UIntPtr baseAddress; + private static ulong size; private static unsafe uint *pageTable; @@ -61,1253 +60,155 @@ namespace Microsoft.Singularity.Memory private static unsafe FreeNode *freeList; private static unsafe FreeNode *saveList; - - // This is a representation of MemoryAffinity. IMPORTANT: The - // rule of the thumb is, always check "isValid" flag before - // using any of the member variables. The reason is not all - // subMemoryMap can be created from the initial freeList. The - // caveat is some top memory addresses have been allocated at - // boot process (e.g. from experiences almost 8 MB of memory - // has been taken). SRAT table usually gives separate entry - // for the first 640 KB region. Since the first 8 MB has gone - // from the free list, we could not create the first - // SubMemoryMap that represents the 640 KB region (hence - // isValid is false for this SubMemoryMap). - private struct SubMemoryMap - { - public int memoryId; - public UIntPtr baseAddr; - public UIntPtr endAddr; - public UIntPtr length; - public uint domain; - public bool isValid; - } - - // This is a per-processor memory map/address space. Each - // processor will have each own free list. Future memory - // allocation for MP must consult this structure, the free - // list in particular. Note: Be careful that, the memory area - // from baseAddr to endAddr does not necessary belong to this - // processor. A processor is not guaranteed to have contiguous - // memory addresses in NUMA node. The "length" field - // specifies how big of memory this processor has. Other - // Notes: - // . Maybe in the future we need to create an array of - // sub memory maps within this processorMemoryMap - // . Code for accounting has not been written - private struct ProcessorMemoryMap - { - public int processorId; - public uint domain; - public UIntPtr baseAddr; - public UIntPtr endAddr; - public UIntPtr length; - public FreeNode procFreeListTail; - public FreeNode procSaveListTail; - public unsafe FreeNode *procFreeList; - public unsafe FreeNode *procSaveList; - public bool isInitialized; - } - - // This is a domain mapping for dividing memory across - // processors evenly. Using this domain, we ensure that each - // processor gets memory from the domain where it belongs. If - // SRAT table is not at available, we just create 1 domain. - // Notes: - // . Currently, the code does not support a domain that does not - // have any memory. In other words, currently, we do not allow - // "borrowing" memory from other domains. If this is the case, - // an error will generated and a following DebugStub.Break - private struct DomainMap - { - public uint domain; - public ProcessorMemoryMap [] processors; - public SubMemoryMap [] subMemories; - public UIntPtr totalMemSize; - public FreeNode domFreeListTail; - public FreeNode domSaveListTail; - public unsafe FreeNode *domFreeList; - public unsafe FreeNode *domSaveList; - public bool isSubMemConnected; - } - - private static SubMemoryMap [] subMemoryMap; - private static ProcessorMemoryMap [] processorMemoryMap; - private static DomainMap [] domainMap; - private static bool isProcessorMemoryInitialized; - - - ////////////////////////////////////////////////////////////////// // - // haryadi: MP FlatPages routines start here. + // This represents the real time count of available memory + // + private static UIntPtr availableMemory; - private static unsafe void PrintSubMemoryMap() + [NoHeapAllocation] + [Inline] + private unsafe static bool RangeWithinLimits(ulong addr, ulong size, UIntPtr upperLimit, UIntPtr lowerLimit, + out UIntPtr baseAddress, out ulong newSize) { - DebugStub.WriteLine("\n\n SUB MEMORY MAP"); - DebugStub.WriteLine(" --------------------------------------------"); - for (int i = 0; i < subMemoryMap.Length; i++) { - DebugStub.WriteLine(" [m{0}] b.{1:x8} e.{2:x8} l.{3:x8} d.{4} i.{5}", - __arglist(subMemoryMap[i].memoryId, - subMemoryMap[i].baseAddr, - subMemoryMap[i].endAddr, - subMemoryMap[i].length, - subMemoryMap[i].domain, - subMemoryMap[i].isValid)); + + baseAddress = addr; + newSize = size; + + if(addr + size < lowerLimit) { + return false; } - DebugStub.WriteLine(); - } - - private static unsafe void PrintProcessorMemoryMap() - { - DebugStub.WriteLine("\n\n PROCESSOR MEMORY MAP"); - DebugStub.WriteLine(" --------------------------------------------"); - - for (int i = 0; i < processorMemoryMap.Length; i++) { - DebugStub.WriteLine(" [p{0}] b.{1:x8} e.{2:x8} l.{3:x8} d.{4} f.{5:x8} s.{6:x8} i.{7:x8} ", - __arglist(processorMemoryMap[i].processorId, - processorMemoryMap[i].baseAddr, - processorMemoryMap[i].endAddr, - processorMemoryMap[i].length, - processorMemoryMap[i].domain, - (UIntPtr)processorMemoryMap[i].procFreeList, - (UIntPtr)processorMemoryMap[i].procSaveList, - processorMemoryMap[i].isInitialized)); + if(addr > upperLimit) { + return false; } - DebugStub.WriteLine(); - } - - private static unsafe void PrintDomainMap() - { - DebugStub.WriteLine("\n\n DOMAIN MAP"); - DebugStub.WriteLine(" --------------------------------------------"); - for (int i = 0; i < domainMap.Length; i++) { - DebugStub.Print(" [d{0}] ts.{1:x8} dl.{2:x8}", - __arglist(i, domainMap[i].totalMemSize, (UIntPtr)domainMap[i].domFreeList)); - - for (int j = 0; j < domainMap[i].processors.Length; j++) { - DebugStub.Print(" (p{0},{1:x8}) ", __arglist(domainMap[i].processors[j].processorId, - domainMap[i].processors[j].baseAddr)); - } - for (int j = 0; j < domainMap[i].subMemories.Length; j++) { - DebugStub.Print(" (m{0},{1:x8}) ", __arglist(domainMap[i].subMemories[j].memoryId, - domainMap[i].subMemories[j].baseAddr)); - } - DebugStub.WriteLine(); + if((addr > lowerLimit) && ((addr + size) < upperLimit)) { + return true; } - DebugStub.WriteLine(); - } - - private static unsafe void PrintAllMaps() - { - DebugStub.WriteLine("\n\n **** PRINT ALL MAPS ****"); - PrintSubMemoryMap(); - PrintProcessorMemoryMap(); - PrintDomainMap(); - DebugStub.WriteLine(); - } - - // Create manually simple SubMemoryMap - private static unsafe void PrepareSubMemoryMapSimpleTest() - { - int memoryCount = 5; - subMemoryMap = new SubMemoryMap [memoryCount]; - fixed (SubMemoryMap *s = &(subMemoryMap[0])) { - s->memoryId = 0; - s->baseAddr = 0x0; - s->endAddr = 0x000a0000; // 640 KB - s->length = s->endAddr - s->baseAddr; - s->domain = 0; - s->isValid = false; + //trim the limits to fit into the valid address range + if(addr < lowerLimit) { + baseAddress = lowerLimit; + newSize = newSize - ((ulong)lowerLimit - addr); } - fixed (SubMemoryMap *s = &(subMemoryMap[1])) { - s->memoryId = 1; - s->baseAddr = 0x01000000; // 16 MB - s->endAddr = 0x20000000; // 512 MB - s->length = s->endAddr - s->baseAddr; - s->domain = 0; - s->isValid = false; + if((addr + size) > upperLimit) { + newSize = newSize - (((ulong)baseAddress + size) - (ulong)upperLimit); } - fixed (SubMemoryMap *s = &(subMemoryMap[2])) { - s->memoryId = 2; - s->baseAddr = 0x20000000; // 512 MB - s->endAddr = 0x40000000; // 1 GB - s->length = s->endAddr - s->baseAddr; - s->domain = 1; - s->isValid = false; - } - fixed (SubMemoryMap *s = &(subMemoryMap[3])) { - s->memoryId = 3; - s->baseAddr = 0x40000000; // 1 GB - s->endAddr = 0x60000000; // 1.5 GB - s->length = s->endAddr - s->baseAddr; - s->domain = 0; - s->isValid = false; - } - fixed (SubMemoryMap *s = &(subMemoryMap[4])) { - s->memoryId = 4; - s->baseAddr = 0x60000000; // 1.5 GB - s->endAddr = 0x80000000; // 2 GB - s->length = s->endAddr - s->baseAddr; - s->domain = 1; - s->isValid = false; - } - } - - // Create manually complex SubMemoryMap. - // Current complexTest: 3 domains, 12 processors, 9 memories - // 1 G: 0x4000_0000 - // 2 G: 0x8000_0000 - private static unsafe void PrepareSubMemoryMapComplexTest() - { - int memoryCount = 9; - subMemoryMap = new SubMemoryMap [memoryCount]; - uint domain = 0; - UIntPtr cur = 0; - UIntPtr length = 0x04000000; - - for (int i = 0; i < memoryCount; i++) { - fixed (SubMemoryMap *s = &(subMemoryMap[i])) { - s->memoryId = i; - s->baseAddr = cur; - s->endAddr = cur+length; - s->length = s->endAddr - s->baseAddr; - s->domain = domain; - s->isValid = false; - - // the last one eat up everything - /* - if (i == memoryCount - 1) { - s->baseAddr = cur; - s->endAddr = 0x80000000; - s->length = s->endAddr - s->baseAddr; - }*/ - - } - cur += length; - // flip domain, so that we have non-contiguous memory - if (domain == 0) { - domain = 1; - } - else if (domain == 1){ - domain = 2; - } - else { - domain = 0; - } - } - } - - private static unsafe void PrepareSubMemoryMap() - { - // get memory banks - IHalMemory.MemoryAffinity[] memories = - Processor.GetMemoryAffinity(); - int memoryCount = memories.Length; - subMemoryMap = new SubMemoryMap [memoryCount]; - for (int i = 0; i < subMemoryMap.Length; i++) { - subMemoryMap[i].memoryId = i; - subMemoryMap[i].baseAddr = memories[i].baseAddress; - subMemoryMap[i].endAddr = memories[i].endAddress; - subMemoryMap[i].length = memories[i].memorySize; - subMemoryMap[i].domain = memories[i].domain; - subMemoryMap[i].isValid = false; - } - } - - // If we don't have SRAT table, then we treat the whole memory - // as 1 subMemoryMap. Since, we don't break the memory, so - // isValid is set to true - private static unsafe void PrepareSubMemoryMapNoAffinity() - { - subMemoryMap = new SubMemoryMap[1]; - subMemoryMap[0].memoryId = 0; - subMemoryMap[0].baseAddr = 0x0; - subMemoryMap[0].endAddr = GetMaxMemory(); - subMemoryMap[0].length = GetMaxMemory(); - subMemoryMap[0].domain = 0; - subMemoryMap[0].isValid = true; - } - - // Based on the SRAT table, we try to break original free list - // into multiple free nodes. The rule is we break at the start - // address of every sub memory map - private static unsafe void CreateSubMemoryMap() - { - for (int mNum = 0; mNum < subMemoryMap.Length; mNum++) { - subMemoryMap[mNum].isValid = - CreateSubMemory(mNum, - subMemoryMap[mNum].baseAddr, - subMemoryMap[mNum].endAddr); - if (!subMemoryMap[mNum].isValid) { -#if MP_VERBOSE - DebugStub.WriteLine - (" WARNING: SubMap-{0} [{1:x8}..{2:x8}] cannot be initialized", - __arglist(mNum, - subMemoryMap[mNum].baseAddr, - subMemoryMap[mNum].endAddr)); -#endif - } - } - } - - // First, given the base address, we find the free node - // (curNode) that will be cut by the base address. In other - // words, the curNode is the node that will be broken to 2 - // parts. If we could not find it, then curNode is null. - // Second, we need to check if the memory area from - // memBaseAddr to memEndAddr is intersecting with any free - // node. (See more detailed comment in IsPartialIntersect() - // function). IsPartialIntersect will give the new breakAddr. - // The corresponding subMemoryMap's base address also must be - // updated with the new breakAddr. If the two conditions - // above fail. Then this subMemory cannot be initialized. - private static unsafe bool CreateSubMemory(int memoryNumber, - UIntPtr memBaseAddr, - UIntPtr memEndAddr) - { - // always break at the memBaseAddr - UIntPtr breakAddr = memBaseAddr; - -#if MP_VERBOSE - DebugStub.WriteLine("\n SubMap[{0}]: Creating at {1:x8}", - __arglist(memoryNumber,breakAddr)); -#endif - FreeNode* curNode = FreeNode.GetFreeNodeAtBreakAddr(freeList, breakAddr); - if (curNode == null) { - // now check just in case the bottom part of this - // subMem is intersect with one of the free list node - breakAddr = FreeNode.IsPartialIntersect(freeList, memBaseAddr, memEndAddr); - curNode = FreeNode.GetFreeNodeAtBreakAddr(freeList, breakAddr); - - if (curNode == null) { - return false; - } - - // update base address - if (breakAddr != 0) { - subMemoryMap[memoryNumber].baseAddr = breakAddr; - } - } -#if MP_VERBOSE - DebugStub.WriteLine(" SubMap[{0}]: braking list at {1.x8}", - __arglist(memoryNumber, breakAddr)); -#endif - FreeNode.BreakListAt(freeList, curNode, breakAddr); return true; } - private static unsafe void PrepareProcessorMemoryMapNoAffinity() - { - int processorCount = Processor.GetProcessorCount(); - processorMemoryMap = new ProcessorMemoryMap [processorCount]; - PrepareProcessorMemoryMapCommonFields(); - } - - private static unsafe void PrepareProcessorMemoryMap() - { - IHalMemory.ProcessorAffinity [] halProcessors = - Processor.GetProcessorAffinity(); - int processorCount = halProcessors.Length; - processorMemoryMap = new ProcessorMemoryMap [processorCount]; - PrepareProcessorMemoryMapCommonFields(); - - // update domain - for (int i = 0; i < processorCount; i++) { - processorMemoryMap[i].domain = halProcessors[i].domain; - } - } - - private static unsafe void PrepareProcessorMemoryMapCommonFields() - { - for (int i=0; i < processorMemoryMap.Length; i++) { - processorMemoryMap[i].domain = 0; - processorMemoryMap[i].processorId = i; - processorMemoryMap[i].baseAddr = 0x0; - processorMemoryMap[i].endAddr = 0x0; - processorMemoryMap[i].length = 0x0; - - // Initialize the free and save lists. - fixed (FreeNode *tail = &(processorMemoryMap[i].procFreeListTail)) { - processorMemoryMap[i].procFreeList = tail; - FreeNode.Init(processorMemoryMap[i].procFreeList, false); - } - fixed (FreeNode *tail = &(processorMemoryMap[i].procSaveListTail)) { - processorMemoryMap[i].procSaveList = tail; - FreeNode.Init(processorMemoryMap[i].procSaveList, true); - } - } - } - - private static unsafe void PrepareProcessorMemoryMapComplexTest() - { - int processorCount = 12; - uint domain = 0; - processorMemoryMap = new ProcessorMemoryMap [processorCount]; - for (int i=0; i < processorCount; i++) { - if (i == 4) { - domain++; - } - if (i == 8) { - domain++; - } - processorMemoryMap[i].domain = domain; - processorMemoryMap[i].processorId = i; - processorMemoryMap[i].baseAddr = 0x0; - processorMemoryMap[i].endAddr = 0x0; - processorMemoryMap[i].length = 0x0; - - // Initialize the free and save lists. - fixed (FreeNode *tail = &(processorMemoryMap[i].procFreeListTail)) { - processorMemoryMap[i].procFreeList = tail; - FreeNode.Init(processorMemoryMap[i].procFreeList, false); - } - fixed (FreeNode *tail = &(processorMemoryMap[i].procSaveListTail)) { - processorMemoryMap[i].procSaveList = tail; - FreeNode.Init(processorMemoryMap[i].procSaveList, true); - } - } - } - - private static unsafe void PrepareDomainMapCommonFields() - { - for (int i = 0; i < domainMap.Length; i ++) { - domainMap[i].domain = (uint)i; - domainMap[i].isSubMemConnected = false; - // Initialize the free and save lists. - fixed (FreeNode *tail = &(domainMap[i].domFreeListTail)) { - domainMap[i].domFreeList = tail; - FreeNode.Init(domainMap[i].domFreeList, false); - } - fixed (FreeNode *tail = &(domainMap[i].domSaveListTail)) { - domainMap[i].domSaveList = tail; - FreeNode.Init(domainMap[i].domSaveList, true); - } - } - } - - // Just attach processors and memories if we don't have SRAT table - private static unsafe void PrepareDomainMapNoAffinity() - { - domainMap = new DomainMap [1]; - PrepareDomainMapCommonFields(); - domainMap[0].processors = processorMemoryMap; - domainMap[0].subMemories = subMemoryMap; - domainMap[0].totalMemSize = 0; - for (int i = 0; i < subMemoryMap.Length; i++) { - domainMap[0].totalMemSize += subMemoryMap[i].length; - } - } - - // Per Domain: Traverse the processor and memory maps, and put - // then in domainMap according to their domain numbers - private static unsafe void PrepareDomainMap(int domainCount) - { - int count; - domainMap = new DomainMap [domainCount]; - PrepareDomainMapCommonFields(); - - for (int i = 0; i < domainCount; i ++) { - domainMap[i].totalMemSize = 0; - // processor, 1st pass, count - count = 0; - for (int j = 0; j < processorMemoryMap.Length; j++) { - if (processorMemoryMap[j].domain == domainMap[i].domain) { - count++; - } - } - - domainMap[i].processors = new ProcessorMemoryMap[count]; - - // processor, 2nd pass, count - count = 0; - for (int j = 0; j < processorMemoryMap.Length; j++) { - if (processorMemoryMap[j].domain == domainMap[i].domain) { - domainMap[i].processors[count] = processorMemoryMap[j]; - count++; - } - } - - // sub, 1st pass, count - count = 0; - for (int j = 0; j < subMemoryMap.Length; j++) { - if (subMemoryMap[j].domain == domainMap[i].domain) { - count++; - } - } - - domainMap[i].subMemories = new SubMemoryMap[count]; - - // sub, 2nd pass, count - count = 0; - for (int j = 0; j < subMemoryMap.Length; j++) { - if (subMemoryMap[j].domain == domainMap[i].domain) { - domainMap[i].subMemories[count] = subMemoryMap[j]; - domainMap[i].totalMemSize += subMemoryMap[j].length; - count++; - } - } - } - } - - // Basically, this function grab the original free list and - // and attach it to the domain tail free list. After this - // function is called, we should no longer use the original - // free list - private static unsafe void ConnectSubMemoriesPerDomainNoAffinity() - { - FreeNode *dom = domainMap[0].domFreeList; - FreeNode *first = freeList->next; - FreeNode *last = freeList->prev; - - dom->next = first; - dom->prev = last; - first->prev = dom; - first->next = dom; - - domainMap[0].isSubMemConnected = true;; - -#if MP_VERBOSE - DebugStub.WriteLine("\n\n Connect memory no affinity: "); - DebugStub.WriteLine(" dl.{0:x8} dn.{1:x8} dp.{2:x8}", - __arglist((UIntPtr)dom, - (UIntPtr)dom->next, - (UIntPtr)dom->prev)); - DebugStub.WriteLine(" ff.{0:x8} fn.{1:x8} fp.{2:x8}", - __arglist((UIntPtr)first, - (UIntPtr)first->next, - (UIntPtr)first->prev)); - DebugStub.WriteLine(" ll.{0:x8} ln.{1:x8} lp.{2:x8}", - __arglist((UIntPtr)last, - (UIntPtr)last->next, - (UIntPtr)last->prev)); -#endif - } - - private static unsafe void ConnectSubMemoriesPerDomain() - { - for (int i = 0; i < domainMap.Length; i++) { -#if MP_VERBOSE - DebugStub.WriteLine("\n Domain [{0}]:", __arglist(i)); -#endif - ConnectSubMemoriesInDomain(domainMap[i]); - domainMap[i].isSubMemConnected = true; - } - } - - // At this point, the original free list should have been - // partitioned according to the subMemoryMap. Now, we attach - // the sub memory maps to their corresponding domain free - // list. After this function is called, we should no longer - // use the original free list - private static unsafe void ConnectSubMemoriesInDomain(DomainMap dMap) - { - if (dMap.subMemories.Length == 0) { - DebugStub.WriteLine("\n\n **** ERROR, one of the domain does not have memory ****"); - DebugStub.WriteLine("\n\n **** this is not currently supported ****"); - DebugStub.Break(); - } - -#if MP_VERBOSE - DebugStub.WriteLine("\n Connection SubMemories in Domain {0}:", - __arglist(dMap.domain)); - DebugStub.WriteLine(" -----------------------------------------------"); -#endif - - FreeNode *domTailNode = dMap.domFreeList; - FreeNode *curNode; - FreeNode *prevNode = null; - int validMem = 0; - int validMemCount = 0; - -#if MP_VERBOSE - DebugStub.WriteLine(" Checking valid memory map:"); -#endif - for (int i = 0; i < dMap.subMemories.Length; i++) { - if (dMap.subMemories[i].isValid) { - -#if MP_VERBOSE - DebugStub.WriteLine(" Valid: sm{0} smb.{1:x8} sme.{2:x8}", - __arglist(i, - dMap.subMemories[i].baseAddr, - dMap.subMemories[i].endAddr)); -#endif - validMemCount++; - } - } - -#if MP_VERBOSE - DebugStub.WriteLine("\n Connecting sub memories:"); -#endif - for (int i = 0; i < dMap.subMemories.Length; i++) { - - // if not valid continue - if (!dMap.subMemories[i].isValid) { - continue; - } - - // this is wrong - curNode = (FreeNode*) dMap.subMemories[i].baseAddr; - -#if MP_VERBOSE - DebugStub.WriteLine("\n [{0}]. curNode is at [base.{1:x8}]", - __arglist(validMem, (UIntPtr)curNode)); -#endif - - // if this is the first valid memory, update the head - // of the linked list - if (validMem == 0) { - - domTailNode->next = curNode; - curNode->prev = domTailNode; - -#if MP_VERBOSE - DebugStub.WriteLine(" [{0}]. [d.{1:x8}] dn.{2:x8} = c.{3:x8}", - __arglist(validMem, - (UIntPtr)domTailNode, - (UIntPtr)domTailNode->next, - (UIntPtr)curNode)); - DebugStub.WriteLine(" [{0}]. [c.{1:x8}] cp.{2:x8} = d.{3:x8}", - __arglist(validMem, - (UIntPtr)curNode, - (UIntPtr)curNode->prev, - (UIntPtr)domTailNode)); -#endif - } - - // this is the last valid memory, update the tail of - // the linked list - if (validMem == validMemCount - 1) { - - if (prevNode != null) { - prevNode->next = curNode; - curNode->prev = prevNode; - -#if MP_VERBOSE - DebugStub.WriteLine(" [{0}]. [p.{1:x8}] pn.{2:x8} = c.{3:x8}", - __arglist(validMem, - (UIntPtr)prevNode, - (UIntPtr)prevNode->next, - (UIntPtr)curNode)); - - DebugStub.WriteLine(" [{0}]. [c.{1:x8}] cp.{2:x8} = p.{3:x8}", - __arglist(validMem, - (UIntPtr)curNode, - (UIntPtr)curNode->prev, - (UIntPtr)prevNode)); -#endif - } - - domTailNode->prev = curNode; - curNode->next = domTailNode; - -#if MP_VERBOSE - DebugStub.WriteLine(" [{0}]. [d.{1:x8}] dp.{2:x8} = c.{3:x8}", - __arglist(validMem, - (UIntPtr)domTailNode, - (UIntPtr)domTailNode->prev, - (UIntPtr)curNode)); - DebugStub.WriteLine(" [{0}]. [c.{1:x8}] cn.{2:x8} = d.{3:x8}", - __arglist(validMem, - (UIntPtr)curNode, - (UIntPtr)curNode->next, - (UIntPtr)domTailNode)); -#endif - } - - // else this is the middle - if (validMem > 0 && validMem < validMemCount - 1) { - - prevNode->next = curNode; - curNode->prev = prevNode; - -#if MP_VERBOSE - DebugStub.WriteLine(" [{0}]. [p.{1:x8}] pn.{2:x8} = c.{3:x8}", - __arglist(validMem, - (UIntPtr)prevNode, - (UIntPtr)prevNode->next, - (UIntPtr)curNode)); - - DebugStub.WriteLine(" [{0}]. [c.{1:x8}] cp.{2:x8} = p.{3:x8}", - __arglist(validMem, - (UIntPtr)curNode, - (UIntPtr)curNode->prev, - (UIntPtr)prevNode)); -#endif - - } - prevNode = curNode; - validMem++; - } - } - - // Since, a processor might not have a contiguous memory, this - // function performs the conversion from the relativeAddr to th - // realAddr. For example if a processor has 20 bytes memory at - // mem[0..10] and mem[20..30], a relative address of mem[15] will - // be converted to the real address mem[25] - private static unsafe UIntPtr GetRealBaseAddrInDomainMap(DomainMap dMap, UIntPtr relativeAddr) - { - for (int i = 0; i < dMap.subMemories.Length; i++) { - - // We should not take into account subMemories - // that are not valid. Remember that the first - // subMemory usually can not be created because that - // part of the memory is not in the original free list - if (!dMap.subMemories[i].isValid) { - continue; - } - - if (relativeAddr < dMap.subMemories[i].length) { - return (dMap.subMemories[i].baseAddr + relativeAddr); - } - relativeAddr = relativeAddr - dMap.subMemories[i].length; - } - - DebugStub.WriteLine("\n\n **** ERROR relativeAddr.{0:x8} is to big??, overflow ****", - __arglist(relativeAddr)); - DebugStub.Break(); - return 0; - } - - // Convert relative end addr - private static unsafe UIntPtr GetRealEndAddrInDomainMap (DomainMap dMap, UIntPtr relativeAddr) - { - for (int i = 0; i < dMap.subMemories.Length; i++) { - - if (!dMap.subMemories[i].isValid) { - continue; - } - - if (relativeAddr <= dMap.subMemories[i].length) { - return (dMap.subMemories[i].baseAddr + relativeAddr); - } - relativeAddr = relativeAddr - dMap.subMemories[i].length; - } - DebugStub.WriteLine("\n\n **** ERROR relativeAddr.{0:x8} is to big??, overflow ****", - __arglist(relativeAddr)); - DebugStub.Break(); - return 0; - } - - // This is for rounding. Consider 1 memory, and 3 processors. - // If first two processor get 0.33 of the whole memories, the - // last one gets 0.34. - private static unsafe UIntPtr GetLastAddrInDomainMap (DomainMap dMap) - { - return dMap.subMemories[dMap.subMemories.Length - 1].endAddr; - } - - // For each Domain dMap, we will partitioned the memories in this - // domain across the processors in the same domain. - private static unsafe void CreatePerProcessorMemoryInDomain(DomainMap dMap) - { - int processorCount = dMap.processors.Length; - UIntPtr pageCount = dMap.totalMemSize >> MemoryManager.PageBits; - UIntPtr pagePerProcessor = - (UIntPtr)((ulong)pageCount / (ulong)processorCount); - UIntPtr curPage = 0; - FreeNode *curNode; - UIntPtr breakAddr; - -#if MP_VERBOSE - DebugStub.WriteLine("\n\n Creating Domain [{0}]", - __arglist(dMap.domain)); - DebugStub.WriteLine(" ---------------------------------------"); - DebugStub.WriteLine(" Total MemSize : {0:x8}", - __arglist(dMap.totalMemSize)); - DebugStub.WriteLine(" Page Count : {0}", - __arglist(pageCount)); - DebugStub.WriteLine(" Processor Cnt : {0}", - __arglist(processorCount)); - DebugStub.WriteLine(" Page/Proc : {0}", - __arglist(pagePerProcessor)); - DebugStub.WriteLine(" DomFreeList : {0:x8}", - __arglist((UIntPtr)dMap.domFreeList)); -#endif - - for (int i = 0; i < processorCount; i++) { - -#if MP_VERBOSE - DebugStub.WriteLine("\n\n PROCESSOR-{0}", __arglist(i)); - DebugStub.WriteLine(" -------------------------------"); -#endif - - dMap.processors[i].baseAddr = - GetRealBaseAddrInDomainMap(dMap, - curPage << - MemoryManager.PageBits); - -#if MP_VERBOSE - DebugStub.WriteLine(" GetRealAddr: curPage,{0} --> baseAddr.{1:x8}", - __arglist(curPage, - dMap.processors[i].baseAddr)); -#endif - - // if last processor, take all what is left - if (i == processorCount - 1) { - dMap.processors[i].endAddr = GetLastAddrInDomainMap(dMap); - - // is not necessary contiguous - dMap.processors[i].length = - (pageCount - curPage) << MemoryManager.PageBits; -#if MP_VERBOSE - DebugStub.WriteLine(" LastProcessor in Domain gets all"); -#endif - } - else { - dMap.processors[i].endAddr = - GetRealEndAddrInDomainMap(dMap, - (curPage + pagePerProcessor) - << MemoryManager.PageBits); - - // is not necessary contiguous - dMap.processors[i].length = - (pagePerProcessor << MemoryManager.PageBits); - } - -#if MP_VERBOSE - DebugStub.WriteLine(" GetEndAddr : curPage.{0} --> endAddr.{1:x8}, length.{2:x8}", - __arglist(curPage, - dMap.processors[i].endAddr, - dMap.processors[i].length)); -#endif - - // now, let's break at the start addr - breakAddr = dMap.processors[i].baseAddr; - curNode = FreeNode.GetFreeNodeAtBreakAddr(dMap.domFreeList, breakAddr); - - if (curNode != null) { -#if MP_VERBOSE - DebugStub.WriteLine(" Breaking at StartAddr.{0:x8}", - __arglist(breakAddr)); - DebugStub.WriteLine(" curNode found around StartAddr: node.{0:x8} prev.{1:x8} next.{2:x8}", - __arglist((UIntPtr)curNode, - (UIntPtr)curNode->prev, - (UIntPtr)curNode->next)); -#endif - FreeNode.BreakListAt(dMap.domFreeList, curNode, breakAddr); - } - else { -#if MP_VERBOSE - DebugStub.WriteLine(" Breaking at StartAddr.{0:x8} -- cancelled, can't find freeNode", - __arglist(breakAddr)); -#endif - } - - // don't forget to add current page - curPage += pagePerProcessor; - -#if MP_VERBOSE - DebugStub.WriteLine(" Processor[{0}] initialized at base.{0:x8} end.{1:x8} length.{2:x8}", - __arglist(dMap.processors[i].baseAddr, - dMap.processors[i].endAddr, - dMap.processors[i].length)); -#endif - - } - } - - // Note that this function only performs computation. I.e. - // it calculates the memory ranges that a process will be given. - // It does not steal the free list yet. - private static unsafe void CreatePerProcessorMemory() - { - for (int i = 0; i < domainMap.Length; i++) { - CreatePerProcessorMemoryInDomain(domainMap[i]); - } - } - - // just copy back some fields, because domainMap.processors and - // processorMemoryMap do not point to the same object????? - private static unsafe void CopyDomainProcessorsToProcessors() - { - for (int i = 0; i < processorMemoryMap.Length; i++) { - for (int j = 0; j < domainMap.Length; j++) { - for (int k = 0; k < domainMap[j].processors.Length; k++) { - // same, copy - if (processorMemoryMap[i].processorId == - domainMap[j].processors[k].processorId) { - - processorMemoryMap[i].domain = - domainMap[j].processors[k].domain; - processorMemoryMap[i].baseAddr = - domainMap[j].processors[k].baseAddr; - processorMemoryMap[i].endAddr = - domainMap[j].processors[k].endAddr; - processorMemoryMap[i].length = - domainMap[j].processors[k].length; - } - } - } - } - } - - // Get lowest free node from all domains - private static unsafe FreeNode* GetLowestNodeFromDomains() - { - // how about in 64 bits architecture?? - UIntPtr lowest = (UIntPtr) 0xffffffff; - - FreeNode *tail = null; - FreeNode *first = null; - for (int i = 0; i < domainMap.Length; i++) { - tail = domainMap[i].domFreeList; - first = tail->next; - - // need to check if tail == first, then we have problem: - // there is no free list!! - if (tail == first) { - DebugStub.WriteLine("\n\n****** ERROR ******"); - DebugStub.WriteLine("GetLow: Domain [{0}] has no free list at tail {1:x8}", - __arglist(i,(UIntPtr)tail)); - DebugStub.Break(); - } - - - if ((UIntPtr)first < lowest) { - lowest = (UIntPtr)first; - } - } - return (FreeNode*)lowest; - } - - // Get highest node from all domains - private static unsafe FreeNode* GetHighestNodeFromDomains() - { - UIntPtr highest = (UIntPtr) 0x0; - FreeNode *tail = null; - FreeNode *last = null; - for (int i = 0; i < domainMap.Length; i++) { - tail = domainMap[i].domFreeList; - last = tail->prev; - if (tail == last) { - DebugStub.WriteLine("\n\n****** ERROR ******"); - DebugStub.WriteLine("GetHigh: Domain [{0}] has no free list at tail {1:x8}", - __arglist(i,(UIntPtr)tail)); - DebugStub.Break(); - } - if ((UIntPtr)last > highest) { - highest = (UIntPtr)last; - } - } - return (FreeNode*)highest; - } - - // At this point, each processor should know the baseAddr of - // the memory ranges that it should have. Now, we need to - // partitioned the domain's free lists for the processors in - // the domain. This is a similar operation that we did when - // we partition the original free list to sub-memories. The - // way we break it here, is we will break the free list at - // each processor's baseAddr. After we break the domain's free - // list, we steal the free list and attach it to the - // corresponding processors' free list. - private static unsafe void AttachPerProcessorMemoryToFreeListTail() - { - FreeNode *procTailNode; - FreeNode *firstNode; - LastNode *last; - FreeNode *lastNode; - FreeNode *prevNode = null; - - for (int i = 0; i < processorMemoryMap.Length; i++) { - - procTailNode = processorMemoryMap[i].procFreeList; - - // Special case: The edges of the memory. i.e. - // memories of processor[0] and processor[lastProc] - if (i == 0) { - firstNode = GetLowestNodeFromDomains(); - last = (LastNode*) (processorMemoryMap[i].endAddr - MemoryManager.PageSize); - lastNode = last->node; - } - else if (i == processorMemoryMap.Length - 1) { - firstNode = (FreeNode*) processorMemoryMap[i].baseAddr; - last = null; - lastNode = GetHighestNodeFromDomains(); - } - else { - firstNode = (FreeNode*) processorMemoryMap[i].baseAddr; - last = (LastNode*) (processorMemoryMap[i].endAddr - MemoryManager.PageSize); - lastNode = last->node; - } - - - - // if processor is the lowest -#if MP_VERBOSE - DebugStub.WriteLine(); - DebugStub.WriteLine("\n Attaching Processor[{0}]", __arglist(i)); - DebugStub.WriteLine(" -------------------------------------------"); - - DebugStub.WriteLine(" firstNode = {0:x8}", __arglist((UIntPtr)firstNode)); - DebugStub.WriteLine(" last = {0:x8}", __arglist((UIntPtr)last)); - DebugStub.WriteLine(" lastNode = {0:x8}", __arglist((UIntPtr)lastNode)); - DebugStub.WriteLine(" procTailNode = {0:x8}", __arglist((UIntPtr)procTailNode)); - DebugStub.WriteLine(" procTailNode = {0:x8}", __arglist((UIntPtr)procTailNode)); - - DebugStub.WriteLine("\n Before Attaching: \n"); - - if (last != null) { - DebugStub.WriteLine(" last a.{0:x8} n.{1:x8} ", - __arglist((UIntPtr)last, (UIntPtr)last->node)); - } - DebugStub.WriteLine(" procTail a.{0:x8} p.{1:x8} n.{2:x8} l.{3:x8} ", - __arglist((UIntPtr)procTailNode, (UIntPtr)procTailNode->prev, - (UIntPtr)procTailNode->next, (UIntPtr)procTailNode->last)); - - DebugStub.WriteLine(" firstNode a.{0:x8} p.{1:x8} n.{2:x8} l.{3:x8} ", - __arglist((UIntPtr)firstNode, (UIntPtr)firstNode->prev, - (UIntPtr)firstNode->next, (UIntPtr)firstNode->last)); - - DebugStub.WriteLine(" lastNode a.{0:x8} p.{1:x8} n.{2:x8} l.{3:x8} ", - __arglist((UIntPtr)lastNode, (UIntPtr)lastNode->prev, - (UIntPtr)lastNode->next, (UIntPtr)lastNode->last)); -#endif - - // set heads - procTailNode->next = firstNode; - firstNode->prev = procTailNode; - - // set tails - procTailNode->prev = lastNode; - lastNode->next = procTailNode; - - processorMemoryMap[i].isInitialized = true; - - -#if MP_VERBOSE - DebugStub.WriteLine("\n After Attaching: \n"); - - if (last != null) { - DebugStub.WriteLine(" last a.{0:x8} n.{1:x8} ", - __arglist((UIntPtr)last, (UIntPtr)last->node)); - } - DebugStub.WriteLine(" procTail a.{0:x8} p.{1:x8} n.{2:x8} l.{3:x8} ", - __arglist((UIntPtr)procTailNode, (UIntPtr)procTailNode->prev, - (UIntPtr)procTailNode->next, (UIntPtr)procTailNode->last)); - - DebugStub.WriteLine(" firstNode a.{0:x8} p.{1:x8} n.{2:x8} l.{3:x8} ", - __arglist((UIntPtr)firstNode, (UIntPtr)firstNode->prev, - (UIntPtr)firstNode->next, (UIntPtr)firstNode->last)); - - DebugStub.WriteLine(" lastNode a.{0:x8} p.{1:x8} n.{2:x8} l.{3:x8} ", - __arglist((UIntPtr)lastNode, (UIntPtr)lastNode->prev, - (UIntPtr)lastNode->next, (UIntPtr)lastNode->last)); -#endif - } - } - - - private static unsafe void DebugMpPhase(int phase) - { -#if MP_VERBOSE - DebugStub.WriteLine("\n\n"); - DebugStub.Print("PHASE {0}: ", __arglist(phase)); - switch (phase) { - case 0: - DebugStub.WriteLine("MP FLAT-PAGES START"); - break; - case 1: - FreeNode.PrintFreeList(freeList); - DebugStub.WriteLine("PREPARE SUB MEMORY MAP"); - break; - case 2: - DebugStub.WriteLine("CREATE SUB MEMORY MAP"); - break; - case 3: - PrintSubMemoryMap(); - FreeNode.PrintFreeList(freeList); - DebugStub.WriteLine("PREPARE PROCESSOR MEM MAP"); - break; - case 4: - PrintProcessorMemoryMap(); - DebugStub.WriteLine("PREPARE DOMAIN MAPPING"); - break; - case 5: - PrintDomainMap(); - PrintSubMemoryMap(); - DebugStub.WriteLine("CREATE SUB MEM PER DOMAIN:"); - break; - case 6: - FreeNode.PrintDomainFreeLists(); - DebugStub.WriteLine("CREATE PER PROC MEMORY"); break; - case 7: - FreeNode.PrintDomainFreeLists(); - DebugStub.WriteLine("COPY DOMAIN TO PMAP"); - break; - case 8: - PrintAllMaps(); - DebugStub.WriteLine("ATTACH PROC FREE LIST"); - break; - case 9: - PrintAllMaps(); - FreeNode.PrintProcessorFreeLists(); - DebugStub.WriteLine("MP FLAT-PAGES DONE"); break; - default: DebugStub.WriteLine(); break; - } - DebugStub.WriteLine("*************************************"); -#endif - } - - - // At this point, all subMemories can be considered - // independent. Even though, they are all still - // full-linked under freeList we are going to break the - // links. - internal static unsafe void InitializeProcessorAddressSpace() - { - bool hasAffinityInfo = Processor.HasAffinityInfo(); - - DebugMpPhase(0); - -#if COMPLEX_TEST - DebugMpPhase(1); - PrepareSubMemoryMapComplexTest(); - - DebugMpPhase(2); - CreateSubMemoryMap(); - - DebugMpPhase(3); - PrepareProcessorMemoryMapComplexTest(); - - DebugMpPhase(4); - PrepareDomainMap(3); - - DebugMpPhase(5); - ConnectSubMemoriesPerDomain(); -#else - if (!hasAffinityInfo) { - - DebugMpPhase(1); - PrepareSubMemoryMapNoAffinity(); - - // skip Phase 2, since only has 1 sub memory - - DebugMpPhase(3); - PrepareProcessorMemoryMapNoAffinity(); - - DebugMpPhase(4); - PrepareDomainMapNoAffinity(); - - DebugMpPhase(5); - ConnectSubMemoriesPerDomainNoAffinity(); - } - else { - DebugMpPhase(1); - PrepareSubMemoryMap(); - - DebugMpPhase(2); - CreateSubMemoryMap(); - - DebugMpPhase(3); - PrepareProcessorMemoryMap(); - - DebugMpPhase(4); - PrepareDomainMap(Processor.GetDomainCount()); - - DebugMpPhase(5); - ConnectSubMemoriesPerDomain(); - } -#endif - - // At this point, domain is ready, then we can break the - // each domain's sub memories across the processors in the - // domain - - DebugMpPhase(6); - CreatePerProcessorMemory(); - - DebugMpPhase(7); - CopyDomainProcessorsToProcessors(); - - DebugMpPhase(8); - AttachPerProcessorMemoryToFreeListTail(); - - // Reset back processor[0].baseAddr = 0. This is a hack for - // now, the top part of the memory is already gone during - // MP FlatPage initialization. - processorMemoryMap[0].length += processorMemoryMap[0].baseAddr; - processorMemoryMap[0].baseAddr = 0; - - // Each Processor's memory is ready - isProcessorMemoryInitialized = true; - - DebugMpPhase(9); - - // Final check, dump to debugger - FreeNode.PrintProcessorsAddressSpaces(); - } - - internal static unsafe void Initialize() { Tracing.Log(Tracing.Debug, "FlatPages.Initialize() called"); - InitializeLock(); + // Initialize spinlock info + pageLock = new SpinLock(SpinLock.Types.FlatPages); - BootInfo * bi = BootInfo.HalGetBootInfo(); + Platform p = Platform.ThePlatform; + HistogramSize = 64; + HistogramGranularityShift = 20; + FreeMemoryCounters = null; + CurrentSystemLogger = null; - isProcessorMemoryInitialized = false; + // Retrieve the highest RAM address + MemoryManager.PhysicalMemoryLimits(out lowerLimit, out upperLimit); - // First pass over SMAP, find the highest RAM address - SMAPINFO *smap = (SMAPINFO*)bi->SmapData32; - addressLimit = UIntPtr.Zero; - for (uint i = 0; i < bi->SmapCount; i++) { - if (smap[i].type == (ulong)SMAPINFO.AddressType.Free && - smap[i].addr + smap[i].size > addressLimit) { - addressLimit = smap[i].addr + smap[i].size; - } + DebugStub.WriteLine("MemoryManager Physical limits {0:x8}...{1:x8}\n", + __arglist(lowerLimit, upperLimit)); - unchecked { - Tracing.Log(Tracing.Debug, - " [{0,8:x8}..{1,8:x8}] = {2,8:x8}", - (UIntPtr)(uint)smap[i].addr, - (UIntPtr)(uint)(smap[i].addr + smap[i].size), - (UIntPtr)(uint)smap[i].type); + // Compute page count + + pageCount = Pad((upperLimit >> MemoryManager.PageBits) + 1, + MemoryManager.PageSize / sizeof(uint)); + + // Find a spot for the page table. + pageTable = null; + + // Figure out the lowest possible location for positioning the pageMap + UIntPtr limit = Pad(lowerLimit, 0x00200000); + + // Make sure "limit" doesn't straddle some of the other non-SMAP regions + if (p.BootAllocatedMemorySize != 0) { + if ((p.BootAllocatedMemory < limit) && + (limit < p.BootAllocatedMemory + p.BootAllocatedMemorySize)) { + limit = MemoryManager.PagePad(p.BootAllocatedMemory + p.BootAllocatedMemorySize); } } - pageCount = Pad((addressLimit >> MemoryManager.PageBits) + 1, MemoryManager.PageSize / sizeof(uint)); - UIntPtr limit = Pad(bi->DumpLimit, 0x200000); + if (p.KernelDllSize != 0) { + if ((p.KernelDllBase < limit) && + (limit < p.KernelDllBase + p.KernelDllSize)) { + limit = MemoryManager.PagePad(p.KernelDllBase + p.KernelDllSize); + } + } + DebugStub.WriteLine("KernelDllBase {0,8:x} size {1,8:x} limit {2,8:x} \n", __arglist(p.KernelDllBase, p.KernelDllSize, limit)); + + SMAPINFO *smap = (SMAPINFO*)p.Smap; + for (uint i = 0; i < p.SmapCount; i++) { + if (smap[i].type == (ulong)SMAPINFO.AddressType.Free) { + if (RangeWithinLimits(smap[i].addr, smap[i].size, upperLimit, lowerLimit, out baseAddress, out size)){ + if(baseAddress + size >= limit) { + UIntPtr start = baseAddress; + UIntPtr end = baseAddress + size; + + if (start < limit) { + start = limit; + } + if ((end - start) >= pageCount * sizeof(uint)) { + pageTable = (uint *) start; + break; + } + } + } + } + } + + if (pageTable == null) { + DebugStub.WriteLine("pageTable == null, un-able to find page table memory\n"); + DebugStub.Break(); + } + + DebugStub.WriteLine("p.PhysicalBase 0x{0,8:x8} upperLimit: 0x{1,8:x8}, pageTable: 0x{2,8:x8}", + __arglist(p.PhysicalBase, upperLimit, (UIntPtr)pageTable)); Tracing.Log(Tracing.Debug, "Limit of RAM={0,8:x}, entries={1:x}, table={2:x}", - addressLimit, pageCount, limit); - - // Create the page descriptor table. - pageTable = (uint *)limit; + upperLimit, pageCount, (UIntPtr) pageTable); // Initialize all page descriptors to Unknown. SetPages(0, pageCount, MemoryManager.PageUnknown); // Second pass over SMAP, mark known RAM. - for (uint i = 0; i < bi->SmapCount; i++) { - if (smap[i].type == (ulong)SMAPINFO.AddressType.Free) { - SetRange(smap[i].addr, smap[i].size, MemoryManager.PageFree); + // we use to bootloader knowledge to map our address space + for (uint i = 0; i < p.SmapCount; i++) { + if (RangeWithinLimits(smap[i].addr, smap[i].size, upperLimit, lowerLimit, out baseAddress, out size)) { + if (smap[i].type == (ulong)SMAPINFO.AddressType.Free) { + SetRange(baseAddress, size, MemoryManager.PageFree); + } + else if(smap[i].type == (ulong)SMAPINFO.AddressType.KernelNonGc) { + SetRange(baseAddress, size, MemoryManager.KernelPageNonGC); + } + else if(smap[i].type == (ulong)SMAPINFO.AddressType.KernelStack) { + SetRange(baseAddress, size, MemoryManager.KernelPageStack); + } + else { + ; + } + } + else { + DebugStub.WriteLine("Ignoring Entry: Smap range {0:x8}...{1:x8} memory range {2:x8}...{3:x8}", + __arglist( + smap[i].addr, + smap[i].addr + smap[i].size, + lowerLimit, + upperLimit) + ); } } + // Set non-physical pages as unknown + SetRange(0, Platform.ThePlatform.PhysicalBase, MemoryManager.PageUnknown); + // Record the page table memory. - SetRange(limit, pageCount * sizeof(uint), MemoryManager.KernelPageNonGC); + SetRange((UIntPtr) pageTable, pageCount * sizeof(uint), MemoryManager.KernelPageNonGC); - // Record the kernel memory. - SetRange(0x0, BootInfo.KERNEL_STACK_BEGIN, MemoryManager.KernelPageImage); - SetRange(bi->DumpBase, limit - bi->DumpBase, MemoryManager.KernelPageNonGC); - SetRange(BootInfo.KERNEL_STACK_BEGIN, - BootInfo.KERNEL_STACK_LIMIT - BootInfo.KERNEL_STACK_BEGIN, - MemoryManager.KernelPageStack); - - // Note, normally filtered out by boot loader. - // SetRange(bi->DumpAddr32, bi->DumpAddr32 + bi->DumpSize32, MemoryManager.PageUnknown); - - // Third pass over SMAP, mark hardware reserved memory as Unknown. - for (uint i = 0; i < bi->SmapCount; i++) { - if (smap[i].type != (ulong)SMAPINFO.AddressType.Free && - smap[i].addr < addressLimit) { - SetRange(smap[i].addr, smap[i].size, MemoryManager.PageUnknown); - } - } // Initialize the free and save lists. fixed (FreeNode *tail = &freeListTail) { @@ -1319,8 +220,13 @@ namespace Microsoft.Singularity.Memory FreeNode.Init(saveList, true); } + availableMemory = UIntPtr.Zero; + + // Initialize the memory reservations + MemoryReservations.Initialize(); + uint *desc = pageTable; - uint last = *desc; + uint last = *desc & MemoryManager.SystemPageMask; UIntPtr begin = UIntPtr.Zero; for (UIntPtr i = UIntPtr.Zero; i < pageCount; i++) { @@ -1330,100 +236,12 @@ namespace Microsoft.Singularity.Memory if (last == MemoryManager.PageFree) { FreeNode.CreateAndInsert(freeList, AddrFromPage(begin), - AddrFromPage(i - begin)); + AddrFromPage(i) - AddrFromPage(begin)); } begin = i; last = val; } } - - Dump("Initialized"); - -#if TEST - UIntPtr l1 = RawAllocateBelow(0x1000000, 0x20000, 0x20000, 0x88810000u); - UIntPtr l2 = RawAllocateBelow(0x1000000, 0x10000, 0x20000, 0x88820000u); - UIntPtr l3 = RawAllocateBelow(0x1000000, 0x20000, 0x20000, 0x88830000u); - UIntPtr l4 = RawAllocateBelow(0x1000000, 0x10000, 0x20000, 0x88840000u); - - UIntPtr a1 = RawAllocate( 0x1000, 0x100000, 0x4000, 0x99910000u); - UIntPtr a2 = RawAllocate( 0x10000, 0x100000, 0x4000, 0x99920000u); - UIntPtr a3 = RawAllocate(0x100000, 0x100000, 0x4000, 0x99930000u); - UIntPtr a4 = RawAllocate( 0x1000, 0x10000, 0x4000, 0x99940000u); - UIntPtr a5 = RawAllocate( 0x1000, 0x10000, 0x4000, 0x99950000u); - - Dump("Base Allocations"); - - UIntPtr a1a = a1 != UIntPtr.Zero - ? RawAllocateExtend(a1 + 0x1000, 0xf000, 0x99910001u) : UIntPtr.Zero; - UIntPtr a2a = a2 != UIntPtr.Zero - ? RawAllocateExtend(a2 + 0x10000, 0x10000, 0x99920001u) : UIntPtr.Zero; - UIntPtr a4a = a4 != UIntPtr.Zero - ? RawAllocateExtend(a4 + 0x1000, 0xf000, 0x99940001u) : UIntPtr.Zero; - - Dump("Extend Allocations"); - - Tracing.Log(Tracing.Debug, "Query Tests:"); - DumpQuery(0); - DumpQuery(0x100000); - DumpQuery(0x200000); - DumpQuery(0x300000); - DumpQuery(bi->DumpBase + 0x1000); - DumpQuery(BootInfo.KERNEL_STACK_BEGIN + 0x1000); - DumpQuery(l1); - DumpQuery(l1 + 0x20000); - DumpQuery(l2); - DumpQuery(l2 + 0x20000); - DumpQuery(l3); - DumpQuery(l3 + 0x20000); - DumpQuery(l4); - DumpQuery(l4 + 0x20000); - DumpQuery(a1); - DumpQuery(a1 + 0x20000); - DumpQuery(a2); - DumpQuery(a2 + 0x20000); - DumpQuery(a3); - DumpQuery(a3 + 0x20000); - DumpQuery(a4); - DumpQuery(a4 + 0x20000); - DumpQuery(a5); - DumpQuery(a5 + 0x20000); - - if (l1 != UIntPtr.Zero) { - RawFree(l1, 0x20000, 0x88810000u); - } - if (l3 != UIntPtr.Zero) { - RawFree(l3, 0x20000, 0x88830000u); - } - if (a1 != UIntPtr.Zero) { - RawFree(a1, 0x10000, 0x99910000u); - } - if (a3 != UIntPtr.Zero) { - RawFree(a3, 0x100000, 0x99930000u); - } - if (a5 != UIntPtr.Zero) { - RawFree(a5, 0x1000, 0x99950000u); - } - - Dump("First Free"); - - if (l2 != UIntPtr.Zero) { - RawFree(l2, 0x10000, 0x88820000u); - } - if (l4 != UIntPtr.Zero) { - RawFree(l4, 0x10000, 0x88840000u); - } - if (a2 != UIntPtr.Zero) { - RawFree(a2, 0x20000, 0x99920000u); - } - if (a4 != UIntPtr.Zero) { - RawFree(a4, 0x10000, 0x99940000u); - } - - Dump("Final Free"); - DebugStub.Break(); - DebugStub.Break(); - DebugStub.Break(); -#endif } internal static void Finalize() @@ -1431,14 +249,7 @@ namespace Microsoft.Singularity.Memory // Doesn't actually do anything. } - private static void InitializeLock() - { -#if SINGULARITY_MP - pageLock = new SpinLock(); -#endif // SINGULARITY_MP - } - - [NoStackLinkCheck] + [NoStackLinkCheckTrans] private static bool Lock() { bool enabled = Processor.DisableInterrupts(); @@ -1448,7 +259,7 @@ namespace Microsoft.Singularity.Memory return enabled; } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] private static void Unlock(bool iflag) { #if SINGULARITY_MP @@ -1461,22 +272,12 @@ namespace Microsoft.Singularity.Memory // future, this should consult the ProcessorMemoryMap private static unsafe FreeNode* GetFreeList() { - if (isProcessorMemoryInitialized) { - return processorMemoryMap[0].procFreeList; - } - else { return freeList; - } } private static unsafe FreeNode* GetSaveList() { - if (isProcessorMemoryInitialized) { - return processorMemoryMap[0].procSaveList; - } - else { return saveList; - } } @@ -1493,13 +294,15 @@ namespace Microsoft.Singularity.Memory get { return pageTable; } } - [NoStackLinkCheck] - internal static UIntPtr Allocate(UIntPtr bytes, - UIntPtr reserve, - UIntPtr alignment, - Process process, - uint extra, - PageType type) + [NoStackLinkCheckTrans] + [FlatPagesLock] + internal static UIntPtr StackAllocate(UIntPtr bytes, + UIntPtr reserve, + UIntPtr alignment, + Process process, + uint extra, + bool kernelAllocation, + bool initialStack) { #if NO__TRACE_PAGES #else @@ -1512,7 +315,85 @@ namespace Microsoft.Singularity.Memory got = RawAllocate(bytes, reserve, alignment, (process != null ? process.ProcessTag : MemoryManager.KernelPage) | (extra & MemoryManager.ExtraMask) - | (uint)type); + | (uint)PageType.Stack, kernelAllocation); +#if VERBOSE + Tracing.Log(Tracing.Debug, "{0:x8} Allocate({1:x},{2:x},{3:x}", + Kernel.AddressOf(process), bytes, reserve, + alignment); +#endif + if (got != UIntPtr.Zero) { + + if (initialStack) { + // increase our kernel stack reservation + MemoryReservations.ThreadCreateNotification(); + } + + if (process != null) { + process.Allocated(bytes); + } + } + } + finally { + Unlock(iflag); + } +#if NO__TRACE_PAGES +#else + Kernel.Waypoint(961); +#endif + return got; + } + + [NoStackLinkCheckTrans] + [FlatPagesLock] + internal static void StackFree(UIntPtr addr, + UIntPtr bytes, + Process process, + bool kernelAllocation, + bool initialStack) + { + bool iflag = Lock(); + try { + RawFree(addr, bytes, process != null ? process.ProcessTag : MemoryManager.KernelPage, true, kernelAllocation); + if (process != null) { + process.Freed(bytes); + } + + if (initialStack) { + // release our kernel stack reservation + MemoryReservations.ThreadDestroyNotification(); + } + } + finally { + Unlock(iflag); + } + return; + } + + // + // If process == null, its a kernel allocation, otherwise its + // a user (SIP) allocation. + // + [NoStackLinkCheckTrans] + [FlatPagesLock] + internal static UIntPtr Allocate(UIntPtr bytes, + UIntPtr reserve, + UIntPtr alignment, + Process process, + uint extra, + PageType type) + { +#if NO__TRACE_PAGES +#else + Kernel.Waypoint(960); +#endif + UIntPtr got = new UIntPtr(); + + bool iflag = Lock(); + try { + got = RawAllocate(bytes, reserve, alignment, + (process != null ? process.ProcessTag : MemoryManager.KernelPage) + | (extra & MemoryManager.ExtraMask) + | (uint)type, true); #if VERBOSE Tracing.Log(Tracing.Debug, "{0:x8} Allocate({1:x},{2:x},{3:x}", Kernel.AddressOf(process), bytes, reserve, @@ -1532,6 +413,7 @@ namespace Microsoft.Singularity.Memory return got; } + [FlatPagesLock] internal static UIntPtr AllocateBelow(UIntPtr limit, UIntPtr bytes, UIntPtr alignment, @@ -1557,6 +439,7 @@ namespace Microsoft.Singularity.Memory return got; } + [FlatPagesLock] internal static UIntPtr AllocateExtend(UIntPtr addr, UIntPtr bytes, Process process, @@ -1584,14 +467,15 @@ namespace Microsoft.Singularity.Memory return got; } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] + [FlatPagesLock] internal static void Free(UIntPtr addr, UIntPtr bytes, Process process) { bool iflag = Lock(); try { - RawFree(addr, bytes, process != null ? process.ProcessTag : MemoryManager.KernelPage); + RawFree(addr, bytes, process != null ? process.ProcessTag : MemoryManager.KernelPage, false, true); if (process != null) { process.Freed(bytes); } @@ -1601,6 +485,7 @@ namespace Microsoft.Singularity.Memory } } + [FlatPagesLock] internal static unsafe UIntPtr FreeAll(Process process) { DebugStub.Assert(process != null, @@ -1640,8 +525,9 @@ namespace Microsoft.Singularity.Memory *begin); bool iflag = Lock(); + UIntPtr sizeInBytes = (size << MemoryManager.PageBits); try { - RawFree(AddrFromPage(page), AddrFromPage(size), tag); + RawFree(AddrFromPage(page), sizeInBytes, tag, false, false); } finally { Unlock(iflag); @@ -1670,6 +556,7 @@ namespace Microsoft.Singularity.Memory return bytes * MemoryManager.PageSize; } + [FlatPagesLock] internal static PageType Query(UIntPtr queryAddr, Process process, out UIntPtr regionAddr, @@ -1691,19 +578,19 @@ namespace Microsoft.Singularity.Memory ////////////////////////////////////////////////////////////////////// // - [NoStackLinkCheck] + [NoStackLinkCheckTrans] private static unsafe UIntPtr RawAllocate(UIntPtr bytes, UIntPtr reserve, UIntPtr alignment, - uint tag) + uint tag, + bool kernelAllocation) { VTable.Assert(Processor.InterruptsDisabled()); #if NO__TRACE_PAGES #else Kernel.Waypoint(970); #endif - if (alignment < MemoryManager.PageSize) - { + if (alignment < MemoryManager.PageSize) { alignment = MemoryManager.PageSize; } if (reserve < bytes) { @@ -1714,6 +601,10 @@ namespace Microsoft.Singularity.Memory " size={0:x}, res={1:x}, aln={2:x}, tag={3:x}", bytes, reserve, alignment, tag); #endif + // Check the allocation against memory reservations + if (MemoryReservations.MemoryReservationExceeded((ulong)availableMemory, tag, kernelAllocation, bytes + reserve + alignment)) { + return UIntPtr.Zero; + } FreeNode * node = FreeNode.FindGoodFit(GetFreeList(), reserve, alignment); if (node == null) { @@ -1723,15 +614,15 @@ namespace Microsoft.Singularity.Memory if (node == null) { node = FreeNode.FindGoodFit(GetSaveList(), bytes, alignment); if (node == null) { - // We should try to combine free and save pages... + // TODO: We should try to combine free and save pages... // But for now, we just fail. + DebugStub.WriteLine("Failed to allocate on the heap!!! {0} bytes kernelalloc {1}\n", + __arglist(bytes, kernelAllocation)); return UIntPtr.Zero; } } } } - - UIntPtr addr = (UIntPtr)node; UIntPtr adjust = SpaceNotAligned(addr + node->bytes, alignment); UIntPtr found = node->bytes; @@ -1740,12 +631,9 @@ namespace Microsoft.Singularity.Memory Tracing.Log(Tracing.Debug, " 0. {0:x8}..{1:x8}: res={2:x}, adj={3:x}", addr, addr + found, reserve, adjust); #endif - - if (found > reserve + adjust) { // Put the extraneous pages in the free list. FreeNode.ReturnExtraBelow(GetFreeList(), ref addr, ref found, reserve + adjust); - #if VERBOSE Tracing.Log(Tracing.Debug, " 1. {0:x8}..{1:x8}", addr, addr + found); @@ -1760,7 +648,6 @@ namespace Microsoft.Singularity.Memory if (found > bytes) { // Put extra pages in the save list. FreeNode.ReturnExtraAbove(GetSaveList(), addr, ref found, bytes); - #if VERBOSE Tracing.Log(Tracing.Debug, " 2. {0:x8}..{1:x8}", addr, addr + found); @@ -1777,10 +664,30 @@ namespace Microsoft.Singularity.Memory #else Kernel.Waypoint(971); #endif + // + // The owner information is not reliable for determining kernel + // or SIP allocation when the page type is stack, so the logic + // is a little extra complicated here. + // + if ((tag & MemoryManager.TypeMask) == (uint)PageType.Stack) { - allocatedCount++; - allocatedBytes += (ulong)bytes; + // Stack pages use the kernelAllocation flag + if (kernelAllocation) { + MemoryReservations.AllocationForStack((ulong)bytes); + } + else { + MemoryReservations.SIPAllocationForStack((ulong)bytes); + } + } + else if ((tag & MemoryManager.ProcessPageMask) != MemoryManager.KernelPage) { + // process tag is reliable for non-stack allocations + MemoryReservations.SIPAllocationForHeap((ulong)bytes); + } + else { + MemoryReservations.AllocationForHeap((ulong)bytes); + } + LogMemoryOperation(kernelAllocation, tag, addr, bytes); return addr; } @@ -1794,8 +701,7 @@ namespace Microsoft.Singularity.Memory #else Kernel.Waypoint(972); #endif - if (alignment < MemoryManager.PageSize) - { + if (alignment < MemoryManager.PageSize) { alignment = MemoryManager.PageSize; } @@ -1804,11 +710,16 @@ namespace Microsoft.Singularity.Memory "lim={0:x8}, size={1:x8}, align={2}, tag={3:x}", limit, bytes, alignment, tag); #endif + // Check the allocation against memory reservations + if (MemoryReservations.MemoryReservationExceeded((ulong)availableMemory, tag, false, bytes + alignment)) { + return UIntPtr.Zero; + } + FreeNode * node = FreeNode.FindBelow(limit, GetFreeList(), bytes, alignment); if (node == null) { node = FreeNode.FindBelow(limit, GetSaveList(), bytes, alignment); if (node == null) { - // We should try to combine free and save pages... + // TODO: We should try to combine free and save pages... // But for now, we just fail. return UIntPtr.Zero; } @@ -1848,9 +759,17 @@ namespace Microsoft.Singularity.Memory #else Kernel.Waypoint(973); #endif + // Stacks are only allocated through RawAllocate() + VTable.Assert((tag & MemoryManager.TypeMask) != (uint)PageType.Stack); - allocatedCount++; - allocatedBytes += (ulong)bytes; + if ((tag & MemoryManager.ProcessPageMask) != MemoryManager.KernelPage) { + MemoryReservations.SIPAllocationForHeap((ulong)bytes); + } + else { + MemoryReservations.AllocationForHeap((ulong)bytes); + } + + LogMemoryOperation(false, tag, addr, bytes); return addr; } @@ -1869,6 +788,12 @@ namespace Microsoft.Singularity.Memory Tracing.Log(Tracing.Error, "{0:x} is not first free page {1:x}.", addr, *(pageTable + page)); + + return UIntPtr.Zero; + } + + // Check the allocation against memory reservations + if (MemoryReservations.MemoryReservationExceeded((ulong)availableMemory, tag, false, bytes)) { return UIntPtr.Zero; } @@ -1905,27 +830,42 @@ namespace Microsoft.Singularity.Memory #else Kernel.Waypoint(975); #endif + // Stacks are only allocated through RawAllocate() + VTable.Assert((tag & MemoryManager.TypeMask) != (uint)PageType.Stack); - allocatedCount++; - allocatedBytes += (ulong)bytes; + if ((tag & MemoryManager.ProcessPageMask) != MemoryManager.KernelPage) { + MemoryReservations.SIPAllocationForHeap((ulong)bytes); + } + else { + MemoryReservations.AllocationForHeap((ulong)bytes); + } + LogMemoryOperation(false, tag, addr, bytes); return addr; } - [NoStackLinkCheck] - private static unsafe void VerifyOwner(UIntPtr page, UIntPtr pages, uint tag) + [NoStackLinkCheckTrans] + private static unsafe bool VerifyOwner(UIntPtr page, UIntPtr pages, uint tag) { tag &= MemoryManager.ProcessPageMask; for (UIntPtr i = UIntPtr.Zero; i < pages; i++) { + if (((*(pageTable + page + i)) & MemoryManager.ProcessPageMask) != tag) { + DebugStub.WriteLine("FlatPages.VerifyOwner page={0} i={1} tag={2} address 0x{3,8:x}", + __arglist(page, i, tag, ((pageTable+page)))); + return false; + } +#if false DebugStub.Assert (((*(pageTable + page + i)) & MemoryManager.ProcessPageMask) == tag, "FlatPages.VerifyOwner page={0} i={1} tag={2}", __arglist(page, i, tag)); +#endif } + return true; } - [NoStackLinkCheck] - private static unsafe void RawFree(UIntPtr addr, UIntPtr bytes, uint tag) + [NoStackLinkCheckTrans] + private static unsafe void RawFree(UIntPtr addr, UIntPtr bytes, uint tag, bool stack, bool kernelAllocation) { VTable.Assert(Processor.InterruptsDisabled()); UIntPtr bytesIn = bytes; @@ -1938,7 +878,12 @@ namespace Microsoft.Singularity.Memory addr, bytes, tag); #endif - VerifyOwner(MemoryManager.PageFromAddr(addr), MemoryManager.PagesFromBytes(bytes), tag); + bool result = VerifyOwner(MemoryManager.PageFromAddr(addr), MemoryManager.PagesFromBytes(bytes), tag); + if(false == result) { + DebugStub.WriteLine("VerifyOwner Failed for addr 0x{0,8:x} bytes {1} page {2} pages {3}\n", + __arglist(addr, bytes, MemoryManager.PageFromAddr(addr), MemoryManager.PagesFromBytes(bytes))); + DebugStub.Break(); + } FreeNode *node = FreeNode.GetNodeAt(addr + bytes); FreeNode *prev = FreeNode.GetNodeFromLast(addr - MemoryManager.PageSize); @@ -1970,9 +915,24 @@ namespace Microsoft.Singularity.Memory #else Kernel.Waypoint(977); #endif + // Stack allocations are ambiguous and must be called out separately + if (stack) { + if (kernelAllocation) { + MemoryReservations.FreeForStack((ulong)bytesIn); + } + else { + MemoryReservations.SIPFreeForStack((ulong)bytesIn); + } + } + else if (tag != MemoryManager.KernelPage) { - freedCount++; - freedBytes += (ulong)bytesIn; + // Heap pages are reliable as per kernel/SIP + MemoryReservations.SIPFreeForHeap((ulong)bytesIn); + } + else { + MemoryReservations.FreeForHeap((ulong)bytesIn); + } + LogMemoryOperation(false, tag, addr, bytesIn); } private static unsafe PageType RawQuery(UIntPtr queryAddr, @@ -2056,7 +1016,8 @@ namespace Microsoft.Singularity.Memory AddrFromPage(startPage), AddrFromPage(limitPage)); #endif regionAddr = AddrFromPage(startPage); - regionSize = AddrFromPage(limitPage - startPage); + regionSize = AddrFromPage(limitPage) - AddrFromPage(startPage); + return type; } @@ -2080,10 +1041,10 @@ namespace Microsoft.Singularity.Memory private static unsafe void DumpFreeNodes(FreeNode *list, bool isSave) { if (isSave) { - Tracing.Log(Tracing.Debug, " SaveList:"); + DebugStub.WriteLine(" SaveList:"); } else { - Tracing.Log(Tracing.Debug, " FreeList:"); + DebugStub.WriteLine(" FreeList:"); } for (FreeNode *node = list->next; node != list; node = node->next) { @@ -2097,17 +1058,18 @@ namespace Microsoft.Singularity.Memory } } unchecked { - Tracing.Log(Tracing.Debug, fmt, + DebugStub.WriteLine( fmt, + __arglist( (UIntPtr)node, (UIntPtr)node + node->bytes, (UIntPtr)node->prev, (UIntPtr)node->next, - (UIntPtr)node->last); + (UIntPtr)node->last)); } } } internal static unsafe void Dump(string where) { - Tracing.Log(Tracing.Debug, "FlatPages.Dump: {0}", where); + DebugStub.WriteLine( "FlatPages.Dump: {0}", __arglist(where)); uint *descriptors = pageTable; uint last = *descriptors++ & MemoryManager.SystemPageMask; @@ -2138,42 +1100,46 @@ namespace Microsoft.Singularity.Memory } if (dsc != last) { - Tracing.Log(Tracing.Debug, " {0:x8}..{1:x8} : {2:x8} : {3:x8}", + DebugStub.WriteLine( " {0:x8}..{1:x8} : {2:x8} : {3:x8}", + __arglist( begin << MemoryManager.PageBits, i << MemoryManager.PageBits, last, - (i - begin) << MemoryManager.PageBits); + (i - begin) << MemoryManager.PageBits)); last = dsc; begin = i; } } - Tracing.Log(Tracing.Debug, " {0:x8}..{1:x8} : {2:x8} : {3:x8}", + DebugStub.WriteLine(" {0:x8}..{1:x8} : {2:x8} : {3:x8}", + __arglist( begin << MemoryManager.PageBits, pageCount << MemoryManager.PageBits, last, - (pageCount - begin) << MemoryManager.PageBits); + (pageCount - begin) << MemoryManager.PageBits)); DumpFreeNodes(GetFreeList(), false); DumpFreeNodes(GetSaveList(), true); - Tracing.Log(Tracing.Audit, - "Totals: free={0:x8}, used={1:x8}, unknown={2:x8}, reserved={3:x8}", + DebugStub.WriteLine("Totals: free={0:x8}, used={1:x8}, unknown={2:x8}, reserved={3:x8}", + __arglist( freePages << MemoryManager.PageBits, usedPages << MemoryManager.PageBits, unknownPages << MemoryManager.PageBits, - sharedPages << MemoryManager.PageBits); + sharedPages << MemoryManager.PageBits)); } ////////////////////////////////////////////////////////////////////// // - [NoStackLinkCheck] + [NoStackLinkCheckTrans] + [NoHeapAllocation] private static unsafe void SetPages(UIntPtr startPage, UIntPtr pageCount, uint tag) { uint * descriptor = pageTable + startPage; #if VERY_VERBOSE Tracing.Log(Tracing.Audit, - "SetPages(beg={0:x},num={1:x},val={2}", + "SetPages(beg={0:x},num={1:x},val={2:x}", startPage << MemoryManager.PageBits, pageCount << MemoryManager.PageBits, tag); + #endif while (pageCount > UIntPtr.Zero) { @@ -2182,14 +1148,15 @@ namespace Microsoft.Singularity.Memory } } - [NoStackLinkCheck] - private static void SetRange(UIntPtr start, UIntPtr bytes, uint tag) + [NoStackLinkCheckTrans] + [NoHeapAllocation] + internal static void SetRange(UIntPtr start, UIntPtr bytes, uint tag) { - if (start > addressLimit) { + if (start > upperLimit) { return; } - if (start + bytes > addressLimit) { - bytes = addressLimit - start; + if (start + bytes > upperLimit) { + bytes = upperLimit - start; } SetPages(MemoryManager.PageFromAddr(start), MemoryManager.PagesFromBytes(bytes), tag); } @@ -2198,26 +1165,65 @@ namespace Microsoft.Singularity.Memory public static UIntPtr GetMaxMemory() { - return addressLimit; + return upperLimit; } + [FlatPagesLock] public static unsafe UIntPtr GetFreeMemory() { - uint *descriptors = pageTable; +#if TEST_MEMORY_TRACKING UIntPtr retval = 0; + bool iflag = Lock(); - // Count free pages - for (UIntPtr i = (UIntPtr)1; i < pageCount; i++) { - uint dsc = *descriptors++; - uint val = dsc & MemoryManager.SystemPageMask; + try { + uint *descriptors = pageTable; - if (val == MemoryManager.PageFree) - { - retval++; - } + // Count free pages + for (UIntPtr i = (UIntPtr)1; i < pageCount; i++) { + uint dsc = *descriptors++; + uint val = dsc & MemoryManager.SystemPageMask; + + if (val == MemoryManager.PageFree) { + retval++; + } + } + + // + // Make sure everything reports the same info + // + UIntPtr freeListSize = FreeNode.GetFreeListTotalSize(freeList); + UIntPtr saveListSize = FreeNode.GetFreeListTotalSize(saveList); + UIntPtr totalFreeList = freeListSize + saveListSize; + + if (((retval * MemoryManager.PageSize) != totalFreeList) || + (totalFreeList != availableMemory)) { + + DebugStub.WriteLine("GetFreeMemory: Internal inconsistency"); + + DebugStub.WriteLine("Pages total 0x{0:x8}", __arglist(retval * MemoryManager.PageSize)); + + DebugStub.WriteLine("FreeList 0x{0:x8}, SaveList 0x{1:x8}, Total 0x{2:x8}", + __arglist( + (ulong)freeListSize, (ulong)saveListSize, + ((ulong)freeListSize + (ulong)saveListSize))); + + DebugStub.WriteLine("availableMemory: 0x{0:x8}\n", __arglist(availableMemory)); + + DebugStub.Break(); + } + } + finally { + Unlock(iflag); } return retval * MemoryManager.PageSize; +#else + // + // We track it in real time so it may be used by other components + // at runtime. + // + return availableMemory; +#endif } public static unsafe UIntPtr GetUsedMemory() @@ -2225,13 +1231,12 @@ namespace Microsoft.Singularity.Memory uint *descriptors = pageTable; UIntPtr retval = 0; - // Count free pages + // Count non-free pages for (UIntPtr i = (UIntPtr)1; i < pageCount; i++) { uint dsc = *descriptors++; uint val = dsc & MemoryManager.SystemPageMask; - if (val != MemoryManager.PageFree) - { + if (val != MemoryManager.PageFree) { retval++; } } @@ -2239,23 +1244,127 @@ namespace Microsoft.Singularity.Memory return retval * MemoryManager.PageSize; } - public static void GetUsageStatistics(out ulong allocatedCount, - out ulong allocatedBytes, - out ulong freedCount, - out ulong freedBytes) + // + // Memory monitoring support + // + + [Inline] + private static void LogMemoryOperation(bool isKernelMemory, uint tag, UIntPtr address, UIntPtr size) { - allocatedCount = FlatPages.allocatedCount; - allocatedBytes = FlatPages.allocatedBytes; - freedCount = FlatPages.freedCount; - freedBytes = FlatPages.freedBytes; + if (CurrentSystemLogger != null) { + + ushort memoryType = (ushort)(tag & MemoryManager.TypeMask); + + if (isKernelMemory && ((tag & MemoryManager.TypeMask) == (uint)PageType.Stack)) { + + memoryType |= 0x8000; + } + + CurrentSystemLogger.Log((ushort)((tag & MemoryManager.ProcessPageMask) >> 16), + memoryType, + address, + size); + } } + public static void InitializeMemoryMonitoring() + { + CurrentSystemLogger = SystemAllocationLogger.Create("SystemMemoryLogger"); + DebugStub.Assert(CurrentSystemLogger != null); + + FreeMemoryCounters = FreeMemoryDistribution.Create("FreeMemoryCounters", HistogramSize); + DebugStub.Assert(FreeMemoryCounters != null); + + InitializeMemoryCounters(); + } + + [FlatPagesLock] + // moved into separate function to localize lock auditing + private static void InitializeMemoryCounters() + { + bool iflag = Lock(); + try { + + for (int i = 0; i < HistogramSize; i++) { + FreeMemoryCounters.Buffer[i].BlockCount = 0; + FreeMemoryCounters.Buffer[i].TotalBucketSize = 0; + } + + unsafe { + RegisterExitentFreeNodes(GetFreeList()); + RegisterExitentFreeNodes(GetSaveList()); + } + } + finally { + Unlock(iflag); + } + } + + internal static int HistogramSize; + internal static int HistogramGranularityShift; + private static FreeMemoryDistribution FreeMemoryCounters; + private static SystemAllocationLogger CurrentSystemLogger; + + [Inline] + internal static int GetBucketIndex(UIntPtr bytes) + { + UIntPtr bucket = bytes >> 20; + + if (bucket < (UIntPtr)HistogramSize) { + + return (int)bucket; + } + + return (HistogramSize - 1); + } + + + [NoStackLinkCheckTrans] + internal static void RegisterFreeNode(UIntPtr blockSize) + { + if (FreeMemoryCounters != null) { + + int i = GetBucketIndex(blockSize); + + FreeMemoryCounters.Buffer[i].BlockCount += 1; + FreeMemoryCounters.Buffer[i].TotalBucketSize += blockSize; + } + } + + [NoStackLinkCheckTrans] + internal static void DeRegisterFreeNode(UIntPtr blockSize) + { + if (FreeMemoryCounters != null) { + + int i = GetBucketIndex(blockSize); + + DebugStub.Assert((FreeMemoryCounters.Buffer[i].BlockCount > 0), + "ASSERT: FreeMemoryCounters.BlockCount > 0"); + + DebugStub.Assert((FreeMemoryCounters.Buffer[i].TotalBucketSize >= blockSize), + "ASSERT: FreeMemoryCounters.TotalBucketSize > blockSize"); + + FreeMemoryCounters.Buffer[i].BlockCount -= 1; + FreeMemoryCounters.Buffer[i].TotalBucketSize -= blockSize; + } + } + + private static unsafe void RegisterExitentFreeNodes(FreeNode *list) + { + for (FreeNode *node = list->next; node != list; node = node->next) { + + RegisterFreeNode(node->bytes); + } + } + + ////////////////////////////////////////////////////////////////////// // [Inline] - internal static UIntPtr AddrFromPage(UIntPtr page) { - return (page << MemoryManager.PageBits); + internal static UIntPtr AddrFromPage(UIntPtr page) + { + return (page << MemoryManager.PageBits); } [Inline] @@ -2293,7 +1402,7 @@ namespace Microsoft.Singularity.Memory internal uint signature; internal unsafe FreeNode * node; - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe LastNode * Create(UIntPtr addr, FreeNode *node) { LastNode *last = (LastNode *)addr; @@ -2307,14 +1416,14 @@ namespace Microsoft.Singularity.Memory return last; } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe void Remove(LastNode *last) { last->signature = Removed; last->node = null; } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe void PrintLastNode(UIntPtr addr) { LastNode *last = (LastNode *)addr; @@ -2339,7 +1448,7 @@ namespace Microsoft.Singularity.Memory internal UIntPtr bytes; internal bool isSave; - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe void Init(FreeNode *list, bool isSave) { list->signature = Signature; @@ -2350,12 +1459,15 @@ namespace Microsoft.Singularity.Memory list->isSave = isSave; } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe bool Remove(FreeNode *node) { FreeNode * prev; FreeNode * next; + availableMemory -= node->bytes; + DeRegisterFreeNode(node->bytes); + UIntPtr page = MemoryManager.PageFromAddr((UIntPtr)node); *(pageTable + page) = MemoryManager.PageFree; @@ -2372,7 +1484,7 @@ namespace Microsoft.Singularity.Memory return (next == prev); } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] private static unsafe void InsertAsPrev(FreeNode *list, FreeNode *node) { FreeNode * prev; @@ -2384,7 +1496,7 @@ namespace Microsoft.Singularity.Memory list->prev = node; } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] private static unsafe void InsertAsNext(FreeNode *list, FreeNode *node) { FreeNode * next; @@ -2396,7 +1508,7 @@ namespace Microsoft.Singularity.Memory list->next = node; } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] private static unsafe void InsertBySize(FreeNode *list, FreeNode *node) { #if ALLOW_BOOT_ARGLIST @@ -2427,7 +1539,7 @@ namespace Microsoft.Singularity.Memory } /////////////////////////////////////////////////////////// - // haryadi FreeNode's new routines start here + // FreeNode's new routines start here internal static unsafe void PrintFreeList(FreeNode *list) { @@ -2463,35 +1575,6 @@ namespace Microsoft.Singularity.Memory } } - internal static unsafe void PrintDomainFreeLists() - { - DebugStub.WriteLine(" DOMAIN FREE LIST"); - DebugStub.WriteLine(" ------------------------------------------"); - for (int i = 0; i < domainMap.Length; i++) { - if (domainMap[i].isSubMemConnected) { - DebugStub.WriteLine("\n\n Domain [{0}]:", __arglist(i)); - PrintFreeList(domainMap[i].domFreeList); - } - - } - } - - internal static unsafe void PrintProcessorFreeLists() - { - DebugStub.WriteLine("\n"); - DebugStub.WriteLine(" ******************************************"); - DebugStub.WriteLine(" PROCESSOR FREE LIST"); - DebugStub.WriteLine(" ******************************************"); - for (int i = 0; i < processorMemoryMap.Length; i++) { - DebugStub.WriteLine("\n\n Processor [{0}]:", __arglist(i)); - if (processorMemoryMap[i].isInitialized) { - PrintFreeList(processorMemoryMap[i].procFreeList); - } - - } - DebugStub.WriteLine(); - } - internal static unsafe UIntPtr GetFreeListTotalSize(FreeNode *list) { UIntPtr size = 0; @@ -2502,37 +1585,7 @@ namespace Microsoft.Singularity.Memory return size; } - internal static unsafe void PrintProcessorAddressSpace(FreeNode *list) - { - ulong MB = 1024*1024; - for (FreeNode *node = list->next; - node != list; node = node->next) { - DebugStub.Print - ("[{0:x8}..{1:x8},{2,3}MB] ", - __arglist((UIntPtr)node, - (UIntPtr)node + node->bytes, - (ulong)(node->bytes)/MB)); - } - } - - internal static unsafe void PrintProcessorsAddressSpaces() - { - UIntPtr size = 0; - ulong MB = 1024*1024; - - DebugStub.WriteLine("Processor Address Space (Current Free List):"); - for (int i = 0; i < processorMemoryMap.Length; i++) { - if (processorMemoryMap[i].isInitialized) { - size = GetFreeListTotalSize(processorMemoryMap[i].procFreeList); - DebugStub.Print(" p{0} ({1,3}MB) : ", - __arglist(i, (ulong)size/MB)); - PrintProcessorAddressSpace(processorMemoryMap[i].procFreeList); - } - DebugStub.WriteLine(); - } - } - - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe FreeNode* GetFreeNodeAtBreakAddr(FreeNode *list, UIntPtr breakAddr) { int count = 0; @@ -2554,119 +1607,8 @@ namespace Microsoft.Singularity.Memory return null; } - // Imagine the case where the current free list contains - // node from address 100 to 1000 Now, the SRAT table says - // that a sub memory is from range 50 to 500. In - // CreateSubMemory, when we call GetFreeNodeBreakAddr(50) - // it will fail, because there is no free node at address - // 50. However this sub memory is actually intersects with - // the free list node. So the correct thing to do is to - // break it at address 100. This function will return the - // correct address (i.e. 100) to the caller, so that the - // caller can break the free list at 100 instead of 50. - // return breakAddr - [NoStackLinkCheck] - internal static unsafe UIntPtr IsPartialIntersect(FreeNode *list, UIntPtr baseAddr, UIntPtr endAddr) - { - UIntPtr nodeBaseAddr; - UIntPtr nodeEndAddr; - for (FreeNode *node = list->next; - node != list; node = node->next) { - - nodeBaseAddr = (UIntPtr)node; - nodeEndAddr = (UIntPtr)(node) + node->bytes; - - if (nodeBaseAddr < endAddr && nodeBaseAddr >= baseAddr) { -#if MP_VERBOSE - DebugStub.WriteLine(" ** Return Nb.{0:x8}", - __arglist(baseAddr)); -#endif - return nodeBaseAddr; - } - } - return 0; - } - - - - // This will break curNode into two nodes. For example - // curNode is from address X to Y The two nodes will be - // one from X to breakAddr and the other from breakAddr to - // Y. Also prev and next pointers are updated - [NoStackLinkCheck] - internal static unsafe void BreakListAt(FreeNode *list, FreeNode *curNode, UIntPtr breakAddr) - { - // Before breaking, need to check if this breakAddr - // has been broken before or not. If so, don't double - // break. One way to find out is to check the - // signature of the lastnode and freenode before and - // after the breakAddress respectively - FreeNode *freeNode = (FreeNode*) breakAddr; - LastNode *lastNode = (LastNode*) (breakAddr - MemoryManager.PageSize); - if (lastNode->signature == LastNode.Signature && - freeNode->signature == FreeNode.Signature) { -#if MP_VERBOSE - DebugStub.WriteLine(" {0:x8} Has been broken before. Cancel braking.", - __arglist(breakAddr)); -#endif - return; - } - - // If this is the first node in the list, and the address of - // the first node is the same as curNode. Then, - // don't break this node. - if ((UIntPtr) freeNode == breakAddr && - freeNode->prev == list) { -#if MP_VERBOSE - DebugStub.WriteLine(" {0:x8} is the first node. Cancel braking.", - __arglist(breakAddr)); -#endif - return; - } - -#if MP_VERBOSE - DebugStub.WriteLine(" {0:x8} is okay. Proceed Breaking", __arglist(breakAddr)); -#endif - - // first remember originals - LastNode *origLast = curNode->last; - FreeNode *origNext = curNode->next; - FreeNode *origPrev = curNode->prev; - UIntPtr origBytes = curNode->bytes; - bool origIsSave = curNode->isSave; - uint origSignature = curNode->signature; - - // prepare the two nodes - FreeNode *firstNode = curNode; - FreeNode *secondNode = (FreeNode*)breakAddr; - UIntPtr firstNodeBase = (UIntPtr) firstNode; - UIntPtr firstNodeEnd = breakAddr; - UIntPtr secondNodeBase = breakAddr; - UIntPtr secondNodeEnd = (UIntPtr)curNode + curNode->bytes; - - // now fix the second node FIRST!! (before the first node) - secondNode->next = origNext; - secondNode->prev = firstNode; - secondNode->bytes = secondNodeEnd - secondNodeBase; - secondNode->isSave = origIsSave; - secondNode->signature = origSignature; - LastNode.Create(secondNodeEnd - MemoryManager.PageSize, secondNode); - - // now fix the first node - firstNode->next = secondNode; - firstNode->prev = origPrev; - firstNode->bytes = firstNodeEnd - firstNodeBase; - firstNode->isSave = origIsSave; - firstNode->signature = origSignature; - LastNode.Create(firstNodeEnd - MemoryManager.PageSize, firstNode); - - // now fix the original next's previous pointer - origNext->prev = secondNode; - } - - - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe FreeNode * FindGoodFit(FreeNode *list, UIntPtr bytes, UIntPtr alignment) { @@ -2714,7 +1656,7 @@ namespace Microsoft.Singularity.Memory } } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe FreeNode * FindBelow(UIntPtr limit, FreeNode *list, UIntPtr bytes, UIntPtr alignment) { @@ -2735,7 +1677,7 @@ namespace Microsoft.Singularity.Memory return null; } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe FreeNode * GetNodeAt(UIntPtr addr) { UIntPtr page = MemoryManager.PageFromAddr(addr); @@ -2746,7 +1688,7 @@ namespace Microsoft.Singularity.Memory return null; } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe FreeNode * GetNodeFromLast(UIntPtr addr) { UIntPtr page = MemoryManager.PageFromAddr(addr); @@ -2762,7 +1704,7 @@ namespace Microsoft.Singularity.Memory return null; } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe FreeNode * Create(UIntPtr addr, UIntPtr bytes, bool isSave) { // Mark a page as a node in the free list, initialize the node struct. @@ -2792,13 +1734,20 @@ namespace Microsoft.Singularity.Memory return node; } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe void CreateAndInsert(FreeNode *list, UIntPtr addr, UIntPtr bytes) { FreeNode * node = Create(addr, bytes, list->isSave); - + // + // This memory is available on the freeList, or the + // SaveList. + // + // Upper level memory lock protects access to this field. + // + availableMemory += bytes; + RegisterFreeNode(bytes); #if VERBOSE Tracing.Log(Tracing.Debug, list->isSave ? @@ -2820,7 +1769,7 @@ namespace Microsoft.Singularity.Memory InsertBySize(list, node); } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe void ReturnExtraAbove(FreeNode *list, UIntPtr addr, ref UIntPtr found, @@ -2830,7 +1779,7 @@ namespace Microsoft.Singularity.Memory found = keep; } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe void ReturnExtraBelow(FreeNode *list, ref UIntPtr addr, ref UIntPtr found, @@ -2843,5 +1792,3 @@ namespace Microsoft.Singularity.Memory } } } - -#endif // !PAGING diff --git a/base/Kernel/Singularity/Memory/HandleTable.cs b/base/Kernel/Singularity/Memory/HandleTable.cs index 2fdb7c0..37a377c 100644 --- a/base/Kernel/Singularity/Memory/HandleTable.cs +++ b/base/Kernel/Singularity/Memory/HandleTable.cs @@ -39,6 +39,9 @@ namespace Microsoft.Singularity.Memory [CLSCompliant(false)] internal class HandleTable { +#if ISA_ARM + [AccessedByRuntime("Referenced in interlocked.cpp")] +#endif internal struct HandlePage { private UIntPtr table; @@ -57,6 +60,9 @@ namespace Microsoft.Singularity.Memory } } +#if ISA_ARM + [AccessedByRuntime("Referenced in interlocked.cpp")] +#endif internal struct HandleEntry { internal UIntPtr item; internal unsafe HandleEntry * next; // next free node, null if last or in use. @@ -68,13 +74,14 @@ namespace Microsoft.Singularity.Memory } [Inline] + [NoHeapAllocation] internal Object Get() { return Magic.fromAddress(item); } [Inline] - internal unsafe void Visit(NonNullReferenceVisitor visitor) + internal unsafe void Visit(DirectReferenceVisitor visitor) { if (item != UIntPtr.Zero) { fixed (UIntPtr *loc = &item) { @@ -118,6 +125,7 @@ namespace Microsoft.Singularity.Memory } [Inline] + [NoHeapAllocation] internal static unsafe Object GetHandle(UIntPtr handle) { HandleEntry *entry = (HandleEntry *)handle; @@ -136,13 +144,13 @@ namespace Microsoft.Singularity.Memory freeList = null; } - internal unsafe void VisitSpecialData(NonNullReferenceVisitor visitor) + internal unsafe void VisitSpecialData(DirectReferenceVisitor visitor) { HandlePage *limit = null; HandlePage *began; do { // Repeat this loop as long as new pages appear. - // I'm not sure this is needed, the write barrier might + // REVIEW: I'm not sure this is needed, the write barrier might // take care of this for us. began = pages; for (HandlePage *page = began; page != limit; page = page->next) { diff --git a/base/Kernel/Singularity/Memory/MemoryDiagnosis.cs b/base/Kernel/Singularity/Memory/MemoryDiagnosis.cs new file mode 100644 index 0000000..786e1fa --- /dev/null +++ b/base/Kernel/Singularity/Memory/MemoryDiagnosis.cs @@ -0,0 +1,245 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: MemoryDiagnosis.cs +// +// + +using System; +using System.Threading; +using System.Runtime.CompilerServices; + +using Microsoft.Singularity; +using Microsoft.Singularity.V1.Services; +using Microsoft.Singularity.Eventing; +using System.Collections; + +namespace Microsoft.Singularity.Eventing +{ + + [CLSCompliant(false)] + public class SystemAllocationLogger : EventSource { + + private const uint DefaultLogSize = 0x10000; + + public static SystemAllocationLogger Create(string sourceName) { + + EventingStorage storage = EventingStorage.CreateLocalStorage(QualityOfService.RecyclableEvents, + DefaultLogSize); + + if (storage == null) { + + return null; + } + + SystemAllocationLogger Logger = new SystemAllocationLogger(sourceName, + storage, + ENABLE_ALL_MASK); + + if (Logger != null) { + + Logger.Register(); + } + + return Logger; + } + + + //[EventType=Interrupt] + [NoHeapAllocation] + public bool Log(ushort processTag, ushort memoryType, UIntPtr address, UIntPtr size) { // !!!!! this line must be entered to define a schema + + + if ((ControlFlags & MemoryAllocation_Control_flag) != 0) { + + unsafe { + + MemoryAllocationEvent memEvent; + + memEvent.MemoryType = memoryType; + memEvent.ProcessTag = processTag; + memEvent.Address = address; + memEvent.Size = size; + + return (LogEntry(ControlFlags, + eventTypeMemoryAllocation, + (byte *)&memEvent, + sizeof(MemoryAllocationEvent)) != 0); + } + } + + return false; + } + + public unsafe struct MemoryAllocationEvent { + + public UIntPtr Address; + public UIntPtr Size; + public ushort ProcessTag; + public ushort MemoryType; + }; + + UIntPtr eventTypeMemoryAllocation; + const uint MemoryAllocation_Control_flag = 0x10000; + + public override bool Register() { + + if (!base.Register()) { + + return false; + } + + if (HostController.RegisterEvent("SystemMemoryAllocation", + "Memory allocation event: Address={0}, Memory type = {3}", + ref eventTypeMemoryAllocation)) { + + HostController.RegisterEventField(eventTypeMemoryAllocation, + "Address", + 0, + DataType.__UIntPtr); + + HostController.RegisterEventField(eventTypeMemoryAllocation, + "Size", + 0, + DataType.__UIntPtr); + + HostController.RegisterEventField(eventTypeMemoryAllocation, + "ProcessTag", + 0, + DataType.__uint16); + + HostController.RegisterEventField(eventTypeMemoryAllocation, + "MemoryType", + 0, + DataType.__uint16); + + } else { + + // The event might have been registered already + // Check whether we foundit already in the table or not + + if (eventTypeMemoryAllocation == 0) { + return false; + } + } + + return true; + } + + SystemAllocationLogger (string sourceName, EventingStorage storage, uint controlFlags) + :base(sourceName, storage, controlFlags) {} + + } + + [CLSCompliant(false)] + [NoCCtor] + public class FreeMemoryDistribution : ActiveSource{ + + // Empty function declaration. Active counters are never logged through functions + // Present here for further usage with Phoenix codegen + // [ActiveCounter] + void TraceFreeBlock(UIntPtr BlockCount, UIntPtr TotalBlocksSize){} + + // Here should start the codegen's work. Do it by hand for now + + public struct tmpFreeBlockEntry{ + + public UIntPtr BlockCount; + public UIntPtr TotalBucketSize; + } + + internal tmpFreeBlockEntry[] Buffer; + + public static FreeMemoryDistribution Create(string sourceName, int size) { + + FreeMemoryDistribution Logger = new FreeMemoryDistribution(sourceName, size, ENABLE_ALL_MASK); + + if (Logger != null) { + + Logger.Register(); + } + + return Logger; + } + + public override bool Register() { + + Buffer = new tmpFreeBlockEntry[Count]; + + if (Buffer == null) { + return false; + } + + if (HostController.RegisterEvent("MemoryFreeBucket", + "Memory free buckets: Count = {0}", + ref EventTypeHandle)) { + + HostController.RegisterEventField(EventTypeHandle, + "BlockCount", + 0, + DataType.__UIntPtr); + + HostController.RegisterEventField(EventTypeHandle, + "TotalBucketSize", + 0, + DataType.__UIntPtr); + } else { + + // The event might have been registered already + // Check whether we foundit already in the table or not + + if (EventTypeHandle == 0) { + return false; + } + } + + unsafe { + + fixed (void * ptr = &Buffer[0]) { + + DebugBufferAddress = (UIntPtr)ptr; + } + } + + // After all internal fields are setup, we can go ahead and register with the controller + + if (!base.Register()) { + + return false; + } + + return true; + } + + public tmpFreeBlockEntry this[int index] + { + get { + return Buffer[index]; + } + } + + public override unsafe bool GetActiveEntry(int index, + UIntPtr * type, + byte * buffer, + UInt16 bufferSize ) + { + if (!base.GetActiveEntry(index, type, buffer, bufferSize)) { + return false; + } + + *(tmpFreeBlockEntry *)buffer = Buffer[index]; + return true; + } + + FreeMemoryDistribution(string sourceName, int size, uint controlFlags) + :base(sourceName, size, controlFlags) + { + unsafe { + EntrySize = (ushort)sizeof(tmpFreeBlockEntry); + } + } + } +} + + diff --git a/base/Kernel/Singularity/Memory/MemoryManager.cs b/base/Kernel/Singularity/Memory/MemoryManager.cs index 51090a7..7d542ed 100644 --- a/base/Kernel/Singularity/Memory/MemoryManager.cs +++ b/base/Kernel/Singularity/Memory/MemoryManager.cs @@ -9,7 +9,6 @@ // Note: // - using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -17,18 +16,20 @@ using System.Threading; using System.GCs; using Microsoft.Singularity; +using Microsoft.Singularity.Hal; +using Microsoft.Bartok.Runtime; namespace Microsoft.Singularity.Memory { [NoCCtor] [CLSCompliant(false)] + [AccessedByRuntime("referenced from halkd.cpp")] public class MemoryManager { ///////////////////////////////////// // STATIC FIELDS ///////////////////////////////////// -#if PAGING private static PhysicalAddress IOMemoryBaseAddr; private static PhysicalHeap KernelIOMemoryHeap; private static VirtualMemoryRange_struct KernelRange; @@ -36,7 +37,13 @@ namespace Microsoft.Singularity.Memory [AccessedByRuntime("referenced from halkd.cpp")] private static bool isInitialized; -#endif + + private static bool useAddressTranslation; + + public static bool UseAddressTranslation + { + get { return useAddressTranslation; } + } ///////////////////////////////////// // CONSTANTS @@ -68,6 +75,45 @@ namespace Microsoft.Singularity.Memory internal const uint KernelPageImage = KernelPage + (uint)PageType.System; internal const uint KernelPageStack = KernelPage + (uint)PageType.Stack; + // + // we must make a determination of the "useAddressTranslation" arguments existence + // before memory management is setup -- so do this rudely using the buffer in Platform. + // + // If we find it in the cmdline then we actually need to remove it before continuing + // or the shell will eventually attempt to execute it as a command. + // + [NoHeapAllocation] + private unsafe static bool UseAddressTranslationInCmdLine() + { + const string uatKey = "/useAddressTranslation"; + char* cmdPtr = Platform.ThePlatform.CommandLine; + + fixed (char* keyPtr = uatKey) { + if (cmdPtr != null) { + while (*cmdPtr != 0) { + char* cp = cmdPtr; + char* kp = keyPtr; + while (*cp != 0 && *kp != 0) { + if (*cp != *kp) + break; + ++cp; ++kp; + } + if (*kp == 0) { + while (*cp != 0) { + *cmdPtr++ = *cp++; + } + *cmdPtr = '\0'; + return true; + } + else { + ++cmdPtr; + } + } + } + } + return false; + } + ///////////////////////////////////// // PUBLIC METHODS ///////////////////////////////////// @@ -76,86 +122,122 @@ namespace Microsoft.Singularity.Memory { DebugStub.WriteLine("Initializing memory subsystem..."); -#if PAGING - // Set up the hardware-pages table and reserve a range for - // I/O memory - IOMemoryBaseAddr = PhysicalPages.Initialize(BootInfo.IO_MEMORY_SIZE); - - // Set up the I/O memory heap - KernelIOMemoryHeap = new PhysicalHeap((UIntPtr)IOMemoryBaseAddr.Value, - (UIntPtr)(IOMemoryBaseAddr.Value + BootInfo.IO_MEMORY_SIZE)); - - // Set up virtual memory. ** This enables paging ** ! - VMManager.Initialize(); - - // Set up the kernel's memory ranges. - // - // The kernel's general-purpose range is special because - // it *describes* low memory as well as the GC range proper - // so the kernel's GC doesn't get confused by pointers to - // static data in the kernel image. - KernelRange = new VirtualMemoryRange_struct( - VMManager.KernelHeapBase, - VMManager.KernelHeapLimit, - UIntPtr.Zero, - VMManager.KernelHeapLimit, - null); // no concurrent access to page descriptors yet - - // Mark the kernel's special areas - KernelRange.SetPages(0x0, BootInfo.KERNEL_STACK_BEGIN, KernelPageImage); - KernelRange.SetPages(BootInfo.GetBootInfo().DumpBase, - BootInfo.GetBootInfo().DumpLimit, - MemoryManager.KernelPageNonGC); - KernelRange.SetPages(BootInfo.KERNEL_STACK_BEGIN, BootInfo.KERNEL_STACK_LIMIT, - MemoryManager.KernelPageStack); - - DebugStub.WriteLine("MemoryManager initialized with {0} physical pages still free", - __arglist(PhysicalPages.GetFreePageCount())); - - isInitialized = true; + // Only allow paging in HALs which support running in Ring 0 + // but always force paging in the HIP builds for compatibility +#if !PAGING + useAddressTranslation = UseAddressTranslationInCmdLine(); #else - FlatPages.Initialize(); -#endif // PAGING + useAddressTranslation = true; +#endif + + if (useAddressTranslation) { + DebugStub.WriteLine("Using address translation...\n"); + Platform p = Platform.ThePlatform; + + // Set up the hardware-pages table and reserve a range for + // I/O memory + + IOMemoryBaseAddr = PhysicalPages.Initialize(Platform.IO_MEMORY_SIZE); + + // Set up the I/O memory heap + KernelIOMemoryHeap = new PhysicalHeap((UIntPtr)IOMemoryBaseAddr.Value, + (UIntPtr)(IOMemoryBaseAddr.Value + Platform.IO_MEMORY_SIZE)); + + // Set up virtual memory. ** This enables paging ** ! + VMManager.Initialize(); + + // Set up the kernel's memory ranges. + // + // The kernel's general-purpose range is special because + // it *describes* low memory as well as the GC range proper + // so the kernel's GC doesn't get confused by pointers to + // static data in the kernel image. + KernelRange = new VirtualMemoryRange_struct( + VMManager.KernelHeapBase, + VMManager.KernelHeapLimit, + UIntPtr.Zero, + VMManager.KernelHeapLimit, + null); // no concurrent access to page descriptors yet + + // Mark the kernel's special areas. First, record the kernel memory. + if (p.KernelDllSize != 0) { + UIntPtr kernelDllLimit = p.KernelDllBase + p.KernelDllSize; + + KernelRange.SetRange(p.KernelDllBase, kernelDllLimit, + MemoryManager.KernelPageNonGC); + } + + // Record the boot allocated kernel memory. + if (p.BootAllocatedMemorySize != 0) { + UIntPtr bootAllocatedMemoryLimit = p.BootAllocatedMemory + p.BootAllocatedMemorySize; + + KernelRange.SetRange(p.BootAllocatedMemory, bootAllocatedMemoryLimit, + MemoryManager.KernelPageNonGC); + } + + // Set stack page for CPU 0 + KernelRange.SetRange(Platform.BootCpu.KernelStackLimit, + (Platform.BootCpu.KernelStackBegin - Platform.BootCpu.KernelStackLimit), + MemoryManager.KernelPageStack); + + DebugStub.WriteLine("MemoryManager initialized with {0} physical pages still free", + __arglist(PhysicalPages.GetFreePageCount())); + KernelRange.Dump("Initialized"); + + isInitialized = true; + } + else { + FlatPages.Initialize(); + DebugStub.WriteLine("KernelBaseAddr: {0:x8} KernelLimitAddr {1:x8}", + __arglist(KernelBaseAddr, + KernelBaseAddr + BytesFromPages(KernelPageCount))); + } } internal static void Finalize() { -#if PAGING - VMManager.Finalize(); -#endif // PAGING + if (useAddressTranslation) { + VMManager.Finalize(); + } } internal static unsafe void PostGCInitialize() { -#if PAGING - VMManager.PostGCInitialize(); + if (useAddressTranslation) { + VMManager.PostGCInitialize(); - // Create the wrapper for the kernel range. The fixed - // statement is safe since KernelRange is a static - // struct. - fixed (VirtualMemoryRange_struct* pKernelRange = &KernelRange) { - KernelRangeWrapper = new VirtualMemoryRange(pKernelRange, - ProtectionDomain.DefaultDomain); + // Create the wrapper for the kernel range. The fixed + // statement is safe since KernelRange is a static + // struct. + fixed (VirtualMemoryRange_struct* pKernelRange = &KernelRange) + { + KernelRangeWrapper = new VirtualMemoryRange(pKernelRange, + ProtectionDomain.DefaultDomain); + } } -#endif } - // haryadi - internal static void InitializeProcessorAddressSpace() + /// + /// SetRange - provide access to the GC PageTable + /// + /// Beginning address + /// Number of bytes + /// PageType with which to mark + [NoHeapAllocation] + internal static void SetRange(UIntPtr start, UIntPtr bytes, uint tag) { -#if PAGING - // non-implemented yet for paging -#else - FlatPages.InitializeProcessorAddressSpace(); -#endif + if (useAddressTranslation) { + KernelRange.SetRange(start, bytes, tag); + } + else { + FlatPages.SetRange(start, bytes, tag); + } } - ///////////////////////////////////// // PUBLIC PAGING-SPECIFIC METHODS ///////////////////////////////////// -#if PAGING internal static unsafe VirtualMemoryRange GetKernelRange() { DebugStub.Assert(KernelRangeWrapper != null); @@ -165,7 +247,6 @@ namespace Microsoft.Singularity.Memory // // Get a new physical page and map it to the provided virtual address // - // private static bool CommitAndMapNewPage(UIntPtr virtualAddr, ProtectionDomain inDomain) { @@ -243,7 +324,7 @@ namespace Microsoft.Singularity.Memory ///////////////////////////////////// - // PAGING-ENABLED KERNEL MEMORY OPERATIONS + // KERNEL MEMORY OPERATIONS ///////////////////////////////////// internal static PhysicalAddress IOMemoryBase { @@ -252,20 +333,90 @@ namespace Microsoft.Singularity.Memory } } + private static bool InRange(UIntPtr value, UIntPtr lower, UIntPtr max) + { + return value >= lower && value <= max; + } + + // + // Calculate the size of physical memory from the boottime + // information -- SHOULD ONLY BE CALLED FROM INITIALIZE + // + internal unsafe static void PhysicalMemoryLimits(out UIntPtr lowerAddress, + out UIntPtr upperAddress) + { + Platform p = Platform.ThePlatform; + uint i; + + int cpuId = p.ApicId; + uint j; + DebugStub.WriteLine("MemoryManager.Initialize: Memory Map: Apic ID {0}\n", + __arglist(cpuId)); + + UIntPtr baseSmapAddress = UIntPtr.MaxValue; + UIntPtr maxSmapAddress = UIntPtr.Zero; + + // First pass over SMAP, find the highest RAM address + unsafe { + SMAPINFO* smap = (SMAPINFO*)p.Smap; + for (i = 0; i < p.SmapCount; i++) { + uint smapType = (uint)smap[i].type; + if (smapType != SMAPINFO.AddressTypeFree && + smapType != SMAPINFO.AddressTypeKernelStack) { + continue; + } + + if (smap[i].addr < baseSmapAddress) { + baseSmapAddress = smap[i].addr; + } + if ((smap[i].addr + smap[i].size) > maxSmapAddress) { + maxSmapAddress = smap[i].addr + smap[i].size; + } + + DebugStub.WriteLine("{0}: {1,8:x8} - {2,8:x8}, type={3,8:x8}", + __arglist(i, smap[i].addr, smap[i].addr + smap[i].size, smap[i].type)); + + unchecked { + Tracing.Log(Tracing.Debug, + " [{0,8:x8}..{1,8:x8}] = {2,8:x8}", + (UIntPtr)(uint)smap[i].addr, + (UIntPtr)(uint)(smap[i].addr + smap[i].size), + (UIntPtr)(uint)smap[i].type); + } + } + } + + lowerAddress = baseSmapAddress; + upperAddress = maxSmapAddress; + + // Verify kernel and boot memory are corretly reporting within the SMAP range + // it is a hal error to configure this otherwise. + + //DebugStub.Assert(p.KernelDllBase + p.KernelDllSize <= upperAddress); + //DebugStub.Assert(p.BootAllocatedMemory + p.BootAllocatedMemorySize <= upperAddress); + } + [AccessedByRuntime("referenced from halkd.cpp")] internal static UIntPtr KernelMapPhysicalMemory(PhysicalAddress physStart, UIntPtr numBytes) { - UIntPtr permaLoc = VMManager.TranslatePhysicalRange(physStart, numBytes); + if (useAddressTranslation) { + UIntPtr permaLoc = VMManager.TranslatePhysicalRange(physStart, numBytes); - if (permaLoc != UIntPtr.Zero) { - // This location has a permanent mapping - return permaLoc; - } else { - // This location must be mapped on the fly - return VMManager.MapPhysicalMemory(KernelRangeWrapper, - Process.kernelProcess, - physStart, numBytes); + if (permaLoc != UIntPtr.Zero) { + // This location has a permanent mapping + return permaLoc; + } + else { + // This location must be mapped on the fly + return VMManager.MapPhysicalMemory(KernelRangeWrapper, + Process.kernelProcess, + physStart, numBytes); + } + } + else { + // identity map without paging + return unchecked((UIntPtr)physStart.Value); } } @@ -273,51 +424,155 @@ namespace Microsoft.Singularity.Memory internal static void KernelUnmapPhysicalMemory(UIntPtr startAddr, UIntPtr limitAddr) { - if (VMManager.IsPermaMapped(startAddr, limitAddr)) { - return; // nothing to do - } else { - VMManager.UnmapPhysicalMemory(KernelRangeWrapper, - Process.kernelProcess, - startAddr, limitAddr); + if (useAddressTranslation) { + if (VMManager.IsPermaMapped(startAddr, limitAddr)) { + return; // nothing to do + } + else { + VMManager.UnmapPhysicalMemory(KernelRangeWrapper, + Process.kernelProcess, + startAddr, limitAddr); + } + } + // else do nothing + } + + // + // This is called to allocate memory for a stack, either an initial stack + // or a dynamically allocated stack chunk. + // + internal static UIntPtr StackAllocate(UIntPtr numPages, Process process, + uint extra, bool kernelAllocation, bool initialStack) + { + UIntPtr result = UIntPtr.Zero; + + if (useAddressTranslation) { + if (KernelRangeWrapper != null) { + result = KernelRangeWrapper.Allocate(numPages, process, extra, PageType.Stack); + } + else { + // Very early in the initialization sequence; ASSUME there is not + // yet any concurrent access to paging descriptors, and allocate + // memory without a paging-descriptor lock. + result = KernelRange.Allocate(numPages, process, extra, PageType.Stack, null); + } + } + else { + result = FlatPages.StackAllocate(BytesFromPages(numPages), + UIntPtr.Zero, + MemoryManager.PageSize, + process, extra, kernelAllocation, initialStack); + } + + if (kernelAllocation && result == UIntPtr.Zero) { + DebugStub.WriteLine("******** Kernel OOM on Stack ********"); + + // + // Our kernel runtime can not handle this right now, so rather than + // return a null which will show up as a cryptic lab failure, always + // drop to the debugger. + // + // Note: Reservations should avoid this, so this is an indication that + // something has gone wrong in our reservation policy and estimates + // of kernel stack usage. + // + DebugStub.Break(); + } + + return result; + } + + internal static void StackFree(UIntPtr startAddr, UIntPtr numPages, Process process, bool kernelAllocation, bool initialStack) + { + if (useAddressTranslation) { + KernelRange.Free(startAddr, numPages, process); + } + else { + FlatPages.StackFree(startAddr, MemoryManager.BytesFromPages(numPages), process, kernelAllocation, initialStack); } } internal static UIntPtr KernelAllocate(UIntPtr numPages, Process process, - uint extra, PageType type) + uint extra, PageType type) { - // - if (KernelRangeWrapper != null) { - return KernelRangeWrapper.Allocate(numPages, process, extra, type); - } else { - // Very early in the initialization sequence; ASSUME there is not - // yet any concurrent access to paging descriptors, and allocate - // memory without a paging-descriptor lock. - return KernelRange.Allocate(numPages, process, extra, type, null); + UIntPtr result = UIntPtr.Zero; + + if (useAddressTranslation) { + if (KernelRangeWrapper != null) { + result = KernelRangeWrapper.Allocate(numPages, process, extra, type); + } + else { + // Very early in the initialization sequence; ASSUME there is not + // yet any concurrent access to paging descriptors, and allocate + // memory without a paging-descriptor lock. + result = KernelRange.Allocate(numPages, process, extra, type, null); + } } + else { + result = FlatPages.Allocate(BytesFromPages(numPages), + UIntPtr.Zero, + MemoryManager.PageSize, + process, extra, type); + } + + if (result == UIntPtr.Zero) { + DebugStub.WriteLine("******** Kernel OOM on Heap ********"); + + // + // Our kernel runtime can not handle this right now, so rather than + // return a null which will show up as a cryptic lab failure, always + // drop to the debugger. + // + DebugStub.Break(); + } + + return result; } internal static UIntPtr KernelExtend(UIntPtr addr, UIntPtr numPages, Process process, PageType type) { - // TODO: Extend not yet implemented - DebugStub.Break(); - return UIntPtr.Zero; + // + // We do not report failure here since callers will default to + // KernelAllocate and copy if it can't extend the range + // + if (useAddressTranslation) { + // TODO: Extend not yet implemented + DebugStub.Break(); + return UIntPtr.Zero; + } + else { + return FlatPages.AllocateExtend(addr, BytesFromPages(numPages), process, 0, type); + } } internal static PageType KernelQuery(UIntPtr startAddr, out UIntPtr regionAddr, out UIntPtr regionSize) { - // TODO: Query not yet implemented - DebugStub.Break(); - regionAddr = UIntPtr.Zero; - regionSize = UIntPtr.Zero; - return PageType.Unknown; + if (useAddressTranslation) { + // TODO: Query not yet implemented + DebugStub.Break(); + regionAddr = UIntPtr.Zero; + regionSize = UIntPtr.Zero; + return PageType.Unknown; + } + else { + return FlatPages.Query(startAddr, Process.kernelProcess, out regionAddr, + out regionSize); + } } + [NoStackLinkCheckTrans] + [NoStackOverflowCheck] internal static void KernelFree(UIntPtr startAddr, UIntPtr numPages, Process process) { - KernelRange.Free(startAddr, numPages, process); + if (useAddressTranslation) { + KernelRange.Free(startAddr, numPages, process); + } + else { + FlatPages.Free(startAddr, MemoryManager.BytesFromPages(numPages), process); + } } internal static UIntPtr AllocateIOMemory(UIntPtr limitAddr, @@ -325,58 +580,169 @@ namespace Microsoft.Singularity.Memory UIntPtr alignment, Process process) { - return KernelIOMemoryHeap.Allocate(limitAddr, bytes, alignment, process); + UIntPtr result = UIntPtr.Zero; + + if (useAddressTranslation) { + result = KernelIOMemoryHeap.Allocate(limitAddr, bytes, alignment, process); + } + else { + if (limitAddr > 0) { + result = FlatPages.AllocateBelow(limitAddr, bytes, alignment, process, 0, PageType.NonGC); + } + else { + result = FlatPages.Allocate(bytes, bytes, alignment, process, 0, PageType.NonGC); + } + } + + if (result == UIntPtr.Zero) { + DebugStub.WriteLine("******** Kernel OOM on IoMemory ********"); + + // + // Our kernel runtime can not handle this right now, so rather than + // return a null which will show up as a cryptic lab failure, always + // drop to the debugger. + // + DebugStub.Break(); + } + + return result; } internal static void FreeIOMemory(UIntPtr addr, UIntPtr size, Process process) { - KernelIOMemoryHeap.Free(addr, size, process); + if (useAddressTranslation) { + KernelIOMemoryHeap.Free(addr, size, process); + } + else { + FlatPages.Free(addr, size, process); + } } internal static UIntPtr KernelBaseAddr { get { - return KernelRange.BaseAddress; + return useAddressTranslation ? KernelRange.BaseAddress : UIntPtr.Zero; } } internal static unsafe uint* KernelPageTable { get { - return KernelRange.PageTable; + return useAddressTranslation ? KernelRange.PageTable : FlatPages.PageTable; } } internal static UIntPtr KernelPageCount { get { - return KernelRange.PageCount; + return useAddressTranslation ? KernelRange.PageCount : FlatPages.PageCount; } } + // + // This returns total (kernel + SIP) memory usage information + // + internal static void GetUsageStatistics(out ulong allocatedCount, + out ulong allocatedBytes, + out ulong freedCount, + out ulong freedBytes, + out ulong kernelHeapReservation) + { + if (useAddressTranslation) { + ProtectionDomain.CurrentDomain.UserRange.GetUsageStatistics( + out allocatedCount, + out allocatedBytes, + out freedCount, + out freedBytes); + + kernelHeapReservation = 0; + } + else { + MemoryReservations.GetUsageStatistics(out allocatedCount, out allocatedBytes, + out freedCount, out freedBytes, out kernelHeapReservation); + } + } + + // + // Return stack usage as raw counters + // + [NoHeapAllocation] + internal static void GetStackUsage( + out ulong kernelStackCount, + out ulong kernelStackReturnCount, + out ulong kernelStackBytes, + out ulong kernelStackReturnBytes, + out ulong kernelStackReservation, + out ulong userStackCount, + out ulong userStackReturnCount, + out ulong userStackBytes, + out ulong userStackReturnBytes, + out ulong userStackReservation + ) + { + MemoryReservations.GetStackUsage( + out kernelStackCount, + out kernelStackReturnCount, + out kernelStackBytes, + out kernelStackReturnBytes, + out kernelStackReservation, + out userStackCount, + out userStackReturnCount, + out userStackBytes, + out userStackReturnBytes, + out userStackReservation + ); + } + ///////////////////////////////////// - // PAGING-ENABLED USER MEMORY OPERATIONS + // USER MEMORY OPERATIONS ///////////////////////////////////// internal static UIntPtr UserBaseAddr { get { - return ProtectionDomain.CurrentDomain.UserRange.BaseAddress; +#if false + return useAddressTranslation + ? ProtectionDomain.CurrentDomain.UserRange.BaseAddress + : UIntPtr.Zero; +#endif + return useAddressTranslation + ? ProtectionDomain.CurrentDomain.UserRange.BaseAddress + : UIntPtr.Zero; + } } internal static UIntPtr UserMapPhysicalMemory(PhysicalAddress physStart, UIntPtr numBytes) { - return VMManager.MapPhysicalMemory(ProtectionDomain.CurrentDomain.UserRange, - Thread.CurrentProcess, physStart, numBytes); + return useAddressTranslation + ? VMManager.MapPhysicalMemory(ProtectionDomain.CurrentDomain.UserRange, + Thread.CurrentProcess, physStart, numBytes) + : unchecked((UIntPtr)physStart.Value); } internal static void UserUnmapPhysicalMemory(UIntPtr startAddr, UIntPtr limitAddr) { - VMManager.UnmapPhysicalMemory(ProtectionDomain.CurrentDomain.UserRange, - Thread.CurrentProcess, startAddr, limitAddr); + if (useAddressTranslation) { + VMManager.UnmapPhysicalMemory(ProtectionDomain.CurrentDomain.UserRange, + Thread.CurrentProcess, startAddr, limitAddr); + } + } + + // + // This is invoked on SIP OOM to "fail-fast" the SIP + // + internal static void UserMemoryFailure() + { + DebugStub.WriteLine("******** SIP OOM on Heap, Failing Fast ********"); + + // This does not make a stack transition record + Thread.CurrentProcess.Stop((int)System.ProcessExitCode.ErrorDefault); + + // Should not return + DebugStub.Break(); } internal static UIntPtr UserAllocate(UIntPtr numPages, @@ -384,70 +750,125 @@ namespace Microsoft.Singularity.Memory uint extra, PageType type) { - return ProtectionDomain.CurrentDomain.UserRange.Allocate( - numPages, process, extra, type); + UIntPtr result = UIntPtr.Zero; + + if (useAddressTranslation) { + result = ProtectionDomain.CurrentDomain.UserRange.Allocate( + numPages, process, extra, type); + } + else { + result = FlatPages.Allocate(BytesFromPages(numPages), + UIntPtr.Zero, + MemoryManager.PageSize, + process, extra, type); + } + + if (result == UIntPtr.Zero) { + UserMemoryFailure(); + } + + return result; } internal static UIntPtr UserExtend(UIntPtr addr, UIntPtr numPages, Process process, PageType type) { - // TODO: Extend not yet implemented - DebugStub.Break(); - return UIntPtr.Zero; + UIntPtr result = UIntPtr.Zero; + + if (useAddressTranslation) { + // TODO: Extend NYI + DebugStub.Break(); + result = UIntPtr.Zero; + } + else { + result = FlatPages.AllocateExtend(addr, BytesFromPages(numPages), process, 0, type); + } + + if (result == UIntPtr.Zero) { + UserMemoryFailure(); + } + + return result; } internal static void UserFree(UIntPtr addr, UIntPtr numPages, Process process) { - ProtectionDomain.CurrentDomain.UserRange.Free(addr, numPages, process); + if (useAddressTranslation) { + ProtectionDomain.CurrentDomain.UserRange.Free(addr, numPages, process); + } + else { + FlatPages.Free(addr, BytesFromPages(numPages), process); + } } internal static PageType UserQuery(UIntPtr startAddr, out UIntPtr regionAddr, out UIntPtr regionSize) { - // TODO: Query not yet implemented - DebugStub.Break(); - regionAddr = UIntPtr.Zero; - regionSize = UIntPtr.Zero; - return PageType.Unknown; + if (useAddressTranslation) { + // TODO: Query NYI + DebugStub.Break(); + regionAddr = UIntPtr.Zero; + regionSize = UIntPtr.Zero; + return PageType.Unknown; + } + else { + return FlatPages.Query(startAddr, Thread.CurrentProcess, + out regionAddr, out regionSize); + } } internal static UIntPtr FreeProcessMemory(Process process) { - return ProtectionDomain.CurrentDomain.UserRange.FreeAll(process); + if (useAddressTranslation) { + return ProtectionDomain.CurrentDomain.UserRange.FreeAll(process); + } + else { + return FlatPages.FreeAll(process); + } } internal static unsafe uint* UserPageTable { get { - return ProtectionDomain.CurrentDomain.UserRange.PageTable; + return useAddressTranslation + ? ProtectionDomain.CurrentDomain.UserRange.PageTable + : FlatPages.PageTable; } } internal static UIntPtr UserPageCount { get { - return ProtectionDomain.CurrentDomain.UserRange.PageCount; + return useAddressTranslation + ? ProtectionDomain.CurrentDomain.UserRange.PageCount + : FlatPages.PageCount; } } ///////////////////////////////////// - // PAGING-ENABLED DIAGNOSTICS + // DIAGNOSTICS ///////////////////////////////////// public static ulong GetFreePhysicalMemory() { - return PhysicalPages.GetFreeMemory(); + return useAddressTranslation + ? PhysicalPages.GetFreeMemory() + : (ulong)FlatPages.GetFreeMemory(); } public static ulong GetUsedPhysicalMemory() { - return PhysicalPages.GetUsedMemory(); + return useAddressTranslation + ? PhysicalPages.GetUsedMemory() + : (ulong)FlatPages.GetUsedMemory(); } public static ulong GetMaxPhysicalMemory() { - return PhysicalPages.GetMaxMemory(); + return useAddressTranslation + ? PhysicalPages.GetMaxMemory() + : (ulong)FlatPages.GetMaxMemory(); } internal static void GetUserStatistics(out ulong allocatedCount, @@ -455,198 +876,19 @@ namespace Microsoft.Singularity.Memory out ulong freedCount, out ulong freedBytes) { - ProtectionDomain.CurrentDomain.UserRange.GetUsageStatistics( - out allocatedCount, - out allocatedBytes, - out freedCount, - out freedBytes); - } - -#else // PAGING - - ///////////////////////////////////// - // NO-PAGING KERNEL MEMORY OPERATIONS - ///////////////////////////////////// - - internal static UIntPtr KernelMapPhysicalMemory(PhysicalAddress physStart, - UIntPtr numBytes) - { - // identity map without paging - return unchecked((UIntPtr)physStart.Value); - } - - internal static void KernelUnmapPhysicalMemory(UIntPtr startAddr, - UIntPtr limitAddr) - { - // do nothing - } - - internal static UIntPtr KernelAllocate(UIntPtr numPages, Process process, - uint extra, PageType type) - { - return FlatPages.Allocate(BytesFromPages(numPages), - UIntPtr.Zero, - MemoryManager.PageSize, - process, extra, type); - } - - internal static UIntPtr KernelExtend(UIntPtr addr, UIntPtr numPages, Process process, - PageType type) - { - return FlatPages.AllocateExtend(addr, BytesFromPages(numPages), process, 0, type); - } - - internal static void KernelFree(UIntPtr startAddr, UIntPtr numPages, Process process) - { - FlatPages.Free(startAddr, MemoryManager.BytesFromPages(numPages), process); - } - - internal static PageType KernelQuery(UIntPtr startAddr, out UIntPtr regionAddr, - out UIntPtr regionSize) - { - return FlatPages.Query(startAddr, Process.kernelProcess, out regionAddr, - out regionSize); - } - - internal static UIntPtr AllocateIOMemory(UIntPtr limitAddr, - UIntPtr bytes, - UIntPtr alignment, - Process process) - { - if (limitAddr > 0) { - return FlatPages.AllocateBelow(limitAddr, bytes, alignment, process, 0, PageType.NonGC); - } else { - return FlatPages.Allocate(bytes, bytes, alignment, process, 0, PageType.NonGC); + if (useAddressTranslation) { + ProtectionDomain.CurrentDomain.UserRange.GetUsageStatistics( + out allocatedCount, + out allocatedBytes, + out freedCount, + out freedBytes); + } + else { + MemoryReservations.GetUserStatistics(out allocatedCount, out allocatedBytes, + out freedCount, out freedBytes); } } - internal static void FreeIOMemory(UIntPtr addr, UIntPtr size, Process process) - { - FlatPages.Free(addr, size, process); - } - - internal static UIntPtr KernelBaseAddr - { - get { - return UIntPtr.Zero; - } - } - - internal static unsafe uint* KernelPageTable - { - get { - return FlatPages.PageTable; - } - } - - internal static UIntPtr KernelPageCount - { - get { - return FlatPages.PageCount; - } - } - - ///////////////////////////////////// - // NO-PAGING USER MEMORY OPERATIONS - ///////////////////////////////////// - - internal static UIntPtr UserBaseAddr - { - get { - return UIntPtr.Zero; - } - } - - internal static UIntPtr UserMapPhysicalMemory(PhysicalAddress physStart, - UIntPtr numBytes) - { - return unchecked((UIntPtr)physStart.Value); - } - - internal static void UserUnmapPhysicalMemory(UIntPtr startAddr, - UIntPtr limitAddr) - { - // do nothing - } - - internal static UIntPtr UserAllocate(UIntPtr numPages, - Process process, - uint extra, - PageType type) - { - return FlatPages.Allocate(BytesFromPages(numPages), - UIntPtr.Zero, - MemoryManager.PageSize, - process, extra, type); - } - - internal static UIntPtr UserExtend(UIntPtr addr, UIntPtr numPages, Process process, - PageType type) - { - return FlatPages.AllocateExtend(addr, BytesFromPages(numPages), process, 0, type); - } - - internal static void UserFree(UIntPtr addr, UIntPtr numPages, Process process) - { - FlatPages.Free(addr, BytesFromPages(numPages), process); - } - - internal static PageType UserQuery(UIntPtr startAddr, out UIntPtr regionAddr, - out UIntPtr regionSize) - { - return FlatPages.Query(startAddr, Thread.CurrentProcess, - out regionAddr, out regionSize); - } - - internal static UIntPtr FreeProcessMemory(Process process) - { - return FlatPages.FreeAll(process); - } - - internal static unsafe uint* UserPageTable - { - get { - return FlatPages.PageTable; - } - } - - internal static UIntPtr UserPageCount - { - get { - return FlatPages.PageCount; - } - } - - ///////////////////////////////////// - // NO-PAGING DIAGNOSTICS - ///////////////////////////////////// - - public static ulong GetFreePhysicalMemory() - { - return (ulong)FlatPages.GetFreeMemory(); - } - - public static ulong GetUsedPhysicalMemory() - { - return (ulong)FlatPages.GetUsedMemory(); - } - - public static ulong GetMaxPhysicalMemory() - { - return (ulong)FlatPages.GetMaxMemory(); - } - - internal static void GetUserStatistics(out ulong allocatedCount, - out ulong allocatedBytes, - out ulong freedCount, - out ulong freedBytes) - { - FlatPages.GetUsageStatistics(out allocatedCount, out allocatedBytes, - out freedCount, out freedBytes); - } - -#endif - // Simpler overload internal static UIntPtr AllocateIOMemory(UIntPtr bytes, Process process) { @@ -658,89 +900,129 @@ namespace Microsoft.Singularity.Memory ///////////////////////////////////// [Inline] + [NoHeapAllocation] internal static ulong PagePad(ulong addr) { return ((addr + PageMask) & ~((ulong)PageMask)); } [Inline] - internal static UIntPtr PagePad(UIntPtr addr) { + [NoHeapAllocation] + internal static UIntPtr PagePad(UIntPtr addr) + { return ((addr + PageMask) & ~PageMask); } [Inline] - internal static ulong BytesNotAligned(ulong data, ulong size) { + [NoHeapAllocation] + internal static ulong BytesNotAligned(ulong data, ulong size) + { return ((data) & (size - 1)); } [Inline] - internal static UIntPtr BytesNotAligned(UIntPtr data, UIntPtr size) { + [NoHeapAllocation] + internal static UIntPtr BytesNotAligned(UIntPtr data, UIntPtr size) + { return ((data) & (size - 1)); } [Inline] - internal static UIntPtr Pad(UIntPtr data, UIntPtr align) { + [NoHeapAllocation] + internal static UIntPtr Pad(UIntPtr data, UIntPtr align) + { return ((data + align - 1) & ~(align - 1)); } [Inline] - internal static UIntPtr Trunc(UIntPtr addr, UIntPtr align) { + [NoHeapAllocation] + internal static UIntPtr Trunc(UIntPtr addr, UIntPtr align) + { return addr - BytesNotAligned(addr, align); } [Inline] - internal static ulong Trunc(ulong addr, ulong align) { + [NoHeapAllocation] + internal static ulong Trunc(ulong addr, ulong align) + { return addr - BytesNotAligned(addr, align); } [Inline] - internal static UIntPtr PageTrunc(UIntPtr addr) { + [NoHeapAllocation] + internal static UIntPtr PageTrunc(UIntPtr addr) + { return (addr & ~PageMask); } [Inline] - internal static UIntPtr PageFromAddr(UIntPtr addr) { + [NoHeapAllocation] + internal static UIntPtr Align(UIntPtr data, UIntPtr size) + { + return ((data) & ~(size - 1)); + } + + [Inline] + [NoHeapAllocation] + internal static UIntPtr PageFromAddr(UIntPtr addr) + { return (addr >> PageBits); } [Inline] - internal static UIntPtr AddrFromPage(UIntPtr pageIdx) { - return (pageIdx << PageBits); + [NoHeapAllocation] + internal static UIntPtr AddrFromPage(UIntPtr pageIdx) + { + return (pageIdx << PageBits); } [Inline] - internal static ulong PagesFromBytes(ulong size) { + [NoHeapAllocation] + internal static ulong PagesFromBytes(ulong size) + { return ((size + PageMask) >> PageBits); } [Inline] - internal static UIntPtr PagesFromBytes(UIntPtr size) { + [NoHeapAllocation] + internal static UIntPtr PagesFromBytes(UIntPtr size) + { return ((size + PageMask) >> PageBits); } [Inline] - internal static UIntPtr BytesFromPages(UIntPtr pages) { + [NoHeapAllocation] + internal static UIntPtr BytesFromPages(UIntPtr pages) + { return (UIntPtr)(pages << PageBits); } [Inline] - internal static UIntPtr BytesFromPages(ulong pages) { + [NoHeapAllocation] + internal static UIntPtr BytesFromPages(ulong pages) + { return (UIntPtr)(pages << PageBits); } [Inline] + [NoHeapAllocation] [AccessedByRuntime("referenced from halkd.cpp")] internal static UIntPtr PageAlign(UIntPtr addr) { return (addr & ~PageMask); } [Inline] - internal static bool IsPageAligned(UIntPtr addr) { + [NoHeapAllocation] + internal static bool IsPageAligned(UIntPtr addr) + { return ((addr & PageMask) == 0); } [Inline] - internal static bool IsPageAligned(ulong addr) { + [NoHeapAllocation] + internal static bool IsPageAligned(ulong addr) + { return ((addr & (ulong)PageMask) == 0); } } } + diff --git a/base/Kernel/Singularity/Memory/MemoryReservations.cs b/base/Kernel/Singularity/Memory/MemoryReservations.cs new file mode 100644 index 0000000..aaa0303 --- /dev/null +++ b/base/Kernel/Singularity/Memory/MemoryReservations.cs @@ -0,0 +1,430 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: MemoryReservations.cs - Reservations and tracking of memory for +// Heap, Stacks, SIP's, and Kernel. +// +// Note: +// + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.GCs; + +using Microsoft.Singularity; + +namespace Microsoft.Singularity.Memory +{ + [NoCCtor] + [CLSCompliant(false)] + public class MemoryReservations { + + // + // This class provides one place for memory reservation policy + // and is used by the different memory managers in the system + // (FlatPages, Paging allocator, etc.) + // + // Memory reservation policy is covered by SDN42 - Handling OutOfMemory + // + // Summary of the policy: + // + // We ensure there is enough memory for kernel stacks by implementing a per + // thread kernel stack reservation. We receive notifications of thread create + // and destroy so this reservation may be tracked in real time. + // + // A basic reservation is available for the kernel heap. + // + // Allocations for SIP's that exceed the reservations provided above, fail. + // + // The reservation policy ensures that we can reliably "fail-fast" the SIP + // without crashing the kernel due to OOM for stack or heap. + // + // The memory managers must live under some strict constraints in regards to + // allocations themselves, stack linking, no access to managed pointers that + // could mutate, etc. The methods in this class + // must adhere to these constraints. + // + + ///////////////////////////////////// + // CONSTANTS + ///////////////////////////////////// + + // Note: These should be passed in from memory manager during initialize() + private const ulong kernelStackReservationPerThread = (4096*4); // 16k + + private const ulong kernelStackInitialThreads = 10; + + // Set enough stack reservation for an initial 10 threads + private const ulong initialKernelStackReservation = kernelStackInitialThreads * kernelStackReservationPerThread; + + private const ulong initialKernelHeapReservation = 1024*512; + + private const ulong initialUserStackReservation = 1024*128; + + + ///////////////////////////////////// + // STATIC FIELDS + ///////////////////////////////////// + + // + // These counters serve two purposes: + // + // 1: Provide resource usage monitoring information + // + // 2: Provide for memory reservations. In order to provide + // for memory reservations reliably in an MP environment, they + // are only manipulated while holding the pageLock. + // + + // + // This represents total memory allocated and freed + // + private static ulong allocatedBytes; + private static ulong allocatedCount; + private static ulong freedBytes; + private static ulong freedCount; + + // + // This represents the real time count of user (SIP) allocated + // memory + // + private static ulong userAllocatedBytes; + private static ulong userAllocatedCount; + private static ulong userFreedBytes; + private static ulong userFreedCount; + + // + // These represent kernel and SIP stack usage + // + private static ulong stackAllocatedBytes; + private static ulong stackAllocatedCount; + private static ulong stackFreedBytes; + private static ulong stackFreedCount; + + private static ulong userStackAllocatedBytes; + private static ulong userStackAllocatedCount; + private static ulong userStackFreedBytes; + private static ulong userStackFreedCount; + + // + // This value is updated as threads are created and destroyed + // and represents the amount that available memory can not + // drop below without possibly running into a kernel stack OOM. + // + private static ulong kernelStackReservation; + + // + // This represents a kernel heap reservation + // + private static ulong kernelHeapReservation; + + // + // This represents a reservation to allow SIP stacks + // to be allocated once SIP heap reservations have been + // exceeded, but not kernel stack+heap reservations. + // + // This allows a SIP to continue executing after a SIP + // allocations are failed to allow the SIP to exit and cleanup. + // + // (Ideally, we should not need to rely on allowing further SIP + // thread execution for cleanup, but this is a later stage + // ) + // + private static ulong userStackReservation; + + ///////////////////////////////////// + // PUBLIC METHODS + ///////////////////////////////////// + + [NoHeapAllocation] + [NoStackLinkCheck] + internal static void Initialize() + { + allocatedBytes = 0; + allocatedCount = 0; + freedBytes = 0; + freedCount = 0; + + userAllocatedBytes = 0; + userAllocatedCount = 0; + userFreedBytes = 0; + userFreedCount = 0; + + stackAllocatedBytes = 0; + stackAllocatedCount = 0; + stackFreedBytes = 0; + stackFreedCount = 0; + + userStackAllocatedBytes = 0; + userStackAllocatedCount = 0; + userStackFreedBytes = 0; + userStackFreedCount = 0; + + kernelStackReservation = initialKernelStackReservation; + kernelHeapReservation = initialKernelHeapReservation; + + userStackReservation = initialUserStackReservation; + + return; + } + + // + // This returns true if an allocation should fail based on the memory + // reservation policy and currently available memory. + // + // This is called under the lock of the calling memory manager to ensure + // a consistent view in a multithreaded environment. + // + [NoHeapAllocation] + [NoStackLinkCheck] + internal static bool MemoryReservationExceeded( + ulong availableMemory, + uint tag, + bool kernelAllocation, + UIntPtr size) + { + // + // The owner information is not reliable for determining kernel + // or SIP allocation when the page type is stack, so the logic + // is a little extra complicated here. + // + if ((tag & MemoryManager.TypeMask) == (uint)PageType.Stack) { + + // + // Stack pages use the kernelAllocation flag to determine + // whether the allocation is for the SIP or the kernel stack + // + if (kernelAllocation) { + return false; + } + else { + if (availableMemory < (kernelStackReservation + kernelHeapReservation + size)) { + //DebugStub.Break(); + //DebugStub.WriteLine("CheckMemoryReservation: Failing allocation request for SIP stack due to reservations\n"); + return true; + } + else { + return false; + } + } + } + else if ((tag & MemoryManager.ProcessPageMask) != MemoryManager.KernelPage) { + + // process tag is reliable for non-stack allocations + + // + // User heap allocation must be under any outstanding reservations + // for the kernel, or SIP stack. + // + if (availableMemory < (kernelStackReservation + kernelHeapReservation + userStackReservation + size)) { + //DebugStub.Break(); + //DebugStub.WriteLine("CheckMemoryReservation: Failing allocation request for SIP heap due to reservations\n"); + return true; + } + else { + return false; + } + } + else { + // Kernel heap allocation + return false; + } + } + + // + // These are called by the memory manager in order to keep an accurate view + // of memory reservations in real time. They are called under the memory + // managers lock. + // + [Inline] + [NoHeapAllocation] + [NoStackLinkCheck] + internal static void ThreadCreateNotification() + { + kernelStackReservation += kernelStackReservationPerThread; + } + + [Inline] + [NoHeapAllocation] + [NoStackLinkCheck] + internal static void ThreadDestroyNotification() + { + kernelStackReservation -= kernelStackReservationPerThread; + } + + // + // Allocation tracking + // + [Inline] + [NoHeapAllocation] + [NoStackLinkCheck] + internal static void AllocationForHeap(ulong size) + { + allocatedBytes += size; + allocatedCount++; + } + + [Inline] + [NoHeapAllocation] + [NoStackLinkCheck] + internal static void AllocationForStack(ulong size) + { + stackAllocatedBytes += size; + stackAllocatedCount++; + + allocatedBytes += size; + allocatedCount++; + } + + [Inline] + [NoHeapAllocation] + [NoStackLinkCheck] + internal static void SIPAllocationForHeap(ulong size) + { + userAllocatedBytes += size; + userAllocatedCount++; + + allocatedBytes += size; + allocatedCount++; + } + + [Inline] + [NoHeapAllocation] + [NoStackLinkCheck] + internal static void SIPAllocationForStack(ulong size) + { + userStackAllocatedBytes += size; + userStackAllocatedCount++; + + allocatedBytes += size; + allocatedCount++; + } + + // + // Free tracking + // + [Inline] + [NoHeapAllocation] + [NoStackLinkCheck] + internal static void FreeForHeap(ulong size) + { + freedBytes += size; + freedCount++; + } + + [Inline] + [NoHeapAllocation] + [NoStackLinkCheck] + internal static void FreeForStack(ulong size) + { + stackFreedBytes += size; + stackFreedCount++; + + freedBytes += size; + freedCount++; + } + + [Inline] + [NoHeapAllocation] + [NoStackLinkCheck] + internal static void SIPFreeForHeap(ulong size) + { + userFreedBytes += size; + userFreedCount++; + + freedBytes += size; + freedCount++; + } + + [Inline] + [NoHeapAllocation] + [NoStackLinkCheck] + internal static void SIPFreeForStack(ulong size) + { + userStackFreedBytes += size; + userStackFreedCount++; + + freedBytes += size; + freedCount++; + } + + // + // Statistics gathering methods + // + // Calling under a lock is not required unless you want an accurate view + // + [NoHeapAllocation] + [NoStackLinkCheck] + internal static void GetUsageStatistics(out ulong allocatedCount, + out ulong allocatedBytes, + out ulong freedCount, + out ulong freedBytes, + out ulong kernelHeapReservationArg) + { + allocatedCount = MemoryReservations.allocatedCount; + allocatedBytes = MemoryReservations.allocatedBytes; + freedCount = MemoryReservations.freedCount; + freedBytes = MemoryReservations.freedBytes; + kernelHeapReservationArg = MemoryReservations.kernelHeapReservation; + } + + // + // Return user (SIP) memory statistics + // + [NoHeapAllocation] + [NoStackLinkCheck] + internal static void GetUserStatistics(out ulong allocatedCountArg, + out ulong allocatedBytesArg, + out ulong freedCountArg, + out ulong freedBytesArg) + { + allocatedCountArg = userAllocatedCount; + allocatedBytesArg = userAllocatedBytes; + freedCountArg = userFreedCount; + freedBytesArg = userFreedBytes; + } + + // + // Return stack usage as raw counters + // + [NoHeapAllocation] + [NoStackLinkCheck] + internal static void GetStackUsage( + out ulong kernelStackCount, + out ulong kernelStackReturnCount, + out ulong kernelStackBytes, + out ulong kernelStackReturnBytes, + out ulong kernelStackReservationArg, + out ulong userStackCount, + out ulong userStackReturnCount, + out ulong userStackBytes, + out ulong userStackReturnBytes, + out ulong userStackReservation + ) + { + kernelStackCount = stackAllocatedCount; + kernelStackReturnCount = stackFreedCount; + + kernelStackBytes = stackAllocatedBytes; + kernelStackReturnBytes = stackFreedBytes; + + kernelStackReservationArg = kernelStackReservation; + + userStackCount = userStackAllocatedCount; + userStackReturnCount = userStackFreedCount; + + userStackBytes = userStackAllocatedBytes; + userStackReturnBytes = userStackFreedBytes; + + userStackReservation = 0; + + return; + } + } +} + diff --git a/base/Kernel/Singularity/Memory/PhysicalAddress.cs b/base/Kernel/Singularity/Memory/PhysicalAddress.cs index b0572ae..badf6f0 100644 --- a/base/Kernel/Singularity/Memory/PhysicalAddress.cs +++ b/base/Kernel/Singularity/Memory/PhysicalAddress.cs @@ -16,6 +16,7 @@ namespace Microsoft.Singularity.Memory { [NoCCtor] [CLSCompliant(false)] + [AccessedByRuntime("referenced from halkd.cpp")] public struct PhysicalAddress { // This is a processor detail. 52 is for x86-derived @@ -62,7 +63,8 @@ namespace Microsoft.Singularity.Memory { if (o is PhysicalAddress) { return this == (PhysicalAddress)o; - } else { + } + else { return false; } } diff --git a/base/Kernel/Singularity/Memory/PhysicalHeap.cs b/base/Kernel/Singularity/Memory/PhysicalHeap.cs index 2089429..ec75616 100644 --- a/base/Kernel/Singularity/Memory/PhysicalHeap.cs +++ b/base/Kernel/Singularity/Memory/PhysicalHeap.cs @@ -17,8 +17,6 @@ // memory that it is pointed to at initialization is always mapped. // -#if PAGING - using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -76,7 +74,7 @@ namespace Microsoft.Singularity.Memory // PUBLIC METHODS ///////////////////////////////////// - internal unsafe PhysicalHeap(UIntPtr start, UIntPtr limit) + public unsafe PhysicalHeap(UIntPtr start, UIntPtr limit) { DebugStub.Assert(MemoryManager.IsPageAligned(start)); DebugStub.Assert(MemoryManager.IsPageAligned(limit)); @@ -92,7 +90,7 @@ namespace Microsoft.Singularity.Memory startAddr = start + bytesForTable; heapLimit = limit; pageTable = (ushort*)start; - heapLock = new SpinLock(); + heapLock = new SpinLock(SpinLock.Types.PhysicalHeap); // The entire heap is free to start out with freeList = new FreeList(); @@ -109,17 +107,17 @@ namespace Microsoft.Singularity.Memory // We always hand out blocks consisting of complete // pages of memory. - [NoStackLinkCheck] - internal unsafe UIntPtr Allocate(UIntPtr bytes, Process process) + [NoStackLinkCheckTrans] + public unsafe UIntPtr Allocate(UIntPtr bytes, Process process) { return Allocate(0, bytes, 0, process); } - [NoStackLinkCheck] - internal unsafe UIntPtr Allocate(UIntPtr limitAddr, - UIntPtr bytes, - UIntPtr alignment, - Process process) + [NoStackLinkCheckTrans] + public unsafe UIntPtr Allocate(UIntPtr limitAddr, + UIntPtr bytes, + UIntPtr alignment, + Process process) { ushort tag = process != null ? (ushort)process.ProcessId : KernelPage; UIntPtr blockPtr; @@ -178,8 +176,8 @@ namespace Microsoft.Singularity.Memory return blockPtr; } - [NoStackLinkCheck] - internal unsafe void Free(UIntPtr addr, UIntPtr bytes, Process process) + [NoStackLinkCheckTrans] + public unsafe void Free(UIntPtr addr, UIntPtr bytes, Process process) { if (addr == UIntPtr.Zero) { // Silently accept freeing null @@ -276,7 +274,7 @@ namespace Microsoft.Singularity.Memory #if SELF_TEST UIntPtr freePagesByTable = 0; - for(UIntPtr i = 0; i < pageCount; i++) { + for (UIntPtr i = 0; i < pageCount; i++) { UIntPtr pageAddr = startAddr + (MemoryManager.PageSize * i); if (PageWord(i) == FreePage) { @@ -301,7 +299,8 @@ namespace Microsoft.Singularity.Memory i += numPages - 1; freePagesByTable += numPages; - } else { + } + else { // Single-page free block if (i != pageCount - 1) { DebugStub.Assert(PageWord(i + 1) != FreePage); @@ -317,7 +316,7 @@ namespace Microsoft.Singularity.Memory #endif } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] private unsafe void SetPages(UIntPtr addr, UIntPtr pageCount, ushort tag) { UIntPtr idx = PageIndex(addr); @@ -329,7 +328,7 @@ namespace Microsoft.Singularity.Memory } } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] private unsafe void VerifyOwner(UIntPtr addr, UIntPtr pages, ushort tag) { UIntPtr idx = PageIndex(addr); @@ -346,19 +345,24 @@ namespace Microsoft.Singularity.Memory private UIntPtr PageIndex(UIntPtr pageAddr) { DebugStub.Assert(MemoryManager.IsPageAligned(pageAddr)); - DebugStub.Assert(pageAddr >= startAddr); + DebugStub.Assert(pageAddr >= startAddr && pageAddr <= heapLimit, + "PhysicalHeap.PageIndex pageAddr = {0:x} Range = {1:x} ... {2:x}", + __arglist(pageAddr, startAddr, heapLimit)); DebugStub.Assert(pageAddr < heapLimit); - return MemoryManager.PageFromAddr(pageAddr - startAddr); + + return MemoryManager.PagesFromBytes(pageAddr - startAddr); } [Inline] private unsafe ushort PageWord(UIntPtr pageIdx) { - DebugStub.Assert(pageIdx < pageCount); + DebugStub.Assert(pageIdx < pageCount, + "PhysicalHeap.PageWord pageIdx {0} >= pageCount {1}", + __arglist(pageIdx, pageCount)); return *(pageTable + (ulong)pageIdx); } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] private bool Lock() { bool enabled = Processor.DisableInterrupts(); @@ -368,7 +372,7 @@ namespace Microsoft.Singularity.Memory return enabled; } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] private void Unlock(bool iflag) { #if SINGULARITY_MP @@ -387,7 +391,7 @@ namespace Microsoft.Singularity.Memory internal unsafe FreeNode* head; internal unsafe FreeNode* tail; - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal unsafe void CreateAndInsert(PhysicalHeap* inHeap, UIntPtr addr, UIntPtr pages) @@ -470,7 +474,8 @@ namespace Microsoft.Singularity.Memory if (target == head) { InsertAsHead(node); - } else { + } + else { node->prev = target->prev; node->next = target; target->prev = node; @@ -481,7 +486,7 @@ namespace Microsoft.Singularity.Memory } } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal unsafe void InsertBySize(FreeNode* node) { if (head == null) { @@ -520,7 +525,7 @@ namespace Microsoft.Singularity.Memory } } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal unsafe FreeNode * FindGoodFit(UIntPtr bytes, UIntPtr alignment) { DebugStub.Assert(alignment >= MemoryManager.PageSize); @@ -586,7 +591,7 @@ namespace Microsoft.Singularity.Memory internal unsafe LastNode * last; internal UIntPtr bytes; - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe FreeNode * Create(PhysicalHeap* inHeap, UIntPtr addr, UIntPtr pages) { @@ -612,7 +617,7 @@ namespace Microsoft.Singularity.Memory return node; } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe FreeNode * GetNodeAt(PhysicalHeap* inHeap, UIntPtr addr) { @@ -655,7 +660,7 @@ namespace Microsoft.Singularity.Memory internal uint signature; internal unsafe FreeNode * node; - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe LastNode * Create(PhysicalHeap* inHeap, UIntPtr addr, FreeNode *node) { @@ -668,14 +673,14 @@ namespace Microsoft.Singularity.Memory return last; } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal unsafe void Remove() { signature = Removed; node = null; } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe FreeNode * GetNodeFromLast(PhysicalHeap* inHeap, UIntPtr addr) { @@ -689,7 +694,7 @@ namespace Microsoft.Singularity.Memory // addr must specify a page such that the next page (if there // is one) is not free - if ( (idx != inHeap->pageCount - 1) && + if ((idx != inHeap->pageCount - 1) && (inHeap->PageWord(idx + 1) == FreePage)) { return null; } @@ -703,7 +708,7 @@ namespace Microsoft.Singularity.Memory // If the preceding page is free, then addr specifies // the last page in a multi-page block, otherwise it // specifies the only page in a one-page block. - if ( inHeap->PageWord(idx - 1) == FreePage ) { + if (inHeap->PageWord(idx - 1) == FreePage) { DebugStub.Assert(((LastNode*)addr)->signature == Signature); return ((LastNode *)addr)->node; } @@ -715,5 +720,3 @@ namespace Microsoft.Singularity.Memory } } } - -#endif // PAGING diff --git a/base/Kernel/Singularity/Memory/PhysicalPages.cs b/base/Kernel/Singularity/Memory/PhysicalPages.cs index 8c408a6..5a866a0 100644 --- a/base/Kernel/Singularity/Memory/PhysicalPages.cs +++ b/base/Kernel/Singularity/Memory/PhysicalPages.cs @@ -14,14 +14,13 @@ // as no mapping to them is likely to exist. // -#if PAGING - using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using Microsoft.Singularity; +using Microsoft.Singularity.Hal; namespace Microsoft.Singularity.Memory { @@ -71,6 +70,10 @@ namespace Microsoft.Singularity.Memory // internal static unsafe PhysicalAddress Initialize(ulong reserveSize) { + Platform p = Platform.ThePlatform; + UIntPtr lowerAddress = UIntPtr.Zero; + UIntPtr upperAddress = UIntPtr.Zero; + // NB: our initialization is currently naive; we // set up a table to describe all of physical memory, // even though there are holes in it for reserved @@ -79,33 +82,25 @@ namespace Microsoft.Singularity.Memory InitializeLock(); - // First figure out how much RAM there is in this - // machine - BootInfo * bi = BootInfo.HalGetBootInfo(); - SMAPINFO *smap = (SMAPINFO*)bi->SmapData32; - addressLimit = 0; + // Retrieve the highest RAM address + MemoryManager.PhysicalMemoryLimits(out lowerAddress, out upperAddress); + addressLimit = (ulong)upperAddress; - for (uint i = 0; i < bi->SmapCount; i++) { - if (smap[i].type == (ulong)SMAPINFO.AddressType.Free && - smap[i].addr + smap[i].size > addressLimit) { - addressLimit = smap[i].addr + smap[i].size; - } - } - - DebugStub.WriteLine("Highest usable RAM address is {0}", + DebugStub.WriteLine("Highest usable RAM address is 0x{0:x8}", __arglist(addressLimit)); - // How much room do we need for the page table? + // How much room do we need for the pageblock table? numBlocks = GetNumBlocks(addressLimit); ulong requiredSpace = (ulong)numBlocks * (ulong)sizeof(PageBlock); // Now we need to find a location in physical memory - // where we can stick the physical page table. This needs - // to be addressable with a UIntPtr. + // where we can stick the physical pageblock table. ulong startAddr = LowerRAMBlackout; - if (bi->DumpLimit > startAddr) { - startAddr = (ulong)bi->DumpLimit; - startAddr = MemoryManager.PagePad(startAddr); + if (p.BootAllocatedMemorySize != 0) { + if (startAddr > p.BootAllocatedMemory && + startAddr < p.BootAllocatedMemory + p.BootAllocatedMemorySize) { + startAddr = (ulong)p.BootAllocatedMemory + (ulong)p.BootAllocatedMemorySize; + } } ulong physLocation = FindFreeBlockInSMAP(startAddr, requiredSpace); @@ -128,8 +123,7 @@ namespace Microsoft.Singularity.Memory thisBlock->nextIdx = (i == numBlocks - 1) ? PageBlock.Sentinel : i + 1; } - // Mark the blackout area of low physical memory, along - // with room used by the boot loader, as used + // Mark the blackout area of low physical memory as used MarkRangeUsed(0, startAddr - 1); // Now mark the range of physical pages occupied by the @@ -137,7 +131,8 @@ namespace Microsoft.Singularity.Memory MarkRangeUsed((ulong)physTable, requiredSpace); // Mark any non-Free areas (according to SMAP) as in use - for (uint i = 0; i < bi->SmapCount; i++) { + SMAPINFO* smap = p.Smap; + for (uint i = 0; i < p.SmapCount; i++) { if ((smap[i].type != (ulong)SMAPINFO.AddressType.Free) && ((smap[i].addr + smap[i].size) > startAddr) && (smap[i].addr < addressLimit)) { @@ -147,7 +142,8 @@ namespace Microsoft.Singularity.Memory if (smap[i].addr >= startAddr) { unaccountedStart = smap[i].addr; unaccountedLength = smap[i].size; - } else { + } + else { // Part of this memory window is already accounted for unaccountedStart = startAddr; unaccountedLength = smap[i].size - (startAddr - smap[i].addr); @@ -157,6 +153,21 @@ namespace Microsoft.Singularity.Memory } } + // Mark out all the Platform special regions as busy + if (p.MiniDumpLimit != 0) { + ulong miniDumpSize = (ulong)p.MiniDumpLimit - (ulong)p.MiniDumpBase; + MarkRangeUsed((ulong)p.MiniDumpBase, + MemoryManager.PagePad(miniDumpSize)); + } + if (p.KernelDllSize != 0) { + MarkRangeUsed((ulong)p.KernelDllBase, + MemoryManager.PagePad((ulong)p.KernelDllSize)); + } + if (p.BootAllocatedMemorySize != 0) { + MarkRangeUsed((ulong)p.BootAllocatedMemory, + MemoryManager.PagePad((ulong)p.BootAllocatedMemorySize)); + } + // Last step: find an available area to situate the caller's // requested reserve-block, if any. PhysicalAddress reservedPtr = PhysicalAddress.Null; @@ -170,6 +181,10 @@ namespace Microsoft.Singularity.Memory if (reservedPtr == PhysicalAddress.Null) { Kernel.Panic("Couldn't find enough physically contiguous memory to reserve"); } + + if (reservedPtr.Value + reserveSize > 0xFFFFFF) { + Kernel.Panic("Couldn't find enough physically contiguous memory below 0xFFFFFF for I/O memory"); + } } // Mark the reserved block as used. It's up to the caller @@ -178,8 +193,11 @@ namespace Microsoft.Singularity.Memory CheckConsistency(); - DebugStub.WriteLine("PhysicalPages initialized with {0} physical pages available.", - __arglist(GetFreePageCount())); + DebugStub.WriteLine("PhysicalPages initialized at 0x{0:x8} - 0x{1:x8} with {2} physical pages available.", + __arglist(reservedPtr.Value, + reservedPtr.Value + reserveSize, + GetFreePageCount())); + return reservedPtr; } @@ -309,23 +327,15 @@ namespace Microsoft.Singularity.Memory } } - private static unsafe void MarkPageUsed(ulong pageAddr) - { - // Please only give us page-aligned addresses - DebugStub.Assert(MemoryManager.IsPageAligned(pageAddr), "Hardware.MarkPageUsed: misaligned address"); - - ulong pageNum = MemoryManager.PagesFromBytes(pageAddr); - uint blockIdx = (uint)(pageNum / (ulong)PageBlock.PagesPerBlock); - uint pageInBlock = (uint)(pageNum % (ulong)PageBlock.PagesPerBlock); - MarkPageInBlockUsed(blockIdx, pageInBlock); - } - - // Mark a range of physical memory as being used. This is only - // used during initialization, and so is not particularly + // Mark a range of physical memory as being used. THIS IS ONLY + // USED DURING INITIALIZATION, and so is not particularly // efficient. // // Addresses do not have to be page-aligned, but whole pages will // be marked. + // + // To allow SMAP overlaps with HAL regions make this ignore + // region overlaps. private static unsafe void MarkRangeUsed(ulong startAddr, ulong length) { ulong firstPage = startAddr & ~MemoryManager.PageMask; @@ -333,17 +343,27 @@ namespace Microsoft.Singularity.Memory // the data ends. ulong lastPage = MemoryManager.PagePad(startAddr + length); - for (ulong page = firstPage; page < lastPage; page += MemoryManager.PageSize) { - MarkPageUsed(page); + for (ulong pageAddr = firstPage; pageAddr < lastPage; pageAddr += MemoryManager.PageSize) { + ulong pageNum = MemoryManager.PagesFromBytes(pageAddr); + uint blockIdx = (uint)(pageNum / (ulong)PageBlock.PagesPerBlock); + uint pageInBlock = (uint)(pageNum % (ulong)PageBlock.PagesPerBlock); + PageBlock* pBlock = GetBlock(blockIdx); + + if (!pBlock->PageInUse(pageInBlock)) { + pBlock->MarkAsUsed(pageInBlock); + + if (pBlock->Full) { + pBlock->Unlink(ref freeList); + } + } } } private static unsafe ulong FindFreeBlockInSMAP(ulong startAddr, ulong requiredSpace) { - BootInfo * bi = BootInfo.HalGetBootInfo(); - SMAPINFO *smap = (SMAPINFO*)bi->SmapData32; + SMAPINFO *smap = Platform.ThePlatform.Smap; - for (uint i = 0; i < bi->SmapCount; i++) { + for (uint i = 0; i < Platform.ThePlatform.SmapCount; i++) { if (smap[i].type == (ulong)SMAPINFO.AddressType.Free && smap[i].addr + smap[i].size > startAddr) { @@ -352,7 +372,8 @@ namespace Microsoft.Singularity.Memory if (smap[i].addr < startAddr) { usableSize = smap[i].size - (startAddr - smap[i].addr); usableStart = startAddr; - } else { + } + else { usableSize = smap[i].size; usableStart = smap[i].addr; } @@ -389,7 +410,7 @@ namespace Microsoft.Singularity.Memory // Now walk the whole page table and count up // pages that way - for (uint i = 0 ; i < numBlocks; i++) { + for (uint i = 0; i < numBlocks; i++) { PageBlock* pBlock = GetBlock(i); pagesFromWalk += pBlock->FreePages; } @@ -401,11 +422,11 @@ namespace Microsoft.Singularity.Memory private static void InitializeLock() { #if SINGULARITY_MP - pagesLock = new SpinLock(); + pagesLock = new SpinLock(SpinLock.Types.PhysicalPages); #endif // SINGULARITY_MP } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] private static bool Lock() { bool enabled = Processor.DisableInterrupts(); @@ -415,7 +436,7 @@ namespace Microsoft.Singularity.Memory return enabled; } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] private static void Unlock(bool iflag) { #if SINGULARITY_MP @@ -432,6 +453,7 @@ namespace Microsoft.Singularity.Memory // This is our descriptor for a range of physical // pages. Each PageBlock describes a range of // physical pages with a used/not-used bitmap. + [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)] private struct PageBlock { // CONSTS @@ -491,7 +513,8 @@ namespace Microsoft.Singularity.Memory if (prevIdx != Sentinel) { DebugStub.Assert(GetBlock(prevIdx)->nextIdx == Index, "PhysicalPages.PageBlock.Unlink: inconsistent prev->next"); GetBlock(prevIdx)->nextIdx = nextIdx; - } else { + } + else { // We are the list head. DebugStub.Assert(listHead == Index, "PhysicalPages.PageBlock.Unlink: inconsistent head"); listHead = nextIdx; @@ -597,7 +620,7 @@ namespace Microsoft.Singularity.Memory DebugStub.Assert(!Full, "PhysicalPages.PageBlock.FirstFreePage: Block already full"); // Look for a clear bit in the region map - for (int i = 0 ; i < RegionsPerBlock; i++) { + for (int i = 0; i < RegionsPerBlock; i++) { if ((blockMap & (uint)((uint)1 << i)) == 0) { // This region is available. Look // for a free page in the specific @@ -643,7 +666,7 @@ namespace Microsoft.Singularity.Memory uint retval = 0; - for (int i = 0 ; i < RegionsPerBlock; i++) { + for (int i = 0; i < RegionsPerBlock; i++) { if ((blockMap & (uint)((uint)1 << i)) == 0) { // This region has free pages. fixed (uint* pBeforeRegions = &blockMap) { @@ -707,7 +730,7 @@ namespace Microsoft.Singularity.Memory DebugStub.Assert((blockMap & ((uint)1 << (int)i)) == 0, "PhysicalPages.PageBlock.CheckConsistency: blockMap shows a free region as full"); } else { - DebugStub.Assert((blockMap & ((uint)1 << (int)i)) != 0, "PhysicalPages.PageBlock.CheckConsistency: blockMap shows a free region as full"); + DebugStub.Assert((blockMap & ((uint)1 << (int)i)) != 0, "PhysicalPages.PageBlock.CheckConsistency: blockMap shows a full region as free"); } } } @@ -717,4 +740,3 @@ namespace Microsoft.Singularity.Memory } } -#endif // PAGING diff --git a/base/Kernel/Singularity/Memory/SharedHeap.cs b/base/Kernel/Singularity/Memory/SharedHeap.cs index 1a7cf7d..996aeac 100644 --- a/base/Kernel/Singularity/Memory/SharedHeap.cs +++ b/base/Kernel/Singularity/Memory/SharedHeap.cs @@ -3,7 +3,7 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: SharedHeap.cs - Heap for memory passed between processes. +// Heap for memory passed between processes. // using System; @@ -17,6 +17,9 @@ using System.Diagnostics; using Microsoft.Singularity; using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Hal; +using Microsoft.Singularity.Isal; + namespace Microsoft.Singularity.Memory { // @@ -36,9 +39,7 @@ namespace Microsoft.Singularity.Memory // An allocated region's ownership may be transferred between owners. // A region may also have multiple owners. // - [NoCCtor] - [CLSCompliant(false)] - public class SharedHeap { + public partial class SharedHeap { // // The system always has at least one shared heap @@ -58,10 +59,12 @@ namespace Microsoft.Singularity.Memory } } -#if PAGING internal static unsafe void Initialize() { - kernelSharedHeap = new SharedHeap(ProtectionDomain.DefaultDomain, MemoryManager.GetKernelRange()); + kernelSharedHeap = new SharedHeap(ProtectionDomain.DefaultDomain, + MemoryManager.UseAddressTranslation + ? MemoryManager.GetKernelRange() + : null); } // Debug-only sanity check that we are in the right protection domain. @@ -79,19 +82,7 @@ namespace Microsoft.Singularity.Memory parentDomain.AddressSpace); } } -#else - internal static void Initialize() - { - kernelSharedHeap = new SharedHeap(); - } - protected void CheckDomain() - { - // Do nothing; there are no protection domains on a non-PAGING build - } -#endif - -#region Small/Large Region Accounting // // If we can fit multiple regions of a certain size on a single page, // then we consider them to be "small" regions. Large regions get @@ -119,10 +110,8 @@ namespace Microsoft.Singularity.Memory private const byte TotalBuckets = 8; private unsafe UIntPtr *freeSmallRegions; -#if PAGING private VirtualMemoryRange memoryRange; // where we get our pages private ProtectionDomain parentDomain; -#endif // // Given a bucket number, return the size of the regions @@ -181,12 +170,12 @@ namespace Microsoft.Singularity.Memory refCountLocation = &(((SmallRegion *)region)->referenceCount); do { + oldRefCount = *refCountLocation; DebugStub.Assert(oldRefCount != uint.MaxValue); newRefCount = oldRefCount + 1; - } while (oldRefCount != Interlocked.CompareExchange( - (UIntPtr *)refCountLocation, newRefCount, - oldRefCount)); + + } while (oldRefCount != Interlocked.CompareExchange(ref *refCountLocation, newRefCount, oldRefCount)); } // @@ -204,12 +193,12 @@ namespace Microsoft.Singularity.Memory refCountLocation = &(((SmallRegion *)region)->referenceCount); do { + oldRefCount = *refCountLocation; DebugStub.Assert(oldRefCount != 0); newRefCount = oldRefCount - 1; - } while (oldRefCount != Interlocked.CompareExchange( - (UIntPtr *)refCountLocation, newRefCount, - oldRefCount)); + + } while (oldRefCount != Interlocked.CompareExchange(ref *refCountLocation, newRefCount, oldRefCount)); return newRefCount; } @@ -258,6 +247,7 @@ namespace Microsoft.Singularity.Memory descriptorLocation = SharedHeapPageTable + page; do { + oldDescriptor = *descriptorLocation; refCount = (oldDescriptor & LargeRegionReferenceMask) >> LargeRegionReferenceShift; @@ -267,9 +257,7 @@ namespace Microsoft.Singularity.Memory newDescriptor = (oldDescriptor & ~LargeRegionReferenceMask) | (refCount << LargeRegionReferenceShift); - } while (oldDescriptor != Interlocked.CompareExchange( - (UIntPtr *)descriptorLocation, newDescriptor, - oldDescriptor)); + } while (oldDescriptor != Interlocked.CompareExchange(ref *descriptorLocation, newDescriptor, oldDescriptor)); } // @@ -287,6 +275,7 @@ namespace Microsoft.Singularity.Memory descriptorLocation = SharedHeapPageTable + page; do { + oldDescriptor = *descriptorLocation; refCount = (oldDescriptor & LargeRegionReferenceMask) >> LargeRegionReferenceShift; @@ -295,9 +284,7 @@ namespace Microsoft.Singularity.Memory newDescriptor = (oldDescriptor & ~LargeRegionReferenceMask) | (refCount << LargeRegionReferenceShift); - } while (oldDescriptor != Interlocked.CompareExchange( - (UIntPtr *)descriptorLocation, newDescriptor, - oldDescriptor)); + } while (oldDescriptor != Interlocked.CompareExchange(ref *descriptorLocation, newDescriptor, oldDescriptor)); return refCount; } @@ -339,7 +326,8 @@ namespace Microsoft.Singularity.Memory AtomicReferenceSmallRegion(region); - } else { + } + else { // // This is a large region. We need to walk all the pages // it covers, and increment the reference count on each. @@ -417,7 +405,8 @@ namespace Microsoft.Singularity.Memory lastReference = false; } - } else { + } + else { // // This is a large region. We need to walk all the pages // it covers, and decrement the reference count on each. @@ -455,9 +444,7 @@ namespace Microsoft.Singularity.Memory } return lastReference; } -#endregion -#region Allocation Owner Accounting // // Whether to use spinlocks (true) or disabling of interrupts (false) // to protect the ownership lists. @@ -472,7 +459,7 @@ namespace Microsoft.Singularity.Memory private UIntPtr freeOwnerList; // - // NOTE The shared heap has a concept of AllocationOwnerIds, + // NOTE: The shared heap has a concept of AllocationOwnerIds, // but in our current system there is a single one of these for // all data block and endpoint allocs in a given heap. // @@ -515,25 +502,24 @@ namespace Microsoft.Singularity.Memory internal struct AllocationOwner { internal unsafe Allocation *sentinel; private SpinLock listLock; - private bool lockInterrupt; internal long outstandingBlocks; - internal static unsafe void Lock(AllocationOwner *owner) + internal static unsafe bool Lock(AllocationOwner *owner) { + bool wasEnabled = Processor.DisableInterrupts(); if (useAllocationOwnerLocks) { owner->listLock.Acquire(Thread.CurrentThread); - } else { - owner->lockInterrupt = Processor.DisableInterrupts(); } + return wasEnabled; } - internal static unsafe void Unlock(AllocationOwner *owner) + internal static unsafe void Unlock(AllocationOwner* owner, + bool doRestore) { if (useAllocationOwnerLocks) { owner->listLock.Release(Thread.CurrentThread); - } else { - Processor.RestoreInterrupts(owner->lockInterrupt); } + Processor.RestoreInterrupts(doRestore); } internal static unsafe void ReuseOrCreateOwner( @@ -576,6 +562,8 @@ namespace Microsoft.Singularity.Memory ((AllocationOwner*)descriptorAddress)->sentinel = Allocation.CreateSentinel(sharedHeap); + ((AllocationOwner*)descriptorAddress)->listLock = + new SpinLock(SpinLock.Types.SharedHeapAllocationOwner); ownerId.owner = (AllocationOwner*)descriptorAddress; } @@ -593,7 +581,7 @@ namespace Microsoft.Singularity.Memory unsafe public static void Iterate(AllocationOwner* owner, AllocationVisitor visitor) { - Lock(owner); + bool wasEnabled = Lock(owner); Allocation* sentinel = owner->sentinel; // @@ -607,7 +595,7 @@ namespace Microsoft.Singularity.Memory } current = current->next; } - Unlock(owner); + Unlock(owner, wasEnabled); } /// @@ -641,7 +629,7 @@ namespace Microsoft.Singularity.Memory Allocation.Initialize(rover, 0, 0, roverType); AllocationOwner* owner = ownerId.owner; Allocation.AssignOwnership(rover, ownerId); - Lock(owner); + bool wasEnabled = Lock(owner); Allocation* sentinel = owner->sentinel; // @@ -653,18 +641,18 @@ namespace Microsoft.Singularity.Memory if (Allocation.GetType(current) != roverType && matches(current)) { - Unlock(owner); + Unlock(owner, wasEnabled); visitor(current); - Lock(owner); + wasEnabled = Lock(owner); } else if (releaseCount == 0) { - Unlock(owner); + Unlock(owner, wasEnabled); // TODO: pause here? (prevent lock capture effects) - Lock(owner); + wasEnabled = Lock(owner); } Allocation.MoveForward(rover); } - Unlock(owner); + Unlock(owner, wasEnabled); Allocation.RemoveOwnership(rover, ownerId); } } @@ -692,25 +680,18 @@ namespace Microsoft.Singularity.Memory this, matches, visitor); } } -#endregion // // Initialize the shared heap subsystem. // Note: We trust the system to initialize statics to zero. // -#if PAGING internal unsafe SharedHeap(ProtectionDomain parentDomain, VirtualMemoryRange range) -#else - internal unsafe SharedHeap() -#endif { - useAllocationOwnerLocks = false; + useAllocationOwnerLocks = true; // required at least for MP -#if PAGING this.memoryRange = range; this.parentDomain = parentDomain; -#endif // // We don't have another memory allocator handy, so we grab @@ -738,84 +719,77 @@ namespace Microsoft.Singularity.Memory // Doesn't actually do anything. } + // + // When paging is not enabled, the (one and only) shared heap + // always uses general-purpose kernel memory. + // private UIntPtr AllocatePages(UIntPtr numPages) { return AllocatePages(numPages, 0); } -#if PAGING private unsafe UIntPtr AllocatePages(UIntPtr numPages, uint extra) { - return memoryRange.Allocate(numPages, null, extra, PageType.Shared); + if (memoryRange != null) { + return memoryRange.Allocate(numPages, null, extra, PageType.Shared); + } + else { + return MemoryManager.KernelAllocate(numPages, null, extra, PageType.Shared); + } } private unsafe void FreePages(UIntPtr addr, UIntPtr numPages) { - memoryRange.Free(addr, numPages, null); + if (memoryRange != null) { + memoryRange.Free(addr, numPages, null); + } + else { + MemoryManager.KernelFree(addr, numPages, null); + } } private unsafe uint* SharedHeapPageTable { get { - return memoryRange.PageTable; + if (memoryRange != null) { + return memoryRange.PageTable; + } + else { + return MemoryManager.KernelPageTable; + } } } private unsafe UIntPtr SharedHeapPageCount { get { - return memoryRange.PageCount; + if (memoryRange != null) { + return memoryRange.PageCount; + } + else { + return MemoryManager.KernelPageCount; + } } } private unsafe UIntPtr PageFromAddr(UIntPtr addr) { - return memoryRange.PageFromAddr(addr); + if (memoryRange != null) { + return memoryRange.PageFromAddr(addr); + } + else { + return MemoryManager.PageFromAddr(addr); + } } private unsafe UIntPtr AddrFromPage(UIntPtr pageIdx) { - return memoryRange.AddrFromPage(pageIdx); - } - -#else // PAGING - // - // When paging is not enabled, the (one and only) shared heap - // always uses general-purpose kernel memory. - // - private unsafe UIntPtr AllocatePages(UIntPtr numPages, uint extra) - { - return MemoryManager.KernelAllocate(numPages, null, extra, PageType.Shared); - } - - private void FreePages(UIntPtr addr, UIntPtr numPages) - { - MemoryManager.KernelFree(addr, numPages, null); - } - - private unsafe uint* SharedHeapPageTable { - get { - return MemoryManager.KernelPageTable; + if (memoryRange != null) { + return memoryRange.AddrFromPage(pageIdx); + } + else { + return MemoryManager.AddrFromPage(pageIdx); } } - private UIntPtr SharedHeapPageCount { - get { - return MemoryManager.KernelPageCount; - } - } - - private UIntPtr PageFromAddr(UIntPtr addr) - { - return MemoryManager.PageFromAddr(addr); - } - - private UIntPtr AddrFromPage(UIntPtr pageIdx) - { - return MemoryManager.AddrFromPage(pageIdx); - } - -#endif // PAGING - - // // Initialize internal per-owner data structure. // Returns an opaque identifier to the owner (usually a process) @@ -848,7 +822,7 @@ namespace Microsoft.Singularity.Memory if (Allocation.GetSize(source) > MaxSmallRegion) { // Large region. Move it by page-flipping. // - // NOTE we currently ASSUME that the block + // NOTE: we currently ASSUME that the block // being moved is not being shared, since the sharing // feature is being deprecated. UIntPtr rangeStart = (UIntPtr)Allocation.GetDataUnchecked(source); @@ -890,7 +864,8 @@ namespace Microsoft.Singularity.Memory return AllocationFromRawMemory(mapPoint, size, type, targetOwner); - } else + } + else #endif { // Small region. Move it by copying. @@ -949,10 +924,12 @@ namespace Microsoft.Singularity.Memory // if (bytes == 0) { region = UIntPtr.Zero; - } else { + } + else { if (bytes + sizeof(SmallRegion) > MaxSmallRegion) { region = AllocateLargeRegion(bytes); - } else { + } + else { region = AllocateSmallRegion(bytes); } // Allocation failed, request denied. Should @@ -984,13 +961,13 @@ namespace Microsoft.Singularity.Memory // // Put descriptor on per-process list. - // all descriptors live on the kernel proc for now. + // all descriptors live on the kernel proc for now. // Allocation.AssignOwnership(allocation, ownerId); // Figure out who our caller is. UIntPtr pc1, pc2, pc3; - Processor.GetStackEips(out pc1, out pc2, out pc3); + Isa.GetStackReturnAddresses(out pc1, out pc2, out pc3); Tracing.Log(Tracing.Debug, "SharedHeap.Allocate stack trace {0:x} {1:x} {2:x}", pc1, pc2, pc3); Tracing.Log(Tracing.Debug, "result = {0:x}", (UIntPtr)allocation); @@ -1085,8 +1062,8 @@ namespace Microsoft.Singularity.Memory } - /// - /// Free a previously-allocated region of memory. + /// + /// Free a previously-allocated region of memory. /// /// true if Free releases last reference to underlying memory public unsafe bool Free( @@ -1133,7 +1110,7 @@ namespace Microsoft.Singularity.Memory // REVIEW: Do we need this lock? This should be the last code // for this process to ever call the SharedHeap. // - AllocationOwner.Lock(owner); + bool wasEnabled = AllocationOwner.Lock(owner); // // The FreeAll for a process is called by a kernel service @@ -1143,7 +1120,7 @@ namespace Microsoft.Singularity.Memory // first = owner->sentinel; current = Allocation.Next(first); - while(current != first) { + while (current != first) { Allocation *victim = current; current = Allocation.Next(current); DereferenceRegion(Allocation.GetDataUnchecked(victim), @@ -1156,7 +1133,7 @@ namespace Microsoft.Singularity.Memory AtomicPushFreeList(ref freeAllocationList, (UIntPtr)first); owner->sentinel = null; - AllocationOwner.Unlock(owner); + AllocationOwner.Unlock(owner, wasEnabled); // // Free our owner descriptor. @@ -1310,7 +1287,7 @@ namespace Microsoft.Singularity.Memory // a valid record in our heap. public unsafe bool Validate(Allocation *allocation) { -#if PAGING +#if MEMORY_TEST CheckDomain(); UIntPtr pData = (UIntPtr)allocation; @@ -1345,24 +1322,16 @@ namespace Microsoft.Singularity.Memory // // Structure for tracking allocated regions. // - [StructLayout(LayoutKind.Sequential)] - public struct Allocation { - private UIntPtr data; // Address of the allocated memory region. - private UIntPtr size; // Size of above in bytes. - private unsafe UIntPtr type; // Type information. - internal unsafe Allocation *next; // Next on owner's list. - private unsafe Allocation *prev; // Previous on owner's list. - private int owner; - + public partial struct Allocation { [Conditional("DEBUG")] [NoHeapAllocation] private static unsafe void AssertCorrectOwner(Allocation* allocation) { - /* - if (Thread.CurrentProcess.ProcessId != allocation->owner) { - DebugStub.Break(); - } - */ + // + //if (Thread.CurrentProcess.ProcessId != allocation->owner) { + // DebugStub.Break(); + //} + // } [NoHeapAllocation] @@ -1426,15 +1395,15 @@ namespace Microsoft.Singularity.Memory (UIntPtr)Thread.CurrentProcess.ProcessId); } // Figure out who our caller is. - /* - UIntPtr[] stack = new UIntPtr[15]; - Processor.GetStackEips(stack); - for(int i=0; iowner = processId; } @@ -1535,13 +1504,14 @@ namespace Microsoft.Singularity.Memory #if PAGING // Sanity: we shouldn't be putting user blocks onto // the kernel's shared heap lists, and vice versa. - if ((UIntPtr)owner >= BootInfo.KERNEL_BOUNDARY) { - DebugStub.Assert((UIntPtr)allocation >= BootInfo.KERNEL_BOUNDARY); - } else { - DebugStub.Assert((UIntPtr)allocation < BootInfo.KERNEL_BOUNDARY); + if ((UIntPtr)owner >= Platform.KERNEL_BOUNDARY) { + DebugStub.Assert((UIntPtr)allocation >= Platform.KERNEL_BOUNDARY); + } + else { + DebugStub.Assert((UIntPtr)allocation < Platform.KERNEL_BOUNDARY); } #endif - AllocationOwner.Lock(owner); + bool wasEnabled = AllocationOwner.Lock(owner); // // Put allocation at beginning of owner's list. @@ -1551,7 +1521,7 @@ namespace Microsoft.Singularity.Memory allocation->next->prev = allocation; owner->sentinel->next = allocation; owner->outstandingBlocks++; - AllocationOwner.Unlock(owner); + AllocationOwner.Unlock(owner, wasEnabled); } // @@ -1563,7 +1533,7 @@ namespace Microsoft.Singularity.Memory { AllocationOwner *owner = ownerId.owner; - AllocationOwner.Lock(owner); + bool wasEnabled = AllocationOwner.Lock(owner); // // Remove allocation from owner's list. @@ -1572,7 +1542,7 @@ namespace Microsoft.Singularity.Memory allocation->prev->next = allocation->next; owner->outstandingBlocks--; - AllocationOwner.Unlock(owner); + AllocationOwner.Unlock(owner, wasEnabled); // // Sanitize this element. @@ -1666,7 +1636,7 @@ namespace Microsoft.Singularity.Memory // So we loop back and try it all over again. // - } while(firstElementLocation != Interlocked.CompareExchange( + } while (firstElementLocation != Interlocked.CompareExchange( ref head, secondElementLocation, firstElementLocation)); @@ -1716,7 +1686,7 @@ namespace Microsoft.Singularity.Memory // So we loop back and try it all over again. // - } while(firstElementLocation != Interlocked.CompareExchange( + } while (firstElementLocation != Interlocked.CompareExchange( ref head, newElementLocation, firstElementLocation)); } diff --git a/base/Kernel/Singularity/Memory/Stacks.cs b/base/Kernel/Singularity/Memory/Stacks.cs index b30a2e7..161ca31 100644 --- a/base/Kernel/Singularity/Memory/Stacks.cs +++ b/base/Kernel/Singularity/Memory/Stacks.cs @@ -4,38 +4,50 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Stacks.cs - Primitive stack segment manager -// -// Note: +// Primitive stack segment manager // -//#define TEST_STACK_LINKING +#if SINGULARITY_LINKED_STACKS +#else +#define USE_BIG_STACKS +#endif + //#define DEBUG_STACK_VERBOSE -#define NO_TRACE_STACKS - +//#define DO_TRACE_STACKS namespace Microsoft.Singularity.Memory { + using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using System.GCs; using Microsoft.Singularity; - using Microsoft.Singularity.X86; + using Microsoft.Singularity.Isal; + + internal partial class Stacks { + + private static GetKernelStackCallback getKernelStackCallback; + private static GetSipStackCallback getSipStackCallback; + private static ReturnKernelStackCallback returnKernelStackCallback; + private static ReturnSipStackCallback returnSipStackCallback; - [NoCCtor] - [CLSCompliant(false)] - [RequiredByBartok] - internal class Stacks - { // This constant gives a reasonable size for an initial stack // chunk, leaving room for the metadata that will be added to // the top of the stack (sizeof(StackHead)). - internal const int InitialStackSize = 0x1f00; - [AccessedByRuntime("referenced from halstack.asm")] - internal static int GetCount; - internal static int ReturnCount; + +#if USE_BIG_STACKS + internal const int InitialStackSize = 0xfb00; + internal const int SafetyBufferSize = 0x0400; +#elif ISA_IX64 || ISA_ARM || PHXBRIDGE + // TODO: FIXFIX set back to f00 + internal const int InitialStackSize = 0xfb00; + internal const int SafetyBufferSize = 0x0400; +#else + internal const int InitialStackSize = 0x0f00; + internal const int SafetyBufferSize = 0x0000; +#endif [StructLayout(LayoutKind.Sequential)] internal struct StackHead @@ -45,59 +57,176 @@ namespace Microsoft.Singularity.Memory internal UIntPtr esp; }; - internal static unsafe void Initialize() + internal static void Initialize() { Tracing.Log(Tracing.Debug, "Stacks.Initialize() called"); - - GetCount = 0; - ReturnCount = 0; + getKernelStackCallback = new GetKernelStackCallback(); + getSipStackCallback = new GetSipStackCallback(); + returnKernelStackCallback = new ReturnKernelStackCallback(); + returnSipStackCallback = new ReturnSipStackCallback(); } - internal static unsafe void Finalize() + internal static void Finalize() { - unchecked { - Tracing.Log(Tracing.Debug, "Stacks.Finalize() GetStackSegment called {0} times.", - (UIntPtr)(uint)GetCount); - } - PrintCounts(); + Tracing.Log(Tracing.Debug, "Stacks.Finalize() KernelStacks"); + Tracing.Log(Tracing.Debug, "Stacks.Finalize()"); } - internal static void PrintCounts() + private class GetKernelStackCallback : Isa.ICallback { - unchecked { - Tracing.Log(Tracing.Debug, "Segments: {1} links, {2} outstanding", - (UIntPtr)(uint)GetCount, - (UIntPtr)(uint)(GetCount - ReturnCount)); + internal override UIntPtr Callback(UIntPtr param) + { + VTable.Assert(Isa.IsRunningOnInterruptStack); + + unsafe { + return GetStackSegment(param, + ref *Processor.GetCurrentThreadContext(), true, false); + } } } - [AccessedByRuntime("referenced from halstack.asm")] - [NoStackLinkCheck] - internal static unsafe UIntPtr GetStackSegment(UIntPtr size, - ref ThreadContext context) + private class GetSipStackCallback : Isa.ICallback { - return GetStackSegment(size, ref context, false); + internal override UIntPtr Callback(UIntPtr param) + { + VTable.Assert(Isa.IsRunningOnInterruptStack); + + unsafe { + UIntPtr stack = GetStackSegment(param, + ref *Processor.GetCurrentThreadContext(), + false, false); + if (stack == 0) { + + // Allocate from the kernel reservation so we may terminate the SIP + + stack = GetStackSegment(param, ref *Processor.GetCurrentThreadContext(), + true, false); + + // Note that even if we failed again and are returning null, we + // must return before any overflow handling logic, to get off + // the interrupt stack. + + } + + return stack; + } + } } - [NoStackLinkCheck] - internal static unsafe UIntPtr GetInitialStackSegment(ref ThreadContext context) + private class ReturnKernelStackCallback : Isa.ICallback + { + internal override UIntPtr Callback(UIntPtr param) + { + VTable.Assert(Isa.IsRunningOnInterruptStack); + + unsafe { + ReturnStackSegmentRawCommon(ref *Processor.GetCurrentThreadContext(), + true, false); + } + return 0; + } + } + + private class ReturnSipStackCallback : Isa.ICallback + { + internal override UIntPtr Callback(UIntPtr param) + { + VTable.Assert(Isa.IsRunningOnInterruptStack); + + unsafe { + ReturnStackSegmentRawCommon(ref *Processor.GetCurrentThreadContext(), + false, false); + } + return 0; + } + } + + [NoStackLinkCheckTrans] + internal static UIntPtr GetSipStackSegment(UIntPtr size) + { + UIntPtr stack; + + // @TODO: Historically we have disabled interrupts around stack growth. + // Actually I think it is unnecessary; however to be conservative for + // now we will disable interrupts while we use the interrupt stack. + + bool en = Processor.DisableInterrupts(); + try { + unsafe { + // Sanity check: we allocate from the current stack segment, and + // will set the thread context to point to a new stack segment + + VTable.Assert(Isa.GetStackPointer() <= + Processor.GetCurrentThreadContext()->stackBegin); + VTable.Assert(Isa.GetStackPointer() >= + Processor.GetCurrentThreadContext()->stackLimit); + } + stack = Isa.CallbackOnInterruptStack(getSipStackCallback, size); + } + finally { + Processor.RestoreInterrupts(en); + } + + return stack; + } + + [NoStackLinkCheckTrans] + internal static UIntPtr GetKernelStackSegment(UIntPtr size) + { + UIntPtr stack; + + // @TODO: see note about disabling interrupts above. + bool en = Processor.DisableInterrupts(); + try { + unsafe { + // Sanity check: we allocate from the current stack segment, and + // will set the thread context to point to a new stack segment + + VTable.Assert(Isa.GetStackPointer() <= + Processor.GetCurrentThreadContext()->stackBegin); + VTable.Assert(Isa.GetStackPointer() >= + Processor.GetCurrentThreadContext()->stackLimit); + } + stack = Isa.CallbackOnInterruptStack(getKernelStackCallback, size); + } + finally { + Processor.RestoreInterrupts(en); + } + + return stack; + } + + // + // This is called for each new thread to get the initial stack segment. + // + [NoStackLinkCheckTrans] + internal static UIntPtr GetInitialStackSegment(ref ThreadContext context) { // The first stack segment is always in kernel memory - return GetStackSegment(0, ref context, true); + UIntPtr ret = GetStackSegment(0, ref context, true, true); + return ret; } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe UIntPtr GetStackSegment(UIntPtr size, ref ThreadContext context, - bool forceToKernel) + bool kernelAllocation, + bool initialStack) { +#if SINGULARITY_LINKED_STACKS +#else + if (!initialStack) { + // If we get here, then the initial stack size must not have + // been sufficient to ensure that we don't need linked stacks. + DebugStub.Break(); + } +#endif UIntPtr begin = context.stackBegin; UIntPtr limit = context.stackLimit; -#if NO_TRACE_STACKS -#else +#if DO_TRACE_STACKS Kernel.Waypoint(666); #endif - StackHead *head = GetStackSegmentRaw(size, ref context, forceToKernel); + StackHead *head = GetStackSegmentRaw(size, ref context, kernelAllocation, initialStack); if (head != null) { head->prevBegin = begin; head->prevLimit = limit; @@ -106,46 +235,55 @@ namespace Microsoft.Singularity.Memory return (UIntPtr)head; } - [NoStackLinkCheck] + [NoStackLinkCheckTrans] internal static unsafe StackHead * GetStackSegmentRaw(UIntPtr size, ref ThreadContext context, - bool forceToKernel) + bool kernelAllocation, + bool initialStack) { // Allocate a new chunk, making room for StackHead at the top. // If you change these constants to add more data, see the // comment about InitialStackSize at the top of this file! -#if NO_TRACE_STACKS -#else +#if DO_TRACE_STACKS Kernel.Waypoint(667); #endif if (size == UIntPtr.Zero) { size = InitialStackSize; } - size = MemoryManager.PagePad(size + sizeof(StackHead)); + size = MemoryManager.PagePad(size + sizeof(StackHead) + SafetyBufferSize); UIntPtr chunk; Process owner = Process.GetProcessByID(context.processId); - /* - // NOTE here's where we should be clever about - // whether to allocate a stack chunk in the user range - // or the kernel range. Except, if we switch contexts - // during an ABI call while using a user-range stack - // segment on a paging machine, we die. Gloss over - // this hackily by always getting stack segments - // from the kernel range. - if (forceToKernel || (owner == Process.kernelProcess)) { - chunk = MemoryManager.KernelAllocate( - MemoryManager.PagesFromBytes(size), owner, 0, PageType.Stack); - } else { - chunk = MemoryManager.UserAllocate( - MemoryManager.PagesFromBytes(size), owner, 0, PageType.Stack); - } - */ + // + //// NOTE: here's where we should be clever about + //// whether to allocate a stack chunk in the user range + //// or the kernel range. Except, if we switch contexts + //// during an ABI call while using a user-range stack + //// segment on a paging machine, we die. Gloss over + //// this hackily by always getting stack segments + //// from the kernel range. + //if (kernelAllocation || (owner == Process.kernelProcess)) { + // chunk = MemoryManager.KernelAllocate( + // MemoryManager.PagesFromBytes(size), owner, 0, PageType.Stack); + //} + //else { + // chunk = MemoryManager.UserAllocate( + // MemoryManager.PagesFromBytes(size), owner, 0, PageType.Stack); + //} + // - chunk = MemoryManager.KernelAllocate( - MemoryManager.PagesFromBytes(size), owner, 0, PageType.Stack); + UIntPtr pageCount = MemoryManager.PagesFromBytes(size); +#if DEBUG_STACK_VERBOSE + fixed (ThreadContext *ptr = &context) { + Tracing.Log(Tracing.Debug, + "GetStackSegmentRaw(ctx={0:x8},size={1:d}) pages={2} [{3:x8}..{4:x8}]", + (UIntPtr)ptr, size, pageCount, + context.stackLimit, context.stackBegin); + } +#endif + chunk = MemoryManager.StackAllocate(pageCount, owner, 0, kernelAllocation, initialStack); if (chunk != UIntPtr.Zero) { // NB: We do _not_ zero out stack memory! @@ -153,117 +291,118 @@ namespace Microsoft.Singularity.Memory StackHead *head = (StackHead *)(chunk + size - sizeof(StackHead)); context.stackBegin = chunk + size; - context.stackLimit = chunk; + context.stackLimit = chunk + SafetyBufferSize; #if DEBUG_STACK_VERBOSE - Tracing.Log(Tracing.Debug, "GetStackSegmentRaw(size={0:d}) -> [{1,8:x}..{2,8:x}", + Tracing.Log(Tracing.Debug, + "GetStackSegmentRaw(size={0:d}) -> [{1:x8}..{2:x8}]", size, context.stackLimit, context.stackBegin); #endif - GetCount++; - return head; } else { // Stack allocation failed. In the future, we should // trigger a kernel exception; for now, we break to the // debugger. - DebugStub.Break(); +#if DEBUG_STACK_VERBOSE + Tracing.Log(Tracing.Debug, + "GetStackSegmentRaw: KernelAllocate failed!(siz={0:d})", + size); +#endif + //DebugStub.Break(); return null; } } - [AccessedByRuntime("reference from halstack.asm")] - [NoStackLinkCheck] - // NB: This function must execute in low-stack conditions! - // See the comment at the top of this file. - // Returns the address of the stack with argsN pushed. - internal static unsafe UIntPtr GetStackSegmentAndCopy(UIntPtr size, - ref ThreadContext context, - uint *arg2, - uint args, - UIntPtr esp, - UIntPtr begin, - UIntPtr limit) + // This is called when returning a kernel stack segment + [AccessedByRuntime("referenced from halstack.asm")] + [NoStackOverflowCheck] + internal static void ReturnKernelStackSegment() { -#if NO_TRACE_STACKS -#else - Kernel.Waypoint(668); -#endif -#if DEBUG_STACK_VERBOSE - fixed (ThreadContext *ptr = &context) { - DebugStub.Print("GetStackSegmentAndCopy(" + - "size={0},cxt={1:x8},arg={2:x8},num={3}," + - "esp={4:x8},beg={5:x8},lim={6:x8}\n", - __arglist((int)size, - (UIntPtr)ptr, - (UIntPtr)arg2, - (int)args, - esp, - begin, - limit)); - } -#endif - StackHead *head = GetStackSegmentRaw(size, ref context, false); - if (head == null) { - // We are in serious trouble... - DebugStub.Break(); - return (UIntPtr)head; - } + // @TODO: see note about disabling interrupts above. + bool en = Processor.DisableInterrupts(); + try { + Isa.CallbackOnInterruptStack(returnKernelStackCallback, 0); - head->prevBegin = begin; - head->prevLimit = limit; - head->esp = esp; - arg2 += args; - uint *nsp = (uint *)head; - for (uint n = 0; n < args; n++) { - *--nsp = *--arg2; + unsafe { + // Sanity check: we freed from the previous segment, and + // should have set the thread context to point to this segment now. + VTable.Assert(Isa.GetStackPointer() <= + Processor.GetCurrentThreadContext()->stackBegin); + VTable.Assert(Isa.GetStackPointer() >= + Processor.GetCurrentThreadContext()->stackLimit); + } } -#if DEBUG_STACK_VERBOSE - DebugStub.Print(" [old {0:x8}..{1:x8}..{2:x8}] {3:x8} {4:x8} {5:x8}\n", - __arglist(limit, (UIntPtr)(arg2 - 2), begin, - arg2[-2], arg2[-1], arg2[0])); - DebugStub.Print(" [raw {0:x8}..{1:x8}..{2:x8}]\n", - __arglist(context.stackLimit, - (UIntPtr)head, - context.stackBegin)); - DebugStub.Print(" [new {0:x8}..{1:x8}..{2:x8}] {3:x8} {4:x8} {5:x8}\n", - __arglist(context.stackLimit, - (UIntPtr)nsp, - context.stackBegin, - nsp[0], nsp[1], nsp[2])); + finally { + Processor.RestoreInterrupts(en); + } + } - DebugStub.Print(" [ret {0:x8}] {1:x8} {2:x8}\n", - __arglist((UIntPtr)(arg2 + args - 5), - arg2[-2], arg2[-1])); -#endif - return (UIntPtr)nsp; + // This is called when returning a stack segment allocated for a SIP + [AccessedByRuntime("referenced from halstack.asm")] + [NoStackOverflowCheck] + internal static void ReturnSipStackSegment() + { + // @TODO: see note about disabling interrupts above. + bool en = Processor.DisableInterrupts(); + try { + Isa.CallbackOnInterruptStack(returnSipStackCallback, 0); + + unsafe { + // Sanity check: we freed from the previous segment, and + // should have set the thread context to point to this segment now. + + VTable.Assert(Isa.GetStackPointer() <= + Processor.GetCurrentThreadContext()->stackBegin); + VTable.Assert(Isa.GetStackPointer() >= + Processor.GetCurrentThreadContext()->stackLimit); + } + } + finally { + Processor.RestoreInterrupts(en); + } + } + + [NoStackOverflowCheck] + internal static unsafe void ActivatePreviousStackSegmentLimit() + { + // To avoid sprinkling [NoStackOverflowCheck] attributes + // on too many methods, we manually inline a couple of methods. + + // ThreadContext *context = Processor.GetCurrentThreadContext(); + ThreadRecord *threadRecord = Isa.GetCurrentThread(); + ThreadContext *context = (ThreadContext *) threadRecord; + StackHead *head = (StackHead *) + (context->stackBegin - sizeof(StackHead)); + + // Isa.StackLimit = head->prevLimit; + threadRecord->activeStackLimit = head->prevLimit; } [AccessedByRuntime("referenced from halstack.asm")] - [NoStackLinkCheck] - [NoStackOverflowCheck] - internal static unsafe void ReturnStackSegmentRaw(ref ThreadContext context, - UIntPtr begin, - UIntPtr limit) + [NoStackLinkCheckTrans] + internal static unsafe void ReturnStackSegmentRawCommon(ref ThreadContext context, + bool kernelAllocation, + bool initialStack) { + UIntPtr begin = context.stackBegin; + UIntPtr limit = context.stackLimit; + StackHead *head = (StackHead *)(begin - sizeof(StackHead)); -#if NO_TRACE_STACKS -#else +#if DO_TRACE_STACKS Kernel.Waypoint(669); #endif - UIntPtr addr = limit; - UIntPtr size = begin - limit; + UIntPtr addr = limit - SafetyBufferSize; + UIntPtr size = begin - limit + SafetyBufferSize; #if DEBUG_STACK_VERBOSE fixed (ThreadContext *ptr = &context) { - DebugStub.Print("ReturnStackSegmentRaw(ctx={0:x8},beg={1:x8},lim={2:x8}\n", - __arglist((UIntPtr)ptr, begin, limit)); + Tracing.Log(Tracing.Debug, + "ReturnStackSegmentRaw(ctx={0:x8}) [{1:x8}..{2:x8}]\n", + (UIntPtr)ptr, context.stackLimit, context.stackBegin); } -#if TEST_STACK_LINKING - DumpStack(Processor.GetFramePointer()); -#endif #endif #if !PAGING @@ -289,38 +428,76 @@ namespace Microsoft.Singularity.Memory #endif Process owner = Process.GetProcessByID(context.processId); - /* - // See note above in GetStackSegmentRaw - if ((owner != Process.kernelProcess) && - (addr >= BootInfo.KERNEL_BOUNDARY)) { - MemoryManager.UserFree(addr, MemoryManager.PagesFromBytes(size), owner); - } else { - MemoryManager.KernelFree(addr, MemoryManager.PagesFromBytes(size), owner); - } - */ - MemoryManager.KernelFree(addr, MemoryManager.PagesFromBytes(size), owner); + // + //// See note above in GetStackSegmentRaw + //if ((owner != Process.kernelProcess) && + //(addr >= BootInfo.KERNEL_BOUNDARY)) { + //MemoryManager.UserFree(addr, MemoryManager.PagesFromBytes(size), owner); + //} + //else { + //MemoryManager.KernelFree(addr, MemoryManager.PagesFromBytes(size), owner); + //} + // + MemoryManager.StackFree(addr, MemoryManager.PagesFromBytes(size), owner, kernelAllocation, initialStack); - ReturnCount++; - -#if DEBUG_STACK_VERBOSE - DebugStub.Print("ReturnStackSegment({0:x8}, {1:x8}\n", - __arglist(addr, size)); -#endif #if PAGING // See comments above. context.stackBegin = stackBegin; context.stackLimit = stackLimit; #endif + +#if DEBUG_STACK_VERBOSE + Tracing.Log(Tracing.Debug, + "ReturnStackSegment({0:x8}, {1:x8}) [{2:x8}..{3:x8}]\n", + addr, size, context.stackLimit, context.stackBegin); +#endif } + // + // This is called when a thread is destroyed and its last + // stack segment is returned to the system + // [AccessedByRuntime("referenced from halstack.asm")] - [NoStackLinkCheck] - [NoStackOverflowCheck] + [NoStackLinkCheckTrans] // NB: This function must execute in low-stack conditions! // See the comment at the top of this file. - internal static unsafe void ReturnStackSegment(ref ThreadContext context) + internal static void ReturnInitialStackSegment(ref ThreadContext context) { - ReturnStackSegmentRaw(ref context, context.stackBegin, context.stackLimit); + ReturnStackSegmentRawCommon(ref context, true, true); + } + + // + // This is called when cleaning up orphaned stack segments of the thread + // when it is destroyed, usually as a result of an exception such as SIP + // stack overflow. + // + [AccessedByRuntime("referenced from halstack.asm")] + [NoStackLinkCheckTrans] + // NB: This function must execute in low-stack conditions! + // See the comment at the top of this file. + internal static void ReturnStackSegment(ref ThreadContext context) + { + ReturnStackSegmentRawCommon(ref context, true, false); + } + + // + // This is invoked by ring0_halstack.asm when a SIP stack overflows + // and no more memory can be allocated from the OS for it. + // + // It is expected that the SIP is "failed fast" and does not + // return from this call. + // + [ExternalEntryPoint] + [AccessedByRuntime("reference from halstack.asm")] + internal static void StackOverflowForSIP() + { + DebugStub.WriteLine("******** SIP OOM on Stack, Failing Fast ********"); + + // This does not make a stack transition record + Thread.CurrentProcess.Stop((int)System.ProcessExitCode.ErrorDefault); + + // Should not return + DebugStub.Break(); } internal static unsafe void WalkStack(UIntPtr ebp) @@ -330,30 +507,17 @@ namespace Microsoft.Singularity.Memory kernMarker = Processor.GetCurrentThreadContext()->stackMarkers; procMarker = Processor.GetCurrentThreadContext()->processMarkers; - UIntPtr ebpKern = kernMarker != null ? kernMarker->EBP : UIntPtr.Zero; - UIntPtr ebpProc = procMarker != null ? procMarker->EBP : UIntPtr.Zero; + UIntPtr ebpKern = kernMarker != null ? kernMarker->calleeSaveRegisters.GetFramePointer() : UIntPtr.Zero; + UIntPtr ebpProc = procMarker != null ? procMarker->calleeSaveRegisters.GetFramePointer() : UIntPtr.Zero; #if DEBUG_STACK_VERBOSE - fixed (byte * begin = &LinkedStackBegin) { - fixed (byte * limit = &LinkedStackLimit) { - DebugStub.Print("LinkedStack: {0:x8}..{1:x8}\n", - __arglist((UIntPtr)begin, (UIntPtr)limit)); - } - } - fixed (byte * begin = &LinkStackBegin) { fixed (byte * limit = &LinkStackLimit) { - DebugStub.Print("LinkStack: {0:x8}..{1:x8}\n", + DebugStub.Print("LinkStack: {0:x8}..{1:x8}\n", __arglist((UIntPtr)begin, (UIntPtr)limit)); } } - fixed (byte * begin = &UnlinkStackBegin) { - fixed (byte * limit = &UnlinkStackLimit) { - DebugStub.Print("UnlinkStack: {0:x8}..{1:x8}\n", - __arglist((UIntPtr)begin, (UIntPtr)limit)); - } - } #endif DebugStub.Print("EBP={0:x8}, kernMarkers={1:x8}, procMarkers={2:x8}\n", @@ -369,7 +533,7 @@ namespace Microsoft.Singularity.Memory (UIntPtr)kernMarker->oldTransitionRecord, kernMarker->stackBottom)); kernMarker = kernMarker->oldTransitionRecord; - ebpKern = kernMarker != null ? kernMarker->EBP : UIntPtr.Zero; + ebpKern = kernMarker != null ? kernMarker->calleeSaveRegisters.GetFramePointer() : UIntPtr.Zero; } if (ebp == ebpProc) { DebugStub.Print("--proc--: {0:x8} {1:x8} {2:x8} {3:x8} {4:x8}: \n", @@ -380,7 +544,7 @@ namespace Microsoft.Singularity.Memory procMarker->stackBottom)); procMarker = procMarker->oldTransitionRecord; - ebpProc = procMarker != null ? procMarker->EBP : UIntPtr.Zero; + ebpProc = procMarker != null ? procMarker->calleeSaveRegisters.GetFramePointer() : UIntPtr.Zero; } DebugStub.Print("{0:x8}: {1:x8} {2:x8}\n", __arglist(ebp, @@ -393,596 +557,5 @@ namespace Microsoft.Singularity.Memory } // DebugStub.Break(); } - - //////////////////////////////////////////////////// LinkStackN Stubs. - // - // Note: As per the description in SDN20, the LinkStackN stubs use - // a non-standard calling convention. However, they are - // provided here to given Bartok a symbol to reference. - // - [ExternalStaticData] - internal static byte LinkedStackBegin; - [ExternalStaticData] - internal static byte LinkStackBegin; - [ExternalStaticData] - internal static byte LinkStackLimit; - [ExternalStaticData] - internal static byte UnlinkStackBegin; - [ExternalStaticData] - internal static byte UnlinkStackLimit; - [ExternalStaticData] - internal static byte LinkedStackLimit; - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [RequiredByBartok] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void LinkStack0(); // Copy 0 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [RequiredByBartok] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void LinkStack4(); // Copy 4 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [RequiredByBartok] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void LinkStack8(); // Copy 8 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [RequiredByBartok] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void LinkStack12(); // Copy 12 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [RequiredByBartok] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void LinkStack16(); // Copy 16 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [RequiredByBartok] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void LinkStack20(); // Copy 20 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [RequiredByBartok] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void LinkStack24(); // Copy 24 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [RequiredByBartok] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void LinkStack28(); // Copy 28 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [RequiredByBartok] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void LinkStack32(); // Copy 32 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [RequiredByBartok] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void LinkStack36(); // Copy 36 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [RequiredByBartok] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void LinkStack40(); // Copy 40 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [RequiredByBartok] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void LinkStack44(); // Copy 44 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [RequiredByBartok] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void LinkStack48(); // Copy 48 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [RequiredByBartok] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void LinkStack52(); // Copy 52 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [RequiredByBartok] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void LinkStack56(); // Copy 56 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [RequiredByBartok] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void LinkStack60(); // Copy 60 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [RequiredByBartok] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void LinkStack64(); // Copy 64 bytes of arguments on stack. - - - ////////////////////////////////////////////////// UnlinkStackN Stubs. - // - // Note: As per the description in SDN20, the UnlinkStackN stubs use - // a non-standard calling convention and should be referenced - // only by the LinkStackN stubs which insert them into the stack. - // However, they are provided here for completeness. - // - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void UnlinkStack0(); // Remove 0 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void UnlinkStack4(); // Remove 4 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void UnlinkStack8(); // Remove 8 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void UnlinkStack12(); // Remove 12 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void UnlinkStack16(); // Remove 16 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void UnlinkStack20(); // Remove 20 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void UnlinkStack24(); // Remove 24 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void UnlinkStack28(); // Remove 28 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void UnlinkStack32(); // Remove 32 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void UnlinkStack36(); // Remove 36 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void UnlinkStack40(); // Remove 40 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void UnlinkStack44(); // Remove 44 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void UnlinkStack48(); // Remove 48 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void UnlinkStack52(); // Remove 52 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void UnlinkStack56(); // Remove 56 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void UnlinkStack60(); // Remove 60 bytes of arguments on stack. - - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void UnlinkStack64(); // Remove 64 bytes of arguments on stack. - -#if TEST_STACK_LINKING - [NoStackLinkCheck] - internal static unsafe void TestStack() - { - ThreadContext *thread = Processor.GetCurrentThreadContext(); - ProcessorContext *processor = Processor.GetCurrentProcessorContext(); - UIntPtr oldLimit = thread->stackLimit; - UIntPtr newLimit = (Processor.GetStackPointer() - 512); - - DebugStub.Print("************************************************************\n"); - DebugStub.Print("threadContext={0:x8}, stackBegin={1:x8}, "+ - "stackLimit={2:x8}, "+ - "esp={3:x8}, stackLimit={4:x8}\n", - __arglist( - (UIntPtr)thread, - thread->stackBegin, - oldLimit, - Processor.GetStackPointer(), - newLimit)); - DebugStub.Print("processorContext={0:x8}, "+ - "schedulerStackBegin={1:x8},"+ - "schedulerStackLimit={2:x8}\n", - __arglist( - (UIntPtr)processor, - processor->schedulerStackBegin, - processor->schedulerStackLimit)); - DebugStub.Print("[TestStack.\n"); - Test1(1); - DebugStub.Print(".TestStack]\n"); - thread->stackLimit = oldLimit; - } - - internal static unsafe void DumpStack(UIntPtr ebp) - { - ThreadContext *thread = Processor.GetCurrentThreadContext(); - UIntPtr begin = thread->stackBegin; - - DebugStub.Print(" [{0:x8}..{1:x8}]: "+ - "{2:x8} {3:x8} {4:x8} {5:x8} {6:x8} {7:x8} {8:x8} {8:x8}\n", - __arglist( - ebp, - begin, - ((uint*)ebp)[0], - ((uint*)ebp)[1], - ((uint*)ebp)[2], - ((uint*)ebp)[3], - ((uint*)ebp)[4], - ((uint*)ebp)[5], - ((uint*)ebp)[6], - ((uint*)ebp)[7])); - } - - [NoInline] - [StackLinkCheck] - internal static int Test1(int a) - { - DebugStub.Print("[Test1. ({0})\n", __arglist(a)); - DumpStack(Processor.GetFramePointer()); - TestStackLink1024(); - a = Test2(a, a + 1) + 1; - DebugStub.Print(".Test1] = {0}\n", __arglist(a)); - return a; - } - - [OutsideGCDomain] - [NoInline] - [StackLinkCheck] - internal static int Test2(int a, int b) - { - DebugStub.Print("[Test2. ({0}, {1})\n", - __arglist(a, b)); - DumpStack(Processor.GetFramePointer()); - TestStackLink4096(); //TestStackLink1024(); - a = Test3(a, a + 1, a + 2) + 1; - DebugStub.Print(".Test2] = {0}", __arglist(a)); - return a; - } - - [NoInline] - [StackLinkCheck] - internal static int Test3(int a, int b, int c) - { - DebugStub.Print("[Test3. ({0},{1},{2})", - __arglist(a, b, c)); - DumpStack(Processor.GetFramePointer()); - TestStackLink4096(); //TestStackLink1024(); - a = Test4(a, a + 1, a + 2, a + 3) + 1; - DebugStub.Print(".Test3] = {0}", __arglist(a)); - return a; - } - - [OutsideGCDomain] - [NoInline] - [StackLinkCheck] - internal static int Test4(int a, int b, int c, int d) - { - DebugStub.Print("[Test4. ({0},{1},{2},{3})", - __arglist(a, b, c, d)); - DumpStack(Processor.GetFramePointer()); - TestStackLink4096(); //TestStackLink2048(); - a = Test5(a, a + 1, a + 2, a + 3, a + 4) + 1; - DebugStub.Print(".Test4] = {0}", __arglist(a)); - return a; - } - - [NoInline] - [StackLinkCheck] - internal static int Test5(int a, int b, int c, int d, int e) - { - DebugStub.Print("[Test5. ({0},{1},{2},{3},{4})", - __arglist(a, b, c, d, e)); - DumpStack(Processor.GetFramePointer()); - TestStackLink4096(); //TestStackLink2048(); - a = Test6(a, a + 1, a + 2, a + 3, a + 4, a + 5) + 1; - DebugStub.Print(".Test5] = {0}", __arglist(a)); - return a; - } - - [OutsideGCDomain] - [NoInline] - [StackLinkCheck] - internal static int Test6(int a, int b, int c, int d, int e, int f) - { - DebugStub.Print("[Test6. ({0},{1},{2},{3},{4},{5})", - __arglist(a, b, c, d, e, f)); - DumpStack(Processor.GetFramePointer()); - TestStackLink4096(); //TestStackLink2048(); - a = Test7(a, a + 1, a + 2, a + 3, a + 4, a + 5, a + 6) + 1; - DebugStub.Print(".Test6] = {0}", __arglist(a)); - return a; - } - - [NoInline] - [StackLinkCheck] - internal static int Test7(int a, int b, int c, int d, int e, int f, - int g) - { - DebugStub.Print("[Test7. ({0},{1},{2},{3},{4},{5},{6})", - __arglist(a, b, c, d, e, f, g)); - DumpStack(Processor.GetFramePointer()); - TestStackLink4096(); - a = Test8(a, a + 1, a + 2, a + 3, a + 4, a + 5, a + 6, a + 7) + 1; - DebugStub.Print(".Test7] = {0}", __arglist(a)); - return a; - } - - [OutsideGCDomain] - [NoInline] - [StackLinkCheck] - internal static int Test8(int a, int b, int c, int d, int e, int f, - int g, int h) - { - DebugStub.Print("[Test8. ({0},{1},{2},{3},{4},{5},{6},{7})", - __arglist(a, b, c, d, e, f, g, h)); - DumpStack(Processor.GetFramePointer()); - TestStackLink4096(); - a = Test9(a, a + 1, a + 2, a + 3, a + 4, a + 5, a + 6, a + 7, - a + 8) + 1; - DebugStub.Print(".Test8] = {0}", __arglist(a)); - return a; - } - - [NoInline] - [StackLinkCheck] - internal static int Test9(int a, int b, int c, int d, int e, int f, - int g, int h, int i) - { - DebugStub.Print("[Test9. ({0},{1},{2},{3},{4},{5},{6},{7},{8})", - __arglist(a, b, c, d, e, f, g, h, i)); - DumpStack(Processor.GetFramePointer()); - TestStackLink4096(); - a = Test10(a, a + 1, a + 2, a + 3, a + 4, a + 5, a + 6, a + 7, - a + 8, a + 9) + 1; - DebugStub.Print(".Test9] = {0}", __arglist(a)); - return a; - } - - [OutsideGCDomain] - [NoInline] - [StackLinkCheck] - internal static int Test10(int a, int b, int c, int d, int e, int f, - int g, int h, int i, int j) - { - DebugStub.Print("[Test10. ({0},{1},{2},{3},{4},{5},{6},{7},{8},"+ - "{9})", - __arglist(a, b, c, d, e, f, g, h, i, j)); - DumpStack(Processor.GetFramePointer()); - TestStackLink4096(); - a = Test11(a, a + 1, a + 2, a + 3, a + 4, a + 5, a + 6, a + 7, - a + 8, a + 9, a + 10) + 1; - DebugStub.Print(".Test10] = {0}", __arglist(a)); - return a; - } - - [NoInline] - [StackLinkCheck] - internal static int Test11(int a, int b, int c, int d, int e, int f, - int g, int h, int i, int j, int k) - { - DebugStub.Print("[Test11. ({0},{1},{2},{3},{4},{5},{6},{7},{8},"+ - "{9},{10})", - __arglist(a, b, c, d, e, f, g, h, i, j, k)); - DumpStack(Processor.GetFramePointer()); - TestStackLink4096(); - a = Test12(a, a + 1, a + 2, a + 3, a + 4, a + 5, a + 6, a + 7, - a + 8, a + 9, a + 10, a + 11) + 1; - DebugStub.Print(".Test11] = {0}", __arglist(a)); - return a; - } - - [OutsideGCDomain] - [NoInline] - [StackLinkCheck] - internal static int Test12(int a, int b, int c, int d, int e, int f, - int g, int h, int i, int j, int k, int l) - { - DebugStub.Print("[Test12. ({0},{1},{2},{3},{4},{5},{6},{7},{8},"+ - "{9},{10},{11})", - __arglist(a, b, c, d, e, f, g, h, i, j, k, l)); - DumpStack(Processor.GetFramePointer()); - TestStackLink4096(); - a = Test13(a, a + 1, a + 2, a + 3, a + 4, a + 5, a + 6, a + 7, - a + 8, a + 9, a + 10, a + 11, a + 12) + 1; - DebugStub.Print(".Test12] = {0}", __arglist(a)); - return a; - } - - [NoInline] - [StackLinkCheck] - internal static int Test13(int a, int b, int c, int d, int e, int f, - int g, int h, int i, int j, int k, int l, - int m) - { - DebugStub.Print("[Test13. ({0},{1},{2},{3},{4},{5},{6},{7},{8},"+ - "{9},{10},{11},{12})", - __arglist(a, b, c, d, e, f, g, h, i, j, k, l, m)); - DumpStack(Processor.GetFramePointer()); - TestStackLink4096(); - a = Test14(a, a + 1, a + 2, a + 3, a + 4, a + 5, a + 6, a + 7, - a + 8, a + 9, a + 10, a + 11, a + 12, a + 13) + 1; - DebugStub.Print(".Test13] = {0}", __arglist(a)); - return a; - } - - [OutsideGCDomain] - [NoInline] - [StackLinkCheck] - internal static int Test14(int a, int b, int c, int d, int e, int f, - int g, int h, int i, int j, int k, int l, - int m, int n) - { - DebugStub.Print("[Test14. ({0},{1},{2},{3},{4},{5},{6},{7},{8},"+ - "{9},{10},{11},{12},{13})", - __arglist(a, b, c, d, e, f, g, h, i, j, k, l, - m, n)); - DumpStack(Processor.GetFramePointer()); - TestStackLink4096(); - a = Test15(a, a + 1, a + 2, a + 3, a + 4, a + 5, a + 6, a + 7, - a + 8, a + 9, a + 10, a + 11, a + 12, a + 13, a + 14) + 1; - DebugStub.Print(".Test14] = {0}", __arglist(a)); - return a; - } - - [NoInline] - [StackLinkCheck] - internal static int Test15(int a, int b, int c, int d, int e, int f, - int g, int h, int i, int j, int k, int l, - int m, int n, int o) - { - DebugStub.Print("[Test15. ({0},{1},{2},{3},{4},{5},{6},{7},{8},"+ - "{9},{10},{11},{12},{13},{14})", - __arglist(a, b, c, d, e, f, g, h, i, j, k, l, - m, n, o)); - DumpStack(Processor.GetFramePointer()); - TestStackLink8192(); - a = Test16(a, a + 1, a + 2, a + 3, a + 4, a + 5, a + 6, a + 7, - a + 8, a + 9, a + 10, a + 11, a + 12, a + 13, a + 14, - a + 15) + 1; - DebugStub.Print(".Test15] = {0}", __arglist(a)); - return a; - } - - [OutsideGCDomain] - [NoInline] - [StackLinkCheck] - internal static int Test16(int a, int b, int c, int d, int e, int f, - int g, int h, int i, int j, int k, int l, - int m, int n, int o, int p) - { - DebugStub.Print("[Test16. ({0},{1},{2},{3},{4},{5},{6},{7},{8},"+ - "{9},{10},{11},{12},{13},{14},{15})", - __arglist(a, b, c, d, e, f, g, h, i, j, k, l, - m, n, o, p)); - DumpStack(Processor.GetFramePointer()); - TestStackLink8192(); - a = Test17(a, a + 1, a + 2, a + 3, a + 4, a + 5, a + 6, a + 7, - a + 8, a + 9, a + 10, a + 11, a + 12, a + 13, a + 14, - a + 15, a + 16) + 1; - DebugStub.Print(".Test16] = {0}", __arglist(a)); - return a; - } - - [NoInline] - [StackLinkCheck] - internal static int Test17(int a, int b, int c, int d, int e, int f, - int g, int h, int i, int j, int k, int l, - int m, int n, int o, int p, int q) - { - DebugStub.Print("[Test17. ({0},{1},{2},{3},{4},{5},{6},{7},{8},"+ - "{9},{10},{11},{12},{13},{14},{15},{16})", - __arglist(a, b, c, d, e, f, g, h, i, j, k, l, - m, n, o, p, q)); - DumpStack(Processor.GetFramePointer()); - TestStackLink8192(); - a = a + 1; - WalkStack(Processor.GetFramePointer()); - DebugStub.Print(".Test17] = {0}", __arglist(a)); - return a; - } - - [AccessedByRuntime("output to header : defined in stacks.cpp")] - [StackBound(1024)] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void TestStackLink1024(); - - [AccessedByRuntime("output to header : defined in stacks.cpp")] - [StackBound(2048)] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void TestStackLink2048(); - - [AccessedByRuntime("output to header : defined in stacks.cpp")] - [StackBound(4096)] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void TestStackLink4096(); - - [AccessedByRuntime("output to header : defined in stacks.cpp")] - [StackBound(8192)] - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void TestStackLink8192(); - -#endif } } diff --git a/base/Kernel/Singularity/Memory/VMManager.cs b/base/Kernel/Singularity/Memory/VMManager.cs index ed710f6..b95bc3a 100644 --- a/base/Kernel/Singularity/Memory/VMManager.cs +++ b/base/Kernel/Singularity/Memory/VMManager.cs @@ -11,15 +11,14 @@ // This module manages page-translation tables for virtual memory // -#if PAGING - using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using Microsoft.Singularity; -using Microsoft.Singularity.X86; +using Microsoft.Singularity.Hal; +using Microsoft.Singularity.Isal.IX; using PageType = System.GCs.PageType; @@ -27,6 +26,7 @@ namespace Microsoft.Singularity.Memory { [NoCCtor] [CLSCompliant(false)] + [AccessedByRuntime("referenced from halkd.cpp")] public class VMManager { // @@ -75,7 +75,7 @@ namespace Microsoft.Singularity.Memory // These are the PDPT entries for the kernel space. Whether we // are using one, two or three of them is determined by - // BootInfo.KERNEL_BOUNDARY + // Platform.KERNEL_BOUNDARY private static PhysicalAddress kernelPDPE1; private static PhysicalAddress kernelPDPE2; private static PhysicalAddress kernelPDPE3; @@ -103,7 +103,7 @@ namespace Microsoft.Singularity.Memory // The PDPT always lives in the user space, so its mapping gets // switched naturally when we change spaces - private static PDPT* pdptAddr; + private unsafe static PDPT* pdptAddr; // Here are two page of VM space we keep available for scratch use. // We must take care to use them atomically! @@ -117,19 +117,26 @@ namespace Microsoft.Singularity.Memory private static UIntPtr uncachedWindowBase; private static UIntPtr uncachedWindowSize; + // Here is where we have situated the bootRecord + private static UIntPtr bootRecordBase; + private static UIntPtr bootRecordLimit; + + // Here is where we have situated the executable image that + // contains the kernel (where it gets put by the hal loader) + private static UIntPtr kernelImageBase; + private static UIntPtr kernelImageLimit; + // Here is where we have situated the executable images that // are actually in high physical memory (where they get put // by the boot loader) - private static UIntPtr execImageBase; - private static UIntPtr execImageSize; + private static UIntPtr miniDumpBase; + private static UIntPtr miniDumpLimit; -#if RESERVE_SMAP // Here is where we remap areas of physical memory that are // reported as reserved by the SMAP (in the order they appear // in the SMAP) private static UIntPtr smapRemapBase; private static UIntPtr smapRemapSize; -#endif // Here are the bounds of the VM space that the kernel should use // for its GC heap @@ -148,10 +155,10 @@ namespace Microsoft.Singularity.Memory ///////////////////////////////////// // Each PT describes 512 4K pages - private const uint SpacePerPT = 4 * 1024 * 512; // 2MB + private const uint SpacePerPT = 4 * 1024 * 512; // 2MB // Each PDT describes 512 PTs - private const uint SpacePerPDT = 512 * SpacePerPT; // 1GB + private const uint SpacePerPDT = 512 * SpacePerPT; // 1GB ///////////////////////////////////// // PROPERTIES @@ -189,89 +196,176 @@ namespace Microsoft.Singularity.Memory // This must be called with paging TURNED OFF, and it has the // side effect of TURNING PAGING ON! // + + // + // The virtual address map used by VMManager is set up in Initialize + // + // Part of the layout is inheirated from MemoryManager who (ignoring SMAP + // complications) has set up a range for the pageBlocks and a physicalHeap + // for I/O memory below the 0xFFFFFF threshold. This function picks up + // from there and lays out the rest of the virtual address space. + // + // TODO: Create region structure and use it here to interleave fixed address + // blocks and SMAP blocks + // TODO: Better checks for overlapping range requests + internal static unsafe void Initialize() { + Platform p = Platform.ThePlatform; + uint numKernelPTs = Platform.KERNEL_BOUNDARY / SpacePerPT; + UIntPtr userSpaceSize = Platform.MAX_VIRTUAL_ADDR - Platform.KERNEL_BOUNDARY + 1; + UIntPtr nextVirtualAddr = MemoryManager.IOMemoryBase.Value + Platform.IO_MEMORY_SIZE; + + kernelPDTOffset = MemoryManager.BytesFromPages(numKernelPTs); + numKernelPDPEs = (uint)(Platform.KERNEL_BOUNDARY / SpacePerPDT); + scratchSpin = new SpinLock(SpinLock.Types.VMManager); + // How big is the kernel space? - DebugStub.Assert(MemoryManager.BytesNotAligned(BootInfo.KERNEL_BOUNDARY,SpacePerPDT) == 0); - numKernelPDPEs = (uint)(BootInfo.KERNEL_BOUNDARY / SpacePerPDT); + DebugStub.Assert(MemoryManager.BytesNotAligned(Platform.KERNEL_BOUNDARY,SpacePerPDT) == 0); DebugStub.Assert(numKernelPDPEs <= 3); - // Figure out where the kernel page-table pages will live. - // We'll stick it immediately above the IO memory heap, on + // First off locate the kernel and bootRecord which must live + // in identity mapped memory + if (p.KernelDllSize != 0) { + kernelImageBase = MemoryManager.PagePad(p.KernelDllBase); + kernelImageLimit = MemoryManager.PagePad(p.KernelDllBase + p.KernelDllSize); + + DebugStub.WriteLine("Executable Image at 0x{0:x8} - 0x{1:x8}.", + __arglist(kernelImageBase, + kernelImageLimit)); + + if (nextVirtualAddr < kernelImageLimit) { + nextVirtualAddr = kernelImageLimit; + } + } + + if (p.BootAllocatedMemorySize != 0) { + bootRecordBase = MemoryManager.PagePad(p.BootAllocatedMemory); + bootRecordLimit = MemoryManager.PagePad( + p.BootAllocatedMemory + p.BootAllocatedMemorySize); + + DebugStub.WriteLine("BootRecord at 0x{0:x8} - 0x{1:x8}.", + __arglist(bootRecordBase, + bootRecordLimit)); + + if (nextVirtualAddr < bootRecordLimit) { + nextVirtualAddr = bootRecordLimit; + } + } + + // Now figure out where the kernel page-table pages will live. + // We'll stick it immediately above the identity mapped range, on // a page boundary. - kernelPageTableBase = (UIntPtr)MemoryManager.IOMemoryBase.Value; - kernelPageTableBase += BootInfo.IO_MEMORY_SIZE; - kernelPageTableBase = MemoryManager.PagePad(kernelPageTableBase); + identityMapLimit = MemoryManager.PagePad(nextVirtualAddr); - // Everything below this limit is identity-mapped - identityMapLimit = kernelPageTableBase; - - // Now figure out the kernel's page-table layout - uint numPTs = BootInfo.KERNEL_BOUNDARY / SpacePerPT; - kernelPDTOffset = MemoryManager.BytesFromPages(numPTs); // numKernelPDPEs == the number of PDTs, by definition - kernelPageTableLimit = kernelPageTableBase + MemoryManager.BytesFromPages(numPTs + numKernelPDPEs); + kernelPageTableBase = nextVirtualAddr; + nextVirtualAddr = + kernelPageTableLimit = MemoryManager.PagePad(kernelPageTableBase + + MemoryManager.BytesFromPages(numKernelPTs + numKernelPDPEs)); + + DebugStub.WriteLine("KernelPageTable initialized at 0x{0:x8} - 0x{1:x8}.", + __arglist(kernelPageTableBase, + kernelPageTableLimit)); + + // Locate the uncached window immediately after the kernel's page-table space + uncachedWindowBase = nextVirtualAddr; + uncachedWindowSize = Platform.UNCACHED_MAPPED; + nextVirtualAddr = MemoryManager.PagePad(uncachedWindowBase + + uncachedWindowSize); + + DebugStub.WriteLine("Uncached Window at 0x{0:x8} - 0x{1:x8}.", + __arglist(uncachedWindowBase, + uncachedWindowBase + uncachedWindowSize)); + + // Now accomidate the minidump containing the executable cache. + if (p.MiniDumpBase != p.MiniDumpLimit) { + miniDumpBase = nextVirtualAddr; + nextVirtualAddr = + miniDumpLimit = MemoryManager.PagePad(miniDumpBase + p.MiniDumpLimit - p.MiniDumpBase); + + DebugStub.WriteLine("MiniDump at 0x{0:x8} - 0x{1:x8}.", + __arglist(miniDumpBase, + miniDumpLimit)); + } + + // Locate the SMAP window next + smapRemapBase = nextVirtualAddr; + smapRemapSize = 0; + + // Calculate its size + SMAPINFO* smap = (SMAPINFO*)p.Smap32; + for (uint i = 0; i < p.SmapCount; i++) { + if ((smap[i].type != (ulong)SMAPINFO.AddressType.Free) && + (smap[i].addr >= identityMapLimit)) { + smapRemapSize += smap[i].size; + } + } + if (smapRemapSize != 0) { + DebugStub.WriteLine("SMAP reserve at 0x{0:x8} - 0x{1:x8}.", + __arglist(smapRemapBase, + smapRemapBase+smapRemapSize)); + + nextVirtualAddr = MemoryManager.PagePad(smapRemapBase + + smapRemapSize); + } + + // Locate the one-page scratch window immediately above the smap area + scratchAddress1 = nextVirtualAddr; + scratchAddress2 = scratchAddress1 + MemoryManager.PageSize; + nextVirtualAddr = scratchAddress2 + MemoryManager.PageSize; + + DebugStub.WriteLine("scratch#1 at 0x{0:x8}, scratch#2 at 0x{1:x8}.", + __arglist(scratchAddress1, + scratchAddress2)); + + // That's all for our fixed low-memory structures + kernelHeapBase = nextVirtualAddr; + kernelHeapLimit = Platform.KERNEL_BOUNDARY; + DebugStub.Assert(kernelHeapLimit > kernelHeapBase); + + DebugStub.WriteLine("KernelHeap initialized at 0x{0:x8} - 0x{1:x8}.", + __arglist(kernelHeapBase, + kernelHeapLimit)); // Stick the user-space page-table mapping right at the // beginning of the user's address space - userPageTableBase = BootInfo.KERNEL_BOUNDARY; - UIntPtr userSpaceSize = BootInfo.MAX_VIRTUAL_ADDR - BootInfo.KERNEL_BOUNDARY + 1; - numPTs = (uint)(userSpaceSize / SpacePerPT); - userPDTOffset = MemoryManager.BytesFromPages(numPTs); + userPageTableBase = Platform.KERNEL_BOUNDARY; + uint numPTs = (uint)(userSpaceSize / SpacePerPT); uint numPDTs = (uint)(userSpaceSize / SpacePerPDT); + userPDTOffset = MemoryManager.BytesFromPages(numPTs); pdptAddr = (PDPT*)(userPageTableBase + MemoryManager.BytesFromPages(numPTs + numPDTs)); // Count the PDPT as part of the user's page table so we don't // step on the PDPT mapping later userPageTableLimit = (UIntPtr)pdptAddr + MemoryManager.PageSize; + DebugStub.WriteLine("UserPageTable initialized at 0x{0:x8} - 0x{1:x8}.", + __arglist(userPageTableBase, + userPageTableLimit)); + // Define the limits of general-purpose user space userHeapBase = userPageTableLimit; - userHeapLimit = BootInfo.MAX_VIRTUAL_ADDR; + userHeapLimit = Platform.MAX_VIRTUAL_ADDR; userHeapLimit = MemoryManager.Trunc(userHeapLimit, MemoryManager.PageSize); - // Locate the uncached window immediately after the kernel's page-table space - uncachedWindowBase = kernelPageTableLimit; - uncachedWindowSize = BootInfo.UNCACHED_MAPPED; - - BootInfo bi = BootInfo.GetBootInfo(); - - // Locate the executable images right after the uncached-window - execImageBase = MemoryManager.PagePad(uncachedWindowBase + uncachedWindowSize); - execImageSize = bi.DumpSize32; - -#if RESERVE_SMAP - // Locate the SMAP window next - smapRemapBase = MemoryManager.PagePad(execImageBase + execImageSize); - smapRemapSize = 0; - - // Calculate its size - SMAPINFO *smap = (SMAPINFO*)bi.SmapData32; - - for (uint i = 0; i < bi.SmapCount; i++) { - if ((smap[i].type != (ulong)SMAPINFO.AddressType.Free) && - (smap[i].addr >= identityMapLimit)) { - smapRemapSize += smap[i].size; - } - } - // Locate the one-page scratch window immediately above the smap area - scratchAddress1 = MemoryManager.PagePad(smapRemapBase + smapRemapSize); -#else - scratchAddress1 = MemoryManager.PagePad(execImageBase + execImageSize); -#endif - - scratchAddress2 = scratchAddress1 + MemoryManager.PageSize; - scratchSpin = new SpinLock(); - - // That's all for our fixed low-memory structures - kernelHeapBase = scratchAddress2 + MemoryManager.PageSize; - kernelHeapLimit = BootInfo.KERNEL_BOUNDARY; - DebugStub.Assert(kernelHeapLimit > kernelHeapBase); + DebugStub.WriteLine("UserHeap initialized at 0x{0:x8} - 0x{1:x8}.", + __arglist(userHeapBase, + userHeapLimit)); // Now we can create the kernel's address space! BootstrapSpace = CreateBootstrapSpace(); // TURN ON PAGING! + // + // Save the kernel's operating Pdpt value so that non-boot CPU's + // may have the proper map set by the boot loader in undump.cpp + // + Platform nativePlatform = Platform.ThePlatform; + // temporary workaround for Bartok bug + AddressSpace b = BootstrapSpace; + nativePlatform.KernelPdpt64 = b.PdptPage.Value; + Processor.EnablePaging(BootstrapSpace); #if DEBUG @@ -316,16 +410,18 @@ namespace Microsoft.Singularity.Memory internal static void Finalize() { - // Restore the PDPT that the boot loader created - Processor.ChangeAddressSpace(new AddressSpace( - new PhysicalAddress(BootInfo.GetBootInfo().Pdpt32))); + Platform nativePlatform = Platform.ThePlatform; + if (nativePlatform != null) { + // Restore the PDPT that the boot loader created + Processor.ChangeAddressSpace(new AddressSpace(new PhysicalAddress(nativePlatform.Pdpt32))); + } } internal static void PostGCInitialize() { // Create a spinlock to protect the kernel's page-mapping // structures - KernelMappingLock = new SmartSpinlock(); + KernelMappingLock = new SmartSpinlock(SpinLock.Types.VMKernelMapping); } // @@ -369,9 +465,10 @@ namespace Microsoft.Singularity.Memory SmartSpinlock mappingLock = null; - if (virtualAddr < BootInfo.KERNEL_BOUNDARY) { + if (virtualAddr < Platform.KERNEL_BOUNDARY) { mappingLock = KernelMappingLock; // Might be null during early boot - } else { + } + else { DebugStub.Assert(inDomain != null); mappingLock = inDomain.UserMappingLock; DebugStub.Assert(mappingLock != null); @@ -423,9 +520,10 @@ namespace Microsoft.Singularity.Memory SmartSpinlock mappingLock = null; - if (limitAddr <= BootInfo.KERNEL_BOUNDARY) { + if (limitAddr <= Platform.KERNEL_BOUNDARY) { mappingLock = KernelMappingLock; // Might be null during early boot - } else { + } + else { DebugStub.Assert(inDomain != null); mappingLock = inDomain.UserMappingLock; DebugStub.Assert(mappingLock != null); @@ -439,12 +537,13 @@ namespace Microsoft.Singularity.Memory } try { - for( UIntPtr i = 0; i < numPages; i++ ) { + for (UIntPtr i = 0; i < numPages; i++) { MapPageNoLock(new PhysicalAddress(stepPhys), stepVirt); stepPhys += MemoryManager.PageSize; stepVirt += MemoryManager.PageSize; } - } finally { + } + finally { if (mappingLock != null) { mappingLock.Unlock(iflag); } @@ -480,7 +579,7 @@ namespace Microsoft.Singularity.Memory DebugStub.Assert(MemoryManager.IsPageAligned(virtualAddr)); UIntPtr stepVirt = virtualAddr; - for( UIntPtr i = 0; i < numPages; i++ ) { + for (UIntPtr i = 0; i < numPages; i++) { UnmapPage(stepVirt); stepVirt += MemoryManager.PageSize; } @@ -497,7 +596,8 @@ namespace Microsoft.Singularity.Memory if (pte->Valid) { return new PhysicalAddress(pte->Address); - } else { + } + else { return PhysicalAddress.Null; } } @@ -517,22 +617,25 @@ namespace Microsoft.Singularity.Memory // This is the address of a kernel page-table page. // There should always be sufficient PTs to describe these. return IsKernelDescriptorPageMapped(pageAddr); - } else if ((pageAddr >= userPageTableBase) && + } + else if ((pageAddr >= userPageTableBase) && (pageAddr < userPageTableLimit)) { // User-land page-table page return IsUserDescriptorPageMapped(pageAddr); - } else { + } + else { // This page isn't part of the page table, so its // PT may be missing. Check that. PT* pt = GetPT(MemoryManager.Trunc(pageAddr, SpacePerPT)); - if (pageAddr >= BootInfo.KERNEL_BOUNDARY) { + if (pageAddr >= Platform.KERNEL_BOUNDARY) { if (!IsUserDescriptorPageMapped((UIntPtr)pt)) { // The PT for this address isn't even mapped, // so the address itself can't be mapped! return false; } - } else { + } + else { if (!IsKernelDescriptorPageMapped((UIntPtr)pt)) { return false; } @@ -558,17 +661,10 @@ namespace Microsoft.Singularity.Memory return true; } - if ((virtualAddr >= execImageBase) && - (limitAddr <= execImageBase + execImageSize)) { - return true; - } - -#if RESERVE_SMAP if ((virtualAddr >= smapRemapBase) && (limitAddr <= smapRemapBase + smapRemapSize)) { return true; } -#endif if ((virtualAddr >= uncachedWindowBase) && (limitAddr <= uncachedWindowBase + uncachedWindowSize)) { @@ -586,53 +682,50 @@ namespace Microsoft.Singularity.Memory internal static unsafe UIntPtr TranslatePhysicalRange(PhysicalAddress physAddress, UIntPtr numBytes) { - BootInfo bi = BootInfo.GetBootInfo(); + Platform p = Platform.ThePlatform; ulong physAddr = physAddress.Value; ulong physLimit = physAddr + (ulong)numBytes; // Some physical pointers are identity-mapped - if (physLimit < identityMapLimit) { + if (physLimit <= identityMapLimit) { return physAddr; } // Is this an address into the executable-image range // created by the loader? - if ((physAddr >= bi.DumpAddr32) && - (physLimit <= bi.DumpAddr32 + execImageSize)) { + if ((physAddr >= p.MiniDumpBase) && (physLimit <= p.MiniDumpLimit)) { // Assume we move the exec image lower in memory - DebugStub.Assert(bi.DumpAddr32 > execImageBase); - return (UIntPtr)(physAddr - (bi.DumpAddr32 - execImageBase)); + DebugStub.Assert(p.MiniDumpBase > miniDumpBase); + return (UIntPtr)(physAddr - (Platform.ThePlatform.MiniDumpBase - miniDumpBase)); + } + + // Is this a pointer into the uncached window at the + // top of physical memory? + ulong uncachedLimit = (ulong)Platform.UNCACHED_PHYSICAL + + (ulong)Platform.UNCACHED_MAPPED; + + if ((physAddr >= Platform.UNCACHED_PHYSICAL) && + (physLimit <= uncachedLimit)) { + return uncachedWindowBase + (UIntPtr)(physAddr - Platform.UNCACHED_PHYSICAL); } -#if RESERVE_SMAP // Is this an address into an SMAP-reserved area? - SMAPINFO *smap = (SMAPINFO*)bi.SmapData32; + SMAPINFO *smap = (SMAPINFO*)p.Smap32; UIntPtr stepVirtual = smapRemapBase; - for (uint i = 0; i < bi.SmapCount; i++) { + for (uint i = 0; i < p.SmapCount; i++) { if ((smap[i].type != (ulong)SMAPINFO.AddressType.Free) && (smap[i].addr >= identityMapLimit)) { if ((physAddr >= smap[i].addr) && (physLimit <= smap[i].addr + smap[i].size)) { // Match! - return stepVirtual + (physAddr - smap[i].addr) + return stepVirtual + (physAddr - smap[i].addr); } stepVirtual += smap[i].size; } } -#endif - - // Is this a pointer into the uncached window at the - // top of physical memory? - ulong uncachedLimit = (ulong)BootInfo.UNCACHED_PHYSICAL + - (ulong)BootInfo.UNCACHED_MAPPED; - - if ((physAddr >= BootInfo.UNCACHED_PHYSICAL) && - (physLimit <= uncachedLimit)) { - return uncachedWindowBase + (UIntPtr)(physAddr - BootInfo.UNCACHED_PHYSICAL); - } // No match return UIntPtr.Zero; @@ -704,9 +797,11 @@ namespace Microsoft.Singularity.Memory // // Zero through BootInfo.PHYSICAL_DISABLED is always kept unmapped // - // Memory from PHYSICAL_DISABLED to the top of the I/O heap is + // Memory from PhysicalBase to the top of the I/O heap is // identity-mapped (we could probably stand to punch some holes - // in this) + // in this). + // + // The kernel image and the boot record need to be identity mapped. // // Next comes the page-table area, starting at kernelPageTableBase. // It is large enough to cover all the pages that could be @@ -720,6 +815,7 @@ namespace Microsoft.Singularity.Memory // except that from UNCACHED_PHYSICAL to the top (4GB) is marked // uncached. // + Platform p = Platform.ThePlatform; // To get started, we need a PDPT. PhysicalAddress phys = PhysicalPages.AllocPage(); @@ -727,41 +823,37 @@ namespace Microsoft.Singularity.Memory PDPT* pdpt = (PDPT*)phys.Value; pdpt->Init(); - // Identity-map the range from PHYSICAL_DISABLED to the top - // of the IO heap - BootstrapMapRange(pdpt, - (UIntPtr)BootInfo.PHYSICAL_DISABLED, identityMapLimit, - new PhysicalAddress(BootInfo.PHYSICAL_DISABLED), + DebugStub.WriteLine("Allocated PDPT at 0x{0:x8}.", __arglist(phys.Value)); + + // Start out mapping the entire identityMapped region as writable, not writethrough, + // with caching enabled + // TODO: Can't use p.PhysicalBase because hypervisor is currently using pages below + // that threshold. s/b fix hypervisor PhysicalBase reporting. + BootstrapMapRange(pdpt, 0, + identityMapLimit, + new PhysicalAddress(0), true, // writeable false, // not write-through false); // don't disable caching - // Map the uncached area of high memory down into the kernel space BootstrapMapRange(pdpt, uncachedWindowBase, MemoryManager.PagePad(uncachedWindowBase + uncachedWindowSize), - new PhysicalAddress(BootInfo.UNCACHED_PHYSICAL), + new PhysicalAddress(Platform.UNCACHED_PHYSICAL), true, // writeable - true, // write-through + true, // writethrough true); // disable caching - - // Map the area of high memory that contains executable images - // down into its kernel-space window - BootInfo bi = BootInfo.GetBootInfo(); - PhysicalAddress physStart = new PhysicalAddress(bi.DumpAddr32); - - BootstrapMapRange(pdpt, execImageBase, - MemoryManager.PagePad(execImageBase + execImageSize), - physStart, - false, // read-only - false, // not write-through + BootstrapMapRange(pdpt, miniDumpBase, + MemoryManager.PagePad(miniDumpLimit), + new PhysicalAddress(p.MiniDumpBase), + true, // writeable + false, // not writethrough false); // don't disable caching -#if RESERVE_SMAP // Walk the SMAP and remap any reserved areas into the smap window - SMAPINFO *smap = (SMAPINFO*)bi.SmapData32; + SMAPINFO* smap = (SMAPINFO*)p.Smap32; UIntPtr stepVirtual = smapRemapBase; - for (uint i = 0; i < bi.SmapCount; i++) { + for (uint i = 0; i < p.SmapCount; i++) { if ((smap[i].type != (ulong)SMAPINFO.AddressType.Free) && (smap[i].addr >= identityMapLimit)) { DebugStub.Assert(MemoryManager.IsPageAligned(smap[i].size)); @@ -775,11 +867,10 @@ namespace Microsoft.Singularity.Memory } DebugStub.Assert((stepVirtual - smapRemapBase) == smapRemapSize); -#endif // Make sure we have committed a PDT for each kernel PDPE slot, so // other address spaces can alias the kernel range sensibly - BootstrapEnsurePDTs(pdpt, UIntPtr.Zero, BootInfo.KERNEL_BOUNDARY); + BootstrapEnsurePDTs(pdpt, UIntPtr.Zero, Platform.KERNEL_BOUNDARY); kernelPDPE1 = new PhysicalAddress(((*pdpt)[0])->Address); DebugStub.Assert(kernelPDPE1 != PhysicalAddress.Null); @@ -789,7 +880,7 @@ namespace Microsoft.Singularity.Memory DebugStub.Assert(kernelPDPE2 != PhysicalAddress.Null); } - if (numKernelPDPEs >=3 ) { + if (numKernelPDPEs >= 3) { kernelPDPE3 = new PhysicalAddress(((*pdpt)[2])->Address); DebugStub.Assert(kernelPDPE3 != PhysicalAddress.Null); } @@ -824,7 +915,7 @@ namespace Microsoft.Singularity.Memory kernelPageTableBase + kernelPDTOffset + MemoryManager.BytesFromPages(i), new PhysicalAddress((ulong)pdt), true); - for ( uint j = 0; j < 512; j++) { + for (uint j = 0; j < 512; j++) { if (((*pdt)[j])->Valid) { PT* pt = (PT*)(((*pdt)[j])->Address); @@ -845,7 +936,7 @@ namespace Microsoft.Singularity.Memory userPageTableBase + userPDTOffset + MemoryManager.BytesFromPages(i), new PhysicalAddress((ulong)pdt), true); - for ( uint j = 0; j < 512; j++) { + for (uint j = 0; j < 512; j++) { if (((*pdt)[j])->Valid) { PT* pt = (PT*)(((*pdt)[j])->Address); @@ -964,12 +1055,13 @@ namespace Microsoft.Singularity.Memory uint pageIndex = (uint)MemoryManager.PagesFromBytes(offsetInPT); while ((stepAddr < limitAddr) && (pageIndex < 512)) { - DebugStub.Assert(!((*pt)[pageIndex])->Valid); + // allow overlapping writes for convience... + //DebugStub.Assert(!((*pt)[pageIndex])->Valid); ((*pt)[pageIndex])->Address = stepPhysical; ((*pt)[pageIndex])->User = true; // Ring3 ((*pt)[pageIndex])->Valid = true; ((*pt)[pageIndex])->Writeable = writeable; - ((*pt)[pageIndex])->Global = stepAddr < BootInfo.KERNEL_BOUNDARY; + ((*pt)[pageIndex])->Global = stepAddr < Platform.KERNEL_BOUNDARY; ((*pt)[pageIndex])->WriteThrough = writeThrough; ((*pt)[pageIndex])->CacheDisable = cacheDisable; @@ -1017,7 +1109,7 @@ namespace Microsoft.Singularity.Memory ((*pt)[pageIndex])->Address = physPage.Value; ((*pt)[pageIndex])->User = true; // Ring3 ((*pt)[pageIndex])->Writeable = writeable; - ((*pt)[pageIndex])->Global = pageAddr < BootInfo.KERNEL_BOUNDARY; + ((*pt)[pageIndex])->Global = pageAddr < Platform.KERNEL_BOUNDARY; ((*pt)[pageIndex])->Valid = true; } @@ -1031,7 +1123,7 @@ namespace Microsoft.Singularity.Memory // private static unsafe AddressSpace CreateNewKernelSpace() { - // REVIEW it's regrettable that we have to lock + // REVIEW: it's regrettable that we have to lock // for access to the scratch area. Maybe we should try to // reserve two pages of kernel-space memory instead? bool iflag = ScratchLock(); @@ -1085,7 +1177,7 @@ namespace Microsoft.Singularity.Memory DebugStub.Assert((userPageTableBase / SpacePerPDT) == ((userPageTableLimit - 1) / SpacePerPDT)); - // REVIEW it's regrettable that we have to lock + // REVIEW: it's regrettable that we have to lock // for access to the scratch area. Maybe we should try to // reserve two pages of kernel-space memory instead? bool iflag = ScratchLock(); @@ -1210,7 +1302,7 @@ namespace Microsoft.Singularity.Memory PDPE* pdpe = GetPDPE(forAddr); // must succeed PDT* retval = GetPDT(forAddr); - if (! IsPageMapped((UIntPtr)retval)) { + if (!IsPageMapped((UIntPtr)retval)) { // Don't panic; grab a new page to use as a PDT PhysicalAddress newPdt = PhysicalPages.AllocPage(); @@ -1231,7 +1323,8 @@ namespace Microsoft.Singularity.Memory DebugStub.Assert(!pdpe->Valid); pdpe->Address = newPdt.Value; pdpe->Valid = true; - } else { + } + else { DebugStub.Assert(pdpe->Valid); } @@ -1257,7 +1350,7 @@ namespace Microsoft.Singularity.Memory PDE* pde = EnsurePDE(forAddr); PT* retval = GetPT(forAddr); - if (! IsPageMapped((UIntPtr)retval)) { + if (!IsPageMapped((UIntPtr)retval)) { // Don't panic; grab a new page to use as a PT PhysicalAddress newPt = PhysicalPages.AllocPage(); @@ -1280,7 +1373,8 @@ namespace Microsoft.Singularity.Memory pde->User = true; // Ring3 pde->Valid = true; pde->Writeable = true; - } else { + } + else { DebugStub.Assert(pde->Valid); } @@ -1310,10 +1404,10 @@ namespace Microsoft.Singularity.Memory UIntPtr pdtBase = kernelPageTableBase + kernelPDTOffset; UIntPtr addr = forAddr; - if (forAddr >= BootInfo.KERNEL_BOUNDARY) { + if (forAddr >= Platform.KERNEL_BOUNDARY) { // User-space address pdtBase = userPageTableBase + userPDTOffset; - addr -= BootInfo.KERNEL_BOUNDARY; + addr -= Platform.KERNEL_BOUNDARY; } // Index into the table of PDTs @@ -1331,10 +1425,10 @@ namespace Microsoft.Singularity.Memory UIntPtr ptBase = kernelPageTableBase; UIntPtr addr = forAddr; - if (forAddr >= BootInfo.KERNEL_BOUNDARY) { + if (forAddr >= Platform.KERNEL_BOUNDARY) { // User-space address ptBase = userPageTableBase; - addr -= BootInfo.KERNEL_BOUNDARY; + addr -= Platform.KERNEL_BOUNDARY; } // Index into the table of PTs @@ -1424,7 +1518,7 @@ namespace Microsoft.Singularity.Memory pte->User = true; // Ring3 pte->Valid = true; pte->Writeable = true; - pte->Global = virtualAddr < BootInfo.KERNEL_BOUNDARY; + pte->Global = virtualAddr < Platform.KERNEL_BOUNDARY; Processor.InvalidateTLBEntry(virtualAddr); } @@ -1436,7 +1530,7 @@ namespace Microsoft.Singularity.Memory } [Inline] - [NoStackLinkCheck] + [NoStackLinkCheckTrans] private static bool ScratchLock() { bool enabled = Processor.DisableInterrupts(); @@ -1447,7 +1541,7 @@ namespace Microsoft.Singularity.Memory } [Inline] - [NoStackLinkCheck] + [NoStackLinkCheckTrans] private static void ScratchUnlock(bool iflag) { #if SINGULARITY_MP @@ -1457,5 +1551,3 @@ namespace Microsoft.Singularity.Memory } } } - -#endif // PAGING diff --git a/base/Kernel/Singularity/Memory/VirtualMemoryRange.cs b/base/Kernel/Singularity/Memory/VirtualMemoryRange.cs index bfc648b..28db54f 100644 --- a/base/Kernel/Singularity/Memory/VirtualMemoryRange.cs +++ b/base/Kernel/Singularity/Memory/VirtualMemoryRange.cs @@ -13,8 +13,6 @@ // page table that tracks how each virtual page is being used, to support a GC. // -#if PAGING - using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -22,6 +20,7 @@ using System.Threading; using System.GCs; using Microsoft.Singularity; +using Microsoft.Singularity.Hal; namespace Microsoft.Singularity.Memory { @@ -92,6 +91,8 @@ namespace Microsoft.Singularity.Memory { DebugStub.Assert(MemoryManager.IsPageAligned(baseAddr)); DebugStub.Assert(MemoryManager.IsPageAligned(limitAddr)); + DebugStub.Assert(MemoryManager.IsPageAligned(descrBaseAddr)); + DebugStub.Assert(MemoryManager.IsPageAligned(descrLimitAddr)); // The descriptive range can't be smaller than the managed range DebugStub.Assert(descrLimitAddr >= limitAddr); @@ -101,19 +102,16 @@ namespace Microsoft.Singularity.Memory descrLimit = descrLimitAddr; rangeBase = baseAddr; rangeLimit = limitAddr; - rangeLock = new SpinLock(); + rangeLock = new SpinLock(SpinLock.Types.VirtualMemoryRange); - // Figure out how many pages we need for a page table - UIntPtr pageTableSize = descrLimit - descrBase; - pageTableSize = MemoryManager.PagesFromBytes(pageTableSize); - pageTableSize *= 4; // one uint per page - pageTableSize = MemoryManager.PagePad(pageTableSize); + describedPages = MemoryManager.PagesFromBytes(descrLimit - descrBase); + + // Figure out how many pages we need for a page description table + UIntPtr pageTableSize = MemoryManager.PagePad(describedPages * sizeof(uint)); dataStart = baseAddr + pageTableSize; nextAlloc = dataStart; - describedPages = MemoryManager.PagesFromBytes(descrLimit - descrBase); - // Commit and prepare the page table pageTable = (uint*)baseAddr; @@ -131,20 +129,20 @@ namespace Microsoft.Singularity.Memory // Describe the pages outside our range as Unknown if (descrBase < rangeBase) { - SetPages(descrBase, rangeBase, MemoryManager.PageUnknown); + SetRange(descrBase, rangeBase, MemoryManager.PageUnknown); } if (descrLimit > rangeLimit) { - SetPages(rangeLimit, descrLimit, MemoryManager.PageUnknown); + SetRange(rangeLimit, descrLimit, MemoryManager.PageUnknown); } - // The page-table pages are in use by the System - SetPages((UIntPtr)pageTable, + // The page-table pages themselves are in use by the System + SetRange((UIntPtr)pageTable, (UIntPtr)pageTable + pageTableSize, MemoryManager.KernelPageNonGC); // Describe pages in-range as Free - SetPages(dataStart, rangeLimit, MemoryManager.PageFree); + SetRange(dataStart, rangeLimit, MemoryManager.PageFree); #if DEBUG // Check that our data range is pristine @@ -239,6 +237,8 @@ namespace Microsoft.Singularity.Memory // // Releases and uncommits a range of memory // + [NoStackLinkCheckTrans] + [NoStackOverflowCheck] internal unsafe UIntPtr Free(UIntPtr startPage, UIntPtr numPages, Process process) @@ -249,7 +249,7 @@ namespace Microsoft.Singularity.Memory DebugStub.Assert(blockLimit <= rangeLimit); // - // NOTE This is strictly a sanity check. The pagetable + // NOTE: This is strictly a sanity check. The pagetable // is ultimately not trustworthy because it is writeable by // user-mode code. // @@ -263,12 +263,21 @@ namespace Microsoft.Singularity.Memory // Returns the number of *addressable* pages, which may be less than // the number of actually *usable* pages, if we describe a larger // range than we manage. - internal UIntPtr PageCount - { get { return describedPages; } } + internal UIntPtr PageCount { + [NoHeapAllocation] + get { + return describedPages; + } + } // This lets the caller look directly into the page table; beware! - internal unsafe uint* PageTable - { get { return pageTable; } } + internal unsafe uint* PageTable { + [NoHeapAllocation] + get + { + return pageTable; + } + } internal UIntPtr AllocateExtend(UIntPtr addr, UIntPtr bytes, @@ -295,7 +304,7 @@ namespace Microsoft.Singularity.Memory UIntPtr bytesFreed = 0; // - // NOTE here we choose to use the pagetable data, + // NOTE: here we choose to use the pagetable data, // which is writeable by user-mode code. Keep in mind that // it may be corrupt. We choose NOT to lock while traversing // for a particular process' pages because no further allocations @@ -336,7 +345,8 @@ namespace Microsoft.Singularity.Memory // // Sets a range of pages to have the provided tag // - internal unsafe void SetPages(UIntPtr startAddr, UIntPtr limitAddr, uint tag) + [NoHeapAllocation] + internal unsafe void SetRange(UIntPtr startAddr, UIntPtr limitAddr, uint tag) { DebugStub.Assert(startAddr >= descrBase); DebugStub.Assert(limitAddr <= descrLimit); @@ -364,15 +374,70 @@ namespace Microsoft.Singularity.Memory internal unsafe void Dump(string where) { - // TODO: Dump not yet implemented + DebugStub.WriteLine( "VirtualMemoryRange.Dump: {0}", __arglist(where)); + + uint *descriptors = pageTable; + uint last = *descriptors++ & MemoryManager.SystemPageMask; + UIntPtr begin = UIntPtr.Zero; + + UIntPtr freePages = UIntPtr.Zero; + UIntPtr usedPages = UIntPtr.Zero; + UIntPtr unknownPages = UIntPtr.Zero; + UIntPtr sharedPages = UIntPtr.Zero; + + for (UIntPtr i = (UIntPtr)1; i < PageCount; i++) { + uint dsc = *descriptors++; + uint val = dsc & MemoryManager.SystemPageMask; + + switch (val) { + case MemoryManager.PageUnknown: + unknownPages++; + break; + case MemoryManager.PageShared: + sharedPages++; + break; + case MemoryManager.PageFree: + freePages++; + break; + default: + usedPages++; + break; + } + + if (dsc != last) { + DebugStub.WriteLine( " {0:x8}..{1:x8} : {2:x8} : {3:x8}", + __arglist( + begin << MemoryManager.PageBits, i << MemoryManager.PageBits, last, + (i - begin) << MemoryManager.PageBits)); + last = dsc; + begin = i; + } + } + + DebugStub.WriteLine(" {0:x8}..{1:x8} : {2:x8} : {3:x8}", + __arglist( + begin << MemoryManager.PageBits, PageCount << MemoryManager.PageBits, last, + (PageCount - begin) << MemoryManager.PageBits)); + + //DumpFreeNodes(GetFreeList(), false); + //DumpFreeNodes(GetSaveList(), true); + + DebugStub.WriteLine("Totals: free={0:x8}, used={1:x8}, unknown={2:x8}, reserved={3:x8}", + __arglist( + freePages << MemoryManager.PageBits, + usedPages << MemoryManager.PageBits, + unknownPages << MemoryManager.PageBits, + sharedPages << MemoryManager.PageBits)); } + [NoHeapAllocation] public UIntPtr GetMaxMemory() { // Return the amount of space we *manage* return rangeLimit - rangeBase; } + [NoHeapAllocation] public unsafe UIntPtr GetFreeMemory() { // Report as free only that memory above our current @@ -382,11 +447,13 @@ namespace Microsoft.Singularity.Memory return rangeLimit - nextAlloc; } + [NoHeapAllocation] public unsafe UIntPtr GetUsedMemory() { return GetMaxMemory() - GetFreeMemory(); } + [NoHeapAllocation] public void GetUsageStatistics(out ulong allocatedCount, out ulong allocatedBytes, out ulong freedCount, @@ -398,6 +465,7 @@ namespace Microsoft.Singularity.Memory freedBytes = this.freedBytes; } + [NoHeapAllocation] internal UIntPtr PageFromAddr(UIntPtr addr) { DebugStub.Assert(addr >= descrBase); @@ -405,6 +473,7 @@ namespace Microsoft.Singularity.Memory return MemoryManager.PageFromAddr(addr - descrBase); } + [NoHeapAllocation] internal UIntPtr AddrFromPage(UIntPtr pageNum) { DebugStub.Assert(pageNum < PageCount); @@ -413,7 +482,9 @@ namespace Microsoft.Singularity.Memory internal UIntPtr BaseAddress { - get { + [NoHeapAllocation] + get + { // Return the first *described* address return descrBase; } @@ -423,7 +494,9 @@ namespace Microsoft.Singularity.Memory // For validating pointers into this range. internal UIntPtr DataStartAddr { - get { + [NoHeapAllocation] + get + { return dataStart; } } @@ -431,7 +504,9 @@ namespace Microsoft.Singularity.Memory // Returns the limit of the range we use for allocations. internal UIntPtr DataLimitAddr { - get { + [NoHeapAllocation] + get + { return rangeLimit; } } @@ -455,29 +530,60 @@ namespace Microsoft.Singularity.Memory // The expectation is that higher-level components will do smarter // space management than this! // - private UIntPtr ReserveInternal(UIntPtr numPages, Process process, uint extra, PageType type) + private unsafe UIntPtr ReserveInternal(UIntPtr numPages, Process process, uint extra, PageType type) { + DebugStub.Assert(numPages >= 1); UIntPtr mapAddr = UIntPtr.Zero; - if (nextAlloc + MemoryManager.BytesFromPages(numPages) <= rangeLimit) { + uint tag = + (process != null ? process.ProcessTag : MemoryManager.KernelPage) + | (extra & MemoryManager.ExtraMask) + | (uint)type; + + UIntPtr blockBytes = MemoryManager.BytesFromPages(numPages); + + if (nextAlloc + blockBytes <= rangeLimit) { mapAddr = nextAlloc; - UIntPtr limitAddr = mapAddr + MemoryManager.BytesFromPages(numPages); + UIntPtr limitAddr = mapAddr + blockBytes; nextAlloc = limitAddr; - uint tag = - (process != null ? process.ProcessTag : MemoryManager.KernelPage) - | (extra & MemoryManager.ExtraMask) - | (uint)type; - - SetPages(mapAddr, limitAddr, tag); + SetRange(mapAddr, limitAddr, tag); } else { - // TODO slow-path allocation not yet implemented + // slow-path allocation - just do first-fit for now... + // TODO: Need to integrate freelist management from flatpages et al + UIntPtr startIdx = PageFromAddr(dataStart); + UIntPtr limitIdx = MemoryManager.PageFromAddr(rangeLimit - descrBase) - (numPages - 1); + DebugStub.Assert(limitIdx <= PageCount); + + for (UIntPtr pageIdx = startIdx; pageIdx < limitIdx; pageIdx++) { + UIntPtr pageCount = 0; + + while (pageCount < numPages) { + uint pageTag = *(pageTable + pageIdx + pageCount); + if (pageTag != MemoryManager.PageFree) { + break; + } + pageCount++; + } + + if (pageCount == numPages) { + mapAddr = dataStart + MemoryManager.BytesFromPages(pageIdx - startIdx); + + SetRange(mapAddr, mapAddr + blockBytes, tag); + break; + } + else { + pageIdx += pageCount; + } + } } return mapAddr; } + [NoStackLinkCheckTrans] + [NoStackOverflowCheck] private unsafe UIntPtr FreeAndUncommit(UIntPtr startPage, UIntPtr numPages) { @@ -503,7 +609,7 @@ namespace Microsoft.Singularity.Memory try { // Mark the pages free within our lock so we can rely on // blocks being uniformly marked free in here - SetPages(startPage, blockLimit, MemoryManager.PageFree); + SetRange(startPage, blockLimit, MemoryManager.PageFree); // Reduce fragmentation: lower the nextAlloc pointer if // we have freed the top end of memory. @@ -511,7 +617,7 @@ namespace Microsoft.Singularity.Memory nextAlloc = startPage; // - // NOTE this chooses to use the page table + // NOTE: this chooses to use the page table // information, which is currently usermode-accessible // in the case of a user-mode range. Keep in mind that // the information may be corrupt! @@ -529,7 +635,7 @@ namespace Microsoft.Singularity.Memory uint dsc = pageTable[(uint)PageFromAddr(stepPage)]; val = dsc & MemoryManager.SystemPageMask; } - while((val == MemoryManager.PageFree) && + while ((val == MemoryManager.PageFree) && (!VMManager.IsPageMapped(stepPage))); // sanity } @@ -545,6 +651,8 @@ namespace Microsoft.Singularity.Memory // Confirms that the provided tag is consistent with the pagetable over // a certain range + [NoStackLinkCheckTrans] + [NoStackOverflowCheck] private unsafe void VerifyOwner(UIntPtr startAddr, UIntPtr limitAddr, uint tag) { #if DEBUG @@ -553,7 +661,7 @@ namespace Microsoft.Singularity.Memory UIntPtr startIdx = PageFromAddr(startAddr); UIntPtr limitIdx = MemoryManager.PagesFromBytes(limitAddr - descrBase); - DebugStub.Assert(limitIdx < PageCount); + DebugStub.Assert(limitIdx <= PageCount); tag &= MemoryManager.ProcessPageMask; @@ -567,7 +675,7 @@ namespace Microsoft.Singularity.Memory } [Inline] - [NoStackLinkCheck] + [NoStackLinkCheckTrans] private static bool Lock() { bool enabled = Processor.DisableInterrupts(); @@ -578,7 +686,7 @@ namespace Microsoft.Singularity.Memory } [Inline] - [NoStackLinkCheck] + [NoStackLinkCheckTrans] private static void Unlock(bool iflag) { #if SINGULARITY_MP @@ -644,7 +752,8 @@ namespace Microsoft.Singularity.Memory if (indirect) { return pRange->Reserve(numPages, process, extra, type); - } else { + } + else { return range.Reserve(numPages, process, extra, type); } } @@ -656,7 +765,8 @@ namespace Microsoft.Singularity.Memory if (indirect) { return pRange->Unreserve(startPage, numPages, process); - } else { + } + else { return range.Unreserve(startPage, numPages, process); } } @@ -669,7 +779,8 @@ namespace Microsoft.Singularity.Memory if (indirect) { return pRange->Allocate(numPages, process, extra, type, parentDomain); - } else { + } + else { return range.Allocate(numPages, process, extra, type, parentDomain); } @@ -682,7 +793,8 @@ namespace Microsoft.Singularity.Memory if (indirect) { return pRange->Free(startPage, numPages, process); - } else { + } + else { return range.Free(startPage, numPages, process); } } @@ -694,7 +806,8 @@ namespace Microsoft.Singularity.Memory if (indirect) { return pRange->PageCount; - } else { + } + else { return range.PageCount; } } @@ -707,7 +820,8 @@ namespace Microsoft.Singularity.Memory if (indirect) { return pRange->PageTable; - } else { + } + else { return range.PageTable; } } @@ -723,7 +837,8 @@ namespace Microsoft.Singularity.Memory if (indirect) { return pRange->AllocateExtend(addr, bytes, process, extra, type); - } else { + } + else { return range.AllocateExtend(addr, bytes, process, extra, type); } } @@ -734,7 +849,8 @@ namespace Microsoft.Singularity.Memory if (indirect) { return pRange->FreeAll(process); - } else { + } + else { return range.FreeAll(process); } } @@ -749,7 +865,8 @@ namespace Microsoft.Singularity.Memory if (indirect) { return pRange->Query(queryAddr, process, out regionAddr, out regionSize); - } else { + } + else { return range.Query(queryAddr, process, out regionAddr, out regionSize); } @@ -761,7 +878,8 @@ namespace Microsoft.Singularity.Memory if (indirect) { pRange->Dump(where); - } else { + } + else { range.Dump(where); } } @@ -772,7 +890,8 @@ namespace Microsoft.Singularity.Memory if (indirect) { return pRange->GetMaxMemory(); - } else { + } + else { return range.GetMaxMemory(); } } @@ -783,7 +902,8 @@ namespace Microsoft.Singularity.Memory if (indirect) { return pRange->GetFreeMemory(); - } else { + } + else { return range.GetFreeMemory(); } } @@ -794,7 +914,8 @@ namespace Microsoft.Singularity.Memory if (indirect) { return pRange->GetUsedMemory(); - } else { + } + else { return range.GetUsedMemory(); } } @@ -810,7 +931,8 @@ namespace Microsoft.Singularity.Memory pRange->GetUsageStatistics( out allocatedCount, out allocatedBytes, out freedCount, out freedBytes); - } else { + } + else { range.GetUsageStatistics( out allocatedCount, out allocatedBytes, out freedCount, out freedBytes); @@ -823,7 +945,8 @@ namespace Microsoft.Singularity.Memory if (indirect) { return pRange->PageFromAddr(addr); - } else { + } + else { return range.PageFromAddr(addr); } } @@ -834,7 +957,8 @@ namespace Microsoft.Singularity.Memory if (indirect) { return pRange->AddrFromPage(pageNum); - } else { + } + else { return range.AddrFromPage(pageNum); } } @@ -846,7 +970,8 @@ namespace Microsoft.Singularity.Memory if (indirect) { return pRange->BaseAddress; - } else { + } + else { return range.BaseAddress; } } @@ -859,7 +984,8 @@ namespace Microsoft.Singularity.Memory if (indirect) { return pRange->DataStartAddr; - } else { + } + else { return range.DataStartAddr; } } @@ -872,7 +998,8 @@ namespace Microsoft.Singularity.Memory if (indirect) { return pRange->DataLimitAddr; - } else { + } + else { return range.DataLimitAddr; } } @@ -886,11 +1013,12 @@ namespace Microsoft.Singularity.Memory if (indirect) { rangeLimit = pRange->DataLimitAddr; - } else { + } + else { rangeLimit = range.DataLimitAddr; } - if (rangeLimit > BootInfo.KERNEL_BOUNDARY) { + if (rangeLimit > Platform.KERNEL_BOUNDARY) { // We are a user-space range. We had better // be using the right address space! DebugStub.Assert(parentDomain.AddressSpace == Processor.GetCurrentAddressSpace()); @@ -899,5 +1027,3 @@ namespace Microsoft.Singularity.Memory } } } - -#endif // PAGING diff --git a/base/Kernel/Singularity/Monitoring.cs b/base/Kernel/Singularity/Monitoring.cs index 3afc28d..a164c67 100644 --- a/base/Kernel/Singularity/Monitoring.cs +++ b/base/Kernel/Singularity/Monitoring.cs @@ -25,6 +25,7 @@ namespace Microsoft.Singularity { [NoCCtor] [CLSCompliant(false)] + [AccessedByRuntime("referenced from Monitoring.cpp")] public class Monitoring { [AccessedByRuntime("referenced from Monitoring.cpp")] @@ -102,55 +103,7 @@ namespace Microsoft.Singularity } #endif - [AccessedByRuntime("output to header : defined in Monitoring.cpp")] - [StackBound(64)] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void Finalize(); - - [AccessedByRuntime("output to header : defined in Monitoring.cpp")] - [StackBound(64)] - [MethodImpl(MethodImplOptions.InternalCall)] - private static unsafe extern byte * - CompareExchange(byte **dest, byte *exch, byte *comp); - - [AccessedByRuntime("output to header : defined in Monitoring.cpp")] - [StackBound(64)] - [MethodImpl(MethodImplOptions.InternalCall)] - private static unsafe extern LogEntry * - CompareExchange(LogEntry **dest, LogEntry *exch, LogEntry *comp); - - public static unsafe void GetMonitoringHeaders(out byte * _buffer) - { - _buffer = buffer; - } - - [AccessedByRuntime("output to header : defined in Monitoring.cpp")] - [StackBound(64)] - [MethodImpl(MethodImplOptions.InternalCall)] - private static unsafe extern LogEntry * IndexToPointer(ushort index); - - [AccessedByRuntime("output to header : defined in Monitoring.cpp")] - [StackBound(64)] - [MethodImpl(MethodImplOptions.InternalCall)] - private static unsafe extern ushort Dequeue(); - - [AccessedByRuntime("output to header : defined in Monitoring.cpp")] - [StackBound(64)] - [MethodImpl(MethodImplOptions.InternalCall)] - private static unsafe extern void Enqueue(ushort newElement); - - [AccessedByRuntime("output to header : defined in Monitoring.cpp")] - [StackBound(64)] - [MethodImpl(MethodImplOptions.InternalCall)] - private static unsafe extern void Enqueue(ushort newElement, - ulong * ptr); - - [AccessedByRuntime("output to header : defined in Monitoring.cpp")] - [StackBound(64)] - [MethodImpl(MethodImplOptions.InternalCall)] - private static unsafe extern byte * AddText(byte *buffer, string arg); - - /* Well-known provider IDs */ + // Well-known provider IDs public struct Provider { public const ushort SysInfo = 1; @@ -194,12 +147,12 @@ namespace Microsoft.Singularity public static unsafe extern void Log(ushort provider, ushort type, string s); - /* \brief Return one LogEntry to caller - * \param min_counter minimum event counter to read event for, will be - * modified and contains the actually read event in - * it after return - * \param log log entry to be filled - */ + // \brief Return one LogEntry to caller + // \param min_counter minimum event counter to read event for, will be + // modified and contains the actually read event in + // it after return + // \param log log entry to be filled + // public static unsafe int GetEntry(ref ulong min_counter, out LogEntry log) { @@ -215,12 +168,12 @@ namespace Microsoft.Singularity return ret; } - /* \brief Return text part for log entry - * \param src where to get the data from - * \param counter counter to expect at start of src - * \param dst where to copy the byte array to - * \param max_size copy max_size bytes at max - */ + // \brief Return text part for log entry + // \param src where to get the data from + // \param counter counter to expect at start of src + // \param dst where to copy the byte array to + // \param max_size copy max_size bytes at max + // public static unsafe int GetText(byte * src, ulong counter, byte * dst, int max_size) { @@ -251,25 +204,5 @@ namespace Microsoft.Singularity [StackBound(32)] [MethodImpl(MethodImplOptions.InternalCall)] public static unsafe extern void setActive(bool active); - - [AccessedByRuntime("output to header : defined in Monitoring.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - public static unsafe extern - void DebugTest(out ulong h_ts, out ulong t_ts, out ulong min, - out ulong max); - - [AccessedByRuntime("output to header : defined in Monitoring.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - public static unsafe extern uint ConsistencyCheck(); - -/* - [AccessedByRuntime("output to header : defined in Monitoring.cpp")] - [StackBound(128)] - [MethodImpl(MethodImplOptions.InternalCall)] - private static unsafe extern LogEntry * CreateLog(byte severity, - UIntPtr eip, - int chars, - out byte *text); - */ } } diff --git a/base/Kernel/Singularity/MpBootInfo.cs b/base/Kernel/Singularity/MpBootInfo.cs index 1402cb4..36164aa 100644 --- a/base/Kernel/Singularity/MpBootInfo.cs +++ b/base/Kernel/Singularity/MpBootInfo.cs @@ -15,12 +15,14 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Microsoft.Singularity.Hal; using Microsoft.Singularity.Memory; namespace Microsoft.Singularity { [StructLayout(LayoutKind.Sequential, Pack=4)] [CLSCompliant(false)] + [AccessedByRuntime("referenced in c++")] public struct MpBootInfo { #if SINGULARITY_MP @@ -33,7 +35,7 @@ namespace Microsoft.Singularity # elif MAX_CPU2 2; # else -# error "MAX_CPUx needs conversion definition." + 15; # endif // MAX_CPUX #else [AccessedByRuntime("referenced in c++")] @@ -55,51 +57,43 @@ namespace Microsoft.Singularity [AccessedByRuntime("referenced in c++")] public volatile int TargetCpu; + [AccessedByRuntime("defined in MpBootInfo.cpp")] [MethodImpl(MethodImplOptions.InternalCall)] - [StackBound(16)] + [StackBound(256)] internal static unsafe extern MpBootInfo * HalGetMpBootInfo(); - [AccessedByRuntime("referenced in c++")] + [AccessedByRuntime("defined in MpBootInfo.cpp")] [MethodImpl(MethodImplOptions.InternalCall)] - [StackBound(16)] + [StackBound(256)] internal static unsafe extern void HalReleaseMpStartupLock(); - public static unsafe bool PrepareForCpuStart(int targetCpu) + public unsafe void ReleaseMpStartupLock() { - UIntPtr size = MemoryManager.PagePad( - new UIntPtr(BootInfo.KERNEL_STACK_LIMIT - BootInfo.KERNEL_STACK_BEGIN) - ); + HalReleaseMpStartupLock(); + } - MpBootInfo* mbi = HalGetMpBootInfo(); - mbi->KernelStackBegin = MemoryManager.KernelAllocate( + public unsafe bool PrepareForCpuStart(int targetCpu) + { + + UIntPtr size = MemoryManager.PagePad( + Platform.BootCpu.KernelStackBegin + - Platform.BootCpu.KernelStackLimit); + + UIntPtr stack = MemoryManager.KernelAllocate( MemoryManager.PagesFromBytes(size), null, 0, System.GCs.PageType.Stack); - if (mbi->KernelStackBegin == UIntPtr.Zero) - { - mbi->KernelStackLimit = UIntPtr.Zero; - mbi->KernelStack = UIntPtr.Zero; - mbi->signature = 0; + if (stack == UIntPtr.Zero) { return false; } - mbi->KernelStackLimit = mbi->KernelStackBegin + size; - mbi->KernelStack = mbi->KernelStackLimit - (BootInfo.KERNEL_STACK_LIMIT - BootInfo.KERNEL_STACK); - mbi->signature = Signature; + KernelStackLimit = stack; + KernelStackBegin = stack + size; + signature = Signature; + TargetCpu = targetCpu; - mbi->TargetCpu = targetCpu; HalReleaseMpStartupLock(); return true; } - - // NB attribute is necessary to get definition of MpBootInfo as - // a struct by Bartok for all builds (including those that do not - // use MpBootInfo). - [AccessedByRuntime("referenced in c++")] - public static unsafe MpBootInfo GetMpBootInfo() - { - MpBootInfo *ptr = HalGetMpBootInfo(); - return *ptr; - } } } diff --git a/base/Kernel/Singularity/MpBootStatus.cs b/base/Kernel/Singularity/MpBootStatus.cs index 9de4b68..d90bdea 100644 --- a/base/Kernel/Singularity/MpBootStatus.cs +++ b/base/Kernel/Singularity/MpBootStatus.cs @@ -20,6 +20,7 @@ namespace Microsoft.Singularity /// progress of an initializing application processor /// (AP). [CLSCompliant(false)] + [AccessedByRuntime("referenced in c++")] public struct MpBootStatus { [AccessedByRuntime("referenced in c++")] diff --git a/base/Kernel/Singularity/MpExecution.cs b/base/Kernel/Singularity/MpExecution.cs index deaed3d..1877e4c 100644 --- a/base/Kernel/Singularity/MpExecution.cs +++ b/base/Kernel/Singularity/MpExecution.cs @@ -22,11 +22,12 @@ using System.Threading; using Microsoft.Singularity.Hal; using Microsoft.Singularity.Io; -using Microsoft.Singularity.X86; +using Microsoft.Singularity.Isal; namespace Microsoft.Singularity { [CLSCompliant(false)] + [AccessedByRuntime("referenced from halkd.cpp")] public class MpExecution { // Enumeration of Processor states @@ -55,17 +56,55 @@ namespace Microsoft.Singularity if (FreezeRequested) goto start; ProcessorContext* head = Processor.processorTable[0].context; + context->nextProcessorContext = head->nextProcessorContext; head->nextProcessorContext = context; context->ipiFreeze = Running; // From this point on the processor is visible // in the debugger - DebugStub.AddProcessor(context->cpuId); + DebugStub.AddProcessor(context->cpuRecord.id); } finally { freezeLock.Release(); } } +#if NOP + [ AccessedByRuntime("referenced from halkd.cpp") ] + [ NoHeapAllocation ] + static internal unsafe void FreezeAllProcessors() + { + freezeLock.Acquire(); + try { + freezeCount++; + if (FreezeRequested == true) { + // Processors are already frozen + return; + } + + ownerCpuContext = Processor.GetCurrentProcessorContext(); + activeCpuContext = ownerCpuContext; + if (activeCpuContext->displacedProcessorContext->nextProcessorContext == activeCpuContext) { + return; + } + FreezeRequested = true; + + activeCpuContext->displacedProcessorContext->ipiFreeze = FreezeActive | FreezeOwner; +//TODOMP Platform.FreezeProcessors(); + + // Wait until all running processors have gone into freeze + ProcessorContext* context = activeCpuContext->displacedProcessorContext->nextProcessorContext; + do + { + if ((context->displacedProcessorContext->ipiFreeze & (TargetFrozen | FreezeActive)) != 0) { + context = context->displacedProcessorContext->nextProcessorContext; + } + } while (context->displacedProcessorContext->nextProcessorContext != activeCpuContext); + } + finally { + freezeLock.Release(); + } + } +#else [ AccessedByRuntime("referenced from halkd.cpp") ] [ NoHeapAllocation ] @@ -84,32 +123,33 @@ namespace Microsoft.Singularity ownerCpuContext = Processor.GetCurrentProcessorContext(); activeCpuContext = ownerCpuContext; - if (activeCpuContext->nextProcessorContext == - activeCpuContext) { + if (activeCpuContext->nextProcessorContext == activeCpuContext) { return; } FreezeRequested = true; activeCpuContext->ipiFreeze = FreezeActive | FreezeOwner; - HalDevices.FreezeProcessors(); + // Generate an NMI on all processors (except this one) + Platform.ThePlatform.FreezeAllCpus(); // Wait until all running processors have gone into freeze ProcessorContext* context = activeCpuContext->nextProcessorContext; - do { - if ((context->ipiFreeze & (TargetFrozen|FreezeActive)) - != 0) { + do + { + if ((context->ipiFreeze & (TargetFrozen | FreezeActive)) != 0) { context = context->nextProcessorContext; } - } while (context->nextProcessorContext != activeCpuContext); + } while (context != activeCpuContext); } finally { freezeLock.Release(); } } +#endif [ NoHeapAllocation ] static internal unsafe void - FreezeProcessor(ref ThreadContext threadContext) + FreezeProcessor(ref SpillContext threadContext) { ProcessorContext* context = Processor.GetCurrentProcessorContext(); @@ -148,14 +188,15 @@ namespace Microsoft.Singularity static internal unsafe bool SwitchFrozenProcessor(int cpuId) { ProcessorContext* currentContext = Processor.GetCurrentProcessorContext(); - if (currentContext->cpuId == cpuId) { + if (currentContext->cpuRecord.id == cpuId) { // No processor to switch to. return false; } ProcessorContext* context = currentContext->nextProcessorContext; - do { - if (context->cpuId == cpuId) { + do + { + if (context->cpuRecord.id == cpuId) { currentContext->ipiFreeze &= ~FreezeActive; context->ipiFreeze |= FreezeActive; goto WaitForWakeupOrReselection; @@ -203,7 +244,8 @@ namespace Microsoft.Singularity FreezeRequested = false; ProcessorContext* context = activeCpuContext; - do { + do + { context->ipiFreeze = TargetThaw; context = context->nextProcessorContext; } while (context != activeCpuContext); @@ -213,561 +255,143 @@ namespace Microsoft.Singularity } } - // ------------------------------------------------------------------- - // haryadi -- circular buffer for ping pong integer storage - private const int MPBUFFERSIZE = 5; - private struct IntrPingPong - { - public SpinLock spinLock; - public int head; - public int tail; - public int [] buffer; - } - - public struct ApImage - { - // public UIntPtr virtAddr; - // public UIntPtr phyAddr; - // public UIntPtr length; - public UIntPtr entryPoint; - } - - private struct IntrApImage - { - public SpinLock spinLock; - public int head; - public int tail; - public ApImage [] buffer; - } - - static private IntrPingPong [] intrPingPong; - static private IntrApImage [] intrApImage; - - ///////////////////////////////////////////////////////////// - // PingPongInt routines - - [ NoHeapAllocation ] - static internal unsafe void StartPingPongInt(int from, int to, - byte vector) - { - intrPingPong[to].spinLock.Acquire(); - try { - HalDevices.SendFixedIPI(vector, from, to); - } - finally { - intrPingPong[to].spinLock.Release(); - } - } - - [ NoHeapAllocation ] - static internal unsafe int GetIntrPingPong(int cpuId) - { - int head = intrPingPong[cpuId].head; - int tail = intrPingPong[cpuId].tail; - int max = MPBUFFERSIZE; - int retval; - - // empty - if (head == tail) { - return -1; - } - retval = intrPingPong[cpuId].buffer[head]; - intrPingPong[cpuId].head = (head + 1) % max; - - // DebugStub.WriteLine("HSG: Get p{0} it[{1}] = {2}", - // __arglist(cpuId, head, retval)); - return retval; - } - - [ NoHeapAllocation ] - static internal unsafe bool PutIntrPingPong(int cpuId, - int task) - { - int tail = intrPingPong[cpuId].tail; - int head = intrPingPong[cpuId].head; - int max = MPBUFFERSIZE; - - // full - if ((tail == max - 1 && head == 0) || - (tail + 1 == head)) { - return false; - } - intrPingPong[cpuId].buffer[tail] = task; - intrPingPong[cpuId].tail = (tail + 1) % max; - - // DebugStub.WriteLine("HSG: Put p{0} it[{1}] = {2}", - // __arglist(cpuId, tail, task)); - return true; - } - - ///////////////////////////////////////////////////////////// - // ApImage routines - - [ NoHeapAllocation ] - static internal void SendApImage(int from, int to) - { - byte vector = (byte)EVectors.ApImage; - intrApImage[to].spinLock.Acquire(); - try { - HalDevices.SendFixedIPI(vector, from, to); - } - finally { - intrApImage[to].spinLock.Release(); - } - } - - [ NoHeapAllocation ] - static internal bool GetIntrApImage(int cpuId, - out ApImage apImage) - { - int head = intrApImage[cpuId].head; - int tail = intrApImage[cpuId].tail; - int max = MPBUFFERSIZE; - - // empty - if (head == tail) { - apImage.entryPoint = 0; - return false; - } - apImage = intrApImage[cpuId].buffer[head]; - intrApImage[cpuId].head = (head + 1) % max; - - return true; - } - - - [ NoHeapAllocation ] - static internal bool PutIntrApImage(int cpuId, - UIntPtr entryPoint) - { - int tail = intrApImage[cpuId].tail; - int head = intrApImage[cpuId].head; - int max = MPBUFFERSIZE; - - // full - if ((tail == max - 1 && head == 0) || - (tail + 1 == head)) { - return false; - } - intrApImage[cpuId].buffer[tail].entryPoint = entryPoint; - intrApImage[cpuId].tail = (tail + 1) % max; - return true; - } - - - ///////////////////////////////////////////////////////////// - // haryadi: AbiCall structures - - // *************** DEPRECATED if MpCall is working *************** - - // Note: AbiCall buffers should not be treated as queue. - // When BSP receives IPI and the corresponding AbiCall buffer, the - // buffer should not be released (unlike queue). The BSP will call - // the actual ABI and store the return value in the same buffer. - // The AP will obtain the return value from the buffer and then release - // the buffer. - public struct AbiCall - { - // We need to know the 'position', so - // that when the abi is completed we know - // where we must put the value - public int position; - public int retVal; - public int argVal; - - public bool validRetVal; - public bool valid; - public bool served; - } - - private struct IntrAbiCall - { - public SpinLock spinLock; - public int head; - public int tail; - public AbiCall [] buffer; - } - - static private IntrAbiCall [] intrAbiCall; - - /* - ///////////////////////////////////////////////////////////// - // AbiCall routines - - // PutAbiCall: the caller passes the ABI arguments through - // the "AbiCall". The rest of the function just store the - // argVal and initialize the slot - [ NoHeapAllocation ] - static internal int PutAbiCall(int cpuId, - AbiCall abiCall) - { - int pos = -1; - intrAbiCall[cpuId].spinLock.Acquire(); - try { - // Loop to find free abi call slot. This is slow. In - // the future we should have a helper queue that - // points to free slots, hence avoid traversing. - for (int i = 0; i < intrAbiCall[cpuId].buffer.Length; i++) { - if (!intrAbiCall[cpuId].buffer[i].valid) { - - // initialize fields and copy the abi arguments - intrAbiCall[cpuId].buffer[i].valid = true; - intrAbiCall[cpuId].buffer[i].served = false; - intrAbiCall[cpuId].buffer[i].validRetVal = false; - intrAbiCall[cpuId].buffer[i].retVal = 0; - intrAbiCall[cpuId].buffer[i].argVal = abiCall.argVal; - - // the caller need to know on which abi call it - // should wait, so we copy back for the caller - abiCall = intrAbiCall[cpuId].buffer[i]; - pos = i; - break; - } - } - } - finally { - intrAbiCall[cpuId].spinLock.Release(); - } - return pos; - } - - // SendAbiCall: Send notice to the receiver processor that - // there is abi call that needs to be served - [ NoHeapAllocation ] - static internal void SendAbiCall(int from, int to) - { - byte vector = (byte)EVectors.AbiCall; - intrAbiCall[to].spinLock.Acquire(); - try { - HalDevices.SendFixedIPI(vector, from, to); - } - finally { - intrAbiCall[to].spinLock.Release(); - } - } - - // WaitAbiCall: The caller should wait until the retval is - // ready. Currently we just loop. In the future, busy loop is - // not necessary when scheduler comes into play. - [ NoHeapAllocation ] - static internal void WaitAbiCall(int cpuId, int pos, - out AbiCall abiCall) - { - // wait for validRetVal; - while (!intrAbiCall[cpuId].buffer[pos].validRetVal) { - // spin loop until ready - } - - // if reach here we have a validRetVal, copy back to caller - intrAbiCall[cpuId].spinLock.Acquire(); - try { - abiCall = intrAbiCall[cpuId].buffer[pos]; - } - finally { - intrAbiCall[cpuId].spinLock.Release(); - } - } - - // ReleaseAbiCall: release the abi slot - [ NoHeapAllocation ] - static internal void ReleaseAbiCall(int cpuId, int pos) - { - intrAbiCall[cpuId].spinLock.Acquire(); - try { - if (pos < intrAbiCall[cpuId].buffer.Length) { - intrAbiCall[cpuId].buffer[pos].valid = false; - } - } - finally { - intrAbiCall[cpuId].spinLock.Release(); - } - } - - // GetAbiCall: We should find valid but !served abi calls. The - // caller should loop until all abi calls are served. - [ NoHeapAllocation ] - static internal bool GetAbiCall(int cpuId, out AbiCall abiCall) - { - bool isUnservedAbi = false; - intrAbiCall[cpuId].spinLock.Acquire(); - try { - abiCall = intrAbiCall[cpuId].buffer[0]; // init - - for (int i = 0; i < intrAbiCall[cpuId].buffer.Length; i++) { - if (intrAbiCall[cpuId].buffer[i].valid && - !intrAbiCall[cpuId].buffer[i].served) { - - intrAbiCall[cpuId].buffer[i].served = true; - abiCall = intrAbiCall[cpuId].buffer[i]; - isUnservedAbi = true; - break; - } - } - } - finally { - intrAbiCall[cpuId].spinLock.Release(); - } - return isUnservedAbi; - } - - // ReturnAbiCall: The abi call has finished, copy the return - // value and set validRetVal to true - [ NoHeapAllocation ] - static internal void ReturnAbiCall(int cpuId, AbiCall abiCall) - { - intrAbiCall[cpuId].spinLock.Acquire(); - try { - int pos = abiCall.position; - if (abiCall.position < intrAbiCall[cpuId].buffer.Length) { - intrAbiCall[cpuId].buffer[pos] = abiCall; - intrAbiCall[cpuId].buffer[pos].validRetVal = true; - } - } - finally { - intrAbiCall[cpuId].spinLock.Release(); - } - } - */ - - - ///////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////// - // haryadi: MpCall structures - - // Note: MpCall buffers should not be treated as queue. - // When BSP receives IPI and the corresponding MpCall buffer, the - // buffer should not be released (unlike queue). The BSP will call - // the actual ABI and store the return value in the same buffer. - // The AP will obtain the return value from the buffer and then release - // the buffer. - public class MpCall - { - public MpCall() {} - - // We need to know the 'position', so - // that when the abi is completed we know - // where we must put the value - public int abiNum; - public int position; - - // public int argVal; - // public int retVal; - - public const int MAX_RET_SIZE = 20; - public const int MAX_ARG_SIZE = 100; - public byte [] retBuffer = new byte [MAX_RET_SIZE]; - public byte [] argBuffer = new byte [MAX_ARG_SIZE]; - - public bool validRetVal; - public bool valid; - public bool served; - } - - private struct IntrMpCall - { - public SpinLock spinLock; - public int head; - public int tail; - public MpCall [] buffer; - } - - static private IntrMpCall [] intrMpCall; - - - ///////////////////////////////////////////////////////////// - // MpCall routines - - - [ NoHeapAllocation ] - static internal MpCall ReserveMpCall(int cpuId) - { - intrMpCall[cpuId].spinLock.Acquire(); - try { - // Loop to find free abi call slot. This is slow. In - // the future we should have a helper queue that - // points to free slots, hence avoid traversing. - for (int i = 0; i < intrMpCall[cpuId].buffer.Length; i++) { - if (!intrMpCall[cpuId].buffer[i].valid) { - - intrMpCall[cpuId].buffer[i].valid = true; - intrMpCall[cpuId].buffer[i].served = false; - intrMpCall[cpuId].buffer[i].validRetVal = false; - - // the caller need to know on which abi call it - // should wait, so we copy back for the caller - return intrMpCall[cpuId].buffer[i]; - } - } - } - finally { - intrMpCall[cpuId].spinLock.Release(); - } - return null; - } - - - // SendMpCall: Send notice to the receiver processor that - // there is abi call that needs to be served - [ NoHeapAllocation ] - static internal void SendMpCall(int from, int to) - { - byte vector = (byte)EVectors.AbiCall; - intrMpCall[to].spinLock.Acquire(); - try { - HalDevices.SendFixedIPI(vector, from, to); - } - finally { - intrMpCall[to].spinLock.Release(); - } - } - - // WaitMpCall: The caller should wait until the retval is - // ready. Currently we just loop. In the future, busy loop is - // not necessary when scheduler comes into play. - [ NoHeapAllocation ] - static internal void WaitMpCall(int cpuId, int pos) - { - // wait for validRetVal; - while (!intrMpCall[cpuId].buffer[pos].validRetVal) { - // spin loop until ready - } - } - - // ReleaseMpCall: release the abi slot - [ NoHeapAllocation ] - static internal void ReleaseMpCall(int cpuId, int pos) - { - intrMpCall[cpuId].spinLock.Acquire(); - try { - if (pos < intrMpCall[cpuId].buffer.Length) { - intrMpCall[cpuId].buffer[pos].valid = false; - } - } - finally { - intrMpCall[cpuId].spinLock.Release(); - } - } - - // GetMpCall: We should find valid but !served abi calls. The - // caller should loop until all abi calls are served. - [ NoHeapAllocation ] - static internal MpCall GetMpCall(int cpuId) - { - intrMpCall[cpuId].spinLock.Acquire(); - try { - for (int i = 0; i < intrMpCall[cpuId].buffer.Length; i++) { - if (intrMpCall[cpuId].buffer[i].valid && - !intrMpCall[cpuId].buffer[i].served) { - - intrMpCall[cpuId].buffer[i].served = true; - return intrMpCall[cpuId].buffer[i]; - } - } - } - finally { - intrMpCall[cpuId].spinLock.Release(); - } - return null; - } - - // ReturnMpCall: The abi call has finished, copy the return - // value and set validRetVal to true - [ NoHeapAllocation ] - static internal void ReturnMpCall(int cpuId, int pos) - { - intrMpCall[cpuId].spinLock.Acquire(); - try { - if (pos < intrMpCall[cpuId].buffer.Length) { - intrMpCall[cpuId].buffer[pos].validRetVal = true; - } - } - finally { - intrMpCall[cpuId].spinLock.Release(); - } - } - - - ///////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////// // Initialization function internal static void Initialize() { - freezeLock = new SpinLock(); - InitializePingPong(); - InitializeApImage(); - InitializeAbiCall(); - InitializeMpCall(); + freezeLock = new SpinLock(SpinLock.Types.MpExecutionFreeze); } - // initialize circular buffer for all processors - internal static void InitializePingPong() + // + // This is called for a StopTheWorldCollector to ensure CPU's + // other than the one performing the GC do not service interrupts + // that can corrupt the heap. + // + [NoHeapAllocation] + static internal unsafe void StopProcessorsForGC() { - intrPingPong = - new IntrPingPong [MpBootInfo.MAX_CPU]; - for (int i = 0; i < intrPingPong.Length; i++) { - intrPingPong[i].head = 0; - intrPingPong[i].tail = 0; - intrPingPong[i].buffer = new int [MPBUFFERSIZE]; - intrPingPong[i].spinLock = new SpinLock(); + ProcessorContext* current = Processor.GetCurrentProcessorContext(); + + // Set gcIpiGate to 0 for all processors + ProcessorContext* context = current; + do { + context->gcIpiGate = 0; + + context = context->nextProcessorContext; + + } while (context != current); + + // + // We don't want to send a broadcast to all but self on a single + // processor system since the call will fail with no receivers. + // + // Note we may have other processors that have not been enabled + // by the OS and are sitting in SIPI, and won't answer the IPI. + // + if (Processor.GetRunningProcessorCount() > 1) { + Platform.BroadcastFixedIPI((byte)Isal.IX.EVectors.GCSynchronization, false); } + + // Wait for ackknowledgement from all CPU's but self + context = current->nextProcessorContext; + + //DebugStub.WriteLine("StopProcessorsForGC: GC CPU (sender) is {0}\n", + //__arglist(current->cpuId)); + + while (context != current) { + + //DebugStub.WriteLine("StopProcessorsForGC: Waiting for CPU {0}\n", + //__arglist(context->cpuId)); + + while (context->gcIpiGate == 0) ; + + //DebugStub.WriteLine("StopProcessorsForGC: CPU {0} Acknowledged\n", + //__arglist(context->cpuId)); + + context = context->nextProcessorContext; + } + + return; } - // initialize circular buffer for all processors - internal static void InitializeApImage() + // + // Called by the StopTheWorldCollector to allow processors + // to re-enable their interrupts + // + [ NoHeapAllocation ] + static internal unsafe void ResumeProcessorsAfterGC() { - intrApImage = - new IntrApImage [MpBootInfo.MAX_CPU]; - for (int i = 0; i < intrApImage.Length; i++) { - intrApImage[i].head = 0; - intrApImage[i].tail = 0; - intrApImage[i].buffer = new ApImage [MPBUFFERSIZE]; - intrApImage[i].spinLock = new SpinLock(); - } + // clear the GC wait flag for each CPU + ProcessorContext* current = Processor.GetCurrentProcessorContext(); + + ProcessorContext* context = current; + do { + context->gcIpiGate = 0; + + context = context->nextProcessorContext; + + } while (context != current); + + //DebugStub.WriteLine("ResumeProcessorsAfterGC: All processors resumed\n"); + + return; } - // initialize circular buffer for abi calls - internal static void InitializeAbiCall() + // + // This interrupt occurs when the processor executing the elected + // GC thread requires all the other processors to stop touching the + // heap. + // + // The GC has ensured that no threads are running through the GC thread + // synchronization, but interrupts may still occur and enter managed + // code, and this must be stopped to prevent corruptions on the heap + // for non-concurrent collectors. + // + // The processor with the elected GC thread has already masked off its + // interrupts and is waiting for the non-GC processors to report + // that their interrupts are disabled. + // + [NoHeapAllocation] + static internal unsafe void GCSynchronizationInterrupt() { - intrAbiCall = - new IntrAbiCall [MpBootInfo.MAX_CPU]; - for (int i = 0; i < intrAbiCall.Length; i++) { - intrAbiCall[i].head = 0; - intrAbiCall[i].tail = 0; - intrAbiCall[i].buffer = new AbiCall [MPBUFFERSIZE]; - for (int j = 0; j < intrAbiCall[i].buffer.Length; j++) { - intrAbiCall[i].buffer[j].position = j; - intrAbiCall[i].buffer[j].valid = false; - } - intrAbiCall[i].spinLock = new SpinLock(); + // - member barriers and other issues with spinwaiting on a variable + + bool en = Processor.DisableInterrupts(); + + //DebugStub.WriteLine("Processor {0} received GCSynchronizationInterrupt!\n", + //__arglist(Processor.GetCurrentProcessorId())); + + + ProcessorContext* current = Processor.GetCurrentProcessorContext(); + + // Write value for this processor stating we ackknowledge the interrupt + current->gcIpiGate = 1; + + // Question: Are we at a GC safe point? For all GC's? + + // + // Spinwait until the GC processor indicates its done by clearing + // this flag. + // + + while (current->gcIpiGate != 0) { + // } + + Processor.RestoreInterrupts(en); + + //DebugStub.WriteLine("Processor {0} done with GCSynchronizationInterrupt!\n", + //__arglist(Processor.GetCurrentProcessorId())); + + return; } - - // initialize circular buffer for abi calls - internal static void InitializeMpCall() - { - intrMpCall = - new IntrMpCall [MpBootInfo.MAX_CPU]; - for (int i = 0; i < intrMpCall.Length; i++) { - intrMpCall[i].head = 0; - intrMpCall[i].tail = 0; - intrMpCall[i].buffer = new MpCall [MPBUFFERSIZE]; - for (int j = 0; j < intrMpCall[i].buffer.Length; j++) { - intrMpCall[i].buffer[j] = new MpCall(); - intrMpCall[i].buffer[j].position = j; - intrMpCall[i].buffer[j].valid = false; - } - intrMpCall[i].spinLock = new SpinLock(); - } - } - - // Test .. internal static void Test() { } - } + } } diff --git a/base/Kernel/Singularity/NativeBootInfo.cs b/base/Kernel/Singularity/NativeBootInfo.cs new file mode 100644 index 0000000..a8c1ab5 --- /dev/null +++ b/base/Kernel/Singularity/NativeBootInfo.cs @@ -0,0 +1,122 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace Microsoft.Singularity +{ + + [StructLayout(LayoutKind.Sequential, Pack=4)] + [CLSCompliant(false)] + [AccessedByRuntime("referenced in c++")] + public struct NativeBootInfo + { + + // IDT and PIC + [AccessedByRuntime("referenced in c++")] + internal IX.IDTP BiosIdtPtr; + [AccessedByRuntime("referenced in c++")] + internal ushort BiosPicMask; + [AccessedByRuntime("referenced in c++")] + internal byte BiosWarmResetCmos; + [AccessedByRuntime("referenced in c++")] + internal uint BiosWarmResetVector; + [AccessedByRuntime("referenced in c++")] + internal uint Info16; + + // Temporary IDT + [AccessedByRuntime("referenced in c++")] + internal ulong IdtEnter0; + [AccessedByRuntime("referenced in c++")] + internal ulong IdtEnter1; + [AccessedByRuntime("referenced in c++")] + internal ulong IdtEnterN; + [AccessedByRuntime("referenced in c++")] + internal ulong IdtTarget; + + [AccessedByRuntime("referenced in c++")] + internal ulong Pdpt32; + + [AccessedByRuntime("referenced in c++")] + internal ulong Undump; + + // + [AccessedByRuntime("referenced in c++")] + internal ulong Heap32; + + // PCI Information (V2.0+) + [AccessedByRuntime("referenced in c++")] + internal uint PciBiosAX; + [AccessedByRuntime("referenced in c++")] + internal uint PciBiosBX; + [AccessedByRuntime("referenced in c++")] + internal uint PciBiosCX; + [AccessedByRuntime("referenced in c++")] + internal uint PciBiosEDX; + + // BIOS Information + [AccessedByRuntime("referenced in c++")] + public ulong AcpiRoot32; + [AccessedByRuntime("referenced in c++")] + internal ulong PnpNodesAddr32; + [AccessedByRuntime("referenced in c++")] + internal uint PnpNodesSize32; + [AccessedByRuntime("referenced in c++")] + internal ulong SmbiosRoot32; + [AccessedByRuntime("referenced in c++")] + internal ulong DmiRoot32; + [AccessedByRuntime("referenced in c++")] + internal uint IsaCsns; + [AccessedByRuntime("referenced in c++")] + internal ushort IsaReadPort; + [AccessedByRuntime("referenced in c++")] + internal uint Ebda32; + [AccessedByRuntime("referenced in c++")] + public uint MpFloat32; + + // 1394 Information + [AccessedByRuntime("referenced in c++")] + internal ulong Ohci1394Base; + [AccessedByRuntime("referenced in c++")] + internal ulong Ohci1394BufferAddr32; + [AccessedByRuntime("referenced in c++")] + internal uint Ohci1394BufferSize32; + + // MP specific variables + [AccessedByRuntime("referenced in c++")] + public ulong MpEnter32; // Entry point + [AccessedByRuntime("referenced in c++")] + public uint MpCpuCount; // No of AP's booted + [AccessedByRuntime("referenced in c++")] + public uint MpStatus32; // Error indicator + [AccessedByRuntime("referenced in c++")] + public ulong MpStartupLock32; // Pointer to MP init lock var + [AccessedByRuntime("referenced in c++")] + public ulong MpBootInfo32; // Pointer to MpBootInfo + +#if ISA_IX64 + [AccessedByRuntime("referenced in c++")] + internal ulong Ppml432; // pointer to Page Map Level-4 + [AccessedByRuntime("referenced in c++")] + internal ulong AddrTss; // pointer to the 64-bit TSS containing the IST stack table +#endif + + [AccessedByRuntime("defined in BootInfo.cpp")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(256)] + [NoHeapAllocation] + internal static unsafe extern NativeBootInfo * HalGetBootInfo(); + + [NoHeapAllocation] + public static unsafe NativeBootInfo GetBootInfo() + { + NativeBootInfo *ptr; + + ptr = HalGetBootInfo(); + return *ptr; + } + } +} diff --git a/base/Kernel/Singularity/PrivilegedGate.cs b/base/Kernel/Singularity/PrivilegedGate.cs new file mode 100644 index 0000000..307d5b4 --- /dev/null +++ b/base/Kernel/Singularity/PrivilegedGate.cs @@ -0,0 +1,90 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Processor.cs +// +// Note: +// + +using System; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Threading; + +using Microsoft.Singularity.Hal; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Memory; +using Microsoft.Singularity.Scheduling; +using Microsoft.Singularity.V1.Threads; +using Microsoft.Singularity.Isal; + +namespace Microsoft.Singularity +{ + + [CLSCompliant(false)] + [AccessedByRuntime("Method called from HAL.cpp")] + public class PrivilegedGate + { + + [NoHeapAllocation] + [AccessedByRuntime("referenced from c++")] + public static bool DisableInterrupts() + { + if ((ProcessPrivileges.GetCurrentPrivileges().AllowedOperations & + ProcessPrivileges.Operations.DisableInterrupts) != 0) { + + return Processor.DisableInterrupts(); + } + + // ISSUE: Assert here until all instances get cleaned up from SIPs + // This assertion should be removed / replaced with something that would + // flag / halt / break only the bogus SIP, not the entire system + + VTable.Assert(false, "DisableInterrupts called from unprivileged SIP"); + + return false; + } + + [NoHeapAllocation] + [AccessedByRuntime("referenced from c++")] + public static void RestoreInterrupts(bool enabled) + { + if ((ProcessPrivileges.GetCurrentPrivileges().AllowedOperations & + ProcessPrivileges.Operations.DisableInterrupts) != 0) { + + Processor.RestoreInterrupts(enabled); + + } else { + + // ISSUE: Assert here until all instances get cleaned up from SIPs + // This assertion should be removed / replaced with something that would + // flag / halt / break only the bogus SIP, not the entire system + + VTable.Assert(false, "RestoreInterrupts called from unprivileged SIP"); + } + } + + // Use this method for assertions only! + [NoHeapAllocation] + [AccessedByRuntime("referenced from c++")] + public static bool InterruptsDisabled() + { + if ((ProcessPrivileges.GetCurrentPrivileges().AllowedOperations & + ProcessPrivileges.Operations.DisableInterrupts) != 0) { + + return Processor.InterruptsDisabled(); + } + + // ISSUE: Assert here until all instances get cleaned up from SIPs + // This assertion should be removed / replaced with something that would + // flag / halt / break only the bogus SIP, not the entire system + + VTable.Assert(false, "InterruptsDisabled called from unprivileged SIP"); + + return false; + } + } +} diff --git a/base/Kernel/Singularity/Process.cs b/base/Kernel/Singularity/Process.cs index 4f7fa0c..4edd1ee 100644 --- a/base/Kernel/Singularity/Process.cs +++ b/base/Kernel/Singularity/Process.cs @@ -11,6 +11,7 @@ // #define TEST_CREATE_PROCESS_TIME // #define VERBOSE +// #define APPLY_TPM using System; using System.Collections; @@ -30,6 +31,7 @@ using Microsoft.Singularity.Scheduling; using Microsoft.Singularity.V1.Threads; using Microsoft.Singularity.V1.Services; using Microsoft.Singularity.Security; +using Microsoft.Singularity.Eventing; namespace Microsoft.Singularity { @@ -41,6 +43,53 @@ namespace Microsoft.Singularity ImageLoad = 3, } + // + // Per-SIP privileges. They are available only to the kernel side of the process + // They cannot also be altered by the SIP, assumming strong guarantees about mnemory + // safety. + // + + [CLSCompliant(false)] + public class ProcessPrivileges + { + // + // Default privileges for processes which do not override them from a + // trusted component base. + // + + public static ProcessPrivileges DefaultPrivileges = new ProcessPrivileges(Operations.None); + + [Flags] + public enum Operations + { + None = 0, + + // This privilege is checked for operations such as enabling / disabling + // interrupts and querying the interrupts flag. SIP should not have this + // privilege set by default, with the few exceptions of drivers + + DisableInterrupts = 1, + }; + + public ProcessPrivileges(Operations allowedOpperations) + { + AllowedOperations = allowedOpperations; + } + + [NoHeapAllocation] + public static ProcessPrivileges GetCurrentPrivileges() + { + Process process = Thread.CurrentProcess; + + // The privileges should only be checked within a valid context + + VTable.Assert(process != null); + return process.GetPrivileges(); + } + + public readonly Operations AllowedOperations; + } + [NoCCtor] [CLSCompliant(false)] public class Process @@ -155,7 +204,7 @@ namespace Microsoft.Singularity if (str == null) return null; - while((start <= end) && (at > -1)) { + while ((start <= end) && (at > -1)) { count = end - start; at = str.IndexOf(' ', start, count); if (at == -1) { @@ -168,7 +217,7 @@ namespace Microsoft.Singularity } retVal = new string[tokens.Count]; - for (int i=0; i< tokens.Count; i++){ + for (int i = 0; i < tokens.Count; i++) { retVal[i] = (string) tokens[i]; } return retVal; @@ -208,13 +257,22 @@ namespace Microsoft.Singularity } internal static - void VisitSpecialData(System.GCs.NonNullReferenceVisitor visitor) + void VisitSpecialData(System.GCs.DirectReferenceVisitor visitor) { visitor.VisitReferenceFields(Process.processTable); for (int i = 0; i < processTable.Length; i++) { - if (processTable[i] != null) { - processTable[i].handles.VisitSpecialData(visitor); + Process process = processTable[i]; + // Conditions to check: + // 1. process is not null. + // 2. process.handles is not null (during process initialization, + // the process instance is added to processTable before + // handles is initialized). + // 3. process.processIndex is not -1 (during process shutdown, + // processIndex is set to -1 as a signal that GC shouldn't + // touch the pages, so the pages can be freed safely). + if (process != null && process.handles != null && process.processIndex != -1) { + process.handles.VisitSpecialData(visitor); } } } @@ -224,7 +282,7 @@ namespace Microsoft.Singularity Thread currentThread = Thread.CurrentThread; Kernel.Waypoint(621); - bool disabled = Processor.DisableInterrupts(); + bool saved = Processor.DisableInterrupts(); processTableLock.Acquire(currentThread); int foundIndex = -1; try { @@ -238,9 +296,10 @@ namespace Microsoft.Singularity break; } } - } finally { + } + finally { processTableLock.Release(currentThread); - Processor.RestoreInterrupts(disabled); + Processor.RestoreInterrupts(saved); } Kernel.Waypoint(623); @@ -255,6 +314,7 @@ namespace Microsoft.Singularity idleProcess = new Process(2); // Initialize process management table. + processTableLock = new SpinLock(SpinLock.Types.ProcessTable); processTable = new Process[maxProcesses]; processTable[0] = kernelProcess; // ensure slot zero is never used processTable[1] = kernelProcess; // 1 is the kernel process. @@ -270,15 +330,6 @@ namespace Microsoft.Singularity ProcessConfig(config); } - internal static void InitializeSharedHeapWalker() - { - // TODO The walker needs to become per-protection-domain - // on paging systems - SharedHeapWalker.Initialize( - SharedHeap.KernelSharedHeap.DataOwnerId, - SharedHeap.KernelSharedHeap.EndpointOwnerId); - } - // The next 2 functions are used by the loader to determine // what address space SIPs should be created in @@ -291,8 +342,8 @@ namespace Microsoft.Singularity { isKernelDomain = false; if (processGroups == null) return null; - for (int i=0; i < processGroups.Length; i++) { - for (int j=0; j < processGroups[i].ProcessSet.Length; j++) { + for (int i = 0; i < processGroups.Length; i++) { + for (int j = 0; j < processGroups[i].ProcessSet.Length; j++) { if (processGroups[i].ProcessSet[j] == name) { isKernelDomain = processGroups[i].KernelDomain; return processGroups[i].GroupName; @@ -310,6 +361,7 @@ namespace Microsoft.Singularity // Per-Instance Members: private ProcessState state; // acquire processMutex before modifying + private bool wasStarted; // were we ever in the Running state? private Mutex suspendMutex; // lock ordering: if you acquire both suspendMutex and processMutex, acquire suspendMutex first private Mutex processMutex; // lock ordering: acquire child lock before acquiring parent lock private int processIndex; @@ -326,6 +378,9 @@ namespace Microsoft.Singularity private bool silentLoad; private UIntPtr pagesNow; private UIntPtr pagesMax; +#if false + private ThreadScheduleData defaultScheduleData; +#endif private PEImage image; private IoMemory loadedImage; @@ -345,13 +400,51 @@ namespace Microsoft.Singularity private LongArg[] longArgSet; private StringArrayArg[] stringArrayArgSet; -#if PAGING private ProtectionDomain protectionDomain; -#endif + private ProcessPrivileges privileges = null; // The principal of the process private Principal principal; + /// + /// Get the process's current privileges + /// + [NoHeapAllocation] + public ProcessPrivileges GetPrivileges() + { + if (privileges != null) { + return privileges; + } else { + return ProcessPrivileges.DefaultPrivileges; + } + } + + [NoHeapAllocation] + public void SetPrivileges(ProcessPrivileges newPrivileges) + { + privileges = newPrivileges; + } + + /// + /// Get the parent process + /// + public Process Parent + { + get { return parent; } + } + +#if false + internal ThreadScheduleData DefaultScheduleData + { + [Inline] + [NoHeapAllocation] + get + { + return defaultScheduleData; + } + } +#endif + private Process(int index) { this.state = ProcessState.Unstarted; @@ -374,16 +467,16 @@ namespace Microsoft.Singularity //DebugStub.WriteLine("new process(index): clearing StringArray"); this.stringArrayArgSet = null; -#if PAGING this.protectionDomain = ProtectionDomain.DefaultDomain; protectionDomain.AddRef(); +#if VERBOSE DebugStub.WriteLine("Loaded process \"{0}\" into {1}protection domain \"{2}\"", __arglist(this.imageName, this.protectionDomain.KernelMode ? "kernel " : "", this.protectionDomain.Name)); #endif - // This constructor is currently used only to create the + // This constructor is currently used only to create the // process for the kernel. If this changes, the code below should // change as well. In general, use the other constructor, as it // provides sufficient information to create the process principal. @@ -397,6 +490,9 @@ namespace Microsoft.Singularity { Kernel.Waypoint(620); this.processIndex = AllocateProcessTableSlot(this); +#if false + this.defaultScheduleData = parent.defaultScheduleData; +#endif Kernel.Waypoint(625); this.state = ProcessState.Unstarted; this.suspendMutex = new Mutex(); @@ -414,6 +510,8 @@ namespace Microsoft.Singularity //DebugStub.WriteLine("clearing StringArray"); this.stringArrayArgSet = null; + parent.AddChild(this); + Monitoring.Log(Monitoring.Provider.Process, (ushort)ProcessEvent.CreateUserProcess, 0, (uint)this.ProcessId, (uint)parent.ProcessId, @@ -439,6 +537,7 @@ namespace Microsoft.Singularity this.protectionDomain = ProtectionDomain.FindOrCreateByName(domainName, kernelMode); } } +#endif if (this.protectionDomain == null) { // Just inherit from our parent @@ -446,6 +545,7 @@ namespace Microsoft.Singularity } protectionDomain.AddRef(); +#if PAGING try { // Temporarily switch to the domain of the process // we are creating @@ -455,7 +555,7 @@ namespace Microsoft.Singularity Kernel.Waypoint(510); - // haryadi -- this is a normal load (for BSP), so + // this is a normal load (for BSP), so // set the 4th argument (isForMp) to false this.image = PEImage.Load(this, rawImage, out loadedImage, false); @@ -495,13 +595,11 @@ namespace Microsoft.Singularity loadedImage.VirtualAddress, loadedImage.VirtualAddress + loadedImage.Length)); #endif -#if PAGING DebugStub.WriteLine( "Loaded process \"{0}\" into {1}protection domain \"{2}\"", __arglist(this.imageName, this.protectionDomain.KernelMode ? "kernel " : "", this.protectionDomain.Name)); -#endif DebugStub.LoadedBinary(loadedImage.VirtualAddress, (UIntPtr) loadedImage.Length, @@ -521,7 +619,11 @@ namespace Microsoft.Singularity #endif // create the principal of the process +#if APPLY_TPM + this.principal = PrincipalImpl.NewInvocation(parent.Principal, appManifest, role, rawImage); +#else this.principal = PrincipalImpl.NewInvocation(parent.Principal, appManifest, role, null); +#endif Tracing.Log(Tracing.Audit, "Created PE process at {0:x8}", loadedImage.VirtualAddress); @@ -571,7 +673,6 @@ namespace Microsoft.Singularity #endif } -#if PAGING public ProtectionDomain Domain { get { @@ -579,6 +680,7 @@ namespace Microsoft.Singularity } } +#if PAGING public bool RunsAtKernelPrivilege { get { return protectionDomain.KernelMode; @@ -597,8 +699,8 @@ namespace Microsoft.Singularity // Routines to maintain child process list. // - // Called when a child process starts (not called when - // a child Process object is allocated). + // Called when a child process object is allocated (not called when + // a child Process starts). private void AddChild(Process child) { Kernel.Waypoint(514); @@ -607,7 +709,6 @@ namespace Microsoft.Singularity for (int i = 0; i < children.Length; i++) { if (children[i] == null) { children[i] = child; - startedChildCount++; return; } } @@ -619,7 +720,6 @@ namespace Microsoft.Singularity } nc[children.Length] = child; children = nc; - startedChildCount++; } finally { processMutex.ReleaseMutex(); @@ -635,7 +735,9 @@ namespace Microsoft.Singularity for (int i = 0; i < children.Length; i++) { if (children[i] == child) { children[i] = null; - startedChildCount--; + if (child.wasStarted) { + startedChildCount--; + } return true; } } @@ -651,13 +753,15 @@ namespace Microsoft.Singularity // Called when a thread is created (not when a thread // is started) - internal void AddThread(Thread thread, out ThreadHandle handle) + internal void AddThread(Thread thread, bool needThreadHandle) { Kernel.Waypoint(516); processMutex.AcquireMutex(); try { - handle = new ThreadHandle(AllocateHandle(thread)); - thread.threadHandle = handle; + VTable.Assert(thread.threadHandle.id == UIntPtr.Zero); + if (needThreadHandle) { + thread.threadHandle = new ThreadHandle(AllocateHandle(thread)); + } for (int i = 0; i < threads.Length; i++) { if (threads[i] == null) { @@ -683,16 +787,15 @@ namespace Microsoft.Singularity private bool ServiceRemoveThread(Thread thread) { Kernel.Waypoint(517); - // An unstarted thread cannot exit (and should not appear in - // startedThreadCount): - VTable.Assert(thread.ThreadState != - System.Threading.ThreadState.Unstarted); processMutex.AcquireMutex(); try { for (int i = 0; i < threads.Length; i++) { if (threads[i] == thread) { threads[i] = null; - startedThreadCount--; + if (thread.ThreadState != + System.Threading.ThreadState.Unstarted) { + Interlocked.Decrement(ref startedThreadCount); + } return true; } } @@ -710,6 +813,7 @@ namespace Microsoft.Singularity Tracing.Log(Tracing.Audit, "Failed CreateThread request too late."); return false; } + VTable.Assert(thread.threadHandle.id == UIntPtr.Zero); unchecked { Tracing.Log(Tracing.Audit, "Created new PE thread {1:x3}.", @@ -723,8 +827,9 @@ namespace Microsoft.Singularity out UIntPtr threadContext) { Thread thread = - Thread.CreateThread(this, new ThreadStart(this.PeStartThread)); + Thread.CreateThread(this, new ThreadStart(this.PeStartThread), true); if (thread != null) { + VTable.Assert(thread.threadHandle.id != UIntPtr.Zero); thread.processThreadIndex = threadIndex; handle = thread.threadHandle; fixed (void *contextAddr = &thread.context) { @@ -763,12 +868,37 @@ namespace Microsoft.Singularity processMutex.ReleaseMutex(); } + // Clear out any unstarted children and threads + processMutex.AcquireMutex(); + Process[] childArray = children; + Thread[] threadArray = threads; + processMutex.ReleaseMutex(); + foreach (Process c in childArray) { + if (c != null) { + VTable.Assert(c.state == ProcessState.Unstarted); + c.ServiceRelease(); + } + } + foreach (Thread t in threadArray) { + if (t != null) { + VTable.Assert(t.ThreadState + == System.Threading.ThreadState.Unstarted); + t.ServiceStopped(); + } + } + + // Remove ourself bool found = parent.ServiceRemoveChild(this); VTable.Assert(found); VTable.Assert(processIndex >= 0); Kernel.Waypoint(522); + // Set processIndex to -1 so that we don't race with GC + // (see VisitSpecialData) + int cachedProcessIndex = this.processIndex; + this.processIndex = -1; + UIntPtr imageBytes = UIntPtr.Zero; UIntPtr handleBytes; UIntPtr pageBytes; @@ -795,12 +925,14 @@ namespace Microsoft.Singularity Kernel.Waypoint(525); handleBytes = handles.FreeAllPages(); + Controller.GetSystemController().UnRegisterController(cachedProcessIndex); Kernel.Waypoint(526); pageBytes = Memory.MemoryManager.FreeProcessMemory(this); Kernel.Waypoint(527); #if PAGING - } finally { + } + finally { Thread.RevertToParentDomain(); } #endif @@ -809,11 +941,11 @@ namespace Microsoft.Singularity imageBytes, pageBytes, pagesMax, handleBytes, imageBytes + pageBytes + handleBytes); - // This must follow FreeAll, which relies on the processIndex - // (or rather, on the processTag). PrincipalImpl.Dispose(principal); - processTable[processIndex] = null; - processIndex = -1; + // This must follow handles.FreeAllPages, which relies on processTag. + // If we null out the entry earlier then the tag could be + // reused and handles.FreeAllPages could free another process's memory + processTable[cachedProcessIndex] = null; joinEvent.Set(); SharedHeapWalker.walker.RequestWalk(); @@ -840,10 +972,10 @@ namespace Microsoft.Singularity internal unsafe bool CheckEndpointsSet () { if (endpointSet == null) return true; - for (int i=0; i < endpointSet.Length; i++){ - if (endpointSet[i] == null){ - DebugStub.WriteLine("Process.Start: Endpoint ({0}) is not set. Process will not start", - __arglist(i)); + for (int i = 0; i < endpointSet.Length; i++) { + if (endpointSet[i] == null) { + DebugStub.WriteLine("Process.Start: Cannot start process (id {0}, image {1}), because endpoint {2} is not set", + __arglist(processIndex, imageName, i)); return false; } } @@ -858,19 +990,22 @@ namespace Microsoft.Singularity SuspendBarrierCheckParents(); processMutex.AcquireMutex(); + parent.processMutex.AcquireMutex(); try { if (state != ProcessState.Unstarted) { DebugStub.WriteLine(" process not in unstarted state!\n"); return false; } - if (!CheckEndpointsSet() ) { + if (!CheckEndpointsSet()) { return false; } - parent.AddChild(this); + parent.startedChildCount++; state = ProcessState.Running; + wasStarted = true; threads[0].SetMainThreadRunning(); } finally { + parent.processMutex.ReleaseMutex(); processMutex.ReleaseMutex(); } Kernel.Waypoint(528); @@ -879,31 +1014,20 @@ namespace Microsoft.Singularity return true; } - internal void StartThread(ref System.Threading.ThreadState threadState) + internal void StartThread() { // If we're being suspended, wait until after resumption to start. SuspendBarrier(); - processMutex.AcquireMutex(); - try { - if (threadState != System.Threading.ThreadState.Unstarted) { - throw new ThreadStateException("Cannot start thread in state " + threadState); - } - threadState = System.Threading.ThreadState.Running; - startedThreadCount++; - } - finally { - processMutex.ReleaseMutex(); - } + // Increment number of threads in a process and continue + Interlocked.Increment (ref this.startedThreadCount); } // precondition: processMutex held - internal void StartMainThread(ref System.Threading.ThreadState threadState) + internal void StartMainThread() { - VTable.Assert(threadState == System.Threading.ThreadState.Unstarted); VTable.Assert(state == ProcessState.Running); VTable.Assert(startedThreadCount == 0); - threadState = System.Threading.ThreadState.Running; startedThreadCount++; } @@ -1054,8 +1178,7 @@ namespace Microsoft.Singularity processMutex.ReleaseMutex(); } if (thread != null) { - bool suspended = thread.Suspend(aboutToStop); - if (!suspended) yieldAndRepeat = true; + thread.Suspend(aboutToStop); } } @@ -1202,6 +1325,10 @@ namespace Microsoft.Singularity { processMutex.AcquireMutex(); try { + // Note: we cannot be "Stopped" here, because there + // are no calls to ServiceRelease between here and + // the beginning of ServiceStop. If we ever decide to have + // more than one service thread, we'll have to revisit this. Debug.Assert(state == ProcessState.Suspended || state == ProcessState.Unstarted, "unexpected process state"); state = ProcessState.Stopping; @@ -1240,7 +1367,7 @@ namespace Microsoft.Singularity processMutex.ReleaseMutex(); } if (thread != null) { - thread.Stop(); + thread.StopSuspended(); } } @@ -1251,6 +1378,9 @@ namespace Microsoft.Singularity else { this.exitCode = (int) ProcessExitCode.StopDefault; } + + // Needed only to handle the Unstarted state: + ServiceCheckForExit(); } // Should only execute in the kernel service thread @@ -1416,7 +1546,7 @@ namespace Microsoft.Singularity // Set the endpoint and individual endpoint. public unsafe bool SetEndpoint(int index, ref SharedHeap.Allocation * endpoint) { - // FIXFIX: I short-circuited the check below to bypass + // TODO: FIXFIX: I short-circuited the check below to bypass // issues with stdin pipes. // if (state != ProcessState.Unstarted) { @@ -1427,7 +1557,7 @@ namespace Microsoft.Singularity DebugStub.WriteLine("Process.SetEndpoint is NULL!"); throw new ProcessStateException("endpoint set not allocated, attempting to set"); } - if (index > endpointSet.Length-1) { + if (index > endpointSet.Length - 1) { DebugStub.WriteLine("Process.SetEndpoint {0} out of range!",__arglist(index)); throw new ProcessStateException("endpoint index out of range"); } @@ -1522,7 +1652,7 @@ namespace Microsoft.Singularity strings = null; return ParameterCode.NotSet; } - if (index > stringArrayArgSet.Length-1) { + if (index > stringArrayArgSet.Length - 1) { strings = null; return ParameterCode.OutOfRange; } @@ -1544,7 +1674,7 @@ namespace Microsoft.Singularity public ParameterCode SetStartupStringArrayArg(int index, string[] strings) { //DebugStub.WriteLine("Setting string Arg array"); - if (index > stringArrayArgSet.Length-1) { + if (index > stringArrayArgSet.Length - 1) { return ParameterCode.OutOfRange; } @@ -1560,7 +1690,7 @@ namespace Microsoft.Singularity public ParameterCode SetStartupStringArg(int index, string value) { //DebugStub.WriteLine("set string attempting to set idx={0}", __arglist(index)); - if (index > stringArgSet.Length-1) { + if (index > stringArgSet.Length - 1) { //DebugStub.WriteLine("Process.SetStringArg {0} out of range!",__arglist(index)); //throw new ProcessStateException("StringArg index out of range"); return ParameterCode.OutOfRange; @@ -1591,7 +1721,7 @@ namespace Microsoft.Singularity value = null; return ParameterCode.OutOfRange; } - if (stringArgSet[arg] == null ) { + if (stringArgSet[arg] == null) { value = null; return ParameterCode.Success; } @@ -1624,7 +1754,7 @@ namespace Microsoft.Singularity } public unsafe ParameterCode SetStartupLongArg(int index, long value) { - if (index > longArgSet.Length-1) { + if (index > longArgSet.Length - 1) { //DebugStub.WriteLine("Process.SetLongArg {0} out of range!",__arglist(index)); //throw new ProcessStateException("LongArg index out of range"); return ParameterCode.OutOfRange; @@ -1652,7 +1782,7 @@ namespace Microsoft.Singularity return ParameterCode.OutOfRange; } - if ( longArgSet[arg] == null) { + if (longArgSet[arg] == null) { value = -1; return ParameterCode.NotSet; } @@ -1694,7 +1824,7 @@ namespace Microsoft.Singularity public unsafe ParameterCode SetStartupBoolArg(int index, bool value) { if (boolArgSet == null) return ParameterCode.OutOfRange; - if (index > boolArgSet.Length-1) { + if (index > boolArgSet.Length - 1) { //DebugStub.WriteLine("Process.SetBoolArg {0} out of range!",__arglist(index)); //throw new ProcessStateException("BoolArg index out of range"); return ParameterCode.OutOfRange; @@ -1714,7 +1844,7 @@ namespace Microsoft.Singularity return ParameterCode.OutOfRange; } - if ( boolArgSet[index] == null) { + if (boolArgSet[index] == null) { value = false; return ParameterCode.NotSet; } @@ -1825,7 +1955,8 @@ namespace Microsoft.Singularity } } //else DebugStub.Break(); - } finally { + } + finally { processMutex.ReleaseMutex(); } return temp; diff --git a/base/Kernel/Singularity/ProcessStart.cs b/base/Kernel/Singularity/ProcessStart.cs index 54f3361..2d9a23a 100644 --- a/base/Kernel/Singularity/ProcessStart.cs +++ b/base/Kernel/Singularity/ProcessStart.cs @@ -4,7 +4,8 @@ // // ==--== -namespace Microsoft.Singularity { +namespace Microsoft.Singularity +{ using System; diff --git a/base/Kernel/Singularity/ProcessStopException.cs b/base/Kernel/Singularity/ProcessStopException.cs index 1a1424b..67cd536 100644 --- a/base/Kernel/Singularity/ProcessStopException.cs +++ b/base/Kernel/Singularity/ProcessStopException.cs @@ -1,5 +1,5 @@ // -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // namespace Microsoft.Singularity diff --git a/base/Kernel/Singularity/ProcessUncaughtException.cs b/base/Kernel/Singularity/ProcessUncaughtException.cs index e58d35a..36ccf38 100644 --- a/base/Kernel/Singularity/ProcessUncaughtException.cs +++ b/base/Kernel/Singularity/ProcessUncaughtException.cs @@ -1,18 +1,18 @@ // -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // namespace Microsoft.Singularity { using System; using System.Runtime.CompilerServices; - using Microsoft.Singularity.X86; // If a process throws an exception to the kernel, the // exception handling mechanism throws a new ProcessUncaughtException // into the kernel's code. This ensures that the kernel // does not see the process's exception object, which lives // in a different GC domain. + [AccessedByRuntime("referenced from halforgc.asm")] public class ProcessUncaughtException : SystemException { public ProcessUncaughtException() : base("Arg_ProcessUncaughtException") {} diff --git a/base/Kernel/Singularity/Processor.cs b/base/Kernel/Singularity/Processor.cs index 6dff76f..5e6bbd0 100644 --- a/base/Kernel/Singularity/Processor.cs +++ b/base/Kernel/Singularity/Processor.cs @@ -13,7 +13,7 @@ //#define DEBUG_INTERRUPTS //#define DEBUG_DISPATCH_TIMER //#define DEBUG_DISPATCH_IO -//#define SAMPLE_PC +//#define CHECK_DISABLE_INTERRUPTS // #define SINGULARITY_ASMP @@ -22,17 +22,20 @@ using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Threading; +using Microsoft.Singularity.Eventing; using Microsoft.Singularity.Hal; using Microsoft.Singularity.Io; +using Microsoft.Singularity.Isal; using Microsoft.Singularity.Memory; using Microsoft.Singularity.Scheduling; -using Microsoft.Singularity.X86; +using Microsoft.Singularity.V1.Threads; -// haryadi: for Abi Call +// For Abi Call // using Microsoft.Singularity.V1.Services; namespace Microsoft.Singularity { + [CLSCompliant(false)] public enum ProcessorEvent : ushort { @@ -43,39 +46,32 @@ namespace Microsoft.Singularity [CLSCompliant(false)] [CCtorIsRunDuringStartup] + [AccessedByRuntime("referenced in hal.cpp/processor.cpp")] public class Processor { - private const uint interruptStackSize = 0x2000; - private const uint exceptionStackSize = 0x2000; - private const uint schedulerStackSize = 0x2000; + // Callback object for context switching + static ResumeThreadCallback resumeThreadCallback; - internal unsafe ProcessorContext* context; + private ProcessorLogger ProcessorLog = null; + private ProcessorCounter processorCounter = null; + internal SamplingProfiler Profiler = null; + internal bool nextSampleIdle = false; - public static Processor[] processorTable; + internal static bool IsSamplingEnabled = false; - //As requested/desired by the SchedulerClock - - private HalPic pic; - private HalTimer timer; - private HalClock clock; - - // haryadi - private static IHalMemory halMemory; - - private byte timerInterrupt; - private byte clockInterrupt; - - private bool inInterruptContext = false; - private bool halted = false; - - public readonly int processorIndex; - public uint NumExceptions = 0; - public uint NumInterrupts = 0; - public uint NumContextSwitches = 0; - private int[] interruptCounts; - private ulong cyclesPerSecond; - - private static int runningCpus = 0; + // + // This is called by HalDevicesApic.StartApProcessors() when + // initializing additional physical AP processors to set its + // hardware state when its started. + // + public void InitializeKernelThreadState(Thread thread, + UIntPtr kernelStackBegin, + UIntPtr kernelStackLimit) + { + kernelThread = thread; + kernelStackBegin = kernelStackBegin; + kernelStackLimit = kernelStackLimit; + } public HalTimer Timer { @@ -83,16 +79,22 @@ namespace Microsoft.Singularity get { return timer; } } - public Thread IdleThread; - - internal static void InitializeProcessorTable() + public HalClock Clock { - processorTable = new Processor [MpBootInfo.MAX_CPU]; + [NoHeapAllocation] + get { return clock; } + } + + internal static void InitializeProcessorTable(int cpus) + { + // use the full value initially + ExpectedProcessors = cpus; + processorTable = new Processor[cpus]; for (int i = 0; i < processorTable.Length; i++) { processorTable[i] = new Processor(i); } DebugStub.WriteLine("Processors: {0} of {1}", - __arglist(processorTable.Length, MpBootInfo.MAX_CPU)); + __arglist(processorTable.Length, cpus)); } internal static void AllocateStack(UIntPtr size, out UIntPtr begin, out UIntPtr limit) @@ -108,40 +110,110 @@ namespace Microsoft.Singularity private Processor(int index) { processorIndex = index; + if (interruptCounts == null) { interruptCounts = new int [256]; } + + ProcessorLog = ProcessorLogger.Create("ProcessorLogger:"+index.ToString()); + processorCounter = ProcessorCounter.Create("ProcessorCounters:"+index.ToString(), 256); + + DebugStub.WriteLine("Processor: {0}", __arglist(index)); } - public unsafe void Initialize(int processorId) + public void EnableProfiling() { + if (SamplingEnabled()) { + Profiler = SamplingProfiler.Create("SampleProfiler:" + Id.ToString(), + 32, // maximum stack depth + Kernel.ProfilerBufferSize); // sampling buffer size + + DebugStub.WriteLine("Sampling profiler enabled"); + } + } + + public static Processor EnableProcessor(int processorId) + { + Processor p = processorTable[processorId]; + p.Initialize(processorId); + return p; + } + + private unsafe void Initialize(int processorId) + { + uint DefaultStackSize = 0xA000; + processorTable[processorId] = this; - BootInfo bi = BootInfo.GetBootInfo(); - CpuInfo ci = bi.GetCpuInfo(processorId); - context = (ProcessorContext*) ci.Fs32; + context = (ProcessorContext*) Isa.GetCurrentCpu(); + + DebugStub.WriteLine("Processor context: {0} {1:x8}", + __arglist(processorId, Kernel.AddressOf(context))); + context->UpdateAfterGC(this); - AllocateStack(interruptStackSize, - out context->interruptStackBegin, - out context->interruptStackLimit); - AllocateStack(exceptionStackSize, - out context->exceptionStackBegin, - out context->exceptionStackLimit); - AllocateStack(schedulerStackSize, - out context->schedulerStackBegin, - out context->schedulerStackLimit); + if (0 != processorId) { + Thread.BindKernelThread(kernelThread, + kernelStackBegin, + kernelStackLimit); + } + + AllocateStack(DefaultStackSize, + out context->cpuRecord.interruptStackBegin, + out context->cpuRecord.interruptStackLimit); Tracing.Log(Tracing.Debug, "Initialized Processor {0}", (UIntPtr)processorId); - Tracing.Log(Tracing.Debug, "asmInterruptStack={0:x}..{1:x}", - context->interruptStackBegin, - context->interruptStackLimit); - inInterruptContext = false; - MpExecution.AddProcessorContext(context); + Tracing.Log(Tracing.Debug, "asmInterruptStack={0:x}..{1:x}", + context->cpuRecord.interruptStackBegin, + context->cpuRecord.interruptStackLimit); + +#if false + DebugStub.WriteLine("proc{0}: InterruptStack={1:x}..{2:x}", + __arglist( + processorId, + context->cpuRecord.interruptStackBegin, + context->cpuRecord.interruptStackLimit + )); +#endif Interlocked.Increment(ref runningCpus); + MpExecution.AddProcessorContext(context); + + // Need to allocate this callback object outside of NoThreadAllocation region + if (processorId == 0) { + resumeThreadCallback = new ResumeThreadCallback(); + } + + Isa.EnableCycleCounter(); + } + + /// + /// + /// Initialize dispatcher + /// + /// + /// Id of the processor dispatcher belongs to + /// + public static void InitializeDispatcher(int processorId) + { + // Create a processor dispatcher + processorTable[processorId].dispatcher = new ProcessorDispatcher(); + + // Initialize dispatcher + processorTable[processorId].dispatcher.Initialize(processorTable[processorId]); + } + + /// + /// + /// Activate Timer + /// + /// + /// + public static void ActivateTimer(int processorId) + { + processorTable[processorId].timer.SetNextInterrupt(TimeSpan.FromMilliseconds(5)); } [NoHeapAllocation] @@ -152,78 +224,22 @@ namespace Microsoft.Singularity Interlocked.Decrement(ref runningCpus); +// #if DEBUG + // Interrupts should be off now + if (!InterruptsDisabled()) { + DebugStub.WriteLine("Processor::Uninitialize AP Processor does not have interrupts disabled\n"); + DebugStub.Break(); + } +// #endif // DBG + // Processor is out of commission HaltUntilInterrupt(); - } +// #if DEBUG - private static void MeasureFxSavePerformance() - { -#if DO_FXSAVE_TEST - DebugStub.WriteLine("Starting fxsave test."); - int timePerFxsave = TestFxsave(); - DebugStub.Print("Cycles per fxsave/fxrstor pair: {0}", - __arglist(timePerFxsave)); -#endif // DO_FXSAVE_TEST - } + DebugStub.WriteLine("Processor::Uninitialize: AP processor woke up on shutdown!\n"); + DebugStub.Break(); +// #endif // DBG - private static void MeasureFs0Performance() - { -#if DO_FS0_TEST - DebugStub.WriteLine("Starting fs:[0] test."); - int timePerFs0 = TestFs0(); - DebugStub.WriteLine("Cycles per fs:[0] test: {0}", - __arglist(timePerFs0)); -#endif // DO_FS0_TEST - } - - private static void MeasureCliStiPerformance() - { -#if DO_CLI_STI_TEST - DebugStub.WriteLine("Starting cli/sti test."); - int timePerCli = TestCliSti(); - DebugStub.WriteLine("Cycles per cli/sti test: {0}", - __arglist(timePerCli)); - - ulong beg; - ulong end; - int loops = 1000000; - - DebugStub.WriteLine("Starting C# cli/sti test."); - bool enabled = DisableInterrupts(); - - RestoreInterrupts(true); - - beg = HalGetCycleCount(); - while (loops-- > 0) { - RestoreInterrupts(true); // 1 - DisableInterrupts(); - RestoreInterrupts(true); // 2 - DisableInterrupts(); - RestoreInterrupts(true); // 3 - DisableInterrupts(); - RestoreInterrupts(true); // 4 - DisableInterrupts(); - RestoreInterrupts(true); // 5 - DisableInterrupts(); - RestoreInterrupts(true); // 6 - DisableInterrupts(); - RestoreInterrupts(true); // 7 - DisableInterrupts(); - RestoreInterrupts(true); // 8 - DisableInterrupts(); - RestoreInterrupts(true); // 9 - DisableInterrupts(); - RestoreInterrupts(true); // 10 - DisableInterrupts(); - } - end = HalGetCycleCount(); - DisableInterrupts(); - RestoreInterrupts(enabled); - - timePerCli = ((int)((end - beg) / 1000000)) / 10; - DebugStub.WriteLine("Cycles per C# cli/sti test: {0}", - __arglist(timePerCli)); -#endif // DO_CLI_STI_TEST } public void AddPic(HalPic pic) @@ -231,10 +247,12 @@ namespace Microsoft.Singularity Tracing.Log(Tracing.Audit, "AddPic({0})\n", Kernel.TypeName(pic)); this.pic = pic; + } - MeasureFxSavePerformance(); - MeasureFs0Performance(); - MeasureCliStiPerformance(); + [NoHeapAllocation] + public HalPic GetPic() + { + return this.pic; } [NoHeapAllocation] @@ -256,7 +274,7 @@ namespace Microsoft.Singularity } [NoHeapAllocation] - public static void AddHalMemory(IHalMemory aHalMemory) + public static void AddMemory(HalMemory aHalMemory) { Tracing.Log(Tracing.Audit, "AddHalMemory({0})\n", Kernel.TypeName(aHalMemory)); @@ -272,19 +290,30 @@ namespace Microsoft.Singularity unchecked { Tracing.Log(Tracing.Debug, "Interrupt stack: {0:x} {1:x}..{2:x} uses", currentStack, - context->interruptStackBegin, - context->interruptStackLimit); + context->cpuRecord.interruptStackBegin, + context->cpuRecord.interruptStackLimit); } } // Returns the processor that the calling thread is running on. - // Needs to be fixed. (Added for consistency) + // TODO:Needs to be fixed. (Added for consistency) public static Processor CurrentProcessor { [NoHeapAllocation] get { return GetCurrentProcessor(); } } + /// + /// + /// Retrieve current dispatcher + /// + public ProcessorDispatcher Dispatcher + { + [NoHeapAllocation] + get { return dispatcher; } + } + + [NoHeapAllocation] public static int GetCurrentProcessorId() { @@ -294,20 +323,32 @@ namespace Microsoft.Singularity public unsafe int Id { [NoHeapAllocation] - get { return context->cpuId; } + get { + return context->cpuRecord.id; + } + } + + [NoHeapAllocation] + public static Processor GetProcessor(int i) + { + if (null == processorTable && i == 0) { + return CurrentProcessor; + } + else { + return processorTable[i]; + } + } + + public static int CpuCount + { + [NoHeapAllocation] + get { return null == processorTable ? 1 : processorTable.Length; } } [NoHeapAllocation] public static void HaltUntilInterrupt() { - CurrentProcessor.halted = true; - HaltUntilInterruptNative(); - } - - public bool InInterruptContext - { - [NoHeapAllocation] - get { return inInterruptContext; } + Platform.ThePlatform.Halt(); } [NoHeapAllocation] @@ -319,11 +360,24 @@ namespace Microsoft.Singularity public int GetIrqCount(byte irq) { HalPic pic = CurrentProcessor.pic; + + // Only set on native hal + if (pic == null) { + return 0; + } + return interruptCounts[pic.IrqToInterrupt(irq)]; } public static byte GetMaxIrq() { + HalPic pic = CurrentProcessor.pic; + + // This is not set on halhyper or halwin32 + if (pic == null) { + return 0; + } + return CurrentProcessor.pic.MaximumIrq; } @@ -339,132 +393,148 @@ namespace Microsoft.Singularity public static ulong CycleCount { [NoHeapAllocation] - get { return GetCycleCount(); } + get { return Isa.GetCycleCount(); } } ////////////////////////////////////////////////////////////////////// // // -#if SAMPLE_PC - #if SINGULARITY_MP - // This should be easy to fix. - #error "SAMPLE_PC does not work in conjunction with SINGULARITY_MP." - #endif - - const int pcLog2Samples = 16; - const int pcMaxSamples = 2 << pcLog2Samples; - const int sampleMask = pcMaxSamples - 1; - - static bool nextSampleIdle = false; - - static UIntPtr[] pcSamples = null; - static int pcHead = 0; // head position - static int pcLength = 0; // length of live data in buffer - - static uint pcSamplesI = 0; - static ulong pcSamplesCounterThen; -#endif // SAMPLE_PC [NoHeapAllocation] public static bool SamplingEnabled() { -#if SAMPLE_PC - return true; -#else - return false; -#endif // SAMPLE_PC + return (Kernel.ProfilerBufferSize != 0); } internal static void StartSampling() { -#if SAMPLE_PC - pcSamplesCounterThen = Processor.CycleCount; - pcSamples = new UIntPtr[2 << pcLog2Samples]; - nextSampleIdle = false; -#endif // SAMPLE_PC + if (SamplingEnabled()) { + IsSamplingEnabled = true; + } } [NoHeapAllocation] - internal static void NextSampleIsIdle() + internal void NextSampleIsIdle() { -#if SAMPLE_PC nextSampleIdle = true; -#endif // SAMPLE_PC } //////////////////////////////////////////////////// External Methods. // - [MethodImpl(MethodImplOptions.InternalCall)] - [StackBound(32)] [NoHeapAllocation] - internal static extern ulong GetCycleCount(); + internal static Processor GetCurrentProcessor() + { + unsafe { + return GetCurrentProcessorContext()->processor; + } + } - [MethodImpl(MethodImplOptions.InternalCall)] - [StackBound(32)] [NoHeapAllocation] - internal static unsafe extern UIntPtr GetFrameEip(UIntPtr ebp); + [AccessedByRuntime("output to header : called by c code")] + internal static unsafe ThreadContext * GetCurrentThreadContext() + { + unsafe { + return (ThreadContext *) Isa.GetCurrentThread(); + } + } - [MethodImpl(MethodImplOptions.InternalCall)] - [StackBound(32)] [NoHeapAllocation] - internal static unsafe extern UIntPtr GetFrameEbp(UIntPtr ebp); + [AccessedByRuntime("output to header : called by c code")] + internal static unsafe ProcessorContext * GetCurrentProcessorContext() + { + unsafe { + return (ProcessorContext *) Isa.GetCurrentCpu(); + } + } - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(32)] [NoHeapAllocation] - internal static extern UIntPtr GetStackPointer(); + internal static Thread GetCurrentThread() + { + unsafe { + return GetCurrentThreadContext()->thread; + } + } - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(32)] [NoHeapAllocation] - internal static extern UIntPtr GetFramePointer(); + internal static void SetCurrentThreadContext(ref ThreadContext context) + { + unsafe { + fixed (ThreadContext *c = &context) { + Isa.SetCurrentThread(ref c->threadRecord); + } + } + } - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(32)] + /// + /// + /// Verify if thread currently is running on interrupt stack + /// + /// [NoHeapAllocation] - private static extern Processor GetCurrentProcessor(); + [Inline] + internal bool IsOnInterruptStack(Thread currentThread) + { + return Isa.IsRunningOnInterruptStack; + } - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(32)] - [NoHeapAllocation] - internal static unsafe extern ThreadContext * GetCurrentThreadContext(); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(32)] - [NoHeapAllocation] - internal static unsafe extern ProcessorContext * GetCurrentProcessorContext(); - - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(32)] - [NoHeapAllocation] - internal static extern Thread GetCurrentThread(); - - [MethodImpl(MethodImplOptions.InternalCall)] - [StackBound(32)] - [NoHeapAllocation] - internal static extern void SetCurrentThreadContext(ref ThreadContext context); + internal bool InInterruptContext + { + [NoHeapAllocation] + get { + return Isa.InInterruptContext; + } + } + [AccessedByRuntime("defined in halforgc.asm")] [MethodImpl(MethodImplOptions.InternalCall)] [GCAnnotation(GCOption.GCFRIEND)] [StackBound(32)] [NoHeapAllocation] internal static extern void SwitchToThreadContext(ref ThreadContext oldContext, ref ThreadContext newContext); - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(32)] + private class ResumeThreadCallback : Isa.ICallback + { + internal override UIntPtr Callback(UIntPtr param) + { + unsafe { + ThreadContext* newContext = (ThreadContext *) param; + + // Switch our thread context, synchronizing with the dispatcher as necessary. + ProcessorDispatcher.TransferToThreadContext(ref *GetCurrentThreadContext(), + ref *newContext); + + // Resume in the new context. Note that this call does not return. + newContext->threadRecord.spill.Resume(); + + return 0; + } + } + } + + [AccessedByRuntime("referenced by halforgc.asm")] [NoHeapAllocation] - internal static extern void SwitchToThreadContextNoGC(ref ThreadContext newContext); + [GCAnnotation(GCOption.NOGC)] + internal static unsafe void SwitchToThreadContextNoGC(ref ThreadContext newContext) + { + // Interrupts should be disabled at this point + VTable.Assert(Processor.InterruptsDisabled()); + + // Save appears to returns twice: once with true on this thread after + // the save, and once with false when the context is restored. + + if (GetCurrentThreadContext()->threadRecord.spill.Save()) { + // Initial return from save; time to swap in the new context. + // Must do this on the interrupt stack, since once we release the + // dispatch lock the saved context is free to run (and we would + // be on the same stack.) + fixed (ThreadContext *c = &newContext) { + // Note that this does not return. + Isa.CallbackOnInterruptStack(resumeThreadCallback, (UIntPtr) c); + } + } + + // Saved context will resume here + } [MethodImpl(MethodImplOptions.InternalCall)] [GCAnnotation(GCOption.NOGC)] @@ -478,539 +548,102 @@ namespace Microsoft.Singularity [NoHeapAllocation] internal static extern void TestSave(ref ThreadContext newContext); - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(32)] - [NoHeapAllocation] - internal static extern void EnterRing3(); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(32)] - [NoHeapAllocation] - internal static extern bool AtKernelPrivilege(); - ////////////////////////////////////////////////////////////////////// // - // // These methods are currently marked external because they are used // by device drivers. We need a tool to verify that device drivers // are in fact using them correctly! // - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] - [NoHeapAllocation] - public static extern bool DisableInterrupts(); - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] + [AccessedByRuntime("accessed by C++")] [NoHeapAllocation] - public static extern void RestoreInterrupts(bool enabled); + [GCAnnotation(GCOption.NOGC)] + public static bool DisableLocalPreemption() + { + return DisableInterrupts(); + } - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] + [AccessedByRuntime("accessed by C++")] [NoHeapAllocation] - public static extern void SetIdtTable(); + [GCAnnotation(GCOption.NOGC)] + public static void RestoreLocalPreemption(bool enabled) + { + RestoreInterrupts(enabled); + } - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] + [AccessedByRuntime("accessed by C++")] [NoHeapAllocation] - internal static extern void ClearIdtTable(); + [GCAnnotation(GCOption.NOGC)] + public static bool DisableInterrupts() + { +#if CHECK_DISABLE_INTERRUPTS + bool wasDisabled = InterruptsDisabled(); +#endif + bool result = Isa.DisableInterrupts(); + +#if CHECK_DISABLE_INTERRUPTS + if (result && wasDisabled) { + DebugStub.Break(); + } +#endif + + return result; + } + + [AccessedByRuntime("accessed by C++")] + [NoHeapAllocation] + [GCAnnotation(GCOption.NOGC)] + public static void RestoreInterrupts(bool enabled) + { + int i = 0; + try { +#if CHECK_DISABLE_INTERRUPTS + if (!InterruptsDisabled()) { + DebugStub.Break(); + } +#endif + i = 1; + if (enabled) { + i = 2; + // Processor flag should be turned off before this call + if (GetCurrentProcessor().InInterruptContext) { + DebugStub.Break(); + } + i = 3; + Isa.EnableInterrupts(); + } + i = 5; +#if CHECK_DISABLE_INTERRUPTS + if (enabled && InterruptsDisabled()) { + DebugStub.Break(); + } +#endif + } + catch(Exception e) { + DebugStub.Break(); + } + } // Use this method for assertions only! - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] + [AccessedByRuntime("accessed by C++")] + [NoHeapAllocation] [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] - [NoHeapAllocation] - public static extern bool InterruptsDisabled(); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] - [NoHeapAllocation] - public static extern void HaltUntilInterruptNative(); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] - [NoHeapAllocation] - public static extern void InitFpu(); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] - [NoHeapAllocation] - private static extern uint ReadFpuStatus(); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] - [NoHeapAllocation] - private static extern void ClearFpuStatus(); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] - [NoHeapAllocation] - public static extern void WriteMsr(uint offset, - ulong value); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] - [NoHeapAllocation] - public static extern ulong ReadMsr(uint offset); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] - [NoHeapAllocation] - public static extern ulong ReadPmc(uint offset); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [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); - -#if DO_FXSAVE_TEST - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(128)] - [NoHeapAllocation] - public static extern int TestFxsave(); -#endif - -#if DO_FS0_TEST - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(128)] - [NoHeapAllocation] - public static extern int TestFs0(); -#endif - -#if DO_CLI_STI_TEST - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(128)] - [NoHeapAllocation] - public static extern int TestCliSti(); -#endif - - ////////////////////////////////////////////////////////////////////// - // - - /// - /// Hardware exceptions are mapped to vectors 0x00..0x1f. - /// They always use a processor context record. - /// - [AccessedByRuntime("referenced from Processor.cpp")] - [NoHeapAllocation] - internal static void DispatchException(int interrupt, - ref ThreadContext context) + public static bool InterruptsDisabled() { - // Tracing.Log(Tracing.Debug, "Exception {0}", (uint)interrupt); - GetCurrentProcessor().DispatchSpecificException(interrupt, ref context); + return Platform.ThePlatform.AreInterruptsDisabled(); } [NoHeapAllocation] - private void DispatchSpecificException(int interrupt, - ref ThreadContext context) + public unsafe void IncrementInterruptCounts(int interrupt) { - // Indicate that we are in an interrupt context. - bool wasInInterrupt = inInterruptContext; - inInterruptContext = true; - - NumExceptions++; - interruptCounts[interrupt]++; - - if (interrupt == EVectors.Nmi && MpExecution.FreezeRequested) { - MpExecution.FreezeProcessor(ref context); - } - else if (interrupt == EVectors.SingleStep) { - DebugStub.Trap(ref context, false); - } - else if (interrupt == EVectors.Breakpoint) { - DebugStub.Trap(ref context, false); - } - else if (interrupt == EVectors.FirstChanceException) { - DebugStub.Trap(ref context, true); - } - else if (interrupt == EVectors.FpuMathFault) { - if ((context.mmx.fsw & Fpsw.StackFaultError) != 0) { - if ((context.mmx.fsw & Fpsw.C1) != 0) { - DebugStub.WriteLine("FPU Stack Overflow Exception (FP SW = 0x{0:x4})", - __arglist(context.mmx.fsw)); - } - else { - DebugStub.WriteLine("FPU Stack Underflow Exception (FP SW = 0x{0:x4})", - __arglist(context.mmx.fsw)); - } - } - else { - DebugStub.WriteLine("FPU Unit exception (FP SW = 0x{0:x4})", - __arglist(context.mmx.fsw)); - } - - DebugStub.Trap(ref context, false); - context.mmx.fcw |= Fpsw.ErrorClearMask; - } - else { - Tracing.Log(Tracing.Debug, - "No recognized exception handler (0x{0:x2}", - (uint)interrupt); - DebugStub.Trap(ref context, false); - - DebugStub.WriteLine("No recognized exception handler (0x{0:x2}", - __arglist(interrupt)); - Thread.Display(ref context, ""); - } - inInterruptContext = wasInInterrupt; - } - - /// - /// Hardware interrupts are mapped to vectors 0x20..0xff. - /// They always use the thread's context record. - /// - [AccessedByRuntime("referenced from Processor.cpp")] - [NoHeapAllocation] - internal static void DispatchInterrupt(int interrupt, - ref ThreadContext context) - { - GetCurrentProcessor().DispatchSpecificInterrupt(interrupt, ref context); - } - - [NoHeapAllocation] - private unsafe void DispatchSpecificInterrupt(int interrupt, - ref ThreadContext context) - { - // Indicate that we are in an interrupt context. - Thread target = null; - bool wasInInterrupt = inInterruptContext; - inInterruptContext = true; - - Kernel.Waypoint(801); - NumInterrupts++; NumExceptions++; interruptCounts[interrupt]++; - // Don't generate loads of output for debugger-related interrupts -#if DEBUG_INTERRUPTS - DebugStub.WriteLine("Int{0:x2}", __arglist(interrupt)); - Thread.DisplayAbbrev(ref context, " int beg"); - Thread.Display(ref context, " int beg"); - - if (!context.IsFirst()) { - DebugStub.WriteLine("*-*-*-*-*-* !IsFirst *-*-*-*-*-*"); - Thread.Display(ref context, " !IsFirst"); + if (ProcessorLog != null) { + // ProcessorLog.Log(interrupt); + processorCounter.Buffer[interrupt].Hits += 1; } -#endif - -#if SAMPLE_PC - // Sample PC values - ulong pcNow = Processor.CycleCount; - uint pcDiff = unchecked((uint)(pcNow - pcSamplesCounterThen)); - if (pcSamples != null && pcDiff != 0 && nextSampleIdle == false) { - int oldPcHead = pcHead; - - // Sample instance number - pcSamples[pcHead] = (UIntPtr)unchecked(pcSamplesI++); - pcHead = unchecked((pcHead + 1) & sampleMask); - - // Save relative time in ticks - pcSamples[pcHead] = pcDiff; - pcHead = unchecked((pcHead + 1) & sampleMask); - - pcSamples[pcHead] = (UIntPtr)interrupt; - pcHead = unchecked((pcHead + 1) & sampleMask); - - pcSamplesCounterThen = pcNow; - - // Save stack - UIntPtr eip = context.eip; - UIntPtr ebp = context.ebp; - while (true) { - if (eip == 0) { break; } - pcSamples[pcHead] = eip; - pcHead = (pcHead + 1) & sampleMask; - if (ebp == 0) { break; } - eip = Processor.GetFrameEip(ebp); - ebp = Processor.GetFrameEbp(ebp); - } - pcSamples[pcHead] = 0; - pcHead = (pcHead + 1) & sampleMask; - - pcLength += (pcHead + pcMaxSamples - oldPcHead) & sampleMask; - if (pcLength > pcMaxSamples) - pcLength = pcMaxSamples; - - // Clear any partially overwritten sample set ahead - int i = pcHead; - while (pcSamples[i] != 0) { - pcSamples[i] = 0; - pcLength--; - i = (i + 1) & sampleMask; - } - } - nextSampleIdle = false; -#endif // SAMPLE_PC - - - if (halted) { - clock.CpuResumeFromHaltEvent(); - halted = false; - } - - unchecked { - if (interrupt != clockInterrupt) { - // We don't log the clockInterrupt because of all the spew. - Tracing.Log(Tracing.Debug, "Interrupt 0x{0:x}, count={1:x}", - (UIntPtr)(uint)interrupt, - (UIntPtr) interruptCounts[interrupt] - ); - } - } - - Monitoring.Log(Monitoring.Provider.Processor, - (ushort)ProcessorEvent.Interrupt, 0, - (uint)interrupt, 0, 0, 0, 0); - - - if ((context.efl & EFlags.IF) == 0) { - DebugStub.WriteLine("Int{0:x2}", __arglist(interrupt)); - Thread.DisplayAbbrev(ref context, " int beg"); - DebugStub.WriteLine("Interrupt 0x{0:x2} thrown while interrupts disabled.", - __arglist(interrupt)); - DebugStub.Break(); - } - - - if (interrupt == timerInterrupt) { - timer.ClearInterrupt(); - SchedulerTime now = SchedulerTime.Now; - Scheduler.DispatchLock(); - try { - if (context.thread == IdleThread) { -#if TRACE_INTERRUPTS - Tracing.Log(Tracing.Audit, - "Timer interrupt in idle."); -#endif // TRACE_INTERRUPTS - target = Scheduler.OnTimerInterrupt(null, now); - } - else { -#if TRACE_INTERRUPTS - Tracing.Log(Tracing.Audit, - "Timer interrupt in tid={0:x3}.", - (uint)context.thread.GetThreadId()); -#endif // TRACE_INTERRUPTS - target = Scheduler.OnTimerInterrupt(context.thread, now); - } - Scheduler.SelectingThread(target); -#if DEBUG_DISPATCH_TIMER - DebugStub.WriteLine("OnTime.Selecting"); -#endif // DEBUG_DISPATCH_TIMER - } - finally { - Scheduler.DispatchRelease(); - } - } - else if (interrupt == clockInterrupt) { - clock.ClearInterrupt(); - -#if DEBUG - // Check for a debug break. - if (DebugStub.PollForBreak()) { - DebugStub.WriteLine("Debugger ctrl-break after interrupt 0x{0:x2}", - __arglist(interrupt)); - MpExecution.FreezeAllProcessors(); - DebugStub.Break(); // used to be: Trap(ref context, false); - MpExecution.ThawAllProcessors(); - } -#endif - } - else if (interrupt == EVectors.PingPongInt) { - HalDevices.ClearFixedIPI(); - - inInterruptContext = false; - - int task = MpExecution.GetIntrPingPong(this.Id); - DebugStub.WriteLine - ("HSG: ***** cpu.{0} gets {1}", __arglist(this.Id, task)); - - RunPingPongInt(task-1); // send again - inInterruptContext = true; - } - else if (interrupt == EVectors.ApImage) { - HalDevices.ClearFixedIPI(); - inInterruptContext = false; - MpExecution.ApImage apImage; - bool okay = MpExecution.GetIntrApImage(this.Id, out apImage); - if (!okay) { - DebugStub.WriteLine("ERROR: ApImage queue is empty"); - DebugStub.Break(); - } - - inInterruptContext = true; - - DebugStub.WriteLine ("HSG: ** cpu.{0} gets e.{1:x}", - __arglist(this.Id, - apImage.entryPoint)); - - Processor.MpCallEntryPoint(apImage.entryPoint); - } - else if (interrupt == EVectors.AbiCall) { - HalDevices.ClearFixedIPI(); -#if GENERATE_ABI_SHIM - // haryadi: tells AP service thread to process - // this abi call later - ApServiceThread.abiEvent.Set(); -#endif - } - else if (interrupt == EVectors.HaltApProcessors) { - Processor p = Processor.CurrentProcessor; - if (p.Id != 0) { - if (context.thread != IdleThread) { - Scheduler.DispatchLock(); - try { - Scheduler.OnProcessorShutdown(context.thread); - } - finally { - Scheduler.DispatchRelease(); - } - } - p.Uninitialize(p.Id); - } - } - else if (!HalDevices.InternalInterrupt((byte)interrupt)) { - IoIrq.SignalInterrupt(pic.InterruptToIrq((byte)interrupt)); - pic.ClearInterrupt((byte)interrupt); - - Scheduler.DispatchLock(); - try { - if (context.thread == IdleThread) { -#if TRACE_INTERRUPTS - Tracing.Log(Tracing.Audit, - "I/O interrupt in idle."); -#endif // TRACE_INTERRUPTS - target = Scheduler.OnIoInterrupt(null); - } - else { -#if TRACE_INTERRUPTS - Tracing.Log(Tracing.Audit, - "I/O interrupt in tid={0:x3}.", - (uint)context.thread.GetThreadId()); -#endif // TRACE_INTERRUPTS - target = Scheduler.OnIoInterrupt(context.thread); - } -#if DEBUG_DISPATCH_IO - DebugStub.WriteLine("++DispatchInterruptEvent Irq={0:x2}, Thread={1:x8}", - __arglist(pic.InterruptToIrq((byte)interrupt), - Kernel.AddressOf(target))); -#endif // DEBUG_DISPATCH_IO - Scheduler.SelectingThread(target); - } - finally { - Scheduler.DispatchRelease(); - } - } - -#if DEBUG_INTERRUPTS - DebugStub.WriteLine("Int{0:x2}", __arglist(interrupt)); - Thread.DisplayAbbrev(ref context, " int fin"); - if (!InterruptsDisabled()) { - DebugStub.WriteLine(" interrupts enabled!!!!!!!"); - Thread.Display(ref context, " !IsFirst"); - DebugStub.Break(); - } -#if DEBUG_DEEPER - DebugStub.WriteLine("Int{0:x2}", __arglist(interrupt)); - Thread.DisplayAbbrev(ref context, " int end"); -#endif -#endif - - // if (context.lastExecutionTimeUpdate != - // context.thread.context.lastExecutionTimeUpdate) { - // DebugStub.WriteLine("context != context.thread.context: {0}, {1}, ", - // __arglist(context.lastExecutionTimeUpdate, - // context.thread.context.lastExecutionTimeUpdate)); - // } - -#if THREAD_TIME_ACCOUNTING - ulong now_a = Processor.CycleCount; - context.executionTime += now_a - - context.lastExecutionTimeUpdate; -#endif - - if (target != null) { -#if THREAD_TIME_ACCOUNTING - target.context.lastExecutionTimeUpdate = now_a; -#endif - Monitoring.Log(Monitoring.Provider.Processor, - (ushort)ProcessorEvent.Resume, 0, - (uint)target.context.threadIndex, 0, 0, 0, 0); - // Return to the thread selected by the scheduler if any. - SetCurrentThreadContext(ref target.context); - } - else { -#if THREAD_TIME_ACCOUNTING - context.lastExecutionTimeUpdate = now_a; -#endif - Monitoring.Log(Monitoring.Provider.Processor, - (ushort)ProcessorEvent.Resume, 0, - (uint)context.threadIndex, 0, 0, 0, 0); - SetCurrentThreadContext(ref context); - } - - inInterruptContext = wasInInterrupt; - } - - //Simulator Use - //Sets the nextTimerInterrupt - [NoHeapAllocation] - public bool SetNextTimerInterrupt(TimeSpan delta) - { - long span = delta.Ticks; - if (span > timer.MaxInterruptInterval) { - span = timer.MaxInterruptInterval; - } - else if (span < timer.MinInterruptInterval) { - span = timer.MinInterruptInterval; - } - - bool success = timer.SetNextInterrupt(span); - DebugStub.Assert(success); - return success; - } - - [NoHeapAllocation] - public bool SetNextTimerInterrupt(SchedulerTime until) - { - return SetNextTimerInterrupt(until - SchedulerTime.Now); } [NoHeapAllocation] @@ -1027,167 +660,108 @@ namespace Microsoft.Singularity SetCurrentThreadContext(ref currentThread.context); } - - ////////////////////////////////////////////////////////////////////// - // - // - // These methods are public and safe to use from any where provided - // there's at least 2 call frame on the stack. - // [NoHeapAllocation] - public static UIntPtr GetCallerEip() + internal void ActivateDispatcher() { - UIntPtr currentFrame = GetFramePointer(); - UIntPtr callerFrame = GetFrameEbp(currentFrame); - if (callerFrame == UIntPtr.Zero) { - return UIntPtr.Zero; - } - UIntPtr callersCaller = GetFrameEip(callerFrame); - return callersCaller; + if (Platform.Cpu(processorIndex) != null){ + + // Wake processor + Platform.ThePlatform.WakeNow(processorIndex); + } } + /// /// - /// Provides a mini stack trace starting from the caller of the caller - /// of this method. + /// Set next alarm: timer that we are interested in /// + /// + /// Time until the next time we would like to be awaken + /// [NoHeapAllocation] - public static void GetStackEips(out UIntPtr pc1, out UIntPtr pc2, out UIntPtr pc3) + public void SetNextTimerInterrupt(TimeSpan delta) { - pc1 = UIntPtr.Zero; - pc2 = UIntPtr.Zero; - pc3 = UIntPtr.Zero; + // Make sure that interrupts are disabled + bool iflag = Processor.DisableInterrupts(); - UIntPtr currentFrame = GetFramePointer(); - UIntPtr callerFrame = GetFrameEbp(currentFrame); - if (callerFrame == UIntPtr.Zero) { - return; + TimeSpan start = delta; + if (delta < timer.MinInterruptInterval) { + delta = timer.MinInterruptInterval; } - pc1 = GetFrameEip(callerFrame); - callerFrame = GetFrameEbp(callerFrame); - if (callerFrame == UIntPtr.Zero) { - return; + if (delta > timer.MaxInterruptInterval) { + delta = timer.MaxInterruptInterval; } - pc2 = GetFrameEip(callerFrame); - callerFrame = GetFrameEbp(callerFrame); - if (callerFrame == UIntPtr.Zero) { - return; - } - pc3 = GetFrameEip(callerFrame); - } +#if false + DebugStub.WriteLine("-- SetNextTimerInterrupt(delta={0}, start={1} [min={2},max={3})", + __arglist(delta.Ticks, + start.Ticks, + timer.MinInterruptInterval.Ticks, + timer.MaxInterruptInterval.Ticks)); +#endif + timer.SetNextInterrupt(delta); - /// - /// Provides the full stack trace starting from the caller of the caller - /// of this method. - /// - /// Eip values in stack array from top to bottom - [NoHeapAllocation] - public static void GetStackEips(UIntPtr[] stack) - { - if (stack == null) { - return; - } - UIntPtr currentFrame = GetFramePointer(); - UIntPtr callerFrame = GetFrameEbp(currentFrame); - for (int index = 0; callerFrame != UIntPtr.Zero && index < stack.Length; index++) { - stack[index] = GetFrameEip(callerFrame); - callerFrame = GetFrameEbp(callerFrame); - } + // Restore interrupts if necessary + Processor.RestoreInterrupts(iflag); } ////////////////////////////////////////////////////////////////////// // - // // These (native) methods manipulate the local processor's paging // hardware. They can be used even before Processor.Initialize() // has been called. // - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] - [NoHeapAllocation] - private static extern void PrivateEnablePaging(uint pdpt); internal static void EnablePaging(AddressSpace bootstrapSpace) { - PrivateEnablePaging((uint)bootstrapSpace.PdptPage.Value); + Isa.EnablePaging((uint)bootstrapSpace.PdptPage.Value); } - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] - [NoHeapAllocation] - private static extern void PrivateChangeAddressSpace(uint pdpt); - internal static void ChangeAddressSpace(AddressSpace space) { - PrivateChangeAddressSpace((uint)space.PdptPage.Value); + Isa.ChangePageTableRoot((uint)space.PdptPage.Value); } - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] - [NoHeapAllocation] - internal static extern void DisablePaging(); - - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] - [NoHeapAllocation] - private static extern void PrivateInvalidateTLBEntry(UIntPtr pageAddr); - internal static void InvalidateTLBEntry(UIntPtr pageAddr) { DebugStub.Assert(MemoryManager.IsPageAligned(pageAddr)); - PrivateInvalidateTLBEntry(pageAddr); + Isa.InvalidateTLBEntry(pageAddr); } - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(16)] - [NoHeapAllocation] - private static extern uint GetCr3(); - - // haryadi - [AccessedByRuntime("output to header : defined in Processor.cpp")] - [MethodImpl(MethodImplOptions.InternalCall)] - [GCAnnotation(GCOption.NOGC)] - [StackBound(64)] - [NoHeapAllocation] - public static extern void MpCallEntryPoint(UIntPtr entry); - internal static AddressSpace GetCurrentAddressSpace() { - return new AddressSpace(new PhysicalAddress(GetCr3())); + return new AddressSpace(new PhysicalAddress(Isa.GetPageTableRoot())); } // - public static IHalMemory.ProcessorAffinity[] GetProcessorAffinity() + public static HalMemory.ProcessorAffinity[] GetProcessorAffinity() { - IHalMemory.ProcessorAffinity[] processors = + HalMemory.ProcessorAffinity[] processors = halMemory.GetProcessorAffinity(); return processors; } - public static IHalMemory.MemoryAffinity[] GetMemoryAffinity() + public static HalMemory.MemoryAffinity[] GetMemoryAffinity() { - IHalMemory.MemoryAffinity[] memories = + HalMemory.MemoryAffinity[] memories = halMemory.GetMemoryAffinity(); return memories; } + + public static unsafe void EnableMoreProcessors(int cpus) + { + ExpectedProcessors = cpus; + Platform.ThePlatform.EnableMoreCpus(cpus); + StartApProcessors(cpus); + } + /// Start application processors. [System.Diagnostics.Conditional("SINGULARITY_MP")] - public static void StartApProcessors() + public static void StartApProcessors(int cpuCount) { // At this point only the BSP is running. Tracing.Log(Tracing.Debug, "Processor.StartApProcessors()"); - HalDevices.StartApProcessors(); + Platform.StartApProcessors(cpuCount); // At this point the BSP and APs are running. } @@ -1197,19 +771,37 @@ namespace Microsoft.Singularity [System.Diagnostics.Conditional("SINGULARITY_MP")] public static void StopApProcessors() { - // At this point the BSP and APs are running. + // + // Note: This should go into a HAL interface and this + // code confined to Platform.cs + // + // At this point the BSP and APs are running. Tracing.Log(Tracing.Debug, "Processor.StopApProcessors()"); - HalDevices.BroadcastFixedIPI((byte)EVectors.HaltApProcessors, - true); + + if (Processor.GetRunningProcessorCount() > 1) { + + // + // This stops them in MpExecution in a halt state with + // interrupts off. + // + Platform.BroadcastFixedIPI((byte)Isal.IX.EVectors.HaltApProcessors, true); + } while (GetRunningProcessorCount() != 1) { - /* Thread.Sleep(100); Thread.Sleep needs NoHeapAllocation annotation */ + // Thread.Sleep(100); Thread.Sleep needs NoHeapAllocation annotation Thread.Yield(); } - DebugStub.RevertToUniprocessor(); + // + // We must reset the AP Processors since a debug entry + // will generated a NMI which will wake them up from HALT, + // and they may start executing code again while the kernel + // is still shutting down. + // + Platform.ResetApProcessors(); + DebugStub.RevertToUniprocessor(); // At this point only the BSP is running. } @@ -1227,126 +819,131 @@ namespace Microsoft.Singularity [NoHeapAllocation] public static int GetProcessorCount() { - return HalDevices.GetProcessorCount(); + return Platform.GetProcessorCount(); } - public static int GetDomainCount() + // + //public static int GetDomainCount() + //{ + // HalMemory.ProcessorAffinity[] processors = + // halMemory.GetProcessorAffinity(); + // uint domain = 0; + // for (int i = 0; i < processors.Length; i++) { + // if (processors[i].domain > domain) { + // domain = processors[i].domain; + // } + // } + // domain++; // domain number starts from 0 + // return (int)domain; + //} + // + + public static bool PerProcessorAddressSpaceDisabled() { - IHalMemory.ProcessorAffinity[] processors = - halMemory.GetProcessorAffinity(); - uint domain = 0; - for (int i = 0; i < processors.Length; i++) { - if (processors[i].domain > domain) { - domain = processors[i].domain; - } - } - domain++; // domain number starts from 0 - return (int)domain; + return halMemory.PerProcessorAddressSpaceDisabled(); } public static bool HasAffinityInfo() { - IHalMemory.ProcessorAffinity[] processors = - halMemory.GetProcessorAffinity(); - IHalMemory.MemoryAffinity[] memories = - halMemory.GetMemoryAffinity(); + HalMemory.ProcessorAffinity[] processors = halMemory.GetProcessorAffinity(); + HalMemory.MemoryAffinity[] memories = halMemory.GetMemoryAffinity(); if (processors == null || memories == null) { return false; } return true; } - // haryadi -- return cpuId if context is not null + // return cpuId if context is not null [NoHeapAllocation] public unsafe int ValidProcessorId(int i) { if (context != null) { - return context->cpuId; + return context->cpuRecord.id; } else { return -1; } } - // haryadi -- determine next ping pong receiver - // i.e. find next processor, if this is - // the last processors, wrap to p0 - - [NoHeapAllocation] - private static int GetPingPongReceiver() + /// + /// + /// Is system MP or UP + /// + /// + public static bool IsUP { - int from = GetCurrentProcessorId(); - int to; - - // check if processor table is valid - if (processorTable == null || processorTable.Length == 1) { - DebugStub.WriteLine - ("HSG: Processors.cs, procTable null/uniproc ..."); - return -1; + [NoHeapAllocation] + get + { + return CpuCount == 1; } - - // set up neighbor - to = (from + 1) % processorTable.Length; - - // check if processor is valid - if (processorTable[to].ValidProcessorId(to) >= 0) { - to = processorTable[to].ValidProcessorId(to); - } - // this is the last processors, wrap to p0 - else { - // DebugStub.WriteLine - // ("HSG: Processors.cs, cpu.{0} is the last processor", - // __arglist(from)); - to = 0; - to = processorTable[to].ValidProcessorId(to); - if (to == from) { - DebugStub.WriteLine - ("HSG: Processor.cs, only 1 valid processor (error)"); - return -1; - } - } - return to; } - // haryadi -- start ping pong - [NoHeapAllocation] - public static int RunPingPongInt(int start) - { - // Done ping pong - if (start == 0) { - return -1; - } + /// Count of the number of processors expected to be running + public static int ExpectedProcessors; - int from = GetCurrentProcessorId(); - int to = GetPingPongReceiver(); + /// Global processor table + public static Processor[] processorTable; - // invalid to (or no destination processor) - if (to < 0) { - return -1; - } + /// Processor contexts + internal unsafe ProcessorContext* context; - // from and to are ready now - DebugStub.WriteLine - ("HSG: ***** cpu.{0} sends {1} to p{2}", - __arglist(from, start, to)); + /// Processor dispatcher + [AccessedByRuntime("referenced from c++")] + internal ProcessorDispatcher dispatcher; - // disable interrupt - bool iflag = Processor.DisableInterrupts(); + // Hardware pic, or its emulation + private HalPic pic; - // put integer "to" to neighbor interrupt task queue - MpExecution.PutIntrPingPong(to, start); + /// Per processor HalTimer + internal HalTimer timer = null; - // send ping pong - MpExecution.StartPingPongInt(from, to, (byte)EVectors.PingPongInt); + /// Per processor HalClock + internal HalClock clock = null; - // enable interrupt - Processor.RestoreInterrupts(iflag); + /// Hal memory interface + private static HalMemory halMemory; - // DebugStub.WriteLine - // ("HSG: Processors.cs, cpu.{0} sent to p{1} (pc:{2}) ", - // __arglist(from, to, start)); + /// Id of a timer interrupt + internal byte timerInterrupt; - return 0; - } + /// Id of a clock interrupt + internal byte clockInterrupt; + + /// Shows if a processor currently in a context of interrupt + private bool inInterruptContext = false; + + /// Shows if a processor currently in halt state + private bool halted = false; + + /// Processor Index + private int processorIndex; + + /// A number of exception occured on this processor + public uint NumExceptions = 0; + + /// A number of interrupts occrued on this processor + public uint NumInterrupts = 0; + + /// A number of context switches + public uint NumContextSwitches = 0; + + /// A interrupt statistics per processor + internal int[] interruptCounts; + + /// A number of cycles per second on a given processor + private ulong cyclesPerSecond; + + /// A number of active Processors + private static int runningCpus = 0; + + /// An initial per processor kernel thread + private Thread kernelThread; + + /// Beginning of kernel thread stack + private UIntPtr kernelStackBegin = 0; + + /// Limit of kernel thread stack + private UIntPtr kernelStackLimit = 0; } } diff --git a/base/Kernel/Singularity/ProcessorContext.cs b/base/Kernel/Singularity/ProcessorContext.cs new file mode 100644 index 0000000..df27245 --- /dev/null +++ b/base/Kernel/Singularity/ProcessorContext.cs @@ -0,0 +1,70 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ProcessorContext.cs +// +// Note: +// + +namespace Microsoft.Singularity +{ + using System; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using System.Threading; + using Microsoft.Singularity.Isal; +#if SINGULARITY_KERNEL + using Microsoft.Singularity.Hal; +#endif + + [NoCCtor] + [CLSCompliant(false)] + [StructLayout(LayoutKind.Sequential)] + [AccessedByRuntime("referenced from c++")] + internal struct ProcessorContext + { + // All fields are private to the kernel. + // + // It's important that the embedded ThreadContexts in this struct are paragraph-aligned. + // That's because those ThreadContexts contain 'mmx' fields, which are the target of + // FXSAVE and FXRSTOR instructions. Intel specifies that those fields must be paragraph- + // aligned, or the instructions will fault. + + [AccessedByRuntime("referenced from c++")] + internal CpuRecord cpuRecord; + + [AccessedByRuntime("referenced from c++")] private unsafe void *halCpu; + [AccessedByRuntime("referenced from c++")] private unsafe void *_processor; // Only changed by garbage collector. + + [AccessedByRuntime("referenced from c++")] internal UIntPtr exception; + + [AccessedByRuntime("referenced from c++")] internal volatile int ipiFreeze; + [AccessedByRuntime("referenced from c++")] internal unsafe ProcessorContext* nextProcessorContext; // singly-linked circular list node for MpExecution use + + [AccessedByRuntime("referenced from c++")] internal volatile int gcIpiGate; // + + //////////////////////////////////////////////////// External Methods. + // + internal Processor processor { + [NoHeapAllocation] + get { return GetProcessor(); } + } + + [AccessedByRuntime("output to header: defined in c++")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(32)] + [NoHeapAllocation] + internal extern void UpdateAfterGC(Processor processor); + + [AccessedByRuntime("output to header: defined in c++")] + [MethodImpl(MethodImplOptions.InternalCall)] + [StackBound(32)] + [NoHeapAllocation] + private extern Processor GetProcessor(); + } +} + + diff --git a/base/Kernel/Singularity/ProtectionDomain.cs b/base/Kernel/Singularity/ProtectionDomain.cs index c29c19f..0babd9f 100644 --- a/base/Kernel/Singularity/ProtectionDomain.cs +++ b/base/Kernel/Singularity/ProtectionDomain.cs @@ -9,7 +9,7 @@ // Note: // -#if PAGING +//#define VERBOSE using System; using System.Runtime.CompilerServices; @@ -57,11 +57,13 @@ namespace Microsoft.Singularity // -------------- Valid after InitHook() private VirtualMemoryRange userRange; +#if PAGING private SharedHeap userSharedHeap; // These are always null for a kernel domain private PEImage ring3AbiImage; private ExportTable ring3AbiExports; +#endif // Used for initialization private bool initialized; @@ -73,7 +75,7 @@ namespace Microsoft.Singularity internal static void Initialize() { - tableLock = new SmartSpinlock(); + tableLock = new SmartSpinlock(SpinLock.Types.ProtectionDomainTable); PdTable = new ProtectionDomain[maxPDs]; PdIndexGenerator = 1; @@ -82,13 +84,22 @@ namespace Microsoft.Singularity PdTable[0] = new ProtectionDomain(defaultSpace, "Default", true); } + internal static void InitializeSharedHeapWalker() + { + // TODO: The walker needs to become per-protection-domain + // on paging systems + SharedHeapWalker.Initialize( + SharedHeap.KernelSharedHeap.DataOwnerId, + SharedHeap.KernelSharedHeap.EndpointOwnerId); + } + internal static ProtectionDomain FindOrCreateByName(string name, bool kernelMode) { bool iflag = tableLock.Lock(); try { - for(uint i = 0; i < PdTable.Length; i++) { + for (uint i = 0; i < PdTable.Length; i++) { if ((PdTable[i] != null) && (PdTable[i].name == name)) { // found it @@ -114,7 +125,11 @@ namespace Microsoft.Singularity internal static ProtectionDomain CurrentDomain { get { +#if PAGING return Thread.CurrentThread.CurrentDomain; +#else + return Thread.CurrentThread.Process.Domain; +#endif } } @@ -152,11 +167,13 @@ namespace Microsoft.Singularity } } +#if PAGING internal ExportTable ABIStubs { get { return ring3AbiExports; } } +#endif // // Someone must arrange to call this from *within* the @@ -165,6 +182,11 @@ namespace Microsoft.Singularity // internal unsafe void InitHook() { + // If paging is disabled then just return immediately + if (!MemoryManager.UseAddressTranslation) { + return; + } + DebugStub.Assert(AddressSpace.CurrentAddressSpace == this.AddressSpace); if (this.initialized) { @@ -183,27 +205,32 @@ namespace Microsoft.Singularity // // We're first into this space, so set it up. // +#if VERBOSE DebugStub.WriteLine("Setting up protection domain \"{0}\"", __arglist(this.name)); +#endif userRange = new VirtualMemoryRange(VMManager.UserHeapBase, VMManager.UserHeapLimit, this); - +#if PAGING if (kernelMode) { // This will be a ring-0, trusted domain, so just // point the userSharedHeap at the kernel's comm heap. userSharedHeap = SharedHeap.KernelSharedHeap; this.initialized = true; - } else { + } + else { // Create a new shared heap that lives in // user-land. userSharedHeap = new SharedHeap(this, userRange); +#if VERBOSE DebugStub.WriteLine(" ...Created a shared heap"); +#endif // - // N.B. this is kind of tricky. Loading an + // N.B.: this is kind of tricky. Loading an // executable image involves allocating memory, // which goes through this object. So, before // attempting the load, mark ourselves as initialized. @@ -222,7 +249,7 @@ namespace Microsoft.Singularity // the kernel process the logical owner. This seems like // the only sensible approach since the stubs do not // belong to any particular process but must be in the - // user range of memory. -- maiken + // user range of memory. // N.B.: RE-ENTERS this object! ring3AbiImage = PEImage.Load(Process.kernelProcess, syscallsMemory, @@ -232,10 +259,16 @@ namespace Microsoft.Singularity ); ring3AbiExports = ring3AbiImage.GetExportTable(loadedMemory); +#if VERBOSE DebugStub.WriteLine(" ...Loaded ring-3 ABI stubs"); +#endif } +#else // PAGING + this.initialized = true; +#endif // PAGING - } finally { + } + finally { DebugStub.Assert(this.initialized); initSpin.Unlock(iflag); } @@ -257,7 +290,7 @@ namespace Microsoft.Singularity } } - +#if PAGING internal SharedHeap UserSharedHeap { [Inline] get { @@ -289,6 +322,7 @@ namespace Microsoft.Singularity return UserSharedHeap.EndpointPeerOwnerId; } } +#endif ///////////////////////////////////// // PRIVATE METHODS @@ -312,35 +346,30 @@ namespace Microsoft.Singularity this.space = space; this.name = name; this.kernelMode = isKernelDomain; - this.initSpin = new SmartSpinlock(); - this.userMappingLock = new SmartSpinlock(); + this.initSpin = new SmartSpinlock(SpinLock.Types.ProtectionDomainInit); + this.userMappingLock = new SmartSpinlock(SpinLock.Types.ProtectionDomainMapping); this.refCount = 1; // represents the table entry +#if VERBOSE DebugStub.WriteLine("Created protection domain \"{0}\"", __arglist(name)); +#endif } private static uint AssignNewPDSlot(ProtectionDomain pd) { - bool iflag = tableLock.Lock(); + // Consider; Assert tableLock is aquired? - try { - for(uint i = 0; i < PdTable.Length; i++) { - uint index = (uint)((PdIndexGenerator + i) % PdTable.Length); + for (uint i = 0; i < PdTable.Length; i++) { + uint index = (uint)((PdIndexGenerator + i) % PdTable.Length); - if (PdTable[index] == null) { - PdTable[index] = pd; - PdIndexGenerator = (uint)((index + 1) % PdTable.Length); - return index; - } + if (PdTable[index] == null) { + PdTable[index] = pd; + PdIndexGenerator = (uint)((index + 1) % PdTable.Length); + return index; } } - finally { - tableLock.Unlock(iflag); - } DebugStub.Assert(false, "Out of process domain slots!"); return uint.MaxValue; } } } - -#endif diff --git a/base/Kernel/Singularity/Scheduling/AffinityScheduler.cs b/base/Kernel/Singularity/Scheduling/AffinityScheduler.cs new file mode 100644 index 0000000..3869f72 --- /dev/null +++ b/base/Kernel/Singularity/Scheduling/AffinityScheduler.cs @@ -0,0 +1,336 @@ +//------------------------------------------------------------------------------ +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Description: $filename$ +// +// Minimal round-robin style without priorities scheduler. +// +// AffinityScheduler favors thread that have recently become unblocked +// and tries to avoid reading the clock or reseting the timer as +// much as possible. +// +// The minimal scheduler maintains two queues of threads that can +// be scheduled. The unblockedThreads queue contains threads which +// have become unblocked during this scheduling quantum; mostly, +// these are threads that were unblocked by the running thread. +// The runnableThreads queue contains all other threads that are +// currently runnable. If the current thread blocks, AffinityScheduler +// will schedule threads from the unblockedThread queue, without +// reseting the timer. When the timer finaly fires, AffinityScheduler +// moves all unblockedThreads to the end of the runnableThreads +// queue and schedules the next runnableThread. +//------------------------------------------------------------------------------ + +using System; +using System.Collections; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; + +using Microsoft.Bartok.Options; + +using Microsoft.Singularity; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Scheduling; + +namespace Microsoft.Singularity.Scheduling +{ + /// + /// + /// Summary description for AffinityScheduler. + /// + /// + [NoCCtor] + [CLSCompliant(false)] + public class AffinityScheduler : Scheduler + { + public AffinityScheduler() + { + } + + /// + /// + /// Initialize min scheduler + /// + /// + public override void Initialize() { + + // HACK use 30ms for now to give time for debug output. + // If busy, don't run for more than 10ms on same task. + TimeSpan minSlice = TimeSpan.FromMilliseconds(10); + + schedulers = new BaseScheduler [Processor.processorTable.Length]; + + // Create the idle threads and put them on the idleThreads loop. + for (int i = 0; i < Processor.processorTable.Length; i++) { + + // Create scheduler + schedulers[i] = BaseSchedulerManager.CreateScheduler(minSlice, i); + } + + // Wait until first dispatcher will call into the scheduler + numberOfActiveDispatchers = 0; + } + + /// + /// + /// Finalize scheduler object + /// + /// + public override void Finalize() + { + } + + /// + /// + /// Notify scheduler about new dispatcher + /// + /// + public override void OnDispatcherInitialize(int dispatcherId) + { + // Increment number of active dispatchers + int dispatchers = Interlocked.Increment (ref numberOfActiveDispatchers); + + // Check if a now we really MP enabled + if (dispatchers == Processor.ExpectedProcessors) { + isMPEnabled = true; + } + } + + /// + /// + /// Attach thread to scheduler: thread specific initializtion + /// + /// + /// Thread to attach + /// Have we called thread constructor + /// + public override void OnThreadStateInitialize(Thread thread, bool constructorCalled) + { + // Only initialize thread-local state! No locks held and interrupts on. + } + + /// + /// + /// Start thread - put a thread on unblocked queue so it can be run + /// + /// + /// Thread to start + /// + public override void OnThreadStart(Thread thread) + { + // TODO: this needs to check whether the processor is running yet. + int threadID = thread.GetThreadId(); + // NOTE Affinity may be legally set to any int. NoAffinity(-1) is special. + // All other ints must be converted to a legal index into the processor table + int initial = thread.Affinity == Thread.NoAffinity + ? threadID + : Math.Abs(thread.Affinity); + int cpu = isMPEnabled ? initial % numberOfActiveDispatchers : 0; + thread.Affinity = cpu; + schedulers[cpu].OnThreadStart(thread); + } + + /// + /// + /// Block thread - put a thread on block queue and retrieve next thread to run + /// + /// + /// Thread that blocks + /// Amount of time for the thread to be blocked + /// + public override void OnThreadBlocked(Thread thread, SchedulerTime stop) + { + schedulers[thread.Affinity].OnThreadBlocked(thread, stop); + } + + /// + /// + /// Unblock thread - resume thread by putting it on unblock queue. This method + /// can be invoked by threads running on other processors + /// + /// + /// Thread to resume + /// + [NoHeapAllocation] + public override void OnThreadUnblocked(Thread thread) + { + schedulers[thread.Affinity].OnThreadUnblocked(thread); + } + + /// + /// + /// Yield thread - suspend thread based on time slice + /// + /// + /// Thread that is yielding + /// + [NoHeapAllocation] + public override void OnThreadYield(Thread thread) + { + schedulers[thread.Affinity].OnThreadYield(thread); + } + + /// + /// + /// Stop thread execution + /// + /// + /// Thread that is stopping + /// + public override void OnThreadStop(Thread thread) + { + schedulers[thread.Affinity].OnThreadStop(thread); + } + + /// + /// + /// Increment frozen counter + /// + /// + /// Thread for which to increment freeze counter + /// + public override void OnThreadFreezeIncrement(Thread thread) + { + schedulers[thread.Affinity].OnThreadFreezeIncrement(thread); + } + + /// + /// + /// Decrement frozen counter + /// + /// + /// Thread for which to update freeze counter + /// + public override void OnThreadFreezeDecrement(Thread thread) + { + schedulers[thread.Affinity].OnThreadFreezeDecrement(thread); + } + + /// + /// + /// Dispatch timer interrupt + /// + /// + /// Processor affinity + /// Current time + /// + /// Queue used to put threads to be waken up. Latter on dispatcher will add threads + /// to scheduler's runnable queue + /// + /// + [CLSCompliant(false)] + [NoHeapAllocation] + public override TimeSpan OnTimerInterrupt(int affinity, SchedulerTime now) + { + return schedulers[affinity].OnTimerInterrupt(affinity, now); + } + + /// + /// + /// Add thread to runnable queue. This methods is called by dispatcher at the + /// point when both dispatcher lock and interrupts are disabled + /// + /// + /// Thread to add to runnable queue + /// Place to where enqueue thread in a runnable queue + /// + [Inline] + [NoHeapAllocation] + public override void AddRunnableThread(Thread thread) + { + schedulers[thread.Affinity].AddRunnableThread(thread); + } + + /// + /// + /// Run scheduling policy to decide the next thread to run + /// + /// + /// The affinity of the scheduler + /// The running thread returned should be + /// set to this affinity. It's same as schedulerAffinity except during + /// thread migration + /// + /// the thread currently running + /// thread state to change to for the current thread + /// Current system time + /// + [NoHeapAllocation] + public override Thread RunPolicy( + int schedulerAffinity, + int runningThreadAffinity, + Thread currentThread, + ThreadState schedulerAction, + SchedulerTime currentTime) + { + // Assert preconditions: current thread can be either NULL or its base scheduler + // should be the same as specified by affinity + VTable.Assert(currentThread == null || + currentThread.Affinity == schedulerAffinity); + + // Use affinity to derive actual scheduler + return schedulers[schedulerAffinity].RunPolicy( + schedulerAffinity, + runningThreadAffinity, + currentThread, + schedulerAction, + currentTime); + } + + /// + /// + /// Suspend thread and wait until it is suspended. + /// + /// + /// Thread to suspend + /// + [NoHeapAllocation] + public override void Suspend(Thread thread) + { + schedulers[thread.Affinity].Suspend(thread); + } + + /// + /// + /// Resume thread from being suspended + /// + /// + /// Thread to resume + /// + [NoHeapAllocation] + public override void Resume(Thread thread) + { + schedulers[thread.Affinity].Resume(thread); + } + + /// + /// + /// Retrieve scheduler lock - used by dispatcher to protect scheduler state + /// + /// + /// Affinity of dispatcher making actual call + /// + [CLSCompliant(false)] + [NoHeapAllocation] + internal override SchedulerLock RetrieveSchedulerLock(int affinity) + { + // Use the our defualt scheder's to return + return schedulers[affinity].MyLock; + } + + /// An array of schedulers + private static BaseScheduler [] schedulers; + + /// A number of active dispatchers + private static int numberOfActiveDispatchers; + + /// Flag showing whether multi processing is enabled + private static bool isMPEnabled; + } + +} + diff --git a/base/Kernel/Singularity/Scheduling/Full/Activity.cs b/base/Kernel/Singularity/Scheduling/Full/Activity.cs deleted file mode 100644 index 4f2f334..0000000 --- a/base/Kernel/Singularity/Scheduling/Full/Activity.cs +++ /dev/null @@ -1,80 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Activity.cs -// -// Note: -// - -using System; -using System.Collections; - -namespace Microsoft.Singularity.Scheduling -{ - /// - /// This class is the base of the resource class. To reduce code management cost - /// between the simulator and kernel, both kernel and simulator resource container - /// classes derive from this base class, which contains most of the functionality. - /// - public class Activity - { - Task defaultTask; - //IResourceReservation[] reservations; - Hashtable reservations; - internal ISchedulerActivity schedulerActivity; - - public Activity() - { - defaultTask = new Task(null, null, DateTime.MaxValue, true, null, null, this); //The default task - reservations = new Hashtable(); - schedulerActivity = CpuResource.CreateSchedulerActivity(); - schedulerActivity.EnclosingActivity = this; - } - - /// - /// Return the default task object for this Activity. - /// Any code not inside an explicit task is working on behalf of the default "Task". - /// Would you not prefer this be a property? - /// - public Task DefaultTask() - { - return defaultTask; - } - - /// - /// Returns the ongoing resource reservations for this resource container. - /// - public ICollection Reservations() - { - return reservations.Values; - } - - internal IResourceReservation GetResourceReservation(string resourceString) - { - return (IResourceReservation)reservations[resourceString]; - } - - internal void SetResourceReservation(string resourceString, IResourceReservation reservation) - { - if (reservation == null) { - reservations.Remove(resourceString); - } - else { - reservations[resourceString] = reservation; - } - } - - /// - /// This only need be called by the thread which creates the resource container, - /// and should do so once the reference won't be used outside the resource - /// container (i.e. to add threads to it). - /// - public void ReleaseReference() - { - schedulerActivity.ReleaseReference(); - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Full/CpuResource.cs b/base/Kernel/Singularity/Scheduling/Full/CpuResource.cs deleted file mode 100644 index 51c9b04..0000000 --- a/base/Kernel/Singularity/Scheduling/Full/CpuResource.cs +++ /dev/null @@ -1,215 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Kernel\Singularity\Scheduling\CpuResource.cs -// -// Note: -// - -using System; -using System.Collections; -using System.Diagnostics; -using System.Threading; -using Microsoft.Singularity; -#if SIMULATOR -using Thread = Microsoft.Singularity.Scheduling.Thread; -using Processor = Microsoft.Singularity.Scheduling.Processor; -#endif - -namespace Microsoft.Singularity.Scheduling -{ - /// - /// The CPU resource provider object. - /// - [CLSCompliant(false)] - public class CpuResource : IResource - { - /// - /// Return a reference to the CPU resource provider object. - /// - static public CpuResource Provider() - { - return cpuResourceProvider; - } - - public override string ResourceString - { - get { return "Microsoft.Singularity.Scheduling.CpuResource"; } - } - - /// - /// Constructor for the static CPU Resource Provider object - /// - private CpuResource() - { - } - - /// - /// The static CPU Resource Provider object - /// - private static CpuResource cpuResourceProvider = new CpuResource(); - - //Establishes the system scheduler -- should be called by system initialization, and should - //throw an error when called a 2nd time. - public static void RegisterSystemScheduler(ICpuScheduler scheduler) - { - if(cpuResourceScheduler == null) { - cpuResourceScheduler = scheduler; - } - else { - //TODO: Throw Exception Perhaps! - } - } - - /// - /// This is the actual system CPU scheduler. It is used for CPU reservations, - /// constraints, to interact with the timer, and to allow the CPU scheduler - /// to be swapped out while maintaining a consistent interface. - /// - private static ICpuScheduler cpuResourceScheduler; - - public static ISchedulerActivity CreateSchedulerActivity() - { - return cpuResourceScheduler.CreateSchedulerActivity(); - } - - public static ISchedulerThread CreateSchedulerThread(Thread thread) - { - return cpuResourceScheduler.CreateSchedulerThread(thread); - } - - public static ISchedulerProcessor CreateSchedulerProcessor(Processor processor) - { - return cpuResourceScheduler.CreateSchedulerProcessor(processor); - } - - public static readonly TimeSpan MaxPeriod = new TimeSpan(1 * TimeSpan.TicksPerSecond); - - /// - /// Return amount of CPU available for reservation for the specified Activity and period - /// - public CpuResourceAmount AvailableCpu(Activity activity, TimeSpan period) - { - // XXX TBD - return new CpuResourceAmount((cyclesPerSecond * period.Ticks) - / TimeSpan.TicksPerSecond); - } - - /// - /// Reserve CPU on an ongoing basis for a resource container. - /// Replaces any existing reservation for that resource container. - /// Specify 0 cycles to end the CPU reservation for the resource container. - /// Returns null if reservation is denied. XXX Should this throw an exception instead? - /// - public CpuResourceReservation ReserveCpu(Activity activity, - CpuResourceAmount amount, TimeSpan period) - { - object tempOldReservation = activity.GetResourceReservation(CpuResource.Provider().ResourceString); - Debug.Assert(tempOldReservation == null || - tempOldReservation is CpuResourceReservation); - CpuResourceReservation oldReservation = (CpuResourceReservation)tempOldReservation; - - ISchedulerCpuReservation schedulerReservation = - cpuResourceScheduler.ReserveCpu(activity.schedulerActivity, amount, period); - - CpuResourceReservation reservation = null; - if(schedulerReservation != null) { - reservation = new CpuResourceReservation(amount, period, schedulerReservation); - schedulerReservation.EnclosingCpuReservation = reservation; - } - - activity.SetResourceReservation(CpuResource.Provider().ResourceString, reservation); - return reservation; - } - - /// - /// Convert a CPU amount to the minimum amount of time required for that amount. - /// This conversion assumes that the CPU is running at its maximum rate. - /// - public CpuResourceAmount TimeToCpu(TimeSpan time) - { - return new CpuResourceAmount((cyclesPerSecond * time.Ticks) - / TimeSpan.TicksPerSecond); - } - - /// - /// Convert a timespan to the maximum amount of CPU that can be used in that timespan. - /// This conversion assumes that the CPU is running at its maximum rate. - /// - public TimeSpan CpuToTime(CpuResourceAmount amount) - { - if(amount == null) { - return new TimeSpan(0); - } - return new TimeSpan((TimeSpan.TicksPerSecond * amount.Cycles) - / cyclesPerSecond); - } - - private long cyclesPerSecond; - - volatile static int do_something = 0; - static void Spinner() - { - do_something++; - } - - public bool NextThread(out Thread nextThread) - { - return cpuResourceScheduler.NextThread(out nextThread); - } - - public bool ShouldReschedule() - { - return cpuResourceScheduler.ShouldReschedule(); - } - - //XXX: One resource world only. - /// - /// Note that as tasks now contain resource accounting, there is - /// no need to pass out the resource usage from the scheduler. - /// For consistency we'll still pass it out of the Task API at least - /// until instantaneous usage is fixed. - /// - /// - /// - /// - /// - /// Returns true if admitted. - internal bool BeginConstraint(Hashtable resourceEstimates, - DateTime deadline, - Task taskToEnd, - out ISchedulerTask schedulerTask) - { - return cpuResourceScheduler.BeginConstraint(resourceEstimates, - deadline, ((taskToEnd==null)?null:taskToEnd.schedulerTask), out schedulerTask); - } - - internal void BeginDelayedConstraint(Hashtable resourceEstimates, TimeSpan relativeDeadline, Task taskToEnd, out ISchedulerTask schedulerTask) - { - cpuResourceScheduler.BeginDelayedConstraint(resourceEstimates, relativeDeadline, ((taskToEnd==null)?null:taskToEnd.schedulerTask), out schedulerTask); - } - - internal Hashtable EndConstraint(Task taskToEnd) - { - cpuResourceScheduler.EndConstraint(((taskToEnd==null)?null:taskToEnd.schedulerTask)); - return taskToEnd.resourcesUsed; - } - - public void Initialize() - { - Debug.Assert(cpuResourceScheduler != null, "No registered scheduler!"); - cyclesPerSecond = (long)Processor.CyclesPerSecond; - - Debug.Assert(Processor.processorTable != null, "Processor table is null!"); - for (int i = 0; i < Processor.processorTable.Length; i++) { - Debug.Assert(Processor.processorTable[i] != null, "Processor is null!"); - Processor.processorTable[i].InitializeSchedulerProcessor(); - } - - cpuResourceScheduler.Initialize(); - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Full/CpuResourceAmount.cs b/base/Kernel/Singularity/Scheduling/Full/CpuResourceAmount.cs deleted file mode 100644 index 2e303d6..0000000 --- a/base/Kernel/Singularity/Scheduling/Full/CpuResourceAmount.cs +++ /dev/null @@ -1,58 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Kernel\Singularity\Scheduling\CpuResourceAmount.cs -// -// Note: -// - -using System; -using System.Diagnostics; - -namespace Microsoft.Singularity.Scheduling -{ - /// - /// The quantity by which amounts of CPU time are measured: cycles. - /// - public class CpuResourceAmount : IResourceAmount - { - /// - /// Return a reference to the CpuResource provider - /// - public override IResource Resource - { - get { return CpuResource.Provider(); } - } - - /// - /// Create a CpuResourceAmount with the specified number of cycles - /// - /// - public CpuResourceAmount(long cycles) - { - this.cycles = cycles; - } - - /// - /// Return the number of cycles in the CpuResourceAmount. - /// - public long Cycles - { - get { return cycles; } - set { cycles = value; } - } - - // The actual number of cycles represented in the amount are stored in this private variable. - private long cycles; - - public override IResourceAmount AddTo(IResourceAmount amount) - { - Debug.Assert(amount is CpuResourceAmount); - cycles += ((CpuResourceAmount)amount).Cycles; - return this; - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Full/CpuResourceReservation.cs b/base/Kernel/Singularity/Scheduling/Full/CpuResourceReservation.cs deleted file mode 100644 index 801329b..0000000 --- a/base/Kernel/Singularity/Scheduling/Full/CpuResourceReservation.cs +++ /dev/null @@ -1,83 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Kernel\Singularity\Scheduling\CpuResourceReservation.cs -// -// Note: -// - -using System; - -namespace Microsoft.Singularity.Scheduling -{ - /// - /// An ongoing CPU resource reservation object. - /// - public class CpuResourceReservation : IResourceReservation - { - public ISchedulerCpuReservation schedulerReservation; - - /// - /// Return a reference to the CpuResource provider - /// - public override IResource Resource - { - get { return CpuResource.Provider(); } - } - - private CpuResourceAmount requestedAmount; - private TimeSpan requestedPeriod; - - public CpuResourceReservation(CpuResourceAmount requestedAmount, - TimeSpan requestedPeriod, - ISchedulerCpuReservation schedulerReservation) - { - this.requestedAmount = requestedAmount; - this.requestedPeriod = requestedPeriod; - this.schedulerReservation = schedulerReservation; - } - - /// - /// The amount requested for an ongoing CPU reservation - /// - public CpuResourceAmount RequestedAmount - { - get { return requestedAmount; } - } - - /// - /// The period requested for an ongoing CPU reservation - /// - public TimeSpan RequestedPeriod - { - get { return requestedPeriod; } - } - - /// - /// The amount actually granted for an ongoing CPU reservation - /// - public CpuResourceAmount ActualAmount - { - get { - return (schedulerReservation == null) - ? null : schedulerReservation.ReservedAmount; - // XXX TBD May change as global schedule changes - } - } - - /// - /// The period actually granted for an ongoing CPU reservation - /// - public TimeSpan ActualPeriod - { - get { - return (schedulerReservation == null) - ? new TimeSpan(0) : schedulerReservation.ReservedPeriod; - // XXX TBD May change as global schedule changes - } - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Full/ICpuScheduler.cs b/base/Kernel/Singularity/Scheduling/Full/ICpuScheduler.cs deleted file mode 100644 index 8f50b5b..0000000 --- a/base/Kernel/Singularity/Scheduling/Full/ICpuScheduler.cs +++ /dev/null @@ -1,52 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Kernel\Singularity\Scheduling\ICpuScheduler.cs -// -// Note: -// - -using System; -using System.Collections; -using System.Threading; -#if SIMULATOR -using Thread = Microsoft.Singularity.Scheduling.Thread; -#endif - -namespace Microsoft.Singularity.Scheduling -{ - [CLSCompliant(false)] - public abstract class ICpuScheduler - { - public abstract ISchedulerThread CreateSchedulerThread(Thread thread); - public abstract ISchedulerProcessor CreateSchedulerProcessor(Processor processor); - public abstract ISchedulerActivity CreateSchedulerActivity(); - public abstract ISchedulerCpuReservation ReserveCpu(ISchedulerActivity activity, - CpuResourceAmount amount, - TimeSpan period); - public abstract bool NextThread(out Thread nextThread); - public abstract bool ShouldReschedule(); - - /// - /// Note that as tasks now contain resource accounting, there is - /// no need to pass out the resource usage from the scheduler. - /// For consistency we'll still pass it out of the Task API at least - /// until instantaneous usage is fixed. - /// - /// Returns true if admitted. - public abstract bool BeginConstraint(Hashtable resourceEstimates, - DateTime deadline, - ISchedulerTask taskToEnd, - out ISchedulerTask schedulerTask); - public abstract void BeginDelayedConstraint(Hashtable resourceEstimates, - TimeSpan relativeDeadline, - ISchedulerTask taskToEnd, - out ISchedulerTask schedulerTask); - public abstract bool EndConstraint(ISchedulerTask taskToEnd); - public abstract void Initialize(); - } - -} diff --git a/base/Kernel/Singularity/Scheduling/Full/IGetBeneficiary.cs b/base/Kernel/Singularity/Scheduling/Full/IGetBeneficiary.cs deleted file mode 100644 index 63a0654..0000000 --- a/base/Kernel/Singularity/Scheduling/Full/IGetBeneficiary.cs +++ /dev/null @@ -1,23 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Kernel\Singularity\IGetBeneficiary.cs -// -// Note: -// - -using System; -using System.Threading; - -namespace Microsoft.Singularity.Scheduling -{ - [CLSCompliant(false)] - public abstract class IGetBeneficiary - { - ///Returns the beneficiary of scheduling inheritance on this object. - public abstract Thread GetBeneficiary(); - } -} diff --git a/base/Kernel/Singularity/Scheduling/Full/IResource.cs b/base/Kernel/Singularity/Scheduling/Full/IResource.cs deleted file mode 100644 index cd08f1b..0000000 --- a/base/Kernel/Singularity/Scheduling/Full/IResource.cs +++ /dev/null @@ -1,32 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Kernel\Singularity\IResource.cs -// -// Note: -// - -using System; - -namespace Microsoft.Singularity.Scheduling -{ - /// - /// A resource-independent Resource object. Actual resources are derived from this base class. - /// - public abstract class IResource - { - /// - /// Activity contains a hashtable of resource reservations. A - /// reservation for a given resource object will be stored and retrieved - /// based on the ResourceString returned via that resource. This - /// flexibility will allow more than just on type, since some resource - /// types may have distinct providers for different resources (e.g. - /// multiple hard disks, network cards, etc), whereas multiple CPU's will - /// likely only have one provider. - /// - public abstract string ResourceString { get; } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Full/IResourceAmount.cs b/base/Kernel/Singularity/Scheduling/Full/IResourceAmount.cs deleted file mode 100644 index 0eecd89..0000000 --- a/base/Kernel/Singularity/Scheduling/Full/IResourceAmount.cs +++ /dev/null @@ -1,31 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Kernel\Singularity\IResourceAmount.cs -// -// Note: -// - -using System; - -namespace Microsoft.Singularity.Scheduling -{ - /// - /// The base class from which resource-specific resource amount classes are derived. - /// - public abstract class IResourceAmount - { - /// - /// Get the resource object that this IResourceAmount object quantifies - /// - public abstract IResource Resource - { - get; - } - - public abstract IResourceAmount AddTo(IResourceAmount amount); - } -} diff --git a/base/Kernel/Singularity/Scheduling/Full/IResourceReservation.cs b/base/Kernel/Singularity/Scheduling/Full/IResourceReservation.cs deleted file mode 100644 index 1b22c5e..0000000 --- a/base/Kernel/Singularity/Scheduling/Full/IResourceReservation.cs +++ /dev/null @@ -1,30 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Kernel\Singularity\IResourceReservation.cs -// -// Note: -// - -using System; - -namespace Microsoft.Singularity.Scheduling -{ - /// - /// A resource-independent ongoing resource reservation object. - /// Resource reservation objects for actual resources are derived from this base class. - /// - public abstract class IResourceReservation - { - /// - /// Get the resource object that this IResourceReservation object quantifies - /// - public abstract IResource Resource - { - get; - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Full/ISchedulerActivity.cs b/base/Kernel/Singularity/Scheduling/Full/ISchedulerActivity.cs deleted file mode 100644 index d459ef4..0000000 --- a/base/Kernel/Singularity/Scheduling/Full/ISchedulerActivity.cs +++ /dev/null @@ -1,30 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Kernel\Singularity\ISchedulerActivity.cs -// -// Note: -// - -namespace Microsoft.Singularity.Scheduling -{ - /// - /// Summary description for ISchedulerActivity. - /// - public abstract class ISchedulerActivity - { - public abstract Activity EnclosingActivity { set; get; } - //CpuResourceReservation CpuResourceReservation { get; } - /// - /// ReleaseReference is used by the thread creating the - /// resource container to release its reference to it - /// voluntarily so that the resource container knows the - /// only things added to it can occur as a result of - /// threads currently assigned to the resource container. - /// - public abstract void ReleaseReference(); - } -} diff --git a/base/Kernel/Singularity/Scheduling/Full/ISchedulerCpuReservation.cs b/base/Kernel/Singularity/Scheduling/Full/ISchedulerCpuReservation.cs deleted file mode 100644 index 4d69dea..0000000 --- a/base/Kernel/Singularity/Scheduling/Full/ISchedulerCpuReservation.cs +++ /dev/null @@ -1,37 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Kernel\Singularity\ISchedulerCpuReservation.cs -// -// Note: -// - -using System; - -namespace Microsoft.Singularity.Scheduling -{ - /// - /// Summary description for ISchedulerCpuReservation. - /// - public abstract class ISchedulerCpuReservation - { - public abstract CpuResourceReservation EnclosingCpuReservation - { - get; set; - } - - public abstract CpuResourceAmount ReservedAmount - { - get; - } - - public abstract TimeSpan ReservedPeriod - { - get; - } - - } -} diff --git a/base/Kernel/Singularity/Scheduling/Full/ISchedulerProcessor.cs b/base/Kernel/Singularity/Scheduling/Full/ISchedulerProcessor.cs deleted file mode 100644 index a5f7d81..0000000 --- a/base/Kernel/Singularity/Scheduling/Full/ISchedulerProcessor.cs +++ /dev/null @@ -1,24 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Kernel\Singularity\ISchedulerProcessor.cs -// -// Note: -// - -using System; - -namespace Microsoft.Singularity.Scheduling -{ - /// - /// Summary description for ISchedulerProcessor. - /// - [CLSCompliant(false)] - public abstract class ISchedulerProcessor - { - public abstract Processor EnclosingProcessor { get; } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Full/ISchedulerTask.cs b/base/Kernel/Singularity/Scheduling/Full/ISchedulerTask.cs deleted file mode 100644 index 7c27357..0000000 --- a/base/Kernel/Singularity/Scheduling/Full/ISchedulerTask.cs +++ /dev/null @@ -1,28 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Kernel\Singularity\ISchedulerTask.cs -// -// Note: -// - -using System.Collections; - -namespace Microsoft.Singularity.Scheduling -{ - /// - /// Summary description for ISchedulerTask. - /// - public abstract class ISchedulerTask - { - public abstract Task EnclosingTask { set; get; } - - //This is temporary in the one resource world. It needs - // to change for the multi-resource world. - //XXX - public abstract Hashtable ResourcesGranted { get; } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Full/ISchedulerThread.cs b/base/Kernel/Singularity/Scheduling/Full/ISchedulerThread.cs deleted file mode 100644 index facffa5..0000000 --- a/base/Kernel/Singularity/Scheduling/Full/ISchedulerThread.cs +++ /dev/null @@ -1,42 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Kernel\Singularity\ISchedulerThread.cs -// -// Note: -// - -using System; -using System.Threading; -#if SIMULATOR -using Thread = Microsoft.Singularity.Scheduling.Thread; -#endif - -namespace Microsoft.Singularity.Scheduling -{ - /// - /// Summary description for ISchedulerThread. - /// - [CLSCompliant(false)] - public abstract class ISchedulerThread - { - public abstract Thread EnclosingThread { get; } - - public abstract void Start(); - public abstract void SetActivity(ISchedulerActivity activity); - public abstract void Cleanup(); - public abstract TimeSpan ExecutionTime { get; } - /// - /// Tells the scheduler that this thread should wait until awoken, or until the timeout. - /// - /// A timeout of DateTime.MaxValue means no timeout. Otherwise the time to wake up. - public abstract void SetStateWaiting(DateTime timeOut); - public abstract void Wake(); - public abstract ISchedulerTask PrepareDelayedTask(ISchedulerTask taskToEnd, - ref TimeConstraint timeConstraint, - DateTime timeNow); - } -} diff --git a/base/Kernel/Singularity/Scheduling/Full/ListNode.cs b/base/Kernel/Singularity/Scheduling/Full/ListNode.cs deleted file mode 100644 index 22ae3a9..0000000 --- a/base/Kernel/Singularity/Scheduling/Full/ListNode.cs +++ /dev/null @@ -1,72 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: ListNode.cs -// -// Note: -// - -using System; - -namespace Microsoft.Singularity.Scheduling -{ - /// - /// Summary description for ListNode. - /// - public class ListNode - { - - public ListNode Previous; // Before this node - public ListNode Next; // After this node - - [CLSCompliant(false)] - public ISchedulerThread Data; - - public ListNode() - { - Previous = null; - Next = null; - } - - [CLSCompliant(false)] - public ListNode(ISchedulerThread dataref) - { - Previous = null; - Next = null; - Data = dataref; - } - - // DI -- safe - public ListNode ListRemove() - { - ListNode next; - - if ((next = Next) == this) - return null; - - next.Previous = Previous; - Previous.Next = next; - Previous = Next = this; - return next; - } - - // DI -- safe - public ListNode InsertIntoList(ListNode list) - { - if (list == null) { - Next = Previous = this; - } - else { - Next = list.Next; - Next.Previous = this; - list.Next = this; - Previous = list; - } - return this; - } - - } -} diff --git a/base/Kernel/Singularity/Scheduling/Full/QueueType.cs b/base/Kernel/Singularity/Scheduling/Full/QueueType.cs deleted file mode 100644 index 6129ba9..0000000 --- a/base/Kernel/Singularity/Scheduling/Full/QueueType.cs +++ /dev/null @@ -1,30 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: QueueType.cs -// -// Note: -// - -using System; -using System.Collections; -using System.Diagnostics; -using System.Threading; -using Microsoft.Singularity; - -namespace Microsoft.Singularity.Scheduling -{ - public enum QueueType - { - GuaranteedQueue = 1, - NonGuaranteedQueue = 2, - IdleQueue = 3, - UnfinishedQueue = 4, - NoQueue = 5, - }; -} - - diff --git a/base/Kernel/Singularity/Scheduling/Full/Scheduler.cs b/base/Kernel/Singularity/Scheduling/Full/Scheduler.cs deleted file mode 100644 index 0099742..0000000 --- a/base/Kernel/Singularity/Scheduling/Full/Scheduler.cs +++ /dev/null @@ -1,272 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Scheduler.cs -// -// Note: -// - -// #define DEBUG_SCHEDULER - -using System; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Threading; -using Microsoft.Singularity; -using Microsoft.Singularity.Hal; -using Microsoft.Singularity.Io; - -namespace Microsoft.Singularity.Scheduling -{ - public class Scheduler - { - public static readonly TimeSpan MaxPeriod = new TimeSpan(10000000); - - private static bool yieldFlag; - public static bool YieldFlag - { - get { return yieldFlag; } - set { yieldFlag = value; } - } - private static bool timerInterruptedFlag = true; - - public static bool TimerInterruptedFlag - { - get { return timerInterruptedFlag; } - set { timerInterruptedFlag = value; } - } - - ////////////////////////////////////////////////////////////////////// - /// - /// Return the Task object that the calling thread is currently working on behalf of. - /// - public static Task CurrentTask() - { - return Thread.CurrentThread.CurrentTask(); - } - - /// - /// Return the Activity object that the calling thread is currently working on behalf of. - /// - public static Activity CurrentActivity() - { - return Thread.CurrentThread.Activity; // XXX TBD - } - - /// - /// Information logging used for debugging schedulers. - /// - [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] - [CLSCompliant(false)] - public static void LogWakeThread(Thread thread) - { - } - - [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] - public static void LogSchedulerLate() - { - } - - [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] - public static void LogContextSwitch() - { - } - - [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] - public static void LogReservationId(ISchedulerTask task) - { - } - - [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] - [CLSCompliant(false)] - public static void LogBeginConstraint(Thread foo, bool ok, ulong start, ulong stop) - { - } - - [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] - public static void LogEndConstraint(TimeSpan diff) - { - } - - [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] - public static void LogTimeJump() - { - } - - [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] - public static void LogSleepAdd() - { - } - - [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] - public static void LogDeleteActivity() - { - } - - [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] - public static void LogEndConstraint() - { - } - - [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] - public static void LogResolveConstraint() - { - } - - [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] - public static void LogReschedule() - { - } - - [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] - public static void LogReservedActivity(int proxyCount) - { - } - - [System.Diagnostics.Conditional("DEBUG_SCHEDULER")] - public static void LogRecurringCpuReservation() - { - } - - // - ////////////////////////////////////////////////////////////////////////////// - - [CLSCompliant(false)] - public static void OnTimerInterrupt(IHalTimer timer) - { - // For now: We don't invoke the scheduler here, as the base - // interrupt function will switch to the scheduler - // thread context. (Q: Should we do that here instead?) - // Instead, for now just set the interrupted flag. - // Also -- in the future instead we may be calling - // scheduler.NextThread() and setting to that context. - //DebugStub.Print("Timer Interrupt: {0} #\n", __arglist(kCurrentTime)); - Scheduler.TimerInterruptedFlag = true; - } - - [CLSCompliant(false)] - public static Thread GetResumedThread() - { - if (!CpuResource.Provider().ShouldReschedule() && - !IoSystem.DeferredSignalsPending()) { - //Kernel.Waypoint(302); - return Thread.CurrentThread; - } - else { - if (Thread.CurrentThread != Thread.schedulingThread) { - //Kernel.Waypoint(303); - return Thread.schedulingThread; - } - else { - //Kernel.Waypoint(304); - return Thread.CurrentThread; - } - } - } - - [CLSCompliant(false)] - public bool GetNextThread(out Thread next) - { - //Kernel.Waypoint(3); - - // DrainDeferredSignals returns true if any signals were queued. - IoSystem.DrainDeferredSignals(); - //Kernel.Waypoint(4); - - // XXX If there is an interrupt right here that adds - // a deferred signals to the list it won't get - // signalled until after the next thread runs. - // - // A potential fix is to loop here and make sure the flag is - // clear before scheduling the next thread. - - //DebugStub.Print("Scheduler.GetNextThread()\n"); - //return (item != null) ? item.Thread : null; - Debug.Assert(Thread.CurrentThread != null); - Thread foo; - //Kernel.Waypoint(5); - bool halted = CpuResource.Provider().NextThread(out foo); - next = (Thread)foo; - yieldFlag = false; - //DebugStub.Print("Exiting Scheduler.GetNextThread({0:x8},{1}\n", - // __arglist(next.threadIndex, halted)); - //Kernel.Waypoint(6); - return halted; - } - - static bool notExiting = true; - public static void StopSystem() - { - notExiting = false; - } - - //TODO: Replace with a Processor Halt! - void Halt() - { - // DebugStub.Print("Doing Processor hlt\n"); -#if false - //[SOSP-2005] Disable hlt so we can always use the cycle counter! - Tracing.Log(Tracing.Debug, "Halting processor with hlt."); - Processor.HaltUntilInterrupt(); -#endif - - // Check for a debug break. - if (DebugStub.PollForBreak()) { - DebugStub.Print("Debugger breakin.\n"); - DebugStub.Break(); - } - } - - [CLSCompliant(false)] - public void Start(Processor rootProcessor) - { - // WARNING: Because the scheduler thread cannot go to sleep, - // we must avoid any operation (such as memory allocation) - // that might require the current thread to block. -#if DEBUG_SCHEDULER || !DEBUG_SCHEDULER - Tracing.Log(Tracing.Audit, "Scheduler Started."); -#endif - Thread.schedulingThread = Thread.CurrentThread; - - IHalTimer timer = rootProcessor.Timer; - timer.SetNextInterrupt(timer.MaxInterruptInterval); - - Thread next; -#if DEBUG_SCHEDULER || !DEBUG_SCHEDULER - Tracing.Log(Tracing.Audit, "Scheduler Loop Started."); -#endif - - while (notExiting) { - bool halted = GetNextThread(out next); - Debug.Assert(halted || next != null); - if (!halted) { -#if DEBUG_SCHEDULER - Tracing.Log(Tracing.Audit, "Scheduling thread {0:x3}", - (UIntPtr)unchecked((uint)next.threadIndex)); -#endif - next.Schedule(); - if (next != null && next.IsStopping()) { -#if DEBUG_SCHEDULER - Tracing.Log(Tracing.Audit, "Cleaning up after thread {0:x3}", - (UIntPtr)unchecked((uint)next.threadIndex)); -#endif - next.Cleanup(); - } - } - else { - if (notExiting) { - Kernel.Waypoint(99); - Halt(); - } - } - } - -#if DEBUG_SCHEDULER || !DEBUG_SCHEDULER - Tracing.Log(Tracing.Audit, "Scheduler loop terminated."); -#endif - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Full/SchedulerClock.cs b/base/Kernel/Singularity/Scheduling/Full/SchedulerClock.cs deleted file mode 100644 index 4bf6fa1..0000000 --- a/base/Kernel/Singularity/Scheduling/Full/SchedulerClock.cs +++ /dev/null @@ -1,103 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: SchedulerClock.cs -// -// Note: -// - -using System; -using System.Diagnostics; -using Microsoft.Singularity; - -namespace Microsoft.Singularity.Scheduling -{ - /// - /// Interface for the system clock device. - /// - public class SchedulerClock - { - public static readonly DateTime BootTime = new DateTime(0); - public static DateTime LastTime = new DateTime(0); - - public static DateTime GetUpTime() - { - LastTime = SystemClock.GetKernelTime(); - return LastTime; - } - - //Simulator Use - //Increments the time - public static void SimulateTime(TimeSpan diff) - { - return; - } - - //Simulator Use - //Returns the time between now and the nextTimerInterrupt - public static TimeSpan TimeToInterrupt() - { - return Processor.CurrentProcessor.NextTimerInterrupt - GetUpTime(); - } - - //Simulator Use - //Sets the nextTimerInterrupt - public static bool SetNextInterrupt(DateTime time) - { - long span = (time - GetUpTime()).Ticks; - if (span > Processor.CurrentProcessor.Timer.MaxInterruptInterval) { - span = Processor.CurrentProcessor.Timer.MaxInterruptInterval; - } - - if (span < Processor.CurrentProcessor.Timer.MinInterruptInterval) { -#if false - DebugStub.Print("SetNextInterrupt warning: requested span {0} " + - " < MinInterruptInterval {1}" + - " at cycle count {2}\n", - __arglist( - span, - Processor.CurrentProcessor.Timer.MinInterruptInterval, - Kernel.GetCpuCycleCount())); -#endif - span = Processor.CurrentProcessor.Timer.MinInterruptInterval; - } - - bool success = Processor.CurrentProcessor.Timer.SetNextInterrupt(span); - //Debug.Print("SetNextInterrupt -- Span: "); - //Debug.Print(span); - //Debug.Print(" End: "); - //Debug.Print(time.Ticks); - //Debug.Print(" Success: "); - //Debug.Print((success?1:0)); - //Debug.Print("\n"); - if (success) { - Processor.CurrentProcessor.NextTimerInterrupt = time; - Scheduler.TimerInterruptedFlag = false; - } - return success; - } - - //Allow timer periods longer than timer.MaxInterruptInterval - public static void CheckInterrupt() - { - long span = (Processor.CurrentProcessor.NextTimerInterrupt - GetUpTime()).Ticks; - if (span < Processor.CurrentProcessor.Timer.MinInterruptInterval) { - return; - } - SetNextInterrupt(Processor.CurrentProcessor.NextTimerInterrupt); - return; - } - - // need to tell other processors an important scheduling change - // has been made. - // OPEN QUESTION: Do we need to tell all processors, or just the ones - // which are idle? - public static void SignalOtherProcessors() - { - // needs implementation. - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Full/Task.cs b/base/Kernel/Singularity/Scheduling/Full/Task.cs deleted file mode 100644 index edbd090..0000000 --- a/base/Kernel/Singularity/Scheduling/Full/Task.cs +++ /dev/null @@ -1,312 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Task.cs -// -// Note: -// - -using System; -using System.Diagnostics; -using System.Collections; -using System.Threading; -#if SIMULATOR -using Thread = Microsoft.Singularity.Scheduling.Thread; -#endif - -namespace Microsoft.Singularity.Scheduling -{ - /// - /// A Singularity Task object represents work requiring a set of resources to be performed by a deadline. - /// - public class Task - { - /// - /// True if task was successfully admitted - /// - public bool Admitted - { - get { return admitted; } - } - internal bool admitted; - - internal Task parentTask; - private DateTime deadline; - internal Activity activity; - private Hashtable resourceEstimates; - internal Hashtable resourcesGranted; - internal Hashtable resourcesUsed; - internal ISchedulerTask schedulerTask; - - /// - /// To begin a constraint, use Task.BeginConstraint. The Task constructors - /// are to be used only within the system, and perform no reservation - /// activities. - /// - /// This constructor makes a task as a subtask of CurrentTask() and - /// CurrentActivity() - /// - internal Task(Hashtable resourceEstimates, - Hashtable resourcesGranted, - DateTime deadline, - bool admitted, - ISchedulerTask schedulerTask) - : this(resourceEstimates, resourcesGranted, deadline, admitted, schedulerTask, - Scheduler.CurrentTask(), Scheduler.CurrentActivity()) - { - } - - /// - /// To begin a constraint, use Task.BeginConstraint. The Task constructors - /// are to be used only within the system, and perform no reservation - /// activities. - /// - /// This constructor makes a task as a subtask of parentTask (possibly null) - /// and activity (likely a new resource container). - /// - internal Task(Hashtable resourceEstimates, - Hashtable resourcesGranted, - DateTime deadline, - bool admitted, - ISchedulerTask schedulerTask, - Task parentTask, - Activity activity) - { - this.activity = activity; - this.parentTask = parentTask; - this.resourceEstimates = resourceEstimates; - this.resourcesGranted = (resourcesGranted == null) ? new Hashtable() : resourcesGranted; - this.deadline = deadline; - this.resourcesUsed = new Hashtable(); - this.admitted = admitted; - this.schedulerTask = schedulerTask; - } - - public void ClearSchedulerTask() - { - schedulerTask = null; - } - - public void UpdateSchedulingState(bool admitted, - DateTime deadline, - Hashtable resourcesGranted) - { - this.admitted = admitted; - this.deadline = deadline; - this.resourcesGranted = resourcesGranted; - } - - //Because there are no timed-wait-and-begin-constraint (i.e. future constraints), - // I had to modify some of the simfiles to keep threads from exiting early. - // Currently start-times later than now are treated as starting now. - public static Task BeginTask() - { - Hashtable foo; - return BeginTask(null, DateTime.MaxValue, null, out foo); - } - - public static Task BeginTask(Task taskToEnd, out Hashtable used) - { - return BeginTask(null, DateTime.MaxValue, taskToEnd, out used); - } - - public static Task BeginTask(Hashtable resourceEstimates, DateTime deadline) - { - Hashtable foo; - return BeginTask(resourceEstimates, deadline, null, out foo); - } - - public static Task BeginTask(Hashtable resourceEstimates, - DateTime deadline, - Task taskToEnd, - out Hashtable actualUsed) - { - //In a single resource world, it is sufficient here to - // ask the CpuProvider to begin the constraint. However, - // this code will be the beginning of the starting point - // for multi-resource scheduling in this capacity. - ISchedulerTask schedulerTask; - Task task; - if ((resourceEstimates != null && resourceEstimates.Count > 0) || - (Scheduler.CurrentTask().resourceEstimates != null && Scheduler.CurrentTask().resourceEstimates.Count > 0)) { - bool admitted = CpuResource.Provider().BeginConstraint(resourceEstimates, - deadline, - taskToEnd, - out schedulerTask); - if (taskToEnd == null) { - task = new Task(resourceEstimates, - schedulerTask.ResourcesGranted, - deadline, - admitted, - schedulerTask); - } - else { - task = new Task(resourceEstimates, - schedulerTask.ResourcesGranted, - deadline, - admitted, - schedulerTask, - taskToEnd.parentTask, - taskToEnd.activity); - } - schedulerTask.EnclosingTask = task; - } - else { - if (taskToEnd != null) { - taskToEnd.End(); - } - task = new Task(null, null, DateTime.MaxValue, true, null); - } - if (taskToEnd != null) { - actualUsed = taskToEnd.resourcesUsed; - } - else { - actualUsed = null; - } - Thread.CurrentThread.currentTask = task; - Debug.Assert(task.resourcesUsed.Count == 0); - return task; - } - - public static Task BeginDelayedTask(Hashtable resourceEstimates, - TimeSpan relativeDeadline) - { - Hashtable foo; - return BeginDelayedTask(resourceEstimates, relativeDeadline, null, out foo); - } - - /// - /// Delayed tasks are used for such things as atomic wait and begin constraints. - /// The interpretation is that when a thread is woken up, the scheduler promises - /// to resolve any pending constraints atomically with the wakeup. After calling - /// BeginDelayedTask, the task won't actually begin until the thread performs - /// some action that causes a sleep/wait and is being woken up from it. - /// - /// Example uses: - /// - /// while (doingSomeStuff) { - /// Task t = BeginDelayedTask(resourceEstimates, relativeDeadline); - /// autoResetEvent.Wait(); - /// if (t.Admitted) { - /// //do full work - /// } else { - /// //do less work is possible - /// } - /// t.End(); - /// } - /// - /// while (doingSomeStuff) { - /// Task t = BeginDelayedTask(resourceEstimates, relativeDeadline); - /// Thread.Sleep(500); - /// if (t.Admitted) { - /// //do full work - /// } else { - /// //do less work is possible - /// } - /// t.End(); - /// } - /// - /// while (doingSomeStuff) { - /// Task t = BeginDelayedTask(resourceEstimates, relativeDeadline); - /// Thread.Sleep(500); - /// if (t.Admitted) { - /// //do full work - /// } else { - /// //do less work is possible - /// } - /// t.End(); - /// } - /// - /// Mutex m; - /// while (doingSomeStuff) { - /// Task t = BeginDelayedTask(resourceEstimates, relativeDeadline); - /// m.Acquire(); - /// if (t.Admitted) { - /// //do full work - /// } else { - /// //do less work is possible - /// } - /// t.End(); - /// } - /// - /// - /// - /// - /// - /// - /// - public static Task BeginDelayedTask(Hashtable resourceEstimates, - TimeSpan relativeDeadline, - Task taskToEnd, - out Hashtable actualUsed) - { - //In a single resource world, it is sufficient here to - // ask the CpuProvider to begin the constraint. However, - // this code will be the beginning of the starting point - // for multi-resource scheduling in this capacity. - ISchedulerTask schedulerTask; - Task task; - - Debug.Assert((resourceEstimates != null && resourceEstimates.Count > 0) || - (Scheduler.CurrentTask().resourceEstimates != null && Scheduler.CurrentTask().resourceEstimates.Count > 0)); - CpuResource.Provider().BeginDelayedConstraint(resourceEstimates, relativeDeadline, taskToEnd, out schedulerTask); - Debug.Assert(schedulerTask != null); - if (taskToEnd == null) { - task = new Task(resourceEstimates, schedulerTask.ResourcesGranted, DateTime.MaxValue, false, schedulerTask); - } - else { - task = new Task(resourceEstimates, schedulerTask.ResourcesGranted, DateTime.MaxValue, false, schedulerTask, taskToEnd.parentTask, taskToEnd.activity); - } - schedulerTask.EnclosingTask = task; - if (taskToEnd != null) { - actualUsed = taskToEnd.resourcesUsed; - } - else { - actualUsed = null; - } - Thread.CurrentThread.currentTask = task; - Debug.Assert(task.resourcesUsed.Count == 0); - return task; - } - - /// - /// The common case method for indicating task completion - /// - /// Returns the actual amounts of resources consumed - public Hashtable End() - { - //Do we want this static -- i.e. to prevent the thread from trying - // to end a task it's not presently working on? - Debug.Assert(this == Scheduler.CurrentTask(), "Cannot end a task other than the current task!"); - Debug.Assert(this != Activity().DefaultTask(), "Cannot end the default task for the resource container!"); - CpuResource.Provider().EndConstraint(this); - Thread.CurrentThread.currentTask = parentTask; - return resourcesUsed; // XXX TBD - } - - /// - /// Return the Activity object that this task is part of - /// - public Activity Activity() - { - return activity; - } - - public void AddResourceAmountUsed(string resourceString, IResourceAmount amount) - { - IResourceAmount current = (IResourceAmount)resourcesUsed[resourceString]; - if (current == null) { - resourcesUsed[resourceString] = amount; - } - else { - ((IResourceAmount)resourcesUsed[resourceString]).AddTo(amount); - } - if (parentTask != null) { - parentTask.AddResourceAmountUsed(resourceString, amount); - } - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Full/TimeConstraint.cs b/base/Kernel/Singularity/Scheduling/Full/TimeConstraint.cs deleted file mode 100644 index fd1aa1e..0000000 --- a/base/Kernel/Singularity/Scheduling/Full/TimeConstraint.cs +++ /dev/null @@ -1,27 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: TimeConstraint.cs -// -// Note: -// - -using System; - -namespace Microsoft.Singularity.Scheduling -{ - /// - /// A public structure used to pass data between the Kernel and the Application to express a - /// time constraint. - /// - public struct TimeConstraint - { - public DateTime Start; - public TimeSpan Estimate; - public DateTime Deadline; - public TimeSpan RelativeDeadline; - } -} diff --git a/base/Kernel/Singularity/Scheduling/Laxity/LaxityActivity.cs b/base/Kernel/Singularity/Scheduling/Laxity/LaxityActivity.cs deleted file mode 100644 index 1b6fd12..0000000 --- a/base/Kernel/Singularity/Scheduling/Laxity/LaxityActivity.cs +++ /dev/null @@ -1,134 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: LaxityActivity.cs -// -// Note: -// - -using System; -using System.Diagnostics; -using System.Threading; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling.Laxity -{ - /// - /// The resource container class includes a recurring reservation - /// (k out of every n), a list of runnable threads, unfinished constraints - /// (i.e. non-guaranteed or non-feasible), as well as pointers to handle - /// its role in lists for the non-blocked activity list. - /// It also has references for the last node to execute it, - /// a node assigned to it (which is a list via SameActivityNext), and a node - /// assigned to it under the new scheduling plan. It also has some bookkeeping. - /// - public class LaxityActivity : ISchedulerActivity - { - Activity enclosingActivity; - - public RecurringReservation MyRecurringCpuReservation; - - public LaxityThread RunnableThreads;// list of runnable threads, if any - -#if false - public OneShotReservation UnfinishedConstraints; -#endif - - public LaxityActivity Next; // Next and previous activities - public LaxityActivity Previous; - - public int ReferenceCount; - public int CountNodes; - - public LaxityActivity() - { - //Below is InitializeActivity(); - ReferenceCount = 1; - RunnableThreads = null; - -// MyRecurringCpuReservation = new RecurringReservation(); -// MyRecurringCpuReservation.EnclosingCpuReservation = new CpuResourceReservation(MyRecurringCpuReservation); - - bool iflag = Processor.DisableInterrupts(); - LaxityScheduler.EnqueueActivity(this); // add in last position in Laxity! - Processor.RestoreInterrupts(iflag); - } - - public void ReleaseActivity() - { - bool iflag = Processor.DisableInterrupts(); - LaxityScheduler.DequeueActivity(this); // update Laxity queue, if required. - Processor.RestoreInterrupts(iflag); - Scheduler.LogDeleteActivity(); - } - - //public-normal API - //TODO: Call this from finalize? - public override void ReleaseReference() - { - // DI replaced by AtomicDec(&activity->ReferenceCount); - Debug.Assert(ReferenceCount > 0); - int newrefcnt = Interlocked.Decrement(ref ReferenceCount); - - if (newrefcnt == CountNodes) { - if (CountNodes > 0) { - return; - } - bool iflag = Processor.DisableInterrupts(); - LaxityScheduler.DequeueActivity(this); // update Laxity queue, if required. - Processor.RestoreInterrupts(iflag); - } - Scheduler.LogDeleteActivity(); - } - - // find the runnable thread of an activity: - public LaxityThread GetRunnableThread() - { - // dnarayan: we don't schedule tasks from resource containers anymore, - // but directly from the global least-laxity heap -#if false - OneShotReservation reservation = UnfinishedConstraints; - Debug.Assert(reservation == null || reservation.AssociatedActivity == null); - // avoid stack overflow : GetRunnableThread & GetRunnableThread - while (reservation != null) { - // search in the circular list - LaxityThread thread = reservation.GetRunnableThread(); - if (thread != null) { - return thread; - } - reservation = reservation.Next; - if (reservation == UnfinishedConstraints) { - break; - } - } -#endif - LaxityThread runThread = RunnableThreads; - while (runThread != null && runThread.ActiveProcessor != null && runThread != RunnableThreads.Previous) { - runThread = runThread.Next; - } - if (runThread == null || runThread.ActiveProcessor != null) { - return null; - } - else { - return runThread; - } - } - -#region ISchedulerActivity Members - - public override Activity EnclosingActivity - { - get { return enclosingActivity; } - set { enclosingActivity = value; } - } - - public CpuResourceReservation CpuResourceReservation - { - get { return MyRecurringCpuReservation.EnclosingCpuReservation; } - } -#endregion - } -} diff --git a/base/Kernel/Singularity/Scheduling/Laxity/LaxityHeap.cs b/base/Kernel/Singularity/Scheduling/Laxity/LaxityHeap.cs deleted file mode 100644 index 85e4b86..0000000 --- a/base/Kernel/Singularity/Scheduling/Laxity/LaxityHeap.cs +++ /dev/null @@ -1,273 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: LaxityHeap.cs -// -// Note: -// - -using System; -using System.Collections; -using System.Diagnostics; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling.Laxity -{ - /// - /// A generic heap implementation - /// - public class Heap - { - private class HeapElem - { - public object elem; - public IComparable key; - - public HeapElem(object elem, IComparable key) - { - this.elem = elem; - this.key = key; - } - } - - private Hashtable map; - private ArrayList heap; - - // float the value at position i down the heap, assuming - // subtrees already satisfy the heap property - // this is HEAPIFY from CLR pg 143. - private void pushdown(int i) - { - while (i*2 + 1 < heap.Count) { - int ichild = i*2 + 1; - HeapElem child = (HeapElem)heap[ichild]; - if (ichild + 1 < heap.Count) { - HeapElem otherchild = (HeapElem)heap[ichild+1]; - if (otherchild.key.CompareTo(child.key) < 0) { - child = otherchild; - ichild++; - } - } - - HeapElem cur = (HeapElem)heap[i]; - // now child contains the child with the smaller key - // if that is smaller than parent's key, swap them - if (child.key.CompareTo(cur.key) < 0) { - heap[i] = child; - heap[ichild] = cur; - map[child.elem] = i; - map[cur.elem] = ichild; - } - - // and continue one level down - i = ichild; - } - } - - // push a value up the heap until it exceeds its parent - // this is HEAP-INSERT from CLR pg 150 - private void pushup(int i) - { - while (i > 0) { - HeapElem cur = (HeapElem) heap[i]; - HeapElem parent = (HeapElem) heap[(i-1)/2]; - if (parent.key.CompareTo(cur.key) <= 0) { - break; - } - // swap with parent and continue - heap[i] = parent; - heap[(i-1)/2] = cur; - map[parent.elem] = i; - map[cur.elem] = (i-1)/2; - i = (i-1)/2; - } - } - - - // assert heap invariant at node i - private void assertHeap(int i) - { - // first make sure map and heap are consistent - Debug.Assert(heap.Count == map.Count); - for (int j = 0; j < heap.Count; j++) { - HeapElem cur = (HeapElem) heap[j]; - int jj = (int) map[cur.elem]; - Debug.Assert(j == jj); - } - - for (; i < heap.Count/2; i++) { - HeapElem cur = (HeapElem) heap[i]; - HeapElem child = (HeapElem) heap[i*2+1]; - Debug.Assert(child.key.CompareTo(cur.key) >= 0); - if (i*2 + 2 < heap.Count) { - child = (HeapElem)heap[i*2+2]; - Debug.Assert(child.key.CompareTo(cur.key) >= 0); - } - } - } - - /// - /// Create a new, empty heap. - /// - public Heap() - { - map = new Hashtable(); - heap = new ArrayList(); - } - - public int Count - { - get { return heap.Count; } - } - - /// - /// Check if the heap is legit else assert - /// - public void AssertHeap() - { - assertHeap(0); - } - - /// - /// Insert an object into the heap - /// - /// the object to be inserted - /// the key value with which the object is associated - /// false if object already exists in the heap (key is not updated in this case) - - public bool Insert(object elem, IComparable key) - { - if (map.ContainsKey(elem)) { - return false; - } - int i = heap.Count; - map[elem] = i; - heap.Add(new HeapElem(elem, key)); - pushup(i); - return true; - } - - /// - /// Update a key value for an existing object - /// - /// the object to update the key for - /// the new key value - /// false if the object does not exist in the heap - public bool Update(object elem, IComparable newkey) - { - if (!map.ContainsKey(elem)) { - return false; - } - int i = (int)map[elem]; - HeapElem cur = (HeapElem) heap[i]; - cur.key = newkey; - - // we need to push it either up or down, just do both - pushdown(i); - pushup(i); - - return true; - } - - /// - /// Delete an object from the heap - /// - /// the object to delete - /// false if object does not exist in the heap - public bool Delete(object elem) - { - if (!map.ContainsKey(elem)) { - return false; - } - - int i = (int) map[elem]; - int ilast = heap.Count - 1; - HeapElem lastelem = (HeapElem)heap[ilast]; - - // remove from mapping and shrink the array - heap.RemoveAt(ilast); - map.Remove(elem); - - if (i != ilast) { - // move erstwhile last elem into current position and update mapping - heap[i] = lastelem; - map[lastelem.elem] = i; - - // push up/down to rightful place - pushdown(i); - pushup(i); - } - - return true; - } - - /// - /// Return the heap object with the least key, without removing it. - /// - /// the heap object with the least key - public object Min - { - get { return (heap.Count > 0) ? ((HeapElem)heap[0]).elem : null; } - } - - // debug the heap - static void TestHeap(int maxelem) - { - Heap newheap = new Heap(); - Random r = new Random(); - bool[] inserted = new bool[maxelem]; - int[] updated = new int[maxelem]; - - for (int i = 0; i < maxelem; i++) { - DebugStub.WriteLine("Inserting {0}\r", i); - int ins = r.Next() % maxelem; - bool insret = newheap.Insert(ins, ins); - newheap.AssertHeap(); - Debug.Assert(insret != inserted[ins]); // returns false if already inserted - inserted[ins] = true; - } - DebugStub.WriteLine("{0} inserted", newheap.Count); - - for (int i = 0; i < maxelem; i++) { - DebugStub.WriteLine("Updating {0}", i); - int newkey = r.Next() % 1000; - bool upret = newheap.Update(i, newkey); - newheap.AssertHeap(); - Debug.Assert(upret == inserted[i]); - updated[i] = upret ? newkey : -1; - } - DebugStub.WriteLine(); - - for (int i = 0; i < maxelem; i++) { - DebugStub.WriteLine("Deleting {0}", i); - int del = r.Next() % maxelem; - bool delret = newheap.Delete(del); - newheap.AssertHeap(); - Debug.Assert(delret == inserted[del]); // returns false if not inserted - inserted[del] = false; - } - DebugStub.WriteLine(); - - int prev = 0; - int c = newheap.Count; - DebugStub.WriteLine("{0} remaining", c); - for (int i = 0; i < c; i++) { - int nextval = (int) newheap.Min; - newheap.AssertHeap(); - int nextkey = updated[nextval]; - Debug.Assert(nextkey != -1); - Debug.Assert(nextkey >= prev); - DebugStub.Write("{0} ", nextkey); - prev = nextkey; - Debug.Assert(newheap.Delete(nextval)); - newheap.AssertHeap(); - } - DebugStub.WriteLine() - Debug.Assert(newheap.Count == 0); - DebugStub.WriteLine("All serene ..."); - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Laxity/LaxityProcessor.cs b/base/Kernel/Singularity/Scheduling/Laxity/LaxityProcessor.cs deleted file mode 100644 index 40671c9..0000000 --- a/base/Kernel/Singularity/Scheduling/Laxity/LaxityProcessor.cs +++ /dev/null @@ -1,48 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: LaxityProcessor.cs -// -// Note: -// - -using System; -using System.Diagnostics; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling.Laxity -{ - /// - /// - public class LaxityProcessor : ISchedulerProcessor - { - private readonly Processor enclosingProcessor; - public override Processor EnclosingProcessor - { - get { return enclosingProcessor; } - } - - internal LaxityActivity CurrentActivity; - internal TimeSpan SliceLeft; - internal LaxityThread RunningThread; - internal DateTime SchedulingTime; - internal bool NeedToReschedule; - internal bool Idle; - - public void Reschedule() - { - NeedToReschedule = true; - } - - public LaxityProcessor(Processor processor) - { - enclosingProcessor = processor; - SchedulingTime = SchedulerClock.BootTime; - } - - } - -} diff --git a/base/Kernel/Singularity/Scheduling/Laxity/LaxityScheduler.cs b/base/Kernel/Singularity/Scheduling/Laxity/LaxityScheduler.cs deleted file mode 100644 index cf618fb..0000000 --- a/base/Kernel/Singularity/Scheduling/Laxity/LaxityScheduler.cs +++ /dev/null @@ -1,842 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: LaxityScheduler.cs -// -// Note: -// - -using System; -using System.Collections; -using System.Diagnostics; -using System.Threading; -using Microsoft.Singularity; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling -{ -#if !SIMULATOR - public class SystemScheduler - { - static public void RegisterScheduler() - { - Laxity.LaxityScheduler.RegisterScheduler(); - } - } -#endif -} - -namespace Microsoft.Singularity.Scheduling.Laxity -{ - /// - /// Summary description for LaxityScheduler. - /// - public class LaxityScheduler : ICpuScheduler - { - public static readonly TimeSpan ContextSwitch = new TimeSpan(200); - public static readonly TimeSpan MinSlice = new TimeSpan(10 * ContextSwitch.Ticks); - public static readonly TimeSpan AFewSlice = new TimeSpan(3 * MinSlice.Ticks); - public static readonly TimeSpan RobinSlice = new TimeSpan(10 * TimeSpan.TicksPerMillisecond); - - static public void RegisterScheduler() - { - CpuResource.RegisterSystemScheduler(new LaxityScheduler()); - } - - public override ISchedulerProcessor CreateSchedulerProcessor(Processor processor) - { - return new LaxityProcessor(processor); - } - - public override ISchedulerThread CreateSchedulerThread(Thread thread) - { - return new LaxityThread(thread); - } - - public override ISchedulerActivity CreateSchedulerActivity() - { - return new LaxityActivity(); - } - - public override ISchedulerCpuReservation ReserveCpu(ISchedulerActivity activity, CpuResourceAmount amount, TimeSpan period) - { - return null; - } - - public bool ReserveRecurringCpu(Activity activity, ref TimeSpan amount, ref TimeSpan period) - { - return true; - } - - public override bool ShouldReschedule() - { - LaxityThread currentThread = GetCurrentThread(); - if (Scheduler.TimerInterruptedFlag && currentThread != null) { - //SchedulerClock.CheckInterrupt(); - } - - if (currentThread == null || Scheduler.YieldFlag || currentThread.EnclosingThread.IsWaiting() || currentThread.EnclosingThread.IsStopped()) { - Reschedule(); - } - return NeedToReschedule || Scheduler.TimerInterruptedFlag; - // || timer should have fired but didn't? - } - - // DI -- this is somehow similar to EnableInterrupts - public override bool NextThread(out Thread nextThread) - { - Debug.Assert(!Processor.InterruptsDisabled()); - bool iflag = Processor.DisableInterrupts(); - bool halted = false; - LaxityThread currentThread = GetCurrentThread(); - - SchedulerClock.CheckInterrupt(); - if (ShouldReschedule()) { - //Debug.Print("Calling RescheduleInterrupt()\n"); - halted = RescheduleInterrupt(); //TODO: RescheduleInterrupt returns true if the processor needs to be halted. - currentThread = GetCurrentThread(); - } - else { - //Debug.Print("No call to RescheduleInterrupt()\n"); - } - - if (currentThread != null) { - nextThread = currentThread.EnclosingThread; - } - else { - Debug.Assert(halted); - nextThread = null; - } - - ((LaxityProcessor)Processor.CurrentProcessor.SchedulerProcessor).NeedToReschedule = false; - // printf("-------------------------currentThread-Id %d\n", currentThread.Id); - //return currentThread; - Processor.RestoreInterrupts(iflag); - return halted; - } - //public static void Yield(); -#region Constants - - // Maximum Scheduling Period is 1 sec (i.e. in 100s of nanoseconds). - static readonly TimeSpan AmountCpu = CpuResource.MaxPeriod; //(MaxPeriod * 100); //TODO: TICKS vs. TIME_SPAN vs DATE_TIME - -#endregion - -#region Static data members - static LaxityActivity CircularActivityList = null; - /// - /// This must ALWAYS (when lock not held) point to a current resource - /// container in the queue. - /// - static internal LaxityActivity NextActivity; - - - //moved to be processor specific - //static LaxityActivity LaxityActivityNext = null; - //static LaxityActivity LaxityActivityCurrent = null; // activity currently executing from - - private static OneShotReservation ReservationFreePool; - private static TimeSpan IdleTime = new TimeSpan(0); - private static bool Idle - { - get { return GetCurrentProcessor().Idle; } - } - - //moved to be processor specific - //static LaxityThread CurrentThreadGlobal = null; - //static TimeSpan LaxitySliceLeft = LaxityScheduler.LaxitySlice; //new TimeSpan(0); // the round robin list, if any, and its CPU slice - //public static DateTime SchedulingTime; - //static bool NeedToReschedule = false; - - public static TimeSpan LA_Time = LaxityScheduler.MinSlice; - - public static DateTime SchedulingTime - { - get { - return ((LaxityProcessor)Processor.CurrentProcessor.SchedulerProcessor).SchedulingTime; - } - } - - public static bool NeedToReschedule - { - get { - return ((LaxityProcessor)Processor.CurrentProcessor.SchedulerProcessor).NeedToReschedule; - } - } - -#endregion - - public LaxityScheduler() - { - } - - public override void Initialize() - { - LaxityScheduler.InitializeScheduler(); - } - - public static void InitializeScheduler() - { //TODO: Should all the null's be added in here? - for (int i = 0; i < Processor.processorTable.Length; i++) { - ((LaxityProcessor)Processor.processorTable[i].SchedulerProcessor).SchedulingTime = SystemClock.GetKernelTime(); - } -// -// TimeSpan currentSliceLeft = LaxitySliceLeft; - bool iflag = Processor.DisableInterrupts(); - SchedulerClock.SetNextInterrupt(SchedulingTime + LaxityScheduler.RobinSlice); - Processor.RestoreInterrupts(iflag); - } - -#region ISystemScheduler Members - - public override void BeginDelayedConstraint(Hashtable resourceEstimates, TimeSpan relativeDeadline, ISchedulerTask taskToEnd, out ISchedulerTask schedulerTask) - { - TimeConstraint timeConstraint = new TimeConstraint(); - timeConstraint.Estimate = CpuResource.Provider().CpuToTime((CpuResourceAmount)resourceEstimates[CpuResource.Provider().ResourceString]); - timeConstraint.Start = new DateTime(0); //Start now. - timeConstraint.RelativeDeadline = relativeDeadline; - timeConstraint.Deadline = new DateTime(0); //A 0-deadline means relative instead. - schedulerTask = Thread.CurrentThread.SchedulerThread.PrepareDelayedTask(taskToEnd, ref timeConstraint, SystemClock.GetKernelTime()); - } - - public override bool BeginConstraint(Hashtable resourceEstimates, DateTime deadline, ISchedulerTask taskToEnd, out ISchedulerTask schedulerTask) - { - DateTime timeNow = SystemClock.GetKernelTime(); - LaxityThread thread = GetCurrentThread(); - ulong start; - ulong stop; - Debug.Assert(!Processor.InterruptsDisabled()); - - thread.IpcCheckFreeConstraint(); - - start = Processor.CycleCount; - - Debug.Assert(taskToEnd == null || taskToEnd == thread.ReservationStack); - bool end_previous = (taskToEnd != null); - TimeConstraint constraint = new TimeConstraint(); - constraint.Deadline = deadline; - constraint.Estimate = CpuResource.Provider().CpuToTime((CpuResourceAmount)resourceEstimates[CpuResource.Provider().ResourceString]); - constraint.Start = timeNow; - - bool ok = thread.BeginConstraintBeforeWaitValidate(end_previous, ref constraint, timeNow); - schedulerTask = thread.PendingReservation; - if (ok) { - OneShotReservation.BeginConstraintBeforeWait(thread, end_previous, constraint, timeNow); - bool iflag = Processor.DisableInterrupts(); - ok = OneShotReservation.ResolveConstraint(thread); - Processor.RestoreInterrupts(iflag); - } - stop = Processor.CycleCount; - - Scheduler.LogBeginConstraint(thread.EnclosingThread, ok, start, stop); - - //TODO: Check if this is a necessity, or if it's only for sim time. Call in wrapper if necessary. - //NextThread(); - return ok; - } - - public override bool EndConstraint(ISchedulerTask taskToEnd) - { - Debug.Assert(!Processor.InterruptsDisabled()); - Debug.Assert(taskToEnd == GetCurrentThread().ReservationStack); - bool iflag = Processor.DisableInterrupts(); - bool ok = OneShotReservation.EndPreviousConstraint(GetCurrentThread(), SystemClock.GetKernelTime()); - // - // need to reschedule only if on a reserved slot and the top of EarliestDeadlineFirst doesn't - // belong to this task - // - if ((OneShotReservation.CurrentReservation != null) && - (OneShotReservation.TopGuaranteedReservation != null) && - (OneShotReservation.TopGuaranteedReservation.ReservTask != (GetCurrentThread()))) { - Reschedule(); - } - - Processor.RestoreInterrupts(iflag); - Scheduler.LogEndConstraint(); - - //TODO: In the system wrapper for BeginConstraint -- it needs to call Yield/NextThread/MaybeYield - //NextThread(); - return ok; - } - - public static bool ReserveRecurringCpu(LaxityActivity activity, - ref TimeSpan amount, - ref TimeSpan period) - { - return true; - } - -#endregion - - -#region OneShotCpuReservation related functions - // OneShotCpuReservation related functions begin: - - public static int ReleaseReservationProtected(OneShotReservation reservation) - { - int newrefcnt; - Debug.Assert(Processor.InterruptsDisabled()); - - Debug.Assert(reservation.ReferenceCount > 0); - newrefcnt = Interlocked.Decrement(ref reservation.ReferenceCount); - - if (newrefcnt == 0) { - // Estimate may be positive when reservation reaches - // its Deadline w/o using its Estimate - Debug.Assert((reservation.Next == null) && (reservation.Previous == null)); - if (reservation.OriginalThread != null) { - reservation.OriginalThread.ReleaseProtected(); - //Debug.Assert(reservation.ActiveThread != null); - //reservation.ActiveThread.ReleaseProtected(); - } - else { - Debug.Assert(reservation.AssociatedActivity != null); - ActivityReleaseProtected(reservation.AssociatedActivity); - } - Debug.Assert(reservation.Next == null); - CacheFreeReservation(reservation); - } - return newrefcnt; - } - - - public static OneShotReservation AllocateReservation() - { - OneShotReservation reservation; - - // DisableInterrupts(); - reservation = IpcAllocateReservation(); - // EnableInterrupts(); - - if (reservation == null) { - reservation = new OneShotReservation(); //(PRESERVATION)malloc(sizeof(struct OneShotReservation)); // to be done while on the I-stack! - reservation.Clear(); - } - return reservation; - } - - static void FreeReservation(OneShotReservation reservation) - { - reservation = null; //free(reservation); - } - - // Garbage-collect unused Reservations. - - public static int ReleaseReservation(OneShotReservation reservation) - { - // Debug.Assert(!Processor.InterruptsDisabled()); - Debug.Assert(reservation.ReferenceCount > 0); - int newrefcnt = reservation.ReferenceCount - 1; - reservation.ReferenceCount = newrefcnt; - - if (newrefcnt == 0) { - // Estimate may be positive when reservation reaches its Deadline w/o using its Estimate - Debug.Assert((reservation.Next == null) && (reservation.Previous == null)); - if (reservation.OriginalThread != null) { - reservation.OriginalThread.Release(); - //Debug.Assert(reservation.ActiveThread != null); - //reservation.ActiveThread.Release(); - } - else if (reservation.AssociatedActivity != null) { - ActivityObjRelease(reservation.AssociatedActivity); - } - CacheFreeReservation(reservation); - } - return newrefcnt; - } - - // Like CheckFreeConstraint, but callable from the IPC path. - // Because we can't call AllocateReservation, we use the helper thread - // if there aren't any free Reservations. - static void CacheFreeReservation(OneShotReservation reservation) - { - if (reservation.OriginalThread != null && - reservation.OriginalThread.FreeReservation == null) { - reservation.OriginalThread.FreeReservation = reservation; - } - else { - IpcFreeReservation(reservation); - } - } - - - // Callable with preemption disabled. Allocates a OneShotReservation - // from the global free list. - public static OneShotReservation IpcAllocateReservation() - { - OneShotReservation reservation; - - if ((reservation = ReservationFreePool) != null) { - ReservationFreePool = reservation.FreeListNext; - } - - return reservation; - } - - - // Callable with preemption disabled. Frees a OneShotReservation - // to the global free list. - public static void IpcFreeReservation(OneShotReservation reservation) - { - Debug.Assert( reservation != null); - Debug.Assert(reservation.ReferenceCount == 0); - - reservation.FreeListNext = ReservationFreePool; - ReservationFreePool = reservation; - } - - ////////////////////////////////////////////////////////////////////// - - public static void AddRefReservation(OneShotReservation reservation) - { - Debug.Assert(reservation.ReferenceCount >= 0); - reservation.ReferenceCount++; - } - -#endregion - -#region Potpourri (Unregioned functions) - - public static LaxityThread GetCurrentThread() - { - return ((LaxityProcessor)Processor.CurrentProcessor.SchedulerProcessor).RunningThread; - } - - public static void IChangeCurrentThread(LaxityThread thread) - { - ((LaxityProcessor)Processor.CurrentProcessor.SchedulerProcessor).RunningThread = thread; - } - - // Auxiliary Routines. - // TODO: Revisit the necessity of these functions. - public static TimeSpan minInterval(TimeSpan TimeInterv0, TimeSpan TimeInterv1) - { - return (TimeInterv0 < TimeInterv1? TimeInterv0: TimeInterv1); - } - - public static DateTime minTime(DateTime Time0, DateTime Time1) - { - return (Time0 < Time1? Time0: Time1); - } - - static TimeSpan maxInterval(TimeSpan TimeInterv0, TimeSpan TimeInterv1) - { - return (TimeInterv0 < TimeInterv1? TimeInterv1: TimeInterv0); - } - - static DateTime maxTime(DateTime Time0, DateTime Time1) - { - return (Time0 < Time1? Time1: Time0); - } - - internal static LaxityProcessor GetCurrentProcessor() - { - return (LaxityProcessor)Processor.CurrentProcessor.SchedulerProcessor; - } - - // Add Activity in the last Round-Robin position. - public static void EnqueueActivity(LaxityActivity activity) - { - Debug.Assert(Processor.InterruptsDisabled()); - if (CircularActivityList == null) { - NextActivity = CircularActivityList /*= GetCurrentProcessor().CurrentActivity */= - activity.Next = activity.Previous = activity; - //Here modifying other processor specific data rather than global. -// for (int i=0; i NewTimerTimeout) { - return false; - } - - Debug.Assert(Processor.InterruptsDisabled()); - // Debug.Assert(CurrentThread()->GetState() == Thread.ThreadState.ThreadRunning); - - Debug.Assert(NewTimerTimeout > SchedulingTime); - // Debug.Assert(SleepTimeout > SchedulingTime); - - //TODO: Need LA_Time? - // if (NewTimerTimeout > SleepTimeout - LA_Time) - // NewTimerTimeout = SleepTimeout - LA_Time; - if (NewTimerTimeout > LaxityThread.GetSleepTimeout()) { - NewTimerTimeout = LaxityThread.GetSleepTimeout(); - } - - //DebugStub.Print("Setting Next Interrupt for: {0} ....\n", - //__arglist(NewTimerTimeout.Ticks)); - bool success = SchedulerClock.SetNextInterrupt(NewTimerTimeout); //TODO: Perhaps only call this if the time changed. - - //DebugStub.Print(success?"SUCCESS\n":"FAILED\n"); - - return success; - } - - public static void UpdateSchedulingStatus() - { - TimeSpan timeRun; - LaxityThread currentThread = GetCurrentThread(); - - DateTime newSchedulingTime = SystemClock.GetKernelTime(); - - timeRun = newSchedulingTime - SchedulingTime; - LaxityProcessor processor = (LaxityProcessor)Processor.CurrentProcessor.SchedulerProcessor; - processor.SchedulingTime = newSchedulingTime; - - if (Idle) { - // UpdateIdleTime - IdleTime += timeRun; - return; - } - - // If IDLE compute CurrentPlanNode & set currentThread to null. - if (currentThread != null) { - // Unless thread just exited, - // Update Thread & Activity execution times. - currentThread.AddExecutionTime(timeRun); - Scheduler.CurrentTask().AddResourceAmountUsed(CpuResource.Provider().ResourceString, CpuResource.Provider().TimeToCpu(timeRun)); -// if (currentThread.AssociatedActivity != null) { -// currentThread.AssociatedActivity.MyRecurringCpuReservation.EnclosingCpuReservation.AddTimeUsed(timeRun); -// } - } - if (OneShotReservation.CurrentReservation != null) { - // Slice used for a reservation. - OneShotReservation.CurrentReservation.Estimate -= timeRun; - } - else if (processor.CurrentActivity != null) { - // Slice used for round robin. - processor.SliceLeft -= timeRun; - processor.CurrentActivity = null; - } - } - - internal static void CheckInvariants() - { - if (CircularActivityList != null) { - bool listNextFound = (CircularActivityList == NextActivity); - LaxityActivity start = CircularActivityList, current = CircularActivityList.Next, previous = CircularActivityList; - while (current != start) { - //perhaps check thread invariants here too. - Debug.Assert(current != null, "resource container list is not circular"); - Debug.Assert(current.Previous == previous, "back pointer doesn't match forward pointer"); - if (current == NextActivity) { - listNextFound = true; - } - previous = current; - current = current.Next; - } - Debug.Assert(listNextFound, "NextActivity isn't in the loop!"); - Debug.Assert(current.Previous == previous, "Head doesn't point to tail"); - } - } - - // Always called when another thread needs to be scheduled. - static bool RescheduleInterrupt() - { - CheckInvariants(); - Debug.Assert(Processor.InterruptsDisabled()); - Debug.Assert(!Processor.InterruptsDisabled()); - LaxityThread currentThread = GetCurrentThread(); - Debug.Assert(currentThread == null || currentThread.ActiveProcessor == GetCurrentProcessor()); - if (currentThread != null) { - currentThread.ActiveProcessor = null; - } - - DateTime nextStart; - LaxityThread previousThread; - - RescheduleAgain: // !!!!!!!!!! SIM ONLY !!!!!!!!! - if (Idle) { - currentThread = null; - } - - previousThread = currentThread; - - UpdateSchedulingStatus(); - - // if thread was the head of the runnable threads Q: - // Advance the runnable threads Q. - if ((currentThread != null) && - (currentThread.AssociatedActivity != null) && - (currentThread.AssociatedActivity.RunnableThreads == currentThread)) { - currentThread.AssociatedActivity.RunnableThreads = currentThread.Next; - Debug.Assert(currentThread.AssociatedActivity.RunnableThreads != null); - } - - OneShotReservation.UpdateCurrentReservation(); - OneShotReservation.ClearCurrentReservation(); - currentThread = null; - - // Finished first stage, i.e. updated state. - // Start second stage: wakeup threads & select Next CPU slice. - - LaxityThread.WakeThreads(); - - // NOTE: In the original Laxity Simulator Code (& MMOSA code) - // The call to DrainDeferredConditions() was made here. - // In Singularity, this will basically be replaced with a - // queue of wait-events to fix. - - OneShotReservation.FreshenReservationQueues(); - - TimeSpan currentNodeSliceLeft; - LaxityProcessor currentProcessor = (LaxityProcessor)Processor.CurrentProcessor.SchedulerProcessor; - if (currentProcessor.CurrentActivity != null && currentProcessor.SliceLeft >= LaxityScheduler.MinSlice) { - currentThread = currentProcessor.CurrentActivity.GetRunnableThread(); - if (currentThread != null) { - goto exit; - } - } - - // Find runnable one-shot reservation if any. - OneShotReservation.FindRunnableReservation(ref currentThread); - if (currentThread != null) { - goto exit; - } - - // Find next runnable Activity. - currentProcessor.SliceLeft = LaxityScheduler.RobinSlice; - currentProcessor.CurrentActivity = NextActivity; // !!!!!!!!!!!!!SIM ONLY !!!!!!!!!! - while ((currentThread = NextActivity.GetRunnableThread()) == null) { - NextActivity = NextActivity.Next; - //currentProcessor.SliceLeft = LaxityScheduler.LaxitySlice; - if (NextActivity == currentProcessor.CurrentActivity) { - // !!!!!!!!SIM ONLY !!!!!!! - // in the real scheduler, execute halt - if (OneShotReservation.IdleReservations != null) { - // reuse nextStart - nextStart = minTime(LaxityThread.GetSleepTimeout(), OneShotReservation.IdleReservations.Start); - } - else { - nextStart = LaxityThread.GetSleepTimeout(); - DebugStub.Print("Idle, sleeping until {0} cf maxvalue {1}\n", - __arglist(nextStart.Ticks, - DateTime.MaxValue.Ticks)); - } - if (nextStart == DateTime.MaxValue) { - Scheduler.StopSystem(); - } - if (! ResetTimerTimeout(nextStart)) { - //Error setting timer. Try scheduling again. - DebugStub.Print("Thought Idle, failed to set interrupt.\n"); - goto RescheduleAgain; - } - GetCurrentProcessor().Idle = true; - - // !!!!!!!!SIM ONLY !!!!!!! - currentThread = null; // !!!!!!!!SIM ONLY !!!!!!! - OneShotReservation.ClearCurrentReservation(); - currentProcessor.CurrentActivity = null; - - if (DateTime.MaxValue != nextStart) { - //Scheduler.LogTimeJump(); - } - - //DebugStub.Print("Halted.\n"); - IChangeCurrentThread(null); - return true; // !!!!!!!!SIM ONLY !!!!!!! - } - // !!!!!!!!SIM ONLY !!!!!!! - } - //DebugStub.Print("Running Round Robin Resource Container\n"); - currentProcessor.CurrentActivity = NextActivity; // we probably need only one of the two variables - NextActivity = NextActivity.Next; - - exit: - currentNodeSliceLeft = currentProcessor.SliceLeft; - - Debug.Assert(currentThread != null); - if (currentThread != previousThread) { - Scheduler.LogContextSwitch(); // Context Switch statistics - } - - if (OneShotReservation.IdleReservations != null) { - // reuse nextStart - nextStart = minTime(LaxityThread.GetSleepTimeout(), OneShotReservation.IdleReservations.Start); - } - else { - nextStart = LaxityThread.GetSleepTimeout(); - } - - if (SchedulingTime + currentNodeSliceLeft /* CurrentSlice */ > nextStart) { - currentNodeSliceLeft /* CurrentSlice */ = nextStart - currentProcessor.SchedulingTime; - } - - Scheduler.LogReschedule(); - if (!ResetTimerTimeout(currentProcessor.SchedulingTime + currentNodeSliceLeft) || Scheduler.TimerInterruptedFlag) { - //TODO: What do we REALLY want here? - currentThread = null; // !!!!!!!!SIM ONLY !!!!!!! - OneShotReservation.ClearCurrentReservation(); - currentProcessor.CurrentActivity = null; - goto RescheduleAgain; - } - - Debug.Assert(currentThread.ActiveProcessor == null); - currentThread.ActiveProcessor = GetCurrentProcessor(); - Debug.Assert(currentThread.ActiveProcessor != null); - - if (currentThread != previousThread) { - IChangeCurrentThread(currentThread); - } - GetCurrentProcessor().Idle = false; - - // Not necessarily true: Debug.Assert(!Scheduler.TimerInterruptedFlag); - CheckInvariants(); - return false; - } - -#endregion - -#region Laxity Changes - - -#endregion - -#region Constraint feasibility analysis support functions - // Node can provide more than time than necessary between start and deadline; come shorter deadline: - // add a new pointer to a reservation to a node and clean the ordered array -#endregion - - public static void Reschedule() - { - ((LaxityProcessor)Processor.CurrentProcessor.SchedulerProcessor).Reschedule(); - } - - public static void ActivityObjAddRef(LaxityActivity activity) - { - activity.ReferenceCount++; - } - - - public static void ActivityObjRelease(LaxityActivity activity) - { - Debug.Assert(activity.ReferenceCount >= 1); - activity.ReleaseReference(); - } - - public static void ActivityReleaseProtected(LaxityActivity activity) - { - Debug.Assert(activity.ReferenceCount >= 1); - Debug.Assert(Processor.InterruptsDisabled(), "Interrupts Not Disabled!"); - activity.ReleaseReference(); - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Laxity/LaxityThread.cs b/base/Kernel/Singularity/Scheduling/Laxity/LaxityThread.cs deleted file mode 100644 index 965ab9d..0000000 --- a/base/Kernel/Singularity/Scheduling/Laxity/LaxityThread.cs +++ /dev/null @@ -1,518 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: LaxityThread.cs -// -// Note: -// - -using System; -using System.Diagnostics; -using System.Threading; -using Microsoft.Singularity; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling.Laxity -{ - /// - /// The kernel structure for the thread. Contains an associated activity, a - /// stack of reservations executing the activity, a reference for a free - /// reservation (rather than allocating a new one all the time), a pending - /// reservation (used when new constraints are being created), a reference to the - /// thread it’s waiting on (for use in scheduling inheritance), the basic thread - /// queue and sleep queue pointers, lists of people waiting on mutexes and cv’s - /// owned by this thread, bookkeeping for the type of waiting that I’m doing, and - /// general bookkeeping. - /// - public class LaxityThread : ISchedulerThread - { - private static ListNode SleepingThreads = null; // list of sleeping threads - private static DateTime SleepTimeout = DateTime.MaxValue; // Next thread wakeup time - - private TimeSpan executionTime; - public override TimeSpan ExecutionTime - { - get { return executionTime; } - } - - public LaxityActivity AssociatedActivity; // Thread Activity - //public LaxityThread.ThreadState State; // RUNNING, WAITING, SLEEPING - - public OneShotReservation ReservationStack;// Stack of Reservations, one per active Constraint - public OneShotReservation FreeReservation; - public OneShotReservation PendingReservation; // PendingConstraint if any - // DI -- corresponds to MMID Tid - //TODO: Replace with TASK public int Tid; // Id to mark node reservations made for this Constraint Stack - // DI :: NOTE the Owner field in Kernel is used only for Inheritance control - // need to REMOVE it -- this will help to locate easy all these functions and - // comment them out - //public int Epoch; // inc-ed at every kernel unlock - - public DateTime StartTime; // only while on the sleep Q - - public LaxityThread Next; // Next and previous threads - public LaxityThread Previous; - - // DI -- maybe don't use it, see vtlb.c - // UINT QueueType; // Type of queue thread is on, if any. - - // DI -- ??!! this might replace Next and Previous from above - public ListNode Queue; // List of threads in a queue. - // DI -- need it as a thread may wait on a condition (i.e. use Queue) - // and wait for the timeout (Condition_TimedWait) - public ListNode SleepQueue; // Another queue link, for sleeping only. - - public int ReferenceCount; - public readonly Thread enclosingThread; - - public LaxityProcessor ActiveProcessor; //Set only when running on processor -- no other processor may take the thread to run. - - //public interface - public LaxityThread(Thread thread) - { - enclosingThread = thread; - Queue = new ListNode(this); - SleepQueue = new ListNode(this); - AssociatedActivity = null; - StartTime = new DateTime(0); - //TODO: BUGBUGBUG - //Tid = Id; REPLACE WITH DEFAULT TASK? - Queue.Next = Queue.Previous = Queue; - SleepQueue.Next = SleepQueue.Previous = SleepQueue; - //State = LaxityThread.ThreadState.ThreadWaiting; - } - -#region ISchedulerThread Members - - public override Thread EnclosingThread - { - get { return enclosingThread; } - } - - //public interface - public override void SetActivity(ISchedulerActivity iActivityNew) - { - DebugStub.Print("LaxityThread.SetActivity()\n"); - Debug.Assert(iActivityNew != null); - Debug.Assert(iActivityNew is LaxityActivity); - Debug.Assert(((LaxityActivity)iActivityNew).ReferenceCount > 0); - LaxityActivity activityNew = (LaxityActivity)iActivityNew; - LaxityActivity activityOld = AssociatedActivity; - - - if (activityOld == activityNew) - return; - - bool iflag = Processor.DisableInterrupts(); - - if (activityOld != null) { - //if (State != LaxityThread.ThreadState.ThreadWaiting) - if (enclosingThread.ThreadState == System.Threading.ThreadState.Running) - LaxityScheduler.DequeueRunThread(this); - LaxityScheduler.ActivityReleaseProtected(activityOld); - } - - if (activityNew != null) { - //if (State != LaxityThread.ThreadState.ThreadWaiting) - if (enclosingThread.ThreadState == System.Threading.ThreadState.Running) { - AssociatedActivity = activityNew; - LaxityScheduler.EnqueueRunThread(this, false); - } - LaxityScheduler.ActivityObjAddRef(activityNew); - } - else if (this == LaxityScheduler.GetCurrentThread()) { - //TODO: Double Check! //State == LaxityThread.ThreadState.ThreadRunning) - if (this == LaxityScheduler.GetCurrentThread() && activityOld != null) { - LaxityScheduler.UpdateSchedulingStatus(); - } - } - AssociatedActivity = activityNew; - DebugStub.Print("Exiting LaxityThread.SetActivity()\n"); - Processor.RestoreInterrupts(iflag); - } - - //public interface - public override void Start() - { - //TODO: No need for assertion perhaps -- default somehow? - Debug.Assert(AssociatedActivity != null); - bool iflag = Processor.DisableInterrupts(); - IReady(); - Processor.RestoreInterrupts(iflag); - } - - //public interface - public override void Cleanup() - { - OneShotReservation reservation; - DebugStub.Print("Cleaning LaxityThread\n"); - - SetStateWaiting(DateTime.MaxValue); - DebugStub.Print("At this point thread is dequeued.\n"); - - while ((reservation = ReservationStack) != null) { - ReservationStack = reservation.Next; // !!!! don't remove Caller's constraints !!! - reservation.DequeueReservation(); - } - - LaxityScheduler.ActivityObjRelease(AssociatedActivity); - - //TODO: In the "real" system, cleanup is called by someone who will pick next thread, right? - } - -#endregion - - public static DateTime GetSleepTimeout() - { - if (SleepingThreads != null) { - return SleepTimeout; - } - else { - return DateTime.MaxValue; - } - } - - public void AddRef() - { - ReferenceCount++; - } - - // DI -- instead of calling this, one should call ::ReleaseActivity - public void Release() - { - ReferenceCount--; - Debug.Assert(ReferenceCount >= 0); - } - - public void ReleaseProtected() - { - ReferenceCount--; - Debug.Assert(ReferenceCount >= 0); - } - - public override void SetStateWaiting(DateTime timeOut) - { - bool iflag = Processor.DisableInterrupts(); - InternalSetStateWaiting(timeOut); - Processor.RestoreInterrupts(iflag); - } - - public void InternalSetStateWaiting(DateTime timeOut) - { - Debug.Assert(Processor.InterruptsDisabled()); - // DI -- don't need activity state update - //This to handle sleeps and timed waits. - if (timeOut != DateTime.MaxValue) { - if (timeOut - SystemClock.GetKernelTime() > LaxityScheduler.MinSlice) { - Scheduler.LogSleepAdd(); - StartTime = timeOut; - - PutOnSleepQueue(); - } - else { - //Sleeping for less than MinSlice is treated as not sleeping/yield. - if (PendingReservation != null) { - ResolvePendingReservation(); - } - return; - } - } - - LaxityScheduler.DequeueRunThread(this); // from RunnableThreads - } - - - // DI -- this is the function in !LAXITY version - //TODO: REPLACE WITH ENCLOSING LOGIC! - //public void SetState(ThreadState newState) - //{ - // State = newState; - // Debug.Assert(newState != ThreadState.ThreadWaiting); - //} - - // DI -- safe - //TODO: REPLACE WITH ENCLOSING LOGIC! - void IReady() - { - Debug.Assert(Processor.InterruptsDisabled()); - // TODO -- maybe something for directed context switch - // DI -- don't need PutThreadOnReadyQueue outside schedule.c - // PutThreadOnReadyQueue(thread); - - LaxityScheduler.EnqueueRunThread(this, true); - } - - // DI -- this is called only from PutThreadOnSleepQueue - // DI -- merge them ?? - static ListNode InsertThreadOnSleepQueue(LaxityThread thread, ListNode listThreadHead) - { - LaxityThread threadHead, threadTail, tempThread; - - if (listThreadHead == null) { - // Queue empty. - return thread.SleepQueue; - } - - threadHead = GetThreadFromListNode(listThreadHead); - threadTail = GetThreadFromListNode(threadHead.SleepQueue.Previous); - - if (thread.StartTime >= threadTail.StartTime) { - thread.SleepQueue.InsertIntoList(threadTail.SleepQueue); - // DI -- if merge: update SleepTimeout here - return threadHead.SleepQueue; - } - - - if (thread.StartTime < threadHead.StartTime) { - thread.SleepQueue.InsertIntoList(threadTail.SleepQueue); - return thread.SleepQueue; - } - - tempThread = GetThreadFromListNode(threadHead.SleepQueue.Next); - for (;;) { - if (tempThread.StartTime > thread.StartTime) { - thread.SleepQueue.InsertIntoList(tempThread.SleepQueue.Previous); - break; - } - tempThread = GetThreadFromListNode(tempThread.SleepQueue.Next); - } - - return threadHead.SleepQueue; - } - - public void PutOnSleepQueue() - { - SleepingThreads = InsertThreadOnSleepQueue(this,SleepingThreads); - SleepTimeout = (GetThreadFromListNode(SleepingThreads)).StartTime; - } - - // public void Sleep(DateTime time) - // { - // if (time - SystemClock.GetKernelTime() > Node.MinSlice) - // { - // - // Scheduler.LogSleepAdd(); - // StartTime = time; - // - // SetStateWaiting(); - // - // // DI -- need it, called from WaitCond also - // PutOnSleepQueue(); - // } - // } - - // Di -- safe -- optimized - void PullThreadOffSleepQueue() - { - ListNode listThread, listNewHead; - - listThread = SleepQueue; - listNewHead = listThread.ListRemove(); - - if (SleepingThreads == listThread) { - if ((SleepingThreads = listNewHead) != null) { - SleepTimeout =(GetThreadFromListNode(SleepingThreads)).StartTime; - } - else { - SleepTimeout = DateTime.MaxValue; - } - } - } - - private void ResolvePendingReservation() - { - Debug.Assert(Processor.InterruptsDisabled()); - Debug.Assert(PendingReservation != null); - bool admitted = OneShotReservation.ResolveConstraint(this); - if (ReservationStack.EnclosingTask != null) { - // Not clear if this will work in real system or not. Here for simulator mainly. - ReservationStack.EnclosingTask.UpdateSchedulingState(admitted, ReservationStack.Deadline, ReservationStack.ResourcesGranted); - } - } - - // DI -- safe - public override void Wake() - { - bool iflag = Processor.DisableInterrupts(); - PullThreadOffSleepQueue(); - IReady(); - - if (PendingReservation != null) { - ResolvePendingReservation(); - } - - Processor.RestoreInterrupts(iflag); - } - - - OneShotReservation AllocationReservation() - { - OneShotReservation reservation = FreeReservation; - - Debug.Assert(reservation != null); - FreeReservation = null; - reservation.Clear(); - return reservation; - } - - - public void IpcCheckFreeConstraint() - { - if (FreeReservation == null) { - OneShotReservation reservation; - - reservation = LaxityScheduler.IpcAllocateReservation(); - - if (reservation == null) { - reservation = LaxityScheduler.AllocateReservation(); - } - - FreeReservation = reservation; - } - } - - // If this thread doesn't have a free OneShotReservation reserved - // for its use, then try to grab one from the global free list. - // If there aren't any there, then allocate a new one. - void ICheckFreeConstraint() - { - if (FreeReservation == null) { - FreeReservation = LaxityScheduler.AllocateReservation(); - } - } - - - // Free the Reservations associated with a thread. - // Used by thread cleanup. - public void IFreeConstraints() - { - OneShotReservation reservation; - - // Debug.Assert(!Processor.InterruptsDisabled()); - - if (FreeReservation != null) { - LaxityScheduler.IpcFreeReservation(FreeReservation); - } - while ((reservation = ReservationStack) != null) { - ReservationStack = reservation.SurroundingReservation; - LaxityScheduler.ReleaseReservation(reservation); - } - } - - // DI -- safe but optimize to because don't need QueueType - public ListNode Dequeue() - { - return Queue.ListRemove(); - } - - public static ListNode QueueFifo(LaxityThread thread, ListNode threadHead) - { - if (threadHead != null) { - thread.Queue.InsertIntoList(threadHead.Previous); - } - else { - threadHead = thread.Queue; - } - return threadHead; - } - - // DI -- safe - public static LaxityThread GetThreadFromListNode(ListNode listNode) - { - Debug.Assert(listNode == null || listNode.Data is LaxityThread); - if (listNode == null) { - return null; - } - if (listNode.Data is LaxityThread) { - return (LaxityThread)listNode.Data; - } - else { - return null; - } - } - - public static void WakeThreads() - { - LaxityThread ptemp; - Debug.Assert(Processor.InterruptsDisabled()); - while ((ptemp = LaxityThread.GetThreadFromListNode(SleepingThreads)) != null && - ptemp.StartTime < LaxityScheduler.SchedulingTime + LaxityScheduler.LA_Time) { - //TODO: LA_Time here? - SleepingThreads = SleepingThreads.ListRemove(); - - ptemp.IReady(); - - if (ptemp.PendingReservation != null) { - ptemp.ResolvePendingReservation(); - } - Scheduler.LogWakeThread(ptemp.EnclosingThread); - } - - if (ptemp != null) { - SleepTimeout = ptemp.StartTime; - } - else { - SleepTimeout = DateTime.MaxValue; - } - } - - - /// - /// - /// - /// - /// - /// - /// TODO: UNUSED - /// - public bool BeginConstraintBeforeWaitValidate(bool endPrevious, - ref TimeConstraint timeConstraint, - DateTime timeNow) - { - if (endPrevious && // check to have a valid constraint in the stack. - (ReservationStack == null || ReservationStack.OriginalThread == null)) { - return false; - } - - if (timeConstraint.Estimate.Ticks < 0) { - return false; - } - - // By this time, something should be in FreeReservation - // grab it now such to enable the EndPreviousConstraint to - // save its reservation in the cache - Debug.Assert(FreeReservation != null); - Debug.Assert(FreeReservation != OneShotReservation.CurrentReservation); - - PendingReservation = AllocationReservation(); - - return true; - } - - public void AddExecutionTime(TimeSpan delta) - { - executionTime += delta; - } - - public override ISchedulerTask PrepareDelayedTask(ISchedulerTask taskToEnd, - ref TimeConstraint timeConstraint, - DateTime timeNow) - { - // DI -- the Next calls are from TimedWaitAndBeginConstraint - IpcCheckFreeConstraint(); - - Debug.Assert(taskToEnd == null || taskToEnd == ReservationStack); - bool endPrevious = (taskToEnd != null); - - if (!BeginConstraintBeforeWaitValidate(endPrevious, ref timeConstraint, timeNow)) { - goto Exit; - } - OneShotReservation.BeginConstraintBeforeWait(this, endPrevious, timeConstraint, timeNow); - Exit: - return PendingReservation; - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Laxity/OneShotReservation.cs b/base/Kernel/Singularity/Scheduling/Laxity/OneShotReservation.cs deleted file mode 100644 index 8a14c24..0000000 --- a/base/Kernel/Singularity/Scheduling/Laxity/OneShotReservation.cs +++ /dev/null @@ -1,768 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: OneShotReservation.cs -// -// Note: -// - -using System; -using System.Collections; -using System.Diagnostics; -using System.Threading; -using Microsoft.Singularity; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling.Laxity -{ - /// - /// OneShotReservations are created from tasks for the calling thread. - /// It has fields to track the thread calling and the thread waiting on, - /// the activity associated when it becomes an activity reservation, its - /// surrounding reservation, its place on the free reservation list, - /// general bookkeeping, bookkeeping for possible stolen time, and - /// references for the general reservation list. - /// - public class OneShotReservation : ISchedulerTask - { - // override the global LaxityQueueType enum with our own - public enum LaxityQueueType - { - NoQueue = 5, - GuaranteedQueue = 1, - // NonGuaranteedQueue = 2, - IdleQueue = 3, - // UnfinishedQueue = 4 - }; - - // heap keyed on laxity holding all guaranteed one-shot reservations - // that currently have at least one runnable thread. - private static Heap guaranteedReservations = new Heap(); - public static OneShotReservation TopGuaranteedReservation - { - get { return (OneShotReservation)guaranteedReservations.Min; } - } - - static OneShotReservation idleReservations = null; - public static OneShotReservation IdleReservations - { - get { return idleReservations; } - } - - static OneShotReservation currentReservation = null; - public static OneShotReservation CurrentReservation - { - get { return currentReservation; } - //set { currentReservation = value; } - } - - public Hashtable resourcesGranted = new Hashtable(); - public override Hashtable ResourcesGranted - { - get - { - resourcesGranted[CpuResource.Provider().ResourceString] = CpuResource.Provider().TimeToCpu(InitialEstimate); - return resourcesGranted; - } - } - - - private Task enclosingTask; - public override Task EnclosingTask - { - get { return enclosingTask; } - set { enclosingTask = value; } - } - - public OneShotReservation SurroundingReservation; - public OneShotReservation FreeListNext; - public LaxityThread ReservTask; - - TimeSpan constraintExecution; - public TimeSpan ConstraintExecution - { - get { return constraintExecution; } - } - - TimeSpan initialThreadExecution; - - public DateTime Start, Deadline; - public TimeSpan Estimate, RelativeDeadline; - - public DateTime Laxity - { - // dnarayan - // 1. This is actually (laxity + timeNow). It has the same effect as laxity - // for comparing across tasks at any given instant, and the additional - // advantage that it doesn't change over time, only when resources are consumed. - // Another invariant is that with this definition, "laxity" of an individual task - // never increases. - - // 2. The assumption is that Estimate always holds (in time units) the total - // remaining amount of resource (i.e. reserved - used). Currently this is just - // the CPU-remaining estimate - - // 3. If we use more than our allocation, our laxity starts increasing, i.e. - // we get punished. This is a feature. However, it's still possible that we - // have overused one resource but have low laxity due to being behind on another. - // We might want to punish tasks for that as well? - - get { return Deadline - Estimate; } - } - - public TimeSpan InitialEstimate; // test only - public LaxityQueueType MyLaxityQueueType; - public bool Guaranteed; - public bool Valid; - public bool Satisfied; - - // Reservations can be made for Threads XOR Activities! - public LaxityThread OriginalThread; // always the constraint thread -- non-null until the reservation is complete - //public LaxityThread ActiveThread; // the above, or the thread blocking it - //public int ActiveThreadEpoch; - public LaxityActivity AssociatedActivity; // the two fields above must be null! - - - public OneShotReservation Next; - public OneShotReservation Previous; - - public TimeSpan InheritedEstimate; // Stolen => Inherited, no longer critical - public int ResolutionEpoch; // Stolen => Resolution, since not stealing. - - public int ReferenceCount; - - public OneShotReservation() - { - } - - public void Clear() - { - //ActiveThread = null; - //ActiveThreadEpoch = 0; - constraintExecution = new TimeSpan(0); //Move to enclosing class - initialThreadExecution = new TimeSpan(0); //Move to enclosing class - Deadline = new DateTime(0); - RelativeDeadline = new TimeSpan(0); - Estimate = new TimeSpan(0); - FreeListNext = null; - Guaranteed = false; - InitialEstimate = new TimeSpan(0); - Next = null; - OriginalThread = null; - AssociatedActivity = null; - Previous = null; - MyLaxityQueueType = LaxityQueueType.NoQueue; - ReferenceCount = 0; - ReservTask = null; - Satisfied = false; - Start = new DateTime(0); - SurroundingReservation = null; - ResolutionEpoch = 0; - InheritedEstimate = new TimeSpan(0); - Valid = false; - if (enclosingTask != null) { - enclosingTask.ClearSchedulerTask(); - enclosingTask = null; - } - - } - - // find the runnable thread of a reservation: - public LaxityThread GetRunnableThread() - { -// LaxityThread thread; -// - if (AssociatedActivity != null) { - // if an activity reservation - return AssociatedActivity.GetRunnableThread(); - } - - Debug.Assert(OriginalThread != null); - -// if ( (ActiveThread == OriginalThread) && -// (ActiveThread.EnclosingThread.ThreadState == System.Threading.ThreadState.Running)) -// return ActiveThread; -// -// thread = OriginalThread; // search for active thread starting w/ original thread -// -// if ((ActiveThread != null) && -// (ActiveThread.Epoch == ActiveThreadEpoch) ) { -// if (ActiveThread.EnclosingThread.ThreadState == System.Threading.ThreadState.Running) { -//#if DEBUG_MUTEX -// DebugStub.WriteLine("Mutex inheritance (Epoch ok): Orig {0} Active {1} GO ON", -// thread.Id, ActiveThread.Id); -//#endif -// return ActiveThread; -// } -// } - //CKillian -- can't do this because of the array of blockedOn. - // else if (ActiveThread.EnclosingThread.IsSleeping()) { - // // more efficient: search for active thread starting w/ current active thread - // thread = ActiveThread; - // } - // else { - // Debug.Assert(false); - // } - //} - - //A "smarter" thing to do would be to have GetRunnableBeneficiary check for - // the ActiveProcessor case. But as it stands that's info in the scheduler only - // the "good" news is that if we return null based on the active processor case - // we at least know someone is current running on a processor who would inherit - // time from us. - Thread temp = /*Active*/OriginalThread.EnclosingThread.GetRunnableBeneficiary(); - if (temp == null || ((LaxityThread)temp.SchedulerThread).ActiveProcessor != null) { - return null; - } - else { - return (LaxityThread)temp.SchedulerThread; - } - } - - public void StopThreadExecution(LaxityThread thread) - { - Debug.Assert(thread.ExecutionTime >= initialThreadExecution); - constraintExecution += thread.ExecutionTime - initialThreadExecution; - //Between two reservations which are atomically stopped/started, some - // time is charged against the default parent resource container. This - // is because I changed the way we account for tasks to do the general - // rule we wanted. In particular, I update the scheduling status just - // before resolving a new reservation. Not sure what the right fix is, - // so I'm leaving it. The assertion below will reveal the "error." The - // old code to track CPU usage is still active under constraintExecution, - // but isn't presently exposed to the applications. Currently UpdateSchedulingStatus() - // updates the CPU resources used for the whole task stack. - //Debug.Assert(CpuResource.Provider().TimeToCpu(constraintExecution).Cycles == ((CpuResourceAmount)enclosingTask.resourcesUsed[CpuResource.Provider().ResourceString]).Cycles); - //EnclosingTask.AddResourceAmountUsed(CpuResource.Provider().ResourceString, CpuResource.Provider().TimeToCpu(constraintExecution)); - } - - public void StartThreadExecution(LaxityThread thread) - { - initialThreadExecution = thread.ExecutionTime; - } - - public void StartThreadExecution(LaxityThread thread, TimeSpan delta) - { - initialThreadExecution = thread.ExecutionTime + delta; - } - - public static void BeginConstraintBeforeWait(LaxityThread thread, bool endPrevious, - TimeConstraint timeConstraint, DateTime timeNow) - { - OneShotReservation reservation; - - Debug.Assert(thread == LaxityScheduler.GetCurrentThread()); - Debug.Assert(!Processor.InterruptsDisabled()); - if (endPrevious) { - bool iflag = Processor.DisableInterrupts(); - EndPreviousConstraint(thread, timeNow); - Processor.RestoreInterrupts(iflag); - } - - LaxityScheduler.UpdateSchedulingStatus(); - - reservation = thread.PendingReservation; - - reservation.StartThreadExecution(thread, (timeNow - LaxityScheduler.SchedulingTime)); - reservation.constraintExecution = new TimeSpan(0); - reservation.Start = timeConstraint.Start; - reservation.Deadline = timeConstraint.Deadline; - reservation.RelativeDeadline = timeConstraint.RelativeDeadline; - reservation.Estimate = timeConstraint.Estimate; - reservation.Valid = reservation.Guaranteed = true; - reservation.MyLaxityQueueType = LaxityQueueType.NoQueue; - reservation.ReservTask = thread; - reservation.OriginalThread = /*reservation.ActiveThread =*/ thread; - thread.AddRef(); - //thread.AddRef(); - //reservation.ActiveThreadEpoch = thread.Epoch; - reservation.AssociatedActivity = null; - reservation.Next = reservation.Previous = null; - - } - - // DI -- this is a new function used in both Begin and EndConstraint - public static bool EndPreviousConstraint(LaxityThread thread, DateTime timeNow) - { - OneShotReservation reservation, reservationNext; - bool success; - LaxityQueueType tempQueue; //Value assigned but never used. - - Debug.Assert(Processor.InterruptsDisabled()); - - reservation = thread.ReservationStack; - - success = (timeNow <= reservation.Deadline); - LaxityScheduler.UpdateSchedulingStatus(); - reservation.StopThreadExecution(thread); - - Debug.Assert(reservation.ConstraintExecution.Ticks >= 0); -// if (timeTaken.Ticks != 0) { -// timeTaken = reservation.ConstraintExecution; -// Debug.Assert( timeTaken.Ticks >= 0); -// } - - thread.ReservationStack = reservation.SurroundingReservation; - reservation.SurroundingReservation = null; - - if (currentReservation == reservation) { - // note that it might be the case that - // a reservation for the task id exist on the guaranteed Q but the - // the actual (executed) constraint is in the UnfinishedQ - currentReservation = null; // ???????????? - LaxityScheduler.Reschedule(); - } - Debug.Assert(reservation.OriginalThread == thread); - Debug.Assert(reservation.Start <= timeNow); - - if (thread.ReservationStack == null) { - if (reservation.Estimate >= LaxityScheduler.MinSlice) { - // the constraint used less than estimated: - Debug.Assert(reservation.MyLaxityQueueType == LaxityQueueType.GuaranteedQueue); -#if false - Debug.Assert((reservation.ActiveThread == null) || - (reservation.ActiveThread.Epoch != reservation.ActiveThreadEpoch) || - (reservation.ActiveThread == reservation.OriginalThread)); - reservation.ActiveThread.ReleaseProtected(); -#endif - reservation.OriginalThread.ReleaseProtected(); - reservation.AssociatedActivity = reservation.OriginalThread.AssociatedActivity; // and make it an activity reserv - reservation.OriginalThread = null; - //reservation.ActiveThread = null; - reservation.ReservTask = null; - LaxityScheduler.ActivityObjAddRef(reservation.AssociatedActivity); - // leave it on whatever Q it happens to be on - } - else { - reservation.DequeueReservation(); // from whatever Q it happens to be on - } - } - else { - tempQueue = reservation.MyLaxityQueueType; - reservationNext = thread.ReservationStack; - reservationNext.Estimate += reservation.Estimate; - reservationNext.constraintExecution += reservation.constraintExecution; - reservationNext.StartThreadExecution(thread); -// Debug.Assert( ((reservation.MyLaxityQueueType == LaxityQueueType.GuaranteedQueue) && -// (reservationNext.MyLaxityQueueType == LaxityQueueType.NoQueue)) || -// ((reservation.MyLaxityQueueType == LaxityQueueType.UnfinishedQueue) && -// ((reservationNext.Estimate.Ticks <= 0) || (reservationNext.MyLaxityQueueType != LaxityQueueType.UnfinishedQueue )))); - reservation.Estimate = new TimeSpan(0); - Debug.Assert(reservation.Next != null); - reservation.DequeueReservation(); - ReplaceOnLaxityHeap(reservationNext, timeNow); - - } - // fprintf(stdout,"EndPrevConstr 0x%x %d\n", reservation, reservation.ReferenceCount); - reservation.ReleaseReservationProtected(); - - Scheduler.LogEndConstraint(); - - if (success == false) { - Scheduler.LogSchedulerLate(); - } - return success; - } - - - public static bool ResolveConstraint(LaxityThread thread) - { - TimeSpan timeLeft, ownNodesTimeToSteal = new TimeSpan(1); - DateTime start, deadline; - TimeSpan timeInherited; - OneShotReservation pendingReservation = thread.PendingReservation, reservationPrevious; - DateTime timeNow = SystemClock.GetKernelTime(); - bool ok = false; - - Debug.Assert(Processor.InterruptsDisabled()); - Debug.Assert(pendingReservation != null); - - Scheduler.LogResolveConstraint(); - - thread.PendingReservation = null; // just clean the place - - Debug.Assert(pendingReservation.Start.Ticks >= 0); - start = pendingReservation.Start; - if (start < timeNow) { - start = timeNow; - } - Debug.Assert(pendingReservation.Deadline.Ticks >= 0); - deadline = pendingReservation.Deadline; - if (deadline.Ticks == 0) { - deadline = timeNow + pendingReservation.RelativeDeadline; - } - - if (thread.ReservationStack != null) { - OneShotReservation reservation = thread.ReservationStack; - while (!reservation.Valid && ((reservation = reservation.SurroundingReservation)!= null)) { - // no body. - } - - if (reservation!= null) { - deadline = LaxityScheduler.minTime(deadline, reservation.Deadline); - } - } - - // update data ure - pendingReservation.Start = start; - pendingReservation.Deadline = deadline; - //TODO: Should be for SIM ONLY! - Scheduler.LogReservationId(pendingReservation); // SIM only - timeInherited = new TimeSpan(0); - timeLeft = new TimeSpan(0); - ok = true; - pendingReservation.InitialEstimate = pendingReservation.Estimate; - reservationPrevious = thread.ReservationStack; - pendingReservation.SurroundingReservation= thread.ReservationStack; - thread.ReservationStack = pendingReservation; - LaxityScheduler.AddRefReservation(pendingReservation); - if (reservationPrevious != null) { - reservationPrevious.constraintExecution += pendingReservation.initialThreadExecution - - reservationPrevious.initialThreadExecution; - } - // - // Reschedule() called if currently we are executing on a pendingReservation - // and the EarliestDeadlineFirst top changed - // or the current pendingReservation goes into the idle list - // solve in SetStateWaiting - // - pendingReservation.EnqueueReservation(timeNow); - - return ok; - } - - void EnqueueReservation(DateTime timeNow) - { - OneShotReservation temp; - - Debug.Assert((AssociatedActivity != null) || - (OriginalThread != null)); - - // dnarayan: for simplicity, we schedule all tasks using global - // least-laxity, nothing out of individual resource containers. -#if false - // Infeasible or (Estimate == 0) Reservations: - if (Estimate <= LaxityScheduler.AFewSlice) { - LaxityActivity activity; - Debug.Assert(OriginalThread != null); - Debug.Assert(AssociatedActivity == null); - activity = OriginalThread.AssociatedActivity; - MyLaxityQueueType = LaxityQueueType.UnfinishedQueue; - if (activity.UnfinishedConstraints == null) { - Next = Previous = this; - activity.UnfinishedConstraints = this; - } - else { - // no order enforced - Next = activity.UnfinishedConstraints; - Previous = activity.UnfinishedConstraints.Previous; - activity.UnfinishedConstraints.Previous.Next = this; - activity.UnfinishedConstraints.Previous = this; - activity.UnfinishedConstraints = this; // optional - } - // DebugStub.WriteLine("Add: EnqueueReserve 1 0x{0:x} {1}", this, ReferenceCount); - LaxityScheduler.AddRefReservation(this); - return; - } - // Debug.Assert(Valid); -#endif - // Idle Reservations: - if (Start > timeNow + LaxityScheduler.AFewSlice) { - // take thread off activity Laxity queue and simulate a sleep - // i.e. don't set a timer interrupt - - OriginalThread.InternalSetStateWaiting(DateTime.MaxValue); - -#if DEBUG_START_RESERV - DebugStub.WriteLine("Put reservation {0}:{1} in IdleQueue\n", OriginalThread, - ReservationId); -#endif - Debug.Assert(OriginalThread != null); - MyLaxityQueueType = LaxityQueueType.IdleQueue; - - if (idleReservations == null) { - Next = Previous = this; - idleReservations = this; - } - else { - temp = idleReservations; - if (Start < temp.Start) { - idleReservations = this; - } - else { - do { - temp = temp.Next; - } while (temp != idleReservations && - Start >= temp.Start); // >= is compulsory to keep it 'FIFO' - } // insert before 'temp' - Next = temp; - Previous = temp.Previous; - temp.Previous.Next = this; - temp.Previous = this; - } - // fprintf(stdout,"Add: EnqueueReserve 2 0x%x %d\n", this, ReferenceCount); - LaxityScheduler.AddRefReservation(this); - return; - } - - // Guaranteed OneShotReservation: - MyLaxityQueueType = LaxityQueueType.GuaranteedQueue; - if (GetRunnableThread() != null) { - Debug.Assert(guaranteedReservations.Insert(this, Laxity)); - } - - LaxityScheduler.AddRefReservation(this); -#if DEBUG_RESERV - PrintQueueReserv(guaranteedReservations, timeNow); //TODO: PrintQueueReserv -#endif - return; - } - - // XXX dnarayan this is a replacement for ReplaceOnEarliestDeadlineFirst, but I - // don't really understand what it's supposed to do, so for now it does nothing (!) - - static void ReplaceOnLaxityHeap(OneShotReservation reservation, DateTime timeNow) - { -#if false - while (reservation != null) { - if (reservation.Estimate.Ticks > 0) { - if (reservation.MyLaxityQueueType == LaxityQueueType.NoQueue) { - reservation.EnqueueReservation(timeNow); - } - else { - Debug.Assert(reservation.MyLaxityQueueType == LaxityQueueType.GuaranteedQueue); - } - break; - } - Debug.Assert(reservation.MyLaxityQueueType == LaxityQueueType.NoQueue); - reservation.EnqueueReservation(timeNow); - - reservation = reservation.SurroundingReservation; - } -#endif - } - -#if false - public static bool SatisfyAcceptedConstraint(DateTime timeNow) - { - OneShotReservation reservation; - - if (currentReservation != null) { - currentReservation.Estimate -= timeNow - LaxityScheduler.SchedulingTime; - if (currentReservation.Estimate.Ticks <= 0) { - LaxityThread tempThread = currentReservation.OriginalThread; - currentReservation.DequeueReservation(); - if (tempThread != null) { - // activity reservations vanish here - //Debug.Assert(currentReservation.ActiveThread == LaxityScheduler.GetCurrentThread()); // only thread reserv are added to - currentReservation.EnqueueReservation(LaxityScheduler.SchedulingTime); // the UnfinishedConstraints list of the OriginalThread's Activ - OneShotReservation.ReplaceOnEarliestDeadlineFirst(currentReservation.SurroundingReservation, LaxityScheduler.SchedulingTime); - } - } - } - - reservation = guaranteedReservations; - while (reservation != null) { - reservation = reservation.Next; - if (reservation == guaranteedReservations) { - break; - } - } - reservation = idleReservations; - while (reservation != null) { - reservation = reservation.Next; - if (reservation == idleReservations) { - break; - } - } - return true; - } -#endif - - public static void UpdateCurrentReservation() - { - if ((currentReservation != null) && - (currentReservation.Estimate <= LaxityScheduler.AFewSlice) ) { - // slice used for a reservation (CK: & estimate now empty ) - LaxityThread tempThread = currentReservation.OriginalThread; - currentReservation.DequeueReservation(); - if (tempThread != null) { - // activity reservations vanish here - currentReservation.EnqueueReservation(LaxityScheduler.SchedulingTime); // the UnfinishedConstraints list of the OriginalThread's Activ - ReplaceOnLaxityHeap(currentReservation.SurroundingReservation, LaxityScheduler.SchedulingTime); - } - } - - } - - public static void FreshenReservationQueues() - { - OneShotReservation tempReservation; - - // Move 'idle' reservations to the 'active' reservations list, if necessary. - // A reservation is 'idle' until its Start time is reached. - // The 'active' reservation list is ordered 'Earliest Deadline Firts'. - // The 'idle' queue is ordered Earliest Start First. - while ((tempReservation = IdleReservations) != null) { - if (LaxityScheduler.SchedulingTime + LaxityScheduler.AFewSlice < tempReservation.Start) { - break; - } - tempReservation.DequeueReservation(); // Remove OneShotReservation from the Idle Queue - tempReservation.EnqueueReservation(LaxityScheduler.SchedulingTime); // Earliest Deadline First in the (Non)Guaranteed Queue - LaxityScheduler.EnqueueRunThread(tempReservation.OriginalThread, true); -#if DEBUG_START_RESERV - DebugStub.WriteLine("Get reservation {0}:{1} from IdleQueue", tempReservation.OriginalThread, - tempReservation.ReservationId); -#endif - - } - - // dnarayan: we don't remove deadline-passed tasks from the guaranteed queue, - // however they should get penalized by having increasing laxity. We might want - // to reconsider this decision. -#if false - // Remove the active reservations from the guaranteed and nonguaranteed queues - // if their Deadline is <= CurrentTime. - while ((tempReservation = guaranteedReservations) != null) { - if (LaxityScheduler.SchedulingTime + LaxityScheduler.AFewSlice < tempReservation.Deadline) { - break; - } - // fprintf(stdout,"Add: Reschedule Guaranteed Deadline 0x%x %d\n", tempReservation, tempReservation.ReferenceCount); - LaxityScheduler.AddRefReservation(tempReservation); - tempReservation.DequeueReservation(); // Remove OneShotReservation from the Guaranteed Queue - if ((tempReservation.OriginalThread != null) && // if a thread reservation - (tempReservation.OriginalThread.AssociatedActivity != null)) { - // If a thread reservation. - tempReservation.Estimate = new TimeSpan(0); - tempReservation.EnqueueReservation(LaxityScheduler.SchedulingTime); // add it to the Pending End Constraint List - } - // fprintf(stdout,"Reschedule Guaranteed Deadline off 0x%x %d\n", tempReservation, tempReservation.ReferenceCount); - tempReservation.ReleaseReservationProtected(); - } -#endif - } - - int ReleaseReservationProtected() - { - return LaxityScheduler.ReleaseReservationProtected(this); - } - - public void DequeueReservation() //TODO: Should this be a resource container function? - { - Debug.Assert(Processor.InterruptsDisabled()); - Debug.Assert((AssociatedActivity != null) || - (OriginalThread != null)); - - if (MyLaxityQueueType == LaxityQueueType.GuaranteedQueue) { - Debug.Assert(guaranteedReservations.Delete(this)); - } - else { - Debug.Assert(MyLaxityQueueType == LaxityQueueType.IdleQueue); - - if (Next != this) { - //If there's another reservation, remove us from the queue - Next.Previous = Previous; - Previous.Next = Next; - Debug.Assert(Next.Deadline.Ticks > 0); - } - else { - //Otherwise we are only reservation. - Debug.Assert(Previous == this); - Next = null; - } - - if (this == IdleReservations) { - // if we were head of the queue - idleReservations = Next; - } - - Next = Previous = null; - // DebugStub.WriteLine("DequeueReservation 0x{0:x} {1}", this, ReferenceCount); - MyLaxityQueueType = LaxityQueueType.NoQueue; - ReleaseReservationProtected(); - } - } - - public static void FindRunnableReservation(ref LaxityThread currentThread) - { - // dnarayan: this is a bit hokey. What we really want is to maintain the - // invariant that the heap only contains tasks with runnable threads. However - // this involves hooking all the places where threads get added/removed as - // task beneficiaries. - - ArrayList nonRunnableTasks = new ArrayList(); - currentThread = null; - while (((currentReservation = (OneShotReservation) guaranteedReservations.Min) - != null) && (currentThread == null)) { - currentThread = currentReservation.GetRunnableThread(); - if (currentThread == null) { - Debug.Assert(guaranteedReservations.Delete(currentReservation)); - nonRunnableTasks.Add(currentReservation); - } - } - - // Now put all the non-runnable tasks back in the heap (!) - foreach (OneShotReservation resv in nonRunnableTasks) { - Debug.Assert(guaranteedReservations.Insert(resv, resv.Laxity)); - } - } - - // dnarayan: never called -#if false - public static void InheritAndReplaceOnEarliestDeadlineFirst(OneShotReservation reservation, - TimeSpan timeInherited, - DateTime timeNow) - { - OneShotReservation current = null; - - while (reservation != null) { - if (reservation.Estimate.Ticks > 0) { - if (current == null) { - Debug.Assert(reservation.MyLaxityQueueType != LaxityQueueType.NoQueue); - current = reservation; - } - else { - Debug.Assert(reservation.MyLaxityQueueType == LaxityQueueType.NoQueue); - } - } - else if (reservation.MyLaxityQueueType != LaxityQueueType.NoQueue) { - Debug.Assert (current != null || reservation.MyLaxityQueueType == LaxityQueueType.UnfinishedQueue); - reservation.DequeueReservation(); - } - Debug.Assert(timeInherited >= reservation.InheritedEstimate); - timeInherited -= reservation.InheritedEstimate; - Debug.Assert(reservation.Estimate >= reservation.InheritedEstimate); - reservation.Estimate -= reservation.InheritedEstimate; - reservation = reservation.SurroundingReservation; - } - - if (current != null) { - if (current == CurrentReservation) { - current.Estimate -= timeNow - LaxityScheduler.SchedulingTime; - currentReservation = null; - LaxityScheduler.Reschedule(); - } - current.DequeueReservation(); - } - } -#endif - public static void InheritOnEarliestDeadlineFirst(OneShotReservation reservation, TimeSpan timeToInherit) - { - while (reservation != null && timeToInherit.Ticks > 0) { - - Debug.Assert(timeToInherit >= reservation.InheritedEstimate); - reservation.Estimate -= reservation.InheritedEstimate; - timeToInherit -= reservation.InheritedEstimate; - reservation = reservation.SurroundingReservation; - } - } - - public static void ClearCurrentReservation() - { - currentReservation = null; - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Laxity/RecurringReservation.cs b/base/Kernel/Singularity/Scheduling/Laxity/RecurringReservation.cs deleted file mode 100644 index 3dd29e5..0000000 --- a/base/Kernel/Singularity/Scheduling/Laxity/RecurringReservation.cs +++ /dev/null @@ -1,51 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: RecurringReservation.cs -// -// Note: -// - -using System; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling.Laxity -{ - /// - /// Summary description for RecurringReservation. - /// - public class RecurringReservation : ISchedulerCpuReservation - { - CpuResourceReservation enclosingCpuReservation; - - public RecurringReservation() - { - } - -#region ISchedulerCpuReservation Members - - public override CpuResourceReservation EnclosingCpuReservation - { - get { return enclosingCpuReservation; } - set { enclosingCpuReservation = value; } - } - -#endregion - - public TimeSpan Slice; - public TimeSpan Period; - - public override CpuResourceAmount ReservedAmount - { - get { return CpuResource.Provider().TimeToCpu(Slice); } - } - - public override TimeSpan ReservedPeriod - { - get { return Period; } - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Min/MinScheduler.cs b/base/Kernel/Singularity/Scheduling/Min/MinScheduler.cs deleted file mode 100644 index c33f3ee..0000000 --- a/base/Kernel/Singularity/Scheduling/Min/MinScheduler.cs +++ /dev/null @@ -1,440 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: MinScheduler.cs -// -// Note: Minimal round-robin style without priorities scheduler. -// -// MinScheduler favors thread that have recently become unblocked -// and tries to avoid reading the clock or reseting the timer as -// much as possible. -// -// The minimal scheduler maintains two queues of threads that can -// be scheduled. The unblockedThreads queue contains threads which -// have become unblocked during this scheduling quantum; mostly, -// these are threads that were unblocked by the running thread. -// The runnableThreads queue contains all other threads that are -// currently runnable. If the current thread blocks, MinScheduler -// will schedule threads from the unblockedThread queue, without -// reseting the timer. When the timer finally fires, MinScheduler -// moves all unblockedThreads to the end of the runnableThreads -// queue and schedules the next runnableThread. -// - -//#define DEBUG_DISPATCH -//#define DEBUG_TIMER - -using System; -using System.Collections; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Threading; - -using Microsoft.Bartok.Options; - -using Microsoft.Singularity; -using Microsoft.Singularity.Io; -using Microsoft.Singularity.Scheduling; -using Microsoft.Singularity.V1.Threads; - -namespace Microsoft.Singularity.Scheduling -{ - /// - /// Summary description for MinScheduler. - /// - [NoCCtor] - [CLSCompliant(false)] - [Mixin(typeof(Scheduler))] - public class MinScheduler : Scheduler - { - // List of recently runnable, but unscheduled threads. - private static ThreadQueue unblockedThreads; - - // List of runnable, but unscheduled threads. - private static ThreadQueue runnableThreads; - - // List of blocked threads, sorted by wait time. - private static ThreadQueue blockedThreads; - - // List of frozen threads (unsorted) - private static ThreadQueue frozenThreads; - - // set to something large to debug! - private static TimeSpan minSlice; - private static TimeSpan idleSlice; - - ////////////////////////////////////////////////////////////////////// - // - [MixinExtend("Initialize")] - public static void Initialize(Process idleProcess) { - Scheduler.Initialize(); - - unblockedThreads = new ThreadQueue(); - runnableThreads = new ThreadQueue(); - blockedThreads = new ThreadQueue(); - frozenThreads = new ThreadQueue(); - - minSlice = TimeSpan.FromMilliseconds(10); - // If busy, don't run for more than 10ms on same task. - idleSlice = TimeSpan.FromDays(30); - // If idle, wake up once a month whether we need to or not. - - // Create the idle threads and put them on the idleThreads loop. - for (int i = 0; i < Processor.processorTable.Length; i++) { - Thread idle = Thread.CreateIdleThread(Processor.processorTable[i]); - Processor.processorTable[i].IdleThread = idle; - - ThreadHandle handle; - idleProcess.AddThread(idle,out handle); - - DispatchLock(); - idleThreads.EnqueueTail(idle.schedulerEntry); - DispatchRelease(); - } - - bool iflag = Processor.DisableInterrupts(); - Processor.CurrentProcessor.SetNextTimerInterrupt( - SchedulerTime.MinValue + TimeSpan.FromMilliseconds(5) - ); - Processor.RestoreInterrupts(iflag); - } - - [MixinExtend("Finalize")] - new public static void Finalize() - { - Scheduler.Finalize(); - } - - /////////////////////////////////////////// Scheduling Event Handlers. - // - // Each of these should be replaced by the scheduler mixin. - // - - [MixinOverride] - new public static void OnThreadStateInitialize(Thread thread, bool constructorCalled) - { - // Only initialize thread-local state! No locks held and interrupts on. - } - - [MixinOverride] - new public static void OnThreadStart(Thread thread) - { - Scheduler.AssertDispatchLockHeld(); - DebugStub.Assert(thread.freezeCount == 0); - EnqueueThreadToRun(unblockedThreads, thread); - } - - [MixinOverride] - new public static Thread OnThreadBlocked(Thread thread, SchedulerTime stop) - { - Scheduler.AssertDispatchLockHeld(); - - // First, put the thread on the blocked queue. - EnqueueBlockedThread(thread, stop); - - // Now, find a runnable thread - return NextRunnableThread(); - } - - [MixinOverride] - [NoHeapAllocation] - new public static void OnThreadUnblocked(Thread thread) - { - Scheduler.AssertDispatchLockHeld(); - -#if DEBUG_DISPATCH - DebugStub.WriteLine("OnThreadUnblocked: thread={0:x8} [before]", - __arglist(Kernel.AddressOf(thread))); - //#if DEBUG_DISPATCH - //DumpBlockedThreads(); -#endif // DEBUG_DISPATCH - - ThreadEntry entry = thread.schedulerEntry; - DebugStub.Assert(entry.queue == blockedThreads - || entry.queue == frozenThreads); - entry.RemoveFromQueue(); - EnqueueThreadToRun(unblockedThreads, thread); - -#if DEBUG_DISPATCH - DebugStub.WriteLine("OnThreadUnblocked: after={0:x8} [before]", - __arglist(Kernel.AddressOf(thread))); - DumpBlockedThreads(); -#endif // DEBUG_DISPATCH - } - - [MixinOverride] - [NoHeapAllocation] - new public static Thread OnThreadYield(Thread thread) - { - Scheduler.AssertDispatchLockHeld(); - - Thread target = NextRunnableThread(); - if (target == null) { - return thread; - } - else { - EnqueueThreadToRun(runnableThreads, thread); - return target; - } - } - - [MixinOverride] - new public static Thread OnThreadStop(Thread thread) - { - Scheduler.AssertDispatchLockHeld(); - - return NextRunnableThread(); - } - - [MixinOverride] - new public static void OnThreadFreezeIncrement(Thread thread) - { - Scheduler.AssertDispatchLockHeld(); - thread.freezeCount++; - if (thread.ActiveProcessor == null) { - ThreadEntry entry = thread.schedulerEntry; - entry.RemoveFromQueue(); - frozenThreads.EnqueueTail(entry); - } - // (If thread.ActiveProcessor != null, thread will move - // to frozenThreads when it is descheduled.) - } - - [MixinOverride] - new public static void OnThreadFreezeDecrement(Thread thread) - { - Scheduler.AssertDispatchLockHeld(); - DebugStub.Assert(thread.freezeCount > 0); - thread.freezeCount--; - if (thread.freezeCount == 0) { - ThreadEntry entry = thread.schedulerEntry; - frozenThreads.Remove(entry); - EnqueueThread(thread); - } - } - - [MixinOverride] - [CLSCompliant(false)] - [NoHeapAllocation] - new public static Thread OnTimerInterrupt(Thread thread, SchedulerTime now) - { - Scheduler.AssertDispatchLockHeld(); - - ThreadEntry entry; - - // Move the recently unblocked threads to the runnable queue. - while ((entry = unblockedThreads.DequeueHead()) != null) { - EnqueueThreadToRun(runnableThreads, entry.Thread); - } - - // Quantum is up, move current thread back to the runnable list. - if (thread != null) { - EnqueueThreadToRun(runnableThreads, thread); - } - -#if DEBUG_DISPATCH - DebugStub.WriteLine("OnTimerInterrupt : thread={0:x8} cpu={1} [before]", - __arglist(Kernel.AddressOf(thread), - Processor.GetCurrentProcessorId())); - //#if DEBUG_DISPATCH - //DumpBlockedThreads(); -#endif // DEBUG_DISPATCH - - // Now, unblock any threads whose timers have elapsed. - while ((entry = blockedThreads.Head) != null && - entry.Thread.BlockedUntil <= now) { - -#if DEBUG_DISPATCH - DebugStub.WriteLine("OnTimerInterrupt : thread={0:x8} cpu={1} [will remove]", - __arglist(Kernel.AddressOf(entry.Thread), - Processor.GetCurrentProcessorId())); -#endif // DEBUG_DISPATCH - - blockedThreads.Remove(entry); - entry.Thread.WaitFail(); - EnqueueThreadToRun(runnableThreads, entry.Thread); - } - -#if DEBUG_DISPATCH - DebugStub.WriteLine("OnTimerInterrupt : thread={0:x8} cpu={1} [after]", - __arglist(Kernel.AddressOf(thread), - Processor.GetCurrentProcessorId())); - DumpBlockedThreads(); -#endif // DEBUG_DISPATCH - - DebugStub.Assert(minSlice.Ticks != 0); // Initialization failure - DebugStub.Assert(idleSlice.Ticks != 0); // Initialization failure - - // Choose a default timeout depending on whether their are - // runnable threads or not. - SchedulerTime stop; - if (runnableThreads.IsEmpty()) { - stop = new SchedulerTime(now.Ticks + idleSlice.Ticks); - } - else { - stop = new SchedulerTime(now.Ticks + minSlice.Ticks); - } - - // Then adjust the timeout so that we wake up sooner if - // there is a thread waiting on a timer. - if (entry != null && entry.Thread.BlockedUntil < stop) { - stop = entry.Thread.BlockedUntil; - } - Processor.CurrentProcessor.SetNextTimerInterrupt(stop); - -#if DEBUG_TIMER - DebugStub.WriteLine("Next Timer: {0}ms", - __arglist((stop.Ticks - now.Ticks) / TimeSpan.TicksPerMillisecond)); -#endif // DEBUG_TIMER - - VTable.Assert(unblockedThreads.Head == null); - return runnableThreads.DequeueHeadThread(); - } - - [MixinOverride] - [NoHeapAllocation] - new public static Thread OnIoInterrupt(Thread thread) - { - Scheduler.AssertDispatchLockHeld(); - - if (thread != null) { - EnqueueThreadToRun(unblockedThreads, thread); - } - return unblockedThreads.DequeueHeadThread(); - } - - [MixinOverride] - [NoHeapAllocation] - new public static void OnProcessorShutdown(Thread thread) - { - Scheduler.AssertDispatchLockHeld(); - - if (thread != null) { - EnqueueThreadToRun(unblockedThreads, thread); - } - } - - /////////////////////////////////// Helper Methods - // - [Inline] - [NoHeapAllocation] - private static Thread NextRunnableThread() { - Thread target = unblockedThreads.DequeueHeadThread(); - if (target == null) { - target = runnableThreads.DequeueHeadThread(); - } - return target; - } - - private static void EnqueueThread(Thread thread) - { - Scheduler.AssertDispatchLockHeld(); - if (thread.blocked) { - EnqueueBlockedThread(thread, thread.BlockedUntil); - } - else { - EnqueueThreadToRun(runnableThreads, thread); - } - } - - // Enqueue ready-to-run thread in queue - // (where queue==runnableThreads or queue==unblockedThreads). - // Frozen threads are rerouted to the frozen queue. - [Inline] - [NoHeapAllocation] - private static void EnqueueThreadToRun(ThreadQueue queue, - Thread thread) - { - if (thread.freezeCount == 0) { - queue.EnqueueTail(thread.schedulerEntry); - } - else { - frozenThreads.EnqueueTail(thread.schedulerEntry); - } - } - - [Inline] - private static void EnqueueBlockedThread(Thread thread, SchedulerTime stop) - { - Scheduler.AssertDispatchLockHeld(); - // Put the thread on the blocked queue. - bool updateStop = ((blockedThreads.Head == null) || - (blockedThreads.Head.Thread.BlockedUntil > stop)); - - if (updateStop && stop <= SchedulerTime.Now) { - // Optimization: don't bother setting a timer for a thread - // whose blocking has already timed out. Wake it immediately. - thread.WaitFail(); - EnqueueThreadToRun(runnableThreads, thread); - return; - } - - if (stop == SchedulerTime.MaxValue) { - blockedThreads.EnqueueTail(thread.schedulerEntry); -#if DEBUG_DISPATCH - DebugStub.Print("OnThreadBlocked : thread={0:x8} cpu={1} EnqueueTail\n", - __arglist(Kernel.AddressOf(thread), - Processor.GetCurrentProcessorId())); -#endif // DEBUG_DISPATCH - } - else { - ThreadEntry entry = blockedThreads.Head; -#if DEBUG_DISPATCH - DebugStub.Print("OnThreadBlocked : thread={0:x8} cpu={1} stop={2}\n", - __arglist(Kernel.AddressOf(thread), - Processor.GetCurrentProcessorId(), - stop.Ticks)); -#endif // DEBUG_DISPATCH - while (entry != null && entry.Thread.BlockedUntil <= stop) { - // Loop until we find the first thread with a later stop. - entry = entry.Next; -#if DEBUG_DISPATCH - DebugStub.Print("OnThreadBlocked : BlockedUntil={0}\n", - __arglist(entry.Thread.BlockedUntil.Ticks)); -#endif // DEBUG_DISPATCH - } - - blockedThreads.InsertBefore(entry, thread.schedulerEntry); - } - - VTable.Assert(blockedThreads.IsEnqueued(thread.schedulerEntry)); - - - if (updateStop) { -#if DEBUG_DISPATCH - DebugStub.Print("OnThreadBlocked : SetNextTimerInterrupt({0})\n", - __arglist(stop.Ticks)); -#endif // DEBUG_DISPATCH - Processor.CurrentProcessor.SetNextTimerInterrupt(stop); - } - else { -#if DEBUG_DISPATCH - DebugStub.Print("OnThreadBlocked : No timer update needed\n"); -#endif // DEBUG_DISPATCH - } - - DumpBlockedThreads(); - } - - /////////////////////////////////// Diagnostics and Debugging Support. - // - [Conditional("DEBUG_DISPATCH")] - public static void DumpBlockedThreads() - { - // First, put the thread on the blocked queue. - ThreadEntry entry = blockedThreads.Head; - for (; entry != null; entry = entry.Next) { - DebugStub.Print(" thread={0:x8} stop= {1}\n", - __arglist( - Kernel.AddressOf(entry.Thread), - entry.Thread.BlockedUntil.Ticks)); - } - } - - ////////////////////////////////////////////////////////////////////// - // - } -} diff --git a/base/Kernel/Singularity/Scheduling/MinScheduler.cs b/base/Kernel/Singularity/Scheduling/MinScheduler.cs new file mode 100644 index 0000000..967528e --- /dev/null +++ b/base/Kernel/Singularity/Scheduling/MinScheduler.cs @@ -0,0 +1,686 @@ +//------------------------------------------------------------------------------ +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Description: MinScheduler.cs +// +// Minimal round-robin style without priorities scheduler. +// +// MinScheduler favors thread that have recently become unblocked +// and tries to avoid reading the clock or reseting the timer as +// much as possible. +// +// The minimal scheduler maintains two queues of threads that can +// be scheduled. The unblockedThreads queue contains threads which +// have become unblocked during this scheduling quantum; mostly, +// these are threads that were unblocked by the running thread. +// The runnableThreads queue contains all other threads that are +// currently runnable. If the current thread blocks, MinScheduler +// will schedule threads from the unblockedThread queue, without +// reseting the timer. When the timer finally fires, MinScheduler +// moves all unblockedThreads to the end of the runnableThreads +// queue and schedules the next runnableThread. +//------------------------------------------------------------------------------ + +using System; +using System.Collections; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; + +using Microsoft.Bartok.Options; + +using Microsoft.Singularity; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Scheduling; + +namespace Microsoft.Singularity.Scheduling +{ + /// + /// Summary description for MinScheduler. + /// + [NoCCtor] + [CLSCompliant(false)] + public class MinScheduler : Scheduler + { + /// + /// + /// Constructor for MinScheduler object + /// + /// + public MinScheduler() + { + // If busy, don't run for more than 10ms on same task. + minSlice = TimeSpan.FromMilliseconds(10); + + // Note that we have made the idleSlice small always. + // This is necessary in the MP case - otherwise a + // CPU seeing no work will go to sleep for a month and + // there is no mechanism to be woken when another CPU + // has excess work. (This support should be added + // eventually if we intend to use this scheduler.) + idleSlice = TimeSpan.FromMilliseconds(100); + + affinity = 0; + + // Create a scheduler lock + this.runnableLock = new SchedulerLock(); + + // Initialize timer's spinlock + this.timerLock = new SpinLock(SpinLock.Types.Timer); + } + + /// + /// Initialize min scheduler + /// + /// + public override void Initialize() + { + } + + /// + /// Finalize scheduler object + /// + /// + public override void Finalize() + { + } + + /// + /// + /// Notify scheduler about new dispatcher + /// + /// + public override void OnDispatcherInitialize(int dispatcherId) + { + // Min scheduler doesn't care about multiple dispatchers + } + + /// + /// Attach thread to scheduler: thread specific initializtion + /// + /// + /// Thread to attach + /// Have we called thread constructor + /// + public override void OnThreadStateInitialize(Thread thread, bool constructorCalled) + { + // Only initialize thread-local state! No locks held and interrupts on. + } + + /// + /// + /// Retrieve scheduler lock - used by dispatcher to protect scheduler state + /// + /// + /// Affinity of dispatcher making actual call + /// + [CLSCompliant(false)] + [NoHeapAllocation] + internal override SchedulerLock RetrieveSchedulerLock(int affinity) + { + // Return our default scheduler + return MyLock; + } + + /// + /// + /// Run scheduling policy. This method is called by dispatcher when both interrupts + /// disabled and disptach lock is acquired. As long as multiple dispatchers don't have access + /// to the this method no protection is required. + /// + /// + /// Set the returned running thread to this affinity. + /// the thread currently running + /// thread state to change to for the current thread + /// Current system time + /// + [NoHeapAllocation] + public override Thread RunPolicy( + int affinity, + Thread currentThread, + ThreadState schedulerAction, + SchedulerTime currentTime) + { + Thread threadToReturn = null; + ThreadState newState = ThreadState.Undefined; + ThreadEntry entry = null; + + // Put current thread on a runnable queue only if it is currently in a running state + if (currentThread != null) { + + // At this point current threads state has to be + // running - when running derived scheduler state we will deduce new state + // but until then it has to be running + VTable.Assert(currentThread.ThreadState == ThreadState.Running); + + // If scheduler action is running make it runnable to have proper state transition + // Currently we disallow to go from running to running + if (schedulerAction == ThreadState.Running) { + schedulerAction = ThreadState.Runnable; + } + + // Change current's thread new scheudler state + newState = currentThread.ChangeSchedulerState(schedulerAction); + + // If new state is runnable add it to runnable queue + if (newState == ThreadState.Runnable) { + // REVIEW: Should we make sure the thread is enqueue already? + + // Indicate that thread has been marked as runnable + this.runnableThreads.EnqueueTail(currentThread.schedulerEntry); + } + } + + // Derived state of entry on the runnable queue can be either suspended or runnable. + // Consequently first we remove an entry from the queue. If it is runnable, + // we will be able to convert it to running, If it is suspended, + // we will convert its real state to suspended. The first thread that marks the entry + // unsuspended will be responsible for putting it on back on a runnable queue. + + // Check the unblocked queue first. + while ((entry = this.unblockedThreads.DequeueHead()) != null) { + + // At this point thread direct state can be only runnable... + VTable.Assert(entry.Thread.IsRunnable); + + // Attempt to make thread running + newState = entry.Thread.ChangeSchedulerState(ThreadState.Running); + + // Get of the loop if we marked one of the entries as running + if (newState == ThreadState.Running) { + break; + } + + // If the thread is suspended, then look for another. + VTable.Assert(newState == ThreadState.Suspended); + } + + // If no recently unblocked threads, then check the runnable queue. + if (entry == null) { + while ((entry = this.runnableThreads.DequeueHead()) != null) { + + // At this point thread direct state can be only runnable... + VTable.Assert(entry.Thread.IsRunnable); + + // Attempt to make thread running + newState = entry.Thread.ChangeSchedulerState(ThreadState.Running); + + // Get of the loop if we marked one of the entries as running + if (newState == ThreadState.Running) { + break; + } + + // If the thread is suspended, then look for another. + VTable.Assert(newState == ThreadState.Suspended); + } + } + + // We got an entry from the runnable queue that we can actually run. + if (entry != null) { + + // Thread must realy by in the running state. + VTable.Assert(newState == ThreadState.Running); + + // Initialize thread we about to return. + threadToReturn = entry.Thread; + threadToReturn.Affinity = affinity; + } + + return threadToReturn; + } + + /// + /// + /// Add thread to runnable queue. This method is called by dispatcher at the + /// point when both dispatcher lock and interrupts are disabled + /// + /// + /// Thread to add to runnable queue + /// + [Inline] + [NoHeapAllocation] + public override void AddRunnableThread(Thread thread) + { + ThreadState newState; + + // Change thread's state to runnable: Runpolicy will decide latter on + // if it can actually run the thread + newState = thread.ChangeSchedulerState(ThreadState.Runnable); + + // Add thread to runnable queue only if new state is runnable + if (newState == ThreadState.Runnable) { + // While we adding a thread to runnable queue someone could have called freeze on + // it again - RunPolicy will notice it and remove thread from runnable queue appropriatly + + this.unblockedThreads.EnqueueTail(thread.schedulerEntry); + } + } + + /// + /// + /// Start thread - put a thread on dispatcher's runnable queue so it can be run + /// + /// + /// Thread to start + /// + [NoHeapAllocation] + public override void OnThreadStart(Thread thread) + { + // Initialize thread's affinity + thread.Affinity = (int)ProcessorDispatcher.Affinity.All; + + // Enqueue thread into dispatcher. + ProcessorDispatcher.AddRunnableThread(thread); + } + + /// + /// + /// Block thread -process thread blocking including putting it on a timer queue if + /// timeout is specified. + /// + /// + /// Thread that blocks + /// Amount of time for the thread to be blocked + /// + [NoHeapAllocation] + public override void OnThreadBlocked(Thread thread, SchedulerTime stop) + { + // Assert preconditions + VTable.Assert(thread == Thread.CurrentThread); + + if (stop != SchedulerTime.MaxValue) { + // Enqueue timer so that if it expires we will notice it right a way + EnqueueTimer(thread, stop); + } + + // Thread is blocked now - indicate this to dispatcher. Dispatcher will make + // make sure that thread's scheduler is aware of the blockage + ProcessorDispatcher.SwitchContext(ThreadState.Blocked); + + // We just got unblocked and happilly running. We need to remove ourselves + // from the timer queue if we are unblocked by signal rather than by timeout + if (thread.UnblockedBy != WaitHandle.WaitTimeout) { + // One of our buddies unblocked us - remove the time out. Before we can + // actually assert that we were indeed unblocked by someone. + VTable.Assert(thread.UnblockedBy != WaitHandle.UninitWait); + + // Remove a timer from the timer queue and happilly continue! + RemoveTimer(thread); + } + } + + /// + /// + /// Unblock thread - resume thread by putting it on the dispatcher runnable queue. + /// This method can be invoked by threads running on other processors + /// + /// + /// Thread to resume + /// + [NoHeapAllocation] + public override void OnThreadUnblocked(Thread thread) + { + // Only call this method if thread is Indeed blocked + VTable.Assert(thread.IsBlocked); + + // Thread is ready to run: Add it to dispatcher + ProcessorDispatcher.AddRunnableThread(thread); + } + + /// + /// + /// Yield thread - suspend thread based on time slice + /// + /// + /// Thread that is yielding + /// + [NoHeapAllocation] + public override void OnThreadYield(Thread thread) + { + // Perform a context switch: If thread is runnable it will be put on a runnable queue + // by dispatcher. + ProcessorDispatcher.SwitchContext(ThreadState.Running); + } + + /// + /// + /// Stop thread execution + /// + /// + /// Thread that is stopping + /// + [NoHeapAllocation] + public override void OnThreadStop(Thread thread) + { + // Perform a context switch: If thread is stopped it will be cleaned up, otherwise + // it is suspended and will be cleaned up latter + ProcessorDispatcher.SwitchContext(ThreadState.Stopped); + } + + /// + /// + /// Increment frozen counter + /// + /// + /// Thread for which to increment freeze counter + /// + [NoHeapAllocation] + public override void OnThreadFreezeIncrement(Thread thread) + { + // Update threads: Freeze count + thread.IncrementFreezeCounter(); + } + + /// + /// + /// Decrement frozen counter + /// + /// + /// Thread for which to update freeze counter + /// + [NoHeapAllocation] + public override void OnThreadFreezeDecrement(Thread thread) + { + bool shouldPutThreadOnRunnableQueue = false; + + // Assert preconditions + DebugStub.Assert(thread.FreezeCount > 0); + + // Update threads: Freeze count + if (thread.DecrementFreezeCounter(ref shouldPutThreadOnRunnableQueue) == 0 && + shouldPutThreadOnRunnableQueue) { + + // Thread became runnable - add it to runnable queue + ProcessorDispatcher.AddRunnableThread(thread); + } + } + + /// + /// + /// Suspend thread and wait until it is suspended. + /// + /// + /// Thread to suspend + /// + [NoHeapAllocation] + public override void Suspend(Thread thread) + { + // Assert preconditions: We can't call suspend on ourselves + VTable.Assert(thread != Thread.CurrentThread); + + // Increment freeze counter and then spin wait until thread will become suspended + OnThreadFreezeIncrement(thread); + + // Wait until thread is capable of running + while (thread.IsRunning || thread.IsRunnable) { + Thread.SpinWait(100); + } + } + + /// + /// + /// Resume thread from being suspended + /// + /// + /// Thread to resume + /// + [NoHeapAllocation] + public override void Resume(Thread thread) + { + // Increment freeze counter and then spin wait until thread will becom suspended + OnThreadFreezeDecrement(thread); + } + + /// + /// + /// Dispatch timer interrupt. This method is called by dispather when interrupts are + /// disabled. + /// + /// + /// Processor affinity + /// Current time + /// + [CLSCompliant(false)] + [NoHeapAllocation] + [HalLock] + public override TimeSpan OnTimerInterrupt(int affinity, SchedulerTime now) + { + ThreadEntry entry; + TimeSpan delta; + + // Assert pre conditions + DebugStub.Assert(this.minSlice.Ticks != 0); + DebugStub.Assert(this.idleSlice.Ticks != 0); + + // For now interrupts should be disabled when this method is called + VTable.Assert(Processor.InterruptsDisabled()); + + // Move all of the unblockedThreads to the runnable queue. + while ((entry = this.unblockedThreads.DequeueHead()) != null) { + this.runnableThreads.EnqueueTail(entry); + } + + // Acquire timer lock + timerLock.Acquire(); + + // Unblock any threads whose timers have elapsed. + while ((entry = this.timerThreads.Head) != null && + entry.Thread.BlockedUntil <= now) { + + // Remove thread from the timer queue: There shouldn't be race with RemoveTimer + // call since we are protected by a timer queue's lock + this.timerThreads.Remove(entry); + + // Indicate to the thread that its wait completed with timeout + if (entry.Thread.Unblock(WaitHandle.WaitTimeout) == WaitHandle.WaitTimeout) { + // Only if we have unblocked thread and it was already in blocked state + // put it on a dispatcher deferred wake up queue: otherwise scheduler will notice + // expiration itself and put the thread on a runnable queue. For more info + // see RunPolicy + if (entry.Thread.ShouldCallSchedulerUnBlock(WaitHandle.WaitTimeout)) { + + // Change thread's state to runnable: Runpolicy will decide latter on + // if it can actually run the thread + entry.Thread.ChangeSchedulerState(ThreadState.Runnable); + + // Place thread at the tail of the unblocked queue + this.unblockedThreads.EnqueueTail(entry); + } + } + } + + // Adjust the timeout so that we wake up sooner if + // there is a thread waiting on a timer. + if (!this.timerThreads.IsEmpty() && + this.timerThreads.Head.Thread.blockedUntil < (now + this.minSlice)) { + this.nextTimer = this.timerThreads.Head.Thread.blockedUntil; + } + else { + this.nextTimer = now + this.idleSlice; + } + + // Remember new alarm we are to set + delta = this.nextTimer - now; + + // We are done: Don't forget to release timer lock: + timerLock.Release(); + + return delta; + } + + /// + /// + /// Remove a timer from timer queue: if thread is still on a timer queue. Need to + /// disable interrupts before acquire timer lock - dispatcher always calls into timer + /// with interrupts disable. If we don't disable interrupts we will get into possible deadlock + /// with dispatcher + /// + /// + [CLSCompliant(false)] + [NoHeapAllocation] + [HalLock] + private void RemoveTimer(Thread thread) + { + // For now disable interrupts and acquire scheduler lock + bool shouldEnable = Processor.DisableInterrupts(); + + // Acquire timer lock + timerLock.Acquire(); + + if (thread.timerEntry.queue != null) { + this.timerThreads.Remove(thread.timerEntry); + } + + // Release timer lock + timerLock.Release(); + + // Reenable interrupts + Processor.RestoreInterrupts(shouldEnable); + } + + /// + /// + /// Park blocking thread in a timer queue with a given time out. Need to + /// disable interrupts before acquiring timer lock - dispatcher always calls into timer + /// with interrupts disable. If we don't disable interrupts we will get into deadlock + /// + /// + /// Thread to block + /// Period of time for a thread to wait before timeout + /// + [NoHeapAllocation] + [HalLock] + private void EnqueueTimer(Thread thread, SchedulerTime stop) + { + // For now disable interrupts and acquire scheduler lock... + bool shouldEnable = Processor.DisableInterrupts(); + int unblockedBy; + SchedulerTime now = SchedulerTime.Now; + TimeSpan delta = stop - now; + + //Acquire scheduler lock: + this.timerLock.Acquire(); + + // Find out if we need to update alarm. + bool shouldChangeAlarm = ((this.timerThreads.Head == null) || + (this.timerThreads.Head.Thread.blockedUntil > stop)); + + // If new wait already expired just try to fail it right a way. Alarm upate + // indicates that only this new wait can be retired + if (shouldChangeAlarm && stop <= now) { + + unblockedBy = thread.Unblock(WaitHandle.WaitTimeout); + + // We no longer should be changing alarm + shouldChangeAlarm = false; + + // if we unblocked by timer or by someone else we don't have to anything - + // OnThreadBlock will adjust thread's state accordingly. For more info see + // OnThreadBlock, Dispatcher.ContextSwitch and RunPolicy + } + else { + // Enqueue thread in the right place + ThreadEntry entry = this.timerThreads.Head; + + while (entry != null && entry.Thread.blockedUntil <= stop) { + // Loop until we find the first thread with a later stop. + entry = entry.Next; + } + + // Store thread's block until information + thread.blockedUntil = stop; + + // Found the right place go ahead and put the thread into the queue + this.timerThreads.InsertBefore(entry, thread.timerEntry); + } + + // Before we let others to party on a timer check if timer has to be reset: + // and if so rember it and reset once we release the lock + if (shouldChangeAlarm && this.nextTimer > stop) { + this.nextTimer = stop; + } + else { + shouldChangeAlarm = false; + } + + // Release timer lock + this.timerLock.Release(); + + // If we are required to set a time on dispatcher, do it outside of + // spinlock since nobody is currently can be running on our processor + // since we have interrupts disabled. + if (shouldChangeAlarm) { + Processor.CurrentProcessor.SetNextTimerInterrupt(delta); + } + + // Re-enable interrupts + Processor.RestoreInterrupts(shouldEnable); + } + + /// + /// + /// Property to get to the scheduler runnable queue lock + /// + /// + internal SchedulerLock MyLock + { + [Inline] + [NoHeapAllocation] + get + { + return this.runnableLock; + } + } + + /// + /// + /// Get the affinity of this base scheduler + /// + /// + internal int Affinity + { + [Inline] + [NoHeapAllocation] + get + { + return this.affinity; + } + } + + /// A spinlock protecting state of the timer queue + private SchedulerLock runnableLock; + + /// A spinlock protecting state of the timer queue + private SpinLock timerLock; + + /// List of recently runnable, but unscheduled threads. + private ThreadQueue unblockedThreads = new ThreadQueue(); + + /// List of runnable, but unscheduled threads. + private ThreadQueue runnableThreads = new ThreadQueue(); + + /// List of blocked threads, sorted by wait time. + private ThreadQueue timerThreads = new ThreadQueue(); + + /// Scheduler affinity + private readonly int affinity; + + /// List of frozen threads (unsorted) + private SchedulerTime nextTimer; + + /// Idle thread + private Thread idleThread; + + /// Run time slice + private TimeSpan minSlice; + + /// Run time for idle slice + private TimeSpan idleSlice; + + /// Check if need to process interprocess interrupt + private bool isIpiNeeded; + } +} diff --git a/base/Kernel/Singularity/Scheduling/ProcessorDispatcher.cs b/base/Kernel/Singularity/Scheduling/ProcessorDispatcher.cs new file mode 100644 index 0000000..3b2e84a --- /dev/null +++ b/base/Kernel/Singularity/Scheduling/ProcessorDispatcher.cs @@ -0,0 +1,1471 @@ +//------------------------------------------------------------------------------ +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// This file contains an implementation of processor dispatcher. There is one processor +// Dispatcher per processor +//------------------------------------------------------------------------------ + +#if DEBUG +#define SHOW_TWIDDLE +#endif + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.GCs; +using System.Threading; +using Microsoft.Singularity; +using Microsoft.Singularity.Hal; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Isal; + +namespace Microsoft.Singularity.Scheduling +{ + /// + /// + /// A Processor Dispatcher is responsible for handling processor runnable queue, + /// timer and i/o interrupts as well as forwarding a dispatching event to a proper scheduler + /// if required + /// + /// + /// + [CLSCompliant(false)] + [AccessedByRuntime("referenced from halidt.asm")] + public class ProcessorDispatcher + { + + /// + /// + /// Initialize processor dispatcher + /// + /// + /// Scheduler to use + /// + [NoHeapAllocation] + public static void StaticInitialize(Scheduler schedulerToUse) + { + // Initialize scheduler + scheduler = schedulerToUse; + } + + /// + /// + /// Constructor + /// + /// + internal ProcessorDispatcher() + { + this.id = Interlocked.Increment(ref numberOfDispatchers) - 1; + +#if SHOW_TWIDDLE + this.color = (ushort)((this.id == 0) ? 0x3a00 : 0x2a00); +#endif + + // Create stopped thread queue for processing stopped thread + this.stoppedThreadQueue = new ThreadQueue(null); + + // Create scavanged thread queue for processing stopped thread that ready to be cleaned up + this.scavengerThreadQueue = new ThreadQueue(null); + } + + /// + /// + /// First part of the initialization of processor dispatcher + /// + /// + /// Processor dispatcher belongs to + /// + public void Initialize(Processor myProcessor) + { + // TODO: This is the wrong place for this. + InitializeTwiddle(); + + // Mark this dispatcher as active + this.isDispatcherInUse = true; + + // Initalize procssor + this.myProcessor = myProcessor; + + // Create an idle thread for this processor + this.idleThread = Thread.CreateIdleThread(this.myProcessor); + + // Create a scavenger thread + this.scavengerThread = Thread.CreateIdleThread(this.myProcessor); + + // Notify scheduler about new dispatcher + scheduler.OnDispatcherInitialize(myProcessor.Id); + } + + /// + /// + /// Finalize method + /// + /// + public void Finalize() + { + } + + /// + /// + /// Check if thread index maps to idle thread + /// + /// + /// Thread index to check if it is actually idle or not + /// + [Inline] + [NoHeapAllocation] + public static bool IsIdleThread(int threadIndex) { + + // Map index to thread + Thread thread = Thread.threadTable[threadIndex]; + + // Check if thread is indeed idle thread + return IsIdleThread(thread); + } + + /// + /// + /// Check if thread is an idle thread + /// + /// + /// Thread to check if it is idle + /// + [Inline] + [NoHeapAllocation] + internal static bool IsIdleThread(Thread thread) + { + return (thread != null) ? thread.IsIdle : false; + } + + /// + /// + /// Check if thread is an idle thread + /// + /// + /// Thread to check if it is idle + /// + [Inline] + [NoHeapAllocation] + internal bool IsMyIdleThread(Thread thread) + { + return thread == this.idleThread; + } + + /// + /// + /// Check if thread is one of a dispatcher's threads + /// + /// + /// Thread to check if it is idle + /// + [Inline] + [NoHeapAllocation] + internal bool IsOneOfMyDispatcherThreads(Thread thread) + { + return thread == this.scavengerThread || thread == this.idleThread; + } + + /// + /// + /// Handle I/O interrupt and perform context switch if required + /// + /// + [NoHeapAllocation] + public void HandleIOReschedule() + { + Thread currentThread = Processor.GetCurrentThread(); + SchedulerTime currentTime = SchedulerTime.Now; + + // Validate preconditions + ValidateInvariantsOnInterruptEnter(currentThread); + + // Let scheduler decide if we should run different thread + RunScheduler(currentThread, ThreadState.Running, currentTime); + + // Validate post conditions + ValidateInvariantsOnInterruptExit(); + } + + /// + /// + /// Handle timer interrupt and perform context switch if required + /// + /// + /// + /// Interrupts disabled + /// Thread is not running from processor point of view + /// + /// + [NoHeapAllocation] + public void HandlePreemptionReschedule(HalTimer timer) + { + Thread currentThread = Thread.CurrentThread; + SchedulerTime currentTime = SchedulerTime.Now; + + // Validate preconditions + ValidateInvariantsOnInterruptEnter(currentThread); + + // Twiddle the thread spinner. + Twiddle(); + + // Process timer interrupt + if (isDispatcherInUse) { + TimeSpan delta = timer.MaxInterruptInterval; + + if (!isActingForEntireSystem) { + // This is the normal case, the processor is in use, notify + // the scheduler about timer interrupt on this processor + delta = NotifyTimerInterrupt(myProcessor.Id, currentTime); + } + else { + // This happens only during system shutdown where the boot processor + // simulates all processors, we need to notify scheduler about the + // timer interrupt on all virtual processors. + for (int id = 0; id < Processor.processorTable.Length; id++) { + TimeSpan next = NotifyTimerInterrupt(id, currentTime); + if (delta > next) { + delta = next; + } + } + } + + TimeSpan start = delta; + if (delta < timer.MinInterruptInterval) { + delta = timer.MinInterruptInterval; + } + if (delta > timer.MaxInterruptInterval) { + delta = timer.MaxInterruptInterval; + } +#if false + DebugStub.WriteLine("-- NextTimer(delta={0}, start={1} [min={2},max={3})", + __arglist(delta.Ticks, + start.Ticks, + timer.MinInterruptInterval.Ticks, + timer.MaxInterruptInterval.Ticks)); +#endif + timer.SetNextInterrupt(delta); + } + + // Let scheduler decide if we should run different thread + RunScheduler(currentThread, ThreadState.Running, currentTime); + + // Validate post conditions + ValidateInvariantsOnInterruptExit(); + } + + /// + /// + /// Actual implementation of notifying the scheduler about the timer interrupt + /// + /// + /// Id of the processor, correspond to affinity of the threads. + /// Note during system shutdown this is virtualized. + /// + [NoHeapAllocation] + private TimeSpan NotifyTimerInterrupt(int processorId, SchedulerTime now) + { + // We need to acquire scheduler lock + SchedulerLock schedulerLock = scheduler.RetrieveSchedulerLock(processorId); + schedulerLock.Acquire(); + + // No need to acquire scheduler lock - timer has its own lock + TimeSpan delta = scheduler.OnTimerInterrupt(processorId, now); + + // We are done with this scheduler release its lock + schedulerLock.Release(); + return delta; + } + + /// + /// + /// Add runnable thread to runnable queue + /// + /// + /// Thread to add to runnable queue + /// + /// + /// During this operation we have to keep interrupts disabled even though we might + /// be adding a thread to different dispatcher - the problem is if we enable interrupts we + /// might get reschedule to the dispatcher which lock we currently holding - that will + /// cause definite deadlock + /// + /// + [NoHeapAllocation] + public static void AddRunnableThread(Thread threadToAdd) + { + // At this point we need to disable interrupts and acquire dispatcher lock: + bool shouldEnable = Processor.DisableInterrupts(); + ProcessorDispatcher dispatcher = Processor.CurrentProcessor.Dispatcher; + Thread currentThread = Thread.CurrentThread; + int affinity; + int dispatcherIdxToActivate; + Scheduler scheduler = Kernel.TheScheduler; + SchedulerLock schedulerLock = + scheduler.RetrieveSchedulerLock(threadToAdd.Affinity); + + // Validate pre conditions + dispatcher.ValidateInvariantsOnAddRunnableThreadEnter(currentThread, threadToAdd); + + // Acquire scheduler lock + schedulerLock.Acquire(currentThread); + + // We will use thread's affinity to find dispatcher to signal + affinity = threadToAdd.Affinity; + + // We have protected runnable queue - now we need to call back into scheduler + // to add thread to runnable queue + scheduler.AddRunnableThread(threadToAdd); + + // Release scheduler lock + schedulerLock.Release(currentThread); + + // Make sure that appropriate dispatchers are active + if (affinity == (int)Affinity.All) { + // Calculate next dispatcher to activate + dispatcherIdxToActivate = GetNextDispatcherToActivate(); + } + else { + dispatcherIdxToActivate = affinity; + } + + // If we are running on a dispatcher we have been asked to activate don't do anything + if (dispatcherIdxToActivate != dispatcher.myProcessor.Id) { + ProcessorDispatcher dispatcherToActivate = + Processor.processorTable[dispatcherIdxToActivate].Dispatcher; + // During MP boot there could be race condition where the dispatcher is + // not initialized. Activate it only if it's initialized. + if (dispatcherToActivate != null) { + // During MP system shutdown, if the dispatcher is not in use, then + // activate the boot processor instead + if (dispatcherToActivate.isDispatcherInUse) { + dispatcherToActivate.ActivateDispatcher(); + } + else { + Processor.processorTable[0].Dispatcher.ActivateDispatcher(); + } + } + } + + // Validate post conditions: For now we can't call method when current thread is + // the same as thread to add: It is possible that they are the same only when + // the method is called from processor shutdown. + if (currentThread != threadToAdd) { + dispatcher.ValidateInvariantsOnAddRunnableThreadExit(currentThread, threadToAdd); + } + + // Reenable interrupts + Processor.RestoreInterrupts(shouldEnable); + } + + /// + /// + /// Switch to the new thread context. + /// + /// + [NoHeapAllocation] + public static void SwitchContext(ThreadState schedulerAction) + { + // At this point we need to disable interrupts and acquire dispatcher lock: + bool shouldEnable = Processor.DisableInterrupts(); + + ProcessorDispatcher dispatcher = Processor.CurrentProcessor.Dispatcher; + + // Retrieve current thread + Thread currentThread = Thread.CurrentThread; + + // Validate pre conditions + dispatcher.ValidateInvariantsOnSwitchContexEnter(currentThread); + + // We have protected runnable queue - now we need to call back into scheduler + // Let scheduler decide if we should run different thread + dispatcher.RunScheduler(currentThread, schedulerAction, SchedulerTime.MinValue); + + // STOP! STOP! STOP! STOP! STOP! STOP! STOP! STOP! STOP! STOP! STOP! + // DON'T TRY TO ACCESS DISPATCHERS AT THIS POINTER - WE CAN BE RETURNING + // ON DIFFERENT. DISPATCHER LOCKS SHOULD BE RELEASED BY NOW! + + // Validate post conditions + Processor.CurrentProcessor.Dispatcher.ValidateInvariantsOnSwitchContextExit(); + + Processor.RestoreInterrupts(shouldEnable); + } + + /// + /// + /// Shutdown this processor as part of system shutdown. All running and runnable + /// threads on this processor will be run on the boot processor after this. + /// + /// This must NOT be called on the boot processor. + /// + /// To avoid race conditions, the shutdown was done by letting the boot processor + /// work with the scheduler to run all runnable threads on the system. The scheduler + /// doesn't have any knowledge of the shutdown, ProcessorDispatcher takes care of all + /// the necessary plumbing. + /// + /// See also comments on data members: isDispatcherInUse and isActingForEntireSystem. + /// + /// + /// Thread currently running + /// + /// This method should be called with interrupt disabled + /// + [NoHeapAllocation] + public void OnProcessorShutdown(Thread currentThread) + { + Scheduler scheduler; + + // Validate preconditions + ValidateInvariantsOnInterruptEnter(currentThread); + + // This method must not be called by the boot processor + VTable.Assert(myProcessor.Id != 0); + + // Turn off this processor dispatcher. Once isDispatcherInUse is false, + // all interrupts and SwitchContext calls will go directly to idle loop + // and halt the processor + this.isDispatcherInUse = false; + + // Add the current running thread to its scheduler's runnable queue, + // do so only if it's not idle or scavenger thread + if (!IsOneOfMyDispatcherThreads(currentThread)) { + AddRunnableThread(currentThread); + } + + // Decrement the number of active dispatchers + int currentNumberOfDispatchers = Interlocked.Decrement(ref numberOfDispatchers); + + if (currentNumberOfDispatchers == 1) { + // If there is only a single dispatcher activate, let the boot processor know + // that it should now act for the entire system. + Processor.processorTable[0].Dispatcher.isActingForEntireSystem = true; + + // The boot processor may be halted, if so wake it up. + Processor.processorTable[0].Dispatcher.ActivateDispatcher(); + } + } + + /// + /// + /// Retrieve Kernel scheduler + /// + /// + public Processor Processor + { + [NoHeapAllocation] + get + { + return this.myProcessor; + } + } + + + /// + /// + /// Add threadToAdd to its scheduler's runnable queue if it's in the right state. + /// It's possible that the thread is ready to be blocked, in which case this + /// function call will change the thread's state to blocked and it will not be + /// added to the runnable queue. + /// + /// + /// Thread to add to runnable queue + /// The state of the thread + /// + [NoHeapAllocation] + private static void AddRunnableThreadIfRunnable( + Thread threadToAdd, + ThreadState schedulerAction) + { + ThreadState newState = ThreadState.Undefined; + + if (threadToAdd.ChangeSchedulerState(schedulerAction) == ThreadState.Runnable) { + AddRunnableThread(threadToAdd); + } + } + + /// + /// + /// Run the scheduler to find a thread to run. + /// + /// + /// Thread that was previously running + /// Whether the previously running thread is blocked + /// Current wall clock time. + /// The thread to run next, null if there is no thread to run + /// + [NoHeapAllocation] + private Thread FindSchedulerThreadToRun( + Thread schedulerThread, + ThreadState schedulerAction, + SchedulerTime currentTime) + { + Thread threadSwitchTo = null; + + // Assert preconditions: At this point dispatcher has to be in use otherwise we in trouble + VTable.Assert(this.isDispatcherInUse); + + // We need to consider if we are acting on behalf of all processors, so that in the process + // of being shutdown we don't starve other schedulers + if (!this.isActingForEntireSystem) { + // We are running normally just try to find a thread to run + threadSwitchTo = FindSchedulerThreadToRunInternal( + schedulerThread, + schedulerAction, + myProcessor.Id, + currentTime); + } + else { + // This happens only during system shutdown where the boot processor needs + // to simulates all processors. We need to walk all virtual processors + // in round robin fashion to find a thread to run. Once the next thread is + // found, this function returns, and our virtual processor position is kept + // in nextVirtualProcessorId. + for (int i = 0; i < Processor.processorTable.Length; i++) { + // simulate next processor, wrap around if we walk across the boundary + nextVirtualProcessorId++; + if (nextVirtualProcessorId == Processor.processorTable.Length) { + nextVirtualProcessorId = 0; + } + + // Now call FindSchedulerThreadToRunInternal to find a thread to run for + // the virtual processor. Note that we can must pass in the current + // running thread exactly once. + Thread threadToPassIn = i == 0 ? schedulerThread : null; + if (threadToPassIn != null) { + threadToPassIn.Affinity = nextVirtualProcessorId; + } + threadSwitchTo = FindSchedulerThreadToRunInternal( + threadToPassIn, + schedulerAction, + nextVirtualProcessorId, + currentTime); + + if (threadSwitchTo != null) { + break; + } + } + } + + return threadSwitchTo; + } + + /// + /// + /// Actual implementation to run the scheduler to find a thread to run. + /// + /// + /// Thread that was previously running + /// Whether the previously running thread is blocked + /// Id of the processor, correspond to affinity of the threads. + /// Note during system shutdown this is virtualized. + /// Current wall clock time. + /// The thread to run next, null if there is no thread to run + /// + [NoHeapAllocation] + private Thread FindSchedulerThreadToRunInternal( + Thread schedulerThread, + ThreadState schedulerAction, + int processorId, + SchedulerTime currentTime) + { + Thread threadSwitchTo = null; + + // Get to the scheduler lock and save it in dispatcher - we will be releasing it + // latter on as a part of context switch - + SchedulerLock schedulerLock = scheduler.RetrieveSchedulerLock(processorId); + + // currentThread can be null so use implicit version to lock dispatcher + schedulerLock.Acquire(); + + // Run Scheduling policy + threadSwitchTo = scheduler.RunPolicy( + processorId, + schedulerThread, + schedulerAction, + currentTime); + + // We are done working with this scheduler + schedulerLock.Release(); + + return threadSwitchTo; + } + + /// + /// + /// Run schedulers Helper method finds actual threads to run + /// + /// + /// If currentThread is idle or scavenger, then + /// schedulerThread is null, otherwise it's same as currentThread + /// State of the currentThread + /// Current wall clock time. + /// + [NoHeapAllocation] + private Thread ChooseThreadOrScavengerToRun( + Thread schedulerThread, + ThreadState schedulerAction, + SchedulerTime currentTime) + { + Thread threadSwitchTo = null; + + // Assert preconditions + VTable.Assert(this.isDispatcherInUse); + + // Run scheduling policies and find thread to run + // Do so only if the dispatcher is in use + do { + + // Run the scavenger if the stopped queue isn't empty. + if (!this.stoppedThreadQueue.IsEmpty()) { + threadSwitchTo = this.scavengerThread; + } + else { + // Find a thread from schedulers to run + threadSwitchTo = FindSchedulerThreadToRun(schedulerThread, + schedulerAction, + currentTime); + + // We no longer need current scheduler's thread + schedulerThread = null; + + // If we found a thread to run we still need to check if we + // need to stop it right a way + if (threadSwitchTo != null) { + + // Check if thread has to be stopped then stop it and get another one + if (!threadSwitchTo.ShouldStop()) { + // We found thread for running + break; + } + else { + // Enqueue stopped thread + EnqueueStoppedThread(threadSwitchTo); + + // Need to retry once more + threadSwitchTo = null; + } + } + else { + // Well we don't have any thread to run just quit + break; + } + } + } while (threadSwitchTo == null); + + return threadSwitchTo; + } + + /// + /// + /// Run scheduler and retrieve a thread to run + /// + /// + /// The current running thread + /// State of the currentThread + /// Current wall clock time. + /// + [NoHeapAllocation] + private void RunScheduler( + Thread currentThread, + ThreadState schedulerAction, + SchedulerTime currentTime) + { + Thread threadSwitchTo = null; + + // Check if we are currently running idle thread + Thread schedulerThread + = IsOneOfMyDispatcherThreads(currentThread) ? null : currentThread; + + // At this point current thread shouldn't be inside of context swictch - we don't allow + // reentrancy and dispatcher has to be in use + VTable.Assert(!currentThread.IsInsideOfContextSwitch()); + VTable.Assert(this.isDispatcherInUse); + + // Before we proceed any further mark a thread as to be inside of context switch + // When calling into the scheduler it is possible that it will put current thread into + // runnable queue so that other dispatcher can pick it up. By marking the thread + // we avoid possible race condition + currentThread.TurnOnInsideOfContextSwitch(); + + // Check if we should stop current thread + if (schedulerThread != null && + (schedulerAction == ThreadState.Stopped || schedulerThread.ShouldStop())) { + + // Enqueue stopped thread + EnqueueStoppedThread(schedulerThread); + + // We no longer need current thread + schedulerThread = null; + + //Don't forget to mark state + schedulerAction = ThreadState.Stopped; + } + + // Run scheduler until we either have a thread to run, or choose + // scavenger thread or go idle + do { + // Mark ourselves active before we try to find a thread + this.MarkActive(); + + // Run scheduling policies to find out thread to run + threadSwitchTo = ChooseThreadOrScavengerToRun(schedulerThread, + schedulerAction, + currentTime); + + // We can no longer use schedulerThread + schedulerThread = null; + + // If we found a thread to run do special work to accomdate scavenger thread + if (threadSwitchTo != null) { + + // If we are about to run scavenger try to transfer stopped threads + if (threadSwitchTo == this.scavengerThread) { + + // Transfer threads only if scavenger is done with a previous set of stopped + // threads - it is no longer running. Otherwise we in danger of scavenger being + // context switched out and its list being in inconsistent state + if (!this.isScavengerRunning) { + + // Stopped thread queue can't be empty + VTable.Assert (!this.stoppedThreadQueue.IsEmpty()); + + // Transfer queue of stopped threads + this.stoppedThreadQueue.DequeueAll(this.scavengerThreadQueue); + this.stoppedThreadQueueLength = 0; + } + + // Don't forget to indicate that scavenger thread is active + this.isScavengerRunning = true; + } + } + else { + // Try to mark ourselves idle if we fail we have to retry + // run scheduler: one of the scheudulers now has a thread + // to run + if (this.TryMarkIdle()) { + // No choice left now but the idle thread + threadSwitchTo = idleThread; + } + } + } while(threadSwitchTo == null); + + // Update the twiddle display. +#if SHOW_TWIDDLE + if (threadSwitchTo != idleThread) { + DisplayThread(color, threadSwitchTo.threadIndex); + } + Twiddle(); +#endif + + // Perform context switch: Thread contex swich releases dispatcher lock + SwitchThreadContextInternal(currentThread, threadSwitchTo); + + // STOP! STOP! STOP! STOP! STOP! STOP! STOP! STOP! STOP! STOP! STOP! + // DON'T TRY TO ACCESS THIS POINTER - WE CAN BE RETURNING ON DIFFERENT + // DISPATCHER. DISPATCHER LOCKS SHOULD BE RELEASED BY NOW! + } + + /// + /// + /// Switch to the new thread context. This works differently depending on + /// whether we are in interrupt mode or not. + /// + /// + /// Thread we are currently executing on + /// Thread switch to + /// + [NoHeapAllocation] + private void SwitchThreadContextInternal(Thread currentThread, Thread threadSwitchTo) + { + // Assert preconditions: currentThread and thread on the processor should be the same + VTable.Assert(currentThread == Thread.CurrentThread); + + // If current thread is not the same as we are switching to: Proceed with the context + // switch. + if (threadSwitchTo != currentThread) { + + // Update statistic + UpdateStats (currentThread, threadSwitchTo); + + // Bind dispatcher to thread and thread to dispatcher + this.runningThread = threadSwitchTo; + threadSwitchTo.Dispatcher = this; + + if (currentThread.context.IsRunning()) { + + // We are running outside of interrupt context. We cannot mark current thread + // as exited context switch because we are still inside of context switch + // SwitchTo will mark thread as outside of context switch + + // Life is good ... + SwitchTo(threadSwitchTo); + + // STOP! STOP! STOP! STOP! STOP! STOP! STOP! STOP! STOP! STOP! STOP! + // DON'T ADD ANY CODE HERE, AS WE COULD BE RETURNING ON DIFFERENT DISPATCHER + return; + } + else { + + // Move over to the new thread context + + TransferToThreadContext(ref currentThread.context, + ref threadSwitchTo.context); + + } + } + else { + + // If we are switching to the same thread don't forget to turn off inside of context + // switch bit + currentThread.TurnOffInsideOfContextSwitch(); + } + + // Assert post conditions: current thread of the processor shouldn't be inside of context switch... + VTable.Assert(!threadSwitchTo.IsInsideOfContextSwitch()); + } + + /// + /// + /// Transfer control from the current to a new thread context, obeying the + /// synchronization requirements + /// + /// + [NoHeapAllocation] + internal unsafe static void TransferToThreadContext(ref ThreadContext from, + ref ThreadContext to) + { + // NOTE: As soon as we call TurnOffInsideOfContextSwitch, + // we will have no valid current thread to point to; the old thread may + // be scheduled on another processor, and the new thread may not be ready to run yet. + + // As a stopgap, we set the current target thread record to be the cpu's boot thread + // record. This will at least ensure that stack limit checks work. Note + // that we are still in a dangerous situation because of many places in the code + // where it casts the Isal.ThreadRecord to a ThreadContext, which will not be valid. + Isa.SetCurrentThread(ref Isa.GetCurrentCpu()->bootThread); + + // Mark current thread that it is ready to be picked up for + // running + from.thread.TurnOffInsideOfContextSwitch(); + + // Wait until our thread is ready to be swapped in + to.thread.WaitUntilReadyForContextSwitch(); + + // Set the current thread to our new context + Isa.SetCurrentThread(ref to.threadRecord); + } + + /// + /// + /// Switch processor to this thread + /// + /// + [NoHeapAllocation] + private void SwitchTo(Thread threadSwitchTo) + { + Thread currentThread = Thread.CurrentThread; + bool needGC; + + // Upate processor statistics + Processor.CurrentProcessor.NumContextSwitches++; + + Monitoring.Log(Monitoring.Provider.Thread, + (ushort)ThreadEvent.SwitchTo, 0, + (uint)threadSwitchTo.context.threadIndex, 0, 0, 0, 0); + + unsafe { + needGC = Transitions.InMutatorState(Processor.GetCurrentThreadContext()); + } + + if (needGC) + { + Processor.SwitchToThreadContext(ref currentThread.context, + ref threadSwitchTo.context); + } + else + { + Processor.SwitchToThreadContextNoGC(ref threadSwitchTo.context); + } + } + + /// + /// + /// Try to mark dispather as idle we only can do it if we still in context switch state + /// + /// + [NoHeapAllocation] + public bool TryMarkIdle() + { + // Assert preconditions: + VTable.Assert(this == Processor.CurrentProcessor.Dispatcher); + + return (int)State.Active == + Interlocked.CompareExchange (ref this.currentState, + (int)State.Idle, + (int)State.Active); + } + + /// + /// + /// Mark dispather as active - should be always called from the thread running on + /// dispatcher + /// + /// + [NoHeapAllocation] + public int MarkActive() + { + return (int)Interlocked.Exchange(ref this.currentState, + (int)State.Active); + } + + + /// + /// + /// Try to mark dispatcher as active with request - should be always called from the thread running on + /// dispatcher + /// + /// + [NoHeapAllocation] + public int MarkActiveWithRequest() + { + return Interlocked.Exchange(ref this.currentState, + (int)State.ActiveWithRequests); + } + + /// + /// + /// Calculate next disaptcher to activate + /// + /// + [NoHeapAllocation] + public static int GetNextDispatcherToActivate() + { + int newIdx; + int oldIdx; + + do { + oldIdx = nextDispatcherToActivate; + newIdx = oldIdx; + + if (++newIdx == numberOfDispatchers) { + newIdx = 0; + } + // Activate disaptcher first + } while ( oldIdx != Interlocked.CompareExchange(ref nextDispatcherToActivate, + newIdx, + oldIdx)); + return newIdx; + } + + /// + /// + /// Activate disaptcher - if it is idle send IPI + /// + /// + [NoHeapAllocation] + private void ActivateDispatcher() + { + // We only want to send IPI if previous dispatcher's state is idle + if (MarkActiveWithRequest() == (int)State.Idle) { + this.myProcessor.ActivateDispatcher(); + } + } + + /// + /// + /// Ilde thread loop + /// + /// + [NoHeapAllocation] + public static void IdleThreadLoop() + { + while (true) { + // Check for a debug break. + bool iflag = Processor.DisableInterrupts(); + try { + Processor.CurrentProcessor.NextSampleIsIdle(); + + // Check if we need to break into debugger + if (DebugStub.PollForBreak()) { + DebugStub.Print("Debugger breakin.\n"); + DebugStub.Break(); + } + } + finally { + //Kernel.Waypoint(114); + Processor.RestoreInterrupts(iflag); + } + + // Halt processor until is interrupted + Processor processor = Processor.CurrentProcessor; + uint statusQuo = processor.NumInterrupts; + while (processor.NumInterrupts == statusQuo) { + // HaltUntilInterrupt may be a nop + Processor.HaltUntilInterrupt(); + } + } + } + + /// + /// + /// Scavenger thread loop + /// + /// + public static void ScavengerThreadLoop() + { + ThreadEntry threadEntry; + ProcessorDispatcher dispatcher = Processor.CurrentProcessor.Dispatcher; + + while (true) { + + // For now disable interrupts: Service thread has a lock that might + // be problematic... + bool iflag = Processor.DisableInterrupts(); + + // Now just go through all threads and service their stop request + while ((threadEntry = dispatcher.scavengerThreadQueue.DequeueHead()) != null) { + + // It is possible that stopping thread is still in a process of running - wait + // until it stops--ready for context switch--and then stop it + threadEntry.Thread.WaitUntilReadyForContextSwitch(); + + // Process stopped call and go to the next one + ThreadLocalServiceRequest.ThreadStopped(threadEntry.Thread); + + // Poll for possible debug break: + if (DebugStub.PollForBreak()) { + DebugStub.Print("Debugger breakin.\n"); + DebugStub.Break(); + } + } + + // Reenable interrupts + Processor.RestoreInterrupts(iflag); + + // We are done in this round. Let's see if there is more we have to do: we will be + // rescheduled by dispather if there is nobody to run and there are more threads + // to clean up + dispatcher.isScavengerRunning = false; + + SwitchContext(ThreadState.Running); + } + } + + /// + /// + /// Enqueue stopped thread + /// + /// + [NoHeapAllocation] + private void EnqueueStoppedThread(Thread currentThread) + { + // Change current state to stopped state + currentThread.ChangeSchedulerState(ThreadState.Stopped); + + // At this point thread can't be on any queue + VTable.Assert(!currentThread.schedulerEntry.Enqueued); + + // Put it on a stopped queue + this.stoppedThreadQueue.EnqueueHead(currentThread.schedulerEntry); + + // Update queue length + this.stoppedThreadQueueLength++; + } + + /// + /// + /// Validated common invariants that should hold true for every entry point + /// to dispatcher, except for addrunnable thred + /// + /// + /// Thread dispatcher is currently bound to" + /// + [NoHeapAllocation] + private void ValidateCommonInvariantsOnEnter(Thread currentThread) + { + // Validate processor invariants + + // Interrupts have to be disabled + VTable.Assert(Processor.InterruptsDisabled()); + + // Validate dispatcher invariants + + // Thread should belong to this dispatcher or if thread is a main kernel thread it can be null + VTable.Assert(currentThread.Dispatcher == this || + currentThread.Dispatcher == null); + + // Validate thread invariants: + + // Thread can't be inside of context switch: + VTable.Assert(!currentThread.IsInsideOfContextSwitch()); + } + + /// + /// + /// Validated common invariant that should hold true for every exit point + /// from dispatcher, except for addrunnable thred + /// + /// + /// Thread dispatcher is currently bound to" + /// + [NoHeapAllocation] + private void ValidateCommonInvariantsOnExit(Thread currentThread) + { + // Validate processor invariants + + // Interrupts have to be disabled + VTable.Assert(Processor.InterruptsDisabled()); + + // Validate dispatcher invariants + + // Thread should belong to this dispatcher or if thread is a first thread it might not + // be bound to dispatcher even after interrupt, this can happen when after interrupt + // thread picks up itself. + VTable.Assert(currentThread.Dispatcher == this || + currentThread.Dispatcher == null); + + // Validate thread invariants: + + // Thread can't be inside of context switch: + VTable.Assert(!currentThread.IsInsideOfContextSwitch()); + } + + /// + /// + /// Validate invariants that should hold true for every entry point + /// to dispatcher on interrupt + /// + /// + /// Thread dispatcher is currently bound to" + /// + [NoHeapAllocation] + private void ValidateInvariantsOnInterruptEnter(Thread currentThread) + { + // Validate common invariants + ValidateCommonInvariantsOnEnter(currentThread); + + // Validate processor invariants + + // We should be on interrupt stack + VTable.Assert(Isa.IsRunningOnInterruptStack); + + // Processor should be inside of interrupt context + VTable.Assert(Processor.InInterruptContext); + + // Validate threads invariants + + // Thread's context can't be running from thread's context point of view + VTable.Assert(!currentThread.context.IsRunning()); + } + + /// + /// + /// Validate invariants that should hold true for every exit point + /// from dispatcher's interrupt handler + /// + /// + [NoHeapAllocation] + private void ValidateInvariantsOnInterruptExit() + { + Thread currentThread = Thread.CurrentThread; + + // Validate common invariants + ValidateCommonInvariantsOnExit(currentThread); + + // Validate processor invariants + + // We should be on interrupt stack + VTable.Assert(Isa.IsRunningOnInterruptStack); + + // Processor should be inside of interrupt context + VTable.Assert(Processor.InInterruptContext); + + // Validate threads invariants + + // Thread's context can't be running from thread's context point of view + VTable.Assert(!currentThread.context.IsRunning()); + } + + + /// + /// + /// Validate invariants that should hold true for entry point + /// to dispatcher through SwitchContext call + /// + /// + /// Thread dispatcher is currently bound to" + /// + [NoHeapAllocation] + private void ValidateInvariantsOnSwitchContexEnter(Thread currentThread) + { + // Validate common invariants + ValidateCommonInvariantsOnEnter(currentThread); + + // Thread's context should be running from thread's context point of view + VTable.Assert(currentThread.context.IsRunning()); + } + + /// + /// + /// Validate invariants that should hold true for exit point + /// from dispatcher's SwitchContext call + /// + /// + [NoHeapAllocation] + private void ValidateInvariantsOnSwitchContextExit() + { + Thread currentThread = Thread.CurrentThread; + + // Validate common invariants + ValidateCommonInvariantsOnExit(currentThread); + + // Thread's context should be running from thread's context point of view + VTable.Assert(currentThread.context.IsRunning()); + } + + /// + /// + /// Validate invariants that should hold true for entry point + /// to dispatcher through AddRunnableThread call + /// + /// + /// Thread dispatcher is currently bound to" + /// + [NoHeapAllocation] + private void ValidateInvariantsOnAddRunnableThreadEnter( + Thread currentThread, + Thread newThread) + { + // Validate processor invariants: + + // Interrupts have to be disabled + VTable.Assert(Processor.InterruptsDisabled()); + + // Validate dispatcher invariants + + // Thread should belong to this dispatcher or if thread is a kernel thread it can be null + VTable.Assert(currentThread.Dispatcher == this || + currentThread.Dispatcher == null); + + // Validate thread invariants: + + // Threads can't be inside of context switch: + VTable.Assert(!currentThread.IsInsideOfContextSwitch()); + + // New thread can't be a dispatcher thread + VTable.Assert(!IsIdleThread(newThread)); + } + + /// + /// + /// Validate invariants that should hold true for entry point + /// from dispatcher's AddRunnableThread call. At this point we can't make any + /// assumptions about new thread + /// + /// + /// Thread dispatcher is currently bound to" + /// + [NoHeapAllocation] + private void ValidateInvariantsOnAddRunnableThreadExit( + Thread currentThread, + Thread newThread) + { + + // Interrupts have to be disabled + VTable.Assert(Processor.InterruptsDisabled()); + + // Validate dispatcher invariants + + // Thread should belong to this dispatcher or if thread is a main kernel thread it can be null + VTable.Assert(currentThread.Dispatcher == this || + currentThread.Dispatcher == null); + + // Validate thread invariants: + + // Current thread can't be inside of context switch + VTable.Assert(!currentThread.IsInsideOfContextSwitch()); + + } + + /// + /// + /// Assert that we are running on the processor we are thinking we are running on + /// + /// + [Conditional("DEBUG")] + [NoHeapAllocation] + public void AssertCurrentProcessor(int cpu) + { + VTable.Assert(Processor.GetCurrentProcessorId() == cpu); + } + + /// + /// + /// Record debugging event + /// + /// + /// Current thread + /// Thread we are switchint to + /// + [Inline] + [NoHeapAllocation] + private void UpdateStats(Thread currentThread, Thread threadSwitchTo) + { +#if THREAD_TIME_ACCOUNTING + + ulong now_a = this.mProcessor.CycleCount; + + currentThread.context.executionTime += now_a - + currentThread.context.lastExecutionTimeUpdate; + + threadSwitchTo.context.lastExecutionTimeUpdate = now_a; +#endif + } + + private static unsafe void InitializeTwiddle() + { +#if SHOW_TWIDDLE + if (Platform.ThePlatform.TwiddleSpinBase != 0) { + // [pbar] Scheduler visualization hack + // we use unsafe access to minimize the scheduler overhead. + screenMem = IoMemory.MapPhysicalMemory(0xb8000, 80*50*2, true, true); + screenPos = 0; + screenPtr = (ushort *)screenMem.VirtualAddress; + tpos = 0; + } +#endif + } + + [Inline] + [NoHeapAllocation] + private static unsafe void DisplayThread(ushort color, int id) + { +#if SHOW_TWIDDLE + if (screenPtr != null) { + screenPtr[screenPos] = (ushort)(color | ('@' + id)); + screenPos = (screenPos + 1) % 80; + } +#endif + } + + [Inline] + [NoHeapAllocation] + private static unsafe void Twiddle() + { +#if SHOW_TWIDDLE + if (screenPtr != null) { + ushort twiddle; + switch (tpos) { + default: + case 0: twiddle = '|'; break; + case 1: twiddle = '/'; break; + case 2: twiddle = '-'; break; + case 3: twiddle = '\\'; break; + } + screenPtr[screenPos] = (ushort)(0xe000 | twiddle); + tpos = ++tpos & 3; + } +#endif + } + + /// + /// + /// Affinity consts states + /// + /// + internal enum Affinity + { + All = -1 + } + + /// + /// + /// Dispatcher State + /// + /// + private enum State + { + Halted, + Idle, + ActiveWithRequests, + Active + }; + + /// + /// True if the processor is in use, including halted during idle. + /// False only if the processor is down during system shutdown. + /// + private bool isDispatcherInUse; + + /// + /// True only on boot processor during shutdown when all other processors are down + /// + private bool isActingForEntireSystem; + + /// + /// Only used when isActingForEntireSystem is true, the next processorId to simulate + /// + private int nextVirtualProcessorId; + + /// Number of dispatchers in the system + private static int numberOfDispatchers = 0; + + /// Next dispatcher to activate + private static int nextDispatcherToActivate; + + /// Threre is a single dispatch lock per processor dispatcher + private static Scheduler scheduler; + + /// Dispatcher Id + private int id; + + /// Processor object to which dispatcher is bound + private Processor myProcessor; + + /// Thread queue is used to collect stopped threads + private ThreadQueue stoppedThreadQueue; + + /// Length of the stopped thread queue + private int stoppedThreadQueueLength = 0; + + /// Length of the stopped thread queue + private const int maxStoppedThreadQueueLength = 64; + + /// Thread queue is used to scavanged stopped threads + private ThreadQueue scavengerThreadQueue; + + /// A pool of idle thread used by dispatcher + private Thread idleThread; + + /// A dispatcher helper thread that is performing scavanging and other types of work + private Thread scavengerThread; + + /// A current thread currently running on the processor + private Thread runningThread; + + /// State of the dispatcher - (idle, halted, running) + private int currentState = (int) State.Active; + + /// State of scavenger thread + private bool isScavengerRunning; + +#if SHOW_TWIDDLE + //; Memory Interface to allocate physical memory + private static IoMemory screenMem; + + /// Possition twiddle on the screen + private static int screenPos; + + /// Virtual address of the screen memory + private static unsafe ushort * screenPtr; + + /// Current possition inside of array + private static int tpos; + + private ushort color; +#endif + + } +} diff --git a/base/Kernel/Singularity/Scheduling/Rialto/GraphNode.cs b/base/Kernel/Singularity/Scheduling/Rialto/GraphNode.cs deleted file mode 100644 index 73b7ae3..0000000 --- a/base/Kernel/Singularity/Scheduling/Rialto/GraphNode.cs +++ /dev/null @@ -1,88 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: GraphNode.cs -// -// Note: -// - -using System; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling.Rialto -{ - /// - /// Nodes represent blocks of the Rialto pre-computed schedule. A node has a - /// period and slice, accounting for its default activity, time to origin in - /// the schedule, next execution time, and a set of variables which form the - /// tree. A node tree has strictly increasing periods at each level in the - /// tree (it doubles), and a node can have a `next' node (at the same level), - /// can branch into left and right nodes (at one level below), or can account - /// free time (and have neither). The type field stores those cases as well - /// as the direction of the next branch to follow (Used, Free, LeftBranch, - /// RightBranch). When traversing the tree, the next node can be determined - /// by the following steps: (1) If the current node has a next node, select it - /// next. (2) Otherwise, if it has type Left or Right branch, follow the - /// branch in that direction, and toggle which branch is stored. (3) If it is - /// a Free node, start back at the root of the tree. - /// - public class GraphNode - { -#region Constants and Enumerations - - public enum NodeType - { - Free = 0, - Used = 1, - LeftBranch = 2, - RightBranch = 3 - }; - - public static readonly int MaxNumReservations = 250; - - public static readonly int MaxProxySplits = 10; - public static readonly int MaxMarcelTries = 2000; - -#endregion - - public NodeType Type; // Free, Used, BRANCH, (Includes mark of what next branch to take is) etc.... - public TimeSpan Slice; // node's slice (time units) - public TimeSpan Period; // node's period (time units) - public DateTime NextExec; // start of Next or current CPU slice - public TimeSpan TimeToOrigin;// time to the first node of the tree - public RialtoActivity DefaultActivity; // default activity for Used nodes - - public int ReservCount; // valid entries in the array above - - public GraphNode Next; // Next sibling - public GraphNode Left; // left and right children of a BRANCH node - public GraphNode Right; // leaf nodes have left == right == Next == null - public GraphNode SameActivityNext; // Next node of the same activity - public ReservationSlice[] ReservationArray; // array of pointers to CPU reservations - - public GraphNode() - { - ReservationArray = new ReservationSlice[MaxNumReservations]; - } - - public static GraphNode InitTree() - { - GraphNode node = new GraphNode(); - - node.Type = NodeType.Free; - node.Slice = CpuResource.MaxPeriod; - node.Period = CpuResource.MaxPeriod; - node.TimeToOrigin = CpuResource.MaxPeriod; - node.NextExec = new DateTime(0); - node.ReservCount = 0; - node.Next = null; - node.Left = null; - node.Right = null; - node.SameActivityNext = null; - return node; - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Rialto/NodeProxy.cs b/base/Kernel/Singularity/Scheduling/Rialto/NodeProxy.cs deleted file mode 100644 index 9a622c4..0000000 --- a/base/Kernel/Singularity/Scheduling/Rialto/NodeProxy.cs +++ /dev/null @@ -1,54 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: NodeProxy.cs -// -// Note: -// - -using System; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling.Rialto -{ - /// - /// NodeProxies are created as temporary activity proxies for use in building a new - /// scheduler plan. The slice, period, and associated activity are relatively easy - /// to digest, but there are two variables TreeLevel and FreeLevel which are a bit - /// more. FreeLevel refers to the row in the SchedulerPlan which this node should be - /// in based on its period, and TreeLevel represents the exact position in a full - /// binary tree of the schedule. It is computed by 2^row + position in row, and - /// corresponds to the position in the FreeSlots[][] (see below). (For that matter, - /// FreeLevel refers to which FreeSlots top level array its time is subtracted from). - /// - public class NodeProxy : ICloneable - { - - public int TreeLevel; //TODO: BAD NAME - public int FreeLevel; - public TimeSpan Slice, Period; - public RialtoActivity AssociatedActivity; - - public NodeProxy() - { - } - -#region ICloneable Members - - public object Clone() - { - NodeProxy newObj = new NodeProxy(); - newObj.TreeLevel = TreeLevel; - newObj.FreeLevel = FreeLevel; - newObj.Slice = Slice; //NOTE: A copy of the reference. But OK since originally was a pointer. - newObj.Period = Period; - newObj.AssociatedActivity = AssociatedActivity; - return newObj; - } - -#endregion - } -} diff --git a/base/Kernel/Singularity/Scheduling/Rialto/OneShotReservation.cs b/base/Kernel/Singularity/Scheduling/Rialto/OneShotReservation.cs deleted file mode 100644 index 92dde23..0000000 --- a/base/Kernel/Singularity/Scheduling/Rialto/OneShotReservation.cs +++ /dev/null @@ -1,787 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: OneShotReservation.cs -// -// Note: -// - -using System; -using System.Collections; -using System.Diagnostics; -using System.Threading; -using Microsoft.Singularity; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling.Rialto -{ - /// - /// OneShotReservations are created from tasks for the calling thread. - /// It has fields to track the thread calling and the thread waiting on, - /// the activity associated when it becomes an activity reservation, its - /// surrounding reservation, its place on the free reservation list, - /// general bookkeeping, bookkeeping for possible stolen time, and - /// references for the general reservation list. - /// - public class OneShotReservation : ISchedulerTask - { - - static OneShotReservation guaranteedReservations = null; - public static OneShotReservation GuaranteedReservations - { - get { return guaranteedReservations; } - } - - static OneShotReservation idleReservations = null; - public static OneShotReservation IdleReservations - { - get { return idleReservations; } - } - - static OneShotReservation currentReservation = null; - public static OneShotReservation CurrentReservation - { - get { return currentReservation; } - //set { currentReservation = value; } - } - - public Hashtable resourcesGranted = new Hashtable(); - public override Hashtable ResourcesGranted - { - get { - resourcesGranted[CpuResource.Provider().ResourceString] = CpuResource.Provider().TimeToCpu(InitialEstimate); - return resourcesGranted; - } - } - - private Task enclosingTask; - public override Task EnclosingTask - { - get { return enclosingTask; } - set { enclosingTask = value; } - } - - public OneShotReservation SurroundingReservation; - public OneShotReservation FreeListNext; - public RialtoThread ReservTask; - - TimeSpan constraintExecution; - public TimeSpan ConstraintExecution - { - get { return constraintExecution; } - } - - TimeSpan initialThreadExecution; - - public DateTime Start, Deadline; - public TimeSpan Estimate, RelativeDeadline; - - public TimeSpan InitialEstimate; // test only - public QueueType MyQueueType; - public bool Guaranteed; - public bool Valid; - public bool Satisfied; - - // Reservations can be made for Threads XOR Activities!. - public RialtoThread OriginalThread; - // always the constraint thread -- non-null until the reservation is complete - //public RialtoThread ActiveThread; - // the above, or the thread blocking it - //public int ActiveThreadEpoch; - public RialtoActivity AssociatedActivity; // the two fields above must be null! - - - public OneShotReservation Next; - public OneShotReservation Previous; - - public TimeSpan InheritedEstimate; // Stolen => Inherited, no longer critical - public int ResolutionEpoch; // Stolen => Resolution, since not stealing. - - public int ReferenceCount; - - public OneShotReservation() - { - } - - public void Clear() - { - //ActiveThread = null; - //ActiveThreadEpoch = 0; - constraintExecution = new TimeSpan(0); //Move to enclosing class - initialThreadExecution = new TimeSpan(0); //Move to enclosing class - Deadline = new DateTime(0); - RelativeDeadline = new TimeSpan(0); - Estimate = new TimeSpan(0); - FreeListNext = null; - Guaranteed = false; - InitialEstimate = new TimeSpan(0); - Next = null; - OriginalThread = null; - AssociatedActivity = null; - Previous = null; - MyQueueType = QueueType.NoQueue; - ReferenceCount = 0; - ReservTask = null; - Satisfied = false; - Start = new DateTime(0); - SurroundingReservation = null; - ResolutionEpoch = 0; - InheritedEstimate = new TimeSpan(0); - Valid = false; - if (enclosingTask != null) { - enclosingTask.ClearSchedulerTask(); - enclosingTask = null; - } - } - - // find the runnable thread of a reservation: - public RialtoThread GetRunnableThread() - { -// RialtoThread thread; -// - if (AssociatedActivity != null) { - // if an activity reservation - return AssociatedActivity.GetRunnableThread(); - } - - Debug.Assert(OriginalThread != null); - -// if ((ActiveThread == OriginalThread) && -// (ActiveThread.EnclosingThread.ThreadState == System.Threading.ThreadState.Running)) -// return ActiveThread; -// -// thread = OriginalThread; // search for active thread starting w/ original thread -// -// if ((ActiveThread != null) && -// (ActiveThread.Epoch == ActiveThreadEpoch)) { -// if (ActiveThread.EnclosingThread.ThreadState == System.Threading.ThreadState.Running) { -//#if DEBUG_MUTEX -// DebugStub.WriteLine("Mutex inheritance (Epoch ok): Orig {0} Active {1} GO ON", -// thread.Id, ActiveThread.Id); -//#endif -// return ActiveThread; -// } -// } - //CKillian -- can't do this because of the array of blockedOn. - // else if (ActiveThread.EnclosingThread.IsSleeping()) { - // // more efficient: search for active thread starting w/ current active thread - // thread = ActiveThread; - // } - // else { - // Debug.Assert(false); - // } - //} - - Thread temp = /*Active*/OriginalThread.EnclosingThread.GetRunnableBeneficiary(); - if (temp == null) { - return null; - } - else { - return (RialtoThread)temp.SchedulerThread; - } - - } - - public void StopThreadExecution(RialtoThread thread) - { - Debug.Assert(thread.ExecutionTime >= initialThreadExecution); - constraintExecution += thread.ExecutionTime - initialThreadExecution; - //Between two reservations which are atomically stopped/started, some - // time is charged against the default parent resource container. This - // is because I changed the way we account for tasks to do the general - // rule we wanted. In particular, I update the scheduling status just - // before resolving a new reservation. Not sure what the right fix is, - // so I'm leaving it. The assertion below will reveal the "error." The - // old code to track CPU usage is still active under constraintExecution, - // but isn't presently exposed to the applications. Currently UpdateSchedulingStatus() - // updates the CPU resources used for the whole task stack. - //Debug.Assert(CpuResource.Provider().TimeToCpu(constraintExecution).Cycles == ((CpuResourceAmount)enclosingTask.resourcesUsed[CpuResource.Provider().ResourceString]).Cycles); - //EnclosingTask.AddResourceAmountUsed(CpuResource.Provider().ResourceString, CpuResource.Provider().TimeToCpu(constraintExecution)); - } - - public void StartThreadExecution(RialtoThread thread) - { - initialThreadExecution = thread.ExecutionTime; - } - - public void StartThreadExecution(RialtoThread thread, TimeSpan delta) - { - initialThreadExecution = thread.ExecutionTime + delta; - } - - public static void BeginConstraintBeforeWait(RialtoThread thread, - bool endPrevious, - TimeConstraint timeConstraint, - DateTime timeNow) - { - OneShotReservation reservation; - - Debug.Assert(thread == RialtoScheduler.GetCurrentThread()); - Debug.Assert(!Processor.InterruptsDisabled()); - if (endPrevious) { - bool iflag = Processor.DisableInterrupts(); - EndPreviousConstraint(thread, timeNow); - Processor.RestoreInterrupts(iflag); - } - - RialtoScheduler.UpdateSchedulingStatus(); - - reservation = thread.PendingReservation; - - reservation.StartThreadExecution(thread, (timeNow - RialtoScheduler.SchedulingTime)); - reservation.constraintExecution = new TimeSpan(0); - reservation.Start = timeConstraint.Start; - reservation.Deadline = timeConstraint.Deadline; - reservation.RelativeDeadline = timeConstraint.RelativeDeadline; - reservation.Estimate = timeConstraint.Estimate; - reservation.Valid = reservation.Guaranteed = true; - reservation.MyQueueType = QueueType.NoQueue; - reservation.ReservTask = thread; - reservation.OriginalThread = /*reservation.ActiveThread =*/ thread; - thread.AddRef(); - //thread.AddRef(); - //reservation.ActiveThreadEpoch = thread.Epoch; - reservation.AssociatedActivity = null; - reservation.Next = reservation.Previous = null; - } - - // DI -- this is a new function used in both Begin and EndConstraint - public static bool EndPreviousConstraint(RialtoThread thread, DateTime timeNow) - { - OneShotReservation reservation; - OneShotReservation reservationNext; - bool success; - QueueType tempQueue; //Value assigned but never used. - - Debug.Assert(Processor.InterruptsDisabled()); - - reservation = thread.ReservationStack; - - success = (timeNow <= reservation.Deadline); - RialtoScheduler.UpdateSchedulingStatus(); - reservation.StopThreadExecution(thread); - - Debug.Assert(reservation.ConstraintExecution.Ticks >= 0); -// if (timeTaken.Ticks != 0) { -// timeTaken = reservation.ConstraintExecution; -// Debug.Assert(timeTaken.Ticks >= 0); //CK: Added = because VPC returns 0 sometimes. -// } - - thread.ReservationStack = reservation.SurroundingReservation; - reservation.SurroundingReservation = null; - - if (currentReservation == reservation) { - // note that it might be the case that - // a reservation for the task id exist on the guaranteed Q but the - // the actual (executed) constraint is in the UnfinishedQ - currentReservation = null; // ???????????? - RialtoScheduler.Reschedule(); - } - Debug.Assert(reservation.OriginalThread == thread); - Debug.Assert(reservation.Start <= timeNow); - - if (thread.ReservationStack == null) { - if (reservation.Estimate >= RialtoScheduler.MinSlice) { - // the constraint used less than estimated: - Debug.Assert(reservation.MyQueueType == QueueType.GuaranteedQueue); - //Debug.Assert((reservation.ActiveThread == null) || - // (reservation.ActiveThread.Epoch != reservation.ActiveThreadEpoch) || - // (reservation.ActiveThread == reservation.OriginalThread)); - // reservation.ActiveThread.ReleaseProtected(); - reservation.OriginalThread.ReleaseProtected(); - reservation.AssociatedActivity = reservation.OriginalThread.AssociatedActivity; // and make it an activity reserv - reservation.OriginalThread = null; - //reservation.ActiveThread = null; - reservation.ReservTask = null; - RialtoScheduler.ActivityObjAddRef(reservation.AssociatedActivity); - // leave it on whatever Q it happens to be on - } - else { - reservation.DequeueReservation(); // from whatever Q it happens to be on - } - } - else { - tempQueue = reservation.MyQueueType; - reservationNext = thread.ReservationStack; - reservationNext.Estimate += reservation.Estimate; - reservationNext.constraintExecution += reservation.constraintExecution; - reservationNext.StartThreadExecution(thread); - Debug.Assert(((reservation.MyQueueType == QueueType.GuaranteedQueue) && - (reservationNext.MyQueueType == QueueType.NoQueue)) || - ((reservation.MyQueueType == QueueType.UnfinishedQueue) && - ((reservationNext.Estimate.Ticks <= 0) || (reservationNext.MyQueueType != QueueType.UnfinishedQueue)))); - reservation.Estimate = new TimeSpan(0); - Debug.Assert(reservation.Next != null); - reservation.DequeueReservation(); - ReplaceOnEarliestDeadlineFirst(reservationNext, timeNow); - - } - // fprintf(stdout,"EndPrevConstr 0x%x %d\n", reservation, reservation.ReferenceCount); - reservation.ReleaseReservationProtected(); - - Scheduler.LogEndConstraint(); - - if (success == false) { - Scheduler.LogSchedulerLate(); - } - - // DebugStub.WriteLine("EndPreviousConstraint() {0} constraintExecution: {1}", success?"SUCCESS":"FAILURE", reservation.constraintExecution.Ticks); - - return success; - } - - - public static bool ResolveConstraint(RialtoThread thread) - { - TimeSpan timeLeft, ownNodesTimeToSteal = new TimeSpan(1); - DateTime start, deadline; - TimeSpan timeInherited; - OneShotReservation pendingReservation = thread.PendingReservation; - OneShotReservation reservationPrevious; - DateTime timeNow = SystemClock.GetKernelTime(); - bool ok = false; - - Debug.Assert(Processor.InterruptsDisabled()); - Debug.Assert(pendingReservation != null); - - Scheduler.LogResolveConstraint(); - - thread.PendingReservation = null; // just clean the place - - Debug.Assert(pendingReservation.Start.Ticks >= 0); - start = pendingReservation.Start; - if (start < timeNow) { - start = timeNow; - } - Debug.Assert(pendingReservation.Deadline.Ticks >= 0); - deadline = pendingReservation.Deadline; - if (deadline.Ticks == 0) { - deadline = timeNow + pendingReservation.RelativeDeadline; - } - - if (thread.ReservationStack != null) { - OneShotReservation reservation = thread.ReservationStack; - while (!reservation.Valid && ((reservation = reservation.SurroundingReservation)!= null)) { - // no body. - } - - if (reservation!= null) { - deadline = RialtoScheduler.minTime(deadline, reservation.Deadline); - } - } - - // update data ure - pendingReservation.Start = start; - pendingReservation.Deadline = deadline; - //TODO: Should be for SIM ONLY! - Scheduler.LogReservationId(pendingReservation); // SIM only - if (start + pendingReservation.Estimate > deadline) { - // DebugStub.WriteLine("RialtoScheduler::ResolveConstraint FAIL - start({0}) + pendingReservation.Estimate({1}) > deadline({2})", start.Ticks, pendingReservation.Estimate.Ticks, deadline.Ticks); - ok = false; - goto Failure; - } - timeInherited = new TimeSpan(0); - RialtoScheduler.ResolutionAttempt++; // mark a new resolution epoch - - RecurringReservation recurringReservation = thread.AssociatedActivity.MyRecurringCpuReservation; - RialtoScheduler.ReserveSlices(start, deadline, pendingReservation.Estimate, - thread, out timeLeft, ref timeInherited, - pendingReservation, (recurringReservation != null?recurringReservation.AssignedNodes:null), timeNow); - - if (timeLeft.Ticks == 0) { - // Success: - goto Success; - } - - RialtoScheduler.ReserveSlices(start, deadline, timeLeft, - thread, out timeLeft, ref timeInherited, - pendingReservation, RialtoScheduler.FreeNodes, timeNow); - if (timeLeft.Ticks <= 0) { - // Success: - goto Success; - } - // DebugStub.WriteLine("RialtoScheduler::ResolveConstraint FAIL - couldn't find time in schedule"); - Failure: - // Failure: - pendingReservation.Valid = false; - pendingReservation.Start = timeNow; // no waiting if failed - pendingReservation.Estimate = new TimeSpan(0); - goto Exit; - - Success: - - // DebugStub.WriteLine("RialtoScheduler::ResolveConstraint SUCCESS"); - InheritAndReplaceOnEarliestDeadlineFirst(thread.ReservationStack, timeInherited, timeNow); - ok = true; - Exit: - pendingReservation.InitialEstimate = pendingReservation.Estimate; - reservationPrevious = thread.ReservationStack; - pendingReservation.SurroundingReservation= thread.ReservationStack; - thread.ReservationStack = pendingReservation; - RialtoScheduler.AddRefReservation(pendingReservation); - if (reservationPrevious != null) { - reservationPrevious.constraintExecution += pendingReservation.initialThreadExecution - - reservationPrevious.initialThreadExecution; - } - // - // Reschedule() called if currently we are executing on a pendingReservation - // and the EarliestDeadlineFirst top changed - // or the current pendingReservation goes into the idle list - // solve in SetStateWaiting - // - pendingReservation.EnqueueReservation(timeNow); - RialtoScheduler.DirectSwitchOnConstraint(); - -#if DEBUG_RESERV - PrintReservations(pendingReservation, sc, timeNow); -#endif - return ok; - } - - void EnqueueReservation(DateTime timeNow) - { - OneShotReservation temp; - - Debug.Assert((AssociatedActivity != null) || - (OriginalThread != null)); - - // Infeasible or (Estimate == 0) Reservations: - if (Estimate <= RialtoScheduler.AFewSlice) { - RialtoActivity activity; - Debug.Assert(OriginalThread != null); - Debug.Assert(AssociatedActivity == null); - activity = OriginalThread.AssociatedActivity; - MyQueueType = QueueType.UnfinishedQueue; - if (activity.UnfinishedConstraints == null) { - Next = Previous = this; - activity.UnfinishedConstraints = this; - } - else { - // no order enforced - Next = activity.UnfinishedConstraints; - Previous = activity.UnfinishedConstraints.Previous; - activity.UnfinishedConstraints.Previous.Next = this; - activity.UnfinishedConstraints.Previous = this; - activity.UnfinishedConstraints = this; // optional - } - // DebugStub.WriteLine("Add: EnqueueReserve 1 0x{0:x} {1}", this, ReferenceCount); - RialtoScheduler.AddRefReservation(this); - return; - } - // Debug.Assert(Valid); - - // Idle Reservations: - if (Start > timeNow + RialtoScheduler.AFewSlice) { - // take thread off activity RoundRobin queue and simulate a sleep - // i.e. don't set a timer interrupt - - OriginalThread.InternalSetStateWaiting(DateTime.MaxValue); - -#if DEBUG_START_RESERV - DebugStub.WriteLine("Put reservation {0}:{1} in IdleQueue\n", OriginalThread, - ReservationId); -#endif - Debug.Assert(OriginalThread != null); - MyQueueType = QueueType.IdleQueue; - - if (idleReservations == null) { - Next = Previous = this; - idleReservations = this; - } - else { - temp = idleReservations; - if (Start < temp.Start) { - idleReservations = this; - } - else { - do { - temp = temp.Next; - } while (temp != idleReservations && - Start >= temp.Start); // >= is compulsory to keep it 'FIFO' - } // insert before 'temp' - Next = temp; - Previous = temp.Previous; - temp.Previous.Next = this; - temp.Previous = this; - } - // fprintf(stdout,"Add: EnqueueReserve 2 0x%x %d\n", this, ReferenceCount); - RialtoScheduler.AddRefReservation(this); - return; - } - - // Guaranteed OneShotReservation: - MyQueueType = QueueType.GuaranteedQueue; - if (GuaranteedReservations == null) { - Next = Previous = this; - guaranteedReservations = this; - } - else { - temp = GuaranteedReservations; - if (Deadline < GuaranteedReservations.Deadline) { - guaranteedReservations = this; - } - else { - do { - temp = temp.Next; - } while (temp != guaranteedReservations && - Deadline > temp.Deadline); // > is compulsory to keep it 'FIFO' - } // insert before 'temp' - Next = temp; - Previous = temp.Previous; - temp.Previous.Next = this; - temp.Previous = this; - } - RialtoScheduler.AddRefReservation(this); -#if DEBUG_RESERV - PrintQueueReserv(guaranteedReservations, timeNow); //TODO: PrintQueueReserv -#endif - return; - } - - - static void ReplaceOnEarliestDeadlineFirst(OneShotReservation reservation, DateTime timeNow) - { - while (reservation != null) { - if (reservation.Estimate.Ticks > 0) { - if (reservation.MyQueueType == QueueType.NoQueue) { - reservation.EnqueueReservation(timeNow); - } - else { - Debug.Assert(reservation.MyQueueType == QueueType.GuaranteedQueue); - } - break; - } - if (reservation.MyQueueType == QueueType.NoQueue) { - reservation.EnqueueReservation(timeNow); - } - else { - Debug.Assert(reservation.MyQueueType == QueueType.UnfinishedQueue); - } - reservation = reservation.SurroundingReservation; - } - } - - public static bool SatisfyAcceptedConstraint(DateTime timeNow) - { - OneShotReservation reservation; - - if (currentReservation != null) { - currentReservation.Estimate -= timeNow - RialtoScheduler.SchedulingTime; - if (currentReservation.Estimate.Ticks <= 0) { - RialtoThread tempThread = currentReservation.OriginalThread; - currentReservation.DequeueReservation(); - if (tempThread != null) { - // activity reservations vanish here - //Debug.Assert(currentReservation.ActiveThread == RialtoScheduler.GetCurrentThread()); // only thread reserv are added to - currentReservation.EnqueueReservation(RialtoScheduler.SchedulingTime); // the UnfinishedConstraints list of the OriginalThread's Activ - OneShotReservation.ReplaceOnEarliestDeadlineFirst(currentReservation.SurroundingReservation, RialtoScheduler.SchedulingTime); - } - } - } - - reservation = guaranteedReservations; - while (reservation != null) { - if (!RialtoScheduler.CheckReservation(reservation, timeNow)) { - return false; - } - reservation = reservation.Next; - if (reservation == guaranteedReservations) { - break; - } - } - reservation = idleReservations; - while (reservation != null) { - if (!RialtoScheduler.CheckReservation(reservation, timeNow)) { - return false; - } - reservation = reservation.Next; - if (reservation == idleReservations) { - break; - } - } - return true; - } - - public static void UpdateCurrentReservation() - { - if ((currentReservation != null) && - (currentReservation.Estimate <= RialtoScheduler.AFewSlice)) { - // slice used for a reservation (CK: & estimate now empty) - RialtoThread tempThread = currentReservation.OriginalThread; - currentReservation.DequeueReservation(); - if (tempThread != null) { - // activity reservations vanish here - currentReservation.EnqueueReservation(RialtoScheduler.SchedulingTime); // the UnfinishedConstraints list of the OriginalThread's Activ - ReplaceOnEarliestDeadlineFirst(currentReservation.SurroundingReservation, RialtoScheduler.SchedulingTime); - } - } - - } - - public static void FreshenReservationQueues() - { - OneShotReservation tempReservation; - - // Move 'idle' reservations to the 'active' reservations list, if necessary. - // A reservation is 'idle' until its Start time is reached. - // The 'active' reservation list is ordered 'Earliest Deadline Firts'. - // The 'idle' queue is ordered Earliest Start First. - while ((tempReservation = IdleReservations) != null) { - if (RialtoScheduler.SchedulingTime + RialtoScheduler.AFewSlice < tempReservation.Start) { - break; - } - tempReservation.DequeueReservation(); // Remove OneShotReservation from the Idle Queue - tempReservation.EnqueueReservation(RialtoScheduler.SchedulingTime); // Earliest Deadline First in the (Non)Guaranteed Queue - RialtoScheduler.EnqueueRunThread(tempReservation.OriginalThread); -#if DEBUG_START_RESERV - DebugStub.WriteLine("Get reservation {0}:{1} from IdleQueue", tempReservation.OriginalThread, - tempReservation.ReservationId); -#endif - } - - // Remove the active reservations from the guaranteed and nonguaranteed queues - // if their Deadline is <= CurrentTime. - while ((tempReservation = guaranteedReservations) != null) { - if (RialtoScheduler.SchedulingTime + RialtoScheduler.AFewSlice < tempReservation.Deadline) { - break; - } - // fprintf(stdout,"Add: Reschedule Guaranteed Deadline 0x%x %d\n", tempReservation, tempReservation.ReferenceCount); - RialtoScheduler.AddRefReservation(tempReservation); - tempReservation.DequeueReservation(); // Remove OneShotReservation from the Guaranteed Queue - if ((tempReservation.OriginalThread != null) && // if a thread reservation - (tempReservation.OriginalThread.AssociatedActivity != null)) { - // if a thread reservation - tempReservation.Estimate = new TimeSpan(0); - tempReservation.EnqueueReservation(RialtoScheduler.SchedulingTime); // add it to the Pending End Constraint List - } - // fprintf(stdout,"Reschedule Guaranteed Deadline off 0x%x %d\n", tempReservation, tempReservation.ReferenceCount); - tempReservation.ReleaseReservationProtected(); - } - - } - - int ReleaseReservationProtected() - { - return RialtoScheduler.ReleaseReservationProtected(this); - } - - public void DequeueReservation() //TODO: Should this be a resource container function? - { - Debug.Assert(Processor.InterruptsDisabled()); - OneShotReservation next; - // OneShotReservation pSurrounding = null; - - Debug.Assert((AssociatedActivity != null) || - (OriginalThread != null)); - - if (Next != this) { - //If there's another reservation, remove us from the queue - Next.Previous = Previous; - Previous.Next = Next; - next = Next; - Debug.Assert(next.Deadline.Ticks > 0); - } - else { - //Otherwise we are only reservation. - Debug.Assert(Previous == this); - next = null; - } - - if (this == GuaranteedReservations) { - //Take care when we're the head of the list. - guaranteedReservations = next; -#if DEBUG_RESERV - DebugStub.WriteLine("Dequeue {0}: {1}", ReservationId, Estimate); - // Debug.Assert(Criticality != CRITICAL || !Valid || Estimate ==0); - PrintQueueReserv(GuaranteedReservations, SystemClock.GetKernelTime()); -#endif - } - else if (this == IdleReservations) { - //Or maybe we're the head of this list (but definitely not both). - idleReservations = next; - } - else if ((OriginalThread != null) && - (this == OriginalThread.AssociatedActivity.UnfinishedConstraints)) { - //Or else we might be the head of the unfinished constraints list for the activity associated with our original thread. - OriginalThread.AssociatedActivity.UnfinishedConstraints = next; - } // else { we're OK! } - - Next = Previous = null; - // DebugStub.WriteLine("DequeueReservation 0x{0:x} {1}", this, ReferenceCount); - MyQueueType = QueueType.NoQueue; - ReleaseReservationProtected(); - } - - public static void FindRunnableReservation(ref RialtoThread currentThread) - { - currentReservation = guaranteedReservations; - while (currentReservation != null) { - if ((currentThread = currentReservation.GetRunnableThread()) != null) { - break; - } - currentReservation = currentReservation.Next; - if (currentReservation == guaranteedReservations) { - break; - } - } - } - - static void InheritAndReplaceOnEarliestDeadlineFirst(OneShotReservation reservation, - TimeSpan timeInherited, - DateTime timeNow) - { - OneShotReservation current = null; - - while (reservation != null) { - if (reservation.Estimate.Ticks > 0) { - if (current == null) { - Debug.Assert(reservation.MyQueueType != QueueType.NoQueue); - current = reservation; - } - else { - Debug.Assert(reservation.MyQueueType == QueueType.NoQueue); - } - } - else if (reservation.MyQueueType != QueueType.NoQueue) { - Debug.Assert (current != null || reservation.MyQueueType == QueueType.UnfinishedQueue); - reservation.DequeueReservation(); - } - if (reservation.ResolutionEpoch == RialtoScheduler.ResolutionAttempt) { - Debug.Assert(timeInherited >= reservation.InheritedEstimate); - timeInherited -= reservation.InheritedEstimate; - Debug.Assert(reservation.Estimate >= reservation.InheritedEstimate); - reservation.Estimate -= reservation.InheritedEstimate; - } - reservation = reservation.SurroundingReservation; - } - if (current != null) { - if (current == CurrentReservation) { - current.Estimate -= timeNow - RialtoScheduler.SchedulingTime; - currentReservation = null; - RialtoScheduler.Reschedule(); - } - current.DequeueReservation(); - } - } - - public static void InheritOnEarliestDeadlineFirst(OneShotReservation reservation, - TimeSpan timeToInherit) - { - while (reservation != null && timeToInherit.Ticks > 0) { - if (reservation.ResolutionEpoch == RialtoScheduler.ResolutionAttempt) { - Debug.Assert(timeToInherit >= reservation.InheritedEstimate); - reservation.Estimate -= reservation.InheritedEstimate; - timeToInherit -= reservation.InheritedEstimate; - } - reservation = reservation.SurroundingReservation; - } - } - - public static void ClearCurrentReservation() { - currentReservation = null; - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Rialto/RecurringReservation.cs b/base/Kernel/Singularity/Scheduling/Rialto/RecurringReservation.cs deleted file mode 100644 index 8caad28..0000000 --- a/base/Kernel/Singularity/Scheduling/Rialto/RecurringReservation.cs +++ /dev/null @@ -1,60 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: RecurringReservation.cs -// -// Note: -// - -using System; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling.Rialto -{ - /// - /// Summary description for RecurringReservation. - /// - public class RecurringReservation : ISchedulerCpuReservation - { - private CpuResourceReservation enclosingCpuReservation; - - internal TimeSpan SliceLeft; - // CPU time left from the last run - internal GraphNode AssignedNodes; - // nodes assigned to the activity - internal GraphNode TempAssignedNodes; - // same as above in the new scheduling plan - - - internal RecurringReservation() - { - SliceLeft = new TimeSpan(0); - AssignedNodes = null; - TempAssignedNodes = null; - } - - internal TimeSpan Slice; - internal TimeSpan Period; - - public override CpuResourceAmount ReservedAmount - { - get { return CpuResource.Provider().TimeToCpu(Slice); } - } - - public override TimeSpan ReservedPeriod - { - get { return Period; } - } - -#region ISchedulerCpuReservation Members - public override CpuResourceReservation EnclosingCpuReservation - { - get { return enclosingCpuReservation; } - set { enclosingCpuReservation = value; } - } -#endregion - } -} diff --git a/base/Kernel/Singularity/Scheduling/Rialto/ReservationSlice.cs b/base/Kernel/Singularity/Scheduling/Rialto/ReservationSlice.cs deleted file mode 100644 index 4de6624..0000000 --- a/base/Kernel/Singularity/Scheduling/Rialto/ReservationSlice.cs +++ /dev/null @@ -1,50 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: ReservationSlice.cs -// -// Note: -// - -using System; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling.Rialto -{ - /// - /// A reservation slice is a slice of time assigned to the reservation. It includes a - /// reference to it associated reservation, is stored with the node, and contains a - /// flag to say if it’s stolen. Note that there is no reference from a reservation to - /// its slices, only the other way around. - /// - public class ReservationSlice : ICloneable - { - - public DateTime Start; // between [i].Start and [i+1].Start, node is reserved for AssociatedReservation - public DateTime End; - public TimeSpan Available; // how much RT-time this entry represents - public OneShotReservation AssociatedReservation; - - public ReservationSlice() - { - - } - -#region ICloneable Members - - public object Clone() - { - ReservationSlice newObj = new ReservationSlice(); - newObj.Available = Available; - newObj.End = End; - newObj.AssociatedReservation = AssociatedReservation; //NOTE: A copy of the reference. But OK since originally was a pointer. - newObj.Start = Start; - return newObj; - } - -#endregion - } -} diff --git a/base/Kernel/Singularity/Scheduling/Rialto/RialtoActivity.cs b/base/Kernel/Singularity/Scheduling/Rialto/RialtoActivity.cs deleted file mode 100644 index da5644d..0000000 --- a/base/Kernel/Singularity/Scheduling/Rialto/RialtoActivity.cs +++ /dev/null @@ -1,158 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: RialtoActivity.cs -// -// Note: -// - -using System; -using System.Diagnostics; -using System.Threading; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling.Rialto -{ - /// - /// The resource container class includes a recurring reservation - /// (k out of every n), a list of runnable threads, unfinished constraints - /// (i.e. non-guaranteed or non-feasible), as well as pointers to handle - /// its role in lists for the non-blocked activity list and Standby - /// activity list. It also has references for the last node to execute it, - /// a node assigned to it (which is a list via SameActivityNext), and a node - /// assigned to it under the new scheduling plan. It also has some bookkeeping. - /// - public class RialtoActivity : ISchedulerActivity - { - private Activity enclosingActivity; - - /// - /// When null, there is no active recurring reservation. - /// - internal RecurringReservation MyRecurringCpuReservation; - - public RialtoThread RunnableThreads; - // list of runnable threads, if any - - public GraphNode LastNode; - // last node used to execute the activity - // non-null only for incomplete activities - // List of Constraints: not accepted || with the estimate consumed - public OneShotReservation UnfinishedConstraints; - - public RialtoActivity Next; // Next and previous activities - public RialtoActivity Previous; - - // An activity is incomplete if it didn't use its previous CPU - // slice completely. These activities are linked in a separate list. - public RialtoActivity NextStandbyActivity; - // Next and previous 'incomplete' activities - public RialtoActivity PreviousStandbyActivity; - // NOT a circular list! - - public int ReferenceCount; - public int CountNodes; - public int Id; - public static int nextRCId = 1; - - public RialtoActivity() - { - //Below is InitializeActivity(); - ReferenceCount = 1; - RunnableThreads = null; - - LastNode = null; - NextStandbyActivity = null; - PreviousStandbyActivity = null; - - //Commenting out because a null reservation means no reservation. - //MyRecurringCpuReservation = new RecurringReservation(); - //MyRecurringCpuReservation.EnclosingCpuReservation = new CpuResourceReservation(MyRecurringCpuReservation); - - bool iflag = Processor.DisableInterrupts(); - Id = Interlocked.Increment(ref nextRCId); - RialtoScheduler.EnqueueActivity(this); // add in last position in RoundRobin! - Processor.RestoreInterrupts(iflag); - } - - //public-normal API - //TODO: Call this from finalize? - /// - /// ReleaseReference is called when: - /// - A thread with this as resource container exits - /// - The thread creating the resource container releases it voluntarily - /// - A node assigned to this resource container's CPU reservation is removed - /// - When a constraint which became a resource container constraint exits - /// - /// When the only references left are from the nodes assigned, a CPU resource - /// reservation for no time is requested. When there are no additional - /// nodes left, the resource container is dequeued. - /// - public override void ReleaseReference() - { - int newrefcnt; - - // DI replaced by AtomicDec(&activity->ReferenceCount); - Debug.Assert(ReferenceCount > 0); - newrefcnt = Interlocked.Decrement(ref ReferenceCount); - - if (newrefcnt == CountNodes) { - if (CountNodes > 0) { - //ISchedulerCpuReservation reservation = ((CpuResourceReservation)EnclosingActivity.GetResourceReservation(CpuResource.Provider().ResourceString)).schedulerReservation; - Debug.Assert(MyRecurringCpuReservation != null); - if (MyRecurringCpuReservation.Slice.Ticks > 0) { - CpuResourceAmount amount = CpuResource.Provider().TimeToCpu(new TimeSpan(0)); - TimeSpan period = MyRecurringCpuReservation.Period; - CpuResource.Provider().ReserveCpu(EnclosingActivity, amount, period); //release is a reservation for 0! - Scheduler.LogRecurringCpuReservation(); //TODO: SIM-huh? - } - //system.SetNextThreadContext(GetThread(0)); //TODO: What does nextThread mean here? - //TODO: Shouldn't we be just setting it to null and letting nextThread take care of it? - return; - } - bool iflag = Processor.DisableInterrupts(); - RialtoScheduler.DequeueActivity(this); // update RoundRobin queue, if required. - Processor.RestoreInterrupts(iflag); - } - Scheduler.LogDeleteActivity(); - } - - // find the runnable thread of an activity: - public RialtoThread GetRunnableThread() - { - OneShotReservation reservation = UnfinishedConstraints; - Debug.Assert(reservation == null || reservation.AssociatedActivity == null); - // avoid stack overflow : GetRunnableThread & GetRunnableThread - - while (reservation != null) { - // search in the circular list - RialtoThread thread = reservation.GetRunnableThread(); - if (thread != null) { - return thread; - } - reservation = reservation.Next; - if (reservation == UnfinishedConstraints) { - break; - } - } - return RunnableThreads; - } - -#region ISchedulerActivity Members - public override Activity EnclosingActivity - { - get { return enclosingActivity; } - set { enclosingActivity = value; } - } - - public CpuResourceReservation CpuResourceReservation - { - get { return MyRecurringCpuReservation.EnclosingCpuReservation; } - } -#endregion - } - -} diff --git a/base/Kernel/Singularity/Scheduling/Rialto/RialtoProcessor.cs b/base/Kernel/Singularity/Scheduling/Rialto/RialtoProcessor.cs deleted file mode 100644 index b458387..0000000 --- a/base/Kernel/Singularity/Scheduling/Rialto/RialtoProcessor.cs +++ /dev/null @@ -1,34 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: RialtoProcessor.cs -// -// Note: -// - -using System; -using System.Diagnostics; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling.Rialto -{ - /// - /// - public class RialtoProcessor : ISchedulerProcessor - { - private readonly Processor enclosingProcessor; - - public RialtoProcessor(Processor processor) - { - enclosingProcessor = processor; - } - - public override Processor EnclosingProcessor - { - get { return enclosingProcessor; } - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Rialto/RialtoScheduler.cs b/base/Kernel/Singularity/Scheduling/Rialto/RialtoScheduler.cs deleted file mode 100644 index abe608e..0000000 --- a/base/Kernel/Singularity/Scheduling/Rialto/RialtoScheduler.cs +++ /dev/null @@ -1,3162 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: RialtoScheduler.cs -// -// Note: -// - -// #define LIFO -#define ASCEND_RESERV -#define LOG_SCHEDULER_DETAILS -// #define DEBUG_TREE - -using System; -using System.Collections; -using System.Diagnostics; -using System.Threading; -using Microsoft.Singularity; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling -{ -#if !SIMULATOR - public class SystemScheduler - { - static public void RegisterScheduler() - { - Rialto.RialtoScheduler.RegisterScheduler(); - } - } -#endif -} - -namespace Microsoft.Singularity.Scheduling.Rialto -{ - /// - /// Summary description for RialtoScheduler. - /// - public class RialtoScheduler : ICpuScheduler - { -#region Constants - static readonly TimeSpan AmountCpu = CpuResource.MaxPeriod; //(MaxPeriod * 100); - //TODO: TICKS vs. TIME_SPAN vs DATE_TIME - - public static readonly TimeSpan ContextSwitch = new TimeSpan(200); - public static readonly TimeSpan MinSlice = new TimeSpan(10 * ContextSwitch.Ticks); - public static readonly TimeSpan AFewSlice = new TimeSpan(3 * MinSlice.Ticks); - public static readonly TimeSpan RobinSlice = new TimeSpan(10 * TimeSpan.TicksPerMillisecond); -#endregion - -#region Static data members - public static OneShotReservation ReservationFreePool; - public static GraphNode SchedulerPlan; - //static GraphNode NextPlan; - private static DateTime changePlanTime; - // static Mutex SchedPlanMutex; - - public static GraphNode CurrentPlanNode; - - private static RialtoActivity circularActivityList; - private static RialtoActivity robinActivityNext; - private static RialtoActivity robinActivityCurrent; // activity currently executing from - - //static TimeSpan CurrentSlice; - private static TimeSpan idleTime; - private static bool idle; - - private static RialtoThread currentThreadGlobal; - public static bool EarliestDeadlineFirstBlocked; - - private static RialtoActivity standbyActivities; // head of the Standby Activities list - private static RialtoActivity currentStandbyActivity; // Standby Activity executing, if any - - private static TimeSpan robinSliceLeft = RialtoScheduler.RobinSlice; //new TimeSpan(0); // the round robin list, if any, and its CPU slice - - public static DateTime SchedulingTime; // moment of the last reschedules - private static bool needToReschedule; - private static RialtoThread directSwitchTo; - - private static TimeSpan freeCpu; // (freeCpu/AmountCpu) * 100. == % of CPU free - - //static NodeProxy[] nodeProxies; - private static ArrayList nodeProxies = new ArrayList(); - //static int ProxyCount; - //static int MaxProxies; - - private static TimeSpan[][] freeSlots; // root of helper data structure - //static int FreeLevels; // levels in the helper data structures - - public static TimeSpan LA_Time = RialtoScheduler.MinSlice; - - private static TimeSpan minPeriod; // minimum period in the system - - public static GraphNode FreeNodes; // list of free nodes in current scheduling tree - private static GraphNode tempFreeNodes; // list of free nodes in new scheduling tree - - public static int ResolutionAttempt; - - private static int hackTries; - -#endregion - - static public void RegisterScheduler() - { - CpuResource.RegisterSystemScheduler(new RialtoScheduler()); - } - - public override ISchedulerProcessor CreateSchedulerProcessor(Processor processor) - { - return new RialtoProcessor(processor); - } - - public override ISchedulerThread CreateSchedulerThread(Thread thread) - { - return new RialtoThread(thread); - } - - public override ISchedulerActivity CreateSchedulerActivity() - { - return new RialtoActivity(); - } - - public override ISchedulerCpuReservation ReserveCpu(ISchedulerActivity schedulerActivity, CpuResourceAmount cpuAmount, TimeSpan period) - { - RialtoActivity activity = (RialtoActivity) schedulerActivity; - - if (activity.MyRecurringCpuReservation == null) { - activity.MyRecurringCpuReservation = new RecurringReservation(); - activity.MyRecurringCpuReservation.Slice = new TimeSpan(0); - activity.MyRecurringCpuReservation.Period = CpuResource.MaxPeriod; - } - //Activity activity = GetActivity(activityId); - TimeSpan oldSlice; - TimeSpan oldPeriod; - TimeSpan amount = CpuResource.Provider().CpuToTime(cpuAmount); - GraphNode newSchedulerPlan; - GraphNode oldSchedulerPlan; - TimeSpan deltaReservation; - DateTime timeNow; -#if DEBUG_TREE - TimeSpan requestedSlice = amount; - TimeSpan requestedPeriod = period; -#endif - bool flagContext = false; - bool localNeedToReschedule; - // bool InterruptsDisableFlag; - ulong start; - ulong stop; - - // if (InThreadContext()) { - // Mutex_Lock(&SchedPlanMutex); - // flagContext = true; - // } - // else { - // Debug.Assert(Processor.InterruptsDisabled()); - // } - Scheduler.LogRecurringCpuReservation(); // record fixed cost - // hack!!!! - hackTries = 0; - - start = Processor.CycleCount; - - localNeedToReschedule = false; - oldSlice = activity.MyRecurringCpuReservation.Slice; - oldPeriod = activity.MyRecurringCpuReservation.Period; - Debug.Assert(oldPeriod.Ticks != 0); - - if (period.Ticks == 0) { - Debug.Assert(amount.Ticks == 0); - period = oldPeriod; - } - else if (amount.Ticks != 0) { - if (period > CpuResource.MaxPeriod) { - amount = new TimeSpan((amount.Ticks * CpuResource.MaxPeriod.Ticks)/ period.Ticks); - period = CpuResource.MaxPeriod; - } - - if (amount < RialtoScheduler.MinSlice) { - amount = RialtoScheduler.MinSlice; - // DebugStub.Print("ReserveRecurringCpu:: S_FALSE amount < RialtoScheduler.MinSlice.\n"); - goto Exit; - } - } - else if (oldSlice.Ticks == 0) { - // case slice == 0 - // DebugStub.Print("ReserveRecurringCpu:: S_OK amount == 0.\n"); - goto Exit; - } - deltaReservation = new TimeSpan((amount.Ticks * AmountCpu.Ticks) / period.Ticks - (oldSlice.Ticks * AmountCpu.Ticks)/oldPeriod.Ticks); - - // Handle a simple case first. - - if (deltaReservation > freeCpu) { - // not enough free CPU - // slice = (TIME)((freeCpu + (oldSlice * AmountCpu)/oldPeriod) * period); - amount = new TimeSpan((((freeCpu.Ticks + (oldSlice.Ticks * AmountCpu.Ticks)/oldPeriod.Ticks) * period.Ticks)/ AmountCpu.Ticks)); - if (amount < RialtoScheduler.MinSlice) - amount = new TimeSpan(0); - // DebugStub.Print("ReserveRecurringCpu:: S_FALSE deltaReservation > freeCpu.\n"); - goto Exit; - // return old reservation - } - - if (IncrementalReserveActivity(activity, ref amount, ref period)) { -#if DEBUG_TREE - DebugStub.Print("IncReservActivity: 0x{0:x} Req: {1}/{2} Get: " + - "{3}/{4} Activ {5}:{6}\n", - __arglist( - activity, - requestedSlice, - requestedPeriod, - amount, - period, - activity.MyRecurringCpuReservation.Slice, - activity.MyRecurringCpuReservation.Period)); - - timeNow = SystemClock.GetKernelTime(); - - PrintSchedPlan(SchedulerPlan, timeNow); -#endif - - GetActivityRecurringCpuReservation(FreeNodes, out oldPeriod, out oldSlice); - freeCpu = new TimeSpan((oldSlice.Ticks * AmountCpu.Ticks) / oldPeriod.Ticks); -#if VERBOSE - DebugStub.Print("ReserveRecurringCpu:: S_OK Via Incremental Reservation.\n"); -#endif - goto Exit; - } - - activity.MyRecurringCpuReservation.Slice = amount; - activity.MyRecurringCpuReservation.Period = period; - - newSchedulerPlan = BuildSchedulerPlan(); - // Scheduler.LogReservedActivity(ProxyCount); - if (newSchedulerPlan != null) { - // if successful - bool iflag = Processor.DisableInterrupts(); - - timeNow = SystemClock.GetKernelTime(); - if (!OneShotReservation.SatisfyAcceptedConstraint(timeNow)) { - Processor.RestoreInterrupts(iflag); - FreeSchedulerPlan(newSchedulerPlan); - activity.MyRecurringCpuReservation.Slice = oldSlice; - activity.MyRecurringCpuReservation.Period = oldPeriod; - amount = new TimeSpan((period.Ticks * freeCpu.Ticks)/AmountCpu.Ticks); -#if VERBOSE - DebugStub.Print("ReserveRecurringCpu:: S_FALSE SatisfyAcceptedConstraint false.\n"); -#endif - goto Exit; - } - // TODO: compute time change - // set a timer interrupt, if needed, for the moment when the plan will - // change - oldSchedulerPlan = SynchronizeSchedulerPlans(newSchedulerPlan, timeNow, ref localNeedToReschedule); - Processor.RestoreInterrupts(iflag); - - if (oldSchedulerPlan != null) { - // this may execute with preemption enabled - FreeSchedulerPlan(oldSchedulerPlan); - } - GetActivityRecurringCpuReservation(activity.MyRecurringCpuReservation.AssignedNodes, out period, out amount); - activity.MyRecurringCpuReservation.Slice = amount; - activity.MyRecurringCpuReservation.Period = period; - -#if DEBUG_TREE - DebugStub.Print("ReservActivity: 0x{0:x} Req: {1}/{2} Get {3}/{4} --- {5}:{6}\n", - __arglist(activity, - requestedSlice, - requestedPeriod, - amount, - period, - activity.MyRecurringCpuReservation.Slice, - activity.MyRecurringCpuReservation.Period)); - - PrintSchedPlan(newSchedulerPlan, timeNow); -#endif - GetActivityRecurringCpuReservation(FreeNodes, out oldPeriod, out oldSlice); - freeCpu = new TimeSpan((oldSlice.Ticks * AmountCpu.Ticks) / oldPeriod.Ticks); - - } - else { - // otherwise return old reservation (possible null) - Debug.Assert(oldPeriod.Ticks != 0); - activity.MyRecurringCpuReservation.Slice = oldSlice; - activity.MyRecurringCpuReservation.Period = oldPeriod; - amount = new TimeSpan((period.Ticks * freeCpu.Ticks)/AmountCpu.Ticks); - minPeriod = SchedulerPlan.Period; -#if VERBOSE - DebugStub.Print("ReserveRecurringCpu:: S_FALSE new scheduler plan null.\n"); -#endif - } -#if VERBOSE - DebugStub.Print("ReserveRecurringCpu:: S_OK new scheduler plan.\n"); -#endif - Exit: - if (flagContext) { - // Debug.Assert(InThreadContext()); - // if (localNeedToReschedule && !DuringBootstrap()) { - // RescheduleWithInterruptsEnabled(); - // } - // localNeedToReschedule = false; - // Mutex_Unlock(&SchedPlanMutex); - } - else { - if (localNeedToReschedule) { - localNeedToReschedule = false; - Reschedule(); - } - } - stop = Processor.CycleCount; -#if PRINT_RESERV_OVERHEAD - DebugStub.Print("ReservActivity internal timing: {0} cycles, " + - "Free CPU {0} " + - "hackTries {0}\n", - __arglist(stop-start, - freeCpu, - hackTries)); -#endif - if (activity.MyRecurringCpuReservation.Slice.Ticks == 0) { - activity.MyRecurringCpuReservation = null; - } - return activity.MyRecurringCpuReservation; - } - - public override bool ShouldReschedule() - { - RialtoThread currentThread = GetCurrentThread(); - if (Scheduler.TimerInterruptedFlag && currentThread != null) { - //SchedulerClock.CheckInterrupt(); - } - - if (currentThread == null || Scheduler.YieldFlag || currentThread.EnclosingThread.IsWaiting() || currentThread.EnclosingThread.IsStopped()) { - Reschedule(); - } - return needToReschedule || Scheduler.TimerInterruptedFlag; - // || timer should have fired but didn't? - } - - // DI -- this is somehow similar to EnableInterrupts - public override bool NextThread(out Thread nextThread) - { - Debug.Assert(!Processor.InterruptsDisabled()); - bool iflag = Processor.DisableInterrupts(); - bool halted = false; - RialtoThread currentThread = GetCurrentThread(); - - SchedulerClock.CheckInterrupt(); - if (ShouldReschedule()) { - //Debug.Print("Calling RescheduleInterrupt()\n"); - halted = RescheduleInterrupt(); //TODO: RescheduleInterrupt returns true if the processor needs to be halted. - currentThread = GetCurrentThread(); - } - else { - //Debug.Print("No call to RescheduleInterrupt()\n"); - } - - if (currentThread != null) { - nextThread = currentThread.EnclosingThread; - } - else { - Debug.Assert(false); - nextThread = null; - } - - if (nextThread != null) { - unchecked { - Tracing.Log(Tracing.Debug, "NextThread => {0}", - (UIntPtr)(uint)nextThread.GetThreadId()); - } - } - else { - Tracing.Log(Tracing.Debug, "NextThread => none"); - } - - needToReschedule = false; - directSwitchTo = null; - // DebugStub.Print("-------------------------currentThread-Id {0}\n", - // __arglist(currentThread.Id)); - //return currentThread; - Processor.RestoreInterrupts(iflag); - return halted; - } - - ////////////////////////////////////////////////////////////////////// - //public static void Yield(); - - public RialtoScheduler() - { - } - - public override void Initialize() - { - RialtoScheduler.InitializeScheduler(); - } - - /// - /// Initialize types that would otherwise cause dynamic allocations at runtime with preemption disabled - /// - static void InitializeSchedulerTypes() - { - Kernel.InitType(typeof(OneShotReservation)); - } - - public static void InitializeScheduler() - { - InitializeSchedulerTypes(); - - //TODO: Should all the null's be added in here? - SchedulerPlan = GraphNode.InitTree(); - SchedulerPlan.NextExec = SystemClock.GetKernelTime(); //perhaps this should happen when the first thread is created, instead - CurrentPlanNode = SchedulerPlan; - FreeNodes = SchedulerPlan; - - SchedulingTime = SystemClock.GetKernelTime(); - freeCpu = AmountCpu; - - minPeriod = CpuResource.MaxPeriod; - - TimeSpan currentSliceLeft = SchedulerPlan.Slice; - currentSliceLeft /* CurrentSlice */ = minInterval(currentSliceLeft , robinSliceLeft); - bool iflag = Processor.DisableInterrupts(); - SchedulerClock.SetNextInterrupt(SchedulingTime + currentSliceLeft); - Processor.RestoreInterrupts(iflag); - } - -#region ISystemScheduler Members - - public override void BeginDelayedConstraint(Hashtable resourceEstimates, - TimeSpan relativeDeadline, - ISchedulerTask taskToEnd, - out ISchedulerTask schedulerTask) - { - TimeConstraint timeConstraint = new TimeConstraint(); - timeConstraint.Estimate = CpuResource.Provider().CpuToTime((CpuResourceAmount)resourceEstimates[CpuResource.Provider().ResourceString]); - timeConstraint.Start = new DateTime(0); //Start now. - timeConstraint.RelativeDeadline = relativeDeadline; - timeConstraint.Deadline = new DateTime(0); //A 0-deadline means relative instead. - schedulerTask = Thread.CurrentThread.SchedulerThread.PrepareDelayedTask(taskToEnd, ref timeConstraint, SystemClock.GetKernelTime()); - } - - //I'm afraid the preemption needs to be disabled here around beginBeforeWait, - // but I feel like there was a problem. - public override bool BeginConstraint(Hashtable resourceEstimates, - DateTime deadline, - ISchedulerTask taskToEnd, - out ISchedulerTask schedulerTask) - { - DateTime timeNow = SystemClock.GetKernelTime(); - RialtoThread thread = GetCurrentThread(); - ulong start; - ulong stop; - - Debug.Assert(!Processor.InterruptsDisabled()); - Debug.Assert(taskToEnd == null || taskToEnd == thread.ReservationStack); - - bool endPrevious = (taskToEnd != null); - - thread.IpcCheckFreeConstraint(); - - start = Processor.CycleCount; - TimeConstraint constraint = new TimeConstraint(); - constraint.Deadline = deadline; - constraint.Estimate = CpuResource.Provider().CpuToTime((CpuResourceAmount)resourceEstimates[CpuResource.Provider().ResourceString]); - constraint.Start = timeNow; - - bool ok = thread.BeginConstraintBeforeWaitValidate(endPrevious, ref constraint, timeNow); - schedulerTask = thread.PendingReservation; - if (ok) { - OneShotReservation.BeginConstraintBeforeWait(thread, endPrevious, constraint, timeNow); - bool iflag = Processor.DisableInterrupts(); - ok = OneShotReservation.ResolveConstraint(thread); - Processor.RestoreInterrupts(iflag); - } - else { - // DebugStub.Print("RialtoScheduler::BeginConstraintBeforeWaitValidate failed\n"); - } - stop = Processor.CycleCount; - - Scheduler.LogBeginConstraint(thread.EnclosingThread, ok, start, stop); - - //TODO: Check if this is a necessity, or if it's only for sim time. Call in wrapper if necessary. - //NextThread(); - return ok; - } - - public override bool EndConstraint(ISchedulerTask taskToEnd) - { - Debug.Assert(taskToEnd == GetCurrentThread().ReservationStack); - Debug.Assert(!Processor.InterruptsDisabled()); - bool iflag = Processor.DisableInterrupts(); - bool ok = OneShotReservation.EndPreviousConstraint(GetCurrentThread(), SystemClock.GetKernelTime()); - // - // need to reschedule only if on a reserved slot and the top of EarliestDeadlineFirst doesn't - // belong to this task - // - if ((OneShotReservation.CurrentReservation != null) && - (OneShotReservation.GuaranteedReservations != null) && - (OneShotReservation.GuaranteedReservations.ReservTask != (GetCurrentThread()))) { - Reschedule(); - } - - Processor.RestoreInterrupts(iflag); - Scheduler.LogEndConstraint(); - - //TODO: In the system wrapper for BeginConstraint -- it needs to call Yield/NextThread/MaybeYield - //NextThread(); - return ok; - } - -#endregion - -#region Reserve Activity (x out of y) support functions - - public static void FreeCurrentSchedulerPlan() - { - FreeSchedulerPlan(SchedulerPlan); - } - - static void FreeSchedulerPlan(GraphNode oldPlan) - { - GraphNode leftNode, rightNode; - int i; - - // Debug.Assert(!Processor.InterruptsDisabled()); - minPeriod = SchedulerPlan.Period; - - if (oldPlan == null) - return; - if ((oldPlan.Type == GraphNode.NodeType.Free) || (oldPlan.Type == GraphNode.NodeType.Used)) { - leftNode = oldPlan.Next; // reuse local - FreeSchedulerPlan(leftNode); - } - else { - leftNode = oldPlan.Left; - rightNode = oldPlan.Right; - FreeSchedulerPlan(leftNode); - FreeSchedulerPlan(rightNode); - } - - for (i = 0; i < oldPlan.ReservCount; i++) { - // DebugStub.Print("FreeSchedulerPlan {0} {1}\n", - // __arglist(POINTER(oldPlan->ReservationArray[i]), - // POINTER(oldPlan->ReservationArray[i])->ReferenceCount)); - ReleaseReservation(oldPlan.ReservationArray[i].AssociatedReservation); - } - if (oldPlan.Type == GraphNode.NodeType.Used) { - Interlocked.Decrement(ref oldPlan.DefaultActivity.CountNodes); - ActivityObjRelease(oldPlan.DefaultActivity); - } - oldPlan = null; //free(oldPlan); - } - - static int NextProxyOnLevel(int nextProxy, int level) - { - for (int i = nextProxy; i < nodeProxies.Count; i++) { - if (((NodeProxy)nodeProxies[i]).TreeLevel == level) { - return i; - } - } - return nodeProxies.Count; - } - - /// - /// Search for the existence of any node with a higher TreeLevel "under" me. - /// - /// TreeLevel (basically tree position, 2^level + place in row) - /// - static bool NextProxyOnHigherLevel(int level) - { - // search for a node w/ TreeLevel in a 'cone' under Level - for (int i = 0; i < nodeProxies.Count; i++) { - int min = (level << 1) + 1; - int max = (level << 1) + 2; - while (((NodeProxy)nodeProxies[i]).TreeLevel >= min) { - if (((NodeProxy)nodeProxies[i]).TreeLevel <= max) { - return true; - } - min = (min << 1) + 1; - max = (max << 1) + 2; - } - } - return false; - } - - static void ActivityAddNode(RialtoActivity activity) - { - Interlocked.Increment(ref activity.CountNodes); - ActivityObjAddRef(activity); - } - - static void ActivityRemoveNode(RialtoActivity activity) - { - int newrefcnt; - - Debug.Assert(Processor.InterruptsDisabled()); - ActivityReleaseProtected(activity); - newrefcnt = Interlocked.Decrement(ref activity.CountNodes); - - Debug.Assert(newrefcnt >= 0); - - } - - // CK: Recursive function to build the GraphNode tree from the GraphNode Proxies. - // NOTE: Level refers to tree position in full binary tree -- not row. - static GraphNode BuildLevel(int Level, TimeSpan Period, TimeSpan timeLeft, int NextProxy) - { - int i; - GraphNode ptemp; - - if (timeLeft < RialtoScheduler.MinSlice) - return null; - - ptemp = new GraphNode(); //(PNODE)malloc(sizeof (GraphNode)); - //memset(ptemp, 0, sizeof(GraphNode)); - - ptemp.TimeToOrigin = timeLeft; - Debug.Assert(Period.Ticks % minPeriod.Ticks == 0); - ptemp.Period = Period; - - if ((i = NextProxyOnLevel(NextProxy, Level)) < nodeProxies.Count) { - // allocate an Used GraphNode - - ptemp.Type = GraphNode.NodeType.Used; - ptemp.Slice = ((NodeProxy)nodeProxies[i]).Slice; - Debug.Assert(ptemp.Slice >= RialtoScheduler.MinSlice); - Debug.Assert(Period == ((NodeProxy)nodeProxies[i]).Period); - ptemp.DefaultActivity = ((NodeProxy)nodeProxies[i]).AssociatedActivity; - ActivityAddNode(ptemp.DefaultActivity); - - ptemp.SameActivityNext = ptemp.DefaultActivity.MyRecurringCpuReservation.TempAssignedNodes; - ptemp.DefaultActivity.MyRecurringCpuReservation.TempAssignedNodes = ptemp; - - ptemp.Left = ptemp.Right = null; - ptemp.Next = BuildLevel(Level, Period, - timeLeft - ptemp.Slice - RialtoScheduler.ContextSwitch, i + 1); - } - else if (NextProxyOnHigherLevel(Level)) { - ptemp.Type = GraphNode.NodeType.LeftBranch; - ptemp.Slice = timeLeft; - ptemp.Next = null; - ptemp.Left = BuildLevel(2 * Level + 1, Period+Period, timeLeft, 0); - ptemp.Right = BuildLevel(2 * Level + 2, Period+Period, timeLeft, 0); - } - else { - // free node - ptemp.Type = GraphNode.NodeType.Free; - ptemp.Slice = timeLeft; - ptemp.Next = null; - ptemp.SameActivityNext = tempFreeNodes; - tempFreeNodes = ptemp; - } - return ptemp; - } - - static void FreeHelperDataStructures() - { - // Free the (FreeLevels) freeSlots Arrays! - if (freeSlots != null) { - for (int i = 0; i < freeSlots.Length; i++) { - freeSlots[i] = null; //free(freeSlots[i]); - } - if (freeSlots != null) { - freeSlots = null; //free(freeSlots); - } - } - nodeProxies = null; //free(nodeProxies); // free the ordered array of reservations - } - - static bool BuildHelperDataStructures() - { - int count, i; - RialtoActivity ptemp; - TimeSpan T; - - // Allocate 'nodeProxies', 'ordered array' of node proxies: - nodeProxies = new ArrayList(); //NodeProxy[MaxProxies]; //(NodeProxy*) malloc(MaxProxies * sizeof(NodeProxy)); - //for (i=0; i ((NodeProxy)nodeProxies[i]).Slice))) - break; -# else -#if ASCEND_RESERV //Sort the NodeProxy array in descending order of fraction of CPU - //NOTE: The multiplication in numerator and denominator is to allow this to be done as integer math. - //This is testing if (ptemp.MyRecurringCpuReservation.Slice.Ticks) / ptemp.MyRecurringCpuReservation.Period.Ticks > (((NodeProxy)nodeProxies[i]).Slice.Ticks) / ((NodeProxy)nodeProxies[i]).Period.Ticks - //Converting to multiply only, for integer math. - if ((ptemp.MyRecurringCpuReservation.Slice.Ticks * ((NodeProxy)nodeProxies[i]).Period.Ticks) > - (((NodeProxy)nodeProxies[i]).Slice.Ticks * ptemp.MyRecurringCpuReservation.Period.Ticks)) - break; - // if ((ptemp.MyRecurringCpuReservation.Slice.Ticks * AmountCpu.Ticks * 100) / ptemp.MyRecurringCpuReservation.Period.Ticks > //TODO: Is this enough for accurate? Or just "close enough"? - // (((NodeProxy)nodeProxies[i]).Slice.Ticks * AmountCpu.Ticks * 100) / ((NodeProxy)nodeProxies[i]).Period.Ticks) - // break; -#else - break; //TODO: Does this mean anything? -#endif -# endif - // make room for the new activity - - //if (i < ProxyCount) - //Array.Copy(nodeProxies, i, nodeProxies, i+1, ProxyCount - i); - //memmove(&(nodeProxies[i+1]), &(nodeProxies[i]), - // sizeof(NodeProxy) * (ProxyCount - i)); - nodeProxies.Insert(i, new NodeProxy()); - - //ProxyCount++; - //Debug.Assert(nodeProxies.Count <= MaxProxies); - - ((NodeProxy)nodeProxies[i]).AssociatedActivity = ptemp; - ((NodeProxy)nodeProxies[i]).Slice = ptemp.MyRecurringCpuReservation.Slice; - ((NodeProxy)nodeProxies[i]).Period = ptemp.MyRecurringCpuReservation.Period; - ((NodeProxy)nodeProxies[i]).TreeLevel = -1; - if (((NodeProxy)nodeProxies[i]).Period < minPeriod) - minPeriod = ((NodeProxy)nodeProxies[i]).Period; - } - ptemp = ptemp.Next; - } while (ptemp != circularActivityList); - - Debug.Assert(minPeriod <= CpuResource.MaxPeriod); - int freeLevels = 0; - freeSlots = null; - for (i = 0; i < nodeProxies.Count; i++) { - T = minPeriod; - count = 0; - - while ((((NodeProxy)nodeProxies[i]).Period >= T+T) && (T+T <= CpuResource.MaxPeriod)) { - T = T+T; - count++; - } - - if (count > freeLevels) { - //NOTE: Basically FreeLevels is the max height of the tree (free list) - freeLevels = count; - } - - //Store the slice as the scaled slice (fraction of the period). - ((NodeProxy)nodeProxies[i]).Slice = new TimeSpan((((NodeProxy)nodeProxies[i]).Slice.Ticks * T.Ticks)/((NodeProxy)nodeProxies[i]).Period.Ticks); - - if (((NodeProxy)nodeProxies[i]).Slice < RialtoScheduler.MinSlice) { - //free(nodeProxies); - return false; - } - Debug.Assert(T.Ticks % minPeriod.Ticks == 0); - ((NodeProxy)nodeProxies[i]).Period = T; - ((NodeProxy)nodeProxies[i]).FreeLevel = count; //CK NOTE: This is essentially the level in the tree this reservation preferably goes. - } - - // Allocate level 0 activities: - for (i = 0, T = minPeriod, count = 0; i < nodeProxies.Count; i++) { - if (((NodeProxy)nodeProxies[i]).Period == minPeriod) { - ((NodeProxy)nodeProxies[i]).TreeLevel = 0; - T -= RialtoScheduler.ContextSwitch + ((NodeProxy)nodeProxies[i]).Slice; - } - else { - Debug.Assert(((NodeProxy)nodeProxies[i]).Period.Ticks % minPeriod.Ticks == 0); - count++; - } - } - - if ((count > 0) && (T < RialtoScheduler.MinSlice)) { //CK- There exist non-level-0 activities, but not enough schedule time Left for more scheduled activities - // free(nodeProxies); - return false; - } - - // Allocate the (FreeLevels) freeSlots Arrays! - freeLevels +=1; - freeSlots = new TimeSpan[freeLevels][]; //(FREESLOT**) malloc(FreeLevels * sizeof(FREESLOT *)); - for (i = 0; i < freeSlots.Length; i++) { - freeSlots[i] = new TimeSpan[1 << i]; //(FREESLOT*) malloc((1 << i) * sizeof(FREESLOT)); - } - - for (i = 0; i < freeSlots.Length; i++) { - for (count = 0; count < (1 << i); count++) { - freeSlots[i][count] = T; //CK- Initialize all free slots to have T (time left after level-0 activities) - } - } - - return true; - } - - // - // Returns the first index of a NodeProxy which has TreeLevel == -1 - // - static int NextProxy(int ProxyIndex) - { - while ((ProxyIndex < nodeProxies.Count) && - (((NodeProxy)nodeProxies[ProxyIndex]).TreeLevel != -1)) { - ProxyIndex++; - } - return ProxyIndex; - } - - // - // Returns NextProxy(0) - // - static int FirstProxy() - { - return NextProxy(0); - } - - /// - /// Updates freeSlots by subtracting the slice from all appropriate descendants and parents. - /// - /// - /// - /// - static void SubtractSlice(int AllocLevel, int AllocPosition, TimeSpan Slice) - { - int Level, Position, Sibling, ParentPosition, i; - - freeSlots[AllocLevel][AllocPosition] -= Slice; - - Debug.Assert(freeSlots[AllocLevel][AllocPosition].Ticks >= 0); - - // update Free Slots; the larger periods first: - // CK- At all points in time, freeSlots seems to keep the time available at the slot, to adding to a place in the tree requires reducing all descendants - for (i = 1; AllocLevel + i < freeSlots.Length; i++) { - for (Position = AllocPosition * (1 << i); - Position < (AllocPosition + 1) * (1 << i); Position++) { - Debug.Assert(freeSlots[AllocLevel + i][Position] >= Slice); - freeSlots[AllocLevel + i][Position] -= Slice; - } - } - - // smaller periods Next: - // CK- Update parent(s) if we now have less than our sibling. (parent is min of me and sibling). - for (Level = AllocLevel, Position = AllocPosition; - Level > 0; Level--, Position = ParentPosition) { - Sibling = (Position & 0x01)==1 ? Position - 1 : Position + 1; - if (freeSlots[Level][Position] >= freeSlots[Level][Sibling]) { - break; // no need to go further - } - ParentPosition = Position >> 1; - freeSlots[Level - 1][ParentPosition] = freeSlots[Level][Position]; - } - } - - /// - /// Updates freeSlots by adding the slice to all appropriate descendants and parents. - /// - /// - /// - /// - static void AddSlice(int AllocLevel, int AllocPosition, TimeSpan Slice) - { - int Level, Position, Sibling, ParentPosition, i; - - freeSlots[AllocLevel][AllocPosition] += Slice; - - //Update Free Slots; the larger periods first: - for (i = 1; AllocLevel + i < freeSlots.Length; i++) { - for (Position = AllocPosition * (1 << i); - Position < (AllocPosition + 1) * (1 << i); - Position++) { - freeSlots[AllocLevel + i][Position] += Slice; - } - } - - // smaller periods Next: - for (Level = AllocLevel, Position = AllocPosition; - Level > 0; Level--, Position = ParentPosition) { - Sibling = (Position & 0x01)==1 ? Position - 1 : Position + 1; - ParentPosition = Position >> 1; - if (freeSlots[Level][Position] <= freeSlots[Level][Sibling]) { - freeSlots[Level - 1][ParentPosition] = freeSlots[Level][Position]; - } - else { - freeSlots[Level - 1][ParentPosition] = freeSlots[Level][Sibling]; - } - } - } - - // Temporarily, it disrupts the order of the 'NodeProxy' array - static void SplitSlice(int ProxyIndex, TimeSpan AllocSlice) - { - Debug.Assert(AllocSlice >= RialtoScheduler.MinSlice); - - // if (nodeProxies.Count == MaxProxies) { - // MaxProxies += GraphNode.MaxProxySplits; - // //ArrayList already takes care of the allocation - // //nodeProxies = (NodeProxy*) realloc(nodeProxies, MaxProxies * sizeof(NodeProxy)); - // } - //memmove(&nodeProxies[ProxyIndex + 1], &nodeProxies[ProxyIndex], - // sizeof(NodeProxy) * (ProxyCount - ProxyIndex)); - - //ProxyCount++; - nodeProxies.Insert(ProxyIndex+1, ((NodeProxy)nodeProxies[ProxyIndex]).Clone()); - //Debug.Assert(nodeProxies.Count <= MaxProxies); - - ((NodeProxy)nodeProxies[ProxyIndex + 1]).Slice = ((NodeProxy)nodeProxies[ProxyIndex]).Slice - AllocSlice; - ((NodeProxy)nodeProxies[ProxyIndex]).Slice = AllocSlice; - } - - // Expects the split to be performed by 'SplitSlice' above. - static void RestoreSlice(int ProxyIndex) - { - Debug.Assert(((NodeProxy)nodeProxies[ProxyIndex + 1]).AssociatedActivity == ((NodeProxy)nodeProxies[ProxyIndex]).AssociatedActivity); - - ((NodeProxy)nodeProxies[ProxyIndex]).Slice += ((NodeProxy)nodeProxies[ProxyIndex + 1]).Slice; - - nodeProxies.RemoveAt(ProxyIndex+1); - } - - /// - /// Next largest less than previous max. - /// - /// - /// - /// - /// - static int NextLargestFreeSlot(TimeSpan PrevMax, int PrevIndex, int Level) - { - TimeSpan[] LevelSlots; - int position, MaxPosition = (1 << Level); - TimeSpan MaxValue = RialtoScheduler.MinSlice; - - LevelSlots = freeSlots[Level]; - for (position = 0; position < LevelSlots.Length; position++) { - if ((LevelSlots[position] < PrevMax) || - ((LevelSlots[position] == PrevMax) && (position > PrevIndex))) { - if (LevelSlots[position] > MaxValue) { - MaxPosition = position; - MaxValue = LevelSlots[position]; - } - } - } - return MaxPosition; - } - - /// - /// Assigns Activities (w/ non-zero reservations) to their Scheduling Tree Branches/SubTrees - /// Recursive. - /// - /// - /// - static bool AssignBranch(int ProxyIndex) - { - int level, position; - TimeSpan slice, FirstHalf; - TimeSpan[] LevelSlots; - - if (hackTries++ > GraphNode.MaxMarcelTries) { - return false; - } - if (ProxyIndex >= nodeProxies.Count) { - return true; - } - level = ((NodeProxy)nodeProxies[ProxyIndex]).FreeLevel; - Debug.Assert(level >= 0); - - LevelSlots = freeSlots[level]; - slice = ((NodeProxy)nodeProxies[ProxyIndex]).Slice; - if (slice < RialtoScheduler.MinSlice) { - return false; - } - - // Search for a fit that will leave enough free time in slot: - for (position = NextLargestFreeSlot(TimeSpan.MaxValue, 0, level); - position < LevelSlots.Length; - position = NextLargestFreeSlot(LevelSlots[position], position, level)) { - if (LevelSlots[position] - slice - RialtoScheduler.ContextSwitch >= RialtoScheduler.MinSlice) { - // try allocation - SubtractSlice(level, position, slice + RialtoScheduler.ContextSwitch); - //Note: TreeLevel is a composite number. Its value defines both the freelevel and the - //position within that level. It's of magnitude 1 << level, guaranteeing that - //position is less than 1 << level, so you can tell the level by the magnitude, and the - //position by looking at the remainder, so to speak. - ((NodeProxy)nodeProxies[ProxyIndex]).TreeLevel = (1 << level) - 1 + position; - if (AssignBranch(NextProxy(ProxyIndex))) { - // try allocate Next activity - return true; // look for A solution, not for the BEST one - } - - ((NodeProxy)nodeProxies[ProxyIndex]).TreeLevel = -1; - AddSlice(level, position, slice + RialtoScheduler.ContextSwitch); // restore the freeSlots[] values - } - else { - //Since the slot is only getting smaller, no need to continue looking. - break; - } - } - - // Search for a 'perfect fit': - for (position = 0; position < LevelSlots.Length; position++) { - if ((slice <= LevelSlots[position]) && // try allocation - (LevelSlots[position] - slice < (RialtoScheduler.AFewSlice))) { - - ((NodeProxy)nodeProxies[ProxyIndex]).Slice = LevelSlots[position]; - SubtractSlice(level, position, LevelSlots[position]); - - //Note: TreeLevel is a composite number. Its value defines both the freelevel and the - //position within that level. It's of magnitude 1 << level, guaranteeing that - //position is less than 1 << level, so you can tell the level by the magnitude, and the - //position by looking at the remainder, so to speak. - ((NodeProxy)nodeProxies[ProxyIndex]).TreeLevel = (1 << level) - 1 + position; // assign branch - if (AssignBranch(NextProxy(ProxyIndex))) { - // try allocate Next activity - return true; // return if successful (look for A solution, not for the BEST one) - } - ((NodeProxy)nodeProxies[ProxyIndex]).Slice = slice; - ((NodeProxy)nodeProxies[ProxyIndex]).TreeLevel = -1; - AddSlice(level, position, LevelSlots[position]); // restore the freeSlots[] values - } - } - - // We'll have to split the slice: - for (position = NextLargestFreeSlot(TimeSpan.MaxValue, 0, level); - position < LevelSlots.Length; - position = NextLargestFreeSlot(LevelSlots[position], position, level)) { - - FirstHalf = minInterval(LevelSlots[position] - RialtoScheduler.MinSlice - RialtoScheduler.ContextSwitch, - slice - RialtoScheduler.MinSlice - RialtoScheduler.ContextSwitch); - if (FirstHalf >= RialtoScheduler.MinSlice) { - Debug.Assert(LevelSlots[position] >= RialtoScheduler.MinSlice + FirstHalf); - SplitSlice(ProxyIndex, FirstHalf); - SubtractSlice(level, position, FirstHalf + RialtoScheduler.ContextSwitch); - //Note: TreeLevel is a composite number. Its value defines both the freelevel and the - //position within that level. It's of magnitude 1 << level, guaranteeing that - //position is less than 1 << level, so you can tell the level by the magnitude, and the - //position by looking at the remainder, so to speak. - ((NodeProxy)nodeProxies[ProxyIndex]).TreeLevel = (1 << level) - 1 + position; - if (AssignBranch(NextProxy(ProxyIndex))) { - return true; // look for A solution, not for the BEST one - } - ((NodeProxy)nodeProxies[ProxyIndex]).TreeLevel = -1; - AddSlice(level, position, FirstHalf + RialtoScheduler.ContextSwitch); // restore Previous freeSlots[] - RestoreSlice(ProxyIndex); - } - else { - //No point in continuing, the slots only get smaller. - break; - } - } -#if NOT_DEFINED - if (slice > (RialtoScheduler.MinSlice << 1)) { - FirstHalf = slice >> 1; - for (position = 0; position < LevelSlots.Length; position++) { - if (LevelSlots[position] - FirstHalf - RialtoScheduler.ContextSwitch >= RialtoScheduler.MinSlice) { - // try allocation w/ split - Debug.Assert(LevelSlots[position] >= RialtoScheduler.MinSlice); - SplitSlice(ProxyIndex, FirstHalf); - SubtractSlice(level, position, FirstHalf + RialtoScheduler.ContextSwitch); - nodeProxies[ProxyIndex].TreeLevel = (1 << level) - 1 + position; - if (AssignBranch(NextProxy(ProxyIndex))) { - return true; // look for A solution, not for the BEST one - } - nodeProxies[ProxyIndex].TreeLevel = -1; - AddSlice(level, position, FirstHalf + RialtoScheduler.ContextSwitch); // restore Previous freeSlots[] - RestoreSlice(ProxyIndex); - } - } - } -#endif - // Sorry.... - return false; - } - - static GraphNode BuildSchedulerPlan() - { - GraphNode newPlan = null; - - if (circularActivityList == null) { - // build a 'large' free node - newPlan = GraphNode.InitTree(); - } - else { - if (BuildHelperDataStructures()) { - // build the sorted nodeProxies and freeSlots arrays - - Scheduler.LogReservedActivity(nodeProxies.Count); // record additional cost - if (AssignBranch(FirstProxy())) { - newPlan = BuildLevel(0, // Level - minPeriod,// Period - minPeriod,// timeLeft - 0); // NextProxy - } - } - } - FreeHelperDataStructures(); - return newPlan; - } - - // TODO: (OLD) look only to the list of nodes of the activity - static TimeSpan ReservedPeriod(GraphNode plan, RialtoActivity activity) - { - if (plan == null) { - return new TimeSpan(0); - } - - switch(plan.Type) - { - case GraphNode.NodeType.Used: - if (plan.DefaultActivity == activity) { - return maxInterval(plan.Period, ReservedPeriod(plan.Next, activity)); - } - else { - return ReservedPeriod(plan.Next, activity); - } - case GraphNode.NodeType.Free: - return ReservedPeriod(plan.Next, activity); - default: // branch node - return maxInterval(ReservedPeriod(plan.Left, activity), - ReservedPeriod(plan.Right, activity)); - } - } - - // TODO - look only to the list of nodes of the activity - static TimeSpan ReservedSlice(GraphNode plan, TimeSpan period, RialtoActivity activity) - { - if (plan == null) { - return new TimeSpan(0); - } - - switch(plan.Type) - { - case GraphNode.NodeType.Used: - if (plan.DefaultActivity == activity) { - return (new TimeSpan((plan.Slice.Ticks * period.Ticks) / plan.Period.Ticks) + - ReservedSlice(plan.Next, period, activity)); - } - else { - return ReservedSlice(plan.Next, period, activity); - } - case GraphNode.NodeType.Free: - return ReservedSlice(plan.Next, period, activity); - default: // branch node - return ReservedSlice(plan.Left, period, activity) + - ReservedSlice(plan.Right, period, activity); - } - } - - static void GetActivityRecurringCpuReservation(GraphNode startList, out TimeSpan period, out TimeSpan slice) - { - GraphNode node; - TimeSpan tempPeriod, tempSlice; - - if ((node = startList) == null) { - period = CpuResource.MaxPeriod; - slice = new TimeSpan(0); - return; - } - tempPeriod = new TimeSpan(0); - tempSlice = new TimeSpan(0); - while (node != null) { - if (node.Period > tempPeriod) tempPeriod = node.Period; - node = node.SameActivityNext; - } - Debug.Assert(tempPeriod.Ticks != 0); - node = startList; - while (node != null) { - tempSlice += new TimeSpan((node.Slice.Ticks * tempPeriod.Ticks) / node.Period.Ticks); - node = node.SameActivityNext; - } - period = tempPeriod; - slice = tempSlice; - return; - } - - static void SetNextExec(GraphNode plan, DateTime time) - { - if (plan == null) { - return; - } - - plan.NextExec = time; - - if (plan.Type == GraphNode.NodeType.LeftBranch) { - SetNextExec(plan.Left, time); - SetNextExec(plan.Right, time + plan.Period); - } - else if (plan.Type == GraphNode.NodeType.RightBranch) { - SetNextExec(plan.Left, time + plan.Period); - SetNextExec(plan.Right, time); - } - else - SetNextExec(plan.Next, time + plan.Slice + RialtoScheduler.ContextSwitch); - - } - - static GraphNode SynchronizeSchedulerPlans(GraphNode newPlan, - DateTime timeChange, - ref bool localNeedToReschedule) - { - GraphNode oldPlan; - RialtoActivity activity = circularActivityList; // any pointer into the activity circular list works - Debug.Assert(Processor.InterruptsDisabled()); - - // set the pointers to the Used and Free nodes in the new plan: - FreeNodes = tempFreeNodes; - tempFreeNodes = null; - do { - if (activity.MyRecurringCpuReservation != null && activity.MyRecurringCpuReservation.Slice.Ticks > 0) { - activity.MyRecurringCpuReservation.AssignedNodes = activity.MyRecurringCpuReservation.TempAssignedNodes; - activity.MyRecurringCpuReservation.TempAssignedNodes = null; - } - activity = activity.Next; - } while (activity != circularActivityList); - -#if VERBOSE - DebugStub.Print("SettingNextExec: {0}\n", __arglist(timeChange.Ticks)); -#endif - SetNextExec(newPlan, timeChange); // TODO: extend later! - - oldPlan = SchedulerPlan; - SchedulerPlan = newPlan; - CurrentPlanNode = SchedulerPlan; - localNeedToReschedule = true; - - return oldPlan; - } - - static void ClearActivityNodes(GraphNode startNode, - RialtoActivity activity) - { - GraphNode temp; - - if (startNode == null) { - return; - } - - temp = startNode; - Debug.Assert((temp.Type == GraphNode.NodeType.Used) && (temp.DefaultActivity == activity)); - - while (temp.SameActivityNext != null) { - ActivityRemoveNode(activity); - temp.Type = GraphNode.NodeType.Free; - temp = temp.SameActivityNext; - Debug.Assert((temp.Type == GraphNode.NodeType.Used) && (temp.DefaultActivity == activity)); - } - - ActivityRemoveNode(activity); // for the last node in the list - temp.Type = GraphNode.NodeType.Free; - temp.DefaultActivity = null; - - // this should be non preemptible - temp.SameActivityNext = FreeNodes; - FreeNodes = startNode; - } - - // attaches nodes found to TempAssignedNodes - static bool AllocateActivityNodes(RialtoActivity activity, - ref TimeSpan slice, - ref TimeSpan period) - { - GraphNode node, bestNode = null; - GraphNode tmpNode, tmpBest = null; - TimeSpan adjustedSlice, bestPeriod = new TimeSpan(0), diffSlice; - bool split = false; - bool iflag = false; //So far we haven't disabled preemption. - TimeSpan bestDiffSlice = new TimeSpan(0), currentPeriod; - bool currentSplit; - GraphNode rightNode, leftNode, next, free; - for (tmpNode = null, node = FreeNodes; node != null; tmpNode = node, node = node.SameActivityNext) { - //node = *tmpNode; - Debug.Assert(node.Type == GraphNode.NodeType.Free); - - if (node.Period > period || - node.Period < bestPeriod) { - continue; - } - - // the activity may be assigned a node with reservations - // this increases the chances to find a node, although - // with such a node, the activity will not fully benefit of the - // allocation until the reservations are finished - // The alternative to the current choice: - // if (node.ReservCount > 0) - // continue; - - // S97 -- if the request's period is larger than the maximum period - // in the system, extend the tree with branch and activity - // nodes instead of converting the reservation to the maximum - // existing period - // -- is a request's period is equal to some period in the system, pick - // the best free node (not the first fit node) to make the - // reservation from. - - // - node.Next != null -- can not be transformed in a branch node - // apply the scaling technique - // - node.Next == null - if (node.Next != null || node.ReservCount != 0) { - currentPeriod = node.Period; - } - else { - currentPeriod = period; - } - diffSlice = new TimeSpan(-1); - - - while (currentPeriod >= node.Period) { - adjustedSlice = new TimeSpan((slice.Ticks * currentPeriod.Ticks)/ period.Ticks); - if (adjustedSlice < RialtoScheduler.MinSlice) { - break; - } - diffSlice = node.Slice - adjustedSlice - RialtoScheduler.ContextSwitch; - if (diffSlice.Ticks >= 0) { - break; - } - currentPeriod = new TimeSpan(currentPeriod.Ticks / 2); - } - if (diffSlice.Ticks < 0) { - continue; - } - - if (diffSlice >= RialtoScheduler.AFewSlice) { - currentSplit = true; - } - else { - currentSplit = false; - } - - if (currentSplit && diffSlice < RialtoScheduler.MinSlice) { - // the remaining slice is too long to be entirely reserved for the - // current request, but is also too small to represent an - // acceptable slice - continue; - } - // assume accepted - - if (bestNode == null || // no previous fit - currentPeriod > bestPeriod || // larger period - // at equal periods... - (split != currentSplit && split) || // the no split has priority - (split == currentSplit && // at the same split type... - // is split, larger diff is better, and otherwise, - // smaller diff is better - ((currentSplit && bestDiffSlice < diffSlice) || - (!currentSplit && bestDiffSlice > diffSlice)))) { - bestNode = node; - tmpBest = tmpNode; - bestPeriod = currentPeriod; - bestDiffSlice = diffSlice; - split = currentSplit; - } - } - - if (bestNode == null) { - return false; - } - - currentPeriod = bestNode.Period; - node = null; - adjustedSlice = new TimeSpan((slice.Ticks * bestPeriod.Ticks)/ period.Ticks); - - if (currentPeriod != bestPeriod) { - // not the same period: need to extend the tree up to bestPeriod. - - // create the subtree, but do not modify yet the bestNode (try - // to do atomically the modifications that require preemption disabled. - rightNode = new GraphNode(); //(PNODE)malloc(sizeof(GraphNode)); - //memset(rightNode, 0, sizeof(GraphNode)); - leftNode = new GraphNode(); //(PNODE)malloc(sizeof(GraphNode)); - //memset(leftNode, 0, sizeof(GraphNode)); - - TimeSpan halfPeriod = currentPeriod; //CK TEST - - // set rightNode as a free node except for SameActivityNext - currentPeriod += currentPeriod; - rightNode.Type = GraphNode.NodeType.Free; - rightNode.Period = currentPeriod; - rightNode.Slice = bestNode.Slice; - rightNode.NextExec = bestNode.NextExec + halfPeriod; //CK TEST currentPeriod - rightNode.TimeToOrigin = bestNode.TimeToOrigin; - free = rightNode; - // set leftNode as a branch node. - node = leftNode; - - for (;;) { - node.Type = GraphNode.NodeType.LeftBranch; - node.Period = currentPeriod; - Debug.Assert(currentPeriod.Ticks % minPeriod.Ticks == 0); - node.Slice = bestNode.Slice; - node.NextExec = bestNode.NextExec; - node.TimeToOrigin = bestNode.TimeToOrigin; - - if (currentPeriod == bestPeriod) { - break; - } - - halfPeriod = currentPeriod; //CK TEST - currentPeriod += currentPeriod; - - // set the right branch. - next = new GraphNode(); // (GraphNode)malloc(sizeof(GraphNode)); - //memset(Next, 0, sizeof(GraphNode)); - next.Type = GraphNode.NodeType.Free; - next.Period = currentPeriod; - Debug.Assert(currentPeriod.Ticks % minPeriod.Ticks == 0); - next.Slice = bestNode.Slice; - next.NextExec = bestNode.NextExec + halfPeriod; //CK TEST currentPeriod - next.TimeToOrigin = bestNode.TimeToOrigin; - next.SameActivityNext = free; - free = next; - - node.Right = next; - next = new GraphNode(); // (GraphNode)malloc(sizeof(GraphNode)); - //memset(Next, 0, sizeof(GraphNode)); - node.Left = next; - node = next; - } - if (split) { - next = new GraphNode(); - iflag = Processor.DisableInterrupts(); - // insert Used node before the Free node: - next.Slice = node.Slice - adjustedSlice - RialtoScheduler.ContextSwitch; - node.Slice = adjustedSlice; - node.Next = next; - - next.Type = GraphNode.NodeType.Free; - next.NextExec = node.NextExec + node.Slice + RialtoScheduler.ContextSwitch; - next.TimeToOrigin = node.TimeToOrigin - node.Slice - RialtoScheduler.ContextSwitch; - next.SameActivityNext = free; - free = next; - - // now set all the characteristics the new node - next.Period = node.Period; - Debug.Assert(node.Period.Ticks % minPeriod.Ticks == 0); - Debug.Assert(next.TimeToOrigin.Ticks >= 0); - } - else { - iflag = Processor.DisableInterrupts(); - } - - // modify bestNode. - bestNode.Type = GraphNode.NodeType.LeftBranch; - bestNode.Slice = bestNode.Slice; - bestNode.Left = leftNode; - bestNode.Right = rightNode; - if (tmpBest == null) { - FreeNodes = bestNode.SameActivityNext; - } - else { - tmpBest.SameActivityNext = bestNode.SameActivityNext; - } - - // add the new free nodes to the global list. - rightNode.SameActivityNext = FreeNodes; - FreeNodes = free; - } - else { - // allocate node at bestNode's period. - - if (split) { - node = new GraphNode(); //(PNODE)malloc(sizeof(GraphNode)); - //memset(node, 0, sizeof(GraphNode)); - - // insert Used node before Free node: - node.Slice = bestNode.Slice - adjustedSlice - RialtoScheduler.ContextSwitch; - node.NextExec = bestNode.NextExec + adjustedSlice + RialtoScheduler.ContextSwitch; - node.TimeToOrigin = bestNode.TimeToOrigin - adjustedSlice - RialtoScheduler.ContextSwitch; - node.Period = bestNode.Period; - Debug.Assert(node.TimeToOrigin.Ticks >= 0); - Debug.Assert(node.Period.Ticks % minPeriod.Ticks == 0); - - iflag = Processor.DisableInterrupts(); - bestNode.Slice = adjustedSlice; - bestNode.Next = node; - if (tmpBest == null) { - FreeNodes = bestNode.SameActivityNext; - } - else { - tmpBest.SameActivityNext = bestNode.SameActivityNext; - } - - if ((node.ReservCount = bestNode.ReservCount) > 0) { - int i; - node.ReservationArray = (ReservationSlice[])bestNode.ReservationArray.Clone(); - //memcpy(node.ReservationArray, bestNode.ReservationArray, - // sizeof(ReservationSlice) * node.ReservCount); - for (i = 0; i < node.ReservCount; i++) { - node.ReservationArray[i] = (ReservationSlice)node.ReservationArray[i].Clone(); - AddRefReservation(node.ReservationArray[i].AssociatedReservation); - } - } - node.SameActivityNext = FreeNodes; - FreeNodes = node; - } - else { - if (tmpBest == null) { - FreeNodes = bestNode.SameActivityNext; - } - else { - tmpBest.SameActivityNext = bestNode.SameActivityNext; - } - } - node = bestNode; - } - - node.Type = GraphNode.NodeType.Used; - node.DefaultActivity = activity; - node.SameActivityNext = activity.MyRecurringCpuReservation.TempAssignedNodes; - activity.MyRecurringCpuReservation.TempAssignedNodes = node; - - Processor.RestoreInterrupts(iflag); - - ActivityAddNode(activity); - - slice = node.Slice; - period = node.Period; - Debug.Assert(period.Ticks % minPeriod.Ticks == 0); - - return true; - } - - static bool IncrementalReserveActivity(RialtoActivity activity, - ref TimeSpan slice, - ref TimeSpan period) - // preemption disabled for the entire or part of the function - { - // UNLESS success is returned, DON'T modify slice and period - TimeSpan newSlice, newPeriod; - GraphNode node; - - Debug.Assert(period <= CpuResource.MaxPeriod); - // adjust Period and scale Slice down - if (slice.Ticks == 0 && activity.MyRecurringCpuReservation.Slice.Ticks > 0) { - bool iflag = Processor.DisableInterrupts(); - node = activity.MyRecurringCpuReservation.AssignedNodes; - activity.MyRecurringCpuReservation.AssignedNodes = null; - ClearActivityNodes(node, activity); - activity.MyRecurringCpuReservation.Slice = slice; - activity.MyRecurringCpuReservation.Period = period; - - Processor.RestoreInterrupts(iflag); - return true; - } - if (period < minPeriod) { - return false; - } - - for (newPeriod = minPeriod; newPeriod + newPeriod <= period; newPeriod += newPeriod) { - // no body. - } - newSlice = new TimeSpan((slice.Ticks * newPeriod.Ticks)/ period.Ticks); - - if ((activity.MyRecurringCpuReservation.Slice.Ticks > 0) && (activity.MyRecurringCpuReservation.Period > newPeriod)) { - // new period smaller than previous one - if (AllocateActivityNodes(activity, ref newSlice, ref newPeriod)) { - // uses TempAssignedNodes - Debug.Assert((newPeriod <= period) && (newSlice.Ticks >= (slice.Ticks * newPeriod.Ticks)/ period.Ticks)); - Debug.Assert(newPeriod.Ticks != 0); - bool iflag = Processor.DisableInterrupts(); - node = activity.MyRecurringCpuReservation.AssignedNodes; - activity.MyRecurringCpuReservation.AssignedNodes = activity.MyRecurringCpuReservation.TempAssignedNodes; - activity.MyRecurringCpuReservation.TempAssignedNodes = null; - activity.MyRecurringCpuReservation.Slice = slice = newSlice; - activity.MyRecurringCpuReservation.Period = period = newPeriod; - Debug.Assert(period == newPeriod); - Debug.Assert(newPeriod.Ticks % minPeriod.Ticks == 0); - - ClearActivityNodes(node, activity); - Processor.RestoreInterrupts(iflag); - return true; - } - else { - return false; - } - } - - - // compute the additional slice you need to allocate - newSlice -= new TimeSpan((activity.MyRecurringCpuReservation.Slice.Ticks * newPeriod.Ticks)/activity.MyRecurringCpuReservation.Period.Ticks); - Debug.Assert(newSlice.Ticks >= 0); - if (newSlice < RialtoScheduler.MinSlice) { - return false; - } - - if (AllocateActivityNodes(activity, ref newSlice, ref newPeriod)) { - freeCpu -= new TimeSpan((newSlice.Ticks * AmountCpu.Ticks)/newPeriod.Ticks); - - // transfer the new nodes from the TempAssignedNodes list to the AssignedNodes list: - node = activity.MyRecurringCpuReservation.TempAssignedNodes; - - while (node.SameActivityNext != null) { - node = node.SameActivityNext; - } - - bool iflag = Processor.DisableInterrupts(); - node.SameActivityNext = activity.MyRecurringCpuReservation.AssignedNodes; - activity.MyRecurringCpuReservation.AssignedNodes = activity.MyRecurringCpuReservation.TempAssignedNodes; - activity.MyRecurringCpuReservation.TempAssignedNodes = null; - GetActivityRecurringCpuReservation(activity.MyRecurringCpuReservation.AssignedNodes, out newPeriod, out newSlice); - activity.MyRecurringCpuReservation.Slice = slice = newSlice; - activity.MyRecurringCpuReservation.Period = period = newPeriod; - Processor.RestoreInterrupts(iflag); - - Debug.Assert(newPeriod.Ticks != 0); - // *pNewSlice is the total slice, not only the additional one - - Debug.Assert((newPeriod <= period) && (newSlice.Ticks >= (slice.Ticks * newPeriod.Ticks)/ period.Ticks)); - return true; - } - return false; - } - - // Reserve Activity (x out of any y) support functions end. - // ---------------------------------------------------------------------------------- - -#endregion - - // NOTE: 6/15/2004. This comment copied verbatim from the SOSP'97. As such, it may not - // be fully accurate. Read for yourself. - // - // In the following, reservation refers to a data structure allocated - // when 'BeginConstraint' is executed. - // - // IMPORTANT: - // The OneShotReservation data structure is always allocated, whether - // the constraint is found feasible or not. - // The OneShotReservation data structure is freed after the - // matching 'EndConstraint' call is executed AND the estimate is exhausted. - // Constraints that are not found feasible are assigned a zero estimate. - // - // At 'BeginConstraint', the new reservation is placed on a stack associated with - // the issuing thread; ReservationStack (in Thread) points to the top of the stack and - // SurroundingReservation (in OneShotReservation) points to the Next reservation on the stack. - // Reservations are removed from this stack at 'EndConstraint'. - // The stack mirrors the 'nesting' relationship between constraints. - // - // In addition, new reservations are placed on a double linked list: - // - feasible constraints on the 'GuaranteedReservations' list or, if Start < CurrentTime, - // on the 'IdleReservations' list. - // - constraints found non-feasible are placed on the 'UnfinishedConstraints' list - // associated with the activity of the issuing thread; since 'GetRunnableThread' looks for - // a runnable thread in the 'UnfinishedConstraints' list first, the issuing thread has - // 'higher priority' than the other threads of the same activity (and the matching - // 'EndConstraint' is executed earlier). - // - // Feasible NonCritical constraints can have their CPU reservation partially stolen by - // a Critical Constraint in the same activity. - // When this happens, the estimate of the victim reservation is diminished accordingly, - // and if the estimate gets to zero it is transfered to the 'UnfinishedConstraints' list - // - // When a reservation becomes active, it is moved from the 'pIdleReservation' to the - // 'GuaranteedReservations' - // - // Reservations WITH NO SurroundingReservations(i.e., NOT nested) - // -------------------------------------------------------------- - // If 'EndConstraint' is executed while the reservation is active, the status of the - // reservation changes to an activity reservation, i.e. the remaining CPU is allocated - // to the activity and not only to the issuing thread (see 'GetRunnableThread'). - // The reservation is not removed from the 'p(Non)GuaranteedReservations' lists until its - // estimate is exhausted. - // - // An active reservation is removed from the 'p(Non)GuaranteedReservations' lists - // when (see 'Reschedule' and 'EnqueueReservation'): - // - CurrentTime >= Deadline or - // - Estimate <= 0; the estimate is decremented every time the active thread of the - // reservation (see above and 'GetRunnableThread') is executed. If the matching - // 'EndConstraint' has not been executed by the issuing thread, the reservation is - // is placed 'UnfinishedConstraints' list of the issuing thread's activity. - // - // Reservations WITH SurroundingReservations(i.e., nested) - // --------------------------------------------------------- - // such a reservation may inherit time for any of the reservations on its nested stack. - // When an inherit happens, the estimate of the 'giving' reservation is diminished - // accordingly. - // When a nested reservation is granted (i.e., estimate > 0), it will be positioned on the - // idle or Guaranteed lists AND, all other surrounding reservations will be dequeued (from - // which ever Guaranteed or Unfinished list they are. - // The reservation is not removed from the 'GuaranteedReservations' lists until its - // estimate is exhausted or at EndConstraint. - // - // If 'EndConstraint' is executed - // while the reservation is active (i.e., estimate > 0), - // the estimate is given to the immediately surrounding reservation, and this is - // placed on Guaranteed list. - // while the reservation has no estimate (on the Unfinished list) - // it will be simply removed from the list - // - // When a reservation gets to estimate 0 before EndConstraint - // (see 'Reschedule', StealNonCriticalCpu) - // it is placed on the Unfinished list. The same happen with all the surrounding - // reservations up to the first with estimate non zero which is placed on the - // Guaranteed list. - // - -#region OneShotCpuReservation related functions - - // OneShotCpuReservation related functions begin: - - public static int ReleaseReservationProtected(OneShotReservation reservation) - { - int newrefcnt; - Debug.Assert(Processor.InterruptsDisabled()); - - Debug.Assert(reservation.ReferenceCount > 0); - newrefcnt = Interlocked.Decrement(ref reservation.ReferenceCount); - - if (newrefcnt == 0) { - // Estimate may be positive when reservation reaches - // its Deadline w/o using its Estimate - Debug.Assert((reservation.Next == null) && (reservation.Previous == null)); - if (reservation.OriginalThread != null) { - reservation.OriginalThread.ReleaseProtected(); - //Debug.Assert(reservation.ActiveThread != null); - //reservation.ActiveThread.ReleaseProtected(); - } - else { - Debug.Assert(reservation.AssociatedActivity != null); - ActivityReleaseProtected(reservation.AssociatedActivity); - } - Debug.Assert(reservation.Next == null); - CacheFreeReservation(reservation); - } - return newrefcnt; - } - - - public static OneShotReservation AllocateReservation() - { - OneShotReservation reservation; - - // DisableInterrupts(); - reservation = IpcAllocateReservation(); - // EnableInterrupts(); - - if (reservation == null) { - reservation = new OneShotReservation(); //(PRESERVATION)malloc(sizeof(struct OneShotReservation)); // to be done while on the I-stack! - reservation.Clear(); - } - return reservation; - } - - static void FreeReservation(OneShotReservation reservation) - { - reservation = null; //free(reservation); - } - - // Garbage-collect unused Reservations. - - public static int ReleaseReservation(OneShotReservation reservation) - { - int newrefcnt; - // Debug.Assert(!Processor.InterruptsDisabled()); - Debug.Assert(reservation.ReferenceCount > 0); - newrefcnt = reservation.ReferenceCount - 1; - reservation.ReferenceCount = newrefcnt; - - if (newrefcnt == 0) { - // Estimate may be positive when reservation reaches its Deadline w/o using its Estimate - Debug.Assert((reservation.Next == null) && (reservation.Previous == null)); - if (reservation.OriginalThread != null) { - reservation.OriginalThread.Release(); - //Debug.Assert(reservation.ActiveThread != null); - //reservation.ActiveThread.Release(); - } - else if (reservation.AssociatedActivity != null) { - ActivityObjRelease(reservation.AssociatedActivity); - } - CacheFreeReservation(reservation); - } - return newrefcnt; - } - - // Like CheckFreeConstraint, but callable from the IPC path. - // Because we can't call AllocateReservation, we use the helper thread - // if there aren't any free Reservations. - static void CacheFreeReservation(OneShotReservation reservation) - { - if (reservation.OriginalThread != null && - reservation.OriginalThread.FreeReservation == null) { - reservation.OriginalThread.FreeReservation = reservation; - } - else { - IpcFreeReservation(reservation); - } - } - - - // Callable with preemption disabled. Allocates a OneShotReservation - // from the global free list. - public static OneShotReservation IpcAllocateReservation() - { - OneShotReservation reservation; - - if ((reservation = ReservationFreePool) != null) { - ReservationFreePool = reservation.FreeListNext; - } - - return reservation; - } - - - // Callable with preemption disabled. Frees a OneShotReservation - // to the global free list. - public static void IpcFreeReservation(OneShotReservation reservation) - { - Debug.Assert(reservation != null); - Debug.Assert(reservation.ReferenceCount == 0); - - reservation.FreeListNext = ReservationFreePool; - ReservationFreePool = reservation; - } - - //========================================================= - - public static void AddRefReservation(OneShotReservation reservation) - { - Debug.Assert(reservation.ReferenceCount >= 0); - reservation.ReferenceCount++; - } - -#if DEBUG_RESERV - static void PrintQueueReserv(OneShotReservation reservation, DateTime timeNow) - { - OneShotReservation pSave = reservation; - - DebugStub.Print("ReservQueue {0} {1:d}\n", - __arglist((pSave == null? "null":""), timeNow)); - while (reservation != null) { - DebugStub.Print("R{0} T{1}:A{2} - Deadline {3:d} Estimate {4:d} Lax {5:d}", - __arglist( - reservation.ReservationId, - (reservation.OriginalThread != null - ? reservation.OriginalThread.Id : -1), - (reservation.OriginalThread != null - ? reservation.OriginalThread.pActivity.Id - : reservation.pActivity.Id), - reservation.Deadline, - reservation.Estimate, - reservation.Deadline - timeNow)); - - if ((reservation = reservation.Next) == pSave) { - break; - } - } - } -#endif -#endregion - -#region Potpourri (Unregioned functions) - - static GraphNode NextNode(GraphNode currentNode) - { - if (currentNode.Next != null) { - currentNode = currentNode.Next; - } - else { - currentNode = SchedulerPlan; - } - - while ((currentNode.Type == GraphNode.NodeType.RightBranch) || - (currentNode.Type == GraphNode.NodeType.LeftBranch)) { - - if (currentNode.Type == GraphNode.NodeType.RightBranch) { - currentNode.Type = GraphNode.NodeType.LeftBranch; // Next time take the other branch - currentNode = currentNode.Right; - } - else if (currentNode.Type == GraphNode.NodeType.LeftBranch) { - currentNode.Type = GraphNode.NodeType.RightBranch;// Next time take the other branch - currentNode = currentNode.Left; - } - } - Debug.Assert(currentNode != null); - Debug.Assert(currentNode.TimeToOrigin.Ticks >= 0); - Debug.Assert(currentNode.Period.Ticks % minPeriod.Ticks == 0); - - return currentNode; - } - - - // returns true if there is at least one free slot left in the array - static bool CleanReservationArray(GraphNode node, DateTime timeNow) - { - int i, j; - OneShotReservation reservation; - - for (i = j = 0; i < node.ReservCount; i++) { - reservation = node.ReservationArray[i].AssociatedReservation; - if (node.ReservationArray[i].End <= timeNow + RialtoScheduler.AFewSlice || !reservation.Valid || - (reservation.Estimate <= RialtoScheduler.AFewSlice && reservation.OriginalThread == null)) { - // DebugStub.Print("CleanReservationArray {0} {1}\n", - // __arglist(reservation, reservation.ReferenceCount)); - ReleaseReservationProtected(reservation); // each slice has own reference - } - else { - if (j < i) { - node.ReservationArray[j] = (ReservationSlice)node.ReservationArray[i].Clone(); - } - //memcpy(&(node.ReservationArray[j]), &(node.ReservationArray[i]), sizeof(ReservationSlice)); - j++; - } - } - node.ReservCount = j; - Debug.Assert(node.ReservCount <= GraphNode.MaxNumReservations); - return (j < GraphNode.MaxNumReservations); - } - - public static RialtoThread GetCurrentThread() - { - return currentThreadGlobal; - } - - public static void IChangeCurrentThread(RialtoThread thread) - { - currentThreadGlobal = thread; - } - - // Auxiliary Routines - // TODO: Revisit the necessity of these functions. - public static TimeSpan minInterval(TimeSpan TimeInterv0, TimeSpan TimeInterv1) - { - return (TimeInterv0 < TimeInterv1? TimeInterv0: TimeInterv1); - } - - public static DateTime minTime(DateTime Time0, DateTime Time1) - { - return (Time0 < Time1? Time0: Time1); - } - - static TimeSpan maxInterval(TimeSpan TimeInterv0, TimeSpan TimeInterv1) - { - return (TimeInterv0 < TimeInterv1? TimeInterv1: TimeInterv0); - } - - static DateTime maxTime(DateTime Time0, DateTime Time1) - { - return (Time0 < Time1 ? Time1: Time0); - } - - // The Queue of Standby Activities is circular and - // standbyActivities points to the Next activity - // to get spare CPU. By default, the Queue is FIFO. - public static void EnqueueStandbyActivity(RialtoActivity activity) - { - Debug.Assert(Processor.InterruptsDisabled()); - activity.LastNode = null; - - if (activity.NextStandbyActivity != null) { - Debug.Assert(activity.PreviousStandbyActivity != null); - return; - } - - if (standbyActivities == null) { - activity.PreviousStandbyActivity = - activity.NextStandbyActivity = activity; - standbyActivities = activity; - return; - } - // else ... - // insert in front of 'standbyActivities' - activity.NextStandbyActivity = standbyActivities; - activity.PreviousStandbyActivity = standbyActivities.PreviousStandbyActivity; - standbyActivities.PreviousStandbyActivity.NextStandbyActivity = activity; - standbyActivities.PreviousStandbyActivity = activity; -#if LIFO - standbyActivities = activity; -#endif - } - - static void DequeueStandbyActivity(RialtoActivity activity) - { - Debug.Assert(Processor.InterruptsDisabled()); - - if (activity.NextStandbyActivity == null) { - // if not in Queue, a NOP - Debug.Assert(activity.PreviousStandbyActivity == null); - return; - } - - if (activity.NextStandbyActivity == activity) { - // last one in the Queue - Debug.Assert(activity.PreviousStandbyActivity == activity); - standbyActivities = null; - } - else { - activity.PreviousStandbyActivity.NextStandbyActivity = - activity.NextStandbyActivity; - activity.NextStandbyActivity.PreviousStandbyActivity = - activity.PreviousStandbyActivity; - if (standbyActivities == activity) { - // advance the Queue head - standbyActivities = activity.NextStandbyActivity; - } - } - activity.NextStandbyActivity = activity.PreviousStandbyActivity = null; - } - - // Add Activity in the last Round-Robin position - public static void EnqueueActivity(RialtoActivity activity) - { - Debug.Assert(Processor.InterruptsDisabled()); - if (circularActivityList == null) { - robinActivityNext = circularActivityList = robinActivityCurrent = - activity.Next = activity.Previous = activity; - } - else { - activity.Next = robinActivityNext; - activity.Previous = robinActivityNext.Previous; - robinActivityNext.Previous.Next = activity; - robinActivityNext.Previous = activity; - } - } - - public static void DequeueActivity(RialtoActivity activity) //TODO: Make an activity function? - { - // In our simulated environment, Activity 0 is never removed from the Q - Debug.Assert(Processor.InterruptsDisabled()); - //NOTE: Might be replaced with a test and return. - Debug.Assert(activity.Next != null); - Debug.Assert(activity.Previous != null); - - // Previous. in rialto if (activity.Id == 0) return; - - DequeueStandbyActivity(activity); - - activity.Next.Previous = activity.Previous; - activity.Previous.Next = activity.Next; - if (circularActivityList == activity) { - circularActivityList = activity.Next; - } - if (robinActivityNext == activity) { - robinActivityNext = activity.Next; - } - } - - // if possible, places thread in the 2nd position in the RunnableThreads Q - // Make this a function on a resource container. - public static void EnqueueRunThread(RialtoThread thread) - { - Debug.Assert(Processor.InterruptsDisabled()); - RialtoActivity activity = thread.AssociatedActivity; - if (thread.Next != null) { - Debug.Assert(thread.Previous != null); - return; - } - - // Debug.Assert(thread.QueueType == QUEUE_NONE); - - //if (thread.State == RialtoThread.ThreadState.ThreadWaiting) { - // thread.SetState(RialtoThread.ThreadState.ThreadReady); - //} - - if (activity.RunnableThreads == null) { - thread.Next = thread.Previous = thread; - activity.RunnableThreads = thread; - } - else { - thread.Previous = activity.RunnableThreads; - thread.Next = activity.RunnableThreads.Next; - activity.RunnableThreads.Next.Previous = thread; - activity.RunnableThreads.Next = thread; - } - } - - public static bool DequeueRunThread(RialtoThread thread) - { - Debug.Assert(Processor.InterruptsDisabled()); - if (thread.Next == null) { - Debug.Assert(thread.Previous == null); - return false; - } - - if (thread.Next == thread) { - Debug.Assert(thread.Previous == thread); - thread.AssociatedActivity.RunnableThreads = null; - DequeueStandbyActivity(thread.AssociatedActivity); - } - else { - // multiple threads in the RunnableThreads Q - thread.Previous.Next = thread.Next; - thread.Next.Previous = thread.Previous; - if (thread.AssociatedActivity.RunnableThreads == thread) { - thread.AssociatedActivity.RunnableThreads = thread.Next; - } - } - thread.Next = thread.Previous = null; - return true; - } -#endregion - -#region Additions according to kernel implementation - - static bool ResetTimerTimeout(DateTime NewTimerTimeout) - { - DateTime timeNow = SchedulingTime; - - if (timeNow > NewTimerTimeout) { - return false; - } - - Debug.Assert(Processor.InterruptsDisabled()); - // Debug.Assert(CurrentThread()->GetState() == Thread.ThreadState.ThreadRunning); - - // Debug.Assert(NewTimerTimeout > SchedulingTime); - - //TODO: Need LA_Time? - // if (NewTimerTimeout > SleepTimeout - LA_Time) { - // NewTimerTimeout = SleepTimeout - LA_Time; - // } - if (NewTimerTimeout > RialtoThread.GetSleepTimeout()) { - NewTimerTimeout = RialtoThread.GetSleepTimeout(); - } - - //DebugStub.Print("Setting Next Interrupt for: {0} ...\n", - // __arglist(NewTimerTimeout.Ticks)); - bool success = SchedulerClock.SetNextInterrupt(NewTimerTimeout); //TODO: Perhaps only call this if the time changed. - - //DebugStub.Print(success?"SUCCESS\n":"FAILED\n"); - - return success; - } - - public static void MoveToStandby(RialtoThread thread, bool fromWakeUp) - { - if (thread.AssociatedActivity.LastNode == null) { - if (fromWakeUp) { - // a thread that wakes up always gets a (small) chance to run: - // but ...IReadyThread would not do this enqueue - //thread.AssociatedActivity.MyRecurringCpuReservation.SliceLeft = RialtoScheduler.MinSlice; - EnqueueStandbyActivity(thread.AssociatedActivity); - } - } - else { - // sets 'pthd.pActivity.LastNode' to null. - EnqueueStandbyActivity(thread.AssociatedActivity); - if (CurrentPlanNode == thread.AssociatedActivity.LastNode) { - //TODO: since EnqueueStandbyActivity sets it to null, isn't this a no-op? - Reschedule(); - } - } - } - - - static void UpdateStandbyStatus(RialtoThread thread) - { - RialtoActivity activity = thread.AssociatedActivity; - - if ((activity.RunnableThreads == null) && - (CurrentPlanNode.Type == GraphNode.NodeType.Used) && - (CurrentPlanNode.DefaultActivity == activity) && - ((activity.MyRecurringCpuReservation.SliceLeft = CurrentPlanNode.NextExec + CurrentPlanNode.Slice - - SystemClock.GetKernelTime()) >= RialtoScheduler.MinSlice)) { - activity.LastNode = CurrentPlanNode; // record it. - } - } - - - public static void UpdateSchedulingStatus() - { - TimeSpan timeRun; - RialtoThread currentThread = GetCurrentThread(); - - DateTime newSchedulingTime = SystemClock.GetKernelTime(); - - timeRun = newSchedulingTime - SchedulingTime; - SchedulingTime = newSchedulingTime; - -#if false - Tracing.Log(Tracing.Debug, "UpdateSchedulingStatus"); -#endif - - if (idle) { - // UpdateIdleTime - idleTime += timeRun; - return; - } - - // if IDLE compute CurrentPlanNode & set currentThread to null - if (currentThread != null) { - // unless thread just exited - // Update Thread & Activity execution times - currentThread.AddExecutionTime(timeRun); - // In the current implementation, the scheduler has to call CurrentTask on the - // known running thread, since the thread may not actually be running when - // UpdateSchedulingStatus() is called. It may be the scheduler thread running. - // I left things the way they are though since Scheduler is suitable to - // use for any application/non-kernel code. We may need to document it there - // too, or perhaps have it ask the CpuResource what the default task is at the - // moment, since presumably it may be the CpuResource which actually tracks that. - currentThread.EnclosingThread.CurrentTask().AddResourceAmountUsed(CpuResource.Provider().ResourceString, CpuResource.Provider().TimeToCpu(timeRun)); - //Scheduler.CurrentTask().AddResourceAmountUsed(CpuResource.Provider().ResourceString, CpuResource.Provider().TimeToCpu(timeRun)); - - // if (currentThread.AssociatedActivity != null) { - // currentThread.AssociatedActivity.MyRecurringCpuReservation.EnclosingCpuReservation.AddTimeUsed(timeRun); - // } - } - if (OneShotReservation.CurrentReservation != null) { - // slice used for a reservation. - OneShotReservation.CurrentReservation.Estimate -= timeRun; - } - else if (currentStandbyActivity != null) { - if (currentStandbyActivity.MyRecurringCpuReservation != null) { - currentStandbyActivity.MyRecurringCpuReservation.SliceLeft -= timeRun; - } - currentStandbyActivity = null; - } - else if (robinActivityCurrent != null) { - // slice used for RoundRobin. - robinSliceLeft -= timeRun; - robinActivityCurrent = null; - } - } - - - // Always called when another thread needs to be scheduled. - static bool RescheduleInterrupt() - { - Debug.Assert(Processor.InterruptsDisabled()); - Debug.Assert(!Processor.InterruptsDisabled()); - RialtoThread currentThread = GetCurrentThread(); - - DateTime nextStart; - RialtoThread previousThread; - - RescheduleAgain: // !!!!!!!!!! SIM ONLY !!!!!!!!! - if (idle) { - currentThread = null; - } - - previousThread = currentThread; - - EarliestDeadlineFirstBlocked = false; - - UpdateSchedulingStatus(); - - // if thread was the head of the runnable threads Q: - // Advance the runnable threads Q. - if ((currentThread != null) && - (currentThread.AssociatedActivity != null) && - (currentThread.AssociatedActivity.RunnableThreads == currentThread)) { - currentThread.AssociatedActivity.RunnableThreads = currentThread.Next; - Debug.Assert(currentThread.AssociatedActivity.RunnableThreads != null); - } - - OneShotReservation.UpdateCurrentReservation(); - -#if LOG_SCHEDULER_DETAILS - bool logDirectSwitch = (directSwitchTo != null); - long logCurrentNodeSliceLeft1 = 0; - long logCurrentNodeSliceLeft2 = 0; - long logCurrentNodeSliceLeft3 = 0; - int logCurrentPlanNodeId = -1; - int logReservCount = 0; - bool logRunningDefault = false; - bool logRunningStandby = false; - bool logRunningRobin = false; - long logCurrentNodeSliceLeft4 = 0; - long logCurrentNodeSliceLeft = 0; -#endif - - //Why not check this directly? - if (directSwitchTo != null) { - //Debug.Print("directSwitchTo!\n"); - Debug.Assert(OneShotReservation.CurrentReservation != null); - Scheduler.LogContextSwitch(); // Context Switch statistics - // Debug.Assert(currentThread.QueueType == QUEUE_NONE); - currentThread = directSwitchTo; - directSwitchTo = null; - Scheduler.LogReschedule(); - // DebugStub.Print("Directed Context Switch.\n"); - goto exitOK; // don't set TIMER interrupt - } - - OneShotReservation.ClearCurrentReservation(); - currentThread = null; - - // Finished first stage, i.e. updated state. - // Start second stage: wakeup threads & select Next CPU slice. - - RialtoThread.WakeThreads(); - - // NOTE: In the original Rialto Simulator Code (& MMOSA code) - // The call to DrainDeferredConditions() was made here. - // In Singularity, this will basically be replaced with a - // queue of wait-events to fix. - - OneShotReservation.FreshenReservationQueues(); - -#if NOT_DEFINED - if (SchedulingTime >= changePlanTime) { - GraphNode ptemp = SchedulerPlan; - SchedulerPlan = pNextPlan; - pNextPlan = null; - changePlanTime = TIME_FOREVER; - Debug.Assert(false); - FreeSchedulerPlan(ptemp); - CurrentPlanNode = SchedulerPlan; - } -#endif - TimeSpan currentNodeSliceLeft; - // Compute currentNodeSliceLeft (possible (very) negative). - - Debug.Assert(CurrentPlanNode != null); - //DebugStub.Print("Next Exec: {0} Slide: {1} SchedulingTime: {2}\n", - //__arglist(CurrentPlanNode.NextExec.Ticks, - // CurrentPlanNode.Slice.Ticks, - // SchedulingTime.Ticks)); - currentNodeSliceLeft = CurrentPlanNode.NextExec + CurrentPlanNode.Slice - SchedulingTime; - - Tracing.Log(Tracing.Debug, "Reservation for {0} ticks", - (UIntPtr)unchecked((uint)currentNodeSliceLeft.Ticks)); - -#if LOG_SCHEDULER_DETAILS - logCurrentNodeSliceLeft1 = currentNodeSliceLeft.Ticks; -#endif - - while (currentNodeSliceLeft.Ticks < 0) { - // choose Next node - GraphNode tempNode = CurrentPlanNode; - DateTime tempTime = CurrentPlanNode.NextExec; - CurrentPlanNode.NextExec += CurrentPlanNode.Period; // time of Next execution - // If incomplete, the default activity will be added - // to the 'pStandbyActiv' List when ready - CurrentPlanNode = NextNode(CurrentPlanNode); - Debug.Assert(CurrentPlanNode.NextExec <= tempNode.NextExec); - Debug.Assert(CurrentPlanNode.NextExec > tempTime); - Debug.Assert(CurrentPlanNode.NextExec <= SchedulerPlan.NextExec); //In fact, should be min of all nodes. - currentNodeSliceLeft = CurrentPlanNode.NextExec + CurrentPlanNode.Slice - SchedulingTime; - } - -#if LOG_SCHEDULER_DETAILS - logCurrentNodeSliceLeft2 = currentNodeSliceLeft.Ticks; -#endif - - if (currentNodeSliceLeft < RialtoScheduler.MinSlice) { - // choose Next node - CurrentPlanNode.NextExec += CurrentPlanNode.Period; // time of Next execution - // If incomplete, the default activity will be added - // to the 'pStandbyActiv' List when ready - CurrentPlanNode = NextNode(CurrentPlanNode); - Debug.Assert(CurrentPlanNode.NextExec <= SchedulerPlan.NextExec); - currentNodeSliceLeft = CurrentPlanNode.NextExec + CurrentPlanNode.Slice - SchedulingTime; // don't waste the leftover - } - -#if LOG_SCHEDULER_DETAILS - logCurrentNodeSliceLeft3 = currentNodeSliceLeft.Ticks; - logCurrentPlanNodeId = (CurrentPlanNode.DefaultActivity != null) ? CurrentPlanNode.DefaultActivity.Id : -1; -#endif - - // Do adjustment before computation of CurrentSlice is started - // currentNodeSliceLeft -= RESCHEDULE_OVHD; // !!!!!!!!!!! SIM only !!!!!!!!!!! - - //CurrentSlice = currentNodeSliceLeft; - Debug.Assert(currentNodeSliceLeft /* CurrentSlice */ >= RialtoScheduler.MinSlice); - - if (CurrentPlanNode.ReservCount > 0) { - if (!CurrentPlanNode.ReservationArray[0].AssociatedReservation.Valid || // need to do some cleaning - (CurrentPlanNode.ReservationArray[0].End <= SchedulingTime + RialtoScheduler.AFewSlice) || - (CurrentPlanNode.ReservationArray[0].AssociatedReservation.Estimate <= RialtoScheduler.AFewSlice && CurrentPlanNode.ReservationArray[0].AssociatedReservation.OriginalThread == null)) { - CleanReservationArray(CurrentPlanNode, SchedulingTime); - } - - if (CurrentPlanNode.ReservCount > 0) { - // are there any active reservation left? -#if LOG_SCHEDULER_DETAILS - logReservCount = CurrentPlanNode.ReservCount; -#endif - - nextStart = CurrentPlanNode.ReservationArray[0].Start; - - if (nextStart > SchedulingTime + RialtoScheduler.AFewSlice) { - if (nextStart < SchedulingTime + currentNodeSliceLeft) { - // GATECH - currentNodeSliceLeft /* CurrentSlice */ = nextStart - SchedulingTime; // GATECH - Debug.Assert(currentNodeSliceLeft /* CurrentSlice */.Ticks > 0); - } - } - else { - // GraphNode OneShotReservation in effect; find a runnable reservation: - OneShotReservation.FindRunnableReservation(ref currentThread); - if (currentThread != null) { - // a runnable reservation was found: - // TO DO: optimize the comparisons - currentNodeSliceLeft /* CurrentSlice */ = minInterval(currentNodeSliceLeft /* CurrentSlice */, - CurrentPlanNode.ReservationArray[0].End - SchedulingTime); - currentNodeSliceLeft /* CurrentSlice */ = minInterval(currentNodeSliceLeft /* CurrentSlice */, OneShotReservation.CurrentReservation.Estimate); - currentNodeSliceLeft /* CurrentSlice */ = minInterval(currentNodeSliceLeft /* CurrentSlice */, - OneShotReservation.CurrentReservation.Deadline - SchedulingTime); - // DebugStub.Print("OneShotCpuReservation in effect.\n"); - goto AdjustSlice; - } - else { - EarliestDeadlineFirstBlocked = true; - OneShotReservation.ClearCurrentReservation(); - } - } // else - } // 2nd if (CurrentPlanNode.ReservCount > 0) - } // 1st if (CurrentPlanNode.ReservCount > 0) - - // No GraphNode OneShotReservation in effect; run default activity, if any: - if (CurrentPlanNode.Type == GraphNode.NodeType.Used) { - DequeueStandbyActivity(CurrentPlanNode.DefaultActivity); // nop if not on list - if ((currentThread = CurrentPlanNode.DefaultActivity.GetRunnableThread()) != null) { - // DebugStub.Print("Running Default Resource Container.\n"); - Tracing.Log(Tracing.Debug, "Default resource container."); -#if LOG_SCHEDULER_DETAILS - logRunningDefault = true; -#endif - goto AdjustSlice; - } - else if (CurrentPlanNode.DefaultActivity.LastNode == CurrentPlanNode) { - CurrentPlanNode.DefaultActivity.LastNode = null; - } - } - - // Current GraphNode is Free or the default activity is not runnable; - // Try to use slice for the queue of Standby Activities */ - while (standbyActivities != null) { - // Dequeue some of the activities that have too little left to do! - if (standbyActivities.MyRecurringCpuReservation != null && standbyActivities.MyRecurringCpuReservation.SliceLeft < RialtoScheduler.MinSlice) { - DequeueStandbyActivity(standbyActivities); - continue; // if it was the last incomplete activity, exit while - } - - currentThread = standbyActivities.GetRunnableThread(); - currentNodeSliceLeft /* CurrentSlice */ = minInterval(currentNodeSliceLeft /* CurrentSlice */, (standbyActivities.MyRecurringCpuReservation == null? RialtoScheduler.MinSlice: standbyActivities.MyRecurringCpuReservation.SliceLeft)); - currentStandbyActivity = standbyActivities; - if (currentStandbyActivity.MyRecurringCpuReservation == null) { - DequeueStandbyActivity(standbyActivities); - } - Debug.Assert(currentThread != null); - //DebugStub.Print("Running a standby resource container\n"); -#if LOG_SCHEDULER_DETAILS - logRunningStandby = true; -#endif - goto AdjustSlice; - } - - // Same about Current GraphNode but no Standby Activity to run. - // Use slice for the RoundRobin queue (always nonempty). - if (robinSliceLeft < RialtoScheduler.MinSlice) { - robinActivityNext = robinActivityNext.Next; // don't run it again - robinSliceLeft = RialtoScheduler.RobinSlice; - } - // Find Next Runnable Activity. - robinActivityCurrent = robinActivityNext; // !!!!!!!!!!!!!SIM ONLY !!!!!!!!!! - while ((currentThread = robinActivityNext.GetRunnableThread()) == null) { - robinActivityNext = robinActivityNext.Next; - robinSliceLeft = RialtoScheduler.RobinSlice; - if (robinActivityNext == robinActivityCurrent) { - // !!!!!!!!SIM ONLY !!!!!!! - // in the real scheduler, execute halt - if (OneShotReservation.IdleReservations != null) { - // reuse nextStart - nextStart = minTime(RialtoThread.GetSleepTimeout(), OneShotReservation.IdleReservations.Start); - } - else { - nextStart = RialtoThread.GetSleepTimeout(); - //DebugStub.Print("idle, sleeping until {0} cf maxvalue {1}\n", - // __arglist(nextStart.Ticks, DateTime.MaxValue.Ticks)); - } - - if (nextStart == DateTime.MaxValue) { - Scheduler.StopSystem(); - } - Tracing.Log(Tracing.Debug, ""); - - if (! ResetTimerTimeout(nextStart)) { - //Error setting timer. Try scheduling again. - DebugStub.Print("Thought idle, failed to set interrupt.\n"); - goto RescheduleAgain; - } - idle = true; - - // !!!!!!!!SIM ONLY !!!!!!! - currentThread = null; // !!!!!!!!SIM ONLY !!!!!!! - OneShotReservation.ClearCurrentReservation(); - robinActivityCurrent = null; - currentStandbyActivity = null; - - if (DateTime.MaxValue != nextStart) { - Scheduler.LogTimeJump(); - } - - //DebugStub.Print("Halted.\n"); - return true; // !!!!!!!!SIM ONLY !!!!!!! - } - // !!!!!!!!SIM ONLY !!!!!!! - } - //DebugStub.Print("Running Round Robin Resource Container\n"); -#if LOG_SCHEDULER_DETAILS - logRunningRobin = true; -#endif - robinActivityCurrent = robinActivityNext; // we probably need only one of the two variables - - currentNodeSliceLeft /* CurrentSlice */ = minInterval(currentNodeSliceLeft /* CurrentSlice */, robinSliceLeft); - -#if LOG_SCHEDULER_DETAILS - logCurrentNodeSliceLeft4 = currentNodeSliceLeft.Ticks; -#endif - - AdjustSlice: // always set timer before Next wakeup time! - - Debug.Assert(currentThread != null); - if (currentThread != previousThread) { - Scheduler.LogContextSwitch(); // Context Switch statistics - } - - if (OneShotReservation.IdleReservations != null) { - // reuse nextStart - nextStart = minTime(RialtoThread.GetSleepTimeout(), OneShotReservation.IdleReservations.Start); - } - else { - nextStart = RialtoThread.GetSleepTimeout(); - } - - if (SchedulingTime + currentNodeSliceLeft /* CurrentSlice */ > nextStart) { - currentNodeSliceLeft /* CurrentSlice */ = nextStart - SchedulingTime; - } - -#if LOG_SCHEDULER_DETAILS - logCurrentNodeSliceLeft = currentNodeSliceLeft.Ticks; - -#if VERBOSEX - unchecked - { - Tracing.LogSchedulerDetails( - logReservCount, logDirectSwitch, logRunningDefault, logRunningStandby, logRunningRobin, - (int) logCurrentNodeSliceLeft1, (int) logCurrentNodeSliceLeft2, (int) logCurrentNodeSliceLeft3, - (int) logCurrentNodeSliceLeft4, (int) logCurrentNodeSliceLeft, logCurrentPlanNodeId); - } -#endif -#endif - - Scheduler.LogReschedule(); - if (!ResetTimerTimeout(SchedulingTime + currentNodeSliceLeft) || Scheduler.TimerInterruptedFlag) { - //TODO: What do we REALLY want here? - currentThread = null; // !!!!!!!!SIM ONLY !!!!!!! - OneShotReservation.ClearCurrentReservation(); - robinActivityCurrent = null; - currentStandbyActivity = null; - goto RescheduleAgain; - } - - exitOK: - - if (currentThread != previousThread) { - IChangeCurrentThread(currentThread); - } - idle = false; - - // Not necessarily true: Debug.Assert(!Scheduler.TimerInterruptedFlag); - - return false; - } - -#endregion - -#region Constraint feasibility analysis support functions - // Constraint Feasibility Analysis support functions begin: - // ---------------------------------------------------------------------------------- - static TimeSpan ComputeAvailableCpu(DateTime begin, DateTime end, GraphNode node) - { - if (begin >= end) { - return new TimeSpan(0); - } - else { - TimeSpan reserved = new TimeSpan(0); - DateTime nextExecution; - int k; - - if (node == CurrentPlanNode) { - nextExecution = node.NextExec + node.Period; - if (begin < node.NextExec + node.Slice) { // TempStart >= CurrentTime - reserved += node.NextExec + node.Slice - begin; - } - } - else { - nextExecution = node.NextExec; - } - - if (begin > nextExecution) { - k = (int) ((begin - nextExecution).Ticks/node.Period.Ticks); - reserved -= new TimeSpan(k * node.Slice.Ticks) + minInterval(node.Slice, - begin - nextExecution - new TimeSpan(k * node.Period.Ticks)); - } - - if (end > nextExecution) { - k = (int) ((end - nextExecution).Ticks/node.Period.Ticks); - reserved += new TimeSpan(k * node.Slice.Ticks) + minInterval(node.Slice, - end - nextExecution - new TimeSpan(k * node.Period.Ticks)); - } - - return reserved; - } - } - - // GraphNode can provide more than time than necessary between start and deadline; come shorter deadline: - static TimeSpan AvailableCpuAndDeadline(DateTime begin, - DateTime end, - out DateTime shortDeadline, - GraphNode node, - TimeSpan requested) - { - TimeSpan reserved = new TimeSpan(0); - DateTime nextExecution; - DateTime temp; - TimeSpan tempSpan; - int k; - - shortDeadline = end; - if (node == CurrentPlanNode) { - //If the node specified is the current one running? - if (node.NextExec + node.Slice - begin >= requested) { - //If there's enough time between begin and the end of current node's execution - shortDeadline = begin + requested; //case 1 - return requested; - } - else if (node.NextExec.Ticks + node.Slice.Ticks - begin.Ticks >= 0) { - //case 2 - reserved += node.NextExec + node.Slice - begin; // reserved < requested - } - nextExecution = node.NextExec + node.Period; - } - else { - nextExecution = node.NextExec; - } - - if (begin > nextExecution) { - //case 3: note -- precludes case 2 - k = (int) ((begin - nextExecution).Ticks/node.Period.Ticks); // k -> number of complete periods before begin. - reserved -= new TimeSpan(k * node.Slice.Ticks) + minInterval(node.Slice, - begin - nextExecution - new TimeSpan(k * node.Period.Ticks)); // calculates the total time remaining before begin we could have reserved -- subtracts it from reserved - } - - k = (int) ((requested - reserved).Ticks/node.Slice.Ticks); //case 2 above -- number of remaining slices needed - //case 3 above -- number of complete slices in the calculation + number of slices for the request - tempSpan = requested - new TimeSpan(k * node.Slice.Ticks) - reserved; //remainder ms from (requested-reserved)/slice - if (tempSpan.Ticks > 0) { - temp = nextExecution + new TimeSpan(k * node.Period.Ticks) + tempSpan; - } - else { - temp = nextExecution + new TimeSpan(((k>0)?k-1:0) * node.Period.Ticks) + node.Slice; - } - - if (temp <= end) { - shortDeadline = temp; - return requested; - } - - if (end > nextExecution) { - k = (int) ((end - nextExecution).Ticks/node.Period.Ticks); - reserved += new TimeSpan(k * node.Slice.Ticks) + minInterval(node.Slice, - end - nextExecution - new TimeSpan(k * node.Period.Ticks)); - } - return reserved; - } - - // add a new pointer to a reservation to a node and clean the ordered array - static void AddReservationToReservationArray(GraphNode node, - OneShotReservation reservation, - DateTime start, - DateTime end, - TimeSpan available, - DateTime timeNow) - { - int i, j, insertPosition = -1; - int release = 0; - OneShotReservation tempReservation; - - Debug.Assert(node.ReservCount < GraphNode.MaxNumReservations); - Debug.Assert((start < end) && (available.Ticks > 0)); - Debug.Assert(node.ReservCount <= GraphNode.MaxNumReservations); - - for (i = j = 0; i < node.ReservCount; i++) { - tempReservation = node.ReservationArray[i].AssociatedReservation; - if (node.ReservationArray[i].End <= timeNow || !tempReservation.Valid) { - ReleaseReservationProtected(tempReservation); - release++; - } - else { - if ((insertPosition < 0) && (start < node.ReservationArray[i].Start)) { - insertPosition = j; - } - if (j < i) { - node.ReservationArray[j] = (ReservationSlice)node.ReservationArray[i].Clone(); - } - //memcpy(&(node.ReservationArray[j]), &(node.ReservationArray[i]), sizeof(ReservationSlice)); - j++; - } - } - if (insertPosition < 0) { - insertPosition = j; - } - else { - Array.Copy(node.ReservationArray, insertPosition, node.ReservationArray, insertPosition+1, (j-insertPosition)); - } - //memmove(&(node.ReservationArray[insertPosition+1]), &(node.ReservationArray[insertPosition]), - // sizeof(ReservationSlice) * (j - insertPosition)); - - node.ReservationArray[insertPosition] = new ReservationSlice(); - node.ReservationArray[insertPosition].Start = start; - node.ReservationArray[insertPosition].End = end; - node.ReservationArray[insertPosition].Available = available; - node.ReservationArray[insertPosition].AssociatedReservation = reservation; - AddRefReservation(reservation); - Debug.Assert(node.ReservCount == j + release); - node.ReservCount = j + 1; - Debug.Assert(node.ReservCount <= GraphNode.MaxNumReservations); - } - - public static void ReserveSlices(DateTime start, - DateTime deadline, - TimeSpan requestedTime, - RialtoThread task, - out TimeSpan leftUnreserved, - ref TimeSpan timeToInherit, - OneShotReservation reservation, - GraphNode nodeList, - DateTime timeNow) - { - GraphNode node; - OneShotReservation localReservation; - int i; - TimeSpan stillNeeded, toBeInherited, availableCpu, tempSpan; - DateTime tempStart, tempEnd, tempMiddle, newEnd, temp; - - Debug.Assert(Processor.InterruptsDisabled()); - - stillNeeded = requestedTime; - if (timeToInherit.Ticks >= 0) { - toBeInherited = timeToInherit; - } - else { - toBeInherited = new TimeSpan(0); - } - - ArrayList newNodeReservations = new ArrayList(); - - for (node = nodeList; node != null; node = node.SameActivityNext) { - - //NewReservCount = 0; - newNodeReservations.Clear(); - - // check if there is room to place another node reservation for this node - if ((node.ReservCount == GraphNode.MaxNumReservations) && !CleanReservationArray(node, timeNow)) { - continue; // failed to find room for another reservation - } - - tempStart = start; // compute all CPU time available from this node - for (i = 0; (i < node.ReservCount) && (tempStart < deadline); i++) { - // the Begin fields are in increasing order: - localReservation = node.ReservationArray[i].AssociatedReservation; - if (!localReservation.Valid) { - continue; - } - tempEnd = node.ReservationArray[i].End; - if (tempEnd <= tempStart) { - continue; // this node reservation doesn't count - } - - tempMiddle = node.ReservationArray[i].Start; - - // tempEnd > tempStart - // tempStart..min(tempMiddle, deadline) can be allocated, - // max(tempStart, tempMiddle)..min(tempEnd, deadline) may be stolen or inherited - - if ((tempMiddle > tempStart) && - (node.ReservCount + newNodeReservations.Count < GraphNode.MaxNumReservations)) { - // allocate [tempStart , min(tempMiddle, deadline)] or a fraction of it: - temp = minTime(tempMiddle, deadline); - availableCpu = AvailableCpuAndDeadline(tempStart, temp, out newEnd, node, stillNeeded); - Debug.Assert((availableCpu <= stillNeeded) || (newEnd <= temp)); - - if (availableCpu >= stillNeeded) { - // success: - ReservationSlice tempSlice = new ReservationSlice(); - tempSlice.Start = tempStart; - tempSlice.End = newEnd; - tempSlice.Available = availableCpu; - tempSlice.AssociatedReservation = reservation; - newNodeReservations.Add(tempSlice); - //NewNodeReserv[NewReservCount] = new ReservationSlice(); - goto early_exit; - } - else if (availableCpu.Ticks > 0) { - ReservationSlice tempSlice = new ReservationSlice(); - stillNeeded -= availableCpu; - tempSlice.Start = tempStart; - tempSlice.End = temp; - tempSlice.Available = availableCpu; - tempSlice.AssociatedReservation = reservation; - newNodeReservations.Add(tempSlice); - //NewNodeReserv[NewReservCount] = new ReservationSlice(); - } - } - - // max(tempStart, tempMiddle)..min(tempEnd, deadline) may be inherited: - if ((timeToInherit.Ticks != -1) && (localReservation.ReservTask == task)) { - // not yet stolen slice - // we can count on the time provided by this reservation - // as it is somewhere down the current stack of constraints - if (localReservation.ResolutionEpoch != ResolutionAttempt) { - localReservation.InheritedEstimate = new TimeSpan(0); - localReservation.ResolutionEpoch = ResolutionAttempt; - } - tempSpan = localReservation.Estimate - - localReservation.InheritedEstimate; - if (OneShotReservation.CurrentReservation == localReservation) { - tempSpan -= timeNow - SchedulingTime; - } - - if ((tempStart > tempMiddle) || (tempEnd > deadline)) { - // tempStart >= timeNow - availableCpu = minInterval(ComputeAvailableCpu(maxTime(tempStart, tempMiddle), - minTime(tempEnd, deadline), node), tempSpan); - } - else { // use $$-ed value - availableCpu = minInterval(node.ReservationArray[i].Available, tempSpan); - } - - // inherit no more than needed: - availableCpu = minInterval(availableCpu, stillNeeded); - if (availableCpu.Ticks > 0) { - toBeInherited += availableCpu; - stillNeeded -= availableCpu; - localReservation.InheritedEstimate += availableCpu; - Debug.Assert(localReservation.InheritedEstimate - <= localReservation.Estimate); - if (stillNeeded.Ticks == 0) { - goto exit; - } - } - } // Inherit End. - - tempStart = tempEnd; - } // for (i = 0;......) - - for (i = 0; i < newNodeReservations.Count; i++) { - // add collected slice reservations: - AddReservationToReservationArray(node, ((ReservationSlice)newNodeReservations[i]).AssociatedReservation, - ((ReservationSlice)newNodeReservations[i]).Start, - ((ReservationSlice)newNodeReservations[i]).End, - ((ReservationSlice)newNodeReservations[i]).Available, timeNow); - } - - if (node.ReservCount < GraphNode.MaxNumReservations) { - // if there is room for one more reservation - // Compute the last possible CPU slice(s) to be allocated at this node: - availableCpu = AvailableCpuAndDeadline(tempStart, deadline, out newEnd, node, stillNeeded); - if (availableCpu >= stillNeeded) { - AddReservationToReservationArray(node, reservation, tempStart, newEnd, availableCpu, timeNow); - stillNeeded = new TimeSpan(0); - goto exit; - } - - if (availableCpu.Ticks > 0) { - stillNeeded -= availableCpu; - AddReservationToReservationArray(node, reservation, tempStart, deadline, availableCpu, timeNow); - } - } - } // for each node IN THE input list (free or from own activity).... - exit: - leftUnreserved = stillNeeded; - if (timeToInherit.Ticks != -1) { - timeToInherit = toBeInherited; - } - return; - - early_exit: - for (i = 0; i < newNodeReservations.Count; i++) { - // add collected slice reservations: - AddReservationToReservationArray(node, - ((ReservationSlice)newNodeReservations[i]).AssociatedReservation, - ((ReservationSlice)newNodeReservations[i]).Start, - ((ReservationSlice)newNodeReservations[i]).End, - ((ReservationSlice)newNodeReservations[i]).Available, timeNow); - } - goto exit; - } - - public static bool CheckReservation(OneShotReservation reservation, - DateTime timeNow) - { - TimeSpan timeLeft, timeInherited; - GraphNode nodeList; - TimeSpan timeToInherit; - - if (reservation.SurroundingReservation != null) { - if (!reservation.Valid) { - return CheckReservation(reservation.SurroundingReservation, timeNow); - } - if (!CheckReservation(reservation.SurroundingReservation, timeNow)) { - return false; - } - } - else if (!reservation.Valid) { - return true; - } - - timeInherited = new TimeSpan(0); - - if (reservation.AssociatedActivity != null) { - timeToInherit = new TimeSpan(-1); - nodeList = reservation.AssociatedActivity.MyRecurringCpuReservation.TempAssignedNodes; - } - else { - ResolutionAttempt++; - timeToInherit = timeInherited; - nodeList = reservation.OriginalThread.AssociatedActivity.MyRecurringCpuReservation.TempAssignedNodes; - } - - TimeSpan foo = new TimeSpan(0); //TODO: May need a separate flag to signal not interested if 0 isn't good enough - ReserveSlices(reservation.Start, reservation.Deadline, reservation.Estimate, - reservation.ReservTask, // taskId null Activ Reservs - out timeLeft, ref timeToInherit, - reservation, nodeList, timeNow); - - if (timeLeft.Ticks != 0) { - ReserveSlices(reservation.Start, reservation.Deadline, reservation.Estimate, - reservation.ReservTask, // taskId null Activ Reservs - out timeLeft, ref timeToInherit, - reservation, tempFreeNodes, timeNow); - } - - if (timeLeft.Ticks > 0) { - return false; - } - if (reservation.OriginalThread != null && timeInherited.Ticks > 0) { - OneShotReservation.InheritOnEarliestDeadlineFirst(reservation.SurroundingReservation, timeInherited); - } - - return true; - } - - public static void DirectSwitchOnWait() - { - if (SchedulerClock.TimeToInterrupt() < RialtoScheduler.MinSlice) { - return; //Don't do a directed context switch if there isn't much time. - } - if (OneShotReservation.CurrentReservation != null) { - Debug.Assert(OneShotReservation.GuaranteedReservations != null); - directSwitchTo = OneShotReservation.GuaranteedReservations.GetRunnableThread(); - } - // else if ((threadWaiter == GetCurrentThread()) && - // (threadWaiter.AssociatedActivity.RunnableThreads == null)) { - // directSwitchTo = pTH(thread); - // } - } - - public static void DirectSwitchOnWakeup() - { - if (((OneShotReservation.CurrentReservation != null) && - (OneShotReservation.CurrentReservation != OneShotReservation.GuaranteedReservations)) || - EarliestDeadlineFirstBlocked) { - Reschedule(); - } - } - - public static void DirectSwitchOnConstraint() - { - if ((OneShotReservation.CurrentReservation != null) && - (OneShotReservation.GuaranteedReservations != null) && - (OneShotReservation.GuaranteedReservations.ReservTask != (GetCurrentThread()))) { - Reschedule(); - } - } -#endregion - - public static void Reschedule() - { - needToReschedule = true; - } - - public static void ActivityObjAddRef(RialtoActivity activity) - { - Interlocked.Increment(ref activity.ReferenceCount); - } - - - public static void ActivityObjRelease(RialtoActivity activity) - { - Debug.Assert(activity.ReferenceCount >= 1); - activity.ReleaseReference(); - } - - public static void ActivityReleaseProtected(RialtoActivity activity) - { - Debug.Assert(activity.ReferenceCount >= 1); - Debug.Assert(Processor.InterruptsDisabled(), "Interrupts not disabled!"); - // XXX Need to implement HelperActivityRelease() that does release with preemption enabled - // activity.ReleaseReference(); - // DebugStub.Print("Need to implement HelperActivityRelease()\n"); - } - - - ///////////////////////////////////////////////////// Debugging Tools. - // -#if DEBUG_TREE - static void PrintNode(GraphNode pNode, int level, string indent) - { - int k; - if (pNode == null) { - DebugStub.Print("\n"); - return; - } - if (pNode.Type == GraphNode.NodeType.Used) { - DebugStub.Print("{0}L{1} -- Per {2} --- Act {3} Slice {4}\n", - __arglist(indent, level, pNode.Period, - pNode.DefaultActivity.Id, - pNode.Slice)); - } - else if (pNode.Type == GraphNode.NodeType.Free) { - DebugStub.Print("{0}L{1} -- Per {2} --- FREE Slice {3}\n", - __arglist(indent, level, pNode.Period, - pNode.Slice)); - } - else { - // BRANCH - DebugStub.Print("{0}L{1} -- Per {2} --- BRANCH\n", - __arglist(indent, level, pNode.Period)); - string newIndent = indent + " "; - PrintNode (pNode.Left, level+1, newIndent); - PrintNode (pNode.Right, level+1, newIndent); - Debug.Assert(pNode.Next == null); - } - PrintNode(pNode.Next, level, indent); - } - - static void PrintSchedPlan(GraphNode SchedPlan, DateTime StartingAt) - { - int level= 0; - DebugStub.Print("Scheduling plan starting at {0}\n", - __arglist(StartingAt.Ticks)); - PrintNode(SchedPlan, level, ""); - - } -#endif - -#if DEBUG_RESERV - // This code was ported over with PrintNode but hasn't been fully converted to working C# yet - static TimeSpan ComputeTotalAvailableCpu(GraphNode pNode, int ReservIndex, DateTime TimeNow) - { - DateTime TempStart, TempEnd; - TimeSpan AvailableCpu = 0; - PRESERVATION pReserv; - PRESERVATION pResInterest; - int i; - pResInterest = (PRESERVATION) POINTER(pNode.ReservArray[ReservIndex]); - TempStart = maxTime(pResInterest->Start, TimeNow); - for (i = 0; i < ReservIndex; i++) { - // the Begin fields are in increasing order: - pReserv = POINTER(pNode.ReservArray[i]); - - if (!pReserv->Valid) { - continue; - } - - TempEnd = NODE_DEADLINE(pNode.ReservArray[i]); - if (TempEnd <= TempStart) { - continue; // this node reservation doesn't count - } - - // TempEnd > TempStart && NodeReservation->ReservTaskId != TaskId - // TempStart..min(pReserv->Start, Deadline) can be allocated, - // max(TempStart, pReserv->Start)..min(TempEnd, Deadline) may be stolen - AvailableCpu += - ComputeAvailableCpu(TempStart, minTime(min(pReserv->Start, TimeNow), - pResInterest->Deadline), pNode); - TempStart = TempEnd; - } - AvailableCpu += ComputeAvailableCpu(TempStart, - NODE_DEADLINE(pNode.ReservArray[ReservIndex]), pNode); - // assert (AvailableCpu != 0); - return AvailableCpu; - } - - static void PrintReservationNode(GraphNode pNode, int level, string indent, DateTime TimeNow) - { - int i, k; - if (pNode == null) { - DebugStub.Print("\n"); - return; - } - - if (pNode.Type == GraphNode.NodeType.Used || - pNode.Type == GraphNode.NodeType.Free) { - - if (pNode.Type == GraphNode.NodeType.Used) { - DebugStub.Print("{0}L{1} -- Per {2} --- Act {3} Slice {4}\n", - __arglist(indent, level, pNode.Period, - pNode.DefaultActivity.Id, - pNode.Slice)); - } - else { - // FREE - DebugStub.Print("{0}L{1} -- Per {2} --- FREE Slice {3}\n", - __arglist(indent, level, pNode.Period, - pNode.Slice)); - } - - for (i = 0; i < pNode.ReservCount; i++) { - PRESERVATION pRes = POINTER(pNode.ReservArray[i]); - Debug.Assert(NODE_DEADLINE(pNode.ReservArray[i]) != 0); - DebugStub.Print("{0} R{1} - T{2}:A{3} (T{4}:A{5}) {6}-{7}"+ - "{8} {9} Av.{10} Est {11}\n", - __arglist( - indent, - pRes->ReservationId, - (pRes->OriginalThread - ? pRes->OriginalThread.Id : -1), - (pRes->OriginalThread - ? pRes->OriginalThread->pActivity.Id - : pRes->pActivity.Id), - (pRes->ActiveThread - ? pRes->ActiveThread.Id : -1), - (pRes->ActiveThread - ? pRes->ActiveThread->pActivity.Id - : pRes->pActivity.Id), - pRes->Start, - NODE_DEADLINE(pNode.ReservArray[i]), - ((UINT)pNode.ReservArray[i] & 0x02 ? "STL":"NSTL"), - (pRes->Valid ? "Vld":"NVld"), - (pRes->Valid - ? ComputeTotalAvailableCpu(pNode, i, TimeNow.Ticks) - : -1), - pRes->Estimate)); - } - } - else { - // BRANCH - DebugStub.Print("{0}L{1} -- Per {2} --- BRANCH\n", - __arglist(indent, level, pNode.Period)); - string newIndent = indent + " "; - PrintReservationNode (pNode.Left, level+1, newIndent, TimeNow); - PrintReservationNode (pNode.Right, level+1, newIndent, TimeNow); - Debug.Assert(pNode.Next == null); - } - PrintReservationNode(pNode.Next, level, indent, TimeNow); - } - - void PrintReservations(PRESERVATION pReservation, BOOL ok, DateTime TimeNow) - { - int level = 0; - - DebugStub.Print("Reserv {0} (T{1}:A{2}): {3} {4} {5} ({6}) {7} at {8}\n", - __arglist( - pReservation->ReservationId, - (pReservation->OriginalThread != null - ? pReservation->OriginalThread.Id: -1), - (pReservation->OriginalThread != null - ? pReservation->OriginalThread->pActivity.Id - : pReservation->pActivity.Id), - pReservation->Start, - pReservation->Deadline, - pReservation->Estimate, - (pReservation->Criticality == CRITICAL ? "CRT":"NCRT"), - (ok ? "OK":"NO"), - TimeNow.Ticks)); - // DebugStub.Print(" (O {0} + I {0})\n", - // __arglist(pReservation->OwnEstimate, pReservation->InheritedEstimate)); - PrintReservationNode(pSchedPlan, level, "", TimeNow); - } -#endif - } -} diff --git a/base/Kernel/Singularity/Scheduling/Rialto/RialtoThread.cs b/base/Kernel/Singularity/Scheduling/Rialto/RialtoThread.cs deleted file mode 100644 index a6c0b32..0000000 --- a/base/Kernel/Singularity/Scheduling/Rialto/RialtoThread.cs +++ /dev/null @@ -1,554 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: RialtoThread.cs -// -// Note: -// - -using System; -using System.Diagnostics; -using System.Threading; -using Microsoft.Singularity; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling.Rialto -{ - /// - /// The kernel structure for the thread. Contains an associated activity, a - /// stack of reservations executing the activity, a reference for a free - /// reservation (rather than allocating a new one all the time), a pending - /// reservation (used when new constraints are being created), a reference to the - /// thread it’s waiting on (for use in scheduling inheritance), the basic thread - /// queue and sleep queue pointers, lists of people waiting on mutexes and cv’s - /// owned by this thread, bookkeeping for the type of waiting that I’m doing, and - /// general bookkeeping. - /// - public class RialtoThread : ISchedulerThread - { - // Thread State values passed to SetState - // ThreadReady 0 Able to be run but not yet running - // ThreadWaiting 1 Waiting for object and/or timeout - // ThreadRunning 2 Current thread (for a given CPU) - - //public enum ThreadState { ThreadReady = 0, ThreadWaiting = 1, ThreadRunning = 2}; - - static private ListNode sleepingThreads = null; // list of sleeping threads - static private DateTime sleepTimeout = DateTime.MaxValue; // Next thread wakeup time - private TimeSpan executionTime; - - public override TimeSpan ExecutionTime - { - get { return executionTime; } - } - - public RialtoActivity AssociatedActivity; // Thread Activity - //public RialtoThread.ThreadState State; // RUNNING, WAITING, SLEEPING - - public OneShotReservation ReservationStack; - // Stack of Reservations, one per active Constraint - public OneShotReservation FreeReservation; - public OneShotReservation PendingReservation; - // PendingConstraint if any - - // DI :: NOTE the Owner field in Kernel is used only for Inheritance control - // need to REMOVE it -- this will help to locate easy all these functions and - // comment them out - //public int Epoch; // inc-ed at every kernel unlock - - public DateTime StartTime; // only while on the sleep Q - public RialtoThread Next; // Next and previous threads - public RialtoThread Previous; - - // DI -- ??!! this might replace Next and Previous from above - public ListNode Queue; // List of threads in a queue - // DI -- need it as a thread may wait on a condition (i.e. use Queue) - // and wait for the timeout (Condition_TimedWait) - public ListNode SleepQueue; // Another queue link, for sleeping only - - public int ReferenceCount; - public readonly Thread enclosingThread; - - //public interface - public RialtoThread(Thread thread) - { - enclosingThread = thread; - Queue = new ListNode(this); - SleepQueue = new ListNode(this); - AssociatedActivity = null; - StartTime = new DateTime(0); - //TODO: BUGBUGBUG - //Tid = Id; REPLACE WITH DEFAULT TASK? - Queue.Next = Queue.Previous = Queue; - SleepQueue.Next = SleepQueue.Previous = SleepQueue; - //State = RialtoThread.ThreadState.ThreadWaiting; - } - -#region ISchedulerThread Members - - public override Thread EnclosingThread - { - get { return enclosingThread; } - } - - //public interface - public override void SetActivity(ISchedulerActivity iActivityNew) - { - // DebugStub.Print("RialtoThread.SetActivity()\n"); - Debug.Assert(iActivityNew != null); - Debug.Assert(iActivityNew is RialtoActivity); - RialtoActivity activityNew = (RialtoActivity)iActivityNew; - RialtoActivity activityOld = AssociatedActivity; - - - if (activityOld == activityNew) { - return; - } - - bool iflag = Processor.DisableInterrupts(); - - if (activityOld != null) { - //if (State != RialtoThread.ThreadState.ThreadWaiting) - if (enclosingThread.ThreadState == System.Threading.ThreadState.Running) - RialtoScheduler.DequeueRunThread(this); - RialtoScheduler.ActivityReleaseProtected(activityOld); - } - - if (activityNew != null) { - //if (State != RialtoThread.ThreadState.ThreadWaiting) - if (enclosingThread.ThreadState == System.Threading.ThreadState.Running) { - AssociatedActivity = activityNew; - RialtoScheduler.EnqueueRunThread(this); - } - RialtoScheduler.ActivityObjAddRef(activityNew); - } - else if (this == RialtoScheduler.GetCurrentThread()) { - //TODO: Double Check! //State == RialtoThread.ThreadState.ThreadRunning) - if (this == RialtoScheduler.GetCurrentThread() && activityOld != null) { - RialtoScheduler.UpdateSchedulingStatus(); - } - } - AssociatedActivity = activityNew; - // DebugStub.Print("Exiting RialtoThread.SetActivity()\n"); - Processor.RestoreInterrupts(iflag); - } - - //public interface - public override void Start() - { - //TODO: No need for assertion perhaps -- default somehow? - Debug.Assert(AssociatedActivity != null); - bool iflag = Processor.DisableInterrupts(); - IReady(); - Processor.RestoreInterrupts(iflag); - } - - //public interface - public override void Cleanup() - { - OneShotReservation reservation; - // DebugStub.Print("Cleaning RialtoThread\n"); - - SetStateWaiting(DateTime.MaxValue); - // DebugStub.Print("At this point thread is dequeued.\n"); - - while ((reservation = ReservationStack) != null) { - ReservationStack = reservation.Next; // !!!! don't remove Caller's constraints !!! - reservation.DequeueReservation(); - } - - RialtoScheduler.ActivityObjRelease(AssociatedActivity); - - //TODO: In the "real" system, cleanup is called by someone who will pick next thread, right? - } - -#endregion - - public static DateTime GetSleepTimeout() - { - if (sleepingThreads != null) { - return sleepTimeout; - } - else { - return DateTime.MaxValue; - } - } - - public void AddRef() - { - ReferenceCount++; - } - - // DI -- instead of calling this, one should call ::ReleaseActivity - public void Release() - { - ReferenceCount--; - Debug.Assert(ReferenceCount >= 0); - } - - public void ReleaseProtected() - { - ReferenceCount--; - Debug.Assert(ReferenceCount >= 0); - } - - public override void SetStateWaiting(DateTime timeOut) - { - bool iflag = Processor.DisableInterrupts(); - InternalSetStateWaiting(timeOut); - RialtoScheduler.DirectSwitchOnWait(); - Processor.RestoreInterrupts(iflag); - } - - public void InternalSetStateWaiting(DateTime timeOut) - { - Debug.Assert(Processor.InterruptsDisabled()); - // DI -- don't need activity state update - //This to handle sleeps and timed waits. - if (timeOut != DateTime.MaxValue) { - if (timeOut - SystemClock.GetKernelTime() > RialtoScheduler.MinSlice) { - Scheduler.LogSleepAdd(); - StartTime = timeOut; - - PutOnSleepQueue(); - } - else { - //Sleeping for less than MinSlice is treated as not sleeping/yield. - if (PendingReservation != null) { - ResolvePendingReservation(); - } - return; - } - } - - RialtoScheduler.DequeueRunThread(this); // from RunnableThreads - if (this == RialtoScheduler.GetCurrentThread()) { - // if activity blocks during its own regular CPU slice; same code as in Sleep! - if ((AssociatedActivity.RunnableThreads == null) && - (RialtoScheduler.CurrentPlanNode.Type == GraphNode.NodeType.Used) && - (RialtoScheduler.CurrentPlanNode.DefaultActivity == AssociatedActivity) && - ((AssociatedActivity.MyRecurringCpuReservation.SliceLeft = - RialtoScheduler.CurrentPlanNode.NextExec + RialtoScheduler.CurrentPlanNode.Slice - - SystemClock.GetKernelTime()) >= RialtoScheduler.MinSlice)) { - - AssociatedActivity.LastNode = RialtoScheduler.CurrentPlanNode; // record it - } - } - - } - - - // DI -- this is the function in !LAXITY version - //TODO: REPLACE WITH ENCLOSING LOGIC! - //public void SetState(ThreadState newState) - //{ - // State = newState; - // Debug.Assert(newState != ThreadState.ThreadWaiting); - //} - - // DI -- safe - //TODO: REPLACE WITH ENCLOSING LOGIC! - void IReady() - { - Debug.Assert(Processor.InterruptsDisabled()); - // TODO -- maybe something for directed context switch - // DI -- don't need PutThreadOnReadyQueue outside schedule.c - // PutThreadOnReadyQueue(thread); - - RialtoScheduler.EnqueueRunThread(this); - - // When an activity becomes runnable during - // its own regular CPU slice, a reschedule occurs. - if (AssociatedActivity.LastNode != null) - { - RialtoScheduler.EnqueueStandbyActivity(AssociatedActivity); // sets 'thread.AssociatedActivity.LastNode' to null - Debug.Assert(AssociatedActivity.LastNode == null && RialtoScheduler.CurrentPlanNode != null, "Removing unused code! IF EXCEPTION HAPPENS, CODE SHOULD BE REPLACED"); - // if (RialtoScheduler.CurrentPlanNode == thread.AssociatedActivity.LastNode) { //TODO: This is really an equivalence with null? - // RialtoScheduler.Reschedule(); - // } - } - } - - // DI -- this is called only from PutThreadOnSleepQueue - // DI -- merge them ?? - static ListNode InsertThreadOnSleepQueue(RialtoThread thread, ListNode listThreadHead) - { - RialtoThread threadHead, threadTail, tempThread; - - if (listThreadHead == null) { - // Queue empty - return thread.SleepQueue; - } - - threadHead = GetThreadFromListNode(listThreadHead); - threadTail = GetThreadFromListNode(threadHead.SleepQueue.Previous); - - if (thread.StartTime >= threadTail.StartTime) { - thread.SleepQueue.InsertIntoList(threadTail.SleepQueue); - // DI -- if merge: update sleepTimeout here - return threadHead.SleepQueue; - } - - - if (thread.StartTime < threadHead.StartTime) { - thread.SleepQueue.InsertIntoList(threadTail.SleepQueue); - return thread.SleepQueue; - } - - tempThread = GetThreadFromListNode(threadHead.SleepQueue.Next); - for (;;) { - if (tempThread.StartTime > thread.StartTime) { - thread.SleepQueue.InsertIntoList(tempThread.SleepQueue.Previous); - break; - } - tempThread = GetThreadFromListNode(tempThread.SleepQueue.Next); - } - - return threadHead.SleepQueue; - } - - public void PutOnSleepQueue() - { - sleepingThreads = InsertThreadOnSleepQueue(this,sleepingThreads); - sleepTimeout = (GetThreadFromListNode(sleepingThreads)).StartTime; - } - - - // public void Sleep(DateTime time) - // { - // if (time - SystemClock.GetKernelTime() > GraphNode.MinSlice) { - // RialtoScheduler.LogSleepAdd(); - // StartTime = time; - // - // SetStateWaiting(); - // - // // DI -- need it, called from WaitCond also - // PutOnSleepQueue(); - // } - // } - - // Di -- safe -- optimized - void PullThreadOffSleepQueue() - { - ListNode listThread, listNewHead; - - listThread = SleepQueue; - listNewHead = listThread.ListRemove(); - - if (sleepingThreads == listThread) { - if ((sleepingThreads = listNewHead) != null) { - sleepTimeout = (GetThreadFromListNode(sleepingThreads)).StartTime; - } - else { - sleepTimeout = DateTime.MaxValue; - } - } - } - - private void ResolvePendingReservation() - { - Debug.Assert(Processor.InterruptsDisabled()); - Debug.Assert(PendingReservation != null); - bool admitted = OneShotReservation.ResolveConstraint(this); - //else { DebugStub.WriteLine("Wake()-Resolve returned non S_OK"); } - if (ReservationStack.EnclosingTask != null) { - // Not clear if this will work in real system or not. Here for simulator mainly. - ReservationStack.EnclosingTask.UpdateSchedulingState(admitted, ReservationStack.Deadline, ReservationStack.ResourcesGranted); - } - else { - Debug.Assert(false); - } - } - - // DI -- safe - public override void Wake() - { - bool iflag = Processor.DisableInterrupts(); - PullThreadOffSleepQueue(); - IReady(); - - if (PendingReservation != null) { - ResolvePendingReservation(); - } - - RialtoScheduler.DirectSwitchOnWakeup(); - Processor.RestoreInterrupts(iflag); - } - - - OneShotReservation AllocationReservation() - { - OneShotReservation reservation = FreeReservation; - - Debug.Assert(reservation != null); - FreeReservation = null; - reservation.Clear(); - return reservation; - } - - - public void IpcCheckFreeConstraint() - { - if (FreeReservation == null) { - OneShotReservation reservation; - - reservation = RialtoScheduler.IpcAllocateReservation(); - - if (reservation == null) { - reservation = RialtoScheduler.AllocateReservation(); - } - - FreeReservation = reservation; - } - } - - // If this thread doesn't have a free OneShotReservation reserved - // for its use, then try to grab one from the global free list. - // If there aren't any there, then allocate a new one. - void ICheckFreeConstraint() - { - if (FreeReservation == null) { - OneShotReservation reservation; - - reservation = RialtoScheduler.AllocateReservation(); - - FreeReservation = reservation; - } - } - - - // Free the Reservations associated with a thread. - // Used by thread cleanup. - public void IFreeConstraints() - { - OneShotReservation reservation; - - // Debug.Assert(!Processor.InterruptsDisabled()); - - if (FreeReservation != null) { - RialtoScheduler.IpcFreeReservation(FreeReservation); - } - - while ((reservation = ReservationStack) != null) { - ReservationStack = reservation.SurroundingReservation; - RialtoScheduler.ReleaseReservation(reservation); - } - } - - // DI -- safe but optimize to because don't need QueueType - public ListNode Dequeue() - { - return Queue.ListRemove(); - } - - public static ListNode QueueFifo(RialtoThread thread, ListNode threadHead) - { - if (threadHead != null) { - thread.Queue.InsertIntoList(threadHead.Previous); - } - else { - threadHead = thread.Queue; - } - return threadHead; - } - - // DI -- safe - public static RialtoThread GetThreadFromListNode(ListNode listNode) - { - Debug.Assert(listNode == null || listNode.Data is RialtoThread); - if (listNode == null) { - return null; - } - if (listNode.Data is RialtoThread) { - return (RialtoThread)listNode.Data; - } - else { - return null; - } - } - - public static void WakeThreads() - { - RialtoThread ptemp; - Debug.Assert(Processor.InterruptsDisabled()); - while ((ptemp = RialtoThread.GetThreadFromListNode(sleepingThreads)) != null && - ptemp.StartTime < RialtoScheduler.SchedulingTime + RialtoScheduler.LA_Time) { - //TODO: LA_Time here? - sleepingThreads = sleepingThreads.ListRemove(); - - RialtoScheduler.MoveToStandby(ptemp, true); //NOTE: Sets LastNode to null, prevents IReadythread from also EnqueueingStandbyActivity - ptemp.IReady(); - - if (ptemp.PendingReservation != null) { - ptemp.ResolvePendingReservation(); - } - Scheduler.LogWakeThread(ptemp.EnclosingThread); - } - - if (ptemp != null) - sleepTimeout = ptemp.StartTime; - else - sleepTimeout = DateTime.MaxValue; - } - - - /// - /// - /// - /// - /// - /// - /// TODO: UNUSED - /// - public bool BeginConstraintBeforeWaitValidate(bool endPrevious, - ref TimeConstraint timeConstraint, - DateTime timeNow) - { - if (endPrevious && // check to have a valid constraint in the stack. - (ReservationStack == null || ReservationStack.OriginalThread == null)) { - return false; - } - - if (timeConstraint.Estimate.Ticks < 0) { - return false; - } - - // By this time, something should be in FreeReservation - // grab it now such to enable the EndPreviousConstraint to - // save its reservation in the cache - Debug.Assert(FreeReservation != null); - Debug.Assert(FreeReservation != OneShotReservation.CurrentReservation); - - PendingReservation = AllocationReservation(); - - return true; - } - - public void AddExecutionTime(TimeSpan delta) - { - executionTime += delta; - } - - public override ISchedulerTask PrepareDelayedTask(ISchedulerTask taskToEnd, - ref TimeConstraint timeConstraint, - DateTime timeNow) - { - // DI -- the Next calls are from TimedWaitAndBeginConstraint - IpcCheckFreeConstraint(); - - Debug.Assert(taskToEnd == null || taskToEnd == ReservationStack); - bool endPrevious = (taskToEnd != null); - - if (!BeginConstraintBeforeWaitValidate(endPrevious, ref timeConstraint, timeNow)) { - goto Exit; - } - - OneShotReservation.BeginConstraintBeforeWait(this, endPrevious, timeConstraint, timeNow); - Exit: - return PendingReservation; - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Robin/OneShotReservation.cs b/base/Kernel/Singularity/Scheduling/Robin/OneShotReservation.cs deleted file mode 100644 index 4030741..0000000 --- a/base/Kernel/Singularity/Scheduling/Robin/OneShotReservation.cs +++ /dev/null @@ -1,748 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: OneShotReservation.cs -// -// Note: -// - -using System; -using System.Collections; -using System.Diagnostics; -using System.Threading; -using Microsoft.Singularity; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling.Robin -{ - /// - /// OneShotReservations are created from tasks for the calling thread. - /// It has fields to track the thread calling and the thread waiting on, - /// the activity associated when it becomes an activity reservation, its - /// surrounding reservation, its place on the free reservation list, - /// general bookkeeping, bookkeeping for possible stolen time, and - /// references for the general reservation list. - /// - public class OneShotReservation : ISchedulerTask - { - - static OneShotReservation guaranteedReservations = null; - public static OneShotReservation GuaranteedReservations - { - get { return guaranteedReservations; } - } - - static OneShotReservation idleReservations = null; - public static OneShotReservation IdleReservations - { - get { return idleReservations; } - } - - static OneShotReservation currentReservation = null; - public static OneShotReservation CurrentReservation - { - get { return currentReservation; } - //set { currentReservation = value; } - } - - public Hashtable resourcesGranted = new Hashtable(); - public override Hashtable ResourcesGranted - { - get { - resourcesGranted[CpuResource.Provider().ResourceString] = CpuResource.Provider().TimeToCpu(InitialEstimate); - return resourcesGranted; - } - } - - - private Task enclosingTask; - public override Task EnclosingTask - { - get { return enclosingTask; } - set { enclosingTask = value; } - } - - public OneShotReservation SurroundingReservation; - public OneShotReservation FreeListNext; - public RobinThread ReservTask; - - TimeSpan constraintExecution; - public TimeSpan ConstraintExecution - { - get { return constraintExecution; } - } - - TimeSpan initialThreadExecution; - - public DateTime Start; - public DateTime Deadline; - public TimeSpan Estimate; - public TimeSpan RelativeDeadline; - - public TimeSpan InitialEstimate; // test only - public QueueType MyQueueType; - public bool Guaranteed; - public bool Valid; - public bool Satisfied; - - // Reservations can be made for Threads XOR Activities! - public RobinThread OriginalThread; // always the constraint thread -- non-null until the reservation is complete - //public RobinThread ActiveThread; // the above, or the thread blocking it - //public int ActiveThreadEpoch; - public RobinActivity AssociatedActivity; // the two fields above must be null! - - - public OneShotReservation Next; - public OneShotReservation Previous; - - public TimeSpan InheritedEstimate; // Stolen => Inherited, no longer critical - public int ResolutionEpoch; // Stolen => Resolution, since not stealing. - - public int ReferenceCount; - - public OneShotReservation() - { - } - - public void Clear() - { - //ActiveThread = null; - //ActiveThreadEpoch = 0; - constraintExecution = new TimeSpan(0); //Move to enclosing class - initialThreadExecution = new TimeSpan(0); //Move to enclosing class - Deadline = new DateTime(0); - RelativeDeadline = new TimeSpan(0); - Estimate = new TimeSpan(0); - FreeListNext = null; - Guaranteed = false; - InitialEstimate = new TimeSpan(0); - Next = null; - OriginalThread = null; - AssociatedActivity = null; - Previous = null; - MyQueueType = QueueType.NoQueue; - ReferenceCount = 0; - ReservTask = null; - Satisfied = false; - Start = new DateTime(0); - SurroundingReservation = null; - ResolutionEpoch = 0; - InheritedEstimate = new TimeSpan(0); - Valid = false; - if (enclosingTask != null) { - enclosingTask.ClearSchedulerTask(); - enclosingTask = null; - } - - } - - // find the runnable thread of a reservation: - public RobinThread GetRunnableThread() - { -// RobinThread thread; -// - if (AssociatedActivity != null) { - // if an activity reservation - return AssociatedActivity.GetRunnableThread(); - } - - Debug.Assert(OriginalThread != null); - -// if ( (ActiveThread == OriginalThread) && -// (ActiveThread.EnclosingThread.ThreadState == System.Threading.ThreadState.Running)) -// return ActiveThread; -// -// thread = OriginalThread; // search for active thread starting w/ original thread -// -// if ((ActiveThread != null) && -// (ActiveThread.Epoch == ActiveThreadEpoch) ) { -// if (ActiveThread.EnclosingThread.ThreadState == System.Threading.ThreadState.Running) { -//#if DEBUG_MUTEX -// DebugStub.WriteLine("Mutex inheritance (Epoch ok): Orig {0} Active {1} GO ON", -// thread.Id, ActiveThread.Id); -//#endif -// return ActiveThread; -// } -// } - //CKillian -- can't do this because of the array of blockedOn. - // else if (ActiveThread.EnclosingThread.IsSleeping()) { - // // more efficient: search for active thread starting w/ current active thread - // thread = ActiveThread; - // } - // else { - // Debug.Assert(false); - // } - //} - - //A "smarter" thing to do would be to have GetRunnableBeneficiary check for - // the ActiveProcessor case. But as it stands that's info in the scheduler only - // the "good" news is that if we return null based on the active processor case - // we at least know someone is current running on a processor who would inherit - // time from us. - Thread temp = /*Active*/OriginalThread.EnclosingThread.GetRunnableBeneficiary(); - if (temp == null || ((RobinThread)temp.SchedulerThread).ActiveProcessor != null) { - return null; - } - else { - return (RobinThread)temp.SchedulerThread; - } - - } - - public void StopThreadExecution(RobinThread thread) - { - Debug.Assert(thread.ExecutionTime >= initialThreadExecution); - constraintExecution += thread.ExecutionTime - initialThreadExecution; - //Between two reservations which are atomically stopped/started, some - // time is charged against the default parent resource container. This - // is because I changed the way we account for tasks to do the general - // rule we wanted. In particular, I update the scheduling status just - // before resolving a new reservation. Not sure what the right fix is, - // so I'm leaving it. The assertion below will reveal the "error." The - // old code to track CPU usage is still active under constraintExecution, - // but isn't presently exposed to the applications. Currently UpdateSchedulingStatus() - // updates the CPU resources used for the whole task stack. - //Debug.Assert(CpuResource.Provider().TimeToCpu(constraintExecution).Cycles == ((CpuResourceAmount)enclosingTask.resourcesUsed[CpuResource.Provider().ResourceString]).Cycles); - //EnclosingTask.AddResourceAmountUsed(CpuResource.Provider().ResourceString, CpuResource.Provider().TimeToCpu(constraintExecution)); - } - - public void StartThreadExecution(RobinThread thread) - { - initialThreadExecution = thread.ExecutionTime; - } - - public void StartThreadExecution(RobinThread thread, TimeSpan delta) - { - initialThreadExecution = thread.ExecutionTime + delta; - } - - public static void BeginConstraintBeforeWait(RobinThread thread, - bool endPrevious, - TimeConstraint timeConstraint, - DateTime timeNow) - { - OneShotReservation reservation; - - Debug.Assert(thread == RobinScheduler.GetCurrentThread()); - Debug.Assert(!Processor.InterruptsDisabled()); - if (endPrevious) { - bool iflag = Processor.DisableInterrupts(); - EndPreviousConstraint(thread, timeNow); - Processor.RestoreInterrupts(iflag); - } - - RobinScheduler.UpdateSchedulingStatus(); - - reservation = thread.PendingReservation; - - reservation.StartThreadExecution(thread, (timeNow - RobinScheduler.SchedulingTime)); - reservation.constraintExecution = new TimeSpan(0); - reservation.Start = timeConstraint.Start; - reservation.Deadline = timeConstraint.Deadline; - reservation.RelativeDeadline = timeConstraint.RelativeDeadline; - reservation.Estimate = timeConstraint.Estimate; - reservation.Valid = reservation.Guaranteed = true; - reservation.MyQueueType = QueueType.NoQueue; - reservation.ReservTask = thread; - reservation.OriginalThread = /*reservation.ActiveThread =*/ thread; - thread.AddRef(); - //thread.AddRef(); - //reservation.ActiveThreadEpoch = thread.Epoch; - reservation.AssociatedActivity = null; - reservation.Next = reservation.Previous = null; - - } - - - // DI -- this is a new function used in both Begin and EndConstraint - public static bool EndPreviousConstraint(RobinThread thread, DateTime timeNow) - { - OneShotReservation reservation; - OneShotReservation reservationNext; - bool success; - QueueType tempQueue; //Value assigned but never used. - - Debug.Assert(Processor.InterruptsDisabled()); - - reservation = thread.ReservationStack; - - success = (timeNow <= reservation.Deadline); - RobinScheduler.UpdateSchedulingStatus(); - reservation.StopThreadExecution(thread); - - Debug.Assert(reservation.ConstraintExecution.Ticks >= 0); -// if (timeTaken.Ticks != 0) { -// timeTaken = reservation.ConstraintExecution; -// Debug.Assert( timeTaken.Ticks >= 0); -// } - - thread.ReservationStack = reservation.SurroundingReservation; - reservation.SurroundingReservation = null; - - if (currentReservation == reservation) { - // note that it might be the case that - // a reservation for the task id exist on the guaranteed Q but the - // the actual (executed) constraint is in the UnfinishedQ - currentReservation = null; // ???????????? - RobinScheduler.Reschedule(); - } - Debug.Assert(reservation.OriginalThread == thread); - Debug.Assert(reservation.Start <= timeNow); - - if (thread.ReservationStack == null) { - if (reservation.Estimate >= RobinScheduler.MinSlice) { - // the constraint used less than estimated: - Debug.Assert(reservation.MyQueueType == QueueType.GuaranteedQueue); -#if false - Debug.Assert((reservation.ActiveThread == null) || - (reservation.ActiveThread.Epoch != reservation.ActiveThreadEpoch) || - (reservation.ActiveThread == reservation.OriginalThread)); - reservation.ActiveThread.ReleaseProtected(); -#endif - reservation.OriginalThread.ReleaseProtected(); - reservation.AssociatedActivity = reservation.OriginalThread.AssociatedActivity; // and make it an activity reserv - reservation.OriginalThread = null; - //reservation.ActiveThread = null; - reservation.ReservTask = null; - RobinScheduler.ActivityObjAddRef(reservation.AssociatedActivity); - // leave it on whatever Q it happens to be on - } - else { - reservation.DequeueReservation(); // from whatever Q it happens to be on - } - } - else { - tempQueue = reservation.MyQueueType; - reservationNext = thread.ReservationStack; - reservationNext.Estimate += reservation.Estimate; - reservationNext.constraintExecution += reservation.constraintExecution; - reservationNext.StartThreadExecution(thread); -// Debug.Assert( ((reservation.MyQueueType == QueueType.GuaranteedQueue) && -// (reservationNext.MyQueueType == QueueType.NoQueue)) || -// ((reservation.MyQueueType == QueueType.UnfinishedQueue) && -// ((reservationNext.Estimate.Ticks <= 0) || (reservationNext.MyQueueType != QueueType.UnfinishedQueue )))); - reservation.Estimate = new TimeSpan(0); - Debug.Assert(reservation.Next != null); - reservation.DequeueReservation(); - ReplaceOnEarliestDeadlineFirst(reservationNext, timeNow); - - } - // fprintf(stdout,"EndPrevConstr 0x%x %d\n", reservation, reservation.ReferenceCount); - reservation.ReleaseReservationProtected(); - - Scheduler.LogEndConstraint(); - - if (success == false) { - Scheduler.LogSchedulerLate(); - } - return success; - } - - public static bool ResolveConstraint(RobinThread thread) - { - TimeSpan timeLeft, ownNodesTimeToSteal = new TimeSpan(1); - DateTime start, deadline; - TimeSpan timeInherited; - OneShotReservation pendingReservation = thread.PendingReservation; - OneShotReservation reservationPrevious; - DateTime timeNow = SystemClock.GetKernelTime(); - bool ok = false; - - Debug.Assert(Processor.InterruptsDisabled()); - Debug.Assert(pendingReservation != null); - - Scheduler.LogResolveConstraint(); - - thread.PendingReservation = null; // just clean the place - - Debug.Assert(pendingReservation.Start.Ticks >= 0); - start = pendingReservation.Start; - if (start < timeNow) { - start = timeNow; - } - Debug.Assert(pendingReservation.Deadline.Ticks >= 0); - deadline = pendingReservation.Deadline; - if (deadline.Ticks == 0) { - deadline = timeNow + pendingReservation.RelativeDeadline; - } - - if (thread.ReservationStack != null) { - OneShotReservation reservation = thread.ReservationStack; - while (!reservation.Valid && ((reservation = reservation.SurroundingReservation)!= null)) { - // nothing. - } - if (reservation!= null) { - deadline = RobinScheduler.minTime(deadline, reservation.Deadline); - } - } - - // update data ure - pendingReservation.Start = start; - pendingReservation.Deadline = deadline; - //TODO: Should be for SIM ONLY! - Scheduler.LogReservationId(pendingReservation); // SIM only - timeInherited = new TimeSpan(0); - timeLeft = new TimeSpan(0); - ok = true; - pendingReservation.InitialEstimate = pendingReservation.Estimate; - reservationPrevious = thread.ReservationStack; - pendingReservation.SurroundingReservation= thread.ReservationStack; - thread.ReservationStack = pendingReservation; - RobinScheduler.AddRefReservation(pendingReservation); - if (reservationPrevious != null) { - reservationPrevious.constraintExecution - += pendingReservation.initialThreadExecution - - reservationPrevious.initialThreadExecution; - } - // - // Reschedule() called if currently we are executing on a pendingReservation - // and the EarliestDeadlineFirst top changed - // or the current pendingReservation goes into the idle list - // solve in SetStateWaiting - // - pendingReservation.EnqueueReservation(timeNow); - - return ok; - } - - void EnqueueReservation(DateTime timeNow) - { - OneShotReservation temp; - - Debug.Assert((AssociatedActivity != null) || - (OriginalThread != null)); - - // Infeasible or (Estimate == 0) Reservations: - if (Estimate <= RobinScheduler.AFewSlice) { - RobinActivity activity; - Debug.Assert(OriginalThread != null); - Debug.Assert(AssociatedActivity == null); - activity = OriginalThread.AssociatedActivity; - MyQueueType = QueueType.UnfinishedQueue; - if (activity.UnfinishedConstraints == null) { - Next = Previous = this; - activity.UnfinishedConstraints = this; - } - else { - // no order enforced - Next = activity.UnfinishedConstraints; - Previous = activity.UnfinishedConstraints.Previous; - activity.UnfinishedConstraints.Previous.Next = this; - activity.UnfinishedConstraints.Previous = this; - activity.UnfinishedConstraints = this; // optional - } - // DebugStub.WriteLine("Add: EnqueueReserve 1 0x{0:x} {1}", this, ReferenceCount); - RobinScheduler.AddRefReservation(this); - return; - } - // Debug.Assert(Valid); - - // Idle Reservations: - if (Start > timeNow + RobinScheduler.AFewSlice) { - // take thread off activity RoundRobin queue and simulate a sleep - // i.e. don't set a timer interrupt - - OriginalThread.InternalSetStateWaiting(DateTime.MaxValue); - -#if DEBUG_START_RESERV - DebugStub.WriteLine("Put reservation {0}:{1} in IdleQueue\n", OriginalThread, - ReservationId); -#endif - Debug.Assert(OriginalThread != null); - MyQueueType = QueueType.IdleQueue; - - if (idleReservations == null) { - Next = Previous = this; - idleReservations = this; - } - else { - temp = idleReservations; - if (Start < temp.Start) { - idleReservations = this; - } - else { - do { - temp = temp.Next; - } while (temp != idleReservations && - Start >= temp.Start); // >= is compulsory to keep it 'FIFO' - } // insert before 'temp' - Next = temp; - Previous = temp.Previous; - temp.Previous.Next = this; - temp.Previous = this; - } - // fprintf(stdout,"Add: EnqueueReserve 2 0x%x %d\n", this, ReferenceCount); - RobinScheduler.AddRefReservation(this); - return; - } - - // Guaranteed OneShotReservation: - MyQueueType = QueueType.GuaranteedQueue; - if (GuaranteedReservations == null) { - Next = Previous = this; - guaranteedReservations = this; - } - else { - temp = GuaranteedReservations; - if (Deadline < GuaranteedReservations.Deadline) { - guaranteedReservations = this; - } - else { - do { - temp = temp.Next; - } while (temp != guaranteedReservations && - Deadline > temp.Deadline); // > is compulsory to keep it 'FIFO' - } // insert before 'temp' - Next = temp; - Previous = temp.Previous; - temp.Previous.Next = this; - temp.Previous = this; - } - RobinScheduler.AddRefReservation(this); -#if DEBUG_RESERV - PrintQueueReserv(guaranteedReservations, timeNow); //TODO: PrintQueueReserv -#endif - return; - } - - - static void ReplaceOnEarliestDeadlineFirst(OneShotReservation reservation, - DateTime timeNow) - { - while (reservation != null) { - if (reservation.Estimate.Ticks > 0) { - if (reservation.MyQueueType == QueueType.NoQueue) { - reservation.EnqueueReservation(timeNow); - } - else { - Debug.Assert(reservation.MyQueueType == QueueType.GuaranteedQueue); - } - break; - } - if (reservation.MyQueueType == QueueType.NoQueue) { - reservation.EnqueueReservation(timeNow); - } - else { - Debug.Assert(reservation.MyQueueType == QueueType.UnfinishedQueue); - } - reservation = reservation.SurroundingReservation; - } - } - - public static bool SatisfyAcceptedConstraint(DateTime timeNow) - { - OneShotReservation reservation; - - if (currentReservation != null) { - currentReservation.Estimate -= timeNow - RobinScheduler.SchedulingTime; - if (currentReservation.Estimate.Ticks <= 0) { - RobinThread tempThread = currentReservation.OriginalThread; - currentReservation.DequeueReservation(); - if (tempThread != null) { - // activity reservations vanish here - //Debug.Assert(currentReservation.ActiveThread == RobinScheduler.GetCurrentThread()); // only thread reserv are added to - currentReservation.EnqueueReservation(RobinScheduler.SchedulingTime); // the UnfinishedConstraints list of the OriginalThread's Activ - OneShotReservation.ReplaceOnEarliestDeadlineFirst(currentReservation.SurroundingReservation, RobinScheduler.SchedulingTime); - } - } - } - - reservation = guaranteedReservations; - while (reservation != null) { - reservation = reservation.Next; - if (reservation == guaranteedReservations) { - break; - } - } - reservation = idleReservations; - while (reservation != null) { - reservation = reservation.Next; - if (reservation == idleReservations) { - break; - } - } - - return true; - } - - public static void UpdateCurrentReservation() - { - if ((currentReservation != null) && - (currentReservation.Estimate <= RobinScheduler.AFewSlice) ) { - // slice used for a reservation (CK: & estimate now empty ) - RobinThread tempThread = currentReservation.OriginalThread; - currentReservation.DequeueReservation(); - if (tempThread != null) { - // activity reservations vanish here - currentReservation.EnqueueReservation(RobinScheduler.SchedulingTime); // the UnfinishedConstraints list of the OriginalThread's Activ - ReplaceOnEarliestDeadlineFirst(currentReservation.SurroundingReservation, RobinScheduler.SchedulingTime); - } - } - - } - - public static void FreshenReservationQueues() - { - OneShotReservation tempReservation; - - // Move 'idle' reservations to the 'active' reservations list, if necessary. - // A reservation is 'idle' until its Start time is reached. - // The 'active' reservation list is ordered 'Earliest Deadline Firts'. - // The 'idle' queue is ordered Earliest Start First. - while ((tempReservation = IdleReservations) != null) { - if (RobinScheduler.SchedulingTime + RobinScheduler.AFewSlice < tempReservation.Start) { - break; - } - tempReservation.DequeueReservation(); // Remove OneShotReservation from the Idle Queue - tempReservation.EnqueueReservation(RobinScheduler.SchedulingTime); // Earliest Deadline First in the (Non)Guaranteed Queue - RobinScheduler.EnqueueRunThread(tempReservation.OriginalThread, true); -#if DEBUG_START_RESERV - DebugStub.WriteLine("Get reservation {0}:{1} from IdleQueue", tempReservation.OriginalThread, - tempReservation.ReservationId); -#endif - - } - - // Remove the active reservations from the guaranteed and nonguaranteed queues - // if their Deadline is <= CurrentTime. - while ((tempReservation = guaranteedReservations) != null) { - if (RobinScheduler.SchedulingTime + RobinScheduler.AFewSlice < tempReservation.Deadline) { - break; - } - - // fprintf(stdout,"Add: Reschedule Guaranteed Deadline 0x%x %d\n", tempReservation, tempReservation.ReferenceCount); - RobinScheduler.AddRefReservation(tempReservation); - tempReservation.DequeueReservation(); // Remove OneShotReservation from the Guaranteed Queue - if ((tempReservation.OriginalThread != null) && // if a thread reservation - (tempReservation.OriginalThread.AssociatedActivity != null)) { - // If a thread reservation. - tempReservation.Estimate = new TimeSpan(0); - tempReservation.EnqueueReservation(RobinScheduler.SchedulingTime); // add it to the Pending End Constraint List - } - // fprintf(stdout,"Reschedule Guaranteed Deadline off 0x%x %d\n", tempReservation, tempReservation.ReferenceCount); - tempReservation.ReleaseReservationProtected(); - } - - } - - int ReleaseReservationProtected() - { - return RobinScheduler.ReleaseReservationProtected(this); - } - - public void DequeueReservation() //TODO: Should this be a resource container function? - { - Debug.Assert(Processor.InterruptsDisabled()); - OneShotReservation next; - - Debug.Assert((AssociatedActivity != null) || - (OriginalThread != null)); - - if (Next != this) { - //If there's another reservation, remove us from the queue - Next.Previous = Previous; - Previous.Next = Next; - next = Next; - Debug.Assert(next.Deadline.Ticks > 0); - } - else { - //Otherwise we are only reservation. - Debug.Assert(Previous == this); - next = null; - } - - if (this == GuaranteedReservations) { - //Take care when we're the head of the list. - guaranteedReservations = next; -#if DEBUG_RESERV - DebugStub.WriteLine("Dequeue {0}: {1}", ReservationId, Estimate); - // Debug.Assert(Criticality != CRITICAL || !Valid || Estimate ==0); - PrintQueueReserv(GuaranteedReservations, SystemClock.GetKernelTime()); -#endif - } - else if (this == IdleReservations) { - //Or maybe we're the head of this list (but definitely not both). - idleReservations = next; - } - else if ((OriginalThread != null) && - (this == OriginalThread.AssociatedActivity.UnfinishedConstraints) ) { - //Or else we might be the head of the unfinished constraints list for the activity associated with our original thread. - OriginalThread.AssociatedActivity.UnfinishedConstraints = next; - } // else { we're OK! } - - Next = Previous = null; - // DebugStub.WriteLine("DequeueReservation 0x{0:x} {1}", this, ReferenceCount); - MyQueueType = QueueType.NoQueue; - ReleaseReservationProtected(); - } - - public static void FindRunnableReservation(ref RobinThread currentThread) - { - currentReservation = guaranteedReservations; - while (currentReservation != null) { - if ((currentThread = currentReservation.GetRunnableThread()) != null) { - break; - } - currentReservation = currentReservation.Next; - if (currentReservation == guaranteedReservations) { - break; - } - } - } - - static void InheritAndReplaceOnEarliestDeadlineFirst(OneShotReservation reservation, - TimeSpan timeInherited, - DateTime timeNow) - { - OneShotReservation current = null; - - while (reservation != null) { - if (reservation.Estimate.Ticks > 0) { - if (current == null) { - Debug.Assert(reservation.MyQueueType != QueueType.NoQueue); - current = reservation; - } - else { - Debug.Assert(reservation.MyQueueType == QueueType.NoQueue); - } - } - else if (reservation.MyQueueType != QueueType.NoQueue) { - Debug.Assert (current != null || reservation.MyQueueType == QueueType.UnfinishedQueue); - reservation.DequeueReservation(); - } - Debug.Assert(timeInherited >= reservation.InheritedEstimate); - timeInherited -= reservation.InheritedEstimate; - Debug.Assert(reservation.Estimate >= reservation.InheritedEstimate); - reservation.Estimate -= reservation.InheritedEstimate; - reservation = reservation.SurroundingReservation; - } - if (current != null) { - if (current == CurrentReservation) { - current.Estimate -= timeNow - RobinScheduler.SchedulingTime; - currentReservation = null; - RobinScheduler.Reschedule(); - } - current.DequeueReservation(); - } - } - - public static void InheritOnEarliestDeadlineFirst(OneShotReservation reservation, - TimeSpan timeToInherit) - { - while (reservation != null && timeToInherit.Ticks > 0) { - Debug.Assert(timeToInherit >= reservation.InheritedEstimate); - reservation.Estimate -= reservation.InheritedEstimate; - timeToInherit -= reservation.InheritedEstimate; - reservation = reservation.SurroundingReservation; - } - } - - public static void ClearCurrentReservation() - { - currentReservation = null; - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Robin/RecurringReservation.cs b/base/Kernel/Singularity/Scheduling/Robin/RecurringReservation.cs deleted file mode 100644 index 1e05a06..0000000 --- a/base/Kernel/Singularity/Scheduling/Robin/RecurringReservation.cs +++ /dev/null @@ -1,51 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: RecurringReservation.cs -// -// Note: -// - -using System; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling.Robin -{ - /// - /// Summary description for RecurringReservation. - /// - public class RecurringReservation : ISchedulerCpuReservation - { - CpuResourceReservation enclosingCpuReservation; - - public RecurringReservation() - { - } - -#region ISchedulerCpuReservation Members - - public override CpuResourceReservation EnclosingCpuReservation - { - get { return enclosingCpuReservation; } - set { enclosingCpuReservation = value; } - } - -#endregion - - public TimeSpan Slice; - public TimeSpan Period; - - public override CpuResourceAmount ReservedAmount - { - get { return CpuResource.Provider().TimeToCpu(Slice); } - } - - public override TimeSpan ReservedPeriod - { - get { return Period; } - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Robin/RobinActivity.cs b/base/Kernel/Singularity/Scheduling/Robin/RobinActivity.cs deleted file mode 100644 index 234b017..0000000 --- a/base/Kernel/Singularity/Scheduling/Robin/RobinActivity.cs +++ /dev/null @@ -1,124 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: RobinActivity.cs -// -// Note: -// - -using System; -using System.Diagnostics; -using System.Threading; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling.Robin -{ - /// - /// The resource container class includes a recurring reservation - /// (k out of every n), a list of runnable threads, unfinished constraints - /// (i.e. non-guaranteed or non-feasible), as well as pointers to handle - /// its role in lists for the non-blocked activity list. - // It also has references for the last node to execute it, - /// a node assigned to it (which is a list via SameActivityNext), and a node - /// assigned to it under the new scheduling plan. It also has some bookkeeping. - /// - public class RobinActivity : ISchedulerActivity - { - private Activity enclosingActivity; - - public RecurringReservation MyRecurringCpuReservation; - public RobinThread RunnableThreads;// list of runnable threads, if any - public OneShotReservation UnfinishedConstraints; - public RobinActivity Next; // Next and previous activities - public RobinActivity Previous; - public int ReferenceCount; - public int CountNodes; - - public RobinActivity() - { - //Below is InitializeActivity(); - ReferenceCount = 1; - RunnableThreads = null; - -// MyRecurringCpuReservation = new RecurringReservation(); -// MyRecurringCpuReservation.EnclosingCpuReservation = new CpuResourceReservation(MyRecurringCpuReservation); - - bool iflag = Processor.DisableInterrupts(); - RobinScheduler.EnqueueActivity(this); // add in last position in RoundRobin! - Processor.RestoreInterrupts(iflag); - } - - public void ReleaseActivity() - { - bool iflag = Processor.DisableInterrupts(); - RobinScheduler.DequeueActivity(this); // update RoundRobin queue, if required. - Processor.RestoreInterrupts(iflag); - Scheduler.LogDeleteActivity(); - } - - //public-normal API - //TODO: Call this from finalize? - public override void ReleaseReference() - { - // DI replaced by AtomicDec(&activity->ReferenceCount); - Debug.Assert(ReferenceCount > 0); - int newrefcnt = Interlocked.Decrement(ref ReferenceCount); - if (newrefcnt == CountNodes) { - if (CountNodes > 0) { - return; - } - bool iflag = Processor.DisableInterrupts(); - RobinScheduler.DequeueActivity(this); // update RoundRobin queue, if required. - Processor.RestoreInterrupts(iflag); - } - Scheduler.LogDeleteActivity(); - } - - // find the runnable thread of an activity: - public RobinThread GetRunnableThread() - { - OneShotReservation reservation = UnfinishedConstraints; - Debug.Assert(reservation == null || reservation.AssociatedActivity == null); // avoid stack overflow : GetRunnableThread & GetRunnableThread - while (reservation != null) { - // search in the circular list - RobinThread thread = reservation.GetRunnableThread(); - if (thread != null) { - return thread; - } - reservation = reservation.Next; - if (reservation == UnfinishedConstraints) { - break; - } - } - RobinThread runThread = RunnableThreads; - while (runThread != null && runThread.ActiveProcessor != null && runThread != RunnableThreads.Previous) { - runThread = runThread.Next; - } - if (runThread == null || runThread.ActiveProcessor != null) { - return null; - } - else { - return runThread; - } - } - -#region ISchedulerActivity Members - - public override Activity EnclosingActivity - { - get { return enclosingActivity; } - set { enclosingActivity = value; } - } - - public CpuResourceReservation CpuResourceReservation - { - get { return MyRecurringCpuReservation.EnclosingCpuReservation; } - } - -#endregion - } - -} diff --git a/base/Kernel/Singularity/Scheduling/Robin/RobinProcessor.cs b/base/Kernel/Singularity/Scheduling/Robin/RobinProcessor.cs deleted file mode 100644 index 29a1686..0000000 --- a/base/Kernel/Singularity/Scheduling/Robin/RobinProcessor.cs +++ /dev/null @@ -1,46 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: RobinProcessor.cs -// -// Note: -// - -using System; -using System.Diagnostics; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling.Robin -{ - /// - /// - public class RobinProcessor : ISchedulerProcessor - { - private readonly Processor enclosingProcessor; - public override Processor EnclosingProcessor - { - get { return enclosingProcessor; } - } - - internal RobinActivity CurrentActivity; - internal TimeSpan SliceLeft; - internal RobinThread RunningThread; - internal DateTime SchedulingTime; - internal bool NeedToReschedule; - internal bool Idle; - - public void Reschedule() - { - NeedToReschedule = true; - } - - public RobinProcessor(Processor processor) - { - enclosingProcessor = processor; - SchedulingTime = SchedulerClock.BootTime; - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Robin/RobinScheduler.cs b/base/Kernel/Singularity/Scheduling/Robin/RobinScheduler.cs deleted file mode 100644 index d2bc813..0000000 --- a/base/Kernel/Singularity/Scheduling/Robin/RobinScheduler.cs +++ /dev/null @@ -1,836 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: RobinScheduler.cs -// -// Note: -// - -using System; -using System.Collections; -using System.Diagnostics; -using System.Threading; -using Microsoft.Singularity; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling -{ -#if !SIMULATOR - public class SystemScheduler - { - static public void RegisterScheduler() - { - Robin.RobinScheduler.RegisterScheduler(); - } - } -#endif -} - -namespace Microsoft.Singularity.Scheduling.Robin -{ - /// - /// Summary description for RobinScheduler. - /// - public class RobinScheduler : ICpuScheduler - { - public static readonly TimeSpan ContextSwitch = new TimeSpan(200); - // minimum time slice to be worth scheduling the CPU: 1 millisec - public static readonly TimeSpan MinSlice = new TimeSpan(10 * ContextSwitch.Ticks); - public static readonly TimeSpan AFewSlice = new TimeSpan(3 * MinSlice.Ticks); - public static readonly TimeSpan RobinSlice = new TimeSpan(10 * TimeSpan.TicksPerMillisecond); - - - static public void RegisterScheduler() - { - CpuResource.RegisterSystemScheduler(new RobinScheduler()); - } - - public override ISchedulerProcessor CreateSchedulerProcessor(Processor processor) - { - return new RobinProcessor(processor); - } - - public override ISchedulerThread CreateSchedulerThread(Thread thread) - { - return new RobinThread(thread); - } - - public override ISchedulerActivity CreateSchedulerActivity() - { - return new RobinActivity(); - } - - public override ISchedulerCpuReservation ReserveCpu(ISchedulerActivity activity, - CpuResourceAmount amount, - TimeSpan period) - { - return null; - } - - public bool ReserveRecurringCpu(Activity activity, - ref TimeSpan amount, - ref TimeSpan period) - { - return true; - } - - public override bool ShouldReschedule() - { - RobinThread currentThread = GetCurrentThread(); - if (Scheduler.TimerInterruptedFlag && currentThread != null) { - //SchedulerClock.CheckInterrupt(); - } - - if (currentThread == null || - Scheduler.YieldFlag || - currentThread.EnclosingThread.IsWaiting() || - currentThread.EnclosingThread.IsStopped()) { - - Reschedule(); - } - return NeedToReschedule || Scheduler.TimerInterruptedFlag; - // || timer should have fired but didn't? - } - - // DI -- this is somehow similar to EnableInterrupts - public override bool NextThread(out Thread nextThread) - { - Debug.Assert(!Processor.InterruptsDisabled()); - bool iflag = Processor.DisableInterrupts(); - bool halted = false; - RobinThread currentThread = GetCurrentThread(); - - SchedulerClock.CheckInterrupt(); - if (ShouldReschedule()) { - //Debug.Print("Calling RescheduleInterrupt()\n"); - halted = RescheduleInterrupt(); //TODO: RescheduleInterrupt returns true if the processor needs to be halted. - currentThread = GetCurrentThread(); - } - else { - //Debug.Print("No call to RescheduleInterrupt()\n"); - } - if (currentThread != null) { - nextThread = currentThread.EnclosingThread; - } - else { - Debug.Assert(halted); - nextThread = null; - } - - ((RobinProcessor)Processor.CurrentProcessor.SchedulerProcessor).NeedToReschedule = false; - // printf("-------------------------currentThread-Id %d\n", currentThread.Id); - //return currentThread; - Processor.RestoreInterrupts(iflag); - return halted; - } - //public static void Yield(); - -#region Constants - - // Maximum Scheduling Period is 1 sec (i.e. in 100s of nanoseconds). - static readonly TimeSpan AmountCpu = CpuResource.MaxPeriod; - //TODO: TICKS vs. TIME_SPAN vs DATE_TIME - -#endregion - -#region Static data members - - static RobinActivity CircularActivityList = null; - /// - /// This must ALWAYS (when lock not held) point to a current resource - /// container in the queue. - /// - static internal RobinActivity NextActivity; - - - //moved to be processor specific - //static RobinActivity RobinActivityNext = null; - //static RobinActivity RobinActivityCurrent = null; // activity currently executing from - - static OneShotReservation ReservationFreePool; - static TimeSpan IdleTime = new TimeSpan(0); - static bool Idle - { - get { return GetCurrentProcessor().Idle; } - } - - //moved to be processor specific - //static RobinThread CurrentThreadGlobal = null; - //static TimeSpan RobinSliceLeft = RobinScheduler.RobinSlice; //new TimeSpan(0); // the round robin list, if any, and its CPU slice - //public static DateTime SchedulingTime = new DateTime(0); - //static bool NeedToReschedule = false; - - public static TimeSpan LA_Time = RobinScheduler.MinSlice; - - public static DateTime SchedulingTime - { - get { - return ((RobinProcessor)Processor.CurrentProcessor.SchedulerProcessor).SchedulingTime; - } - } - - public static bool NeedToReschedule - { - get { - return ((RobinProcessor)Processor.CurrentProcessor.SchedulerProcessor).NeedToReschedule; - } - } - -#endregion - - public RobinScheduler() - { - } - - public override void Initialize() - { - RobinScheduler.InitializeScheduler(); - } - - public static void InitializeScheduler() - { //TODO: Should all the null's be added in here? - for (int i=0; i 0); - - int newrefcnt = Interlocked.Decrement(ref reservation.ReferenceCount); - - if (newrefcnt == 0) { - // Estimate may be positive when reservation reaches - // its Deadline w/o using its Estimate - Debug.Assert((reservation.Next == null) && (reservation.Previous == null)); - if (reservation.OriginalThread != null) { - reservation.OriginalThread.ReleaseProtected(); - //Debug.Assert(reservation.ActiveThread != null); - //reservation.ActiveThread.ReleaseProtected(); - } - else { - Debug.Assert(reservation.AssociatedActivity != null); - ActivityReleaseProtected(reservation.AssociatedActivity); - } - Debug.Assert(reservation.Next == null); - CacheFreeReservation(reservation); - } - return newrefcnt; - } - - - public static OneShotReservation AllocateReservation() - { - OneShotReservation reservation; - - // DisableInterrupts(); - reservation = IpcAllocateReservation(); - // EnableInterrupts(); - - if (reservation == null) { - reservation = new OneShotReservation(); //(PRESERVATION)malloc(sizeof(struct OneShotReservation)); // to be done while on the I-stack! - reservation.Clear(); - } - return reservation; - } - - static void FreeReservation(OneShotReservation reservation) - { - reservation = null; //free(reservation); - } - - // Garbage-collect unused Reservations. - public static int ReleaseReservation(OneShotReservation reservation) - { - // Debug.Assert(!Processor.InterruptsDisabled()); - Debug.Assert(reservation.ReferenceCount > 0); - int newrefcnt = reservation.ReferenceCount - 1; - reservation.ReferenceCount = newrefcnt; - - if (newrefcnt == 0) { - // Estimate may be positive when reservation reaches its Deadline w/o using its Estimate - Debug.Assert((reservation.Next == null) && (reservation.Previous == null)); - if (reservation.OriginalThread != null) { - reservation.OriginalThread.Release(); - //Debug.Assert(reservation.ActiveThread != null); - //reservation.ActiveThread.Release(); - } - else if (reservation.AssociatedActivity != null) { - ActivityObjRelease(reservation.AssociatedActivity); - } - CacheFreeReservation(reservation); - } - return newrefcnt; - } - - // Like CheckFreeConstraint, but callable from the IPC path. - // Because we can't call AllocateReservation, we use the helper thread - // if there aren't any free Reservations. - static void CacheFreeReservation(OneShotReservation reservation) - { - if (reservation.OriginalThread != null && - reservation.OriginalThread.FreeReservation == null) { - reservation.OriginalThread.FreeReservation = reservation; - } - else { - IpcFreeReservation(reservation); - } - } - - - // Callable with preemption disabled. Allocates a OneShotReservation - // from the global free list. - public static OneShotReservation IpcAllocateReservation() - { - OneShotReservation reservation; - - if ((reservation = ReservationFreePool) != null) { - ReservationFreePool = reservation.FreeListNext; - } - - return reservation; - } - - // Callable with preemption disabled. Frees a OneShotReservation - // to the global free list. - public static void IpcFreeReservation(OneShotReservation reservation) - { - Debug.Assert(reservation != null); - Debug.Assert(reservation.ReferenceCount == 0); - - reservation.FreeListNext = ReservationFreePool; - ReservationFreePool = reservation; - } - - ////////////////////////////////////////////////////////////////////// - - public static void AddRefReservation(OneShotReservation reservation) - { - Debug.Assert(reservation.ReferenceCount >= 0); - reservation.ReferenceCount++; - } - -#endregion - -#region Potpourri (Unregioned functions) - - public static RobinThread GetCurrentThread() - { - return ((RobinProcessor)Processor.CurrentProcessor.SchedulerProcessor).RunningThread; - } - - public static void IChangeCurrentThread(RobinThread thread) - { - ((RobinProcessor)Processor.CurrentProcessor.SchedulerProcessor).RunningThread = thread; - } - - // Auxiliary Routines - // TODO: Revisit the necessity of these functions. - public static TimeSpan minInterval(TimeSpan TimeInterv0, TimeSpan TimeInterv1) - { - return (TimeInterv0 < TimeInterv1? TimeInterv0: TimeInterv1); - } - - public static DateTime minTime(DateTime Time0, DateTime Time1) - { - return (Time0 < Time1? Time0: Time1); - } - - static TimeSpan maxInterval(TimeSpan TimeInterv0, TimeSpan TimeInterv1) - { - return (TimeInterv0 < TimeInterv1? TimeInterv1: TimeInterv0); - } - - static DateTime maxTime(DateTime Time0, DateTime Time1) - { - return (Time0 < Time1? Time1: Time0); - } - - internal static RobinProcessor GetCurrentProcessor() - { - return (RobinProcessor)Processor.CurrentProcessor.SchedulerProcessor; - } - - // Add Activity in the last Round-Robin position - public static void EnqueueActivity(RobinActivity activity) - { - Debug.Assert(Processor.InterruptsDisabled()); - if (CircularActivityList == null) { - NextActivity = CircularActivityList /*= GetCurrentProcessor().CurrentActivity */= - activity.Next = activity.Previous = activity; - //Here modifying other processor specific data rather than global. -// for (int i=0; i NewTimerTimeout) { - return false; - } - - Debug.Assert(Processor.InterruptsDisabled()); - // Debug.Assert(CurrentThread().GetState() == Thread.ThreadState.ThreadRunning); - - Debug.Assert(NewTimerTimeout > SchedulingTime); - // Debug.Assert(SleepTimeout > SchedulingTime); - - //TODO: Need LA_Time? - // if (NewTimerTimeout > SleepTimeout - LA_Time) - // NewTimerTimeout = SleepTimeout - LA_Time; - if (NewTimerTimeout > RobinThread.GetSleepTimeout()) { - NewTimerTimeout = RobinThread.GetSleepTimeout(); - } - - //DebugStub.Print("Setting Next Interrupt for: {0}...\n", - //__arglist(NewTimerTimeout.Ticks); - bool success = SchedulerClock.SetNextInterrupt(NewTimerTimeout); //TODO: Perhaps only call this if the time changed. - - //DebugStub.Print(success ? "SUCCESS\n" : "FAILED\n"); - - return success; - } - - public static void UpdateSchedulingStatus() - { - TimeSpan timeRun; - RobinThread currentThread = GetCurrentThread(); - - DateTime newSchedulingTime = SystemClock.GetKernelTime(); - - timeRun = newSchedulingTime - SchedulingTime; - RobinProcessor processor = (RobinProcessor)Processor.CurrentProcessor.SchedulerProcessor; - processor.SchedulingTime = newSchedulingTime; - - if (Idle) { - // UpdateIdleTime - IdleTime += timeRun; - return; - } - - // if IDLE compute CurrentPlanNode & set currentThread to null. - if (currentThread != null) { - // unless thread just exited. - // Update Thread & Activity execution times. - currentThread.AddExecutionTime(timeRun); - Scheduler.CurrentTask().AddResourceAmountUsed(CpuResource.Provider().ResourceString, CpuResource.Provider().TimeToCpu(timeRun)); -// if (currentThread.AssociatedActivity != null) { -// currentThread.AssociatedActivity.MyRecurringCpuReservation.EnclosingCpuReservation.AddTimeUsed(timeRun); -// } - } - - if (OneShotReservation.CurrentReservation != null) { - // Slice used for a reservation. - OneShotReservation.CurrentReservation.Estimate -= timeRun; - } - else if (processor.CurrentActivity != null) { - // Slice used for RoundRobin. - processor.SliceLeft -= timeRun; - processor.CurrentActivity = null; - } - } - - internal static void CheckInvariants() - { - if (CircularActivityList != null) { - bool listNextFound = (CircularActivityList == NextActivity); - RobinActivity start = CircularActivityList, current = CircularActivityList.Next, previous = CircularActivityList; - while (current != start) { - //perhaps check thread invariants here too. - Debug.Assert(current != null, "resource container list is not circular"); - Debug.Assert(current.Previous == previous, "back pointer doesn't match forward pointer"); - if (current == NextActivity) { - listNextFound = true; - } - previous = current; - current = current.Next; - } - Debug.Assert(listNextFound, "NextActivity isn't in the loop!"); - Debug.Assert(current.Previous == previous, "Head doesn't point to tail"); - } - } - - // Always called when another thread needs to be scheduled. - static bool RescheduleInterrupt() - { - CheckInvariants(); - Debug.Assert(Processor.InterruptsDisabled()); - Debug.Assert(!Processor.InterruptsDisabled()); - RobinThread currentThread = GetCurrentThread(); - Debug.Assert(currentThread == null || currentThread.ActiveProcessor == GetCurrentProcessor()); - if (currentThread != null) { - currentThread.ActiveProcessor = null; - } - - DateTime nextStart; - RobinThread previousThread; - - RescheduleAgain: // !!!!!!!!!! SIM ONLY !!!!!!!!! - if (Idle) { - currentThread = null; - } - - previousThread = currentThread; - - UpdateSchedulingStatus(); - - // if thread was the head of the runnable threads Q: - // Advance the runnable threads Q. - if ((currentThread != null) && - (currentThread.AssociatedActivity != null) && - (currentThread.AssociatedActivity.RunnableThreads == currentThread)) { - - currentThread.AssociatedActivity.RunnableThreads = currentThread.Next; - Debug.Assert(currentThread.AssociatedActivity.RunnableThreads != null); - } - - OneShotReservation.UpdateCurrentReservation(); - OneShotReservation.ClearCurrentReservation(); - currentThread = null; - - // Finished first stage, i.e. updated state. - // Start second stage: wakeup threads & select Next CPU slice. - - RobinThread.WakeThreads(); - - // NOTE: In the original RoundRobin Simulator Code (& MMOSA code) - // The call to DrainDeferredConditions() was made here. - // In Singularity, this will basically be replaced with a - // queue of wait-events to fix. - - OneShotReservation.FreshenReservationQueues(); - - TimeSpan currentNodeSliceLeft; - RobinProcessor currentProcessor = (RobinProcessor)Processor.CurrentProcessor.SchedulerProcessor; - if (currentProcessor.CurrentActivity != null && currentProcessor.SliceLeft >= RobinScheduler.MinSlice) { - currentThread = currentProcessor.CurrentActivity.GetRunnableThread(); - if (currentThread != null) { - goto exit; - } - } - - // Find Next Runnable Activity. - currentProcessor.SliceLeft = RobinScheduler.RobinSlice; - currentProcessor.CurrentActivity = NextActivity; // !!!!!!!!!!!!!SIM ONLY !!!!!!!!!! - while ((currentThread = NextActivity.GetRunnableThread()) == null) { - NextActivity = NextActivity.Next; - //currentProcessor.SliceLeft = RobinScheduler.RobinSlice; - if (NextActivity == currentProcessor.CurrentActivity) { - // !!!!!!!!SIM ONLY !!!!!!! - // in the real scheduler, execute halt - if (OneShotReservation.IdleReservations != null) { - // reuse nextStart - nextStart = minTime(RobinThread.GetSleepTimeout(), OneShotReservation.IdleReservations.Start); - } - else { - nextStart = RobinThread.GetSleepTimeout(); - DebugStub.Print("Idle, sleeping until {0} cf maxvalue {1}\n", - __arglist(nextStart.Ticks, - DateTime.MaxValue.Ticks)); - } - - if (nextStart == DateTime.MaxValue) { - Scheduler.StopSystem(); - } - - if (! ResetTimerTimeout(nextStart)) { - //Error setting timer. Try scheduling again. - DebugStub.Print("Thought Idle, failed to set interrupt.\n"); - goto RescheduleAgain; - } - GetCurrentProcessor().Idle = true; - - // !!!!!!!!SIM ONLY !!!!!!! - currentThread = null; // !!!!!!!!SIM ONLY !!!!!!! - OneShotReservation.ClearCurrentReservation(); - currentProcessor.CurrentActivity = null; - - if (DateTime.MaxValue != nextStart) { - //Scheduler.LogTimeJump(); - } - - //DebugStub.Print("Halted.\n"); - IChangeCurrentThread(null); - return true; // !!!!!!!!SIM ONLY !!!!!!! - } - // !!!!!!!!SIM ONLY !!!!!!! - } - //DebugStub.Print("Running Round Robin Resource Container\n"); - currentProcessor.CurrentActivity = NextActivity; // we probably need only one of the two variables - NextActivity = NextActivity.Next; - - exit: - currentNodeSliceLeft = currentProcessor.SliceLeft; - - Debug.Assert(currentThread != null); - if (currentThread != previousThread) { - Scheduler.LogContextSwitch(); // Context Switch statistics - } - - if (OneShotReservation.IdleReservations != null) { - // reuse nextStart - nextStart = minTime(RobinThread.GetSleepTimeout(), OneShotReservation.IdleReservations.Start); - } - else { - nextStart = RobinThread.GetSleepTimeout(); - } - - if (SchedulingTime + currentNodeSliceLeft /* CurrentSlice */ > nextStart) { - currentNodeSliceLeft /* CurrentSlice */ = nextStart - currentProcessor.SchedulingTime; - } - - - Scheduler.LogReschedule(); - if (!ResetTimerTimeout(currentProcessor.SchedulingTime + currentNodeSliceLeft) || Scheduler.TimerInterruptedFlag) { - //TODO: What do we REALLY want here? - currentThread = null; // !!!!!!!!SIM ONLY !!!!!!! - OneShotReservation.ClearCurrentReservation(); - currentProcessor.CurrentActivity = null; - goto RescheduleAgain; - } - - Debug.Assert(currentThread.ActiveProcessor == null); - currentThread.ActiveProcessor = GetCurrentProcessor(); - Debug.Assert(currentThread.ActiveProcessor != null); - - if (currentThread != previousThread) { - IChangeCurrentThread(currentThread); - } - GetCurrentProcessor().Idle = false; - - // Not necessarily true: Debug.Assert(!Scheduler.TimerInterruptedFlag); - CheckInvariants(); - return false; - } - -#endregion - - public static void Reschedule() - { - ((RobinProcessor)Processor.CurrentProcessor.SchedulerProcessor).Reschedule(); - } - - public static void ActivityObjAddRef(RobinActivity activity) - { - activity.ReferenceCount++; - } - - - public static void ActivityObjRelease(RobinActivity activity) - { - Debug.Assert(activity.ReferenceCount >= 1); - activity.ReleaseReference(); - } - - public static void ActivityReleaseProtected(RobinActivity activity) - { - Debug.Assert(activity.ReferenceCount >= 1); - Debug.Assert(Processor.InterruptsDisabled(), "Interrupts Not Disabled!"); - activity.ReleaseReference(); - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Robin/RobinThread.cs b/base/Kernel/Singularity/Scheduling/Robin/RobinThread.cs deleted file mode 100644 index 1d4fd56..0000000 --- a/base/Kernel/Singularity/Scheduling/Robin/RobinThread.cs +++ /dev/null @@ -1,522 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: RobinThread.cs -// -// Note: -// - -using System; -using System.Diagnostics; -using System.Threading; -using Microsoft.Singularity; -using Microsoft.Singularity.Scheduling; - -namespace Microsoft.Singularity.Scheduling.Robin -{ - /// - /// The kernel structure for the thread. Contains an associated activity, a - /// stack of reservations executing the activity, a reference for a free - /// reservation (rather than allocating a new one all the time), a pending - /// reservation (used when new constraints are being created), a reference to the - /// thread it’s waiting on (for use in scheduling inheritance), the basic thread - /// queue and sleep queue pointers, lists of people waiting on mutexes and cv’s - /// owned by this thread, bookkeeping for the type of waiting that I’m doing, and - /// general bookkeeping. - /// - public class RobinThread : ISchedulerThread - { - static ListNode SleepingThreads = null; // list of sleeping threads - static DateTime SleepTimeout = DateTime.MaxValue; // Next thread wakeup time - - TimeSpan executionTime; - public override TimeSpan ExecutionTime - { - get{ return executionTime; } - } - - public RobinActivity AssociatedActivity; // Thread Activity - //public RobinThread.ThreadState State; // RUNNING, WAITING, SLEEPING - - public OneShotReservation ReservationStack;// Stack of Reservations, one per active Constraint - public OneShotReservation FreeReservation; - public OneShotReservation PendingReservation; // PendingConstraint if any - // DI -- corresponds to MMID Tid - //TODO: Replace with TASK public int Tid; // Id to mark node reservations made for this Constraint Stack - // DI :: NOTE the Owner field in Kernel is used only for Inheritance control - // need to REMOVE it -- this will help to locate easy all these functions and - // comment them out - //public int Epoch; // inc-ed at every kernel unlock - - public DateTime StartTime; // only while on the sleep Q - - public RobinThread Next; // Next and previous threads - public RobinThread Previous; - - // DI -- maybe don't use it, see vtlb.c - // UINT QueueType; // Type of queue thread is on, if any - - // DI -- ??!! this might replace Next and Previous from above - public ListNode Queue; // List of threads in a queue - // DI -- need it as a thread may wait on a condition (i.e. use Queue) - // and wait for the timeout (Condition_TimedWait) - public ListNode SleepQueue; // Another queue link, for sleeping only - - public int ReferenceCount; - public readonly Thread enclosingThread; - - public RobinProcessor ActiveProcessor; //Set only when running on processor -- no other processor may take the thread to run. - - //public interface - public RobinThread(Thread thread) - { - enclosingThread = thread; - Queue = new ListNode(this); - SleepQueue = new ListNode(this); - AssociatedActivity = null; - StartTime = new DateTime(0); - //TODO: BUGBUGBUG - //Tid = Id; REPLACE WITH DEFAULT TASK? - Queue.Next = Queue.Previous = Queue; - SleepQueue.Next = SleepQueue.Previous = SleepQueue; - //State = RobinThread.ThreadState.ThreadWaiting; - } - -#region ISchedulerThread Members - - public override Thread EnclosingThread - { - get { return enclosingThread; } - } - - //public interface - public override void SetActivity(ISchedulerActivity iActivityNew) - { - DebugStub.Print("RobinThread.SetActivity()\n"); - Debug.Assert(iActivityNew != null); - Debug.Assert(iActivityNew is RobinActivity); - Debug.Assert(((RobinActivity)iActivityNew).ReferenceCount > 0); - RobinActivity activityNew = (RobinActivity)iActivityNew; - RobinActivity activityOld = AssociatedActivity; - - - if (activityOld == activityNew) { - return; - } - - bool iflag = Processor.DisableInterrupts(); - - if (activityOld != null) { - //if (State != RobinThread.ThreadState.ThreadWaiting) - if (enclosingThread.ThreadState == System.Threading.ThreadState.Running) { - RobinScheduler.DequeueRunThread(this); - } - RobinScheduler.ActivityReleaseProtected(activityOld); - } - - if (activityNew != null) { - //if (State != RobinThread.ThreadState.ThreadWaiting) - if (enclosingThread.ThreadState == System.Threading.ThreadState.Running) { - AssociatedActivity = activityNew; - RobinScheduler.EnqueueRunThread(this, false); - } - RobinScheduler.ActivityObjAddRef(activityNew); - } - else if (this == RobinScheduler.GetCurrentThread()) { - //TODO: Double Check! //State == RobinThread.ThreadState.ThreadRunning) - if (this == RobinScheduler.GetCurrentThread() && activityOld != null) { - RobinScheduler.UpdateSchedulingStatus(); - } - } - AssociatedActivity = activityNew; - DebugStub.Print("Exiting RobinThread.SetActivity()\n"); - Processor.RestoreInterrupts(iflag); - } - - //public interface - public override void Start() - { - //TODO: No need for assertion perhaps -- default somehow? - Debug.Assert(AssociatedActivity != null); - bool iflag = Processor.DisableInterrupts(); - IReady(); - Processor.RestoreInterrupts(iflag); - } - - //public interface - public override void Cleanup() - { - OneShotReservation reservation; - DebugStub.Print("Cleaning RobinThread\n"); - - SetStateWaiting(DateTime.MaxValue); - DebugStub.Print("At this point thread is dequeued.\n"); - - while ((reservation = ReservationStack) != null) { - ReservationStack = reservation.Next; // !!!! don't remove Caller's constraints !!! - reservation.DequeueReservation(); - } - - RobinScheduler.ActivityObjRelease(AssociatedActivity); - - //TODO: In the "real" system, cleanup is called by someone who will pick next thread, right? - } - -#endregion - - public static DateTime GetSleepTimeout() - { - if (SleepingThreads != null) { - return SleepTimeout; - } - else { - return DateTime.MaxValue; - } - } - - public void AddRef() - { - ReferenceCount++; - } - - // DI -- instead of calling this, one should call ::ReleaseActivity - public void Release() - { - ReferenceCount--; - Debug.Assert(ReferenceCount >= 0); - } - - public void ReleaseProtected() - { - ReferenceCount--; - Debug.Assert(ReferenceCount >= 0); - } - - public override void SetStateWaiting(DateTime timeOut) - { - bool iflag = Processor.DisableInterrupts(); - InternalSetStateWaiting(timeOut); - Processor.RestoreInterrupts(iflag); - } - - public void InternalSetStateWaiting(DateTime timeOut) - { - Debug.Assert(Processor.InterruptsDisabled()); - // DI -- don't need activity state update - //This to handle sleeps and timed waits. - if (timeOut != DateTime.MaxValue) { - if (timeOut - SystemClock.GetKernelTime() > RobinScheduler.MinSlice) { - Scheduler.LogSleepAdd(); - StartTime = timeOut; - - PutOnSleepQueue(); - } - else { - //Sleeping for less than MinSlice is treated as not sleeping/yield. - if (PendingReservation != null) { - ResolvePendingReservation(); - } - return; - } - } - - RobinScheduler.DequeueRunThread(this); // from RunnableThreads - } - - - // DI -- this is the function in !LAXITY version - //TODO: REPLACE WITH ENCLOSING LOGIC! - //public void SetState(ThreadState newState) - //{ - // State = newState; - // Debug.Assert(newState != ThreadState.ThreadWaiting); - //} - - // DI -- safe - //TODO: REPLACE WITH ENCLOSING LOGIC! - void IReady() - { - Debug.Assert(Processor.InterruptsDisabled()); - // TODO -- maybe something for directed context switch - // DI -- don't need PutThreadOnReadyQueue outside schedule.c - // PutThreadOnReadyQueue(thread); - - RobinScheduler.EnqueueRunThread(this, true); - } - // DI -- this is called only from PutThreadOnSleepQueue - // DI -- merge them ?? - static ListNode InsertThreadOnSleepQueue(RobinThread thread, ListNode listThreadHead) - { - RobinThread threadHead, threadTail, tempThread; - - if (listThreadHead == null) { - // Queue empty - return thread.SleepQueue; - } - - threadHead = GetThreadFromListNode(listThreadHead); - threadTail = GetThreadFromListNode(threadHead.SleepQueue.Previous); - - if (thread.StartTime >= threadTail.StartTime) { - thread.SleepQueue.InsertIntoList(threadTail.SleepQueue); - // DI -- if merge: update SleepTimeout here - return threadHead.SleepQueue; - } - - - if (thread.StartTime < threadHead.StartTime) { - thread.SleepQueue.InsertIntoList(threadTail.SleepQueue); - return thread.SleepQueue; - } - - - for (tempThread = GetThreadFromListNode(threadHead.SleepQueue.Next);; - tempThread = GetThreadFromListNode(tempThread.SleepQueue.Next)) { - - if (tempThread.StartTime > thread.StartTime) { - thread.SleepQueue.InsertIntoList(tempThread.SleepQueue.Previous); - break; - } - } - - return threadHead.SleepQueue; - } - - public void PutOnSleepQueue() - { - SleepingThreads = InsertThreadOnSleepQueue(this,SleepingThreads); - SleepTimeout = (GetThreadFromListNode(SleepingThreads)).StartTime; - } - - -// public void Sleep(DateTime time) -// { -// if (time - SystemClock.GetKernelTime() > RobinScheduler.MinSlice) { -// -// RobinScheduler.LogSleepAdd(); -// StartTime = time; -// -// SetStateWaiting(); -// -// // DI -- need it, called from WaitCond also -// PutOnSleepQueue(); -// } -// } - - // Di -- safe -- optimized - void PullThreadOffSleepQueue() - { - ListNode listThread, listNewHead; - - listThread = SleepQueue; - listNewHead = listThread.ListRemove(); - - if (SleepingThreads == listThread) { - if ((SleepingThreads = listNewHead) != null) { - SleepTimeout =(GetThreadFromListNode(SleepingThreads)).StartTime; - } - else { - SleepTimeout = DateTime.MaxValue; - } - } - } - - private void ResolvePendingReservation() - { - Debug.Assert(Processor.InterruptsDisabled()); - Debug.Assert(PendingReservation != null); - bool admitted = OneShotReservation.ResolveConstraint(this); - if (ReservationStack.EnclosingTask != null) { - // Not clear if this will work in real system or not. Here for simulator mainly. - ReservationStack.EnclosingTask.UpdateSchedulingState(admitted, ReservationStack.Deadline, ReservationStack.ResourcesGranted); - } - } - - // DI -- safe - public override void Wake() - { - bool iflag = Processor.DisableInterrupts(); - PullThreadOffSleepQueue(); - IReady(); - - if (PendingReservation != null) { - ResolvePendingReservation(); - } - - Processor.RestoreInterrupts(iflag); - } - - - OneShotReservation AllocationReservation() - { - OneShotReservation reservation = FreeReservation; - - Debug.Assert(reservation != null); - FreeReservation = null; - reservation.Clear(); - return reservation; - } - - - public void IpcCheckFreeConstraint() - { - if (FreeReservation == null) { - OneShotReservation reservation; - reservation = RobinScheduler.IpcAllocateReservation(); - - if (reservation == null) { - reservation = RobinScheduler.AllocateReservation(); - } - - FreeReservation = reservation; - } - } - - // If this thread doesn't have a free OneShotReservation reserved - // for its use, then try to grab one from the global free list. - // If there aren't any there, then allocate a new one. - void ICheckFreeConstraint() - { - if (FreeReservation == null) { - OneShotReservation reservation; - reservation = RobinScheduler.AllocateReservation(); - FreeReservation = reservation; - } - } - - - // Free the Reservations associated with a thread. - // Used by thread cleanup. - public void IFreeConstraints() - { - OneShotReservation reservation; - - // Debug.Assert(!Processor.InterruptsDisabled()); - - if (FreeReservation != null) { - RobinScheduler.IpcFreeReservation(FreeReservation); - } - - while ((reservation = ReservationStack) != null) { - ReservationStack = reservation.SurroundingReservation; - RobinScheduler.ReleaseReservation(reservation); - } - } - - // DI -- safe but optimize to because don't need QueueType - public ListNode Dequeue() - { - return Queue.ListRemove(); - } - - public static ListNode QueueFifo(RobinThread thread, ListNode threadHead) - { - if (threadHead != null) { - thread.Queue.InsertIntoList(threadHead.Previous); - } - else { - threadHead = thread.Queue; - } - return threadHead; - } - - // DI -- safe - public static RobinThread GetThreadFromListNode(ListNode listNode) - { - Debug.Assert(listNode == null || listNode.Data is RobinThread); - if (listNode == null) { - return null; - } - if (listNode.Data is RobinThread) { - return (RobinThread)listNode.Data; - } - else { - return null; - } - } - - public static void WakeThreads() - { - RobinThread ptemp; - Debug.Assert(Processor.InterruptsDisabled()); - while ((ptemp = RobinThread.GetThreadFromListNode(SleepingThreads)) != null && - ptemp.StartTime < RobinScheduler.SchedulingTime + RobinScheduler.LA_Time) { - //TODO: LA_Time here? - - SleepingThreads = SleepingThreads.ListRemove(); - - ptemp.IReady(); - - if (ptemp.PendingReservation != null) { - ptemp.ResolvePendingReservation(); - } - Scheduler.LogWakeThread(ptemp.EnclosingThread); - } - - if (ptemp != null) { - SleepTimeout = ptemp.StartTime; - } - else { - SleepTimeout = DateTime.MaxValue; - } - } - - - /// - /// - /// - /// - /// - /// TODO: UNUSED - /// - public bool BeginConstraintBeforeWaitValidate(bool endPrevious, - ref TimeConstraint timeConstraint, - DateTime timeNow) - { - if (endPrevious && - (ReservationStack == null || - ReservationStack.OriginalThread == null)) { - // check to have a valid constraint in the stack - return false; - } - - if (timeConstraint.Estimate.Ticks < 0) { - return false; - } - - // By this time, something should be in FreeReservation - // grab it now such to enable the EndPreviousConstraint to - // save its reservation in the cache - Debug.Assert(FreeReservation != null); - Debug.Assert(FreeReservation != OneShotReservation.CurrentReservation); - - PendingReservation = AllocationReservation(); - return true; - } - - public void AddExecutionTime(TimeSpan delta) - { - executionTime += delta; - } - - public override ISchedulerTask PrepareDelayedTask(ISchedulerTask taskToEnd, ref TimeConstraint timeConstraint, DateTime timeNow) - { - // DI -- the Next calls are from TimedWaitAndBeginConstraint - IpcCheckFreeConstraint(); - - Debug.Assert(taskToEnd == null || taskToEnd == ReservationStack); - bool endPrevious = (taskToEnd != null); - - if (!BeginConstraintBeforeWaitValidate(endPrevious, ref timeConstraint, timeNow)) { - goto Exit; - } - - OneShotReservation.BeginConstraintBeforeWait(this, endPrevious, timeConstraint, timeNow); - Exit: - return PendingReservation; - } - } -} diff --git a/base/Kernel/Singularity/Scheduling/Scheduler.cs b/base/Kernel/Singularity/Scheduling/Scheduler.cs index 82cc6eb..91bfed2 100644 --- a/base/Kernel/Singularity/Scheduling/Scheduler.cs +++ b/base/Kernel/Singularity/Scheduling/Scheduler.cs @@ -9,12 +9,7 @@ // Note: // -// #define DEBUG_SCHEDULER -#if DEBUG -#define SHOW_TWIDDLE -#else -//#define SHOW_TWIDDLE -#endif +//#define DEBUG_SCHEDULER using System; using System.Diagnostics; @@ -28,270 +23,214 @@ namespace Microsoft.Singularity.Scheduling { [NoCCtor] [CLSCompliant(false)] - public class Scheduler + [AccessedByRuntime("referenced from halidt.asm")] + public abstract class Scheduler { - // Global + /// + /// + /// Constructor - is used to generate scheduler Ids + /// + /// + + + protected Scheduler() + { + } + + public abstract void Initialize(); + + [CLSCompliant(false)] + public abstract void Finalize(); + + [CLSCompliant(false)] + public abstract void OnDispatcherInitialize(int dispatcherId); + + [CLSCompliant(false)] + public abstract void OnThreadStateInitialize(Thread thread, bool constructorCalled); + + [CLSCompliant(false)] + public abstract void OnThreadStart(Thread thread); + + [CLSCompliant(false)] + public abstract void OnThreadBlocked(Thread thread, SchedulerTime stop); + + [CLSCompliant(false)] + [NoHeapAllocation] + public abstract void OnThreadUnblocked(Thread thread); + + [CLSCompliant(false)] + [NoHeapAllocation] + public abstract void OnThreadYield(Thread thread); + + [CLSCompliant(false)] + public abstract void OnThreadStop(Thread thread); + + [CLSCompliant(false)] + [NoHeapAllocation] + public abstract void OnThreadFreezeIncrement(Thread thread); + + [CLSCompliant(false)] + [NoHeapAllocation] + public abstract void OnThreadFreezeDecrement(Thread thread); + + [CLSCompliant(false)] + [NoHeapAllocation] + public abstract TimeSpan OnTimerInterrupt(int affinity, SchedulerTime now); + + [CLSCompliant(false)] + [NoHeapAllocation] + public static bool IsIdleThread (int threadIdx) + { + return ProcessorDispatcher.IsIdleThread(threadIdx); + } + + [CLSCompliant(false)] + [NoHeapAllocation] + public abstract void AddRunnableThread(Thread thread); + + /// + /// + /// Run scheduling policy to decide the next thread to run + /// + /// + /// Set the returned running thread to this affinity. + /// the thread currently running + /// thread state to change to for the current thread + /// Current system time + /// + [CLSCompliant(false)] + [NoHeapAllocation] + public abstract Thread RunPolicy( + int affinity, + Thread currentThread, + ThreadState schedulerAction, + SchedulerTime currentTime); + + [CLSCompliant(false)] + [NoHeapAllocation] + public abstract void Suspend(Thread thread); + + [CLSCompliant(false)] + [NoHeapAllocation] + public abstract void Resume(Thread thread); + + /// + /// + /// Scheduler hints for enquing threads + /// + /// + [Flags] + public enum Hint + { + Head = 0, + Tail = 1, + } + + /// + /// + /// Retrieve scheduler lock - used by dispatcher to protect scheduler state + /// + /// + [CLSCompliant(false)] + [NoHeapAllocation] + internal abstract SchedulerLock RetrieveSchedulerLock(int affinity); + } + + /// + /// + /// Scheduler lock class is used to wrap a spinlock structure so that it can be shared with a + /// dispatcher. + /// + /// + [NoCCtor] + [CLSCompliant(false)] + [AccessedByRuntime("referenced from halidt.asm")] + internal class SchedulerLock + { + /// + /// + /// Constructor + /// + /// + public SchedulerLock() + { + // Initialize spinlock + this.spinlock = new SpinLock(SpinLock.Types.Scheduler); + } + + + /// + /// + /// A method is used to acquire scheduler lock + /// + /// + [NoHeapAllocation] + public void Acquire(Thread currentThread) + { + this.spinlock.Acquire(currentThread); + } + + /// + /// + /// A method is used to release scheduler lock + /// + /// + [NoHeapAllocation] + public void Release(Thread currentThread) + { + this.spinlock.Release(currentThread); + } + + /// + /// + /// A method is used to acquire scheduler lock + /// + /// + [NoHeapAllocation] + public void Acquire() + { + this.spinlock.Acquire(); + } + + /// + /// + /// Try to acquire the spin lock. Always return immediately. + /// + /// true if the spin lock is acquired. + /// + [NoHeapAllocation] + public bool TryAcquire() + { + return this.spinlock.TryAcquire(); + } + + /// + /// + /// A method is used to release scheduler lock + /// + /// + [NoHeapAllocation] + public void Release() + { + this.spinlock.Release(); + } + + + /// + /// + /// Check if thread holds the spinlock + /// + /// + [NoHeapAllocation] + public bool IsHeldBy(Thread currentThread) + { + return this.spinlock.IsHeldBy(currentThread); + } + + + /// Actual spinlock that class wraps [AccessedByRuntime("referenced from halidt.asm")] - private static SpinLock dispatchLock; + private SpinLock spinlock; - // List of per-processor idle threads. - protected static ThreadQueue idleThreads; - - [NoHeapAllocation] - public static void DispatchLock() - { - dispatchLock.Acquire(); - } - - [NoHeapAllocation] - public static bool EnsureDispatchLock(int currentThreadIndex) { - if (dispatchLock.IsHeldBy(currentThreadIndex)) { - return false; - } else { - DispatchLock(); - return true; - } - } - - [NoHeapAllocation] - public static void DispatchRelease() - { - dispatchLock.Release(); - } - - [Conditional("DEBUG")] - [NoHeapAllocation] - public static void AssertDispatchLockHeld() - { - VTable.Assert(Processor.InterruptsDisabled()); - VTable.Assert(dispatchLock.IsHeldBy(Thread.CurrentThread)); - } - - public static unsafe void Initialize() - { - // Scheduler visualization hack - // we use unsafe access to minimize the scheduler overhead. - screenMem = IoMemory.MapPhysicalMemory(0xb8000, 80*50*2, true, true); - screenPos = 0; - screenPtr = (ushort *)screenMem.VirtualAddress; - - // Create the idle threads queue - idleThreads = new ThreadQueue(); - - // Create the twiddle values. - twiddles = new ushort[] { - (ushort)(0xe000 | '|'), - (ushort)(0xe000 | '/'), - (ushort)(0xe000 | '-'), - (ushort)(0xe000 | '\\') - }; - tpos = 0; - } - - public static void Finalize() - { - } - - ////////////////////////////////////////// Heads-up Scheduler Display. - // - private static IoMemory screenMem; - private static int screenPos; - private static unsafe ushort * screenPtr; - - [Inline] - [NoHeapAllocation] - private static unsafe void DisplayThread(int id) - { -#if SHOW_TWIDDLE - screenPtr[screenPos] = (ushort)(0x2a00| ('@'+id)); - screenPos = (screenPos + 1) % 80; -#endif - } - - private static ushort[] twiddles; - - private static int tpos; - - [Inline] - [NoHeapAllocation] - private static unsafe void Twiddle() - { -#if SHOW_TWIDDLE - screenPtr[screenPos] = twiddles[tpos]; - tpos = ++tpos & 3; -#endif - } - - /////////////////////////////////////////////////// Logging Functions. - // - [NoHeapAllocation] - public static void SelectingThread(Thread thread) - { - Scheduler.AssertDispatchLockHeld(); - Thread.CurrentThread.ActiveProcessor = null; - - if (thread != null) { - int id = thread.GetThreadId(); - - DisplayThread(id); - thread.ActiveProcessor = Processor.CurrentProcessor; -#if DEBUG_DISPATCH - DebugStub.Print("Thread {0:x8} Selecting {1:x8}\n", - __arglist( - Kernel.AddressOf(Thread.CurrentThread), - Kernel.AddressOf(thread))); -#endif // DEBUG_DISPATCH - Tracing.Log(Tracing.Debug, "Selecting tid={0:x3}", (uint)id); - - VTable.Assert(!thread.schedulerEntry.Enqueued); - } - Twiddle(); - } - - [NoHeapAllocation] - public static bool IsIdleThread(int threadIndex) { - Thread thread = Thread.threadTable[threadIndex]; - return (thread != null && - idleThreads.IsEnqueued(thread.schedulerEntry)); - } - - [Conditional("DEBUG_SCHEDULER")] - [CLSCompliant(false)] - public static void LogWakeThread(Thread thread) - { - } - - [Conditional("DEBUG_SCHEDULER")] - public static void LogSchedulerLate() - { - } - - [Conditional("DEBUG_SCHEDULER")] - public static void LogContextSwitch() - { - } - - [Conditional("DEBUG_SCHEDULER")] - public static void LogTimeJump() - { - } - - [Conditional("DEBUG_SCHEDULER")] - public static void LogSleepAdd() - { - } - - [Conditional("DEBUG_SCHEDULER")] - public static void LogReschedule() - { - } - - /////////////////////////////////////////// Scheduling Event Handlers. - // - // Each of these should be replaced by the scheduler mixin. - // - [CLSCompliant(false)] - public static void OnThreadStateInitialize(Thread thread, bool constructorCalled) - { - // Only initialize thread-local state! No locks held and interrupts on. - DebugStub.Break(); - } - - [CLSCompliant(false)] - public static void OnThreadStart(Thread thread) - { - AssertDispatchLockHeld(); - Debug.Assert(thread.freezeCount == 0); - DebugStub.Break(); - } - - [CLSCompliant(false)] - public static Thread OnThreadBlocked(Thread thread, SchedulerTime stop) - { - // Scheduler should return the target thread. - AssertDispatchLockHeld(); - DebugStub.Break(); - return null; - } - - [CLSCompliant(false)] - [NoHeapAllocation] - public static void OnThreadUnblocked(Thread thread) - { - // The scheduler should add this thread to the runnable queue, - // but should not perform a context switch yet. - AssertDispatchLockHeld(); - DebugStub.Break(); - } - - [CLSCompliant(false)] - [NoHeapAllocation] - public static Thread OnThreadYield(Thread thread) - { - AssertDispatchLockHeld(); - DebugStub.Break(); - return null; - } - - [CLSCompliant(false)] - public static Thread OnThreadStop(Thread thread) - { - AssertDispatchLockHeld(); - DebugStub.Break(); - return null; - } - - [CLSCompliant(false)] - public static void OnThreadFreezeIncrement(Thread thread) - { - // Increment freezeCount and unschedule the thread until - // freezeCount==0. (Used for Thread.Stop, Thread.Suspend, - // and modifying a thread's garbage collection state) - // - // If the thread is already running on a processor, - // the scheduler need not unschedule it immediately, but: - // - the thread must be unscheduled within a bounded time - // (e.g. a time slice) - // - once unscheduled, the thread must not be scheduled - // again until freezeCount==0. - AssertDispatchLockHeld(); - DebugStub.Break(); - } - - [CLSCompliant(false)] - public static void OnThreadFreezeDecrement(Thread thread) - { - // Decrement freezeCount. If freezeCount reaches 0, - // the thread may now be scheduled (if it is - // not blocked). - AssertDispatchLockHeld(); - DebugStub.Break(); - } - - [CLSCompliant(false)] - [NoHeapAllocation] - public static Thread OnTimerInterrupt(Thread thread, SchedulerTime now) - { - AssertDispatchLockHeld(); - DebugStub.Break(); - return null; - } - - [CLSCompliant(false)] - [NoHeapAllocation] - public static Thread OnIoInterrupt(Thread thread) - { - AssertDispatchLockHeld(); - DebugStub.Break(); - return null; - } - - [CLSCompliant(false)] - [NoHeapAllocation] - public static void OnProcessorShutdown(Thread thread) - { - AssertDispatchLockHeld(); - DebugStub.Break(); - } } } diff --git a/base/Kernel/Singularity/Scheduling/ThreadEntry.cs b/base/Kernel/Singularity/Scheduling/ThreadEntry.cs index 9e2769b..a1abc31 100644 --- a/base/Kernel/Singularity/Scheduling/ThreadEntry.cs +++ b/base/Kernel/Singularity/Scheduling/ThreadEntry.cs @@ -20,12 +20,15 @@ using Microsoft.Singularity; namespace Microsoft.Singularity.Scheduling { [CLSCompliant(false)] + [AccessedByRuntime("referenced from threads.cpp")] public class ThreadEntry { - public readonly Thread Thread = null; - internal ThreadEntry next = null; - internal ThreadEntry prev = null; - [AccessedByRuntime("referenced from c++")] + public readonly Thread Thread = null; + internal int id = -1; + internal ThreadEntry next = null; + internal ThreadEntry prev = null; + + [AccessedByRuntime("referenced from threads.cpp")] internal ThreadQueue queue = null; public ThreadEntry(Thread thread) @@ -68,5 +71,27 @@ namespace Microsoft.Singularity.Scheduling { return queue.Handle.GetBeneficiary(); } + + /// + /// + /// Set/Get Entry Id + /// + /// + public int Id + { + [Inline] + [NoHeapAllocation] + get + { + return this.id; + } + + [Inline] + [NoHeapAllocation] + set + { + this.id = value; + } + } } } diff --git a/base/Kernel/Singularity/Scheduling/ThreadQueue.cs b/base/Kernel/Singularity/Scheduling/ThreadQueue.cs index 4ef6fba..9c8f00d 100644 --- a/base/Kernel/Singularity/Scheduling/ThreadQueue.cs +++ b/base/Kernel/Singularity/Scheduling/ThreadQueue.cs @@ -27,20 +27,25 @@ namespace Microsoft.Singularity.Scheduling [AccessedByRuntime("referenced from c++")] public class ThreadQueue { - private ThreadEntry head = null; - private ThreadEntry tail = null; - private WaitHandle handle = null; + private ThreadEntry head; + private ThreadEntry tail; + private WaitHandleBase handle; public ThreadQueue() { + this.head = null; + this.tail = null; + this.handle = null; } - public ThreadQueue(WaitHandle handle) + public ThreadQueue(WaitHandleBase handle) { + this.head = null; + this.tail = null; this.handle = handle; } - public WaitHandle Handle { + public WaitHandleBase Handle { [NoHeapAllocation] get { return handle; } } @@ -60,7 +65,6 @@ namespace Microsoft.Singularity.Scheduling [NoHeapAllocation] public void EnqueueTail(ThreadEntry entry) { - Scheduler.AssertDispatchLockHeld(); VTable.Assert(entry.next == null); VTable.Assert(entry.prev == null); VTable.Assert(entry.queue == null); @@ -83,7 +87,6 @@ namespace Microsoft.Singularity.Scheduling [NoHeapAllocation] public void EnqueueHead(ThreadEntry entry) { - Scheduler.AssertDispatchLockHeld(); VTable.Assert(entry.next == null); VTable.Assert(entry.prev == null); VTable.Assert(entry.queue == null); @@ -106,7 +109,6 @@ namespace Microsoft.Singularity.Scheduling [NoHeapAllocation] public void InsertBefore(ThreadEntry position, ThreadEntry entry) { - Scheduler.AssertDispatchLockHeld(); if (position == null) { EnqueueTail(entry); } @@ -128,7 +130,6 @@ namespace Microsoft.Singularity.Scheduling [NoHeapAllocation] public Thread DequeueHeadThread() { - Scheduler.AssertDispatchLockHeld(); ThreadEntry entry = DequeueHead(); if (entry != null) { return entry.Thread; @@ -139,7 +140,6 @@ namespace Microsoft.Singularity.Scheduling [NoHeapAllocation] public Thread DequeueTailThread() { - Scheduler.AssertDispatchLockHeld(); ThreadEntry entry = DequeueTail(); if (entry != null) { return entry.Thread; @@ -150,7 +150,6 @@ namespace Microsoft.Singularity.Scheduling [NoHeapAllocation] public ThreadEntry DequeueHead() { - Scheduler.AssertDispatchLockHeld(); ThreadEntry entry = head; if (entry != null) { @@ -165,7 +164,6 @@ namespace Microsoft.Singularity.Scheduling [NoHeapAllocation] public ThreadEntry DequeueTail() { - Scheduler.AssertDispatchLockHeld(); ThreadEntry entry = tail; if (entry != null) { @@ -177,10 +175,41 @@ namespace Microsoft.Singularity.Scheduling } } + [NoHeapAllocation] + public void DequeueAll(ThreadQueue newParentQueue) + { + + ThreadEntry threadEntry = head; + + // Navigate through all elements and set the queue to the new queue + while ( threadEntry != null) { + threadEntry.queue = newParentQueue; + threadEntry = threadEntry.next; + } + + // Move queue elements to parent queue + if (newParentQueue.tail !=null) { + // If new parent queue is not empty enqueue new elements at the end of the + // new parent queue + newParentQueue.tail.next = this.head; + newParentQueue.tail = this.tail; + } + else { + // New queue is empty update head and tail + newParentQueue.head = this.head; + newParentQueue.tail = this.tail; + } + + // Reset initial queue + this.head = null; + this.tail = null; + + return; + } + [NoHeapAllocation] public void Remove(ThreadEntry entry) { - Scheduler.AssertDispatchLockHeld(); VTable.Assert(entry.queue == this); if (entry.next != null) { @@ -213,4 +242,160 @@ namespace Microsoft.Singularity.Scheduling return (head == null); } } + + // This struct is designed to support queues whose enqueue, + // dequeue, and remove operations do not allocate memory. + // This feature is useful when writing code that needs to + // do such operations with interrupts off. + [CLSCompliant(false)] + [AccessedByRuntime("referenced from c++")] + public struct ThreadQueueStruct + { + private ThreadEntry head; + private ThreadEntry tail; + + public ThreadEntry Head + { + [NoHeapAllocation] + get { return head; } + } + + [NoHeapAllocation] + public void EnqueueTail(ThreadEntry entry) + { + VTable.Assert(entry.next == null); + VTable.Assert(entry.prev == null); + VTable.Assert(entry.queue == null); + + entry.prev = this.tail; + + if (tail != null) { + VTable.Assert(tail.next == null); + tail.next = entry; + } + else { + VTable.Assert(head == null); + head = entry; + } + tail = entry; + } + + [NoHeapAllocation] + public void EnqueueHead(ThreadEntry entry) + { + VTable.Assert(entry.next == null); + VTable.Assert(entry.prev == null); + VTable.Assert(entry.queue == null); + + entry.next = head; + + if (head != null) { + VTable.Assert(head.prev == null); + head.prev = entry; + } + else { + VTable.Assert(tail == null); + tail = entry; + } + head = entry; + } + + [NoHeapAllocation] + public ThreadEntry DequeueHead() + { + ThreadEntry entry = head; + + if (entry != null) { + Remove(entry); + return entry; + } + else { + return null; + } + } + + [NoHeapAllocation] + public ThreadEntry DequeueTail() + { + ThreadEntry entry = tail; + + if (entry != null) { + Remove(entry); + return entry; + } + else { + return null; + } + } + + [NoHeapAllocation] + public void Remove(ThreadEntry entry) + { + if (entry.next != null) { + entry.next.prev = entry.prev; + } + else { + VTable.Assert(entry == tail); + tail = entry.prev; + } + + if (entry.prev != null) { + entry.prev.next = entry.next; + } + else { + VTable.Assert(entry == head); + head = entry.next; + } + + entry.next = null; + entry.prev = null; + entry.queue = null; + } + + [NoHeapAllocation] + public bool IsEmpty() + { + return (head == null); + } + } + + /// + /// + /// Class to enumerate thread entries in a queue + /// + /// + [CLSCompliant(false)] + public struct ThreadEntryEnum + { + /// + /// + /// Constructor + /// + /// + [NoHeapAllocation] + public ThreadEntryEnum(ThreadEntry entry) + { + this.current = entry; + } + + /// + /// + /// GetNext element in enumarator + /// + /// + [NoHeapAllocation] + public ThreadEntry GetNext() + { + ThreadEntry entryToReturn = this.current; + + if (this.current != null) { + current = current.next; + } + + return entryToReturn; + } + + /// Current element in enumerator + private ThreadEntry current; + } } diff --git a/base/Kernel/Singularity/Security/SecurityAttributes.cs b/base/Kernel/Singularity/Security/SecurityAttributes.cs index 384d067..a1d2140 100644 --- a/base/Kernel/Singularity/Security/SecurityAttributes.cs +++ b/base/Kernel/Singularity/Security/SecurityAttributes.cs @@ -18,7 +18,6 @@ // PropertySet or Category. using System; -using Microsoft.Singularity.Configuration; namespace Microsoft.Singularity.Security { diff --git a/base/Kernel/Singularity/ServiceRequest.cs b/base/Kernel/Singularity/ServiceRequest.cs index a966fac..8b1730c 100644 --- a/base/Kernel/Singularity/ServiceRequest.cs +++ b/base/Kernel/Singularity/ServiceRequest.cs @@ -16,4 +16,4 @@ namespace Microsoft.Singularity internal ServiceRequest next = null; protected internal abstract void Service(); } -} \ No newline at end of file +} diff --git a/base/Kernel/Singularity/ServiceRequestQueue.cs b/base/Kernel/Singularity/ServiceRequestQueue.cs index c3bad34..313298a 100644 --- a/base/Kernel/Singularity/ServiceRequestQueue.cs +++ b/base/Kernel/Singularity/ServiceRequestQueue.cs @@ -10,6 +10,7 @@ // using System.Threading; +using System.Runtime.CompilerServices; namespace Microsoft.Singularity { @@ -20,12 +21,26 @@ namespace Microsoft.Singularity private ServiceRequest head = null; private ServiceRequest tail = null; private SpinLock spinLock; - private AutoResetEvent enqueueEvent = new AutoResetEvent(false); + private InterruptAwareAutoResetEvent enqueueEvent = new InterruptAwareAutoResetEvent(false); + + /// + /// + /// Constructor + /// + /// + public ServiceRequestQueue() + { + spinLock = new SpinLock(SpinLock.Types.ServiceQueue); + } + // Insert an element at the tail of the queue. + [NoHeapAllocation] public void Enqueue(ServiceRequest req) { + // For now disable interrupts: bool iflag = Processor.DisableInterrupts(); + spinLock.Acquire(); try { if (head == null) { @@ -36,23 +51,29 @@ namespace Microsoft.Singularity tail.next = req; tail = req; } - enqueueEvent.Set(); } finally { spinLock.Release(); - Processor.RestoreInterrupts(iflag); } + + // Signal an event : for now it is possible that it can be spirous one... + enqueueEvent.InterruptAwareSet(); + + // Reenable interrupts + Processor.RestoreInterrupts(iflag); } // Block while the queue is empty, then return the head of the queue. public ServiceRequest Dequeue() { while (true) { + + // For now disable interrupts: bool iflag = Processor.DisableInterrupts(); + spinLock.Acquire(); try { - if (tail != null) - { + if (tail != null) { ServiceRequest req = head; if (req != tail) { head = req.next; @@ -66,9 +87,13 @@ namespace Microsoft.Singularity } finally { spinLock.Release(); + + // Reenable interrupts Processor.RestoreInterrupts(iflag); } - enqueueEvent.WaitOne(); + + // Wait on event + enqueueEvent.InterruptAwareWaitOne(); } } } diff --git a/base/Kernel/Singularity/ServiceThread.cs b/base/Kernel/Singularity/ServiceThread.cs index 18a8a27..7673a25 100644 --- a/base/Kernel/Singularity/ServiceThread.cs +++ b/base/Kernel/Singularity/ServiceThread.cs @@ -10,30 +10,54 @@ // using System.Threading; +using System.Runtime.CompilerServices; namespace Microsoft.Singularity { + /// + /// + /// Class processing asynchronous requeusts + /// + /// public class ServiceThread { - private static ServiceRequestQueue queue; - internal static void Initialize() { queue = new ServiceRequestQueue(); Thread.CreateThread(Thread.CurrentProcess, new ThreadStart(ServiceLoop)).Start(); } + /// + /// + /// Enqueue a request into the service queue + /// + /// + /// Requeuest to enqueu + /// + [NoHeapAllocation] internal static void Request(ServiceRequest req) { + // Dequeue the request queue.Enqueue(req); } + /// + /// + /// Service thread loop processing requests + /// + /// private static void ServiceLoop() { while (true) { + ServiceRequest req = queue.Dequeue(); + req.Service(); } } + + /// Service queue + private static ServiceRequestQueue queue; + } } diff --git a/base/Kernel/Singularity/SharedHeapWalker.cs b/base/Kernel/Singularity/SharedHeapWalker.cs index d86fa2b..69c592b 100644 --- a/base/Kernel/Singularity/SharedHeapWalker.cs +++ b/base/Kernel/Singularity/SharedHeapWalker.cs @@ -92,7 +92,7 @@ namespace Microsoft.Singularity // as part of walking the endpoint list.) Tracing.Log(Tracing.Audit, "Shared heap walk end"); } - catch(System.Exception) { + catch (System.Exception) { Tracing.Log(Tracing.Warning, "Exception thrown in shared heap walker"); } } diff --git a/base/Kernel/Singularity/SmapInfo.cs b/base/Kernel/Singularity/SmapInfo.cs index 1b7d5a6..35b225c 100644 --- a/base/Kernel/Singularity/SmapInfo.cs +++ b/base/Kernel/Singularity/SmapInfo.cs @@ -17,42 +17,50 @@ using System.Runtime.CompilerServices; namespace Microsoft.Singularity { [StructLayout(LayoutKind.Sequential)] - internal struct SMAPINFO + [CLSCompliant(false)] + [AccessedByRuntime("referenced from c++")] + public struct SMAPINFO { [AccessedByRuntime("referenced from c++")] - internal const uint AddressTypeFree = 1; + public const uint AddressTypeFree = 1; [AccessedByRuntime("referenced from c++")] - internal const uint AddressTypeReserved = 2; + public const uint AddressTypeReserved = 2; [AccessedByRuntime("referenced from c++")] - internal const uint AddressTypeACPI = 3; + public const uint AddressTypeACPI = 3; [AccessedByRuntime("referenced from c++")] - internal const uint AddressTypeNVS = 4; + public const uint AddressTypeNVS = 4; [AccessedByRuntime("referenced from c++")] - internal const uint AddressTypeUnusable = 5; + public const uint AddressTypeUnusable = 5; [AccessedByRuntime("referenced from c++")] - internal const uint AddressTypeMax = 5; + public const uint AddressTypeKernelNonGc = 6; + [AccessedByRuntime("referenced from c++")] + public const uint AddressTypeKernelStack = 7; + [AccessedByRuntime("referenced from c++")] + public const uint AddressTypeMax = 7; [AccessedByRuntime("referenced from c++")] - internal const uint ExtendedAttributeRangeEnabled = 1; + public const uint ExtendedAttributeRangeEnabled = 1; [AccessedByRuntime("referenced from c++")] - internal const uint ExtendedAttributeRangeNV = 2; + public const uint ExtendedAttributeRangeNV = 2; [AccessedByRuntime("referenced from c++")] - internal ulong addr; + public ulong addr; [AccessedByRuntime("referenced from c++")] - internal ulong size; + public ulong size; [AccessedByRuntime("referenced from c++")] - internal uint type; + public uint type; [AccessedByRuntime("referenced from c++")] - internal uint extendedAttributes; + public uint extendedAttributes; - internal enum AddressType : uint + public enum AddressType : uint { Free = AddressTypeFree, Reserved = AddressTypeReserved, ACPI = AddressTypeACPI, NVS = AddressTypeNVS, Unusable = AddressTypeUnusable, + KernelNonGc = AddressTypeKernelNonGc, + KernelStack = AddressTypeKernelStack, Max = AddressTypeMax } } diff --git a/base/Kernel/Singularity/SystemClock.cs b/base/Kernel/Singularity/SystemClock.cs index efb1293..208818e 100644 --- a/base/Kernel/Singularity/SystemClock.cs +++ b/base/Kernel/Singularity/SystemClock.cs @@ -26,7 +26,11 @@ namespace Microsoft.Singularity { private static HalClock clockDevice; private static long rtcUtcOffset; - + + private const int year = 2007; + private const int month = 1; + private const int day = 1; + /// /// Constructor. /// Underlying clock device to use. @@ -66,6 +70,13 @@ namespace Microsoft.Singularity public static DateTime GetUtcTime() { + if (clockDevice == null) { + // the clock device is not initialized yet + // This will happen when we try to set the times for + // the inital objects in the Directory Service + // We will cons up a fixed value; + return new DateTime(year,month,day); + } DateTime rtc = new DateTime(clockDevice.GetRtcTime()); return rtc.AddTicks(rtcUtcOffset); } @@ -92,13 +103,13 @@ namespace Microsoft.Singularity /// Get the time since booting in kernel ticks of 100ns. /// - /* - [NoHeapAllocation] - public static long GetKernelTicks() - { - return clockDevice.GetKernelTicks(); - } - */ + // + //[NoHeapAllocation] + //public static long GetKernelTicks() + //{ + // return clockDevice.GetKernelTicks(); + //} + // [NoHeapAllocation] private static bool UtcOffsetValid(long offset) diff --git a/base/Kernel/Singularity/X86/ThreadContext.cs b/base/Kernel/Singularity/ThreadContext.cs similarity index 79% rename from base/Kernel/Singularity/X86/ThreadContext.cs rename to base/Kernel/Singularity/ThreadContext.cs index 0924424..a812e8c 100644 --- a/base/Kernel/Singularity/X86/ThreadContext.cs +++ b/base/Kernel/Singularity/ThreadContext.cs @@ -9,7 +9,7 @@ // Note: // -namespace Microsoft.Singularity.X86 +namespace Microsoft.Singularity { using System; using System.Runtime.CompilerServices; @@ -17,13 +17,18 @@ namespace Microsoft.Singularity.X86 using System.Threading; using Microsoft.Singularity.V1.Threads; - using Microsoft.Singularity.X86; + using Microsoft.Singularity.Isal; [NoCCtor] [CLSCompliant(false)] [StructLayout(LayoutKind.Sequential)] + [StructAlign(16)] + [AccessedByRuntime("referenced from c++")] internal struct ThreadContext { + [AccessedByRuntime("referenced from c++")] + internal ThreadRecord threadRecord; + [AccessedByRuntime("referenced from c++")] internal ushort num; [AccessedByRuntime("referenced from c++")] @@ -32,10 +37,6 @@ namespace Microsoft.Singularity.X86 internal unsafe ThreadContext * prev; [AccessedByRuntime("referenced from c++")] internal unsafe ThreadContext * next; - [AccessedByRuntime("referenced from c++")] - internal UIntPtr cr2; - [AccessedByRuntime("referenced from c++")] - internal UIntPtr cr3; [AccessedByRuntime("referenced from c++")] internal unsafe ThreadContext * prevInKern; @@ -46,33 +47,6 @@ namespace Microsoft.Singularity.X86 [AccessedByRuntime("referenced from c++")] internal unsafe ThreadContext * nextInProc; - [AccessedByRuntime("referenced from c++")] - internal UIntPtr err; - [AccessedByRuntime("referenced from c++")] - internal UIntPtr eip; - [AccessedByRuntime("referenced from c++")] - internal UIntPtr cs0; - [AccessedByRuntime("referenced from c++")] - internal UIntPtr efl; - - [AccessedByRuntime("referenced from c++")] - internal UIntPtr eax; - [AccessedByRuntime("referenced from c++")] - internal UIntPtr ebx; - [AccessedByRuntime("referenced from c++")] - internal UIntPtr ecx; - [AccessedByRuntime("referenced from c++")] - internal UIntPtr edx; - - [AccessedByRuntime("referenced from c++")] - internal UIntPtr esp; - [AccessedByRuntime("referenced from c++")] - internal UIntPtr ebp; - [AccessedByRuntime("referenced from c++")] - internal UIntPtr esi; - [AccessedByRuntime("referenced from c++")] - internal UIntPtr edi; - [AccessedByRuntime("referenced from c++")] internal UIntPtr stackBegin; [AccessedByRuntime("referenced from c++")] @@ -88,7 +62,7 @@ namespace Microsoft.Singularity.X86 // fields alias differently for kernel or process code. #if SINGULARITY_KERNEL [AccessedByRuntime("referenced from c++")] - internal unsafe Thread *_thread; + internal unsafe void *_thread; [AccessedByRuntime("referenced from c++")] internal UIntPtr processThread; [AccessedByRuntime("referenced from c++")] @@ -103,7 +77,7 @@ namespace Microsoft.Singularity.X86 [AccessedByRuntime("referenced from c++")] internal UIntPtr kernelThread; [AccessedByRuntime("referenced from c++")] - internal unsafe Thread *_thread; + internal unsafe void *_thread; [AccessedByRuntime("referenced from c++")] internal unsafe System.GCs.CallStack.TransitionRecord * kernelMarkers; [AccessedByRuntime("referenced from c++")] @@ -115,26 +89,8 @@ namespace Microsoft.Singularity.X86 #endif [AccessedByRuntime("referenced from halforgc.asm")] internal unsafe int gcStates; - - // See ThreadContext.IsInKernelMode private unsafe System.GCs.CallStack.TransitionRecord * modeMarker; - [AccessedByRuntime("referenced from c++")] - internal UIntPtr dr0; - [AccessedByRuntime("referenced from c++")] - internal UIntPtr dr1; - [AccessedByRuntime("referenced from c++")] - internal UIntPtr dr2; - - [AccessedByRuntime("referenced from c++")] - internal UIntPtr dr3; - [AccessedByRuntime("referenced from c++")] - internal UIntPtr dr6; - [AccessedByRuntime("referenced from c++")] - internal UIntPtr dr7; - - [AccessedByRuntime("referenced from c++")] - internal MmxContext mmx; #if PAGING [AccessedByRuntime("referenced from c++")] @@ -165,11 +121,6 @@ namespace Microsoft.Singularity.X86 return (((UIntPtr)prev) == UIntPtr.Zero); } - internal bool WasInterrupted { - [NoHeapAllocation] - get { return (efl & EFlags.IF) != 0; } - } - [AccessedByRuntime("referenced from c++")] // Return true if the thread is in kernel mode, false if the // thread is in process mode. @@ -234,6 +185,12 @@ namespace Microsoft.Singularity.X86 modeMarker = processMarkers; } + [NoHeapAllocation] + internal bool IsRunning() + { + return !threadRecord.spill.IsSpilled; + } + [NoHeapAllocation] static private unsafe System.GCs.CallStack.TransitionRecord * ParentModeMarker(System.GCs.CallStack.TransitionRecord * child) @@ -264,6 +221,7 @@ namespace Microsoft.Singularity.X86 [NoHeapAllocation] internal extern void UpdateAfterGC(Thread thread); +#if SINGULARITY_KERNEL [AccessedByRuntime("output to header: defined in c++")] [MethodImpl(MethodImplOptions.InternalCall)] [GCAnnotation(GCOption.NOGC)] @@ -271,14 +229,30 @@ namespace Microsoft.Singularity.X86 [NoHeapAllocation] internal extern void Initialize(int threadIndex, UIntPtr stackBegin, uint cr3); -#if SINGULARITY_KERNEL [AccessedByRuntime("output to header: defined in c++")] [MethodImpl(MethodImplOptions.InternalCall)] [GCAnnotation(GCOption.NOGC)] [StackBound(32)] [NoHeapAllocation] internal extern void InitializeIdle(int threadIndex, UIntPtr stackBegin, uint cr3); - #endif } } + +// This is a temporary workaround to get around bartok assumptions +namespace Microsoft.Singularity.X86 +{ + using System.Runtime.CompilerServices; + + struct ProcessorContext + { + [RequiredByBartok] + Microsoft.Singularity.X86.ThreadContext threadContext; + } + + struct ThreadContext + { + [RequiredByBartok] + System.UIntPtr stackLimit; + } +} diff --git a/base/Kernel/Singularity/Tracing.cs b/base/Kernel/Singularity/Tracing.cs index 5dbbd0e..e055ed8 100644 --- a/base/Kernel/Singularity/Tracing.cs +++ b/base/Kernel/Singularity/Tracing.cs @@ -16,62 +16,14 @@ using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Threading; using Microsoft.Singularity; +using Microsoft.Singularity.Eventing; namespace Microsoft.Singularity { - /// - /// Provides a simple tracing facility. Writes and displays log records of the form: - /// long cycleCounter; // GetCycleCounter() value - /// ushort recordType; // TracingType value - /// ushort byteCount; // Bytes of data in record including these 12 bytes - /// [type-specific data] - /// - /// Records are aligned on 8-byte boundaries to make them easier to view in memory dumps. - /// - /// XXX Code needs to be implemented to allow buffers to be switched, particularly after calling DumpLogRecords() - /// - [NoCCtor] [CLSCompliant(false)] + [AccessedByRuntime("referenced from Tracing.cpp")] public class Tracing { - [AccessedByRuntime("referenced from Tracing.cpp")] - public struct LogEntry - { - [AccessedByRuntime("referenced from Tracing.cpp")] - public ulong cycleCount; - [AccessedByRuntime("referenced from Tracing.cpp")] - public UIntPtr eip; - [AccessedByRuntime("referenced from Tracing.cpp")] - public ushort cpuId; - [AccessedByRuntime("referenced from Tracing.cpp")] - public ushort threadId; - [AccessedByRuntime("referenced from Tracing.cpp")] - public ushort processId; - [AccessedByRuntime("referenced from Tracing.cpp")] - public ushort tag; - [AccessedByRuntime("referenced from Tracing.cpp")] - public byte severity; - [AccessedByRuntime("referenced from Tracing.cpp")] - public byte strings; - [AccessedByRuntime("referenced from Tracing.cpp")] - public ushort padding0; - [AccessedByRuntime("referenced from Tracing.cpp")] - public unsafe byte * text; - [AccessedByRuntime("referenced from Tracing.cpp")] - public UIntPtr arg0; - [AccessedByRuntime("referenced from Tracing.cpp")] - public UIntPtr arg1; - [AccessedByRuntime("referenced from Tracing.cpp")] - public UIntPtr arg2; - [AccessedByRuntime("referenced from Tracing.cpp")] - public UIntPtr arg3; - [AccessedByRuntime("referenced from Tracing.cpp")] - public UIntPtr arg4; - [AccessedByRuntime("referenced from Tracing.cpp")] - public UIntPtr arg5; - [AccessedByRuntime("referenced from Tracing.cpp")] - public uint padding1; - } public const byte Fatal = 0xe; // system crashed. public const byte Error = 0xc; // system will crash. @@ -81,50 +33,12 @@ namespace Microsoft.Singularity public const byte Audit = 0x4; // impact on performance. public const byte Debug = 0x2; // used only for debugging. - [Flags] - public enum Strings + public static void InitializeSystem() { - [AccessedByRuntime("referenced from Tracing.cpp")] - String0 = 0x01, - [AccessedByRuntime("referenced from Tracing.cpp")] - String1 = 0x02, - String2 = 0x04, - String3 = 0x08, - String4 = 0x10, - String5 = 0x20, - String6 = 0x40, - String7 = 0x80, } - // Note: These fields are initialized by the code in Tracing.cpp. - // - [AccessedByRuntime("referenced from Tracing.cpp")] - private static unsafe byte * txtBegin; - [AccessedByRuntime("referenced from Tracing.cpp")] - private static unsafe byte * txtLimit; -#if SINGULARITY_KERNEL - [AccessedByRuntime("referenced from Tracing.cpp")] - private static unsafe byte * txtHead; -#endif - [AccessedByRuntime("referenced from Tracing.cpp")] - private static unsafe byte ** ptxtHead; - - [AccessedByRuntime("referenced from Tracing.cpp")] - private static unsafe LogEntry *logBegin; - [AccessedByRuntime("referenced from Tracing.cpp")] - private static unsafe LogEntry *logLimit; -#if SINGULARITY_KERNEL - [AccessedByRuntime("referenced from Tracing.cpp")] - private static unsafe LogEntry *logHead; -#endif - [AccessedByRuntime("referenced from Tracing.cpp")] - private static unsafe LogEntry **plogHead; -#if SINGULARITY_KERNEL - [AccessedByRuntime("referenced from Tracing.cpp")] - private static unsafe long *tscOffsets; -#endif - // - // End Note. + [NoHeapAllocation] + public static void SetTscOffset(long tscOffset){} [AccessedByRuntime("output to header : defined in Tracing.cpp")] [StackBound(64)] @@ -136,81 +50,20 @@ namespace Microsoft.Singularity [StackBound(64)] [MethodImpl(MethodImplOptions.InternalCall)] [NoHeapAllocation] - public static extern void Finalize(); + public static extern UIntPtr GetSystemTracingStorageHandle(); - [NoHeapAllocation] - public static unsafe void GetTracingHeaders(out LogEntry *_logBegin, - out LogEntry *_logLimit, - out LogEntry **_logHead, - out byte *_txtBegin, - out byte *_txtLimit, - out byte **_txtHead) - { - _logBegin = logBegin; - _logLimit = logLimit; - _logHead = plogHead; - - _txtBegin = txtBegin; - _txtLimit = txtLimit; - _txtHead = ptxtHead; - } - - [AccessedByRuntime("output to header : defined in Tracing.cpp")] - [StackBound(64)] - [MethodImpl(MethodImplOptions.InternalCall)] - [NoHeapAllocation] - private static unsafe extern byte * AddText(byte *buffer, string arg); - - [AccessedByRuntime("output to header : defined in Tracing.cpp")] - [StackBound(128)] - [MethodImpl(MethodImplOptions.InternalCall)] - [NoHeapAllocation] - private static unsafe extern LogEntry * CreateLog(byte severity, - UIntPtr eip, - int chars, - out byte *text); - - [Conditional("TRACING")] [AccessedByRuntime("output to header : defined in Tracing.cpp")] [StackBound(128)] [MethodImpl(MethodImplOptions.InternalCall)] [NoHeapAllocation] public static unsafe extern void Log(byte severity); - [Conditional("TRACING")] - [AccessedByRuntime("output to header : defined in Tracing.cpp")] - [StackBound(128)] - [MethodImpl(MethodImplOptions.InternalCall)] - [NoHeapAllocation] - public static unsafe extern void Log(byte severity, sbyte * msg); - - [Conditional("TRACING")] - [AccessedByRuntime("output to header : defined in Tracing.cpp")] - [StackBound(128)] - [MethodImpl(MethodImplOptions.InternalCall)] - [NoHeapAllocation] - public static unsafe extern void Log(byte severity, sbyte * msg, - String arg0, - UIntPtr arg1); - - [Conditional("TRACING")] - [AccessedByRuntime("output to header : defined in Tracing.cpp")] - [StackBound(128)] - [MethodImpl(MethodImplOptions.InternalCall)] - [NoHeapAllocation] - public static unsafe extern void Log(byte severity, sbyte * msg, - String arg0, - UIntPtr arg1, - UIntPtr arg2); - - [Conditional("TRACING")] [AccessedByRuntime("output to header : defined in Tracing.cpp")] [StackBound(256)] [MethodImpl(MethodImplOptions.InternalCall)] [NoHeapAllocation] public static unsafe extern void Log(byte severity, string msg); - [Conditional("TRACING")] [AccessedByRuntime("output to header : defined in Tracing.cpp")] [StackBound(256)] [MethodImpl(MethodImplOptions.InternalCall)] @@ -218,7 +71,6 @@ namespace Microsoft.Singularity public static unsafe extern void Log(byte severity, string msg, UIntPtr arg0); - [Conditional("TRACING")] [AccessedByRuntime("output to header : defined in Tracing.cpp")] [StackBound(256)] [MethodImpl(MethodImplOptions.InternalCall)] @@ -226,7 +78,6 @@ namespace Microsoft.Singularity public static unsafe extern void Log(byte severity, string msg, UIntPtr arg0, UIntPtr arg1); - [Conditional("TRACING")] [AccessedByRuntime("output to header : defined in Tracing.cpp")] [StackBound(256)] [MethodImpl(MethodImplOptions.InternalCall)] @@ -234,7 +85,6 @@ namespace Microsoft.Singularity public static unsafe extern void Log(byte severity, string msg, UIntPtr arg0, UIntPtr arg1, UIntPtr arg2); - [Conditional("TRACING")] [AccessedByRuntime("output to header : defined in Tracing.cpp")] [StackBound(256)] [MethodImpl(MethodImplOptions.InternalCall)] @@ -243,7 +93,6 @@ namespace Microsoft.Singularity UIntPtr arg0, UIntPtr arg1, UIntPtr arg2, UIntPtr arg3); - [Conditional("TRACING")] [AccessedByRuntime("output to header : defined in Tracing.cpp")] [StackBound(256)] [MethodImpl(MethodImplOptions.InternalCall)] @@ -252,7 +101,6 @@ namespace Microsoft.Singularity UIntPtr arg0, UIntPtr arg1, UIntPtr arg2, UIntPtr arg3, UIntPtr arg4); - [Conditional("TRACING")] [AccessedByRuntime("output to header : defined in Tracing.cpp")] [StackBound(256)] [MethodImpl(MethodImplOptions.InternalCall)] @@ -261,7 +109,6 @@ namespace Microsoft.Singularity UIntPtr arg0, UIntPtr arg1, UIntPtr arg2, UIntPtr arg3, UIntPtr arg4, UIntPtr arg5); - [Conditional("TRACING")] [AccessedByRuntime("output to header : defined in Tracing.cpp")] [StackBound(256)] [MethodImpl(MethodImplOptions.InternalCall)] @@ -269,7 +116,6 @@ namespace Microsoft.Singularity public static unsafe extern void Log(byte severity, string msg, string arg0); - [Conditional("TRACING")] [AccessedByRuntime("output to header : defined in Tracing.cpp")] [StackBound(256)] [MethodImpl(MethodImplOptions.InternalCall)] @@ -277,7 +123,6 @@ namespace Microsoft.Singularity public static unsafe extern void Log(byte severity, string msg, string arg0, UIntPtr arg1); - [Conditional("TRACING")] [AccessedByRuntime("output to header : defined in Tracing.cpp")] [StackBound(256)] [MethodImpl(MethodImplOptions.InternalCall)] @@ -285,16 +130,6 @@ namespace Microsoft.Singularity public static unsafe extern void Log(byte severity, string msg, string arg0, UIntPtr arg1, UIntPtr arg2); - [Conditional("TRACING")] - [AccessedByRuntime("output to header : defined in Tracing.cpp")] - [StackBound(256)] - [MethodImpl(MethodImplOptions.InternalCall)] - [NoHeapAllocation] - public static unsafe extern void Log(byte severity, string msg, - string arg0, UIntPtr arg1, UIntPtr arg2, - UIntPtr arg3); - - [Conditional("TRACING")] [AccessedByRuntime("output to header : defined in Tracing.cpp")] [StackBound(256)] [MethodImpl(MethodImplOptions.InternalCall)] @@ -302,12 +137,6 @@ namespace Microsoft.Singularity public static unsafe extern void Log(byte severity, string msg, string arg0, string arg1); -#if SINGULARITY_KERNEL - [AccessedByRuntime("output to header : defined in Tracing.cpp")] - [StackBound(64)] - [MethodImpl(MethodImplOptions.InternalCall)] - [NoHeapAllocation] - public static extern void SetTscOffset(long tscOffset); -#endif // SINGULARITY_KERNEL } + } diff --git a/base/Kernel/Singularity/V1/Processes/ProcessHandle.cs b/base/Kernel/Singularity/V1/Processes/ProcessHandle.cs index 817dbb1..fc1ca16 100644 --- a/base/Kernel/Singularity/V1/Processes/ProcessHandle.cs +++ b/base/Kernel/Singularity/V1/Processes/ProcessHandle.cs @@ -15,13 +15,13 @@ using System.Runtime.CompilerServices; using System.Threading; using Microsoft.Singularity; +using Microsoft.Singularity.Hal; using Microsoft.Singularity.Io; using Microsoft.Singularity.Loader; using Microsoft.Singularity.Memory; using Microsoft.Singularity.V1.Services; using Microsoft.Singularity.V1.Security; using Microsoft.Singularity.Security; -using Microsoft.Singularity.X86; using Microsoft.Singularity.V1.Types; using InternalProcessState = System.Threading.ProcessState; @@ -114,7 +114,7 @@ namespace Microsoft.Singularity.V1.Processes // process to hold it. // - // REVIEW create empty args array so that we + // REVIEW: create empty args array so that we // do not need to change the process constructor; String[] myargs; if (myaction != null) { @@ -217,78 +217,6 @@ namespace Microsoft.Singularity.V1.Processes Kernel.Waypoint(551); - // haryadi -- this is currently a hack-kind of hook - // if the app to be run is HelloMp then we intercept control - // and run our own MpLoad, the Ap processor will run the HelloMp - if (arguments.Length != 0 && arguments[0] == "hellomp") { - - // IoMemory mpImage = Binder.HelloMpLoadImage(); - IoMemory mpImage = Binder.LoadRawImage("/init/hellomp", "hellomp.x86"); - IoMemory mpLoadedImage; - - // mpLoadedImage should have physical address - PEImage mpPeImage = - PEImage.HelloMpLoad(Thread.CurrentProcess, - mpImage, out mpLoadedImage); - UIntPtr mpEntryPoint = mpPeImage.GetEntryPoint(mpLoadedImage); - - DebugStub.WriteLine - ("HSG: Sending ApImage e.{0:x}", - __arglist(mpEntryPoint)); - - // send IPI - bool iflag = Processor.DisableInterrupts(); - MpExecution.PutIntrApImage(1, mpEntryPoint); - MpExecution.SendApImage(0,1); - Processor.RestoreInterrupts(iflag); - - // uncomment this if want to run this on BSP - // Processor.MpCallEntryPoint(mpEntryPoint); - - // return failure to Shell - handle = new ProcessHandle(); - return false; - } - - // haryadi -- intercept TestMpAbi - if (arguments.Length != 0 && arguments[0] == "testmpabi") { - - IoMemory mpImage = Binder.LoadRawImage("/init/testmpabi", "testmpabi.x86"); - IoMemory mpLoadedImage; - - // mpLoadedImage should have physical address - bool isForMp = true; - PEImage mpPeImage = - PEImage.Load(Thread.CurrentProcess, - mpImage, out mpLoadedImage, isForMp); - UIntPtr mpEntryPoint = mpPeImage.GetEntryPoint(mpLoadedImage); - - // prepare IPI - // MpExecution.ApImage apImage = new MpExecution.ApImage(); - // apImage.virtAddr = mpLoadedImage.VirtualAddress; - // apImage.phyAddr = mpLoadedImage.PhysicalAddress.Value; - // apImage.length = (UIntPtr)mpLoadedImage.Length; - // apImage.entryPoint = mpEntryPoint; - - DebugStub.WriteLine - ("HSG: Sending ApImage e.{0:x}", - __arglist(mpEntryPoint)); - - // send IPI - bool iflag = Processor.DisableInterrupts(); - MpExecution.PutIntrApImage(1, mpEntryPoint); - MpExecution.SendApImage(0,1); - Processor.RestoreInterrupts(iflag); - - // uncomment this if want to run this on BSP - // Processor.MpCallEntryPoint(mpEntryPoint); - - // return failure to Shell - handle = new ProcessHandle(); - return false; - } - - // // Find image to run. // @@ -374,7 +302,7 @@ namespace Microsoft.Singularity.V1.Processes int index) { //convert contract to string - if (contractLen == 0 ) return false; + if (contractLen == 0) return false; Process process = HandleTable.GetHandle(handle.id) as Process; string contract = String.StringCTOR(contractChars, 0, contractLen); if (contract == null) return false; diff --git a/base/Kernel/Singularity/V1/Security/PrincipalHandle.cs b/base/Kernel/Singularity/V1/Security/PrincipalHandle.cs index ab0d5a1..e05b047 100644 --- a/base/Kernel/Singularity/V1/Security/PrincipalHandle.cs +++ b/base/Kernel/Singularity/V1/Security/PrincipalHandle.cs @@ -32,10 +32,10 @@ namespace Microsoft.Singularity.V1.Security return new PrincipalHandle(Thread.CurrentProcess.Principal.Val); } - /* For the following routine, assume that the length of the output string - is nchars. If nchars is larger than outNameLength, this routine returns - -nchars without touching outName. Otherwise, the output string is copied - into outName, and nchars is returns. */ + // For the following routine, assume that the length of the output string + // is nchars. If nchars is larger than outNameLength, this routine returns + // -nchars without touching outName. Otherwise, the output string is copied + // into outName, and nchars is returns. [ExternalEntryPoint] public static unsafe int GetPrincipalNameImpl(PrincipalHandle handle, /*[out]*/ char *outName, int outNameLength) diff --git a/base/Kernel/Singularity/V1/Services/ChannelService.cs b/base/Kernel/Singularity/V1/Services/ChannelService.cs index 4cf870d..6929ea0 100644 --- a/base/Kernel/Singularity/V1/Services/ChannelService.cs +++ b/base/Kernel/Singularity/V1/Services/ChannelService.cs @@ -1,7 +1,6 @@ //////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity - Singularity ABI Implementation -// // Copyright (c) Microsoft Corporation. All rights reserved. // // File: EndpointCore.cs @@ -34,22 +33,36 @@ namespace Microsoft.Singularity.V1.Services TransferContentOwnership = 2, } + [CLSCompliant(false)] unsafe public struct EndpointCore { -#if !PAGING - /// - /// Flag indicating that the endpoint is closed. - /// This gets set either by an explicit close, or when - /// the kernel determines that the endpoint is not reachable. - /// NOTE: A channel only goes away entirely, once both ends are closed! - /// - private volatile bool closed; + //////////////////////////////////////////////////////////////////// + // Fields + //////////////////////////////////////////////////////////////////// + // + // NOTE: The fields specified here must match those in: + // Kernel/Singularity/Channels/EndpointCore.cs + // Kernel/Singularity/V1/Services/ChannelServices.cs + // Libraries/Singuarity.V1/Services/ChannelServices.cs /// - /// The endpoint to which this endpoint is connected. + /// Handle to the actual message delivery mechanism /// - private Allocation* /*EndpointCore* opt(ExHeap)*/ peer; + private DeliveryHandle deliveryHandle; + + /// + /// Event handle in case this endpoint is part of a collection + /// + private AutoResetEventHandle collectionEvent; + + // + // These "cached" fields are directly accessable by user programs, + // but are not trusted by the kernel (as they could be modified by untrusted + // code). The kernel relies on the trusted shadow copies held in the + // deliveryImpl object, but updates these fields to reflect any changes to user + // apps. + // /// /// Event on which sends are signaled to this endpoint. @@ -57,44 +70,50 @@ namespace Microsoft.Singularity.V1.Services /// The kernel deallocates the handle when the channel is deallocated. /// NOTE: stays valid until the entire channel gets collected. /// - private AutoResetEventHandle messageEvent; + private AutoResetEventHandle cachedMessageEvent; /// - /// Event handle in case this endpoint is part of a collection + /// Closed flag /// - private AutoResetEventHandle collectionEvent; + private bool cachedClosed; /// /// Contains the process id of the process currently owning this end of the /// channel. /// - private int ownerProcessId; - - /// - /// Contains the principal handle of the process currently owning this end of the - /// channel. - /// - private PrincipalHandle ownerPrincipalHandle; - - /// - /// Contains the number of sends to this endpoint. - /// - private int receiveCount; + private int cachedOwnerProcessId; /// /// Contains the channelId (positive on the EXP endpoint, negative on the imp endpoint) /// - private int channelId; -#else - internal EndpointTrusted* id; -#endif //PAGING + private int cachedChannelId; + + /// + /// Whether to marshall or not + /// + private bool cachedMarshall; + + /// + /// Points to the peer endpoint + /// + private Allocation* /*EndpointCore* opt(ExHeap)*/ cachedPeer; + + /// + /// If true then the peer state can be queried directly from cachedPeer + /// + private bool peerStateValid; + + //////////////////////////////////////////////////////////////////// + // Methods + //////////////////////////////////////////////////////////////////// + /// /// Used to allocate a channel endpoint. The size must be correctly computed by /// the trusted caller (currently trusted code NewChannel) /// [ExternalEntryPoint] - public static Allocation* /*EndpointCore* opt(ExHeap)!*/ + public static Allocation* //EndpointCore* opt(ExHeap)! Allocate(uint size, SystemType st) { Allocation* ep = (Allocation*) SharedHeap.CurrentProcessSharedHeap.Allocate( @@ -114,20 +133,10 @@ namespace Microsoft.Singularity.V1.Services [ExternalEntryPoint] public static bool Dispose(ref EndpointCore endpoint) { -#if !PAGING fixed (EndpointCore* ep = &endpoint) { EndpointCoreImplementation* epimp = (EndpointCoreImplementation*)ep; return epimp->Dispose(); } -#else - try { - EndpointCoreImplementation ep = new EndpointCoreImplementation(endpoint.id); - EndpointCoreImplementation.Dispose(ref ep); - return true; - } catch(ApplicationException) { - return false; - } -#endif //PAGING } /// @@ -144,89 +153,97 @@ namespace Microsoft.Singularity.V1.Services /// Performs the initialization of the core part of each endpoint and cross links /// them to form a channel. /// + [ExternalEntryPoint] public static void Connect( Allocation* /*EndpointCore* opt(ExHeap)!*/ imp, - Allocation* /*EndpointCore* opt(ExHeap)!*/ exp) + Allocation* /*EndpointCore* opt(ExHeap)!*/ exp, + Allocation* /*EndpointCore* opt(ExHeap) */ ep) { EndpointCoreImplementation.Connect((SharedHeap.Allocation*)imp, - (SharedHeap.Allocation*)exp); + (SharedHeap.Allocation*)exp, + (SharedHeap.Allocation*)ep); } /// /// Indicates if this endpoint is closed /// -#if !PAGING [NoHeapAllocation] - public static bool Closed(ref EndpointCore ep) + public static bool Closed(ref EndpointCore endpoint) { - return ep.closed; + // kernel methods always access the shadowed copy in deliveryImpl + // not the user accessable copy in EndpointCore + fixed (EndpointCore* ep = &endpoint) { + EndpointCoreImplementation* epimp = (EndpointCoreImplementation*)ep; + return epimp->Closed(); + } } - [NoHeapAllocation] + /// + /// Indicates if the peer endpoint is closed + /// public static bool PeerClosed(ref EndpointCore ep) { - EndpointCore * peer = (EndpointCore*)SharedHeapService.GetData(ep.peer); - return peer->closed; + // kernel methods always access the shadowed copy in deliveryImpl + // not the user accessable copy in EndpointCore + return PeerClosedABI(ref ep); } -#else + + /// + /// Indicates if the peer endpoint is closed (ABI call) + /// [ExternalEntryPoint] - public static bool Closed(ref EndpointCore ep) { - return new EndpointCoreImplementation(ep.id).Closed(); + public static bool PeerClosedABI(ref EndpointCore endpoint) { + fixed (EndpointCore* ep = &endpoint) { + EndpointCoreImplementation* epimp = (EndpointCoreImplementation*)ep; + return epimp->PeerClosed(); + } } - [ExternalEntryPoint] - public static bool PeerClosed(ref EndpointCore ep) { - return new EndpointCoreImplementation(ep.id).PeerClosed(); - } -#endif //PAGING /// /// Set this end to closed /// -#if !PAGING - public static void Close(ref EndpointCore ep) { - ep.closed = true; - } -#else [ExternalEntryPoint] - public static void Close(ref EndpointCore ep) { - new EndpointCoreImplementation(ep.id).Close(); + public static void Close(ref EndpointCore endpoint) { + fixed (EndpointCore* ep = &endpoint) { + EndpointCoreImplementation* epimp = (EndpointCoreImplementation*)ep; + epimp->Close(); + } } -#endif //PAGING /// /// The endpoint to which this endpoint is connected. /// -#if !PAGING public static Allocation* /*EndpointCore* opt(ExHeap) */ GetPeer(ref EndpointCore ep, out bool marshall) { - marshall = false; - return ep.peer; + // kernel methods always access the shadowed copy in deliveryImpl + // not the user accessable copy in EndpointCore + return GetPeerABI(ref ep, out marshall); } -#else + [ExternalEntryPoint] - public static Allocation* /*EndpointCore* opt(ExHeap) */ GetPeer(ref EndpointCore ep, - out bool marshall) + public static Allocation* /*EndpointCore* opt(ExHeap) */ GetPeerABI(ref EndpointCore endpoint, + out bool marshall) { - return (Allocation*)new EndpointCoreImplementation(ep.id).Peer(out marshall); + fixed (EndpointCore* ep = &endpoint) { + EndpointCoreImplementation* epimp = (EndpointCoreImplementation*)ep; + return (Allocation*) epimp->Peer(out marshall); + } } -#endif //PAGING /// /// The event to wait for messages on this endpoint. Used by Select. /// -#if !PAGING - public static SyncHandle GetWaitHandle(ref EndpointCore ep) + public static SyncHandle GetWaitHandle(ref EndpointCore endpoint) { - return ep.messageEvent; + // kernel methods always access the shadowed copy in deliveryImpl + // not the user accessable copy in EndpointCore + fixed (EndpointCore* ep = &endpoint) { + EndpointCoreImplementation* epimp = (EndpointCoreImplementation*)ep; + return epimp->GetWaitHandle(); + } } -#else - [ExternalEntryPoint] - public static SyncHandle GetWaitHandle(ref EndpointCore ep) { - return new EndpointCoreImplementation(ep.id).GetWaitHandle(); - } -#endif //PAGING /// /// Notify the owner of this endpoint that a message is ready. @@ -234,177 +251,127 @@ namespace Microsoft.Singularity.V1.Services /// [ExternalEntryPoint] public static void NotifyPeer(ref EndpointCore endpoint) { -#if !PAGING fixed (EndpointCore* ep = &endpoint) { EndpointCoreImplementation* epimp = (EndpointCoreImplementation*)ep; epimp->NotifyPeer(); } -#else - new EndpointCoreImplementation(endpoint.id).NotifyPeer(); -#endif //PAGING } /// /// Wait for a message to arrive on this endpoint. /// -#if !PAGING public static void Wait(ref EndpointCore ep) { - SyncHandle.WaitOne(ep.messageEvent); + SyncHandle.WaitOne(GetWaitHandle(ref ep)); } -#else - [ExternalEntryPoint] - public static void Wait(ref EndpointCore ep) { - SyncHandle.WaitOne(GetWaitHandle(ref ep)); - } -#endif //PAGING /// /// Transfer the given Allocation block to the target endpoint /// -#if !PAGING [ExternalEntryPoint] public static void TransferBlockOwnership(Allocation* ptr, ref EndpointCore target) { - Monitoring.Log(Monitoring.Provider.ChannelService, - (ushort)ChannelServiceEvent.TransferBlockOwnership, 0, - (uint)target.channelId, (uint)target.ownerProcessId, - 0, 0, 0); - SharedHeapService.SetOwnerProcessId(ptr, target.ownerProcessId); -#if CHANNEL_COUNT - EndpointCoreImplementation.IncreaseBytesSentCount((long)SharedHeapService.GetSize(ptr)); -#endif + fixed (EndpointCore* ep = &target) { + EndpointCoreImplementation* epimp = (EndpointCoreImplementation*)ep; + EndpointCoreImplementation.TransferBlockOwnership((SharedHeap.Allocation*)ptr, ref *epimp); + } } -#else - [ExternalEntryPoint] - // TODO: change "ref EndpointCore" to "EndpointCore" - public static void TransferBlockOwnership(Allocation* ptr, ref EndpointCore target) - { - EndpointCoreImplementation ep = new EndpointCoreImplementation(target.id); - EndpointCoreImplementation.TransferBlockOwnership((SharedHeap.Allocation*)ptr, ref ep); - } -#endif //PAGING /// /// Transfer any contents that needs to be adjusted from the transferee to the target /// endpoint. Currently, this means setting the ownerProcessId of the /// transferee to that of the target. /// -#if !PAGING [ExternalEntryPoint] public static void TransferContentOwnership( ref EndpointCore transferee, ref EndpointCore target) { - Monitoring.Log(Monitoring.Provider.ChannelService, - (ushort)ChannelServiceEvent.TransferContentOwnership, 0, - (uint)transferee.ownerProcessId, - (uint)target.ownerProcessId, - (uint)transferee.channelId, - (uint)target.channelId, - // Not allowed to look at target.peer. We don't own that! - // (uint)((EndpointCore*)(SharedHeapService.GetData(target.peer)))->channelId); - (uint)target.channelId); + fixed (EndpointCore* ep1 = &transferee, ep2 = &target) { + EndpointCoreImplementation* epimp1 = (EndpointCoreImplementation*)ep1; + EndpointCoreImplementation* epimp2 = (EndpointCoreImplementation*)ep2; + EndpointCoreImplementation.TransferContentOwnership(ref *epimp1, ref *epimp2); + } + } - transferee.ownerProcessId = target.ownerProcessId; - transferee.ownerPrincipalHandle = target.ownerPrincipalHandle; - // also fix up ownership of peer allocation - Allocation* transfereePeerAllocation = transferee.peer; - TransferBlockOwnership(transfereePeerAllocation, ref target); - } -#else - [ExternalEntryPoint] - // TODO: change "ref EndpointCore" to "EndpointCore" - public static void TransferContentOwnership( - ref EndpointCore transferee, - ref EndpointCore target) + [NoHeapAllocation] + public static int GetChannelID(ref EndpointCore endpoint) { - EndpointCoreImplementation ep1 = new EndpointCoreImplementation(transferee.id); - EndpointCoreImplementation ep2 = new EndpointCoreImplementation(target.id); - EndpointCoreImplementation.TransferContentOwnership(ref ep1, ref ep2); + // kernel methods always access the shadowed copy in deliveryImpl + // not the user accessable copy in EndpointCore + fixed (EndpointCore* ep = &endpoint) { + EndpointCoreImplementation* epimp = (EndpointCoreImplementation*)ep; + return epimp->ChannelId; + } } -#endif //PAGING -#if !PAGING [NoHeapAllocation] - public static int GetChannelID(ref EndpointCore ep) + public static int GetOwnerProcessID(ref EndpointCore endpoint) { - return ep.channelId; + // kernel methods always access the shadowed copy in deliveryImpl + // not the user accessable copy in EndpointCore + fixed (EndpointCore* ep = &endpoint) { + EndpointCoreImplementation* epimp = (EndpointCoreImplementation*)ep; + return epimp->ProcessId; + } } -#else - [ExternalEntryPoint] - [NoHeapAllocation] - public static int GetChannelID(ref EndpointCore ep) - { - return new EndpointCoreImplementation(ep.id).ChannelId; - } -#endif //PAGING -#if !PAGING - [NoHeapAllocation] - public static int GetOwnerProcessID(ref EndpointCore ep) - { - return ep.ownerProcessId; - } -#else - [ExternalEntryPoint] - [NoHeapAllocation] - public static int GetOwnerProcessID(ref EndpointCore ep) - { - return new EndpointCoreImplementation(ep.id).ProcessId; - } -#endif //PAGING - -#if !PAGING [NoHeapAllocation] public static int GetPeerProcessID(ref EndpointCore ep) { - EndpointCore * peer = (EndpointCore*)SharedHeapService.GetData(ep.peer); - return peer->ownerProcessId; + // kernel methods always access the shadowed copy in deliveryImpl + // not the user accessable copy in EndpointCore + return GetPeerProcessIDABI(ref ep); } -#else + [ExternalEntryPoint] [NoHeapAllocation] - public static int GetPeerProcessID(ref EndpointCore ep) + public static int GetPeerProcessIDABI(ref EndpointCore endpoint) { - return new EndpointCoreImplementation(ep.id).PeerProcessId; + fixed (EndpointCore* ep = &endpoint) { + EndpointCoreImplementation* epimp = (EndpointCoreImplementation*)ep; + return epimp->PeerProcessId; + } } -#endif //PAGING - // It is unfortunate that PrincipalHandle and Principal are not - // compatible types. They are only distinct because ABI types are disfavored - // for code that doesn't deal directly with the ABI. - -#if !PAGING - [NoHeapAllocation] - public static PrincipalHandle GetPeerPrincipalHandle(ref EndpointCore ep) + [ExternalEntryPoint] + public static void AcceptDelegation(Allocation* /*EndpointCore* opt(ExHeap)!*/ imp, + Allocation* /*EndpointCore* opt(ExHeap)!*/ exp, + Allocation* /*EndpointCore* opt(ExHeap)!*/ ep) { - EndpointCore * peer = (EndpointCore*)SharedHeapService.GetData(ep.peer); - return peer->ownerPrincipalHandle; + EndpointCoreImplementation.AcceptDelegation((SharedHeap.Allocation*)imp, + (SharedHeap.Allocation*)exp, + (SharedHeap.Allocation*)ep); } -#else + + [ExternalEntryPoint] + public static void EnableDelegation(ref EndpointCore endpoint, bool allowMediation) + { + fixed (EndpointCore* ep = &endpoint) { + EndpointCoreImplementation* epimp = (EndpointCoreImplementation*)ep; + epimp->EnableDelegation(allowMediation); + } + } + [ExternalEntryPoint] [NoHeapAllocation] - public static PrincipalHandle GetPeerPrincipalHandle(ref EndpointCore ep) + public static PrincipalHandle GetPeerPrincipalHandle(ref EndpointCore endpoint) { - return new EndpointCoreImplementation(ep.id).PeerPrincipalHandle; + fixed (EndpointCore* ep = &endpoint) { + EndpointCoreImplementation* epimp = (EndpointCoreImplementation*)ep; + return epimp->PeerPrincipalHandle; + } } -#endif -#if !PAGING - [NoHeapAllocation] - public static PrincipalHandle GetOwnerPrincipalHandle(ref EndpointCore ep) - { - return ep.ownerPrincipalHandle; - } -#else [ExternalEntryPoint] [NoHeapAllocation] - public static PrincipalHandle GetOwnerPrincipalHandle(ref EndpointCore ep) + public static PrincipalHandle GetOwnerPrincipalHandle(ref EndpointCore endpoint) { - return new EndpointCoreImplementation(ep.id).OwnerPrincipalHandle; + fixed (EndpointCore* ep = &endpoint) { + EndpointCoreImplementation* epimp = (EndpointCoreImplementation*)ep; + return epimp->OwnerPrincipalHandle; + } } -#endif //PAGING /// /// Instruct the selectable object to signal events on the given AutoResetEvent @@ -412,57 +379,42 @@ namespace Microsoft.Singularity.V1.Services /// A selectable object need only support being part of a single collection at /// any point in time. /// - [ExternalEntryPoint] - public static void LinkIntoCollection(ref EndpointCore ep, AutoResetEventHandle ev) { -#if !PAGING - // Debug.Assert(this.collectionEvent.id == UIntPtr.Zero); + public static void LinkIntoCollection(ref EndpointCore ep, + AutoResetEventHandle ev) { ep.collectionEvent = ev; -#else - new EndpointCoreImplementation(ep.id).LinkIntoCollection(ev); -#endif - Tracing.Log(Tracing.Debug, "Ev:{0:x}", ev.id); } /// /// Instruct the selectable object to stop signalling events on the given /// AutoResetEvent. /// - [ExternalEntryPoint] - public static void UnlinkFromCollection(ref EndpointCore ep, AutoResetEventHandle ev) { -#if !PAGING - // Debug.Assert(this.collectionEvent.id != UIntPtr.Zero); + public static void UnlinkFromCollection(ref EndpointCore ep, + AutoResetEventHandle ev) { ep.collectionEvent = new AutoResetEventHandle(); -#else - new EndpointCoreImplementation(ep.id).UnlinkFromCollection(ev); -#endif - } - -#if PAGING - [ExternalEntryPoint] - unsafe public static void MarshallMessage(ref EndpointCore ep, - byte* basep, byte* source, - int* tagAddress, int size) - { - Tracing.Log(Tracing.Debug, - "source offset:{0:x} tagLoc offset:{1:x} size {2:x}", - (uint)source-(uint)basep, (uint)tagAddress-(uint)basep, - (uint)size); - - new EndpointCoreImplementation(ep.id).BeginUpdate(basep, source, tagAddress, size); } [ExternalEntryPoint] - unsafe public static void MarshallPointer(ref EndpointCore ep, - byte* basep, byte** target, SystemType type) + unsafe public static void MarshallMessage(ref EndpointCore endpoint, + byte* basep, + byte* source, + int* tagAddress, + int msgSize) { - Tracing.Log(Tracing.Debug, - "source offset:{0:x} type:{1}", - (uint)target-(uint)basep, type.id); - - new EndpointCoreImplementation(ep.id).MarshallPointer(basep, (void**)target, type); + fixed (EndpointCore* ep = &endpoint) { + EndpointCoreImplementation* epimp = (EndpointCoreImplementation*)ep; + epimp->BeginUpdate(basep, source, tagAddress, msgSize); + } } -#endif + [ExternalEntryPoint] + public static void MarshallPointer(ref EndpointCore endpoint, byte* basep, byte** target, SystemType type, byte* parent, int offset) + { + fixed (EndpointCore* ep = &endpoint) + { + EndpointCoreImplementation* epimp = (EndpointCoreImplementation*)ep; + epimp->MarshallPointer(basep, target, type, parent, offset); + } + } } } diff --git a/base/Kernel/Singularity/V1/Services/DebugService.cs b/base/Kernel/Singularity/V1/Services/DebugService.cs index 30c809c..ece971d 100644 --- a/base/Kernel/Singularity/V1/Services/DebugService.cs +++ b/base/Kernel/Singularity/V1/Services/DebugService.cs @@ -14,6 +14,7 @@ using System.Runtime.CompilerServices; using System.Threading; using Microsoft.Singularity; using Microsoft.Singularity.Memory; +using Microsoft.Singularity.Isal; namespace Microsoft.Singularity.V1.Services { @@ -110,7 +111,7 @@ namespace Microsoft.Singularity.V1.Services [ExternalEntryPoint] public static void WalkStack() { - Stacks.WalkStack(Processor.GetFramePointer()); + Stacks.WalkStack(Isa.GetFramePointer()); } [ExternalEntryPoint] diff --git a/base/Kernel/Singularity/V1/Services/DeliveryHandle.cs b/base/Kernel/Singularity/V1/Services/DeliveryHandle.cs new file mode 100644 index 0000000..088fca9 --- /dev/null +++ b/base/Kernel/Singularity/V1/Services/DeliveryHandle.cs @@ -0,0 +1,39 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: DeliveryHandle.cs +// +// Note: An indirection mechanism used by EndpointCore to abstract the +// Delivery mechanism used to notify the another endpoint of a new +// message and potentially marshall and copy the message to another +// Domain. +// + +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.Singularity; +using Microsoft.Singularity.Memory; + +namespace Microsoft.Singularity.V1.Services +{ + using DeliveryHandleImp = Microsoft.Singularity.Channels.DeliveryHandle; + + [CLSCompliant(false)] + public struct DeliveryHandle + { + public readonly UIntPtr id; + + [ExternalEntryPoint] + public static unsafe void Dispose(ref DeliveryHandle handle) + { + fixed (DeliveryHandle* h = &handle) { + DeliveryHandleImp* himp = (DeliveryHandleImp*) h; + DeliveryHandleImp.Dispose(Thread.CurrentProcess, *himp); + } + } + } +} diff --git a/base/Kernel/Singularity/V1/Services/DeviceService.cs b/base/Kernel/Singularity/V1/Services/DeviceService.cs index e1fe751..ef9ceb5 100644 --- a/base/Kernel/Singularity/V1/Services/DeviceService.cs +++ b/base/Kernel/Singularity/V1/Services/DeviceService.cs @@ -46,41 +46,11 @@ namespace Microsoft.Singularity.V1.Services } [ExternalEntryPoint] - public static unsafe bool GetPciConfigImpl( - ushort * addrPort, - ushort * dataPort, - ushort * identifier) + public static unsafe bool GetPciPort( + out PciPortHandle pciPortHandle + ) { - return GetPciConfig(out *addrPort, out *dataPort, out *identifier); - } - - public static bool GetPciConfig(out ushort addrPort, - out ushort dataPort, - out ushort identifier) - { - bool ret = false; - - - addrPort = 0; - dataPort = 0; - identifier = 0; - - IoConfig config = Thread.CurrentProcess.IoConfig; - - PciConfig pci = config as PciConfig; - if (pci != null) { - addrPort = pci.AddressPort; - dataPort = pci.DataPort; - identifier = pci.Identifier; - ret = true; - } - - Tracing.Log(Tracing.Debug, - "DeviceService.GetPciConfig(out addr={0:x4}, out data={1:x4}, out id={2:x8})", - addrPort, dataPort, identifier); - Tracing.Log(Tracing.Debug, " {0}", config.ToString()); - - return ret; + return PciPortHandle.Create(out pciPortHandle); } [ExternalEntryPoint] diff --git a/base/Kernel/Singularity/V1/Services/DiagnosisService.cs b/base/Kernel/Singularity/V1/Services/DiagnosisService.cs new file mode 100644 index 0000000..11c0124 --- /dev/null +++ b/base/Kernel/Singularity/V1/Services/DiagnosisService.cs @@ -0,0 +1,322 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity - Singularity ABI +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: DiagnosisService.cs +// +// Note: +// + +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.Singularity; +using Microsoft.Singularity.Memory; +using Microsoft.Singularity.Eventing; + +namespace Microsoft.Singularity.V1.Services +{ + [CLSCompliant(false)] + public struct DiagnosisService + { + + public static ulong SIPBufferSize = 0; + public static ulong SIPProfileOptions = 0; + + public static ulong KernelBufferSize = 0; + public static ulong KernelProfileOptions = 0; + + // External entry points + + [ExternalEntryPoint] + [CLSCompliant(false)] + public static unsafe int GCProfileSettings( + ulong *defaultMemorySize, + ulong *Options + ) + { + + *defaultMemorySize = SIPBufferSize; + *Options = SIPProfileOptions; + + return 0; + } + + [ExternalEntryPoint] + [CLSCompliant(false)] + public static bool RegisterEventingController( + UIntPtr controllerHandle, + UIntPtr executionContextHandle + ) + { + return KernelController.KernelControllerObject.RegisterControllerImpl( + controllerHandle, + executionContextHandle); + } + + [ExternalEntryPoint] + [CLSCompliant(false)] + public static bool DebugPrintLogEntry(UIntPtr controllerHandle, UIntPtr entryHandle) + { + return KernelController.DebugPrintLogEntry( + controllerHandle, + entryHandle); + } + + [ExternalEntryPoint] + [CLSCompliant(false)] + public static bool TestKernelStorage() + { + return KernelController.TestKernelStorageImpl(); + } + + [ExternalEntryPoint] + [CLSCompliant(false)] + public static UIntPtr OpenGlobalStorage(UIntPtr storageHandle) + { + return KernelController.OpenStorageImpl(storageHandle); + } + + [ExternalEntryPoint] + [CLSCompliant(false)] + public static void CloseGlobalStorage(UIntPtr storageHandle) + { + KernelController.KernelControllerObject.CloseStorageImpl(storageHandle); + } + + [ExternalEntryPoint] + [CLSCompliant(false)] + [NoHeapAllocation] + public static unsafe UIntPtr LogSourceEntry(UIntPtr sourceHandle, + uint flags, + UIntPtr eventType, + byte * buffer, + int size) + { + return KernelController.LogSourceEntryImpl(sourceHandle, + flags, + eventType, + buffer, + size); + } + + [ExternalEntryPoint] + [CLSCompliant(false)] + [NoHeapAllocation] + public static unsafe UIntPtr LogSourceEntry(UIntPtr sourceHandle, + uint flags, + UIntPtr eventType, + byte * buffer, + int size, + int stringsCount, + void ** strings) + { + return KernelController.LogSourceEntryImpl(sourceHandle, + flags, + eventType, + buffer, + size, + stringsCount, + strings); + } + + [ExternalEntryPoint] + [CLSCompliant(false)] + [NoHeapAllocation] + public static UIntPtr CreateQueryView(UIntPtr storageHandle, bool forward) + { + return KernelController.CreateQueryViewImpl(storageHandle, forward); + } + + [ExternalEntryPoint] + [CLSCompliant(false)] + [NoHeapAllocation] + public static void DeleteQueryView(UIntPtr storageHandle) + { + KernelController.DeleteQueryViewImpl(storageHandle); + } + + [ExternalEntryPoint] + [CLSCompliant(false)] + public static unsafe UIntPtr GetNextEntry(UIntPtr queryHandle, + UIntPtr * type, + UInt32 * userOffset, + byte * buffer, + UInt16 bufferSize ) + { + return KernelController.GetNextEntryImpl(queryHandle, + type, + userOffset, + buffer, + bufferSize); + } + + [ExternalEntryPoint] + [CLSCompliant(false)] + public static unsafe bool RegisterEvent(char * eventName, char * eventDescription, UIntPtr * eventHandle) { + + return KernelController.RegisterEvent(eventName, eventDescription, eventHandle); + } + + [ExternalEntryPoint] + [CLSCompliant(false)] + public static unsafe bool RegisterEventField(UIntPtr eventHandle, + char * fieldName, + UInt16 offset, + UInt16 type) { + + return KernelController.RegisterEventField(eventHandle, + fieldName, + offset, + type); + } + + [ExternalEntryPoint] + [CLSCompliant(false)] + public static unsafe bool RegisterEventGenericField(UIntPtr eventHandle, + char * fieldName, + UInt16 offset, + UInt16 size, + UIntPtr fieldTypeHandle) { + + return KernelController.RegisterEventGenericField(eventHandle, + fieldName, + offset, + size, + fieldTypeHandle); + } + + [ExternalEntryPoint] + [CLSCompliant(false)] + public static unsafe bool RegisterEnum(char * enumName, UInt16 type, UIntPtr * eventHandle) + { + + return KernelController.RegisterEnum(enumName, type, eventHandle); + } + + [ExternalEntryPoint] + [CLSCompliant(false)] + public static unsafe bool RegisterEnumValue(UIntPtr eventHandle, + char * valueName, + UInt64 value, + byte flagChar) + { + + return KernelController.RegisterEnumValue(eventHandle, + valueName, + value, + flagChar); + } + + + [ExternalEntryPoint] + [CLSCompliant(false)] + public static unsafe UIntPtr WalkEventDescriptor(UIntPtr eventHandle, + UIntPtr currentField, + UInt16 * offset, + UInt16 * type, + char * bufferName, + UInt16 bufferSize ) + { + return KernelController.WalkEventDescriptor(eventHandle, + currentField, + offset, + type, + bufferName, + bufferSize); + } + + [ExternalEntryPoint] + [CLSCompliant(false)] + public static unsafe bool GetSourceInformation(UIntPtr sourceHandle, + UIntPtr * storageHandle, + UIntPtr * eventType, + UInt16 * count, + char * bufferName, + UInt16 bufferSize ) + { + return KernelController.GetSourceInformation(sourceHandle, + storageHandle, + eventType, + count, + bufferName, + bufferSize); + } + + [ExternalEntryPoint] + [CLSCompliant(false)] + public static unsafe int QuerySourcesList(UIntPtr * buffer, int size) + { + return KernelController.QuerySourcesList(buffer, size); + } + + [ExternalEntryPoint] + [CLSCompliant(false)] + public static unsafe int QueryEventTypeList(UIntPtr * buffer, int size) + { + return KernelController.QueryEventTypeList(buffer, size); + } + + [ExternalEntryPoint] + [CLSCompliant(false)] + public static unsafe bool ReadActiveSourceItem(UIntPtr sourceHandle, + int item, + UIntPtr * type, + byte * buffer, + UInt16 bufferSize ) + { + return KernelController.KernelControllerObject.ReadActiveSourceItem(sourceHandle, + item, + type, + buffer, + bufferSize ); + + } + + // Wrap this up with a consistent definition with the SIP side + // we need to fix the silent drop of unsafe + public static int GCProfileSettingsImpl( + out ulong defaultMemorySize, + out ulong Options + ) + { + defaultMemorySize = KernelBufferSize; + Options = KernelProfileOptions; + + return 0; + } + + internal const uint DEFERED_COMMAND_ENABLE_KERNEL_GC = 1; + internal const uint DEFERED_COMMAND_FORCE_GC_COLLECTION = 2; + + internal static uint DeferedCommand = 0; + + public static void DeferedUpdateNotification() + { + uint CapturedValue = DeferedCommand; + + DeferedCommand = 0; + + // TODO: This needs to be be moved later in a work item thread pool mechanism + // of some sort + + if ((CapturedValue & DEFERED_COMMAND_ENABLE_KERNEL_GC) != 0) { + // TODO: This API has disappeared in the arm branch + // - may need to get moved to a new API + // GCProfilerLogger.StartProfiling(); + } + + if ((CapturedValue & DEFERED_COMMAND_FORCE_GC_COLLECTION) != 0) { + DebugStub.WriteLine("Kernel GC collection started\n"); + System.GC.Collect(); + System.GC.WaitForPendingFinalizers(); + System.GC.Collect(); + DebugStub.WriteLine("Kernel GC collection completed\n"); + DebugStub.Break(); + } + + } + } +} diff --git a/base/Kernel/Singularity/V1/Services/MemoryInfoService.cs b/base/Kernel/Singularity/V1/Services/MemoryInfoService.cs new file mode 100644 index 0000000..b58eb04 --- /dev/null +++ b/base/Kernel/Singularity/V1/Services/MemoryInfoService.cs @@ -0,0 +1,110 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity - Singularity ABI +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: MemoryInfoService.cs +// +// Note: +// + +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.Singularity; +using Microsoft.Singularity.Memory; + +namespace Microsoft.Singularity.V1.Services +{ + public struct MemoryInfoService + { + + [ExternalEntryPoint] + [CLSCompliant(false)] + public static unsafe int MemoryUsageInfo( + ulong *totalMemoryFree, + ulong *totalMemoryInUse, + ulong *kernelHeapInUse, + ulong *kernelStackInUse, + ulong *totalSIPHeapInUse, + ulong *totalSIPStackInUse, + ulong *kernelStackReservation, + ulong *kernelHeapReservation + ) + { + ulong allocatedCount = 0; + ulong allocatedBytes = 0; + ulong freedCount = 0; + ulong freedBytes = 0; + + ulong userAllocatedCount = 0; + ulong userAllocatedBytes = 0; + ulong userFreedCount = 0; + ulong userFreedBytes = 0; + ulong kernelHeapReservationLocal = 0; + + *totalMemoryFree = MemoryManager.GetFreePhysicalMemory(); + *totalMemoryInUse = MemoryManager.GetUsedPhysicalMemory(); + + // + // Get general memory info + // + MemoryManager.GetUsageStatistics( + out allocatedCount, + out allocatedBytes, + out freedCount, + out freedBytes, + out kernelHeapReservationLocal + ); + + // + // Get SIP (user) memory usage + // + MemoryManager.GetUserStatistics( + out userAllocatedCount, + out userAllocatedBytes, + out userFreedCount, + out userFreedBytes + ); + + // Kernel heap is total heap in use - SIP heap in use + *kernelHeapInUse = (allocatedBytes - freedBytes) - (userAllocatedBytes - userFreedBytes); + *kernelHeapReservation = kernelHeapReservationLocal; + + *totalSIPHeapInUse = userAllocatedBytes - userFreedBytes; + + // Stack Information + ulong kernelStackCount = 0; + ulong kernelStackReturnCount = 0; + ulong kernelStackBytes = 0; + ulong kernelStackReturnBytes = 0; + ulong kernelStackReservationLocal = 0; + ulong SIPStackCount = 0; + ulong SIPStackReturnCount = 0; + ulong SIPStackBytes = 0; + ulong SIPStackReturnBytes = 0; + ulong SIPStackReservation = 0; + + MemoryManager.GetStackUsage( + out kernelStackCount, + out kernelStackReturnCount, + out kernelStackBytes, + out kernelStackReturnBytes, + out kernelStackReservationLocal, + out SIPStackCount, + out SIPStackReturnCount, + out SIPStackBytes, + out SIPStackReturnBytes, + out SIPStackReservation + ); + + *totalSIPStackInUse = SIPStackBytes - SIPStackReturnBytes; + *kernelStackInUse = kernelStackBytes - kernelStackReturnBytes; + *kernelStackReservation = kernelStackReservationLocal; + + return 0; + } + } +} + diff --git a/base/Kernel/Singularity/V1/Services/PciPortHandle.cs b/base/Kernel/Singularity/V1/Services/PciPortHandle.cs new file mode 100644 index 0000000..50d354b --- /dev/null +++ b/base/Kernel/Singularity/V1/Services/PciPortHandle.cs @@ -0,0 +1,156 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity - Singularity ABI +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: PciPortHandle.cs +// +// Note: +// + +using System; +using System.Runtime.CompilerServices; +using Microsoft.Singularity; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Memory; +using System.Threading; + +namespace Microsoft.Singularity.V1.Services +{ + [CLSCompliant(false)] + public struct PciPortHandle + { + // Internal methods + UIntPtr id; + + internal PciPortHandle(UIntPtr id) + { + this.id = id; + } + + /////////////////////////////////////////////////////////////////////// + // + // ABI Exposed + // + + [ExternalEntryPoint] + public static unsafe bool CreateImpl(PciPortHandle* handle) + { + return Create(out *handle); + } + + public static bool Create(out PciPortHandle handle) + { + IoConfig config = Thread.CurrentProcess.IoConfig; + PciConfig pciConfig = config as PciConfig; + + if (pciConfig != null) { + handle = new PciPortHandle( + Thread.CurrentProcess.AllocateHandle(pciConfig.PciPort) + ); + return true; + } + else { + handle = new PciPortHandle(); + return false; + } + } + + [ExternalEntryPoint] + public static bool Dispose(PciPortHandle handle) + { + if (handle.id != UIntPtr.Zero) { + Thread.CurrentProcess.ReleaseHandle(handle.id); + } + return false; + } + + [ExternalEntryPoint] + public static unsafe + bool Read8Impl(PciPortHandle handle, int offset, byte* value) + { + if (handle.id != UIntPtr.Zero) { + PciPort p = HandleTable.GetHandle(handle.id) as PciPort; + *value = p.Read8(offset); + return true; + } + else { + *value = 0; + return false; + } + } + + [ExternalEntryPoint] + public static unsafe + bool Read16Impl(PciPortHandle handle, int offset, ushort* value) + { + if (handle.id != UIntPtr.Zero) { + PciPort p = HandleTable.GetHandle(handle.id) as PciPort; + *value = p.Read16(offset); + return true; + } + else { + *value = 0; + return false; + } + } + + [ExternalEntryPoint] + public static unsafe + bool Read32Impl(PciPortHandle handle, int offset, uint* value) + { + if (handle.id != UIntPtr.Zero) { + PciPort p = HandleTable.GetHandle(handle.id) as PciPort; + *value = p.Read32(offset); + return true; + } + else { + *value = 0; + return false; + } + } + + [ExternalEntryPoint] + public static + bool Write8(PciPortHandle handle, int offset, byte value) + { + if (handle.id != UIntPtr.Zero) { + PciPort p = HandleTable.GetHandle(handle.id) as PciPort; + p.Write8(offset, value); + return true; + } + else { + return false; + } + } + + [ExternalEntryPoint] + public static + bool Write16(PciPortHandle handle, int offset, ushort value) + { + if (handle.id != UIntPtr.Zero) { + PciPort p = HandleTable.GetHandle(handle.id) as PciPort; + p.Write16(offset, value); + return true; + } + else { + return false; + } + } + + [ExternalEntryPoint] + public static + bool Write32(PciPortHandle handle, int offset, uint value) + { + if (handle.id != UIntPtr.Zero) { + PciPort p = HandleTable.GetHandle(handle.id) as PciPort; + p.Write32(offset, value); + return true; + } + else { + return false; + } + } + } +} diff --git a/base/Kernel/Singularity/V1/Services/PlatformService.cs b/base/Kernel/Singularity/V1/Services/PlatformService.cs new file mode 100644 index 0000000..ca36dbd --- /dev/null +++ b/base/Kernel/Singularity/V1/Services/PlatformService.cs @@ -0,0 +1,90 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity - Singularity ABI +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: DebugService.cs +// +// Note: +// + +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.Singularity; +using Microsoft.Singularity.Hal; +using Microsoft.Singularity.Memory; +using Microsoft.Singularity.Isal; + +namespace Microsoft.Singularity.V1.Services +{ + [AccessedByRuntime("Method called from HAL.cpp")] + public struct PlatformService + { + [ExternalEntryPoint] + [AccessedByRuntime("Called from HAL.cpp")] + public static bool DisableInterrupts() + { + return PrivilegedGate.DisableInterrupts(); + } + + [ExternalEntryPoint] + [AccessedByRuntime("Called from HAL.cpp")] + public static void RestoreInterrupts(bool enabled) + { + PrivilegedGate.RestoreInterrupts(enabled); + } + + [ExternalEntryPoint] + [AccessedByRuntime("Called from HAL.cpp")] + public static bool InterruptsDisabled() + { + return PrivilegedGate.InterruptsDisabled(); + } + + [ExternalEntryPoint] + [AccessedByRuntime("Called from HAL.cpp")] + [CLSCompliant(false)] + public static void CleanAndInvalidateDCache(UIntPtr address, UIntPtr length) + { +#if ISA_ARM + Microsoft.Singularity.Isal.Arm.XScale.Mmu.CleanInvalidateDCacheLines(address, length); +#endif + } + + [ExternalEntryPoint] + [AccessedByRuntime("Called from HAL.cpp")] + [CLSCompliant(false)] + public static void InvalidateDCache(UIntPtr address, UIntPtr length) + { +#if ISA_ARM + Microsoft.Singularity.Isal.Arm.XScale.Mmu.InvalidateDCacheLines(address, length); +#endif + } + + [ExternalEntryPoint] + [AccessedByRuntime("Called from HAL.cpp")] + [CLSCompliant(false)] + public static void SetCacheAttributes(UIntPtr address, UIntPtr length, bool cacheable, bool bufferable) + { +#if ISA_ARM + Microsoft.Singularity.Isal.Arm.XScale.Mmu.SetCacheAttributes(address, length, cacheable, bufferable); +#endif + } + + [ExternalEntryPoint] + [AccessedByRuntime("Called from HAL.cpp")] + public static int GetProcessorContextOffset() + { + return Platform.ThePlatform.CpuRecordPointerOffset; + } + + [ExternalEntryPoint] + [AccessedByRuntime("Called from HAL.cpp")] + public static int GetThreadContextOffset() + { + return Platform.ThePlatform.ThreadRecordPointerOffset; + } + } +} diff --git a/base/Kernel/Singularity/V1/Services/ProcessService.cs b/base/Kernel/Singularity/V1/Services/ProcessService.cs index 3a704f1..dbb8ba3 100644 --- a/base/Kernel/Singularity/V1/Services/ProcessService.cs +++ b/base/Kernel/Singularity/V1/Services/ProcessService.cs @@ -17,6 +17,7 @@ using Microsoft.Singularity.Memory; using Microsoft.Singularity.Security; using Microsoft.Singularity.Io; using Microsoft.Singularity.V1.Security; +using Microsoft.Singularity.Eventing; namespace Microsoft.Singularity.V1.Services { @@ -34,6 +35,7 @@ namespace Microsoft.Singularity.V1.Services } [CLSCompliant(false)] + [AccessedByRuntime("referenced from Tracing.cpp")] public struct ProcessService { // In order to pass the arguments to the kernel, we first need to @@ -57,7 +59,7 @@ namespace Microsoft.Singularity.V1.Services totalCharacters += argstring.Length; } - if (argVector == null ) { + if (argVector == null) { return totalCharacters; } @@ -70,7 +72,7 @@ namespace Microsoft.Singularity.V1.Services } int alen = argstring.Length; //argstring.CopyTo(0, argVector, offset, alen); - for (int i=0; i < alen; i++){ + for (int i = 0; i < alen; i++) { argVector[offset+i] = argstring[i]; } offset += alen; @@ -88,6 +90,13 @@ namespace Microsoft.Singularity.V1.Services Thread.CurrentProcess.Stop(exitCode); } + [ExternalEntryPoint] + public static int GetCpuCount() + { + return Processor.GetRunningProcessorCount(); + } + + [ExternalEntryPoint] public static TimeSpan GetUpTime() { @@ -118,7 +127,13 @@ namespace Microsoft.Singularity.V1.Services [ExternalEntryPoint] public static long GetContextSwitchCount() { - return Processor.CurrentProcessor.NumContextSwitches; + int processors = Processor.CpuCount; + long contextSwitches = 0; + for (int processor = 0; processor < processors; processor++) { + Processor p = Processor.GetProcessor(processor); + contextSwitches += p.NumContextSwitches; + } + return contextSwitches; } [ExternalEntryPoint] @@ -150,7 +165,13 @@ namespace Microsoft.Singularity.V1.Services [ExternalEntryPoint] public static long GetKernelInterruptCount() { - return Processor.CurrentProcessor.NumInterrupts; + int processors = Processor.CpuCount; + long interrupts = 0; + for (int processor = 0; processor < processors; processor++) { + Processor p = Processor.GetProcessor(processor); + interrupts += p.NumInterrupts; + } + return interrupts; } [ExternalEntryPoint] @@ -159,13 +180,13 @@ namespace Microsoft.Singularity.V1.Services return (ushort)Thread.CurrentProcess.ProcessId; } - /* - [ExternalEntryPoint] - public static PrincipalHandle GetCurrentPrincipal() - { - return new PrincipalHandle(Thread.CurrentProcess.PrincipalId.val); - } - */ + // + //[ExternalEntryPoint] + //public static PrincipalHandle GetCurrentPrincipal() + //{ + // return new PrincipalHandle(Thread.CurrentProcess.PrincipalId.val); + //} + // [ExternalEntryPoint] // Return parameter is really: DirectoryService.Imp opt(ExHeap) * @@ -234,48 +255,21 @@ namespace Microsoft.Singularity.V1.Services } [ExternalEntryPoint] - public static unsafe void GetTracingHeadersImpl( - LogEntry **logBegin, - LogEntry **logLimit, - LogEntry ***logHead, - byte **txtBegin, - byte **txtLimit, - byte ***txtHead) + public static unsafe bool GetSharedSourceHandlesImpl(uint infoId, + UIntPtr * storageHandle, + UIntPtr * sourceHandle, + UIntPtr * eventTypeHandle) { - GetTracingHeaders(out *logBegin, out *logLimit, out *logHead, - out *txtBegin, out *txtLimit, out *txtHead); - } - - [AccessedByRuntime("referenced from Tracing.cpp")] - public static unsafe void GetTracingHeaders(out LogEntry *logBegin, - out LogEntry *logLimit, - out LogEntry **logHead, - out byte *txtBegin, - out byte *txtLimit, - out byte **txtHead) - { - Tracing.LogEntry *_logBegin; - Tracing.LogEntry *_logLimit; - Tracing.LogEntry **_logHead; - - Tracing.GetTracingHeaders(out _logBegin, out _logLimit, out _logHead, - out txtBegin, out txtLimit, out txtHead); - - logBegin = (LogEntry *)_logBegin; - logLimit = (LogEntry *)_logLimit; - logHead = (LogEntry **)_logHead; - } - - [ExternalEntryPoint] - public static unsafe void GetMonitoringHeadersImpl(byte * * _buffer) - { - GetMonitoringHeaders(out *_buffer); + return GetSharedSourceHandles(infoId, out *storageHandle, out *sourceHandle, out *eventTypeHandle); } [AccessedByRuntime("referenced from Monitoring.cpp")] - public static unsafe void GetMonitoringHeaders(out byte * _buffer) + public static unsafe bool GetSharedSourceHandles(uint infoId, + out UIntPtr storageHandle, + out UIntPtr sourceHandle, + out UIntPtr eventTypeHandle) { - Monitoring.GetMonitoringHeaders(out _buffer); + return Controller.GetSharedSourceHandles(infoId, out storageHandle, out sourceHandle, out eventTypeHandle); } @@ -406,128 +400,5 @@ namespace Microsoft.Singularity.V1.Services } - // haryadi -- interface to run ping pong from app - [ExternalEntryPoint] - public static int RunPingPongInt(int start) - { - return Processor.RunPingPongInt(start); - } - - // haryadi - // Note: [NoHeapAllocation] is bad .. Since HelloProcessABI - // is called from Processor.DispatchSpecificInterrupt(), then - // every ABI should be annotated with [NoHeapAllocation] - // Probably, in the future when we already use the scheduler, - // instead of direct invocation during interrupt context, we - // don't need [NoHeapAllocation] - [ExternalEntryPoint] - [ NoHeapAllocation ] - public static int HelloProcessABI(int num, int num2) - { - DebugStub.WriteLine - ("HSG: ** cpu.{0} HelloProcessABI({1:x8},{2:x8})", - __arglist(Processor.GetCurrentProcessorId(), - num, num2)); - // return the power of 2 of the value - return num+num2; - } - - - - [ExternalEntryPoint] - [ NoHeapAllocation ] - public static unsafe ulong TestAbiCallOne(ulong a) { - DebugStub.WriteLine("HSG: ** TestAbiCallOne({0:x16})}", - __arglist(a)); - return 18000000000000000000 + a; // 64 bit - } - - - [ExternalEntryPoint] - [ NoHeapAllocation ] - public static unsafe int TestAbiCallTwo(uint a, char *b) - { - DebugStub.WriteLine("HSG: ** TestAbiCallTwo({0:x8}, {1:x8})}", - __arglist((int)(a), (int)(b))); - return ((int)(b) + (int)a); - } - - [ExternalEntryPoint] - [ NoHeapAllocation ] - public static unsafe char* TestAbiCallThree(int a, int *b, byte c) - { - DebugStub.WriteLine - ("HSG: ** TestAbiCallOne({0:x8}, {1:x8}, {2:x8})}", - __arglist((int)(a), (int)(b), (int)(c))); - - return (char*)((int)b + a + (int)c); - } - - - -#if FALSE - [ExternalEntryPoint] - public static int StubHelloProcessABI2(int num) - { - - int ap = Processor.GetCurrentProcessorId(); - int bsp = 0; - - DebugStub.WriteLine - ("\n\nHSG: ** cpu.{0} ProcessService.StubHelloProcessABI({1})", - __arglist(ap, num)); - DebugStub.WriteLine("HSG: ** -----------------------------------"); - - bool iflag = Processor.DisableInterrupts(); - - MpExecution.AbiCall abiCall = new MpExecution.AbiCall(); - - // 1) set up all the parameters - abiCall.argVal = num; - - // prepare IPI - DebugStub.Print - ("HSG: ** cpu.{0} PutAbiCall(cpu.{1}, arg.{2}) --> ", - __arglist(ap, bsp, abiCall.argVal)); - - // 2) register the abiCall, - // after this, we get the position - int pos = MpExecution.PutAbiCall(bsp, abiCall); - - DebugStub.WriteLine("pos.{0}", __arglist(pos)); - - DebugStub.WriteLine - ("HSG: ** cpu.{0} SendAbiCall(cpu.{1}, cpu.{2})", - __arglist(ap, ap, bsp)); - - // 3) send - MpExecution.SendAbiCall(ap, bsp); - - DebugStub.WriteLine - ("HSG: ** cpu.{0} WaitAbiCall(cpu.{1}, pos.{2}) ... zzz ... zzz ... ", - __arglist(ap, bsp, pos)); - - // 4) spin until done - MpExecution.WaitAbiCall(bsp, pos, out abiCall); - - // 5) we have the return value - int retval = abiCall.retVal; - - DebugStub.WriteLine - ("HSG: ** cpu.{0} is waken up and receives retval.{1}", - __arglist(ap, retval)); - - DebugStub.WriteLine - ("HSG: ** cpu.{0} ReleaseAbiCall(cpu.{1}, pos.{2})", - __arglist(ap, bsp, pos)); - - // 6) release abiCall - MpExecution.ReleaseAbiCall(bsp, pos); - - Processor.RestoreInterrupts(iflag); - - return retval; - } -#endif } } diff --git a/base/Kernel/Singularity/V1/Services/StackService.cs b/base/Kernel/Singularity/V1/Services/StackService.cs index 2f0dbdc..5bd1457 100644 --- a/base/Kernel/Singularity/V1/Services/StackService.cs +++ b/base/Kernel/Singularity/V1/Services/StackService.cs @@ -12,15 +12,17 @@ using System; using System.Runtime.CompilerServices; using Microsoft.Singularity.Memory; +using Microsoft.Singularity.Isal; namespace Microsoft.Singularity.V1.Services { + [AccessedByRuntime("referenced from halstack.asm")] public struct StackService { [ExternalEntryPoint] public static void WalkStack() { - Stacks.WalkStack(Processor.GetFramePointer()); + Stacks.WalkStack(Isa.GetFramePointer()); } [ExternalEntryPoint] @@ -31,53 +33,93 @@ namespace Microsoft.Singularity.V1.Services GetUsageStatistics(out *gets, out *returns); } + // The ABI service MemoryUsageInfo provides more comprehensive information [NoHeapAllocation] [CLSCompliant(false)] public static void GetUsageStatistics(out ulong gets, out ulong returns) { - gets = (ulong)Stacks.GetCount; - returns = (ulong)Stacks.ReturnCount; - } + ulong kernelStackCount = 0; + ulong kernelStackReturnCount = 0; + ulong kernelStackBytes = 0; + ulong kernelStackReturnBytes = 0; + ulong kernelStackReservationLocal = 0; + ulong SIPStackCount = 0; + ulong SIPStackReturnCount = 0; + ulong SIPStackBytes = 0; + ulong SIPStackReturnBytes = 0; + ulong SIPStackReservation = 0; - [AccessedByRuntime("referenced from halstack.asm")] - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void LinkSharedStack(); + MemoryManager.GetStackUsage( + out kernelStackCount, + out kernelStackReturnCount, + out kernelStackBytes, + out kernelStackReturnBytes, + out kernelStackReservationLocal, + out SIPStackCount, + out SIPStackReturnCount, + out SIPStackBytes, + out SIPStackReturnBytes, + out SIPStackReservation + ); - [AccessedByRuntime("referenced from halstack.asm")] - [StackBound(64)] - [NoStackLinkCheck] - [NoStackOverflowCheck] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void UnlinkSharedStack(); - - [ExternalEntryPoint] - [CLSCompliant(false)] - public static unsafe UIntPtr LinkNewStackSegment( - UIntPtr size, - uint *arg2, - uint args, - UIntPtr esp, - UIntPtr begin, - UIntPtr limit) - { - Microsoft.Singularity.X86.ThreadContext* context = Processor.GetCurrentThreadContext(); - return Microsoft.Singularity.Memory.Stacks.GetStackSegmentAndCopy( - size, ref *context, arg2, args, esp, begin, limit); + // Just summary information + gets = kernelStackCount + SIPStackCount; + returns = kernelStackReturnCount + SIPStackReturnCount; } [ExternalEntryPoint] [CLSCompliant(false)] - //[NoHeapAllocation] - public static unsafe void ReturnStackSegmentRaw( - UIntPtr begin, - UIntPtr limit) + [NoStackLinkCheckTrans] + public static UIntPtr AllocateStackSegmentAbi(UIntPtr growSize) { - Microsoft.Singularity.X86.ThreadContext* context = Processor.GetCurrentThreadContext(); - Microsoft.Singularity.Memory.Stacks.ReturnStackSegmentRaw( - ref *context, begin, limit); + return Stacks.GetSipStackSegment(growSize); + } + + [ExternalEntryPoint] + [CLSCompliant(false)] + [NoStackLinkCheckTrans] + [NoStackOverflowCheck] + public static void FreeStackSegmentAbi() + { + // First, set threadRecord->activeStackLimit to the proper + // value for the previous (now current) stack segment. This + // ensures that subsequent stack checks are done using the + // correct stack segment limit. + Stacks.ActivatePreviousStackSegmentLimit(); + Stacks.ReturnSipStackSegment(); + } + + [CLSCompliant(false)] + [AccessedByRuntime("called from halstack.asm")] + [NoStackLinkCheckTrans] + public static UIntPtr AllocateStackSegment(UIntPtr growSize) + { + return Stacks.GetKernelStackSegment(growSize); + } + + [CLSCompliant(false)] + [AccessedByRuntime("called from halstack.asm")] + [NoStackLinkCheckTrans] + [NoStackOverflowCheck] + public static void FreeStackSegment() + { + // First, set threadRecord->activeStackLimit to the proper + // value for the previous (now current) stack segment. This + // ensures that subsequent stack checks are done using the + // correct stack segment limit. + Stacks.ActivatePreviousStackSegmentLimit(); + Stacks.ReturnKernelStackSegment(); + } + + [ExternalEntryPoint] + public static void StackOverflowImpl() + { + // + // This is called when a SIP has a stack overflow so + // that it can fail fast. + // + + Stacks.StackOverflowForSIP(); } } } diff --git a/base/Kernel/Singularity/V1/Threads/InterruptHandle.cs b/base/Kernel/Singularity/V1/Threads/InterruptHandle.cs index cff3175..237de85 100644 --- a/base/Kernel/Singularity/V1/Threads/InterruptHandle.cs +++ b/base/Kernel/Singularity/V1/Threads/InterruptHandle.cs @@ -11,6 +11,7 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading; using Microsoft.Singularity; using Microsoft.Singularity.Io; @@ -22,8 +23,8 @@ namespace Microsoft.Singularity.V1.Threads [CLSCompliant(false)] public struct InterruptHandle // : public SyncHandle { - public readonly UIntPtr id; // could be moved to SyncHandle + public readonly UIntPtr id; // could be moved to SyncHandle public static readonly InterruptHandle Zero = new InterruptHandle(); internal InterruptHandle(UIntPtr id) @@ -75,8 +76,7 @@ namespace Microsoft.Singularity.V1.Threads handle.id); bool ret = false; - if (handle.id != UIntPtr.Zero) - { + if (handle.id != UIntPtr.Zero) { // // Releasing the handle will allow the IoIrq event to be // garbage-collected. @@ -91,8 +91,7 @@ namespace Microsoft.Singularity.V1.Threads [ExternalEntryPoint] public static bool Wait(InterruptHandle handle) { - if (handle.id == UIntPtr.Zero) - { + if (handle.id == UIntPtr.Zero) { Tracing.Log(Tracing.Error, "InterruptHandle.Wait(id={0:x8}) on bad handle", handle.id); @@ -113,8 +112,7 @@ namespace Microsoft.Singularity.V1.Threads [ExternalEntryPoint] public static void Pulse(InterruptHandle handle) { - if (handle.id == UIntPtr.Zero) - { + if (handle.id == UIntPtr.Zero) { Tracing.Log(Tracing.Error, "InterruptHandle.Wait(id={0:x8}) on bad handle", handle.id); @@ -129,8 +127,7 @@ namespace Microsoft.Singularity.V1.Threads [ExternalEntryPoint] public static bool Ack(InterruptHandle handle) { - if (handle.id == UIntPtr.Zero) - { + if (handle.id == UIntPtr.Zero) { Tracing.Log(Tracing.Error, "InterruptHandle.Ack(id={0:x8}) on bad handle", handle.id); @@ -146,5 +143,6 @@ namespace Microsoft.Singularity.V1.Threads Tracing.Log(Tracing.Debug, "InterruptHandle.Ack(id={0:x8})", handle.id); return ret; } + } } diff --git a/base/Kernel/Singularity/V1/Threads/MutexHandle.cs b/base/Kernel/Singularity/V1/Threads/MutexHandle.cs index 16ffa19..98b155b 100644 --- a/base/Kernel/Singularity/V1/Threads/MutexHandle.cs +++ b/base/Kernel/Singularity/V1/Threads/MutexHandle.cs @@ -45,8 +45,11 @@ namespace Microsoft.Singularity.V1.Threads // Create a new mutex, and a handle in the current process // to hold it. // + // Note: the mutex is created as a non-kernel object so that + // SIP threads owning mutex can be forcibly stopped + // handle = new MutexHandle(Thread.CurrentProcess.AllocateHandle( - new Mutex(initiallyOwned))); + new Mutex(initiallyOwned, false))); Tracing.Log(Tracing.Debug, "MutexHandle.Create(, out id={0:x8})", handle.id); diff --git a/base/Kernel/Singularity/V1/Threads/SyncHandle.cs b/base/Kernel/Singularity/V1/Threads/SyncHandle.cs index d6d0c19..69e0c26 100644 --- a/base/Kernel/Singularity/V1/Threads/SyncHandle.cs +++ b/base/Kernel/Singularity/V1/Threads/SyncHandle.cs @@ -48,6 +48,18 @@ namespace Microsoft.Singularity.V1.Threads this.id = id; } +#if false + /// REVIEW: generalizes waiting across events and endpoints + /// should review to ensure this is safe + public WaitHandle GetWaitHandle() { + return HandleTable.GetHandle(id) as WaitHandle; + } + + public static implicit operator WaitHandle(SyncHandle s) { + return HandleTable.GetHandle(s.id) as WaitHandle; + } +#endif + ////////////////////////////////////////////////////////////////////// // // The following methods could be moved to WaitHandle if we had diff --git a/base/Kernel/Singularity/V1/Threads/ThreadHandle.cs b/base/Kernel/Singularity/V1/Threads/ThreadHandle.cs index 95e6b8e..5c3096f 100644 --- a/base/Kernel/Singularity/V1/Threads/ThreadHandle.cs +++ b/base/Kernel/Singularity/V1/Threads/ThreadHandle.cs @@ -64,12 +64,31 @@ namespace Microsoft.Singularity.V1.Threads public static void Start(ThreadHandle handle) { Thread thread = HandleTable.GetHandle(handle.id) as Thread; - Tracing.Log(Tracing.Debug, "ThreadHandle.Start(id={0:x8})", handle.id); thread.Start(); } + [ExternalEntryPoint] + public static int GetAffinity(ThreadHandle handle) + { + int affinity = Processor.GetCurrentProcessorId(); + Tracing.Log(Tracing.Debug, "ThreadHandle.GetAffinity(id={0:x8}, out affinity={1})", + handle.id, (UIntPtr)unchecked(affinity)); + return affinity; + } + + [ExternalEntryPoint] + public static void SetAffinity(ThreadHandle handle, int affinity) + { + + Tracing.Log(Tracing.Debug, "ThreadHandle.SetAffinity(id={0:x8}, affinity={1})", + handle.id, (UIntPtr)unchecked(affinity)); + + Thread thread = HandleTable.GetHandle(handle.id) as Thread; + thread.SetAffinity(affinity); + } + [ExternalEntryPoint] public static ThreadState GetThreadState(ThreadHandle handle) { diff --git a/base/Kernel/Singularity/V1/Threads/ThreadState.cs b/base/Kernel/Singularity/V1/Threads/ThreadState.cs index afa3ef8..bb2bf7c 100644 --- a/base/Kernel/Singularity/V1/Threads/ThreadState.cs +++ b/base/Kernel/Singularity/V1/Threads/ThreadState.cs @@ -18,10 +18,12 @@ namespace Microsoft.Singularity.V1.Threads [Flags] public enum ThreadState { - Unstarted = 0x00, - Running = 0x01, - Blocked = 0x02, - Suspended = 0x04, - Stopped = 0x08, + Undefined = 0x0, + Running = 0x1, + Unstarted = 0x2, + Stopped = 0x4, + Suspended = 0x8, + Blocked = 0x10, + Runnable = 0x20 } } diff --git a/base/Kernel/Singularity/V1/Types/SystemType.cs b/base/Kernel/Singularity/V1/Types/SystemType.cs index f73b467..146b051 100644 --- a/base/Kernel/Singularity/V1/Types/SystemType.cs +++ b/base/Kernel/Singularity/V1/Types/SystemType.cs @@ -47,7 +47,7 @@ namespace Microsoft.Singularity.V1.Types string sname = new String(name, 0, nameLength); return Register(sname, lowerHash, upperHash, parent); } - + /// Called from Binder which lives in a separate dll. public static SystemType Register(string name, long lowerHash, @@ -55,21 +55,22 @@ namespace Microsoft.Singularity.V1.Types SystemType parent) { RuntimeSystemType parentrts = HandleTable.GetHandle(parent.id) as RuntimeSystemType; - /* - DebugStub.WriteLine("SystemType.Register '{0}' hash0={1:x8} hash1={2:x8}", - __arglist(name, lowerHash, upperHash)); - */ + +#if false + DebugStub.WriteLine("SystemType.Register '{0}' hash0={1:x8} hash1={2:x8} Parent {3}", + __arglist(name, lowerHash, upperHash, parent.TypeId)); + Tracing.Log(Tracing.Debug, "Type '{0}' (parent={1:x8})", name, parent.id); Tracing.Log(Tracing.Debug, "hash0={0:x8} hash1={1:x8}", lowerHash.ToString(), upperHash.ToString()); +#endif // false UIntPtr childHandle = parentrts.LookupChildHandle(name, lowerHash, upperHash); - /* - DebugStub.WriteLine("SystemType.Register result {0:x8}", - __arglist(childHandle)); - */ + +#if false Tracing.Log(Tracing.Debug, "result UIntPtr = {0:x8}", childHandle); +#endif // false SystemType ret = new SystemType(childHandle); return ret; @@ -82,24 +83,43 @@ namespace Microsoft.Singularity.V1.Types return IsSubtype(child.id, parent.id); } - private static bool IsSubtype(UIntPtr childHandle, UIntPtr parentHandle) + private static bool IsSubtype(UIntPtr childHandle, UIntPtr parentHandle) { - if (childHandle == UIntPtr.Zero) return false; + if (childHandle == UIntPtr.Zero) { + return false; + } if (parentHandle == UIntPtr.Zero) return false; RuntimeSystemType childrts = HandleTable.GetHandle(childHandle) as RuntimeSystemType; + if(childrts == null) { + DebugStub.Print("Ack! Runtime: IsSubType is null!\n"); + return false; + } + bool ret = childrts.IsSubtype(parentHandle); if (ret) { +#if false Tracing.Log(Tracing.Debug, "SystemType.IsSubtype(parent={0:x8}, child={1:x8}) = [true]", parentHandle, childHandle); + + DebugStub.WriteLine("SystemType.IsSubtype(parent={0:x8}, child={1:x8}) = [true]", + __arglist(parentHandle, childHandle) + ); +#endif } else { +#if false Tracing.Log(Tracing.Debug, "SystemType.IsSubtype(parent={0:x8}, child={1:x8}) = [false]", parentHandle, childHandle); + + DebugStub.WriteLine("SystemType.IsSubtype(parent={0:x8}, child={1:x8}) = [false]", + __arglist(parentHandle, childHandle) + ); +#endif } return ret; } @@ -107,7 +127,7 @@ namespace Microsoft.Singularity.V1.Types [ExternalEntryPoint] [CLSCompliant(false)] unsafe public static bool IsSubtype(SharedHeapService.Allocation* childData, - SystemType parent) + SystemType parent) { UIntPtr childHandle = SharedHeapService.GetType(childData); return IsSubtype(childHandle, parent.id); @@ -115,7 +135,7 @@ namespace Microsoft.Singularity.V1.Types [CLSCompliant(false)] unsafe public static bool IsSubtype(SharedHeap.Allocation* childData, - SystemType parent) + SystemType parent) { UIntPtr childHandle = SharedHeap.Allocation.GetType(childData); return IsSubtype(childHandle, parent.id); diff --git a/base/Kernel/Singularity/X86/EVectors.cs b/base/Kernel/Singularity/X86/EVectors.cs deleted file mode 100644 index bdce1f2..0000000 --- a/base/Kernel/Singularity/X86/EVectors.cs +++ /dev/null @@ -1,91 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: EVectors.cs -// -// Note: -// - -namespace Microsoft.Singularity.X86 -{ - using System; - using System.Runtime.InteropServices; - using System.Runtime.CompilerServices; - - [CLSCompliant(false)] - [AccessedByRuntime("referenced from C++")] - internal struct EVectors - { - // Interrupt Vector Assignments - // 0..31 Intel defined - [AccessedByRuntime("referenced from C++")] - internal const uint DivideError = 0; - [AccessedByRuntime("referenced from C++")] - internal const uint SingleStep = 1; - [AccessedByRuntime("referenced from C++")] - internal const uint Nmi = 2; - [AccessedByRuntime("referenced from C++")] - internal const uint Breakpoint = 3; - [AccessedByRuntime("referenced from C++")] - internal const uint OverflowException = 4; - [AccessedByRuntime("referenced from C++")] - internal const uint BoundRangeException = 5; - [AccessedByRuntime("referenced from C++")] - internal const uint IllegalInstruction = 6; - [AccessedByRuntime("referenced from C++")] - internal const uint CoprocessorNotAvailable = 7; - [AccessedByRuntime("referenced from C++")] - internal const uint DoubleFault = 8; - [AccessedByRuntime("referenced from C++")] - internal const uint CoprocessorSegmentOverrun = 9; - [AccessedByRuntime("referenced from C++")] - internal const uint InvalidTss = 10; - [AccessedByRuntime("referenced from C++")] - internal const uint SegmentNotPresent = 11; - [AccessedByRuntime("referenced from C++")] - internal const uint StackSegmentFault = 12; - [AccessedByRuntime("referenced from C++")] - internal const uint GeneralProtectionFault = 13; - [AccessedByRuntime("referenced from C++")] - internal const uint PageFault = 14; - [AccessedByRuntime("referenced from C++")] - internal const uint IntelReserved = 14; - [AccessedByRuntime("referenced from C++")] - internal const uint FpuMathFault = 16; - [AccessedByRuntime("referenced from C++")] - internal const uint AlignmentCheck = 17; - [AccessedByRuntime("referenced from C++")] - internal const uint MachineCheck = 18; - [AccessedByRuntime("referenced from C++")] - internal const uint SseMathFault = 19; - - // Reserved, but used by Singularity - [AccessedByRuntime("referenced from C++")] - internal const uint FirstChanceException = 29; - [AccessedByRuntime("referenced from C++")] - internal const uint SecondChanceException = 30; - [AccessedByRuntime("referenced from C++")] - internal const uint DebuggerBreakRequest = 31; - - // 32..255 User defined - [AccessedByRuntime("referenced from C++")] - internal const uint BaseUserException = 32; - - // haryadi defined - [AccessedByRuntime("referenced from C++")] - internal const uint PingPongInt = 33; - [AccessedByRuntime("referenced from C++")] - internal const uint ApImage = 34; - - // haryadi: currently, this is 1 entry for 1 ABI. - // In the future, 1 entry should represents the whole ABI?? - [AccessedByRuntime("referenced from C++")] - internal const uint AbiCall = 35; - - [AccessedByRuntime("referenced from C++")] - internal const uint HaltApProcessors = 36; - } -} diff --git a/base/Kernel/Singularity/X86/Gdt.cs b/base/Kernel/Singularity/X86/Gdt.cs deleted file mode 100644 index d682c62..0000000 --- a/base/Kernel/Singularity/X86/Gdt.cs +++ /dev/null @@ -1,122 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Gdt.cs -// -// Note: -// - -namespace Microsoft.Singularity.X86 -{ - using System; - using System.Runtime.InteropServices; - using System.Runtime.CompilerServices; - - // A lgdt pointer to the tables - // - [CLSCompliant(false)] - [AccessedByRuntime("referenced from c++")] - internal struct GDTP - { - [AccessedByRuntime("referenced from c++")] - internal ushort pad; - [AccessedByRuntime("referenced from c++")] - internal ushort limit; - [AccessedByRuntime("referenced from c++")] - internal uint addr; - }; - - /////////////////////////////////////////////// Segment Descriptor Tables. - // - // An entry in the Global Descriptor Table - // - [AccessedByRuntime("referenced from c++")] - [CLSCompliant(false)] - internal struct GDTE - { - [AccessedByRuntime("referenced from c++")] - internal ushort limit; - [AccessedByRuntime("referenced from c++")] - internal ushort base0_15; - [AccessedByRuntime("referenced from c++")] - internal byte base16_23; - [AccessedByRuntime("referenced from c++")] - internal byte access; - [AccessedByRuntime("referenced from c++")] - internal byte granularity; - [AccessedByRuntime("referenced from c++")] - internal byte base24_31; - - // granularity bits - [AccessedByRuntime("referenced from c++")] - internal const uint PAGES = 0x80; - [AccessedByRuntime("referenced from c++")] - internal const uint IS32BIT = 0x40; - [AccessedByRuntime("referenced from c++")] - internal const uint IS64BIT = 0x20; - [AccessedByRuntime("referenced from c++")] - internal const uint LIMIT20 = 0x0f; - - // access bits - [AccessedByRuntime("referenced from c++")] - internal const uint PRESENT = 0x80; - [AccessedByRuntime("referenced from c++")] - internal const uint RING3 = 0x60; - [AccessedByRuntime("referenced from c++")] - internal const uint RING2 = 0x40; - [AccessedByRuntime("referenced from c++")] - internal const uint RING1 = 0x20; - [AccessedByRuntime("referenced from c++")] - internal const uint RING0 = 0x00; - [AccessedByRuntime("referenced from c++")] - internal const uint USER = 0x10; - [AccessedByRuntime("referenced from c++")] - internal const uint CODE = 0x08; - [AccessedByRuntime("referenced from c++")] - internal const uint CONFORMING = 0x04; - [AccessedByRuntime("referenced from c++")] - internal const uint EXPANDDOWN = 0x04; // Data - [AccessedByRuntime("referenced from c++")] - internal const uint READABLE = 0x02; - [AccessedByRuntime("referenced from c++")] - internal const uint WRITEABLE = 0x02; // Data - [AccessedByRuntime("referenced from c++")] - internal const uint ACCESSED = 0x01; - - // Non-user types: - [AccessedByRuntime("referenced from c++")] - internal const uint Tss16Free = 0x01; - [AccessedByRuntime("referenced from c++")] - internal const uint Ldt = 0x02; - [AccessedByRuntime("referenced from c++")] - internal const uint Tss16Busy = 0x03; - [AccessedByRuntime("referenced from c++")] - internal const uint CallGate16 = 0x04; - [AccessedByRuntime("referenced from c++")] - internal const uint TaskGate = 0x05; - [AccessedByRuntime("referenced from c++")] - internal const uint IntGate16 = 0x06; - [AccessedByRuntime("referenced from c++")] - internal const uint TrapGate16 = 0x07; - [AccessedByRuntime("referenced from c++")] - internal const uint Reserved8 = 0x08; - [AccessedByRuntime("referenced from c++")] - internal const uint Tss32Free = 0x09; - [AccessedByRuntime("referenced from c++")] - internal const uint Reserved10 = 0x0a; - [AccessedByRuntime("referenced from c++")] - internal const uint Tss32Busy = 0x0b; - [AccessedByRuntime("referenced from c++")] - internal const uint CallGate32 = 0x0c; - [AccessedByRuntime("referenced from c++")] - internal const uint Reserved13 = 0x0d; - [AccessedByRuntime("referenced from c++")] - internal const uint IntGate32 = 0x0e; - [AccessedByRuntime("referenced from c++")] - internal const uint TrapGate32 = 0x0f; - } -} - diff --git a/base/Kernel/Singularity/X86/Idt.cs b/base/Kernel/Singularity/X86/Idt.cs deleted file mode 100644 index 9d4f628..0000000 --- a/base/Kernel/Singularity/X86/Idt.cs +++ /dev/null @@ -1,68 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Idt.cs -// -// Note: -// - -namespace Microsoft.Singularity.X86 -{ - using System; - using System.Runtime.InteropServices; - using System.Runtime.CompilerServices; - - // A lidt pointer to the tables - [CLSCompliant(false)] - [AccessedByRuntime("referenced from c++")] - internal struct IDTP - { - [AccessedByRuntime("referenced from c++")] - internal ushort pad; - [AccessedByRuntime("referenced from c++")] - internal ushort limit; - [AccessedByRuntime("referenced from c++")] - internal uint addr; - }; - - [CLSCompliant(false)] - [AccessedByRuntime("referenced from c++")] - internal struct IDTE - { - // An entry in the Interrupt Descriptor Table - [AccessedByRuntime("referenced from c++")] - internal ushort offset_0_15; - [AccessedByRuntime("referenced from c++")] - internal ushort selector; - [AccessedByRuntime("referenced from c++")] - internal byte zeros; - [AccessedByRuntime("referenced from c++")] - internal byte access; - [AccessedByRuntime("referenced from c++")] - internal ushort offset_16_31; - - ///////////////////////////////////////// Interrupt Descriptor Tables. - // - [AccessedByRuntime("referenced from c++")] - internal const uint PRESENT = 0x80; - [AccessedByRuntime("referenced from c++")] - internal const uint DPL_RING3 = 0x60; - [AccessedByRuntime("referenced from c++")] - internal const uint DPL_RING2 = 0x40; - [AccessedByRuntime("referenced from c++")] - internal const uint DPL_RING1 = 0x20; - [AccessedByRuntime("referenced from c++")] - internal const uint DPL_RING0 = 0x00; - [AccessedByRuntime("referenced from c++")] - internal const uint TASK_GATE = 0x05; - [AccessedByRuntime("referenced from c++")] - internal const uint CALL_GATE = 0x0c; - [AccessedByRuntime("referenced from c++")] - internal const uint INT_GATE = 0x0e; - [AccessedByRuntime("referenced from c++")] - internal const uint TRAP_GATE = 0x0f; - } -} diff --git a/base/Kernel/Singularity/X86/MmxContext.cs b/base/Kernel/Singularity/X86/MmxContext.cs deleted file mode 100644 index 9afddd5..0000000 --- a/base/Kernel/Singularity/X86/MmxContext.cs +++ /dev/null @@ -1,108 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: MmxContext.cs -// -// Note: -// - -namespace Microsoft.Singularity.X86 -{ - using System; - using System.Runtime.InteropServices; - using System.Runtime.CompilerServices; - - [CLSCompliant(false)] - [StructLayout(LayoutKind.Sequential)] - [StructAlign(16)] - //[AccessedByRuntime("")] - internal struct UINT128 - { - //[AccessedByRuntime("")] - public ulong lo; - //[AccessedByRuntime("")] - public ulong hi; - } - - [CLSCompliant(false)] - [StructLayout(LayoutKind.Sequential)] - [StructAlign(16)] - [AccessedByRuntime("referenced from c++")] - internal struct MmxContext - { - [AccessedByRuntime("referenced from c++")] - public ushort fcw; - [AccessedByRuntime("referenced from c++")] - public ushort fsw; - [AccessedByRuntime("referenced from c++")] - public ushort ftw; - [AccessedByRuntime("referenced from c++")] - public ushort fop; - [AccessedByRuntime("referenced from c++")] - public uint eip; - [AccessedByRuntime("referenced from c++")] - public uint cs; - - [AccessedByRuntime("referenced from c++")] - public uint dp; - [AccessedByRuntime("referenced from c++")] - public uint ds; - [AccessedByRuntime("referenced from c++")] - public uint mxcsr; - [AccessedByRuntime("referenced from c++")] - public uint mxcsrmask; - - [AccessedByRuntime("referenced from c++")] - public UINT128 st0; - [AccessedByRuntime("referenced from c++")] - public UINT128 st1; - [AccessedByRuntime("referenced from c++")] - public UINT128 st2; - [AccessedByRuntime("referenced from c++")] - public UINT128 st3; - [AccessedByRuntime("referenced from c++")] - public UINT128 st4; - [AccessedByRuntime("referenced from c++")] - public UINT128 st5; - [AccessedByRuntime("referenced from c++")] - public UINT128 st6; - [AccessedByRuntime("referenced from c++")] - public UINT128 st7; - - [AccessedByRuntime("referenced from c++")] - public UINT128 xmm0; - [AccessedByRuntime("referenced from c++")] - public UINT128 xmm1; - [AccessedByRuntime("referenced from c++")] - public UINT128 xmm2; - [AccessedByRuntime("referenced from c++")] - public UINT128 xmm3; - [AccessedByRuntime("referenced from c++")] - public UINT128 xmm4; - [AccessedByRuntime("referenced from c++")] - public UINT128 xmm5; - [AccessedByRuntime("referenced from c++")] - public UINT128 xmm6; - [AccessedByRuntime("referenced from c++")] - public UINT128 xmm7; - - public UINT128 reserved2; - public UINT128 reserved3; - public UINT128 reserved4; - public UINT128 reserved5; - public UINT128 reserved6; - public UINT128 reserved7; - public UINT128 reserved8; - public UINT128 reserved9; - public UINT128 reservedA; - public UINT128 reservedB; - public UINT128 reservedC; - public UINT128 reservedD; - public UINT128 reservedE; - public UINT128 reservedF; - } -} - diff --git a/base/Kernel/Singularity/X86/ProcessorContext.cs b/base/Kernel/Singularity/X86/ProcessorContext.cs deleted file mode 100644 index 86181c5..0000000 --- a/base/Kernel/Singularity/X86/ProcessorContext.cs +++ /dev/null @@ -1,72 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: ProcessorContext.cs -// -// Note: -// - -namespace Microsoft.Singularity.X86 -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Threading; - - using Microsoft.Singularity.X86; - - [NoCCtor] - [CLSCompliant(false)] - [StructLayout(LayoutKind.Sequential)] - internal struct ProcessorContext - { - [AccessedByRuntime("referenced from c++")] private UIntPtr Reserved0; - - // The remaining fields are private to the kernel. - [AccessedByRuntime("referenced from c++")] internal unsafe ThreadContext *threadContext; - [AccessedByRuntime("referenced from c++")] internal unsafe ProcessorContext *processorContext; - [AccessedByRuntime("referenced from c++")] private unsafe Processor *_processor; // Only changed by garbage collector. - - [AccessedByRuntime("referenced from c++")] internal UIntPtr exception; - - [AccessedByRuntime("referenced from c++")] internal UIntPtr schedulerStackBegin; - [AccessedByRuntime("referenced from c++")] internal UIntPtr schedulerStackLimit; - [AccessedByRuntime("referenced from c++")] internal UIntPtr interruptStackBegin; - [AccessedByRuntime("referenced from c++")] internal UIntPtr interruptStackLimit; - [AccessedByRuntime("referenced from c++")] internal UIntPtr interruptStackPreLimit; - [AccessedByRuntime("referenced from c++")] internal UIntPtr exceptionStackBegin; - [AccessedByRuntime("referenced from c++")] internal UIntPtr exceptionStackLimit; - [AccessedByRuntime("referenced from c++")] internal UIntPtr exceptionStackPreLimit; - - [AccessedByRuntime("referenced from c++")] internal ThreadContext exceptionContext; - [AccessedByRuntime("referenced from c++")] internal ThreadContext thirdContext; - - [AccessedByRuntime("referenced from c++")] internal int cpuId; - [AccessedByRuntime("referenced from c++")] internal volatile int ipiFreeze; - [AccessedByRuntime("referenced from c++")] internal unsafe ProcessorContext* nextProcessorContext; // singly-linked circular list node for MpExecution use - - //////////////////////////////////////////////// Methods & Properties. - // - internal Processor processor { - [NoHeapAllocation] - get { return GetProcessor(); } - } - - //////////////////////////////////////////////////// External Methods. - // - [AccessedByRuntime("output to header: defined in c++")] - [MethodImpl(MethodImplOptions.InternalCall)] - [StackBound(32)] - [NoHeapAllocation] - private extern Processor GetProcessor(); - - [AccessedByRuntime("output to header: defined in c++")] - [MethodImpl(MethodImplOptions.InternalCall)] - [StackBound(32)] - [NoHeapAllocation] - internal extern void UpdateAfterGC(Processor processor); - } -} diff --git a/base/Kernel/Singularity/Xml/XmlException.cs b/base/Kernel/Singularity/Xml/XmlException.cs index 817c1fb..c2df536 100644 --- a/base/Kernel/Singularity/Xml/XmlException.cs +++ b/base/Kernel/Singularity/Xml/XmlException.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; diff --git a/base/Kernel/Singularity/Xml/XmlNode.cs b/base/Kernel/Singularity/Xml/XmlNode.cs index fe7e3cd..66b94b3 100644 --- a/base/Kernel/Singularity/Xml/XmlNode.cs +++ b/base/Kernel/Singularity/Xml/XmlNode.cs @@ -100,6 +100,20 @@ namespace Microsoft.Singularity.Xml children[childrenUsed++] = node; } + public string GetAttribute(string name) + { + return this[name]; + } + + public XmlNode CreateNode (string name) + { + return new XmlNode(name); + } + + public XmlNode[] ChildNodes + { + get { return this.Children ;} + } public XmlNode[] Children { get { @@ -131,7 +145,8 @@ namespace Microsoft.Singularity.Xml public string this[string attributeName] { - get { + get + { for (int i = 0; i < attributesUsed; i++) { if (attributes[i].name == attributeName) { return attributes[i].value; @@ -156,6 +171,28 @@ namespace Microsoft.Singularity.Xml attributes[attributesUsed++] = new XmlAttr(name, value); } + public void PrependAttribute(string name, string value) + { + if (attributes == null) { + attributes = new XmlAttr[defaultAttributesCapacity]; + } + else if (attributesUsed == attributes.Length) { + XmlAttr[] dest = new XmlAttr[attributesUsed * 2]; + for (int i = 0; i < attributesUsed; i++) { + dest[i+1] = attributes[i]; + } + attributes = dest; + } + else { + //shift everything up one + for (int i = attributesUsed; i > 0; i--) { + attributes[i] = attributes[i-1]; + } + } + attributes[0] = new XmlAttr(name, value); + attributesUsed++; + } + // // Safe access to attributes: // since the kernel is going to use this object, we should diff --git a/base/Kernel/Singularity/Xml/XmlReader.cs b/base/Kernel/Singularity/Xml/XmlReader.cs index b20f985..437fbbd 100644 --- a/base/Kernel/Singularity/Xml/XmlReader.cs +++ b/base/Kernel/Singularity/Xml/XmlReader.cs @@ -73,12 +73,16 @@ namespace Microsoft.Singularity.Xml public XmlReader(IoMemory mem) : this() { + if (mem == null) + throw new ArgumentNullException("mem"); stream = new KernelIoMemoryStream(mem); } public XmlReader(byte[] buffer) : this() { + if (buffer == null) + throw new ArgumentNullException("buffer"); stream = new KernelByteMemoryStream(buffer); } @@ -345,8 +349,7 @@ namespace Microsoft.Singularity.Xml buffer.Append(input, 0, start); start++; - for (; ; ) - { + for (;;) { // At this point, 'start' points to a named XML entity. // locate the entity name. int end = input.IndexOf(';', start); @@ -360,8 +363,7 @@ namespace Microsoft.Singularity.Xml string entity_name = input.Substring(start, name_length); string value; - switch (entity_name) - { + switch (entity_name) { case "amp": value = "&"; break; case "lt": value = "<"; break; case "gt": value = ">"; break; @@ -374,8 +376,7 @@ namespace Microsoft.Singularity.Xml // Are there any more entity references in this string? int next = input.IndexOf('&', end + 1); - if (next == -1) - { + if (next == -1) { // There are no more entity references in the string. // Append the rest of the string. buffer.Append(input, end + 1, input.Length - end - 1); @@ -558,13 +559,11 @@ namespace Microsoft.Singularity.Xml // "\\" means \ and "\"" means " bool translateNext = false; int read_or_eof = PeekCharacter(); - if (read_or_eof == -1) - { + if (read_or_eof == -1) { throw new XmlException(lineNumber, "Line " + lineNumber + ": Cannot start a string literal at EOF."); } char read = (char)read_or_eof; - if (read != '"') - { + if (read != '"') { throw new XmlException(lineNumber, "Line " + lineNumber + ": Cannot start a string literal with " + (char)read + ". You must use a '\"'"); } // drop the '"' on the floor @@ -572,7 +571,7 @@ namespace Microsoft.Singularity.Xml read = (char)ReadCharacter(); while (read != '"' || translateNext) { - if (!translateNext ) { + if (!translateNext) { if (read == '\\') { translateNext = true; } @@ -623,6 +622,8 @@ namespace Microsoft.Singularity.Xml public KernelIoMemoryStream(IoMemory buffer) { + if (buffer == null) + throw new ArgumentNullException("buffer"); this.buffer = buffer; position = 0; size = buffer.Length; @@ -646,6 +647,8 @@ namespace Microsoft.Singularity.Xml public KernelByteMemoryStream(byte[] buffer) { + if (buffer == null) + throw new ArgumentNullException("buffer"); this.buffer = buffer; position = 0; size = buffer.Length; diff --git a/base/Kernel/SpecSharp.Contracts/Corlib.Contracts.csproj b/base/Kernel/SpecSharp.Contracts/Corlib.Contracts.csproj deleted file mode 100644 index 5add198..0000000 --- a/base/Kernel/SpecSharp.Contracts/Corlib.Contracts.csproj +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - Corlib.Contracts - Library - true - ENDPOINT_STRUCT;SINGULARITY;PTR_SIZE_32;_NEW_CLASSLOADER;NODEFAULTLIB;NOOWNERSHIPCHECK - $(APPRUNTIMEDIR)\Corlib.dll - false - true - $(APPRUNTIMEDIR) - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/base/Kernel/SpecSharp.Contracts/Kernel.Contracts.csproj b/base/Kernel/SpecSharp.Contracts/Kernel.Contracts.csproj index 2d5f217..b1f202e 100644 --- a/base/Kernel/SpecSharp.Contracts/Kernel.Contracts.csproj +++ b/base/Kernel/SpecSharp.Contracts/Kernel.Contracts.csproj @@ -1,8 +1,6 @@ " + _innerException.ToString() + "\r\n" + " " + "Exception_EndOfInnerExceptionStack"; - } - - return message; - } - - [AccessedByRuntime("referenced from halasm.asm")] - internal static unsafe bool IsUnlinkStack(UIntPtr throwAddr) { - UIntPtr unlinkBegin; - UIntPtr unlinkLimit; - - fixed (byte *begin = &Microsoft.Singularity.Memory.Stacks.UnlinkStackBegin) { - unlinkBegin = (UIntPtr)begin; - } - fixed (byte *limit = &Microsoft.Singularity.Memory.Stacks.UnlinkStackLimit) { - unlinkLimit = (UIntPtr)limit; - } - - if(throwAddr >= unlinkBegin && throwAddr <= unlinkLimit) { - return true; - } else { - return false; - } - } - - // The following functions are for tryall support. - // This is the function actually called by the internal runtime - [NoLoggingForUndo] - [RequiredByBartok] - private Exception internalCloneForUndo() { - /* this function is not allowed to throw an exception. */ - try { - return cloneForUndo(); - } catch (Exception ex) { - return ex; - } - } - // This is the function that users can override - - // BUGBUG: provide a better default (probably deep copy). The - // current one does not deal with references well, and so, is - // not even a good shallow copy (it should at least null out the - // references). - virtual protected Exception cloneForUndo() { - return (Exception)this.MemberwiseClone(); - } - - [AccessedByRuntime("referenced from halexn.cpp")] - internal String _message; - - private Exception _innerException; - - [AccessedByRuntime("referenced from halexn.cpp")] - private UIntPtr _throwAddress; - - [AccessedByRuntime("referenced from halexn.cpp")] - private bool _notifiedDebugger; // True if debugger first chance already thrown. - - // - public string StackTrace - { - get { - return ""; - } - } - } -} diff --git a/base/Kernel/System/FormatException.cs b/base/Kernel/System/FormatException.cs deleted file mode 100644 index 7f8511a..0000000 --- a/base/Kernel/System/FormatException.cs +++ /dev/null @@ -1,36 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================ -** -** Class: FormatException -** -** -** Purpose: Exception to designate an illegal argument to Format. -** -** Date: February 10, 1998 -** -===========================================================*/ -namespace System { - - using System; - //| - public class FormatException : SystemException { - //| - public FormatException() - : base("Arg_FormatException") { - } - - //| - public FormatException(String message) - : base(message) { - } - - //| - public FormatException(String message, Exception innerException) - : base(message, innerException) { - } - } -} diff --git a/base/Kernel/System/IAsyncResult.cs b/base/Kernel/System/IAsyncResult.cs deleted file mode 100644 index 3b6ed39..0000000 --- a/base/Kernel/System/IAsyncResult.cs +++ /dev/null @@ -1,38 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================ -** -** Interface: IAsyncResult -** -** Purpose: Interface to encapsulate the results of an async -** operation -** -===========================================================*/ -namespace System { - - using System; - using System.Threading; - //| - [CLSCompliant(false)] - public interface IAsyncResult - { - //| - bool IsCompleted { get; } - - //| - WaitHandle AsyncWaitHandle { get; } - - - //| - Object AsyncState { get; } - - //| - bool CompletedSynchronously { get; } - - - } - -} diff --git a/base/Kernel/System/IndexOutOfRangeException.cs b/base/Kernel/System/IndexOutOfRangeException.cs deleted file mode 100644 index 5bf2f1a..0000000 --- a/base/Kernel/System/IndexOutOfRangeException.cs +++ /dev/null @@ -1,40 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================================= -** -** Class: IndexOutOfRangeException -** -** -** Purpose: Exception class for invalid array indices. -** -** Date: March 24, 1998 -** -=============================================================================*/ - -namespace System { - - using System; - using System.Runtime.CompilerServices; - - //| - [RequiredByBartok] - public sealed class IndexOutOfRangeException : SystemException { - //| - public IndexOutOfRangeException() - : base("Arg_IndexOutOfRangeException") { - } - - //| - public IndexOutOfRangeException(String message) - : base(message) { - } - - //| - public IndexOutOfRangeException(String message, Exception innerException) - : base(message, innerException) { - } - } -} diff --git a/base/Kernel/System/Internal.cs b/base/Kernel/System/Internal.cs deleted file mode 100644 index 7ac9747..0000000 --- a/base/Kernel/System/Internal.cs +++ /dev/null @@ -1,19 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================ -** -** This file exists to contain module-level custom attributes -** for the BCL. -** -** -** Date: March 9, 2000 -** -===========================================================*/ -using System.Runtime.InteropServices; - -[assembly:Guid("BED7F4EA-1A96-11d2-8F08-00A0C9A6186D")] -[assembly:System.CLSCompliantAttribute(true)] -[assembly:System.Reflection.AssemblyDescriptionAttribute("Common Language Runtime Library")] diff --git a/base/Kernel/System/InvalidCastException.cs b/base/Kernel/System/InvalidCastException.cs deleted file mode 100644 index fa8f9ad..0000000 --- a/base/Kernel/System/InvalidCastException.cs +++ /dev/null @@ -1,39 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================================= -** -** Class: InvalidCastException -** -** -** Purpose: Exception class for bad cast conditions! -** -** Date: March 17, 1998 -** -=============================================================================*/ - -namespace System { - - using System; - using System.Runtime.CompilerServices; - - //| - public class InvalidCastException : SystemException { - //| - public InvalidCastException() - : base("Arg_InvalidCastException") { - } - - //| - public InvalidCastException(String message) - : base(message) { - } - - //| - public InvalidCastException(String message, Exception innerException) - : base(message, innerException) { - } - } -} diff --git a/base/Kernel/System/InvalidOperationException.cs b/base/Kernel/System/InvalidOperationException.cs deleted file mode 100644 index c4c5fe8..0000000 --- a/base/Kernel/System/InvalidOperationException.cs +++ /dev/null @@ -1,40 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================================= -** -** Class: InvalidOperationException -** -** -** Purpose: Exception class for denoting an object was in a state that -** made calling a method illegal. -** -** Date: March 24, 1999 -** -=============================================================================*/ -namespace System { - - using System; - - //| - public class InvalidOperationException : SystemException - { - //| - public InvalidOperationException() - : base("Arg_InvalidOperationException") { - } - - //| - public InvalidOperationException(String message) - : base(message) { - } - - //| - public InvalidOperationException(String message, Exception innerException) - : base(message, innerException) { - } - } -} - diff --git a/base/Kernel/System/MulticastDelegate.cs b/base/Kernel/System/MulticastDelegate.cs deleted file mode 100644 index feafba2..0000000 --- a/base/Kernel/System/MulticastDelegate.cs +++ /dev/null @@ -1,194 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -namespace System { - using System; - using System.Reflection; - - //| - public abstract class MulticastDelegate : Delegate - { - // This is a pointer to a delegate that is logically the - // delegate before this one. - [System.Runtime.CompilerServices.RequiredByBartok] - private MulticastDelegate _prev; - - internal MulticastDelegate Previous - { - get{return _prev;} - set{_prev = value;} - } - - // equals returns true IIF the delegate is not null and has the - // same target, method and invocation list as this object - //| - public override sealed bool Equals(Object obj) - { - if (!base.Equals(obj)) - return false; - if (_prev != null) - return _prev.InvocationListEquals(((MulticastDelegate) obj)._prev); - else - { // if we got here, "this" is a Multicast with only one listener. - if (obj is MulticastDelegate) - return ((MulticastDelegate)obj)._prev == null; - else if (obj is Delegate) - return true; - else - return false; - } - } - - // Recursive function which will check for equality of the invocation list. - private bool InvocationListEquals(MulticastDelegate d) - { - if (!base.Equals(d)) - return false; - if (_prev == d._prev) - return true; - if (_prev == null) - return (d._prev == null) ? true : false; - return _prev.InvocationListEquals(d._prev); - } - - // This method will combine this delegate with the passed delegate - // to form a new delegate. - //| - protected override sealed Delegate CombineImpl(Delegate follow) - { - // Verify that the types are the same... - if (this.GetType() != follow.GetType()) - throw new ArgumentException("Arg_DlgtTypeMis"); - - // We always clone the delegate because this delegate is - // not changed by combine and remove. We can safely tack - // the follow delegate onto the end of the copy. - MulticastDelegate d = (MulticastDelegate) ((MulticastDelegate) follow).MemberwiseClone(); - MulticastDelegate root = d; - while (d._prev != null) { - d._prev = (MulticastDelegate) d._prev.MemberwiseClone(); - d = d._prev; - } - d._prev = (MulticastDelegate) this; - return root; - } - - // This method currently looks backward on the invocation list - // for an element that has Delegate based equality with value. (Doesn't - // look at the invocation list.) If this is found we remove it from - // this list and return a new delegate. If its not found a copy of the - // current list is returned. - //| - protected override sealed Delegate RemoveImpl(Delegate value) - { - // There is a special case were we are removing using a delegate as - // the value we need to check for this case - // - if (!(value is MulticastDelegate)) { - if (base.Equals(value)) - return _prev; - return this; - } - - // Look for the delegate... - MulticastDelegate v = (MulticastDelegate) value; - if (InternalEquals(v)) { - int size = v.DelSize(); - MulticastDelegate p = _prev; - while (--size != 0) - p = p._prev; - return p; - } - - MulticastDelegate d = (MulticastDelegate) this.MemberwiseClone(); - MulticastDelegate root = d; - while (d._prev != null && d._prev.InternalEquals(v) != true) { - d._prev = (MulticastDelegate) d._prev.MemberwiseClone(); - d = d._prev; - } - - if (d._prev != null) { - int size = v.DelSize(); - MulticastDelegate p = d._prev._prev; - while (--size != 0) - p = p._prev; - d._prev = p; - } - return root; - } - private int DelSize() - { - int i=0; - MulticastDelegate d = this; - while (d != null) { - i++; - d = d._prev; - } - return i; - } - - - // Stupid helper function to check equality based upon the super class. - private bool InternalEquals(Delegate d) - { - if (!base.Equals(d)) - return false; - if (((MulticastDelegate) d)._prev != null) { - if (_prev == null) - return false; - return _prev.InternalEquals(((MulticastDelegate) d)._prev); - } - return true; - } - - - // This method returns the Invocation list of this multicast delegate. - //| - public override sealed Delegate[] GetInvocationList() { - int i = 0; - MulticastDelegate p; - - // How big is the invocation list? - for (p = this; p != null; p = p._prev) - i++; - - // Create an array of delegate copies and each - // element into the array (Need to reverse the order and make sure - // we set the _prev to null. - Delegate[] del = new Delegate[i]; - for (p = this; p != null; p = p._prev) { - del[--i] = (Delegate) p.MemberwiseClone(); - ((MulticastDelegate) del[i])._prev = null; - } - return del; - } - - /* private static bool operator equals(MulticastDelegate d1, MulticastDelegate d2) { - if ((Object)d1 == null) - return (Object)d2 == null; - return d1.Equals(d2); - }*/ - - //| - public static bool operator ==(MulticastDelegate d1, MulticastDelegate d2) { - if ((Object)d1 == null) - return (Object)d2 == null; - return d1.Equals(d2); - } - - //| - public static bool operator !=(MulticastDelegate d1, MulticastDelegate d2) { - if ((Object)d1 == null) - return (Object)d2 != null; - return !d1.Equals(d2); - } - - //| - public override sealed int GetHashCode() { - return base.GetHashCode(); - } - - } -} diff --git a/base/Kernel/System/NotSupportedException.cs b/base/Kernel/System/NotSupportedException.cs deleted file mode 100644 index bc5cb82..0000000 --- a/base/Kernel/System/NotSupportedException.cs +++ /dev/null @@ -1,39 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================================= -** -** Class: NotSupportedException -** -** -** Purpose: For methods that should be implemented on subclasses. -** -** Date: September 28, 1998 -** -=============================================================================*/ - -namespace System { - - using System; - - //| - public class NotSupportedException : SystemException - { - //| - public NotSupportedException() - : base("Arg_NotSupportedException") { - } - - //| - public NotSupportedException(String message) - : base(message) { - } - - //| - public NotSupportedException(String message, Exception innerException) - : base(message, innerException) { - } - } -} diff --git a/base/Kernel/System/NullReferenceException.cs b/base/Kernel/System/NullReferenceException.cs deleted file mode 100644 index 0bfd435..0000000 --- a/base/Kernel/System/NullReferenceException.cs +++ /dev/null @@ -1,41 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================================= -** -** Class: NullReferenceException -** -** -** Purpose: Exception class for dereferencing a null reference. -** -** Date: March 17, 1998 -** -=============================================================================*/ - -namespace System { - - using System; - using System.Runtime.CompilerServices; - - //| - [RequiredByBartok] - public class NullReferenceException : SystemException { - //| - [AccessedByRuntime("referenced from halasm.asm")] - public NullReferenceException() - : base("Arg_NullReferenceException") { - } - - //| - public NullReferenceException(String message) - : base(message) { - } - - //| - public NullReferenceException(String message, Exception innerException) - : base(message, innerException) { - } - } -} diff --git a/base/Kernel/System/Object.cs b/base/Kernel/System/Object.cs deleted file mode 100644 index 5aec123..0000000 --- a/base/Kernel/System/Object.cs +++ /dev/null @@ -1,322 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================ -** -** Class: Object -** -** -** Object is the root class for all CLR objects. This class -** defines only the basics. -** -** Date: January 29, 1998 -** -===========================================================*/ - -namespace System { - using System; - using System.Runtime.InteropServices; - using System.Runtime.CompilerServices; - using CultureInfo = System.Globalization.CultureInfo; - using System.Threading; - - using Microsoft.Bartok.Runtime; - - // The Object is the root class for all object in the CLR System. Object - // is the super class for all other CLR objects and provide a set of methods and low level - // services to subclasses. These services include object synchronization and support for clone - // operations. - // - //| - [NoCCtor] - public class Object - { - internal PreHeader preHeader; - [AccessedByRuntime("Accessed from halexn.cpp")] - internal PostHeader postHeader; - - // Allow us to pretend that the vtable field lives directly in Object. - internal VTable vtable { - [NoHeapAllocation] - [Inline] - get { return this.postHeader.vtableObject; } - [NoHeapAllocation] - [Inline] - set { this.postHeader.vtableObject = value; } - } - - internal unsafe UIntPtr* VTableFieldAddr { - [NoHeapAllocation] - get { return Magic.toPointer(ref this.postHeader.vtableObject); } - } - -#if REFERENCE_COUNTING_GC - internal uint REF_STATE { - [Inline] - [ManualRefCounts] - [NoHeapAllocation] - get { - return this.postHeader.refState; - } - [Inline] - [ManualRefCounts] - [NoHeapAllocation] - set { - this.postHeader.refState = value; - } - } -#else // REFERENCE_COUNTING_GC - internal uint REF_STATE { - [NoHeapAllocation] - [Inline] - get { - return 1; - } - [NoHeapAllocation] - [Inline] - set { - } - } -#endif - - // Creates a new instance of an Object. - //| - [Inline] - public Object() - { - } - - // Returns a String which represents the object instance. The default - // for an object is to return the fully qualified name of the class. - // - //| - public virtual String ToString() - { - return GetType().FullName; - } - - // Returns a boolean indicating if the passed in object obj is - // Equal to this. Equality is defined as object equality for reference - // types and bitwise equality for value types using a loader trick to - // replace Equals with EqualsValue for value types). - // - //| - [RequiredByBartok] - public virtual bool Equals(Object obj) - { - // This method is overridden for value types - return (this == obj); - } - - //| - public static bool Equals(Object objA, Object objB) { - if (objA==objB) { - return true; - } - if (objA==null || objB==null) { - return false; - } - return objA.Equals(objB); - } - - //| - public static bool ReferenceEquals (Object objA, Object objB) { - return objA == objB; - } - - // GetHashCode is intended to serve as a hash function for this object. - // Based on the contents of the object, the hash function will return a suitable - // value with a relatively random distribution over the various inputs. - // - // The default implementation returns the sync block index for this instance. - // Calling it on the same object multiple times will return the same value, so - // it will technically meet the needs of a hash function, but it's pretty lame. - // Objects (& especially value classes) should override this method. - // - //| - public virtual int GetHashCode() - { - return MultiUseWord.GetHashCode(this); - } - - /// - /// Test and set the state of the GC mark bit to be the same - /// as the passed flag. Note that this operation is not - /// synchronized so it is possible for multiple marking threads - /// to 'mark' the same object. - /// - [NoHeapAllocation] - internal unsafe bool GcMark(UIntPtr flag) { - UIntPtr *loc = this.VTableFieldAddr; - UIntPtr val = *loc; - VTable.Deny(val == UIntPtr.Zero); - - if ((val & (UIntPtr)3) != flag) { - *loc = (val & ~(UIntPtr)3) + flag; - return true; - } - return false; - } - - /// - /// Return the current state of the GC mark bit. - /// - [NoHeapAllocation] - internal unsafe UIntPtr GcMark() { - UIntPtr *loc = this.VTableFieldAddr; - UIntPtr val = *loc; - return (val & 3); - } - - internal unsafe VTable GcUnmarkedVTable { - [Inline] - [NoHeapAllocation] - get { - UIntPtr *loc = this.VTableFieldAddr; - return Magic.toVTable(Magic.fromAddress(~(UIntPtr)3 & *loc)); - } - } - - // Returns a Type object which represent this object instance. - // - //| - [NoHeapAllocation] - public Type GetType() - { - return vtable.vtableType; - } - - [NoHeapAllocation] - public virtual TypeCode GetTypeCode() - { - return TypeCode.Object; - } - - // Allow an object to free resources before the object is reclaimed by the GC. - // Note: This defines a protected method called Finalize. - //| - [RequiredByBartok] - ~Object() - { - } - - // Returns a new object instance that is a memberwise copy of this - // object. This is always a shallow copy of the instance. The method is protected - // so that other object may only call this method on themselves. It is intended to - // support the ICloneable interface. - // - //| - // BUGBUG: maybe we can try harder to mess up the GC? - protected Object MemberwiseClone() - { - if(this is String) { - return this; - // REVIEW: ok, but what in the world is the CLR doing? - } - Thread thread = Thread.CurrentThread; - if(this is Array) { - Array srcArray = (Array) this; - Array cloneArray; - int srcLength = srcArray.Length; - if (srcArray.IsVector) { - cloneArray = GC.AllocateVector(srcArray.vtable, srcLength); - CloneVectorContents(srcArray, cloneArray); - } else { - int rank = srcArray.Rank; - cloneArray = - GC.AllocateArray(srcArray.vtable, rank, srcLength); - CloneArrayContents(srcArray, cloneArray); - } - return cloneArray; - } else { - Object clone = GC.AllocateObject(this.vtable); - CloneObjectContents(this, clone); - return clone; - } - } - -#if !REFERENCE_COUNTING_GC && !DEFERRED_REFERENCE_COUNTING_GC - - private unsafe static void CloneObjectContents(Object src, Object dst) - { - System.GCs.WriteBarrier.Clone(src, dst); - } - - private unsafe static void CloneVectorContents(Array srcArray, - Array dstArray) - { - System.GCs.WriteBarrier.Clone(srcArray, dstArray); - } - - private unsafe static void CloneArrayContents(Array srcArray, - Array dstArray) - { - System.GCs.WriteBarrier.Clone(srcArray, dstArray); - } - -#else - - private unsafe static void CloneObjectContents(Object src, Object dst) - { - byte * dstNOTFIXED = (byte *)(Magic.addressOf(dst) + PostHeader.Size); - byte * srcNOTFIXED = (byte *)(Magic.addressOf(src) + PostHeader.Size); - int size = unchecked((int) src.vtable.baseLength); - // We don't copy the header fields, the vtable or the RS field! - size -= (PreHeader.Size + PostHeader.Size); -#if REFERENCE_COUNTING_GC - GCs.ReferenceCountingCollector. - IncrementReferentRefCounts(Magic.addressOf(src), src.vtable); - GCs.ReferenceCountingCollector. - DecrementReferentRefCounts(Magic.addressOf(dst), dst.vtable); -#elif DEFERRED_REFERENCE_COUNTING_GC - GCs.DeferredReferenceCountingCollector. - IncrementReferentRefCounts(Magic.addressOf(src), src.vtable); - GCs.DeferredReferenceCountingCollector. - DecrementReferentRefCounts(Magic.addressOf(dst), dst.vtable); -#endif // REFERENCE_COUNTING_GC - Buffer.MoveMemory(dstNOTFIXED,srcNOTFIXED,size); - } - - private unsafe static void CloneVectorContents(Array srcArray, - Array dstArray) - { - int srcLength = srcArray.Length; - fixed (int *srcFieldPtr = &srcArray.field1) { - fixed (int *dstFieldPtr = &dstArray.field1) { - byte *srcDataPtr = (byte *) - srcArray.GetFirstElementAddress(srcFieldPtr); - byte *dstDataPtr = (byte *) - dstArray.GetFirstElementAddress(dstFieldPtr); - int size = srcArray.vtable.arrayElementSize * srcLength; - Buffer.MoveMemory(dstDataPtr, srcDataPtr, size); - } - } - } - - private unsafe static void CloneArrayContents(Array srcArray, - Array dstArray) - { - int srcLength = srcArray.Length; - fixed (int *srcFieldPtr = &srcArray.field1) { - fixed (int *dstFieldPtr = &dstArray.field1) { - byte *srcDataPtr = (byte *) - srcArray.GetFirstElementAddress(srcFieldPtr); - byte *dstDataPtr = (byte *) - dstArray.GetFirstElementAddress(dstFieldPtr); - byte *srcDimPtr = (byte *) - srcArray.GetFirstDimInfoRectangleArray(); - int dimInfoSize = (int) (srcDataPtr - srcDimPtr); - int size = srcArray.vtable.arrayElementSize * srcLength; - Buffer.MoveMemory(dstDataPtr - dimInfoSize, - srcDataPtr - dimInfoSize, - size + dimInfoSize); - } - } - } - -#endif - - } -} diff --git a/base/Kernel/System/OutOfMemoryException.cs b/base/Kernel/System/OutOfMemoryException.cs deleted file mode 100644 index 69ae2da..0000000 --- a/base/Kernel/System/OutOfMemoryException.cs +++ /dev/null @@ -1,39 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================================= -** -** Class: OutOfMemoryException -** -** -** Purpose: The exception class for OOM. -** -** Date: March 17, 1998 -** -=============================================================================*/ - -namespace System { - - using System; - using System.Runtime.CompilerServices; - - //| - public class OutOfMemoryException : SystemException { - //| - public OutOfMemoryException() - : base("Arg_OutOfMemoryException") { - } - - //| - public OutOfMemoryException(String message) - : base(message) { - } - - //| - public OutOfMemoryException(String message, Exception innerException) - : base(message, innerException) { - } - } -} diff --git a/base/Kernel/System/OverflowException.cs b/base/Kernel/System/OverflowException.cs deleted file mode 100644 index 44b39a2..0000000 --- a/base/Kernel/System/OverflowException.cs +++ /dev/null @@ -1,41 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================================= -** -** Class: OverflowException -** -** -** Purpose: Exception class for Arithmetic Overflows. -** -** Date: August 31, 1998 -** -=============================================================================*/ - -namespace System { - - using System; - using System.Runtime.CompilerServices; - - //| - [RequiredByBartok] - public class OverflowException : ArithmeticException { - //| - [AccessedByRuntime("referenced from halasm.asm")] - public OverflowException() - : base("Arg_OverflowException") { - } - - //| - public OverflowException(String message) - : base(message) { - } - - //| - public OverflowException(String message, Exception innerException) - : base(message, innerException) { - } - } -} diff --git a/base/Kernel/System/ParamArrayAttribute.cs b/base/Kernel/System/ParamArrayAttribute.cs deleted file mode 100644 index 29ec46c..0000000 --- a/base/Kernel/System/ParamArrayAttribute.cs +++ /dev/null @@ -1,25 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================================= -** -** Class: ParamArrayAttribute -** -** -** Purpose: Container for assemblies. -** -** Date: Mar 01, 2000 -** -=============================================================================*/ -namespace System -{ - //| - [AttributeUsage (AttributeTargets.Parameter, Inherited=true, AllowMultiple=false)] - public sealed class ParamArrayAttribute : Attribute - { - //| - public ParamArrayAttribute () {} - } -} diff --git a/base/Kernel/System/Random.cs b/base/Kernel/System/Random.cs deleted file mode 100644 index 720e901..0000000 --- a/base/Kernel/System/Random.cs +++ /dev/null @@ -1,196 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================ -** -** Class: Random.cool -** -** -** Purpose: A random number generator. -** -** Date: July 8, 1998 -** -===========================================================*/ -namespace System { - - using System; - using System.Runtime.CompilerServices; - //| - - public class Random { - // - // Private Constants - // - private const int MBIG = Int32.MaxValue; - private const int MSEED = 161803398; - private const int MZ = 0; - - - // - // Member Variables - // - private int inext, inextp; - private int[] SeedArray = new int[56]; - - // - // Public Constants - // - - // - // Native Declarations - // - - // - // Constructors - // - - //| - public Random() - : this(Environment.TickCount) { - } - - //| - public Random(int Seed) { - int ii; - int mj, mk; - - //Initialize our Seed array. - //This algorithm comes from Numerical Recipes in C (2nd Ed.) - mj = MSEED - Math.Abs(Seed); - SeedArray[55]=mj; - mk=1; - for (int i=1; i<55; i++) { //Apparently the range [1..55] is special (Knuth) and so we're wasting the 0'th position. - ii = (21*i)%55; - SeedArray[ii]=mk; - mk = mj - mk; - if (mk<0) mk+=MBIG; - mj=SeedArray[ii]; - } - for (int k=1; k<5; k++) { - for (int i=1; i<56; i++) { - SeedArray[i] -= SeedArray[1+(i+30)%55]; - if (SeedArray[i]<0) SeedArray[i]+=MBIG; - } - } - inext=0; - inextp = 21; - Seed = 1; - } - - // - // Package Private Methods - // - - /*====================================Sample==================================== - **Action: Return a new random number [0..1) and reSeed the Seed array. - **Returns: A double [0..1) - **Arguments: None - **Exceptions: None - ==============================================================================*/ - //| - protected virtual double Sample() { - int retVal; - int locINext = inext; - int locINextp = inextp; - - if (++locINext >=56) locINext=1; - if (++locINextp>= 56) locINextp = 1; - - retVal = SeedArray[locINext]-SeedArray[locINextp]; - - if (retVal<0) retVal+=MBIG; - - SeedArray[locINext]=retVal; - - inext = locINext; - inextp = locINextp; - - //Including this division at the end gives us significantly improved - //random number distribution. - return (retVal*(1.0/MBIG)); - } - - // - // Public Instance Methods - // - - - /*=====================================Next===================================== - **Returns: An int [0.._int4.MaxValue) - **Arguments: None - **Exceptions: None. - ==============================================================================*/ - //| - public virtual int Next() { - return (int)(Sample()*Int32.MaxValue); - } - - /*=====================================Next===================================== - **Returns: An int [minvalue..maxvalue) - **Arguments: minValue -- the least legal value for the Random number. - ** maxValue -- the greatest legal return value. - **Exceptions: None. - ==============================================================================*/ - //| - public virtual int Next(int minValue, int maxValue) { - if (minValue>maxValue) { - throw new ArgumentOutOfRangeException("minValue",String.Format("Argument_MinMaxValue", "minValue", "maxValue")); - } - - int range = (maxValue-minValue); - - //This is the case where we flipped around (e.g. MaxValue-MinValue); - if (range<0) { - long longRange = (long)maxValue-(long)minValue; - return (int)(((long)(Sample()*((double)longRange)))+minValue); - } - - return ((int)(Sample()*(range)))+minValue; - } - - - /*=====================================Next===================================== - **Returns: An int [0..maxValue) - **Arguments: maxValue -- the greatest legal return value. - **Exceptions: None. - ==============================================================================*/ - //| - public virtual int Next(int maxValue) { - if (maxValue<0) { - throw new ArgumentOutOfRangeException("maxValue", String.Format("ArgumentOutOfRange_MustBePositive", "maxValue")); - } - return (int)(Sample()*maxValue); - } - - - /*=====================================Next===================================== - **Returns: A double [0..1) - **Arguments: None - **Exceptions: None - ==============================================================================*/ - //| - public virtual double NextDouble() { - return Sample(); - } - - - /*==================================NextBytes=================================== - **Action: Fills the byte array with random bytes [0..0x7f]. The entire array is filled. - **Returns:Void - **Arguments: buffer -- the array to be filled. - **Exceptions: None - ==============================================================================*/ - //| - public virtual void NextBytes(byte [] buffer){ - if (buffer==null) throw new ArgumentNullException("buffer"); - for (int i=0; i - public class RankException : SystemException - { - //| - public RankException() - : base("Arg_RankException") { - } - - //| - public RankException(String message) - : base(message) { - } - - //| - public RankException(String message, Exception innerException) - : base(message, innerException) { - } - } -} diff --git a/base/Kernel/System/Reflection/Assembly.cs b/base/Kernel/System/Reflection/Assembly.cs deleted file mode 100644 index d2c0008..0000000 --- a/base/Kernel/System/Reflection/Assembly.cs +++ /dev/null @@ -1,112 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================================= -** -** Class: Assembly -** -** -** Purpose: For Assembly-related stuff. -** -** Date: April 1, 1999 -** -=============================================================================*/ - -namespace System.Reflection { - - using System; - using System.Runtime.CompilerServices; - - //| - [RequiredByBartok] - public class Assembly - { - // ---------- Bartok code ---------- - - public AssemblyName Name - { - get { - return this.assemblyName; - } - } - - internal String nGetSimpleName() { - return this.assemblyName.Name; - } - - private String GetFullName() { - String name = - this.nGetSimpleName() - + ", Version=" + this.assemblyName.Version.Major - + "." + this.assemblyName.Version.Minor - + "." + this.assemblyName.Version.Build - + "." + this.assemblyName.Version.Revision - + ", Culture=" - + (this.assemblyName.Culture != "" - ? this.assemblyName.Culture - : "neutral") - + ", PublicKeyToken=" - + (this.assemblyName.GetPublicKeyToken() != null - ? (Assembly.EncodeHexString - (this.assemblyName.GetPublicKeyToken())) - : "null"); - return name; - } - - // ---------- copied from mscorlib System.Security.Util.Hex ---------- - - // changed to lowercase to avoid ToLower call since the code is no - // longer shared in Util.Hex - private static char[] hexValues = - { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'a', 'b', 'c', 'd', 'e', 'f' }; - - private static String EncodeHexString(byte[] sArray) - { - String result = null; - - if(sArray != null) { - char[] hexOrder = new char[sArray.Length * 2]; - - int digit; - for(int i = 0, j = 0; i < sArray.Length; i++) { - digit = (int)((sArray[i] & 0xf0) >> 4); - hexOrder[j++] = hexValues[digit]; - digit = (int)(sArray[i] & 0x0f); - hexOrder[j++] = hexValues[digit]; - } - result = new String(hexOrder); - } - return result; - } - - [RequiredByBartok] - private AssemblyName assemblyName; - - // ---------- mscorlib code ---------- - // (some modifications to pull in less code) - - //| - public virtual String FullName { - get { - // If called by Object.ToString(), return val may be NULL. - String s; - - /* not implementing InternalCache for now - if ((s = (String)Cache[CacheObjType.AssemblyName]) != null) - return s; - */ - - s = GetFullName(); - /* not implementing InternalCache for now - if (s != null) - Cache[CacheObjType.AssemblyName] = s; - */ - - return s; - } - } - } -} diff --git a/base/Kernel/System/Reflection/AssemblyName.cs b/base/Kernel/System/Reflection/AssemblyName.cs deleted file mode 100644 index 2c145a9..0000000 --- a/base/Kernel/System/Reflection/AssemblyName.cs +++ /dev/null @@ -1,77 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================ -** -** File: AssemblyName -** -** -** Purpose: Used for binding and retrieving info about an assembly -** -** Date: June 4, 1999 -** -===========================================================*/ -namespace System.Reflection { - using System.Runtime.CompilerServices; - - [RequiredByBartok] - public class AssemblyName { - // ---------- Bartok code ---------- - - [RequiredByBartok] - private String _Culture; - - public String Culture { - get { return _Culture; } - } - - // ---------- mscorlib code ---------- - // (some modifications to pull in less code) - - [RequiredByBartok] - private String _Name; // Name - [RequiredByBartok] - private byte[] _PublicKeyToken; - [RequiredByBartok] - private Version _Version; - - // Set and get the name of the assembly. If this is a weak Name - // then it optionally contains a site. For strong assembly names, - // the name partitions up the strong name's namespace - //| - public String Name - { - get { return _Name; } - /* not needed for now - set { _Name = value; } - */ - } - - //| - public Version Version - { - get { - return _Version; - } - /* not needed for now - set { - _Version = value; - } - */ - } - - // The compressed version of the public key formed from a truncated hash. - //| - public byte[] GetPublicKeyToken() - { - /* not needed for now - if ((_PublicKeyToken == null) && - (_Flags & AssemblyNameFlags.PublicKey) != 0) - _PublicKeyToken = nGetPublicKeyToken(); - */ - return _PublicKeyToken; - } - } -} diff --git a/base/Kernel/System/Reflection/Module.cs b/base/Kernel/System/Reflection/Module.cs deleted file mode 100644 index 7701c90..0000000 --- a/base/Kernel/System/Reflection/Module.cs +++ /dev/null @@ -1,28 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// -// The Module class represents a module in the COM+ runtime. A module -// may consist of one or more classes and interfaces and represents a physical -// deployment such as a DLL or EXE of those classes. There may be multiple namespaces -// contained in a single module and a namespace may be span multiple modules. -// -// The runtime supports a special type of module that are dynamically created. New -// classes can be created through the dynamic IL generation process. -// -// Date: April 98 -// -namespace System.Reflection { - using System; - using System.Runtime.CompilerServices; - - //| - [RequiredByBartok] - public class Module - { - } -} diff --git a/base/Kernel/System/Runtime/CompilerServices/Attributes.cs b/base/Kernel/System/Runtime/CompilerServices/Attributes.cs deleted file mode 100644 index 6291ab1..0000000 --- a/base/Kernel/System/Runtime/CompilerServices/Attributes.cs +++ /dev/null @@ -1,336 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -namespace System.Runtime.CompilerServices { - - // phx whidbey - - // Indicates that the modified type is const (i.e. has a const modifier) - public class IsConst - { - } - public class IsImplicitlyDereferenced - { - } - public class IsSignUnspecifiedByte - { - } - public class IsLong - { - } - public class IsBoxed - { - } - public class UnsafeValueTypeAttribute : Attribute - { - } - public class FixedAddressValueTypeAttribute : Attribute - { - } - - // end phx whidbey - - [AttributeUsage(AttributeTargets.Constructor| - AttributeTargets.Method)] - internal sealed class InlineAttribute: Attribute { - - } - - [AttributeUsage(AttributeTargets.Constructor| - AttributeTargets.Method)] - internal sealed class NoInlineAttribute: Attribute { - - } - - [AttributeUsage(AttributeTargets.Struct)] - internal sealed class InlineCopyAttribute: Attribute { - } - - [AttributeUsage(AttributeTargets.Constructor| - AttributeTargets.Method)] - internal sealed class DisableBoundsChecksAttribute: Attribute { - - } - - [AttributeUsage(AttributeTargets.Constructor| - AttributeTargets.Method)] - internal sealed class DisableNullChecksAttribute: Attribute { - - } - - [AttributeUsage(AttributeTargets.Constructor| - AttributeTargets.Method)] - internal sealed class InlineIntoOnceAttribute: Attribute { - } - - [AttributeUsage(AttributeTargets.Interface| - AttributeTargets.Class| - AttributeTargets.Struct, - Inherited=false)] - public sealed class CCtorIsRunDuringStartupAttribute : Attribute { - } - - [AttributeUsage(AttributeTargets.Interface| - AttributeTargets.Class| - AttributeTargets.Struct, - Inherited=false)] - public sealed class NoCCtorAttribute : Attribute { - } - - [AttributeUsage(AttributeTargets.Constructor| - AttributeTargets.Method)] - public sealed class NoHeapAllocationAttribute : Attribute { - } - - [AttributeUsage(AttributeTargets.Class| - AttributeTargets.Struct| - AttributeTargets.Interface| - AttributeTargets.Method| - AttributeTargets.Constructor| - AttributeTargets.Field, - Inherited=false)] - [RequiredByBartok] - internal sealed class AccessedByRuntimeAttribute: Attribute { - public AccessedByRuntimeAttribute(string reason) { - this.reason = reason; - } - - public int Option { - get { return option; } - set { option = value; } - } - int option; - string reason; - } - - [AttributeUsage(AttributeTargets.Method| - AttributeTargets.Constructor| - AttributeTargets.Field, - Inherited=false)] - public sealed class ProvidedByOverrideAttribute: Attribute { - } - - [AttributeUsage(AttributeTargets.Field)] - public sealed class ExternalStaticDataAttribute : Attribute { - } - - [AttributeUsage(AttributeTargets.Struct)] - public sealed class StructAlignAttribute : Attribute { - public StructAlignAttribute(int align) {} - } - - [AttributeUsage(AttributeTargets.Method, - Inherited=false)] - public sealed class StackBoundAttribute: Attribute { - public StackBoundAttribute(int bound) {} - } - - [AttributeUsage(AttributeTargets.Method| - AttributeTargets.Constructor, - Inherited=false)] - public sealed class StackLinkCheckAttribute: Attribute { - } - - [AttributeUsage(AttributeTargets.Method| - AttributeTargets.Constructor, - Inherited=false)] - public sealed class RequireStackLinkAttribute: Attribute { - } - - [AttributeUsage(AttributeTargets.Method| - AttributeTargets.Constructor, - Inherited=false)] - public sealed class NoStackLinkCheckAttribute: Attribute { - } - - [AttributeUsage(AttributeTargets.Method| - AttributeTargets.Constructor, - Inherited=false)] - public sealed class NoStackLinkCheckTransAttribute: Attribute { - } - - [AttributeUsage(AttributeTargets.Method| - AttributeTargets.Constructor, - Inherited=false)] - public sealed class NoStackOverflowCheckAttribute: Attribute { - } - - [AttributeUsage(AttributeTargets.Field| - AttributeTargets.Method| - AttributeTargets.Constructor, - Inherited=false)] - public sealed class IntrinsicAttribute: Attribute { - // Intrinsics should never have bodies. However, csc complains if we - // mark a property on a struct extern, so we have to give those bodies. - // Hence this flag. IgnoreBody=true means discard the body. - // IgnoreBody=false means it is an error to supply a body. - public bool IgnoreBody { - get { return ignoreBody; } - set { ignoreBody = value; } - } - bool ignoreBody; - } - - [AttributeUsage(AttributeTargets.Field)] - internal sealed class InlineVectorAttribute : Attribute { - public InlineVectorAttribute(int numElements) {} - } - - // This attribute is used to mark method that needs pushStackMark - // and popStackMark around calls to it. - [AttributeUsage(AttributeTargets.Method, - Inherited=false)] - [RequiredByBartok] - public sealed class OutsideGCDomainAttribute: Attribute { - } - - // This attribute is used to mark method that needs enterGCSafteState - // and leaveGCSafeState around its definition - [AttributeUsage(AttributeTargets.Method, - Inherited=false)] - public sealed class ExternalEntryPointAttribute: Attribute { - public int Option { - get { return option; } - set { option = value; } - } - public int IgnoreCallerTransition { - get { return ignoreCallerTransition; } - set { option = value; } - } - int option, ignoreCallerTransition; - } - - // This attribute is used to mark type/field/methods that are required - // by Bartok compiler. - [AttributeUsage(AttributeTargets.Class | - AttributeTargets.Struct | - AttributeTargets.Enum | - AttributeTargets.Interface | - AttributeTargets.Delegate | - AttributeTargets.Method | - AttributeTargets.Constructor | - AttributeTargets.Field, - Inherited=false)] - public sealed class RequiredByBartokAttribute: Attribute { - public RequiredByBartokAttribute() {} - public RequiredByBartokAttribute(string reason) { - this.reason = reason; - } - string reason; - } - - /// - /// This attribute must be placed on override types that override the class - /// constructor. It is a compile-time error if the attribute is missing - /// during an override. It is also a compile-time error if it exists and - /// either the original or the override type does not have a class - /// constructor. - /// - [AttributeUsage(AttributeTargets.Class| - AttributeTargets.Struct| - AttributeTargets.Interface)] - internal sealed class OverrideCctorAttribute : Attribute { - } - - /// - /// This attribute must be placed on override types that mean to override the - /// base class. If a base class is overridden, then either this attribute or - /// IgnoreOverrideExtendsAttribute must be present. It is also a compile-time - /// error if this attribute exists and the override base class is the same as - /// the original base class. - /// - [AttributeUsage(AttributeTargets.Class)] - internal sealed class OverrideExtendsAttribute : Attribute { - } - - /// - /// This attribute must be placed on override types that override the base - /// class in the override assembly but do not mean to override the base class - /// in the actual type. If a base class is overridden, then either this - /// attribute or OverrideExtendsAttribute must be present. It is also a - /// compile-time error if this attribute exists and the override base class is - /// the same as the original base class. - /// - [AttributeUsage(AttributeTargets.Class)] - internal sealed class IgnoreOverrideExtendsAttribute : Attribute { - } - - /// - /// This attribute is placed on override types to delete the built-in class - /// constructor. Using this is better than overriding with an empty method. - /// - [AttributeUsage(AttributeTargets.Class| - AttributeTargets.Struct| - AttributeTargets.Interface)] - internal sealed class DeleteCctorAttribute : Attribute { - } - - [AttributeUsage(AttributeTargets.Struct)] - public sealed class OverrideLayoutAttribute : Attribute { - } - - // There are at least three reasons why one would need to prevent - // the automatic insertion of vanilla reference counting (RC) code - // into the body of a method, property or constructor: - // - // 1. To suppress reference counting before a reference to - // the installed GC is set up. - // - // 2. Methods that directly manipulate reference counts such - // as allocation routines. - // - // 3. To suppress the insertion of RC code into code bodies - // that may be directly or indirectly invoked from the - // IncrementRefCount or DecrementRefCount methods of the - // reference counting collector. - // - // The IrRCUpdate compiler phase can be made to skip code bodies for - // any of the above reasons by affixing one of two special attributes - // to their declarations. Currently, the [PreInitRefCounts] attribute - // is used to mark code that could be invoked before the GC gets set - // up and that, in its absence, may cause the IrRCUpdate phase to - // insert RC increment and decrement code. The [ManualRefCounts] - // attribute models cases in which the code writer takes the onus of - // maintaining consistent reference counts. - // - // The reason for separating the preinitialization case from the - // other two is because special RC updates, which test for - // initialization of the GC before incrementing or decrementing the - // reference counts, could still have been inserted into code bodies - // marked as [PreInitRefCounts]. However, if the same code body is - // called after initialization, such updates may slow down the - // common case. This provides an optimization opportunity for the - // compiler in which a method f marked with [PreInitRefCounts] could - // be cloned into a version f' that contains plain RC code and that - // is actually called wherever a non-[PreInitRefCounts] method such - // as g calls f. - // - // If a method h has the [ManualRefCounts] attribute and if reference - // counts are directly read or written in h, then the code must either - // be also marked as [NoInline] or must only be called from methods - // that also have the [ManualRefCounts] attribute. This is because if - // h were inlined into a method in which reference counting is on by - // default, the injected RC code may cause the reference counts - // to become inconsistent. - [AttributeUsage(AttributeTargets.Method| - AttributeTargets.Constructor)] - [RequiredByBartok] - internal sealed class PreInitRefCountsAttribute: Attribute { - } - [AttributeUsage(AttributeTargets.Method| - AttributeTargets.Constructor)] - [RequiredByBartok] - internal sealed class ManualRefCountsAttribute: Attribute { - } - // This marks classes that are acyclic reference types. - // (See IrAcyclicRefTypes.cs.) - [AttributeUsage(AttributeTargets.Class)] - [RequiredByBartok] - internal sealed class AcyclicRefTypeAttribute: Attribute { - } - - [AttributeUsage(AttributeTargets.Field)] - internal sealed class TrustedNonNullAttribute: Attribute { - } -} diff --git a/base/Kernel/System/Runtime/InteropServices/Attributes.cs b/base/Kernel/System/Runtime/InteropServices/Attributes.cs deleted file mode 100644 index 1db63d7..0000000 --- a/base/Kernel/System/Runtime/InteropServices/Attributes.cs +++ /dev/null @@ -1,109 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -namespace System.Runtime.InteropServices { - - using System; - - //| - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Struct | AttributeTargets.Delegate, Inherited = false)] - public sealed class GuidAttribute : Attribute - { - internal String _val; - //| - public GuidAttribute(String guid) - { - _val = guid; - } - //| - public String Value { get {return _val;} } - } - - //| - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - public sealed class InAttribute : Attribute - { - //| - public InAttribute() - { - } - } - - //| - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - public sealed class OutAttribute : Attribute - { - //| - public OutAttribute() - { - } - } - - //| - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - public sealed class OptionalAttribute : Attribute - { - //| - public OptionalAttribute() - { - } - } - - //| - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)] - public sealed class StructLayoutAttribute : Attribute - { - internal LayoutKind _val; - //| - public StructLayoutAttribute(LayoutKind layoutKind) - { - _val = layoutKind; - } - //| - public StructLayoutAttribute(short layoutKind) - { - _val = (LayoutKind)layoutKind; - } - //| - public LayoutKind Value { get {return _val;} } - //| - public int Pack; - //| - public int Size; - //| - public CharSet CharSet; - } - - //| - [AttributeUsage(AttributeTargets.Field, Inherited = false)] - public sealed class FieldOffsetAttribute : Attribute - { - internal int _val; - //| - public FieldOffsetAttribute(int offset) - { - _val = offset; - } - //| - public int Value { get {return _val;} } - } - - public enum GCOption { - NONE = 0, - GCFRIEND = 1, - NOGC = 2, - NOSTGC = 3, - } - - [AttributeUsage(AttributeTargets.Method, Inherited = false)] - public sealed class GCAnnotationAttribute : Attribute { - internal GCOption _options; - public GCAnnotationAttribute(GCOption options ) { - _options = options; - } - } -} diff --git a/base/Kernel/System/Runtime/InteropServices/GcHandle.cs b/base/Kernel/System/Runtime/InteropServices/GcHandle.cs deleted file mode 100644 index a593a09..0000000 --- a/base/Kernel/System/Runtime/InteropServices/GcHandle.cs +++ /dev/null @@ -1,283 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -namespace System.Runtime.InteropServices { - - using System; - using System.Threading; - using System.Runtime.CompilerServices; - - // These are the types of handles used by the EE. IMPORTANT: These must - // match the definitions in ObjectHandle.h in the EE. - //| - public enum GCHandleType - { - //| - Weak = 0, - //| - WeakTrackResurrection = 1, - //| - Normal = 2, - //| - Pinned = 3 - } - - // This class allows you to create an opaque, GC handle to any - // managed object. A GC handle is used when an object reference must be - // reachable from unmanaged memory. There are 3 kinds of roots: - // Normal - keeps the object from being collected. - // Weak - allows object to be collected and handle contents will be zeroed. - // Weak references are zeroed before the finalizer runs, so if the - // object is resurrected in the finalizer the weak reference is - // still zeroed. - // WeakTrackResurrection - Same as weak, but stays until after object is - // really gone. - // Pinned - same as normal, but allows the address of the actual object - // to be taken. - // - //| - [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)] - public struct GCHandle - { - // Allocate a handle storing the object and the type. - internal GCHandle(Object value, GCHandleType type) - { - m_handle = InternalAlloc(value, type); - - // Record if the handle is pinned. - if (type == GCHandleType.Pinned) - m_handle |= 1; - } - - // Used in the conversion functions below. - internal GCHandle(IntPtr handle) - { - InternalCheckDomain((int) handle); - m_handle = (int)handle; - } - - // Creates a new GC handle for an object. - // - // value - The object that the GC handle is created for. - // type - The type of GC handle to create. - // - // returns a new GC handle that protects the object. - //| - public static GCHandle Alloc(Object value) - { - return new GCHandle(value, GCHandleType.Normal); - } - - //| - public static GCHandle Alloc(Object value, GCHandleType type) - { - return new GCHandle(value, type); - } - - // Frees a GC handle. The caller must provide synchronization to - // prevent multiple threads from executing this simultaneously for - // a given handle. If you modify this method please modify the - // __InternalFree also which is the internal without the link-time check. - //| - public void Free() - { - // Check if the handle was never initialized for was freed. - if (m_handle == 0) - throw new InvalidOperationException("InvalidOperation_HandleIsNotInitialized"); - - // Free the handle. - InternalFree(m_handle & ~0x1); - m_handle = 0; - } - - internal void __InternalFree() - { - // Check if the handle was never initialized for was freed. - if (m_handle == 0) - throw new InvalidOperationException("InvalidOperation_HandleIsNotInitialized"); - - // Free the handle. - InternalFree(m_handle & ~0x1); - m_handle = 0; - } - - // Target property - allows getting / updating of the handle's referent. If you modify this method - - // then modify the __InternalTarget too which is the internal method without the link-time check. - //| - public Object Target - { - get - { - // Check if the handle was never initialized or was freed. - if (m_handle == 0) - throw new InvalidOperationException("InvalidOperation_HandleIsNotInitialized"); - - return InternalGet(m_handle & ~0x1); - } - - set - { - // Check if the handle was never initialized or was freed. - if (m_handle == 0) - throw new InvalidOperationException("InvalidOperation_HandleIsNotInitialized"); - - InternalSet(m_handle & ~0x1, value, (m_handle & 0x1) != 0 /* isPinned */); - } - } - - internal Object __InternalTarget - { - get - { - // Check if the handle was never initialized or was freed. - if (m_handle == 0) - throw new InvalidOperationException("InvalidOperation_HandleIsNotInitialized"); - - return InternalGet(m_handle & ~0x1); - } - } - - // Retrieve the address of an object in a Pinned handle. This throws - // an exception if the handle is any type other than Pinned. - //| - public IntPtr AddrOfPinnedObject() - { - // Check if the handle was not a pinned handle. - if ((m_handle & 1) == 0) - { - // Check if the handle was never initialized for was freed. - if (m_handle == 0) - throw new InvalidOperationException("InvalidOperation_HandleIsNotInitialized"); - - // You can only get the address of pinned handles. - throw new InvalidOperationException("InvalidOperation_HandleIsNotPinned"); - } - - // Get the address. - return InternalAddrOfPinnedObject(m_handle & ~0x1); - } - - // Determine whether this handle has been allocated or not. - //| - public bool IsAllocated - { - get - { - return m_handle != 0; - } - } - - // Used to create a GCHandle from an int. This is intended to - // be used with the reverse conversion. - //| - public static explicit operator GCHandle(IntPtr value) - { - return new GCHandle(value); - } - - // Used to get the internal integer representation of the handle out. - //| - public static explicit operator IntPtr(GCHandle value) - { - return (IntPtr)value.m_handle; - } - - // Internal native calls that this implementation uses. - internal static int InternalAlloc(Object value, GCHandleType type) - { - int result, newIndex; - if (type == GCHandleType.Pinned) { - throw new Exception("GCHandle for pinned objects not yet implemented"); - } - do { - if (nextFreeIndex < 0) { - lock (handleToken) { - if (nextFreeIndex < 0) { - if (handleTable == null) { - handleTable = new HandleData[3]; - handleTable[0].nextIndex = -1; - handleTable[1].nextIndex = 2; - handleTable[2].nextIndex = -1; - nextFreeIndex = 1; - } else { - int oldLength = handleTable.Length; - int newLength = oldLength * 2; - HandleData[] newTable = new HandleData[newLength]; - Array.Copy(handleTable, 0, newTable, 0, oldLength); - handleTable = newTable; - for (int i = oldLength+1; i < newLength; i++) { - handleTable[i].nextIndex = i + 1; - } - handleTable[newLength-1].nextIndex = -1; - nextFreeIndex = oldLength+1; - } - } - } - } - result = nextFreeIndex; - newIndex = handleTable[result].nextIndex; - } while (Interlocked.CompareExchange(ref nextFreeIndex, newIndex, result) != result); - handleTable[result].obj = value; - handleTable[result].type = type; - // TODO: We should probably maintain a list of handles of each type - return result; - } - - internal static void InternalFree(int handle) - { - handleTable[handle].obj = null; - int oldFreeIndex = nextFreeIndex; - handleTable[handle].nextIndex = oldFreeIndex; - while (Interlocked.CompareExchange(ref nextFreeIndex, handle, oldFreeIndex) != oldFreeIndex) { - oldFreeIndex = nextFreeIndex; - handleTable[handle].nextIndex = oldFreeIndex; - } - } - - internal static Object InternalGet(int handle) - { - return handleTable[handle].obj; - } - - internal static void InternalSet(int handle, Object value, bool isPinned) - { - throw new Exception("System.Runtime.InteropServices.GCHandle.InternalSet not implemented in Bartok!"); - } - - internal static void InternalCompareExchange(int handle, Object value, Object oldValue, bool isPinned) - { - if (isPinned) { - throw new Exception("CompareExchange on a pinned object?!?"); - } - Interlocked.CompareExchange(ref handleTable[handle].obj, value, oldValue); - } - - internal static IntPtr InternalAddrOfPinnedObject(int handle) - { - throw new Exception("System.Runtime.InteropServices.GCHandle.InternalAddrOfPinnedObject not implemented in Bartok!"); - } - - internal static void InternalCheckDomain(int handle) - { - throw new Exception("System.Runtime.InteropServices.GCHandle.InternalCheckDomain not implemented in Bartok!"); - } - - // The actual integer handle value that the EE uses internally. - private int m_handle; - - private static HandleData[] handleTable; - - private static int nextFreeIndex = -1; - - private static Object handleToken = new Object(); - - private struct HandleData { - public Object obj; - public GCHandleType type; - public int nextIndex; - } - } -} diff --git a/base/Kernel/System/RuntimeArgumentHandle.cs b/base/Kernel/System/RuntimeArgumentHandle.cs deleted file mode 100644 index f2d3a87..0000000 --- a/base/Kernel/System/RuntimeArgumentHandle.cs +++ /dev/null @@ -1,39 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -namespace System { - - using System; - using System.Runtime.CompilerServices; - // This value type is used for constructing System.ArgIterator. - // - // SECURITY : m_ptr cannot be set to anything other than null by untrusted - // code. - // - // This corresponds to EE VARARGS cookie. - - //| - public unsafe struct RuntimeArgumentHandle { - // Bartok will override type of m_ptr to be VarargList& - // [TracedPointer(VarargList)]. - private VarargList* m_ptr; - - internal IntPtr Pointer { - [NoHeapAllocation] - get { return (IntPtr) (UIntPtr) m_ptr; } - } - - [RequiredByBartok] - [NoHeapAllocation] - // Bartok will override type of arguments to be VarargList& - // [TracedPointer(VarargList)]. - internal static RuntimeArgumentHandle Arglist - (VarargList* arguments) { - RuntimeArgumentHandle h; - h.m_ptr = arguments; - return h; - } - } -} diff --git a/base/Kernel/System/RuntimeType.cs b/base/Kernel/System/RuntimeType.cs deleted file mode 100644 index af68b15..0000000 --- a/base/Kernel/System/RuntimeType.cs +++ /dev/null @@ -1,394 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// -// __RuntimeType is the basic Type object representing classes as found in the -// system. This type is never creatable by users, only by the system itself. -// The internal structure is known about by the runtime. __RuntimeXXX classes -// are created only once per object in the system and support == comparisons. -// -// Date: March 98 -// -namespace System { - - using Microsoft.Bartok.Runtime; - using System; - using System.Reflection; - using System.Runtime.InteropServices; - using System.Runtime.CompilerServices; - using System.Text; - using Thread = System.Threading.Thread; - using SystemType = Microsoft.Singularity.V1.Types.SystemType; - - [CCtorIsRunDuringStartup] - [StructLayout(LayoutKind.Sequential)] - internal sealed class RuntimeType : Type, ICloneable - { - // expand as needed, but also see/use TypeAttributes - // matches convert\ContainerInfo.cs::Enum_Kind - internal enum Enum_Kind { - NotSpecial = 0, - Vector = 1, - RectangleArray = 2, - Primitive = 3, - Enum = 4, - OtherValueType = 5 - }; - - private readonly Assembly assembly; - [RequiredByBartok] - private readonly RuntimeType enclosingType; - [AccessedByRuntime("Referenced from C++")] - [RequiredByBartok] - private readonly String name; - [RequiredByBartok] - private readonly String nameSpace; - - [RequiredByBartok] - internal readonly RuntimeType baseType; - [RequiredByBartok] - internal readonly System.RuntimeType[] interfaces; - [RequiredByBartok] - internal readonly Enum_Kind kind; - [RequiredByBartok] - internal readonly int rank; - [RequiredByBartok] - internal readonly TypeAttributes attributes; - [AccessedByRuntime("Referenced from C++")] - [RequiredByBartok] - internal readonly VTable classVtable; - - // Putting these here for now because we need them for classes - // and interfaces, and I don't think we build VTable objects for - // interfaces: - - [RequiredByBartok] - internal readonly System.UIntPtr cctor; // HACK: function pointer - [RequiredByBartok] - internal readonly System.UIntPtr ctor; // HACK: function pointer - [RequiredByBartok] - internal Exception cctorException; - [RequiredByBartok] - internal TypeInitState cctorState; - [RequiredByBartok] - internal System.Threading.Thread cctorThread; - - // Prevent from begin created - internal RuntimeType() { - throw new Exception("RuntimeType constructor not supported"); - } - - // Given a class handle, this will return the class for that handle. - [NoHeapAllocation] - public unsafe static Type GetTypeFromHandleImpl(RuntimeTypeHandle handle) { - IntPtr handleAddress = handle.Value; - Object obj = Magic.fromAddress((UIntPtr) handleAddress); - return Magic.toType(obj); - } - - // Return the name of the class. The name does not contain the namespace. - public override String Name { - [NoHeapAllocation] - get { return name; } - } - - public override int GetHashCode() { - return ((int)this.classVtable.arrayOf << 8 + rank) - + (int)this.classVtable.structuralView; - } - - // Return the name of the class. The name does not contain the namespace. - public override String ToString(){ - return InternalGetProperlyQualifiedName(); - } - - private String InternalGetProperlyQualifiedName() { - // markples: see also Lightning\Src\VM\COMClass.cpp::GetProperName - return FullName; - } - - private void AddFullName(StringBuilder sb) { - RuntimeType enclosing = this.enclosingType; - while(enclosing != null) { - enclosing.AddFullName(sb); - sb.Append('+'); - enclosing = enclosing.enclosingType; - } - if(nameSpace != null) { - sb.Append(nameSpace); - sb.Append('.'); - } - sb.Append(name); - } - - // Return the fully qualified name. The name does contain the - // namespace. - public override String FullName { - get { - StringBuilder sb = new StringBuilder(); - AddFullName(sb); - return sb.ToString(); - } - } - - // Return the name of the type including the assembly from which it came. - // This name can be persisted and used to reload the type at a later - // time. - public override String AssemblyQualifiedName { - get { - StringBuilder sb = new StringBuilder(); - AddFullName(sb); - sb.Append(", "); - sb.Append(this.assembly.FullName); - return sb.ToString(); - } - } - - public override Assembly Assembly { - [NoHeapAllocation] - get { return assembly; } - } - - // Return the name space. - public override String Namespace - { - [NoHeapAllocation] - get { return nameSpace; } - } - - // Returns the base class for a class. If this is an interface or has - // no base class null is returned. Object is the only Type that does not - // have a base class. - public override Type BaseType { - [NoHeapAllocation] - get { return baseType; } - } - - - public override int GetArrayRank() { - return rank; - } - - // GetInterfaces - // This method will return all of the interfaces implemented by a - // class - public override Type[] GetInterfaces() - { - return interfaces; - } - - //////////////////////////////////////////////////////////////////////////////// - // - // Attributes - // - // The attributes are all treated as read-only properties on a class. Most of - // these boolean properties have flag values defined in this class and act like - // a bit mask of attributes. There are also a set of boolean properties that - // relate to the classes relationship to other classes and to the state of the - // class inside the runtime. - // - //////////////////////////////////////////////////////////////////////////////// - - // Internal routine to get the attributes. - [NoHeapAllocation] - protected override TypeAttributes GetAttributeFlagsImpl() { - return attributes; - } - - // Internal routine to determine if this class represents an Array - [NoHeapAllocation] - protected override bool IsArrayImpl() { - return this.classVtable.arrayOf != StructuralType.None; - } - - // Internal routine to determine if this class represents a primitive type - [NoHeapAllocation] - protected override bool IsPrimitiveImpl() - { - return kind == Enum_Kind.Primitive; - } - - internal bool IsVector { - [NoHeapAllocation] - get { - return kind == Enum_Kind.Vector; - } - } - - internal bool IsRectangleArray { - [NoHeapAllocation] - get { - return kind == Enum_Kind.RectangleArray; - } - } - - [NoHeapAllocation] - public override Type GetElementType() - { - VTable element = this.classVtable.arrayElementClass; - return (element != null) ? element.vtableType : null; - } - - [NoHeapAllocation] - protected override bool HasElementTypeImpl() - { - return (IsArray); - } - - // Return the underlying Type that represents the IReflect Object. For expando object, - // this is the (Object) IReflectInstance.GetType(). For Type object it is this. - public override Type UnderlyingSystemType { - [NoHeapAllocation] - get {return this;} - } - - // - // ICloneable Implementation - // - - // RuntimeType's are unique in the system, so the only thing that we should do to clone them is - // return the current instance. - public Object Clone() { - return this; - } - - [NoHeapAllocation] - public override bool IsSubclassOf(Type c) { - Type p = this; - if (p == c) { - return false; - } - while (p != null) { - if (p == c) { - return true; - } - p = p.BaseType; - } - return false; - } - - [NoHeapAllocation] - internal override TypeCode GetTypeCodeInternal() - { - switch (classVtable.structuralView) { - case StructuralType.Bool: - return TypeCode.Boolean; - case StructuralType.Char: - return TypeCode.Object; - case StructuralType.Int8: - return TypeCode.SByte; - case StructuralType.Int16: - return TypeCode.Int16; - case StructuralType.Int32: - return TypeCode.Int32; - case StructuralType.Int64: - return TypeCode.Int64; - case StructuralType.UInt8: - return TypeCode.Byte; - case StructuralType.UInt16: - return TypeCode.UInt16; - case StructuralType.UInt32: - return TypeCode.UInt32; - case StructuralType.UInt64: - return TypeCode.UInt64; - case StructuralType.Float32: - return TypeCode.Single; - case StructuralType.Float64: - return TypeCode.Double; - default: - return TypeCode.Object; - } - } - - // This is a cache for the corresponding system-wide type - private SystemType systemType; - - public override SystemType GetSystemType() { - if (SystemType.IsNull(this.systemType)) { - // initialize it - if (this.baseType == null) { - this.systemType = SystemType.RootSystemType(); - } - else { - SystemType baseSt = this.baseType.GetSystemType(); - long lower, upper; - string fullname = this.FullName; - // for now compute an MD5 over the full name - -#if SINGULARITY_PROCESS - unsafe { - byte[] nameArray = - RuntimeTypeHash.ComputeHashAndReturnName(fullname, - out lower, - out upper); - fixed(byte* dataptr = &nameArray[0]) { - char* name = (char*)dataptr; - this.systemType = SystemType.Register(name, - fullname.Length, - lower, upper, baseSt); - } - } -#else - RuntimeTypeHash.ComputeHash(fullname, out lower, out upper); - this.systemType = SystemType.Register(fullname, - lower, - upper, - baseSt); -#endif - } - } - return this.systemType; - } - - } - - - // Pulled these methods out of the main class in order to give - // the IoSystem access to the ComputeHash function when prebinding endpoints - public class RuntimeTypeHash - { - public static void ComputeHash(string fullname, - out long lower, - out long upper) - { - ComputeHashAndReturnName(fullname, out lower, out upper); - } - - unsafe internal static byte[] ComputeHashAndReturnName(string fullname, - out long lower, - out long upper) - { - byte[] data = new byte[fullname.Length*sizeof(char)]; - String.InternalCopy(fullname, data, fullname.Length); - ComputeHash(data, out lower, out upper); - return data; - } - /// - /// Needs to be replaced with real hash computed over the - /// signature of the type - /// - unsafe internal static void ComputeHash(byte[] fullname, - out long lower, - out long upper) - { - byte[] md5 = new Microsoft.Singularity.Crypto.MD5().Hash(fullname); - - lower = ConvertToLong(md5, 0); - upper = ConvertToLong(md5, 8); - } - - private static long ConvertToLong(byte[] data, int start) { - long result = data[start]; - for (int i=1; i<8; i++) { - result = result << 8; - result |= data[start+i]; - } - return result; - } - - } -} diff --git a/base/Kernel/System/String.cs b/base/Kernel/System/String.cs deleted file mode 100644 index 0d09436..0000000 --- a/base/Kernel/System/String.cs +++ /dev/null @@ -1,3140 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================ -** -** Class: String -** -** -** Purpose: Contains headers for the String class. Actual implementations -** are in String.cpp -** -** Date: March 15, 1998 -** -===========================================================*/ -namespace System { - using System.Text; - using System; - using System.Diagnostics; - using System.Globalization; - using System.Threading; - using System.Collections; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using Microsoft.Bartok.Runtime; - - // - // For Information on these methods, please see COMString.cpp - // - // The String class represents a static string of characters. Many of - // the String methods perform some type of transformation on the current - // instance and return the result as a new String. All comparison methods are - // implemented as a part of String. As with arrays, character positions - // (indices) are zero-based. - // - //| - [CCtorIsRunDuringStartup] - public sealed class String : IComparable, ICloneable, IEnumerable { - - // - //NOTE NOTE NOTE NOTE - //These fields map directly onto the fields in an EE StringObject. See object.h for the layout. - //Don't change these fields or add any new fields without first talking to JRoxe. - // - [RequiredByBartok] - private int m_arrayLength; - [AccessedByRuntime("referenced from c++")] - private int m_stringLength; - [AccessedByRuntime("referenced from c++")] - private char m_firstChar; - - //private static readonly char FmtMsgMarkerChar='%'; - //private static readonly char FmtMsgFmtCodeChar='!'; - //These are defined in Com99/src/vm/COMStringCommon.h and must be kept in sync. - private const int TrimHead = 0; - private const int TrimTail = 1; - private const int TrimBoth = 2; - - // The Empty constant holds the empty string value. - //We need to call the String constructor so that the compiler doesn't mark this as a literal. - //Marking this as a literal would mean that it doesn't show up as a field which we can access - //from native. - //| - public static readonly String Empty = ""; - - // - //Native Static Methods - // - - // Joins an array of strings together as one string with a separator between each original string. - // - //| - public static String Join(String separator, String[] value) { - if (value==null) { - throw new ArgumentNullException("value"); - } - return Join(separator, value, 0, value.Length); - } - - /** - * Joins an array of strings together as one string with a separator - * between each original string. - * - * @param separator the string used as a separator. - * @param value the array of strings to be joined. - * @param startIndex the position within the array to start using - * strings. - * @param count the number of strings to take from the array. - * - * @return a string consisting of the strings contained in value - * from startIndex to startIndex + count - * joined together to form a single string, with each of the original - * strings separated by separator. - */ - //| - public static String Join(String separator, String[] value, - int startIndex, int count) - { - // Rusa: see also Lightning\Src\VM\COMString.cpp::JoinArray - if (separator == null) { - separator = String.Empty; - } - if (value == null) { - throw new ArgumentNullException("value array is null"); - } - if (startIndex < 0) { - throw new ArgumentOutOfRangeException("startIndex is negative"); - } - if (count < 0) { - throw new ArgumentOutOfRangeException("count is negative"); - } - if (startIndex + count > value.Length) { - throw new ArgumentOutOfRangeException("startIndex+count>value.Length"); - } - // Special case the empty list of strings - if (count == 0) { - return String.Empty; - } - // Compute the length of the new string - int newLength = 0; - int limit = startIndex + count; - for (int i = startIndex; i < limit; i++) { - String s = value[i]; - if (s != null) { - newLength += s.m_stringLength; - } - } - newLength += (count - 1) * separator.m_stringLength; - // Create a new String - String result = FastAllocateString(newLength); - if (newLength == 0) { - return result; - } - // Fill in the string - int dstIndex = 0; - String firstString = value[startIndex]; - if (firstString != null) { - FillString(result, 0, firstString); - dstIndex = firstString.m_stringLength; - } - for (int i = startIndex+1; i < limit; i++) { - FillString(result, dstIndex, separator); - dstIndex += separator.m_stringLength; - String elementString = value[i]; - if (elementString != null) { - FillString(result, dstIndex, elementString); - dstIndex += elementString.m_stringLength; - } - } - return result; - } - - private unsafe static bool CaseInsensitiveCompHelper(char * strAChars, - char * strBChars, - int aLength, - int bLength, - out int result) { - char charA; - char charB; - char *strAStart; - - strAStart = strAChars; - - result = 0; - - // setup the pointers so that we can always increment them. - // We never access these pointers at the negative offset. - strAChars--; - strBChars--; - - do { - strAChars++; strBChars++; - - charA = *strAChars; - charB = *strBChars; - - //Case-insensitive comparison on chars greater than 0x80 requires - //a locale-aware casing operation and we're not going there. - if (charA>=0x80 || charB>=0x80) { - //[SOSP] we should be able to fix this. - return false; - } - - // Do the right thing if they differ in case only. - // We depend on the fact that the uppercase and lowercase letters - // in the range which we care about (A-Z,a-z) differ only by the - // 0x20 bit. - // The check below takes the xor of the two characters and - // determines if this bit is only set on one of them. - // If they're different cases, we know that we need to execute - // only one of the conditions within block. - if (((charA^charB)&0x20) != 0) { - if (charA>='A' && charA<='Z') { - charA |= (char)0x20; - } else if (charB>='A' && charB<='Z') { - charB |= (char)0x20; - } - } - } while (charA==charB && charA!=0); - - // Return the (case-insensitive) difference between them. - if (charA!=charB) { - result = (int)(charA-charB); - return true; - } - - // The length of b was unknown because it was just a pointer to a - // null-terminated string. - // If we get here, we know that both A and B are pointing at a null. - // However, A can have an embedded null. Check the number of - // characters that we've walked in A against the expected length. - if (bLength==-1) { - if ((strAChars - strAStart)!=aLength) { - result = 1; - return true; - } - result=0; - return true; - } - - result = (aLength - bLength); - return true; - } - - internal unsafe static int nativeCompareOrdinal(String strA, String strB, - bool bIgnoreCase) - { - // Rusa: see also Lightning\Src\VM\COMString.cpp::FCCompareOrdinal - int strALength = strA.Length; - int strBLength = strB.Length; - fixed(char * strAChars = &strA.m_firstChar) { - fixed(char * strBCharsStart = &strB.m_firstChar) { - - // Need copy because fixed vars are readonly - char * strBChars = strBCharsStart; - - // Handle the comparison where we wish to ignore case. - if (bIgnoreCase) { - int result; - if (CaseInsensitiveCompHelper(strAChars, strBChars, - strALength, strBLength, - out result)) { - return result; - } else { - // This will happen if we have characters greater - // than 0x7F. - throw new ArgumentException(); - } - } - - // If the strings are the same length, compare exactly the - // right # of chars. If they are different, compare the - // shortest # + 1 (the '\0'). - int count = strALength; - if (count > strBLength) - count = strBLength; - - long diff = (byte*)strAChars - (byte*)strBChars; - - // Loop comparing a DWORD at a time. - while ((count -= 2) >= 0) { - if(((*(int*)((byte*)strBChars + diff)) - - *(int*)strBChars) != 0) { - char * ptr1 = (char*)((byte*)strBChars + diff); - char * ptr2 = strBChars; - if (*ptr1 != *ptr2) { - return ((int)*ptr1 - (int)*ptr2); - } - return ((int)*(ptr1+1) - (int)*(ptr2+1)); - } - - // differs from COMString.cpp because they have DWORD* - // and we use char* - strBChars += 2; - } - - // Handle an extra WORD. - int c; - if (count == -1) { - c = *((char*)((byte*)strBChars+diff)) - *strBChars; - if(c != 0) { - return c; - } - } - return strALength - strBLength; - } - } - } - - internal static int nativeCompareOrdinalEx(String strA, int indexA, - String strB, int indexB, - int count) - { - // Rusa: see also Lightning\Src\VM\COMString.cpp::CompareOrdinalEx - throw new Exception("System.String.nativeCompareOrdinalEx not implemented in Bartok!"); - } - - // - // This is a helper method for the security team. They need to uppercase some strings (guaranteed to be less - // than 0x80) before security is fully initialized. Without security initialized, we can't grab resources (the nlp's) - // from the assembly. This provides a workaround for that problem and should NOT be used anywhere else. - // - internal static String SmallCharToUpper(String strA) - { - String newString = FastAllocateString(strA.Length); - nativeSmallCharToUpper(strA, newString); - return newString; - } - - private static void nativeSmallCharToUpper(String strIn, String strOut) - { - // Rusa: see also Lightning\Src\VM\COMString.cpp::SmallCharToUpper - throw new Exception("System.String.nativeSmallCharToUpper not implemented in Bartok!"); - } - - // This is a helper method for the security team. They need to construct strings from a char[] - // within their homebrewed XML parser. They guarantee that the char[] they pass in isn't null and - // that the provided indices are valid so we just stuff real fast. - internal static String CreateFromCharArray(char[] array, int start, int count) - { - String newString = FastAllocateString(count); - FillStringArray(newString, 0, array, start, count); - return newString; - } - - // - // - // NATIVE INSTANCE METHODS - // - // - - // - // Search/Query methods - // - - // Determines whether two strings match. - //| - public override bool Equals(Object obj) { - if (obj is String) { - return this.Equals((String) obj); - } else { - return false; - } - } - - // Determines whether two strings match. - //| - public unsafe bool Equals(String value) { - if (value == null) { - return false; - } - if (this.m_stringLength != value.m_stringLength) { - return false; - } - fixed (char *thisCharPtr = &this.m_firstChar) { - fixed (char *valueCharPtr = &value.m_firstChar) { - char *thisCursor = thisCharPtr; - char *valueCursor = valueCharPtr; - for (int i = this.m_stringLength; i > 0; i--) { - if (*thisCursor != *valueCursor) { - return false; - } - thisCursor++; - valueCursor++; - } - } - } - return true; - } - - // Determines whether two Strings match. - //| - public static bool Equals(String a, String b) { - if ((Object)a==(Object)b) { - return true; - } - - if ((Object)a==null || (Object)b==null) { - return false; - } - - return a.Equals(b); - } - - //| - public static bool operator == (String a, String b) - { - return String.Equals(a, b); - } - - //| - public static bool operator != (String a, String b) - { - return !String.Equals(a, b); - } - - internal unsafe char InternalGetChar(int index) - { - // Rusa: see also Lightning\Src\VM\COMString.cpp::GetCharAt - if ((uint) index >= (uint) this.m_stringLength) { - throw new IndexOutOfRangeException(); - } - fixed (char *charPtr = &this.m_firstChar) { - return charPtr[index]; - } - } - - // Gets the character at a specified position. - // - //| - public char this[int index] { - get { return InternalGetChar(index); } - } - - internal unsafe int InternalGetChars(char *output, int maxput) - { - fixed (char *charPtr = &this.m_firstChar) { - int i; - for (i = 0; i < m_stringLength && i < maxput; i++) { - output[i] = charPtr[i]; - } - return i; - } - } - - // Converts a substring of this string to an array of characters. Copies the - // characters of this string beginning at position startIndex and ending at - // startIndex + length - 1 to the character array buffer, beginning - // at bufferStartIndex. - // - //| - public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) - { - if (destination == null) - throw new ArgumentNullException("destination"); - if (count < 0) - throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_NegativeCount"); - if (sourceIndex < 0) - throw new ArgumentOutOfRangeException("sourceIndex", "ArgumentOutOfRange_Index"); - if (count > Length - sourceIndex) - throw new ArgumentOutOfRangeException("sourceIndex", "ArgumentOutOfRange_IndexCount"); - if (destinationIndex > destination.Length-count || destinationIndex < 0) - throw new ArgumentOutOfRangeException("destinationIndex", "ArgumentOutOfRange_IndexCount"); - InternalCopyTo(sourceIndex, destination, destinationIndex, count); - } - - internal unsafe void InternalCopyTo(int sourceIndex, char[] destination, - int destinationIndex, int count) { - // Rusa: see also Lightning\Src\VM\COMString.cpp::GetPreallocatedCharArray - if (sourceIndex + count > this.m_stringLength) { - throw new ArgumentOutOfRangeException(); - } - if (count > 0) { - // Necessary test as &destination[0] is illegal for empty array - fixed (char *srcPtrFixed = &this.m_firstChar) { - fixed (char *dstPtrFixed = destination) { - char *srcPtr = srcPtrFixed + sourceIndex; - char *dstPtr = dstPtrFixed + destinationIndex; - Buffer.MoveMemory((byte *)dstPtr, (byte *)srcPtr, - count * sizeof(char)); - } - } - } - } - - internal unsafe void CopyToByteArray(int sourceIndex, byte[] destination, - int destinationIndex, int charCount) - { - // Rusa: see also Lightning\Src\VM\COMString.cpp::InternalCopyToByteArray - // Rusa: apparently there are no calls to this method - // arlied: Actually, there is at least one call to this method, and I need to use it. - // arlied: It's called by System.Text.UnicodeEncoding.GetBytes() for little-endian Unicode. - - if (destination == null) - throw new ArgumentNullException("destination"); - if (charCount < 0) - throw new ArgumentOutOfRangeException("charCount"); - if (destinationIndex < 0) - throw new ArgumentOutOfRangeException("destinationIndex"); - if (sourceIndex < 0) - throw new ArgumentOutOfRangeException("sourceIndex"); - if (sourceIndex + charCount > this.m_stringLength) - throw new ArgumentOutOfRangeException("charCount"); - - int byteCount = charCount * 2; - if (destinationIndex + byteCount > destination.Length) - throw new ArgumentOutOfRangeException("destinationLength"); - - if (charCount > 0) { - fixed (byte* dest = &destination[destinationIndex]) - fixed (char* src_char0 = &this.m_firstChar) { - byte* src_bytes = (byte*)(&src_char0[sourceIndex]); - Buffer.MoveMemory(dest, src_bytes, byteCount); - } - } - } - - // Returns the entire string as an array of characters. - //| - public char[] ToCharArray() { - return ToCharArray(0,Length); - } - - // Returns a substring of this string as an array of characters. - // - //| - public char[] ToCharArray(int startIndex, int length) - { - // Range check everything. - if (startIndex < 0 || startIndex > Length || startIndex > Length - length) - throw new ArgumentOutOfRangeException("startIndex", "ArgumentOutOfRange_Index"); - if (length < 0) - throw new ArgumentOutOfRangeException("length", "ArgumentOutOfRange_Index"); - - char[] chars = new char[length]; - InternalCopyTo(startIndex, chars, 0, length); - return chars; - } - - // Gets a hash code for this string. If strings A and B are such that A.Equals(B), then - // they will return the same hash code. - //| - public unsafe override int GetHashCode() { - // Rusa: see also Lightning\Src\VM\COMString.cpp::GetHashCode - fixed (char *charPtrFixed = &this.m_firstChar) { - char *charPtr = charPtrFixed; - if (charPtr[this.m_stringLength] != 0) { - throw new Exception("Bartok string is not null terminated"); - } - // Rusa: see also Lightning\Src\UtilCode.h::HashString - uint hash = 5381; - char c = *charPtr; - while (c != 0) { - hash = ((hash << 5) + hash) ^ c; - charPtr++; - c = *charPtr; - } - return (int) hash; - } - } - - // Gets the length of this string - //| - public int Length { - get { return InternalLength(); } - } - - /// This is a EE implemented function so that the JIT can recognise it and - /// treat it specially by eliminating checks on character fetches. - private int InternalLength() { - // Rusa: see also Lightning\Src\VM\COMString.cpp::Length - // Rusa: see also Lightning\Src\VM\object.h::GetStringLength - return this.m_stringLength; - } - - /// - internal int ArrayLength { - get { return (m_arrayLength); } - } - - // Used by StringBuilder - internal int Capacity { - get { return (m_arrayLength - 1); } - } - - // Creates an array of strings by splitting this string at each - // occurrence of a separator. The separator is searched for, and if found, - // the substring preceding the occurrence is stored as the first element in - // the array of strings. We then continue in this manner by searching - // the substring that follows the occurrence. On the other hand, if the separator - // is not found, the array of strings will contain this instance as its only element. - // If the separator is null - // whitespace (i.e., Character.IsWhitespace) is used as the separator. - // - //| - public String [] Split(params char [] separator) { - return Split(separator, Int32.MaxValue); - } - - /*==============================MakeSeparatorList======================== - **Args: baseString -- the string to parse for the given list of - ** separator chars. - ** Separator -- A string containing all of the split characters. - ** list -- a pointer to a caller-allocated array of ints for - ** split char indices. - ** listLength -- the number of slots allocated in list. - **Returns: A list of all of the places within baseString where instances - ** of characters in Separator occur. - **Exceptions: None. - **N.B.: This just returns silently if the caller hasn't allocated - ** enough space for the int list. - =======================================================================*/ - - // WCHAR * -> char * - // int * -> int[] - - // CHARARRAYREF --> char[] - // c->GetNumComponents --> c.Length - - // STRINGREF --> String - // s->GetStringLength --> s.Length - // s->GetBuffer --> fixed(&s.m_firstChar) - - // COMNlsInfo::nativeIsWhiteSpace --> Char.IsWhiteSpace - // ArrayContains(char,char* start,char* end) - // --> ArrayContains(char, char[] buf) - private unsafe static int MakeSeparatorList(String baseString, char[] Separator, int[] list, int listLength) { - // markples: from Lightning\Src\VM\COMString.cpp::MakeSeparatorList - int i; - int foundCount=0; - fixed (char *thisChars = &baseString.m_firstChar) { - int thisLength = baseString.Length; - - if (Separator==null || Separator.Length==0) { - //If they passed null or an empty string, - //look for whitespace. - for (i=0; iGetDataPtr(); - - int searchLength = Separator.Length; - //If they passed in a string of chars, - //actually look for those chars. - for (i=0; i= 0) { - list[foundCount++]=i; - } - } - } - return foundCount; - } - } - - // Creates an array of strings by splitting this string at each - // occurrence of a separator. The separator is searched for, and if found, - // the substring preceding the occurrence is stored as the first element in - // the array of strings. We then continue in this manner by searching - // the substring that follows the occurrence. On the other hand, if the separator - // is not found, the array of strings will contain this instance as its only element. - // If the separator is the empty string (i.e., String.Empty), then - // whitespace (i.e., Character.IsWhitespace) is used as the separator. - // If there are more than count different strings, the last n-(count-1) - // elements are concatenated and added as the last String. - // - //| - //| - - // STRINGREF --> String - // PTRARRAYREF --> String[] - // p->SetAt(0, v) --> p[0] = v - // - // LPVOID --> gone - // CQuickBytes --> gone - - // AllocateObjectArray(x,g_pStringClass) --> new String[x] - // NewString(&ref, index, size) --> ref.Substring(index, size) - - public String[] Split(char[] separator, int count) { - // Rusa: see also Lightning\Src\VM\COMString.cpp::Split - // markples: this implementation based on COMString - - int numReplaces; - int numActualReplaces; - int[] sepList; - int currIndex=0; - int arrIndex=0; - //char *thisChars; - int thisLength; - int i; - String[] splitStrings; - String temp; - - if (count<0) { - throw new ArgumentOutOfRangeException - ("count", "ArgumentOutOfRange_Negative"); - } - - //Allocate space and fill an array of ints with a list of everyplace - //within our String that a separator character occurs. - sepList = new int[this.Length]; - numReplaces = MakeSeparatorList(this, separator, sepList, this.Length); - //Handle the special case of no replaces. - if (0==numReplaces) { - splitStrings = new String[1]; - splitStrings[0] = this; - return splitStrings; - } - thisLength = Length; - - count--; - numActualReplaces = (numReplaces - public String Substring(int startIndex) { - return this.Substring (startIndex, Length-startIndex); - } - - // Returns a substring of this string. - // - //| - public String Substring(int startIndex, int length) { - - int thisLength = Length; - - //Bounds Checking. - if (startIndex<0) { - throw new ArgumentOutOfRangeException("startIndex", "ArgumentOutOfRange_StartIndex"); - } - - if (length<0) { - throw new ArgumentOutOfRangeException("length", "ArgumentOutOfRange_NegativeLength"); - } - - if (startIndex > thisLength-length) { - throw new ArgumentOutOfRangeException("length", "ArgumentOutOfRange_IndexLength"); - } - - String s = FastAllocateString(length); - FillSubstring(s, 0, this, startIndex, length); - - return s; - } - - internal String TrimHelper(char[] trimChars, int trimType) - { - // Rusa: see also Lighting\Src\VM\COMString.cpp::TrimHelper - int stringLength = this.m_stringLength; - int iLeft = 0; - int iRight = stringLength - 1; - this.TrimHelper(trimChars, trimType, ref iLeft, ref iRight); - int newLength = iRight - iLeft + 1; - if (newLength == stringLength) { - return this; - } else if (newLength == 0) { - return String.Empty; - } else { - String result = FastAllocateString(newLength); - FillSubstring(result, 0, this, iLeft, newLength); - return result; - } - } - - private unsafe void TrimHelper(char[] trimChars, int trimType, - ref int iLeft, ref int iRight) - { - fixed (char *charPtr = &this.m_firstChar) { - if (trimType == String.TrimHead || - trimType == String.TrimBoth) { - while (iLeft <= iRight && - ArrayContains(charPtr[iLeft], trimChars) >= 0) { - iLeft++; - } - } - if (trimType == String.TrimTail || - trimType == String.TrimBoth) { - while (iRight >= iLeft && - ArrayContains(charPtr[iRight], trimChars) >= 0) { - iRight--; - } - } - } - } - - private static int ArrayContains(char c, char[] charArray) { - int limit = charArray.Length; - for (int i = 0; i < limit; i++) { - if (charArray[i] == c) { - return i; - } - } - return -1; - } - - /** - * Creates a new string from the characters in a subarray. The new - * string will be created from the characters in value between - * startIndex and startIndex + length - 1. - * - * @param value an array of characters. - * @param startIndex the index at which the subarray begins. - * @param length the length of the subarray. - * - * @exception ArgumentException if value is null. - * @exception ArgumentException if startIndex or - * startIndex+length-1 are not valid indices of - * value. - */ - //| - - public static String StringCTOR(char[] value, int startIndex, - int length) { - // Rusa: see also Lightning\Src\VM\COMString.cpp::StringInitCharArray - if (value == null) { - throw new ArgumentNullException("array value is null"); - } - if (startIndex < 0) { - throw new ArgumentOutOfRangeException("startIndex is negative"); - } - if (length < 0) { - throw new ArgumentOutOfRangeException("length is negative"); - } - if (length == 0) { - return String.Empty; - } else { - String result = FastAllocateString(length); - FillStringArray(result, 0, value, startIndex, length); - return result; - } - } - - /** - * Creates a new string from the characters in a subarray. The new - * string will be created from the characters in value. - * - * @param value an array of characters. - * - * @exception ArgumentException if value is null. - */ - //| - public static String StringCTOR(char[] value) { - // Rusa: see also Lightning\Src\VM\COMString.cpp::StringInitChars - if (value == null) { - return String.Empty; - } - int length = value.Length; - if (length == 0) { - return String.Empty; - } - String result = FastAllocateString(length); - FillStringArray(result, 0, value, 0, length); - return result; - } - - internal static String NewString(String curr,int start,int copyLen, - int capacity) { - String result = FastAllocateString(capacity); - FillSubstring(result, 0, curr, start, copyLen); - return result; - } - - //| - public static String StringCTOR(char c, int count) { - // Rusa: see also Lightning\Src\VM\COMString.cpp::StringInitCharCount - if (count < 0) { - throw new ArgumentOutOfRangeException("count is negative"); - } - if (count == 0) { - return String.Empty; - } else { - String result = FastAllocateString(count); - FillStringChar(result, 0, c, count); - return result; - } - } - - // Removes a string of characters from the ends of this string. - //| - public String Trim(params char[] trimChars) { - if (null==trimChars || trimChars.Length == 0) { - trimChars=CharacterInfo.WhitespaceChars; - } - return TrimHelper(trimChars,TrimBoth); - } - - // Removes a string of characters from the beginning of this string. - //| - public String TrimStart(params char[] trimChars) { - if (null==trimChars || trimChars.Length == 0) { - trimChars=CharacterInfo.WhitespaceChars; - } - return TrimHelper(trimChars,TrimHead); - } - - - // Removes a string of characters from the end of this string. - //| - public String TrimEnd(params char[] trimChars) { - if (null==trimChars || trimChars.Length == 0) { - trimChars=CharacterInfo.WhitespaceChars; - } - return TrimHelper(trimChars,TrimTail); - } - - - // Creates a new string with the characters copied in from ptr. If - // ptr is null, a string initialized to ";<;No Object>;"; (i.e., - // String.NullString) is created. - // - // Issue: This method is only accessible from VC. - //| - [CLSCompliant(false), MethodImpl(MethodImplOptions.InternalCall)] - unsafe public extern String(char *value); - //| - [CLSCompliant(false), MethodImpl(MethodImplOptions.InternalCall)] - unsafe public extern String(char *value, int startIndex, int length); - - //| - [CLSCompliant(false), MethodImpl(MethodImplOptions.InternalCall)] - unsafe public extern String(sbyte *value); - //| - [CLSCompliant(false), MethodImpl(MethodImplOptions.InternalCall)] - unsafe public extern String(sbyte *value, int startIndex, int length); - - //| - [CLSCompliant(false), MethodImpl(MethodImplOptions.InternalCall)] - unsafe public extern String(sbyte *value, int startIndex, int length, Encoding enc); - - unsafe static private String CreateString(sbyte *value, int startIndex, int length, Encoding enc) { - if (enc == null) - return new String(value, startIndex, length); // default to ANSI - if (length < 0) - throw new ArgumentOutOfRangeException("length","ArgumentOutOfRange_NeedNonNegNum"); - if (startIndex < 0) { - throw new ArgumentOutOfRangeException("startIndex","ArgumentOutOfRange_StartIndex"); - } - if ((value + startIndex) < value) { - // overflow check - throw new ArgumentOutOfRangeException("startIndex","ArgumentOutOfRange_PartialWCHAR"); - } - byte [] b = new byte[length]; - if (length > 0) { - fixed(byte* pDest = b) { - Buffer.MoveMemory(pDest, ((byte*)value)+startIndex, length); - } - } - return enc.GetString(b); - } - - // For ASCIIEncoding::GetString() - unsafe static internal String CreateStringFromASCII(byte[] bytes, int startIndex, int length) { - Debug.Assert(bytes != null, "need a byte[]."); - Debug.Assert(startIndex >= 0 && (startIndex < bytes.Length || bytes.Length == 0), "startIndex >= 0 && startIndex < bytes.Length"); - Debug.Assert(length >= 0 && length <= bytes.Length - startIndex, "length >= 0 && length <= bytes.Length - startIndex"); - if (length == 0) - return String.Empty; - String s = FastAllocateString(length); - fixed(char* pChars = &s.m_firstChar) { - for(int i=0; i= 0 && (startIndex < bytes.Length || bytes.Length == 0), "startIndex >= 0 && startIndex < bytes.Length"); - Debug.Assert(length >= 0 && length <= bytes.Length - startIndex, "length >= 0 && length <= bytes.Length - startIndex"); - if (length == 0) - return String.Empty; - String s = FastAllocateString(length); - fixed(char* pChars = &s.m_firstChar) { - for(int i=0; iptr. If - * ptr is null, a string initialized to - * "<No Object>" (i.e., String.NullString) - * is created. - * - * Issue: This method is only accessible from VC. - * @issue The verifier needs to recognize that this is not necessarily - * secure. - * - * @param ptr this is a WCHAR *. - * - */ - //| - [CLSCompliant(false)] - public unsafe static String StringCTOR(char *valuePtr) { - // Rusa: see also Lightning\Src\VM\COMString.cpp::StringInitWCHARPtr - if (valuePtr == null) { - return String.Empty; - } - // First figure out how many characters the string has - int count = 0; - char *cursor = valuePtr; - int c = *cursor; - while (c != 0) { - count++; - cursor++; - c = *cursor; - } - // Allocate and fill in the string object - if (count == 0) { - return String.Empty; - } else { - String result = FastAllocateString(count); - FillStringCharPtr(result, 0, valuePtr, count); - return result; - } - } - - //| - [CLSCompliant(false)] - public unsafe static String StringCTOR(char *valuePtr, int startIndex, - int length) { - // Rusa: see also Lightning\Src\VM\COMString.cpp::StringInitWCHARPtrPartial - if (valuePtr == null) { - return String.Empty; - } - if (length < 0) { - throw new ArgumentOutOfRangeException("length is negative"); - } - if (startIndex < 0) { - throw new ArgumentOutOfRangeException("startIndex is negative"); - } - if (length == 0) { - return String.Empty; - } else { - String result = FastAllocateString(length); - FillStringCharPtr(result, 0, valuePtr + startIndex, length); - return result; - } - } - - //| - [CLSCompliant(false)] - public unsafe static String StringCTOR(byte *valuePtr, int startIndex, - int length) { - // Rusa: see also Lightning\Src\VM\COMString.cpp::StringInitWCHARPtrPartial - if (valuePtr == null) { - return String.Empty; - } - if (length < 0) { - throw new ArgumentOutOfRangeException("length is negative"); - } - if (startIndex < 0) { - throw new ArgumentOutOfRangeException("startIndex is negative"); - } - if (length == 0) { - return String.Empty; - } else { - String result = FastAllocateString(length); - FillStringBytePtr(result, 0, valuePtr + startIndex, length); - return result; - } - } - - //| - [CLSCompliant(false)] - public unsafe static String StringCTOR(sbyte *valuePtr) { - // Rusa: see also Lightning\Src\VM\COMString.cpp::StringInitCharPtr - return StringInitCharHelper(valuePtr, -1); - } - - //| - [CLSCompliant(false)] - public unsafe static String StringCTOR(sbyte *valuePtr, int startIndex, - int length) { - // Rusa: see also Lightning\Src\VM\COMString.cpp::StringInitCharPtrPartial - if (startIndex < 0) { - throw new ArgumentOutOfRangeException("startIndex is negative"); - } - if (length < 0) { - throw new ArgumentOutOfRangeException("length is negative"); - } - return StringInitCharHelper(valuePtr + startIndex, length); - } - - private unsafe static String StringInitCharHelper(sbyte *valuePtr, - int length) { - // Rusa: see also Lightning\Src\VM\COMString.cpp::StringInitCharHelper - throw new Exception("StringInitCharHelper is not implemented in Bartok!"); - } - - //| - [CLSCompliant(false)] - public unsafe static String StringCTOR(sbyte *value, int startIndex, - int length, Encoding enc) { - // Rusa: see also Lightning\Src\VM\COMString.cpp::StringInitSBytPtrPartialEx - throw new Exception("String(sbyte*, int, int, Encoding) not implemented in Bartok!"); - } - - private static String FastAllocateString(int stringLength) { - return GC.AllocateString(stringLength); - } - - [Inline] - internal void InitializeStringLength(int stringLength) { - this.m_arrayLength = stringLength+1; - this.m_stringLength = stringLength; - } - - private unsafe static void FillString(String dest, int destPos, - String src) { - fixed (char *srcPtr = &src.m_firstChar) { - FillStringCharPtr(dest, destPos, srcPtr, src.m_stringLength); - } - } - - private unsafe static void FillStringChecked(String dest, int destPos, - String src) { - if (! (src.Length <= dest.ArrayLength - destPos)) { - throw new IndexOutOfRangeException(); // REVIEW: param? - } - fixed (char *srcPtr = &src.m_firstChar) { - FillStringCharPtr(dest, destPos, srcPtr, src.m_stringLength); - } - } - - private unsafe static void FillStringEx(String dest, int destPos, - String src,int srcLength) { - // ASSERT(srcLength <= dest.ArrayLength - destPos); - fixed (char *srcPtr = &src.m_firstChar) { - FillStringCharPtr(dest, destPos, srcPtr, srcLength); - } - } - - private unsafe static void FillStringChar(String dest, int destPos, - char c, int count) { - fixed (char *destPtr = &dest.m_firstChar) { - char *charPtr = destPtr + destPos; - // Set the odd leader char if necessary - if (destPos % 2 == 1) { - *charPtr = c; - charPtr++; - count--; - } - // Set the buffer 2 chars at a time - int c2 = (c << 16) | c; - int *intPtr = (int *) charPtr; - count--; // Prevent overruns from odd lengths - while (count > 0) { - *intPtr = c2; - intPtr++; - count -= 2; - } - // Set the odd trailer char if necessary - if (count == 0) { - *((char *) intPtr) = c; - } - } - } - - private unsafe static void FillStringCharPtr(String dest, int destPos, - char *srcPtr, int count) { - fixed (char *charPtr = &dest.m_firstChar) { - char *destPtr = charPtr + destPos; - Buffer.MoveMemory((byte *)destPtr, (byte *)srcPtr, count * sizeof(char)); - } - } - - private unsafe static void FillStringBytePtr(String dest, int destPos, - byte *srcPtr, int count) { - fixed (char *charPtr = &dest.m_firstChar) { - char *destPtr = charPtr + destPos; - for (int i = 0; i < count; i++) { - *destPtr++ = (char)*srcPtr++; - } - } - } - - private unsafe static void FillStringArray(String dest, int stringStart, - char[] array, int charStart, - int count) { - fixed (char *destPtr = &array[charStart]) { - FillStringCharPtr(dest, stringStart, destPtr, count); - } - } - - private unsafe static void FillSubstring(String dest, int destPos, - String src, int startPos, - int count) { - fixed (char *srcPtr = &src.m_firstChar) { - FillStringCharPtr(dest, destPos, srcPtr + startPos, count); - } - } - - // Creates a new string from the characters in a subarray. The new string will - // be created from the characters in value between startIndex and - // startIndex + length - 1. - // - //| - [MethodImpl(MethodImplOptions.InternalCall)] - public extern String(char [] value, int startIndex, int length); - - // Creates a new string from the characters in a subarray. The new string will be - // created from the characters in value. - // - //| - [MethodImpl(MethodImplOptions.InternalCall)] - public extern String(char [] value); - - //| - [MethodImpl(MethodImplOptions.InternalCall)] - public extern String(char c, int count); - - - // - // - // INSTANCE METHODS - // - // - - // Provides a culture-correct string comparison. StrA is compared to StrB - // to determine whether it is lexicographically less, equal, or greater, and then returns - // either a negative integer, 0, or a positive integer; respectively. - // - //| - public static int Compare(String strA, String strB) { - return CompareInfo.Compare(strA, strB, CompareOptions.None); - } - - // Provides a culture-correct string comparison. strA is compared to strB - // to determine whether it is lexicographically less, equal, or greater, and then a - // negative integer, 0, or a positive integer is returned; respectively. - // The case-sensitive option is set by ignoreCase - // - //| - public static int Compare(String strA, String strB, bool ignoreCase) { - if (ignoreCase) { - return CompareInfo.Compare(strA, strB, CompareOptions.IgnoreCase); - } - return CompareInfo.Compare(strA, strB, CompareOptions.None); - } - - // - public static int Compare(String strA, String strB, bool ignoreCase, - CultureInfo info) - { - return Compare(strA, strB, ignoreCase); - } - - // Determines whether two string regions match. The substring of strA beginning - // at indexA of length count is compared with the substring of strB - // beginning at indexB of the same length. - // - //| - public static int Compare(String strA, int indexA, String strB, int indexB, int length) { - int lengthA = length; - int lengthB = length; - - if (strA!=null) { - if (strA.Length - indexA < lengthA) { - lengthA = (strA.Length - indexA); - } - } - - if (strB!=null) { - if (strB.Length - indexB < lengthB) { - lengthB = (strB.Length - indexB); - } - } - return CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.None); - } - - - // Determines whether two string regions match. The substring of strA beginning - // at indexA of length count is compared with the substring of strB - // beginning at indexB of the same length. Case sensitivity is determined by the ignoreCase boolean. - // - //| - public static int Compare(String strA, int indexA, String strB, int indexB, int length, bool ignoreCase) { - int lengthA = length; - int lengthB = length; - - if (strA!=null) { - if (strA.Length - indexA < lengthA) { - lengthA = (strA.Length - indexA); - } - } - - if (strB!=null) { - if (strB.Length - indexB < lengthB) { - lengthB = (strB.Length - indexB); - } - } - - if (ignoreCase) { - return CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.IgnoreCase); - } - return CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.None); - } - - // Compares this object to another object, returning an integer that - // indicates the relationship. This method returns a value less than 0 if this is less than value, 0 - // if this is equal to value, or a value greater than 0 - // if this is greater than value. Strings are considered to be - // greater than all non-String objects. Note that this means sorted - // arrays would contain nulls, other objects, then Strings in that order. - // - //| - public int CompareTo(Object value) { - if (value == null) { - return 1; - } - - if (!(value is String)) { - throw new ArgumentException("Arg_MustBeString"); - } - - return String.Compare(this,(String)value); - } - - // Determines the sorting relation of StrB to the current instance. - // - //| - public int CompareTo(String strB) { - if (strB==null) { - return 1; - } - return CompareInfo.Compare(this, strB, 0); - } - - // Compares strA and strB using an ordinal (code-point) comparison. - // - //| - public static int CompareOrdinal(String strA, String strB) { - if (strA == null || strB == null) { - if ((Object)strA==(Object)strB) { //they're both null; - return 0; - } - return (strA==null)? -1 : 1; //-1 if A is null, 1 if B is null. - } - - return nativeCompareOrdinal(strA, strB, false); - } - - // Compares strA and strB using an ordinal (code-point) comparison. - // - //| - public static int CompareOrdinal(String strA, String strB, bool ignoreCase) { - if (strA == null || strB == null) { - if ((Object)strA==(Object)strB) { //they're both null; - return 0; - } - return (strA==null)? -1 : 1; //-1 if A is null, 1 if B is null. - } - - return nativeCompareOrdinal(strA, strB, ignoreCase); - } - - // Compares strA and strB using an ordinal (code-point) comparison. - // - //| - public static int CompareOrdinal(String strA, int indexA, String strB, int indexB, int length) { - if (strA == null || strB == null) { - if ((Object)strA==(Object)strB) { //they're both null; - return 0; - } - - return (strA==null)? -1 : 1; //-1 if A is null, 1 if B is null. - } - - return nativeCompareOrdinalEx(strA, indexA, strB, indexB, length); - } - - - // Determines whether a specified string is a suffix of the the current instance. - // - // The case-sensitive and culture-sensitive option is set by options, - // and the default culture is used. - // - //| - public bool EndsWith(String value) { - if (null==value) { - throw new ArgumentNullException("value"); - } - int valueLen = value.Length; - int thisLen = this.Length; - if (valueLen>thisLen) { - return false; - } - return (0==Compare(this, thisLen-valueLen, value, 0, valueLen)); - } - - public bool EndsWith(char value) { - int thisLen = this.Length; - if (thisLen != 0) { - if (this[thisLen - 1] == value) - return true; - } - return false; - } - - - // Returns the index of the first occurrence of value in the current instance. - // The search starts at startIndex and runs thorough the next count characters. - // - //| - public int IndexOf(char value) { - return IndexOf(value, 0, this.Length); - } - - //| - public int IndexOf(char value, int startIndex) { - return IndexOf(value, startIndex, this.Length - startIndex); - } - - //| - public unsafe int IndexOf(char value, int startIndex, int count) { - // Rusa: see also Lightning\Src\VM\COMString.cpp::IndexOfChar - if (startIndex < 0 || startIndex > this.m_stringLength) { - throw new ArgumentOutOfRangeException("startIndex out of range"); - } - if (count < 0 || startIndex + count > this.m_stringLength) { - throw new ArgumentOutOfRangeException("count out of range"); - } - fixed (char *charPtr = &this.m_firstChar) { - char *cursor = charPtr + startIndex; - for (int i = count; i > 0; i--) { - if (*cursor == value) { - return (startIndex + (count - i)); - } - cursor++; - } - } - return -1; - } - - // Returns the index of the first occurrence of any character in value in the current instance. - // The search starts at startIndex and runs to endIndex-1. [startIndex,endIndex). - // - - //| - public int IndexOfAny(char [] anyOf) { - return IndexOfAny(anyOf,0, this.Length); - } - - //| - public int IndexOfAny(char [] anyOf, int startIndex) { - return IndexOfAny(anyOf, startIndex, this.Length - startIndex); - } - - //| - public unsafe int IndexOfAny(char [] anyOf, int startIndex, int count) { - // Rusa: see also Lightning\Src\VM\COMString.cpp::IndexOfCharArray - if (anyOf == null) { - throw new ArgumentNullException("anyOf array is null"); - } - if (startIndex < 0 || startIndex > this.m_stringLength) { - throw new ArgumentOutOfRangeException("startIndex out of range"); - } - if (count < 0 || startIndex + count > this.m_stringLength) { - throw new ArgumentOutOfRangeException("count out of range"); - } - fixed (char *charPtr = &this.m_firstChar) { - char *cursor = charPtr + startIndex; - for (int i = count; i > 0; i--) { - if (ArrayContains(*cursor, anyOf) >= 0) { - return (startIndex + (count - i)); - } - cursor++; - } - } - return -1; - } - - // Determines the position within this string of the first occurrence of the specified - // string, according to the specified search criteria. The search begins at - // the first character of this string, it is case-sensitive and culture-sensitive, - // and the default culture is used. - // - //| - public int IndexOf(String value) { - return CompareInfo.IndexOf(this,value); - } - - // Determines the position within this string of the first occurrence of the specified - // string, according to the specified search criteria. The search begins at - // startIndex, it is case-sensitive and culture-sensitve, and the default culture is used. - // - //| - public int IndexOf(String value, int startIndex){ - return CompareInfo.IndexOf(this,value,startIndex); - } - - // Determines the position within this string of the first occurrence of the specified - // string, according to the specified search criteria. The search begins at - // startIndex, ends at endIndex and the default culture is used. - // - //| - public int IndexOf(String value, int startIndex, int count){ - if (startIndex + count > this.Length) { - throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_Index"); - } - return CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.None); - } - - - // Returns the index of the last occurrence of value in the current instance. - // The search starts at startIndex and runs to endIndex. [startIndex,endIndex]. - // The character at position startIndex is included in the search. startIndex is the larger - // index within the string. - // - //| - public int LastIndexOf(char value) { - return LastIndexOf(value, this.Length-1, this.Length); - } - - //| - public int LastIndexOf(char value, int startIndex){ - return LastIndexOf(value,startIndex,startIndex + 1); - } - - //| - public unsafe int LastIndexOf(char value, int startIndex, int count) { - // Rusa: see also Lightning\Src\VM\COMString.cpp::LastIndexOfChar - if (this.m_stringLength == 0) { - return -1; - } - if (startIndex < 0 || startIndex >= this.m_stringLength) { - throw new ArgumentOutOfRangeException("startIndex out of range"); - } - if (count < 0 || count - 1 > startIndex) { - throw new ArgumentOutOfRangeException("count out of range"); - } - fixed (char *charPtr = &this.m_firstChar) { - char *cursor = charPtr + startIndex; - for (int i = count; i > 0; i--) { - if (*cursor == value) { - return (startIndex - (count - i)); - } - cursor--; - } - } - return -1; - } - - // Returns the index of the last occurrence of any character in value in the current instance. - // The search starts at startIndex and runs to endIndex. [startIndex,endIndex]. - // The character at position startIndex is included in the search. startIndex is the larger - // index within the string. - // - - //| - public int LastIndexOfAny(char [] anyOf) { - return LastIndexOfAny(anyOf,this.Length-1,this.Length); - } - - //| - public int LastIndexOfAny(char [] anyOf, int startIndex) { - return LastIndexOfAny(anyOf,startIndex,startIndex + 1); - } - - //| - public unsafe int LastIndexOfAny(char [] anyOf, int startIndex, - int count) { - // Rusa: see also Lightning\Src\VM\COMString.cpp::LastIndexOfCharArray - if (anyOf == null) { - throw new ArgumentNullException("anyOf array is null"); - } - if (this.m_stringLength == 0) { - return -1; - } - if (startIndex < 0 || startIndex >= this.m_stringLength) { - throw new ArgumentOutOfRangeException("startIndex out of range"); - } - if (count < 0 || count - 1 > startIndex) { - throw new ArgumentOutOfRangeException("count out of range"); - } - fixed (char *charPtr = &this.m_firstChar) { - char *cursor = charPtr + startIndex; - for (int i = count; i > 0; i--) { - if (ArrayContains(*cursor, anyOf) >= 0) { - return (startIndex - (count - 1)); - } - cursor--; - } - } - return -1; - } - - // Returns the index of the last occurrence of any character in value in the current instance. - // The search starts at startIndex and runs to endIndex. [startIndex,endIndex]. - // The character at position startIndex is included in the search. startIndex is the larger - // index within the string. - // - //| - public int LastIndexOf(String value) { - return LastIndexOf(value, this.Length-1,this.Length); - } - - //| - public int LastIndexOf(String value, int startIndex) { - return LastIndexOf(value, startIndex, startIndex + 1); - } - - //| - public int LastIndexOf(String value, int startIndex, int count) { - if (count<0) { - throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_Count"); - } - return CompareInfo.LastIndexOf(this, value, startIndex, count, CompareOptions.None); - } - - - // - // - //| - public String PadLeft(int totalWidth) { - return PadHelper(totalWidth, ' ', false); - } - - //| - public String PadLeft(int totalWidth, char paddingChar) { - return PadHelper(totalWidth, paddingChar, false); - } - - //| - public String PadRight(int totalWidth) { - return PadHelper(totalWidth, ' ', true); - } - - //| - public String PadRight(int totalWidth, char paddingChar) { - return PadHelper(totalWidth, paddingChar, true); - } - - private String PadHelper(int totalWidth, char paddingChar, - bool isRightPadded) { - // Rusa: see also Lightning\Src\VM\COMString.cpp::PadHelper - if (totalWidth < 0) { - throw new ArgumentOutOfRangeException("totalWidth is negative"); - } - if (totalWidth <= this.m_stringLength) { - return this; - } - int padCount = totalWidth - this.m_stringLength; - String result = FastAllocateString(totalWidth); - if (isRightPadded) { - FillString(result, 0, this); - FillStringChar(result, this.m_stringLength, - paddingChar, padCount); - } else { - - FillStringChar(result, 0, paddingChar, padCount); - FillString(result, padCount, this); - } - return result; - } - - // Determines whether a specified string is a prefix of the current instance - // - //| - public bool StartsWith(String value) { - if (null==value) { - throw new ArgumentNullException("value"); - } - if (this.Length - public String ToLower() { - return TextInfo.ToLower(this); - } - - // Creates a copy of this string in upper case. - //| - public String ToUpper() { - return TextInfo.ToUpper(this); - } - - // Returns this string. - //| - public override String ToString() { - return this; - } - - // Method required for the ICloneable interface. - // There's no point in cloning a string since they're immutable, so we simply return this. - //| - public Object Clone() { - return this; - } - - - // Trims the whitespace from both ends of the string. Whitespace is defined by - // CharacterInfo.WhitespaceChars. - // - //| - public String Trim() { - return this.Trim(CharacterInfo.WhitespaceChars); - } - - // - // - //| - public String Insert(int startIndex, String value) { - // Rusa: see also Lightning\Src\VM\COMString.cpp::Insert - if (startIndex < 0 || startIndex > this.m_stringLength) { - throw new ArgumentOutOfRangeException("startIndex out of range"); - } - if (value == null) { - throw new ArgumentNullException("value string is null"); - } - int newLength = this.m_stringLength + value.m_stringLength; - String result = FastAllocateString(newLength); - FillSubstring(result, 0, this, 0, startIndex); - FillSubstring(result, startIndex, value, 0, value.m_stringLength); - FillSubstring(result, startIndex + value.m_stringLength, - this, startIndex, this.m_stringLength - startIndex); - return result; - } - - internal unsafe void InsertHoleInString(int startIndex, int holeSize) { - int tail = this.m_stringLength - startIndex; - this.m_stringLength += holeSize; - if (tail > 0) { - fixed (char *charPtr = &this.m_firstChar) { - char *srcPtr = charPtr + startIndex; - char *dstPtr = charPtr + holeSize; - Buffer.MoveMemory((byte *)dstPtr, (byte *)srcPtr, - tail * sizeof(char)); - } - } - } - - internal unsafe void InsertStringOverwrite(String value, int startIndex, int valueLength) { - fixed (char *charPtr = &this.m_firstChar) { - char *dstPtr = charPtr + startIndex; - fixed (char *srcPtr = &value.m_firstChar) { - Buffer.MoveMemory((byte *)dstPtr, (byte *)srcPtr, - valueLength * sizeof(char)); - } - } - } - - // Replaces all instances of oldChar with newChar. - // - //| - public String Replace(char oldChar, char newChar) { - // Rusa: see also Lightning\Src\VM\COMString.cpp::Replace - String result = FastAllocateString(this.m_stringLength); - this.Replace(oldChar, newChar, result); - return result; - } - - private unsafe void Replace(char oldChar, char newChar, String newString) { - fixed (char *srcPtr = &this.m_firstChar) { - fixed (char *destPtr = &newString.m_firstChar) { - char *srcCursor = srcPtr; - char *destCursor = destPtr; - for (int i = this.m_stringLength; i > 0; i--) { - char c = *srcCursor; - *destCursor = (c == oldChar) ? newChar : c; - srcCursor++; - destCursor++; - } - } - } - } - - // This method contains the same functionality as StringBuilder Replace. The only difference is that - // a new String has to be allocated since Strings are immutable - //| - public String Replace(String oldValue, String newValue) { - // Rusa: see also Lightning\Src\VM\COMString.cpp::ReplaceString - if (oldValue == null) { - throw new ArgumentNullException("oldString is null"); - } - if (newValue == null) { - newValue = String.Empty; - } - if (oldValue.m_stringLength == 0) { - throw new ArgumentException("oldString is empty string"); - } - int matchIndex = LocalIndexOfString(oldValue, 0); - if (matchIndex < 0) { - return this; - } - // Compute a table of where to insert newValue - int replaceCount = 0; - int[] replacementTable = new int[(this.m_stringLength - matchIndex) / oldValue.m_stringLength + 1]; - do { - replacementTable[replaceCount] = matchIndex; - replaceCount++; - matchIndex = LocalIndexOfString(oldValue, matchIndex+oldValue.m_stringLength); - } while (matchIndex >= 0); - int newLength = this.m_stringLength + replaceCount * (newValue.m_stringLength - oldValue.m_stringLength); - String result = FastAllocateString(newLength); - int srcIndex = 0; - int destIndex = 0; - for (int replIndex = 0; replIndex < replaceCount; replIndex++) { - int count = replacementTable[replIndex] - srcIndex; - FillSubstring(result, destIndex, this, srcIndex, count); - srcIndex += count + oldValue.m_stringLength; - destIndex += count; - FillString(result, destIndex, newValue); - destIndex += newValue.m_stringLength; - } - FillSubstring(result, destIndex, this, srcIndex, - this.m_stringLength - srcIndex); - return result; - } - - private unsafe int LocalIndexOfString(String pattern, int startPos) { - fixed (char *stringPtr = &this.m_firstChar) { - fixed (char *patternPtr = &pattern.m_firstChar) { - char *stringCharPtr = stringPtr + startPos; - int count = this.m_stringLength - pattern.m_stringLength - - startPos + 1; - for (int index = 0; index < count; index++) { - char *stringCursor = stringCharPtr + index; - char *patternCursor = patternPtr; - int i = pattern.m_stringLength; - while (i > 0 && *stringCursor == *patternCursor) { - i--; - stringCursor++; - patternCursor++; - } - if (i == 0) { - return (startPos + index); - } - } - } - } - return -1; - } - - //| - public String Remove(int startIndex, int count) { - if (count < 0) { - throw new ArgumentOutOfRangeException("count is negative"); - } - if (startIndex < 0) { - throw new ArgumentOutOfRangeException("startIndex is negative"); - } - if (startIndex + count > this.m_stringLength) { - throw new ArgumentOutOfRangeException("startIndex+count>Length"); - } - int newLength = this.m_stringLength - count; - String result = FastAllocateString(newLength); - FillSubstring(result, 0, this, 0, startIndex); - FillSubstring(result, startIndex, this, startIndex + count, - newLength - startIndex); - return result; - } - - internal unsafe void RemoveRange(int startIndex, int count) { - int tail = this.m_stringLength - startIndex; - this.m_stringLength -= count; - if (tail > 0) { - fixed (char *charPtr = &this.m_firstChar) { - char *dstPtr = charPtr + startIndex; - char *srcPtr = dstPtr + count; - Buffer.MoveMemory((byte *)dstPtr, (byte *)srcPtr, - tail * sizeof(char)); - } - } - } - - //| - public static String Format(String format, Object arg0) { - return Format(format, new Object[] {arg0}); - } - - //| - public static String Format(String format, Object arg0, Object arg1) { - return Format(format, new Object[] {arg0, arg1}); - } - - //| - public static String Format(String format, Object arg0, Object arg1, Object arg2) { - return Format(format, new Object[] {arg0, arg1, arg2}); - } - - //| - public static String Format(String format, params Object[] args) { - if (format == null || args == null) - throw new ArgumentNullException((format==null)?"format":"args"); - StringBuilder sb = new StringBuilder(format.Length + args.Length * 8); - sb.AppendFormat(format,args); - return sb.ToString(); - } - - //| - public static String Copy (String str) { - if (str==null) { - throw new ArgumentNullException("str"); - } - - int length = str.Length; - - String result = FastAllocateString(length); - FillString(result, 0, str); - return result; - } - - // Used by StringBuilder to avoid data corruption - internal static String InternalCopy (String str) { - int length = str.Length; - String result = FastAllocateString(length); - FillStringEx(result, 0, str, length); // The underlying's String can changed length is StringBuilder - return result; - } - - //| - public static String Concat(Object arg0) { - if (arg0==null) { - return String.Empty; - } - return arg0.ToString(); - } - - //| - public static String Concat(Object arg0, Object arg1) { - if (arg0==null) { - arg0 = String.Empty; - } - - if (arg1==null) { - arg1 = String.Empty; - } - return Concat(arg0.ToString(), arg1.ToString()); - } - - //| - public static String Concat(Object arg0, Object arg1, Object arg2) { - if (arg0==null) { - arg0 = String.Empty; - } - - if (arg1==null) { - arg1 = String.Empty; - } - - if (arg2==null) { - arg2 = String.Empty; - } - - return Concat(arg0.ToString(), arg1.ToString(), arg2.ToString()); - } - - //| - public static String Concat(params Object[] args) { - if (args==null) { - throw new ArgumentNullException("args"); - } - - String[] sArgs = new String[args.Length]; - int totalLength=0; - - for (int i=0; i - public static String Concat(String str0, String str1) { - if (str0 == null) { - if (str1==null) { - return String.Empty; - } - return str1; - } - - if (str1==null) { - return str0; - } - - int str0Length = str0.Length; - - String result = FastAllocateString(str0Length + str1.Length); - - FillStringChecked(result, 0, str0); - FillStringChecked(result, str0Length, str1); - - return result; - } - - //| - public static String Concat(String str0, String str1, String str2) { - if (str0==null && str1==null && str2==null) { - return String.Empty; - } - - if (str0==null) { - str0 = String.Empty; - } - - if (str1==null) { - str1 = String.Empty; - } - - if (str2 == null) { - str2 = String.Empty; - } - - int totalLength = str0.Length + str1.Length + str2.Length; - - String result = FastAllocateString(totalLength); - FillStringChecked(result, 0, str0); - FillStringChecked(result, str0.Length, str1); - FillStringChecked(result, str0.Length + str1.Length, str2); - - return result; - } - - //| - public static String Concat(String str0, String str1, String str2, String str3) { - if (str0==null && str1==null && str2==null && str3==null) { - return String.Empty; - } - - if (str0==null) { - str0 = String.Empty; - } - - if (str1==null) { - str1 = String.Empty; - } - - if (str2 == null) { - str2 = String.Empty; - } - - if (str3 == null) { - str3 = String.Empty; - } - - int totalLength = str0.Length + str1.Length + str2.Length + str3.Length; - - String result = FastAllocateString(totalLength); - FillStringChecked(result, 0, str0); - FillStringChecked(result, str0.Length, str1); - FillStringChecked(result, str0.Length + str1.Length, str2); - FillStringChecked(result, str0.Length + str1.Length + str2.Length, str3); - - return result; - } - - private static String ConcatArray(String[] values, int totalLength) { - String result = FastAllocateString(totalLength); - int currPos=0; - - for (int i=0; i - public static String Concat(params String[] values) { - int totalLength=0; - - if (values==null) { - throw new ArgumentNullException("values"); - } - - // Always make a copy to prevent changing the array on another thread. - String[] internalValues = new String[values.Length]; - - for (int i = 0; i< values.Length; i++) { - string value = values[i]; - internalValues[i] = ((value==null)?(String.Empty):(value)); - totalLength += internalValues[i].Length; - // check for overflow - if (totalLength < 0) { - throw new OutOfMemoryException(); - } - } - - return ConcatArray(internalValues, totalLength); - } - - //| - public static String Intern(String str) { - str.CheckHighChars(); - if (str == null) { - throw new ArgumentNullException("str"); - } - if (internedStringVector == null) { - InitializeInternedTable(); - } - int code = str.GetHashCode(); - lock (internedStringVector) { - int[] codeTable= internedStringHashCodes; - String[] stringTable = internedStringVector; - int tableSize = codeTable.Length; - int indexMask = tableSize - 1; - int hx = code & indexMask; - //Scan up from our initial hash index guess - while (true) { // It is guaranteed there are holes in table - int tableCode = codeTable[hx]; - if (tableCode == code) { - String s = stringTable[hx]; - if (str.Equals(s)) { - return s; - } - } else if (tableCode == 0 && stringTable[hx] == null) { - // We need to insert the string here! - int stringLength = str.Length; - if (str.m_arrayLength > stringLength+1) { - str = NewString(str, 0, stringLength, stringLength); - } - internedStringCount++; - stringTable[hx] = str; // Set string entry first - codeTable[hx] = code; // then set code! - if (internedStringCount >= internedStringBound) { - RehashInternedStrings(); - } - return str; - } - hx++; - hx &= indexMask; - } - } - } - - //| - public static String IsInterned(String str) { - if (str == null) { - throw new ArgumentNullException("str"); - } - if (internedStringVector == null) { - InitializeInternedTable(); - } - int code = str.GetHashCode(); - int[] codeTable; - String[] stringTable; - do { - codeTable = internedStringHashCodes; - stringTable = internedStringVector; - } while (codeTable.Length != stringTable.Length); - int tableSize = codeTable.Length; - int indexMask = tableSize - 1; - int hx = code & indexMask; - // Scan up from our initial hash index guess - while (true) { // It is guaranteed there are holes in table - int tableCode = codeTable[hx]; - if (tableCode == code) { - String s = stringTable[hx]; - if (str.Equals(s)) { - return s; - } - } else if (tableCode == 0 && stringTable[hx] == null) { - return null; - } - hx++; - hx &= indexMask; - } - } - - private static void InitializeInternedTable() { - lock (typeof(String)) { - if (internedStringVector != null) { - return; - } - int tableSize = 128; - int constantCount = internedStringConstants.Length; - while (tableSize <= constantCount) { - tableSize <<= 1; - } - int indexMask = tableSize - 1; - String[] stringTable = new String[tableSize]; - int[] codeTable = new int[tableSize]; - for (int i = 0; i < constantCount; i++) { - String s = internedStringConstants[i]; - int code = s.GetHashCode(); - int hx = code & indexMask; - while (true) { - int tableCode = codeTable[hx]; - VTable.Assert(tableCode != code || - !s.Equals(stringTable[hx]), - "Duplicate interned strings!"); - if (tableCode == 0 && stringTable[hx] == null) { - internedStringCount++; - stringTable[hx] = s; - codeTable[hx] = code; - break; - } - hx++; - hx &= indexMask; - } - } - internedStringBound = tableSize >> 1; - internedStringHashCodes = codeTable; - internedStringVector = stringTable; - } - } - - private static void RehashInternedStrings() { - int[] oldCodeTable = internedStringHashCodes; - String[] oldStringTable = internedStringVector; - int oldSize = oldCodeTable.Length; - int oldIndexMask = oldSize - 1; - int newSize = oldSize << 1; - int newIndexMask = newSize -1; - int[] newCodeTable = new int[newSize]; - String[] newStringTable = new String[newSize]; - for (int i = 0; i < oldSize; i++) { - String s = oldStringTable[i]; - if (s != null) { - int code = oldCodeTable[i]; - int hx = code & newIndexMask; - while (true) { - if (newCodeTable[hx] == 0 && - newStringTable[hx] == null) { - newStringTable[hx] = s; - newCodeTable[hx] = code; - break; - } - hx++; - hx &= newIndexMask; - } - } - } - internedStringBound = newSize >> 1; - internedStringHashCodes = newCodeTable; - internedStringVector = newStringTable; - } - - private static int internedStringCount; - private static int internedStringBound; - private static int[] internedStringHashCodes; - private static String[] internedStringVector; - private static String[] internedStringConstants; - - private static readonly bool [] HighCharTable = { - false, // 0x00, 0x0 - true, // 0x01, . - true, // 0x02, . - true, // 0x03, . - true, // 0x04, . - true, // 0x05, . - true, // 0x06, . - true, // 0x07, . - true, // 0x08, . - false, // 0x09, - false, // 0x0A, - false, // 0x0B, . - false, // 0x0C, . - false, // 0x0D, - true, // 0x0E, . - true, // 0x0F, . - true, // 0x10, . - true, // 0x11, . - true, // 0x12, . - true, // 0x13, . - true, // 0x14, . - true, // 0x15, . - true, // 0x16, . - true, // 0x17, . - true, // 0x18, . - true, // 0x19, . - true, // 0x1A, - true, // 0x1B, . - true, // 0x1C, . - true, // 0x1D, . - true, // 0x1E, . - true, // 0x1F, . - false, // 0x20, - false, // 0x21, ! - false, // 0x22, " - false, // 0x23, # - false, // 0x24, $ - false, // 0x25, % - false, // 0x26, & - true, // 0x27, ' - false, // 0x28, ( - false, // 0x29, ) - false, // 0x2A * - false, // 0x2B, + - false, // 0x2C, , - true, // 0x2D, - - false, // 0x2E, . - false, // 0x2F, / - false, // 0x30, 0 - false, // 0x31, 1 - false, // 0x32, 2 - false, // 0x33, 3 - false, // 0x34, 4 - false, // 0x35, 5 - false, // 0x36, 6 - false, // 0x37, 7 - false, // 0x38, 8 - false, // 0x39, 9 - false, // 0x3A, : - false, // 0x3B, ; - false, // 0x3C, < - false, // 0x3D, = - false, // 0x3E, > - false, // 0x3F, ? - false, // 0x40, @ - false, // 0x41, A - false, // 0x42, B - false, // 0x43, C - false, // 0x44, D - false, // 0x45, E - false, // 0x46, F - false, // 0x47, G - false, // 0x48, H - false, // 0x49, I - false, // 0x4A, J - false, // 0x4B, K - false, // 0x4C, L - false, // 0x4D, M - false, // 0x4E, N - false, // 0x4F, O - false, // 0x50, P - false, // 0x51, Q - false, // 0x52, R - false, // 0x53, S - false, // 0x54, T - false, // 0x55, U - false, // 0x56, V - false, // 0x57, W - false, // 0x58, X - false, // 0x59, Y - false, // 0x5A, Z - false, // 0x5B, [ - false, // 0x5C, \ - false, // 0x5D, ] - false, // 0x5E, ^ - false, // 0x5F, _ - false, // 0x60, ` - false, // 0x61, a - false, // 0x62, b - false, // 0x63, c - false, // 0x64, d - false, // 0x65, e - false, // 0x66, f - false, // 0x67, g - false, // 0x68, h - false, // 0x69, i - false, // 0x6A, j - false, // 0x6B, k - false, // 0x6C, l - false, // 0x6D, m - false, // 0x6E, n - false, // 0x6F, o - false, // 0x70, p - false, // 0x71, q - false, // 0x72, r - false, // 0x73, s - false, // 0x74, t - false, // 0x75, u - false, // 0x76, v - false, // 0x77, w - false, // 0x78, x - false, // 0x79, y - false, // 0x7A, z - false, // 0x7B, { - false, // 0x7C, | - false, // 0x7D, } - false, // 0x7E, ~ - true, // 0x7F,  - }; - - // - // IValue implementation - // - - //| - [NoHeapAllocation] - public override TypeCode GetTypeCode() { - return TypeCode.String; - } - - // Is this a string that can be compared quickly (that is it has only characters > 0x80 - // and not a - or ' - internal bool IsFastSort() { - return ((StringState & StringState.SpecialSort) != 0); - } - - internal bool IsSlowSort() { - return !IsFastSort(); - } - - internal bool IsFastOpsExceptSort() { - return ((StringState & StringState.SpecialSort) != 0 || - (StringState & StringState.FastOps) != 0); - } - - internal bool IsFastCasing() { - return ((StringState & StringState.SpecialSort) != 0 || - (StringState & StringState.FastOps) != 0); - } - - internal bool IsFastIndex() { - return ((StringState & StringState.SpecialSort) != 0 || - (StringState & StringState.FastOps) != 0); - } - - internal bool IsStringStateUndetermined() { - return (StringState == StringState.Undetermined); - } - - internal bool HasHighChars() { - return (StringState == StringState.HighChars); - } - - - /// - /// Get or set the string state for this string. - /// - internal unsafe StringState StringState { - [NoLoggingForUndo] - get { - StringState result = MultiUseWord.GetStringState(this); - return (StringState)result; - } - [NoLoggingForUndo] - set { - MultiUseWord.SetStringState(this, value); - } - } - - internal unsafe StringState CheckHighChars() { - char c; - StringState ss = StringState.FastOps; - int length = m_stringLength; - fixed(char *charPtr = &this.m_firstChar) { - for(int i = 0; i < length; i++) { - c = charPtr[i]; - if(c>=0x80) { - StringState = StringState.HighChars; - return StringState; - } - else if (HighCharTable[(int)c]) { - ss = StringState.SpecialSort; - } - } - } - - StringState = ss; - return StringState; - } - - /// - unsafe internal void SetChar(int index, char value) - { -#if _DEBUG - Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); -#endif - - //Bounds check and then set the actual bit. - if ((UInt32)index >= (UInt32)Length) - throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_Index"); - - fixed (char *p = &this.m_firstChar) { - // Set the character. - p[index] = value; - } - } - -#if _DEBUG - // Only used in debug build. Insure that the HighChar state information for a string is not set as known - [MethodImpl(MethodImplOptions.InternalCall)] - private bool ValidModifiableString() { - throw new Exception("System.String.ValidModifiableString not implemented in Bartok!"); - } -#endif - - /// - unsafe internal void AppendInPlace(char value,int currentLength) - { - Debug.Assert(currentLength < m_arrayLength, "[String.AppendInPlace(char)currentLength < m_arrayLength"); -#if _DEBUG - Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); -#endif - - fixed (char *p = &this.m_firstChar) - { - // Append the character. - p[currentLength] = value; - p[++currentLength] = '\0'; - m_stringLength = currentLength; - } - } - - - /// - unsafe internal void AppendInPlace(char value, int repeatCount, int currentLength) - { - Debug.Assert(currentLength+repeatCount < m_arrayLength, "[String.AppendInPlace]currentLength+repeatCount < m_arrayLength"); -#if _DEBUG - Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); -#endif - - int newLength = currentLength + repeatCount; - - - fixed (char *p = &this.m_firstChar) - { - int i; - for (i=currentLength; i - internal unsafe void AppendInPlace(String value, int currentLength) { - Debug.Assert(value!=null, "[String.AppendInPlace]value!=null"); - Debug.Assert(value.Length + currentLength < this.m_arrayLength, "[String.AppendInPlace]Length is wrong."); -#if _DEBUG - Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); -#endif - - FillString(this, currentLength, value); - SetLength(currentLength + value.Length); - NullTerminate(); - } - - internal void AppendInPlace(String value, int startIndex, int count, int currentLength) { - Debug.Assert(value!=null, "[String.AppendInPlace]value!=null"); - Debug.Assert(count + currentLength < this.m_arrayLength, "[String.AppendInPlace]count + currentLength < this.m_arrayLength"); - Debug.Assert(count>=0, "[String.AppendInPlace]count>=0"); - Debug.Assert(startIndex>=0, "[String.AppendInPlace]startIndex>=0"); - Debug.Assert(startIndex <= (value.Length - count), "[String.AppendInPlace]startIndex <= (value.Length - count)"); -#if _DEBUG - Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); -#endif - - FillSubstring(this, currentLength, value, startIndex, count); - SetLength(currentLength + count); - NullTerminate(); - } - - internal unsafe void AppendInPlace(char *value, int count,int currentLength) { - Debug.Assert(value!=null, "[String.AppendInPlace]value!=null"); - Debug.Assert(count + currentLength < this.m_arrayLength, "[String.AppendInPlace]count + currentLength < this.m_arrayLength"); - Debug.Assert(count>=0, "[String.AppendInPlace]count>=0"); -#if _DEBUG - Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); -#endif - fixed(char *p = &this.m_firstChar) { - int i; - for (i=0; i - internal unsafe void AppendInPlace(char[] value, int start, int count, int currentLength) { - Debug.Assert(value!=null, "[String.AppendInPlace]value!=null"); - Debug.Assert(count + currentLength < this.m_arrayLength, "[String.AppendInPlace]Length is wrong."); - Debug.Assert(value.Length-count>=start, "[String.AppendInPlace]value.Length-count>=start"); -#if _DEBUG - Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); -#endif - - FillStringArray(this, currentLength, value, start, count); - this.m_stringLength = (currentLength + count); - this.NullTerminate(); - } - - - /// - unsafe internal void ReplaceCharInPlace(char oldChar, char newChar, int startIndex, int count,int currentLength) { - Debug.Assert(startIndex>=0, "[String.ReplaceCharInPlace]startIndex>0"); - Debug.Assert(startIndex<=currentLength, "[String.ReplaceCharInPlace]startIndex>=Length"); - Debug.Assert((startIndex<=currentLength-count), "[String.ReplaceCharInPlace]count>0 && startIndex<=currentLength-count"); -#if _DEBUG - Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); -#endif - - int endIndex = startIndex+count; - - fixed (char *p = &this.m_firstChar) { - for (int i=startIndex;i - internal static String GetStringForStringBuilder(String value, int capacity) { - Debug.Assert(value!=null, "[String.GetStringForStringBuilder]value!=null"); - Debug.Assert(capacity>=value.Length, "[String.GetStringForStringBuilder]capacity>=value.Length"); - - String newStr = FastAllocateString(capacity); - if (value.Length==0) { - newStr.m_stringLength=0; - newStr.m_firstChar='\0'; - return newStr; - } - FillString(newStr, 0, value); - newStr.m_stringLength = value.m_stringLength; - return newStr; - } - - /// - private unsafe void NullTerminate() { - fixed(char *p = &this.m_firstChar) { - p[m_stringLength] = '\0'; - } - } - - /// - unsafe internal void ClearPostNullChar() { - int newLength = Length+1; - if (newLength - internal void SetLength(int newLength) { - Debug.Assert(newLength <= m_arrayLength, "newLength<=m_arrayLength"); - m_stringLength = newLength; - } - - - - //| - public CharEnumerator GetEnumerator() { - return new CharEnumerator(this); - } - - //| - /// - IEnumerator IEnumerable.GetEnumerator() { - return new CharEnumerator(this); - } - - // - // This is just designed to prevent compiler warnings. - // This field is used from native, but we need to prevent the compiler warnings. - // -#if _DEBUG - private void DontTouchThis() { - m_arrayLength = 0; - m_stringLength = 0; - m_firstChar = m_firstChar; - } -#endif - - internal unsafe void InternalSetCharNoBoundsCheck(int index, char value) { - fixed (char *p = &this.m_firstChar) { - p[index] = value; - } - } - - -#if false // Unused - - // NOTE: len is not the length in characters, but the length in bytes - - // Copies the source String (byte buffer) to the destination IntPtr memory allocated with len bytes. - internal unsafe static void InternalCopy(String src, IntPtr dest,int len) - { - if (len == 0) - return; - fixed(char* charPtr = &src.m_firstChar) { - byte* srcPtr = (byte*) charPtr; - byte* dstPtr = (byte*) dest.ToPointer(); - Buffer.MoveMemory(dstPtr, srcPtr, len); - } - } - - // memcopies characters inside a String. - internal unsafe static void InternalMemCpy(String src, int srcOffset, String dst, int destOffset, int len) - { - if (len == 0) - return; - fixed(char* srcPtr = &src.m_firstChar) { - fixed(char* dstPtr = &dst.m_firstChar) { - Buffer.MoveMemory((byte*)(dstPtr + destOffset), - (byte*)(srcPtr + srcOffset), - len); - } - } - } -#endif - - // Copies the source String to the destination byte[] - internal unsafe static void InternalCopy(String src, byte[] dest, int stringLength) - { - if (stringLength == 0) - return; - int len = stringLength * sizeof(char); - - Debug.Assert(dest.Length >= len); - - fixed(char* charPtr = &src.m_firstChar) { - fixed(byte* destPtr = &dest[0]) { - byte* srcPtr = (byte*) charPtr; - Buffer.MoveMemory(destPtr, srcPtr, len); - } - } - } - - internal unsafe void InsertInPlace(int index, String value, int repeatCount, int currentLength, int requiredLength) { - Debug.Assert(requiredLength < m_arrayLength, "[String.InsertString] requiredLength < m_arrayLength"); - Debug.Assert(index + value.Length * repeatCount < m_arrayLength, "[String.InsertString] index + value.Length * repeatCount < m_arrayLength"); -#if _DEBUG - Debug.Assert(ValidModifiableString(), "Modifiable string must not have highChars flags set"); -#endif - //Copy the old characters over to make room and then insert the new characters. - fixed(char* srcPtr = &this.m_firstChar) { - fixed(char* valuePtr = &value.m_firstChar) { - Buffer.MoveMemory((byte*)(srcPtr + index + value.Length * repeatCount), - (byte*)(srcPtr + index), - (currentLength - index) * sizeof(char)); - for (int i = 0; i < repeatCount; i++) { - Buffer.MoveMemory((byte*)(srcPtr + index + i * value.Length), - (byte*)valuePtr, - value.Length * sizeof(char)); - } - } - srcPtr[requiredLength] = '\0'; - } - this.m_stringLength = requiredLength; - } - - [AccessedByRuntime("referenced from c++")] - [CLSCompliant(false)] - [NoHeapAllocation] - public static unsafe int LimitedFormatTo(String format, - ArgIterator args, - char *buffer, - int length) - { - unchecked { - - fixed (char *fmtBeg = &format.m_firstChar) { - char *fmtPtr = fmtBeg; - char *fmtEnd = fmtBeg + format.m_stringLength; - - char *outPtr = buffer; - char *outEnd = buffer + length; - - while (fmtPtr < fmtEnd && outPtr < outEnd) { - if (*fmtPtr == '{') { - char * fmtPos = fmtPtr; - bool bad = false; - fmtPtr++; - - int ndx = 0; - int aln = 0; - int wid = 0; - char fmt = 'd'; - - if (*fmtPtr == '{') { - *outPtr++ = *fmtPtr++; - } - else if (*fmtPtr >= '0' && *fmtPtr <= '9') { - - // {index,alignment:type width} - // Get Index - while (*fmtPtr >= '0' && *fmtPtr <= '9') { - ndx = ndx * 10 + (*fmtPtr++ - '0'); - } - - // Get Alignment - if (*fmtPtr == ',') { - fmtPtr++; - if (*fmtPtr == '-') { - // We just ignore left alignment for now. - fmtPtr++; - } - while (*fmtPtr >= '0' && *fmtPtr <= '9') { - aln = aln * 10 + (*fmtPtr++ - '0'); - } - } - - // Get FormatString - if (*fmtPtr == ':') { - fmtPtr++; - if (*fmtPtr >= 'a' && *fmtPtr <= 'z') { - fmt = *fmtPtr++; - } - else if (*fmtPtr >= 'A' && *fmtPtr <= 'Z') { - fmt = (char)(*fmtPtr++ - ('A' + 'a')); - } - while (*fmtPtr >= '0' && *fmtPtr <= '9') { - wid = wid * 10 + (*fmtPtr++ - '0'); - } - } - - // Get closing brace. - if (*fmtPtr == '}') { - fmtPtr++; - } - else { - bad = true; - } - - if (ndx >= args.Length) { - bad = true; - } - - if (bad) { - for (; fmtPos < fmtPtr; fmtPos++) { - if (outPtr < outEnd) { - *outPtr++ = *fmtPos; - } - } - } - else { - // Get the value - char cvalue = '\0'; - long ivalue = 0; - string svalue = null; - ulong value = 0; - IntPtr pvalue; - RuntimeType type; - - type = args.GetArg(ndx, out pvalue); - - switch (type.classVtable.structuralView) { - case StructuralType.Bool: - svalue =*(bool *)pvalue ? "true" : "false"; - break; - case StructuralType.Char: - cvalue = *(char *)pvalue; - break; - case StructuralType.Int8: - ivalue = *(sbyte *)pvalue; - break; - case StructuralType.Int16: - ivalue = *(short *)pvalue; - break; - case StructuralType.Int32: - ivalue = *(int *)pvalue; - break; - case StructuralType.Int64: - ivalue = *(long *)pvalue; - break; - case StructuralType.UInt8: - value = *(byte *)pvalue; - break; - case StructuralType.UInt16: - value = *(ushort *)pvalue; - break; - case StructuralType.UInt32: - value = *(uint *)pvalue; - break; - case StructuralType.UInt64: - value = *(ulong *)pvalue; - break; - case StructuralType.IntPtr: - value = (ulong)*(IntPtr *)pvalue; - break; - case StructuralType.UIntPtr: - value = (ulong)*(UIntPtr *)pvalue; - break; - case StructuralType.Reference: - if (type == typeof(String)) { - svalue = Magic.toString( - Magic.fromAddress(*(UIntPtr *)pvalue)); - } - else { - svalue = type.Name; - } - break; - default: - svalue = "???"; - break; - } - - if (cvalue != '\0') { - outPtr = AddChar(outPtr, outEnd, *(char *)pvalue, aln); - } - else if (svalue != null) { - outPtr = AddString(outPtr, outEnd, svalue, aln); - } - else { - if (aln < wid) { - aln = wid; - } - - if (fmt == 'x') { - if (ivalue != 0) { - value = (ulong)ivalue; - } - outPtr = AddNumber(outPtr, outEnd, value, aln, 16, '0', '\0'); - } - else { - char sign = '\0'; - if (ivalue < 0) { - sign = '-'; - value = unchecked((ulong)-ivalue); - } - else if (ivalue > 0) { - value = unchecked((ulong)ivalue); - } - outPtr = AddNumber(outPtr, outEnd, value, aln, 10, ' ', sign); - } - } - } - } - } - else if (*fmtPtr == '}') { - if (outPtr < outEnd) { - *outPtr++ = *fmtPtr; - } - fmtPtr++; - } - else { - if (outPtr < outEnd) { - *outPtr++ = *fmtPtr; - } - fmtPtr++; - } - } - return (int)(outPtr - buffer); - } - } - } - - [NoHeapAllocation] - private static int CountDigits(ulong value, uint valueBase) - { - int used = 0; - do { - value /= valueBase; - used++; - } while (value != 0); - - return used; - } - - [NoHeapAllocation] - private static unsafe char * AddNumber(char *output, char *limit, - ulong value, int width, uint valueBase, - char fill, char sign) - { - // Make sure we won't overfill the buffer. - if (output >= limit) { - return output; - } - - // Figure out how wide the string will be. - int used = CountDigits(value, valueBase); - - // Check for overflow. - if (output + used + ((sign != '\0') ? 1 : 0) > limit) { - while (width > 0) { - *output++ = '#'; - width--; - } - return output; - } - - // Handle sign and space fill. - if (sign != '\0' && used < width) { - if (fill == ' ') { - while (width > used + 1) { - *output++ = fill; - width--; - } - fill = '\0'; - } - *output++ = sign; - } - - // Handle other non-zero fills. - if (fill != '\0') { - while (width > used) { - *output++ = fill; - width--; - } - } - - // Write the characters into the buffer. - char *outp = output + used; - do { - uint digit = (uint)(value % valueBase); - value /= valueBase; - - if (digit >= 0 && digit <= 9) { - *--outp = (char)('0' + digit); - } - else { - *--outp = (char)('a' + (digit - 10)); - } - } while (value != 0); - - return output + used; - } - - [NoHeapAllocation] - private static unsafe char * AddString(char *output, char *limit, - string value, int width) - { - fixed (char *pwString = &value.m_firstChar) { - return AddString(output, limit, pwString, value.m_stringLength, width); - } - } - - [NoHeapAllocation] - private static unsafe char * AddString(char *output, char *limit, - char *pwString, int cwString, - int width) - { - // width < 0: left justified at -width. - // width = 0: no specified width. - // width > 0: right justified at width. - if (width == 0) { - width = cwString; - } - else if (width < 0) { - width = -width; - for (; width > cwString; width--) { - if (output < limit) { - *output++ = ' '; - } - } - } - - while (cwString > 0 && width > 0) { - if (output < limit) { - *output++ = (char)*pwString++; - } - width--; - cwString--; - } - - for (; width > 0; width--) { - if (output < limit) { - *output++ = ' '; - } - } - return output; - } - - [NoHeapAllocation] - private static unsafe char * AddChar(char *output, char *limit, char value, int width) - { - // width < 0: left justified at -width. - // width = 0: no specified width. - // width > 0: right justified at width. - if (width == 0) { - width = 1; - } - else if (width < 0) { - width = -width; - for (; width > 1; width--) { - if (output < limit) { - *output++ = ' '; - } - } - } - - if (width > 0) { - if (output < limit) { - *output++ = (char)value; - } - width--; - } - - for (; width > 0; width--) { - if (output < limit) { - *output++ = ' '; - } - } - return output; - } - } -} diff --git a/base/Kernel/System/Text/ASCIIEncoding.cs b/base/Kernel/System/Text/ASCIIEncoding.cs deleted file mode 100644 index 46d53f6..0000000 --- a/base/Kernel/System/Text/ASCIIEncoding.cs +++ /dev/null @@ -1,210 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -namespace System.Text { - using System.Text; - using System; - using System.Runtime.CompilerServices; - - //| - [RequiredByBartok] - public class ASCIIEncoding : Encoding - { - private const int ASCII_CODEPAGE=20127; - - //| - [RequiredByBartok] - public ASCIIEncoding() : base(ASCII_CODEPAGE) { - } - - //| - public override int GetByteCount(char[] chars, int index, int count) { - if (chars == null) { - throw new ArgumentNullException("chars", "ArgumentNull_Array"); - } - if (index < 0 || count < 0) { - throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), - "ArgumentOutOfRange_NeedNonNegNum"); - } - if (chars.Length - index < count) { - throw new ArgumentOutOfRangeException("chars", - "ArgumentOutOfRange_IndexCountBuffer"); - } - - return (count); - } - - //| - public override int GetByteCount(String chars) { - if (chars == null) { - throw new ArgumentNullException("chars", "ArgumentNull_Array"); - } - return chars.Length; - } - - //| - public override int GetBytes(char[] chars, int charIndex, int charCount, - byte[] bytes, int byteIndex) { - if (chars == null || bytes == null) { - throw new ArgumentNullException((chars == null ? "chars" : "bytes"), - "ArgumentNull_Array"); - } - if (charIndex < 0 || charCount < 0) { - throw new ArgumentOutOfRangeException((charIndex<0 ? "charIndex" : "charCount"), - "ArgumentOutOfRange_NeedNonNegNum"); - } - if (chars.Length - charIndex < charCount) { - throw new ArgumentOutOfRangeException("chars", - "ArgumentOutOfRange_IndexCountBuffer"); - } - if (byteIndex < 0 || byteIndex > bytes.Length) { - throw new ArgumentOutOfRangeException("byteIndex", - "ArgumentOutOfRange_Index"); - } - if (bytes.Length - byteIndex < charCount) { - throw new ArgumentException("Argument_ConversionOverflow"); - } - int charEnd = charIndex + charCount; - - while (charIndex < charEnd) { - char ch = chars[charIndex++]; - if (ch >= 0x0080) ch = '?'; - bytes[byteIndex++] = (byte)ch; - } - return charCount; - } - - //| - public override int GetBytes(String chars, int charIndex, int charCount, - byte[] bytes, int byteIndex) { - if (chars == null || bytes == null) { - throw new ArgumentNullException((chars == null ? "chars" : "bytes"), - "ArgumentNull_Array"); - } - if (charIndex < 0 || charCount < 0) { - throw new ArgumentOutOfRangeException((charIndex<0 ? "charIndex" : "charCount"), - "ArgumentOutOfRange_NeedNonNegNum"); - } - if (chars.Length - charIndex < charCount) { - throw new ArgumentOutOfRangeException("chars", - "ArgumentOutOfRange_IndexCount"); - } - if (byteIndex < 0 || byteIndex > bytes.Length) { - throw new ArgumentOutOfRangeException("byteIndex", - "ArgumentOutOfRange_Index"); - } - if (bytes.Length - byteIndex < charCount) { - throw new ArgumentException("Argument_ConversionOverflow"); - } - int charEnd = charIndex + charCount; - - while (charIndex < charEnd) { - char ch = chars[charIndex++]; - if (ch >= 0x0080) ch = '?'; - bytes[byteIndex++] = (byte)ch; - } - return charCount; - } - - //| - public override int GetCharCount(byte[] bytes, int index, int count) { - if (bytes == null) { - throw new ArgumentNullException("bytes", - "ArgumentNull_Array"); - } - if (index < 0 || count < 0) { - throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), - "ArgumentOutOfRange_NeedNonNegNum"); - } - if (bytes.Length - index < count) { - throw new ArgumentOutOfRangeException("bytes", - "ArgumentOutOfRange_IndexCountBuffer"); - } - return (count); - - } - - //| - public override int GetChars(byte[] bytes, int byteIndex, int byteCount, - char[] chars, int charIndex) { - if (bytes == null || chars == null) { - throw new ArgumentNullException((bytes == null ? "bytes" : "chars"), - "ArgumentNull_Array"); - } - if (byteIndex < 0 || byteCount < 0) { - throw new ArgumentOutOfRangeException((byteIndex<0 ? "byteIndex" : "byteCount"), - "ArgumentOutOfRange_NeedNonNegNum"); - } - if ( bytes.Length - byteIndex < byteCount) - { - throw new ArgumentOutOfRangeException("bytes", - "ArgumentOutOfRange_IndexCountBuffer"); - } - if (charIndex < 0 || charIndex > chars.Length) { - throw new ArgumentOutOfRangeException("charIndex", - "ArgumentOutOfRange_Index"); - } - if (chars.Length - charIndex < byteCount) { - throw new ArgumentException("Argument_ConversionOverflow"); - } - int byteEnd = byteIndex + byteCount; - while (byteIndex < byteEnd) { - byte b = bytes[byteIndex++]; - if (b > 0x7f) { - // This is an invalid byte in the ASCII encoding. - chars[charIndex++] = '?'; - } else { - chars[charIndex++] = (char)b; - } - } - return (byteCount); - - } - - //| - public override String GetString(byte[] bytes) - { - if (bytes == null) - throw new ArgumentNullException("bytes", "ArgumentNull_Array"); - return String.CreateStringFromASCII(bytes, 0, bytes.Length); - } - - //| - public override String GetString(byte[] bytes, int byteIndex, int byteCount) - { - if (bytes == null) - throw new ArgumentNullException("bytes", "ArgumentNull_Array"); - if (byteIndex < 0 || byteCount < 0) { - throw new ArgumentOutOfRangeException((byteIndex<0 ? "byteIndex" : "byteCount"), - "ArgumentOutOfRange_NeedNonNegNum"); - } - if (bytes.Length - byteIndex < byteCount) - { - throw new ArgumentOutOfRangeException("bytes", - "ArgumentOutOfRange_IndexCountBuffer"); - } - return String.CreateStringFromASCII(bytes, byteIndex, byteCount); - } - - //| - public override int GetMaxByteCount(int charCount) { - if (charCount < 0) { - throw new ArgumentOutOfRangeException("charCount", - "ArgumentOutOfRange_NeedNonNegNum"); - } - - return (charCount); - } - - //| - public override int GetMaxCharCount(int byteCount) { - if (byteCount < 0) { - throw new ArgumentOutOfRangeException("byteCount", - "ArgumentOutOfRange_NeedNonNegNum"); - } - return byteCount; - } - } -} diff --git a/base/Kernel/System/Text/StringBuilder.cs b/base/Kernel/System/Text/StringBuilder.cs deleted file mode 100644 index 9cffa0c..0000000 --- a/base/Kernel/System/Text/StringBuilder.cs +++ /dev/null @@ -1,1275 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================ -** -** Class: StringBuilder -** -** -** Purpose: A prototype implementation of the StringBuilder -** class. -** -** Date: December 8, 1997 -** Last Updated: March 31, 1998 -** -===========================================================*/ -namespace System.Text { - using System.Text; - using System.Threading; - using System; - using System.Diagnostics; - using System.Runtime.CompilerServices; - - // This class represents a mutable string. It is convenient for situations in - // which it is desirable to modify a string, perhaps by removing, replacing, or - // inserting characters, without creating a new String subsequent to - // each modification. - // - // The methods contained within this class do not return a new StringBuilder - // object unless specified otherwise. This class may be used in conjunction with the String - // class to carry out modifications upon strings. - // - // When passing null into a constructor in VJ and VC, the null - // should be explicitly type cast. - // For Example: - // StringBuilder sb1 = new StringBuilder((StringBuilder)null); - // StringBuilder sb2 = new StringBuilder((String)null); - // - //| - [NoCCtor] - [RequiredByBartok] - public sealed class StringBuilder { - - // - // - // CLASS VARIABLES - // - // - internal int m_currentThread = InternalGetCurrentThread(); - internal int m_MaxCapacity = 0; - [RequiredByBartok] - internal String m_StringValue = null; - - - // - // - // STATIC CONSTANTS - // - // - private const int DEFAULT_CAPACITY = 16; - private const int DEFAULT_MAX_CAPACITY = 0x7FFFFFFF; - - // - // - //CONSTRUCTORS - // - // - - // Creates a new empty string builder (i.e., it represents String.Empty) - // with the default capacity (16 characters). - //| - public StringBuilder() - : this(DEFAULT_CAPACITY) { - } - - // Create a new empty string builder (i.e., it represents String.Empty) - // with the specified capacity. - //| - public StringBuilder(int capacity) { - if (capacity<0) { - throw new ArgumentOutOfRangeException("capacity"); - } - - if (capacity == 0) { // MakeFromString enforces this - capacity = DEFAULT_CAPACITY; - } - - m_StringValue = String.GetStringForStringBuilder(String.Empty, capacity); - m_MaxCapacity = Int32.MaxValue; - } - - /*=======================CalculateCapacity=============================== - * Calculates the new capacity of our buffer. If the size of the - * buffer is less than a fixed number (10000 in this case), we just - * double the buffer until we have enough space. Once we get larger - * than 10000, we use a series of heuristics to determine the most - * appropriate size. - ======================================================================*/ - private int CalculateCapacity(int currentCapacity, int neededCapacity) - { - // rusa: see also Lightning\Src\VM\COMStringBuffer.cpp::CalculateCapaity - int newCapacity = neededCapacity; - if (newCapacity == 0) { - newCapacity = DEFAULT_CAPACITY; - } - if (neededCapacity > this.m_MaxCapacity) { - throw new ArgumentOutOfRangeException("exceeding max capacity"); - } - while (newCapacity < neededCapacity && newCapacity > 0) { - newCapacity <<= 1; - } - // Check for overflow - if (newCapacity <= 0) { - // The C++ code throws on overflow, but it really should not - newCapacity = this.m_MaxCapacity; - } else if (newCapacity > this.m_MaxCapacity) { - // We doubled past the max capacity, so back down a bit - newCapacity = this.m_MaxCapacity; - } - return newCapacity; - } - - // Creates a new string builder from the specified string. If value - // is a null String (i.e., if it represents String.NullString) - // then the new string builder will also be null (i.e., it will also represent - // String.NullString). - // - //| - public StringBuilder(String value){ - MakeFromString(value, 0, -1, -1); - } - - // Creates a new string builder from the specified string with the specified - // capacity. If value is a null String (i.e., if it represents - // String.NullString) then the new string builder will also be null - // (i.e., it will also represent String.NullString). - // The maximum number of characters this string may contain is set by capacity. - // - //| - public StringBuilder(String value, int capacity) { - if (capacity<0) { - throw new ArgumentOutOfRangeException("capacity", - String.Format("ArgumentOutOfRange_MustBePositive", "capacity")); - } - - MakeFromString(value, 0, -1, capacity); - } - // Creates a new string builder from the specified substring with the specified - // capacity. The maximum number of characters is set by capacity. - // - - //| - public StringBuilder(String value, int startIndex, int length, int capacity) { - if (capacity<0) { - throw new ArgumentOutOfRangeException("capacity", - String.Format("ArgumentOutOfRange_MustBePositive", "capacity")); - } - if (length<0) { - throw new ArgumentOutOfRangeException("length", - String.Format("ArgumentOutOfRange_MustBeNonNegNum", "length")); - } - - MakeFromString(value, startIndex, length, capacity); - } - - // Creates an empty StringBuilder with a minimum capacity of capacity - // and a maximum capacity of maxCapacity. - //| - public StringBuilder(int capacity, int maxCapacity) { - if (capacity>maxCapacity) { - throw new ArgumentOutOfRangeException("capacity", "ArgumentOutOfRange_Capacity"); - } - if (maxCapacity<1) { - throw new ArgumentOutOfRangeException("maxCapacity", "ArgumentOutOfRange_SmallMaxCapacity"); - } - - if (capacity<0) { - throw new ArgumentOutOfRangeException("capacity", - String.Format("ArgumentOutOfRange_MustBePositive", "capacity")); - } - if (capacity == 0) { - capacity = DEFAULT_CAPACITY; - } - - m_StringValue = String.GetStringForStringBuilder(String.Empty, capacity); - m_MaxCapacity = maxCapacity; - - } - - private String GetThreadSafeString(out int tid) { - String temp = m_StringValue; - tid = InternalGetCurrentThread(); - if (m_currentThread == tid) - return temp; - return String.GetStringForStringBuilder(temp, temp.Capacity); - } - - private static int InternalGetCurrentThread() - { - return Thread.CurrentThread.GetThreadId(); - } - - // - // Private native functions - // - /*=========================MakeFromString================================ - ** If value is null, we simply create an empty string with a default - ** length. If it does contain data, we allocate space for twice this - ** amount of data, copy the data, associate it with the StringBuffer - ** and clear the CopyOnWrite bit. - =======================================================================*/ - private void MakeFromString(String value, int startIndex, int length, int capacity) - { - // rusa: see also Lightning\Src\VM\COMStringBuffer.cpp::MakeFromString - if (capacity <= 0) { - capacity = DEFAULT_CAPACITY; - } - this.m_MaxCapacity = DEFAULT_MAX_CAPACITY; - if (value != null) { - if (startIndex < 0) { - throw new ArgumentOutOfRangeException("negative startIndex"); - } - int stringLength = value.Length; - if (length == -1) { - length = stringLength - startIndex; - } else if (length + startIndex > stringLength) { - throw new ArgumentOutOfRangeException("exceeding string length"); - } - int newCapacity = this.CalculateCapacity(capacity, length); - String newString = - String.GetStringForStringBuilder(String.Empty, newCapacity); - newString.AppendInPlace(value, startIndex, length, 0); - this.m_StringValue = newString; - } else if (capacity == 0) { - this.m_StringValue = String.Empty; - } else { - this.m_StringValue = - String.GetStringForStringBuilder(String.Empty, capacity); - } - } - - //| - public int Capacity { - get {return m_StringValue.Capacity;} //-1 to account for terminating null. - set {InternalSetCapacity(value);} - } - - - //| - public int MaxCapacity { - get { return m_MaxCapacity; } - - } - - // Read-Only Property - // Ensures that the capacity of this string builder is at least the specified value. - // If capacity is greater than the capacity of this string builder, then the capacity - // is set to capacity; otherwise the capacity is unchanged. - // - //| - public int EnsureCapacity(int capacity) { - if (capacity<0) { - throw new ArgumentOutOfRangeException("capacity", "ArgumentOutOfRange_NeedPosCapacity"); - } - - int tid; - String currentString = GetThreadSafeString(out tid); - - //If we need more space or the COW bit is set, copy the buffer. - if (!NeedsAllocation(currentString,capacity)) { - return currentString.Capacity; - } - - String newString = GetNewString(currentString,capacity); - ReplaceString(tid,newString); - return newString.Capacity; - } - - //Sets the capacity to be capacity. If capacity is less than the current - //instance an ArgumentException is thrown. If capacity is greater than the current - //instance, memory is allocated to allow the StringBuilder to grow. - // - internal int InternalSetCapacity(int capacity) - { - // rusa: see also Lighting\Src\VM\COMStringBuffer.cpp::SetCapacity - // The return value of the native code is a pointer to 'this', but - // since it isn't ever used, we return 0; - - int tid; - String thisString = GetThreadSafeString(out tid); - if (capacity < 0) { - throw new ArgumentOutOfRangeException("capacity is negative"); - } - if (capacity < thisString.Length) { - throw new ArgumentOutOfRangeException("capacity lesser than size"); - } - if (capacity > this.m_MaxCapacity) { - throw new ArgumentOutOfRangeException("exceeds max capacity"); - } - if (capacity != this.m_StringValue.ArrayLength-1) { - this.m_StringValue = CopyString(this,thisString,capacity); - this.m_currentThread = tid; - } - return 0; - } - - private String CopyString(StringBuilder thisRef,String CurrString, - int newCapacity) - { - int CurrLen = CurrString.Length; - int copyLength; - if (newCapacity >= CurrLen) { - copyLength = CurrLen; - } else { - copyLength = newCapacity; - } - return String.NewString(CurrString,0,copyLength,newCapacity); - } - - //| - public override String ToString() { - String currentString = m_StringValue; - int currentThread = m_currentThread; - if (currentThread != 0 && currentThread != InternalGetCurrentThread()) { - return String.InternalCopy(currentString); - } - - if ((2 * currentString.Length) < currentString.ArrayLength) { - return String.InternalCopy(currentString); - } - - currentString.ClearPostNullChar(); - m_currentThread = 0; - return currentString; - } - - // Converts a substring of this string builder to a String. - //| - public String ToString(int startIndex, int length) { - return m_StringValue.Substring(startIndex, length); - } - - // Sets the length of the String in this buffer. If length is less than the current - // instance, the StringBuilder is truncated. If length is greater than the current - // instance, nulls are appended. The capacity is adjusted to be the same as the length. - - //| - public int Length { - get { - return m_StringValue.Length; - } - set { - int tid; - String currentString = GetThreadSafeString(out tid); - - if (value==0) { //the user is trying to clear the string - currentString.SetLength(0); - ReplaceString(tid,currentString); - return; - } - - int currentLength = currentString.Length; - int newlength = value; - //If our length is less than 0 or greater than our Maximum capacity, bail. - if (newlength<0) { - throw new ArgumentOutOfRangeException("newlength", "ArgumentOutOfRange_NegativeLength"); - } - - if (newlength>MaxCapacity) { - throw new ArgumentOutOfRangeException("capacity", "ArgumentOutOfRange_SmallCapacity"); - } - - //Jump out early if our requested length our currentlength. - //This will be a pretty rare branch. - if (newlength == currentLength) { - return; - } - - - //If the StringBuilder has never been converted to a string, simply set the length - //without allocating a new string. - if (newlength <= currentString.Capacity) { - if (newlength > currentLength) { - for (int i = currentLength ; i < newlength; i++) // This is a rare case anyway. - currentString.InternalSetCharNoBoundsCheck(i,'\0'); - } - - currentString.InternalSetCharNoBoundsCheck(newlength,'\0'); //Null terminate. - currentString.SetLength(newlength); - ReplaceString(tid,currentString); - - return; - } - - // CopyOnWrite set we need to allocate a String - int newCapacity = (newlength>currentString.Capacity)?newlength:currentString.Capacity; - String newString = String.GetStringForStringBuilder(currentString, newCapacity); - - //We know exactly how many characters we need, so embed that knowledge in the String. - newString.SetLength(newlength); - ReplaceString(tid,newString); - } - } - - //| - public char this[int index] { - get { - return m_StringValue[index]; - } - set { - int tid; - String currentString = GetThreadSafeString(out tid); - currentString.SetChar(index, value); - ReplaceString(tid,currentString); - } - } - - // Appends a character at the end of this string builder. The capacity is adjusted as needed. - //| - public StringBuilder Append(char value, int repeatCount) { - if (repeatCount==0) { - return this; - } - if (repeatCount<0) { - throw new ArgumentOutOfRangeException("repeatCount", "ArgumentOutOfRange_NegativeCount"); - } - - - int tid; - String currentString = GetThreadSafeString(out tid); - - int currentLength = currentString.Length; - int requiredLength = currentLength + repeatCount; - - if (requiredLength < 0) - throw new OutOfMemoryException(); - - if (!NeedsAllocation(currentString,requiredLength)) { - currentString.AppendInPlace(value, repeatCount,currentLength); - ReplaceString(tid,currentString); - return this; - } - - String newString = GetNewString(currentString,requiredLength); - newString.AppendInPlace(value, repeatCount,currentLength); - ReplaceString(tid,newString); - return this; - } - - // Appends an array of characters at the end of this string builder. The capacity is adjusted as needed. - //| - public StringBuilder Append(char[] value, int startIndex, int charCount) { - int requiredLength; - - if (value==null) { - if (startIndex==0 && charCount==0) { - return this; - } - throw new ArgumentNullException("value"); - } - - if (charCount==0) { - return this; - } - - if (startIndex<0) { - throw new ArgumentOutOfRangeException("startIndex", "ArgumentOutOfRange_GenericPositive"); - } - if (charCount<0) { - throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_GenericPositive"); - } - if (charCount>value.Length-startIndex) { - throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_Index"); - } - - int tid; - String currentString = GetThreadSafeString(out tid); - - int currentLength = currentString.Length; - requiredLength = currentLength + charCount; - if (NeedsAllocation(currentString,requiredLength)) { - String newString = GetNewString(currentString,requiredLength); - newString.AppendInPlace(value, startIndex, charCount,currentLength); - ReplaceString(tid,newString); - } else { - currentString.AppendInPlace(value, startIndex, charCount,currentLength); - ReplaceString(tid,currentString); - } - - return this; - } - - // Appends a copy of this string at the end of this string builder. - //| - public StringBuilder Append(String value) { - //If the value being added is null, eat the null - //and return. - if (value==null) { - return this; - } - - int tid; - // hand inlining of GetThreadSafeString - String currentString = m_StringValue; - tid = InternalGetCurrentThread(); - if (m_currentThread != tid) - currentString = String.GetStringForStringBuilder(currentString, currentString.Capacity); - - int currentLength = currentString.Length; - - int requiredLength = currentLength + value.Length; - - if (NeedsAllocation(currentString,requiredLength)) { - String newString = GetNewString(currentString,requiredLength); - newString.AppendInPlace(value,currentLength); - ReplaceString(tid,newString); - } else { - currentString.AppendInPlace(value,currentLength); - ReplaceString(tid,currentString); - } - - return this; - } - - internal unsafe StringBuilder Append(char *value, int count) { - //If the value being added is null, eat the null - //and return. - if (value==null) { - return this; - } - - - int tid; - String currentString = GetThreadSafeString(out tid); - int currentLength = currentString.Length; - - int requiredLength = currentLength + count; - - if (NeedsAllocation(currentString,requiredLength)) { - String newString = GetNewString(currentString,requiredLength); - newString.AppendInPlace(value, count,currentLength); - ReplaceString(tid,newString); - } else { - currentString.AppendInPlace(value,count,currentLength); - ReplaceString(tid,currentString); - } - - return this; - } - - private bool NeedsAllocation(String currentString,int requiredLength) { - //<= accounts for the terminating 0 which we require on strings. - return (currentString.ArrayLength<=requiredLength); - } - - private String GetNewString(String currentString, int requiredLength) { - int newCapacity; - - requiredLength++; //Include the terminating null. - - if (requiredLength < 0) { - throw new OutOfMemoryException(); - } - - if (requiredLength > m_MaxCapacity) { - throw new ArgumentOutOfRangeException("ArgumentOutOfRange_NegativeCapacity", - "requiredLength"); - } - - newCapacity = ( currentString.Capacity)*2; // To force a predictable growth of 160,320 etc. for testing purposes - - if (newCapacity m_MaxCapacity) { - newCapacity = m_MaxCapacity; - } - - if (newCapacity<=0) { - throw new ArgumentOutOfRangeException("ArgumentOutOfRange_NegativeCapacity"); - } - - return String.GetStringForStringBuilder( currentString, newCapacity); - } - - private void ReplaceString(int tid, String value) { - Debug.Assert(value!=null, "[StringBuilder.ReplaceString]value!=null"); - - m_currentThread = tid; // new owner - m_StringValue = value; - } - - // Appends a copy of the characters in value from startIndex to startIndex + - // count at the end of this string builder. - //| - public StringBuilder Append(String value, int startIndex, int count) { - //If the value being added is null, eat the null - //and return. - if (value==null) { - if (startIndex==0 && count==0) { - return this; - } - throw new ArgumentNullException("value"); - } - - if (count<=0) { - if (count==0) { - return this; - } - throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_GenericPositive"); - } - - if (startIndex<0 || (startIndex>value.Length - count)) { - throw new ArgumentOutOfRangeException("startIndex", "ArgumentOutOfRange_Index"); - } - - int tid; - String currentString = GetThreadSafeString(out tid); - int currentLength = currentString.Length; - - int requiredLength = currentLength + count; - - if (NeedsAllocation(currentString,requiredLength)) { - String newString = GetNewString(currentString,requiredLength); - newString.AppendInPlace(value, startIndex, count, currentLength); - ReplaceString(tid,newString); - } else { - currentString.AppendInPlace(value, startIndex, count, currentLength); - ReplaceString(tid,currentString); - } - - return this; - } - - // Inserts multiple copies of a string into this string builder at the specified position. - // Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, this - // string builder is not changed. Inserts ";<;no object>;"; if value - // is null. - // - //| - public unsafe StringBuilder Insert(int index, String value, int count) { - int tid; - String currentString = GetThreadSafeString(out tid); - int currentLength = currentString.Length; - - //If value isn't null, get all of our required values. - if (value == null) { - if (index == 0 && count == 0) { - return this; - } - throw new ArgumentNullException("ArgumentNull_String"); - } - - //Range check the index. - if (index < 0 || index > currentLength) { - throw new ArgumentOutOfRangeException("index","ArgumentOutOfRange_Index"); - } - - if (count < 1) { - throw new ArgumentOutOfRangeException("count","ArgumentOutOfRange_GenericPositive"); - } - - //Calculate the new length, ensure that we have the space and set the space variable for this buffer - int requiredLength; - try { - requiredLength = checked(currentLength + (value.Length * count)); - } - catch (Exception) { - throw new OutOfMemoryException(); - } - - if (NeedsAllocation(currentString,requiredLength)) { - String newString = GetNewString(currentString,requiredLength); - newString.InsertInPlace(index, value, count, currentLength, requiredLength); - ReplaceString(tid,newString); - } - else { - currentString.InsertInPlace(index, value, count, currentLength, requiredLength); - ReplaceString(tid,currentString); - } - return this; - } - - - - // Property. - // Removes the specified characters from this string builder. - // The length of this string builder is reduced by - // length, but the capacity is unaffected. - // - //| - public StringBuilder Remove(int startIndex, int length) - { - // rusa: see also Lightning\Src\VM\COMStringBuffer.cpp::Remove - int tid; - String thisString = GetThreadSafeString(out tid); - int thisLength = thisString.ArrayLength-1; - if (length < 0) { - throw new ArgumentOutOfRangeException("Negative length"); - } - if (startIndex < 0) { - throw new ArgumentOutOfRangeException("Negative start index"); - } - if (startIndex + length > thisLength) { - throw new ArgumentOutOfRangeException("Exceeding string length"); - } - thisString.RemoveRange(startIndex, length); - this.m_StringValue = thisString; - this.m_currentThread = tid; - return this; - } - - // - // - // PUBLIC INSTANCE FUNCTIONS - // - // - - /*====================================Append==================================== - ** - ==============================================================================*/ - // Appends a boolean to the end of this string builder. - // The capacity is adjusted as needed. - //| - public StringBuilder Append(bool value) { - return Append(value.ToString()); - } - - // Appends an sbyte to this string builder. - // The capacity is adjusted as needed. - //| - [CLSCompliant(false)] - public StringBuilder Append(sbyte value) { - return Append(value.ToString()); - } - - // Appends a ubyte to this string builder. - // The capacity is adjusted as needed. - //| - public StringBuilder Append(byte value) { - return Append(value.ToString()); - } - - // Appends a character at the end of this string builder. The capacity is adjusted as needed. - //| - public StringBuilder Append(char value) { - int tid; - - // hand inlining of GetThreadSafeString - String currentString = m_StringValue; - tid = InternalGetCurrentThread(); - if (m_currentThread != tid) - currentString = String.GetStringForStringBuilder(currentString, currentString.Capacity); - - int currentLength = currentString.Length; - if (!NeedsAllocation(currentString,currentLength+1)) { - currentString.AppendInPlace(value,currentLength); - ReplaceString(tid,currentString); - return this; - } - - String newString = GetNewString(currentString,currentLength+1); - newString.AppendInPlace(value,currentLength); - ReplaceString(tid,newString); - return this; - } - - // Appends a short to this string builder. - // The capacity is adjusted as needed. - //| - public StringBuilder Append(short value) { - return Append(value.ToString()); - } - - // Appends an int to this string builder. - // The capacity is adjusted as needed. - //| - public StringBuilder Append(int value) { - return Append(value.ToString()); - } - - // Appends a long to this string builder. - // The capacity is adjusted as needed. - //| - public StringBuilder Append(long value) { - return Append(value.ToString()); - } - - // Appends a float to this string builder. - // The capacity is adjusted as needed. - //| - public StringBuilder Append(float value) { - return Append(value.ToString()); - } - - // Appends a double to this string builder. - // The capacity is adjusted as needed. - //| - public StringBuilder Append(double value) { - return Append(value.ToString()); - } - - //| - public StringBuilder Append(decimal value) { - return Append(value.ToString()); - } - - // Appends an ushort to this string builder. - // The capacity is adjusted as needed. - //| - [CLSCompliant(false)] - public StringBuilder Append(ushort value) { - return Append(value.ToString()); - } - - // Appends an uint to this string builder. - // The capacity is adjusted as needed. - //| - [CLSCompliant(false)] - public StringBuilder Append(uint value) { - return Append(value.ToString()); - } - - // Appends an unsigned long to this string builder. - // The capacity is adjusted as needed. - - //| - [CLSCompliant(false)] - public StringBuilder Append(ulong value) { - return Append(value.ToString()); - } - - // Appends an Object to this string builder. - // The capacity is adjusted as needed. - //| - public StringBuilder Append(Object value) { - if (null==value) { - //Appending null is now a no-op. - return this; - } - return Append(value.ToString()); - } - - // Appends all of the characters in value to the current instance. - //| - public StringBuilder Append(char[] value) { - if (null==value) { - return this; - } - - int valueLength = value.Length; - - int tid; - String currentString = GetThreadSafeString(out tid); - - int currentLength = currentString.Length; - int requiredLength = currentLength + value.Length; - if (NeedsAllocation(currentString,requiredLength)) { - String newString = GetNewString(currentString,requiredLength); - newString.AppendInPlace(value, 0, valueLength,currentLength); - ReplaceString(tid,newString); - } else { - currentString.AppendInPlace(value, 0, valueLength, currentLength); - ReplaceString(tid,currentString); - } - return this; - } - - /*====================================Insert==================================== - ** - ==============================================================================*/ - - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value - // is null. - // - //| - public StringBuilder Insert(int index, String value) { - if (value == null) // This is to do the index validation - return Insert(index,value,0); - else - return Insert(index,value,1); - } - - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value - // is null. - // - //| - public StringBuilder Insert( int index, bool value) { - return Insert(index,value.ToString(),1); - } - - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value - // is null. - // - //| - [CLSCompliant(false)] - public StringBuilder Insert(int index, sbyte value) { - return Insert(index,value.ToString(),1); - } - - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value - // is null. - // - //| - public StringBuilder Insert(int index, byte value) { - return Insert(index,value.ToString(),1); - } - - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value - // is null. - // - //| - public StringBuilder Insert(int index, short value) { - return Insert(index,value.ToString(),1); - } - - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value - // is null. - // - //| - public StringBuilder Insert(int index, char value) { - return Insert(index,Char.ToString(value),1); - } - - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value - // is null. - // - //| - public StringBuilder Insert(int index, char[] value) { - if (null==value) { - return Insert(index, value, 0, 0); - } - return Insert(index, value, 0, value.Length); - } - - // Returns a reference to the StringBuilder with charCount characters from - // value inserted into the buffer at index. Existing characters are shifted - // to make room for the new text and capacity is adjusted as required. If value is null, the StringBuilder - // is unchanged. Characters are taken from value starting at position startIndex. - //| - public StringBuilder Insert(int index, char []value, int startIndex, int charCount) - { - throw new Exception("System.Text.StringBuilder.Insert not implemented in Bartok"); - } - - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value - // is null. - // - //| - public StringBuilder Insert(int index, int value){ - return Insert(index,value.ToString(),1); - } - - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value - // is null. - // - //| - public StringBuilder Insert(int index, long value) { - return Insert(index,value.ToString(),1); - } - - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value - // is null. - // - //| - public StringBuilder Insert(int index, float value) { - return Insert(index,value.ToString(),1); - } - - - // Returns a reference to the StringBuilder with ; value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. ; Inserts ";<;no object>;"; if value - // is null. - // - //| - public StringBuilder Insert(int index, double value) { - return Insert(index,value.ToString(),1); - } - - //| - public StringBuilder Insert(int index, decimal value) { - return Insert(index,value.ToString(),1); - } - - // Returns a reference to the StringBuilder with value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. - // - //| - [CLSCompliant(false)] - public StringBuilder Insert(int index, ushort value) { - return Insert(index, value.ToString(),1); - } - - - // Returns a reference to the StringBuilder with value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. - // - //| - [CLSCompliant(false)] - public StringBuilder Insert(int index, uint value) { - return Insert(index, value.ToString(), 1); - } - - // Returns a reference to the StringBuilder with value inserted into - // the buffer at index. Existing characters are shifted to make room for the new text. - // The capacity is adjusted as needed. - // - //| - [CLSCompliant(false)] - public StringBuilder Insert(int index, ulong value) { - return Insert(index, value.ToString(), 1); - } - - // Returns a reference to this string builder with value inserted into - // the buffer at index. Existing characters are shifted to make room for the - // new text. The capacity is adjusted as needed. If value equals String.Empty, the - // StringBuilder is not changed. No changes are made if value is null. - // - //| - public StringBuilder Insert(int index, Object value) { - //If we get a null - if (null==value) { - return this; - } - return Insert(index,value.ToString(),1); - } - - //| - public StringBuilder AppendFormat(String format, Object arg0) { - return AppendFormat(format, new Object[] {arg0}); - } - - //| - public StringBuilder AppendFormat(String format, Object arg0, Object arg1) { - return AppendFormat(format, new Object[] {arg0, arg1}); - } - - //| - public StringBuilder AppendFormat(String format, Object arg0, Object arg1, Object arg2) { - return AppendFormat(format, new Object[] {arg0, arg1, arg2}); - } - - private static void FormatError() { - throw new FormatException("Format_InvalidString"); - } - - //| - public StringBuilder AppendFormat(String format, params Object[] args) { - if (format == null || args == null) { - throw new ArgumentNullException((format==null)?"format":"args"); - } - char[] chars = format.ToCharArray(0, format.Length); - int pos = 0; - int len = chars.Length; - char ch = '\x0'; - - while (true) { - int p = pos; - int i = pos; - while (pos < len) { - ch = chars[pos]; - - pos++; - if (ch == '}') - { - if(pos < len && chars[pos]=='}') // Treat as escape character for }} - pos++; - else - FormatError(); - } - - if (ch == '{') - { - if(pos < len && chars[pos]=='{') // Treat as escape character for {{ - pos++; - else - { - pos--; - break; - } - } - - chars[i++] = ch; - } - if (i > p) Append(chars, p, i - p); - if (pos == len) break; - pos++; - if (pos == len || (ch = chars[pos]) < '0' || ch > '9') FormatError(); - int index = 0; - do { - index = index * 10 + ch - '0'; - pos++; - if (pos == len) FormatError(); - ch = chars[pos]; - } while (ch >= '0' && ch <= '9' && index < 1000000); - if (index >= args.Length) throw new FormatException("Format_IndexOutOfRange"); - while (pos < len && (ch=chars[pos]) == ' ') pos++; - bool leftJustify = false; - int width = 0; - if (ch == ',') { - pos++; - while (pos < len && chars[pos] == ' ') pos++; - - if (pos == len) FormatError(); - ch = chars[pos]; - if (ch == '-') { - leftJustify = true; - pos++; - if (pos == len) FormatError(); - ch = chars[pos]; - } - if (ch < '0' || ch > '9') FormatError(); - do { - width = width * 10 + ch - '0'; - pos++; - if (pos == len) FormatError(); - ch = chars[pos]; - } while (ch >= '0' && ch <= '9' && width < 1000000); - } - - while (pos < len && (ch=chars[pos]) == ' ') pos++; - Object arg = args[index]; - String fmt = null; - if (ch == ':') { - pos++; - p = pos; - i = pos; - while (true) { - if (pos == len) FormatError(); - ch = chars[pos]; - pos++; - if (ch == '{') - { - if(pos < len && chars[pos]=='{') // Treat as escape character for {{ - pos++; - else - FormatError(); - } - else if (ch == '}') - { - if(pos < len && chars[pos]=='}') // Treat as escape character for }} - pos++; - else - { - pos--; - break; - } - } - - chars[i++] = ch; - } - if (i > p) fmt = new String(chars, p, i - p); - } - if (ch != '}') FormatError(); - pos++; - String s = null; - - if (s==null) { - if (arg is IFormattable) { - s = ((IFormattable)arg).ToString(fmt); - } else if (arg != null) { - s = arg.ToString(); - } - } - - if (s == null) s = String.Empty; - int pad = width - s.Length; - if (!leftJustify && pad > 0) Append(' ', pad); - Append(s); - if (leftJustify && pad > 0) Append(' ', pad); - } - return this; - } - - // Returns a reference to the current StringBuilder with all instances of oldString - // replaced with newString. If startIndex and count are specified, - // we only replace strings completely contained in the range of startIndex to startIndex + - // count. The strings to be replaced are checked on an ordinal basis (e.g. not culture aware). If - // newValue is null, instances of oldValue are removed (e.g. replaced with nothing.). - // - //| - public StringBuilder Replace(String oldValue, String newValue) { - return Replace(oldValue, newValue, 0, Length); - } - - //| - public StringBuilder Replace(String oldValue, String newValue, int startIndex, int count) - { - throw new Exception("System.Text.StringBuilder.Replace not implemented in Bartok"); - } - - //| - public bool Equals(StringBuilder sb) - { - if (sb == null) - return false; - return ((this.Capacity == sb.Capacity) && (this.MaxCapacity == sb.MaxCapacity) && (this. m_StringValue.Equals(sb. m_StringValue))); - } - - // Returns a StringBuilder with all instances of oldChar replaced with - // newChar. The size of the StringBuilder is unchanged because we're only - // replacing characters. If startIndex and count are specified, we - // only replace characters in the range from startIndex to startIndex+count - // - //| - public StringBuilder Replace(char oldChar, char newChar) { - return Replace(oldChar, newChar, 0, Length); - } - //| - public StringBuilder Replace(char oldChar, char newChar, int startIndex, int count) { - int tid; - String currentString = GetThreadSafeString(out tid); - int currentLength = currentString.Length; - - if ((uint)startIndex > (uint)currentLength) { - throw new ArgumentOutOfRangeException("startIndex", "ArgumentOutOfRange_Index"); - } - - if (count<0 || startIndex > currentLength-count) { - throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_Index"); - } - - if (!NeedsAllocation(currentString,currentLength)) { - currentString.ReplaceCharInPlace(oldChar, newChar, startIndex, count, currentLength); - ReplaceString(tid,currentString); - return this; - } - - String newString = GetNewString(currentString,currentLength); - newString.ReplaceCharInPlace(oldChar, newChar, startIndex, count, currentLength); - ReplaceString(tid,newString); - return this; - } - } -} - - - diff --git a/base/Kernel/System/Threading/AutoResetEvent.cs b/base/Kernel/System/Threading/AutoResetEvent.cs index 48ec82d..f3b3604 100644 --- a/base/Kernel/System/Threading/AutoResetEvent.cs +++ b/base/Kernel/System/Threading/AutoResetEvent.cs @@ -17,153 +17,70 @@ using Microsoft.Singularity.Scheduling; namespace System.Threading { - [CLSCompliant(false)] - public enum AutoResetEventEvent : ushort - { - Acquire = 1, - Enqueue = 2 - } - - //| + /// + /// + /// Event class implementing an event with auto reset symantics such event automatically + /// reset to non signaled state when single wait is satisfied + /// + /// [NoCCtor] [CLSCompliant(false)] public sealed class AutoResetEvent : WaitHandle { - //| - public AutoResetEvent(bool initialState) : - base(initialState ? 1 : 0) + /// + /// + /// Constructor + /// + /// + /// Initial state of an event true indciates that event is signaled + /// + public AutoResetEvent(bool initialState) + : base(initialState ? WaitHandle.SignalState.Signaled : + WaitHandle.SignalState.Unsignaled, + WaitHandle.SignalState.Unsignaled, + SpinLock.Types.AutoResetEvent) { } - //| + /// + /// + /// Reset an event state to non signaled + /// + /// [NoHeapAllocation] public bool Reset() { - bool iflag = Processor.DisableInterrupts(); - try { - Scheduler.DispatchLock(); - try { -#if DEBUG_DISPATCH - DebugStub.Print("Thread {0:x8} AutoResetEvent.Reset() on {1:x8}\n", - __arglist( - Kernel.AddressOf(Thread.CurrentThread), - Kernel.AddressOf(this))); -#endif // DEBUG_DISPATCH - signaled = 0; - } - finally { - Scheduler.DispatchRelease(); - } - } - finally { - Processor.RestoreInterrupts(iflag); - } + // Make sure that we don't have any waiters and then change event state to unsignaled + // in all the cases + SignalAll (WaitHandle.SignalState.Unsignaled, WaitHandle.SignalState.Unsignaled); + return true; } - //| + /// + /// + /// Wake up one waiter, if no waiters present set event into signaled state + /// + /// [NoHeapAllocation] public bool Set() { - bool iflag = Processor.DisableInterrupts(); - try { - Scheduler.DispatchLock(); - try { - if (NotifyOne()) { -#if DEBUG_DISPATCH - DebugStub.Print("Thread {0:x8} AutoResetEvent.Set() on {1:x8}" + - "unblocked {2:x8}\n", - __arglist( - Kernel.AddressOf(Thread.CurrentThread), - Kernel.AddressOf(this), - Kernel.AddressOf(owner))); -#endif // DEBUG_DISPATCH - signaled = 0; - } - else { -#if DEBUG_DISPATCH - DebugStub.Print("Thread {0:x8} AutoResetEvent.Set() on {1:x8} ready\n", - __arglist( - Kernel.AddressOf(Thread.CurrentThread), - Kernel.AddressOf(this))); -#endif // DEBUG_DISPATCH - signaled = 1; - } - } - finally { - Scheduler.DispatchRelease(); - } - } - finally { - Processor.RestoreInterrupts(iflag); - } + // Signal one waiter if present otherwise set event to signaled + SignalOne(WaitHandle.SignalState.Signaled); + return true; } - //| + /// + /// + /// Wake up all waiters and if event is not signaled set it into signaled state + /// + /// public bool SetAll() { - bool iflag = Processor.DisableInterrupts(); - try { - Scheduler.DispatchLock(); - try { - if (NotifyAll()) { - signaled = 0; - } - else { - signaled = 1; - } - } - finally { - Scheduler.DispatchRelease(); - } - } - finally { - Processor.RestoreInterrupts(iflag); - } + SignalAll (WaitHandle.SignalState.Signaled,WaitHandle.SignalState.Unsignaled); + return true; } - - // Called with dispatch lock held and interrupts off. - // Returns true if the AutoResetEvent was signaled. - internal override bool AcquireOrEnqueue(ThreadEntry entry) - { - if (signaled != 0) { -#if DEBUG_DISPATCH - DebugStub.Print("Thread {0:x8} AutoResetEvent.Acquire on {1:x8}\n", - __arglist( - Kernel.AddressOf(Thread.CurrentThread), - Kernel.AddressOf(this))); -#endif // DEBUG_DISPATCH - signaled = 0; - Monitoring.Log(Monitoring.Provider.AutoResetEvent, - (ushort)AutoResetEventEvent.Acquire, 0, - (uint)this.id, (uint)entry.Thread.threadIndex, - 0, 0, 0); - return true; - } - else { -#if DEBUG_DISPATCH - DebugStub.Print("Thread {0:x8} AutoResetEvent.Enqueue on {1:x8}\n", - __arglist( - Kernel.AddressOf(Thread.CurrentThread), - Kernel.AddressOf(this))); -#endif // DEBUG_DISPATCH - - queue.EnqueueTail(entry); - Monitoring.Log(Monitoring.Provider.AutoResetEvent, - (ushort)AutoResetEventEvent.Enqueue, 0, - (uint)this.id, (uint)entry.Thread.threadIndex, - 0, 0, 0); - return false; - } - } - - // Return thread who could use our priority. - [NoHeapAllocation] - internal override Thread GetBeneficiary() - { - return null; - } } } diff --git a/base/Kernel/System/Threading/Interlocked.cs b/base/Kernel/System/Threading/Interlocked.cs deleted file mode 100644 index 274b22d..0000000 --- a/base/Kernel/System/Threading/Interlocked.cs +++ /dev/null @@ -1,167 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -namespace System.Threading { - - using System; - using System.Runtime.CompilerServices; - -#if SINGULARITY_KERNEL - using Microsoft.Singularity.Memory; -#endif - - //| - public sealed class Interlocked - { - private Interlocked() { - } - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern int Increment(ref int location); - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern int Decrement(ref int location); - - //| - [NoHeapAllocation] - public static long Increment(ref long location) { - long value = location; - while (CompareExchange(ref location, value+1, value) != value) { - value = location; - } - return value+1; - } - - //| - [NoHeapAllocation] - public static long Decrement(ref long location) { - long value = location; - while (CompareExchange(ref location, value-1, value) != value) { - value = location; - } - return value-1; - } - - [CLSCompliant(false)] - [NoHeapAllocation] - public static ulong Add(ref ulong location, ulong value) - { - ulong ov = location; - ulong nv = ov + value; - while (CompareExchange(ref location, nv, ov) != ov) { - ov = location; - nv = ov + value; - } - return nv; - } - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern int Exchange(ref int location1, int value); - - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern int CompareExchange(ref int location1, int value, int comparand); - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern float Exchange(ref float location1, float value); - - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern float CompareExchange(ref float location1, float value, float comparand); - - // added for thread initialization. - [Intrinsic] - [NoHeapAllocation] - public static extern ThreadState CompareExchange(ref ThreadState location1, ThreadState value, ThreadState comparand); - - - [Intrinsic] - [NoHeapAllocation] - public static extern long CompareExchange(ref long location1, long value, long comparand); - - [CLSCompliant(false)] - [Intrinsic] - [NoHeapAllocation] - public static extern ulong CompareExchange(ref ulong location1, ulong value, ulong comparand); - - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern Object Exchange(ref Object location1, Object value); - - //| - [Intrinsic] - [NoHeapAllocation] - public static extern Object CompareExchange(ref Object location1, Object value, Object comparand); - - [CLSCompliant(false)] - [Intrinsic] - [NoHeapAllocation] - public static extern uint Exchange(ref uint location1, uint value); - - [CLSCompliant(false)] - [Intrinsic] - [NoHeapAllocation] - public static extern uint CompareExchange(ref uint location1, uint value, uint comparand); - - [CLSCompliant(false)] - [Intrinsic] - [NoHeapAllocation] - public static extern UIntPtr Exchange(ref UIntPtr location1, UIntPtr value); - - [CLSCompliant(false)] - [Intrinsic] - [NoHeapAllocation] - public static extern UIntPtr CompareExchange(ref UIntPtr location1, UIntPtr value, UIntPtr comparand); - - [CLSCompliant(false)] - [Intrinsic] - [NoHeapAllocation] - public static extern unsafe UIntPtr Exchange(UIntPtr * location1, UIntPtr value); - - [CLSCompliant(false)] - [Intrinsic] - [NoHeapAllocation] - public static extern unsafe UIntPtr CompareExchange(UIntPtr * location1, UIntPtr value, UIntPtr comparand); - - - [CLSCompliant(false)] - [Intrinsic] - [NoHeapAllocation] - public static extern unsafe void * CompareExchange(ref void * location1, void * value, void * comparand); - -#if SINGULARITY_KERNEL - [CLSCompliant(false)] - [Intrinsic] - [NoHeapAllocation] - internal static extern unsafe HandleTable.HandlePage * CompareExchange( - ref HandleTable.HandlePage * location1, - HandleTable.HandlePage * value, - HandleTable.HandlePage * comparand); - - [CLSCompliant(false)] - [Intrinsic] - [NoHeapAllocation] - internal static extern unsafe HandleTable.HandleEntry * CompareExchange( - ref HandleTable.HandleEntry * location1, - HandleTable.HandleEntry * value, - HandleTable.HandleEntry * comparand); -#endif - - } -} diff --git a/base/Kernel/System/Threading/InterruptAwareAutoResetEvent.cs b/base/Kernel/System/Threading/InterruptAwareAutoResetEvent.cs new file mode 100644 index 0000000..c69ad7a --- /dev/null +++ b/base/Kernel/System/Threading/InterruptAwareAutoResetEvent.cs @@ -0,0 +1,49 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Runtime.CompilerServices; +using Microsoft.Singularity.Scheduling; + +namespace System.Threading +{ + /// + /// + /// Event class implementing an event with auto reset symantics such event automatically + /// reset to non signaled state when single wait is satisfied. + /// + /// This class is interrupt aware: interrupts are disabled during the period when the + /// spin lock is held. Code that deals with interrupt handling, for example HAL + /// communication, that could be reentrant during interrupt handling should use + /// this class instead of AutoResetEvent + /// + /// + [NoCCtor] + [CLSCompliant(false)] + public sealed class InterruptAwareAutoResetEvent : InterruptAwareWaitHandle + { + /// + /// + /// Constructor + /// + /// + /// Initial state of an event true indciates that event is signaled + /// + public InterruptAwareAutoResetEvent(bool initialState) + : base(initialState ? WaitHandle.SignalState.Signaled : + WaitHandle.SignalState.Unsignaled, + WaitHandle.SignalState.Unsignaled, + SpinLock.Types.InterruptAutoResetEvent) + { + } + + [NoHeapAllocation] + public void InterruptAwareSet() + { + base.InterruptAwareSignalOne(SignalState.Signaled); + } + } +} \ No newline at end of file diff --git a/base/Kernel/System/Threading/InterruptAwareWaitHandle.cs b/base/Kernel/System/Threading/InterruptAwareWaitHandle.cs new file mode 100644 index 0000000..a184bc7 --- /dev/null +++ b/base/Kernel/System/Threading/InterruptAwareWaitHandle.cs @@ -0,0 +1,188 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Runtime.CompilerServices; +using Microsoft.Singularity; +using Microsoft.Singularity.Scheduling; + +namespace System.Threading +{ + /// + /// + /// Base class for all interrupt aware synchronization objects + /// + /// This class is interrupt aware: interrupts are disabled during the period when the + /// spin lock is held. + /// + /// + [NoCCtor] + [CLSCompliant(false)] + public abstract class InterruptAwareWaitHandle : WaitHandleBase + { + /// + /// + /// Constructor + /// + /// + /// Initial state of an handle + /// + /// Value represents a state of a handle when wait satisfied right a way + /// + /// The spin lock type of the wait handle + /// + protected InterruptAwareWaitHandle( + SignalState initialState, + SignalState signalStateAfterImediateWait, + SpinLock.Types spinLockType) + : base(initialState, signalStateAfterImediateWait) + { + this.singleHandle = new InterruptAwareWaitHandle[1] { this }; + // Initialize waithandle spinlock + this.myLock = new SpinLock(spinLockType); + } + + /// + /// + /// Signal wait handle by waking up a single waiter or if there are no waiters + /// set handle to specified stated. + /// + /// + /// + /// Set the wait handle into specified state if no waiters present + /// + /// + [NoHeapAllocation] + protected void InterruptAwareSignalOne(SignalState signalStateIfNoWaiters) + { + // Disable interrupt + bool interruptFlag = Processor.DisableInterrupts(); + + // Single a waiting thread and put it in the deferredWakeupQueue + ThreadQueueStruct deferredWakeupQueue = SignalOneWithNoWakeup(signalStateIfNoWaiters); + + // Restore interrupt if necessary + Processor.RestoreInterrupts(interruptFlag); + + // Wakeup a waiter if we need to + WakeupOneWaiter(deferredWakeupQueue); + } + + /// + /// + /// Associate a thread with wait handles if one of the waits satisfied return + /// waithandle id - actual unblocker. if none of the states satisfied return UninitWait + /// indicating that thread has to proceede with blocking + /// + /// + private static int InterruptAwarePreWaitAnyInternal( + Thread currentThread, + WaitHandleBase[] waitHandles, + ThreadEntry[] entries, + int waitHandlesCount) + { + // Disable interrupt + bool interruptFlag = Processor.DisableInterrupts(); + + int unblockedBy = PreWaitAnyInternal( + currentThread, + waitHandles, + entries, + waitHandlesCount); + + // Restore interrupt + Processor.RestoreInterrupts(interruptFlag); + + return unblockedBy; + } + + /// + /// + /// Post wait is to disassociate thread from all handlers + /// + /// + protected static void InterruptAwarePostWaitAnyInternal( + Thread currentThread, + WaitHandleBase[] waitHandles, + ThreadEntry[] entries, + int waitHandlesCount) + { + // Disable interrupt + bool interruptFlag = Processor.DisableInterrupts(); + + PostWaitAnyInternal(currentThread, waitHandles, entries, waitHandlesCount); + + // Restore interrupt + Processor.RestoreInterrupts(interruptFlag); + } + + /// + /// + /// Wait on a set of handles until one of them becomes signaled with a specified time out. + /// + /// !!! If you change this method, please review WaitHandle.WaitAny() + /// and see if the changes need to be propagated there. + /// + /// + public void InterruptAwareWaitOne() + { + // Retrieve current thread information + Thread currentThread = Thread.CurrentThread; + Thread target = null; + int unblockedBy; + ThreadEntry[] entries = currentThread.GetWaitEntries(1); + + // Before we attempting to enqueue ourselves into the wait queues make sure + // we disable abort + currentThread.DelayStop(true); + + // Perepare for a wait - enqueue ourselves into every wait handle + unblockedBy = InterruptAwarePreWaitAnyInternal(currentThread, singleHandle, entries, 1); + + // If we are in the process of blocking: Block + if (UninitWait == unblockedBy) { + // Allow thread to be aborted at this point + currentThread.DelayStop(false); + + // Write out log record + Monitoring.Log(Monitoring.Provider.Thread, + (ushort)ThreadEvent.WaitAny, 0, 0, 0, + (uint)currentThread.threadIndex, 0, 0); + + + // Let scheduler know that we are blocking + Kernel.TheScheduler.OnThreadBlocked(currentThread, SchedulerTime.MaxValue); + + // Our thread is about to run so we can disassociate it from wait handles + InterruptAwarePostWaitAnyInternal(currentThread, singleHandle, entries, 1); + + // Thread has the unblocked information + unblockedBy = currentThread.UnblockedBy; + } + + // Assert post condition: since there is no timeout, and we are waiting + // on a single handle, the unblockedBy must be 0 + VTable.Assert(unblockedBy == 0); + + // Complete wait + CompleteWait(currentThread); + + // When we were signalged delay abort has been set - now we can turn it off + // For mutex complete wait will add delay abort. It will remain on until + // mutex is released + currentThread.DelayStop(false); + + // Make sure that we pay attention to abort + currentThread.ProcessAbortIfRequired(); + } + + /// + /// This field is an array of length 1 containing 'this'. + /// It is used to avoid allocation when calling WaitAny from WaitOne. + /// + private InterruptAwareWaitHandle[] singleHandle; + } +} diff --git a/base/Kernel/System/Threading/KernelSpinLock.cs b/base/Kernel/System/Threading/KernelSpinLock.cs new file mode 100644 index 0000000..538be7b --- /dev/null +++ b/base/Kernel/System/Threading/KernelSpinLock.cs @@ -0,0 +1,540 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: KernelSpinlock.cs +// +// Note: +// Kernel spinlock functionality +// +using System; +using Microsoft.Singularity; +using Microsoft.Singularity.Audit; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace System.Threading +{ + /// + /// + /// Implementation of Kernel Spinlocks + /// + /// + [NoCCtor] + [CLSCompliant(false)] + [AccessedByRuntime("referenced from halidt.asm")] + public struct SpinLock + { + /// + /// + /// Spinlock Rank is enumerator used to enumerate spinlock ranks. The rule is: + /// holders of lower rank spinlocks can't acquire spinlocks with higher ranks. + /// Note this needs to be a bitmask enum because of the validation logic in Thread.cs + /// + /// + internal enum Ranks : short + { + /// Placeholder. eventually all spinlocks should be ranked + NoRank = 0x0, + + /// + /// Used by flag pages (low level memory management). This is nested + /// within several spin locks with Dispatcher rank, including + /// PageManager (GC) and linked stack. + /// + FlatPages = 0x1, + + /// + /// Use by Hal. This can be nested within other + /// spin locks with Dispatcher rank. + /// + Hal = 0x2, + + /// + /// Used by scheduler and dispatcher code, and other code run with + /// interrupt disabled. + /// + Dispatcher = 0x4, + + /// + /// The lowest rank above Dispatcher. A thread holding a lock with this rank + /// cannot acquire another spinlock, unless it enters scheduling code through + /// Yield or blocking. Most code above dispatcher level should use this rank. + /// + /// Add higher ranks only if the code needs to acquire multiple spinlocks, + /// which should be avoided unless absolutely necessary. + /// + Passive = 0x8, + + /// + /// Used by services implemented in kernel + /// + Service = 0x10, + }; + + /// + /// + /// Spinlock Type is enumerator used to enumerate spinlock types so that we can keep + /// proper statistic for each spinlock. Statistic will allow us to identify spinlock problems + /// + /// + public enum Types : int + { + NoType = 0, + InterruptMutex = (Ranks.Dispatcher << RankShift) | 1, + InterruptAutoResetEvent = (Ranks.Dispatcher << RankShift) | 2, + AutoResetEvent = (Ranks.Passive << RankShift) | 3, + Mutex = (Ranks.Passive << RankShift) | 4, + ManualResetEvent = (Ranks.Passive << RankShift) | 5, + Timer = (Ranks.Hal << RankShift) | 6, + IoApic = (Ranks.Hal << RankShift) | 7, + MpHalClock = (Ranks.Hal << RankShift) | 8, + RTClock = (Ranks.Hal << RankShift) | 9, + HalScreen = (Ranks.Hal << RankShift) | 10, + FlatPages = (Ranks.FlatPages << RankShift) | 11, + VirtualMemoryRange = (Ranks.Dispatcher << RankShift) | 12, + VMManager = (Ranks.Dispatcher << RankShift) | 13, + VMKernelMapping = (Ranks.Dispatcher << RankShift) | 14, + ProtectionDomainTable = (Ranks.Dispatcher << RankShift) | 15, + ProtectionDomainInit = (Ranks.Dispatcher << RankShift) | 16, + ProtectionDomainMapping = (Ranks.Dispatcher << RankShift) | 17, + SharedHeapAllocationOwner = (Ranks.Service << RankShift) | 18, + PhysicalHeap = (Ranks.Dispatcher << RankShift) | 19, + PhysicalPages = (Ranks.Dispatcher << RankShift) | 20, + PageManager = (Ranks.Dispatcher << RankShift) | 21, + IoResources = (Ranks.Dispatcher << RankShift) | 22, + Finalizer = (Ranks.Dispatcher << RankShift) | 23, + MpExecutionFreeze = (Ranks.Dispatcher << RankShift) | 24, + MpExecutionMpCall = (Ranks.Dispatcher << RankShift) | 25, + Scheduler = (Ranks.Dispatcher << RankShift) | 26, + GCTracing = (Ranks.Dispatcher << RankShift) | 27, + IoIrq = (Ranks.Dispatcher << RankShift) | 28, + ServiceQueue = (Ranks.Dispatcher << RankShift) | 29, + EndpointCore = (Ranks.Dispatcher << RankShift) | 30, + KernelTestCase = (Ranks.Dispatcher << RankShift) | 31, + HandleTable = (Ranks.Passive << RankShift) | 32, + ThreadTable = (Ranks.Passive << RankShift) | 33, + ProcessTable = (Ranks.Passive << RankShift) | 34, + Thread = (Ranks.Passive << RankShift) | 35, + MaxTypeId = (Thread & TypeMask) +1 + }; + + /// + /// + /// Static initializer + /// + /// + static public void StaticInitialize() + { + } + + /// + /// + /// Init spinlock + /// + /// Type of SpinLock + /// + public SpinLock(Types type) + { + baseLock = new SpinLockBase((int)type); + } + + /// + /// + /// SpinLock Rank + /// + /// + internal int Rank + { + [NoHeapAllocation] + get + { + return baseLock.Type >> RankShift; + } + } + + /// + /// + /// SpinLock Type + /// + /// + internal int Type + { + [NoHeapAllocation] + get + { + return baseLock.Type & TypeMask; + } + } + + /// + /// + /// Acquire spinlock + /// + /// + [NoHeapAllocation] + [Inline] + public void Acquire() + { + Thread thread = Thread.CurrentThread; + int threadId = (thread==null)?InitialThreadId:thread.GetThreadId(); + + AcquireInternal(thread, threadId); + } + + /// + /// + /// Acquire spinlock + /// + /// + /// Thread's Id acquiring spinlock + /// + [NoHeapAllocation] + [Inline] + public void Acquire(int threadId) + { + AcquireInternal(Thread.GetThreadFromThreadId(threadId), threadId); + } + + /// + /// + /// Acquire spinlock + /// + /// + /// Thread acquiring spinlock + /// + [NoHeapAllocation] + [Inline] + public void Acquire(Thread thread) + { + int threadId = (thread == null)?InitialThreadId:thread.GetThreadId(); + + AcquireInternal(thread, threadId); + } + + /// + /// + /// Release spinlock + /// + /// + [NoHeapAllocation] + [Inline] + public void Release() + { + Thread thread = Thread.CurrentThread; + int threadId = (thread == null)?InitialThreadId: + thread.GetThreadId(); + // Release spinlock + ReleaseInternal(thread, threadId); + } + + /// + /// + /// Release spinlock + /// + /// + /// Thread's Id releasing spinlock + /// + [NoHeapAllocation] + [Inline] + public void Release(int threadId) + { + // Release spinlock + ReleaseInternal(Thread.GetThreadFromThreadId(threadId), threadId); + } + + /// + /// + /// Release spinlock + /// + /// + /// Thread releasing spinlock + /// + [NoHeapAllocation] + [Inline] + public void Release(Thread thread) + { + int threadId = (thread == null)?InitialThreadId:thread.GetThreadId(); + + // Release spinlock + ReleaseInternal(thread, threadId); + } + + /// + /// + /// Try to acquire the spin lock. Always return immediately. + /// + /// true if the spin lock is acquired. + /// + [NoHeapAllocation] + [Inline] + public bool TryAcquire() + { + Thread thread = Thread.CurrentThread; + int threadId =(thread == null)?InitialThreadId:thread.GetThreadId(); + + return TryAcquireInternal(thread, threadId); + } + + /// + /// + /// Try to acquire the spin lock. Always return immediately. + /// + /// + /// true if the spin lock is acquired. + /// + /// Thread acquiring spinlock + /// + [NoHeapAllocation] + [Inline] + public bool TryAcquire(Thread thread) + { + int threadId = (thread == null)?InitialThreadId:thread.GetThreadId(); + + return TryAcquireInternal(thread, thread.GetThreadId()); + } + + /// + /// + /// Method to find out if spinlock is held by specified thread + /// + /// true if the spin lock is acquired. + /// + /// Thread to verify possible spinlock's ownership + /// + [NoHeapAllocation] + public bool IsHeldBy(Thread thread) + { + int threadId = (thread == null)?InitialThreadId:thread.GetThreadId(); + return baseLock.IsHeldBy(threadId + 1); + } + + /// + /// + /// Method to find out if spinlock is held by specified thread + /// + /// true if the spin lock is acquired. + /// + /// Thread's Id to verify possible spinlock's ownership + /// + [NoHeapAllocation] + public bool IsHeldBy(int threadId) + { + return baseLock.IsHeldBy(threadId + 1); + } + + /// + /// + /// Assert thatf spinlock is held by specified thread + /// + /// + /// Thread to verify possible spinlock's ownership + /// + [System.Diagnostics.Conditional("DEBUG")] + [NoHeapAllocation] + public void AssertHeldBy(Thread thread) + { + VTable.Assert(IsHeldBy(thread)); + } + + /// + /// + /// Assert thatf spinlock is held by specified thread + /// + /// + /// Thread's Id to verify possible spinlock's ownership + /// + [System.Diagnostics.Conditional("DEBUG")] + [NoHeapAllocation] + public void AssertHeldBy(int threadId) + { + VTable.Assert(IsHeldBy(threadId)); + } + + /// + /// + /// Given integer : derive type of a lock + /// + /// + /// Parameter from which we can derive actual type of spinlock + /// + [NoHeapAllocation] + [Inline] + internal static int DeriveType (int type) + { + // Type has to be in correct range + VTable.Assert((type & TypeMask) >= (int)Types.NoType && + (type & TypeMask) < (int)Types.MaxTypeId); + + return (type & (int)TypeMask); + } + + /// + /// + /// Given integer : derive type of a lock + /// + /// + /// Parameter from which we can derive actual rank of spinlock + /// + [NoHeapAllocation] + [Inline] + internal static int DeriveRank (int type) + { + // Type has to be in correct range + VTable.Assert((type & TypeMask) >= (int)Types.NoType && + (type & TypeMask) < (int)Types.MaxTypeId); + + return (type >> RankShift); + } + + /// + /// + /// Try to acquire the spin lock. Always return immediately. + /// + /// true if the spin lock is acquired. + /// + /// Thread acquiring spinlock + /// Thread's Id acquiring spinlock + /// + [NoHeapAllocation] + [Inline] + private bool TryAcquireInternal(Thread thread, int threadId) + { + bool result; + + // Assert preconditions:for spinlock with a rank = DisabledInterrupts and below + // interrupts have to be disabled. + VTable.Assert(Rank == (int)Ranks.NoRank || + Rank > (int)Ranks.Dispatcher || + Processor.InterruptsDisabled()); + + + // Notify thread that we are about to acquire spinlock + if (thread != null) { + thread.NotifySpinLockAboutToAcquire(this.baseLock.Type); + } + + result = baseLock.TryAcquire(threadId + 1); + + // If we didn't acquire spinlock -we should notify thread about it: Just use release + // notification + if (thread != null && !result) { + thread.NotifySpinLockReleased(this.baseLock.Type); + } + + return result; + } + + /// + /// + /// Acquire the spin lock. + /// + /// + /// Thread acquiring spinlock + /// Thread's Id acquiring spinlock + /// + [NoHeapAllocation] + [Inline] + private void AcquireInternal(Thread thread, int threadId) + { + // Assert preconditions:for spinlock with a rank = DisabledInterrupts and below + // interrupts have to be disabled. + VTable.Assert(Rank == (int)Ranks.NoRank || + Rank > (int)Ranks.Dispatcher || + Processor.InterruptsDisabled()); + + + // Thread has to be notified if we are about to acquire spinlock + if (thread != null) { + thread.NotifySpinLockAboutToAcquire(this.baseLock.Type); + } + + // Get lock + baseLock.Acquire(threadId + 1); + } + + /// + /// + /// Release the spin lock. + /// + /// + /// Thread releasing spinlock + /// Thread's Id releasing spinlock + /// + [NoHeapAllocation] + [Inline] + private void ReleaseInternal(Thread thread, int threadId) + { + // Assert preconditions:for spinlock with a rank = DisabledInterrupts and below + // interrupts have to be disabled. + VTable.Assert(Rank == (int)Ranks.NoRank || + Rank > (int)Ranks.Dispatcher || + Processor.InterruptsDisabled()); + + + // Release spinlock + baseLock.Release(threadId + 1); + + if (thread != null) { + // Don't forget to notify thread that it just released spinlock + thread.NotifySpinLockReleased(this.baseLock.Type); + } + } + + /// Constant defines shift of the rank + internal const int RankShift = 0x10; + + /// Constant defines mask for getting type of gompound spinlock type + internal const int TypeMask = 0xFFFF; + + /// Id of a initial thread + private const int InitialThreadId = -2; + + /// Actual mechanism implementing spinlock + private SpinLockBase baseLock; + } + + /// Attribute to mark methods that stop lock rank verification + [Layer(0)] + public class IgnoreLockRankAttribute : Attribute + { + public IgnoreLockRankAttribute() {} + } + + /// Attribute to mark classes / methods that hold FlatPages locks + [Layer(1)] + public class FlatPagesLockAttribute : Attribute + { + public FlatPagesLockAttribute() {} + } + + /// Attribute to mark classes / methods that hold Hal locks + [Layer(2)] + public class HalLockAttribute : Attribute + { + public HalLockAttribute() {} + } + + /// Attribute to mark classes / methods that hold Dispatcher locks + [Layer(3)] + public class DispatcherLockAttribute : Attribute + { + public DispatcherLockAttribute() {} + } + + /// Attribute to mark classes / methods that hold Passive locks + [Layer(4)] + public class PassiveLockAttribute : Attribute + { + public PassiveLockAttribute() {} + } + + /// Attribute to mark classes / methods that hold Service locks + [Layer(5)] + public class ServiceLockAttribute : Attribute + { + public ServiceLockAttribute() {} + } +} diff --git a/base/Kernel/System/Threading/ManualResetEvent.cs b/base/Kernel/System/Threading/ManualResetEvent.cs index dd93931..1352794 100644 --- a/base/Kernel/System/Threading/ManualResetEvent.cs +++ b/base/Kernel/System/Threading/ManualResetEvent.cs @@ -17,93 +17,57 @@ using Microsoft.Singularity.Scheduling; namespace System.Threading { - [CLSCompliant(false)] - public enum ManualResetEventEvent : ushort - { - Acquire = 1, - Enqueue = 2 - } - - //| + + /// + /// + /// Event class implementing an event with manual reset symantics such event has to + /// be manually set to unsignaled state + /// + /// [NoCCtor] [CLSCompliant(false)] public sealed class ManualResetEvent : WaitHandle { - //| + /// + /// + /// Constructor + /// + /// + /// Initial state of an event true indciates that event is signaled + /// public ManualResetEvent(bool initialState) - : base(initialState ? 1 : 0) + : base(initialState ? WaitHandle.SignalState.Signaled : + WaitHandle.SignalState.Unsignaled, + WaitHandle.SignalState.Signaled, + SpinLock.Types.ManualResetEvent) { } - //| + /// + /// + /// Reset an event state to non signaled + /// + /// + [NoHeapAllocation] public bool Reset() { - bool iflag = Processor.DisableInterrupts(); - try { - Scheduler.DispatchLock(); - try { - signaled = 0; - } - finally { - Scheduler.DispatchRelease(); - } - } - finally { - Processor.RestoreInterrupts(iflag); - } - return true; + // Make sure that we don't have any waiters and then change event state to unsignaled + // in all cases + SignalAll (WaitHandle.SignalState.Unsignaled, WaitHandle.SignalState.Unsignaled); + + return true; } - //| + /// + /// + /// Wake up all waiters and leave state signaled, if no waiters present set event into signaled state + /// + /// + [NoHeapAllocation] public bool Set() { - bool iflag = Processor.DisableInterrupts(); - try { - Scheduler.DispatchLock(); - try { - signaled = 1; - NotifyAll(); - } - finally { - Scheduler.DispatchRelease(); - } - } - finally { - Processor.RestoreInterrupts(iflag); - } + SignalAll (WaitHandle.SignalState.Signaled,WaitHandle.SignalState.Signaled); return true; } - - // Called with dispatch lock held and interrupts off. - // Returns true if the ManuelResetEvent was signaled. - internal override bool AcquireOrEnqueue(ThreadEntry entry) - { -#if DEBUG_DISPATCH - DebugStub.Print("ManualResetEvent:AcquireOrEnqueue 001\n"); -#endif // DEBUG_DISPATCH - - if (signaled > 0) { - Monitoring.Log(Monitoring.Provider.ManualResetEvent, - (ushort)ManualResetEventEvent.Acquire, 0, - (uint)this.id, (uint)entry.Thread.threadIndex, - 0, 0, 0); - return true; - } - else { - queue.EnqueueTail(entry); - Monitoring.Log(Monitoring.Provider.ManualResetEvent, - (ushort)ManualResetEventEvent.Enqueue, 0, - (uint)this.id, (uint)entry.Thread.threadIndex, - 0, 0, 0); - return false; - } - } - - // Return thread who could use our priority. - [NoHeapAllocation] - internal override Thread GetBeneficiary() - { - return null; - } } } diff --git a/base/Kernel/System/Threading/Monitor.cs b/base/Kernel/System/Threading/Monitor.cs deleted file mode 100644 index f5fc1f1..0000000 --- a/base/Kernel/System/Threading/Monitor.cs +++ /dev/null @@ -1,416 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -namespace System.Threading { - - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - - using Microsoft.Bartok.Runtime; - - using Microsoft.Singularity; - - /// - /// A monitor is used for synchronization. Only a single thread can - /// hold the monitor at any given time. - /// - /// The monitor maintains two lists of threads: one for threads waiting - /// to enter the monitor, and one for threads waiting for a pulse within - /// the monitor. - /// - [NoCCtor] - public sealed class Monitor { - /// - /// Private so that only we can create instances. - /// - internal Monitor() - { - this.mutex = new Mutex(); - this.depth = 0; - } - - /// - /// Attempt to enter the monitor, blocking until it is held. - /// - [Intrinsic] - public static extern void Enter(Object obj); - - [RequiredByBartok("Enter gets lowered to this")] - private static void EnterImpl(Object obj) { - Monitor monitor = GetMonitorFromObject(obj); - monitor.Enter(); - } - - /// - /// Exit the monitor. - /// - [Intrinsic] - public static extern void Exit(Object obj); - - [RequiredByBartok("Exit gets lowered to this")] - public static void ExitImpl(Object obj) { - Monitor monitor = GetMonitorFromObject(obj); - monitor.Exit(); - } - - /// - /// Wake up a thread waiting on the monitor. - /// - public static void Pulse(Object obj) - { - Monitor monitor = GetMonitorFromObject(obj); - monitor.Pulse(); - } - - /// - /// Wake up all threads waiting on the monitor. - /// - public static void PulseAll(Object obj) - { - Monitor monitor = GetMonitorFromObject(obj); - monitor.PulseAll(); - } - - /// - /// Attempt to enter the monitor, returning immediately if it is - /// already held by another thread. - /// - public static bool TryEnter(Object obj) - { - Monitor monitor = GetMonitorFromObject(obj); - return monitor.TryEnter(); - } - - /// - /// Attempt to enter the monitor, returning if it can not be taken - /// within the specified timeout. - /// - public static bool TryEnter(Object obj, TimeSpan timeout) - { - Monitor monitor = GetMonitorFromObject(obj); - return monitor.TryEnter(SchedulerTime.Now + timeout); - } - - /// - /// Wait to be woken up by a holder of the monitor. - /// - public static bool Wait(Object obj) - { - Monitor monitor = GetMonitorFromObject(obj); - return monitor.Wait(SchedulerTime.MaxValue); - } - - /// - /// Wait to be woken up by a holder of the monitor. Give up after - /// a specified timeout. - /// - public static bool Wait(Object obj, TimeSpan timeout) - { - Monitor monitor = GetMonitorFromObject(obj); - return monitor.Wait(SchedulerTime.Now + timeout); - } - - /// - /// Wait to be woken up by a holder of the monitor. Give up after - /// a specified timeout. - /// - /// Overload exists to match the CLR. Exit Context not supported. - /// - public static bool Wait(Object obj, - TimeSpan timeout, - bool exitContext) - { - if (exitContext) { - DebugStub.Break(); - throw new NotSupportedException("exitContext not supported!"); - } - Monitor monitor = GetMonitorFromObject(obj); - return monitor.Wait(SchedulerTime.Now + timeout); - } - - /// - /// Enter the monitor, blocking until it is held. - /// - internal void Enter() - { - TryEnter(SchedulerTime.MaxValue); - } - - /// - /// Exit the monitor. - /// - internal void Exit() - { - if (!mutex.IsOwnedByCurrentThread()) { - DebugStub.Break(); - throw new SynchronizationLockException("Monitor not held on Exit"); - } - - depth--; - if (depth == 0) { - mutex.ReleaseMutex(); - } - } - - /// - /// Wake up a single thread waiting on the monitor. - /// - internal void Pulse() - { - if (!mutex.IsOwnedByCurrentThread()) { - DebugStub.Break(); - throw new SynchronizationLockException("Monitor not held on Pulse"); - } - - // Wake up thread at the head of the wait list. - if (waitListHead != null) { - Thread t = Dequeue(); - if (t != null) { - t.nextThread = null; - t.SignalMonitor(); - } - } - } - - /// - /// Wake up all threads waiting on the monitor. - /// - internal void PulseAll() - { - if (!mutex.IsOwnedByCurrentThread()) { - DebugStub.Break(); - throw new SynchronizationLockException("Monitor not held on PulseAll"); - } - - // Wake up all threads the wait list. - if (waitListHead != null) { - Thread t = waitListHead; - while (t != null) { - Thread next = t.nextThread; - t.nextThread = null; - t.SignalMonitor(); - t = next; - } - waitListHead = null; - waitListTail = null; - } - } - - /// - /// Try to enter the monitor, returning immediately if it is - /// already held. - /// - internal bool TryEnter() - { - return TryEnter(new SchedulerTime(0)); - } - - /// - /// Try to enter the monitor, giving up if it cannot be - /// entered after a timeout. - /// - internal bool TryEnter(SchedulerTime stop) - { - if (mutex.IsOwnedByCurrentThread()) { - depth++; - return true; - } - - if (mutex.AcquireMutex(stop)) { - depth = 1; - return true; - } - return false; - } - - /// - /// Wait within the monitor for a Pulse. - /// - internal bool Wait(SchedulerTime stop) - { - Thread currentThread = Thread.CurrentThread; - if (!mutex.IsOwnedByCurrentThread()) { - DebugStub.Break(); - throw new SynchronizationLockException("Monitor not held on Wait"); - } - - int rememberedDepth = depth; - depth = 0; - - // Add me onto the waiting list. - Enqueue(currentThread); - - // Exit the monitor - mutex.ReleaseMutex(); - - // Wait - currentThread.WaitForMonitor(stop); - - // Re-enter the monitor - mutex.AcquireMutex(); - depth = rememberedDepth; - - bool success = !Remove(currentThread); - - if (!success && stop == SchedulerTime.MaxValue) { - VTable.DebugBreak(); - } - - return success; - } - - /// - /// Ensure that the passed object has a monitor (and associated - /// SyncBlock) allocated. - /// - internal static void CreateMonitor(Object obj) - { - GetMonitorFromObject(obj); - } - - // BUGBUG: The garbage collectors will not collect monitors that are - // in use. Use a very defensive strategy for now. - internal bool IsInUse() { - return true; - } - - /// - /// Internal Type conversion method. - /// Note: we don't use VTable.fromAddress because we - /// cannot do a checked cast from Object to Monitor during GC - /// (because the GC may be using the vtable word) - /// - /// - internal static Monitor FromAddress(UIntPtr v) { - return Magic.toMonitor(Magic.fromAddress(v)); - } - - /// - /// Look up the Monitor for the specified object in the SyncBlock - /// tables. If no Monitor exists for the object then one is created. - /// - private static Monitor GetMonitorFromObject(Object obj) - { - if (obj == null) { - DebugStub.Break(); - throw new ArgumentNullException("obj"); - } - Monitor result = MultiUseWord.GetMonitor(obj); - return result; - } - - ////////////////////////////////////////////////////////////////////// - // - // Linked list of threads waiting for a Pulse in a monitor. - private Thread waitListHead; - private Thread waitListTail; - - /// - /// Dequeue a thread from the singly linked list from head to tail, - /// acquiring the ListLock if necessary. - /// - /// If the list is empty then this method returns null. - /// - [Inline] - private Thread Dequeue() - { - Thread result; - if (waitListHead == null) { - // Empty list - result = null; - } - else if (waitListHead == waitListTail) { - // Single entry on list - VTable.Assert(waitListHead.nextThread == null); - result = waitListHead; - waitListHead = waitListTail = null; - } - else { - // Multiple entries on list - result = waitListHead; - waitListHead = waitListHead.nextThread; - } - return result; - } - - /// - /// Search the linked list and remove the specified thread if - /// it is linked in. - /// - /// Acquires the ListLock if necessary. - /// - [Inline] - private bool Remove(Thread target) - { - if (waitListHead == null) { - return false; - } - - if (waitListHead == waitListTail) { - // Single entry on list - VTable.Assert(waitListHead.nextThread == null); - - if (waitListHead != target) { - // Not on list - return false; - } - - waitListHead = waitListTail = null; - } - else if (waitListHead == target) { - // At waitListHead of list - waitListHead = target.nextThread; - target.nextThread = null; - } - else { - // Multiple entries on list - Thread next = waitListHead; - while (next != null && next.nextThread != target) { - next = next.nextThread; - } - - if (next == null) { - // Not on list - return false; - } - - if (waitListTail == target) { - // Update the waitListTail - waitListTail = next; - } - - next.nextThread = target.nextThread; - target.nextThread = null; - } - return true; - } - - /// - /// Append a thread at the tail of a queue. If the queue is - /// currently null this method initializes it. - /// - /// Acquires the ListLock if necessary. - /// - [Inline] - private void Enqueue(Thread target) - { - if (waitListHead == null) { - waitListHead = waitListTail = target; - } else { - waitListTail.nextThread = target; - waitListTail = target; - } - } - - /// - /// The recursion depth of the current holder of the monitor. - /// - private int depth; - - /// - /// The mutex that is held by the thread that holds the monitor - /// - private Mutex mutex; - } -} diff --git a/base/Kernel/System/Threading/MonitorWaitHandle.cs b/base/Kernel/System/Threading/MonitorWaitHandle.cs index 4f683e5..c610e47 100644 --- a/base/Kernel/System/Threading/MonitorWaitHandle.cs +++ b/base/Kernel/System/Threading/MonitorWaitHandle.cs @@ -1,18 +1,19 @@ // -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // -namespace System.Threading { +namespace System.Threading +{ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; /// /// A monitor is used for synchronization. Only a single thread can - /// hold the monitor at any given time. - /// - /// The monitor maintains two lists of threads: one for threads waiting - /// to enter the monitor, and one for threads waiting for a pulse within + /// hold the monitor at any given time. + /// + /// The monitor maintains two lists of threads: one for threads waiting + /// to enter the monitor, and one for threads waiting for a pulse within /// the monitor. /// public sealed class Monitor { @@ -49,7 +50,7 @@ namespace System.Threading { Monitor monitor = GetMonitorFromSyncBlock(obj); monitor.Pulse(); } - + /// /// Wake up all threads waiting on the monitor. /// @@ -59,7 +60,7 @@ namespace System.Threading { } /// - /// Attempt to enter the monitor, returning immediately if it is + /// Attempt to enter the monitor, returning immediately if it is /// already held by another thread. /// public static bool TryEnter(Object obj) { @@ -86,7 +87,7 @@ namespace System.Threading { } /// - /// Wait to be woken up by a holder of the monitor. + /// Wait to be woken up by a holder of the monitor. /// public static bool Wait(Object obj) { Monitor monitor = GetMonitorFromSyncBlock(obj); @@ -95,7 +96,7 @@ namespace System.Threading { /// /// Wait to be woken up by a holder of the monitor. Give up after - /// a specified timeout. + /// a specified timeout. /// public static bool Wait(Object obj, int millisecondsTimeout) { Monitor monitor = GetMonitorFromSyncBlock(obj); @@ -104,7 +105,7 @@ namespace System.Threading { /// /// Wait to be woken up by a holder of the monitor. Give up after - /// a specified timeout. + /// a specified timeout. /// public static bool Wait(Object obj, TimeSpan timeout) { Monitor monitor = GetMonitorFromSyncBlock(obj); @@ -113,12 +114,12 @@ namespace System.Threading { /// /// Wait to be woken up by a holder of the monitor. Give up after - /// a specified timeout. - /// + /// a specified timeout. + /// /// Overload exists to match the CLR. Exit Context not supported. /// - public static bool Wait(Object obj, - int millisecondsTimeout, + public static bool Wait(Object obj, + int millisecondsTimeout, bool exitContext) { if (exitContext) throw new NotSupportedException("exitContext not supported!"); @@ -128,12 +129,12 @@ namespace System.Threading { /// /// Wait to be woken up by a holder of the monitor. Give up after - /// a specified timeout. - /// + /// a specified timeout. + /// /// Overload exists to match the CLR. Exit Context not supported. /// - public static bool Wait(Object obj, - TimeSpan timeout, + public static bool Wait(Object obj, + TimeSpan timeout, bool exitContext) { if (exitContext) throw new NotSupportedException("exitContext not supported!"); @@ -147,13 +148,13 @@ namespace System.Threading { [Inline] private static int GetMillisecondTimeout(TimeSpan timeout) { int tm = (int)timeout.TotalMilliseconds; - if (tm < -1 || tm > Int32.MaxValue) { - throw new ArgumentOutOfRangeException("timeout", + if (tm < - 1 || tm > Int32.MaxValue) { + throw new ArgumentOutOfRangeException("timeout", "ArgumentOutOfRange_NeedNonNegOrNegative1"); } return tm; } - + /// /// Enter the monitor, blocking until it is held. /// @@ -209,7 +210,7 @@ namespace System.Threading { } /// - /// Try to enter the monitor, giving up if it cannot be + /// Try to enter the monitor, giving up if it cannot be /// entered after a timeout. /// internal bool TryEnter(int timeout) { @@ -237,12 +238,12 @@ namespace System.Threading { bool waitSuccess = this.WaitEvent.WaitOne(); this.Mutex.WaitOne(); this.Depth = rememberedDepth; - + return waitSuccess; } /// - /// Ensure that the passed object has a monitor (and associated + /// Ensure that the passed object has a monitor (and associated /// SyncBlock) allocated. /// internal static void CreateMonitor(Object obj) { @@ -265,11 +266,12 @@ namespace System.Threading { } Monitor monitor = table[index].Monitor; if (monitor == null) { - monitor = new Monitor(); - table[index].Monitor = monitor; + Interlocked.CompareExchange(ref table[index].Monitor, new Monitor(), null); + monitor = table[index].Monitor; } return monitor; - } else { + } + else { return table[index].Monitor; } } diff --git a/base/Kernel/System/Threading/Mutex.cs b/base/Kernel/System/Threading/Mutex.cs index a36aa43..ac65b81 100644 --- a/base/Kernel/System/Threading/Mutex.cs +++ b/base/Kernel/System/Threading/Mutex.cs @@ -26,135 +26,239 @@ namespace System.Threading Enqueue = 3, } - //| + /// + /// + /// Mutex class implementing a mutex functionality + /// + /// [NoCCtor] [CLSCompliant(false)] public sealed class Mutex : WaitHandle { - private int acquired = 0; // Number of times acquired by same thread. - - //| - public Mutex(bool initiallyOwned) - : base(initiallyOwned ? 0 : 1) + /// + /// + /// Default constructor + /// + /// + public Mutex() + : this(false, true) { + } + + /// + /// + /// Constructor + /// + /// + /// Initial state of a mutex + /// + /// We assume that mutex can initially be owned by current thread only + /// + public Mutex(bool initiallyOwned) + : this(initiallyOwned, true) + { + } + + /// + /// + /// Constructor + /// + /// + /// Initial state of a mutex + /// + /// True if this mutex is created by kernel and therefore used by kernel threads. + /// False if this mutex is created by a SIP and therefore used only by the SIP. + /// + /// A kernel thread is not allowed to be forcibly stopped while owning a mutex, + /// whereas SIP threads can be forcibly stopped while owning a mutex. This doesn't + /// create problems for SIPs because the only time a SIP thread is forced to stop is + /// during process torn down. + /// + /// + /// We assume that mutex can initially be owned by current thread only + /// + public Mutex(bool initiallyOwned, bool isKernelObject) + : base(initiallyOwned ? WaitHandle.SignalState.Unsignaled : + WaitHandle.SignalState.Signaled, + WaitHandle.SignalState.Unsignaled, + SpinLock.Types.Mutex) + + { + this.isKernelObject = isKernelObject; if (initiallyOwned) { - owner = Thread.CurrentThread; - acquired = 1; + Thread currentThread = Thread.CurrentThread; + this.owner = Thread.CurrentThread; + + if (this.isKernelObject) { + // Kernel thread can't be stop if it owns mutex + currentThread.DelayStop(true); + } + + this.acquired = 1; } } - //| - public Mutex() - : base(1) - { + /// + /// + /// Finalize method - release mutex by hand if it is going away + /// + /// + [CLSCompliant(false)] + public void Finalize() { + + // If a thread owns mutex and mutex is pure kernel object update its dealy abort counter + if (this.owner != null && this.isKernelObject) { + this.owner.DelayStop(false); + } } - public bool AcquireMutex() + /// + /// + /// Acquire mutex without time out specified + /// + /// + public void AcquireMutex() { - return WaitOne(); + WaitOne(SchedulerTime.MaxValue); } + /// + /// + /// Acquire mutex with time out specified + /// + /// + /// Specified time out + /// public bool AcquireMutex(SchedulerTime stop) { - return WaitOne(stop); + bool result = true;; + Thread currentThread = Thread.CurrentThread; + + // If we own the mutex we can by pass the wait - do wait only if we don't own + if (this.owner != currentThread) { + result = WaitOne(stop); + } + else { + // Don't forget to update recursion counter + this.acquired++; + } + + + return result; } - //| + /// + /// + /// Release mutex + /// + /// public void ReleaseMutex() { -#if DEBUG_DISPATCH - DebugStub.Print("Mutex:ReleaseMutex 001\n"); -#endif // DEBUG_DISPATCH - if (Thread.CurrentThread != owner) { - VTable.Assert(Thread.CurrentThread == owner); - return; - } + // Assert precondition: Only thread that owns mutex can release it and mutex + // acquired counter should be strictly positive + VTable.Assert(Thread.CurrentThread == this.owner); + VTable.Assert(this.acquired > 0); - bool iflag = Processor.DisableInterrupts(); - try { - Scheduler.DispatchLock(); - try { - VTable.Assert(acquired >= 0); + if (Thread.CurrentThread == this.owner && this.acquired > 0) { + // Decrement recurtion counter - first step for giving up ownership + this.acquired--; - acquired--; - if (acquired == 0) { -#if DEBUG_DISPATCH - DebugStub.Print("Mutex:ReleaseMutex 002\n"); -#endif // DEBUG_DISPATCH - if (NotifyOne()) { -#if DEBUG_DISPATCH - DebugStub.Print("Mutex:ReleaseMutex 003\n"); -#endif // DEBUG_DISPATCH - signaled = 0; - acquired = 1; - } - else { - signaled = 1; - owner = null; - } + // If we are done with mutex - give up ownership + if (this.acquired == 0) { + + //Indicate that noone longer owns mutex + this.owner = null; + + // Wakeup waiters + SignalOne(WaitHandle.SignalState.Signaled); + + // If this is kernel object, the thread can be aborted now + if (this.isKernelObject) { + Thread.CurrentThread.DelayStop(false); } } - finally { - Scheduler.DispatchRelease(); - } - } - finally { - Processor.RestoreInterrupts(iflag); } } - // Called by monitor to see if its lock is held by the current thread. + /// + /// + /// Check if mutex is owned by the current thread + /// + /// internal bool IsOwnedByCurrentThread() { return (Thread.CurrentThread == owner); } - // Called with dispatch lock held and interrupts off. - // Returns true if the mutex was acquired. - internal override bool AcquireOrEnqueue(ThreadEntry entry) + /// + /// + /// Try to acquire a mutex if acquire fail entry will be enqueued onto wait queue + /// + /// + /// Entry represents a thread attempting to acquire mutex + /// + /// Id of the handle that we are trying to acquire - is used to check if thread can be unblocked + /// + /// + [NoHeapAllocation] + protected override bool AcquireOrEnqueue(ThreadEntry entry, int handleId) { - if (owner == entry.Thread) { -#if DEBUG_DISPATCH - DebugStub.Print("Mutex:AcquireOrEnqueue 001\n"); -#endif // DEBUG_DISPATCH - acquired++; - Monitoring.Log(Monitoring.Provider.Mutex, - (ushort)MutexEvent.AcquireAgain, 0, - (uint)this.id, (uint)entry.Thread.threadIndex, - 0, 0, 0); - return true; + bool didAcquire = true; + + // Assert preconditions: Current thread and entry's thread should be the same + VTable.Assert(Thread.CurrentThread == entry.Thread); + VTable.Assert(Thread.CurrentThread.IsAbortDelayed()); + + // If we currently don't own mutex try to acquire it or enqueue ourselves.. + if (this.owner != entry.Thread) { + didAcquire = base.AcquireOrEnqueue (entry, handleId); } - else if (signaled != 0) { -#if DEBUG_DISPATCH - DebugStub.Print("Mutex:AcquireOrEnqueue 002\n"); -#endif // DEBUG_DISPATCH - signaled = 0; - owner = entry.Thread; - acquired = 1; - Monitoring.Log(Monitoring.Provider.Mutex, - (ushort)MutexEvent.Acquire, 0, - (uint)this.id, (uint)entry.Thread.threadIndex, - 0, 0, 0); - return true; - } - else { -#if DEBUG_DISPATCH - DebugStub.Print("Mutex:AcquireOrEnqueue 003\n"); -#endif // DEBUG_DISPATCH - queue.EnqueueTail(entry); - Monitoring.Log(Monitoring.Provider.Mutex, - (ushort)MutexEvent.Enqueue, 0, - (uint)this.id, (uint)entry.Thread.threadIndex, - 0, 0, 0); - return false; + + return didAcquire; + } + + /// + /// + /// Complete wait - used by mutex to record ownership + /// + /// + /// Thread owner + /// + [NoHeapAllocation] + protected override void CompleteWait(Thread ownerThread) + { + // Assert preconditions + VTable.Assert(Thread.CurrentThread == ownerThread); + + // Update recursion counter + this.acquired++; + + //If this is first time we acquired mutex don't forget to update owner + if (this.acquired == 1) { + + if (this.isKernelObject) { + // Kernel thread can't be stop if it owns mutex + ownerThread.DelayStop(true); + } + this.owner = ownerThread; } } - // Return mutex owner. - [NoHeapAllocation] - internal override Thread GetBeneficiary() - { - return owner; - } + /// + /// Recursion counter indicating number of times mutex is acquired by the same + /// thread + /// + private int acquired = 0; + + /// + /// True if this mutex is created by kernel and therefore used by kernel threads. + /// False if this mutex is created by a SIP and therefore used only by the SIP. + /// + /// A kernel thread is not allowed to be forcibly stopped while owning a mutex, + /// whereas SIP threads can be forcibly stopped while owning a mutex. This doesn't + /// create problems for SIPs because the only time a SIP thread is forced to stop is + /// during process torn down. + /// + private readonly bool isKernelObject; } } diff --git a/base/Kernel/System/Threading/PreemptionLock.cs b/base/Kernel/System/Threading/PreemptionLock.cs new file mode 100644 index 0000000..db7f470 --- /dev/null +++ b/base/Kernel/System/Threading/PreemptionLock.cs @@ -0,0 +1,42 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using Microsoft.Singularity; +using System; +using System.Runtime.CompilerServices; + +namespace System.Threading +{ + + /// + // Prevents preemption by disabling interrupts. + // + [NoCCtor] + [CLSCompliant(false)] + public struct PreemptionLock + { + [Inline] + [NoHeapAllocation] + public bool Lock() + { +#if TRACE + Tracing.Log(Tracing.Debug, "PreemptionLock.Acquire()"); +#endif + return Processor.DisableLocalPreemption(); + } + + [Inline] + [NoHeapAllocation] + public void Unlock(bool iflag) + { +#if TRACE + Tracing.Log(Tracing.Debug, "PreemptionLock.Release()"); +#endif + Processor.RestoreLocalPreemption(iflag); + } + } +} diff --git a/base/Kernel/System/Threading/SmartSpinlock.cs b/base/Kernel/System/Threading/SmartSpinlock.cs index 0b81e64..dc3de55 100644 --- a/base/Kernel/System/Threading/SmartSpinlock.cs +++ b/base/Kernel/System/Threading/SmartSpinlock.cs @@ -19,16 +19,17 @@ using System.Runtime.CompilerServices; namespace System.Threading { + [CLSCompliant(false)] public class SmartSpinlock { #if SINGULARITY_MP private SpinLock spin; #endif - public SmartSpinlock() + public SmartSpinlock(SpinLock.Types type) { #if SINGULARITY_MP - this.spin = new SpinLock(); + this.spin = new SpinLock(type); #endif } diff --git a/base/Kernel/System/Threading/SpinLock.cs b/base/Kernel/System/Threading/SpinLock.cs deleted file mode 100644 index 41aef76..0000000 --- a/base/Kernel/System/Threading/SpinLock.cs +++ /dev/null @@ -1,166 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -namespace System.Threading { - - using Microsoft.Singularity; - - using System.Runtime.CompilerServices; - using System; - - [NoCCtor] - [CLSCompliant(false)] - public struct SpinLock - { - [NoHeapAllocation] - public void Acquire() - { - Acquire(Thread.CurrentThread); - } - - // Note: any changes to this code must be reflected in - // the Release code in SwitchToThreadContext in halidt.asm. - [NoHeapAllocation] - public void Release() - { - Release(Thread.CurrentThread); - } - - [NoHeapAllocation] - public void Acquire(Thread thread) - { -#if TRACE - Tracing.Log(Tracing.Debug, "SpinLock.Acquire({0:x8},{1})", - VTable.addressOf(thread), - (UIntPtr)unchecked((uint)(thread != null ? thread.threadIndex : -1))); -#endif - AcquireInternal(thread.GetThreadId() + 1); - } - - // Note: any changes to this code must be reflected in - // the Release code in SwitchToThreadContext in halidt.asm. - [NoHeapAllocation] - public void Release(Thread thread) - { -#if TRACE - Tracing.Log(Tracing.Debug, "SpinLock.Release({0:x8},{1})", - VTable.addressOf(thread), - (UIntPtr)unchecked((uint)(thread != null ? thread.threadIndex : -1))); -#endif - ReleaseInternal(thread.GetThreadId() + 1); - } - - [System.Diagnostics.Conditional("DEBUG")] - [NoHeapAllocation] - public void AssertHeldBy(Thread thread) - { - VTable.Assert(IsHeldBy(thread)); - } - - [NoHeapAllocation] - public bool IsHeldBy(Thread thread) - { -#if TRACE - Tracing.Log(Tracing.Debug, "SpinLock.IsHeldBy({0:x8},{1})", - VTable.addressOf(thread), - (UIntPtr)unchecked((uint)(thread != null ? thread.threadIndex : -1))); -#endif - return IsHeldByInternal(thread.GetThreadId() + 1); - } - - [NoHeapAllocation] - public void Acquire(int threadId) - { - AcquireInternal(threadId + 1); - } - - [NoHeapAllocation] - public void Release(int threadId) - { - ReleaseInternal(threadId + 1); - } - - [System.Diagnostics.Conditional("DEBUG")] - [NoHeapAllocation] - public void AssertHeldBy(int threadId) - { - VTable.Assert(IsHeldByInternal(threadId + 1)); - } - - [NoHeapAllocation] - public bool IsHeldBy(int threadId) - { - return IsHeldByInternal(threadId + 1); - } - - [NoHeapAllocation] - private void AcquireInternal(int currentThreadIndexPlusOne) - { - // SpinLocks are *NOT* re-entrant. - VTable.Assert(lockingThreadIndexPlusOne != - currentThreadIndexPlusOne); - VTable.Assert(Processor.InterruptsDisabled()); - - int result = Interlocked.Exchange(ref this.lockWord, 1); - if (result != 0) { - // spin for exponentially backed off delay - int count = 31; - while (true) { -#if SINGULARITY_KERNEL - Kernel.Waypoint(888); -#endif // SINGULARITY_KERNEL - Thread.SpinWait(count); - result = Interlocked.Exchange(ref this.lockWord, 1); - if (result == 0) { - break; - } -#if !SINGULARITY_KERNEL - // We yield to allow the thread holding the lock to run - Thread.Yield(); - // check the lock word once after yielding - result = Interlocked.Exchange(ref this.lockWord, 1); - if (result == 0) { - break; - } -#endif // !SINGULARITY_KERNEL - count = (count == 0x7ffffffu) ? count : count + count + 1; - } // while - } - - VTable.Assert(lockWord == 1 && lockingThreadIndexPlusOne == 0); - this.lockingThreadIndexPlusOne = currentThreadIndexPlusOne; - } - - // Note: any changes to this code must be reflected in - // the Release code in SwitchToThreadContext in halidt.asm. - [NoHeapAllocation] - private void ReleaseInternal(int currentThreadIndexPlusOne) - { - VTable.Assert(lockWord == 1 && - lockingThreadIndexPlusOne == currentThreadIndexPlusOne); - VTable.Assert(Processor.InterruptsDisabled()); - - this.lockingThreadIndexPlusOne = 0; - this.lockWord = 0; - } - - [NoHeapAllocation] - private bool IsHeldInternal() - { - return this.lockWord != 0; - } - - [NoHeapAllocation] - private bool IsHeldByInternal(int threadIndexPlusOne) - { - return (this.lockWord != 0 && - this.lockingThreadIndexPlusOne == threadIndexPlusOne); - } - - [AccessedByRuntime("referenced from halidt.asm")] - private int lockWord; - [AccessedByRuntime("referenced from halidt.asm")] - private int lockingThreadIndexPlusOne; // Thread.GetThreadId() + 1 - } -} diff --git a/base/Kernel/System/Threading/SpinLockBase.cs b/base/Kernel/System/Threading/SpinLockBase.cs new file mode 100644 index 0000000..752ef76 --- /dev/null +++ b/base/Kernel/System/Threading/SpinLockBase.cs @@ -0,0 +1,235 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: SpinlockBase.cs +// +// Note: +// Base spinlock functionality that is shared between user and kernel level spinlocks +// + +using System; +using Microsoft.Singularity; +using System.Runtime.CompilerServices; + +namespace System.Threading +{ + /// + /// + /// Spinlock Base - actual implementation of spinlock mechanism + /// + /// + [NoCCtor] + [CLSCompliant(false)] + public struct SpinLockBase + { + /// + /// + /// Construct a SpinLockBase and initialize its type + /// + /// + /// Type of the spin lock. See KernelSpinLock.cs for details + /// + [NoHeapAllocation] + public SpinLockBase(int type) + { + this.type = type; + this.ownerId = 0; + } + + /// + /// + /// Assert if the specified thread actual owner of the spinlock + /// + /// + /// Owner Id that request is using to get spinlock + /// + [System.Diagnostics.Conditional("DEBUG")] + [NoHeapAllocation] + public void AssertHeldBy(int ownerId) + { + VTable.Assert(IsHeldBy(ownerId)); + } + + /// + /// + /// Internal method to find out if spinlock is held by any thread + /// + /// true if the spin lock is acquired. + /// + [NoHeapAllocation] + public bool IsHeld() + { + return this.ownerId != 0; + } + + /// + /// + /// Method to find out if spinlock is held by specific thread + /// + /// true if the spin lock is acquired by specific thread. + /// + /// Owner Id that request is using to check for spinlock ownership + /// + [NoHeapAllocation] + public bool IsHeldBy(int ownerId) + { + return (this.ownerId == ownerId); + } + + /// + /// + /// Try to acquire the spin lock. Always return immediately. + /// + /// true if the spin lock is acquired. + /// + [Inline] + [NoHeapAllocation] + public bool TryAcquire(int ownerId) + { + bool result; + + // Assert preconditions SpinLocks are *NOT* re-entrant, thread can't hold spinlocks of + // lower rank + VTable.Assert(ownerId != this.ownerId); + + // Try to acquire the spin lock + result = (Interlocked.CompareExchange(ref this.ownerId, + ownerId, + 0) == 0); + + return result; + } + + /// + /// + /// Acquire a lock + /// + /// + /// Owner Id that request is using to acquire spinlock + /// + [NoHeapAllocation] + [Inline] + public void Acquire(int ownerId) + { + // Thead Id can't be null + VTable.Assert(ownerId != 0); + + // Try to acquire spinlock + if (!TryAcquire(ownerId)){ + // We failed to acquire just go ahead and dive into deeper spin loop with attempts + // to acquire lock + SpinToAcquire(ownerId); + } + } + + /// + /// + /// Release a lock + /// + /// + /// Spinlock owner + /// + [NoHeapAllocation] + [Inline] + public void Release(int ownerId) + { + // Assert preconditions: Thread should own spinlock + VTable.Assert(this.ownerId == ownerId); + + Interlocked.Exchange (ref this.ownerId, 0); + } + + /// + /// + /// Type of a spinlock + /// + /// + internal int Type + { + [NoHeapAllocation] + [Inline] + get + { + return this.type; + } + } + + /// + /// + /// Spin until we can actually acquire spinlock + /// + /// + /// Owner Id that request is using to acquire spinlock + /// + [NoHeapAllocation] + private void SpinToAcquire (int ownerId) + { + int iSpin; + int backoffs = 0; + + // Assert preconditions: thread's id and passed in id's should be the same + VTable.Assert(ownerId != 0); + + while (true) { + + // It is assumed this routine is only called after the inline + // method has failed the interlocked spinlock test. Therefore we + // retry using the safe test only after cheaper, unsafe test + // succeeds. + for (iSpin = 0; (this.ownerId != 0 && iSpin < MaxSpinLimit); iSpin++) { + // Hopefully ownerId will not be enregistered, and this read will + // always hit memory, if it does then we are in trouble + + // Perform HT friendly pause: + Thread.NativeNoOp(); + } + + // If we exited the loop prematurely, then try to get the lock + if (iSpin < MaxSpinLimit) { + + // Attempt to grab the spinlock + if (TryAcquire(ownerId)) { + break; + } + + // If we couldn't get the lock, at least we know someone did, + // and the system is making progress; there is no need to + // back off. + backoffs = 0; + continue; + } + + // Increment back off stats + backoffs++; + } + } + + /// A timer for short back off in ms + const int MaxSpinLimit = 10000; + + /// A counter for short back off in ms + const long ShortBackOffLimit = 8; + + /// A counter for short back off in ms +#if DEBUG + const long LongBackOffLimit = 1000; +#else + const long LongBackOffLimit = 10000; +#endif + /// A timer for short back off in ms + const long ShortBackOff = 1; + + /// A timer for long back off in ms + const long LongBackOff = 5000; + + /// Thread owner + private int ownerId; + + /// Type of a spinlock + private readonly int type; + + } +} diff --git a/base/Kernel/System/Threading/SynchronizationLockException.cs b/base/Kernel/System/Threading/SynchronizationLockException.cs deleted file mode 100644 index 181cd41..0000000 --- a/base/Kernel/System/Threading/SynchronizationLockException.cs +++ /dev/null @@ -1,41 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================================= -** -** Class: SynchronizationLockException -** -** -** Purpose: Wait(), Notify() or NotifyAll() was called from an unsynchronized -** block of code. -** -** Date: March 30, 1998 -** -=============================================================================*/ - -namespace System.Threading { - - using System; - - //| - public class SynchronizationLockException : SystemException { - //| - public SynchronizationLockException() - : base("Arg_SynchronizationLockException") { - } - - //| - public SynchronizationLockException(String message) - : base(message) { - } - - //| - public SynchronizationLockException(String message, Exception innerException) - : base(message, innerException) { - } - } -} - - diff --git a/base/Kernel/System/Threading/Thread.cs b/base/Kernel/System/Threading/Thread.cs index 8c6d40b..80d2021 100644 --- a/base/Kernel/System/Threading/Thread.cs +++ b/base/Kernel/System/Threading/Thread.cs @@ -25,13 +25,13 @@ // Scheduler.OnThreadYield(): Thread yields processor. // Scheduler.OnThreadStop(): Thread is ready to stop. // Scheduler.OnThreadFreezeIncrement(): Freeze thread, incr count. -// Scheduler.OnThreadFreezeDecrement(): Decrement count, if 0 then unfreeze +// Scheduler.OnThreadFreezeDecrement(): Decr count, if 0 then unfreeze. // // Third, the Scheduler calls Thread.Stopped() when it has finish with a // thread that is no longer runnable. // -// #define DEBUG_SWITCH +//#define DEBUG_SWITCH namespace System.Threading { @@ -49,8 +49,8 @@ namespace System.Threading using Microsoft.Singularity.Scheduling; using Microsoft.Singularity.Security; using Microsoft.Singularity.V1.Threads; - using Microsoft.Singularity.X86; using Microsoft.Singularity.Memory; + using Microsoft.Singularity.Isal; using Microsoft.Bartok.Runtime; [CLSCompliant(false)] @@ -64,223 +64,184 @@ namespace System.Threading ThreadPackageInit = 10 } - //| - [CCtorIsRunDuringStartup] - [CLSCompliant(false)] - [RequiredByBartok] - public class Thread + internal enum AbortRequestSource { - // GC fields. - [RequiredByBartok] // Thread-specific alloc heap - internal SegregatedFreeList segregatedFreeList; - [RequiredByBartok] // Thread-specific bump allocator - internal BumpAllocator bumpAllocator; + ABIExit = 0, + ABINoGCExit = 1, + WaitHandle = 2, + } - // MultiUseWord (object header) fields. - internal UIntPtr externalMultiUseObjAllocListHead; - internal UIntPtr externalMultiUseObjAllocListTail; - - // Scheduling fields. - internal int processThreadIndex; - internal int threadIndex; - private ThreadStart threadStart; - private ThreadState threadState; // acquire Process.processLock when changing unstarted->running - - private AutoResetEvent autoEvent; - private bool gcEventBlocked; // Are we blocked on the gcEvent? - private bool gcEventSignaled; // Has the gcEvent been signaled - private ManualResetEvent joinEvent; - internal Thread blockingCctorThread; - internal ThreadLocalServiceRequest localServiceRequest; - - // Scheduler dispatching fields. - [AccessedByRuntime("referenced from c++")] - internal bool blocked; // Thread blocked on something. - [AccessedByRuntime("referenced from c++")] - private ThreadEntry[] blockedOn; // WaitHandles blocked on (if any). - [AccessedByRuntime("referenced from c++")] - private int blockedOnCount; // Number of valid WaitHandles. - [AccessedByRuntime("referenced from c++")] - private SchedulerTime blockedUntil; // Timeout of Wait. - public SchedulerTime BlockedUntil - { - [NoHeapAllocation] - get { return blockedUntil; } - } - private volatile int unblockedBy; // WaitHandle signaled in unblock. - internal int freezeCount; // >0 prevents thread from being scheduled. - - [AccessedByRuntime("referenced from c++")] - public ThreadEntry schedulerEntry; // Entry for some scheduler queue. - public Processor ActiveProcessor; // Processor running this thread (null if not running). - public Processor AffinityProcessor; // Soft affinity. - public Processor LockedProcessor; // non-null if thread dedicated. - - // Singularity specific fields - [AccessedByRuntime("referenced from c++")] - internal ThreadContext context; - - [AccessedByRuntime("referenced from c++")] - internal Process process; - internal ThreadHandle threadHandle; - internal UIntPtr threadLocalValue; - - // Most recently thrown exception object that the thread - // did not catch at all (i.e. that propagated to the bottom - // of the stack without encountering an appropriate catch clause). - internal Exception lastUncaughtException; - - // Monitor fields. - // Remove these (& Monitor) as soon as stack is out of kernel. - internal Thread nextThread; // Link for linked lists of threads - - private Object exceptionStateInfo; // Exception info latched to the thread on a thread abort - - // Bartok specific fields - [RequiredByBartok] - internal TryAllManager tryAllManager; - - private static long totalArrayAllocations; - private static long totalArrayBytesAllocated; - private static long totalBytes; - private static long totalStructBytesAllocated; - - internal static Thread[] threadTable; - private static SpinLock threadTableLock; - - private static LocalDataStore localDataStore; - - internal static Thread initialThread; - - private static SpinLock threadStopLock; - private static ProcessStopException processStopException; - -#if PAGING - // For use when we temporarily switch to a different domain - private ProtectionDomain tempDomain; -#endif - - // This is used by the Bartok backend. When Bartok try to generate - // callback stub for delegate, it checks to see if it is - // ThreadProc, if it is not, Bartok adds leaveGCSafeState and - // enterGCSafeState around the delegate call. - [RequiredByBartok] - private unsafe delegate uint ThreadProc(void *param); - - ////////////////////////////////////////////////////////////////////// - // This manager is responsible for storing the global data that is - // shared amongst all the thread local stores. - // - static private LocalDataStoreMgr m_LocalDataStoreMgr; - - ////////////////////////////////////////////////////////////////////// - // Creates a new Thread object which will begin execution at - // start.ThreadStart on a new thread when the Start method is called. - // - // Exceptions: ArgumentNullException if start == null. - // - //| - internal const int maxThreads = 1024; // Must be power of 2 >= 64 - private static int threadIndexGenerator; + /// + /// + /// Class implements thread functionality in Singluarity + /// + /// + public sealed partial class Thread + { + /// + /// + /// Constructor to create a new thread + /// + /// + /// A process to which thread belongs to + /// private Thread(Process process) { + + // Initialize thread fields to default values this.processThreadIndex = -1; this.threadIndex = -1; - this.threadState = ThreadState.Unstarted; + this.schedulerInfo.State = ThreadState.Unstarted; + + // Set up thread kernel mode information this.SetKernelMode(); + + // Bind thread to a process this.process = process; - context.threadIndex = unchecked((ushort)-1); - context.processThreadIndex = unchecked((ushort)-1); - context.processId = unchecked((ushort)-1); - Transitions.InitializeStatusWord(ref context); + // Bind thread to a proper scheduler +#if false + if (process != null) { + this.ScheduleData = process.DefaultScheduleData; + } +#endif + + // Initialize context fields + this.context.threadIndex = unchecked((ushort)-1); + this.context.processThreadIndex = unchecked((ushort)-1); + this.context.processId = unchecked((ushort)-1); + Transitions.InitializeStatusWord(ref this.context); // Allocate the kernel objects needed by the thread. - autoEvent = new AutoResetEvent(false); - joinEvent = new ManualResetEvent(false); - localServiceRequest = new ThreadLocalServiceRequest(); - schedulerEntry = new ThreadEntry(this); - this.GetWaitEntries(1); // Cache allows wait without allocation + this.threadLock = new SpinLock(SpinLock.Types.Thread); + this.autoEvent = new AutoResetEvent(false); + this.joinEvent = new ManualResetEvent(false); + this.localServiceRequest = new ThreadLocalServiceRequest(); + this.schedulerEntry = new ThreadEntry(this); + this.timerEntry= new ThreadEntry(this); + this.deferredEntry= new ThreadEntry(this); + + // Initialize wait entries cache so that we don't have to perform allocation in most + // common case, when thread is waiting on single handle + this.GetWaitEntries(1); // Try to put the thread in the thread table. - bool iflag = Processor.DisableInterrupts(); + bool saved = Processor.DisableInterrupts(); + threadTableLock.Acquire(CurrentThread); try { - threadTableLock.Acquire(CurrentThread); - try { - for (int i = 0; i < threadTable.Length; i++) { - int index = (threadIndexGenerator + i) % threadTable.Length; - if (threadTable[index] == null) { + // Grab the first empty spot in thread table + for (int i = 0; i < threadTable.Length; i++) { + // Calculate possible thread index starting from the last time success + int index = (threadIndexGenerator + i) % threadTable.Length; + + // If none of the existing thread occupies this particular index use it: + if (threadTable[index] == null) { + + // Bind table to our thread and our thread to table threadTable[index] = this; this.threadIndex = index; + + // Remember last we visited this place threadIndexGenerator = index + 1; + // NB: We call this once, subsequently the GC visitor calls it. context.UpdateAfterGC(this); break; - } - } - } - finally { - threadTableLock.Release(CurrentThread); - } - - // Save the TID and PID into the thread context. - context.threadIndex = unchecked((ushort)(threadIndex)); - Transitions.InitializeStatusWord(ref context); - if (process != null) - { - context.processId = unchecked((ushort)(process.ProcessId)); + } } } finally { - Processor.RestoreInterrupts(iflag); + threadTableLock.Release(CurrentThread); + Processor.RestoreInterrupts(saved); } - // Allocate the thread's stack. + // Assert that thread table is not full temporary before real fix + VTable.Assert(this.threadIndex != -1); + + // Initialize context with proper thread index + context.threadIndex = unchecked((ushort)(threadIndex)); + Transitions.InitializeStatusWord(ref context); + + // Bind context to a process if one exists + if (process != null) { + context.processId = unchecked((ushort)(process.ProcessId)); + } + + // Initialize thread's stack. context.stackBegin = 0; context.stackLimit = 0; } - // Constructor for processor idle threads. + /// + /// + /// Idle thread constructor, deligates major initialization to main constructor by + /// using idle process + /// + /// + /// Indicates if thread is idle or not + /// protected Thread(bool idle) : this(Process.idleProcess) { + // Initialize stack UIntPtr stackSegment = Stacks.GetInitialStackSegment(ref context); + + // At this point we can't fail DebugStub.Assert(stackSegment != UIntPtr.Zero); + #if PAGING + // Initialize idle context context.InitializeIdle(threadIndex, stackSegment, unchecked((uint)Process.kernelProcess.Domain.AddressSpace.PdptPage.Value)); #else + // Initialize idle context context.InitializeIdle(threadIndex, stackSegment, 0); #endif + // Record ilde thread creation event Monitoring.Log(Monitoring.Provider.Thread, (ushort)ThreadEvent.CreateIdle, 0, (uint)threadIndex, 0, 0, 0, 0); } - // Constructor for all other threads. + /// + /// + /// A thread constructor with a start routine, deligates major initialization to main constructor by + /// using idle process + /// + /// + /// A process to which thread belongs to + /// Thread's start routine + /// protected Thread(Process process, ThreadStart start) : this(process) { + // If no start routine specified throw exception if (start == null) { throw new ArgumentNullException("start"); } + // Initialize thread's start routine this.threadStart = start; #if PAGING - context.abiStackHead = Stacks.GetStackSegment(0, ref context); - context.abiStackBegin = context.stackBegin; - context.abiStackLimit = context.stackLimit; - context.stackBegin = 0; - context.stackLimit = 0; + // In case of paging initialize abi stack + this.context.abiStackHead = Stacks.GetStackSegment(0, ref context, true, false); + this.context.abiStackBegin = this.context.stackBegin; + this.context.abiStackLimit = this.context.stackLimit; + this.context.stackBegin = 0; + this.context.stackLimit = 0; #endif + // Allocation initial stack segment for the thread UIntPtr stackSegment = Stacks.GetInitialStackSegment(ref context); + // Assert condition - no stack, no thread - is this really true? DebugStub.Assert(stackSegment != UIntPtr.Zero); #if PAGING + // Initialize context context.Initialize(threadIndex, stackSegment, unchecked((uint)(Process.Domain.AddressSpace.PdptPage.Value))); #else + // Initialize context context.Initialize(threadIndex, stackSegment, 0); #endif Monitoring.Log(Monitoring.Provider.Thread, @@ -288,158 +249,396 @@ namespace System.Threading (uint)threadIndex, 0, 0, 0, 0); } - // To create a new idle thread. + /// + /// + /// An API to create a thread + /// + /// + /// An idle thread for a given processor + /// public static Thread CreateIdleThread(Processor processor) { - // Allocate the thread. + // Allocate new idle thread. Thread idle = new Thread(true); + idle.Affinity = processor.Id; + // Check if new thread is valid if (idle.threadIndex < 0) { Tracing.Log(Tracing.Warning, "Thread table is full."); DebugStub.Break(); return null; } - //MemoryBarrier();? - - DebugStub.WriteLine("CreateIdleThread tid={0:x3}, proc={1:x3}", - __arglist(idle.threadIndex, - processor.processorIndex)); - idle.LockedProcessor = processor; return idle; } - // Entry point for kernel to create new process threads. - public static Thread CreateThread(Process process, - ThreadStart start) + /// + /// + /// An API to create a thread + /// + /// + /// A process to which thread belongs to + /// Thread's start routine + /// + [Inline] + public static Thread CreateThread(Process process, ThreadStart start) { + return CreateThread(process, start, false); + } + + /// + /// + /// An API to create a thread + /// + /// + /// A process to which thread belongs to + /// Thread's start routine + /// Whether a thread handle should be created + /// + public static Thread CreateThread(Process process, ThreadStart start, bool needThreadHandle) + { + ThreadHandle handle; + + // If no process specified use current one if (process == null) { process = CurrentProcess; } + //Process can't be null from this point on + VTable.Assert(process != null); + // Allocate the thread. Thread thread = new Thread(process, start); + // Check if thread is valid, return right a way if it is not if (thread.threadIndex < 0) { Tracing.Log(Tracing.Warning, "Thread table is full."); DebugStub.Break(); return null; } - ThreadHandle handle; - - process.AddThread(thread, out handle); + // Add newly created thread to the process + process.AddThread(thread, needThreadHandle); //MemoryBarrier();? - // Tell the scheduler to initialize the thread. - Scheduler.OnThreadStateInitialize(thread, true); - + // Notifiy perfcounters about thread creation PerfCounters.IncrementThreadsCreated(); + DebugStub.AddToPerfCounter(4, 1); return thread; } - ////////////////////////////////////////////////////////////////////// - // Spawns off a new thread which will begin executing at the - // ThreadStart delegate passed in the constructor. Once the thread is - // dead, it cannot be restarted with another call to Start. - // - // Exceptions: ThreadStateException if the thread has already been started. - // - //| + /// + /// + /// Retrieve thread's scheduler for a thread + /// + /// + public ProcessorDispatcher Dispatcher + { + [Inline] + [NoHeapAllocation] + get + { + return this.dispatcher; + } + + [Inline] + [NoHeapAllocation] + set + { + this.dispatcher = value; + } + } + + /// + /// + /// Retrieve thread's scheduler info + /// + /// + public SchedulerInfo ThreadSchedulerInfo + { + [Inline] + [NoHeapAllocation] + get + { + return this.schedulerInfo; + } + } + + /// + /// + /// Find out if thread is suspended + /// + /// + public bool IsSuspended + { + [Inline] + [NoHeapAllocation] + get + { + return (this.schedulerInfo.State == ThreadState.Suspended); + } + } + + /// + /// + /// Find out if thread is blocked + /// + /// + public bool IsBlocked + { + [Inline] + [NoHeapAllocation] + get + { + return this.schedulerInfo.State == ThreadState.Blocked; + } + } + + /// + /// + /// Check if thread is idle + /// + /// + public bool IsIdle + { + [Inline] + [NoHeapAllocation] + get + { + return this.type == ThreadType.Idle; + } + } + + /// + /// + /// Check if thread is idle + /// + /// + public bool IsScavenger + { + [Inline] + [NoHeapAllocation] + get + { + return this.type == ThreadType.Scavenger; + } + } + + + /// + /// + /// Check if thread is idle + /// + /// + public ThreadType Type + { + [Inline] + [NoHeapAllocation] + get + { + return this.type; + } + + [Inline] + [NoHeapAllocation] + set + { + this.type = value;; + } + } + + + /// + /// + /// Find out if thread is runnable + /// + /// + public bool IsRunnable + { + [Inline] + [NoHeapAllocation] + get + { + return this.schedulerInfo.State == ThreadState.Runnable; + } + } + + /// + /// + /// Find out if thread is running + /// + /// + public bool IsRunning + { + [Inline] + [NoHeapAllocation] + get + { + return this.schedulerInfo.State == ThreadState.Running; + } + } + + + /// + /// + /// Find out who unblocked the thread + /// + /// + public int UnblockedBy + { + [Inline] + [NoHeapAllocation] + get + { + return this.schedulerInfo.UnblockedBy; + } + + [Inline] + [NoHeapAllocation] + set + { + this.schedulerInfo.UnblockedBy = value; + } + } + + /// + /// + /// Find out thread freeze count + /// + /// + public int FreezeCount + { + [Inline] + [NoHeapAllocation] + get + { + return this.schedulerInfo.FreezeCount; + } + } + + /// + /// + /// Spawns off a new thread which will begin executing at the + /// ThreadStart delegate passed in the constructor. Once the thread is + /// dead, it cannot be restarted with another call to Start. + /// + /// public void Start() { - process.StartThread(ref threadState); + // Notify process that thread is about to start + process.StartThread(); + + // Go ahead and start thread - attach it scheduler and start running StartRunningThread(); } - // precondition: process.processLock held + /// + /// + /// Start running main thread inside of process + /// + /// + /// precondition: process.processLock held internal void SetMainThreadRunning() { - VTable.Assert(threadState == ThreadState.Unstarted); - process.StartMainThread(ref threadState); + VTable.Assert(this.ThreadState == ThreadState.Unstarted); + process.StartMainThread(); } + /// + /// + /// Start thread running: Attach to GC and process's scheduler + /// + /// internal void StartRunningThread() { - VTable.Assert(threadState == ThreadState.Running); + VTable.Assert(this.ThreadState == ThreadState.Unstarted); // Tell the GC that we have created the thread GC.NewThreadNotification(this, false); - // Tell the scheduler to start the thread. - bool iflag = Processor.DisableInterrupts(); - try { - Scheduler.DispatchLock(); - try { - //Kernel.Waypoint(1); - Scheduler.OnThreadStart(this); - } - finally { - Scheduler.DispatchRelease(); - } - } - finally { - //Kernel.Waypoint(114); - Processor.RestoreInterrupts(iflag); - } + // Tell the scheduler to initialize the thread. + Kernel.TheScheduler.OnThreadStateInitialize(this, true); + + // Let scheduler start thread: Scheduler is responsible for changing thread state + Kernel.TheScheduler.OnThreadStart(this); } - - // ThreadContext.InitializeIdle sets ThreadIdleStub as the first instruction to be - // executed in a new thread context. + /// + /// + /// An entry point for an idle thread. ThreadContext.InitializeIdle sets + /// DispatcherThreadStub as the first instruction to be executed in a new thread context. + /// + /// + /// + /// There are currently only two dispatcher threads: Idle and Scavenger. Both + /// of them are created during processor startup + /// + /// + /// An index represents a thread we are starting + /// [AccessedByRuntime("referenced from c++")] - private static unsafe void ThreadIdleStub(int index) + private static unsafe void DispatcherThreadStub(int index) { - for (;;) { - // Check for a debug break. - // Tell the scheduler to start the thread. - bool iflag = Processor.DisableInterrupts(); - try { - Processor.NextSampleIsIdle(); - if (DebugStub.PollForBreak()) { - DebugStub.Print("Debugger breakin.\n"); - DebugStub.Break(); - } - } - finally { - //Kernel.Waypoint(114); - Processor.RestoreInterrupts(iflag); - } - Processor.HaltUntilInterrupt(); + Thread currentThread = Thread.CurrentThread; + + // Assert preconditions: Thread should be really one of dispatcher threads + VTable.Assert(threadTable[index] == currentThread); + VTable.Assert( + Processor.CurrentProcessor.Dispatcher.IsOneOfMyDispatcherThreads(currentThread));; + + // If processor idle use idle loop otherwise use scavanager loop + if (Processor.CurrentProcessor.Dispatcher.IsMyIdleThread(currentThread)) { + // Mark thread idle before running + Thread.CurrentThread.Type = ThreadType.Idle; + ProcessorDispatcher.IdleThreadLoop(); + } + else { + Thread.CurrentThread.Type = ThreadType.Scavenger; + // This is a scavanger thread so use the appropriate loop + ProcessorDispatcher.ScavengerThreadLoop(); } } - // ThreadContext.Initialize sets ThreadStub as the first instruction to be - // executed in a new thread context. + /// + /// + /// An entry point for any thread. ThreadContext.Initialize sets ThreadStub as + /// the first instruction to be executed in a new thread context. + /// + /// + /// An index represents a thread we are starting + /// [AccessedByRuntime("referenced from c++")] private static unsafe void ThreadStub(int threadIndex) { Thread currentThread = threadTable[threadIndex]; -#if PAGING // Give our Protection Domain a chance to set up // if we're first in here. Run this before anything // else! currentThread.process.Domain.InitHook(); -#endif + // Attach thread to GC Transitions.ThreadStart(); GC.ThreadStartNotification(threadIndex); + Tracing.Log(Tracing.Trace, "ThreadStub() entered"); ThreadStart startFun = currentThread.threadStart; - Processor.InitFpu(); + // Initialize procesor FPU + Isa.InitFpu(); + // Start an entry point of actual thread try { startFun(); } catch (ProcessStopException) { + // Stop exception is the only "good" exception expect at this point // Ok, exit thread without failure. } catch (Exception e) { - // Not ok, fail. + // Not ok, fail: This is pretty much unhandled exception Tracing.Log(Tracing.Notice, "Thread failed with exception {0}.{1}", e.GetType().Namespace, e.GetType().Name); @@ -456,97 +655,88 @@ namespace System.Threading Tracing.Log(Tracing.Trace, "{0:x} ThreadStub() stopping", Kernel.AddressOf(currentThread)); - currentThread.threadState = ThreadState.Stopping; - currentThread.joinEvent.Set(); - - // The scheduler takes care of exiting MutatorState, so we - // don't need the following call. - // Transitions.ThreadEnd(threadIndex); - - bool iflag = Processor.DisableInterrupts(); - try { - Thread target = null; - - // Block the ServiceStopped method until we acquire DispatchLock - // (but don't acquire DispatchLock yet, because - // CurrentThreadStopped would try to double-acquire it). - threadStopLock.Acquire(); - - // Tell service thread to call currentThread.ServiceStopped() - ThreadLocalServiceRequest.CurrentThreadStopped(); - - Scheduler.DispatchLock(); - try { - threadStopLock.Release(); - Kernel.Waypoint(1); - - // We change to the Stopped state here so that - // Process.ServiceSuspend can stop waiting for us. - // (We can't wait for the service thread to - // set the Stopped state, because if the - // service thread is running Process.ServiceSuspend - // it could deadlock.) - currentThread.threadState = ThreadState.Stopped; - target = Scheduler.OnThreadStop(currentThread); - Scheduler.SelectingThread(target); - } - catch(Exception e) { - Scheduler.DispatchRelease(); - throw e; - } - - if (target == null) { - target = Processor.CurrentProcessor.IdleThread; - } - - target.SwitchTo(); - } - finally { - //Kernel.Waypoint(114); - Processor.RestoreInterrupts(iflag); - } - DebugStub.Break(); + // We are done: stop thread and disassociate it from scheduler + Kernel.TheScheduler.OnThreadStop(currentThread); } - // ServiceStopped is called by the service thread when the thread is stopped. + /// + /// + /// Service a stop thread - Method perfoms final tearing down of thread it is called + /// on special service thread. + /// + /// internal void ServiceStopped() { // Make sure ThreadStub has gotten a chance to exit before // we deallocate its stack: - bool iflag = Processor.DisableInterrupts(); + bool wasUnstarted; + + // Indicate all waiters on this thread that we are done + joinEvent.Set(); + + // Make sure nobody else has access to the thread + bool saved = Processor.DisableInterrupts(); + this.threadLock.Acquire(); try { - threadStopLock.Acquire(); - Scheduler.DispatchLock(); - Scheduler.DispatchRelease(); - threadStopLock.Release(); - } finally { - Processor.RestoreInterrupts(iflag); + // Thread might have never started + wasUnstarted = (this.ThreadState == ThreadState.Unstarted); + + // If thread has never started set state by hand to stopped + if (wasUnstarted) { + this.schedulerInfo.State = ThreadState.Stopped; + } + + } + finally { + this.threadLock.Release(); + Processor.RestoreInterrupts(saved); } - VTable.Assert(threadState == ThreadState.Stopped); + + // Asssert preconditions + VTable.Assert(this.ThreadState == ThreadState.Stopped); VTable.Assert(threadIndex > 0); - VTable.Deny(Transitions.InMutatorState(threadIndex)); - GC.DeadThreadNotification(this); + // Notify GC that thread is tearing down + if (!wasUnstarted) { + if (Transitions.InMutatorState(GetThreadId())){ + Transitions.ThreadEnd(GetThreadId()); + } + GC.DeadThreadNotification(this); + } + int count = 0; #if !PAGING while (context.stackBegin != 0) { - Stacks.ReturnStackSegment(ref context); + + if (count == 0) { + Stacks.ReturnInitialStackSegment(ref context); + } + else { + Stacks.ReturnStackSegment(ref context); + } + + count++; } #else - int count = 0; + // Free up stack while (context.stackBegin != 0) { - // HACK: if the thread stops abruptly, the stack - // may contain the abi segment + + // HACK: if the thread stops abruptly, the stack may contain the abi segment if (context.stackBegin == context.abiStackBegin) { context.abiStackBegin = 0; context.abiStackLimit = 0; } - Stacks.ReturnStackSegment(ref context); + if (count == 0) { + // First segment is reported to balance AllocateInitialStackSegment + Stacks.ReturnInitialStackSegment(ref context); + } + else { + Stacks.ReturnStackSegment(ref context); + } count++; } -// VTable.Assert(count == 1); // due to unimplemented exception handling code, count may be >1 #endif VTable.Assert(context.stackLimit == 0); @@ -563,17 +753,17 @@ namespace System.Threading Thread currentThread = Thread.CurrentThread; - iflag = Processor.DisableInterrupts(); + // Free up table index + saved = Processor.DisableInterrupts(); + threadTableLock.Acquire(currentThread); + try { - threadTableLock.Acquire(currentThread); - try { - threadTable[threadIndex] = null; - Transitions.DeadThreadNotification(threadIndex); - } finally { - threadTableLock.Release(currentThread); - } - } finally { - Processor.RestoreInterrupts(iflag); + threadTable[threadIndex] = null; + Transitions.DeadThreadNotification(threadIndex); + } + finally { + threadTableLock.Release(currentThread); + Processor.RestoreInterrupts(saved); } if (process != null) { @@ -581,88 +771,88 @@ namespace System.Threading } } - ////////////////////////////////////////////////////////////////////// - // Returns true if the thread has been started and is not dead. - // - //| - public bool IsAlive { - get { return (threadState == ThreadState.Running); } - } - -#if false - // Returns a TimeSpan target for timeout. - static internal SchedulerTime GetTimeoutTarget(TimeSpan delay) + /// + /// + /// Thread is alive only if it has been started and it is not stopped. There is no + /// gurantee as this call returns thread will stay alive. You will need to provide extra + /// synchronization + /// + /// + public bool IsAlive { - if (delay == TimeSpan.Infinite) { -#if DEBUG_SLEEP - DebugStub.Print(" GetTimeTarget(Infinite) = {0}\n", - __arglist(SchedulerTime.MaxValue.Ticks)); -#endif - return SchedulerTime.MaxValue; + [Inline] + [NoHeapAllocation] + get + { + return (this.ThreadState != ThreadState.Unstarted && + this.ThreadState != ThreadState.Stopped); } - else if (delay < TimeSpan.Zero) { - throw new ArgumentOutOfRangeException("delay", "ArgumentOutOfRange_AddTimeout"); - } - SchedulerTime now = SchedulerTime.Now; -#if DEBUG_SLEEP - DebugStub.Print(" GetTimeTarget({0}) = {1:d12} + {2:d12} => {3:d12}\n", - __arglist( - delay.ToString(), - now.Ticks, - delay.Ticks, - (now + delay).Ticks)); -#endif - return now + delay; - } -#endif - - [Inline] - static internal bool TimeoutTargetPassed(SchedulerTime target) - { - return SchedulerTime.Now >= target; } - ////////////////////////////////////////////////////////////////////// - // Waits for the thread to die. - // - // Exceptions: ThreadStateException if the thread has not been started yet. - // - //| + /// + /// + /// Wait for thread to stop + /// + /// + /// + /// Exceptions: ThreadStateException if the thread has not been started yet. + /// public void Join() { Join(SchedulerTime.MaxValue); } - ////////////////////////////////////////////////////////////////////// - // Waits for the thread to die or for timeout milliseconds to elapse. - // Returns true if the thread died, or false if the wait timed out. - // - // Exceptions: ArgumentException if timeout < 0. - // ThreadStateException if the thread has not been started. - // - //| + /// + /// + /// Wait for thread to stop or specified timeout expires + /// + /// + /// + /// Exceptions: ThreadStateException if the thread has not been started yet. + /// public bool Join(TimeSpan timeout) { - if (threadState == ThreadState.Unstarted) { + if (this.ThreadState == ThreadState.Unstarted) { throw new ThreadStateException(); } - else if (threadState == ThreadState.Stopped) { + else if (this.ThreadState == ThreadState.Stopped) { return true; } return joinEvent.WaitOne(timeout); } + /// + /// + /// Wait for thread to stop or specified timeout expires + /// + /// + /// + /// Exceptions: ThreadStateException if the thread has not been started yet. + /// public bool Join(SchedulerTime timeout) { - if (threadState == ThreadState.Unstarted) { + if (this.ThreadState == ThreadState.Unstarted) { throw new ThreadStateException(); } - else if (threadState == ThreadState.Stopped) { + else if (this.ThreadState == ThreadState.Stopped) { return true; } return joinEvent.WaitOne(timeout); } + /// + /// + /// Set thread's affinity + /// + /// + public void SetAffinity(int affinity) + { + // Affinity can only be changed when it has not been set + if (Affinity == NoAffinity) { + Affinity = affinity; + } + } + ////////////////////////////////////////////////////////////////////// // Suspends the current thread for timeout milliseconds. If timeout // == 0, forces the thread to give up the remainder of its timeslice. @@ -678,7 +868,7 @@ namespace System.Threading public static void Sleep(SchedulerTime stop) { Tracing.Log(Tracing.Audit, "Sleep until stop"); - Thread.CurrentThread.WaitAny(null, 0, stop); + WaitHandle.WaitAny(null, 0, stop); Tracing.Log(Tracing.Audit, "Sleep until stop finished"); } @@ -694,7 +884,7 @@ namespace System.Threading ////////////////////////////////////////////////////////////////////// // Wait for a length of time proportional to 'iterations'. Each // iteration is should only take a few machine instructions. Calling - // this method is preferable to coding an explicit busy loop because the + // this method is preferable to coding a explicit busy loop because the // hardware can be informed that it is busy waiting. // //| @@ -732,93 +922,86 @@ namespace System.Threading internal static void WaitForGCEvent(int currentThreadIndex) { - Tracing.Log(Tracing.Audit, "WaitForGCEvent({0})", - (UIntPtr) currentThreadIndex); - VTable.Deny(Scheduler.IsIdleThread(currentThreadIndex)); - bool iflag = Processor.DisableInterrupts(); - try { - bool didLock = - Scheduler.EnsureDispatchLock(currentThreadIndex); - Thread target = null; - Thread currentThread = threadTable[currentThreadIndex]; - try { - if (currentThread.gcEventSignaled) { - // Reset the flag, fall out and keep running. - currentThread.gcEventSignaled = false; - if (didLock) { - Scheduler.DispatchRelease(); - } - return; - } - currentThread.gcEventBlocked = true; - target = Scheduler.OnThreadBlocked(currentThread, - SchedulerTime.MaxValue); - if (target == null) { - target = Processor.CurrentProcessor.IdleThread; - } else { - // This should not be called passing the idle thread - Scheduler.SelectingThread(target); - } - } catch { - if (didLock) { - Scheduler.DispatchRelease(); - } - throw; - } - Processor.CurrentProcessor.NumContextSwitches++; - // This has the side effect of releasing the dispatcher lock. - Processor.SwitchToThreadContextNoGC(ref target.context); - } finally { - Processor.RestoreInterrupts(iflag); - } + Thread.WaitForGCEvent(currentThreadIndex, SchedulerTime.MaxValue); } - internal void SignalEvent() + internal static void WaitForGCEvent(int currentThreadIndex, int millis) { - autoEvent.Set(); + SchedulerTime stop = SchedulerTime.Now.AddMilliseconds(millis); + Thread.WaitForGCEvent(currentThreadIndex, stop); } - internal void SignalMonitor() - { - autoEvent.Set(); - } + /// + /// + /// Signal thread's GC event, we can't use actual event object as it might allow + /// unneccesasry reentrancy. + /// + /// [Inline] internal static void SignalGCEvent(int threadIndex) { SignalGCEvent(Thread.GetCurrentThreadIndex(), threadIndex); } - internal static void SignalGCEvent(int currentThreadIndex, - int threadIndex) + /// + /// + /// Wait for GC - method is called by GC to park thread while GC is up and running + /// At this point we chose not to use autoresetevent as it might introduce reentrancy. + /// In this method we closely mimic to what we are doing in WaitAny when waiting on handles + /// + /// + private static void WaitForGCEvent( + int currentThreadIndex, + SchedulerTime stop) { + Thread currentThread = threadTable[currentThreadIndex]; + + Tracing.Log(Tracing.Audit, "WaitForGCEvent({0})", + (UIntPtr) currentThreadIndex); + VTable.Deny(ProcessorDispatcher.IsIdleThread(currentThreadIndex)); + + while (true) { + // Attempt to reset the one-shot from true to false + if (Interlocked.CompareExchange(ref currentThread.gcEventSignaled, + 0, + 1) == 1) { + break; + } + // If we failed, yield the processor. Eventually we will block, but the + // dispatcher doesn't yet provide a suitable blocking primitive below the + // scheduler -- beyond perhaps Suspend. + Yield(); + } + } + + /// + /// + /// Signal thread's GC event. For more information see WaitForGCEvent + /// + /// + internal static void SignalGCEvent(int currentThreadIndex,int threadIndex) + { + Thread currentThread = threadTable[threadIndex]; + Tracing.Log(Tracing.Audit, "SignalGCEvent({0})", (UIntPtr) threadIndex); - VTable.Deny(Scheduler.IsIdleThread(threadIndex)); - bool iflag = Processor.DisableInterrupts(); - try { - bool didLock = - Scheduler.EnsureDispatchLock(currentThreadIndex); - try { - Thread targetThread = threadTable[threadIndex]; - if (targetThread == null) { - // There is nothing to signal, so just keep going. - } else if (targetThread.gcEventBlocked) { - targetThread.gcEventBlocked = false; - Scheduler.OnThreadUnblocked(targetThread); - } else { - // Nobody was waiting for the event. - targetThread.gcEventSignaled = true; - } - } finally { - if (didLock) { - Scheduler.DispatchRelease(); - } - } - } finally { - Processor.RestoreInterrupts(iflag); + VTable.Deny(ProcessorDispatcher.IsIdleThread(threadIndex)); + + if (currentThread != null) { + Interlocked.Exchange(ref currentThread.gcEventSignaled, 1); } } + internal void SignalEvent() + { + this.autoEvent.Set(); + } + + internal void SignalMonitor() + { + this.autoEvent.Set(); + } + //| public static extern Thread CurrentThread { @@ -830,7 +1013,7 @@ namespace System.Threading public static Process CurrentProcess { - [NoStackLinkCheck] + [NoStackLinkCheckTrans] [NoHeapAllocation] get { return Processor.GetCurrentThread().process; @@ -849,15 +1032,7 @@ namespace System.Threading get { return threadHandle; } } - [NoStackLinkCheck] - [RequiredByBartok] - [NoHeapAllocation] - private static Thread GetCurrentThreadNative() - { - return Processor.GetCurrentThread(); - } - - [NoStackLinkCheck] + [NoStackLinkCheckTrans] [RequiredByBartok] [NoHeapAllocation] public static int GetCurrentThreadIndex() @@ -865,6 +1040,14 @@ namespace System.Threading return Processor.GetCurrentThread().threadIndex; } + [NoStackLinkCheckTrans] + [RequiredByBartok] + [NoHeapAllocation] + private static Thread GetCurrentThreadNative() + { + return Processor.GetCurrentThread(); + } + [NoHeapAllocation] public static UIntPtr GetThreadLocalValue() { @@ -877,15 +1060,19 @@ namespace System.Threading Processor.GetCurrentThread().threadLocalValue = value; } - ////////////////////////////////////////////////////////////////////// - // Return the thread state as a consistent set of bits. This is more - // general then IsAlive or IsBackground. - // - //| + /// + /// + /// Retrieve thread's state + /// + /// public ThreadState ThreadState { + [Inline] [NoHeapAllocation] - get { return threadState; } + get + { + return schedulerInfo.State; + } } [NoHeapAllocation] @@ -918,304 +1105,325 @@ namespace System.Threading context.SetProcessMode(); } - // Briefly suspend a thread in order to modify its state. - // When Freeze() returns, the thread is guaranteed to: - // - be unscheduled (not running on any processor) - // - not to be inside a DisableInterrupts block - // (unless that block voluntarily context switches) - // - not to hold the dispatch lock (because - // CurrentThread holds the dispatch lock) - // These guarantees hold only until the dispatch lock - // is released. - // - // No other guarantees are made about the thread state; in particular, - // the thread could be in the middle of a kernel operation. (This - // distinguishes Freeze from Suspend.) - // - // A thread should not try to freeze itself - // (this would deadlock, as can many other uses of Freeze). - // - // precondition: interrupts enabled - // precondition: dispatch lock not acquired - // (so that other processor schedulers can run) - // postcondition: interrupts disabled - // postcondition: dispatch lock acquired - // postcondition: ActiveProcessor == null - // postcondition: freezeCount > 0 - // - // Example: - // t.Freeze(); - // ...(do some stuff, without releasing dispatch lock)... - // Scheduler.DispatchRelease(); - // Processor.RestoreInterrupts(true); - internal void Freeze() + /// + /// + /// Suspend thread and wait till it is suspended + /// + /// + internal void Suspend(bool aboutToStop) { - Debug.Assert(this != CurrentThread); - bool iflag = Processor.DisableInterrupts(); - Debug.Assert(iflag); - try { - Scheduler.DispatchLock(); - try { - switch (threadState) { - case ThreadState.Unstarted: - case ThreadState.Stopped: - case ThreadState.Suspended: - break; - case ThreadState.Running: - case ThreadState.Stopping: - if (ActiveProcessor == null) { - break; - } - Scheduler.OnThreadFreezeIncrement(this); - WaitUntilInactive(); - // (Try to be courteous to the scheduler -- don't - // ask it to resume a stopped thread.) - if (threadState != ThreadState.Stopped) { - Scheduler.OnThreadFreezeDecrement(this); - } - break; - default: - Debug.Assert(false, "unreachable default case"); - return; - } - } - catch(Exception e) { - Scheduler.DispatchRelease(); - throw e; - } - } - catch(Exception e) { - Processor.RestoreInterrupts(iflag); - throw e; - } - Debug.Assert(ActiveProcessor == null); - Scheduler.AssertDispatchLockHeld(); + Kernel.TheScheduler.Suspend(this); } - internal void Unfreeze() - { - Scheduler.DispatchRelease(); - Processor.RestoreInterrupts(true); - } - - // precondition: interrupts disabled with iflag==true - // precondition: dispatch lock acquired - // invariant: freezeCount > 0 - // postcondition: interrupts disabled with iflag==true - // postcondition: dispatch lock acquired - // postcondition: ActiveProcessor == null - private void WaitUntilInactive() - { - Scheduler.AssertDispatchLockHeld(); - Debug.Assert(this != CurrentThread); - Debug.Assert(freezeCount > 0); - if (ActiveProcessor != null) { - // TODO: send interrupt to ActiveProcessor - - // spin for exponentially backed off delay - int count = 31; - while (ActiveProcessor != null) { - Scheduler.DispatchRelease(); - Processor.RestoreInterrupts(true); - Thread.SpinWait(count); - count = (count == 0x7ffffffu) ? count : count + count + 1; - bool iflag = Processor.DisableInterrupts(); - Debug.Assert(iflag); - Scheduler.DispatchLock(); - } - } - } - - // Do not call Thread.Suspend unless you are suspending all the - // threads in a process. This should only be called from the - // kernel service thread. - // - // Return value: - // true means the thread is now suspended, unstarted, or stopped. - // false means the thread must be allowed to run a little longer, - // so try calling Suspend later. - // - // precondition: interrupts enabled - // precondition: dispatch lock not acquired - // postcondition: interrupts enabled - // postcondition: dispatch lock not acquired - internal bool Suspend(bool aboutToStop) - { - /* - case (kernel, unblocked, running): return false; - case (kernel, unblocked, stopping): return false; - case (process, unblocked, running): DoSuspend(); return true; - case (kernel, blocked, running): DoSuspend(); return true; - case (kernel, unblocked, unstarted): return true; - case (_, _, suspended): return true; - case (kernel, unblocked, stopped): return true; - case _: error - // Also: in the (kernel, blocked, running) case, if - // aboutToStop==true, call WaitDone(). - */ - Freeze(); - try { - if (threadState == ThreadState.Suspended) { - return true; - } - else if (IsInKernelMode()) { // in kernel mode, not suspended - if (blocked) { - if (threadState == ThreadState.Running) { - DoSuspend(); - if (aboutToStop) { - WaitDone(null); - } - return true; - } - else { - Debug.Assert(false, "unexpected thread state"); - return false; - } - } - else { // in kernel mode, unblocked, not suspended - switch (threadState) { - case ThreadState.Running: - case ThreadState.Stopping: - // Let the thread run a while longer, but - // let it know that eventually, it should - // suspend: - // MULTIPROCESSOR NOTE: halforgc.asm reads - // suspendAlert with no explicit - // synchronization. We don't need the - // read to be consistent with the - // following write immediately, but it - // must become consistent eventually. - context.suspendAlert = true; - return false; - case ThreadState.Unstarted: - case ThreadState.Stopped: - // Note: now that process.state==Suspending[Recursive], a barrier - // prevents starting threads, an Unstarted thread stays - // Unstarted. - return true; - case ThreadState.Suspended: - Debug.Assert(false, "unexpected thread state"); - return false; - default: - Debug.Assert(false, "unreachable default case"); - return false; - } - } - } - else { // in process mode, not suspended - if (!blocked && threadState == ThreadState.Running) { - DoSuspend(); - return true; - } - else { - Debug.Assert(false, "unexpected thread state"); - return false; - } - } - } - finally { - Scheduler.DispatchRelease(); - Processor.RestoreInterrupts(true); - } - } - - private void DoSuspend() - { - threadState = ThreadState.Suspended; - Scheduler.OnThreadFreezeIncrement(this); - } - - // Do not call Thread.Resume unless you are resuming all the - // threads in a process. This should only be called from the - // kernel service thread. + /// + /// + /// Resume thread + /// + /// internal void Resume() { - bool iflag = Processor.DisableInterrupts(); - try { - Scheduler.DispatchLock(); - try { - if (threadState == ThreadState.Suspended) { - threadState = ThreadState.Running; - Scheduler.OnThreadFreezeDecrement(this); - } - } - finally { - Scheduler.DispatchRelease(); + Kernel.TheScheduler.Resume(this); + } + + /// + /// + /// Handle abort - if abort is requested for the current thread, calling this + /// method stops the thread immediately. Note this method can only be called + /// by the current thread + /// + /// + /// Where the request come from, for debugging purpose + /// + [Inline] + [NoHeapAllocation] + internal void ProcessAbortIfRequested(AbortRequestSource source) + { + // TODO: This exists with no + // code as a means of getting an updated Bartok in + // the tree - the added files in the process call + // this method and the files can't be edited until + // checked in. +#if false + // Assert preconditions: This method can only be called by the current thread + VTable.Assert(this == Thread.CurrentThread); + + // If the thread is requested to abort, stop it now + if (IsAbortRequested) { + + // We are done: stop thread and disassociate it from scheduler + Scheduler.OnThreadStop(this); + } +#endif // false + } + + /// + /// + /// Abort thread - thread will be aborted right a way unless dealy abort bit is set + /// + /// + internal void Stop() + { + // First make sure that thread is stable - suspended + Suspend(false); + + // Now we can stop thread + StopSuspended(); + } + + /// + /// + /// Abort suspended thread - thread will be aborted right a way unless dealy abort bit is set + /// Once stop call is processed, thread will be resumed to finish stop + /// + /// + internal void StopSuspended() + { + bool shouldResumeAtTheEnd = true; + // Once thread is supsended - turn on abort + Abort(); + + // If thread is not stopped already and is not blocked and not in delay abort scope unblock it + if (!IsStopped() && !IsAbortDelayed()) { + // Try to unblock it if it is blocked + if (this.UnblockedBy == WaitHandle.UninitWait) { + // Just go ahead and try to unblock it if we succesfuly thread will be + // aborted after resume + if (Unblock(WaitHandle.UnblockedByAbort) == WaitHandle.UnblockedByAbort && + ShouldCallSchedulerUnBlock(WaitHandle.UnblockedByAbort)) { + + // Before we can unblock thread we have to resume it -currently + // we don't have a way of going from block and suspend state back + // and forward: + Resume(); + + // Now are ready to unblock it + Kernel.TheScheduler.OnThreadUnblocked(this); + + shouldResumeAtTheEnd = false; + } } } - finally { - Processor.RestoreInterrupts(iflag); + + // We are done with thread just resume it + if (shouldResumeAtTheEnd) { + Resume(); } } - // Do not call Thread.Stop unless you are stopping all the - // threads in a process. This should only be called from the - // kernel service thread. - // - // precondition: interrupts enabled - // precondition: dispatch lock not acquired - // postcondition: interrupts enabled - // postcondition: dispatch lock not acquired - internal void Stop() + /// + /// + /// Move thread abort status to abort state + /// + /// + private void Abort() { - /* - case (kernel, unblocked, unstarted): return; - case (kernel, unblocked, stopped): return; - case (kernel, _, suspended): - if (blocked) WaitDone(null); - t.throw ProcessStopException - threadState = ThreadState.Running; - Scheduler.OnThreadFreezeDecrement(this); - return; - case (process, unblocked, suspended): - t.(pop frames and throw ProcessStopException) - threadState = ThreadState.Running; - Scheduler.OnThreadFreezeDecrement(this); - return; - case _: error - */ - Freeze(); - try { - bool kernelMode = IsInKernelMode(); - if (threadState == ThreadState.Suspended) { - if (kernelMode) { - if (blocked) { - WaitDone(null); - } - // context.eip = __throwBeyondMarker; - // context.eax = context.stackMarkers; // kernel->kernel marker; - // context.ecx = processStopException; - setStopContext(this, processStopException); - } - else if (!kernelMode && !blocked) { - // context.eip = __throwBeyondMarker; - // context.eax = context.stackMarkers; // kernel->process marker; - // context.ecx = processStopException; - setStopContext(this, processStopException); - } - else { - Debug.Assert(false, "unexpected thread state"); - } - Debug.Assert(!blocked); - threadState = ThreadState.Running; - Scheduler.OnThreadFreezeDecrement(this); - } - else if ( - kernelMode && !blocked && - ( threadState == ThreadState.Stopped - || threadState == ThreadState.Unstarted)) { - // nothing needs to happen here. + SchedulerInfo newInfo; + SchedulerInfo oldInfo; + + do { + // Copy scheduler state to memory + newInfo = this.schedulerInfo; + oldInfo = newInfo; + + // Mark thread as aborted + newInfo.IsAborted = true; + + // Attempt to update atomicatlly the state. + } while (oldInfo.Data != Interlocked.CompareExchange(ref this.schedulerInfo.Data, + newInfo.Data, + oldInfo.Data)); + return; + } + + /// + /// + /// Handle Abort - if abort is set and we are not in dealy abort scope throw + /// abort exception + /// + /// + internal void ProcessAbortIfRequired() + { + // Assert preconditions: This method can only be called by the current thread + VTable.Assert(this == Thread.CurrentThread); + + // If thread is aborted and abort is not delayed handle it! + if (ShouldStop()) { + // Stop if we have to + + // We are done: stop thread and disassociate it from scheduler + Kernel.TheScheduler.OnThreadStop(this); + } + return; + } + + /// + /// + /// Enable/Disable delay abort on the thread depending on the passed in flag + /// + /// + /// Flag indicating if abort has to be enabled or disabled + /// + [NoHeapAllocation] + internal void DelayStop(bool shouldDelayAbort) + { + SchedulerInfo newInfo; + SchedulerInfo oldInfo; + + do { + // Copy scheduler state to memory + newInfo = this.schedulerInfo; + oldInfo = newInfo; + + // Check the abort direction - enabling or disabling + if (shouldDelayAbort) { + // We are enabling abort + newInfo.IncrementDelayAbortCount(); } else { - Debug.Assert(false, "unexpected thread state"); + // We are disabling abort + newInfo.DecrementDelayAbortCount(); } - } - finally { - Scheduler.DispatchRelease(); - Processor.RestoreInterrupts(true); + + // Attempt to update atomicatlly the state. + } while (oldInfo.Data != Interlocked.CompareExchange(ref this.schedulerInfo.Data, + newInfo.Data, + oldInfo.Data)); + } + + /// + /// + /// Handle Abort - if abort is set and we are not in dealy abort scope throw + /// abort exception + /// + /// + [Inline] + [NoHeapAllocation] + internal bool ShouldStop() + { + // Check if thread has to be stopped + return (IsAborted() && !IsAbortDelayed()); + } + + /// + /// + /// Check if we can abort thread + /// + /// + [NoHeapAllocation] + internal bool IsAbortDelayed() + { + return this.schedulerInfo.DelayAbortCount > 0; + } + + /// + /// + /// Check if thread has abort bit set + /// + /// + [NoHeapAllocation] + internal bool IsAborted() + { + return this.schedulerInfo.IsAborted; + } + + /// + /// + /// Notify thread that it acquired spinlock of specific rank + /// + /// + /// Type of a spinlock + /// + [NoHeapAllocation] + internal void NotifySpinLockAboutToAcquire(int type) + { + int rank = SpinLock.DeriveRank(type); + + // Assert preconditions - we can't acquire spinlocks of the lower rank than we already + // acquired + VTable.Assert(rank == (int) SpinLock.Ranks.NoRank || + this.spinLockRankMask == (int) SpinLock.Ranks.NoRank || + this.spinLockRankMask > rank); + VTable.Assert(Processor.InterruptsDisabled()); + + // Record rank + this.spinLockRankMask |= rank; + + // Update number of spinlocks held - we have to do until we remove NoRank... + this.numberOfSpinlocksHeld++; + + // If spinlock has a rank higher than dispatcher, we need to mark thread for delay + // abortion + if (rank == (int)SpinLock.Ranks.NoRank || rank > (int)SpinLock.Ranks.Dispatcher) { + DelayStop(true); } } + /// + /// + /// Notify thread that released a spinlock of specific rank + /// + /// + /// Type of a spinlock + /// + [NoHeapAllocation] + internal void NotifySpinLockReleased(int type) + { + VTable.Assert(Processor.InterruptsDisabled()); + + int rank = SpinLock.DeriveRank(type); + + // Assert preconditions: We can't release multiple ranks at the same time: There should + // be only a single bit set for the rank. The following calculation turns off a least + // significant bit: x & (x-1) and hence the assert: + VTable.Assert((rank & (rank-1)) == 0); + + // We can't release spinlocks out of order. The calculation below + // relies on the following property of bitmask operations:If we turn off least significant bit + // and then or it with the rank we are releasing, we should get the same mask as we head + // before. To turn off least significant bit we use operation such as x = x&(x-1). If + // spinlocks released out of order we will turn off least significant bit other than + // rank we about to release so when we apply or with rank to the result we won't get + // the same mask back. + + VTable.Assert(rank == (int)SpinLock.Ranks.NoRank || + this.spinLockRankMask == ( + (this.spinLockRankMask & (this.spinLockRankMask-1)) | rank)); + + // We should have acquired spinlock of such rank before + VTable.Assert(rank == (int)SpinLock.Ranks.NoRank || + (this.spinLockRankMask & rank) != 0); + + // Turn off specified rank + this.spinLockRankMask ^= rank; + + // Update number of spinlocks held - we have to do until we remove NoRank... + this.numberOfSpinlocksHeld--; + + // If spinlock has a rank higher than dispatcher , we need to mark thread that + // it can be stopped + if (rank == (int)SpinLock.Ranks.NoRank || rank > (int)SpinLock.Ranks.Dispatcher) { + DelayStop(false); + } + } + + /// + /// + /// Given a thread id return thread + /// + /// + /// Thread Id + /// + [NoHeapAllocation] + internal static Thread GetThreadFromThreadId(int threadId) + { + // Assert preconditions: threadId has to be in range and thread can't be null + VTable.Assert(threadId < threadTable.Length); + VTable.Assert(threadTable[threadId] != null); + + return threadTable[threadId]; + } + + [MethodImpl(MethodImplOptions.InternalCall)] [NoHeapAllocation] [StackBound(4)] @@ -1227,7 +1435,7 @@ namespace System.Threading //| public static LocalDataStoreSlot AllocateDataSlot() { - return m_LocalDataStoreMgr.AllocateDataSlot(); + return localDataStoreMgr.AllocateDataSlot(); } ////////////////////////////////////////////////////////////////////// @@ -1237,7 +1445,7 @@ namespace System.Threading //| public static LocalDataStoreSlot AllocateNamedDataSlot(String name) { - return m_LocalDataStoreMgr.AllocateNamedDataSlot(name); + return localDataStoreMgr.AllocateNamedDataSlot(name); } ////////////////////////////////////////////////////////////////////// @@ -1247,7 +1455,7 @@ namespace System.Threading //| public static LocalDataStoreSlot GetNamedDataSlot(String name) { - return m_LocalDataStoreMgr.GetNamedDataSlot(name); + return localDataStoreMgr.GetNamedDataSlot(name); } ////////////////////////////////////////////////////////////////////// @@ -1257,7 +1465,7 @@ namespace System.Threading //| public static void FreeNamedDataSlot(String name) { - m_LocalDataStoreMgr.FreeNamedDataSlot(name); + localDataStoreMgr.FreeNamedDataSlot(name); } ////////////////////////////////////////////////////////////////////// @@ -1265,7 +1473,7 @@ namespace System.Threading //| public static Object GetData(LocalDataStoreSlot slot) { - m_LocalDataStoreMgr.ValidateSlot(slot); + localDataStoreMgr.ValidateSlot(slot); if (localDataStore != null) { return localDataStore.GetData(slot); @@ -1280,13 +1488,11 @@ namespace System.Threading { // Create new DLS if one hasn't been created for this thread. if (localDataStore == null) { - localDataStore = m_LocalDataStoreMgr.CreateLocalDataStore(); + localDataStore = localDataStoreMgr.CreateLocalDataStore(); } localDataStore.SetData(slot, data); } - /*=============================================================*/ - internal Object ExceptionState { [NoHeapAllocation] @@ -1382,6 +1588,18 @@ namespace System.Threading [NoHeapAllocation] public static extern Object VolatileRead(ref Object address); + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern byte VolatileReadUnsafe(byte* address); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern short VolatileReadUnsafe(short* address); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern int VolatileReadUnsafe(int* address); + //| [Intrinsic] [NoHeapAllocation] @@ -1452,6 +1670,21 @@ namespace System.Threading [NoHeapAllocation] public static extern void VolatileWrite(ref Object address, Object value); + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern void VolatileWriteUnsafe(int* address, + int value); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern void VolatileWriteUnsafe(short* address, + short value); + + [Intrinsic] + [NoHeapAllocation] + internal static unsafe extern void VolatileWriteUnsafe(byte* address, + byte value); + //| [Intrinsic] [NoHeapAllocation] @@ -1466,16 +1699,26 @@ namespace System.Threading __arglist( s, (UIntPtr)pContext, - context.esp, - context.ebp, - context.eip, + context.threadRecord.spill.StackPointer, + context.threadRecord.spill.FramePointer, + context.threadRecord.spill.InstructionPointer, Kernel.AddressOf(context.thread), - context.efl, + context.threadRecord.spill.Flags, (UIntPtr)context.prev, (UIntPtr)context.next)); } } + [NoHeapAllocation] + internal static unsafe void TraceAbbrev(ref ThreadContext context, String s) + { + fixed (ThreadContext* pContext = &context) + { + Tracing.Log(Tracing.Debug, s); + context.threadRecord.spill.Trace(); + } + } + [NoHeapAllocation] internal static unsafe void Display(ref ThreadContext context, String s) { @@ -1493,291 +1736,57 @@ namespace System.Threading (UIntPtr)context.prev, (UIntPtr)context.next)); - DebugStub.Print(" eax={0:x8} ebx={1:x8} ecx={2:x8} edx={3:x8}\n", - __arglist( - context.eax, - context.ebx, - context.ecx, - context.edx)); + context.threadRecord.spill.Display(); - DebugStub.Print(" esp={0:x8} ebp={1:x8} esi={2:x8} edi={3:x8}\n", - __arglist( - context.esp, - context.ebp, - context.esi, - context.edi)); - - DebugStub.Print(" eip={0:x8} efl={1:x8} err={2:x8} cr2={3:x8}\n", - __arglist( - context.eip, - context.efl, - context.err, - context.cr2)); } - ///////////////////////////////////////////////// Blocking operations. + ///////////////////////////////////////////////// Blocking operatings. // internal int WaitAny(WaitHandle[] waitHandles, int waitHandlesCount, TimeSpan timeout) { - SchedulerTime stop = SchedulerTime.Now + timeout; - return WaitAny(waitHandles, waitHandlesCount, stop); + + SchedulerTime stop; + + if (timeout != TimeSpan.MaxValue) { + stop = SchedulerTime.Now + timeout; + } + else { + stop = SchedulerTime.MaxValue; + } + return WaitHandle.WaitAny(waitHandles, waitHandlesCount, stop); } internal int WaitAny(WaitHandle[] waitHandles, int waitHandlesCount, SchedulerTime stop) { - VTable.Assert(threadState == ThreadState.Running); - VTable.Assert(!blocked); - - Kernel.Waypoint(100); - ThreadEntry[] entries = GetWaitEntries(waitHandlesCount); - - bool iflag = Processor.DisableInterrupts(); - try { - Thread target = null; - - Scheduler.DispatchLock(); - try { - Kernel.Waypoint(101); - - // Enqueue on all of the non-signaled handles. - for (int marked = 0; marked < waitHandlesCount; marked++) { - if (waitHandles[marked].AcquireOrEnqueue(entries[marked])) { - // If one of the handles was signaled, then abort waits... -#if DEBUG_DISPATCH - DebugStub.Print("Thread {0:x8} WaitAny 004 [Presignal] "+ - "on {1:x8}\n", - __arglist( - Kernel.AddressOf(this), - Kernel.AddressOf(waitHandles[marked]))); -#endif // DEBUG_DISPATCH - for (int released = 0; released < marked; released++) { - entries[released].RemoveFromQueue(); - } - Scheduler.DispatchRelease(); - return marked; - } - } - - blocked = true; - blockedOn = entries; - blockedOnCount = waitHandlesCount; - blockedUntil = stop; - Monitoring.Log(Monitoring.Provider.Thread, - (ushort)ThreadEvent.WaitAny, 0, - (uint)stop.Ticks, (uint)(stop.Ticks >> 32), - (uint)this.threadIndex, 0, 0); - unblockedBy = WaitHandle.WaitTimeout; - -#if DEBUG_DISPATCH - DebugStub.Print("Thread {0:x8} WaitAny 007 [Blocking] on\n", - __arglist(Kernel.AddressOf(this))); - for (int i = 0; i < waitHandlesCount; i++) { - DebugStub.Print(" {0:d3}: {1:x8}\n", - __arglist(i, Kernel.AddressOf(waitHandles[i]))); - } -#endif // DEBUG_DISPATCH - - target = Scheduler.OnThreadBlocked(this, stop); - Scheduler.SelectingThread(target); - } - catch(Exception e) { - Scheduler.DispatchRelease(); - throw e; - } - - if (target == null) { -#if DEBUG_DISPATCH - DebugStub.Print("Thread {0:x8} WaitAny 013 [Idle Thread]\n", - __arglist(Kernel.AddressOf(this))); -#endif // DEBUG_DISPATCH - target = Processor.CurrentProcessor.IdleThread; - VTable.Deny(Transitions.UnderGCControl(target.threadIndex)); - } -#if DEBUG_DISPATCH - DebugStub.Print("Thread {0:x8} WaitAny 014 [SwitchTo] thread {1:x8}\n", - __arglist( - Kernel.AddressOf(this), - Kernel.AddressOf(target))); -#endif // DEBUG_DISPATCH - target.SwitchTo(); - - // Execution will return here only after either the timeout - // period has been passed or one of the handles has been - // signalled. If we were unblocked, then WaitDone will have - // been called and unblockedBy will not be WaitHandle.WaitTimeout. - // If we timed out, then WaitFail will have been called. - - } - finally { - //Kernel.Waypoint(114); - Processor.RestoreInterrupts(iflag); -#if DEBUG_DISPATCH - DebugStub.Print("Thread {0:x8} WaitAny 019\n", - __arglist(Kernel.AddressOf(this))); -#endif // DEBUG_DISPATCH - } - - return unblockedBy; + return WaitHandle.WaitAny(waitHandles, waitHandlesCount, stop); } - // This method should only be called by low-level synchronization - // primitives derived from WaitHandle. If you need to cause a - // thread to sleep or wake up, you should use those primitives - // instead. - [NoHeapAllocation] - internal void WaitDone(ThreadEntry entry) - { - Scheduler.AssertDispatchLockHeld(); - // DebugStub.Break(); // - - for (int i = 0; i < blockedOnCount; i++) { - if (blockedOn[i] == entry) { - unblockedBy = i; - } - else { - blockedOn[i].RemoveFromQueue(); - } - } - - // Tell the scheduler this thread is no longer blocked. - Scheduler.OnThreadUnblocked(this); - - // Clear out the blocked information, after we know we won't want it. - blocked = false; - blockedOn = null; //No more beneficiaries of inherited time. - blockedOnCount = 0; - } - - // This method is called when the scheduler has timed-out the wait. - [NoHeapAllocation] - internal void WaitFail() - { - Scheduler.AssertDispatchLockHeld(); - // DebugStub.Break(); // - -#if DEBUG_DISPATCH - DebugStub.Print("Thread {0:x8} [WaitFail]\n", - __arglist(Kernel.AddressOf(this))); -#endif // DEBUG_DISPATCH - - VTable.Assert(unblockedBy == WaitHandle.WaitTimeout); - - for (int i = 0; i < blockedOnCount; i++) { - blockedOn[i].RemoveFromQueue(); - } - - Monitoring.Log(Monitoring.Provider.Thread, - (ushort)ThreadEvent.WaitFail, 0, - (uint)blockedUntil.Ticks, - (uint)(blockedUntil.Ticks >> 32), - (uint)this.threadIndex, 0, 0); - - // Clear out the blocked information, after we know we won't want it. - blocked = false; - blockedOn = null; //No more beneficiaries of inherited time. - blockedOnCount = 0; - } - - ///////////////////////////////////////////////// Context Switch Code. - // - - // This method should only be called by the scheduler and by Yield(). - // precondition: Scheduler.dispatchLock held - // postcondition: Scheduler.dispatchLock released - [NoHeapAllocation] - private void SwitchTo() - { - Thread currentThread = CurrentThread; -#if DONT - - Tracing.Log(Tracing.Audit, "Schedule(old={0:x}, new={1:x})", - Kernel.AddressOf(currentThread), - Kernel.AddressOf(this)); -#endif -#if DEBUG_SWITCH - Display(ref this.context, "New Context"); -#endif - Scheduler.AssertDispatchLockHeld(); - Processor.CurrentProcessor.NumContextSwitches++; - //Tracing.LogContextSwitch(CurrentThread.GetThreadId(), this.GetThreadId()); -#if DEBUG_SWITCH - Thread.DisplayAbbrev(ref currentThread.context, "swi bef"); -#endif - Kernel.Waypoint(2); - Monitoring.Log(Monitoring.Provider.Thread, - (ushort)ThreadEvent.SwitchTo, 0, - (uint)this.context.threadIndex, 0, 0, 0, 0); - Processor.SwitchToThreadContext(ref currentThread.context, - ref this.context); - // Kernel.Waypoint(7); -#if DEBUG_SWITCH - Thread.DisplayAbbrev(ref Thread.CurrentThread.context, "swi aft"); -#endif -#if DONT - Display(ref currentThread.context, "Old Context"); - - Display(ref threadTable[0].context, "[0] Context"); - Display(ref threadTable[1].context, "[1] Context"); -#endif - } - - - // - ////////////////////////////////////////////////////////////////////// - [NoHeapAllocation] public static void Yield() { - bool iflag = Processor.DisableInterrupts(); - try { - Thread target = CurrentThread; - - Scheduler.DispatchLock(); - try { - Kernel.Waypoint(1); - target = Scheduler.OnThreadYield(CurrentThread); -#if DEBUG_DISPATCH - DebugStub.Print("Yield.Selecting\n"); -#endif // DEBUG_DISPATCH - Scheduler.SelectingThread(target); - } - catch(Exception e) { - Scheduler.DispatchRelease(); - throw e; - } - - if (target == null) { - target = CurrentThread; - } - target.SwitchTo(); - } - finally { - //Kernel.Waypoint(114); - Processor.RestoreInterrupts(iflag); - } + Kernel.TheScheduler.OnThreadYield(CurrentThread); } [NoHeapAllocation] public bool IsWaiting() { - return blocked; + return IsBlocked; } [NoHeapAllocation] public bool IsStopped() { - return (threadState == ThreadState.Stopped); + return (this.schedulerInfo.State == ThreadState.Stopped); } [NoHeapAllocation] public bool IsStopping() { - return (threadState == ThreadState.Stopping || - threadState == ThreadState.Stopped); + return IsStopped(); } [PreInitRefCounts] @@ -1789,11 +1798,12 @@ namespace System.Threading // Enable Thread.CurrentThread as soon as we can! initialThread = Magic.toThread(GCs.BootstrapMemory.Allocate(typeof(Thread))); - initialThread.threadState = ThreadState.Running; + initialThread.schedulerInfo.State = ThreadState.Running; initialThread.SetKernelMode(); initialThread.threadIndex = 0; // Allocate tables for thread management + threadTableLock = new SpinLock(SpinLock.Types.ThreadTable); threadTable = (Thread[]) GCs.BootstrapMemory.Allocate(typeof(Thread[]), maxThreads); @@ -1837,7 +1847,6 @@ namespace System.Threading VTable.Assert((int)(((RuntimeType)typeof(Thread)).classVtable).baseAlignment == 16); VTable.Assert((int)(((RuntimeType)typeof(ThreadContext)).classVtable).baseAlignment == 16); - VTable.Assert((int)(((RuntimeType)typeof(MmxContext)).classVtable).baseAlignment == 16); Tracing.Log(Tracing.Debug, "InitialThread={0:x8}", Kernel.AddressOf(initialThread)); @@ -1859,22 +1868,21 @@ namespace System.Threading initialThread.autoEvent = new AutoResetEvent(false); initialThread.joinEvent = new ManualResetEvent(false); initialThread.schedulerEntry = new ThreadEntry(initialThread); + initialThread.timerEntry = new ThreadEntry(initialThread); + initialThread.deferredEntry= new ThreadEntry(initialThread); initialThread.GetWaitEntries(1); // Cache allows wait without alloc Transitions.RuntimeInitialized(); Transitions.ThreadStart(); // Instantiate the static variable that needs to be initialized - m_LocalDataStoreMgr = new LocalDataStoreMgr(); + localDataStoreMgr = new LocalDataStoreMgr(); processStopException = new ProcessStopException(); - - // Tell the scheduler to initialize the thread. - Scheduler.OnThreadStateInitialize(initialThread, false); } /// Prepares a new Thread to take on role as kernel thread /// for upcoming processor. Called by Bootstrap processor. - public static Thread PrepareKernelThread() + public static Thread PrepareKernelThread(Processor p) { Thread kernelThread = new Thread(null); GC.NewThreadNotification(kernelThread, false); @@ -1891,7 +1899,7 @@ namespace System.Threading kernelThread.context.UpdateAfterGC(kernelThread); Processor.SetCurrentThreadContext(ref kernelThread.context); - kernelThread.threadState = ThreadState.Running; + kernelThread.schedulerInfo.State = ThreadState.Running; Transitions.ThreadStart(); } @@ -1902,28 +1910,7 @@ namespace System.Threading Kernel.AddressOf(this), context.stackBegin, context.stackLimit, - Processor.GetStackPointer()); - } - - public Thread GetRunnableBeneficiary() - { - // this does a depth first search, not sure if that is a good idea. - if (threadState == ThreadState.Running) { - return this; - } - Thread tempThread; - for (int i = 0; blockedOn != null && i < blockedOnCount; i++) { - if (blockedOn[i] != null) { - tempThread = blockedOn[i].GetBeneficiary(); - if (tempThread != null) { - tempThread = tempThread.GetRunnableBeneficiary(); - } - if (tempThread != null) { - return tempThread; - } - } - } - return null; + Isa.GetStackPointer()); } #if THREAD_TIME_ACCOUNTING @@ -1939,7 +1926,7 @@ namespace System.Threading // fixme: where to init. this one ??? // FinishInitializeThread() seems to be called before // Processor.CyclesPerSecond is set up - //static private ulong multiplier = Processor.CyclesPerSecond / + //static private ulong multiplyer = Processor.CyclesPerSecond / // TimeSpan.TicksPerSecond #else protected TimeSpan executionTime; @@ -1953,18 +1940,15 @@ namespace System.Threading ulong m = Processor.CyclesPerSecond / TimeSpan.TicksPerSecond; bool saved = Processor.DisableInterrupts(); - try - { - if (Processor.GetCurrentThread() == this) - { + try { + if (Processor.GetCurrentThread() == this) { ulong now = Processor.CycleCount; context.executionTime += now - context.lastExecutionTimeUpdate; LastUpdateTime = now; } } - finally - { + finally { Processor.RestoreInterrupts(saved); } @@ -1988,18 +1972,15 @@ namespace System.Threading get { bool saved = Processor.DisableInterrupts(); - try - { - if (Processor.GetCurrentThread() == this) - { + try { + if (Processor.GetCurrentThread() == this) { ulong now = Processor.CycleCount; context.executionTime += now - context.lastExecutionTimeUpdate; LastUpdateTime = now; } } - finally - { + finally { Processor.RestoreInterrupts(saved); } @@ -2009,7 +1990,7 @@ namespace System.Threading #endif internal static - void VisitBootstrapData(NonNullReferenceVisitor visitor) + void VisitBootstrapData(ReferenceVisitor visitor) { visitor.VisitReferenceFields(initialThread); visitor.VisitReferenceFields(threadTable); @@ -2094,7 +2075,7 @@ namespace System.Threading return selectObjects; } public void PushSelectObjects(ISelectable[] cache) { - for (int i=0; i + public SchedulerTime BlockedUntil + { + [NoHeapAllocation] + get { return blockedUntil; } + } // Given a frame's range in memory (its esp/ebp), check whether // the frame contains the top transition record. If so, // prepare to skip over a process's frames. [AccessedByRuntime("referenced from halasm.asm")] - [NoStackLinkCheck] // We don't want to throw an exception here; + [NoStackLinkCheckTrans] // We don't want to throw an exception here; // Therefore, we cannot risk allocating stack segments, // and we should only call other NoStackLinkCheck functions (XXX). internal static unsafe UIntPtr CheckKernelProcessBoundary(UIntPtr esp, UIntPtr ebp, Exception exn) @@ -2139,27 +2126,30 @@ namespace System.Threading if (esp < topMarkerPtr && topMarkerPtr <= ebp) { Thread.CurrentThread.lastUncaughtException = exn; // Is this a ProcessStopException? If not, it's a bug; log the bug. - if (!(exn is ProcessStopException)) { + + // NOTE: Removing the 'if'. This + // will be removed soon, and the 'is' here + // results in an unbreakable cycle. + //if (!(exn is ProcessStopException)) { // Log the bug, but don't do anything that could // throw another exception (e.g. memory allocation). // XXX: what if stack allocation throws an exception here? DebugStub.Print("Bug: kernel exception thrown to process (saved to Thread.LastUncaughtException)\n"); Tracing.Log(Tracing.Warning, "Bug: kernel exception thrown to process (saved to Thread.LastUncaughtException)\n"); - } + //} // atomic // { // do these together so we never enter process mode // remove top process->kernel marker from marker list // remove top kernel->process marker from marker list // } bool iflag = Processor.DisableInterrupts(); - Scheduler.DispatchLock(); + try { context->processMarkers = context->processMarkers->oldTransitionRecord; context->stackMarkers = context->stackMarkers->oldTransitionRecord; context->SetKernelMode(); } finally { - Scheduler.DispatchRelease(); Processor.RestoreInterrupts(iflag); } @@ -2180,8 +2170,10 @@ namespace System.Threading // containing the marker. After this runs, the topmost stack // segment will contain the marker. [AccessedByRuntime("referenced from halasm.asm")] - [NoStackLinkCheck] - internal static unsafe void DiscardSkippedStackSegments(System.GCs.CallStack.TransitionRecord *marker) + [NoStackLinkCheckTrans] + internal static unsafe void DiscardSkippedStackSegments( + System.GCs.CallStack.TransitionRecord *marker, + System.GCs.CallStack.TransitionRecord *oldMarker) { ThreadContext *context = Processor.GetCurrentThreadContext(); UIntPtr markerPtr = new UIntPtr(marker); @@ -2189,7 +2181,7 @@ namespace System.Threading // pop (and free) stack segment // - // HACKHACK think about what this is doing. The topmost + // HACKHACK: think about what this is doing. The topmost // stack segment is the one *currently in use*. On a paging // system, freeing it unmaps the underlying physical page. // Needless to say, our ability to use esp after that is @@ -2202,7 +2194,10 @@ namespace System.Threading #endif // Unlink marker: - context->stackMarkers = marker->oldTransitionRecord; + context->stackMarkers = oldMarker; + + // Update stack limit. + context->threadRecord.activeStackLimit = context->stackLimit; } // Most recently thrown exception object that the thread @@ -2262,5 +2257,808 @@ namespace System.Threading currentDomain.AddressSpace); } #endif + + /// + /// + /// Prepare thread for blocking - initialize UnblockedBy state + /// + /// + [NoHeapAllocation] + public int PrepareForBlocking() + { + SchedulerInfo newInfo; + SchedulerInfo oldInfo; + int prevUnblockedBy; + + do { + // Copy scheduler state to memory + newInfo = this.schedulerInfo; + oldInfo = newInfo; + + // Initialize unblocked by - this is all we pretty much going to update + newInfo.UnblockedBy = WaitHandle.UninitWait; + + // Remember who unblocked us before + prevUnblockedBy = oldInfo.UnblockedBy; + + // Attempt to update atomicatlly the state. + } while (oldInfo.Data != Interlocked.CompareExchange(ref this.schedulerInfo.Data, + newInfo.Data, + oldInfo.Data)); + return prevUnblockedBy; + } + + /// + /// + /// Finish blocking - only changes blocking state if UnblockedBy is uninitialized + /// + /// + /// + /// Id of waithandle that is attempting to unblock thread + /// + /// + [NoHeapAllocation] + public int Unblock(int unblockedBy) + { + SchedulerInfo newInfo; + SchedulerInfo oldInfo; + int result; + + do { + // Copy scheduler state to memory + newInfo = this.schedulerInfo; + oldInfo = newInfo; + + // We can't unblock stopped and unstarted thread + if ((oldInfo.State & (ThreadState.Stopped | ThreadState.Unstarted)) != 0) { + result = WaitHandle.UninitWait; + break; + } + + // If nobody unblocked thread yet proceed otherwise we can return right away + if (oldInfo.UnblockedBy == WaitHandle.UninitWait) { + + // Remember unblocked information + newInfo.UnblockedBy = unblockedBy; + + // Return unblcoked information + result = unblockedBy; + } + else { + // Remember who unblocked thread + result = oldInfo.UnblockedBy; + + // Now we are ready to return; + break; + } + // Attempt to update atomicatlly the state. + } while (oldInfo.Data != Interlocked.CompareExchange(ref this.schedulerInfo.Data, + newInfo.Data, + oldInfo.Data)); + return result; + } + + /// + /// + /// Block thread - only changes blocking state if UnblockedBy is uninitialized + /// + /// + [NoHeapAllocation] + public int BlockThread() + { + SchedulerInfo newInfo; + SchedulerInfo oldInfo; + int result; + + do { + // Copy scheduler state to memory + newInfo = this.schedulerInfo; + oldInfo = newInfo; + + // If nobody unblocked thread yet proceed otherwise we can return right a way + if (oldInfo.UnblockedBy == WaitHandle.UninitWait) { + + // Mark thread as blocked + newInfo.State = ThreadState.Blocked; + result = WaitHandle.UninitWait; + } + else { + // Remember who unblocked thread + result = oldInfo.UnblockedBy; + + // Now we are ready to return; + break; + } + // Attempt to update atomicatlly the state. + } while (oldInfo.Data != Interlocked.CompareExchange(ref this.schedulerInfo.Data, + newInfo.Data, + oldInfo.Data)); + return result; + } + + + /// + /// + /// Increment thread's freeze counter + /// + /// + [NoHeapAllocation] + public void IncrementFreezeCounter() + { + SchedulerInfo newInfo; + SchedulerInfo oldInfo; + int result; + + do { + // Copy scheduler state to memory + newInfo = this.schedulerInfo; + oldInfo = newInfo; + + // Freeze counter can't be negative + VTable.Assert(newInfo.FreezeCount >= 0); + + // Increment freeze counter + newInfo.FreezeCount++; + + } while (oldInfo.Data != Interlocked.CompareExchange(ref this.schedulerInfo.Data, + newInfo.Data, + oldInfo.Data)); + } + + /// + /// + /// Decrement thread's freeze counter, if thread was really suspended meaing + /// its state was marked suspended by scheduler, return this information so that + /// + /// + /// + /// Indicates if caller has to put thread on a runnable queue + /// + /// + [NoHeapAllocation] + public int DecrementFreezeCounter(ref bool shouldPutOnRunnableQueue) + { + SchedulerInfo newInfo; + SchedulerInfo oldInfo; + int result; + + do { + // By default caller shouldn't be putting this thread on runnable queue + shouldPutOnRunnableQueue = false; + + // Copy scheduler state to memory + newInfo = this.schedulerInfo; + oldInfo = newInfo; + + // Decrement freeze counter + newInfo.FreezeCount--; + + // If thread is really suspended mark it as runnable - caller will put it on runnable + // queue where it will change its state to ether runnable, blocked or suspended + if (newInfo.FreezeCount == 0 && + oldInfo.State == ThreadState.Suspended) { + + // If we succeede with state change, caller will need to put thread on a + // runnable queue + shouldPutOnRunnableQueue = true; + } + + // Freeze counter can't be negative + VTable.Assert(newInfo.FreezeCount >= 0); + + } while (oldInfo.Data != Interlocked.CompareExchange(ref this.schedulerInfo.Data, + newInfo.Data, + oldInfo.Data)); + + // Assert post conditions: If caller has to put thread on runnable queue then counter + // had to be 1 + VTable.Assert(!shouldPutOnRunnableQueue || oldInfo.FreezeCount == 1); + + return oldInfo.FreezeCount -1; + } + + /// + /// + /// Verifies if caller has to call scheduler unblock - this will only required if thread is + /// in blocked state, was unblocked by caller and blocking version is off by one + /// + /// + /// Id of handle unblocker that is attempting to unblock thread + /// + [NoHeapAllocation] + public bool ShouldCallSchedulerUnBlock(int unblocker) + { + return (this.schedulerInfo.State == ThreadState.Blocked && + this.schedulerInfo.UnblockedBy == unblocker); + } + + + /// + /// + /// Given complete thread's scheduler information derive scheduler state + /// + /// + /// Given scheduler atomic info to use for derivation + /// + /// Scheduler info is complex state that consist of three states: Sceduler state + /// unblocked by information and freeze count. We need to examine all three peices + /// of information as well current thread state to derive real scheduelr state + /// + /// + [NoHeapAllocation] + public static ThreadState DeriveCurrentSchedulerState(SchedulerInfo threadSchedulerInfo) + { + // Retrieve state + ThreadState actualState = threadSchedulerInfo.State; + + // If thread is runnable and it is frozen use suspend state otherwise use actual state + if (actualState == ThreadState.Runnable && + threadSchedulerInfo.FreezeCount > 0) { + + actualState = ThreadState.Suspended; + } + + // If thread was unblocked but is marked as blocked it is no longer blocked - it is runnable + if (threadSchedulerInfo.UnblockedBy != WaitHandle.UninitWait && + actualState == ThreadState.Blocked) { + + actualState = ThreadState.Runnable; + } + + return actualState; + } + + /// + /// + /// Derive new scheduler state + /// + /// + /// Given scheduler atomic info to use for derivation + /// scheduling action thread is performing: for now maps to states + /// + /// Scheduler info is complex state that consist of three states: Scheduler state + /// unblocked by information and freeze count. We need to examine all three peices + /// of information as well current thread state to derive real scheduler state + /// + /// + [NoHeapAllocation] + public static ThreadState DeriveNewSchedulerState( + SchedulerInfo threadSchedulerInfo, + ThreadState schedulingAction) + { + ThreadState oldState; + ThreadState newState; + + // First derive old schduler state + oldState = DeriveCurrentSchedulerState(threadSchedulerInfo); + + // Base on the current state and scheduling action calculate new state + switch (oldState) { + case ThreadState.Stopped: { + // When thread is stopped any of scheduling action on it is forbidden + VTable.Assert(false); + break; + } + case ThreadState.Unstarted: { + // Only two actions are allowed in this situation eiterh make thread runnable or + // stop + VTable.Assert(schedulingAction == ThreadState.Runnable || + schedulingAction == ThreadState.Stopped); + + threadSchedulerInfo.State =schedulingAction; + break; + } + case ThreadState.Runnable: { + // When thread is runnable it is normal for scheduler to attempt it to mark + // runnable again - it could happen due to complex runnable state (unitwait + blocked) + // Two other actions can be apllied suspended and running + // any other action should be assumed incorrect + VTable.Assert(schedulingAction == ThreadState.Suspended || + schedulingAction == ThreadState.Running || + schedulingAction == ThreadState.Runnable); + + threadSchedulerInfo.State = schedulingAction; + + break; + } + case ThreadState.Running: { + // From this state we can go to blocked, runnable, stopped no other transition is + // possible + VTable.Assert(schedulingAction == ThreadState.Blocked || + schedulingAction == ThreadState.Runnable || + schedulingAction == ThreadState.Stopped); + + threadSchedulerInfo.State = schedulingAction; + break; + } + case ThreadState.Blocked: { + // From this state we can go to runnable only no other transition is + // possible + VTable.Assert(schedulingAction == ThreadState.Runnable); + + threadSchedulerInfo.State = schedulingAction; + break; + } + case ThreadState.Suspended: { + // From this state we can go to runnable however someone might attempt to + // make us rinnning - we need to ignore it! + VTable.Assert(schedulingAction == ThreadState.Runnable || + schedulingAction == ThreadState.Running); + + // Though we can recieve runnng signal we can't go to running by passing + // runnable + if (schedulingAction == ThreadState.Runnable) { + threadSchedulerInfo.State = schedulingAction; + } + else { + // Otherwise we have to stay suspendedcd . + threadSchedulerInfo.State = ThreadState.Suspended; + } + break; + } + default: { + // We shouldn't get here ever: + VTable.Assert(false); + break; + } + } + + // Now lets derive our new state + return DeriveCurrentSchedulerState(threadSchedulerInfo); + } + + /// + /// + /// Set new scheduler state on thread + /// + /// + /// Scheduler action + /// + [NoHeapAllocation] + internal ThreadState ChangeSchedulerState(ThreadState schedulerAction) + { + Thread.SchedulerInfo oldSchedulerInfo; + Thread.SchedulerInfo newSchedulerInfo; + + do { + // Retrieve thread's complex scheduling state + oldSchedulerInfo = this.ThreadSchedulerInfo; + newSchedulerInfo = this.ThreadSchedulerInfo; + + // Map thread's state to Scheduler state and derive thread's new state + newSchedulerInfo.State = DeriveNewSchedulerState(oldSchedulerInfo, schedulerAction); + + // We calculated new state try change thread's new state. + } while (!TryChangeSchedulerState(newSchedulerInfo.State, oldSchedulerInfo)); + + return newSchedulerInfo.State; + } + + /// + /// + /// Try to change scheduler state to a new state. New state usually is derived from + /// calling DeriveSchedulerState method. For more info see comments to that method + /// + /// + /// New scheduling state + /// Scheduling information base on which we derived new state + /// + [NoHeapAllocation] + private bool TryChangeSchedulerState( + ThreadState newState, + SchedulerInfo previousInfo) + { + bool didStateChange = true; + SchedulerInfo newInfo = previousInfo; + + // Update scheduler state in a new atomic info + newInfo.State = newState; + + //Try to update the state and check if we were succesful + didStateChange = previousInfo.Data == Interlocked.CompareExchange(ref this.schedulerInfo.Data, + newInfo.Data, + previousInfo.Data); + return didStateChange; + } + + /// + /// + /// Wait for reschedule - wait until thread is allowed to be running. This method + /// should be exclusively used by processor dispatcher + /// + /// + [NoHeapAllocation] + internal void WaitUntilReadyForContextSwitch() + { + while (this.insideOfContextSwitchDepth > 0) { + // Loop until thread is inside of context switch + // use SpinWait intrinsic to optimize for hyperthreaded processors. + Thread.SpinWait(1); + } + return; + } + + /// + /// + /// Turn on thread's state inside of context switch + /// + /// + [NoHeapAllocation] + internal void TurnOnInsideOfContextSwitch() + { + // Assert preconditions: Contex Switch Depth can't be negative + VTable.Assert(this.insideOfContextSwitchDepth ==0); + + this.insideOfContextSwitchDepth++; + } + + /// + /// + /// Turn off thread's state inside of context switch + /// + /// + [NoHeapAllocation] + internal void TurnOffInsideOfContextSwitch() + { + // Assert preconditions: Contex Switch Depth can't be 0 + VTable.Assert(this.insideOfContextSwitchDepth == 1); + + this.insideOfContextSwitchDepth--; + } + + /// + /// + /// Find out if we inside of context switch + /// + /// + [NoHeapAllocation] + internal bool IsInsideOfContextSwitch() + { + return this.insideOfContextSwitchDepth > 0; + } + + + /// + /// + /// Scheduler specific information. Includes scheduler state, wait version and + /// unblocked by information + /// + /// + [StructAlign(8)] + public struct SchedulerInfo + { + // NOTE: + // 1. Ideally we can use Interlocked operations on a 64-bit struct, then we + // don't need to use bit masks and shifts. But Bartok doesn't support + // it at the moment. + // 2. Another way is to use StructLayout to emulate union, but we have both + // performance issues in Bartok generated StructCopy and a Bartok bug + // that sometimes optimizes away useful code. + // 3. There are different opinions about whether it's better to define + // constants for the masks and shifts, we'll re-evaluate. + + /// + /// + /// This separated into 5 different fields. We use a single 64-bit integer so that + /// we can use Interlocked operations on this struct. The layout is: + /// + /// State : byte : byte 0 : mask 00000000000000FF + /// DelayAbortCount : byte : bit 0-6 of byte 1 : mask 0000000000007F00 + /// IsAborted : bool : bit 7 of byte 1 : mask 0000000000008000 + /// FreezeCount : UInt16: byte 2-3 : mask 00000000FFFF0000 + /// UnblockedBy : Int32 : byte 4-7 : mask FFFFFFFF00000000 + /// + /// + public UInt64 Data; + + /// + /// + /// State of a thread with respect to scheduler: Runable, Running and etc... + /// + /// + public ThreadState State + { + [Inline] + [NoHeapAllocation] + get + { + // State is byte 0 of the data + return (ThreadState)(byte)Data; + } + [Inline] + [NoHeapAllocation] + set + { + // State is byte 0 of the data + Data &= 0xFFFFFFFFFFFFFF00UL; + Data |= (byte)value; + } + } + + /// + /// + /// Reference counter of delay abort. + /// Thread is not allowed to die with non zero DelayAbortCount. + /// + /// + public byte DelayAbortCount + { + [Inline] + [NoHeapAllocation] + get + { + // AbortState is bit 0-6 of byte 1 of the data + return (byte)(((UInt32)Data >> 8) & 0x7F); + } + } + + + /// + /// + /// Increment the delay abort count. + /// Thread is not allowed to die with non zero DelayAbortCount. + /// + /// + [Inline] + [NoHeapAllocation] + public void IncrementDelayAbortCount() + { + // AbortState is bit 0-6 of byte 1 of the data + // It must not overflow above 7 bits (0x7F) + VTable.Assert(((UInt32)Data & 0x7F00) != 0x7F00); + Data += 0x100; + } + + /// + /// + /// Decrement the delay abort count. + /// Thread is not allowed to die with non zero DelayAbortCount. + /// + /// + [Inline] + [NoHeapAllocation] + public void DecrementDelayAbortCount() + { + // AbortState is bit 0-6 of byte 1 of the data + // It must not underflow below 0 + VTable.Assert(((UInt32)Data & 0x7F00) != 0); + Data -= 0x100; + } + + /// + /// + /// Get or set whether the thread is aborted + /// + /// + public bool IsAborted + { + [Inline] + [NoHeapAllocation] + get + { + // IsAborted is bit 7 of byte 1 of the data + return ((UInt32)Data & 0x8000) != 0; + } + [Inline] + [NoHeapAllocation] + set + { + // IsAborted is bit 7 of byte 1 of the data + if (value) { + Data |= 0x0000000000008000; + } + else { + Data &= 0xFFFFFFFFFFFF7FFFUL; + } + } + } + + /// + /// + /// A reference counter of how many times the thread is suspended. Threads + /// with non zero FreezeCount are not allowed to run. + /// + /// + public UInt16 FreezeCount + { + [Inline] + [NoHeapAllocation] + get + { + // AbortState is byte 2 and 3 of the data + return (UInt16)(Data >> 16); + } + [Inline] + [NoHeapAllocation] + set + { + // AbortState is byte 2 and 3 of the data + Data &= 0xFFFFFFFF0000FFFFUL; + Data |= (((UInt32)value) << 16); + } + } + + /// + /// + /// Filed represents an id of WaitHandle that performed unblock operation + /// + /// + public int UnblockedBy + { + [Inline] + [NoHeapAllocation] + get + { + // UnblockedBy is byte 4 to 7 of the data + return (int)(UInt32)(Data >> 32); + } + [Inline] + [NoHeapAllocation] + set + { + // UnblockedBy is byte 4 to 7 of the data + Data &= 0x00000000FFFFFFFFUL; + Data |= (((UInt64)(UInt32)value) << 32); + } + } + }; + + /// + /// + /// Thread type + /// + /// + public enum ThreadType + { + Unknown, + Idle, + Scavenger + }; + +#if PAGING + /// For use when we temporarily switch to a different domain + private ProtectionDomain tempDomain; +#endif + + /// + /// This manager is responsible for storing the global data that is shared amongst all + /// the thread local stores. + /// + static private LocalDataStoreMgr localDataStoreMgr; + + /// A maximum number of threads , must be power of 2 >=64 + internal const int maxThreads = 1024; + + internal const int NoAffinity = -1; + + /// A global counter to generate next thread index + private static int threadIndexGenerator; + + /// Thread is inside of context switch + [AccessedByRuntime("referenced from halidt.asm")] + internal int insideOfContextSwitchDepth = 0; + + /// MultiUseWord (object header) head + internal UIntPtr externalMultiUseObjAllocListHead; + + /// MultiUseWord (object header) tail + internal UIntPtr externalMultiUseObjAllocListTail; + + /// Thread index inside of a prcess + internal int processThreadIndex; + + /// Global thread index + internal int threadIndex; + + /// A method to start a thread + private ThreadStart threadStart; + + /// A state of a thread from scheduler point of view + private SchedulerInfo schedulerInfo; + + /// Previous scheduler info as seen by record event, used for debugging purposes + private SchedulerInfo prevSchedulerInfo; + + /// Thread state from dispatcher point of view + private ProcessorDispatcher dispatcher; + + /// Indicates if a thread is executing in a nonPreemptible region + private int nonPreemptibleRegionCount; + + /// Thread's event used by monitor + private AutoResetEvent autoEvent; + + /// Indicates if thread's gc event has been signaled + private int gcEventSignaled; + + /// Thread's join event + private ManualResetEvent joinEvent; + + /// This is needed for Bartok + internal Thread blockingCctorThread; + + /// Preallocated services request object + internal ThreadLocalServiceRequest localServiceRequest; + + /// A timer indicating till when thread is blocked + [AccessedByRuntime("referenced from c++")] + internal SchedulerTime blockedUntil; + + /// An entry used by scheduler queues to manipulate wit thread + [AccessedByRuntime("referenced from c++")] + internal ThreadEntry schedulerEntry; + + /// An entry used by timer quieue to manipulate with thread + internal ThreadEntry timerEntry; + + /// An entry used by wait handle to put thread on deferred queue during wakeup + internal ThreadEntry deferredEntry; + + /// Thread context + [AccessedByRuntime("referenced from c++")] + internal ThreadContext context; + + /// Thread's process + [AccessedByRuntime("referenced from c++")] + internal Process process; + + /// Thread's handle + internal ThreadHandle threadHandle; + + /// Thread local value - single place for local storage + internal UIntPtr threadLocalValue; + + /// + /// Most recently thrown exception object that the thread + /// did not catch at all (i.e. that propagated to the bottom + /// of the stack without encountering an appropriate catch clause). + /// + internal Exception lastUncaughtException; + + /// + /// Monitor link list of threads. Remove these and Monitor as soon as + /// stack is out of kernel. + /// + internal Thread nextThread; + + /// + private Object exceptionStateInfo; + + /// Global thread table + internal static Thread[] threadTable; + + /// A lock protecting access to global trhead table + private static SpinLock threadTableLock; + + /// Thread local storage + private static LocalDataStore localDataStore; + + /// First thread in Singularity + internal static Thread initialThread; + + /// Spinlock protecting thread state + private SpinLock threadLock; + + /// An exception object to stop process + private static ProcessStopException + processStopException; + + /// The processor ID this thread is running on or ran on last time + internal int Affinity = NoAffinity; + +#if false + /// scheduler specific data + internal ThreadScheduleData ScheduleData; +#endif + + /// Thread type + internal ThreadType type = ThreadType.Unknown; + + /// SpinLock ranking masks + internal int spinLockRankMask; + + /// A number of spinlocks held by a thread + internal int numberOfSpinlocksHeld = 0; } } + diff --git a/base/Kernel/System/Threading/ThreadLocalServiceRequest.cs b/base/Kernel/System/Threading/ThreadLocalServiceRequest.cs index fb4610f..abc0cb8 100644 --- a/base/Kernel/System/Threading/ThreadLocalServiceRequest.cs +++ b/base/Kernel/System/Threading/ThreadLocalServiceRequest.cs @@ -10,6 +10,7 @@ // using Microsoft.Singularity; +using System.Runtime.CompilerServices; namespace System.Threading { @@ -78,14 +79,14 @@ namespace System.Threading process.Join(); } - public static void CurrentThreadStopped() + [NoHeapAllocation] + [CLSCompliant(false)] + public static void ThreadStopped(Thread threadToStop) { - ThreadLocalServiceRequest req = Thread.CurrentThread.localServiceRequest; + ThreadLocalServiceRequest req = threadToStop.localServiceRequest; req.tag = ThreadLocalServiceRequestTag.ThreadStopped; - req.targetThread = Thread.CurrentThread; + req.targetThread = threadToStop; ServiceThread.Request(req); - // Do not wait for requestFinished; let the current thread die instead. - // (This is the last request this thread ever makes.) } protected override internal void Service() diff --git a/base/Kernel/System/Threading/ThreadState.cs b/base/Kernel/System/Threading/ThreadState.cs deleted file mode 100644 index b111b28..0000000 --- a/base/Kernel/System/Threading/ThreadState.cs +++ /dev/null @@ -1,35 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================================= -** -** Class: ThreadState -** -** -** Purpose: Enum to represent the different thread states -** -** Date: Feb 2, 2000 -** -=============================================================================*/ - -namespace System.Threading { - - //| - public enum ThreadState - { - /*========================================================================= - ** Constants for thread states. - =========================================================================*/ - //| - Running = 0, - //| - Unstarted = 1, - //| - Stopping = 2, - //| - Stopped = 3, - Suspended = 4, - } -} diff --git a/base/Kernel/System/Threading/ThreadStateException.cs b/base/Kernel/System/Threading/ThreadStateException.cs deleted file mode 100644 index dd453a9..0000000 --- a/base/Kernel/System/Threading/ThreadStateException.cs +++ /dev/null @@ -1,38 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================================= -** -** Class: ThreadStateException -** -** -** Purpose: An exception class to indicate that the Thread class is in an -** invalid state for the method. -** -** Date: April 1, 1998 -** -=============================================================================*/ - -namespace System.Threading { - using System; - - //| - public class ThreadStateException : SystemException { - //| - public ThreadStateException() - : base("Arg_ThreadStateException") { - } - - //| - public ThreadStateException(String message) - : base(message) { - } - - //| - public ThreadStateException(String message, Exception innerException) - : base(message, innerException) { - } - } -} diff --git a/base/Kernel/System/Threading/Timeout.cs b/base/Kernel/System/Threading/Timeout.cs deleted file mode 100644 index ce73b5e..0000000 --- a/base/Kernel/System/Threading/Timeout.cs +++ /dev/null @@ -1,25 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== - -namespace System.Threading { - using System.Threading; - using System; - // A constant used by methods that take a timeout (Object.Wait, Thread.Sleep - // etc) to indicate that no timeout should occur. - // - // @todo: this should become an enum. - //| - public sealed class Timeout - { - private Timeout() - { - } - - //| - public const int Infinite = -1; - } - -} diff --git a/base/Kernel/System/Threading/WaitHandle.cs b/base/Kernel/System/Threading/WaitHandle.cs index f17e8b3..7d7e7cc 100644 --- a/base/Kernel/System/Threading/WaitHandle.cs +++ b/base/Kernel/System/Threading/WaitHandle.cs @@ -4,144 +4,842 @@ // // ==--== -namespace System.Threading { - using System; - using System.Threading; - using System.Runtime.CompilerServices; - using System.Collections; - using Microsoft.Singularity; - using Microsoft.Singularity.Io; - using Microsoft.Singularity.Scheduling; +using System; +using System.Threading; +using System.Runtime.CompilerServices; +using System.Collections; +using Microsoft.Singularity; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Scheduling; - [CLSCompliant(false)] - public enum WaitHandleEvent : ushort - { - WaitDone = 10, - } - - //| +namespace System.Threading +{ + /// + /// + /// Base class for all synchronization objects such as events and mutex + /// + /// [NoCCtor] [CLSCompliant(false)] - public abstract class WaitHandle + public abstract class WaitHandle : WaitHandleBase { - // These two fields should only be accessed with interrupts off. - protected volatile int signaled; - protected volatile Thread owner; // Last thread to be notified by this signal. - protected ThreadQueue queue; - protected int id; // unique ID for this waithandle - private static int idGenerator; - - // This field is an array of length 1 containing 'this'. - // It is used to avoid allocation when calling WaitAny from WaitOne. - internal WaitHandle[] singleHandle; - - //| - public const int WaitTimeout = -1; - - //| - protected WaitHandle(int initialState) + /// + /// + /// Constructor + /// + /// + /// Initial state of an handle + /// + /// Value represents a state of a handle when wait satisfied right a way + /// + /// The spin lock type of the wait handle + /// + protected WaitHandle( + SignalState initialState, + SignalState signalStateAfterImediateWait, + SpinLock.Types spinLockType) + : base(initialState, signalStateAfterImediateWait) { - id = ++idGenerator; - owner = null; - queue = new ThreadQueue(this); - signaled = initialState; - singleHandle = new WaitHandle [1] { this }; + this.singleHandle = new WaitHandle[1] { this }; + // Initialize waithandle spinlock + this.myLock = new SpinLock(spinLockType); } - // Called with dispatch lock held and interrupts off. - [NoHeapAllocation] - protected bool NotifyOne() - { - Kernel.Waypoint(201); - - ThreadEntry entry = queue.DequeueHead(); - if (entry != null) { - //Kernel.Waypoint(202); - owner = entry.Thread; - Monitoring.Log(Monitoring.Provider.WaitHandle, - (ushort)WaitHandleEvent.WaitDone, 0, - (uint)this.id, (uint)owner.threadIndex, - 0, 0, 0); - entry.Thread.WaitDone(entry); - //Kernel.Waypoint(203); - return true; - } - return false; - } - - // Called with dispatch lock held and interrupts off. - [NoHeapAllocation] - protected bool NotifyAll() - { - Kernel.Waypoint(204); - bool notified = false; - - for (ThreadEntry entry; (entry = queue.DequeueHead()) != null;) { - //Kernel.Waypoint(205); - owner = entry.Thread; - Monitoring.Log(Monitoring.Provider.WaitHandle, - (ushort)WaitHandleEvent.WaitDone, 0, - (uint)this.id, (uint)owner.threadIndex, - 0, 0, 0); - entry.Thread.WaitDone(entry); - //Kernel.Waypoint(206); - notified = true; - } - //Kernel.Waypoint(207); - return notified; - } - - // Called with interrupts off. - internal abstract bool AcquireOrEnqueue(ThreadEntry entry); - - /// Used to return a thread which can make progress on this object. - [NoHeapAllocation] - internal abstract Thread GetBeneficiary(); - - //| + /// + /// + /// Wait on a handle with a specified time out. + /// + /// + /// Time out + /// public bool WaitOne(TimeSpan timeout) { - return Thread.CurrentThread.WaitAny(singleHandle, 1, timeout) != WaitTimeout; + return (WaitAny(singleHandle, 1, timeout) != WaitTimeout); } - //| + /// + /// + /// Wait on a handle with a specified time out. + /// + /// + /// Time out + /// internal bool WaitOne(SchedulerTime stop) { - return Thread.CurrentThread.WaitAny(singleHandle, 1, stop) != WaitTimeout; + return (WaitAny(singleHandle, 1, stop) != WaitTimeout); } - //| + /// + /// + /// Wait on a handle without time out + /// + /// public bool WaitOne() { return WaitOne(SchedulerTime.MaxValue); } - //| - public static int WaitAny(WaitHandle[] waitHandles, - TimeSpan timeout) + /// + /// + /// Wait on a set of handles until one of them becomes signaled with a specified time out. + /// + /// + /// Wait handles to wait on + /// Time out + /// + public static int WaitAny(WaitHandle[] waitHandles, TimeSpan timeout) { - return Thread.CurrentThread.WaitAny(waitHandles, waitHandles.Length, timeout); + SchedulerTime stop = SchedulerTime.Now + timeout; + return WaitAny(waitHandles, waitHandles.Length, stop); } - //| - public static int WaitAny(WaitHandle[] waitHandles, - SchedulerTime stop) + /// + /// + /// Wait on a set of handles until one of them becomes signaled with a specified time out. + /// + /// + /// Wait handles to wait on + /// Time out + /// + public static int WaitAny(WaitHandle[] waitHandles, SchedulerTime stop) { - return Thread.CurrentThread.WaitAny(waitHandles, waitHandles.Length, stop); + return WaitAny(waitHandles, waitHandles.Length, stop); } - //| - public static int WaitAny(WaitHandle[] waitHandles, - int waitHandlesCount, - SchedulerTime stop) - { - return Thread.CurrentThread.WaitAny(waitHandles, waitHandlesCount, stop); - } - - //| + /// + /// + /// Wait on a set of handles until one of them becomes signaled without timeout. + /// + /// + /// Wait handles to wait on + /// public static int WaitAny(WaitHandle[] waitHandles) { return WaitAny(waitHandles, SchedulerTime.MaxValue); } + + /// + /// + /// Wait on a set of handles until one of them becomes signaled with a specified time out. + /// + /// + /// Wait handles to wait on + /// A number of wait handles to wait on + /// Time out + /// + public int WaitAny( + WaitHandle[] waitHandles, + int waitHandlesCount, + TimeSpan timeout) + { + SchedulerTime stop; + + if (timeout != TimeSpan.MaxValue) { + stop = SchedulerTime.Now + timeout; + } + else { + stop = SchedulerTime.MaxValue; + } + return WaitAny(waitHandles, waitHandlesCount, stop); + } + + /// + /// + /// Wait on a set of handles until one of them becomes signaled with a specified time out. + /// + /// + /// Wait handles to wait on + /// A number of wait handles to wait on + /// Time out + /// + public static int WaitAny( + WaitHandle[] waitHandles, + int waitHandlesCount, + SchedulerTime stop) + { + // Retrieve current thread information + Thread currentThread = Thread.CurrentThread; + Thread target = null; + int unblockedBy; + ThreadEntry[] entries = currentThread.GetWaitEntries( + waitHandlesCount); + + // Before we attempting to enqueue ourselves into the wait queues make sure + // we disable abort + currentThread.DelayStop(true); + + // Perepare for a wait - enqueue ourselves into every wait handle + unblockedBy = PreWaitAnyInternal(currentThread, + waitHandles, + entries, + waitHandlesCount); + + // If we are in the process of blocking: Block + if (UninitWait == unblockedBy) { + // Allow thread to be aborted at this point + currentThread.DelayStop(false); + + // Update thread WaitFor information: Indicate every one that we waiting - + // scheduler ones wakes us up is responsible for cleaning up wait information + // by using ResetWaitInfo call + //currentThread.UpdateWaitInfo (waitHandles, + // waitHandlesCount, + // entries, + // stop); + + // Write out log record + Monitoring.Log(Monitoring.Provider.Thread, + (ushort)ThreadEvent.WaitAny, 0, + (uint)stop.Ticks, (uint)(stop.Ticks >> 32), + (uint)currentThread.threadIndex, 0, 0); + + + // Let scheduler know that we are blocking + Kernel.TheScheduler.OnThreadBlocked(currentThread, stop); + + // Our thread is about to run so we can disassociate it from wait handles + PostWaitAnyInternal(currentThread, + waitHandles, + entries, + waitHandlesCount); + + // Thread has the unblocked information + unblockedBy = currentThread.UnblockedBy; + } + + // Assert post condition: unblocked by can't be uninitialized + VTable.Assert(unblockedBy != WaitHandle.UninitWait); + + // If there are wait handles and we were unblocked by not the timeout + if (waitHandles != null && unblockedBy >= 0 + && unblockedBy < waitHandlesCount) { + // Complete wait + waitHandles[unblockedBy].CompleteWait (currentThread); + + // When we were signalged delay abort has been set - now we can turn it off + // For mutex complete wait will add delay abort. It will remain on until + // mutex is released + currentThread.DelayStop(false); + } + + // Make sure that we pay attention to abort + currentThread.ProcessAbortIfRequired(); + + return unblockedBy; + } + + /// + /// + /// Signal wait handle by waking up a single waiter or if there are no waiters + /// set handle to specified stated + /// + /// + /// + /// Set the wait handle into specified state if no waiters present + /// + /// + [NoHeapAllocation] + protected void SignalOne(SignalState signalStateIfNoWaiters) + { + // Single a waiting thread and put it in the deferredWakeupQueue + ThreadQueueStruct deferredWakeupQueue = SignalOneWithNoWakeup(signalStateIfNoWaiters); + + // Wakeup a waiter if we need to + WakeupOneWaiter(deferredWakeupQueue); + } + + /// + /// + /// Signal wait handle by waking up all waiters. Set wait handle into specified state + /// in both cases when waiters present and not + /// + /// + /// + /// Set the wait handle into specified state if no waiters present + /// + /// + /// + /// Set the wait handle into specified state if waiter are present + /// + /// + [NoHeapAllocation] + protected void SignalAll( + SignalState signalStateIfNoWaiters, + SignalState signalStateIfWaiters) + { + // Single the waiting threads and put them in the deferredWakeupQueue + ThreadQueueStruct deferredWakeupQueue = SignalAllWithNoWakeup( + signalStateIfNoWaiters, + signalStateIfWaiters); + + // Wakeup a waiter if we need to + WakeupAllWaiters(deferredWakeupQueue); + } + + /// + /// This field is an array of length 1 containing 'this'. + /// It is used to avoid allocation when calling WaitAny from WaitOne. + /// + private WaitHandle[] singleHandle; + } + + /// + /// + /// Base class for all synchronization objects such as events and mutex + /// + /// + [NoCCtor] + [CLSCompliant(false)] + public abstract class WaitHandleBase + { + /// Time out constant + public const int WaitTimeout = -1; + + /// Time out constant + public const int UninitWait = -2; + + /// Infinite time out + public const int InfinityTimeout = -1; + + /// GC uses this for unblocked by information + public const int UnblockedByGC = -3; + + /// Unblocked by abort + public const int UnblockedByAbort = -4; + + /// The state of the handle, signaled or unsignaled + protected enum SignalState : int + { + Unsignaled = 0, + Signaled = 1, + } + + /// + /// + /// Constructor + /// + /// + /// Initial state of an handle + /// + /// Value represents a state of a handle when wait satisfied right a way + /// + /// + protected WaitHandleBase( + SignalState initialState, + SignalState signalStateAfterImediateWait) + { + this.id = ++idGenerator; + this.owner = null; + this.signaledQueue = new ThreadQueue(this); + this.waitingQueue = new ThreadQueue(this); + this.signaled = initialState; + this.signalStateAfterImediateWait = signalStateAfterImediateWait; + } + + /// + /// + /// Perform the work to signal wait handle or if there are no waiters + /// set handle to specified stated. Put the signaled waiter to a deferred + /// wakeup queue if there is any and return the queue. + /// + /// + /// + /// Set the wait handle into specified state if no waiters present + /// + /// + [Inline] + [NoHeapAllocation] + protected ThreadQueueStruct SignalOneWithNoWakeup(SignalState signalStateIfNoWaiters) + { + Thread currentThread = Thread.CurrentThread; + ThreadEntry nextThreadEntry; + ThreadEntry waitDoneThreadEntry; + int unblockerId = UninitWait; + int entryId = UninitWait; + + // The method below should allocate struct on the stack!! + ThreadQueueStruct deferredWakeupQueue = new ThreadQueueStruct(); + + bool shouldEnable = Processor.DisableInterrupts(); + myLock.Acquire(currentThread); + try { + // Acquire lock. If you hit assert during acquire, and the code is handling + // interrupt, you may need to use interrupt aware synchronization classes. + // See the comment for InterruptAwareAutoResetEvent, InterruptAwareManualResetEvent, + // and InterruptAwareMutex for details. + + ThreadEntryEnum threadEntryEnum = new ThreadEntryEnum(this.waitingQueue.Head); + nextThreadEntry = threadEntryEnum.GetNext(); + + // Waitdone can move entry from one queue to another. Because of that we have + // to be extremly careful. We move enumerator always one step ahead. + if (nextThreadEntry != null) { + + do { + waitDoneThreadEntry = nextThreadEntry; + entryId = waitDoneThreadEntry.Id; + + // Move enumerator one step further - we have to do it before we call + // WaitDone otherwise we might be enumerating wrong list because entry + // can move lists during WaitDone call + nextThreadEntry = threadEntryEnum.GetNext(); + + // Perform actual signal + unblockerId = WaitDone(waitDoneThreadEntry, + entryId, + ref deferredWakeupQueue); + + // We will need to loop until we have an entry that we succefully signalled + } while (entryId != unblockerId && nextThreadEntry != null); + } + + // If we succesfully process signal mark state appropriately + if (unblockerId != UninitWait && (entryId == unblockerId)) { + + // Make sure that state is no longer signalled + this.signaled = SignalState.Unsignaled; + } + else { + // Set state to specified by caller + signaled = signalStateIfNoWaiters; + } + } + finally { + // Release lock + myLock.Release(); + + // Reenable interrupts + Processor.RestoreInterrupts(shouldEnable); + } + + return deferredWakeupQueue; + } + + /// + /// + /// Wakeup a single waiter in the deferred queue if there is one. + /// + /// + /// The queue that contains the waiter thread + /// + [Inline] + [NoHeapAllocation] + protected static void WakeupOneWaiter(ThreadQueueStruct deferredWakeupQueue) + { + // Wakeup a waiter if we need to + if (!deferredWakeupQueue.IsEmpty()) { + + // Retrieve thread from deferred queue + Thread threadToWakeup = deferredWakeupQueue.DequeueHead().Thread; + + // Add the unblocked thread to the scheduler's runnable queue + Kernel.TheScheduler.OnThreadUnblocked(threadToWakeup); + + // At this point queue should be empty! + VTable.Assert(deferredWakeupQueue.IsEmpty()); + } + } + + /// + /// + /// Perform the work to signal all waiters and then set handle to specified stated. + /// Put the signaled waiters to a deferred wakeup queue if there is any and + /// return the queue. + /// + /// + /// + /// Set the wait handle into specified state if no waiters present + /// + /// + /// + /// Set the wait handle into specified state if waiter are present + /// + /// + [NoHeapAllocation] + protected ThreadQueueStruct SignalAllWithNoWakeup( + SignalState signalStateIfNoWaiters, + SignalState signalStateIfWaiters) + { + bool notified = false; + Thread currentThread = Thread.CurrentThread; + ThreadEntry waitDoneThreadEntry; + ThreadEntry nextThreadEntry; + ThreadQueueStruct deferredWakeupQueue = new ThreadQueueStruct(); + int unblockerId = UninitWait; + int numberOfUnblockedWaiters = 0; + int entryId = UninitWait; + + // Acquire lock. If you hit assert during acquire, and the code is handling + // interrupt, you may need to use interrupt aware synchronization classes. + // See the comment for InterruptAwareAutoResetEvent, InterruptAwareManualResetEvent, + // and InterruptAwareMutex for details. + bool shouldEnable = Processor.DisableInterrupts(); + myLock.Acquire(currentThread); + try { + ThreadEntryEnum threadEntryEnum = new ThreadEntryEnum(this.waitingQueue.Head); + nextThreadEntry = threadEntryEnum.GetNext(); + + // Waitdone can move entry from one queue to another. Because of that we have + // to be extremly careful. We move enumerator always one step ahead. + if (nextThreadEntry != null) { + + do { + waitDoneThreadEntry = nextThreadEntry; + entryId = waitDoneThreadEntry.Id; + + // Move enumerator one step further - we have to do it before we call + // WaitDone otherwise we might be enumerating wrong list because entry + // can move lists during WaitDone call + nextThreadEntry = threadEntryEnum.GetNext(); + + // We are ready to call waitdone + unblockerId = WaitDone(waitDoneThreadEntry, + entryId, + ref deferredWakeupQueue); + + // Remember if we succesfully signalled someone + if (unblockerId == entryId) { + numberOfUnblockedWaiters++; + } + + } while (nextThreadEntry != null); + } + + // If we succesfully signalled someone, record state accordingly + if (numberOfUnblockedWaiters > 0) { + + // Update signal state in case when waiters are present + this.signaled = signalStateIfWaiters; + } + else { + // Don't forget to update state when waiters are not present + this.signaled = signalStateIfNoWaiters; + } + } + finally { + // Release lock + myLock.Release(); + + // Reenable interrupts + Processor.RestoreInterrupts(shouldEnable); + } + + return deferredWakeupQueue; + } + + /// + /// + /// Wakeup all waiters in the deferred queue if there are any + /// + /// + /// The queue that contains the waiter threads + /// + [Inline] + [NoHeapAllocation] + protected static void WakeupAllWaiters(ThreadQueueStruct deferredWakeupQueue) + { + ThreadEntry entryToWakeup; + + // Unblock threads that we found we need to unblock + while ((entryToWakeup = deferredWakeupQueue.DequeueHead()) != null) { + + // Get the thread + Thread threadToWakeup = entryToWakeup.Thread; + + // Add the unblocked thread to the scheduler's runnable queue + Kernel.TheScheduler.OnThreadUnblocked(threadToWakeup); + } + } + + /// + /// + /// Depending on the state of a handle decide either to acquire handle or wait + /// + /// + /// + /// Thread entry to enqueu onto waiting queue when can't satisfy request + /// + /// + /// Id of the handle that we are trying to acquire - is used to check if thread can be unblocked + /// + /// + [NoHeapAllocation] + protected virtual bool AcquireOrEnqueue(ThreadEntry entry, int handleId) + { + bool didAcquire = true; + Thread currentThread = Thread.CurrentThread; + + // Acquire lock. If you hit assert during acquire, and the code is handling + // interrupt, you may need to use interrupt aware synchronization classes. + // See the comment for InterruptAwareAutoResetEvent, InterruptAwareManualResetEvent, + // and InterruptAwareMutex for details. + bool shouldEnable = Processor.DisableInterrupts(); + myLock.Acquire(currentThread); + try { + // If the handle is signaled and thread hasn't been unblocked yet. + if ((this.signaled == SignalState.Signaled) && + (currentThread.Unblock(handleId) == handleId)) { + + // Make signaled state to be as dictated by child class during initialization + this.signaled = this.signalStateAfterImediateWait; + } + else { + // Enqueue entry into wating queue: + this.waitingQueue.EnqueueHead(entry); + + // We couldn't acquire object set return value properly + didAcquire = false; + } + } + finally { + // Release lock + myLock.Release(); + + // Reenable interrupts + Processor.RestoreInterrupts(shouldEnable); + } + + return didAcquire; + } + + /// + /// + /// Associate a thread with wait handles if one of the waits satisfied return + /// waithandle id - actual unblocker. if none of the states satisfied return UninitWait + /// indicating that thread has to proceede with blocking + /// + /// + protected static int PreWaitAnyInternal( + Thread currentThread, + WaitHandleBase[] waitHandles, + ThreadEntry[] entries, + int waitHandlesCount) + { + int marked; + int released; + int unblockedBy = UninitWait; + + VTable.Assert(currentThread.ThreadState == ThreadState.Running); + + // Prepare thread for blocking - Unblock call might be called from + // either AcquireOrEnqueue or from WaitDone + currentThread.PrepareForBlocking(); + + // Enqueue on all of the non-signaled handles. + for (marked = 0; marked < waitHandlesCount; marked++) { + + // Initialize entry id + entries[marked].Id = marked; + + // Attempt to wait on a handler + if (waitHandles[marked].AcquireOrEnqueue(entries[marked], marked)) { + + // Our wait succeeded, remove ourselves from the wait queues + for (released = 0; released < marked; released++) { + waitHandles[released].Remove(entries[released]); + } + + // Remember unblockedBy + unblockedBy = currentThread.UnblockedBy; + break; + } + } + + return unblockedBy; + } + + /// + /// + /// Post wait is to dissasociate thread from all handlers + /// + /// + protected static void PostWaitAnyInternal( + Thread currentThread, + WaitHandleBase[] waitHandles, + ThreadEntry[] entries, + int waitHandlesCount) + { + ThreadState state; + int handlerIdx; + int unblockedBy = WaitHandle.WaitTimeout; + int resultUnblockedBy = WaitHandle.WaitTimeout; + + VTable.Assert(currentThread.ThreadState == ThreadState.Running); + + // Dequeue a thread from all hadndles it was waiting on + for (handlerIdx = 0; handlerIdx < waitHandlesCount; handlerIdx++) { + // Dissaociate handler from entry + waitHandles[handlerIdx].Remove(entries[handlerIdx]); + } + } + + /// + /// + /// This method is called when a synchronization object completes the wait for + /// thread. + /// + /// + /// Thread entry to signal + /// Id of wait handle + /// Deferred queue to use for deferred wakeup + /// + [NoHeapAllocation] + private int WaitDone( + ThreadEntry entry, + int handleId, + ref ThreadQueueStruct deferredQueue) + { + ThreadState state; + int handledIdUnblocked; + + // Assert preconditions: We assume that queues are stable - spinlock is held when + // this method is called + VTable.Assert(myLock.IsHeldBy(Thread.CurrentThread)); + + // Indicate that thread migth be given owrneship of the object so that it can't be aborted + entry.Thread.DelayStop(true); + + //Attempt to unblock thread, if we fail it means that thread has already timed out + // If thread has timed out don't move it to signaled queue + if ((handledIdUnblocked = entry.Thread.Unblock(handleId)) == handleId) { + + // The signal is good-we can take a thread from non-signaled queue and + // move it to signaled + MoveEntryToSignalQueue(entry); + + // If thread state is blocked - we will be responsible for waking it up + if (entry.Thread.ShouldCallSchedulerUnBlock(handleId)) { + // Enqueue entry onto deferred unblock queue. We will call unblock + // once we are done with processing signal and we released the lock + deferredQueue.EnqueueTail(entry.Thread.deferredEntry); + } + } + else { + // We haven't granted ownership to the thread so that we need to restore its + // delay abort status + entry.Thread.DelayStop(false); + } + + return handledIdUnblocked; + } + + /// + /// + /// This method is called when a synchrnoization object completes the wait for + /// thread. + /// + /// + /// Thread entry to move from unsignaled queue to singlaed + /// + [NoHeapAllocation] + private void MoveEntryToSignalQueue(ThreadEntry entry) + { + // Assert preconditions: Entry should be enqueued to nonsignaled queue protectiong + // spinlock has to be taken + VTable.Assert(waitingQueue.IsEnqueued(entry)); + VTable.Assert(myLock.IsHeldBy(Thread.CurrentThread)); + + // Remove thread from waiting queue + waitingQueue.Remove(entry); + + // Insert thread into signaled queue + signaledQueue.EnqueueHead(entry); + } + + /// + /// + /// Remove entry from the wait handle + /// + /// + /// + /// Entry to remove + /// + /// + [NoHeapAllocation] + private void Remove(ThreadEntry entry) + { + Thread currentThread = Thread.CurrentThread; + + // Acquire lock. If you hit assert during acquire, and the code is handling + // interrupt, you may need to use interrupt aware synchronization classes. + // See the comment for InterruptAwareAutoResetEvent, InterruptAwareManualResetEvent, + // and InterruptAwareMutex for details. + bool shouldEnable = Processor.DisableInterrupts(); + myLock.Acquire(currentThread); + try { + // Assert preconditions: entry should be on a either non signaled queue or if it is + // on signaled queue thread's unblocked Id should be equal to entry's Id + // We can run this assert only while spinlock is held because first we unblock thread + // and then moving it from one queue to another. We do both of these operations while + // holding spinlock. It is possible for thread to start running once it was unblocked. + // When it reaches this method it is possible that it hasn't been moved to signaled queue yet + // If that happens and we try to assert without holding spinlock assert will fire. + VTable.Assert(entry.queue == this.waitingQueue || + (entry.queue == this.signaledQueue && + entry.Id == currentThread.UnblockedBy)); + + + // Remove entry from the queue + entry.RemoveFromQueue(); + } + finally { + // Release lock + myLock.Release(); + + // Reenable interrupts + Processor.RestoreInterrupts(shouldEnable); + } + } + + /// + /// + /// Return owner of a handle + /// + /// + /// + /// Consider removing this method + /// + [NoHeapAllocation] + internal Thread GetBeneficiary() + { + return owner; + } + + /// + /// + /// Complete wait - used by mutex to record ownership + /// + /// + /// Thread owner + /// + [NoHeapAllocation] + protected virtual void CompleteWait(Thread ownerThread) + { + return; + } + + /// State of the handler + private volatile SignalState signaled; + + /// Signal state after wait - policy of the child class + protected SignalState signalStateAfterImediateWait; + + /// An owner of the handler + protected volatile Thread owner; + + /// A waiting queue + protected ThreadQueue waitingQueue; + + /// A signaled queue + protected ThreadQueue signaledQueue; + + /// A unique id of wait handler + protected int id; + + /// Uniqifier + private static int idGenerator; + + /// Lock to protect queues + protected SpinLock myLock; } } diff --git a/base/Kernel/System/Type.cs b/base/Kernel/System/Type.cs deleted file mode 100644 index fcf3e71..0000000 --- a/base/Kernel/System/Type.cs +++ /dev/null @@ -1,389 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// -// Type is the root of all reflection and the Object that represents -// a type inside the system. Type is an abstract base class that allows multiple -// implementations. The system will always provide the subclass __RuntimeType. -// In Reflection all of the __RuntimeXXX classes are created only once per object -// in the system and support == comparisons. -// -// Date: March 98 -// -namespace System { - - using System; - using System.Reflection; - using System.Runtime.InteropServices; - using System.Runtime.CompilerServices; - using DebuggerStepThroughAttribute = System.Diagnostics.DebuggerStepThroughAttribute; - - //| - [CCtorIsRunDuringStartup] - [RequiredByBartok] - public abstract class Type - { - //| - public static readonly char Delimiter = '.'; - - // Prevent from begin created, and allow subclass - // to create. - //| - protected Type() {} - - // GetTypeCode - // This method will return a TypeCode for the passed - // type. - //| - [NoHeapAllocation] - public static TypeCode GetTypeCode(Type type) - { - if (type == null) - return TypeCode.Empty; - return type.GetTypeCodeInternal(); - } - - [NoHeapAllocation] - internal virtual TypeCode GetTypeCodeInternal() - { - Type type = this; - - if (type != type.UnderlyingSystemType) - return Type.GetTypeCode(type.UnderlyingSystemType); - - return TypeCode.Object; - } - - //| - [NoHeapAllocation] - public static Type GetTypeFromHandle(RuntimeTypeHandle handle) - { - return RuntimeType.GetTypeFromHandleImpl(handle); - } - - // Property representing the name of the Member. - //| - public abstract String Name { - [NoHeapAllocation] get; - } - - // Return the fully qualified name. The name does contain the namespace. - //| - public abstract String FullName { - get; - } - - // Return the name space of the class. - //| - public abstract String Namespace { - [NoHeapAllocation] - get; - } - - public abstract Assembly Assembly { - [NoHeapAllocation] - get; - } - - //| - public abstract String AssemblyQualifiedName { - get; - } - - // @TODO: Next integration make this method abstract - //| - public virtual int GetArrayRank() { - throw new NotSupportedException("NotSupported_SubclassOverride"); - } - - // Returns the base class for a class. If this is an interface or has - // no base class null is returned. Object is the only Type that does not - // have a base class. - //| - public abstract Type BaseType { - [NoHeapAllocation] - get; - } - - - // GetInterfaces - // This method will return all of the interfaces implemented by a - // class - //| - abstract public Type[] GetInterfaces(); - - //////////////////////////////////////////////////////////////////////////////// - // - // Attributes - // - // The attributes are all treated as read-only properties on a class. Most of - // these boolean properties have flag values defined in this class and act like - // a bit mask of attributes. There are also a set of boolean properties that - // relate to the classes relationship to other classes and to the state of the - // class inside the runtime. - // - //////////////////////////////////////////////////////////////////////////////// - - // The attribute property on the Type. - //| - public TypeAttributes Attributes { - [NoHeapAllocation] - get {return GetAttributeFlagsImpl();} - } - //| - public bool IsNotPublic { - [NoHeapAllocation] - get {return ((GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NotPublic);} - } - //| - public bool IsPublic { - [NoHeapAllocation] - get {return ((GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.Public);} - } - //| - public bool IsNestedPublic { - [NoHeapAllocation] - get {return ((GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic);} - } - //| - public bool IsNestedPrivate { - [NoHeapAllocation] - get {return ((GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPrivate);} - } - //| - public bool IsNestedFamily { - [NoHeapAllocation] - get {return ((GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily);} - } - //| - public bool IsNestedFamANDAssem { - [NoHeapAllocation] - get {return ((GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamANDAssem);} - } - //| - public bool IsNestedFamORAssem{ - [NoHeapAllocation] - get {return ((GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamORAssem);} - } - - //| - public bool IsAutoLayout { - [NoHeapAllocation] - get {return ((GetAttributeFlagsImpl() & TypeAttributes.LayoutMask) == TypeAttributes.AutoLayout);} - } - //| - public bool IsLayoutSequential { - [NoHeapAllocation] - get {return ((GetAttributeFlagsImpl() & TypeAttributes.LayoutMask) == TypeAttributes.SequentialLayout);} - } - //| - public bool IsExplicitLayout { - [NoHeapAllocation] - get {return ((GetAttributeFlagsImpl() & TypeAttributes.LayoutMask) == TypeAttributes.ExplicitLayout);} - } - - //| - public bool IsClass { - [NoHeapAllocation] - get {return ((GetAttributeFlagsImpl() & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Class && !IsSubclassOf(Type.valueType));} - } - //| - public bool IsInterface { - [NoHeapAllocation] - get {return ((GetAttributeFlagsImpl() & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface);} - } - //| - public bool IsValueType { - [NoHeapAllocation] - get {return IsValueTypeImpl();} - } - - //| - public bool IsAbstract { - [NoHeapAllocation] - get {return ((GetAttributeFlagsImpl() & TypeAttributes.Abstract) != 0);} - } - //| - public bool IsSealed { - [NoHeapAllocation] - get {return ((GetAttributeFlagsImpl() & TypeAttributes.Sealed) != 0);} - } - //| - public bool IsEnum { - [NoHeapAllocation] - get {return IsSubclassOf(Type.enumType);} - } - //| - public bool IsSpecialName { - [NoHeapAllocation] - get {return ((GetAttributeFlagsImpl() & TypeAttributes.SpecialName) != 0);} - } - //| - public bool IsImport { - [NoHeapAllocation] - get {return ((GetAttributeFlagsImpl() & TypeAttributes.Import) != 0);} - } - - //| - public bool IsAnsiClass { - [NoHeapAllocation] - get {return ((GetAttributeFlagsImpl() & TypeAttributes.StringFormatMask) == TypeAttributes.AnsiClass);} - } - //| - public bool IsUnicodeClass { - [NoHeapAllocation] - get {return ((GetAttributeFlagsImpl() & TypeAttributes.StringFormatMask) == TypeAttributes.UnicodeClass);} - } - //| - public bool IsAutoClass { - [NoHeapAllocation] - get {return ((GetAttributeFlagsImpl() & TypeAttributes.StringFormatMask) == TypeAttributes.AutoClass);} - } - - // These are not backed up by attributes. Instead they are implemented - // based internally. - //| - public bool IsArray { - [NoHeapAllocation] - get {return IsArrayImpl();} - } - - //| - public bool IsPrimitive { - [NoHeapAllocation] - get {return IsPrimitiveImpl();} - } - //| - public bool HasElementType { - [NoHeapAllocation] - get {return HasElementTypeImpl();} - } - - // Protected routine to determine if this class represents a value class - //| - [NoHeapAllocation] - protected virtual bool IsValueTypeImpl() { - Type type = this; - if (type == Type.valueType || type == Type.enumType) { - return false; - } - return IsSubclassOf(Type.valueType); - } - - // Protected routine to get the attributes. - //| - [NoHeapAllocation] - abstract protected TypeAttributes GetAttributeFlagsImpl(); - - // Protected routine to determine if this class represents an Array - //| - [NoHeapAllocation] - abstract protected bool IsArrayImpl(); - - // Protected routine to determine if this class represents a primitive type - //| - [NoHeapAllocation] - abstract protected bool IsPrimitiveImpl(); - - //| - [NoHeapAllocation] - abstract public Type GetElementType(); - - //| - [NoHeapAllocation] - abstract protected bool HasElementTypeImpl(); - - // Return the underlying Type that represents the IReflect Object. For expando object, - // this is the (Object) IReflectInstance.GetType(). For Type object it is this. - //| - public abstract Type UnderlyingSystemType { - [NoHeapAllocation] - get; - } - - // Returns true of this class is a true subclass of c. Everything - // else returns false. If this class and c are the same class false is - // returned. - // - //| - [NoHeapAllocation] - public virtual bool IsSubclassOf(Type c) - { - Type p = this; - if (p == c) { - return false; - } - while (p != null) { - if (p == c) { - return true; - } - p = p.BaseType; - } - return false; - } - - // ToString - // Print the String Representation of the Type - //| - public override String ToString() - { - return "Type: "+Name; - } - - // This method will return an array of classes based upon the array of - // types. - //| - public static Type[] GetTypeArray(Object[] args) - { - if (args == null) { - throw new ArgumentNullException("args"); - } - Type[] cls = new Type[args.Length]; - for (int i=0;i - public override bool Equals(Object o) - { - if (o == null) { - return false; - } - if (!(o is Type)) { - return false; - } - return (this.UnderlyingSystemType == ((Type) o).UnderlyingSystemType); - } - - //| - public bool Equals(Type o) - { - if (o == null) { - return false; - } - return (this.UnderlyingSystemType == o.UnderlyingSystemType); - } - - //| - public override int GetHashCode() - { - Type SystemType = UnderlyingSystemType; - if (SystemType != this) { - return SystemType.GetHashCode(); - } - return base.GetHashCode(); - } - - // private convenience data - private static readonly Type valueType = typeof(System.ValueType); - private static readonly Type enumType = typeof(System.Enum); - - public abstract Microsoft.Singularity.V1.Types.SystemType GetSystemType(); - } -} diff --git a/base/Kernel/System/TypeInitializationException.cs b/base/Kernel/System/TypeInitializationException.cs deleted file mode 100644 index 8aeb103..0000000 --- a/base/Kernel/System/TypeInitializationException.cs +++ /dev/null @@ -1,53 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================================= -** -** Class: TypeInitializationException -** -** -** Purpose: The exception class to wrap exceptions thrown by -** a type's class initializer (.cctor). This is sufficiently -** distinct from a TypeLoadException, which means we couldn't -** find the type. -** -** Date: May 10, 2000 -** -=============================================================================*/ -using System; - -namespace System { - //| - public sealed class TypeInitializationException : SystemException { - private String _typeName; - - // This exception is not creatable without specifying the - // inner exception. - private TypeInitializationException() - : base("TypeInitialization_Default") { - } - - // This is called from within the runtime. I believe this is necessary - // for Interop only, though it's not particularly useful. - private TypeInitializationException(String message) : base(message) { - } - - //| - public TypeInitializationException(String fullTypeName, Exception innerException) : base(String.Format("TypeInitialization_Type", fullTypeName), innerException) { - _typeName = fullTypeName; - } - - //| - public String TypeName - { - get { - if (_typeName == null) { - return String.Empty; - } - return _typeName; - } - } - } -} diff --git a/base/Kernel/System/ValueType.cs b/base/Kernel/System/ValueType.cs deleted file mode 100644 index 0f1e462..0000000 --- a/base/Kernel/System/ValueType.cs +++ /dev/null @@ -1,50 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================ -** -** Class: ValueType -** -** -** Purpose: Base class for all value classes. -** -** Date: January 4, 2000 -** -===========================================================*/ -namespace System { - using System; - using System.Runtime.CompilerServices; - - //| - [NoCCtor] - public abstract class ValueType { - - //| - public override bool Equals (Object obj) { - throw new Exception("System.ValueType.Equals not implemented in Bartok!"); - } - - /*=================================GetHashCode================================== - **Action: Our algorithm for returning the hashcode is a little bit complex. We look - ** for the first non-static field and get its hashcode. If the type has no - ** non-static fields, we return the hashcode of the type. We can't take the - ** hashcode of a static member because if that member is of the same type as - ** the original type, we'll end up in an infinite loop. - **Returns: The hashcode for the type. - **Arguments: None. - **Exceptions: None. - ==============================================================================*/ - //| - public override int GetHashCode() { - throw new Exception("System.ValueType.GetHashCode not implemented in Bartok!"); - } - - //| - public override String ToString() { - Type thisType = this.GetType(); - return thisType.FullName; - } - } -} diff --git a/base/Kernel/System/Version.cs b/base/Kernel/System/Version.cs deleted file mode 100644 index 4caaefd..0000000 --- a/base/Kernel/System/Version.cs +++ /dev/null @@ -1,300 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================ -** -** File: Version -** -** -** Purpose: -** -** Date: June 4, 1999 -** -===========================================================*/ -namespace System { - - using System.Runtime.CompilerServices; - using CultureInfo = System.Globalization.CultureInfo; - - // A Version object contains four hierarchical numeric components: major, minor, - // build and revision. Build and revision may be unspecified, which is represented - // internally as a -1. By definition, an unspecified component matches anything - // (both unspecified and specified), and an unspecified component is "less than" any - // specified component. - - //| - [RequiredByBartok] - public sealed class Version : ICloneable, IComparable - { - private int _Major; - private int _Minor; - private int _Build = -1; - private int _Revision = -1; - - //| - public Version(int major, int minor, int build, int revision) { - if (major < 0) - throw new ArgumentOutOfRangeException("major","ArgumentOutOfRange_Version"); - - if (minor < 0) - throw new ArgumentOutOfRangeException("minor","ArgumentOutOfRange_Version"); - - if (build < 0) - throw new ArgumentOutOfRangeException("build","ArgumentOutOfRange_Version"); - - if (revision < 0) - throw new ArgumentOutOfRangeException("revision","ArgumentOutOfRange_Version"); - - _Major = major; - _Minor = minor; - _Build = build; - _Revision = revision; - } - - //| - public Version(int major, int minor, int build) { - if (major < 0) - throw new ArgumentOutOfRangeException("major","ArgumentOutOfRange_Version"); - - if (minor < 0) - throw new ArgumentOutOfRangeException("minor","ArgumentOutOfRange_Version"); - - if (build < 0) - throw new ArgumentOutOfRangeException("build","ArgumentOutOfRange_Version"); - - - _Major = major; - _Minor = minor; - _Build = build; - } - - //| - public Version(int major, int minor) { - if (major < 0) - throw new ArgumentOutOfRangeException("major","ArgumentOutOfRange_Version"); - - if (minor < 0) - throw new ArgumentOutOfRangeException("minor","ArgumentOutOfRange_Version"); - - _Major = major; - _Minor = minor; - } - - //| - public Version(String version) { - if ((Object) version == null) - throw new ArgumentNullException("version"); - - String[] parsedComponents = version.Split(new char[] {'.'}); - int parsedComponentsLength = parsedComponents.Length; - if ((parsedComponentsLength < 2) || (parsedComponentsLength > 4)) throw new ArgumentException("Arg_VersionString"); - _Major = Int32.Parse(parsedComponents[0]); - if (_Major < 0) - throw new ArgumentOutOfRangeException("version","ArgumentOutOfRange_Version"); - - _Minor = Int32.Parse(parsedComponents[1]); - if (_Minor < 0) - throw new ArgumentOutOfRangeException("version","ArgumentOutOfRange_Version"); - - parsedComponentsLength -= 2; - if (parsedComponentsLength > 0) { - _Build = Int32.Parse(parsedComponents[2]); - if (_Build < 0) - throw new ArgumentOutOfRangeException("build","ArgumentOutOfRange_Version"); - - parsedComponentsLength--; - if (parsedComponentsLength > 0) { - _Revision = Int32.Parse(parsedComponents[3]); - if (_Revision < 0) - throw new ArgumentOutOfRangeException("revision","ArgumentOutOfRange_Version"); - } - } - } - - //| - public Version() - { - _Major = 0; - _Minor = 0; - } - - // Properties for setting and getting version numbers - //| - public int Major { - get { return _Major; } - } - - //| - public int Minor { - get { return _Minor; } - } - - //| - public int Build { - get { return _Build; } - } - - //| - public int Revision { - get { return _Revision; } - } - - //| - public Object Clone() { - Version v = new Version(); - v._Major = _Major; - v._Minor = _Minor; - v._Build = _Build; - v._Revision = _Revision; - return(v); - } - - //| - public int CompareTo(Object version) - { - if (version == null) - return 1; - - if (!(version is Version)) - throw new ArgumentException("Arg_MustBeVersion"); - - Version v = (Version) version; - - if (this._Major != v._Major) - if (this._Major > v._Major) - return 1; - else - return -1; - - if (this._Minor != v._Minor) - if (this._Minor > v._Minor) - return 1; - else - return -1; - - if (this._Build != v._Build) - if (this._Build > v._Build) - return 1; - else - return -1; - - if (this._Revision != v._Revision) - if (this._Revision > v._Revision) - return 1; - else - return -1; - - return 0; - } - - //| - public override bool Equals(Object obj) { - if (((Object) obj == null) || - (!(obj is Version))) - return false; - - Version v = (Version) obj; - // check that major, minor, build & revision numbers match - if ((this._Major != v._Major) || - (this._Minor != v._Minor) || - (this._Build != v._Build) || - (this._Revision != v._Revision)) - return false; - - return true; - } - - //| - public override int GetHashCode() - { - // Let's assume that most version numbers will be pretty small and just - // OR some lower order bits together. - - int accumulator = 0; - - accumulator |= (this._Major & 0x0000000F) << 28; - accumulator |= (this._Minor & 0x000000FF) << 20; - accumulator |= (this._Build & 0x000000FF) << 12; - accumulator |= (this._Revision & 0x00000FFF); - - return accumulator; - } - - //| - public override String ToString() { - if (_Build == -1) return(ToString(2)); - if (_Revision == -1) return(ToString(3)); - return(ToString(4)); - } - - //| - public String ToString(int fieldCount) { - switch (fieldCount) { - case 0: - return(String.Empty); - case 1: - return(String.Concat(_Major)); - case 2: - return(String.Concat(_Major,".",_Minor)); - default: - if (_Build == -1) - throw new ArgumentException(String.Format("ArgumentOutOfRange_Bounds_Lower_Upper", "0", "2"), "fieldCount"); - if (fieldCount == 3) - return( _Major + "." + _Minor + "." + _Build ); - - if (_Revision == -1) - throw new ArgumentException(String.Format("ArgumentOutOfRange_Bounds_Lower_Upper", "0", "3"), "fieldCount"); - - if (fieldCount == 4) - return( Major + "." + _Minor + "." + _Build + "." + _Revision ); - - throw new ArgumentException(String.Format("ArgumentOutOfRange_Bounds_Lower_Upper", "0", "4"), "fieldCount"); - } - } - - //| - public static bool operator ==(Version v1, Version v2) { - if ((Object) v1 == null) { - if ((Object) v2 == null) - return true; - else - return false; - } - if ((Object) v2 == null) - return false; - - return v1.Equals(v2); - } - - //| - public static bool operator !=(Version v1, Version v2) { - return !(v1 == v2); - } - - //| - public static bool operator <(Version v1, Version v2) { - if ((Object) v1 == null) - throw new ArgumentNullException("v1"); - return (v1.CompareTo(v2) < 0); - } - - //| - public static bool operator <=(Version v1, Version v2) { - if ((Object) v1 == null) - throw new ArgumentNullException("v1"); - return (v1.CompareTo(v2) <= 0); - } - - //| - public static bool operator >(Version v1, Version v2) { - return (v2 < v1); - } - - //| - public static bool operator >=(Version v1, Version v2) { - return (v2 <= v1); - } - } -} diff --git a/base/Kernel/System/_LocalDataStore.cs b/base/Kernel/System/_LocalDataStore.cs deleted file mode 100644 index be10616..0000000 --- a/base/Kernel/System/_LocalDataStore.cs +++ /dev/null @@ -1,131 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================================= -** -** Class: LocalDataStore -** -** -** Purpose: Class that stores local data. This class is used in cooperation -** with the _LocalDataStoreMgr class. -** -** Date: March 25, 1999 -** -=============================================================================*/ - -namespace System { - - using System; - - internal class LocalDataStore - { - /*========================================================================= - ** DON'T CHANGE THESE UNLESS YOU MODIFY LocalDataStoreBaseObject in vm\object.h - =========================================================================*/ - private Object[] m_DataTable; - private LocalDataStoreMgr m_Manager; - - /*========================================================================= - ** Initialize the data store. - =========================================================================*/ - public LocalDataStore(LocalDataStoreMgr mgr, int InitialCapacity) - { - if (null == mgr) - throw new ArgumentNullException("mgr"); - - // Store the manager of the local data store. - m_Manager = mgr; - - // Allocate the array that will contain the data. - m_DataTable = new Object[InitialCapacity]; - } - - /*========================================================================= - ** Retrieves the value from the specified slot. - =========================================================================*/ - public Object GetData(LocalDataStoreSlot slot) - { - Object o = null; - - // Validate the slot. - m_Manager.ValidateSlot(slot); - - // Cache the slot index to avoid synchronization issues. - int slotIdx = slot.Slot; - - if (slotIdx >= 0) - { - // Delay expansion of m_DataTable if we can - if (slotIdx >= m_DataTable.Length) - return null; - - // Retrieve the data from the given slot. - o = m_DataTable[slotIdx]; - } - - // Check if the slot has become invalid. - if (!slot.IsValid()) - throw new InvalidOperationException("InvalidOperation_SlotHasBeenFreed"); - return o; - } - - /*========================================================================= - ** Sets the data in the specified slot. - =========================================================================*/ - public void SetData(LocalDataStoreSlot slot, Object data) - { - // Validate the slot. - m_Manager.ValidateSlot(slot); - - // I can't think of a way to avoid the race described in the - // LocalDataStoreSlot finalizer method without a lock. - lock (m_Manager) - { - if (!slot.IsValid()) - throw new InvalidOperationException("InvalidOperation_SlotHasBeenFreed"); - - // Do the actual set operation. - SetDataInternal(slot.Slot, data, true /*bAlloc*/ ); - } - } - - /*========================================================================= - ** This method does the actual work of setting the data. - =========================================================================*/ - internal void SetDataInternal(int slot, Object data, bool bAlloc) - { - // We try to delay allocate the dataTable (in cases like the manager clearing a - // just-freed slot in all stores - if (slot >= m_DataTable.Length) - { - if (!bAlloc) - return; - SetCapacity(m_Manager.GetSlotTableLength()); - } - - // Set the data on the given slot. - m_DataTable[slot] = data; - } - - /*========================================================================= - ** Method used to set the capacity of the local data store. - =========================================================================*/ - private void SetCapacity(int capacity) - { - // Validate that the specified capacity is larger than the current one. - if (capacity < m_DataTable.Length) - throw new ArgumentException("Argument_ALSInvalidCapacity"); - - // Allocate the new data table. - Object[] NewDataTable = new Object[capacity]; - - // Copy all the objects into the new table. - Array.Copy(m_DataTable, NewDataTable, m_DataTable.Length); - - // Save the new table. - m_DataTable = NewDataTable; - } - } -} diff --git a/base/Kernel/System/_LocalDataStoreMgr.cs b/base/Kernel/System/_LocalDataStoreMgr.cs deleted file mode 100644 index 8eb66eb..0000000 --- a/base/Kernel/System/_LocalDataStoreMgr.cs +++ /dev/null @@ -1,295 +0,0 @@ -// ==++== -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ==--== -/*============================================================================= -** -** Class: LocalDataStoreMgr -** -** -** Purpose: Class that manages stores of local data. This class is used in -** cooperation with the LocalDataStore class. -** -** Date: March 25, 1999 -** -=============================================================================*/ -namespace System { - - using System; - using System.Collections; - - // This is a cheesy internal helper class that is used to make sure memory - // is actually being accessed and not some cached copy of a field in a - // register. - // WARNING: If we every do type analysis to eliminate virtual functions, - // this will break. - internal class LdsSyncHelper - { - internal virtual int Get(ref int slot) - { - return slot; - } - } - - // This class is an encapsulation of a slot so that it is managed in a secure fashion. - // It is constructed by the LocalDataStoreManager, holds the slot and the manager - // and cleans up when it is finalized. - //| - public sealed class LocalDataStoreSlot - { - private static LdsSyncHelper m_helper = new LdsSyncHelper(); - - private LocalDataStoreMgr m_mgr; - private int m_slot; - - // Construct the object to encapsulate the slot. - internal LocalDataStoreSlot(LocalDataStoreMgr mgr, int slot) - { - m_mgr = mgr; - m_slot = slot; - } - - // Accessors for the two fields of this class. - internal LocalDataStoreMgr Manager - { - get - { - return m_mgr; - } - } - internal int Slot - { - get - { - return m_slot; - } - } - - // This is used to make sure we are actually reading and writing to - // memory to fetch the slot (rather than possibly using a value - // cached in a register). - internal bool IsValid() - { - return m_helper.Get(ref m_slot) != -1; - } - - // Release the slot reserved by this object when this object goes away. - // There is a race condition that can happen in the face of - // resurrection where another thread is fetching values or assigning - // while the finalizer thread is here. We are counting on the fact - // that code that fetches values calls IsValid after fetching a value - // and before giving it to anyone. See LocalDataStore for the other - // half of this. We are also counting on code that sets values locks - // the manager. - //| - ~LocalDataStoreSlot() - { - int slot = m_slot; - - // This lock fixes synchronization with the assignment of values. - lock (m_mgr) - { - // Mark the slot as free. - m_slot = -1; - - m_mgr.FreeDataSlot(slot); - } - } - } - - internal class LocalDataStoreMgr - { - private const byte DataSlotOccupied = 0x01; - - private const int InitialSlotTableSize = 64; - private const int SlotTableDoubleThreshold = 512; - private const int LargeSlotTableSizeIncrease = 128; - - /*========================================================================= - ** Create a data store to be managed by this manager and add it to the - ** list. The initial size of the new store matches the number of slots - ** allocated in this manager. - =========================================================================*/ - public LocalDataStore CreateLocalDataStore() - { - // Create a new local data store. - LocalDataStore Store = new LocalDataStore(this, m_SlotInfoTable.Length); - - lock (this) { - // Add the store to the array list and return it. - m_ManagedLocalDataStores.Add(Store); - } - return Store; - } - - /*========================================================================= - * Remove the specified store from the list of managed stores.. - =========================================================================*/ - public void DeleteLocalDataStore(LocalDataStore store) - { - lock (this) { - // Remove the store to the array list and return it. - m_ManagedLocalDataStores.Remove(store); - } - } - - /*========================================================================= - ** Allocates a data slot by finding an available index and wrapping it - ** an object to prevent clients from manipulating it directly, allowing us - ** to make assumptions its integrity. - =========================================================================*/ - public LocalDataStoreSlot AllocateDataSlot() - { - lock (this) { - int i; - LocalDataStoreSlot slot; - - // Retrieve the current size of the table. - int SlotTableSize = m_SlotInfoTable.Length; - - // Check if there are any slots left. - if (m_FirstAvailableSlot < SlotTableSize) - { - // Save the first available slot. - slot = new LocalDataStoreSlot(this, m_FirstAvailableSlot); - m_SlotInfoTable[m_FirstAvailableSlot] = DataSlotOccupied; - - // Find the next available slot. - for (i=m_FirstAvailableSlot+1; i < SlotTableSize; ++i) - if (0 == (m_SlotInfoTable[i] & DataSlotOccupied)) - break; - - // Save the new "first available slot". - m_FirstAvailableSlot = i; - - // Return the slot index. - return slot; - } - - // The table is full so we need to increase its size. - int NewSlotTableSize; - if (SlotTableSize < SlotTableDoubleThreshold) - { - // The table is still relatively small so double it. - NewSlotTableSize = SlotTableSize * 2; - } - else - { - // The table is relatively large so simply increase its size by a given amount. - NewSlotTableSize = SlotTableSize + LargeSlotTableSizeIncrease; - } - - // Allocate the new slot info table. - byte[] NewSlotInfoTable = new byte[NewSlotTableSize]; - - // Copy the old array into the new one. - Array.Copy(m_SlotInfoTable, NewSlotInfoTable, SlotTableSize); - m_SlotInfoTable = NewSlotInfoTable; - - // SlotTableSize is the index of the first empty slot in the expanded table. - slot = new LocalDataStoreSlot(this, SlotTableSize); - m_SlotInfoTable[SlotTableSize] = DataSlotOccupied; - m_FirstAvailableSlot = SlotTableSize + 1; - - // Return the selected slot - return slot; - } - } - - /*========================================================================= - ** Allocate a slot and associate a name with it. - =========================================================================*/ - public LocalDataStoreSlot AllocateNamedDataSlot(String name) - { - lock (this) - { - // Allocate a normal data slot. - LocalDataStoreSlot slot = AllocateDataSlot(); - - // Insert the association between the name and the data slot number - // in the hash table. - m_KeyToSlotMap.Add(name, slot); - return slot; - } - } - - /*========================================================================= - ** Retrieve the slot associated with a name, allocating it if no such - ** association has been defined. - =========================================================================*/ - public LocalDataStoreSlot GetNamedDataSlot(String name) - { - lock (this) - { - // Lookup in the hashtable to try find a slot for the name. - LocalDataStoreSlot slot = (LocalDataStoreSlot) m_KeyToSlotMap[name]; - - // If the name is not yet in the hashtable then add it. - if (null == slot) - return AllocateNamedDataSlot(name); - - // The name was in the hashtable so return the associated slot. - return slot; - } - } - - /*========================================================================= - ** Eliminate the association of a name with a slot. The actual slot will - ** be reclaimed when the finalizer for the slot object runs. - =========================================================================*/ - public void FreeNamedDataSlot(String name) - { - lock (this) - { - // Remove the name slot association from the hashtable. - m_KeyToSlotMap.Remove(name); - } - } - - /*========================================================================= - ** Free's a previously allocated data slot on ALL the managed data stores. - =========================================================================*/ - internal void FreeDataSlot(int slot) - { - lock (this) { - // Go thru all the managed stores and set the data on the specified slot to 0. - for (int i=0; i < m_ManagedLocalDataStores.Count; i++) - { - ((LocalDataStore)m_ManagedLocalDataStores[i]).SetDataInternal( - slot, - null, - false); - } - - // Mark the slot as being no longer occupied. - m_SlotInfoTable[slot] = 0; - if (slot < m_FirstAvailableSlot) - m_FirstAvailableSlot = slot; - } - } - - /*========================================================================= - ** Return the number of allocated slots in this manager. - =========================================================================*/ - public void ValidateSlot(LocalDataStoreSlot slot) - { - // Make sure the slot was allocated for this store. - if (slot==null || slot.Manager != this) - throw new ArgumentException("Argument_ALSInvalidSlot"); - } - - /*========================================================================= - ** Return the number of allocated slots in this manager. - =========================================================================*/ - internal int GetSlotTableLength() - { - return m_SlotInfoTable.Length; - } - - private byte[] m_SlotInfoTable = new byte[InitialSlotTableSize]; - private int m_FirstAvailableSlot = 0; - private ArrayList m_ManagedLocalDataStores = new ArrayList(); - private Hashtable m_KeyToSlotMap = new Hashtable(); - } -} diff --git a/base/Kernel/abi2def.cmd b/base/Kernel/abi2def.cmd index c99535a..4b9426b 100644 --- a/base/Kernel/abi2def.cmd +++ b/base/Kernel/abi2def.cmd @@ -10,6 +10,7 @@ REM set _OBJS= set _DEFS=singularity.V999.def set _SRCH="@Struct_Microsoft_Singularity_V[0-9]*_" +set _DUMPBIN=dumpbin :parse if .==.%1 goto good @@ -37,6 +38,12 @@ if /I .%1==./def ( shift /1 goto parse ) +if /I .%1==./dumpbin ( + set _DUMPBIN=%2 + shift /1 + shift /1 + goto parse +) set _OBJS=%_OBJS% %1 shift /1 @@ -49,8 +56,14 @@ rem echo _SRCH=%_SRCH% rem goto exit echo EXPORTS > %_DEFS% +set PATH=%PATH%; + set FindStr=%SYSTEMROOT%\system32\findstr -dumpbin /symbols %_OBJS% | %FindStr% "External" | %FindStr% -v "\$" | %FindStr% "\?g_[A-z]" | %FindStr% %_SRCH% | substitute.exe ".*(?\?g_[^ ]*).*" " ${x}" >> %_DEFS% +set Sort=%SYSTEMROOT%\system32\sort +set DUMPBIN=%_DUMPBIN% + +%DUMPBIN% /symbols %_OBJS% > %SINGULARITY_ROOT%\dumpbin.txt +%DUMPBIN% /symbols %_OBJS% | %FindStr% "External" | %FindStr% -v "\$" | %FindStr% "\?g_[A-z]" | %FindStr% %_SRCH% | substitute.exe ".*(?\?g_[^ ]*).*" " ${x}" | sort>> %_DEFS% goto exit :usage diff --git a/base/Kernel/bar2win.cmd b/base/Kernel/bar2win.cmd new file mode 100644 index 0000000..f4ae99c --- /dev/null +++ b/base/Kernel/bar2win.cmd @@ -0,0 +1,64 @@ +@echo off +setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION +if .==.%1 goto usage +if .%1==./? goto usage + +set _IN= +set _OUT= +:parse +if .==.%1 goto good + +if /I ./?==./1 ( + shift /1 + goto usage +) +if /I .%1==.-? ( + shift /1 + goto usage +) +if /I .%1==./help ( + shift /1 + goto usage +) + +if .==.%_IN% ( + set _IN=%1 + shift /1 + goto parse +) + +if .==.%_OUT% ( + set _OUT=%1 + shift /1 + goto parse +) + +goto usage + +goto parse + +:good +rem echo _IN=%_IN% +rem echo _OUT=%_OUT% +rem goto exit + +echo #pragma pack(push, 1) > %_OUT% +type %_IN% | substitute.exe "static .*\);" "" | substitute.exe "(?[a-zA-Z_][a-zA-Z0-9_]*) \*\*" "PTR_TYPE /* ${x}** */" | substitute.exe "(?[a-zA-Z_][a-zA-Z0-9_]*) \*" "PTR_TYPE /* ${x}* */" | substitute.exe \bbool\b UCHAR | substitute.exe \bbyte\b UCHAR | substitute \buintptr\b UPTR_TYPE | substitute \bintptr\b SPTR_TYPE | substitute \bintPtr\b SPTR_TYPE | substitute "\buint(?[0-9]*)\b" "UINT${x}" | substitute "\bint(?[0-9]*)\b" "INT${x}" | substitute bartok_char WCHAR | substitute \bUIntPtr\b UPTR_TYPE >> %_OUT% +echo #pragma pack(pop) >> %_OUT% + +goto exit + +:usage +echo.Usage: +echo. BAR2WIN.CMD input output +echo. +echo.Summary: +echo. Converts a header file from bartok-generated format +echo. to Windows friendly, cross-compilable format +echo. +echo.Arguments: +echo. input Input header file +echo. output Output header file +echo. + +:exit diff --git a/base/Kernel/bar2win32.cmd b/base/Kernel/bar2win32.cmd new file mode 100644 index 0000000..797662c --- /dev/null +++ b/base/Kernel/bar2win32.cmd @@ -0,0 +1,64 @@ +@echo off +setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION +if .==.%1 goto usage +if .%1==./? goto usage + +set _IN= +set _OUT= +:parse +if .==.%1 goto good + +if /I ./?==./1 ( + shift /1 + goto usage +) +if /I .%1==.-? ( + shift /1 + goto usage +) +if /I .%1==./help ( + shift /1 + goto usage +) + +if .==.%_IN% ( + set _IN=%1 + shift /1 + goto parse +) + +if .==.%_OUT% ( + set _OUT=%1 + shift /1 + goto parse +) + +goto usage + +goto parse + +:good +rem echo _IN=%_IN% +rem echo _OUT=%_OUT% +rem goto exit + +echo #pragma pack(push, 1) > %_OUT% +type %_IN% | substitute.exe "static .*\);" "" | substitute Class_ Class32_ | substitute Struct_ Struct32_ | substitute.exe "(?[a-zA-Z_][a-zA-Z0-9_]*) \*\*" "UINT32 /* ${x}** */" | substitute.exe "(?[a-zA-Z_][a-zA-Z0-9_]*) \*" "UINT32 /* ${x}* */" | substitute.exe \bbool\b UINT8 | substitute.exe \bbyte\b BYTE | substitute \buintptr\b UINT32 | substitute \bintptr\b INT32 | substitute "\buint(?[0-9]*)\b" "UINT${x}" | substitute "\bint(?[0-9]*)\b" "INT${x}" | substitute bartok_char WCHAR | substitute \bUIntPtr\b UINT32 >> %_OUT% +echo #pragma pack(pop) >> %_OUT% + +goto exit + +:usage +echo.Usage: +echo. BAR2WIN32.CMD input output +echo. +echo.Summary: +echo. Converts a header file from bartok-generated format +echo. to Windows friendly, cross-compilable format +echo. +echo.Arguments: +echo. input Input header file +echo. output Output header file +echo. + +:exit diff --git a/base/Kernel/bararmfix.cmd b/base/Kernel/bararmfix.cmd new file mode 100644 index 0000000..4141995 --- /dev/null +++ b/base/Kernel/bararmfix.cmd @@ -0,0 +1,62 @@ +@echo off +setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION +if .==.%1 goto usage +if .%1==./? goto usage + +set _IN= +set _OUT= +:parse +if .==.%1 goto good + +if /I ./?==./1 ( + shift /1 + goto usage +) +if /I .%1==.-? ( + shift /1 + goto usage +) +if /I .%1==./help ( + shift /1 + goto usage +) + +if .==.%_IN% ( + set _IN=%1 + shift /1 + goto parse +) + +if .==.%_OUT% ( + set _OUT=%1 + shift /1 + goto parse +) + +goto usage + +goto parse + +:good +rem echo _IN=%_IN% +rem echo _OUT=%_OUT% +rem goto exit + +type %_IN% | substitute.exe -e "EXTERN \|(?.*)\|" "IF :def:|defining ${x}|\n EXPORT |${x}|\n ELSE\n EXTERN |${x}|\n ENDIF\n" > %_OUT% + +goto exit + +:usage +echo.Usage: +echo. bararmfix.CMD input output +echo. +echo.Summary: +echo. Converts an inc file from bartok-generated format +echo. to armasm friendly format +echo. +echo.Arguments: +echo. input Input header file +echo. output Output header file +echo. + +:exit diff --git a/base/Kernel/corlib_assembly_ref.il b/base/Kernel/corlib_assembly_ref.il new file mode 100644 index 0000000..15006b1 --- /dev/null +++ b/base/Kernel/corlib_assembly_ref.il @@ -0,0 +1,5 @@ +.assembly extern 'kernel' as 'corlib' { +.ver 1:0:0:0 +.publickeytoken = ( 736440c9b414ea16 ) +} + diff --git a/base/Kernel/dummysysentry.cpp b/base/Kernel/dummysysentry.cpp index 9f789cc..9451142 100644 --- a/base/Kernel/dummysysentry.cpp +++ b/base/Kernel/dummysysentry.cpp @@ -1,6 +1,7 @@ -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // +// ---------------------------------------------------------------------------- int syscallEntryTable[] = {0}; diff --git a/base/Kernel/linkdate.cpp b/base/Kernel/linkdate.cpp index 6c16474..f3900fd 100644 --- a/base/Kernel/linkdate.cpp +++ b/base/Kernel/linkdate.cpp @@ -1,7 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // +// ---------------------------------------------------------------------------- #define __T(x) L ## x #define _T(x) __T(x) diff --git a/base/Kernel/testpe.cpp b/base/Kernel/testpe.cpp index 54494c0..dbd2033 100644 --- a/base/Kernel/testpe.cpp +++ b/base/Kernel/testpe.cpp @@ -2,7 +2,7 @@ // // testpe.cpp - Singularity Hardware Abstraction Layer // -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright Microsoft Corporation. // typedef unsigned short bartok_char; diff --git a/base/Kernel/testpe.manifest b/base/Kernel/testpe.manifest index 1e5b4af..1732701 100644 --- a/base/Kernel/testpe.manifest +++ b/base/Kernel/testpe.manifest @@ -1,4 +1,4 @@  - - + + diff --git a/base/Libraries/CLRProfiler/AssemblyInfo.sg b/base/Libraries/CLRProfiler/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Libraries/CLRProfiler/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Libraries/CLRProfiler/CLRProfiler.csproj b/base/Libraries/CLRProfiler/CLRProfiler.csproj index 5a28028..bcf6a87 100644 --- a/base/Libraries/CLRProfiler/CLRProfiler.csproj +++ b/base/Libraries/CLRProfiler/CLRProfiler.csproj @@ -26,6 +26,7 @@ + diff --git a/base/Libraries/CLRProfiler/CLRProfiler.sg b/base/Libraries/CLRProfiler/CLRProfiler.sg index a20074c..96324a8 100644 --- a/base/Libraries/CLRProfiler/CLRProfiler.sg +++ b/base/Libraries/CLRProfiler/CLRProfiler.sg @@ -1,5 +1,5 @@ // -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // namespace Microsoft.Singularity.Diagnostics @@ -7,6 +7,7 @@ namespace Microsoft.Singularity.Diagnostics using FileSystem.Utils; using Microsoft.Singularity; + using Microsoft.Singularity.Isal; using Microsoft.Singularity.Channels; using Microsoft.Singularity.Directory; using Microsoft.Singularity.V1.Services; @@ -85,7 +86,7 @@ namespace Microsoft.Singularity.Diagnostics byte nybble; // 16 nybbles in an 8-byte quantity - for (int i=0; i<16; i++) { + for (int i = 0; i < 16; i++) { nybble = (byte) ((ul & 0xf000000000000000) >> 60); ul <<= 4; @@ -107,7 +108,7 @@ namespace Microsoft.Singularity.Diagnostics // No space-separator! internal void WriteString(string! s) { - for (int i=0; i ASCII } } @@ -239,7 +240,6 @@ namespace Microsoft.Singularity.Diagnostics filePos += bytesWritten; if (error != 0) { - Console.WriteLine("Failed to write to GC Profiler log\n"); // TODO: We need a real error story for the platform. throw new Exception("Failed to write to GC Profiler log"); } @@ -298,7 +298,7 @@ namespace Microsoft.Singularity.Diagnostics // We are holding the lock. The GC hasn't taken control from us. // There is room for our entry. Blast it without allocating or yielding. - for (int i=0; i=0; i--) { + for (int i = edge; i >= 0; i--) { buffer.WriteNumber((ulong) stackNos[i]); } buffer.WriteCRLF(); @@ -787,7 +787,7 @@ namespace Microsoft.Singularity.Diagnostics { // Well, this actually includes the prolog of this method, so it is off by a // few bytes. - return Processor.GetCallerEip(); + return Isa.GetCallerReturnAddress(); } } } diff --git a/base/Libraries/Console/AssemblyInfo.sg b/base/Libraries/Console/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Libraries/Console/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Libraries/Console/Console.App.csproj b/base/Libraries/Console/Console.App.csproj new file mode 100644 index 0000000..b6653af --- /dev/null +++ b/base/Libraries/Console/Console.App.csproj @@ -0,0 +1,24 @@ + + + + + Console.App + Library + {B5509415-64BD-4BF6-A357-8FDA736D96A5} + true + + + + + + + + + + \ No newline at end of file diff --git a/base/Libraries/Console/Console.cs b/base/Libraries/Console/Console.cs new file mode 100644 index 0000000..53f944f --- /dev/null +++ b/base/Libraries/Console/Console.cs @@ -0,0 +1,287 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +namespace System +{ + using Microsoft.Singularity; + using Microsoft.Singularity.Io; + using System; + using System.Text; + + // Provides static fields for console input and output. Use + // Console.In for input from the standard input stream (stdin), + // Console.Out for output to stdout, and Console.Error + // for output to stderr. If any of those console streams are + // redirected from the command line, these streams will be redirected. + // A program can also redirect its own output or input with the + // SetIn, SetOut, and SetError methods. + // + // The distinction between Console.Out and Console.Error is useful + // for programs that redirect output to a file or a pipe. Note that + // stdout and stderr can be output to different files at the same + // time from the DOS command line: + // + // someProgram 1> out 2> err + // + //| + public static class Console + { + private static void WriteInternal(char[] buffer, int index, int count) + { + ConsoleOutput.Write(buffer, index, count); + } + + private static void WriteInternal(String value) + { + ConsoleOutput.Write(value); + } + + private static void WriteNewLine() + { + WriteInternal(Environment.NewLine); + } + + //| + public static void WriteLine() + { + WriteNewLine(); + } + + //| + public static void WriteLine(String value) + { + WriteInternal(value); + WriteNewLine(); + } + + //| + public static void WriteLine(bool value) + { + WriteInternal(value.ToString()); + WriteNewLine(); + } + + //| + public static void WriteLine(char value) + { + WriteInternal(value.ToString()); + WriteNewLine(); + } + + //| + public static void WriteLine(char[] buffer) + { + WriteInternal(buffer, 0, buffer.Length); + WriteNewLine(); + } + + //| + public static void WriteLine(char[] buffer, int index, int count) + { + WriteInternal(buffer, index, count); + WriteNewLine(); + } + + //| + public static void WriteLine(decimal value) + { + WriteInternal(value.ToString()); + WriteNewLine(); + } + + //| + public static void WriteLine(double value) + { + WriteInternal(value.ToString()); + WriteNewLine(); + } + + //| + public static void WriteLine(float value) + { + WriteInternal(value.ToString()); + WriteNewLine(); + } + + //| + public static void WriteLine(int value) + { + WriteInternal(value.ToString()); + WriteNewLine(); + } + + //| + [CLSCompliant(false)] + public static void WriteLine(uint value) + { + WriteInternal(value.ToString()); + WriteNewLine(); + } + + //| + public static void WriteLine(long value) + { + WriteInternal(value.ToString()); + WriteNewLine(); + } + + //| + [CLSCompliant(false)] + public static void WriteLine(ulong value) + { + WriteInternal(value.ToString()); + WriteNewLine(); + } + + //| + public static void WriteLine(Object value) + { + WriteInternal(value.ToString()); + WriteNewLine(); + } + + //| + public static void WriteLine(String format, Object arg0) + { + WriteInternal(String.Format(format, arg0)); + WriteNewLine(); + } + + //| + public static void WriteLine(String format, Object arg0, Object arg1) + { + WriteInternal(String.Format(format, arg0, arg1)); + WriteNewLine(); + } + + //| + public static void WriteLine(String format, Object arg0, Object arg1, Object arg2) + { + WriteInternal(String.Format(format, arg0, arg1, arg2)); + WriteNewLine(); + } + + //| + public static void WriteLine(String format, params Object[] args) + { + WriteInternal(String.Format(format, args)); + WriteNewLine(); + } + + //| + public static void Write(String format, Object arg0) + { + WriteInternal(String.Format(format, arg0)); + } + + //| + public static void Write(String format, Object arg0, Object arg1) + { + WriteInternal(String.Format(format, arg0, arg1)); + } + + //| + public static void Write(String format, Object arg0, Object arg1, Object arg2) + { + WriteInternal(String.Format(format, arg0, arg1, arg2)); + } + + //| + public static void Write(String format, params Object[] args) + { + WriteInternal(String.Format(format, args)); + } + + //| + public static void Write(bool value) + { + WriteInternal(value.ToString()); + } + + //| + public static void Write(char value) + { + WriteInternal(value.ToString()); + } + + //| + public static void Write(char[] buffer) + { + WriteInternal(buffer, 0, buffer.Length); + } + + //| + public static void Write(char[] buffer, int index, int count) + { + WriteInternal(buffer, index, count); + } + + //| + public static void Write(double value) + { + WriteInternal(value.ToString()); + } + + //| + public static void Write(decimal value) + { + WriteInternal(value.ToString()); + } + + //| + public static void Write(float value) + { + WriteInternal(value.ToString()); + } + + //| + public static void Write(int value) + { + WriteInternal(value.ToString()); + } + + //| + [CLSCompliant(false)] + public static void Write(uint value) + { + WriteInternal(value.ToString()); + } + + //| + public static void Write(long value) + { + WriteInternal(value.ToString()); + } + + //| + [CLSCompliant(false)] + public static void Write(ulong value) + { + WriteInternal(value.ToString()); + } + + //| + public static void Write(Object value) + { + WriteInternal(value.ToString()); + } + + //| + public static void Write(String value) + { + WriteInternal(value); + } + + public static int Read() + { + return ConsoleInput.ReadChar(); + } + public static string ReadLine() + { + return ConsoleInput.ReadLine(); + } + } +} diff --git a/base/Libraries/Console/ConsoleInput.sg b/base/Libraries/Console/ConsoleInput.sg new file mode 100644 index 0000000..c420d62 --- /dev/null +++ b/base/Libraries/Console/ConsoleInput.sg @@ -0,0 +1,317 @@ +////////////////////////////////////////////////////////////////////////////////// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ConsoleInput.sg +// +// Note: Read a line or a char of the Console +// + +using DirectoryService.Utils; +using System.GC; +using System.Runtime.Remoting; +using System.Threading; +using System.Collections; +using System.Diagnostics; +using Microsoft.Singularity.V1.Services; + + +namespace Microsoft.Singularity.Io +{ + using Microsoft.SingSharp; + using Microsoft.Singularity; + using Microsoft.Singularity.V1.Services; + using Microsoft.Singularity.V1.Threads; + using Microsoft.Singularity.Channels; + using Microsoft.Singularity.Directory; + + using System; + using System.Text; + using System.Globalization; + using System.Runtime.CompilerServices; + + using Microsoft.Singularity.Extending; + using Microsoft.Singularity.Runtime; + using Microsoft.Singularity.Io; + using System.Runtime.InteropServices; + + + + public sealed class ConsoleInput // : ITracked, ISelectable + { + private static TContainer consoleContainer; + // protected by TContainer lock. + private static char[]! readLineBuffer = new char[16]; + + // +// + //private char[]! in ExHeap side; + //private UnicodePipeContract.Exp conn; + //private int charCount; + //private int charPos; +// + //private ConsoleInput([Claims] UnicodePipeContract.Exp? pipe) + //{ + // this.side = new[ExHeap] char [280]; + // this.charCount = 0; + // this.charPos = 0; + // this.conn = pipe; +// + // base(); + //} + // + + private ConsoleInput() {} + + private static UnicodePipeContract.Exp? GetPipe() + { + UnicodePipeContract.Exp con = null; + + Endpoint * in ExHeap pipeep = Process.GetStartupEndpoint(0); + if (pipeep != null) { + con = pipeep as UnicodePipeContract.Exp; + if (con == null) { + // This is benign. Processes that use manifests to declare their endpoints + // will probably not have a UnicodePipeContract.Exp at index 0. In fact, + // if they declare such, they will encounter problems because of the conflict + // with this method (and its caller). Need to determine the right way to do + // this. + Process.RetStartupEndpoint(0,pipeep); + return null; + } + else { + //DebugStub.WriteLine("ConsoleInput: connected to pipe."); + return con; + } + } + return null; + } + + + public static void Initialize() + { + UnicodePipeContract.Exp pipe = GetPipe(); + PipeLookAhead bufferedPipe = new PipeLookAhead(pipe, 100); + ConsoleInput.consoleContainer = new TContainer(bufferedPipe); + } + + // + //public void Dispose() + //{ + // delete this.side; + // delete this.conn; + //} +// + //void ITracked.Acquire() { + // Tracing.Log(Tracing.Debug,"ConsoleInput.Acquire"); + //} + //void ITracked.Release() { + // Tracing.Log(Tracing.Debug,"ConsoleInput.Release"); + //} + //void ITracked.Expose() { + // Tracing.Log(Tracing.Debug,"ConsoleInput.Expose"); + //} + //void ITracked.UnExpose() { + // Tracing.Log(Tracing.Debug,"ConsoleInput.UnExpose"); + //} + // + + public static void Finalize() + { + // TContainer chandle = consoleContainer; + TContainer chandle = consoleContainer; + consoleContainer = null; + + if (chandle == null) return; + + PipeLookAhead co = chandle.Acquire(); + co.Dispose(); + } + + // + //private int InternalReadChar() + //{ + // expose (this) { + // if (this.conn != null) { + // if (this.charCount == 0) { + // UnicodePipeContract.Exp pipe = this.conn; +// + // tryAgain: + // switch receive { + // case pipe.Write(buffer,a,b): + // if (b == 0) { + // pipe.SendAckWrite(buffer); + // goto tryAgain; + // } + // if (b == 1) { + // int theChar = (int) buffer[a]; + // pipe.SendAckWrite(buffer); + // return theChar; + // } + // else { + // // switch our buffer with theirs + // pipe.SendAckWrite(this.side); + // this.side = buffer; + // this.charPos = a+1; + // this.charCount = b-1; + // return (int)buffer[a]; + // } +// + // case pipe.ChannelClosed(): + // return -1; + // } + // } + // else { + // // there are more chars in the buffer + // int theChar = this.side[this.charPos++]; + // this.charCount--; + // return theChar; + // } + // } + // } //expose + // return -1; + //} + // + + public static void Close() + { + } + + public static int ReadChar() + { + if (consoleContainer == null) return -1; + PipeLookAhead co = consoleContainer.Acquire(); + int key = co.ReadChar(); + if (key == -1) { + co.Dispose(); + consoleContainer = null; + return key; + } + consoleContainer.Release(co); + return key; + } //ReadChar + + //////////////////////////////////////////////////////// + // + public static string ReadLine() + { + if (consoleContainer == null) return null; + PipeLookAhead bufferedPipe = consoleContainer.Acquire(); + + int pos = 0; + int key; + + do { + key = bufferedPipe.ReadChar(); + if (key == -1) { + bufferedPipe.Dispose(); + consoleContainer = null; + return null; + } + if (key == '\r') { + // DebugStub.WriteLine("key {0}", key); + continue; + } + if (key == '\n') { + // DebugStub.WriteLine("key {0}", key); + break; + } + if (pos + 1 >= readLineBuffer.Length) { + char[] temp = new char[readLineBuffer.Length *2]; + Array.Copy(readLineBuffer,temp,readLineBuffer.Length); + readLineBuffer = temp; + } + readLineBuffer[pos++] = (char) key; + if (key > 31 && key < 126) { + // DebugStub.WriteLine("key {0}", key); + } + } while (key != -1); + + string s = new string(readLineBuffer,0,pos); + //DebugStub.WriteLine("in: count={0}, s=({1})",__arglist(pos, s)); + + consoleContainer.Release(bufferedPipe); + return s; + } //ReadLine + + // + //internal enum ReceiveTag { + // Any = 1, + // GetChar = 2, + //} +// + //[Selectable((int)ReceiveTag.GetChar)] + //public void RecvGetChar(out int key) { + // key = this.InternalReadChar(); + //} +// + ///// + ///// Tests if the message at the head of the queue matches the given tag. If so, + ///// returns true. + ///// + ///// If no match is possible in the future given the current message, possible must + ///// be set to false. + ///// Implementations are disallowed from setting possible to true + ///// so that the context can chain them. + ///// + ///// If the underlying object is an endpoint set, it also returns an object that + ///// serves to extract the endpoint having the match from the set later. + ///// + ///// Again, implementations are disallowed from setting match setMatch to null + ///// so that the context can chain the calls. + ///// + ///// + //bool ISelectable.HeadMatches(int tag, ref bool possible, ref object setMatch) { + // // Only handle pipe case + // expose (this) { + // if (this.conn != null) { + // if (this.charCount == 0) { + // UnicodePipeContract.Exp pipe = this.conn; +// + // // negative tag is channel closed + // int pipeTag = (tag < 0)?tag : UnicodePipeContract.Tags.Write; +// + // return pipe.HeadMatches(pipeTag, ref possible, ref setMatch); + // } + // else { + // // there are more chars in the buffer + // if (tag < 0) { + // // not closed + // possible = false; + // return false; + // } + // return true; + // } + // } + // else { + // // no pipe + // possible = false; + // return false; + // } + // } //expose + //} +// + //SyncHandle ISelectable.GetWaitHandle() { + // expose (this) { + // assert(this.conn != null); + // return this.conn.GetWaitHandle(); + // } + //} +// + // + public static PipeLookAhead! AcquireInput() { + assert consoleContainer != null; + + PipeLookAhead bufferedPipe = consoleContainer.Acquire(); + return bufferedPipe; + } + + public static void ReleaseInput([Claims] PipeLookAhead! bufferedPipe) { + assert consoleContainer != null; + + consoleContainer.Release(bufferedPipe); + } + + } //ConsoleInput +}//namespace diff --git a/base/Libraries/Console/ConsoleOutput.sg b/base/Libraries/Console/ConsoleOutput.sg new file mode 100644 index 0000000..589dbac --- /dev/null +++ b/base/Libraries/Console/ConsoleOutput.sg @@ -0,0 +1,455 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ConsoleOutput.sg +// +// Note: Uses /dev/conout to display console output. +// + +namespace Microsoft.Singularity.Io +{ + using Microsoft.SingSharp; + using Microsoft.Singularity; + using Microsoft.Singularity.V1.Services; + using Microsoft.Singularity.Channels; + using Microsoft.Singularity.Directory; + + using System; + using System.Text; + using System.Globalization; + using System.Runtime.CompilerServices; + + using System.Runtime.InteropServices; + + public sealed class ConsoleOutput : ITracked + { + private static TContainer consoleContainer; + + private struct PipeData : ITracked { + public UnicodePipeContract.Imp channel; + public void Dispose() + { + delete channel; + } + void ITracked.Acquire() {} + void ITracked.Release() {} + public void Expose() {} + public void UnExpose() {} + + public UnicodePipeContract.Imp:READY Swap([Claims] UnicodePipeContract.Imp:READY newPipe) { + expose (this) { + UnicodePipeContract.Imp result = this.channel; + this.channel = newPipe; + return result; + } + } + } + + // + //private struct DeviceData : ITracked { + // public ConsoleDeviceContract.Imp:Ready channel; + // public void Dispose() + // { + // delete channel; + // } + // void ITracked.Acquire() {} + // void ITracked.Release() {} + // public void Expose() {} + // public void UnExpose() {} + //} + // + + private char[]! in ExHeap side; + private PipeData pipe; + // + //private DeviceData device; + // + private ConsoleOutput([Claims] UnicodePipeContract.Imp? pipe) + { + this.side = new[ExHeap] char [280]; + this.pipe.channel = pipe; + } + + private static UnicodePipeContract.Imp GetPipe() + { + UnicodePipeContract.Imp con = null; + + Endpoint* in ExHeap pipeep = Process.GetStartupEndpoint(1); + if (pipeep != null) { + con = pipeep as UnicodePipeContract.Imp; + if (con == null) { + Process.RetStartupEndpoint(1,pipeep); + return null; + } + return con; + } + return null; + } + + public static void Initialize() + { + // + //ConsoleDeviceContract.Imp imp = null; +// + //imp = OpenConsole("/dev/video-text"); + //if (imp == null) { + // imp = OpenConsole("/dev/conout"); + // if (imp == null) { + // DebugStub.Print("Couldn't open console.\n"); + // return; + // } + //} + // + UnicodePipeContract.Imp pipe = GetPipe(); + ConsoleOutput co = new ConsoleOutput(pipe); + consoleContainer = new TContainer(co); + } + + public void Dispose() + { + this.pipe.Dispose(); + // this.device.Dispose(); + delete this.side; + } + + void ITracked.Acquire() { + //Tracing.Log(Tracing.Debug,"ConsoleOutput.Acquire"); + } + void ITracked.Release() { + //Tracing.Log(Tracing.Debug,"ConsoleOutput.Release"); + } + void ITracked.Expose() { + //Tracing.Log(Tracing.Debug,"ConsoleOutput.Expose"); + } + void ITracked.UnExpose() { + //Tracing.Log(Tracing.Debug,"ConsoleOutput.UnExpose"); + } + + public static void Finalize() + { + TContainer chandle = consoleContainer; + consoleContainer = null; + + if (chandle == null) return; + + ConsoleOutput co = chandle.Acquire(); + if (co != null) co.Dispose(); + } + + // + //private static ConsoleDeviceContract.Imp + //OpenConsoleInternal(DirectoryServiceContract.Imp! nsImp, + // [Claims] char[]! in ExHeap deviceName) + //{ + // ConsoleDeviceContract.Exp! exp; + // ConsoleDeviceContract.Imp! imp; + // ConsoleDeviceContract.NewChannel(out imp, out exp); +// + // nsImp.SendBind(deviceName, exp); + // switch receive { + // case nsImp.AckBind(): + // return imp; + // break; + // case nsImp.NakBind(rejectImp, error): + // delete rejectImp; + // delete imp; + // break; + // case nsImp.ChannelClosed(): + // throw new Exception("Channel closed during Console bind."); + // delete imp; + // break; + // case nsImp.NakBindReparse(path, rest, linked, backExp): + // assert linked == true; + // assert rest == null; + // delete backExp; + // delete imp; +// + // return OpenConsoleInternal(nsImp, path); + // break; + // } + // return null; + //} +// + //private static ConsoleDeviceContract.Imp OpenConsole(string! devName) + //{ + // DirectoryServiceContract.Imp ns = DirectoryService.NewClientEndpoint(); + // ConsoleDeviceContract.Imp imp = + // OpenConsoleInternal(ns, Bitter.FromString2(devName)); + // delete ns; +// + // if (imp != null) { + // switch receive { + // case imp.Success(): + // break; + // case imp.ContractNotSupported(): + // throw new Exception("ConsoleOutput: ContractNotSupported"); + // break; + // case imp.ChannelClosed(): + // throw new Exception("ConsoleOutput: ChannelClosed"); + // break; + // } + // } + // return imp; + //} +// + //public static void Clear() + //{ + // if (consoleContainer == null) return; +// + // ConsoleOutput co = consoleContainer.Acquire(); +// + // expose (co) { + // ConsoleDeviceContract.Imp ep = co.device.channel; + // if (ep != null) { + // ep.SendClear(); + // switch receive { + // case ep.AckClear(): + // break; + // case ep.NakClear(): + // break; + // case ep.ChannelClosed(): + // delete ep; + // co.device.channel = null; + // break; + // } + // } + // } + // consoleContainer.Release(co); + //} +// + //public static void CursorFlash() + //{ + // if (consoleContainer == null) return; +// + // ConsoleOutput co = consoleContainer.Acquire(); +// + // expose (co) { + // ConsoleDeviceContract.Imp ep = co.device.channel; + // if (ep != null) { + // ep.SendCursorFlash(); + // switch receive { + // case ep.AckCursorFlash(): + // break; + // case ep.NakCursorFlash(): + // break; + // case ep.ChannelClosed(): + // delete ep; + // co.device.channel = null; + // break; + // } + // } + // } + // consoleContainer.Release(co); + //} +// + //public static void CursorHide() + //{ + // if (consoleContainer == null) return; +// + // ConsoleOutput co = consoleContainer.Acquire(); +// + // expose (co) { + // ConsoleDeviceContract.Imp ep = co.device.channel; + // if (ep != null) { + // ep.SendCursorHide(); + // switch receive { + // case ep.AckCursorHide(): + // break; + // case ep.NakCursorHide(): + // break; + // case ep.ChannelClosed(): + // delete ep; + // co.device.channel = null; + // break; + // } + // } + // } + // consoleContainer.Release(co); + //} +// + //public static void CursorShow() + //{ + // if (consoleContainer == null) return; +// + // ConsoleOutput co = consoleContainer.Acquire(); +// + // expose (co) { + // ConsoleDeviceContract.Imp ep = co.device.channel; + // if (ep != null) { + // ep.SendCursorShow(); + // switch receive { + // case ep.AckCursorShow(): + // break; + // case ep.NakCursorShow(): + // break; + // case ep.ChannelClosed(): + // delete ep; + // co.device.channel = null; + // break; + // } + // } + // } + // consoleContainer.Release(co); + //} + // + + public static char[]! in ExHeap Write([Claims] char[]! in ExHeap buffer, + int index, int count) + { + if (consoleContainer == null) return buffer; + ConsoleOutput co = consoleContainer.Acquire(); + try { + expose (co) { + if (co.pipe.channel != null) { + return Write(buffer, index, count, ref co.pipe); + } + else { + return buffer; + } + } + } + finally { + consoleContainer.Release(co); + } + } + + // + //private static char[]! in ExHeap Write([Claims] char[]! in ExHeap buffer, + // int index, int count, + // ref DeviceData device) + //{ + // expose (device) { + // ConsoleDeviceContract.Imp channel = device.channel; + // if (channel != null) { + // channel.SendWrite(buffer, index, count); + // switch receive { + // case channel.AckWrite(localside): + // return localside; +// + // case channel.NakWrite(localside): + // return localside; +// + // case channel.ChannelClosed(): + // delete channel; + // device.channel = null; + // return new[ExHeap] char[0]; + // } + // } + // else { + // return buffer; + // } + // } + //} + // + + private static char[]! in ExHeap Write([Claims] char[]! in ExHeap buffer, + int index, int count, + ref PipeData pipe) + { + expose (pipe) { + UnicodePipeContract.Imp channel = pipe.channel; + if (channel != null) { + // char[]! in ExHeap buffer=Bitter.FromString2(value); + channel.SendWrite(buffer, index, count); + switch receive { + case channel.AckWrite(i_buffer): + return i_buffer; + + case channel.ChannelClosed(): + delete channel; + pipe.channel = null; + return new[ExHeap] char[0]; + + case unsatisfiable: + DebugStub.Break(); + throw new Exception("Pipe unsatisfiable "); + } + } + else { + return buffer; + } + } + } + + public static void Write(char[]! buffer, int index, int count) + { + if (consoleContainer == null) return; + + ConsoleOutput co = consoleContainer.Acquire(); + + expose (co) { + char[] in ExHeap side = co.side; + + if (side.Length < count) { + delete side; + co.side = side = new[ExHeap] char [count]; + } + + for (int i = 0; i < count; i++) { + side[i] = buffer[index + i]; + } + + if (co.pipe.channel != null) { + co.side = Write(side, index, count, ref co.pipe); + } + // + //else { + // co.side = Write(side, index, count, ref co.device); + //} + // + } + consoleContainer.Release(co); + } + + public static void Write(String! value) + { + if (consoleContainer == null) return; + + ConsoleOutput co = consoleContainer.Acquire(); + + expose (co) { + + char[] in ExHeap side = co.side; + if (side.Length < value.Length) { + delete side; + co.side = side = new[ExHeap] char [value.Length]; + } + + for (int i = 0; i < value.Length; i++) { + side[i] = value[i]; + } + + if (co.pipe.channel != null) { + co.side = Write(side, 0, value.Length, ref co.pipe); + } + // + //else { + // co.side = Write(side, 0, value.Length, ref co.device); + //} + // + } + consoleContainer.Release(co); + return; + } + + public static UnicodePipeContract.Imp:READY Swap([Claims] UnicodePipeContract.Imp:READY newOutput) { + if (consoleContainer == null) { + delete newOutput; + return null; + } + + ConsoleOutput co = consoleContainer.Acquire(); + + UnicodePipeContract.Imp result; + expose(co) { + result = co.pipe.Swap(newOutput); + } + consoleContainer.Release(co); + return result; + } + + } +} diff --git a/base/Libraries/Console/RuntimeConsoleMixin.sg b/base/Libraries/Console/RuntimeConsoleMixin.sg new file mode 100644 index 0000000..62276c0 --- /dev/null +++ b/base/Libraries/Console/RuntimeConsoleMixin.sg @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +namespace Microsoft.Singularity +{ + using Microsoft.Bartok.Options; + using Microsoft.Singularity.Io; + + [Mixin(typeof(Microsoft.Singularity.AppRuntime))] + public static class ConsoleAppRuntimeMixin + { + [MixinOverride] + public static void InitializeConsole() + { + ConsoleInput.Initialize(); + ConsoleOutput.Initialize(); + } + + [MixinOverride] + public static void FinalizeConsole() + { + ConsoleInput.Finalize(); + ConsoleOutput.Finalize(); + } + } +} diff --git a/base/Libraries/Console/__ConsoleTextWriter.cs b/base/Libraries/Console/__ConsoleTextWriter.cs new file mode 100644 index 0000000..85ce783 --- /dev/null +++ b/base/Libraries/Console/__ConsoleTextWriter.cs @@ -0,0 +1,46 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.IO; +using System.Text; +using System.Runtime.InteropServices; + +// [Bartok]: +namespace System +{ + public class __ConsoleTextWriter : TextWriter { + public __ConsoleTextWriter() + { + } + + public override Encoding Encoding { + get { + return Encoding.Default; + } + } + + public override void Write(char[] buffer, int index, int count) + { + Console.Write(buffer, index, count); + } + + public override void Write(String str) + { + Console.Write(str); + } + + public override void WriteLine(char[] buffer, int index, int count) + { + Console.WriteLine(buffer, index, count); + } + + public override void WriteLine(String str) + { + Console.WriteLine(str); + } + } +} diff --git a/base/Libraries/CredentialsManager/AssemblyInfo.sg b/base/Libraries/CredentialsManager/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Libraries/CredentialsManager/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Libraries/CredentialsManager/CredentialsManager.Library.csproj b/base/Libraries/CredentialsManager/CredentialsManager.Library.csproj index 86b25ae..e4f20f4 100644 --- a/base/Libraries/CredentialsManager/CredentialsManager.Library.csproj +++ b/base/Libraries/CredentialsManager/CredentialsManager.Library.csproj @@ -1,33 +1,25 @@  - - Library CredentialsManagerLib + {69F2E090-E495-46C9-9151-ABF2CD516395} - + - + - diff --git a/base/Libraries/CredentialsManager/CredentialsManager.sg b/base/Libraries/CredentialsManager/CredentialsManager.sg index 0b850a9..fe00229 100644 --- a/base/Libraries/CredentialsManager/CredentialsManager.sg +++ b/base/Libraries/CredentialsManager/CredentialsManager.sg @@ -45,23 +45,16 @@ namespace Microsoft.Singularity.Security DirectoryServiceContract.Imp! rootds = DirectoryService.NewClientEndpoint(); - rootds.SendBind(Bitter.FromString2(CredentialsManagerContract.ChannelPath), manager_exp); - - switch receive { - case rootds.AckBind(): - break; - - case rootds.NakBind(exp, error): - delete exp; - delete manager; - delete rootds; - throw new Exception(String.Format("Failed to bind to control channel '{0}': error {1}", CredentialsManagerContract.ChannelPath, error)); + ErrorCode error; + if (SdsUtils.Bind(CredentialsManagerContract.ChannelPath, rootds, manager_exp, out error)) { + manager.RecvSuccess(); + delete rootds; + return manager; } - - delete rootds; - switch receive { - case manager.Success(): - return manager; + else { + delete manager; + delete rootds; + throw new Exception(String.Format("Failed to bind to control channel '{0}': error {1}", CredentialsManagerContract.ChannelPath, error)); } } @@ -127,7 +120,8 @@ namespace Microsoft.Singularity.Security credentialsName, tag, exp); - } finally { + } + finally { delete manager; } } @@ -182,7 +176,8 @@ namespace Microsoft.Singularity.Security exp, out credentialsName, out tag); - } finally { + } + finally { delete manager; } } @@ -220,7 +215,8 @@ namespace Microsoft.Singularity.Security CredentialsManagerContract.Imp! manager = ConnectService(); try { AddCredentials(manager, credentialsName, tag, password, replace); - } finally { + } + finally { delete manager; } } @@ -254,7 +250,8 @@ namespace Microsoft.Singularity.Security CredentialsManagerContract.Imp! manager = ConnectService(); try { DeleteCredentials(manager, credentialsName, tag); - } finally { + } + finally { delete manager; } } @@ -279,7 +276,8 @@ namespace Microsoft.Singularity.Security CredentialsManagerContract.Imp! manager = ConnectService(); try { DeleteAllCredentials(manager); - } finally { + } + finally { delete manager; } } @@ -335,7 +333,8 @@ namespace Microsoft.Singularity.Security credentialsName, tag, replace); - } finally { + } + finally { delete manager; } } @@ -378,7 +377,8 @@ namespace Microsoft.Singularity.Security serviceAddress, authenticationProtocol, realm); - } finally { + } + finally { delete manager; } } @@ -401,7 +401,8 @@ namespace Microsoft.Singularity.Security CredentialsManagerContract.Imp! manager = ConnectService(); try { DeleteAllProtocolMappings(manager); - } finally { + } + finally { delete manager; } } @@ -461,7 +462,8 @@ namespace Microsoft.Singularity.Security useWildcard, out credentialsName, out tag); - } finally { + } + finally { delete manager; } } diff --git a/base/Libraries/Crypto/AssemblyInfo.sg b/base/Libraries/Crypto/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Libraries/Crypto/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Libraries/Crypto/Crypto.csproj b/base/Libraries/Crypto/Crypto.csproj index 27e4d18..7fdc6fd 100644 --- a/base/Libraries/Crypto/Crypto.csproj +++ b/base/Libraries/Crypto/Crypto.csproj @@ -26,14 +26,16 @@ - - - + - - - + + + + + + + diff --git a/base/Libraries/Crypto/Des.cs b/base/Libraries/Crypto/Des.cs index 7946e96..43f28a0 100644 --- a/base/Libraries/Crypto/Des.cs +++ b/base/Libraries/Crypto/Des.cs @@ -1,78 +1,74 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- -/* - -Data Encryption Standard (DES) ------------------------------- - -This is an implementation of the standard 56-bit DES symmetric encryption algorithm. -DES56 is generally considered a weak algorithm, due to its short key length. -However, it is still in use for many purposes. I implemented it in order to implement -the NTLM Challenge/Response authentication protocol, which uses DES to generate -"password equivalents" from cleartext passwords. - -Implemented by Arlie Davis (arlied), June 2006. - - -Implementation Notes --------------------- - -This is a fairly straight-forward implementation of DES. As such, it isn't very fast. -I've done some work to improve performance, but performance has not been the goal, -since the application for which I implemented it (NTLM) required only a few handful -of DES transforms. The priority is therefore simplicity and correctness; this isn't -an implementation you would use for a key-space search. - -DES operates on 64 bit message and cipher blocks, and internally, on values that have -several lengths: 4 bits, 6 bits, 32 bits, 48 bits, 56 bits, and 64 bits. In many -implementations (especially in C/C++) these values are are stored in arrays of bytes. -I chose to store most values in 64-bit values (ulong/UInt64), partly because ulong -is a value-type (can be stored on the stack), and partly because it makes some of the -bit manipulation easy, and in some cases slightly parallel. - -I have not tested this, but my hope is that this implementation works well on 64-bit -processors. - - -Potential Performance Improvements ----------------------------------- - -As I said above, performance is NOT the priority of this implementation. This code -runs at about 1/3 the speed of another implementation that I tested against, which -is good enough for my (current) purposes. But we all like performance, so here are -some ideas for improvement. - - * S-box access could be improved. The classic S-boxes are represented as 8 separate - arrays, each of which has 64 values, and each value of the array contains a 4-bit - substitution value. This works, and is simple enough, but the data density is - rather low; only 4 bits of each array value are used. So a simple improvement - might be to combine the 8 S-box arrays into 4 S-double-box arrays, with each - array containing two S-box values, one in the high nibble and one in the low. - - * The permutation code could be better. Right now, it's a loop that runs 32 or - 48 or 64 times, and each loop iteration consists of a shift, a mask, a shift, - and an OR. This could almost certainly be improved. - - -References ----------- - -There are many references on DES. I used: - - * FIPS 46-3 - http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf - - * http://www.en.wikipedia.org/wiki/Data_Encryption_Standard - - - -*/ +// +// +//Data Encryption Standard (DES) +//------------------------------ +// +//This is an implementation of the standard 56-bit DES symmetric encryption algorithm. +//DES56 is generally considered a weak algorithm, due to its short key length. +//However, it is still in use for many purposes. I implemented it in order to implement +//the NTLM Challenge/Response authentication protocol, which uses DES to generate +//"password equivalents" from cleartext passwords. +// +// +//Implementation Notes +//-------------------- +// +//This is a fairly straight-forward implementation of DES. As such, it isn't very fast. +//I've done some work to improve performance, but performance has not been the goal, +//since the application for which I implemented it (NTLM) required only a few handful +//of DES transforms. The priority is therefore simplicity and correctness; this isn't +//an implementation you would use for a key-space search. +// +//DES operates on 64 bit message and cipher blocks, and internally, on values that have +//several lengths: 4 bits, 6 bits, 32 bits, 48 bits, 56 bits, and 64 bits. In many +//implementations (especially in C/C++) these values are are stored in arrays of bytes. +//I chose to store most values in 64-bit values (ulong/UInt64), partly because ulong +//is a value-type (can be stored on the stack), and partly because it makes some of the +//bit manipulation easy, and in some cases slightly parallel. +// +//I have not tested this, but my hope is that this implementation works well on 64-bit +//processors. +// +// +//Potential Performance Improvements +//---------------------------------- +// +//As I said above, performance is NOT the priority of this implementation. This code +//runs at about 1/3 the speed of another implementation that I tested against, which +//is good enough for my (current) purposes. But we all like performance, so here are +//some ideas for improvement. +// +// * S-box access could be improved. The classic S-boxes are represented as 8 separate +// arrays, each of which has 64 values, and each value of the array contains a 4-bit +// substitution value. This works, and is simple enough, but the data density is +// rather low; only 4 bits of each array value are used. So a simple improvement +// might be to combine the 8 S-box arrays into 4 S-double-box arrays, with each +// array containing two S-box values, one in the high nibble and one in the low. +// +// * The permutation code could be better. Right now, it's a loop that runs 32 or +// 48 or 64 times, and each loop iteration consists of a shift, a mask, a shift, +// and an OR. This could almost certainly be improved. +// +// +//References +//---------- +// +//There are many references on DES. I used: +// +// * FIPS 46-3 +// http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf +// +// * http://www.en.wikipedia.org/wiki/Data_Encryption_Standard +// +// +// +// @@ -115,8 +111,7 @@ namespace System.Security.Cryptography /// static void AdjustPermutationTable(byte[] shifts) { - for (int i = 0; i < shifts.Length; i++) - { + for (int i = 0; i < shifts.Length; i++) { shifts[i] = (byte)(64 - shifts[i]); } } @@ -131,8 +126,7 @@ namespace System.Security.Cryptography static ulong ComputePermutation(ulong input, byte[] shifts) { ulong result = 0; - for (int i = 0; i < shifts.Length; i++) - { + for (int i = 0; i < shifts.Length; i++) { result = (result << 1) | ((input >> shifts[i]) & 1); } @@ -337,8 +331,7 @@ namespace System.Security.Cryptography ulong[] keytable = new ulong[16]; - for (int i = 0; i < 16; i++) - { + for (int i = 0; i < 16; i++) { Debug.Assert((cd & 0xff) == 0); // Next, we left-rotate Cn and Dn *independently* by either 1 or 2 bits. @@ -349,13 +342,11 @@ namespace System.Security.Cryptography // shifts and masks in parallel... aren't we smart? bool shift2 = (KeyRotateSchedule & (1 << i)) != 0; - if (shift2) - { + if (shift2) { cd = ((cd << 2) & 0xffffffcffffffc00UL) // shift 2 sets of 27 bits left by 2 | ((cd >> 26) & 0x0000003000000300UL); } - else - { + else { // Left-rotate 2 sets of 28 bits by 1 position. cd = ((cd << 1) & 0xffffffeffffffe00UL) | ((cd >> 27) & 0x0000001000000100UL); @@ -405,8 +396,7 @@ namespace System.Security.Cryptography // int i = ((input >> 1) & 0xf) | ((input << 4) & 0x10) | (input & 0x20); byte[] adjusted = new byte[64]; - for (int i = 0; i < 64; i++) - { + for (int i = 0; i < 64; i++) { int j = ((i >> 1) & 0xf) | ((i << 4) & 0x10) | (i & 0x20); adjusted[i] = sbox[j]; } @@ -531,8 +521,7 @@ namespace System.Security.Cryptography const int result_length = 48; - for (int kind = 0; kind < 2; kind++) - { + for (int kind = 0; kind < 2; kind++) { bool shift_then_mask = (kind == 0); Console.WriteLine(); @@ -548,8 +537,7 @@ namespace System.Security.Cryptography // pos counts from the LEFT (MSB) to the RIGHT (MSB) of the output word. // the count is ZERO-based. int pos = 0; // index into result, counting from LEFT (MSB) - while (pos < result_length) - { + while (pos < result_length) { // destination of the shift/mask, counting from LEFT (MSB) within the destination word, // and counting is ZERO based. @@ -579,8 +567,7 @@ namespace System.Security.Cryptography ulong before_mask = ((1UL << run_length) - 1) << (64 - src - run_length); ulong after_mask = ((1UL << run_length) - 1) << (64 - dest - run_length); - if ((total_mask_sum & after_mask) != 0) - { + if ((total_mask_sum & after_mask) != 0) { Debug.WriteLine("OVERLAPPING BITS IN AFTER-MASK!"); Debugger.Break(); } @@ -598,8 +585,7 @@ namespace System.Security.Cryptography else shift_string = ""; - if (shift_then_mask) - { + if (shift_then_mask) { Console.WriteLine("result |= (input {1,7}) & 0x{0:x16}UL; // src={2,2} dest={3,2} len = {4}", after_mask, shift_string, @@ -607,8 +593,7 @@ namespace System.Security.Cryptography dest, run_length); } - else - { + else { Console.WriteLine("result |= (input & 0x{0:x16}UL){1,7}; // src={2,2} dest={3,2} len = {4}", before_mask, shift_string, @@ -622,8 +607,7 @@ namespace System.Security.Cryptography Console.WriteLine(); - if (shift_then_mask) - { + if (shift_then_mask) { Console.WriteLine("total mask: 0x{0:x16}", total_mask_sum); Console.WriteLine(); } @@ -663,8 +647,7 @@ namespace System.Security.Cryptography Debug.WriteLine("known good result: " + ToBitStringBe(known_good_result, 48, 6)); Debug.WriteLine("bogus result: " + ToBitStringBe(result, 48, 6)); Debug.WriteLine("difference: " + ToBitStringBe(result ^ known_good_result, 48, 6, '-', 'x')); - if (result != known_good_result) - { + if (result != known_good_result) { Debug.WriteLine("results differ."); Debugger.Break(); } @@ -685,8 +668,7 @@ namespace System.Security.Cryptography // ulong ip = ComputeIP(message); #if DES_TRACE - if (_trace) - { + if (_trace) { Debug.WriteLine("transform: message: " + ToBitStringBe(message, 64, 8)); Debug.WriteLine(" ip: " + ToBitStringBe(ip, 64, 8)); } @@ -697,8 +679,7 @@ namespace System.Security.Cryptography // R is the bottom 32 bits (bits 31-0) ulong lr = ip; - for (int i = 0; i < 16; i++) - { + for (int i = 0; i < 16; i++) { // First, we compute the Expansion function of R(n-1). // This expands a 32 bit quantity to 48 bits. // This is done using a permutation table that repeats some bits in R(n-1). @@ -759,8 +740,7 @@ namespace System.Security.Cryptography lr = next_lr; #if DES_TRACE - if (_trace) - { + if (_trace) { Debug.WriteLine(String.Format("n={0} e(R(n-1))={1} e(R(n-1))+k={2} sbox={3} f={4} L({0})={5} R({0})={6} ", (i + 1).ToString().PadRight(2), ToBitStringBe(expanded, 48, 6), @@ -775,8 +755,7 @@ namespace System.Security.Cryptography } #if DES_TRACE - if (_trace) - { + if (_trace) { Debug.WriteLine(""); Debug.WriteLine("LR final : " + ToBitStringBe(lr, 64, 8)); } @@ -966,8 +945,7 @@ namespace System.Security.Cryptography static string ToBitStringBe(ulong v) { System.Text.StringBuilder buffer = new System.Text.StringBuilder(); - for (int i = 0; i < 64; i++) - { + for (int i = 0; i < 64; i++) { if (i > 0 && (i % 8) == 0) buffer.Append('.'); @@ -986,8 +964,7 @@ namespace System.Security.Cryptography static string ToBitStringBe(ulong v, int length, int groupsize, char c0, char c1) { System.Text.StringBuilder buffer = new System.Text.StringBuilder(); - for (int i = 0; i < length; i++) - { + for (int i = 0; i < length; i++) { if (i > 0 && (i % groupsize) == 0) buffer.Append('.'); diff --git a/base/Libraries/Crypto/MD4.cs b/base/Libraries/Crypto/MD4.cs index 84a120c..660019e 100644 --- a/base/Libraries/Crypto/MD4.cs +++ b/base/Libraries/Crypto/MD4.cs @@ -1,35 +1,35 @@ -/* - -This file contains an implementation of the MD4 hash algorithm. -The implementation was derived from the example source code that -is provided in RFC 1320 - "The MD4 Message-Digest Algorithm". - -The code has been ported from C to C# by Arlie Davis (arlied), June 2006. -The original copyright is preserved here. - - - Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All - rights reserved. - - License to copy and use this software is granted provided that it - is identified as the "RSA Data Security, Inc. MD4 Message-Digest - Algorithm" in all material mentioning or referencing this software - or this function. - - License is also granted to make and use derivative works provided - that such works are identified as "derived from the RSA Data - Security, Inc. MD4 Message-Digest Algorithm" in all material - mentioning or referencing the derived work. - - RSA Data Security, Inc. makes no representations concerning either - the merchantability of this software or the suitability of this - software for any particular purpose. It is provided "as is" - without express or implied warranty of any kind. - - These notices must be retained in any copies of any part of this - documentation and/or software. - -*/ +// +// +//This file contains an implementation of the MD4 hash algorithm. +//The implementation was derived from the example source code that +//is provided in RFC 1320 - "The MD4 Message-Digest Algorithm". +// +//The code has been ported from C to C#. +//The original copyright is preserved here. +// +// +// Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +// rights reserved. +// +// License to copy and use this software is granted provided that it +// is identified as the "RSA Data Security, Inc. MD4 Message-Digest +// Algorithm" in all material mentioning or referencing this software +// or this function. +// +// License is also granted to make and use derivative works provided +// that such works are identified as "derived from the RSA Data +// Security, Inc. MD4 Message-Digest Algorithm" in all material +// mentioning or referencing the derived work. +// +// RSA Data Security, Inc. makes no representations concerning either +// the merchantability of this software or the suitability of this +// software for any particular purpose. It is provided "as is" +// without express or implied warranty of any kind. +// +// These notices must be retained in any copies of any part of this +// documentation and/or software. +// +// using System; using System.Diagnostics; @@ -85,9 +85,9 @@ namespace System.Security.Cryptography _done = false; } - /* - MD4 basic transformation. Transforms state based on block. - */ + // + //MD4 basic transformation. Transforms state based on block. + // void Transform(byte[]/*!*/ block, int offset) { if (offset < 0) @@ -128,59 +128,59 @@ namespace System.Security.Cryptography uint c = _state2; uint d = _state3; - /* Round 1 */ - a = FF(a, b, c, d, x[00], S11); /* 1 */ - d = FF(d, a, b, c, x[01], S12); /* 2 */ - c = FF(c, d, a, b, x[02], S13); /* 3 */ - b = FF(b, c, d, a, x[03], S14); /* 4 */ - a = FF(a, b, c, d, x[04], S11); /* 5 */ - d = FF(d, a, b, c, x[05], S12); /* 6 */ - c = FF(c, d, a, b, x[06], S13); /* 7 */ - b = FF(b, c, d, a, x[07], S14); /* 8 */ - a = FF(a, b, c, d, x[08], S11); /* 9 */ - d = FF(d, a, b, c, x[09], S12); /* 10 */ - c = FF(c, d, a, b, x[10], S13); /* 11 */ - b = FF(b, c, d, a, x[11], S14); /* 12 */ - a = FF(a, b, c, d, x[12], S11); /* 13 */ - d = FF(d, a, b, c, x[13], S12); /* 14 */ - c = FF(c, d, a, b, x[14], S13); /* 15 */ - b = FF(b, c, d, a, x[15], S14); /* 16 */ + // Round 1 + a = FF(a, b, c, d, x[00], S11); // 1 + d = FF(d, a, b, c, x[01], S12); // 2 + c = FF(c, d, a, b, x[02], S13); // 3 + b = FF(b, c, d, a, x[03], S14); // 4 + a = FF(a, b, c, d, x[04], S11); // 5 + d = FF(d, a, b, c, x[05], S12); // 6 + c = FF(c, d, a, b, x[06], S13); // 7 + b = FF(b, c, d, a, x[07], S14); // 8 + a = FF(a, b, c, d, x[08], S11); // 9 + d = FF(d, a, b, c, x[09], S12); // 10 + c = FF(c, d, a, b, x[10], S13); // 11 + b = FF(b, c, d, a, x[11], S14); // 12 + a = FF(a, b, c, d, x[12], S11); // 13 + d = FF(d, a, b, c, x[13], S12); // 14 + c = FF(c, d, a, b, x[14], S13); // 15 + b = FF(b, c, d, a, x[15], S14); // 16 - /* Round 2 */ - a = GG(a, b, c, d, x[00], S21); /* 17 */ - d = GG(d, a, b, c, x[04], S22); /* 18 */ - c = GG(c, d, a, b, x[08], S23); /* 19 */ - b = GG(b, c, d, a, x[12], S24); /* 20 */ - a = GG(a, b, c, d, x[01], S21); /* 21 */ - d = GG(d, a, b, c, x[05], S22); /* 22 */ - c = GG(c, d, a, b, x[09], S23); /* 23 */ - b = GG(b, c, d, a, x[13], S24); /* 24 */ - a = GG(a, b, c, d, x[02], S21); /* 25 */ - d = GG(d, a, b, c, x[06], S22); /* 26 */ - c = GG(c, d, a, b, x[10], S23); /* 27 */ - b = GG(b, c, d, a, x[14], S24); /* 28 */ - a = GG(a, b, c, d, x[03], S21); /* 29 */ - d = GG(d, a, b, c, x[07], S22); /* 30 */ - c = GG(c, d, a, b, x[11], S23); /* 31 */ - b = GG(b, c, d, a, x[15], S24); /* 32 */ + // Round 2 + a = GG(a, b, c, d, x[00], S21); // 17 + d = GG(d, a, b, c, x[04], S22); // 18 + c = GG(c, d, a, b, x[08], S23); // 19 + b = GG(b, c, d, a, x[12], S24); // 20 + a = GG(a, b, c, d, x[01], S21); // 21 + d = GG(d, a, b, c, x[05], S22); // 22 + c = GG(c, d, a, b, x[09], S23); // 23 + b = GG(b, c, d, a, x[13], S24); // 24 + a = GG(a, b, c, d, x[02], S21); // 25 + d = GG(d, a, b, c, x[06], S22); // 26 + c = GG(c, d, a, b, x[10], S23); // 27 + b = GG(b, c, d, a, x[14], S24); // 28 + a = GG(a, b, c, d, x[03], S21); // 29 + d = GG(d, a, b, c, x[07], S22); // 30 + c = GG(c, d, a, b, x[11], S23); // 31 + b = GG(b, c, d, a, x[15], S24); // 32 - /* Round 3 */ - a = HH(a, b, c, d, x[00], S31); /* 33 */ - d = HH(d, a, b, c, x[08], S32); /* 34 */ - c = HH(c, d, a, b, x[04], S33); /* 35 */ - b = HH(b, c, d, a, x[12], S34); /* 36 */ - a = HH(a, b, c, d, x[02], S31); /* 37 */ - d = HH(d, a, b, c, x[10], S32); /* 38 */ - c = HH(c, d, a, b, x[06], S33); /* 39 */ - b = HH(b, c, d, a, x[14], S34); /* 40 */ - a = HH(a, b, c, d, x[01], S31); /* 41 */ - d = HH(d, a, b, c, x[09], S32); /* 42 */ - c = HH(c, d, a, b, x[05], S33); /* 43 */ - b = HH(b, c, d, a, x[13], S34); /* 44 */ - a = HH(a, b, c, d, x[03], S31); /* 45 */ - d = HH(d, a, b, c, x[11], S32); /* 46 */ - c = HH(c, d, a, b, x[07], S33); /* 47 */ - b = HH(b, c, d, a, x[15], S34); /* 48 */ + // Round 3 + a = HH(a, b, c, d, x[00], S31); // 33 + d = HH(d, a, b, c, x[08], S32); // 34 + c = HH(c, d, a, b, x[04], S33); // 35 + b = HH(b, c, d, a, x[12], S34); // 36 + a = HH(a, b, c, d, x[02], S31); // 37 + d = HH(d, a, b, c, x[10], S32); // 38 + c = HH(c, d, a, b, x[06], S33); // 39 + b = HH(b, c, d, a, x[14], S34); // 40 + a = HH(a, b, c, d, x[01], S31); // 41 + d = HH(d, a, b, c, x[09], S32); // 42 + c = HH(c, d, a, b, x[05], S33); // 43 + b = HH(b, c, d, a, x[13], S34); // 44 + a = HH(a, b, c, d, x[03], S31); // 45 + d = HH(d, a, b, c, x[11], S32); // 46 + c = HH(c, d, a, b, x[07], S33); // 47 + b = HH(b, c, d, a, x[15], S34); // 48 _state0 = unchecked(_state0 + a); _state1 = unchecked(_state1 + b); @@ -216,30 +216,30 @@ namespace System.Security.Cryptography } static readonly byte[]/*!*/ PADDING; - /* - { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - */ + // + //{ + // 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + //}; + // - /* F, G and H are basic MD4 functions. - */ + // F, G and H are basic MD4 functions. + // static uint F(uint x, uint y, uint z) { return (x & y) | ((~x) & z); } static uint G(uint x, uint y, uint z) { return (x & y) | (x & z) | (y & z); } static uint H(uint x, uint y, uint z) { return x ^ y ^ z; } - /* ROTATE_LEFT rotates x left n bits. - */ + // ROTATE_LEFT rotates x left n bits. + // static uint RotateLeft(uint x, int n) { return (x << n) | (x >> (32 - n)); } - /* FF, GG and HH are transformations for rounds 1, 2 and 3 */ - /* Rotation is separate from addition to prevent recomputation */ - /* (Or at least it used to be, when these were C macros. */ + // FF, GG and HH are transformations for rounds 1, 2 and 3 + // Rotation is separate from addition to prevent recomputation + // (Or at least it used to be, when these were C macros. static uint FF(uint a, uint b, uint c, uint d, uint x, int s) { @@ -260,10 +260,10 @@ namespace System.Security.Cryptography } - /* Decodes input (unsigned char) into output (uint). Assumes len is - a multiple of 4. - - */ + // Decodes input (unsigned char) into output (uint). Assumes len is + // a multiple of 4. +// + // /// /// @@ -279,8 +279,7 @@ namespace System.Security.Cryptography int outpos = outputoffset; - for (int j = 0; j < length; j += 4) - { + for (int j = 0; j < length; j += 4) { uint value = ((uint)input[inputoffset + j]) | (((uint)input[inputoffset + j + 1]) << 8) | (((uint)input[inputoffset + j + 2]) << 16) @@ -314,17 +313,16 @@ namespace System.Security.Cryptography if (length == 0) return; - /* Compute number of bytes mod 64 */ + // Compute number of bytes mod 64 int index = (int)((_count >> 3) & 0x3F); _count += length << 3; int partLen = 64 - index; - /* Transform as many times as possible. - */ + // Transform as many times as possible. + // int i; - if (length >= partLen) - { + if (length >= partLen) { // MD4_memcpy((POINTER)&buffer_pinned[index], (POINTER)&input[offset], partLen); Array.Copy(input, offset, _buffer, index, partLen); Transform(_buffer, 0); @@ -337,7 +335,7 @@ namespace System.Security.Cryptography else i = 0; - /* Buffer remaining input */ + // Buffer remaining input // MD4_memcpy((POINTER)&_buffer[index], (POINTER)&input[i], inputLen-i); // MD4_memcpy((POINTER)&buffer_pinned[index], (POINTER)&input[offset + i], inputLen-i); Array.Copy(input, offset + i, _buffer, index, length - i); @@ -356,9 +354,9 @@ namespace System.Security.Cryptography return context.GetDigest(); } - /* MD4 finalization. Ends an MD4 message-digest operation, writing the - the message digest and zeroizing the context. - */ + // MD4 finalization. Ends an MD4 message-digest operation, writing the + // the message digest and zeroizing the context. + // public MD4Digest GetDigest() { if (_done) @@ -366,7 +364,7 @@ namespace System.Security.Cryptography byte[]/*!*/ bits = new byte[8]; - /* Save number of bits */ + // Save number of bits EncodeLe((uint)(_count & 0xffffffffu), bits, 0); EncodeLe((uint)(_count >> 32), bits, 4); @@ -439,8 +437,7 @@ namespace System.Security.Cryptography string hex = "0123456789abcdef"; byte[] arr = ToArray(); StringBuilder buffer = new StringBuilder(DigestLength * 2); - for (int i = 0; i < 0x10; i++) - { + for (int i = 0; i < 0x10; i++) { byte b = arr[i]; buffer.Append(hex[b >> 4]); buffer.Append(hex[b & 0xf]); diff --git a/base/Libraries/Crypto/MD5.cs b/base/Libraries/Crypto/MD5.cs index 339cd0b..927bc34 100644 --- a/base/Libraries/Crypto/MD5.cs +++ b/base/Libraries/Crypto/MD5.cs @@ -4,12 +4,9 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: MD5.cs -// // Note: // A simple MD5 hash implementation. Ported from C++ code, -// originally written by Peter L. Montgomery, used in the -// Rotor / Coriolis codebase. Port by maiken. +// originally used in the Rotor / Coriolis codebase. // // For algorithmic background see (for example) // @@ -30,12 +27,6 @@ // QA76.9A25M643 // // Also see RFC (Request For Comments) 1321 from April, 1992. -// A July 2000 search for "rfc md5" or "rfc 1321" -// at www.yahoo.com found the document at -// -// http://www.cis.ohio-state.edu/htbin/rfc/rfc1321.html -// -// and other sites. // using System; @@ -85,39 +76,38 @@ namespace Microsoft.Singularity.Crypto public MD5() { - Waiting_Data = new uint[16]; - NBit_Total = new uint[2]; - Partial_Hash = new uint[4]; - } + Waiting_Data = new uint[16]; + NBit_Total = new uint[2]; + Partial_Hash = new uint[4]; + } public byte[] Hash(byte[] data) { - Reset(); + Reset(); Update(data); return Finish(); } - private void Reset() - { - for (uint i = 0; i != 16; i++) - { - Waiting_Data[i] = 0; - } - - NBit_Total[0] = NBit_Total[1] = 0; + private void Reset() + { + for (uint i = 0; i != 16; i++) { + Waiting_Data[i] = 0; + } - /* - Initialize hash variables. + NBit_Total[0] = NBit_Total[1] = 0; - N.B. The initial values in RFC 1321 appear - in byte-reversed order. Bruce Schneier's - 2nd edition neglects to rearrange them. - */ - Partial_Hash[0] = 0x67452301; - Partial_Hash[1] = 0xefcdab89; - Partial_Hash[2] = ~Partial_Hash[0]; - Partial_Hash[3] = ~Partial_Hash[1]; - } + // + // Initialize hash variables. +// + // N.B. The initial values in RFC 1321 appear + // in byte-reversed order. Bruce Schneier's + // 2nd edition neglects to rearrange them. + // + Partial_Hash[0] = 0x67452301; + Partial_Hash[1] = 0xefcdab89; + Partial_Hash[2] = ~Partial_Hash[0]; + Partial_Hash[3] = ~Partial_Hash[1]; + } // Shift val left by num bits, wrapping bits around. private uint RotL(uint val, byte num) @@ -129,9 +119,9 @@ namespace Microsoft.Singularity.Crypto return val; } - /* - Update the MD5 hash from a fresh 64 bytes of data. - */ + // + //Update the MD5 hash from a fresh 64 bytes of data. + // private void ProcessBlock() { uint a = Partial_Hash[0], b = Partial_Hash[1]; @@ -140,8 +130,7 @@ namespace Microsoft.Singularity.Crypto int i; int consindex = 0; - for (i = 0; i != 16; i++) - { + for (i = 0; i != 16; i++) { // Copy to local array, zero original // Make two copies, to simplify indexing uint datval = Waiting_Data[i]; @@ -150,8 +139,7 @@ namespace Microsoft.Singularity.Crypto } // Round 1 - for (i = -16; i != 0; i += 4) - { + for (i = -16; i != 0; i += 4) { // Rewrite (X & Y) | (~X & Z) as Z ^ (X & (Y ^ Z)) // [Easily validated by checking X = 0 and X = 1 cases.] // This avoids ANDNOT (which X86 lacks) and needs only @@ -176,8 +164,7 @@ namespace Microsoft.Singularity.Crypto } // Round 2 - for (i = -16; i != 0; i += 4) - { + for (i = -16; i != 0; i += 4) { // Rewrite (Z & X) | (~Z & Y) as Y ^ (Z & (X ^ Y)) a += msg16[i+17] + MD5_Const[consindex] + (c ^ (d & (b ^ c))); a = b + RotL(a, 5); @@ -197,8 +184,7 @@ namespace Microsoft.Singularity.Crypto } // Round 3 - for (i = 16; i != 0; i -= 4) - { + for (i = 16; i != 0; i -= 4) { a += msg16[i+5] + MD5_Const[consindex] + ((b ^ c) ^ d); a = b + RotL(a, 4); consindex++; @@ -217,8 +203,7 @@ namespace Microsoft.Singularity.Crypto } // Round 4 - for (i = 16; i != 0; i -= 4) - { + for (i = 16; i != 0; i -= 4) { a += msg16[i ] + MD5_Const[consindex] + (c ^ (~d | b)); a = b + RotL(a, 6); consindex++; @@ -244,9 +229,9 @@ namespace Microsoft.Singularity.Crypto Partial_Hash[3] = unchecked(Partial_Hash[3] + d); } - /* - Append data to a partially hashed MD5 message. - */ + // + //Append data to a partially hashed MD5 message. + // private void Update(byte[] data) { uint nbit_occupied = NBit_Total[0] & 511; @@ -257,26 +242,23 @@ namespace Microsoft.Singularity.Crypto NBit_Total[0] += nbitnew_low; NBit_Total[1] += (uint)(data.Length >> 29); - if (NBit_Total[0] < nbitnew_low) - { + if (NBit_Total[0] < nbitnew_low) { // Overflow (?) NBit_Total[1]++; } - /* Advance to word boundary in waiting_data */ - if ((nbit_occupied & 31) != 0) - { + // Advance to word boundary in waiting_data + if ((nbit_occupied & 31) != 0) { waitingDataIndex = nbit_occupied / 32; - while ((nbit_occupied & 31) != 0 && dataIndex < data.Length) - { + while ((nbit_occupied & 31) != 0 && dataIndex < data.Length) { Waiting_Data[waitingDataIndex] |= (uint)data[dataIndex] << (int)(nbit_occupied & 31); dataIndex++; nbit_occupied += 8; } } // if nbit_occupied - /* Transfer 4 bytes at a time */ + // Transfer 4 bytes at a time do { uint nword_occupied = nbit_occupied/32; @@ -286,19 +268,17 @@ namespace Microsoft.Singularity.Crypto waitingDataIndex = nword_occupied; nbit_occupied += 32*nwcopy; - while (nwcopy != 0) - { + while (nwcopy != 0) { uint byte0 = (uint)data[dataIndex++]; uint byte1 = (uint)data[dataIndex++]; uint byte2 = (uint)data[dataIndex++]; uint byte3 = (uint)data[dataIndex++]; - Waiting_Data[waitingDataIndex++] = /* Little endian */ + Waiting_Data[waitingDataIndex++] = // Little endian byte0 | (byte1 << 8) | (byte2 << 16) | (byte3 << 24); nwcopy--; } - if (nbit_occupied == 512) - { + if (nbit_occupied == 512) { ProcessBlock(); nbit_occupied = 0; waitingDataIndex -= 16; @@ -308,8 +288,7 @@ namespace Microsoft.Singularity.Crypto Debug.Assert(waitingDataIndex == nbit_occupied/32); - while (dataIndex < data.Length) - { + while (dataIndex < data.Length) { uint new_byte = (uint)data[dataIndex++]; Debug.Assert((nbit_occupied & 31) <= 16); Waiting_Data[waitingDataIndex] |= new_byte << (int)(nbit_occupied & 31); @@ -319,9 +298,9 @@ namespace Microsoft.Singularity.Crypto Debug.Assert(nbit_occupied == (NBit_Total[0] & 511)); } - /* - Finish an MD5 hash. - */ + // + //Finish an MD5 hash. + // private byte[] Finish() { uint nbit0 = NBit_Total[0]; @@ -343,7 +322,7 @@ namespace Microsoft.Singularity.Crypto // Append zero bits until length (in bits) is 448 mod 512. // Then append the length, in bits. // Here we assume the buffer was zeroed earlier. - if (nbit_occupied > 448) // If fewer than 64 bits left + if (nbit_occupied > 448) // If fewer than 64 bits left { ProcessBlock(); nbit_occupied = 0; @@ -353,12 +332,11 @@ namespace Microsoft.Singularity.Crypto Waiting_Data[15] = nbit1; ProcessBlock(); - /* Copy final digest to byte array */ + // Copy final digest to byte array byte[] digest = new byte[16]; - for (i = 0; i != 4; i++) - { + for (i = 0; i != 4; i++) { uint dwi = Partial_Hash[i]; // Little-endian diff --git a/base/Libraries/Crypto/Random.cs b/base/Libraries/Crypto/Random.cs index 6038ddb..68506e8 100644 --- a/base/Libraries/Crypto/Random.cs +++ b/base/Libraries/Crypto/Random.cs @@ -1,14 +1,13 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; -namespace Microsoft.Singularity.Crypto.PublicKey { +namespace Microsoft.Singularity.Crypto.PublicKey +{ public class Random { uint[] _state; const int _N = 8; @@ -37,16 +36,24 @@ namespace Microsoft.Singularity.Crypto.PublicKey { } public uint Next() { _Mix(); return _state[_N - 1]; } public uint Next(uint n) { - if (n <= 0) { throw new ArgumentException(); } + if (n <= 0) { + throw new ArgumentException(); + } uint mask = 1; - while (mask < n - 1) { mask = mask << 1 | 1; } + while (mask < n - 1) { + mask = mask << 1 | 1; + } while (true) { uint next = Next() & mask; - if (next < n) { return next; } + if (next < n) { + return next; + } } } public uint Next(uint lo, uint hi) { - if (lo >= hi) { throw new ArgumentException(); } + if (lo >= hi) { + throw new ArgumentException(); + } return lo + Next(hi - lo); } public Random(uint n) { @@ -55,8 +62,6 @@ namespace Microsoft.Singularity.Crypto.PublicKey { _state[i] = n; } } - public Random() : this(250787849) { - // John DeTreville's Social Security Number - } + public Random() : this(1372455507) { } // Randomly selected 32-bit integer } } diff --git a/base/Libraries/Crypto/Digit.cs b/base/Libraries/Crypto/Rsa/Digit.cs similarity index 82% rename from base/Libraries/Crypto/Digit.cs rename to base/Libraries/Crypto/Rsa/Digit.cs index 58e54e0..dbb7f9b 100644 --- a/base/Libraries/Crypto/Digit.cs +++ b/base/Libraries/Crypto/Rsa/Digit.cs @@ -1,16 +1,16 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; using System.Diagnostics; -namespace Microsoft.Singularity.Crypto.PublicKey { - struct Digit { +namespace Microsoft.Singularity.Crypto.PublicKey +{ + struct Digit + { readonly UInt32 _digit; Digit(UInt32 digit) { _digit = digit; } public static implicit operator UInt32(Digit d) { return d._digit; } @@ -37,11 +37,15 @@ namespace Microsoft.Singularity.Crypto.PublicKey { internal static int SigBitN(Digit d) { d |= 1; int bitN = BitN; - while (d < 1U << BitN - 5) { bitN -= 5; d <<= 5; } + while (d < 1U << BitN - 5) { + bitN -= 5; d <<= 5; + } return bitN - leadingZeroBitN[d >> BitN - 4]; } internal static Digit TwoAdicInverse(Digit d) { - if ((d & 1) == 0) { throw new ArgumentException(); } + if ((d & 1) == 0) { + throw new ArgumentException(); + } UInt32 dInverse = unchecked(3 * d ^ 2) , error = unchecked(1 - d * dInverse); Debug.Assert((error & 31) == 0, "internal error"); @@ -58,7 +62,9 @@ namespace Microsoft.Singularity.Crypto.PublicKey { trailingZeroN = new byte[] { 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }; internal static Digit OddGcd(Digit d1, Digit d2) { - if (((d1 | d2) & 1) == 0) { throw new ArgumentException(); } + if (((d1 | d2) & 1) == 0) { + throw new ArgumentException(); + } if (d1 == 0 || d2 == 0) { Debug.Assert(false, "untested code"); return d1 + d2; @@ -81,7 +87,9 @@ namespace Microsoft.Singularity.Crypto.PublicKey { } internal static Digit Random(UInt32 dlow, UInt32 dhigh, Random generator) { - if (dhigh < dlow) { throw new ArgumentException(); } + if (dhigh < dlow) { + throw new ArgumentException(); + } UInt32 spread = dhigh - dlow; int shiftBitN = BitN - SigBitN(spread | 1); UInt32 result = 0; diff --git a/base/Libraries/Crypto/Digit2.cs b/base/Libraries/Crypto/Rsa/Digit2.cs similarity index 80% rename from base/Libraries/Crypto/Digit2.cs rename to base/Libraries/Crypto/Rsa/Digit2.cs index 5642c75..4bb0204 100644 --- a/base/Libraries/Crypto/Digit2.cs +++ b/base/Libraries/Crypto/Rsa/Digit2.cs @@ -1,15 +1,14 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; using System.Diagnostics; -namespace Microsoft.Singularity.Crypto.PublicKey { +namespace Microsoft.Singularity.Crypto.PublicKey +{ internal class Digit2 { internal static Digit Lo(UInt64 x) { return unchecked((UInt32)(x & Digit.MaxValue)); @@ -21,7 +20,9 @@ namespace Microsoft.Singularity.Crypto.PublicKey { internal static void Div(UInt64 num, Digit denom, out Digit q, out Digit r) { Digit numHi = Hi(num), numLo = Lo(num); - if (numHi >= denom) { throw new ArgumentException(); } + if (numHi >= denom) { + throw new ArgumentException(); + } q = (Digit)(num / denom); r = unchecked(numLo - denom * q); Digit rr = unchecked((Digit)(num - denom * q)); diff --git a/base/Libraries/Crypto/Digits.cs b/base/Libraries/Crypto/Rsa/Digits.cs similarity index 86% rename from base/Libraries/Crypto/Digits.cs rename to base/Libraries/Crypto/Rsa/Digits.cs index dfcac56..7463c2a 100644 --- a/base/Libraries/Crypto/Digits.cs +++ b/base/Libraries/Crypto/Rsa/Digits.cs @@ -1,16 +1,16 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; using System.Diagnostics; -namespace Microsoft.Singularity.Crypto.PublicKey { - class Digits { +namespace Microsoft.Singularity.Crypto.PublicKey +{ + class Digits + { readonly Digit[] _digits; readonly int _digitI; internal Digits(int n) { _digits = new Digit[n]; _digitI = 0; } @@ -23,24 +23,30 @@ namespace Microsoft.Singularity.Crypto.PublicKey { get { if (_digitI + i < _digits.Length) { return _digits[_digitI + i]; - } else { + } + else { return 0; } } set { if (_digitI + i < _digits.Length) { _digits[_digitI + i] = value; - } else { + } + else { Debug.Assert(value == 0, "internal error"); } } } int _N { get { return _digits.Length - _digitI; } } internal void _Set(Digits from, int n) { - for (int i = 0; i < n; i++) { this[i] = from[i]; } + for (int i = 0; i < n; i++) { + this[i] = from[i]; + } } internal void _SetAll(Digit val, int n) { - for (int i = 0; i < n; i++) { this[i] = val; } + for (int i = 0; i < n; i++) { + this[i] = val; + } } internal bool _Overlaps(Digits that) { return _digits.Equals(that._digits); @@ -51,7 +57,9 @@ namespace Microsoft.Singularity.Crypto.PublicKey { public override string ToString() { string s = "("; for (int i = _digitI; i < _N; i++) { - if (i > _digitI) { s += ","; } + if (i > _digitI) { + s += ","; + } s += _digits[i].ToString(); } return s + ")"; @@ -72,7 +80,9 @@ namespace Microsoft.Singularity.Crypto.PublicKey { } internal static int SigDigitN(Digits a, int aN) { int i = aN; - while (i != 0 && a[i - 1] == 0) { i--; } + while (i != 0 && a[i - 1] == 0) { + i--; + } return i; } internal static int SigBitN(Digits a, int aN) { @@ -119,10 +129,11 @@ namespace Microsoft.Singularity.Crypto.PublicKey { Debug.Assert(false, "untested code"); b[i] = i + dtranslate < n ? b[i + dtranslate] : 0; } - } else if (itranslate > 0) { + } + else if (itranslate > 0) { Debug.Assert(false, "untested code"); int dtranslate = itranslate; - for (int i = n; i-- != 0; ) { + for (int i = n; i-- != 0;) { Debug.Assert(false, "untested code"); b[i] = i >= dtranslate ? b[i - dtranslate] : 0; } @@ -130,7 +141,9 @@ namespace Microsoft.Singularity.Crypto.PublicKey { } internal static Digit ShiftLost(Digits a, int bitScale, Digits b, int n) { - if (n == 0) { Debug.Assert(false, "untested code"); return 0; } + if (n == 0) { + Debug.Assert(false, "untested code"); return 0; + } if (bitScale == 0) { Debug.Assert(false, "untested code"); b._Set(a, n); @@ -138,26 +151,37 @@ namespace Microsoft.Singularity.Crypto.PublicKey { } Digit bitNLost = 0; if (bitScale > 0) { - if (bitScale > Digit.BitN) { throw new ArgumentException(); } + if (bitScale > Digit.BitN) { + throw new ArgumentException(); + } if (bitScale == Digit.BitN) { bitNLost = a[n - 1]; - for (int i = n - 1; i != 0; i--) { b[i] = a[i - 1]; } + for (int i = n - 1; i != 0; i--) { + b[i] = a[i - 1]; + } b[0] = 0; - } else { + } + else { for (int i = 0; i != n; i++) { Digit bNew = a[i] << bitScale | bitNLost; bitNLost = a[i] >> Digit.BitN - bitScale; b[i] = bNew; } } - } else { - if (bitScale < -Digit.BitN) { throw new ArgumentException(); } + } + else { + if (bitScale < - Digit.BitN) { + throw new ArgumentException(); + } if (bitScale == -Digit.BitN) { bitNLost = a[0]; - for (int i = 1; i != n; i++) { b[i - 1] = a[i]; } + for (int i = 1; i != n; i++) { + b[i - 1] = a[i]; + } b[n - 1] = 0; - } else { - for (int i = n; i-- != 0; ) { + } + else { + for (int i = n; i-- != 0;) { Digit bNew = a[i] >> -bitScale | bitNLost; bitNLost = a[i] << Digit.BitN + bitScale; b[i] = bNew; @@ -178,7 +202,8 @@ namespace Microsoft.Singularity.Crypto.PublicKey { p2 = b; n1 = aN; n2 = bN; - } else { + } + else { p2 = a; p1 = b; n2 = aN; @@ -205,7 +230,9 @@ namespace Microsoft.Singularity.Crypto.PublicKey { } internal static void BytesToDigits(byte[] bytes, int bytesI, Digits digits, int bitN) { - if (bitN == 0) { Debug.Assert(false, "untested code"); return; } + if (bitN == 0) { + Debug.Assert(false, "untested code"); return; + } int digitN = (bitN + (Digit.BitN - 1)) / Digit.BitN; digits._SetAll(0, digitN); for (int iDigit = 0; iDigit != digitN; iDigit++) { @@ -243,7 +270,9 @@ namespace Microsoft.Singularity.Crypto.PublicKey { , Digits q , Digits r ) { - if (denomN == 0) { throw new DivideByZeroException(); } + if (denomN == 0) { + throw new DivideByZeroException(); + } if (num == null || denom == null || r == null) { throw new ArgumentNullException(); } @@ -256,30 +285,39 @@ namespace Microsoft.Singularity.Crypto.PublicKey { ); } Digit dlead = denom[denomN - 1]; - if (dlead == 0) { throw new ArgumentException(); } + if (dlead == 0) { + throw new ArgumentException(); + } if (numN < denomN) { Debug.Assert(false, "untested code"); Set(num, numN, r, denomN); return; } - if (denomN == 1) { Div(num, dlead, recip, q, numN, r); return; } + if (denomN == 1) { + Div(num, dlead, recip, q, numN, r); return; + } if (recip == null) { recip = new Reciprocal(); DivPrecondition(denom, denomN, recip); } r[denomN - 1] = 0; r._Set(num + (numN - denomN + 1), denomN - 1); - for (int iq = numN - denomN + 1; iq-- != 0; ) { + for (int iq = numN - denomN + 1; iq-- != 0;) { Digit rTop = r[denomN - 1]; - for (int i = denomN - 1; i != 0; i--) { r[i] = r[i - 1]; } + for (int i = denomN - 1; i != 0; i--) { + r[i] = r[i - 1]; + } r[0] = num[iq]; Digit qest; if (rTop == 0 && Compare(r, denom, denomN) < 0) { qest = 0; - } else { + } + else { qest = recip.EstQuotient(rTop, r[denomN - 1], r[denomN - 2]); - if (qest < Digit.MaxValue) { qest += 1; } + if (qest < Digit.MaxValue) { + qest += 1; + } Digit borrow = Decumulate(denom, qest, r, denomN); if (borrow > rTop) { qest -= 1; @@ -287,7 +325,9 @@ namespace Microsoft.Singularity.Crypto.PublicKey { } Debug.Assert(borrow == rTop, "internal error"); } - if (q != null) { q[iq] = qest; } + if (q != null) { + q[iq] = qest; + } } } static void Div( @@ -303,37 +343,46 @@ namespace Microsoft.Singularity.Crypto.PublicKey { if (nLeft > 0 && numer[nLeft - 1] < den) { nLeft--; carry = numer[nLeft]; - if (q != null) { q[nLeft] = 0; } + if (q != null) { + q[nLeft] = 0; + } } if (recip == null && nLeft < 2) { - for (int i = nLeft; i-- != 0; ) { + for (int i = nLeft; i-- != 0;) { Digit qest = 0; Digit2.Div((UInt64)carry << Digit.BitN | numer[i] , den , out qest , out carry); - if (q != null) { q[i] = qest; } + if (q != null) { + q[i] = qest; + } } - } else { + } + else { if (recip == null) { recip = new Reciprocal(); DivPrecondition(new Digits(new Digit[] { den }), 1, recip); } - for (int i = nLeft; i-- != 0; ) { + for (int i = nLeft; i-- != 0;) { Digit qest = 0; Digit2.Div((UInt64)carry << Digit.BitN | numer[i] , den , recip , out qest , out carry); - if (q != null) { q[i] = qest; } + if (q != null) { + q[i] = qest; + } } } r[0] = carry; } internal static void DivPrecondition(Digits denom, int denomN, Reciprocal recip) { - if (denom == null) { throw new ArgumentNullException(); } + if (denom == null) { + throw new ArgumentNullException(); + } if (denomN == 0 || denom[denomN - 1] == 0) { throw new ArgumentException(); } @@ -351,22 +400,30 @@ namespace Microsoft.Singularity.Crypto.PublicKey { , dShiftHi , out recipMpy , out r); - if (Digit2.Hi((UInt64)recipMpy * dShiftLo) > r) { recipMpy -= 1; } + if (Digit2.Hi((UInt64)recipMpy * dShiftLo) > r) { + recipMpy -= 1; + } r = (Digit.MaxValue >> recipBitShift) - denom[denomN - 1]; - for (int id = denomN; id-- != 0 && r < recipMpy; ) { + for (int id = denomN; id-- != 0 && r < recipMpy;) { UInt64 test1 = (UInt64)r << Digit.BitN | Digit.MaxValue - (id > 0 ? denom[id - 1] : 0) , test2 = (UInt64)recipMpy * denom[id]; - if (test2 > test1) { recipMpy -= 1; break; } + if (test2 > test1) { + recipMpy -= 1; break; + } test1 = test1 - test2; r = Digit2.Lo(test1); - if (Digit2.Hi(test1) != 0) { break; } + if (Digit2.Hi(test1) != 0) { + break; + } } recip._shiftBitN = recipBitShift; recip._multiplier = recipMpy; } static uint Add(Digits a, int aN, Digits b, int bN, Digits c) { - if (aN < bN || aN < 0 || bN < 0) { throw new ArgumentException(); } + if (aN < bN || aN < 0 || bN < 0) { + throw new ArgumentException(); + } uint carry = Add(a, b, c, bN); return Add(a + bN, carry, c + bN, aN - bN); } @@ -377,7 +434,8 @@ namespace Microsoft.Singularity.Crypto.PublicKey { Debug.Assert(false, "untested code"); carry = Add(b, bN, a, aN, c); cN = bN; - } else { + } + else { carry = Add(a, aN, b, bN, c); cN = aN; } @@ -392,7 +450,9 @@ namespace Microsoft.Singularity.Crypto.PublicKey { Digit bi = a[i] + carry; b[i] = bi; if (bi >= carry) { - if (a != b) { (b + i + 1)._Set(a + i + 1, n - i - 1); } + if (a != b) { + (b + i + 1)._Set(a + i + 1, n - i - 1); + } return 0; } carry = 1; @@ -425,8 +485,12 @@ namespace Microsoft.Singularity.Crypto.PublicKey { } internal static int Compare(Digits a, int aN, Digits b, int bN) { int la = aN, lb = bN; - while (la > lb) { if (a[la - 1] != 0) { return +1; } la--; } - while (lb > la) { if (b[lb - 1] != 0) { return -1; } lb--; } + while (la > lb) { + if (a[la - 1] != 0) { return + 1; } la--; + } + while (lb > la) { + if (b[lb - 1] != 0) { return -1; } lb--; + } Debug.Assert(la == lb, "internal error"); while (la != 0) { if (a[la - 1] != b[la - 1]) { @@ -437,14 +501,16 @@ namespace Microsoft.Singularity.Crypto.PublicKey { return 0; } internal static int Compare(Digits a, Digits b, int n) { - for (int i = n; i-- != 0; ) { - if (a[i] != b[i]) { return a[i] > b[i] ? +1 : -1; } + for (int i = n; i-- != 0;) { + if (a[i] != b[i]) { + return a[i] > b[i] ? + 1 : - 1; + } } return 0; } internal static int CompareSum(Digits a, Digits b, Digits c, int n) { int sumPrev = 0; - for (int i = n; i-- != 0; ) { + for (int i = n; i-- != 0;) { Digit aval = a[i] , bval = b[i] , cval = c[i] @@ -467,7 +533,9 @@ namespace Microsoft.Singularity.Crypto.PublicKey { Digit ai = a[i]; b[i] = ai - borrow; if (ai >= borrow) { - if (a != b) { (b + i + 1)._Set(a + i + 1, n - i - 1); } + if (a != b) { + (b + i + 1)._Set(a + i + 1, n - i - 1); + } return 0; } borrow = 1; @@ -542,7 +610,8 @@ namespace Microsoft.Singularity.Crypto.PublicKey { if (topword0 > Digit.MaxValue - topword1) { if (ibig == 0) { topword0 -= topword1; - } else { + } + else { topword1 -= topword0; } Digit carry = Add(tempsMul[0] @@ -569,7 +638,8 @@ namespace Microsoft.Singularity.Crypto.PublicKey { ab0 -= q * ab1; m00 += q * m10; m01 += q * m11; - } else { + } + else { do { ab0 -= ab1; m00 += m10; @@ -578,13 +648,16 @@ namespace Microsoft.Singularity.Crypto.PublicKey { } } Debug.Assert(ab1 > ab0, "internal error"); - if (ab0 == 0) { break; } + if (ab0 == 0) { + break; + } if (ab1 >> 2 >= ab0) { Digit q = ab1 / ab0; ab1 -= q * ab0; m10 += q * m00; m11 += q * m01; - } else { + } + else { do { ab1 -= ab0; m10 += m00; @@ -604,13 +677,15 @@ namespace Microsoft.Singularity.Crypto.PublicKey { Digits carrys = new Digits(2); Mul22U(mat22, tempsMul[0], tempsMul[1], mulN, carrys); if (carrys[0] == 0 && carrys[1] == 0) { - } else { + } + else { Debug.Assert(mulN < bN, "internal error"); tempsMul[0][mulN] = carrys[0]; tempsMul[1][mulN] = carrys[1]; mulN++; } - } else if ( + } + else if ( sigbitN0 > sigbitN1 + Digit.BitN / 2 || sigbitN1 > sigbitN0 + Digit.BitN / 2 ) { @@ -632,7 +707,8 @@ namespace Microsoft.Singularity.Crypto.PublicKey { , mulN , tempsMul[ibig] , out mulN); - } else { + } + else { int norm = (int)(Digit.BitN * maxlab - maxSigBitN); pab0top[1] = pab1top[1] = 0; Debug.Assert(maxlab >= 2, "internal error"); @@ -674,7 +750,8 @@ namespace Microsoft.Singularity.Crypto.PublicKey { Digits carrys = new Digits(2); Mul22U(mat22, tempsMul[0], tempsMul[1], mulN, carrys); if (carrys[0] == 0 && carrys[1] == 0) { - } else { + } + else { Debug.Assert(mulN < bN, "internal error"); tempsMul[0][mulN] = carrys[0]; tempsMul[1][mulN] = carrys[1]; @@ -690,7 +767,8 @@ namespace Microsoft.Singularity.Crypto.PublicKey { , "internal error"); if (igcd == 0) { ainvmodb._Set(tempsMul[0], bN); - } else { + } + else { Sub(tempsMul[0], tempsMul[1], ainvmodb, bN); } if (binvmoda != null) { @@ -746,7 +824,8 @@ namespace Microsoft.Singularity.Crypto.PublicKey { Debug.Assert((m00 | m01) < 1U << Digit.BitN - 1 , "internal error"); } - } else { + } + else { Digit overflowTest; do { m00 += m10; @@ -785,7 +864,8 @@ namespace Microsoft.Singularity.Crypto.PublicKey { Debug.Assert((m10 | m11) < 1U << Digit.BitN - 1 , "internal error"); } - } else { + } + else { Digit overflowTest; do { m10 += m00; diff --git a/base/Libraries/Crypto/Modular.cs b/base/Libraries/Crypto/Rsa/Modular.cs similarity index 76% rename from base/Libraries/Crypto/Modular.cs rename to base/Libraries/Crypto/Rsa/Modular.cs index a13ed4b..3787bb4 100644 --- a/base/Libraries/Crypto/Modular.cs +++ b/base/Libraries/Crypto/Rsa/Modular.cs @@ -1,41 +1,50 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; using System.Diagnostics; -namespace Microsoft.Singularity.Crypto.PublicKey { - class Modular { +namespace Microsoft.Singularity.Crypto.PublicKey +{ + class Modular + { internal static void Add(Digits a, Digits b, Digits c, Digits mod, int n) { Debug.Assert(n != 0, "internal error"); Digit alead = a[n - 1], blead = b[n - 1], mlead = mod[n - 1]; - if (alead >= mlead) { ValidateData(a, mod, n); } - if (blead >= mlead) { ValidateData(b, mod, n); } + if (alead >= mlead) { + ValidateData(a, mod, n); + } + if (blead >= mlead) { + ValidateData(b, mod, n); + } int test; if (blead > mlead - alead) { test = +1; - } else if (mlead - alead - blead > 1) { + } + else if (mlead - alead - blead > 1) { test = -1; - } else { + } + else { test = Digits.CompareSum(a, b, mod, n); } if (test >= 0) { int carry = Digits.AddSub(a, b, mod, c, n); Debug.Assert(carry == 0, "internal error"); - } else { + } + else { uint carry = Digits.Add(a, b, c, n); Debug.Assert(carry == 0, "internal error"); } } internal static void Neg(Digits a, Digits b, Digits mod, int n) { Digit allZero = 0; - for (int i = 0; i != n; i++) { allZero |= a[i]; b[i] = a[i]; } + for (int i = 0; i != n; i++) { + allZero |= a[i]; b[i] = a[i]; + } if (allZero != 0) { if (Digits.Sub(mod, b, b, n) != 0) { throw new ArgumentException(); @@ -49,14 +58,18 @@ namespace Microsoft.Singularity.Crypto.PublicKey { int itest; if (alead == blead) { itest = Digits.Compare(a, b, n - 1); - } else { + } + else { itest = alead < blead ? -1 : +1; } if (itest < 0) { - if (blead >= mlead) { ValidateData(b, mod, n); } + if (blead >= mlead) { + ValidateData(b, mod, n); + } int carry = Digits.AddSub(a, mod, b, c, n); Debug.Assert(carry == 0, "internal error"); - } else { + } + else { if (alead >= mlead) { Debug.Assert(false, "untested code"); ValidateData(a, mod, n); @@ -78,7 +91,9 @@ namespace Microsoft.Singularity.Crypto.PublicKey { arr[sigDigitN - 1] = 0; sigDigitN--; } - if (sigDigitN == 0) { throw new ArgumentException(); } + if (sigDigitN == 0) { + throw new ArgumentException(); + } Digit nlead = mod[sigDigitN - 1]; int ntry = 0; do { diff --git a/base/Libraries/Crypto/Modulus.cs b/base/Libraries/Crypto/Rsa/Modulus.cs similarity index 89% rename from base/Libraries/Crypto/Modulus.cs rename to base/Libraries/Crypto/Rsa/Modulus.cs index fab6d69..1426968 100644 --- a/base/Libraries/Crypto/Modulus.cs +++ b/base/Libraries/Crypto/Rsa/Modulus.cs @@ -1,16 +1,16 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; using System.Diagnostics; -namespace Microsoft.Singularity.Crypto.PublicKey { - class Modulus { +namespace Microsoft.Singularity.Crypto.PublicKey +{ + class Modulus + { internal readonly int _digitN; readonly int _scaleBitN; readonly bool _fromRight; @@ -35,7 +35,9 @@ namespace Microsoft.Singularity.Crypto.PublicKey { _leftRecip = new Reciprocal(); Digits.DivPrecondition(mod, digitN, _leftRecip); Digit mod0inv = 0; - if ((mod[0] & 1) != 0) { mod0inv = Digit.TwoAdicInverse(mod[0]); } + if ((mod[0] & 1) != 0) { + mod0inv = Digit.TwoAdicInverse(mod[0]); + } _rightRecip = mod0inv; int digitN2 = (digitN + 1) / 2; Digits temp = new Digits(digitN + digitN2); @@ -53,10 +55,13 @@ namespace Microsoft.Singularity.Crypto.PublicKey { Debug.Assert(q[digitN2] == 1, "internal error"); Digits.Add(r, 1U, r, digitN); Digits.Sub(mod, r, r, digitN); - } else { + } + else { _algorithm = new MulAlgorithm(_MulFromRight); _scaleBitN = Digit.BitN * digitN; - if (mod0inv == 0) { throw new ArgumentException(); } + if (mod0inv == 0) { + throw new ArgumentException(); + } _multiplier2[0] = mod0inv; temp[digitN] = Digits.Mul(mod, mod0inv, temp, digitN); Debug.Assert(temp[0] == 1, "internal error"); @@ -74,7 +79,8 @@ namespace Microsoft.Singularity.Crypto.PublicKey { void _BasePowerSquaring(Digits basepower) { _Mul(basepower, basepower, basepower); } - class Temps2000 { + class Temps2000 + { internal Modulus _modulus; internal Digits[] _bucket = new Digits[1L << 5]; internal bool[] _bucketBusy = new bool[1L << 5]; @@ -83,7 +89,8 @@ namespace Microsoft.Singularity.Crypto.PublicKey { Digits bloc = temps._bucket[ibucket]; if (temps._bucketBusy[ibucket]) { _Mul(bloc, mult, bloc); - } else { + } + else { temps._bucketBusy[ibucket] = true; bloc._Set(mult, _digitN); } @@ -114,14 +121,15 @@ namespace Microsoft.Singularity.Crypto.PublicKey { int highExponBitN = 0; bool bighBitNProcessed = false; Digits temp = bucketData; - for (int i = expBitNUsed; i-- != 0; ) { + for (int i = expBitNUsed; i-- != 0;) { Digit expBit = Digits.GetBit(exp, i); if (bighBitNProcessed) { _Mul(temp, temp, temp); if (expBit != 0) { Modular.Add(temp, temp, temp, _mod, _digitN); } - } else { + } + else { highExponBitN = (int)(2 * highExponBitN + expBit); if (i == 0 || 2 * highExponBitN >= shiftMax) { bighBitNProcessed = true; @@ -131,7 +139,8 @@ namespace Microsoft.Singularity.Crypto.PublicKey { } temps._bucket[1] = temp; Debug.Assert(bighBitNProcessed, "internal error"); - } else { + } + else { UInt32 ibucket; for (ibucket = 1; ibucket <= maxBucket; ibucket++) { Digits bloc = bucketData @@ -171,18 +180,22 @@ namespace Microsoft.Singularity.Crypto.PublicKey { bool squareNow = false; if (carried <= maxBucket) { ibucket = carried; - } else if ((carried & 1) == 0) { + } + else if ((carried & 1) == 0) { squareNow = true; - } else if (carried <= 3 * maxBucket) { + } + else if (carried <= 3 * maxBucket) { ibucket = maxBucket; - } else { + } + else { Debug.Assert(false, "untested code"); ibucket = carried & bucketMask; } if (squareNow) { carried /= 2; temps._modulus._BasePowerSquaring(basepower); - } else { + } + else { carried -= ibucket; temps._modulus._BucketMul(ibucket, basepower, temps); } @@ -194,7 +207,8 @@ namespace Microsoft.Singularity.Crypto.PublicKey { Digits bloci; if ((ibucket & 1) == 0) { jbucketMax = ibucket / 2; - } else { + } + else { jbucketMax = 1; } for ( @@ -272,13 +286,19 @@ namespace Microsoft.Singularity.Crypto.PublicKey { } internal void _Mul(Digits a, Digits b, Digits c) { Modular.ValidateData(a, _mod, _digitN); - if (a != b) { Modular.ValidateData(b, _mod, _digitN); } + if (a != b) { + Modular.ValidateData(b, _mod, _digitN); + } _algorithm(a, b, c); } void _Shift(Digits a, int n, Digits b) { - if (a != b) { b._Set(a, _digitN); } + if (a != b) { + b._Set(a, _digitN); + } Modular.ValidateData(a, _mod, _digitN); - if (n < 0 && (_mod[0] & 1) == 0) { throw new ArgumentException(); } + if (n < 0 && (_mod[0] & 1) == 0) { + throw new ArgumentException(); + } while (n > 0) { int shiftNow = n > Digit.BitN ? Digit.BitN : n; Digit carryOut = Digits.ShiftLost(b, shiftNow, b, _digitN) @@ -311,7 +331,8 @@ namespace Microsoft.Singularity.Crypto.PublicKey { aR = new Digits(_digitN); Digits.Div(a, aDigitN, _mod, _digitN, _leftRecip, null, aR); aRDigitN = _digitN; - } else { + } + else { aR = a; aRDigitN = aDigitN; } diff --git a/base/Libraries/Crypto/Prime.cs b/base/Libraries/Crypto/Rsa/Prime.cs similarity index 81% rename from base/Libraries/Crypto/Prime.cs rename to base/Libraries/Crypto/Rsa/Prime.cs index b4e8c8d..457a827 100644 --- a/base/Libraries/Crypto/Prime.cs +++ b/base/Libraries/Crypto/Rsa/Prime.cs @@ -1,20 +1,22 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; using System.Diagnostics; -namespace Microsoft.Singularity.Crypto.PublicKey { - class Prime { +namespace Microsoft.Singularity.Crypto.PublicKey +{ + class Prime + { const uint _RabinTestN = 50; const uint _IterationsAllowed = 5000; internal static Digits NewPrime(int pBitN, Random generator) { - if (pBitN < Digit.BitN) { throw new ArgumentException(); } + if (pBitN < Digit.BitN) { + throw new ArgumentException(); + } int pDigitN = (pBitN + (Digit.BitN - 1)) / Digit.BitN; Digits p = new Digits(pDigitN); while (true) { @@ -28,7 +30,9 @@ namespace Microsoft.Singularity.Crypto.PublicKey { p[0] |= 1; Digits.SetBit(p, pBitN - 1, 1); Digits.SetBit(p, pBitN - 2, 1); - if (DivisibleBySomeLowPrime(p, pDigitN)) { continue; } + if (DivisibleBySomeLowPrime(p, pDigitN)) { + continue; + } Modulus modulus = new Modulus(p, pDigitN, true); Digits minus1 = new Digits(pDigitN); Modular @@ -43,7 +47,8 @@ namespace Microsoft.Singularity.Crypto.PublicKey { , @base , modulus._mod , modulus._digitN); - } else { + } + else { Modular.NonZeroRandom( p, @base, pDigitN, generator); } @@ -84,13 +89,17 @@ namespace Microsoft.Singularity.Crypto.PublicKey { Digit mulby; unchecked { r += digits[i]; - if (r < digits[i]) { r -= product; } + if (r < digits[i]) { + r -= product; + } mulby = productInverse * r; Debug.Assert(mulby * product == r, "internal error"); } r = product - Digit2.Hi((UInt64)mulby * product); } - if (Digit.OddGcd(r, product) != 1) { return true; } + if (Digit.OddGcd(r, product) != 1) { + return true; + } } return false; } diff --git a/base/Libraries/Crypto/Rsa/Rsa.cs b/base/Libraries/Crypto/Rsa/Rsa.cs new file mode 100644 index 0000000..e9ca546 --- /dev/null +++ b/base/Libraries/Crypto/Rsa/Rsa.cs @@ -0,0 +1,282 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +using System.Diagnostics; + +namespace Microsoft.Singularity.Crypto.PublicKey +{ + public class Rsa { + public class Key { + int _eBitN; + byte[] _eBytes; + Digits _eDigits; + int _eDigitN { + get { return (_eBitN + (Digit.BitN - 1)) / Digit.BitN; } + } + int _modBitN; + byte[] _modBytes; + int _modByteN { get { return (_modBitN + 8 - 1) / 8; } } + Digits _modDigits; + int _modDigitN { + get { return (_modBitN + (Digit.BitN - 1)) / Digit.BitN; } + } + int[] _primeBitN; + byte[][] _primeBytes, _dBytes; + Modulus _modulus; + Modulus[] _privateModulus; + Digits[] _dDigits, _chineseDigits; + public Key(int bitN, Random generator) { + _modBitN = bitN; + // The public exponent is 2^16 + 1 + _eBitN = 17; + _eBytes = new byte[] { 1, 0, 1 }; + int p1BitN = (bitN + 1) / 2 + , p1ByteN = (p1BitN + 7) / 8 + , p1DigitN = (p1BitN + (Digit.BitN - 1)) / Digit.BitN + , p2BitN = bitN / 2 + , p2ByteN = (p2BitN + 7) / 8 + , p2DigitN = (p2BitN + (Digit.BitN - 1)) / Digit.BitN + , longerDigitN = p1DigitN > p2DigitN ? p1DigitN : p2DigitN; + Digits d1 = new Digits(p1DigitN), d2 = new Digits(p2DigitN); + _modDigits = new Digits(_modDigitN); + _eDigits = new Digits(1); + Digits gcd = new Digits(longerDigitN) + , temp = new Digits(longerDigitN); + Digits.BytesToDigits(_eBytes, 0, _eDigits, _eBitN); + int[] pBitN = new int[] { p1BitN, p2BitN }; + int nPrimeFound = 0; + Digits p1 = null, p2 = null; + while (nPrimeFound != 2) { + int pNowBitN = pBitN[nPrimeFound] + , pNowDigitN + = (pNowBitN + (Digit.BitN - 1)) / Digit.BitN; + Digits pNow = Prime.NewPrime(pNowBitN, generator); + if (nPrimeFound == 0) { + p1 = pNow; } else { p2 = pNow; + } + Digits.Sub(pNow, 1, temp, pNowDigitN); + int lgcd; + Digits.ExtendedGcd(_eDigits + , _eDigitN + , temp + , pNowDigitN + , nPrimeFound == 0 ? d1 : d2 + , null + , gcd + , out lgcd); + if (Digits.Compare(gcd, 1, lgcd) != 0) { + Debug.Assert(false, "untested code"); + continue; + } + if ( + nPrimeFound == 1 + && Digits.Compare(p1, p1DigitN, p2, p2DigitN) == 0 + ) { + Debug.Assert(false, "untested code"); + continue; + } + nPrimeFound++; + } + Digits.Mul(p1, p1DigitN, p2, p2DigitN, _modDigits); + int modBitN = Digits.SigBitN(_modDigits, _modDigitN); + Debug.Assert(modBitN == p1BitN + p2BitN && modBitN == _modBitN + , "internal error"); + _primeBitN = new int[2] { p1BitN, p2BitN }; + _modBytes = new byte[_modByteN]; + _primeBytes + = new byte[2][] { new byte[p1ByteN], new byte[p2ByteN] }; + _dBytes + = new byte[2][] { new byte[p1ByteN], new byte[p2ByteN] }; + Digits.DigitsToBytes(_modDigits, _modBytes, 0, _modBitN); + for (int ip = 0; ip != 2; ip++) { + Digits.DigitsToBytes( + ip == 0 ? p1 : p2, _primeBytes[ip], 0, pBitN[ip]); + Digits.DigitsToBytes( + ip == 0 ? d1 : d2, _dBytes[ip], 0, pBitN[ip]); + } + int moduliCreated = 0; + Digits.BytesToDigits(_eBytes, 0, _eDigits, _eBitN); + _modulus = new Modulus(_modDigits, _modDigitN, true); + _privateModulus = new Modulus[2]; + _dDigits = new Digits[2]; + _chineseDigits = new Digits[2]; + for (int ip = 0; ip != 2; ip++) { + Digits temp2 = new Digits(_modDigitN); + _dDigits[ip] = new Digits(p1DigitN); + _chineseDigits[ip] = new Digits(p1DigitN); + Digits.BytesToDigits( + _primeBytes[ip], 0, temp2, _primeBitN[ip]); + _privateModulus[ip] + = new + Modulus(temp2 + , (_primeBitN[ip] + (Digit.BitN - 1)) / Digit.BitN + , true); + moduliCreated++; + Digits.BytesToDigits( + _dBytes[ip], 0, _dDigits[ip], _primeBitN[ip]); + } + int lgcd2 = 0; + Digits gcd2 = new Digits(_modDigitN); + Digits.ExtendedGcd(_privateModulus[0]._mod + , p1DigitN + , _privateModulus[1]._mod + , p2DigitN + , _chineseDigits[1] + , _chineseDigits[0] + , gcd2 + , out lgcd2); + if (Digits.Compare(gcd2, 1, lgcd2) != 0) { + throw new ArgumentException(); + } + } + class Block + { + readonly byte[] _bytes; + readonly int _i; + readonly int _n; + internal Block(byte[] bytes, int i, int n) { + _bytes = bytes; + _i = i; + _n = n; + } + internal byte[] _Bytes { get { return _bytes; } } + internal int _ByteI { get { return _i; } } + internal int _ByteN { get { return _n; } } + internal int _BitN { get { return 8 * _n; } } + internal int _DigitN { + get { return (_BitN + (Digit.BitN - 1)) / Digit.BitN; } + } + } + enum Mode { Encrypt, Decrypt }; + public byte[] _Encrypt(byte[] inputBytes, int inputByteN) { + return _DoBlocks(inputBytes + , inputByteN + , _modByteN - 1 + , _modByteN + , Mode.Encrypt); + } + public byte[] _Decrypt(byte[] inputBytes, int inputByteN) { + return _DoBlocks(inputBytes + , inputByteN + , _modByteN + , _modByteN - 1 + , Mode.Decrypt); + } + public byte[] _Sign(byte[] inputBytes, int inputByteN) { + return _DoBlocks(inputBytes + , inputByteN + , _modByteN - 1 + , _modByteN + , Mode.Decrypt); + } + public byte[] _Check(byte[] inputBytes, int inputByteN) { + return _DoBlocks(inputBytes + , inputByteN + , _modByteN + , _modByteN - 1 + , Mode.Encrypt); + } + byte[] _DoBlocks( + byte[] inputBytes + , int inputByteN + , int inputBlockByteN + , int outputBlockByteN + , Mode mode + ) { + int blockN = (inputByteN + inputBlockByteN - 1) + / inputBlockByteN; + byte[] outputBytes = new byte[blockN * outputBlockByteN]; + int i; + for (i = 0; i < blockN - 1; i++) { + Block inputBlock + = new Block( + inputBytes, i * inputBlockByteN, inputBlockByteN) + , outputBlock = new Block(outputBytes + , i * outputBlockByteN + , outputBlockByteN); + if (mode == Mode.Encrypt) { + _EncryptBlock(inputBlock, outputBlock); + } + else { + _DecryptBlock(inputBlock, outputBlock); + } + } + inputByteN -= i * inputBlockByteN; + if (inputByteN > 0) { + byte[] b = new byte[inputBlockByteN]; + for (int j = 0; j < inputByteN; j++) { + b[j] = inputBytes[i * inputBlockByteN + j]; + } + Block inputBlock = new Block(b, 0, inputBlockByteN) + , outputBlock = new Block(outputBytes + , i * outputBlockByteN + , outputBlockByteN); + if (mode == Mode.Encrypt) { + _EncryptBlock(inputBlock, outputBlock); + } + else { + _DecryptBlock(inputBlock, outputBlock); + } + } + return outputBytes; + } + void _EncryptBlock(Block input, Block output) { + if (input == null || output == null) { + throw new ArgumentNullException(); + } + Digits digits = new Digits(_modDigitN); + Digits.BytesToDigits( + input._Bytes, input._ByteI, digits, input._BitN); + Modular.ValidateData(digits, _modulus._mod, _modulus._digitN); + _modulus._ToModular(digits, input._DigitN, digits); + _modulus._Exp(digits, _eDigits, _eDigitN, digits); + _modulus._FromModular(digits, digits); + Digits.DigitsToBytes( + digits, output._Bytes, output._ByteI, output._BitN); + } + void _DecryptBlock(Block input, Block output) { + if (input == null || output == null) { + throw new ArgumentNullException(); + } + if ( + _privateModulus[0]._digitN + + _privateModulus[1]._digitN + - _modDigitN + > 1 + ) { + throw new ArgumentException(); + } + Digits[] dmsg = new Digits[] { + new Digits(_modDigitN + 1) + , new Digits(_modDigitN + 1) + }; + int longerDigitN + = _privateModulus[0]._digitN > _privateModulus[1]._digitN + ? _privateModulus[0]._digitN + : _privateModulus[1]._digitN; + Digits res = new Digits(longerDigitN); + Digits.BytesToDigits( + input._Bytes, input._ByteI, dmsg[1], input._BitN); + Modular.ValidateData(dmsg[1], _modDigits, _modDigitN); + for (int ip = 0; ip != 2; ip++) { + _privateModulus[ip]._ToModular(dmsg[1], _modDigitN, res); + _privateModulus[ip] + ._Exp(res, _dDigits[ip], _privateModulus[ip]._digitN, res); + _privateModulus[ip]._Mul(res, _chineseDigits[ip], res); + Digits.Mul(res + , _privateModulus[ip]._digitN + , _privateModulus[1 - ip]._mod + , _privateModulus[1 - ip]._digitN + , dmsg[ip]); + } + Modular.Add(dmsg[0], dmsg[1], dmsg[0], _modDigits, _modDigitN); + Digits.DigitsToBytes( + dmsg[0], output._Bytes, output._ByteI, output._BitN); + } + } + } +} diff --git a/base/Libraries/DebugUtils/AssemblyInfo.sg b/base/Libraries/DebugUtils/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Libraries/DebugUtils/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Libraries/DebugUtils/DebugUtils.csproj b/base/Libraries/DebugUtils/DebugUtils.csproj new file mode 100644 index 0000000..e26d3c9 --- /dev/null +++ b/base/Libraries/DebugUtils/DebugUtils.csproj @@ -0,0 +1,22 @@ + + + + + DebugUtils + Library + {14BD2604-CAFE-4B1E-95A5-0ECB174384C5} + + + + + + + \ No newline at end of file diff --git a/base/Libraries/DebugUtils/ImpatientWatcher.sg b/base/Libraries/DebugUtils/ImpatientWatcher.sg new file mode 100644 index 0000000..77ae0dc --- /dev/null +++ b/base/Libraries/DebugUtils/ImpatientWatcher.sg @@ -0,0 +1,107 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +using System.Threading; + +namespace Microsoft.Singularity +{ + public class ImpatientWatcher : IDisposable + { + readonly Thread! workerThread; + bool ownerIsDone; + bool workerIsRunning; + string! currentStep; + readonly string! description; + readonly string! dbgprefix; + TimeSpan stepTime; + + public void Dispose() + { + bool workerWasRunning; + lock (this) { + ownerIsDone = true; + workerWasRunning = workerIsRunning; + if (workerIsRunning) { + Monitor.Pulse(this); + } + } + + if (workerWasRunning) + workerThread.Join(); + } + + public void NextStep(string! step, int stepTimeMilliseconds) + { + NextStep(step, TimeSpan.FromMilliseconds(stepTimeMilliseconds)); + } + + public void NextStep(string! step, TimeSpan stepTime) + { + lock (this) { + this.currentStep = step; + this.stepTime = stepTime; + Monitor.Pulse(this); + } + } + + [Microsoft.Contracts.NotDelayed] + public ImpatientWatcher(string! description, string! firstStep, int stepTimeMilliseconds) + : this(description, firstStep, TimeSpan.FromMilliseconds(stepTimeMilliseconds)) + { + } + + [Microsoft.Contracts.NotDelayed] + public ImpatientWatcher(string! description, string! firstStep, TimeSpan stepTime) + { + this.workerThread = new Thread(WorkerRoutine); + this.ownerIsDone = false; + this.workerIsRunning = true; + this.currentStep = firstStep; + this.description = description; + this.stepTime = stepTime; + this.dbgprefix = String.Format("ImpatientWatcher({0}): ", description); + base(); + workerThread.Start(); + } + + private void WorkerRoutine() + { + lock (this) { + for (;;) { + if (ownerIsDone) { + // Dbg("owner is done, exiting"); + workerIsRunning = false; + return; + } + + string! step = currentStep; + // Dbg("waiting for step '{0}'...", step); + + if (Monitor.Wait(this, stepTime)) { + // owner pulsed lock before we gave up + // Dbg("step '{0}' has finished", step); + } + else { + // timeout expired before owner pulsed + Dbg("step '{0}' has timed out, still waiting...", step); + } + } + } + } + + void Dbg(string! line) + { + DebugStub.WriteLine(dbgprefix + line); + } + + void Dbg(string! format, params object[]! args) + { + Dbg(String.Format(format, args)); + } + + } +} diff --git a/base/Libraries/DirectoryService.AclUtils/AssemblyInfo.sg b/base/Libraries/DirectoryService.AclUtils/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Libraries/DirectoryService.AclUtils/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Libraries/DirectoryService.AclUtils/DirectoryService.AclUtils.csproj b/base/Libraries/DirectoryService.AclUtils/DirectoryService.AclUtils.csproj index 1d30068..de0a63d 100644 --- a/base/Libraries/DirectoryService.AclUtils/DirectoryService.AclUtils.csproj +++ b/base/Libraries/DirectoryService.AclUtils/DirectoryService.AclUtils.csproj @@ -28,6 +28,7 @@ + diff --git a/base/Libraries/DirectoryService.Utils/AssemblyInfo.sg b/base/Libraries/DirectoryService.Utils/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Libraries/DirectoryService.Utils/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Libraries/DirectoryService.Utils/DirectoryService.Utils.csproj b/base/Libraries/DirectoryService.Utils/DirectoryService.Utils.csproj index 5346591..2db792b 100644 --- a/base/Libraries/DirectoryService.Utils/DirectoryService.Utils.csproj +++ b/base/Libraries/DirectoryService.Utils/DirectoryService.Utils.csproj @@ -5,8 +5,6 @@ # # Copyright (c) Microsoft Corporation. All rights reserved. # -# File: Libraries\DirectoryServices.Utils\Makefile -# # Note: # ############################################################################## @@ -22,6 +20,7 @@ + diff --git a/base/Libraries/Drivers.Net/AssemblyInfo.sg b/base/Libraries/Drivers.Net/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Libraries/Drivers.Net/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Libraries/Drivers.Net/Drivers.Net.csproj b/base/Libraries/Drivers.Net/Drivers.Net.csproj index 9adc560..7157f58 100644 --- a/base/Libraries/Drivers.Net/Drivers.Net.csproj +++ b/base/Libraries/Drivers.Net/Drivers.Net.csproj @@ -23,6 +23,7 @@ + diff --git a/base/Libraries/Drivers.Net/EthernetAddress.cs b/base/Libraries/Drivers.Net/EthernetAddress.cs index 0a21170..66e2e07 100644 --- a/base/Libraries/Drivers.Net/EthernetAddress.cs +++ b/base/Libraries/Drivers.Net/EthernetAddress.cs @@ -4,9 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: EthernetAddress.cs -// -// using System; @@ -73,12 +70,10 @@ namespace Drivers.Net public EthernetAddress(byte [] addressBytes) { - if (addressBytes == null) - { + if (addressBytes == null) { throw new ArgumentNullException(); } - if (addressBytes.Length != Length) - { + if (addressBytes.Length != Length) { throw new ArgumentException(); } u03 = ((uint)addressBytes[0]) << 24; @@ -191,8 +186,7 @@ namespace Drivers.Net public override bool Equals(object o) { - if (o is EthernetAddress) - { + if (o is EthernetAddress) { return this == (EthernetAddress) o; } return false; @@ -246,8 +240,7 @@ namespace Drivers.Net if (tokens.Length != 6) throw new FormatException(); - for (int i = 0; i < 6; i++) - { + for (int i = 0; i < 6; i++) { string ti = (!)tokens[i]; if (ti.Length != 2) throw new FormatException(); @@ -275,12 +268,10 @@ namespace Drivers.Net /// true on success, false on failure. public static bool Parse(string macString, out EthernetAddress address) { - try - { + try { address = Parse(macString); } - catch (FormatException) - { + catch (FormatException) { address = EthernetAddress.Zero; return false; } @@ -289,12 +280,10 @@ namespace Drivers.Net public static EthernetAddress ParseBytes(byte[] data, int offset) { - if (data == null) - { + if (data == null) { throw new ArgumentNullException(); } - if (data.Length - offset < Length) - { + if (data.Length - offset < Length) { throw new ArgumentException("Byte array too short."); } return new EthernetAddress(data[offset], data[offset + 1], @@ -322,11 +311,10 @@ namespace Drivers.Net { if (other == null) return 1; - if (other is EthernetAddress) - { + if (other is EthernetAddress) { EthernetAddress value = (EthernetAddress) other; if (this < value) return -1; - if (this > value) return +1; + if (this > value) return + 1; return 0; } throw new ArgumentException ("Arg_MustBeEthernetAddress"); diff --git a/base/Libraries/Eventing/EventTemplate.cs b/base/Libraries/Eventing/EventTemplate.cs new file mode 100644 index 0000000..c00a1ff --- /dev/null +++ b/base/Libraries/Eventing/EventTemplate.cs @@ -0,0 +1,217 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +using System.Runtime.CompilerServices; +using Microsoft.Singularity.Transform; +using Microsoft.Singularity.Eventing; +using Microsoft.Singularity.V1.Services; + +// patterns +namespace Microsoft.Singularity.EventTemplate +{ + [Pattern][Data] + public struct EachParam + { + } + + [Pattern][EventEnum] + public enum BasicEnum + { + } + + [Pattern] + public class EachParam_DataType + { + public static readonly ushort TypeID = 0; + } + + [Pattern] + [Event] + [Template] + public abstract class EachSourceClass : EventSource + { + [Pattern] + [Event] + public abstract bool EachLogMethod([Case][EventEnum]BasicEnum eachBasicEnumParam, + [Case][Data]EachParam eachParam); + + // temporary static string for each method that defines a printf format + // todo: move into attribute parameters + public static string EachLogMethod_Format; + + // generate template code + + new public static uint ControlFlag; + + // separate flag for each meth + public static readonly uint EachLogMethod_Flag; + + // separate handle for the type defined for each meth + public static UIntPtr EachLogMethod_Handle; + + public static UIntPtr BasicEnum_Handle; + + // class constructor to intialize flag bits + static EachSourceClass() + { + ControlFlag = 0; + uint flag = 0x10000; + Transform.For("EachLogMethod"); + EachLogMethod_Format = null; + EachLogMethod_Flag = flag; + BasicEnum_Handle = 0; + + flag = flag << 1; + if (flag == 0) { + throw new Exception("Flag overflow - too many methods in EachSourceClass"); + } + Transform.EndFor(); + } + + // template for static creation method + new public static EachSourceClass Create(string sourcename, + uint size, + uint storageOptions, + uint sourceFlags) + { + EventingStorage storage = EventingStorage.CreateLocalStorage(storageOptions, + size); + + if (storage == null) { + + DebugStub.WriteLine("Failure to create local storage " + sourcename); + DebugStub.Break(); + return null; + } + + EachSourceClass Source = new EachSourceClass_Impl(sourcename, + storage, + sourceFlags); + + if (Source != null) { + + if (!Source.Register()) { + + DebugStub.WriteLine("Error initializing the source " + sourcename); + return null; + } + } + + return Source; + + } + + // struct to hold event parameters for each method + public struct EachLogMethod_Event + { + public EachParam eachParam; + public int eachBasicEnumParam; + } + + internal EachSourceClass(string sourceName, EventingStorage storage, uint controlFlags) + :base(sourceName, storage, controlFlags) {} + + } + + [Template] + internal class EachSourceClass_Impl : EachSourceClass + { + + [NoHeapAllocation] + public override bool EachLogMethod([Case][EventEnum]BasicEnum eachBasicEnumParam, + [Case][Data]EachParam eachParam) + { + if ((ControlFlags & EachLogMethod_Flag) != 0) { + + unsafe { + + EachLogMethod_Event Event; + + Transform.For("eachParam"); + Event.eachParam = eachParam; + Transform.EndFor(); + + Transform.For("eachBasicEnumParam"); + Event.eachBasicEnumParam = (int)eachBasicEnumParam; + Transform.EndFor(); + + return (LogEntry(ControlFlags, + EachLogMethod_Handle, + (byte *)&Event, + sizeof(EachLogMethod_Event)) != 0); + } + } + + return false; + } + + public override bool Register() { + + if (!base.Register()) { + + return false; + } + + if (HostController == null) { + + return false; + } + + RegisterEnumSymbols(); + + DataType2 dt2 = new DataType2(); + + string s = "EachSourceClass"; + Transform.For("EachLogMethod"); + string format = EachLogMethod_Format != null ? EachSourceClass.EachLogMethod_Format : "{0}"; + + if (HostController.RegisterEvent(s + ".EachLogMethod", format, ref EachLogMethod_Handle)) { + + DebugStub.WriteLine("register event succeeded"); + + + Transform.For("eachParam"); + HostController.RegisterEventField(EachSourceClass.EachLogMethod_Handle, + "eachParam", + 0, + dt2.__EachParam); + Transform.EndFor(); + + // + // HACK!!!!! Some instruction is needed here, otherwise, the transform + // will merge the enumeration below with the one above + // producing a mismatch in the order of argumets in the structure declaration + // + DebugStub.WriteLine("Registering enums"); + + Transform.For("eachBasicEnumParam"); + + HostController.RegisterEventGenericField(EachSourceClass.EachLogMethod_Handle, + "eachBasicEnumParam", + 0, + sizeof(int), + BasicEnum_Handle); + Transform.EndFor(); + + } else { + + // The event might have been registered already + // Check whether we foundit already in the table or not + + if (EachSourceClass.EachLogMethod_Handle == 0) { + return false; + } + } + Transform.EndFor(); + + return true; + } + + internal EachSourceClass_Impl(string sourceName, EventingStorage storage, uint controlFlags) + :base(sourceName, storage, controlFlags) {} + } +} diff --git a/base/Libraries/Eventing/EventTemplate.csproj b/base/Libraries/Eventing/EventTemplate.csproj new file mode 100644 index 0000000..07d71c1 --- /dev/null +++ b/base/Libraries/Eventing/EventTemplate.csproj @@ -0,0 +1,35 @@ + + + + + + + + Library + EventTemplate + true + Sing# + + + + + + + + + + + + + + diff --git a/base/Libraries/Eventing/Eventing.cs b/base/Libraries/Eventing/Eventing.cs new file mode 100644 index 0000000..43af3dd --- /dev/null +++ b/base/Libraries/Eventing/Eventing.cs @@ -0,0 +1,54 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; + +namespace Microsoft.Singularity.Eventing +{ + public class EventAttribute : Attribute + { + // mark classes & methods for eventing + } + + public class EventEnumAttribute : Attribute + { + // mark classes & methods for eventing + } + + // temporary class for data type - need to move this into DataType in core libs + public class DataType2 + { + public ushort __Boolean; + public ushort __Int8; + public ushort __UInt8; + public ushort __Int16; + public ushort __UInt16; + public ushort __Int32; + public ushort __UInt32; + public ushort __Int64; + public ushort __UInt64; + public ushort __String; + public ushort __EachParam; // hack! necessary for template to compile + + public DataType2() + { + __EachParam = 0; + __Boolean = 1; + __Int8 = 1; + __UInt8 = 2; + __Int16 = 3; + __UInt16 = 4; + __Int32 = 5; + __UInt32 = 6; + __Int64 = 7; + __UInt64 = 8; + + __String = 0x4000; + } + } +} + + diff --git a/base/Libraries/Eventing/Eventing.csproj b/base/Libraries/Eventing/Eventing.csproj new file mode 100644 index 0000000..321c2b6 --- /dev/null +++ b/base/Libraries/Eventing/Eventing.csproj @@ -0,0 +1,28 @@ + + + + + + + + Library + Eventing + C# + + + + + + + + diff --git a/base/Libraries/Eventing/SampleEvent.cs b/base/Libraries/Eventing/SampleEvent.cs new file mode 100644 index 0000000..89d7f8f --- /dev/null +++ b/base/Libraries/Eventing/SampleEvent.cs @@ -0,0 +1,28 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +using System.Runtime.CompilerServices; +using Microsoft.Singularity.Transform; +using Microsoft.Singularity.Eventing; + +namespace Microsoft.Singularity.EventTest +{ + [Event] + public abstract class SampleSource : EventSource + { + [Event] + public abstract bool One(int one, bool two, string three, int four); + public static string One_Format = "One - {0} to {1} with {2} and {3}"; + + [Event] + public abstract bool Two(int a, int b, int c); + public static string Two_Format; // leave default + + SampleSource(string sourceName, EventingStorage storage, uint controlFlags) + :base(sourceName, storage, controlFlags) {} + } +} diff --git a/base/Libraries/Eventing/SampleEvent.csproj b/base/Libraries/Eventing/SampleEvent.csproj new file mode 100644 index 0000000..fc37c4b --- /dev/null +++ b/base/Libraries/Eventing/SampleEvent.csproj @@ -0,0 +1,32 @@ + + + + + + + + Library + SampleEvent + C# + + + + + + + + + + + + diff --git a/base/Libraries/FileSystem.Utils/AssemblyInfo.sg b/base/Libraries/FileSystem.Utils/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Libraries/FileSystem.Utils/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Libraries/FileSystem.Utils/DirectoryUtils.sg b/base/Libraries/FileSystem.Utils/DirectoryUtils.sg index 180e9a7..cb86220 100644 --- a/base/Libraries/FileSystem.Utils/DirectoryUtils.sg +++ b/base/Libraries/FileSystem.Utils/DirectoryUtils.sg @@ -40,8 +40,7 @@ namespace FileSystem.Utils string dirPath, leafName; PathUtils.SplitPath(path, out dirPath, out leafName); - if ((dirPath == null) || (dirPath == "")) - { + if ((dirPath == null) || (dirPath == "")) { // Invalid path; no leading directory component return null; } @@ -92,8 +91,7 @@ namespace FileSystem.Utils DirectoryServiceContract.Imp dirClient = FindDir(dirPath); - if (dirClient == null) - { + if (dirClient == null) { // Couldn't open enclosing directory return -1; } @@ -120,7 +118,7 @@ namespace FileSystem.Utils } //DeleteDirectory #if !REMOVE - //REMOVE version that does not have ds endpoint + // REMOVE version that does not have ds endpoint public static int CreateDirectory (string! dirPath) { DirectoryServiceContract.Imp! rootNS = DirectoryService.NewClientEndpoint(); @@ -143,8 +141,7 @@ namespace FileSystem.Utils DirectoryServiceContract.Imp dirClient = FindDir(dirPath); - if (dirClient == null) - { + if (dirClient == null) { // Couldn't open enclosing directory return -1; } @@ -173,26 +170,30 @@ namespace FileSystem.Utils // Outparam is only valid if return value is 0 (success) public static int PathIsDirectory(string! dirPath, out bool isDir) + { + DirectoryServiceContract.Imp! rootNS = DirectoryService.NewClientEndpoint(); + int ret = PathIsDirectory(dirPath, rootNS, out isDir); + delete rootNS; + return ret; + } + + // Outparam is only valid if return value is 0 (success) + public static int PathIsDirectory(string! dirPath, DirectoryServiceContract.Imp! ds, out bool isDir) { isDir = false; // Get attributes as though this is a file - NodeType nodeType; - long size; ErrorCode error; + FileAttributesRecord fileAttributes; - DirectoryServiceContract.Imp! rootNS = DirectoryService.NewClientEndpoint(); - bool ok = FileUtils.GetAttributes(dirPath, rootNS, out size, out nodeType, out error); - delete rootNS; + bool ok = FileUtils.GetAttributes(dirPath, ds, out fileAttributes, out error); - if (!ok) - { + if (!ok) { return -1; } - if (nodeType != NodeType.Directory) - { - Debug.Assert(size == 0); + if (fileAttributes.Type == NodeType.Directory) { + Debug.Assert(fileAttributes.FileSize == 0); isDir = true; } diff --git a/base/Libraries/FileSystem.Utils/DiskSizeUtils.cs b/base/Libraries/FileSystem.Utils/DiskSizeUtils.cs new file mode 100644 index 0000000..edabef7 --- /dev/null +++ b/base/Libraries/FileSystem.Utils/DiskSizeUtils.cs @@ -0,0 +1,123 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Libraries\Services\Helpers.cs +// +// Note: +// +using System; +using System.IO; +using System.Text; + +namespace FileSystem.Utils +{ + public static class DiskSizeUtils + { + internal struct Quantity + { + internal ulong Unit; + internal string! Suffix; + + internal Quantity(ulong u, string! s) + { + Unit = u; + Suffix = s; + } + + internal static readonly Quantity[]! KnownDisplayQuantities = new Quantity[] { + new Quantity(1152921504606846976, "EiB"), + new Quantity(1125899906842624, "PiB"), + new Quantity(1099511627776, "TiB"), + new Quantity(1073741824, "GiB"), + new Quantity(1048576, "MiB"), + new Quantity(1024, "KiB"), + }; + + internal static readonly Quantity[]! KnownParseQuantities = new Quantity[] { + // Binary units + new Quantity(1152921504606846976, "EiB"), + new Quantity(1125899906842624, "PiB"), + new Quantity(1099511627776, "TiB"), + new Quantity(1073741824, "GiB"), + new Quantity(1048576, "MiB"), + new Quantity(1024, "KiB"), + + // Metric units + new Quantity(1000000000000000000, "EB"), + new Quantity(1000000000000000, "PB"), + new Quantity(1000000000000, "TB"), + new Quantity(1000000000, "GB"), + new Quantity(1000000, "MB"), + new Quantity(1000, "KB"), + + // Use binary units for the casual ones like "10K" + new Quantity(1152921504606846976, "E"), + new Quantity(1125899906842624, "P"), + new Quantity(1099511627776, "T"), + new Quantity(1073741824, "G"), + new Quantity(1048576, "M"), + new Quantity(1024, "K"), + + // Binary units for bits, converted to bytes + new Quantity(144115188075855872, "Eb"), + new Quantity(140737488355328, "Pb"), + new Quantity(137438953472, "Tb"), + new Quantity(134217728, "Gb"), + new Quantity(131072, "Mb"), + new Quantity(128, "Kb"), + + new Quantity(1, "B"), + new Quantity(1, "") + }; + } + + public static string GetPrettySizeString(ulong diskSize) + { + string suffix = "B"; + ulong divisor = 1; + + Quantity[] quantities = Quantity.KnownDisplayQuantities; + + for (int i = 0; i < quantities.Length; i++) { + if (diskSize >= 1 * quantities[i].Unit) { + divisor = quantities[i].Unit; + suffix = quantities[i].Suffix; + break; + } + } + return String.Format("{0:F0} {1}", + (float)diskSize / (float)divisor, suffix); + } + + public static ulong ParsePrettySizeString(string! diskSizeStr) + { + ulong multiplier = 1, numUnits; + + Quantity[] quantities = Quantity.KnownParseQuantities; + + for (int i = 0; i < quantities.Length; i++) { + if (diskSizeStr.EndsWith(quantities[i].Suffix)) { + + // Would use TryParse() if we had .NET Framework 2.0 + try { + numUnits = ulong.Parse( + diskSizeStr.Substring(0, diskSizeStr.Length - + quantities[i].Suffix.Length)); + } + catch (FormatException) { + continue; + } + + multiplier = quantities[i].Unit; + + return multiplier * numUnits; + } + } + + throw new FormatException(); + } + } +} diff --git a/base/Libraries/FileSystem.Utils/FileSystem.Utils.csproj b/base/Libraries/FileSystem.Utils/FileSystem.Utils.csproj index 154db8d..0b9651d 100644 --- a/base/Libraries/FileSystem.Utils/FileSystem.Utils.csproj +++ b/base/Libraries/FileSystem.Utils/FileSystem.Utils.csproj @@ -27,9 +27,11 @@ + + diff --git a/base/Libraries/FileSystem.Utils/FileUtils.sg b/base/Libraries/FileSystem.Utils/FileUtils.sg index 28db0f9..8c09c6a 100644 --- a/base/Libraries/FileSystem.Utils/FileUtils.sg +++ b/base/Libraries/FileSystem.Utils/FileUtils.sg @@ -78,16 +78,10 @@ namespace FileSystem.Utils FileContract.Exp! fileServer; FileContract.NewChannel(out fileClient, out fileServer); - TimeSpan start = ProcessService.GetUpTime(); bool ok = SdsUtils.Bind(filePath, rootNS, fileServer, out errorOut); - if (recording) { - TimeSpan duration = ProcessService.GetUpTime() - start; - lock (lockTarget) { - bindTime += duration; - ++bindCount; - } - } if (!ok) { + //Console.WriteLine(" bind failed (error={0}) for {1}", + // SdsUtils.ErrorCodeToString(errorOut), filePath); delete fileClient; return null; } @@ -101,35 +95,50 @@ namespace FileSystem.Utils public static bool FileExists(DirectoryServiceContract.Imp:Ready! dsRoot,string! filePath) { ErrorCode error; - long size; - NodeType nodeType; - - return GetAttributes(filePath, dsRoot, out size, out nodeType, out error); + FileAttributesRecord fileAttributes; + + return GetAttributes(filePath, dsRoot, out fileAttributes, out error); } #if !REMOVE // REMOVE public static int CreateFile(string! filePath) { + ErrorCode errorCode; DirectoryServiceContract.Imp! rootNS = DirectoryService.NewClientEndpoint(); try { - return CreateFile(filePath, rootNS); + if (!CreateFile(filePath, rootNS, out errorCode)) return -1; + return 0; + } + finally { + delete rootNS; + } + } + + // REMOVE + public static int CreateFile(string! filePath, out ErrorCode errorCode) + { + DirectoryServiceContract.Imp! rootNS = DirectoryService.NewClientEndpoint(); + + try { + if (!CreateFile(filePath, rootNS, out errorCode)) return -1; + return 0; } finally { delete rootNS; } } #endif + public static int CreateFile(string! filePath, DirectoryServiceContract.Imp! ds) { - ErrorCode errorCode; - if (!CreateFile(filePath, ds, out errorCode)) { - return -1; - } - else return 0; + ErrorCode err; + if (!CreateFile(filePath,ds, out err)) return -1; + return 0; } + public static bool CreateFile(string! filePath, DirectoryServiceContract.Imp! ds, out ErrorCode errorCode) { errorCode = ErrorCode.Unknown; @@ -143,8 +152,7 @@ namespace FileSystem.Utils DirectoryServiceContract.Imp dirClient = DirectoryUtils.FindDir(filePath, ds); - if (dirClient == null) - { + if (dirClient == null) { // Couldn't open enclosing directory return false; } @@ -159,6 +167,7 @@ namespace FileSystem.Utils return false; case dirClient.AckCreateFile(): + errorCode = ErrorCode.NoError; // Success break; @@ -176,30 +185,28 @@ namespace FileSystem.Utils // 0 is success public static bool GetAttributes(string! filePath, DirectoryServiceContract.Imp! ds, - out long size, - out NodeType nodeType, + out FileAttributesRecord fileAttributes, out ErrorCode errorOut) { - bool ok = SdsUtils.GetAttributes(filePath, ds, out size, out nodeType, out errorOut); + bool ok = SdsUtils.GetAttributes(filePath, ds, out fileAttributes, out errorOut); return ok; } // 0 is success public static bool GetAttributes(string! filePath, - out long size, - out NodeType nodeType, + out FileAttributesRecord fileAttributes, out ErrorCode errorOut) { + // make the complier happy and assign fileAttributes + fileAttributes = new FileAttributesRecord(); bool ok = false; - nodeType = NodeType.BadNode; errorOut = ErrorCode.Unknown; - size = 0; DirectoryServiceContract.Imp! rootNS = GetDirectoryServiceContract(); try { - ok = GetAttributes(filePath, rootNS, out size, out nodeType, out errorOut); + ok = GetAttributes(filePath, rootNS, out fileAttributes, out errorOut); } catch (Exception e) { - Console.WriteLine("FileUtils.GetAttributes exception: {0}", e.Message); + DebugStub.WriteLine("FileUtils.GetAttributes exception: {0}", __arglist(e.Message)); } finally { ReleaseDirectoryServiceContract(rootNS); @@ -217,7 +224,7 @@ namespace FileSystem.Utils DeleteFile(filePath, ds, out errorOut); } catch (Exception e) { - Console.WriteLine("FileUtils.DeleteFile exception: {0}", e.Message); + DebugStub.WriteLine("FileUtils.DeleteFile exception: {0}", __arglist(e.Message)); } finally { ReleaseDirectoryServiceContract(ds); @@ -238,8 +245,7 @@ namespace FileSystem.Utils DirectoryServiceContract.Imp dirClient = DirectoryUtils.FindDir(filePath, ds); - if (dirClient == null) - { + if (dirClient == null) { // Couldn't open enclosing directory errorOut = ErrorCode.NotFound; return -1; @@ -281,18 +287,17 @@ namespace FileSystem.Utils return null; } - long size; - NodeType ignored; ErrorCode err; - - if (!GetAttributes(filePath, out size, out ignored, out err)) { + FileAttributesRecord fileAttributes; + + if (!GetAttributes(filePath, ds, out fileAttributes, out err)) { delete fileEP; return null; } - byte[] data = new byte[size]; + byte[] data = new byte[fileAttributes.FileSize]; - if (Read(fileEP, 0, (int)size, 0, data) != size) { + if (Read(fileEP, 0, (int)fileAttributes.FileSize, 0, data) != fileAttributes.FileSize) { delete fileEP; return null; } @@ -316,7 +321,8 @@ namespace FileSystem.Utils if (error == 0) { Bitter.ToByteArray(readBytes, 0, (int)readCount, buf, (int)bufOffset); - } else { + } + else { readCount = 0; } diff --git a/base/Libraries/FileSystem.Utils/PathUtils.sg b/base/Libraries/FileSystem.Utils/PathUtils.sg index c7757ee..506a335 100644 --- a/base/Libraries/FileSystem.Utils/PathUtils.sg +++ b/base/Libraries/FileSystem.Utils/PathUtils.sg @@ -10,6 +10,7 @@ // using System; +using System.Text; using System.Threading; using Microsoft.Singularity; using Microsoft.Singularity.Directory; @@ -21,6 +22,8 @@ namespace FileSystem.Utils { public class PathUtils { + const char DirectorySeparatorChar = '/'; + internal static string FileFromPath(string! filePath) { string dirPath, fileName; @@ -37,7 +40,7 @@ namespace FileSystem.Utils dirPath = String.Empty; fileName = String.Empty; - string[]! parts = filePath.Split('/'); + string[]! parts = filePath.Split(DirectorySeparatorChar); if ((parts == null) || (parts.Length <= 1)) { // Not even a leading slash? Bah! return; @@ -51,5 +54,38 @@ namespace FileSystem.Utils int len = (filePath.Length - fileName.Length - 1) > 0? filePath.Length - fileName.Length - 1 : 1; dirPath = filePath.Substring(0, len); } + + + /// Sanitize path string. This method removes + /// trailing separators and repeat sequences of + /// separators. + public static string! ToPath(string! p) + { + int length = p.Length; + + if (p.Length > 0 && p.Length - 1 == DirectorySeparatorChar) { + length = p.LastIndexOf(DirectorySeparatorChar); + } + + StringBuilder sb = new StringBuilder(length); + + bool multi = false; + for (int i = 0; i < length; i++) { + if (p[i] == DirectorySeparatorChar) { + if (multi) { + continue; + } + else { + sb.Append(p[i]); + multi = true; + } + } + else { + sb.Append(p[i]); + multi = false; + } + } + return sb.ToString(); + } } } diff --git a/base/Libraries/Libraries.proj b/base/Libraries/Libraries.proj index 8b70a98..8043b47 100644 --- a/base/Libraries/Libraries.proj +++ b/base/Libraries/Libraries.proj @@ -1,9 +1,16 @@ - + + + - @@ -14,18 +21,14 @@ - - - + - - diff --git a/base/Libraries/Manifest/AssemblyInfo.sg b/base/Libraries/Manifest/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Libraries/Manifest/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Libraries/Manifest/Binder.sg b/base/Libraries/Manifest/Binder.sg index dc23ff0..8aec573 100644 --- a/base/Libraries/Manifest/Binder.sg +++ b/base/Libraries/Manifest/Binder.sg @@ -31,13 +31,19 @@ namespace Microsoft.Singularity.Applications public class Binder { // duplicate of Directory Service consts - public const string ManifestFile = "manifest"; +#if ISA_ARM + public const string ExecutableExtension = ".arm"; + public const string ManifestExtension = ".arm.manifest"; +#elif ISA_IX64 + public const string ExecutableExtension = ".x64"; + public const string ManifestExtension = ".x64.manifest"; +#elif ISA_IX86 public const string ExecutableExtension = ".x86"; - public const string ManifestExtension = ".manifest"; - public const string ExecutablesNamespace = "init"; + public const string ManifestExtension = ".x86.manifest"; +#endif + public const int ExecutableExtensionLength = 4; // Should be (ExecutableExtension.Length); - private static StringBuilder sb; - private static bool isInitialized = false; + public const string ExecutablesNamespace = "init"; const string NameXmlTag = "name"; const string StartStatedIdXmlAttribute = "startStateId"; @@ -45,12 +51,8 @@ namespace Microsoft.Singularity.Applications public static void Initialize() { - if (isInitialized) return; - sb = new StringBuilder(512); // cache away a string form manipulation - isInitialized = true; } - public static Manifest LoadManifest(DirectoryServiceContract.Imp:Ready! ds, String application) { if (application == null) return null; @@ -59,11 +61,10 @@ namespace Microsoft.Singularity.Applications string folderName = application; string appName = application; - - // Open the directory containing the manifest and the .x86 - // remove the ".x86" extension if present + // Open the directory containing the manifest and the ExecutableExtension + // remove the ExecutableExtension (e.g. ".x86") extension if present if (application.EndsWith(ExecutableExtension)) { - folderName =(!)(application.Substring(0, application.Length - ((!)ExecutableExtension).Length)); + folderName =(!)application.Substring(0, application.Length - ExecutableExtensionLength); appName = folderName; } @@ -72,21 +73,11 @@ namespace Microsoft.Singularity.Applications return null; } - if (sb == null) { - sb = new StringBuilder(512); // cache away a string form manipulation - isInitialized = true; - } // if the first character of the folder name is not "/", // then we will redirect to the init namespace. if (folderName[0] != '/') { appName = folderName; - sb.Length = 0; //reset the buffer - sb.Append("/"); - sb.Append(ExecutablesNamespace); - sb.Append("/"); - sb.Append(appName); - - folderName = (!)sb.ToString(); + folderName = "/" + ExecutablesNamespace + "/" + appName; } else { // need to strip off all but last part. @@ -100,31 +91,249 @@ namespace Microsoft.Singularity.Applications DirectoryServiceContract.Imp:Ready dirEP = openDir(ds, folderName); if (dirEP == null) { -#if DEBUG - DebugStub.WriteLine("Binder.LoadManifest: unable to open ,folder={0}", - __arglist (folderName)); -#endif + Tracing.Log(Tracing.Debug, "Unable to open folder {0}", + folderName); return null; } - Manifest man = GetManifest(ManifestFile, dirEP); + + Manifest man = GetManifest(appName + ManifestExtension, dirEP); delete dirEP; return man; } + /// + /// Wrap up all the details of creating a process in one rountine. + /// deals with old and new style processes. Performs manifest parsing + /// argument parsing and setting and manages stdin/out + /// + + public static Process CreateProcess(DirectoryServiceContract.Imp! ds, + string[] args, + PipeMultiplexer! outputMux) + { + Manifest manifest; + return CreateProcess(ds,args,outputMux, out manifest); + } + + public static Process CreateProcess(DirectoryServiceContract.Imp! ds, + string[] args, + PipeMultiplexer! outputMux, + out Manifest outManifest) + { + outManifest = null; + if (args == null || args.Length == 0) return null; + + UnicodePipeContract.Imp! childStdInImp; + UnicodePipeContract.Exp! childStdInExp; + UnicodePipeContract.NewChannel(out childStdInImp, out childStdInExp); + + // Splice a new output pipe into our output MUX. + UnicodePipeContract.Imp childStdOutImp = outputMux.FreshClient(); + if (childStdOutImp == null) { + delete childStdInImp; + delete childStdInExp; + return null; + } + + Process p = CreateProcess(ds,args,childStdInExp,childStdOutImp, out outManifest); + delete childStdInImp; + return p; + } + + public static Process CreateProcess(DirectoryServiceContract.Imp! ds, + string[] args, + [Claims] UnicodePipeContract.Exp:READY? childStdInExp, + [Claims] UnicodePipeContract.Imp:READY! childStdOutImp) + { + Manifest manifest; + return CreateProcess(ds,args,childStdInExp, childStdOutImp, out manifest); + } + + public static Process CreateProcess(DirectoryServiceContract.Imp! ds, + string[] args, + [Claims] UnicodePipeContract.Exp:READY? childStdInExp, + [Claims] UnicodePipeContract.Imp:READY! childStdOutImp, + out Manifest outManifest) + { + return CreateProcess(null, ds, args, childStdInExp, childStdOutImp, out outManifest); + } + + + public static Process CreateProcess( string category, + DirectoryServiceContract.Imp! ds, + string[] args, + out Manifest outManifest) + { + string action; + return CreateProcess(category, ds, args, out outManifest, out action); + } + + public static Process CreateProcess( string category, + DirectoryServiceContract.Imp! ds, + string[] args, + [Claims] UnicodePipeContract.Exp:READY? childStdInExp, + [Claims] UnicodePipeContract.Imp:READY! childStdOutImp, + out Manifest outManifest) + { + string action; + Process p = CreateProcess(category, ds, args, out outManifest, out action); + if (p == null) { + delete childStdInExp; + delete childStdOutImp; + return null; + } + bool ok = ProcessIo(p, outManifest, action, childStdInExp,childStdOutImp); + if (!ok) { + DebugStub.Break(); + return null; + } + return p; + } + + /// + /// Create a new process and set parameters using the manifest + /// + /// + /// The process created. If there are errors return null. + /// + public static Process CreateProcess(string category, + DirectoryServiceContract.Imp! ds, + string[] args, + out Manifest manifest, + out string action) + { + manifest = null; + action = null; + if (args == null || args.Length == 0) { + DebugStub.Break(); + return null; + } + + // Figure out if this process needs any special endpoints. + manifest = Binder.LoadManifest(ds, args[0]); + if (manifest == null) { + Console.WriteLine("'{0}' is not a command or has no manifest", args[0]); + DebugStub.Break(); + return null; + } + + if (manifest.HasParameters()) { + Process child; + XmlNode categoryNode = null; + if (category != null ) { + // the caller expects the category name passed in + // to match the one found in the manifest. If it does + // not throw exception + categoryNode = manifest.GetCategoryNodeByName(category); + if (categoryNode == null) { + DebugStub.WriteLine("Category node not present " + + "in manifest for {0}", __arglist(args[0])); + throw new Exception("no category found in manifest"); + } + } + + if ((category != null) && (category != "console")) { + child = new Process((!)args[0], null, null); + } + else { + ParameterProcessor! parameters = new ParameterProcessor(); + bool ok = parameters.ProcessParameters(args, manifest, + out child, out action); + + if(ok && child != null) { + categoryNode = manifest.GetCategoryNode(action); + if (categoryNode != null) { + } + } + else { + return null; + } + } + + assert child != null; + int result = manifest.SetEndpoints(child, action, false); + if (result < 0) { + Console.WriteLine("Unable to set all endpoints for this process."); + DebugStub.Break(); + return null; + } + + return child; + } + else { + Console.WriteLine(" old style"); + // Old-style app. + return new Process(args, null, 2); + } + } + + /// + /// Try to parse an integer, return true if successful. + /// + private static bool TryParseInt(string input, out int value) + { + try { + value = int.Parse(input); + return true; + } + catch(FormatException) { + value = 0; + return false; + } + catch(OverflowException) { + value = 0; + return false; + } + } + + private static bool ProcessIo( Process! child, + Manifest manifest, + string action, + [Claims] UnicodePipeContract.Exp:READY? childStdInExp, + [Claims] UnicodePipeContract.Imp:READY! childStdOutImp + ) + { + try { + if (manifest != null && manifest.HasParameters()) { + int stdinIndex = manifest.GetInputPipeIndex(action, "data"); + if (stdinIndex == -1) { + Console.WriteLine(" no stdin data pipe specified in manifest"); + delete childStdInExp; + } + else child.SetStartupEndpoint(stdinIndex, (Endpoint * in ExHeap) childStdInExp); + + int stdoutIndex = manifest.GetOutputPipeIndex(action, "data"); + if (stdoutIndex == -1) { + Console.WriteLine(" no stdout data pipe specified in manifest"); + delete childStdOutImp; + } + else child.SetStartupEndpoint(stdoutIndex, (Endpoint * in ExHeap) childStdOutImp); + } + else { + child.SetStartupEndpoint(0, (Endpoint * in ExHeap) childStdInExp); + child.SetStartupEndpoint(1, (Endpoint * in ExHeap) childStdOutImp); + } + + } + catch (Exception ex) { + Console.WriteLine("Exception: " + ex.Message); + } + + return true; + } + public static byte [] FindAndReadManifest(string! file, DirectoryServiceContract.Imp:Ready! dirClient) { - long size = 0; - NodeType nodeType; ErrorCode errorOut; - long readSize = 4096; + long readSize = 0x1000; + FileAttributesRecord fileAttributes; bool ok = SdsUtils.GetAttributes(file, dirClient, - out size, - out nodeType, + out fileAttributes, out errorOut ); if (!ok) return null; @@ -146,7 +355,7 @@ namespace Microsoft.Singularity.Applications if (fileClient != null) { // allocate memory and read file - byte [] region = new byte[size]; + byte [] region = new byte[fileAttributes.FileSize]; if (region == null) { delete fileClient; return null; @@ -211,8 +420,8 @@ namespace Microsoft.Singularity.Applications return dirClient; } - private static Manifest GetManifest(string! name, - DirectoryServiceContract.Imp:Ready! dirEP) + public static Manifest GetManifest(string! name, + DirectoryServiceContract.Imp:Ready! dirEP) { Manifest manifest = null; @@ -221,19 +430,18 @@ namespace Microsoft.Singularity.Applications byte [] manifestMemory = FindAndReadManifest(name,dirEP); if (manifestMemory != null) { manifest = new Manifest(manifestMemory); + Tracing.Log(Tracing.Debug, "Success: {0}", name); } else { - DebugStub.WriteLine(" no manifest! file={0}",__arglist(name)); + Tracing.Log(Tracing.Debug, "Failed: no manifest {0}", name); } } catch (Exception e) { - DebugStub.WriteLine("Exception in GetManifest: {0}", __arglist(e.Message)); Tracing.Log(Tracing.Debug, "Exception in GetManifest: {0}", e.Message); } return manifest; } - // utility function for getting SystemTypes from strings in the manifest private static SystemType GetEndpointType(XmlNode! metadata) { @@ -246,11 +454,15 @@ namespace Microsoft.Singularity.Applications string! name = (!)child.GetAttribute(NameXmlTag, ""); long lower, upper; - // [TODO] the encoding of types will change, and when + // TODO: the encoding of types will change, and when // it does, this will need to change. Right now, we create the // type name of foo.imp as foo+foo.imp (same for exp) - if (name.EndsWith(".Exp") || name.EndsWith(".Imp")) { - name = name.Remove(name.Length - 4, 4) + "+" + name; + // XXX now types are in line with CLR -- foo+exp + if (name.EndsWith(".Exp")) { + name = name.Remove(name.Length - 4, 4) + "+Exp"; + } + else if (name.EndsWith(".Imp")) { + name = name.Remove(name.Length - 4, 4) + "+Imp"; } // get the hash for this type: diff --git a/base/Libraries/Manifest/Manifest.csproj b/base/Libraries/Manifest/Manifest.csproj index 8e2bae0..a0386e5 100644 --- a/base/Libraries/Manifest/Manifest.csproj +++ b/base/Libraries/Manifest/Manifest.csproj @@ -23,6 +23,7 @@ + @@ -31,8 +32,10 @@ - + + + diff --git a/base/Libraries/Manifest/Manifest.sg b/base/Libraries/Manifest/Manifest.sg index c32c42b..b6b3bab 100644 --- a/base/Libraries/Manifest/Manifest.sg +++ b/base/Libraries/Manifest/Manifest.sg @@ -26,11 +26,8 @@ namespace Microsoft.Singularity.Applications { /// - /// This is a placeholder class for an application's manifest. - /// At present the application manifest is represented by a hashtable. - /// The hashtable has several important properties: - /// - name: the name of the application - /// - path: path to the application executable + /// In-memory representation of application manifest + /// Objects in manifest are represented as XmlNodes /// public class Manifest { @@ -40,16 +37,18 @@ namespace Microsoft.Singularity.Applications /// - private Hashtable! data = new Hashtable(); private const string pipeContract = "Microsoft.Singularity.Io.UnicodePipeContract"; private string name; private string path; - + private string manifestVersion; + private string manifestPublicKey; + private readonly XmlNode! applicationNode; private readonly XmlNode! processNode; private readonly XmlNode actionNode; private readonly XmlNode categoriesNode; + private readonly XmlNode assembliesNode; private readonly XmlNode stringParametersNode; private readonly XmlNode stringArrayParametersNode; private readonly XmlNode longParametersNode; @@ -63,14 +62,15 @@ namespace Microsoft.Singularity.Applications [Microsoft.Contracts.NotDelayed] public Manifest(byte[] xml) { - XmlReader! reader = new XmlReader(xml); - XmlNode! doc = (!)reader.Parse(); + XmlReader xmlReader = new XmlReader(); + if (xmlReader == null) throw new Exception("Unable to allocate new XmlReader"); + XmlNode! doc = (!)xmlReader.Parse(xml); this(doc); } [Microsoft.Contracts.NotDelayed] - private Manifest(XmlNode! doc) + public Manifest(XmlNode! doc) { XmlNode app = (!)doc.GetChild("application"); name = app["name"]; @@ -81,6 +81,8 @@ namespace Microsoft.Singularity.Applications } if (process.GetAttribute("main", "false") == "true") { path = process["path"]; + manifestVersion = process["version"]; + manifestPublicKey = process["publickey"]; proc = process; break; } @@ -90,15 +92,18 @@ namespace Microsoft.Singularity.Applications base(); categoriesNode = processNode.GetChild("categories"); + assembliesNode = processNode.GetChild("assemblies"); if (categoriesNode != null) { return; } else { -#if DEBUG +#if VERBOSE DebugStub.WriteLine("no category found in Manifest"); #endif } + + } public XmlNode GetCategoriesNode() @@ -107,9 +112,11 @@ namespace Microsoft.Singularity.Applications } - public void SetProperty(object! name, object value) + public XmlNode AssembliesNode { - data[name] = value; + get { + return assembliesNode; + } } public string Name @@ -119,6 +126,20 @@ namespace Microsoft.Singularity.Applications } } + public string Version + { + get { + return manifestVersion; + } + } + + public string PublicKey + { + get { + return manifestPublicKey; + } + } + public string Path { get { @@ -139,15 +160,19 @@ namespace Microsoft.Singularity.Applications } if (actionName == null) { foreach (XmlNode! c in categoriesNode.Children) { + string name = c.GetAttribute("name",""); + if ( name != "console") return c; bool present = c.GetAttribute("DefaultAction",false); - if (present) return c; - } + if (present) { + return c; + } + } return null; } else { foreach (XmlNode! c in categoriesNode.Children) { string a = c.GetAttribute("Action",null); - if ( a != null && a == actionName) { + if (a != null && a == actionName) { return c; } } @@ -163,7 +188,7 @@ namespace Microsoft.Singularity.Applications foreach (XmlNode! c in categoriesNode.Children) { string a = c.GetAttribute("name",null); - if ( a != null && a == name) { + if (a != null && a == name) { return c; } } @@ -195,21 +220,6 @@ namespace Microsoft.Singularity.Applications return (categoriesNode != null); } -#if false - public void ShowParameters() - { - if (categoryNode == null) { - Console.WriteLine("::: no category"); - return; - } - if (stringParametersNode == null) { - Console.WriteLine("::: no strings"); - } - if (longParametersNode == null) { - Console.WriteLine("::: no integers"); - } - } -#endif private int GetPipeIndex(string action, string! direction, string! desiredKind) { // walk the XmlTree looking InputPipe or UnicodePipe co @@ -255,15 +265,15 @@ namespace Microsoft.Singularity.Applications = (!)endpoint.GetAttribute("contractName", ""); int index = endpoint.GetAttribute("id", -1); - //should really look at Imp vs Exp as well + // TODO: should really look at Imp vs Exp as well if (endpoint.Name == "endpoint") { if (direction == "inputPipe") { - if (contract == pipeContract && index == 0 ) { + if (contract == pipeContract && index == 0) { return 0; } } else { - if (contract == pipeContract && index == 1 ) { + if (contract == pipeContract && index == 1) { return 1; } } @@ -292,7 +302,6 @@ namespace Microsoft.Singularity.Applications // the starting process much like what is done for drivers XmlNode categoryNode = GetCategoryNode(action); - //Console.WriteLine(" Set endpoints"); if (categoryNode == null) { Console.WriteLine("No endpoints in console app!"); //DebugStub.Break(); @@ -317,7 +326,7 @@ namespace Microsoft.Singularity.Applications int index = endpoint.GetAttribute("id", -1); if (endpoint.Name == "endpoint") { - // FIXFIX -- if it is a pipe ignore it for now + // TODO: FIXFIX -- if it is a pipe ignore it for now if (show) { Console.WriteLine(" endpoint({0}): {1}", index, contract); } @@ -341,6 +350,67 @@ namespace Microsoft.Singularity.Applications } return endpointCount; } + + /// + /// Finds the index of a custom endpoint in a manifest. The caller provides the name of the + /// application category (such as "console" or "Service"), and the name of the contract of + /// the endpoint. + /// + /// The name of a category, such as "Service". Case is significant. + /// The full name of a contract type, as specified in the manifest. + /// For example, "Microsoft.Singularity.ServiceManager.ManagedServiceContract". + /// The index of the endpoint, or -1 if none was found. + public int FindCustomEndpointIndex(string! categoryName, string! contractName) + { + XmlNode categoryNode = GetCategoryNodeByName(categoryName); + if (categoryNode == null) + return -1; + + return FindCustomEndpointIndex(categoryNode, contractName); + } + + /// + /// Finds the index of a custom endpoint in a manifest. The caller provides a reference to + /// the XmlNode of the category, such as returned from the GetCategoryNodeByName, and the + /// name of the contract of the endpoint. + /// + /// A reference to the XmlNode of a manifest, referring to a 'category' element. + /// The full name of a contract type, as specified in the manifest. + /// For example, "Microsoft.Singularity.ServiceManager.ManagedServiceContract". + /// The index of the endpoint, or -1 if none was found. + public int FindCustomEndpointIndex(XmlNode! categoryNode, string! contractName) + { + foreach (XmlNode! endpointsNode in categoryNode.Children) { + if (endpointsNode.Name != "endpoints") + continue; + + foreach (XmlNode! endpointNode in endpointsNode.Children) { + if (endpointNode.Name != "customEndpoint") + continue; + + string idString = endpointNode.GetAttribute("id", ""); + if (idString == null || idString.Length == 0) + continue; + + int endpointIndex; + try { + endpointIndex = Int32.Parse(idString); + } + catch { + continue; + } + + string nodeContractName = endpointNode.GetAttribute("contractName", ""); + if (nodeContractName != null && nodeContractName == contractName) { + return endpointIndex; + } + } + } + + return -1; + } + + #if false public override string! ToString() @@ -352,7 +422,7 @@ namespace Microsoft.Singularity.Applications // the main executable sb.AppendFormat("", Path); - foreach(DataItem item in data.Values) { + foreach (DataItem item in data.Values) { if (item.Directory == null) { sb.AppendFormat("", item.Name, item.Value); @@ -377,4 +447,21 @@ namespace Microsoft.Singularity.Applications } #endif } -} + + public class ManifestException : Exception { + public ManifestException() + : base("ManifestException") + { + } + + public ManifestException(string message) + : base(message) + { + } + + public ManifestException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} \ No newline at end of file diff --git a/base/Libraries/Manifest/Parameters.sg b/base/Libraries/Manifest/Parameters.sg index f05007d..1b27520 100644 --- a/base/Libraries/Manifest/Parameters.sg +++ b/base/Libraries/Manifest/Parameters.sg @@ -10,6 +10,7 @@ /////////////////////////////////////////////////////////////////////////////// using System; +using System.Diagnostics; using System.Text; using System.Collections; @@ -30,7 +31,9 @@ namespace Microsoft.Singularity.Applications private string appName; private int totalCount; private string helpMsg; - private int boolCount, longCount, stringCount, stringArrayCount; + private int boolCount, longCount, stringCount; + private bool hasStringArray; + private int stringArrayIndex; public ParameterProcessor() { @@ -54,11 +57,15 @@ namespace Microsoft.Singularity.Applications public bool Resolved; public string HelpMsg; + public bool boolValue; + public string stringValue; + public long longValue; + public string[] stringArrayValue; + public Parameter(pType kind) { Resolved = false; Name = null; Kind = kind; - } } @@ -96,12 +103,12 @@ namespace Microsoft.Singularity.Applications string s = arg.ToLower(); - if (s == "true") { + if (s == "true" || s == "t" || s == "1") { value = true; ok = true; } - if (s == "false") { + if (s == "false" || s == "f" || s == "0") { value = false; ok = true; } @@ -122,8 +129,8 @@ namespace Microsoft.Singularity.Applications start = 2; } else { - for (int i=start; i < arg.Length; i++) { - if (!Char.IsNumber(arg[i]) && arg[i] != '-' ) return false; + for (int i = start; i < arg.Length; i++) { + if (!Char.IsNumber(arg[i]) && arg[i] != '-') return false; } } try { @@ -157,8 +164,7 @@ namespace Microsoft.Singularity.Applications private static bool InSeparators(char c, char []! separators) { - for (int i = 0; i < separators.Length; i++) - { + for (int i = 0; i < separators.Length; i++) { if (separators[i] == c) return true; } @@ -171,11 +177,9 @@ namespace Microsoft.Singularity.Applications { ArrayList tokens = new ArrayList(); - for (int i = 0; i <= last;) - { + for (int i = 0; i <= last;) { // Skip separators - while (i <= last && InSeparators(input[i], separators)) - { + while (i <= last && InSeparators(input[i], separators)) { i++; } @@ -190,14 +194,12 @@ namespace Microsoft.Singularity.Applications { i++; } - if (i != start) - { + if (i != start) { tokens.Add(input.Substring(start, i - start)); } // Skip separators - while (i <= last && InSeparators(input[i], separators)) - { + while (i <= last && InSeparators(input[i], separators)) { i++; } @@ -205,22 +207,18 @@ namespace Microsoft.Singularity.Applications break; // Try to quoted slurp word - if (input[i] == '\'') - { + if (input[i] == '\'') { start = i; i++; - while (i <= last && input[i] != '\'') - { + while (i <= last && input[i] != '\'') { i++; } - if (i <= last && input[i] == '\'') - { + if (i <= last && input[i] == '\'') { tokens.Add(input.Substring(start + 1, i - start - 1)); i++; } - else - { + else { tokens.Add(input.Substring(start, i - start)); i++; } @@ -233,71 +231,56 @@ namespace Microsoft.Singularity.Applications private Parameter FindParameter(string name) { if (parameterSet != null) { - for (int i=0; i < parameterSet.Length; i++) { + for (int i = 0; i < parameterSet.Length; i++) { Parameter a = parameterSet[i]; if (a == null) continue; assert a.Name != null; - if ( a.Name == name) return parameterSet[i]; + if (a.Name == name) return parameterSet[i]; } } - return null; - } - private bool ParseAndSetParameter(Process! p, Parameter! a, string! value) + private bool ParseAndSetParameter(Parameter! a, string! value) { - bool ok; - ParameterCode code; - bool boolValue; - long longValue; + a.Resolved = false; switch (a.Kind) { case pType.boolType: - ok = StringToBool(value, out boolValue); - if (!ok) { - Console.WriteLine("{0} for {1} is not a bool value.", value, a.Name); - } - else { - code = p.SetStartupBoolArg(a.Index, boolValue); - if (code != ParameterCode.Success){ - Console.WriteLine("unable to set bool index {0} to {1} for arg {2}. error={3}", - a.Index, boolValue, a.Name, CodeToString(code) ); - return false; - } - a.Resolved = true; - return true; + if (value == "") { + // Allow for "-x" to mean "-x=true". + a.Resolved = true; + a.boolValue = String.Compare(a.Default, "true", true) == 0; + } + else { + a.Resolved = StringToBool(value, out a.boolValue); } break; case pType.longType: - ok = StringToLong(value, out longValue); - if (!ok) { - Console.WriteLine("{0} for {1} is not a integer value.", value, a.Name); - } - else { - code = p.SetStartupLongArg(a.Index, longValue); - if (code != ParameterCode.Success){ - Console.WriteLine("unable to set string. index {0} to {1} for arg {2}. error={3}", - a.Index, longValue, a.Name, CodeToString(code)); - return false; - } - a.Resolved = true; - return true; - } + a.Resolved = StringToLong(value, out a.longValue); break; case pType.stringType: - code = p.SetStartupStringArg(a.Index, value); - if (code != ParameterCode.Success){ - Console.WriteLine("unable to set string. index {0} to {1} for arg {2}. error={3}", - a.Index, value, a.Name, CodeToString(code)); - return false; - } + a.stringValue = value; a.Resolved = true; - return true; break; } - return false; + + if(a.Resolved) { + return true; + } + else { + if(a.Kind == pType.boolType) { + Console.WriteLine("{0} for {1} is not a bool value.", value, a.Name); + } + else if(a.Kind == pType.longType) { + Console.WriteLine("{0} for {1} is not a integer value.", value, a.Name); + } + else { + Console.WriteLine("Unknown type for {0}", a.Name); + } + return false; + } } private string! KindToString(pType t) @@ -327,35 +310,36 @@ namespace Microsoft.Singularity.Applications } XmlNode catNode = manifest.GetCategoriesNode(); - if (catNode == null ) { + if (catNode == null) { Console.WriteLine(" unable to find categories node!"); return; } ArrayList actions = new ArrayList(); assert catNode != null; - foreach ( XmlNode n in catNode.Children) { + foreach (XmlNode n in catNode.Children) { assert n != null; string s = n.GetAttribute("Action", null); actions.Add(s); } - if (actions.Count > 0 ) { + if (actions.Count > 0) { Console.Write("\n {0} Actions: ", appName); - for (int i=0; i < actions.Count; i++) { + for (int i = 0; i < actions.Count; i++) { Console.Write("{0} ",actions[i]); } Console.WriteLine("\n"); } if (verbose) { - for (int i=0; i < actions.Count; i++) { + for (int i = 0; i < actions.Count; i++) { string a = (string) actions[i]; if (a != null ) Console.Write("\n Configuration Parameters for action @{0}",a); InitializeParameters(a, manifest); ShowArgs(action, manifest); } - } else { + } + else { // Show a reasonably-short list of commands. ShowCommandsSummary(manifest); } @@ -370,7 +354,7 @@ namespace Microsoft.Singularity.Applications void ShowCommandsSummary(Manifest! manifest) { XmlNode catNode = manifest.GetCategoriesNode(); - if (catNode == null ) { + if (catNode == null) { Console.WriteLine(" unable to find categories node!"); return; } @@ -379,7 +363,7 @@ namespace Microsoft.Singularity.Applications bool any = false; - foreach ( XmlNode node in catNode.Children) { + foreach (XmlNode node in catNode.Children) { assert node != null; if (node.Name != "category") @@ -394,7 +378,8 @@ namespace Microsoft.Singularity.Applications if (isDefault) { action = ""; actionWithAt = ""; - } else { + } + else { action = node.GetAttribute("Action", null); if (action == null) continue; @@ -420,18 +405,21 @@ namespace Microsoft.Singularity.Applications if (first) { Console.WriteLine(format, actionWithAt, help_line); first = false; - } else { + } + else { Console.WriteLine(format, "", help_line); } } - } else { + } + else { Console.WriteLine(format, actionWithAt, help); } } if (!any) { Console.WriteLine("The program does not support any commands."); - } else { + } + else { Console.WriteLine(); // Console.WriteLine("Use -?? for verbose info on all actions."); Console.WriteLine("Use '@cmd -?' for detailed help on a particular command."); @@ -512,7 +500,7 @@ namespace Microsoft.Singularity.Applications string[] actions = GetAllActions(manifest); - if (actions.Length > 0 ) { + if (actions.Length > 0) { foreach (string action in actions) { InitializeParameters(action, manifest); @@ -527,7 +515,8 @@ namespace Microsoft.Singularity.Applications for (int i = 0; i < help_lines.Length; i++) { Console.WriteLine(" " + help_lines[i]); } - } else { + } + else { Console.WriteLine(" " + this.helpMsg); } Console.WriteLine(); @@ -550,7 +539,8 @@ namespace Microsoft.Singularity.Applications buffer.Append(appName); buffer.Append(" @"); buffer.Append(action); - } else { + } + else { buffer.Append(appName); } @@ -633,13 +623,13 @@ namespace Microsoft.Singularity.Applications string[]! GetAllActions(Manifest! manifest) { XmlNode catNode = manifest.GetCategoriesNode(); - if (catNode == null ) { + if (catNode == null) { Console.WriteLine(" unable to find categories node!"); return new string[0]; } ArrayList actions = new ArrayList(); - foreach ( XmlNode n in catNode.Children) { + foreach (XmlNode n in catNode.Children) { assert n != null; string s = n.GetAttribute("Action", null); actions.Add(s); @@ -660,13 +650,13 @@ namespace Microsoft.Singularity.Applications } } - /** - - This method shows detailed help for a specific command. - It shows the command syntax, and information on each - parameter. - - */ + /// + // + // This method shows detailed help for a specific command. + // It shows the command syntax, and information on each + // parameter. + // + // void ShowVerboseCommandHelp(Manifest! manifest, string action) { Console.WriteLine("Help for command: " + action); @@ -719,7 +709,8 @@ namespace Microsoft.Singularity.Applications else Console.WriteLine(parameter_format, "", "", "", help_lines[i]); } - } else { + } + else { Console.WriteLine(parameter_format, a.Name, typestring, a.Mandatory ? "yes" : "no", a.HelpMsg != null ? a.HelpMsg : ""); } } @@ -730,52 +721,56 @@ namespace Microsoft.Singularity.Applications // manifest.SetEndpoints(null, action, true); } - private bool Parse(String[]! cmd, Manifest! manifest, out Process process, out string action) + /// + /// Parse the command line and return the appName, action, and strongly typed parameter values. + /// manifest is used to deduce the strong type information for the parameters. + /// + /// + /// True if a new process should be created. + /// There are several reasons this function may return false, including: + /// invalid action, + /// -? or -?? for help information only, + /// missing required parameters, + /// unknown parameter name, + /// incorrect parameter format, + /// + private bool Parse(String[]! cmd, + Manifest! manifest, + out string appName, + out string action, + out Parameter[] parameters) { - string name; + appName = null; action = null; - process = null; - String[] args = cmd; + parameters = null; + + if(cmd == null || cmd.Length == 0 || cmd[0] == null) { + Console.WriteLine("Invalid command line"); + return false; + } + + appName = (!)cmd[0]; + string[] args = cmd; // see if we have an action verb. If so determine if it is valid. // if it is valid prime the parameter set based on it. - if (cmd.Length > 1 && cmd[1] != null) { - assert cmd[1] != null; - string! s = (!)cmd[1]; - if ( s[0] == '@'){ + if (cmd.Length > 1 && cmd[1] != null && ((!)cmd[1]).Length >= 1 + && ((!)cmd[1])[0] == '@') { // we have an action. - action = s.Substring(1); - //Console.WriteLine("Action encountered action={0}",action); - if (!InitializeParameters(action, manifest)) { - Console.WriteLine("action init failed"); - return false; - } - args = new String[cmd.Length -1]; - Array.Copy(cmd, 1, args, 0, cmd.Length-1); - } - else { - if (!InitializeParameters(null, manifest)) { - Console.WriteLine("parameter init failed"); - return false; - } - } + action = ((!)cmd[1]).Substring(1); + InitializeParameters(action, manifest); + args = new String[cmd.Length -1]; + Array.Copy(cmd, 1, args, 0, cmd.Length-1); } else { - if (!InitializeParameters(null, manifest)) { - Console.WriteLine("parameter init failed"); - return false; - } + InitializeParameters(null, manifest); } - //if ( cmd.Length == 1 ) return false; - - appName = cmd[0]; - assert appName != null; - if (args.Length > 1 && args[1] != null && args[1] == "-?") { if (action != null) { ShowVerboseCommandHelp(manifest, action); - } else { + } + else { ShowCommandsSummaryWithArgs(manifest); } return false; @@ -786,57 +781,46 @@ namespace Microsoft.Singularity.Applications return false; } - // FIXFIX: should really parse and validate all the args and cache values - // BEFORE creating the process. Need to change data structures - //DebugStub.WriteLine("parameters.Parse creating new process"); - process = new Process(appName, action, null); - if (process == null) { - Console.WriteLine("Unable to create process {0} with action={1}",appName,action); - } - ArrayList extras = new ArrayList(); - + ArrayList parameterList = new ArrayList(); bool seenPositional = false; int positionalBase = 0; - Parameter a; for (int i=1; i < args.Length ; i++){ - string p = args[i]; - assert p != null; - //Console.WriteLine("processing {0}", p); - if (p[0] == '-') { + Parameter param = null; + string arg = args[i]; + assert arg != null; + if (arg.Length>0 && arg[0] == '-') { // this is an optional arg - p = p.Substring(1); - if ( p== null || p == "") { + arg = arg.Substring(1); + if (arg == null || arg == "") { Console.WriteLine("'-' with no parameter name is invalid"); return false; } - ArrayList tokens = Tokenize(p, p.Length - 1, new char[] {'=', ':'}); - name = (string) tokens[0]; - a = FindParameter(name); - if (a != null) { - //Console.WriteLine("found a match name={0}, rest={1} ", - // a.Name, tokens.Count >=2 ? tokens[1]: "empty"); - + ArrayList tokens = Tokenize(arg, arg.Length - 1, new char[] {'=', ':'}); + string name = (string)tokens[0]; + param = FindParameter(name); + if (param != null) { if (tokens.Count == 1) { - //this is potentially a bool parameter with no value - if (a.Kind == pType.boolType) { - // set to opposite of default - string val = a.Default == "False" ? "True" : "False"; - bool ok = ParseAndSetParameter(process, a, val); - if (!ok) return false; - //Console.WriteLine("Setting {0} to {1}", a.Name, val); - + //this is potentially a bool parameter with no value + if (param.Kind == pType.boolType) { + param.boolValue = true; + param.Resolved = true; } else { Console.WriteLine("invalid parameter assignment"); return false; } } - if (tokens.Count == 2) { - bool ok = ParseAndSetParameter(process, a, (string)(!) tokens[1]); + else if (tokens.Count == 2) { + bool ok = ParseAndSetParameter(param, (string)(!)tokens[1]); if (!ok) return false; } + else { + Console.WriteLine("invalid parameter assignment"); + return false; + } + parameterList.Add(param); } else { //unknown parameter @@ -845,153 +829,175 @@ namespace Microsoft.Singularity.Applications } } else { - a = null; if (!seenPositional) { seenPositional = true; positionalBase = i; } - assert i-positionalBase >= 0; - if (positionSet != null && (i-positionalBase) < positionSet.Length ) { - a = positionSet[i-positionalBase]; + assert i - positionalBase >= 0; + if (positionSet != null && (i - positionalBase) < positionSet.Length) { + param = positionSet[i - positionalBase]; } - if (a != null) { - //Console.WriteLine("Positional match: for {0} at {1} arrayIdx={2}", - // a.Name, a.Position, i); - bool ok = ParseAndSetParameter(process, a, (string)(!) args[i]); + if (param != null) { + bool ok = ParseAndSetParameter(param, arg); if (!ok) return false; - + parameterList.Add(param); } else { - if (stringArrayCount != 1) { - Console.WriteLine("{0} is not a known parameter.", p); + if (hasStringArray) { + // add it to the Array List + // After processing all the tokens the list will be + // converted to StringArray parameter if present + extras.Add(args[i]); + } + else { + Console.WriteLine("{0} is not a known parameter.", arg); + return false; } - // add it to the Array List - // After processing all the tokens the list will be - // converted to StringArray parameter if present - extras.Add(args[i]); - //return false; } } } - // check to see if all positional args were resolved - bool status = true; - if (positionSet != null) { - for (int i=0; i < positionSet.Length; i++ ){ - Parameter p = positionSet[i]; - if (p != null) { - assert p.Name != null; - if (p.Resolved != true){ - if (p.Mandatory) { - Console.WriteLine("Mandatory positional Parameter {0} at {1} not set.", - p.Name, i); - status = false; - } - } + // check to see if all positional args were resolved + if (positionSet != null) { + for (int i=0; i < positionSet.Length; i++) { + Parameter param = positionSet[i]; + if (param != null && ! param.Resolved && param.Mandatory) { + Console.WriteLine("Mandatory positional Parameter {0} at {1} not set.", + param.Name, i); + return false; } } } - - // check to see if all mandatory args were resolved - if (status == true) { - if (parameterSet != null) { - for (int i=0; i < parameterSet.Length; i++ ){ - Parameter p = parameterSet[i]; - assert p != null; - assert p.Name != null; - - if (p.Mandatory == true){ - if (p.Resolved != true){ - Console.WriteLine("Mandatory Parameter {0} not set.", p.Name); - status = false; - } - } - } - } - } - // Set default values if needed on non mandatory parameters - if (status == true) { - if (parameterSet != null) { - for (int i=0; i < parameterSet.Length; i++ ){ - Parameter p = parameterSet[i]; - assert p != null; - assert p.Name != null; - if (p.Kind == pType.stringArrayType) { - // ignore defaults for string type.. - continue; - } - if (p.Resolved != true && p.Default != null){ - //Console.WriteLine("setting default: {0}={1}", p.Name, p.Default); - ParseAndSetParameter(process, p, p.Default); - } + // check to see if all mandatory args were resolved + if (parameterSet != null) { + for (int i=0; i < parameterSet.Length; i++) { + Parameter param = parameterSet[i]; + assert param != null; + assert param.Name != null; + if (param.Mandatory && ! param.Resolved) { + Console.WriteLine("Mandatory Parameter {0} not set.", param.Name); + return false; } } } - // see if there were any extra args. If so pass them on to the process - // if the manifest defined a StringArrayParameter - string[] strings = null; - if (extras.Count > 0) { - // is there 1 ArrayParameter defined in the manifest? - //Console.WriteLine("extras={0}, arrayCount={1}", extras.Count, stringArrayCount); - if (stringArrayCount == 1) { - // Always send in if app requests is, even if 0 extra args - strings = new string[extras.Count]; - for (int i=0; i < extras.Count; i++) { - strings[i] = (string) extras[i]; - //Console.WriteLine(" strings[{0}]={1}",i,strings[i]); + + // Set default values if needed on non mandatory parameters + if (parameterSet != null) { + for (int i=0; i < parameterSet.Length; i++ ) { + Parameter param = parameterSet[i]; + assert param != null; + assert param.Name != null; + // ignore defaults for string array type.. + if (param.Kind != pType.stringArrayType + && !param.Resolved) { + ParseAndSetParameter(param, param.Default != null ? param.Default : ""); + parameterList.Add(param); } - Console.WriteLine("Setting Strings to {0}", strings); - process.SetStartupStringArrayArg(0, strings); - } - else { - Console.WriteLine("There is not exactly ONE StringArray parameter.\n " + - "Unsure where to place strings.\n" + - "#of String Array parameters={0}",stringArrayCount); - return false; } } - return status ; + + // is there a StringArrayParameter defined in the manifest? + // If so pass the extras to the process + if (hasStringArray) { + // note if hasStringArray is true, then parameterSet is guaranteed to be non-null + Parameter! param = (!)((!)parameterSet)[stringArrayIndex]; + param.stringArrayValue = (string[])extras.ToArray(typeof(string)); + parameterList.Add(param); + } + + parameters = (Parameter[])parameterList.ToArray(typeof(Parameter)); + return true; } - public bool ProcessParameters( String[] commandLine, Manifest manifest, out Process p, out string action) + /// + /// Parse the command line into stronly typed parameters using the manifest, + /// validate the parameters, and create the process if all parameters + /// are in the right format. + /// + /// + /// True if process is created and paramters are parsed, + /// validated, and set successfully. + /// + public bool ProcessParameters(string[] commandLine, + Manifest manifest, + out Process process, + out string action) { - ParameterCode code; - long longValue; - string stringValue; - bool boolValue; - + process = null; action = null; - p = null; - if (manifest == null) return false; if (commandLine == null) return false; - return Parse( commandLine, manifest, out p, out action); + string appName; + Parameter[] parameters; + if(! Parse(commandLine, manifest, out appName, out action, out parameters)) { + Console.WriteLine("Failed to parse command line"); + return false; + } + + process = new Process((!)appName, action, null); + if (process == null) { + Console.WriteLine("Unable to create process {0} with action={1}",appName,action); + return false; + } + + if (parameters != null) { + for(int i = 0; i < parameters.Length; i++) { + Parameter! param = (!)parameters[i]; + ParameterCode code = ParameterCode.Undefined; + + switch(param.Kind) { + case pType.boolType: + code = process.SetStartupBoolArg(param.Index, param.boolValue); + break; + case pType.longType: + code = process.SetStartupLongArg(param.Index, param.longValue); + break; + case pType.stringType: + code = process.SetStartupStringArg(param.Index, param.stringValue); + break; + case pType.stringArrayType: + string[] stringArray = param.stringArrayValue == null ? + new string[] {} : param.stringArrayValue; + code = process.SetStartupStringArrayArg(0, stringArray); + break; + } + + if (code != ParameterCode.Success){ + Console.WriteLine("unable to set {0} index {1} to {2} for arg {3}. error={4}", + param.Kind, param.Index, param.boolValue, param.Name, CodeToString(code) ); + return false; + } + } + } + return true; } - private bool SetPosition(Parameter! p) + /// + /// Set the position for the parameter. + /// + private void SetPosition(Parameter! p) { - bool result = true; - if (p != null) { - if (p.Position != -1) { - if (p.Position < 0 || p.Position > totalCount-1) { - Console.WriteLine("{0} position index out of range ({1})", - p.Name, p.Position); - result = false; - } - else { - if (positionSet != null) { - positionSet[p.Position] = p; - } - result = true; - } + if (p != null && p.Position != -1) { + if (p.Position < 0 || p.Position > totalCount-1) { + string error = string.Format("{0} position index out of range ({1})", + p.Name, p.Position); + throw new ManifestException(error); + } + else if (positionSet != null) { + positionSet[p.Position] = p; } } - return result; } - public bool InitializeParameters(string actionName, Manifest! manifest) + /// + /// Use the manifest to initialize the parameters without values. + /// Later ProcessParameters() will fill in the values from the command line. + /// + /// Throws ManifestException upon error. + /// + public void InitializeParameters(string actionName, Manifest! manifest) { XmlNode bools; XmlNode strings; @@ -1003,7 +1009,9 @@ namespace Microsoft.Singularity.Applications longCount = 0; boolCount = 0; stringCount = 0; - stringArrayCount = 0; + hasStringArray = false; + stringArrayIndex = 0; + int stringArrayCount = 0; bools = null; longs = null; @@ -1016,27 +1024,19 @@ namespace Microsoft.Singularity.Applications // If action is null we need to ensure there is 1 Parameter configuration // and its DefaultAction parameter is set to true. XmlNode actionNode = manifest.GetCategoryNode(actionName); + if (actionNode == null) { - if (actionName != null) { - Console.WriteLine("Unknown action {0} recognized", actionName); - } - else { - Console.WriteLine("Unable to find default action"); - } - return false; + string error = actionName != null ? + string.Format("Unknown action {0} recognized", actionName) : + "Unable to find default action"; + throw new ManifestException(error); } string categoryName = actionNode.GetAttribute("name",""); -#if VERBOSE - Console.WriteLine(" using category {0}, action={1}, default={2}", - categoryName, - actionNode.GetAttribute("Action",""), - actionNode.GetAttribute("DefaultAction",false)); -#endif if (categoryName != "console") { - Console.WriteLine("This manifest's category is not 'console': ({0})", categoryName); - return false; + throw new ManifestException(string.Format( + "This manifest's category is not 'console': ({0})", categoryName)); } bools = manifest.GetBoolParameterNode(actionNode); @@ -1045,108 +1045,114 @@ namespace Microsoft.Singularity.Applications stringArrays = manifest.GetStringArrayParameterNode(actionNode); helpMsg = manifest.GetHelpMessage(actionNode); - if ( bools == null && longs == null && strings == null && stringArrays == null) { + if (bools == null && longs == null && strings == null && stringArrays == null) { parameterSet = new Parameter[0]; positionSet = new Parameter[0]; - return true; + return; } - if ( bools != null ) { + if (bools != null) { boolCount = bools.GetAttribute("length", 0); } - if ( longs != null ) { + if (longs != null) { longCount = longs.GetAttribute("length", 0); } - if ( strings != null ) { + if (strings != null) { stringCount = strings.GetAttribute("length", 0); } - if ( stringArrays != null ) { + if (stringArrays != null) { stringArrayCount = stringArrays.GetAttribute("length", 0); + if (stringArrayCount > 1) { + throw new ManifestException("Cannot support more than one string array parameters"); + } + else if (stringArrayCount == 1) { + hasStringArray = true; + } } totalCount = stringCount + stringArrayCount + longCount + boolCount; parameterSet = new Parameter[totalCount]; positionSet = new Parameter[totalCount]; - if (totalCount == 0 ) return true; + if (totalCount == 0 ) return; assert positionSet != null; assert parameterSet != null; pIndex = 0; - if ( bools != null ) { - if (boolCount > 0 ) { + if (bools != null) { + if (boolCount > 0) { foreach (XmlNode! bp in bools.Children) { if ( bp.Name == "BoolParameter") { Parameter p = GetXmlAttributes(bp, pType.boolType); - if (!SetPosition(p)) return false; + SetPosition(p); parameterSet[pIndex++] = p; } } } } - if ( longs != null ) { - if (longCount > 0 ) { + if (longs != null) { + if (longCount > 0) { foreach (XmlNode! lp in longs.Children) { if ( lp.Name == "LongParameter") { Parameter p = GetXmlAttributes(lp, pType.longType); - if (!SetPosition(p)) return false; + SetPosition(p); parameterSet[pIndex++] = p; } } } } - if ( strings != null ) { - if (stringCount > 0 ) { + if (strings != null) { + if (stringCount > 0) { foreach (XmlNode! sp in strings.Children) { if ( sp.Name == "StringParameter") { Parameter p = GetXmlAttributes(sp, pType.stringType); - if (!SetPosition(p)) return false; + SetPosition(p); parameterSet[pIndex++] = p; } } } } - if ( stringArrays != null ) { - if (stringArrayCount > 0 ) { + if (stringArrays != null) { + if (stringArrayCount > 0) { foreach (XmlNode! sp in stringArrays.Children) { if ( sp.Name == "StringArrayParameter") { Parameter p = GetXmlAttributes(sp, pType.stringArrayType); - if (!SetPosition(p)) return false; + SetPosition(p); + stringArrayIndex = pIndex; parameterSet[pIndex++] = p; } } } } - if ( positionSet != null && positionSet[0] != null) { + if (positionSet != null && positionSet[0] != null) { havePositional = true; bool seenNull = false; - for (int i=0; i < positionSet.Length; i++) { - if ( positionSet[i] == null ) { + for (int i = 0; i < positionSet.Length; i++) { + if (positionSet[i] == null) { seenNull = true; continue; } if (seenNull) { - Console.Write("Positional indices not contiguous!"); - for (int j=0; j < positionSet.Length; j++) { + StringBuilder buffer = new StringBuilder(64); + buffer.Append("Positional indices not contiguous! "); + for (int j = 0; j < positionSet.Length; j++) { Parameter p = positionSet[j]; - if ( p != null ) { - Console.Write("{0}({1}) ",j, p.Name); + if (p != null ) { + buffer.AppendFormat("{0}({1}) ",j, p.Name); } } - Console.WriteLine(); - return false; + throw new ManifestException(buffer.ToString()); } } } - return true; } } } diff --git a/base/Libraries/Manifest/job.sg b/base/Libraries/Manifest/job.sg index 6975c81..7f0b7e6 100644 --- a/base/Libraries/Manifest/job.sg +++ b/base/Libraries/Manifest/job.sg @@ -131,7 +131,7 @@ namespace Microsoft.Singularity.ConsoleApplications private static string! CommandLineString(string[]! command) { StringBuilder sb = new StringBuilder(); - foreach(string s in command) { + foreach (string s in command) { sb.Append(s); sb.Append(' '); } @@ -274,7 +274,7 @@ namespace Microsoft.Singularity.ConsoleApplications assert jobControl != null; assert ds != null; - if ( !ds.InState(DirectoryServiceContract.Ready.Value) ) { + if (!ds.InState(DirectoryServiceContract.Ready.Value)) { Console.WriteLine("ConsoleJob:CreateProcess: directory service endpoint not in Ready state"); return false; } @@ -286,7 +286,7 @@ namespace Microsoft.Singularity.ConsoleApplications return false; } Console.Write(" ConsoleJob.CreateProcess: cmd="); - for (int i=0; i < commandLine.Length; i++) { + for (int i = 0; i < commandLine.Length; i++) { Console.Write("{0} ",commandLine[i]); } Console.Write("\n"); @@ -400,7 +400,7 @@ namespace Microsoft.Singularity.ConsoleApplications DebugStub.WriteLine("ConsoleJob.WaitForJob: job is null!"); return -1; } - if ( this.process == null) { + if (this.process == null) { DebugStub.WriteLine("ConsoleJob.WaitForJob: process is null!"); return -1; } @@ -431,7 +431,7 @@ namespace Microsoft.Singularity.ConsoleApplications } } - // [Hawblitzel] TODO: better ways to wait on a child process + // TODO: better ways to wait on a child process private class WaitForChildThread { private Process! process; diff --git a/base/Libraries/NetStack.Channels.Public/AssemblyInfo.sg b/base/Libraries/NetStack.Channels.Public/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Libraries/NetStack.Channels.Public/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Libraries/NetStack.Channels.Public/ChannelUtils.sg b/base/Libraries/NetStack.Channels.Public/ChannelUtils.sg index 79eff84..a52291a 100644 --- a/base/Libraries/NetStack.Channels.Public/ChannelUtils.sg +++ b/base/Libraries/NetStack.Channels.Public/ChannelUtils.sg @@ -4,7 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: ChannelUtils.sg // Note: Utilities for using the NetStack channels // diff --git a/base/Libraries/NetStack.Channels.Public/DNSImpConnection.sg b/base/Libraries/NetStack.Channels.Public/DNSImpConnection.sg index c8e90c2..f70f83a 100644 --- a/base/Libraries/NetStack.Channels.Public/DNSImpConnection.sg +++ b/base/Libraries/NetStack.Channels.Public/DNSImpConnection.sg @@ -4,7 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: DNSImpConnection.gs // Note: Client-side helper for the IP Channel Contract // @@ -30,24 +29,21 @@ namespace NetStack.Channels.Public ep.SendGetNameServers(); ep.RecvAddressList(out addrList); - try - { + try { IPv4[] retval; if (addrList == null) { retval = new IPv4[0]; } else { retval = new IPv4[addrList.Length]; - for (int i = 0; i < addrList.Length; ++i) - { + for (int i = 0; i < addrList.Length; ++i) { retval[i] = new IPv4(addrList[i]); } } return retval; } - finally - { + finally { delete addrList; } } @@ -62,19 +58,15 @@ namespace NetStack.Channels.Public ep.SendResolve(Bitter.FromString(name)); - switch receive - { + switch receive { case ep.RecvDNSResults(uint[] in ExHeap addrList, char[][] in ExHeap aliasesResult) : { - try - { - if (aliasesResult != null) - { + try { + if (aliasesResult != null) { aliases = new string[aliasesResult.Length]; - for (int i = 0; i < aliasesResult.Length; ++i) - { + for (int i = 0; i < aliasesResult.Length; ++i) { expose (aliasesResult[i]) { aliases[i] = Bitter.ToString(aliasesResult[i]); } @@ -86,16 +78,16 @@ namespace NetStack.Channels.Public if (addrList != null) { addrs = new IPv4[addrList.Length]; - for (int i = 0; i < addrList.Length; ++i) - { addrs[i] = new IPv4(addrList[i]); } + for (int i = 0; i < addrList.Length; ++i) { + addrs[i] = new IPv4(addrList[i]); + } } else { addrs = new IPv4[0]; } return true; // success; } - finally - { + finally { delete aliasesResult; // does deep freeing delete addrList; } diff --git a/base/Libraries/NetStack.Channels.Public/NetStack.Channels.Public.csproj b/base/Libraries/NetStack.Channels.Public/NetStack.Channels.Public.csproj index 1e426cf..125f8c8 100644 --- a/base/Libraries/NetStack.Channels.Public/NetStack.Channels.Public.csproj +++ b/base/Libraries/NetStack.Channels.Public/NetStack.Channels.Public.csproj @@ -17,6 +17,7 @@ + diff --git a/base/Libraries/Ntlm/AssemblyInfo.sg b/base/Libraries/Ntlm/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Libraries/Ntlm/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Libraries/Ntlm/Ntlm.csproj b/base/Libraries/Ntlm/Ntlm.csproj index f40f1af..0c4478a 100644 --- a/base/Libraries/Ntlm/Ntlm.csproj +++ b/base/Libraries/Ntlm/Ntlm.csproj @@ -22,6 +22,7 @@ + diff --git a/base/Libraries/Ntlm/NtlmClientCredentials.sg b/base/Libraries/Ntlm/NtlmClientCredentials.sg index 828300e..e69de29 100644 --- a/base/Libraries/Ntlm/NtlmClientCredentials.sg +++ b/base/Libraries/Ntlm/NtlmClientCredentials.sg @@ -1,5 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - diff --git a/base/Libraries/Ntlm/NtlmMessages.sg b/base/Libraries/Ntlm/NtlmMessages.sg index 9f22641..7320f9d 100644 --- a/base/Libraries/Ntlm/NtlmMessages.sg +++ b/base/Libraries/Ntlm/NtlmMessages.sg @@ -8,8 +8,8 @@ // // Note: // -// This file contains structures and enumerated types of the NTLM -// authentication protocol. +// This file contains structures and enumerated types of the NTLM +// authentication protocol. // /////////////////////////////////////////////////////////////////////////////// @@ -32,17 +32,17 @@ namespace System.Security.Protocols.Ntlm 0 }; #endif - + // This is the NTLM message signature, encoded as a little-endian ulong. - public const ulong MessageSignature64Le = - ((ulong)'N') - | (((ulong)'T') << 8) - | (((ulong)'L') << 0x10) - | (((ulong)'M') << 0x18) - | (((ulong)'S') << 0x20) - | (((ulong)'S') << 0x28) - | (((ulong)'P') << 0x30) - /* 0x38 position is zero */; + public const ulong MessageSignature64Le = + ((ulong)'N') + | (((ulong)'T') << 8) + | (((ulong)'L') << 0x10) + | (((ulong)'M') << 0x18) + | (((ulong)'S') << 0x20) + | (((ulong)'S') << 0x28) + | (((ulong)'P') << 0x30) + /* 0x38 position is zero */; public const int HeaderLength = 0x10; @@ -57,187 +57,187 @@ namespace System.Security.Protocols.Ntlm Response = 3, } - /** - - All NTLMSSP messages begin with this header. - - */ - [StructLayout(LayoutKind.Sequential, Pack=1)] - pointerfree struct NtlmMessageHeader - { - /** - This value should always be "NTLMSSP\0", encoded as a little-endian 64-bit ulong. - NtlmConstants.MessageSignature64Le encodes this value. - - */ - public ulong Signature; - - /** A value from NtlmMessageType. */ - public uint MessageType; - } - - /** - - The Negotiate message is the first message in an NTLM exchange. - The client (supplicant) sends this message to the server, requesting a session - and indicating what kind of options this client supports. The OemDomainName - and OemWorkstationName values can be provided for diagnostics and logging, - but the server is under no obligation to trust them. - - */ - [StructLayout(LayoutKind.Sequential, Pack=1)] - pointerfree struct NtlmNegotiateMessage - { - public NtlmMessageHeader Header; - public uint NegotiateFlags; - public BufferRegion OemDomainName; - public BufferRegion OemWorkstationName; - public ulong Version; - } + /// + // + // All NTLMSSP messages begin with this header. + // + // + [StructLayout(LayoutKind.Sequential, Pack=1)] + pointerfree struct NtlmMessageHeader + { + /// + /// This value should always be "NTLMSSP\0", encoded as a little-endian 64-bit ulong. + /// NtlmConstants.MessageSignature64Le encodes this value. + /// + // + public ulong Signature; + + /// A value from NtlmMessageType. + public uint MessageType; + } + + /// + // + //The Negotiate message is the first message in an NTLM exchange. + //The client (supplicant) sends this message to the server, requesting a session + //and indicating what kind of options this client supports. The OemDomainName + //and OemWorkstationName values can be provided for diagnostics and logging, + //but the server is under no obligation to trust them. + // + // + [StructLayout(LayoutKind.Sequential, Pack=1)] + pointerfree struct NtlmNegotiateMessage + { + public NtlmMessageHeader Header; + public uint NegotiateFlags; + public BufferRegion OemDomainName; + public BufferRegion OemWorkstationName; + public ulong Version; + } [Flags] public enum NtlmNegotiateFlags { None = 0, - /** Text strings are in unicode */ + /// Text strings are in unicode NegotiateUnicode = 0x00000001, - - /** Text strings are in OEM */ + + /// Text strings are in OEM NegotiateOem = 0x00000002, - - /** Server should return its authentication realm */ + + /// Server should return its authentication realm RequestTarget = 0x00000004, - /** Request signature capability */ + /// Request signature capability NegotiateSign = 0x00000010, - - /** Request confidentiality */ + + /// Request confidentiality NegotiateSeal = 0x00000020, - - /** Use datagram style authentication */ + + /// Use datagram style authentication NegotiateDatagram = 0x00000040, - - /** Use LM session key for sign/seal */ + + /// Use LM session key for sign/seal NegotiateLmKey = 0x00000080, - /** NetWare authentication */ + /// NetWare authentication NegotiateNetware = 0x00000100, - - /** NTLM authentication */ + + /// NTLM authentication NegotiateNtlm = 0x00000200, - - /** NT authentication only (no LM) */ + + /// NT authentication only (no LM) NegotiateNtOnly = 0x00000400, - - /** NULL Sessions on NT 5.0 and beyond */ + + /// NULL Sessions on NT 5.0 and beyond NegotiateNullSession = 0x00000800, - /** Domain Name supplied on negotiate */ + /// Domain Name supplied on negotiate NegotiateOemDomainSupplied = 0x1000, - - /** Workstation Name supplied on negotiate */ + + /// Workstation Name supplied on negotiate NegotiateOemWorkstationSupplied = 0x2000, - - /** Indicates client/server are same machine */ + + /// Indicates client/server are same machine NegotiateLocalCall = 0x00004000, - - /** Sign for all security levels */ + + /// Sign for all security levels NegotiateAlwaysSign = 0x00008000, } - /** - - - Describes the header for the NTLM "Challenge" message. This message is also known as an - NTLM "Type 3" message. This message is sent from a server (authenticator) to a client - (supplicant). The server generates a random 8-byte challenge (nonce). - - - */ - [StructLayout(LayoutKind.Sequential, Pack=1)] - pointerfree struct NtlmChallengeMessage - { - public NtlmMessageHeader Header; - - /** - - The domain name (realm name) of the server. This value may or may not be present. - If the NtlmNegotiateFlag.RequestTarget flag is set in the Negotiate message, then - the client requests this field, but the server is not obligated to send this value. - - */ - public BufferRegion TargetName; + /// + // + // + // Describes the header for the NTLM "Challenge" message. This message is also known as an + // NTLM "Type 3" message. This message is sent from a server (authenticator) to a client + // (supplicant). The server generates a random 8-byte challenge (nonce). + // + // + // + [StructLayout(LayoutKind.Sequential, Pack=1)] + pointerfree struct NtlmChallengeMessage + { + public NtlmMessageHeader Header; - /** The set of negotiation flags that the server has agreed to. */ - public uint NegotiateFlags; - - /** The 8-byte challenge, encoded as a 64-bit integer. */ - public ulong PackedChallenge; - - // The contents of the TargetName follow. - } + /// + // + // The domain name (realm name) of the server. This value may or may not be present. + // If the NtlmNegotiateFlag.RequestTarget flag is set in the Negotiate message, then + // the client requests this field, but the server is not obligated to send this value. + // + // + public BufferRegion TargetName; + + /// The set of negotiation flags that the server has agreed to. + public uint NegotiateFlags; + + /// The 8-byte challenge, encoded as a 64-bit integer. + public ulong PackedChallenge; + + // The contents of the TargetName follow. + } - /** - - - This structure describes the header for the NTLM "Response" message, also known as the NTLM - "Type 3" message. This message is sent from the client (supplicant) to the server (authenticator), - and is the last message in the exchange. This message proves that the client has valid credentials. - - - */ - [StructLayout(LayoutKind.Sequential, Pack=1)] - pointerfree struct NtlmResponseMessage - { - public NtlmMessageHeader Header; - - /** Describes the size and location of the LAN Manager response. */ - public BufferRegion LmChallengeResponse; - - /** Describes the size and location of the NT response. */ - public BufferRegion NtChallengeResponse; - - /** Describes the size and location of the domain name of the authenticating user. */ - public BufferRegion DomainName; - - /** Describes the size and location of the username of the authenticating user. */ - public BufferRegion UserName; - - /** Describes the size and location of the workstation name (computer name) of the client's computer. */ - public BufferRegion Workstation; - - // The contents of the many BufferRegion fields follow. - } + /// + // + // + // This structure describes the header for the NTLM "Response" message, also known as the NTLM + // "Type 3" message. This message is sent from the client (supplicant) to the server (authenticator), + // and is the last message in the exchange. This message proves that the client has valid credentials. + // + // + // + [StructLayout(LayoutKind.Sequential, Pack=1)] + pointerfree struct NtlmResponseMessage + { + public NtlmMessageHeader Header; - /** - - - Identifies a region within a buffer. This structure is compatible with the STRING32 structure - used by NT, which is used to describe the offset and length of variable-length strings that are - stored after a fixed-length message header. - - - - NTLM uses this structure to describe the size and location variable-length strings in NTLMSSP messages. - - - */ - [StructLayout(LayoutKind.Sequential, Pack=1)] - pointerfree struct BufferRegion - { - public ushort Length; // the length in bytes of the string - public ushort MaximumLength; // the maximum number of bytes to write to the buffer (n/a in networking!) - public ushort Offset; // offset within the message of this string - public ushort Reserved; // always zero - - public BufferRegion(ushort length, ushort maximumLength, ushort offset) - { - this.Length = length; - this.MaximumLength = maximumLength; - this.Offset = offset; - this.Reserved = 0; - } - } + /// Describes the size and location of the LAN Manager response. + public BufferRegion LmChallengeResponse; + + /// Describes the size and location of the NT response. + public BufferRegion NtChallengeResponse; + + /// Describes the size and location of the domain name of the authenticating user. + public BufferRegion DomainName; + + /// Describes the size and location of the username of the authenticating user. + public BufferRegion UserName; + + /// Describes the size and location of the workstation name (computer name) of the client's computer. + public BufferRegion Workstation; + + // The contents of the many BufferRegion fields follow. + } + + /// + // + // + // Identifies a region within a buffer. This structure is compatible with the STRING32 structure + // used by NT, which is used to describe the offset and length of variable-length strings that are + // stored after a fixed-length message header. + // + // + // + // NTLM uses this structure to describe the size and location variable-length strings in NTLMSSP messages. + // + // + // + [StructLayout(LayoutKind.Sequential, Pack=1)] + pointerfree struct BufferRegion + { + public ushort Length; // the length in bytes of the string + public ushort MaximumLength; // the maximum number of bytes to write to the buffer (n/a in networking!) + public ushort Offset; // offset within the message of this string + public ushort Reserved; // always zero + + public BufferRegion(ushort length, ushort maximumLength, ushort offset) + { + this.Length = length; + this.MaximumLength = maximumLength; + this.Offset = offset; + this.Reserved = 0; + } + } } diff --git a/base/Libraries/Ntlm/NtlmSupplicant.sg b/base/Libraries/Ntlm/NtlmSupplicant.sg index 5c1da67..f5b5e1e 100644 --- a/base/Libraries/Ntlm/NtlmSupplicant.sg +++ b/base/Libraries/Ntlm/NtlmSupplicant.sg @@ -54,56 +54,56 @@ using Utils; namespace System.Security.Protocols.Ntlm { - /** - - - Implements the NTLM authentication protocol. - - - This is a "static" class; there are no instance fields or methods. - None of the methods of this class have any side-effects, outside of - operating on the parameters passed to them. - - - */ + /// + // + // + // Implements the NTLM authentication protocol. + // + // + // This is a "static" class; there are no instance fields or methods. + // None of the methods of this class have any side-effects, outside of + // operating on the parameters passed to them. + // + // + // public /*static*/ sealed class NtlmSupplicant { private NtlmSupplicant() {} - /** - - Builds an NTLMSSP "Negotiate" message, which begins an NTLM authentication exchange. - The caller supplies negotiation flags, which request certain behaviors, as well as - the domain name and workstation name of the client computer. - - - - - The caller can also provide the domain name and workstation name of the client. - These values are not required, and are usually used for event logging. - - - This method does not have any side-effects. - - - - - Enables certain NTLM options. See the NtlmNegotiateFlags enumerated type for more info. - - - The domain name of the client computer. If the caller does not want - to provide this information, pass an empty string. - - - The name of the client computer. If the caller does not want to provide this information, - then pass an empty string. - - - A buffer containing the encoded NTLMSSP "Negotiate" message. This message should be sent - to any application that supports NTLM authentication, using whatever transport mechanism - is appropriate for the application. - - */ + /// + // + // Builds an NTLMSSP "Negotiate" message, which begins an NTLM authentication exchange. + // The caller supplies negotiation flags, which request certain behaviors, as well as + // the domain name and workstation name of the client computer. + // +// + // + // + // The caller can also provide the domain name and workstation name of the client. + // These values are not required, and are usually used for event logging. + // + // + // This method does not have any side-effects. + // + // + // + // + // Enables certain NTLM options. See the NtlmNegotiateFlags enumerated type for more info. + // + // + // The domain name of the client computer. If the caller does not want + // to provide this information, pass an empty string. + // + // + // The name of the client computer. If the caller does not want to provide this information, + // then pass an empty string. + // + // + // A buffer containing the encoded NTLMSSP "Negotiate" message. This message should be sent + // to any application that supports NTLM authentication, using whatever transport mechanism + // is appropriate for the application. + // + // public static byte[]! GetNegotiate(NtlmNegotiateFlags flags, string! domain, string! workstation) { Encoding encoding = Encoding.Unicode; @@ -154,8 +154,7 @@ namespace System.Security.Protocols.Ntlm ulong a = x; byte[]! result = new byte[8]; - for (int i = 0; i < 8; i++) - { + for (int i = 0; i < 8; i++) { // the LOWEST bit is the parity bit // right now, we always set the parity bit to 0 byte b = (byte)((a >> 56) & 0xfe); @@ -174,8 +173,7 @@ namespace System.Security.Protocols.Ntlm byte temp = b; int parity = 0; - for (int i = 1; i < 8; i++) - { + for (int i = 1; i < 8; i++) { parity ^= temp & 2; temp >>= 1; } @@ -187,39 +185,39 @@ namespace System.Security.Protocols.Ntlm } #endregion - /** - - - This method computes part of the NTLM hash function. It operates on part of the password, 7 characters - at a time, and computes a hash of them. The hash is then stored in the 'output' buffer at the offset - 'outputindex'. The hash is always 8 bytes long, so the 'output' buffer needs to have a length of at - least outputindex + 8. - - - This hash function is known to be cryptographically weak. - - - This method only works on passwords that contain only 7-bit ASCII character. - Lower-case characters are forced to upper-case. - - - - The cleartext password of the user. - The method will read up to 7 characters of the password, starting at 'passwordindex'. - - - The index of the first character within 'password' to read. - This value may legally be greater than or equal to the length of 'password'. - The method will read the first 7 characters, beginning at this index, and if there - are fewer than 7 valid characters (including no characters), the method will pad - with zeroes. - - - The output buffer in which to store the 8-byte hash of the portion of the password. - The length of this buffer must be at least 'outputindex' + 8. - - The position within the 'output' buffer to store the hash. - */ + /// + // + // + // This method computes part of the NTLM hash function. It operates on part of the password, 7 characters + // at a time, and computes a hash of them. The hash is then stored in the 'output' buffer at the offset + // 'outputindex'. The hash is always 8 bytes long, so the 'output' buffer needs to have a length of at + // least outputindex + 8. + // + // + // This hash function is known to be cryptographically weak. + // + // + // This method only works on passwords that contain only 7-bit ASCII character. + // Lower-case characters are forced to upper-case. + // + // + // + // The cleartext password of the user. + // The method will read up to 7 characters of the password, starting at 'passwordindex'. + // + // + // The index of the first character within 'password' to read. + // This value may legally be greater than or equal to the length of 'password'. + // The method will read the first 7 characters, beginning at this index, and if there + // are fewer than 7 valid characters (including no characters), the method will pad + // with zeroes. + // + // + // The output buffer in which to store the 8-byte hash of the portion of the password. + // The length of this buffer must be at least 'outputindex' + 8. + // + //The position within the 'output' buffer to store the hash. + // static void ComputeLmResponseHalf(string! password, int passwordindex, byte[]! output, int outputindex) requires passwordindex >= 0; // requires passwordindex <= password.Length; <-- This is *NOT* a precondition! @@ -227,8 +225,7 @@ namespace System.Security.Protocols.Ntlm requires outputindex + 8 <= output.Length; { byte[]! desKey7 = new byte[7]; - for (int i = 0; i < 7; i++) - { + for (int i = 0; i < 7; i++) { if (i + passwordindex < password.Length) { char c = password[passwordindex + i]; if (c >= 0x80) @@ -253,19 +250,19 @@ namespace System.Security.Protocols.Ntlm output[i + outputindex] = cipher[i]; } - /** - - This method computes the NTLM v1 One-Way Function. - - Contains the 8-byte challenge (nonce) generated by the server. - - Contains the 21-byte intermediate password hash. - This buffer must be exactly 21 bytes long. - - - The computed One-Way Function. This is the value that is sent to the NTLM authenticator. - - */ + /// + // + // This method computes the NTLM v1 One-Way Function. + // + //Contains the 8-byte challenge (nonce) generated by the server. + // + // Contains the 21-byte intermediate password hash. + // This buffer must be exactly 21 bytes long. + // + // + // The computed One-Way Function. This is the value that is sent to the NTLM authenticator. + // + // public static byte[]! ComputeOwf(byte[]! challenge, byte[]! hash) requires challenge.Length >= 8; requires hash.Length == 21; @@ -280,8 +277,7 @@ namespace System.Security.Protocols.Ntlm #endif byte[] response = new byte[24]; - for (int r = 0; r < 3; r++) - { + for (int r = 0; r < 3; r++) { #if NOISY DebugLine(" r = " + r); #endif @@ -318,29 +314,29 @@ namespace System.Security.Protocols.Ntlm return response; } - /** - - - This method computes the NTLM v1 "LAN Manager" authentication response. - This response is known to be very weak, cryptographically. - - - The 'challenge' parameter contains the 8-byte challenge nonce, which was - generated by the NTLMSSP running in the context of the server application. - The 'password' parameter contains the clear-text user password. - - - This method does not have any side-effects. - - - The 8-byte challenge (nonce) generated by the server. - The cleartext password of the authenticating user. - - The return value is a newly-allocated buffer containing the encoded NTLM - response hash, which proves that the client is in possession of the password - for an identified account. This buffer is always 24 bytes in length. - - */ + /// + // + // + // This method computes the NTLM v1 "LAN Manager" authentication response. + // This response is known to be very weak, cryptographically. + // + // + // The 'challenge' parameter contains the 8-byte challenge nonce, which was + // generated by the NTLMSSP running in the context of the server application. + // The 'password' parameter contains the clear-text user password. + // + // + // This method does not have any side-effects. + // + // + //The 8-byte challenge (nonce) generated by the server. + //The cleartext password of the authenticating user. + // + // The return value is a newly-allocated buffer containing the encoded NTLM + // response hash, which proves that the client is in possession of the password + // for an identified account. This buffer is always 24 bytes in length. + // + // public static byte[]! ComputeLmResponse(byte[]! challenge, string! password) requires challenge.Length == 8; ensures result.Length == 24; @@ -358,29 +354,29 @@ namespace System.Security.Protocols.Ntlm return response; } - /** - - - This method computes the NTLM v1 "NT" authentication response. - This response is known to be weak, cryptographically, but is better than - the NTLM v1 "LAN Manager" response. - - - This method does not have any side-effects. - - - - - Contains the 8-byte challenge nonce, which was generated by the NTLMSSP running - in the context of the server application. - - Contains the clear-text user password. - - A buffer containing the encoded NTLM response hash, which proves that the client - is in possession of the password for an identified account. This buffer is always - 24 bytes in length. - - */ + /// + // + // + // This method computes the NTLM v1 "NT" authentication response. + // This response is known to be weak, cryptographically, but is better than + // the NTLM v1 "LAN Manager" response. + // + // + // This method does not have any side-effects. + // + // +// + // + // Contains the 8-byte challenge nonce, which was generated by the NTLMSSP running + // in the context of the server application. + // + //Contains the clear-text user password. + // + // A buffer containing the encoded NTLM response hash, which proves that the client + // is in possession of the password for an identified account. This buffer is always + // 24 bytes in length. + // + // public static byte[]! ComputeNtResponse(byte[]! challenge, string! password) requires challenge.Length == 8; ensures result.Length == 24; @@ -429,24 +425,24 @@ namespace System.Security.Protocols.Ntlm public const int ChallengeLength = 8; public const int ResponseLength = 24; - /** - - This method generates an NTLMSSP "Response" message, given valid client credentials and - an NTLMSSP "Challenge" message. - - - - Contains the encoded NTLMSSP "Response" message, which was generated by the remote - NTLM-enabled application. - - The domain name of the authenticating user. - The username of the authenticating user. - The cleartext password of the authenticating user. - - A buffer containing the encoded NTLMSSP "Response" message. This buffer should be - sent to the remote application, using whatever transport is appropriate. - - */ + /// + // + // This method generates an NTLMSSP "Response" message, given valid client credentials and + // an NTLMSSP "Challenge" message. + // + // + // + // Contains the encoded NTLMSSP "Response" message, which was generated by the remote + // NTLM-enabled application. + // + //The domain name of the authenticating user. + //The username of the authenticating user. + //The cleartext password of the authenticating user. + // + // A buffer containing the encoded NTLMSSP "Response" message. This buffer should be + // sent to the remote application, using whatever transport is appropriate. + // + // public static byte[]! GetResponse( byte[]! challenge_buffer, string! domain, @@ -519,8 +515,7 @@ namespace System.Security.Protocols.Ntlm // Scan through the variable-length strings again and store the string headers. // Also copy the string body into place. int write_pos = sizeof(NtlmResponseMessage); - for (int i = 0; i < strings.Length; i++) - { + for (int i = 0; i < strings.Length; i++) { byte[]! stringbytes = strings[i]; ref BufferRegion region = ref responseBuffer[sizeof(NtlmMessageHeader) + i * sizeof(BufferRegion)]; region = new BufferRegion((ushort)stringbytes.Length, (ushort)stringbytes.Length, (ushort)write_pos); diff --git a/base/Libraries/Ntlm/NtlmUtil.sg b/base/Libraries/Ntlm/NtlmUtil.sg index 35d1605..b47a0ce 100644 --- a/base/Libraries/Ntlm/NtlmUtil.sg +++ b/base/Libraries/Ntlm/NtlmUtil.sg @@ -84,16 +84,13 @@ namespace System.Security.Protocols.Ntlm DebugLine("NTLM message:"); Util.DumpBuffer(message, 0, length); - if (length < NtlmConstants.HeaderLength) - { + if (length < NtlmConstants.HeaderLength) { DebugLine(" Message is invalid; too short"); return; } - for (int i = 0; i < NtlmConstants.MessageSignature.Length; i++) - { - if (message[i] != NtlmConstants.MessageSignature[i]) - { + for (int i = 0; i < NtlmConstants.MessageSignature.Length; i++) { + if (message[i] != NtlmConstants.MessageSignature[i]) { DebugLine(" Message is invalid; signature does not match"); return; } @@ -101,8 +98,7 @@ namespace System.Security.Protocols.Ntlm NtlmMessageType type = (NtlmMessageType)message[8]; - switch (type) - { + switch (type) { case NtlmMessageType.Negotiate: { DebugLine(" Type: Negotiate"); diff --git a/base/Libraries/Policy/Address.cs b/base/Libraries/Policy/Address.cs index f630c57..6a1fe33 100644 --- a/base/Libraries/Policy/Address.cs +++ b/base/Libraries/Policy/Address.cs @@ -1,14 +1,13 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; -namespace Microsoft.Singularity.Policy.Engine { +namespace Microsoft.Singularity.Policy.Engine +{ // An Address is an index into an Area. internal class Address { readonly uint _at; @@ -52,17 +51,25 @@ namespace Microsoft.Singularity.Policy.Engine { return new Address(a._in, (uint)(a._at - i)); } public static bool operator ==(Address a0, Address a1) { - if ((object)a0 == null) { return (object)a1 == null; } - if ((object)a1 == null) { return false; } + if ((object)a0 == null) { + return (object)a1 == null; + } + if ((object)a1 == null) { + return false; + } return a0._at == a1._at && a0._in == a1._in; } public static bool operator !=(Address a0, Address a1) { return !(a0 == a1); } public override bool Equals(object obj) { - if (obj == null) { return false; } + if (obj == null) { + return false; + } Address that = obj as Address; - if (that == null) { return false; } + if (that == null) { + return false; + } return this == that; } public override int GetHashCode() { diff --git a/base/Libraries/Policy/Area.cs b/base/Libraries/Policy/Area.cs index 66d323e..e6f9c56 100644 --- a/base/Libraries/Policy/Area.cs +++ b/base/Libraries/Policy/Area.cs @@ -1,14 +1,13 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System.Collections; -namespace Microsoft.Singularity.Policy.Engine { +namespace Microsoft.Singularity.Policy.Engine +{ // An Area is a contiguous region of objects, indexed by uints. // The contents of an Area are held in a Hashtable. diff --git a/base/Libraries/Policy/Cell.cs b/base/Libraries/Policy/Cell.cs index 10127d2..ece1e1d 100644 --- a/base/Libraries/Policy/Cell.cs +++ b/base/Libraries/Policy/Cell.cs @@ -1,12 +1,11 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- -namespace Microsoft.Singularity.Policy.Engine { +namespace Microsoft.Singularity.Policy.Engine +{ // A Cell is a value held in the heap or the stack. internal abstract class Cell { } } diff --git a/base/Libraries/Policy/ChoicePoint.cs b/base/Libraries/Policy/ChoicePoint.cs index 3aad3db..d5d6b33 100644 --- a/base/Libraries/Policy/ChoicePoint.cs +++ b/base/Libraries/Policy/ChoicePoint.cs @@ -1,13 +1,12 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------  -namespace Microsoft.Singularity.Policy.Engine { +namespace Microsoft.Singularity.Policy.Engine +{ // A ChoicePoint is a single Cell containing a choice point. This replaces // the multiple cells used in the WAM in a more strongly typed way. The // ChoicePoint is followed on the stack by copies of the arguments. diff --git a/base/Libraries/Policy/Environment.cs b/base/Libraries/Policy/Environment.cs index 8b3c2ed..304f8af 100644 --- a/base/Libraries/Policy/Environment.cs +++ b/base/Libraries/Policy/Environment.cs @@ -1,13 +1,12 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------  -namespace Microsoft.Singularity.Policy.Engine { +namespace Microsoft.Singularity.Policy.Engine +{ // An Environment is a single Cell containing an environment. This replaces // the multiple cells used in the WAM in a more strongly typed way. The // Environment is followed on the stack by the permanent arguments. diff --git a/base/Libraries/Policy/Functor.cs b/base/Libraries/Policy/Functor.cs index 5a0a660..da4311a 100644 --- a/base/Libraries/Policy/Functor.cs +++ b/base/Libraries/Policy/Functor.cs @@ -1,12 +1,11 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- -namespace Microsoft.Singularity.Policy.Engine { +namespace Microsoft.Singularity.Policy.Engine +{ // A Functor contains a symbol and an arity. The // functor f/3 is represented by new Functor("f", 3). internal class Functor : Cell { @@ -24,9 +23,13 @@ namespace Microsoft.Singularity.Policy.Engine { return !(f0 == f1); } public override bool Equals(object obj) { - if (obj == null) { return false; } + if (obj == null) { + return false; + } Functor that = obj as Functor; - if ((object)that == null) { return false; } + if ((object)that == null) { + return false; + } return this == that; } public override int GetHashCode() { diff --git a/base/Libraries/Policy/Instruction.cs b/base/Libraries/Policy/Instruction.cs index dc111eb..375f17f 100644 --- a/base/Libraries/Policy/Instruction.cs +++ b/base/Libraries/Policy/Instruction.cs @@ -1,12 +1,11 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- -namespace Microsoft.Singularity.Policy.Engine { +namespace Microsoft.Singularity.Policy.Engine +{ internal delegate void Execute(); // An Instruction represents an abstract WAM instruction, // using a method delegate. All instructions have length 1. diff --git a/base/Libraries/Policy/Ref.cs b/base/Libraries/Policy/Ref.cs index 85ffb15..50cf11f 100644 --- a/base/Libraries/Policy/Ref.cs +++ b/base/Libraries/Policy/Ref.cs @@ -1,12 +1,11 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- -namespace Microsoft.Singularity.Policy.Engine { +namespace Microsoft.Singularity.Policy.Engine +{ // A Ref is a Cell containing an Address. A new // is represented by a new Ref(addr). internal class Ref : Cell { @@ -16,7 +15,8 @@ namespace Microsoft.Singularity.Policy.Engine { public override string ToString() { if (_value._cell == this) { return ""; - } else { + } + else { return ""; } } diff --git a/base/Libraries/Policy/Str.cs b/base/Libraries/Policy/Str.cs index 1a6d04e..1ff4dd5 100644 --- a/base/Libraries/Policy/Str.cs +++ b/base/Libraries/Policy/Str.cs @@ -1,15 +1,15 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- -namespace Microsoft.Singularity.Policy.Engine { +namespace Microsoft.Singularity.Policy.Engine +{ // A Str is a Cell containing the Address of a functor cell. // A new is represented by a new Str(addr). - class Str : Cell { + class Str : Cell + { readonly Address _value; internal Address Value { get { return _value; } } internal Str(Address value) { _value = value; } diff --git a/base/Libraries/Policy/Wam.cs b/base/Libraries/Policy/Wam.cs index 0b329dd..170df63 100644 --- a/base/Libraries/Policy/Wam.cs +++ b/base/Libraries/Policy/Wam.cs @@ -1,15 +1,14 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; using System.Collections; -namespace Microsoft.Singularity.Policy.Engine { +namespace Microsoft.Singularity.Policy.Engine +{ // The Wam class implements a restricted version of the Warren // Abstract Machine, as described in "Warren's Abstract // Machine: A Tutorial Reconstruction" by Hassan Ait-Kaci @@ -108,7 +107,9 @@ namespace Microsoft.Singularity.Policy.Engine { } internal void GetValue(Address yn, Address ai) { Unify(yn, ai); - if (_fail) { Backtrack(); } + if (_fail) { + Backtrack(); + } } internal void GetStructure(Functor fn, Address ai) { Address addr = Deref(ai); @@ -120,18 +121,23 @@ namespace Microsoft.Singularity.Policy.Engine { // Is this right? It conflicts with the errata. _s = null; _mode = Mode.Write; - } else if (addr._cell is Str) { + } + else if (addr._cell is Str) { Address a = ((Str)addr._cell).Value; if (a._cell.Equals(fn)) { _s = a + 1; _mode = Mode.Read; - } else { + } + else { _fail = true; } - } else { + } + else { _fail = true; } - if (_fail) { Backtrack(); } + if (_fail) { + Backtrack(); + } } // Set instructions. There is no TCO, so set_local_value @@ -175,7 +181,9 @@ namespace Microsoft.Singularity.Policy.Engine { throw new Exception(); } _s = _s + 1; - if (_fail) { Backtrack(); } + if (_fail) { + Backtrack(); + } } // Control instructions. There is no environment trimming, @@ -185,7 +193,8 @@ namespace Microsoft.Singularity.Policy.Engine { Address e; if (Greater(_e, _b)) { e = _e + 1 + ((Environment)_e._cell)._n; - } else { + } + else { e = _b + 1 + ((ChoicePoint)_b._cell)._n; } e._cell = new Environment(n, _e, _cp); @@ -199,7 +208,8 @@ namespace Microsoft.Singularity.Policy.Engine { if (_label.ContainsKey(P)) { _cp = _p; _p = (Address)_label[P]; - } else { + } + else { Backtrack(); } } @@ -211,19 +221,24 @@ namespace Microsoft.Singularity.Policy.Engine { Address b; if (Greater(_e, _b)) { b = _e + 1 + ((Environment)_e._cell)._n; - } else { + } + else { b = _b + 1 + ((ChoicePoint)_b._cell)._n; } ChoicePoint b_ = new ChoicePoint(n, _e, _cp, _b, L, _tr, _h, _hb); b._cell = b_; - for (uint i = 1; i <= n; i++) { b[i] = _a[i]; } + for (uint i = 1; i <= n; i++) { + b[i] = _a[i]; + } _b = b; _hb = _h; } internal void RetryMeElse(Address L) { ChoicePoint b_ = (ChoicePoint)_b._cell; uint n = b_._n; - for (uint i = 1; i <= n; i++) { _a[i] = _b[i]; } + for (uint i = 1; i <= n; i++) { + _a[i] = _b[i]; + } _e = b_._ce; _cp = b_._cp; b_._bp = L; @@ -235,7 +250,9 @@ namespace Microsoft.Singularity.Policy.Engine { internal void TrustMe() { ChoicePoint b_ = (ChoicePoint)_b._cell; uint n = b_._n; - for (uint i = 1; i <= n; i++) { _a[i] = _b[i]; } + for (uint i = 1; i <= n; i++) { + _a[i] = _b[i]; + } _e = b_._ce; _cp = b_._cp; UnwindTrail(b_._tr._address, _tr._address); @@ -249,7 +266,9 @@ namespace Microsoft.Singularity.Policy.Engine { void NeckCut() { if (Greater(_b, _b0)) { _b = _b0; TidyTrail(); } } void GetLevel(Address yn) { yn._address = _b0; } void Cut(Address yn) { - if (Greater(_b, yn._address)) { _b0 = yn._address; TidyTrail(); } + if (Greater(_b, yn._address)) { + _b0 = yn._address; TidyTrail(); + } } // Ancillary functions. @@ -257,14 +276,17 @@ namespace Microsoft.Singularity.Policy.Engine { _fail = false; if (_b == null) { throw new Exception("fail and exit program"); - } else { + } + else { _p = ((ChoicePoint)_b._cell)._bp; } } Address Deref(Address a) { if (a._cell is Ref) { Address value = (a._cell as Ref).Value; - if (value != a) { return Deref(value); } + if (value != a) { + return Deref(value); + } } return a; } @@ -273,7 +295,8 @@ namespace Microsoft.Singularity.Policy.Engine { if (a1._cell is Ref && (!(a2._cell is Ref) || Less(a2, a1))) { a1._cell = a2._cell; Trail(a1); - } else { + } + else { a2._cell = a1._cell; Trail(a2); } @@ -286,7 +309,9 @@ namespace Microsoft.Singularity.Policy.Engine { } void UnwindTrail(Address a1, Address a2) { Address a = a1; - while (Less(a, a2)) { a._cell = new Ref(a); a = a + 1; } + while (Less(a, a2)) { + a._cell = new Ref(a); a = a + 1; + } } void TidyTrail() { Address i = ((ChoicePoint)_b._cell)._tr; @@ -296,7 +321,8 @@ namespace Microsoft.Singularity.Policy.Engine { || Less(_h, i._address) && Less(i._address, _b) ) { i = i + 1; - } else { + } + else { _tr = _tr - 1; i._cell = _tr._cell; } @@ -315,27 +341,32 @@ namespace Microsoft.Singularity.Policy.Engine { Cell cell2 = d2._cell; if (cell1 is Ref) { Bind(d1, d2); - } else { + } + else { if (cell2 is Ref) { Bind(d1, d2); - } else if (cell2 is Str) { + } + else if (cell2 is Str) { if (!(cell1 is Str)) { _fail = true; - } else { + } + else { Address v1 = ((Str)cell1).Value; Address v2 = ((Str)cell2).Value; Functor f1 = (Functor)v1._cell; Functor f2 = (Functor)v2._cell; if (f1 != f2) { _fail = true; - } else { + } + else { for (int i = 1; i <= f1.Arity; i++) { pdl.Push(v1 + i); pdl.Push(v2 + i); } } } - } else { + } + else { _fail = true; } } diff --git a/base/Libraries/ProtoLisp/Engine.cs b/base/Libraries/ProtoLisp/Engine.cs index f3e7cdc..d291eb1 100644 --- a/base/Libraries/ProtoLisp/Engine.cs +++ b/base/Libraries/ProtoLisp/Engine.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; using System.Text; @@ -35,35 +33,28 @@ namespace ProtoLisp Lexer lexer = new Lexer(stream); PLObject lastVal = null; - try - { + try { PLObject obj; - while ((obj = lexer.GetExpression()) != null) - { - if (debugTrace) - { + while ((obj = lexer.GetExpression()) != null) { + if (debugTrace) { lastVal = interpreter.Eval(obj, null, outputStream); } - else - { + else { lastVal = interpreter.Eval(obj, null, null); } - if (printEveryExpression) - { + if (printEveryExpression) { writer.WriteLine(Interpreter.ExpressionToString(lastVal)); writer.Flush(); } } } - catch (Exception e) - { + catch (Exception e) { writer.WriteLine("Exception caught: " + e); } - if (!printEveryExpression) - { + if (!printEveryExpression) { writer.WriteLine(Interpreter.ExpressionToString(lastVal)); } diff --git a/base/Libraries/ProtoLisp/Interpreter.cs b/base/Libraries/ProtoLisp/Interpreter.cs index 3284f45..ad04769 100644 --- a/base/Libraries/ProtoLisp/Interpreter.cs +++ b/base/Libraries/ProtoLisp/Interpreter.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; using System.Collections; @@ -54,12 +52,10 @@ namespace ProtoLisp private PLObject BoolToExpr(bool b) { // True is "t", false is the empty list - if (b) - { + if (b) { return new PLStringAtom("t"); } - else - { + else { return new PLList(); } } @@ -87,12 +83,10 @@ namespace ProtoLisp { // An atom is an object that is an instance of PLAtom, or // the empty list (considered "false") - if (expr is PLAtom) - { + if (expr is PLAtom) { return BoolToExpr(true); } - else if ((expr is PLList) && (((PLList)expr).Count == 0)) - { + else if ((expr is PLList) && (((PLList)expr).Count == 0)) { return BoolToExpr(true); } @@ -102,13 +96,11 @@ namespace ProtoLisp // eq: indicates whether its (two) arguments are equal. private PLObject eq_fun(PLObject a, PLObject b) { - if ((a is PLAtom) && (b is PLAtom)) - { + if ((a is PLAtom) && (b is PLAtom)) { // Two atoms compare by value return BoolToExpr(a.Equals(b)); } - else - { + else { // Two empty lists are equal if ((a is PLList) && (((PLList)a).Count == 0) && (b is PLList) && (((PLList)b).Count == 0)) @@ -131,8 +123,7 @@ namespace ProtoLisp { PLList retval = new PLList(); - for (int i = 1; i < list.Count; ++i) - { + for (int i = 1; i < list.Count; ++i) { retval.Add(list[i]); } @@ -145,8 +136,7 @@ namespace ProtoLisp PLList retval = new PLList(); retval.Add(a); - for (int i = 0; i < b.Count; ++i) - { + for (int i = 0; i < b.Count; ++i) { retval.Add(b[i]); } @@ -157,24 +147,20 @@ namespace ProtoLisp // item in the first pair whose first element evaluates to true. private PLObject cond_fun(PLList condPLList, PLEnvironment localEnv, Stream traceStream) { - for (int i = 0; i < condPLList.Count; ++i) - { - if (!(condPLList[i] is PLList)) - { + for (int i = 0; i < condPLList.Count; ++i) { + if (!(condPLList[i] is PLList)) { throw new Exception("An argument passed to the 'cond' primitive was not a list"); } PLList condition = (PLList)condPLList[i]; - if (condition.Count != 2) - { + if (condition.Count != 2) { throw new Exception("An argument passed to the 'cond' primitive was not a 2-item list"); } PLObject carVal = Eval(car_fun(condition), localEnv, traceStream); - if (ExprToBool(carVal)) - { + if (ExprToBool(carVal)) { // This subexpression evaluated to true. Evaluate // the second part of the condition. return Eval(condition[1], localEnv, traceStream); @@ -227,14 +213,12 @@ namespace ProtoLisp // Execute a given closure, with a given set of argument values private PLObject Execute(PLClosure fun, PLList argVals, Stream traceStream) { - if (traceStream != null) - { + if (traceStream != null) { StreamWriteLine(traceStream, "** Executing a closure..."); } // Check the argument list - if (fun.argNames.Count != argVals.Count) - { + if (fun.argNames.Count != argVals.Count) { throw new Exception ("Function invoked with wrong number of arguments"); } @@ -243,17 +227,14 @@ namespace ProtoLisp // symbolic names, then evaluate the function body. PLEnvironment funEnv; - if (fun.env != null) - { + if (fun.env != null) { funEnv = (PLEnvironment)fun.env.Clone(); } - else - { + else { funEnv = new PLEnvironment(); } - for (int i = 0; i < fun.argNames.Count; ++i) - { + for (int i = 0; i < fun.argNames.Count; ++i) { funEnv.Put(((PLStringAtom)fun.argNames[i]).ToString(), argVals[i]); } @@ -266,79 +247,62 @@ namespace ProtoLisp // expression in its own right. private PLObject Apply(PLObject func, PLList args, PLEnvironment localEnv, Stream traceStream) { - if (traceStream != null) - { + if (traceStream != null) { StreamWriteLine(traceStream, "** Evaluating the expression \"" + ExpressionToString(func) + "\" as a function"); } - if (func is PLStringAtom) - { + if (func is PLStringAtom) { // Function is named by a symbol - if (func.Equals("atom")) - { - if (args.Count != 1) - { + if (func.Equals("atom")) { + if (args.Count != 1) { throw new Exception("Incorrect number of arguments passed to the 'atom' primitive"); } return atom_fun(args[0]); } - else if (func.Equals("eq")) - { - if (args.Count != 2) - { + else if (func.Equals("eq")) { + if (args.Count != 2) { throw new Exception("Incorrect number of arguments passed to the 'eq' primitive"); } return eq_fun(args[0], args[1]); } - else if (func.Equals("car")) - { - if (args.Count != 1) - { + else if (func.Equals("car")) { + if (args.Count != 1) { throw new Exception("Incorrect number of arguments passed to the 'car' primitive"); } - if (!(args[0] is PLList)) - { + if (!(args[0] is PLList)) { throw new Exception("The argument passed to the 'car' primitive was not a list"); } return car_fun((PLList)args[0]); } - else if (func.Equals("cdr")) - { - if (args.Count != 1) - { + else if (func.Equals("cdr")) { + if (args.Count != 1) { throw new Exception("Incorrect number of arguments passed to the 'cdr' primitive"); } - if (!(args[0] is PLList)) - { + if (!(args[0] is PLList)) { throw new Exception("The argument passed to the 'cdr' primitive was not a list"); } return cdr_fun((PLList)args[0]); } - else if (func.Equals("cons")) - { - if (args.Count != 2) - { + else if (func.Equals("cons")) { + if (args.Count != 2) { throw new Exception("Incorrect number of arguments passed to the 'cons' primitive"); } - if (!(args[1] is PLList)) - { + if (!(args[1] is PLList)) { throw new Exception("The second argument passed to the 'cons' primitive was not a list"); } return cons_fun(args[0], (PLList)args[1]); } - else if (func.Equals("+")) - { - if (args.Count != 2) - { + else if (func.Equals("+")) { + if (args.Count != 2) { throw new Exception("Incorrect number of arguments pass to the '+' primitive"); } @@ -350,10 +314,8 @@ namespace ProtoLisp return plus_fun((PLNumberAtom)args[0], (PLNumberAtom)args[1]); } - else if (func.Equals("-")) - { - if (args.Count != 2) - { + else if (func.Equals("-")) { + if (args.Count != 2) { throw new Exception("Incorrect number of arguments pass to the '-' primitive"); } @@ -365,10 +327,8 @@ namespace ProtoLisp return minus_fun((PLNumberAtom)args[0], (PLNumberAtom)args[1]); } - else if (func.Equals("*")) - { - if (args.Count != 2) - { + else if (func.Equals("*")) { + if (args.Count != 2) { throw new Exception("Incorrect number of arguments pass to the '*' primitive"); } @@ -380,10 +340,8 @@ namespace ProtoLisp return mult_fun((PLNumberAtom)args[0], (PLNumberAtom)args[1]); } - else if (func.Equals("/")) - { - if (args.Count != 2) - { + else if (func.Equals("/")) { + if (args.Count != 2) { throw new Exception("Incorrect number of arguments pass to the '/' primitive"); } @@ -400,22 +358,19 @@ namespace ProtoLisp // Look up the function in our environment PLObject funObj = Lookup(func.ToString(), localEnv); - if (!(funObj is PLClosure)) - { + if (!(funObj is PLClosure)) { throw new Exception ("The symbolic name \"" + func + "\" is not bound to a function"); } // Run the function! return Execute((PLClosure)funObj, args, traceStream); } - else - { + else { // The function is an expression, not just a name. This expression // had better evaluate to a closure. PLObject funObj = Eval(func, localEnv, traceStream); - if (! (funObj is PLClosure)) - { + if (!(funObj is PLClosure)) { throw new Exception ("Expression \"" + ExpressionToString(func) + "\" does not evaluate to a function"); } @@ -430,8 +385,7 @@ namespace ProtoLisp { PLList retval = new PLList(); - for (int i = 0; i < list.Count; ++i) - { + for (int i = 0; i < list.Count; ++i) { retval.Add(Eval(list[i], localEnv, traceStream)); } @@ -444,12 +398,10 @@ namespace ProtoLisp { PLObject retval; - if (localEnv != null) - { + if (localEnv != null) { retval = localEnv.Lookup(name); - if (retval != null) - { + if (retval != null) { return retval; } } @@ -459,38 +411,30 @@ namespace ProtoLisp public PLObject Eval(PLObject expr, PLEnvironment localEnv, Stream traceStream) { - if (traceStream != null) - { + if (traceStream != null) { StreamWriteLine(traceStream, "** Evaluating: " + ExpressionToString(expr)); } - if (expr is PLAtom) - { + if (expr is PLAtom) { PLAtom atomExpr = (PLAtom)expr; - if (atomExpr is PLNumberAtom) - { + if (atomExpr is PLNumberAtom) { // Numbers eval to themselves return atomExpr; } - else - { - if (atomExpr.Equals("t")) - { + else { + if (atomExpr.Equals("t")) { // "t" (True) evals to itself return atomExpr; } - else if (atomExpr.Equals("nil")) - { + else if (atomExpr.Equals("nil")) { // Return whatever our native representation for false is return BoolToExpr(false); } - else - { + else { PLObject retval = Lookup(atomExpr.ToString(), localEnv); - if (retval == null) - { + if (retval == null) { throw new Exception("The symbolic name \"" + atomExpr + "\" is not bound."); } @@ -498,84 +442,67 @@ namespace ProtoLisp } } } - else if (expr is PLList) - { + else if (expr is PLList) { PLList listExpr = ((PLList)expr); // The empty list evaluates to itself - if(listExpr.Count == 0) - { + if (listExpr.Count == 0) { return listExpr; } PLObject carVal = car_fun(listExpr); // Check for special-case primitives - if (carVal is PLStringAtom) - { - if (carVal.Equals("cond")) - { - if (listExpr.Count < 2) - { + if (carVal is PLStringAtom) { + if (carVal.Equals("cond")) { + if (listExpr.Count < 2) { throw new Exception("Must pass at least one argument to the 'cond' primitive"); } return cond_fun(cdr_fun(listExpr), localEnv, traceStream); } - else if (carVal.Equals("quote")) - { - if (listExpr.Count != 2) - { + else if (carVal.Equals("quote")) { + if (listExpr.Count != 2) { throw new Exception("Incorrect number of arguments passed to the 'quote' primitive"); } // Return the second portion of the list return listExpr[1]; } - else if (carVal.Equals("define")) - { - if (listExpr.Count != 3) - { + else if (carVal.Equals("define")) { + if (listExpr.Count != 3) { throw new Exception ("Incorrect number of arguments passed to the 'define' primitive"); } - if (! (listExpr[1] is PLStringAtom)) - { + if (!(listExpr[1] is PLStringAtom)) { throw new Exception ("The first argument passed to the 'define' primitive was not a string"); } return define_fun(((PLStringAtom)listExpr[1]).ToString(), Eval(listExpr[2], localEnv, traceStream)); } - else if (carVal.Equals("lambda")) - { - if (listExpr.Count != 3) - { + else if (carVal.Equals("lambda")) { + if (listExpr.Count != 3) { throw new Exception ("Incorrect number of arguments passed to the 'lambda' primitive"); } - if (! (listExpr[1] is PLList)) - { + if (!(listExpr[1] is PLList)) { throw new Exception ("The first argument to the 'lambda' primitive was not a list"); } return lambda_fun((PLList)listExpr[1], listExpr[2], localEnv); } - else if (carVal.Equals("defun")) - { + else if (carVal.Equals("defun")) { // Syntactic sugar for (define (lambda ... - if (listExpr.Count != 4) - { + if (listExpr.Count != 4) { throw new Exception ("Incorrect number of arguments passed to the 'defun' primitive"); } - if (! (listExpr[1] is PLStringAtom)) - { + if (!(listExpr[1] is PLStringAtom)) { throw new Exception ("The first argument passed to the 'defun' primitive was not a simple name"); } - if (! (listExpr[2] is PLList)) - { + if (!(listExpr[2] is PLList)) { throw new Exception ("The second argument passed to the 'defun' argument was not a list"); } @@ -589,8 +516,7 @@ namespace ProtoLisp PLList args = EvalPLList(cdr_fun(listExpr), localEnv, traceStream); return Apply(listExpr[0], args, localEnv, traceStream); } - else - { + else { throw new Exception("Unrecognized type passed to Eval()"); } } @@ -598,27 +524,22 @@ namespace ProtoLisp // Turn a ProtoLisp expression into a printable string public static string ExpressionToString(PLObject obj) { - if (obj is PLStringAtom) - { + if (obj is PLStringAtom) { return ((PLStringAtom)obj).ToString(); } - else if (obj is PLNumberAtom) - { + else if (obj is PLNumberAtom) { return ((PLNumberAtom)obj).ToString(); } - else if (obj is PLList) - { + else if (obj is PLList) { PLList list = (PLList)obj; string retval = "("; - for(int i = 0; i < list.Count; ++i) - { + for (int i = 0; i < list.Count; ++i) { retval += ExpressionToString(list[i]); - if (i < list.Count -1) - { + if (i < list.Count - 1) { retval += " "; } } @@ -626,12 +547,10 @@ namespace ProtoLisp retval += ")"; return retval; } - else if (obj is PLClosure) - { + else if (obj is PLClosure) { return "<>"; } - else - { + else { return "???"; } } diff --git a/base/Libraries/ProtoLisp/Lexer.cs b/base/Libraries/ProtoLisp/Lexer.cs index d3de420..3d10fc2 100644 --- a/base/Libraries/ProtoLisp/Lexer.cs +++ b/base/Libraries/ProtoLisp/Lexer.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; using System.Text; @@ -38,23 +36,19 @@ namespace ProtoLisp // reached. private bool PeekNextChar(out char val) { - if (haveLookAhead) - { + if (haveLookAhead) { val = lookAheadVal; return true; } - else - { + else { int nextVal = inputStream.ReadByte(); - if (nextVal == -1) - { + if (nextVal == -1) { // Nothing more to read val = '\0'; return false; } - else - { + else { haveLookAhead = true; // Hacky cast: just assume everything is 1-byte ASCII lookAheadVal = (char)nextVal; @@ -70,8 +64,7 @@ namespace ProtoLisp { char dummy; - if (!PeekNextChar(out dummy)) - { + if (!PeekNextChar(out dummy)) { val = '\0'; return false; } @@ -96,24 +89,20 @@ namespace ProtoLisp char peekChar; // Skip whitespace - if (!PeekNextChar(out peekChar)) - { + if (!PeekNextChar(out peekChar)) { return null; } - while (Char.IsWhiteSpace(peekChar)) - { + while (Char.IsWhiteSpace(peekChar)) { // Throw away the whitespace char DiscardNextChar(); - if (!PeekNextChar(out peekChar)) - { + if (!PeekNextChar(out peekChar)) { return null; } } // Special one-character tokens - if ((peekChar == '(') || (peekChar == ')') || (peekChar == '\'')) - { + if ((peekChar == '(') || (peekChar == ')') || (peekChar == '\'')) { // Consume and return this character DiscardNextChar(); return new String(peekChar, 1); @@ -126,8 +115,7 @@ namespace ProtoLisp token += peekChar; DiscardNextChar(); - if (!PeekNextChar(out peekChar)) - { + if (!PeekNextChar(out peekChar)) { // Ran into the end of the stream return token; } @@ -140,13 +128,11 @@ namespace ProtoLisp { string token = GetToken(); - if (token == null) - { + if (token == null) { return null; } - if (token.Equals("'")) - { + if (token.Equals("'")) { // The "'" character means "quote", and preserve // the next expression. PLList retval = new PLList(); @@ -154,8 +140,7 @@ namespace ProtoLisp PLObject nextExpr = GetExpression(); - if (nextExpr == null) - { + if (nextExpr == null) { throw new Exception("'quote' was not followed by a complete expression"); } @@ -163,23 +148,19 @@ namespace ProtoLisp return retval; } - if (!token.Equals("(")) - { + if (!token.Equals("(")) { // Not the beginning of a list; the expression is // simply this token. See if it's a number or a // plain string. - try - { + try { return new PLNumberAtom(token); } - catch (Exception) - { + catch (Exception) { // Just treat it as a regular string return new PLStringAtom(token); } } - else - { + else { // We have the beginning of a list, which can contain // any number of expressions. Keep building our list // until we encounter the closing paren. @@ -187,14 +168,12 @@ namespace ProtoLisp PLObject nextExpr = GetExpression(); - while (! nextExpr.Equals(")")) - { + while (! nextExpr.Equals(")")) { retval.Add(nextExpr); nextExpr = GetExpression(); - if (nextExpr == null) - { + if (nextExpr == null) { throw new Exception("Incomplete expression (unbalanced parens?"); } } diff --git a/base/Libraries/ProtoLisp/Types.cs b/base/Libraries/ProtoLisp/Types.cs index e1347c0..865338f 100644 --- a/base/Libraries/ProtoLisp/Types.cs +++ b/base/Libraries/ProtoLisp/Types.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; using System.Collections; @@ -42,12 +40,10 @@ namespace ProtoLisp public override bool Equals(Object other) { - if (other is PLStringAtom) - { + if (other is PLStringAtom) { return myval.Equals(((PLStringAtom)other).myval); } - else - { + else { return myval.Equals(other); } } @@ -81,12 +77,10 @@ namespace ProtoLisp public override bool Equals(Object other) { - if (other is PLNumberAtom) - { + if (other is PLNumberAtom) { return myval.Equals(((PLNumberAtom)other).myval); } - else - { + else { return myval.Equals(other); } } @@ -197,10 +191,8 @@ namespace ProtoLisp public PLClosure(PLList funArgs, PLObject funBody, PLEnvironment environment) { // Check argument names - for (int i = 0; i < funArgs.Count; ++i) - { - if (! (funArgs[i] is PLStringAtom)) - { + for (int i = 0; i < funArgs.Count; ++i) { + if (!(funArgs[i] is PLStringAtom)) { throw new Exception("Attempt to define a procedure with arguments that are not string atoms"); } } @@ -209,8 +201,7 @@ namespace ProtoLisp body = funBody; // Snapshot the environment - if (environment != null) - { + if (environment != null) { env = (PLEnvironment)environment.Clone(); } } diff --git a/base/Libraries/Resiliency/DirectoryServiceProxy.sg b/base/Libraries/Resiliency/DirectoryServiceProxy.sg deleted file mode 100644 index b453094..0000000 --- a/base/Libraries/Resiliency/DirectoryServiceProxy.sg +++ /dev/null @@ -1,572 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Libraries\Resiliency\DirectoryServiceProxy.sg -// -// Note: Intermediary of DirectoryServiceContract -// -using System; -using System.Collections; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Services; - -namespace Microsoft.Singularity.Resiliency -{ - internal sealed class DirectoryServiceProxy : IRunnable, IRecoveryAware - { - private bool recovery; - private Object! recoveryLock; - private ServiceProxy! manager; - private IList! providerList; - private JournaletFactory! factory; - private PersistentMemory! objectStore; - private TRef rootDsRef; - private TRef myDsRef; - private TRef proxyRef; - private TRef signalRef; - private TRef senderRef; - private TRef receiverRef; - - /// dep Channel to the (root) directory service. This channel is - /// necessary to intercept the connection request from a client - /// to the server. - /// fep Channel from the target server. This channel acts as the - /// directory service for the server. - internal DirectoryServiceProxy(ServiceProxy! proxy, - JournaletFactory! factory, - [Claims]DirectoryServiceContract.Imp:Ready! dep, - [Claims]DirectoryServiceContract.Exp:Ready! fep, - [Claims]ServiceProxyContract.Exp:Ready! xep) - { - SignalContract.Imp! imp; - SignalContract.Exp! exp; - - this.manager = proxy; - this.factory = factory; - this.providerList = new ArrayList(); - this.objectStore = new PersistentMemory(); - this.recoveryLock = new Object(); - - this.myDsRef = new TRef(fep); - this.proxyRef = new TRef(xep); - this.rootDsRef = new TRef(dep); - - SignalContract.NewChannel(out imp, out exp); - senderRef = new TRef(imp); - receiverRef = new TRef(exp); - } - - public void Signal([Claims]ThreadTerminationContract.Exp:Start! ep) - { - signalRef = new TRef(ep); - } - - // Proxy recovers from server failure by itself. - public void Run() - { - DirectoryServiceContract.Exp:Ready newEP; - ServiceProxyContract.Exp:Ready newPx; - SignalContract.Imp:Start! signal; - - for (;;) { - DebugStub.Print("DPx: HandleProxyService\n"); - if (!HandleProxyService()) { - break; - } - - manager.RecoverService(out newEP, out newPx); - - if (newEP == null || newPx == null) { - delete newEP; - delete newPx; - break; - } - DebugStub.Print("DPx: Replace DS\n"); - // replace with the new endpoint - delete myDsRef.Acquire(); - myDsRef.Release(newEP); - DebugStub.Print("DPx: Replace proxy\n"); - delete proxyRef.Acquire(); - proxyRef.Release(newPx); - } - - delete signalRef.Acquire(); - } - - /// - /// Handles DirectoryService and ServiceProvider - /// - private bool HandleProxyService() - { - DirectoryServiceContract.Exp:Ready! myDS; - ServiceProxyContract.Exp:Ready! proxy; - DirectoryServiceContract.Imp:Ready! rootDS; - ThreadTerminationContract.Exp:Start! signalFromManager; - SignalContract.Exp:Start! signalInternal; - int currentGeneration = -1; - int previousGeneration = 0; - Random random = new Random(); - - if (signalRef == null) { - DebugStub.Print("DPx: signal is not set!\n"); - return recovery; - } - - myDS = myDsRef.Acquire(); - rootDS = rootDsRef.Acquire(); - proxy = proxyRef.Acquire(); - signalFromManager = signalRef.Acquire(); - signalInternal = receiverRef.Acquire(); - recovery = false; - - for (;;) { - switch receive { - // BEGIN SWR LEVEL0 - // From the service process - case myDS.Register(path, imp): - { - string! pathName; - ServiceProviderProxy provider; - ServiceProviderContract.Imp! client; - ServiceProviderContract.Exp! server; - ServiceProviderContract.Imp:Start! oldEp; - - pathName = Bitter.ToString2(path); - DebugStub.Print("DPx: Register '{0}'\n", - __arglist(pathName)); - - provider = LookupProviderProxy(pathName); - if (provider != null) { // We already have the one. - provider.Stop(out oldEp); - delete oldEp; - provider.Start(imp); - myDS.SendAckRegister(); - delete path; - break; - } - - // Register the JP's endpoint to the NS. - ServiceProviderContract.NewChannel(out client, - out server); - rootDS.SendRegister(path, client); - - // BEGIN SWR LEVEL1 - switch receive { - // From the root DS - case rootDS.AckRegister(): - { - myDS.SendAckRegister(); - CreateProviderProxy(pathName, imp, server); - break; - } - // From the root DS - case rootDS.NakRegister(rejected, error): - { - myDS.SendNakRegister(imp, error); - - delete rejected; - delete server; - break; - } - // From the root DS - case rootDS.NakRegisterReparse(npath, rest, - link, rejected): - { - DebugStub.Print("DPx: NakRegisterReparse\n"); - myDS.SendNakRegisterReparse(npath, rest, - link, imp); - delete rejected; - delete server; - break; - } - // internal communication - case signalFromManager.Stop(): - { - signalFromManager.SendAckStop(); - delete server; - delete imp; - goto exit; - break; - } - // internal communication - case signalFromManager.ChannelClosed(): - { - delete server; - delete imp; - goto exit; - break; - } - case unsatisfiable: - { - // protocol mismatch - delete server; - delete imp; - break; - } - } // END SWR LEVEL1 - break; - } - // From the service process - case myDS.Deregister(path): - { - string! pathName; - ServiceProviderProxy provider; - - pathName = Bitter.ToString2(path); - DebugStub.Print("DPx: Deregister '{0}'\n", - __arglist(pathName)); - rootDS.SendDeregister(path); - - switch receive { - // From the root DS - case rootDS.AckDeregister(ep): - { - provider = LookupProviderProxy(pathName); - if (provider != null) { - ServiceProviderContract.Imp:Start! endpoint; - provider.Terminate(out endpoint); - myDS.SendAckDeregister(endpoint); - RemoveProviderProxy(provider); - } - else { - myDS.SendNakDeregister(ErrorCode.NotFound); - } - delete ep; - break; - } - // From the root DS - case rootDS.NakDeregister(error): - { - myDS.SendNakDeregister(error); - break; - } - case rootDS.NakDeregisterReparse(npath, rest, link): - { - myDS.SendNakDeregisterReparse(npath, rest, - link); - break; - } - } // END SWR LEVEL1 - break; - } - - // - // PRef style - //NOTE: Not tested yet - case proxy.Allocate(obj): - { - proxy.SendNakAllocate(obj); - break; - } - case proxy.Deallocate(id): - { - proxy.SendNakDeallocate(); - break; - } - case proxy.AcquireObject(id): - { - proxy.SendNakAcquireObject(); - break; - } - case proxy.ReleaseObject(obj): - { - proxy.SendNakReleaseObject(); - break; - } - - // - // Serialization style - // - case proxy.Upload(data): - { - proxy.SendAckUpload(); - objectStore.Store(data); - break; - } - case proxy.Download(): - { - byte[] in ExHeap buffer = objectStore.Restore(); - if (buffer != null) { - proxy.SendAckDownload(buffer); - } - else { - proxy.SendNakDownload(); - } - break; - } - - // - // Checkpoint update protocol - //NOTE: not tested yet - case proxy.Suspend(/* journalet id */): - { - proxy.SendAckSuspend(currentGeneration); - SuspendProviderProxies(); - break; - } - case proxy.Resume(generation): - { - proxy.SendAckResume(); - if (generation == currentGeneration) { - ResumeProviderProxies(); - } - break; - } - case proxy.Update(generation): - { - proxy.SendAckUpdate(); - if (generation == currentGeneration) { - objectStore.Flush(); - UpdateProviderProxies(); - previousGeneration = currentGeneration; - do { - currentGeneration = random.Next(); - } while (currentGeneration == previousGeneration); - } - break; - } - - case proxy.ChannelClosed(): - { - DebugStub.Print("DPx: Server channel closed. " + - "Recover.\n"); - objectStore.Undo(); - recovery = true; - goto exit; - break; - } - // From the service process - case myDS.ChannelClosed(): - { - DebugStub.Print("DPx: Server channel closed. " + - "Recover.\n"); - objectStore.Undo(); - recovery = true; - goto exit; - break; - } - // From the root DS - case rootDS.ChannelClosed(): - { - DebugStub.Print("DPx: DS channel closed. Exit.\n"); - goto exit; - break; - } - case signalFromManager.Stop(): - { - TerminateProviderProxies(); - signalFromManager.SendAckStop(); - goto exit; - break; - } - case signalFromManager.ChannelClosed(): - { - TerminateProviderProxies(); - goto exit; - break; - } - case signalInternal.Stop(): - { - recovery = true; - signalInternal.SendAckStop(); - goto exit; - break; - } - // this works with the proxy initiation style - case signalInternal.Update(): - { - DebugStub.Print("DPx: Update Master\n"); - objectStore.Flush(); - signalInternal.SendAckUpdate(); - break; - } - case signalInternal.ChannelClosed(): - { - TerminateProviderProxies(); - goto exit; - break; - } - } // END SWR LEVEL0 - } // END OF LOOP -exit: - signalRef.Release(signalFromManager); - receiverRef.Release(signalInternal); - - myDsRef.Release(myDS); - rootDsRef.Release(rootDS); - proxyRef.Release(proxy); - - return recovery; - } // HandleDirectoryService - - private void CreateProviderProxy(string! path, - [Claims]ServiceProviderContract.Imp:Start! provider, - [Claims]ServiceProviderContract.Exp:Start! myProvider) - { - ServiceProviderProxy pp = new ServiceProviderProxy(this, path, - factory, - myProvider); - lock (providerList) { - providerList.Add(pp); - } - pp.Start(provider); - } - - private ServiceProviderProxy LookupProviderProxy(string! path) - { - ServiceProviderProxy pp; - - lock (providerList) { - foreach (Object obj in providerList) { - if (obj == null) { - break; - } - pp = obj as ServiceProviderProxy; - if (pp == null) { - continue; - } - if (pp.Path == path) { - return pp; - } - } - } - return null; - } - - private void RemoveProviderProxy(ServiceProviderProxy! sp) - { - lock (providerList) { - providerList.Remove(sp); - } - } - - private void TerminateProviderProxies() - { - ServiceProviderProxy pp; - ServiceProviderContract.Imp:Start! ep; - - //DebugStub.Print("DPx: Enter TerminateProviderProxies\n"); - lock (providerList) { - foreach (Object obj in providerList) { - if (obj == null) { - break; - } - pp = obj as ServiceProviderProxy; - if (pp == null) { - continue; - } - pp.Terminate(out ep); - delete ep; - } - } - //DebugStub.Print("DPx: Exit TerminateProviderProxies\n"); - } - - private void SuspendProviderProxies() - { - ServiceProviderProxy pp; - - lock (providerList) { - foreach (Object obj in providerList) { - if (obj == null) { - break; - } - pp = obj as ServiceProviderProxy; - if (pp == null) { - continue; - } - pp.Suspend(); - } - } - } - - private void ResumeProviderProxies() - { - ServiceProviderProxy pp; - - lock (providerList) { - foreach (Object obj in providerList) { - if (obj == null) { - break; - } - pp = obj as ServiceProviderProxy; - if (pp == null) { - break; - } - pp.Resume(); - } - } - } - - private void UpdateProviderProxies() - { - ServiceProviderProxy pp; - - lock (providerList) { - foreach (Object obj in providerList) { - if (obj == null) { - break; - } - pp = obj as ServiceProviderProxy; - if (pp == null) { - break; - } - pp.Flush(); - } - } - } - - /// - /// Signal to the main loop for recovering the server. - /// Invoked by ServiceProviderProxy. - /// - public void NotifyRecovery() - { - SignalContract.Imp:Start! signal; - - if (!Monitor.TryEnter(recoveryLock)) { - return; - } - - if (!recovery) { - signal = senderRef.Acquire(); - signal.SendStop(); - switch receive { - case signal.AckStop(): - break; - case unsatisfiable: - break; - } - senderRef.Release(signal); - } - - Monitor.Exit(recoveryLock); - } - - public void UpdateCheckpoint() - { - SignalContract.Imp:Start! signal; - - //DebugStub.Print("DPx: Enter UpdateCheckpoint\n"); - signal = senderRef.Acquire(); - SuspendProviderProxies(); - signal.SendUpdate(); - switch receive { - case signal.AckUpdate(): - break; - } - UpdateProviderProxies(); - ResumeProviderProxies(); - senderRef.Release(signal); - //DebugStub.Print("DPx: Exit UpdateCheckpoint\n"); - } - } -} diff --git a/base/Libraries/Resiliency/ICheckpoint.sg b/base/Libraries/Resiliency/ICheckpoint.sg deleted file mode 100644 index d4e4db9..0000000 --- a/base/Libraries/Resiliency/ICheckpoint.sg +++ /dev/null @@ -1,20 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Libraries\Resiliency\ICheckpoint.sg -// -// Note: -// - -namespace Microsoft.Singularity.Resiliency -{ - public interface ICheckpoint - { - void Suspend(); - void Flush(); - void Resume(); - } -} diff --git a/base/Libraries/Resiliency/IRecoveryAware.sg b/base/Libraries/Resiliency/IRecoveryAware.sg deleted file mode 100644 index 24aadaf..0000000 --- a/base/Libraries/Resiliency/IRecoveryAware.sg +++ /dev/null @@ -1,19 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Libraries\Resiliency\IRecoveryAware.sg -// -// Note: -// - -namespace Microsoft.Singularity.Resiliency -{ - public interface IRecoveryAware - { - void NotifyRecovery(); - void UpdateCheckpoint(); - } -} diff --git a/base/Libraries/Resiliency/JournalProducer.sg b/base/Libraries/Resiliency/JournalProducer.sg deleted file mode 100644 index 6a05fd0..0000000 --- a/base/Libraries/Resiliency/JournalProducer.sg +++ /dev/null @@ -1,769 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Libraries\Resiliency\JournalProducer.sg -// -// Note: Logging Worker Creator Template -// -using System; -using System.Collections; -using System.Collections.Specialized; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.SingSharp.Reflection; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Configuration; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Services; -using Microsoft.Singularity.ServiceManager; - -namespace Microsoft.Singularity.Resiliency -{ - public abstract class JournalProducer - { - protected Thread dsThread; - protected Thread managerThread; - protected Thread providerThread; - - protected TRef rootDsRef; - protected TRef myDsRef; - protected TRef providerRef; - protected TRef myProviderRef; - protected TRef serviceRef; - protected TRef controlRef; - protected TRef managerRef; - - protected TRef dsSignalSenderRef; - protected TRef dsSignalReceiverRef; - protected TRef mgSignalSenderRef; - protected TRef mgSignalReceiverRef; - protected TRef spSignalSenderRef; - protected TRef spSignalReceiverRef; - protected TRef recoverySignalSenderRef; - protected TRef recoverySignalReceiverRef; - - protected IList! journaletList; - protected Object! journaletListLock; - - public JournalProducer() - { - this.journaletList = new ArrayList(); - this.journaletListLock = new Object(); - } - - /// - /// Checks the type of this endpoint is that of the subclass can handle. - /// - protected abstract bool Accept(ServiceContract.Exp:Start! ep); - - /// - /// Substitutes the ServiceContract with a new one in order to - /// intercept the messages exchanged through the given ServiceContract. - /// - protected abstract void Substitute([Claims]ServiceContract.Exp:Start! ep, - out ServiceContract.Exp! newEp); - - public void RegisterJournalet(Journalet j) - { - if (j != null) { - lock (journaletListLock) { - try { - journaletList.Add(j); - } - catch (Exception) {} - } - } - } - - public void DeregisterJournalet(Journalet j) - { - if (j != null) { - lock (journaletListLock) { - try { - journaletList.Remove(j); - } - catch (Exception) {} - } - } - } - - /// - /// This actually launches the JournalProducer. The management thread - /// is started here. - /// - /// JournalProducer is initialized with three channels. - /// mep Channel from the service manager. JournalProducer is a - /// kind of service, so it has to be controlled by the service - /// manager. - /// dep Channel to the (root) directory service. This channel is - /// necessary to intercept the connection request from a client - /// to the server that JournalProducer supports. - /// fep Channel from the target service. This channel pretends the - /// root directory service. - /// - /// NOTE: x is an temporal solution so that JournalProducer receives a - /// new DS channel during the service recovery. - /// - public void Start([Claims]ManagedServiceContract.Exp:Start! mep, - [Claims]DirectoryServiceContract.Imp:Ready! dep, - [Claims]DirectoryServiceContract.Exp:Start! fep, - [Claims]ManagedProxyContract.Imp:Start! pep) - { - ThreadTerminationContract.Imp! sender; - ThreadTerminationContract.Exp! receiver; - - // - // The root directory service - // - rootDsRef = new TRef(dep); - - // - // Initialize connection to the service manager - // - mep.SendSuccess(); - serviceRef = new TRef(mep); - ThreadTerminationContract.NewChannel(out sender, out receiver); - mgSignalSenderRef = new TRef(sender); - mgSignalReceiverRef = new TRef(receiver); - - // - // Initialize my directory service - // - fep.SendSuccess(); - myDsRef = new TRef(fep); - - // - // Initialize the channel to the service process - // -// xep.SendSuccess(); -// proxyRef = new TRef(xep); - - // - // Set up another connection for recovery to the service manager - // - switch receive { - case pep.Success(): - break; - case unsatisfiable: - DebugStub.Break(); - break; - } - managerRef = new TRef(pep); - ThreadTerminationContract.NewChannel(out sender, out receiver); - recoverySignalSenderRef = new TRef(sender); - recoverySignalReceiverRef = new TRef(receiver); - - // - // Set up the service controller - // - /* - cep.RecvSuccess(); - controlRef = new TRef(cep); - */ - - // - // Set up the thread terminator for the substitute DS thread - // - ThreadTerminationContract.NewChannel(out sender, out receiver); - dsSignalSenderRef = new TRef(sender); - dsSignalReceiverRef = new TRef(receiver); - - // - // Set up the thread terminator for the producer thread - // - ThreadTerminationContract.NewChannel(out sender, out receiver); - spSignalSenderRef = new TRef(sender); - spSignalReceiverRef = new TRef(receiver); - - // - // Create the substitute DS thread - // - dsThread = new Thread(new ThreadStart(DirectoryServiceThread)); - - // - // Create the journalet producer thread - // - providerThread = new Thread(new ThreadStart(ProviderThread)); - - // - // Create and start the service management thread - // - managerThread = new Thread(new ThreadStart(ManagerThread)); - managerThread.Start(); - } - - /// - /// JournalProducer provides a directory service on behalf of the - /// root DirectoryService so that it intercepts all outputs of - /// a service process. - /// - /// Here you will see the first logic of the directory service. It - /// transits back and forth between the normal state and the recovery - /// state. This seems simple but it's a bit tricky. The normal operation - /// state normally deals with DirectoryServiceContract, but it also - /// creates or deletes logging workers. - /// - /// The recovery operation state recovers the service registration - /// state. A service process registers its ServiceProviderContract - /// to the DirectoryService. JournalProducer intercepts this - /// registration and registers another ServiceProviderContract to the - /// DirectoryService. The recovery operation doesn't create and - /// register a new ServiceProvider because it is already registered - /// to the DirectoryService. - /// - /// NOTE: Current implementation doesn't support all protocols of the - /// DirectoryServiceContract. - protected void DirectoryServiceThread() - { - ThreadTerminationContract.Imp:Start! signalToManager; - - signalToManager = mgSignalSenderRef.Acquire(); - for (;;) { - if (!HandleDirectoryService()) { - break; - } - - // Trigger the recovery - signalToManager.SendStop(); - switch receive { - case signalToManager.AckStop(): - // Transit to the recovery mode - if (!RecoverDirectoryService()) { - goto exit; - } - break; - case signalToManager.ChannelClosed(): - // Recovery failed - goto exit; - break; - } - } -exit: - mgSignalSenderRef.Release(signalToManager); - } - - /// - /// Emulates a directory service. - /// - protected bool HandleDirectoryService() - { - // Flag for the recovery mode outside this method - bool recovery = false; - DirectoryServiceContract.Exp:Ready! myDS; - DirectoryServiceContract.Imp:Ready! rootDS; - ThreadTerminationContract.Exp:Start! signalFromManager; - ThreadTerminationContract.Exp:Start! signalFromProvider; - ThreadTerminationContract.Imp:Start! signalToProvider; - - myDS = myDsRef.Acquire(); - rootDS = rootDsRef.Acquire(); - signalToProvider = spSignalSenderRef.Acquire(); - signalFromManager = dsSignalReceiverRef.Acquire(); - signalFromProvider = recoverySignalReceiverRef.Acquire(); - - for (;;) { - switch receive { - // - // The following 4 cases are message from the service - // process. - // - case myDS.Bind(path, exp): - { - DebugStub.Print("JP DS: Bind, break\n"); - DebugStub.Break(); - delete path; - delete exp; - break; - } - case myDS.Register(path, imp): - { - ServiceProviderContract.Imp! client; - ServiceProviderContract.Exp! server; - - // HI: Current implementation can handle only one name - // per service. - DebugStub.Print("JP DS: Register @ '{0}'\n", - __arglist(Bitter.ToString2(path))); - providerRef = new TRef(imp); - ServiceProviderContract.NewChannel(out client, out server); - myProviderRef = new TRef(server); - // Now register the JP's endpoint to the NS. - rootDS.SendRegister(path, client); - break; - } - case myDS.Deregister(path): - { - DebugStub.Print("JP DS: Deregister\n"); - rootDS.SendDeregister(path); - break; - } - case myDS.ChannelClosed(): - { - DebugStub.Print("JP DS: Server channel closed." + - " Waits for provider's signal.\n"); - switch receive { - case signalFromProvider.Stop(): - { - signalFromProvider.SendAckStop(); - recovery = true; - break; - } - case signalFromProvider.ChannelClosed(): - { - recovery = true; - break; - } - case signalFromManager.Stop(): - { - signalToProvider.SendStop(); - signalToProvider.RecvAckStop(); - delete myProviderRef.Acquire(); - delete providerRef.Acquire(); - providerRef = null; - signalFromManager.SendAckStop(); - goto exit; - break; - } - case signalFromManager.ChannelClosed(): - { - goto exit; - break; - } - } - DebugStub.Print("JP DS: Provider's signal received.\n"); - goto exit; - break; - } - - // - // The following messages are delivered from DS. - // - case rootDS.AckRegister(): - { - DebugStub.Print("JP DS: AckRegister\n"); - myDS.SendAckRegister(); - // - // HI: At this point producer thread is allowed to - // start. - // - providerThread.Start(); - break; - } - case rootDS.NakRegister(ep, error): - { - DebugStub.Print("JP DS: NakRegister\n"); - delete ep; - delete myProviderRef.Acquire(); - myDS.SendNakRegister(providerRef.Acquire(), error); - providerRef = null; - break; - } - case rootDS.NakRegisterReparse(path, rest, link, ep): - { - DebugStub.Print("JP DS: NakRegisterReparse\n"); - delete ep; - delete myProviderRef.Acquire(); - myDS.SendNakRegisterReparse(path, rest, link, - providerRef.Acquire()); - providerRef = null; - break; - } - case rootDS.AckDeregister(ep): - { - DebugStub.Print("JP DS: AckDeregister\n"); - delete ep; - signalToProvider.SendStop(); - signalToProvider.RecvAckStop(); - delete myProviderRef.Acquire(); - myDS.SendAckDeregister(providerRef.Acquire()); - providerRef = null; - break; - } - case rootDS.NakDeregister(error): - { - DebugStub.Print("JP DS: NakDeregister\n"); - myDS.SendNakDeregister(error); - break; - } - case rootDS.NakDeregisterReparse(path, rest, link): - { - DebugStub.Print("JP DS: NakDeregisterReparse\n"); - myDS.SendNakDeregisterReparse(path, rest, link); - break; - } - case rootDS.ChannelClosed(): - { - DebugStub.Print("JP DS: DS channel closed. Break.\n"); - // Emulate channel closing - delete providerRef.Acquire(); - providerRef = null; - DebugStub.Break(); - break; - } - - case signalFromManager.Stop(): - { - signalToProvider.SendStop(); - signalToProvider.RecvAckStop(); - - delete myProviderRef.Acquire(); - delete providerRef.Acquire(); - providerRef = null; - signalFromManager.SendAckStop(); - goto exit; - break; - } - - // - // Signal from ProviderThread in this object. This is the - // trigger to start recovery. - // - case signalFromProvider.Stop(): - DebugStub.Print("JP DS: Received signal." + - " Start recovery.\n"); - recovery = true; - signalFromProvider.SendAckStop(); - goto exit; - break; - } - } -exit: - recoverySignalReceiverRef.Release(signalFromProvider); - spSignalSenderRef.Release(signalToProvider); - dsSignalReceiverRef.Release(signalFromManager); - - myDsRef.Release(myDS); - rootDsRef.Release(rootDS); - - return recovery; - } // HandleDirectoryService - - protected bool RecoverDirectoryService() - { - DirectoryServiceContract.Exp:Ready! substitute; - - DebugStub.Print("JP: ENTER RecoveryOperation\n"); - // - // get a new Directory Service endpoint - // - substitute = myDsRef.Acquire(); - DebugStub.Print("JP: Get new substitute."); - switch receive { - case substitute.Register(path, imp): - { - DebugStub.Print("JP DS Recovery: Re-register '{0}'\n", - __arglist(Bitter.ToString2(path))); - // - // Refresh the server-side ServiceProviderContract - // - DebugStub.Print("JP: Replacing the server-side channel... "); - delete providerRef.Acquire(); - providerRef.Release(imp); - DebugStub.Print("done\n"); - - // - // This time, we immediately send back the ack. - // - substitute.SendAckRegister(); - - delete path; - break; - } - } - myDsRef.Release(substitute); - - new Thread(new ThreadStart(RecoveryThread)).Start(); - //DebugStub.Print("JP: EXIT RecoveryOperation\n"); - return true; - - } - - /// - /// This thread communicates with SMS and deals with general stuff on - /// the service management. - /// - // Communicates with DirectoryServiceThread - protected void ManagerThread() - { - ManagedServiceContract.Exp:Ready! manager; - ManagedProxyContract.Imp:Ready! managerRecovery; - ThreadTerminationContract.Imp:Start! signalToDs; - ThreadTerminationContract.Exp:Start! signalFromDs; - - manager = serviceRef.Acquire(); - managerRecovery = managerRef.Acquire(); - signalToDs = dsSignalSenderRef.Acquire(); - signalFromDs = mgSignalReceiverRef.Acquire(); - - for (;;) { - //DebugStub.Print("JP Manager th: Set\n"); - switch receive { - case manager.StartService(): - { - dsThread.Start(); - manager.SendAckStartService(); - //else { - // manager.SendNakStartService(); - //} - break; - } - case manager.StopService(): - { - //DebugStub.Print("JPMan: StopService\n"); - //providerThread.Stop(); - manager.SendAckStopService(); - break; - } - case manager.RestartService(): - { - DebugStub.Print("JPMan: RestartService\n"); - DebugStub.Break(); - //providerThread.Stop(); - //providerThread.Start(); - manager.SendAckRestartService(); - break; - } - case manager.Knock(): - { - manager.SendAlive(); - break; - } - case manager.Stop(): - { - signalToDs.SendStop(); - break; - } - case manager.Restart(): - { - DebugStub.Break(); - break; - } - case signalFromDs.Stop(): - { - delete myDsRef.Acquire(); - managerRecovery.SendRequestDSRecovery(); - break; - } - // Receives a new DirectoryServiceContract for the - // restarted service process. JP provides the directory - // service again with this endpoint. - case managerRecovery.AckDSRecovery(directory): - { - directory.SendSuccess(); - myDsRef.Release(directory); - signalFromDs.SendAckStop(); - break; - } - case managerRecovery.NakDSRecovery(): - { - goto exit; - } - case managerRecovery.ChannelClosed(): - { - goto exit; - } - case signalToDs.AckStop(): - { - manager.SendAckStop(); - goto exit; - break; - } - } - } -exit: - delete manager; - delete managerRecovery; - delete signalFromDs; - delete signalToDs; - } - - /// - /// Creates a Journalet for each C-S connection. - /// - protected void ProviderThread() - { - bool release = false; - bool signal = false; - bool recovery = false; - - // From the root directory service - ServiceProviderContract.Exp:Start! providerFromRootDs; - // To the resilient service - ServiceProviderContract.Imp:Start! provider; - ThreadTerminationContract.Imp:Start! dsSignal; - ThreadTerminationContract.Exp:Start! signalFromDs; - ThreadTerminationContract.Imp:Start! signalToDs; - - providerFromRootDs = myProviderRef.Acquire(); - signalFromDs = spSignalReceiverRef.Acquire(); - signalToDs = recoverySignalSenderRef.Acquire(); - - // - // lock the server - // - provider = providerRef.Acquire(); - - for (;;) { - switch receive { - case providerFromRootDs.Connect(ep): - { - //DebugStub.Print("JP Producer: received" + - // " client connection request ... "); - if (Accept(ep)) { - //DebugStub.Print("accepted.\n"); - ServiceContract.Exp! newExp; - - Substitute(ep, out newExp); - provider.SendConnect(newExp); - switch receive { - case provider.AckConnect(): - providerFromRootDs.SendAckConnect(); - break; - case provider.NackConnect(rejected): - delete rejected; - providerFromRootDs.SendNackConnect(null); - break; - case provider.ChannelClosed(): - // - // Try to recover - // - providerFromRootDs.SendNackConnect(null); - release = true; - signal = false; - recovery = true; - goto exit; - break; - } - } - else { - //DebugStub.Print("denied\n"); - providerFromRootDs.SendNackConnect(ep); - } - break; - } - case providerFromRootDs.ChannelClosed(): - { - goto exit; - break; - } - case signalFromDs.Stop(): - { - release = false; - signal = true; - goto exit; - break; - } - case signalFromDs.ChannelClosed(): - { - goto exit; - release = false; - break; - } - case provider.ChannelClosed(): - { - // Recovery - DebugStub.Print("JP: Service provider lost. " + - "Recover.\n"); - release = true; - signal = false; - recovery = true; - goto exit; - break; - } - } - } -exit: - - // Unlock the server - // This allows the DirectoryServiceThread to replace the - // ServiceProviderContract with a new one. - providerRef.Release(provider); - - if (recovery) { - // Notify the internal DS to restart - signalToDs.SendStop(); - signalToDs.RecvAckStop(); - } - - // No matter if it recovers or not, sends a signal to get the - // DirectoryServiceThread out of the loop. - recoverySignalSenderRef.Release(signalToDs); - - if (release) { - myProviderRef.Release(providerFromRootDs); - } - else { - // Disconnect from the root DS. - delete providerFromRootDs; - } - - if (signal) { - // It's over. Get the ManagerThread out of loop. - signalFromDs.SendAckStop(); - } - spSignalReceiverRef.Release(signalFromDs); - } - - /// - /// Runs Journalets to replay their logs to the restarted and refreshed - /// service process. Each Journalet emulates normal operations: create - /// a new pair of ServiceContract, deliver an endpoint through the - /// ServiceProviderContract, then interacts with the service process. - /// - /// Each Journalet has its own thread to replay the log, so they are - /// running concurrently. - /// - protected virtual void RecoveryThread() - { - Journalet journalet; - ServiceContract.Exp:Start! ep; - ServiceProviderContract.Imp:Start! provider; - - //DebugStub.Print("JP: ENTER Recovery Thread\n"); - - // Lock the server. This blocks new clients to connect the service - // process. - provider = providerRef.Acquire(); - lock (journaletListLock) { - foreach (Object obj in journaletList) { - if (obj == null) { - break; - } - journalet = obj as Journalet; - if (journalet == null) { - continue; - } - - journalet.CreateServerEndpoint(out ep); - - provider.SendConnect(ep); - switch receive { - case provider.AckConnect(): - break; - case provider.NackConnect(rejected): - delete rejected; - DebugStub.Print("JP Recovery Th: " + - "Provider connection rejected\n"); - break; - case provider.ChannelClosed(): - DebugStub.Print("Server lost during recovery." + - " Break,\n"); - DebugStub.Break(); - break; - } - } - } - // unlock the server - providerRef.Release(provider); - - providerThread = new Thread(new ThreadStart(ProviderThread)); - providerThread.Start(); - - //DebugStub.Print("JP: EXIT Recovery Thread\n"); - } - } -} diff --git a/base/Libraries/Resiliency/Journalet.sg b/base/Libraries/Resiliency/Journalet.sg deleted file mode 100644 index 1205e3a..0000000 --- a/base/Libraries/Resiliency/Journalet.sg +++ /dev/null @@ -1,88 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Libraries\Resiliency\Journalet.sg -// -// Note: Logging Worker Template -// -using System; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Services; - -namespace Microsoft.Singularity.Resiliency -{ - public abstract class Journalet - { - protected JournalProducer! producer; - protected Thread serviceThread; - - protected TRef clientRef; - - public Journalet(JournalProducer! producer, - [Claims]ServiceContract.Exp:Start! ep) - { - this.producer = producer; - this.clientRef = new TRef(ep); - } - - public abstract void CreateServerEndpoint(out ServiceContract.Exp:Start! ep); - - protected virtual bool Initialize() - { - return true; - } - - protected virtual bool HandleMessages() - { - return true; - } - - protected virtual bool Recover() - { - return true; - } - - /// - /// This thread interposes between the client and the server. - /// - protected virtual void ProxyThread() { - if (!Initialize()) { - goto exit; - } - - for (;;) { - if (!HandleMessages()) { - goto exit; - } - try { - if (!Recover()) { - goto exit; - } - } - catch (Exception e) { - DebugStub.WriteLine("ProxyThread: Exception caught."); - DebugStub.WriteLine(e.ToString()); - } - } -exit: - producer.DeregisterJournalet(this); - return; - } - - public void Start() { - ThreadTerminationContract.Imp! sender; - ThreadTerminationContract.Exp! receiver; - - serviceThread = new Thread(new ThreadStart(ProxyThread)); - serviceThread.Start(); - } - } -} diff --git a/base/Libraries/Resiliency/Journalet2.sg b/base/Libraries/Resiliency/Journalet2.sg deleted file mode 100644 index 0f19cfa..0000000 --- a/base/Libraries/Resiliency/Journalet2.sg +++ /dev/null @@ -1,148 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Libraries\Resiliency\Journalet2.sg -// -// Note: -// -using System; -using System.Threading; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; - -namespace Microsoft.Singularity.Resiliency -{ - public enum ProxyState : uint { - Waiting, - Running, - Synchronize, - } - - public abstract class Journalet2 : ICheckpoint - { - protected bool running; - protected Thread thread; - protected JournaletGroup group; - protected ManualResetEvent recoveryEvent; - protected ManualResetEvent suspendEvent; - protected ProxyState proxyState; - - public Journalet2() - { - this.recoveryEvent = new ManualResetEvent(false); - this.suspendEvent = new ManualResetEvent(false); - this.proxyState = ProxyState.Waiting; - } - - //NOTE: Be sure the thread is stopped when calling this. - public abstract void CreateEndpoint(out ServiceContract.Exp! newExp); - protected abstract bool Initialize(); - protected abstract bool HandleMessages(); - protected abstract bool Recover(); - - public virtual void Start() - { - if (thread == null) { - thread = new Thread(new ThreadStart(Run)); - thread.Start(); - } - else { - recoveryEvent.Set(); - } - } - - public virtual void Stop() {} - - public virtual void Run() { - if (group == null) { - DebugStub.Print("J: group is not set!\n"); - return; - } - - if (!Initialize()) { - return; - } - - for (;;) { - if (!HandleMessages()) { - break; - } - // Now you can create another endpoint. - - running = false; - group.NotifyRecovery(); - - recoveryEvent.WaitOne(); - - DebugStub.Print("J: Start recovery\n"); - try { - if (!Recover()) { - break; - } - } - catch (ChannelClosedException) { - DebugStub.Print("Channel closed during recovery.\n"); - break; - } - recoveryEvent.Reset(); - Thread.Sleep(0); - } - } - - /// - /// Suspends this journalet at the nearest synchronization point. - /// This method blocks the caller until the journalet suspends. - /// - public void Suspend() - { - if (running && group != null && group.SyncRequest) { - suspendEvent.WaitOne(); - } - } - - public abstract void Flush(); - public void Resume() {} - - // Checks if receiving a synchronization request. - protected void Sync() - { - proxyState = ProxyState.Synchronize; - if (group != null && group.SyncRequest) { - running = false; - suspendEvent.Set(); - DebugStub.Print("J: Syncronize\n"); - group.SyncEvent.WaitOne(); - running = true; - } - } - - protected void Checkpoint() - { - //DebugStub.Print("J: Enter Checkpoint\n"); - group.UpdateCheckpoint(); - //DebugStub.Print("J: Exit Checkpoint\n"); - } - - public ProxyState State - { - get { return proxyState; } - private set - { - lock (this) { - proxyState = value; - } - } - } - - // Invoked by JournaletGroup. - internal JournaletGroup Group - { - get { return group; } - set { group = value; } - } - } -} diff --git a/base/Libraries/Resiliency/JournaletFactory.sg b/base/Libraries/Resiliency/JournaletFactory.sg deleted file mode 100644 index 355fcba..0000000 --- a/base/Libraries/Resiliency/JournaletFactory.sg +++ /dev/null @@ -1,22 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Libraries\Resiliency\JournaletFactory.sg -// -// Note: -// -using Microsoft.SingSharp; -using Microsoft.Singularity.Directory; - -namespace Microsoft.Singularity.Resiliency -{ - public interface JournaletFactory - { - bool Accept(ServiceContract.Exp:Start! ep); - Journalet2! CreateJournalet([Claims]ServiceContract.Exp:Start! ep, - out ServiceContract.Exp! newEp); - } -} diff --git a/base/Libraries/Resiliency/JournaletGroup.sg b/base/Libraries/Resiliency/JournaletGroup.sg deleted file mode 100644 index 2311452..0000000 --- a/base/Libraries/Resiliency/JournaletGroup.sg +++ /dev/null @@ -1,238 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Libraries\Resiliency\JournaletGroup.sg -// -// Note: -// -using System; -using System.Collections; -using System.Threading; -using Microsoft.Singularity; - -namespace Microsoft.Singularity.Resiliency -{ - public class JournaletGroup : IEnumerable, IRecoveryAware, ICheckpoint - { - protected IRecoveryAware parent; - protected bool syncRequest; - protected bool recoveringState; - protected bool checkpointingState = false; - protected Object! recoveryLock; - protected IList! journaletList; - protected Object! listLock; - protected ManualResetEvent! syncEvent; - - public JournaletGroup() - { - recoveryLock = new Object(); - listLock = new Object(); - journaletList = new ArrayList(); - syncEvent = new ManualResetEvent(false); - } - - /// - /// Adds the specified journalet to the group - /// - public void Add(Journalet2 journalet) - { - if (journalet != null) { - lock (listLock) { - journaletList.Add(journalet); - journalet.Group = this; - } - } - } - - /// - /// Removes the specified journalet from the group - /// - public void Remove(Journalet2 journalet) - { - if (journalet != null) { - lock (listLock) { - journaletList.Remove(journalet); - journalet.Group = null; - } - } - } - - // - // Invoked either when the proxy starts up or when the recovery ends. - // - public void Start() - { - Journalet2 j; - - DebugStub.Print("JournaletGroup: Enter Start\n"); - DebugStub.Print("JournaletGroup: {0} journalets\n", - __arglist(journaletList.Count)); - syncRequest = false; - recoveringState = false; - lock (listLock) { - foreach (Object obj in journaletList) { - if (obj == null) { - break; - } - j = obj as Journalet2; - if (j == null) { - continue; - } - j.Start(); - DebugStub.Print("JournaletGroup: J started\n"); - } - } - } - - public void Stop() - { - Journalet2 j; - - recoveringState = true; // to prevent subsequent recovery - - lock (listLock) { - foreach (Object obj in journaletList) { - if (obj == null) { - break; - } - j = obj as Journalet2; - if (j == null) { - continue; - } - j.Stop(); - } - } - } - - public void Suspend() - { - Journalet2 journalet; - - syncRequest = true; - - lock (listLock) { - foreach (Object obj in journaletList) { - if (obj == null) { - break; - } - journalet = obj as Journalet2; - if (journalet != null) { - journalet.Suspend(); - } - } - } - } - - public void Flush() - { - Journalet2 journalet; - - lock (listLock) { - foreach (Object obj in journaletList) { - if (obj == null) { - break; - } - journalet = obj as Journalet2; - if (journalet != null) { - journalet.Flush(); - } - } - } - } - - public void Resume() - { - syncRequest = false; - syncEvent.Set(); - } - - public bool SyncRequest - { - get { return syncRequest; } - private set {} - } - - public ManualResetEvent! SyncEvent - { - get { return syncEvent; } - private set {} - } - - internal IRecoveryAware Parent - { - get { return parent; } - set { parent = value; } - } - - public void NotifyRecovery() - { - DebugStub.Print("JGroup: Enter NotifyRecovery\n"); - // this doesn't immediately returns if the lock - // is already acquired. - //if (!Monitor.TryEnter(recoveryLock)) { - // DebugStub.Print("JGroup: Exit NotifyRecovery\n"); - // return; - //} - - lock (recoveryLock) { - if (recoveringState) { - DebugStub.Print("JGroup: Exit NotifyRecovery\n"); - return; - } - else { - recoveringState = true; - } - } - - if (parent != null || !recoveringState) { - parent.NotifyRecovery(); - //recoveringState = true; - } - //Monitor.Exit(recoveryLock); - - DebugStub.Print("JGroup: Exit NotifyRecovery\n"); - } - - public void UpdateCheckpoint() - { - //DebugStub.Print("JGroup: Enter UpdateCheckpoint\n"); - //Doesn't seem work - //if (!Monitor.TryEnter(recoveryLock)) { - // DebugStub.Print("JGroup: Exit UpdateCheckpoint\n"); - // return; - //} - lock (recoveryLock) { - if (checkpointingState) { - //DebugStub.Print("JGroup: Exit UpdateCheckpoint\n"); - return; - } - else { - checkpointingState = true; - } - } - - // Suspend all other journalets - Suspend(); - - // Notify to ServiceProviderProxy - if (parent != null) { - parent.UpdateCheckpoint(); - } - - checkpointingState = false; - // Resume the journalets - Resume(); - - //Monitor.Exit(recoveryLock); - //DebugStub.Print("JGroup: Exit UpdateCheckpoint\n"); - } - - public IEnumerator GetEnumerator() - { - return journaletList.GetEnumerator(); - } - } -} diff --git a/base/Libraries/Resiliency/Log.sg b/base/Libraries/Resiliency/Log.sg deleted file mode 100644 index 51fae7e..0000000 --- a/base/Libraries/Resiliency/Log.sg +++ /dev/null @@ -1,97 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Libraries\Resiliency\Log.sg -// -// Note: -// -using System; -using System.Collections; - -namespace Microsoft.Singularity.Resiliency -{ - public class Log : IEnumerable - { - protected IList! log; - protected IEnumerator enumerator; - - public Log() - { - log = new ArrayList(); - } - - public void Record(uint msg, IList args) - { - if (enumerator != null) { - enumerator = null; - } - log.Add(new LogData(msg, args)); - } - - public void Record(uint msg) - { - this.Record(msg, null); - } - - public bool Playback(out uint message, out IList args) - { - LogData data; - - if (enumerator == null) { - enumerator = log.GetEnumerator(); - } - - try { - enumerator.MoveNext(); - } - catch (Exception) { - enumerator.Reset(); - enumerator = null; - message = 0; - args = null; - return false; - } - - try { - data = (LogData)enumerator.Current; - if (data == null) { - enumerator = null; - message = 0; - args = null; - return false; - } - else { - message = data.Message; - args = data.Arguments; - return true; - } - } - catch (Exception) { - //DebugStub.Print("Log: {0}\n", __arglist(e.ToString())); - message = 0; - args = null; - return false; - } - } - - public void Flush() - { - enumerator = null; - log.Clear(); - } - - public int Count - { - get { return log.Count; } - private set {} - } - - public IEnumerator GetEnumerator() - { - return log.GetEnumerator(); - } - } // class -} diff --git a/base/Libraries/Resiliency/LogData.sg b/base/Libraries/Resiliency/LogData.sg deleted file mode 100644 index 49a5d36..0000000 --- a/base/Libraries/Resiliency/LogData.sg +++ /dev/null @@ -1,38 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Libraries\Resiliency\LogData.sg -// -// Note: -// -using System.Collections; - -namespace Microsoft.Singularity.Resiliency -{ - public class LogData - { - protected readonly uint message; - protected readonly IList arguments; - - public LogData(uint message, IList arguments) - { - this.message = message; - this.arguments = arguments; - } - - public uint Message - { - get { return message; } - private set{} - } - - public IList Arguments - { - get { return arguments; } - private set {} - } - } -} diff --git a/base/Libraries/Resiliency/PersistentMemory.sg b/base/Libraries/Resiliency/PersistentMemory.sg deleted file mode 100644 index f316303..0000000 --- a/base/Libraries/Resiliency/PersistentMemory.sg +++ /dev/null @@ -1,185 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Libraries\Resiliency\UndoableMemory.sg -// -// Note: -// -using System; -using System.Collections; -using System.Collections.Specialized; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; - -namespace Microsoft.Singularity.Resiliency -{ - public class PersistentMemory - { - private IDictionary! table; - - public PersistentMemory() - { - table = new ListDictionary(); - } - - public void Store([Claims]byte[]! in ExHeap buffer) - { - int offset = 0; - int length = buffer.Length; - int index; - int count; - UndoableMemory memoryObject; - byte[]! copyBuffer = Bitter.ToByteArray(buffer); - byte[] storeBuffer; - - //DebugStub.Print("PMem: Enter Store\n"); - //DebugStub.Print("PMem: Buffer length {0}\n", - // __arglist(buffer.Length)); - - while (offset + 8 < length) { - // - // Deserialize - // - // length - count = BitConverter.ToInt32(copyBuffer, offset); - offset += sizeof(int); - if (offset >= length) { - DebugStub.Print("PMem: deserialize err\n"); - break; - } - - // index - index = BitConverter.ToInt32(copyBuffer, offset); - offset += sizeof(int); - if (offset >= length) { - DebugStub.Print("PMem: deserialize err\n"); - break; - } - - // contents - storeBuffer = new byte[count]; - Buffer.BlockCopy(copyBuffer, offset, storeBuffer, 0, count); - offset += count; - - //DebugStub.Print("PMem: [{0}] {1}:{2}\n", - // __arglist(index, count, offset)); - - // - // Store the data - // - Object obj = table[index]; - if (obj == null) { - memoryObject = new UndoableMemory(); - table[index] = memoryObject; - } - else { - memoryObject = obj as UndoableMemory; - } - - if (memoryObject != null) { - memoryObject.Store(storeBuffer); - } - } - delete buffer; - //DebugStub.Print("PMem: Exit Store\n"); - } - - public byte[] in ExHeap Restore() - { - byte[] buffer = null; - int offset; - int length; - UndoableMemory memoryObject; - - DebugStub.Print("PMem: Enter Restore\n"); - if (table.Count == 0) { - return null; - } - - length = 0; - foreach (DictionaryEntry entry in table) { - memoryObject = entry.Value as UndoableMemory; - if (memoryObject != null) { - memoryObject.Flush(); - length += memoryObject.Length; - } - length += sizeof(int) * 2; - } - - if (length > Int32.MaxValue) { - throw new Exception(); - } - - DebugStub.Print("PMem: Serialize\n"); - // - // Serialize - // - buffer = new byte[length]; - offset = 0; - foreach (DictionaryEntry entry in table) { - int index = (int)entry.Key; - memoryObject = entry.Value as UndoableMemory; - - if (memoryObject == null) { - // length - for (int i = 0; i < sizeof(int); i++) { - buffer[offset + i] = 0; - } - offset += sizeof(int); - - // index - Buffer.BlockCopy(BitConverter.GetBytes(index), 0, - buffer, offset, sizeof(int)); - offset += sizeof(int); - } - else { - // length - Buffer.BlockCopy(BitConverter.GetBytes(memoryObject.Length), - 0, buffer, offset, sizeof(int)); - offset += sizeof(int); - - // index - Buffer.BlockCopy(BitConverter.GetBytes(index), 0, - buffer, offset, sizeof(int)); - offset += sizeof(int); - - // contents - Buffer.BlockCopy(memoryObject.Restore(), 0, - buffer, offset, memoryObject.Length); - offset += memoryObject.Length; - } - } - - DebugStub.Print("PMem: Exit Restore\n"); - return Bitter.FromByteArray(buffer); - } - - public void Flush() - { - //DebugStub.Print("PMem: Enter Flush\n"); - foreach (DictionaryEntry entry in table) { - UndoableMemory memoryObject = entry.Value as UndoableMemory; - if (memoryObject != null) { - //DebugStub.Print("[{0}]\n", __arglist((int)entry.Key)); - memoryObject.Flush(); - } - } - //DebugStub.Print("PMem: Exit Flush\n"); - //DebugStub.Break(); - } - - public void Undo() - { - foreach (DictionaryEntry entry in table) { - UndoableMemory memoryObject = entry.Value as UndoableMemory; - if (memoryObject != null) { - memoryObject.Undo(); - } - } - } - } -} diff --git a/base/Libraries/Resiliency/Resiliency.csproj b/base/Libraries/Resiliency/Resiliency.csproj deleted file mode 100644 index 74855b1..0000000 --- a/base/Libraries/Resiliency/Resiliency.csproj +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - Library - Resiliency - true - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/base/Libraries/Resiliency/ServiceProviderProxy.sg b/base/Libraries/Resiliency/ServiceProviderProxy.sg deleted file mode 100644 index e1de431..0000000 --- a/base/Libraries/Resiliency/ServiceProviderProxy.sg +++ /dev/null @@ -1,332 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Libraries\Resiliency\ServiceProviderProxy.sg -// -// Note: -// -using System; -using System.Collections; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Services; - -namespace Microsoft.Singularity.Resiliency -{ - internal sealed class ServiceProviderProxy : IRecoveryAware, ICheckpoint - { - private readonly string! path; - private IRecoveryAware! dsProxy; - private JournaletFactory! factory; - private JournaletGroup! group; - private IList! blackList; - private Thread thread; - private bool running; - private TRef providerRef; - private TRef myProviderRef; - private TRef internalImpRef; - private TRef internalExpRef; - - internal ServiceProviderProxy(IRecoveryAware! dsProxy, - string! path, - JournaletFactory! factory, - [Claims]ServiceProviderContract.Exp:Start! ep) - { - this.dsProxy = dsProxy; - this.path = path; - this.factory = factory; - this.group = new JournaletGroup(); - this.blackList = new ArrayList(); - this.myProviderRef = new TRef(ep); - - ThreadTerminationContract.Imp! imp; - ThreadTerminationContract.Exp! exp; - ThreadTerminationContract.NewChannel(out imp, out exp); - internalImpRef = new TRef(imp); - internalExpRef = new TRef(exp); - } - - private void Run() - { - ServiceProviderContract.Exp:Start! ep; - ThreadTerminationContract.Exp:Start! signal; - - if (myProviderRef == null) { - DebugStub.Print("PPx: not initialized!\n"); - return; - } - - - lock (this) { - running = true; - } - - try { - Recover(); - } - catch (Exception) { - DebugStub.Print("PPx: exception during recovery\n"); - group.Stop(); - return; - } - - ep = myProviderRef.Acquire(); - signal = internalExpRef.Acquire(); - - for (;;) { - DebugStub.Print("PPx: set\n"); - switch receive { - case ep.Connect(serviceEp): - { - if (factory.Accept(serviceEp)) { - try { - if (CreateJournalet(serviceEp)) { - ep.SendAckConnect(); - } - else { - ep.SendNackConnect(null); - } - } - catch (Exception) { - ep.SendNackConnect(null); - dsProxy.NotifyRecovery(); - goto exit; - } - } - else { - ep.SendNackConnect(serviceEp); - } - break; - } - case ep.ChannelClosed(): - { - group.Stop(); - goto exit; - break; - } - // FIXME: This is confusing because the Stop message is - // used to trigger the recovery action; it's used to - // terminate threads in other parts of the proxy. - case signal.Stop(): - { - signal.SendAckStop(); - dsProxy.NotifyRecovery(); - goto exit; - break; - } - // An explicit channel close terminates this thread. - case signal.ChannelClosed(): - { - group.Stop(); - goto exit; - break; - } - } - } -exit: - myProviderRef.Release(ep); - internalExpRef.Release(signal); - - lock (this) { - running = false; - } - DebugStub.Print("PPx: Quit\n"); - } - - /// - /// Creates and activates a new Journalet with the specified endpoint. - /// - private bool CreateJournalet([Claims]ServiceContract.Exp:Start! ep) - { - bool success = false; - bool throwException = false; - Journalet2! journalet; - ServiceContract.Exp! newEp; - ServiceProviderContract.Imp:Start! provider; - - journalet = factory.CreateJournalet(ep, out newEp); - - provider = providerRef.Acquire(); - provider.SendConnect(newEp); - switch receive { - case provider.AckConnect(): - success = true; - break; - case provider.NackConnect(rejected): - delete rejected; - break; - case provider.ChannelClosed(): - // recovery - throwException = true; - break; - } - providerRef.Release(provider); - - if (throwException) { - throw new ChannelClosedException(); - } - - if (success) { - if (group.Parent == null) { - group.Parent = this; - } - - group.Add(journalet); - journalet.Start(); - } - - return success; - } - - private void Recover() - { - bool throwException = false; - Journalet2 journalet; - ServiceContract.Exp! newEp; - ServiceProviderContract.Imp:Start! provider; - - DebugStub.Print("PPx: Enter Recovery\n"); - provider = providerRef.Acquire(); - foreach (Object obj in group) { - if (obj == null) { - break; - } - journalet = obj as Journalet2; - if (journalet == null) { - continue; - } - - // journalet must be inactive. - journalet.CreateEndpoint(out newEp); - - provider.SendConnect(newEp); - switch receive { - case provider.AckConnect(): - journalet.Start(); - break; - case provider.NackConnect(rejected): - delete rejected; - blackList.Add(journalet); - break; - case provider.ChannelClosed(): - throwException = true; - break; - } - - if (throwException) { - providerRef.Release(provider); - throw new ChannelClosedException(); - } - } - - foreach (Object obj in blackList) { - if (obj == null) { - break; - } - journalet = obj as Journalet2; - if (journalet != null) { - group.Remove(journalet); - } - } - blackList.Clear(); - - // - // Restart journalets - // - group.Start(); - providerRef.Release(provider); - DebugStub.Print("PPx: Exit Recovery\n"); - } - - public void NotifyRecovery() - { - ThreadTerminationContract.Imp:Start! signal; - DebugStub.Print("PPx: NotifyRecovery\n"); - - // - // Terminates itself, then the thread reports to Directory Proxy - // - signal = internalImpRef.Acquire(); - signal.SendStop(); - switch receive { - case signal.AckStop(): - break; - case unsatisfiable: - //DebugStub.Break(); - break; - } - internalImpRef.Release(signal); - } - - public void UpdateCheckpoint() - { - //DebugStub.Print("PPx: Enter UpdateCheckpoint\n"); - dsProxy.UpdateCheckpoint(); - //DebugStub.Print("PPx: Exit UpdateCheckpoint\n"); - } - - internal void Start([Claims]ServiceProviderContract.Imp:Start! ep) - { - assert !running; - - thread = new Thread(new ThreadStart(Run)); - - if (providerRef == null) { - providerRef = new TRef(ep); - } - else { - providerRef.Release(ep); - } - - thread.Start(); - } - - internal void Stop(out ServiceProviderContract.Imp:Start! ep) - { - TRef reference; - - DebugStub.Print("PPx: Stop\n"); - if (running) { - // Stop the thread - NotifyRecovery(); - thread.Join(); - DebugStub.Print("PPx: Joined\n"); - } - - ep = providerRef.Acquire(); - } - - internal void Terminate(out ServiceProviderContract.Imp:Start! ep) - { - delete internalImpRef.Acquire(); - ep = providerRef.Acquire(); - } - - public void Suspend() - { - group.Suspend(); - } - - public void Flush() - { - group.Flush(); - } - - public void Resume() - { - group.Resume(); - } - - internal string! Path - { - get { return path; } - private set {} - } - } -} diff --git a/base/Libraries/Resiliency/ServiceProxy.sg b/base/Libraries/Resiliency/ServiceProxy.sg deleted file mode 100644 index 85c1ab7..0000000 --- a/base/Libraries/Resiliency/ServiceProxy.sg +++ /dev/null @@ -1,187 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Libraries\Resiliency\Proxy.sg -// -// Note: -// -using System; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Services; -using Microsoft.Singularity.ServiceManager; - -namespace Microsoft.Singularity.Resiliency -{ - public sealed class ServiceProxy - { - private Thread serviceThread; - private JournaletFactory! factory; - private TRef serviceRef; - private TRef recoveryRef; - private TRef rootDsRef; - private TRef myDsRef; - private TRef proxyRef; - - public ServiceProxy(JournaletFactory! factory, - [Claims]ManagedServiceContract.Exp:Start! mep, - [Claims]ManagedProxyContract.Imp:Start! pep, - [Claims]DirectoryServiceContract.Imp:Ready! dep, - [Claims]DirectoryServiceContract.Exp:Start! fep, - [Claims]ServiceProxyContract.Exp:Start! xep) - { - this.factory = factory; - this.rootDsRef = new TRef(dep); - - fep.SendSuccess(); - this.myDsRef = new TRef(fep); - - mep.SendSuccess(); - this.serviceRef = new TRef(mep); - - switch receive { - case pep.Success(): - break; - case unsatisfiable: - DebugStub.Break(); - break; - } - this.recoveryRef = new TRef(pep); - - xep.SendSuccess(); - this.proxyRef = new TRef(xep); - } - - public void Start() - { - if (serviceThread == null) { - serviceThread = new Thread(new ThreadStart(Run)); - } - serviceThread.Start(); - } - - public void Run() - { - Thread thread = null; - DirectoryServiceProxy dsProxy; - ManagedServiceContract.Exp:Ready! manager; - ThreadTerminationContract.Imp! imp; - ThreadTerminationContract.Exp! exp; - - DebugStub.Print("-- Start proxy\n"); - - dsProxy = new DirectoryServiceProxy(this, - factory, - rootDsRef.Acquire(), - myDsRef.Acquire(), - proxyRef.Acquire()); - ThreadTerminationContract.NewChannel(out imp, out exp); - dsProxy.Signal(exp); - - manager = serviceRef.Acquire(); - for (;;) { - switch receive { - case manager.StartService(): - { - if (thread == null) { - thread = new Thread(new ThreadStart(dsProxy.Run)); - } - thread.Start(); - manager.SendAckStartService(); - break; - } - case manager.StopService(): - { - DebugStub.Print("Px: recv stop service\n"); - imp.SendStop(); - switch receive { - case imp.AckStop(): - manager.SendAckStopService(); - break; - case unsatisfiable: - manager.SendBusy(); - break; - } - thread = null; - break; - } - case manager.RestartService(): - { - manager.SendAckRestartService(); - break; - } - case manager.Knock(): - { - manager.SendAlive(); - break; - } - case manager.Stop(): - { - imp.SendStop(); - switch receive { - case unsatisfiable: - break; - } - manager.SendAckStop(); - goto exit; - break; - } - case manager.Restart(): - { - DebugStub.Break(); - manager.SendAckRestart(); - break; - } - } - } -exit: - delete manager; - delete recoveryRef.Acquire(); - delete imp; - } - - /// - /// Asks ServiceManager to recover the corresponding service and - /// the initial direct channels to the service. - /// - internal void RecoverService(out DirectoryServiceContract.Exp:Ready ep, - out ServiceProxyContract.Exp:Ready xep) - { - ManagedProxyContract.Imp:Ready! manager; - DebugStub.Print("Px: Enter RecoverService\n"); - - manager = recoveryRef.Acquire(); -#if COMPATIBLE_V1 - manager.SendRequestDSRecovery(); - switch receive { - case manager.AckDSRecovery(proxy): -#else - manager.SendRequestRecovery(); - switch receive { - case manager.AckRecovery(directory, proxy): -#endif - { - directory.SendSuccess(); - proxy.SendSuccess(); - ep = directory; - xep = proxy; - break; - } - case unsatisfiable: - { - ep = null; - xep = null; - break; - } - } - recoveryRef.Release(manager); - DebugStub.Print("Px: Exit RecoverService\n"); - } - } -} diff --git a/base/Libraries/Resiliency/SignalContract.sg b/base/Libraries/Resiliency/SignalContract.sg deleted file mode 100644 index 123dcfe..0000000 --- a/base/Libraries/Resiliency/SignalContract.sg +++ /dev/null @@ -1,30 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Libraries\Resiliency\DirectoryServiceProxy.sg -// -// Note: Intermediary of DirectoryServiceContract -// -using Microsoft.Singularity.Channels; - -namespace Microsoft.Singularity.Resiliency -{ - public contract SignalContract - { - in message Stop(); - in message Suspend(); - in message Update(); - out message AckStop(); - out message AckSuspend(); - out message AckUpdate(); - - state Start : one { - Stop? -> AckStop! -> Start; - Suspend? -> AckSuspend! -> Start; - Update? -> AckUpdate! -> Start; - } - } -} diff --git a/base/Libraries/Resiliency/UndoableMemory.sg b/base/Libraries/Resiliency/UndoableMemory.sg deleted file mode 100644 index a08a396..0000000 --- a/base/Libraries/Resiliency/UndoableMemory.sg +++ /dev/null @@ -1,201 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Libraries\Resiliency\UndoableMemory.sg -// -// Note: -// -using System; -using System.Collections; -using System.IO; -using Microsoft.SingSharp; - -namespace Microsoft.Singularity.Resiliency -{ - internal class UndoableMemory - { - private const int DefaultCapacity = 256; - private byte[]! master; - private byte[] shadow; - private byte[] backup; - private int capacity; - private int length; - - internal UndoableMemory() : this(0) {} - - internal UndoableMemory(int capacity) - requires (capacity >= 0); - { - this.master = this.shadow = new byte[capacity]; - this.capacity = capacity; - } - - internal byte[]! Restore() - { - byte[] buffer = new byte[length]; - - if (length <= 8) { - int byteCount = length; - while (--byteCount >= 0) { - buffer[byteCount] = shadow[byteCount]; - } - } - else { - Buffer.BlockCopy(shadow, 0, buffer, 0, length); - } - return buffer; - } - - internal void Store(byte[]! buffer) - { - int count = buffer.Length; - - if (master == shadow) { - if (backup != null) { - shadow = backup; - } - else { - shadow = new byte[capacity]; - Buffer.BlockCopy(master, 0, shadow, 0, capacity); - } - } - - if (count >= length) { - int newLength = count + 1; - bool mustZero = count > length; - if (newLength >= capacity) { - bool allocatedNewArray = EnsureCapacity(newLength); - if (allocatedNewArray) { - mustZero = false; - } - } - if (mustZero) { - Array.Clear(shadow, length, count - length); - } - length = newLength; - } - - if (count <= 8) { - int byteCount = count; - while (--byteCount >= 0) { - shadow[byteCount] = buffer[byteCount]; - } - } - else { - Buffer.BlockCopy(buffer, 0, shadow, 0, buffer.Length); - } - length = buffer.Length; - } - - internal void DirectStore(byte[]! buffer) - { - shadow = (byte[])buffer; - length = buffer.Length; - capacity = buffer.Length; - backup = null; - } - - private bool EnsureCapacity(int value) - requires (value >= 0); - { - if (value > capacity) { - int newCapacity = value; - if (newCapacity < DefaultCapacity) { - newCapacity = DefaultCapacity; - } - if (newCapacity < capacity * 2) { - newCapacity = capacity * 2; - } - Capacity = newCapacity; - return true; - } - return false; - } - - internal void Flush() - { - if (master != shadow) { - if (shadow != null) { - //DumpMaster(); - //DumpShadow(); - if (master.Length < length) { - master = new byte[capacity]; - } - Buffer.BlockCopy(shadow, 0, master, 0, length); - backup = shadow; - } - else { - Array.Clear(master, 0, length); - } - } - shadow = master; - } - - internal void Undo() - { - shadow = master; - backup = null; - } - - internal int Capacity - { - get { return capacity; } - set - { - if (value != capacity) { - if (value < length) { - throw new ArgumentOutOfRangeException( - "value", "ArgumentOutOfRange_SmallCapacity"); - } - if (value > 0) { - byte[] newBuffer = new byte[value]; - if (length > 0) { - Buffer.BlockCopy(shadow, 0, newBuffer, 0, length); - } - shadow = newBuffer; - } - else { - shadow = null; - } - capacity = value; - } - } - } - - internal int Length - { - get { return length; } - } - - private void Dump(byte[]! buffer) - { - /* - for (int i = 0; i < buffer.Length; i += 20) { - for (int j = 0; j < 20 && i + j < buffer.Length; j++) { - DebugStub.Print("{0:X2} ", __arglist(buffer[i + j])); - } - DebugStub.WriteLine(); - } - */ - for (int i = 0; i < buffer.Length && i < 20; i++) { - DebugStub.Print("{0:X2} ", __arglist(buffer[i])); - } - DebugStub.WriteLine(); - } - - internal void DumpMaster() - { - DebugStub.Print("-- Dump Master\n"); - Dump(master); - } - - internal void DumpShadow() - { - DebugStub.Print("-- Dump Shadow\n"); - Dump(shadow); - } - } -} diff --git a/base/Libraries/Security/AssemblyInfo.sg b/base/Libraries/Security/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Libraries/Security/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Libraries/Security/ChannelPool.sg b/base/Libraries/Security/ChannelPool.sg index 5133286..d354740 100644 --- a/base/Libraries/Security/ChannelPool.sg +++ b/base/Libraries/Security/ChannelPool.sg @@ -1,7 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // +// ---------------------------------------------------------------------------- namespace Microsoft.Singularity.Security { @@ -60,8 +61,7 @@ namespace Microsoft.Singularity.Security // try to bind ns.SendBind(Bitter.FromString2(SecurityDiagnosticsContract.ModuleName), exp); - switch receive - { + switch receive { case ns.AckBind(): break; case ns.NakBind(rejected, error): @@ -81,8 +81,7 @@ namespace Microsoft.Singularity.Security /// public SecurityDiagnosticsContract.Imp! Acquire() { - lock (token) - { + lock (token) { // wait if no channel is available while (true) { if (navail != 0) { @@ -108,8 +107,7 @@ namespace Microsoft.Singularity.Security /// public void Release([Claims]SecurityDiagnosticsContract.Imp! imp) { - lock (token) - { + lock (token) { ((!)trefs[navail++]).Release(imp); Monitor.PulseAll(token); } @@ -120,11 +118,10 @@ namespace Microsoft.Singularity.Security /// public void Dispose() { - lock (token) - { - if (!disposed){ + lock (token) { + if (!disposed) { int i; - for (i=0; i + diff --git a/base/Libraries/Security/SecurityDiagnostics.sg b/base/Libraries/Security/SecurityDiagnostics.sg index 8912a6c..4ba4d68 100644 --- a/base/Libraries/Security/SecurityDiagnostics.sg +++ b/base/Libraries/Security/SecurityDiagnostics.sg @@ -1,7 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // +// ---------------------------------------------------------------------------- namespace Microsoft.Singularity.Security { @@ -28,19 +29,16 @@ namespace Microsoft.Singularity.Security { string result = String.Empty; SecurityDiagnosticsContract.Imp! imp = pool.Acquire(); - try - { + try { imp.SendGetStatistics(); - switch receive - { + switch receive { case imp.GetStatisticsAck(stats): result = Bitter.ToString2(stats); delete stats; break; } } - finally - { + finally { pool.Release(imp); } return result; @@ -49,17 +47,14 @@ namespace Microsoft.Singularity.Security public void ClearStatistics() { SecurityDiagnosticsContract.Imp! imp = pool.Acquire(); - try - { + try { imp.SendClearStatistics(); - switch receive - { + switch receive { case imp.Ack(): break; } } - finally - { + finally { pool.Release(imp); } } @@ -67,17 +62,14 @@ namespace Microsoft.Singularity.Security public void Disable(bool yes) { SecurityDiagnosticsContract.Imp! imp = pool.Acquire(); - try - { + try { imp.SendDisable(yes); - switch receive - { + switch receive { case imp.Ack(): break; } } - finally - { + finally { pool.Release(imp); } } @@ -85,17 +77,14 @@ namespace Microsoft.Singularity.Security public void FlushCaches() { SecurityDiagnosticsContract.Imp! imp = pool.Acquire(); - try - { + try { imp.SendFlushCaches(); - switch receive - { + switch receive { case imp.Ack(): break; } } - finally - { + finally { pool.Release(imp); } } diff --git a/base/Libraries/Services/IRunnable.sg b/base/Libraries/Services/IRunnable.sg deleted file mode 100644 index 98b74e4..0000000 --- a/base/Libraries/Services/IRunnable.sg +++ /dev/null @@ -1,21 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Services\Tests\Calculator\IRunnable.sg -// -// Note: -// -using Microsoft.SingSharp; -using Microsoft.Singularity.ServiceManager; - -namespace Microsoft.Singularity.Services -{ - public interface IRunnable - { - void Run(); - void Signal([Claims]ThreadTerminationContract.Exp:Start! ep); - } -} diff --git a/base/Libraries/Services/ManagementExec.sg b/base/Libraries/Services/ManagementExec.sg deleted file mode 100644 index 23b777b..0000000 --- a/base/Libraries/Services/ManagementExec.sg +++ /dev/null @@ -1,122 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Libraries\Services\ManagementExec.sg -// -// Note: Managed service provider base template -// -using System; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Services; - -namespace Microsoft.Singularity.Services -{ - public class ManagementExec : IRunnable - { - protected readonly IRunnable! service; - protected readonly TRef endpoint; - protected TRef signal; - - public ManagementExec([Claims]ManagedServiceContract.Exp:Start! ep, - IRunnable! service) - { - ep.SendSuccess(); - endpoint = new TRef(ep); - this.service = service; - } - - public void Signal([Claims]ThreadTerminationContract.Exp:Start! ep) - { - delete ep; - } - - public void Run() - { - Thread serviceThread = null; - ManagedServiceContract.Exp:Ready! ep; - ThreadTerminationContract.Imp:Start! sig; - ThreadTerminationContract.Imp! imp; - ThreadTerminationContract.Exp! exp; - - ep = endpoint.Acquire(); - for (;;) { - // HI: can't fully hoist ep and sig together because there - // can be a situation where sig is closed but ep waits for - // messages from Service Manager. - switch receive { - case ep.StartService(): - { - ThreadTerminationContract.NewChannel(out imp, out exp); - signal = new TRef(imp); - service.Signal(exp); - serviceThread = new Thread(new ThreadStart(service.Run)); - serviceThread.Start(); - ep.SendAckStartService(); - break; - } - case ep.StopService(): - { - if (signal != null && serviceThread != null) { - sig = signal.Acquire(); - try { - sig.SendStop(); - switch receive { - case sig.AckStop(): - break; - case sig.ChannelClosed(): - break; - } - } - catch (Exception) { - DebugStub.Print("ManagementExec: " + - "already closed\n"); - } - signal.Release(sig); - serviceThread.Join(); - } - ep.SendAckStopService(); - break; - } - case ep.RestartService(): - { - ep.SendAckRestartService(); - break; - } - case ep.Knock(): - { - ep.SendAlive(); - break; - } - case ep.Restart(): - { - ep.SendAckRestart(); - break; - } - case ep.Stop(): - { - ep.SendAckStop(); - goto exit; - break; - } - case ep.ChannelClosed(): - { - DebugStub.Print("ManagementExec: Bug? " + - "Manager channel closed.\n"); - goto exit; - break; - } - } - } -exit: - delete ep; - return; - } - } // class -} diff --git a/base/Libraries/Services/ServiceExec.sg b/base/Libraries/Services/ServiceExec.sg deleted file mode 100644 index 3acdce9..0000000 --- a/base/Libraries/Services/ServiceExec.sg +++ /dev/null @@ -1,113 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Services\Tests\Calculator\ServiceExec.sg -// -// Note: A generic worker-style server implementation -// -using System; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Services; - -namespace Microsoft.Singularity.Services -{ - // To inherit this class, what you need to do is: - // 1) To call the base constructor from your sub class. - // 2) To implement Listen method. - public abstract class ServiceExec : IRunnable - { - protected readonly string! path; - protected TRef directoryRef; - protected TRef signalRef; - - /// - /// Waits for a client request. An implementation of this method is - /// strongly recommended to handle the signal from the parent thread for - /// terminating the entire process. Otherwise, the child thread leaks. - /// - protected abstract bool Listen(ServiceProviderContract.Exp:Start! provider, - ThreadTerminationContract.Exp:Start! signal); - - public ServiceExec([Claims]DirectoryServiceContract.Imp:Ready! ep, - string! path) - { - this.path = path; - this.directoryRef = new TRef(ep); - } - - public void Signal([Claims]ThreadTerminationContract.Exp:Start! ep) - { - this.signalRef = new TRef(ep); - } - - public virtual void Run() - { - ServiceProviderContract.Exp! provider; - ThreadTerminationContract.Exp:Start! signal; - - if (signalRef == null) { - throw new Exception("Signal is empty."); - } - - if (!RegisterName(out provider)) { - delete provider; - return; - } - - signal = signalRef.Acquire(); - while (Listen(provider, signal)); - signalRef.Release(signal); - - DeregisterName(provider); - } - - protected virtual bool RegisterName(out ServiceProviderContract.Exp! ep) - { - bool success = false; - DirectoryServiceContract.Imp:Ready! ds; - ServiceProviderContract.Imp! imp; - ServiceProviderContract.Exp! exp; - - ds = directoryRef.Acquire(); - ServiceProviderContract.NewChannel(out imp, out exp); - ds.SendRegister(Bitter.FromString2(path), imp); - switch receive { - case ds.AckRegister(): - success = true; - break; - case ds.NakRegister(rejected, error): - delete rejected; - success = false; - break; - case ds.ChannelClosed(): - success = false; - break; - } - directoryRef.Release(ds); - ep = exp; - return success; - } - - protected virtual void DeregisterName([Claims]ServiceProviderContract.Exp! ep) - { - DirectoryServiceContract.Imp:Ready! ds; - ds = directoryRef.Acquire(); - ds.SendDeregister(Bitter.FromString2(path)); - switch receive { - case ds.AckDeregister(imp): - delete imp; - delete ep; - break; - } - directoryRef.Release(ds); - } - } // class -} diff --git a/base/Libraries/Services/Services.Utils.csproj b/base/Libraries/Services/Services.Utils.csproj deleted file mode 100644 index 33637ed..0000000 --- a/base/Libraries/Services/Services.Utils.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - Library - Services.Utils - true - - - - - - - - - - - - - - - - diff --git a/base/Libraries/Services/WorkerServiceExec.sg b/base/Libraries/Services/WorkerServiceExec.sg deleted file mode 100644 index 23095d0..0000000 --- a/base/Libraries/Services/WorkerServiceExec.sg +++ /dev/null @@ -1,145 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Services\Tests\Calculator\ServiceExec.sg -// -// Note: A generic worker-style server implementation -// -using System; -using System.Collections; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.ServiceManager; - -namespace Microsoft.Singularity.Services -{ - // To inherit this class, what you need to do is: - // 1) To call the base constructor in your constructor. - // 2) To implement Accept and NewWorker methods. This class - // automatically creates and assigns threads to Run method of your - // worker object. - public abstract class WorkerServiceExec : ServiceExec - { - protected IList serviceSignalList; - - public WorkerServiceExec([Claims]DirectoryServiceContract.Imp:Ready! ep, - string! path) - { - base(ep, path); - serviceSignalList = new ArrayList(); - } - - /// - /// Checks if the request channel type is acceptable. - /// - protected abstract bool Accept(ServiceContract.Exp:Start! ep); - - /// - /// Create a new worker object for the specified channel. The worker - /// created by this method is assigned to a thread by this class. - /// - protected abstract IRunnable NewWorker([Claims]ServiceContract.Exp:Start! ep); - - protected override bool Listen(ServiceProviderContract.Exp:Start! provider, - ThreadTerminationContract.Exp:Start! signal) - { - bool result = true; - Thread worker; - - switch receive { - case provider.Connect(serviceEp): - { - if (Accept(serviceEp)) { - worker = CreateWorker(serviceEp); - if (worker == null) { - provider.SendNackConnect(null); - } - else { - provider.SendAckConnect(); - worker.Start(); - } - } - else { - provider.SendNackConnect(serviceEp); - } - break; - } - case provider.ChannelClosed(): - { - TerminateWorkers(); - result = false; - break; - } - case signal.Stop(): - { - TerminateWorkers(); - signal.SendAckStop(); - result = false; - break; - } - case signal.ChannelClosed(): - { - TerminateWorkers(); - result = false; - break; - } - } - return result; - } - - protected Thread CreateWorker([Claims]ServiceContract.Exp:Start! ep) - { - IRunnable worker; - CPTestContract.Exp:Start! server; - ThreadTerminationContract.Imp! imp; - ThreadTerminationContract.Exp! exp; - - worker = NewWorker(ep); - if (worker == null) { - return null; - } - - ThreadTerminationContract.NewChannel(out imp, out exp); - worker.Signal(exp); - lock (this) { - serviceSignalList.Add(new TRef(imp)); - } - return new Thread(new ThreadStart(worker.Run)); - } - - private void TerminateWorkers() - { - TRef signalRef; - ThreadTerminationContract.Imp:Start signal; - - lock (this) { - foreach (Object obj in serviceSignalList) - { - if (obj != null) { - signalRef = obj - as TRef; - if (signalRef != null) { - signal = signalRef.Acquire(); - signal.SendStop(); - switch receive { - case signal.AckStop(): - break; - case signal.ChannelClosed(): - break; - } - signalRef.Release(signal); - } - } - } - serviceSignalList.Clear(); - } - } - - } -} diff --git a/base/Libraries/Singularity.V1/Processes/ProcessHandle.cs b/base/Libraries/Singularity.V1/Processes/ProcessHandle.cs index 988409f..f6c3eb4 100644 --- a/base/Libraries/Singularity.V1/Processes/ProcessHandle.cs +++ b/base/Libraries/Singularity.V1/Processes/ProcessHandle.cs @@ -23,6 +23,7 @@ namespace Microsoft.Singularity.V1.Processes Stopped, } + [AccessedByRuntime("referenced from halforgc.asm")] public struct ProcessHandle { public readonly UIntPtr id; @@ -84,7 +85,6 @@ namespace Microsoft.Singularity.V1.Processes int roleLength, ProcessHandle * handle); - [OutsideGCDomain] [NoHeapAllocation] [StackBound(192)] diff --git a/base/Libraries/Singularity.V1/Security/PrincipalHandle.cs b/base/Libraries/Singularity.V1/Security/PrincipalHandle.cs index 5b14df0..4ff5344 100644 --- a/base/Libraries/Singularity.V1/Security/PrincipalHandle.cs +++ b/base/Libraries/Singularity.V1/Security/PrincipalHandle.cs @@ -18,8 +18,8 @@ namespace Microsoft.Singularity.V1.Security { public readonly long val; - private const int DefaultPrincipalSize = 40; - private const int DefaultAclIndirectSize = 200; + private const int DefaultPrincipalSize = 200; + private const int DefaultAclIndirectSize = 512; [OutsideGCDomain] [NoHeapAllocation] diff --git a/base/Libraries/Singularity.V1/Services/ChannelService.cs b/base/Libraries/Singularity.V1/Services/ChannelService.cs index 9adf42f..4544017 100644 --- a/base/Libraries/Singularity.V1/Services/ChannelService.cs +++ b/base/Libraries/Singularity.V1/Services/ChannelService.cs @@ -4,7 +4,7 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: ChannelService.csi +// File: ChannelService.cs // // Note: // @@ -24,19 +24,32 @@ namespace Microsoft.Singularity.V1.Services [CLSCompliant(false)] unsafe public struct EndpointCore { -#if !PAGING - /// - /// Flag indicating that the endpoint is closed. - /// This gets set either by an explicit close, or when - /// the kernel determines that the endpoint is not reachable. - /// NOTE: A channel only goes away entirely, once both ends are closed! - /// - private volatile bool closed; + //////////////////////////////////////////////////////////////////// + // Fields + //////////////////////////////////////////////////////////////////// + // + // NOTE: The fields specified here must match those in: + // Kernel/Singularity/Channels/EndpointCore.cs + // Kernel/Singularity/V1/Services/ChannelServices.cs + // Libraries/Singuarity.V1/Services/ChannelServices.cs /// - /// The endpoint to which this endpoint is connected. + /// Handle to the actual message delivery mechanism /// - private Allocation* /*EndpointCore* opt(ExHeap)*/ peer; + private DeliveryHandle deliveryHandle; + + /// + /// Event handle in case this endpoint is part of a collection + /// + private AutoResetEventHandle collectionEvent; + + // + // These "cached" fields are directly accessable by user programs, + // but are not trusted by the kernel (as they could be modified by untrusted + // code). The kernel relies on the trusted shadow copies held in the + // deliveryImpl object, but updates these fields to reflect any changes to user + // apps. + // /// /// Event on which sends are signaled to this endpoint. @@ -44,37 +57,42 @@ namespace Microsoft.Singularity.V1.Services /// The kernel deallocates the handle when the channel is deallocated. /// NOTE: stays valid until the entire channel gets collected. /// - private AutoResetEventHandle messageEvent; + private AutoResetEventHandle cachedMessageEvent; /// - /// Event handle in case this endpoint is part of a collection + /// Closed flag /// - private AutoResetEventHandle collectionEvent; + private bool cachedClosed; /// /// Contains the process id of the process currently owning this end of the /// channel. /// - private int ownerProcessId; - - /// - /// Contains the security principal that is the current owner of this end of the - /// channel. - /// - private PrincipalHandle ownerPrincipalHandle; - - /// - /// Contains the number of sends to this endpoint. - /// - private int receiveCount; + private int cachedOwnerProcessId; /// /// Contains the channelId (positive on the EXP endpoint, negative on the imp endpoint) /// - private int channelId; -#else - public readonly UIntPtr id; // handle id -#endif //PAGING + private int cachedChannelId; + + /// + /// Whether to marshall or not + /// + private bool cachedMarshall; + + /// + /// Points to the peer endpoint + /// + private Allocation* /*EndpointCore* opt(ExHeap)*/ cachedPeer; + + /// + /// If true then the peer state can be queried directly from cachedPeer + /// + private bool peerStateValid; + + //////////////////////////////////////////////////////////////////// + // Methods + //////////////////////////////////////////////////////////////////// /// /// Used to allocate a channel endpoint. The size must be correctly computed by @@ -84,7 +102,7 @@ namespace Microsoft.Singularity.V1.Services [NoHeapAllocation] [StackBound(1024)] [MethodImpl(MethodImplOptions.InternalCall)] - public static extern Allocation* /*EndpointCore* opt(ExHeap)!*/ + public static extern Allocation* //EndpointCore* opt(ExHeap)! Allocate(uint size, SystemType st); /// @@ -113,97 +131,91 @@ namespace Microsoft.Singularity.V1.Services /// Performs the initialization of the core part of each endpoint and cross links /// them to form a channel. /// + public static void Connect( + Allocation* /*EndpointCore* opt(ExHeap)!*/ imp, + Allocation* /*EndpointCore* opt(ExHeap)!*/ exp) + { + Connect(imp, exp, null); + } + [OutsideGCDomain] [NoHeapAllocation] [StackBound(1024)] [MethodImpl(MethodImplOptions.InternalCall)] public static extern void Connect( Allocation* /*EndpointCore* opt(ExHeap)!*/ imp, - Allocation* /*EndpointCore* opt(ExHeap)!*/ exp); + Allocation* /*EndpointCore* opt(ExHeap)!*/ exp, + Allocation* /*EndpointCore* opt(ExHeap) */ ep); /// /// Indicates if this endpoint is closed /// -#if !PAGING [NoHeapAllocation] public static bool Closed(ref EndpointCore ep) { - return ep.closed; + return ep.cachedClosed; } [NoHeapAllocation] public static bool PeerClosed(ref EndpointCore ep) { - EndpointCore * peer = (EndpointCore*)SharedHeapService.GetData(ep.peer); - return peer->closed; + if (ep.cachedPeer != null) { + EndpointCore * peer = (EndpointCore*)SharedHeapService.GetData(ep.cachedPeer); + return peer->cachedClosed; + } else { + return PeerClosedABI(ref ep); + } } -#else - [OutsideGCDomain] - [NoHeapAllocation] - [StackBound(1024)] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern bool Closed(ref EndpointCore ep); [OutsideGCDomain] [NoHeapAllocation] [StackBound(1024)] [MethodImpl(MethodImplOptions.InternalCall)] - public static extern bool PeerClosed(ref EndpointCore ep); -#endif //PAGING + public static extern bool PeerClosedABI(ref EndpointCore ep); /// /// Set this end to closed /// -#if !PAGING - [NoHeapAllocation] - public static void Close(ref EndpointCore ep) - { - ep.closed = true; - } -#else [OutsideGCDomain] [NoHeapAllocation] [StackBound(1024)] [MethodImpl(MethodImplOptions.InternalCall)] public static extern void Close(ref EndpointCore ep); -#endif //PAGING /// - /// The endpoint to which this endpoint is connected. + /// The endpoint to which this endpoint is connected. (non ABI call version) /// -#if !PAGING [NoHeapAllocation] public static Allocation* /*EndpointCore* opt(ExHeap) */ GetPeer(ref EndpointCore ep, out bool marshall) { - marshall = false; - return ep.peer; + if (ep.cachedPeer != null) { + marshall = ep.cachedMarshall; + return ep.cachedPeer; + } else { + return GetPeerABI(ref ep, out marshall); + } } -#else + + /// + /// The endpoint to which this endpoint is connected. (ABI version if cached peer is + /// not valid) + /// [OutsideGCDomain] [NoHeapAllocation] [StackBound(1174)] [MethodImpl(MethodImplOptions.InternalCall)] - public static extern Allocation* GetPeer(ref EndpointCore ep, - out bool marshall); -#endif //PAGING + public static extern Allocation* GetPeerABI(ref EndpointCore ep, + out bool marshall); /// /// The event to wait for messages on this endpoint. Used by Select. /// -#if !PAGING [NoHeapAllocation] public static SyncHandle GetWaitHandle(ref EndpointCore ep) { - return ep.messageEvent; + return ep.cachedMessageEvent; } -#else - [OutsideGCDomain] - [NoHeapAllocation] - [StackBound(1024)] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern SyncHandle GetWaitHandle(ref EndpointCore ep); -#endif //PAGING /// /// Notify the owner of this endpoint that a message is ready. @@ -219,18 +231,10 @@ namespace Microsoft.Singularity.V1.Services /// /// Wait for a message to arrive on this endpoint. /// -#if !PAGING [NoHeapAllocation] public static void Wait(ref EndpointCore ep) { - SyncHandle.WaitOne(ep.messageEvent); + SyncHandle.WaitOne(ep.cachedMessageEvent); } -#else - [OutsideGCDomain] - [NoHeapAllocation] - [StackBound(1024)] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void Wait(ref EndpointCore ep); -#endif //PAGING /// /// Transfer the given Allocation block to the target endpoint @@ -239,12 +243,7 @@ namespace Microsoft.Singularity.V1.Services [NoHeapAllocation] [StackBound(128)] [MethodImpl(MethodImplOptions.InternalCall)] -#if !PAGING public static extern void TransferBlockOwnership(Allocation* ptr, ref EndpointCore target); -#else - // TODO: change "ref EndpointCore" to "EndpointCore" - public static extern void TransferBlockOwnership(Allocation* ptr, ref EndpointCore target); -#endif //PAGING /// /// Transfer any contents that needs to be adjusted from the transferee to the target @@ -254,90 +253,65 @@ namespace Microsoft.Singularity.V1.Services [NoHeapAllocation] [StackBound(896)] [MethodImpl(MethodImplOptions.InternalCall)] -#if !PAGING public static extern void TransferContentOwnership( ref EndpointCore transferee, ref EndpointCore target); -#else - // TODO: change "ref EndpointCore" to "EndpointCore" - public static extern void TransferContentOwnership( - ref EndpointCore transferee, - ref EndpointCore target); -#endif //PAGING -#if !PAGING [NoHeapAllocation] public static int GetOwnerProcessID(ref EndpointCore ep) { - return ep.ownerProcessId; + return ep.cachedOwnerProcessId; } -#else - [OutsideGCDomain] - [NoHeapAllocation] - [StackBound(1024)] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern int GetOwnerProcessID(ref EndpointCore ep); -#endif //PAGING -#if !PAGING [NoHeapAllocation] public static int GetChannelID(ref EndpointCore ep) { - return ep.channelId; + return ep.cachedChannelId; } -#else - [OutsideGCDomain] - [NoHeapAllocation] - [StackBound(1024)] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern int GetChannelID(ref EndpointCore ep); -#endif //PAGING -#if !PAGING [NoHeapAllocation] public static int GetPeerProcessID(ref EndpointCore ep) { - EndpointCore * peer = (EndpointCore*)SharedHeapService.GetData(ep.peer); - return peer->ownerProcessId; + if (ep.peerStateValid) { + EndpointCore * peer = (EndpointCore*)SharedHeapService.GetData(ep.cachedPeer); + return peer->cachedOwnerProcessId; + } else { + return GetPeerProcessIDABI(ref ep); + } } -#else + [OutsideGCDomain] [NoHeapAllocation] [StackBound(1024)] [MethodImpl(MethodImplOptions.InternalCall)] - public static extern int GetPeerProcessID(ref EndpointCore ep); -#endif //PAGING + public static extern int GetPeerProcessIDABI(ref EndpointCore ep); -#if !PAGING + [OutsideGCDomain] [NoHeapAllocation] - public static PrincipalHandle GetOwnerPrincipalHandle(ref EndpointCore ep) - { - return ep.ownerPrincipalHandle; - } -#else + [StackBound(1024)] + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void AcceptDelegation(Allocation* /*EndpointCore* opt(ExHeap)!*/ imp, + Allocation* /*EndpointCore* opt(ExHeap)!*/ exp, + Allocation* /*EndpointCore* opt(ExHeap)!*/ ep); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(1024)] + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void EnableDelegation(ref EndpointCore ep, bool allowMediation); + [OutsideGCDomain] [NoHeapAllocation] [StackBound(1024)] [MethodImpl(MethodImplOptions.InternalCall)] public static extern PrincipalHandle GetOwnerPrincipalHandle(ref EndpointCore ep); -#endif //PAGING -#if !PAGING - [NoHeapAllocation] - public static PrincipalHandle GetPeerPrincipalHandle(ref EndpointCore ep) - { - EndpointCore * peer = (EndpointCore*)SharedHeapService.GetData(ep.peer); - return peer->ownerPrincipalHandle; - } -#else [OutsideGCDomain] [NoHeapAllocation] [StackBound(1024)] [MethodImpl(MethodImplOptions.InternalCall)] public static extern PrincipalHandle GetPeerPrincipalHandle(ref EndpointCore ep); -#endif //PAGING -#if !PAGING /// /// Instruct the selectable object to signal events on the given AutoResetEvent /// rather than its normal event in order to aggregate signalling into a set. @@ -346,7 +320,6 @@ namespace Microsoft.Singularity.V1.Services /// public static void LinkIntoCollection(ref EndpointCore ep, AutoResetEventHandle ev) { - // Debug.Assert(this.collectionEvent.id == UIntPtr.Zero); ep.collectionEvent = ev; } @@ -356,35 +329,9 @@ namespace Microsoft.Singularity.V1.Services /// public static void UnlinkFromCollection(ref EndpointCore ep, AutoResetEventHandle ev) { - // Debug.Assert(this.collectionEvent.id != UIntPtr.Zero); ep.collectionEvent = new AutoResetEventHandle(); } -#else - /// - /// Instruct the selectable object to signal events on the given AutoResetEvent - /// rather than its normal event in order to aggregate signalling into a set. - /// A selectable object need only support being part of a single collection at - /// any point in time. - /// - [OutsideGCDomain] - [NoHeapAllocation] - [StackBound(1024)] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void LinkIntoCollection(ref EndpointCore ep, - AutoResetEventHandle ev); - - /// - /// Instruct the selectable object to stop signalling events on the given - /// AutoResetEvent. - /// - [OutsideGCDomain] - [NoHeapAllocation] - [StackBound(1024)] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void UnlinkFromCollection(ref EndpointCore ep, - AutoResetEventHandle ev); - /// /// Called when sending a message across domains. Instructs the kernel /// to prepare an update record to push into the peer when the peer @@ -393,23 +340,22 @@ namespace Microsoft.Singularity.V1.Services /// This call starts a sequence of MarshallPointer calls that will /// end with a call to NotifyPeer. /// + [OutsideGCDomain] [NoHeapAllocation] [StackBound(1024)] [MethodImpl(MethodImplOptions.InternalCall)] unsafe public static extern void MarshallMessage(ref EndpointCore ep, - byte* basep, byte* source, - int* tagAddress, int size); + byte* basep, + byte* source, + int* tagAddress, + int size); [OutsideGCDomain] [NoHeapAllocation] [StackBound(1024)] [MethodImpl(MethodImplOptions.InternalCall)] - unsafe public static extern void MarshallPointer(ref EndpointCore ep, - byte* basep, byte** target, SystemType type); - -#endif //PAGING - + unsafe public static extern void MarshallPointer(ref EndpointCore ep, byte* basep, byte** target, SystemType type, byte* parent, int offset); } } diff --git a/base/Libraries/Singularity.V1/Services/DebugService.cs b/base/Libraries/Singularity.V1/Services/DebugService.cs index 45622d5..e2e44f0 100644 --- a/base/Libraries/Singularity.V1/Services/DebugService.cs +++ b/base/Libraries/Singularity.V1/Services/DebugService.cs @@ -13,6 +13,7 @@ using System.Runtime.CompilerServices; namespace Microsoft.Singularity.V1.Services { + [AccessedByRuntime("referenced from hal.cpp")] public struct DebugService { //private readonly int id; diff --git a/base/Libraries/Singularity.V1/Services/DeliveryHandle.cs b/base/Libraries/Singularity.V1/Services/DeliveryHandle.cs new file mode 100644 index 0000000..c4c248f --- /dev/null +++ b/base/Libraries/Singularity.V1/Services/DeliveryHandle.cs @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity - Singularity ABI +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: DeliveryHandle.cs +// +// Note: +// + +using System; +using System.Runtime.CompilerServices; + +namespace Microsoft.Singularity.V1.Services +{ + public struct DeliveryHandle + { + public readonly UIntPtr id; + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(192)] + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void Dispose(DeliveryHandle handle); + } +} diff --git a/base/Libraries/Singularity.V1/Services/DeviceService.cs b/base/Libraries/Singularity.V1/Services/DeviceService.cs index bcb4a5a..5a5d4a6 100644 --- a/base/Libraries/Singularity.V1/Services/DeviceService.cs +++ b/base/Libraries/Singularity.V1/Services/DeviceService.cs @@ -24,30 +24,11 @@ namespace Microsoft.Singularity.V1.Services public static extern unsafe uint GetPnpSignature(int index, /*[out]*/ char * output, uint maxout); - [NoHeapAllocation] - public static bool GetPciConfig( - out ushort pciAddressPort, - out ushort pciDataPort, - out ushort identifier) - { - unsafe { - fixed (ushort * pciAddressPortPtr = &pciAddressPort, - pciDataPortPtr = &pciDataPort, - identifierPtr = &identifier) { - return GetPciConfigImpl(pciAddressPortPtr, pciDataPortPtr, - identifierPtr); - } - } - } - [OutsideGCDomain] [NoHeapAllocation] [StackBound(1178)] [MethodImpl(MethodImplOptions.InternalCall)] - public static extern unsafe bool GetPciConfigImpl( - ushort * pciAddressPort, - ushort * pciDataPort, - ushort * identifier); + public static extern bool GetPciPort(out PciPortHandle handle); [OutsideGCDomain] [NoHeapAllocation] diff --git a/base/Libraries/Singularity.V1/Services/DiagnosisService.cs b/base/Libraries/Singularity.V1/Services/DiagnosisService.cs new file mode 100644 index 0000000..974bbfa --- /dev/null +++ b/base/Libraries/Singularity.V1/Services/DiagnosisService.cs @@ -0,0 +1,237 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity - Singularity ABI +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: DiagnosisService.cs +// +// Note: +// + +using System; +using System.Runtime.CompilerServices; + + +namespace Microsoft.Singularity.V1.Services +{ + public struct DiagnosisService + { + int Reserved2; + + // + // System call boundary must be unsafe to pass pointers + // to basic types across the GC domains. But this should not be + // directly accessable to application programs, but behind a + // trusted safe wrapper that validates parameters and + // enforces type safety. + // + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe int GCProfileSettings( + ulong *defaultMemorySize, + ulong *Options + ); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool RegisterEventingController( + UIntPtr controllerHandle, + UIntPtr executionContextHandle + ); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool DebugPrintLogEntry(UIntPtr controllerHandle, + UIntPtr entryHandle); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool TestKernelStorage(); + + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern UIntPtr OpenGlobalStorage(UIntPtr storageId); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void CloseGlobalStorage(UIntPtr storageHandle); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern unsafe UIntPtr LogSourceEntry(UIntPtr sourceHandle, + uint flags, + UIntPtr eventType, + byte * buffer, + int size); + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern unsafe UIntPtr LogSourceEntry(UIntPtr sourceHandle, + uint flags, + UIntPtr eventType, + byte * buffer, + int size, + int stringsCount, + void ** strings); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern UIntPtr CreateQueryView(UIntPtr storageHandle, bool forward); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void DeleteQueryView(UIntPtr storageHandle); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern unsafe UIntPtr GetNextEntry(UIntPtr queryHandle, + UIntPtr * type, + UInt32 * userOffset, + byte * buffer, + UInt16 bufferSize ); + + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern unsafe bool RegisterEvent(char * eventName, + char * eventDescription, + UIntPtr * eventHandle); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern unsafe bool RegisterEventField(UIntPtr eventHandle, + char * fieldName, + UInt16 offset, + UInt16 type); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern unsafe bool RegisterEventGenericField(UIntPtr eventHandle, + char * fieldName, + UInt16 offset, + UInt16 size, + UIntPtr fieldTypeHandle); + + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern unsafe bool RegisterEnum(char * enumName, UInt16 type, UIntPtr * eventHandle); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern unsafe bool RegisterEnumValue(UIntPtr eventHandle, + char * valueName, + UInt64 value, + byte flagChar); + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern unsafe UIntPtr WalkEventDescriptor(UIntPtr eventHandle, + UIntPtr currentField, + UInt16 * offset, + UInt16 * type, + char * bufferName, + UInt16 bufferSize ); + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern unsafe bool GetSourceInformation(UIntPtr sourceHandle, + UIntPtr * storageHandle, + UIntPtr * eventType, + UInt16 * count, + char * bufferName, + UInt16 bufferSize ); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern unsafe int QuerySourcesList(UIntPtr * buffer, int size); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern unsafe int QueryEventTypeList(UIntPtr * buffer, int size); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern unsafe bool ReadActiveSourceItem(UIntPtr sourceHandle, + int item, + UIntPtr * type, + byte * buffer, + UInt16 bufferSize ); + + // + // The signature offered to our caller is safe + // + public static int GCProfileSettingsImpl( + out ulong defaultMemorySize, + out ulong Options + ) + { + int retval; + + // This keeps unsafe blocks well contained + unsafe { + + // + // Note: Would it be more efficient to declare local stack + // variables and reference these under fixed rather + // than locking the caller supplied references which + // may point to class fields and involve more complicated + // GC interactions? + // + fixed (ulong* + pdefaultMemorySize = &defaultMemorySize, + pOptions = &Options + ) { + + retval = DiagnosisService.GCProfileSettings( + pdefaultMemorySize, + pOptions + ); + } + } + + return retval; + } + } +} diff --git a/base/Libraries/Singularity.V1/Services/MemoryInfoService.cs b/base/Libraries/Singularity.V1/Services/MemoryInfoService.cs new file mode 100644 index 0000000..092b603 --- /dev/null +++ b/base/Libraries/Singularity.V1/Services/MemoryInfoService.cs @@ -0,0 +1,98 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity - Singularity ABI +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: MemoryInfoService.cs +// +// Note: +// + +using System.Runtime.CompilerServices; + +namespace Microsoft.Singularity.V1.Services +{ + // + // Design Issue: Singularity SysCalls should be generated from a description + // since there is tricky code here to ensure proper locking + // objects and validation of parameters occur. + // + public struct MemoryInfoService + { + // + // System call boundary must be unsafe to pass pointers + // to basic types across the GC domains. But this should not be + // directly accessable to application programs, but behind a + // trusted safe wrapper that validates parameters and + // enforces type safety. + // + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(1170)] // How do we get these values? + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe int MemoryUsageInfo( + ulong *totalMemoryFree, + ulong *totalMemoryInUse, + ulong *kernelHeapInUse, + ulong *kernelStackInUse, + ulong *totalSIPHeapInUse, + ulong *totalSIPStackInUse, + ulong *kernelStackReservation, + ulong *kernelHeapReservation + ); + + // + // The signature offered to our caller is safe + // + public static int MemoryUsageInfo( + out ulong totalMemoryFree, + out ulong totalMemoryInUse, + out ulong kernelHeapInUse, + out ulong kernelStackInUse, + out ulong totalSIPHeapInUse, + out ulong totalSIPStackInUse, + out ulong kernelStackReservation, + out ulong kernelHeapReservation + ) + { + int retval; + + // This keeps unsafe blocks well contained + unsafe { + + // + // Note: Would it be more efficient to declare local stack + // variables and reference these under fixed rather + // than locking the caller supplied references which + // may point to class fields and involve more complicated + // GC interactions? + // + fixed (ulong* + ptotalMemoryFree = &totalMemoryFree, + ptotalMemoryInUse = &totalMemoryInUse, + pkernelHeapInUse = &kernelHeapInUse, + pkernelStackInUse = &kernelStackInUse, + ptotalSIPHeapInUse = &totalSIPHeapInUse, + ptotalSIPStackInUse = &totalSIPStackInUse, + pkernelStackReservation = &kernelStackReservation, + pkernelHeapReservation = &kernelHeapReservation + ) { + + retval = MemoryInfoService.MemoryUsageInfo( + ptotalMemoryFree, + ptotalMemoryInUse, + pkernelHeapInUse, + pkernelStackInUse, + ptotalSIPHeapInUse, + ptotalSIPStackInUse, + pkernelStackReservation, + pkernelHeapReservation + ); + } + } + + return retval; + } + } +} diff --git a/base/Libraries/Singularity.V1/Services/PciPortHandle.cs b/base/Libraries/Singularity.V1/Services/PciPortHandle.cs new file mode 100644 index 0000000..1e3ef0f --- /dev/null +++ b/base/Libraries/Singularity.V1/Services/PciPortHandle.cs @@ -0,0 +1,103 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity - Singularity ABI +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: PciPortHandle.cs +// +// Note: +// + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Microsoft.Singularity.V1.Services +{ + public struct PciPortHandle + { + public readonly UIntPtr id; + + public static unsafe bool Read8(PciPortHandle handle, + int offset, + out byte value) + { + unsafe { + fixed (byte *valuePtr = &value) { + return Read8Impl(handle, offset, valuePtr); + } + } + } + + public static unsafe bool Read16(PciPortHandle handle, + int offset, + out ushort value) + { + unsafe { + fixed (ushort *valuePtr = &value) { + return Read16Impl(handle, offset, valuePtr); + } + } + } + + public static unsafe bool Read32(PciPortHandle handle, + int offset, + out uint value) + { + unsafe { + fixed (uint *valuePtr = &value) { + return Read32Impl(handle, offset, valuePtr); + } + } + } + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(1174)] + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern unsafe bool Read8Impl(PciPortHandle h, + int offset, + byte* value); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(1174)] + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern unsafe bool Read16Impl(PciPortHandle h, + int offset, + ushort* value); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(1174)] + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern unsafe bool Read32Impl(PciPortHandle h, + int offset, + uint* value); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(1174)] + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern bool Write8(PciPortHandle h, + int offset, + byte value); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(1174)] + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern bool Write16(PciPortHandle h, + int offset, + ushort value); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(1174)] + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern bool Write32(PciPortHandle h, + int offset, + uint value); + } +} diff --git a/base/Libraries/Singularity.V1/Services/PlatformService.cs b/base/Libraries/Singularity.V1/Services/PlatformService.cs new file mode 100644 index 0000000..872de08 --- /dev/null +++ b/base/Libraries/Singularity.V1/Services/PlatformService.cs @@ -0,0 +1,67 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity - Singularity ABI +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: DebugService.cs +// +// Note: +// + +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.Singularity; + +namespace Microsoft.Singularity.V1.Services +{ + [AccessedByRuntime("Method called from CommonHal.cpp")] + public struct PlatformService + { + [NoHeapAllocation] + [StackBound(128)] + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern bool DisableInterrupts(); + + [NoHeapAllocation] + [StackBound(128)] + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void RestoreInterrupts(bool enabled); + + [NoHeapAllocation] + [StackBound(128)] + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern bool InterruptsDisabled(); + + [NoHeapAllocation] + [StackBound(128)] + [MethodImpl(MethodImplOptions.InternalCall)] + [CLSCompliant(false)] + public static extern void CleanAndInvalidateDCache(UIntPtr address, UIntPtr length); + + [NoHeapAllocation] + [StackBound(128)] + [MethodImpl(MethodImplOptions.InternalCall)] + [CLSCompliant(false)] + public static extern void InvalidateDCache(UIntPtr address, UIntPtr length); + + [NoHeapAllocation] + [StackBound(128)] + [MethodImpl(MethodImplOptions.InternalCall)] + [CLSCompliant(false)] + public static extern void SetCacheAttributes(UIntPtr address, UIntPtr length, bool cacheable, bool bufferable); + + [NoHeapAllocation] + [StackBound(32)] + [MethodImpl(MethodImplOptions.InternalCall)] + [AccessedByRuntime("Called from native kernel code")] + public static extern int GetProcessorContextOffset(); + + [NoHeapAllocation] + [StackBound(32)] + [MethodImpl(MethodImplOptions.InternalCall)] + [AccessedByRuntime("Called from native kernel code")] + public static extern int GetThreadContextOffset(); + } +} diff --git a/base/Libraries/Singularity.V1/Services/ProcessService.cs b/base/Libraries/Singularity.V1/Services/ProcessService.cs index dd3159d..cf72417 100644 --- a/base/Libraries/Singularity.V1/Services/ProcessService.cs +++ b/base/Libraries/Singularity.V1/Services/ProcessService.cs @@ -17,13 +17,12 @@ using Microsoft.Singularity.V1.Security; [assembly: AssemblyTitle("Microsoft.Singularity.V1 ABI")] [assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] [assembly: AssemblyCompany("Microsoft Corporation")] -[assembly: AssemblyVersion("1.0.0.1")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] +[assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.Singularity.V1.Services { - public struct LogEntry - { - } public enum ParameterCode { Success, @@ -33,6 +32,7 @@ namespace Microsoft.Singularity.V1.Services Undefined, } + [AccessedByRuntime("referenced from c++")] public struct ProcessService { //private readonly int id; @@ -43,6 +43,18 @@ namespace Microsoft.Singularity.V1.Services [MethodImpl(MethodImplOptions.InternalCall)] public static extern void Stop(int exitCode); + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern int GetCpuCount(); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern int GetRunningProcessorCount(); + [OutsideGCDomain] [NoHeapAllocation] [StackBound(960)] @@ -103,13 +115,13 @@ namespace Microsoft.Singularity.V1.Services [MethodImpl(MethodImplOptions.InternalCall)] public static extern ushort GetCurrentProcessId(); - /* - [OutsideGCDomain] - [NoHeapAllocation] - [StackBound(128)] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern PrincipalHandle GetCurrentPrincipal(); - */ + // + //[OutsideGCDomain] + //[NoHeapAllocation] + //[StackBound(128)] + //[MethodImpl(MethodImplOptions.InternalCall)] + //public static extern PrincipalHandle GetCurrentPrincipal(); + // [OutsideGCDomain] [NoHeapAllocation] @@ -164,50 +176,24 @@ namespace Microsoft.Singularity.V1.Services public static extern long GetThreadsCreatedCount(); - [NoHeapAllocation] - [AccessedByRuntime("referenced from Tracing.cpp")] - public static unsafe void GetTracingHeaders(out LogEntry *logBegin, - out LogEntry *logLimit, - out LogEntry **logHead, - out byte *txtBegin, - out byte *txtLimit, - out byte **txtHead) - { - fixed (LogEntry **logBeginPtr = &logBegin, - logLimitPtr = &logLimit) { - fixed (LogEntry ***logHeadPtr = &logHead) { - fixed (byte **txtBeginPtr = &txtBegin, - txtLimitPtr = &txtLimit) { - fixed (byte ***txtHeadPtr = &txtHead) { - GetTracingHeadersImpl(logBeginPtr, logLimitPtr, - logHeadPtr, txtBeginPtr, txtLimitPtr, - txtHeadPtr); - } - } - } - } - } - - [OutsideGCDomain] - [NoHeapAllocation] - [StackBound(1186)] - [AccessedByRuntime("referenced from c++")] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern unsafe void GetTracingHeadersImpl( - LogEntry **logBegin, - LogEntry **logLimit, - LogEntry ***logHead, - byte **txtBegin, - byte **txtLimit, - byte ***txtHead); - [NoHeapAllocation] [AccessedByRuntime("referenced from Monitoring.cpp")] - public static unsafe void GetMonitoringHeaders(out byte * buffer) + public static unsafe bool GetSharedSourceHandles(uint infoId, + out UIntPtr storageHandle, + out UIntPtr sourceHandle, + out UIntPtr eventTypeHandle) { unsafe { - fixed (byte * * bufferPtr = &buffer) { - GetMonitoringHeadersImpl(bufferPtr); + fixed (UIntPtr * storageHandlePtr = &storageHandle) { + fixed (UIntPtr * sourceHandlePtr = &sourceHandle) { + fixed (UIntPtr * eventTypeHandlePtr = &eventTypeHandle) { + + return GetSharedSourceHandlesImpl(infoId, + storageHandlePtr, + sourceHandlePtr, + eventTypeHandlePtr); + } + } } } } @@ -217,8 +203,10 @@ namespace Microsoft.Singularity.V1.Services [StackBound(1166)] [AccessedByRuntime("referenced from c++")] [MethodImpl(MethodImplOptions.InternalCall)] - public static extern unsafe void GetMonitoringHeadersImpl( - byte * * buffer); + public static extern unsafe bool GetSharedSourceHandlesImpl(uint infoId, + UIntPtr * storageHandle, + UIntPtr * sourceHandle, + UIntPtr * eventTypeHandle); [OutsideGCDomain] [NoHeapAllocation] @@ -355,55 +343,5 @@ namespace Microsoft.Singularity.V1.Services int *arrayLength, int *totalCharCount ); - - - // haryadi -- ping pong interface to app - [OutsideGCDomain] - [NoHeapAllocation] - [StackBound(128)] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern int RunPingPongInt(int start); - - [OutsideGCDomain] - [NoHeapAllocation] - [StackBound(128)] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern int HelloProcessABI(int num, int num2); - - [OutsideGCDomain] - [NoHeapAllocation] - [StackBound(128)] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern unsafe ulong TestAbiCallOne(ulong a); - - [OutsideGCDomain] - [NoHeapAllocation] - [StackBound(128)] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern unsafe int TestAbiCallTwo(uint a, char *b); - - [OutsideGCDomain] - [NoHeapAllocation] - [StackBound(128)] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern unsafe char* TestAbiCallThree(int a, int *b, byte c); - - - public static unsafe void TestAbiCallAll() - { - ulong a1= 33; - ulong r1 = ProcessService.TestAbiCallOne(a1); - - char x1 = 'a'; - char *b2 = &x1; - uint a2 = 10; - int r2 = ProcessService.TestAbiCallTwo(a2, b2); - - int a3 = 44; - int *b3 = & a3; - byte c3 = 1; - char *r3 = ProcessService.TestAbiCallThree(a3, b3, c3); - } - } } diff --git a/base/Libraries/Singularity.V1/Services/SharedHeapService.cs b/base/Libraries/Singularity.V1/Services/SharedHeapService.cs index 0e6b0b9..6d58068 100644 --- a/base/Libraries/Singularity.V1/Services/SharedHeapService.cs +++ b/base/Libraries/Singularity.V1/Services/SharedHeapService.cs @@ -70,6 +70,14 @@ namespace Microsoft.Singularity.V1.Services return allocation->type; } + [Inline] + [NoHeapAllocation] + public static unsafe void SetOwnerProcessId(Allocation *allocation, + int processId) + { + allocation->owner = processId; + } + #else // ROUTE_INDIRECTION_THROUGH_KERNEL public struct Allocation { @@ -94,6 +102,13 @@ namespace Microsoft.Singularity.V1.Services [MethodImpl(MethodImplOptions.InternalCall)] public static extern unsafe UIntPtr GetType(Allocation *allocation); + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern unsafe UIntPtr SetOwnerProcessId(Allocation *allocation, + int processId); + #endif // ROUTE_INDIRECTION_THROUGH_KERNEL [OutsideGCDomain] diff --git a/base/Libraries/Singularity.V1/Services/StackService.cs b/base/Libraries/Singularity.V1/Services/StackService.cs index ace2cbf..47861f5 100644 --- a/base/Libraries/Singularity.V1/Services/StackService.cs +++ b/base/Libraries/Singularity.V1/Services/StackService.cs @@ -9,17 +9,17 @@ // Note: // +using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Microsoft.Singularity.V1.Services { + [AccessedByRuntime("called from halstack.asm")] public struct StackService { [OutsideGCDomain] - [NoHeapAllocation] [StackBound(1088)] - [MethodImpl(MethodImplOptions.InternalCall)] public static extern void WalkStack(); [NoHeapAllocation] @@ -35,11 +35,64 @@ namespace Microsoft.Singularity.V1.Services } [OutsideGCDomain] - [NoHeapAllocation] [StackBound(1170)] - [MethodImpl(MethodImplOptions.InternalCall)] public static extern unsafe void GetUsageStatisticsImpl( ulong * gets, ulong * returns); - } + + [NoHeapAllocation] + public static void StackOverflow() + { + unsafe { + StackOverflowImpl(); + } + } + + [NoHeapAllocation] + [AccessedByRuntime("called from halstack.asm")] + [NoStackLinkCheckTrans] + public static UIntPtr AllocateStackSegment(UIntPtr growSize) + { + UIntPtr stack = AllocateStackSegmentAbi(growSize); + if (stack == 0) { + + // We end up here if the SIP stack allocation failed, so we must + // invoke a kernel handler that will exit the current SIP. + + StackOverflowImpl(); // will not return. + } + + return stack; + } + + [NoHeapAllocation] + [AccessedByRuntime("called from halstack.asm")] + [NoStackLinkCheckTrans] + [NoStackOverflowCheck] + public static void FreeStackSegment() + { + FreeStackSegmentAbi(); + } + + // It is important that the following three ABI calls do not do the + // usual GC boundary checks. The methods are called in situations + // where there is very little room on the current stack segment. + // The code to perform a GC or collaborate with a GC cycle is + // likely to cause stack growth that exceeds the available space. + + [OutsideGCDomain] + [GCAnnotation(GCOption.NOGC)] + [StackBound(1170)] + public static extern UIntPtr AllocateStackSegmentAbi(UIntPtr growSize); + + [OutsideGCDomain] + [GCAnnotation(GCOption.NOGC)] + [StackBound(1170)] + public static extern void FreeStackSegmentAbi(); + + [OutsideGCDomain] + [GCAnnotation(GCOption.NOGC)] + [StackBound(1170)] + public static extern void StackOverflowImpl(); + } } diff --git a/base/Libraries/Singularity.V1/Singularity.V1.csproj b/base/Libraries/Singularity.V1/Singularity.V1.csproj deleted file mode 100644 index ef73d53..0000000 --- a/base/Libraries/Singularity.V1/Singularity.V1.csproj +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - Singularity.V1 - Library - 2 - true - true - true - C# - $(LIBSDIR) - - - - $(DefineConstants); - $(DefineConstants)CE_PC - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/base/Libraries/Singularity.V1/Threads/InterruptHandle.cs b/base/Libraries/Singularity.V1/Threads/InterruptHandle.cs index 5b880c3..e45b9d7 100644 --- a/base/Libraries/Singularity.V1/Threads/InterruptHandle.cs +++ b/base/Libraries/Singularity.V1/Threads/InterruptHandle.cs @@ -11,6 +11,7 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace Microsoft.Singularity.V1.Threads { diff --git a/base/Libraries/Singularity.V1/Threads/SyncHandle.cs b/base/Libraries/Singularity.V1/Threads/SyncHandle.cs index 97b27d9..c25ed70 100644 --- a/base/Libraries/Singularity.V1/Threads/SyncHandle.cs +++ b/base/Libraries/Singularity.V1/Threads/SyncHandle.cs @@ -11,6 +11,7 @@ using System; using System.Runtime.CompilerServices; +using System.Threading; namespace Microsoft.Singularity.V1.Threads { diff --git a/base/Libraries/Singularity.V1/Threads/ThreadHandle.cs b/base/Libraries/Singularity.V1/Threads/ThreadHandle.cs index cdaa636..70fdb1c 100644 --- a/base/Libraries/Singularity.V1/Threads/ThreadHandle.cs +++ b/base/Libraries/Singularity.V1/Threads/ThreadHandle.cs @@ -54,6 +54,18 @@ namespace Microsoft.Singularity.V1.Threads [MethodImpl(MethodImplOptions.InternalCall)] public static extern void Start(ThreadHandle thread); + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern int GetAffinity(ThreadHandle thread); + + [OutsideGCDomain] + [NoHeapAllocation] + [StackBound(128)] + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void SetAffinity(ThreadHandle thread, int val); + [OutsideGCDomain] [NoHeapAllocation] [StackBound(128)] diff --git a/base/Libraries/Singularity.V1/Threads/ThreadState.cs b/base/Libraries/Singularity.V1/Threads/ThreadState.cs index 307f93f..75d4a25 100644 --- a/base/Libraries/Singularity.V1/Threads/ThreadState.cs +++ b/base/Libraries/Singularity.V1/Threads/ThreadState.cs @@ -17,10 +17,12 @@ namespace Microsoft.Singularity.V1.Threads [Flags] public enum ThreadState { - Unstarted = 0x00, - Running = 0x01, - Blocked = 0x02, - Suspended = 0x04, - Stopped = 0x08, + Undefined = 0x0, + Running = 0x1, + Unstarted = 0x2, + Stopped = 0x4, + Suspended = 0x8, + Blocked = 0x10, + Runnable = 0x20 } } diff --git a/base/Libraries/System.Graphics/Png.cs b/base/Libraries/System.Graphics/Png.cs new file mode 100644 index 0000000..8362c2d --- /dev/null +++ b/base/Libraries/System.Graphics/Png.cs @@ -0,0 +1,1223 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: S3Trio64.cs +// +// Note: +// + +using Microsoft.Singularity; + +using System; +using System; +using System.Text; +using System.Configuration.Assemblies; +using System.Runtime.Remoting; +using System.Runtime.InteropServices; +using System.Collections; + +namespace Microsoft.Singularity.Drivers +{ + public void BitBltPng(int x, int y, byte[] buffer) + { + if (!Ready) { + return; + } + + //Png.Png.DisplayPngImage(x,y,buffer,lineBuffer); + Png obj=new Png(); + obj.chunk_list= new Hashtable(); + ArrayList idat_list = new ArrayList(); + CHUNK chunk; + obj.buffer = buffer; + obj.ptr=0; + if (!obj.chksignature()) { + throw new Exception("Invalid Signature"); + } + + // step 1 : chk signature + // look for header + while (obj.ptr < obj.buffer.Length) { + chunk = new CHUNK(); + chunk.init_CHUNK(obj.buffer, ref obj.ptr,out obj.tp); + switch (obj.tp) { + case TYPE.IHDR: + cIHDR ihdr = new cIHDR(chunk,x,y); + obj.scanlength = ihdr.scanlength; + obj.bpp= ihdr.bpp; + // do some error checking + + if (ihdr.width == 0 || ihdr.height == 0) { + return; + } + + if (x < 0) { + ihdr.x = (int)(ActiveMode.ScreenWidth + x - ihdr.width); + } + if (y < 0) { + ihdr.y = (int)(ActiveMode.ScreenHeight + y - ihdr.height); + } + + if (ihdr.x < 0 || ihdr.x + ihdr.width > ActiveMode.ScreenWidth || + ihdr.y < 0 || ihdr.y + ihdr.height > ActiveMode.ScreenHeight) + { + + throw new OverflowException("Draw bounds invalid."); + } + obj.chunk_list.Add("IHDR",ihdr); + break; + case TYPE.bKGD:// define later + break; + case TYPE.cHRM: + break; + case TYPE.gAMA: + cgAMA gama = new cgAMA(chunk); + obj.chunk_list.Add("gAMA",gama); + break; + case TYPE.hIST: + break; + case TYPE.iCCP: + break; + case TYPE.IDAT:cIDAT idat = new cIDAT(chunk); + idat_list.Add(idat); + if (!obj.chunk_list.ContainsKey("IDAT")) { + obj.chunk_list.Add("IDAT",idat_list); + } +// DebugStub.Print("Chkpoint 1"); +// try +// { +// obj.chunk_list.Add("IDAT",idat_list); +// DebugStub.Print("Chkpoint 1"); +// } +// catch(System.ArgumentException) +// {// do nothing +// DebugStub.Break(); +// } + break; + case TYPE.IEND: + cIEND iend= new cIEND(chunk); + obj.chunk_list.Add("IEND",iend); + break; + case TYPE.iTXt: + break; + case TYPE.meTa: + break; + case TYPE.pHYs: + break; + case TYPE.PLTE: + break; + case TYPE.sBIT: + break; + case TYPE.sPLT: + break; + case TYPE.sRGB: + break; + case TYPE.tEXt: + break; + case TYPE.tIME: + break; + case TYPE.tRNS: + break; + case TYPE.zTXt: + break; + default:throw new Exception("Invalid chunk type"); + + } + } + try { + obj.buffer=((cIDAT)((ArrayList)obj.chunk_list["IDAT"])[0]).decompressandfilter(obj.scanlength,obj.bpp,idat_list); + } + //decompress it + //defilter it + + catch (Exception e) { + DebugStub.Print("message from exception {0} {1}.\n", + __arglist(e.Message, e));// to be removed later + DebugStub.Print(" IDAT CHUNK MISSING"); + throw new Exception(" IDAT CHUNK MISSING or Corrupt"); + } + try { + byte[] correctedbuffer; // buffer after gamma correction + //add later ((cgAMA)obj.chunk_list[TYPE.gAMA]).gammacorrection(obj.buffer,out correctedbuffer); + // add later obj.buffer=correctedbuffer; + //gamma correction + } + catch (Exception) { + // Do nothing as ancilliary chunk + } +// DebugStub.Break(); +// for (long i=0;i>1); + else + c=c>>1; + } + crc_table[i]=(uint)c; + } + crc_table_computed=true; + } + + private static uint calculateCRC(uint crc,byte[] buf,uint start_index,uint end_index) // calculate CRC + { + if (!crc_table_computed) + make_crc_table(); + for (uint i = start_index; i < end_index; i++) { + crc = crc_table[(crc^buf[i]) & 0xff]^(crc>>8); + } + return crc; + } + public static uint getCRC(byte[] buf,uint start_index,uint end_index) // return CRC + { + return (calculateCRC(0xffffffff,buf,start_index, end_index)^0xffffffff); + } + } + + class CHUNK:CRC + { + protected uint length; + protected uint type; + protected Buffer data; + protected uint crc; + protected CHUNK_TYPE chunk_type; + public CHUNK(){} + public CHUNK(CHUNK obj) + { + length= obj.length; + type = obj.type; + data = obj.data; + crc = obj.crc; + chunk_type= obj.chunk_type; + } + public void init_CHUNK(byte[] buffer, ref uint ptr,out TYPE t)// throws an exception if chunk invalid + { + + uint startindex=ptr; + uint calculatedCRC; + try { + length = (256*256*256*(uint)buffer[ptr++]+256*256*(uint)buffer[ptr++] + +256*(uint)buffer[ptr++]+(uint)buffer[ptr++]); + calculatedCRC = getCRC(buffer, ptr, ptr + 4 + length); + type = (uint)buffer[ptr++]*256*256*256+(uint)buffer[ptr++]*256*256+((uint)buffer[ptr++]*256)+(uint)buffer[ptr++]; + t=(TYPE)type; + chunk_type = ((type & 0x20000000)==0)?CHUNK_TYPE.CRITICAL:CHUNK_TYPE.ANCILLARY; + // if bit 5 of first bye of type is 1 then its ancillary otherwise essential + if (buffer.Length<(startindex+length))throw new Exception("invalid CHUNK"); + data = new Buffer(buffer,(int)ptr,(int)length); + ptr += length; + //data = new byte[length]; + //for (int i = 0; i < length; i++) + // data[i]=buffer[ptr++]; + crc=(256*256*256*(uint)buffer[ptr++]+256*256*(uint)buffer[ptr++] + +256*(uint)buffer[ptr++]+(uint)buffer[ptr++]); + } + catch (Exception e) // any exception indicates that the chunk is invalid + { + DebugStub.Print("message from exception {0} {1}", + __arglist(e.Message, e));// to be removed later + throw new Exception("invalid CHUNK"); + } + if (crc != calculatedCRC) { + DebugStub.Print("CRC chk failed in chunk {0}\ncalculated CRC: {1:x}\n actual CRC : {2:x}",__arglist(t,calculatedCRC,crc)); +// Console.Write("start:"); +// for (int i=0;i= scanlength)// thats is for 2nd onwards line + for (int i = 0; i <(scanlength - 1); i++) + sbuf[ptr + i]=(byte)(sbuf[ptr + i]+sbuf[ptr + i -scanlength]); + ptr +=scanlength-1; + } + private static void average(ref uint ptr) + { + if (ptr >= scanlength)// thats is for 2nd onwards line + { + for (int i = 0; i < bpp; i++) + sbuf[ptr]=(byte)(sbuf[ptr]+(sbuf[ptr-scanlength]/2)); // truncate the other part + for (int i = bpp; i <(scanlength - 1); i++) + sbuf[ptr + i]=(byte)(sbuf[ptr + i]+(sbuf[ptr + i -scanlength]+sbuf[ptr+i-bpp])/2); + } + else // first row + { + for (int i = bpp; i <(scanlength - 1); i++) + sbuf[ptr + i]=(byte)(sbuf[ptr + i]+(byte)(sbuf[ptr+i-bpp]/2)); + } + ptr +=scanlength-1; + } + private static void paeth(ref uint ptr) + { + // two cases : + // 1:first row + if (ptr < scanlength)// first row + { + for (uint i = 0; i < bpp; i++) + sbuf[ptr+i]=(byte)(sbuf[ptr+i] + paethpredictor(0,0,0)); + for (uint i = bpp; i <(scanlength - 1); i++) + sbuf[ptr+i]=(byte)(sbuf[ptr+i] + paethpredictor(sbuf[ptr+i-bpp],0,0)); + } + else // not the first row + { + for (uint i = 0; i < bpp; i++) + sbuf[ptr+i]=(byte)(sbuf[ptr+i] + paethpredictor(0x00,sbuf[ptr+i-scanlength],0x00)); + for (uint i = bpp; i <(scanlength - 1); i++) + sbuf[ptr+i]=(byte)(sbuf[ptr+i] + paethpredictor(sbuf[ptr+i-bpp],sbuf[ptr+i-scanlength],sbuf[ptr+i-scanlength-bpp])); + } + ptr +=scanlength-1; + } + private static byte paethpredictor(byte a , byte b , byte c) + { + int p = a + b -c; + int pa = ((p-a)>0)?p-a:a-p; + int pb = ((p-b)>0)?p-b:b-p; + int pc = ((p-c)>0)?p-c:c-p; + if ((pa <= pb) && (pa <= pc))return a; + else if (pb <= pc) return b; + else return c; + } + } // end of Filter class + class Display + { + private static byte[] buffer; + private static ushort[] linebuffer; // reference to the linebuffer of Singularity namespace + private static COLOR_TYPE ctype; + private static IoMemory screenBuffer; + private static S3_VIDEO_MODE ActiveMode; + private static cIHDR ihdr; + public static void display(byte[] buf,Hashtable ht,ushort[] lineBuffer,IoMemory sbuf, S3_VIDEO_MODE actmode) + { + buffer=buf; + screenBuffer = sbuf; + ActiveMode = actmode; + linebuffer = lineBuffer; + ihdr = (cIHDR)ht["IHDR"]; + DebugStub.Print("ihdr :{0} ", __arglist(hdr.width)); + DebugStub.Print("ihdr :{0} ", __arglist(ihdr.color_type)); + DebugStub.Print("ihdr :{0} ", __arglist(COLOR_TYPE.RGBTRIPLATE)); + switch (ihdr.color_type) { + case COLOR_TYPE.GRAY_SCALE:showGRAY_SCALE();break; + case COLOR_TYPE.RGBTRIPLATE:showRGBTRIPLATE();break; + case COLOR_TYPE.PALETTEINDEX:showPALETTEINDEX();break; + case COLOR_TYPE.GRAYSCALE_ALPHA: showGRAYSCALE_ALPHA();break; + case COLOR_TYPE.RGB_ALPHA:showRGBALPHA();break; + default : throw new Exception("Invalid color type"); + } + + } + private static void showGRAY_SCALE() + {// fill in later + showRGBALPHA(); + } + + static ushort abc(byte red,byte green,byte blue) + { + return (ushort)((((ushort)red >> 3) << 11) | + (((ushort)green >> 2) << 5) | + ((ushort)blue >> 3)); + } + + private static void showRGBTRIPLATE() + { + + int pDst = (int)(/*(ihdr.y + ihdr.height - 1) * ActiveMode.ScreenStride + */ihdr.x * ActiveMode.BytesPerPixel); + linebuffer = new ushort[ihdr.width]; + + for (int j = 0; j < ihdr.height; j++) { + for (int i = 0; i < ihdr.width; i++) { + linebuffer[i] = RGB.Compute16(buffer[i*ihdr.bpp+1+j*ihdr.scanlength],buffer[i*ihdr.bpp+2+j*ihdr.scanlength],buffer[i*ihdr.bpp+3+j*ihdr.scanlength]); + // DebugStub.Print("{0} ", + __arglist(abc(buffer[i*ihdr.bpp+1+j*ihdr.scanlength],buffer[i*ihdr.bpp+2+j*ihdr.scanlength],buffer[i*ihdr.bpp+3+j*ihdr.scanlength]))); + } + //DebugStub.Print("\n"); + screenBuffer.Write16(pDst, linebuffer, 0,(int)ihdr.width); + + pDst += ActiveMode.ScreenStride; + } + } + private static void showPALETTEINDEX() + { + + } + private static void showGRAYSCALE_ALPHA() + { + } + private static void showRGBALPHA() + { + //PictureBox pictureBox = new PictureBox(); + int pDst = (int)(/*(ihdr.y + ihdr.height - 1) * ActiveMode.ScreenStride + */ihdr.x * ActiveMode.BytesPerPixel); + + for (int j = 0; j < ihdr.height; j++) { + for (int i = 0; i < ihdr.width; i++) { + linebuffer[i] = RGB.Compute16(/*buffer[x*ihdr.bpp+4+y*ihdr.scanlength],*/buffer[i*ihdr.bpp+1+j*ihdr.scanlength],buffer[i*ihdr.bpp+2+j*ihdr.scanlength],buffer[i*ihdr.bpp+3+j*ihdr.scanlength]); + } + screenBuffer.Write16(pDst, linebuffer, 0,(int)ihdr.width); + + pDst += ActiveMode.ScreenStride; + } + + + } + } + + class Png + { + public Hashtable chunk_list; + public byte[] SIGNATURE=new byte[]{0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a}; // signature of PNG files + public byte[] buffer; + public uint ptr; + public TYPE tp; + public byte bpp; + public uint scanlength; + public bool chksignature() + { + // chk if first 9 bytes of the file matches the signature + // if yes then return true else return false + for (int i = 0; i < 8; i++) + if (buffer[ptr++]!=SIGNATURE[i]) return false; + return true; + + } + + } + + public class Block + { + public enum BTYPE:byte{NO_COMPRESSION,FIXED_HUFFMAN,DYNAMIC_HUFFMAN}; + public bool last; // is this the last block: true: yes false : no + public BTYPE Btype; + public byte[] buf; + public int count; // the length of uncompressed block + } + + public class treenode + { + public uint len; + public uint code; + public uint val; + public treenode(uint len) + { + this.len = len; + } + } + public class searchtree + { + + public Hashtable ht; + public uint MAX_BITS = 0; + public uint MIN_BITS = 0; + + public searchtree(treenode[] t,uint[] bt_count) + { + uint min = t[0].len; + uint max = t[0].len; + for (uint i = 0; i < t.Length; i++) { + if (min == 0) + min = t[i].len; + else if (t[i].len < min && t[i].len != 0) + min = t[i].len; // find minimum non zero + if (t[i].len > max) + max = t[i].len; // find maximun + } + this.MAX_BITS = max; + this.MIN_BITS = min; + this.setsearchtree(t,0,(uint)t.Length,bt_count); + } + + public searchtree (treenode[] t,uint start_index,uint end_index) + { + uint min = t[start_index].len; + uint max = t[start_index].len; + + for (uint i = start_index; i < end_index; i++) { + if (min == 0)min = t[i].len; + else if (t[i].len < min && t[i].len != 0)min = t[i].len; // find minimum non zero + if (t[i].len > max)max = t[i].len; // find maximun + } + this.MAX_BITS = max; + this.MIN_BITS = min; + uint[] bt_count = new uint[MAX_BITS+1]; + + for (uint n = start_index; n < end_index; n++) { + bt_count[t[n].len]++; // no of codes having bit count as this + + } + this.setsearchtree(t,start_index,end_index,bt_count); + } + + void setsearchtree(treenode[] t,uint start_index,uint end_index,uint[] bt_count) + { + uint code=0; + ht = new Hashtable(); + bt_count[0]=0; + + uint[] next_code= new uint[16];// no code larger than 16 bits + for (uint bits = 1; bits <= MAX_BITS; bits++) { + code =((code + bt_count[bits-1])<<1); + next_code[bits]=code; + } + for (uint n = start_index; n < end_index; n++) { + if (t[n].len != 0) { + t[n].code = next_code[t[n].len]++; + t[n].val=n-start_index; + ht.Add(t[n].code,t[n]); + // DebugStub.Print("{0} {1} {2}", __arglist(t[n].code, t[n].len, n)); + } + } + } + + public uint decode(byte[] buf, ref ulong ptr) + { + // return the appropriate symbol after searching from the treenode + // throw an exception if not found + byte bits_read= (byte)MIN_BITS; // start from reading the min no of bits + uint val = Compressed.readnbithuffmancode(buf, ref ptr, bits_read); + + while (bits_read < MAX_BITS) { + if (ht.ContainsKey(val)) { + treenode obj = (treenode)ht[val]; + if (obj.len == (uint)bits_read) { + return obj.val; + } + } + else { + val = (val << 1) | Compressed.readbit(buf, ref ptr); + bits_read++; + } + } + DebugStub.Print("Huffman code at ptr :{0}", __arglist(ptr)); + throw new Exception("Invalid Huffman Code"); + } + } + + public class Compressed + { + uint read_fixed_huffman_val(byte[] buf, ref ulong ptr) + { + uint val = readnbithuffmancode(buf, ref ptr, 7); + if (val <= 0x17) // chk for 7 bit code + return (256+val); + // else read one more bit + val = (val << 1) | readbit(buf, ref ptr); // valid for huffman code but not otherwise + if (val >= 0x30 && val <= 0xBF) // chk for 8 bit code + return (val-0x30); + if (val >= 0xc0 && val <= 0xc7) + return(280+val-0xc0); + val = (val << 1) + readbit(buf, ref ptr); + if (val >= 0x190 && val <= 0x1ff) // finally it must be a 9 bit code + return (144+val-0x190); + throw new Exception("invalid format of file"); + } + + uint read_length(byte[] buf, ref ulong ptr, uint val) + { + // since length is also written as huffman codes so same method will be valid but not valid generally + if (val >= 257) { + if (val <= 264) { // chk for 0 extra bit length + return (3 + val-257); + } + else if (val <= 268) { // chk for 1 extra bit length + return (11 + ((val-265)<<1) + readbit(buf, ref ptr)); + } + else if (val <= 272) { // chk for 2 extra bit length + return (19 + ((val-269)<<2) + readnbit(buf, ref ptr, 2)); + } + else if (val <= 276) { // chk for 3 extra bit length + return (35 + ((val-273)<<3) + readnbit(buf, ref ptr, 3)); + } + else if (val <= 280) { // chk for 4 extra bit length + return (67 + ((val-277)<<4) + readnbit(buf, ref ptr, 4)); + } + else if (val <= 284) { // chk for 5 extra bit length + return (131 + ((val-281)<<5) + readnbit(buf, ref ptr, 5)); + } + else if (val == 285) { + return 258; + } + } + throw new Exception("invalid format of file"); + } + + uint read_dist(byte[] buf, ref ulong ptr, uint val) + { + if (val <= 3) { // chk for 0 extra bit length + return (val+1); + } + else if (val <= 5) { // chk for 1 extra bit length + return (5 + ((val-4)<<1) + readbit(buf, ref ptr)); + } + else if (val <= 7) { // chk for 2 extra bit length + return (9 + ((val-6)<<2) + readnbit(buf, ref ptr, 2)); + } + else if (val <= 9) { // chk for 3 extra bit length + return (17 + ((val-8)<<3) + readnbit(buf, ref ptr,3 )); + } + else if (val <= 11) { // chk for 4 extra bit length + return (33 + ((val-10)<<4) + readnbit(buf, ref ptr, 4)); + } + else if (val <= 13) { // chk for 5 extra bit length + return (65 + ((val-12)<<5) + readnbit(buf, ref ptr, 5)); + } + else if (val <= 15) { // chk for 6 extra bit length + return (129 + ((val-14)<<6) + readnbit(buf, ref ptr, 6)); + } + else if (val <= 17) { // chk for 7 extra bit length + return (257 + ((val-16)<<7) + readnbit(buf, ref ptr, 7)); + } + else if (val <= 19) { // chk for 8 extra bit length + return (513 + ((val-18)<<8) + readnbit(buf, ref ptr, 8)); + } + else if (val <= 21) { // chk for 9 extra bit length + return (1025 + ((val-20)<<9) + readnbit(buf, ref ptr, 9)); + } + else if (val <= 23) { // chk for 10 extra bit length + return (2049 + ((val-22)<<10) + readnbit(buf, ref ptr, 10)); + } + else if (val <= 25) { // chk for 11 extra bit length + return (4097 + ((val-24)<<11) + readnbit(buf, ref ptr, 11)); + } + else if (val <= 27) { // chk for 12 extra bit length + return (8193 + ((val-26)<<12) + readnbit(buf, ref ptr, 12)); + } + else if (val <= 29) { // chk for 13 extra bit length + return (16385 + ((val-28)<<13) + readnbit(buf, ref ptr, 13)); + } + else { + DebugStub.Print(" ptr : {0} | val: {1}", __arglist(ptr, val)); + throw new Exception("invalid format of file"); + } + } + + // function to read a bit from the buffer + public static uint readbit(byte[] buf, ref ulong ptr) + { + uint b = ((((uint)buf[ptr/8]) >> (int)((ptr%8))) & (1)); + ptr++; + return b; + } + + // function to read n bits from the buffer + public static uint readnbit(byte[] buf, ref ulong ptr, byte n) + { + uint val = 0; + for (int i = 0; i < n; i++) { + val |= readbit(buf, ref ptr) << i; + } + return val; + } + + // function to read n bits from the buffer + public static uint readnbithuffmancode(byte[] buf, ref ulong ptr, byte n) + { + uint val=0; + for (; n > 0; n--) { + val = ((val << 1) | readbit(buf, ref ptr)); + } + return val; + } + + public class circularbuffer + { + const uint SIZE = 32768; + uint size;//current size + byte[] buf ; + uint ptr;// to start with + public circularbuffer() + { + ptr=0; + size=0; + buf = new byte[SIZE]; + if (buf==null)throw new Exception("IoMemory can't be allocated"); + } + public byte getindexeddata(uint relative_position) // with respect to the current ptr:behind + { + if (relative_position>size) throw new Exception("out of bounds parameter"); + uint pos= (ptr + SIZE - relative_position)%SIZE; + return buf[pos]; + } + public void writeindexeddata(byte data) // write can only take place at the current position + { + if (SIZE > size) size++; + buf[ptr]= data; + ptr = (ptr + 1)%SIZE; + } + + } + ArrayList BlkList; + + public Compressed(byte[] buf, ref ulong ptr) + { + // ptr th bit is being referred + BlkList= new ArrayList(); + Block blk; + circularbuffer circ_buf = new circularbuffer(); + uint length; + MemoryStream ms; + BinaryWriter bw; + treenode[] code_length_length; + treenode[] code_length; + + do + { + blk = new Block(); + ms = new MemoryStream(); + bw = new BinaryWriter(ms); + blk.last = (readbit(buf, ref ptr) != 0);// for MSB ptr%8=0 + uint btype = readnbit(buf, ref ptr, 2); + switch (btype)// note that readnbithuffmancode is not used + { + case 0:blk.Btype = Block.BTYPE.NO_COMPRESSION;break; + case 1:blk.Btype = Block.BTYPE.FIXED_HUFFMAN;break; + case 2:blk.Btype = Block.BTYPE.DYNAMIC_HUFFMAN;break; + default:throw new Exception("Error in block type"); + } + + switch (blk.Btype) { + case Block.BTYPE.NO_COMPRESSION: + // skip remaining bits in currently partially read byte + ptr -= ptr%8; + ptr +=8; + + uint LEN = readnbit(buf, ref ptr, 16); + // uint NLEN = readnbit(buf, ref ptr, 16); + // to be removed later + // if ((LEN + NLEN )!=0xffff) // chk if one's complement + // throw new Exception("Error in Block"); + length = 0; + blk.buf = new byte[LEN]; + + for (length = 0; length < LEN; length++) // copy length character from the buffer + { + byte readbyte = buf[ptr/8]; + ptr += 8; + bw.Write(readbyte); + circ_buf.writeindexeddata(readbyte); + } + blk.buf=ms.GetBuffer(); + break; + case Block.BTYPE.FIXED_HUFFMAN: + uint val; + + // BinaryReader br = new BinaryReader(ms); + while ((val = read_fixed_huffman_val(buf, ref ptr)) != 256) { + // loop till end character found + if (val < 256) // literal + { + + bw.Write((byte)val); + } + else // length - distance pair + { + length = read_length(buf, ref ptr,val); + val = readnbit(buf, ref ptr, 5); + uint distance = read_dist(buf, ref ptr,val); + for (uint i = 0; i < length; i++) // copy length character from the buffer + { + + byte readbyte = circ_buf.getindexeddata(distance); + bw.Write(readbyte); + circ_buf.writeindexeddata(readbyte); + + } + } + } + // transfer buffer to blk.buf + blk.buf=ms.GetBuffer(); + break; + + case Block.BTYPE.DYNAMIC_HUFFMAN: + uint chkcount =0; + int[] border = new int[] { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + { + uint HLIT = (257 + readnbit(buf, ref ptr, 5)); + uint HDIST = (1 + readnbit(buf, ref ptr, 5)); + uint HCLEN = (4 + readnbit(buf, ref ptr, 4)); + + searchtree code_length_length_tree; + // block 1 :to allocate code_length_length + int i; + code_length_length = new treenode[19]; + + uint[] bt_count = new uint[8]{0,0,0,0,0,0,0,0}; + for (i = 0; i < HCLEN; i++) { + + code_length_length[border[i]] = new treenode(readnbit(buf, ref ptr, 3)); + bt_count[code_length_length[border[i]].len ]++; + + } + for (; i < 19; i++) { + code_length_length[border[i]] = new treenode(0); + bt_count[code_length_length[border[i]].len ]++; + } + code_length_length_tree = new searchtree(code_length_length,bt_count); + // block 1 ends + + code_length = new treenode[HLIT + HDIST];// storing the huffman code length for the literals and code length + + searchtree literal_length_tree; + searchtree distance_tree; + for (i = 0; i <(HLIT + HDIST);) { + + uint bt = code_length_length_tree.decode(buf, ref ptr); + if (bt < 16) { + code_length[i] = new treenode(bt);i++; + } + else if (bt == 16) { + ushort cnt = (ushort)(readnbit(buf, ref ptr, 2) + 3); + for (; cnt > 0; cnt--,i++) + code_length[i] = new treenode(code_length[i-1].len); + } + else if (bt == 17) { + ushort cnt =(ushort)(readnbit(buf, ref ptr, 3) + 3); + for (; cnt > 0; cnt--,i++) + code_length[i] = new treenode(0); + } + else if (bt == 18) { + ushort cnt = (ushort)(readnbit(buf, ref ptr, 7) + 11); + for (; cnt > 0; cnt--,i++) + code_length[i] =new treenode(0); + } + } + //call function and make two huffman treenode + //then use these trees to form original huffman codes and hence decompress + // DebugStub.Print("length literal treenode"); + literal_length_tree = new searchtree(code_length,0,HLIT); + // DebugStub.Print("distance treenode"); + distance_tree = new searchtree(code_length,HLIT,(uint)HLIT + HDIST); + // both trees made + + //we don't need code_length array now + code_length = null; + // make dynamic treenode now + + { // block 2 + + + + val = literal_length_tree.decode(buf, ref ptr); + +// DebugStub.Break(); + while (val != 256) { + // loop till end character found + if (val < 256) // literal + { + bw.Write((byte)val); + // DebugStub.Print("{0:x},{1:x} ", __arglist(val,br.Read())); + circ_buf.writeindexeddata((byte)val); + chkcount++; + } + else // length - distance pair + { + + length = read_length(buf, ref ptr,val); + val = distance_tree.decode(buf, ref ptr); + uint distance = read_dist(buf, ref ptr,val); + for (i = 0; i < length; i++) // copy length character from the buffer + { + byte readbyte = circ_buf.getindexeddata(distance); + bw.Write(readbyte); + // DebugStub.Print("{0:x},{1:x} ",__arglist(val,br.Read()); + circ_buf.writeindexeddata(readbyte); + chkcount++; + + } + } + // DebugStub.Print("The value of chkcount : {0}",__arglist(chkcount)); + val = literal_length_tree.decode(buf, ref ptr); + } // while ends + // transfer buffer to blk.buf + blk.buf=ms.GetBuffer(); +// for (uint ij = 0;ij [Microsoft.Contracts.NotDelayed] public BinaryReader(Stream input, Encoding encoding) { - if (input==null) { + if (input == null) { throw new ArgumentNullException("input"); } - if (encoding==null) { + if (encoding == null) { throw new ArgumentNullException("encoding"); } if (!input.CanRead) @@ -105,7 +103,7 @@ namespace System.IO { //| public virtual int PeekChar() { - if (m_stream==null) __Error.FileNotOpen(); + if (m_stream == null) __Error.FileNotOpen(); if (!m_stream.CanSeek) return -1; @@ -117,7 +115,7 @@ namespace System.IO { //| public virtual int Read() { - if (m_stream==null) { + if (m_stream == null) { __Error.FileNotOpen(); } return InternalReadOneChar(); @@ -145,7 +143,7 @@ namespace System.IO { //| public virtual char ReadChar() { int value = Read(); - if (value==-1) { + if (value == -1) { __Error.EndOfFile(); } return (char)value; @@ -230,20 +228,20 @@ namespace System.IO { int readLength; int charsRead; - if (m_stream==null) + if (m_stream == null) __Error.FileNotOpen(); // Length of the string in bytes, not chars stringLength = Read7BitEncodedInt(); - if (stringLength<0) { + if (stringLength < 0) { throw new IOException(String.Format("IO.IO_InvalidStringLen_Len", stringLength)); } - if (stringLength==0) { + if (stringLength == 0) { return String.Empty; } - if (m_charBytes==null) { + if (m_charBytes == null) { m_charBytes = new byte[MaxCharBytesSize]; } @@ -257,7 +255,7 @@ namespace System.IO { readLength = ((stringLength - currPos)>MaxCharBytesSize)?MaxCharBytesSize:(stringLength - currPos); n = m_stream.Read(m_charBytes, 0, readLength); - if (n==0) { + if (n == 0) { __Error.EndOfFile(); } @@ -271,14 +269,14 @@ namespace System.IO { sb.Append(m_charBuffer, 0, charsRead); currPos +=n; - } while (currPos public virtual int Read(char[] buffer, int index, int count) { - if (buffer==null) { + if (buffer == null) { throw new ArgumentNullException("buffer", "ArgumentNull_Buffer"); } if (index < 0) { @@ -291,7 +289,7 @@ namespace System.IO { throw new ArgumentException("Argument_InvalidOffLen"); } - if (m_stream==null) + if (m_stream == null) __Error.FileNotOpen(); return InternalReadChars(buffer, index, count); @@ -302,11 +300,11 @@ namespace System.IO { int numBytes = 0; int charsRemaining = count; - if (m_charBytes==null) { + if (m_charBytes == null) { m_charBytes = new byte[MaxCharBytesSize]; } - while (charsRemaining>0) { + while (charsRemaining > 0) { // We really want to know what the minimum number of bytes per char // is for our encoding. Otherwise for UnicodeEncoding we'd have to // do ~1+log(n) reads to read n characters. @@ -318,7 +316,7 @@ namespace System.IO { numBytes = m_stream.Read(m_charBytes, 0, numBytes); - if (numBytes==0) { + if (numBytes == 0) { // Console.WriteLine("Found no bytes. We're outta here."); return (count - charsRemaining); } @@ -335,14 +333,14 @@ namespace System.IO { // I know having a separate InternalReadOneChar method seems a little // redundant, but this makes a scenario like the security parser code // 20% faster, in addition to the optimizations for UnicodeEncoding I - // put in InternalReadChars. -- BrianGru, 5/8/2001 + // put in InternalReadChars. int charsRead = 0; int numBytes = 0; - if (m_charBytes==null) { + if (m_charBytes == null) { m_charBytes = new byte[MaxCharBytesSize]; } - if (m_singleChar==null) { + if (m_singleChar == null) { m_singleChar = new char[1]; } @@ -364,7 +362,7 @@ namespace System.IO { numBytes = 1; } - if (numBytes==0) { + if (numBytes == 0) { // Console.WriteLine("Found no bytes. We're outta here."); return -1; } @@ -382,15 +380,15 @@ namespace System.IO { //| public virtual char[] ReadChars(int count) { - if (m_stream==null) { + if (m_stream == null) { __Error.FileNotOpen(); } - if (count<0) { + if (count < 0) { throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_NeedNonNegNum"); } char[] chars = new char[count]; int n = InternalReadChars(chars, 0, count); - if (n!=count) { + if (n != count) { char[] copy = new char[n]; Buffer.BlockCopy(chars, 0, copy, 0, 2*n); // sizeof(char) chars = copy; @@ -401,7 +399,7 @@ namespace System.IO { //| public virtual int Read(byte[] buffer, int index, int count) { - if (buffer==null) + if (buffer == null) throw new ArgumentNullException("buffer", "ArgumentNull_Buffer"); if (index < 0) throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_NeedNonNegNum"); @@ -410,14 +408,14 @@ namespace System.IO { if (buffer.Length - index < count) throw new ArgumentException("Argument_InvalidOffLen"); - if (m_stream==null) __Error.FileNotOpen(); + if (m_stream == null) __Error.FileNotOpen(); return m_stream.Read(buffer, index, count); } //| public virtual byte[] ReadBytes(int count) { if (count < 0) throw new ArgumentOutOfRangeException("count", "ArgumentOutOfRange_NeedNonNegNum"); - if (m_stream==null) __Error.FileNotOpen(); + if (m_stream == null) __Error.FileNotOpen(); byte[] result = new byte[count]; @@ -446,14 +444,14 @@ namespace System.IO { int bytesRead=0; int n = 0; - if (m_stream==null) __Error.FileNotOpen(); + if (m_stream == null) __Error.FileNotOpen(); // @TODO: Find a good threshold for calling ReadByte() repeatedly // vs. calling Read(byte[], int, int) for both buffered & unbuffered // streams. - if (numBytes==1) { + if (numBytes == 1) { n = m_stream.ReadByte(); - if (n==-1) + if (n == -1) __Error.EndOfFile(); m_buffer[0] = (byte)n; return; @@ -461,11 +459,11 @@ namespace System.IO { do { n = m_stream.Read(m_buffer, bytesRead, numBytes-bytesRead); - if (n==0) { + if (n == 0) { __Error.EndOfFile(); } bytesRead+=n; - } while (bytesRead public virtual Stream BaseStream { get { @@ -339,7 +336,7 @@ namespace System.IO { //| public virtual void Write(String value) { - if (value==null) + if (value == null) throw new ArgumentNullException("value"); int len = _encoding.GetByteCount(value); diff --git a/base/Libraries/System.IO/BufferedStream.cs b/base/Libraries/System.IO/BufferedStream.cs index a9d7728..d0d0083 100644 --- a/base/Libraries/System.IO/BufferedStream.cs +++ b/base/Libraries/System.IO/BufferedStream.cs @@ -3,23 +3,20 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: BufferedStream -** -** Original design by Anders Hejlsberg (AndersH) -** -** Purpose: A composable Stream that buffers reads & writes -** to the underlying stream. -** -** Date: February 19, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: BufferedStream +// +// Purpose: A composable Stream that buffers reads & writes +// to the underlying stream. +// +//=========================================================== using System; using System.Diagnostics; using System.Runtime.InteropServices; -namespace System.IO { +namespace System.IO +{ // Implementation notes: // This is a somewhat complex but efficient implementation. The biggest // design goal here is to prevent the buffer from getting in the way and slowing @@ -31,12 +28,12 @@ namespace System.IO { // assumption here is you will almost always be doing a series of reads or // writes, but rarely alternate between the two of them on the same stream. // - // This code is based on Anders' original implementation of his adaptive + // This code is based on the original implementation of the adaptive // buffering code. I had some ideas to make the pathological case better // here, but the pathological cases for the new code would have avoided // memcpy's by introducing more disk writes (which are 3 orders of magnitude // slower). I've made some optimizations, fixed several bugs, and - // tried documenting this code. -- BrianGru, 5/30/2000 + // tried documenting this code. // // Possible future perf optimization: // When we have more time to look at buffering perf, consider the following @@ -55,7 +52,7 @@ namespace System.IO { // avoid a call to memcpy. But for cases when we have less data, it might be // better to copy any spilled over data into our internal buffer. Consider // implementing this and looking at perf results on many random sized writes. - // Also, this should apply to Read trivially. -- BrianGru, 5/30/2000 + // Also, this should apply to Read trivially. // // Class Invariants: // The class has one buffer, shared for reading & writing. It can only be @@ -69,7 +66,6 @@ namespace System.IO { // Either _writePos can be greater than 0, or _readLen & _readPos can be // greater than zero, but neither can be greater than zero at the same time. // - // -- Brian Grunkemeyer, 3/11/2002 //| public sealed class BufferedStream : Stream { private Stream _s; // Underlying stream. Close sets _s to null. @@ -95,7 +91,7 @@ namespace System.IO { [Microsoft.Contracts.NotDelayed] public BufferedStream(Stream stream, int bufferSize) { - if (stream==null) + if (stream == null) throw new ArgumentNullException("stream"); if (bufferSize <= 0) throw new ArgumentOutOfRangeException("bufferSize", String.Format("ArgumentOutOfRange_MustBePositive", "bufferSize")); @@ -127,7 +123,7 @@ namespace System.IO { //| public override long Length { get { - if (_s==null) __Error.StreamIsClosed(); + if (_s == null) __Error.StreamIsClosed(); if (_writePos > 0) FlushWrite(); return _s.Length; } @@ -136,14 +132,14 @@ namespace System.IO { //| public override long Position { get { - if (_s==null) __Error.StreamIsClosed(); + if (_s == null) __Error.StreamIsClosed(); if (!_s.CanSeek) __Error.SeekNotSupported(); // return _s.Seek(0, SeekOrigin.Current) + (_readPos + _writePos - _readLen); return _s.Position + (_readPos - _readLen + _writePos); } set { if (value < 0) throw new ArgumentOutOfRangeException("value", "ArgumentOutOfRange_NeedNonNegNum"); - if (_s==null) __Error.StreamIsClosed(); + if (_s == null) __Error.StreamIsClosed(); if (!_s.CanSeek) __Error.SeekNotSupported(); if (_writePos > 0) FlushWrite(); _readPos = 0; @@ -164,7 +160,7 @@ namespace System.IO { //| public override void Flush() { - if (_s==null) __Error.StreamIsClosed(); + if (_s == null) __Error.StreamIsClosed(); if (_writePos > 0) { FlushWrite(); } @@ -197,7 +193,7 @@ namespace System.IO { //| public override int Read([In, Out] byte[] array, int offset, int count) { - if (array==null) + if (array == null) throw new ArgumentNullException("array", "ArgumentNull_Buffer"); if (offset < 0) throw new ArgumentOutOfRangeException("offset", "ArgumentOutOfRange_NeedNonNegNum"); @@ -206,7 +202,7 @@ namespace System.IO { if (array.Length - offset < count) throw new ArgumentException("Argument_InvalidOffLen"); - if (_s==null) __Error.StreamIsClosed(); + if (_s == null) __Error.StreamIsClosed(); int n = _readLen - _readPos; // if the read buffer is empty, read into either user's array or our @@ -246,8 +242,8 @@ namespace System.IO { // or -1 if reading from the end of the stream. //| public override int ReadByte() { - if (_s==null) __Error.StreamIsClosed(); - if (_readLen==0 && !_s.CanRead) __Error.ReadNotSupported(); + if (_s == null) __Error.StreamIsClosed(); + if (_readLen == 0 && !_s.CanRead) __Error.ReadNotSupported(); if (_readPos == _readLen) { if (_writePos > 0) FlushWrite(); if (_buffer == null) _buffer = new byte[_bufferSize]; @@ -318,8 +314,8 @@ namespace System.IO { //| public override void WriteByte(byte value) { - if (_s==null) __Error.StreamIsClosed(); - if (_writePos==0) { + if (_s == null) __Error.StreamIsClosed(); + if (_writePos == 0) { if (!_s.CanWrite) __Error.WriteNotSupported(); if (_readPos < _readLen) FlushRead(); @@ -327,7 +323,7 @@ namespace System.IO { _readPos = 0; _readLen = 0; } - if (_buffer==null) _buffer = new byte[_bufferSize]; + if (_buffer == null) _buffer = new byte[_bufferSize]; } if (_writePos == _bufferSize) FlushWrite(); @@ -339,7 +335,7 @@ namespace System.IO { //| public override long Seek(long offset, SeekOrigin origin) { - if (_s==null) __Error.StreamIsClosed(); + if (_s == null) __Error.StreamIsClosed(); if (!_s.CanSeek) __Error.SeekNotSupported(); // If we've got bytes in our buffer to write, write them out. // If we've read in and consumed some bytes, we'll have to adjust @@ -356,11 +352,11 @@ namespace System.IO { Debug.Assert(_readLen - _readPos >= 0, "_readLen ("+_readLen+") - _readPos ("+_readPos+") >= 0"); offset -= (_readLen - _readPos); } - /* - _readPos = 0; - _readLen = 0; - return _s.Seek(offset, origin); - */ + // + //_readPos = 0; + //_readLen = 0; + //return _s.Seek(offset, origin); + // long oldPos = _s.Position + (_readPos - _readLen); long pos = _s.Seek(offset, origin); @@ -406,7 +402,7 @@ namespace System.IO { //| public override void SetLength(long value) { if (value < 0) throw new ArgumentOutOfRangeException("value", "ArgumentOutOfRange_NegFileSize"); - if (_s==null) __Error.StreamIsClosed(); + if (_s == null) __Error.StreamIsClosed(); if (!_s.CanSeek) __Error.SeekNotSupported(); if (!_s.CanWrite) __Error.WriteNotSupported(); if (_writePos > 0) { diff --git a/base/Libraries/System.IO/Directory.cs b/base/Libraries/System.IO/Directory.cs index 3d95d2a..d262f5c 100644 --- a/base/Libraries/System.IO/Directory.cs +++ b/base/Libraries/System.IO/Directory.cs @@ -3,18 +3,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: Directory -** -** -** Purpose: Exposes routines for enumerating through a -** directory. -** -** Date: March 5, 2000 -** April 11,2000 -** -===========================================================*/ +//============================================================ +// +// Class: Directory +// +// Purpose: Exposes routines for enumerating through a +// directory. +// +//=========================================================== using System; using System.Collections; @@ -23,8 +19,10 @@ using System.Text; using System.Runtime.InteropServices; using System.Globalization; using Microsoft.Singularity.Directory; +using FileSystem.Utils; -namespace System.IO { +namespace System.IO +{ //| public sealed class Directory { private Directory() @@ -34,16 +32,16 @@ namespace System.IO { //| public static DirectoryInfo GetParent(String path) { - if (path==null) + if (path == null) throw new ArgumentNullException("path"); - if (path.Length==0) + if (path.Length == 0) throw new ArgumentException("Argument_PathEmpty", "path"); String fullPath = Path.GetFullPathInternal(path); String s = Path.GetDirectoryName(fullPath); - if (s==null) + if (s == null) return null; return new DirectoryInfo(s); } @@ -51,7 +49,7 @@ namespace System.IO { //| public static DirectoryInfo CreateDirectory(String path) { - if (path==null) + if (path == null) throw new ArgumentNullException("path"); if (path.Length == 0) throw new ArgumentException("Argument_PathEmpty"); @@ -91,10 +89,9 @@ namespace System.IO { } } - if (list.Count != 0) - { + if (list.Count != 0) { String [] securityList = (String[])list.ToArray(typeof(String)); - for (int j = 0 ; j < securityList.Length; j++) + for (int j = 0; j < securityList.Length; j++) securityList[j] += "\\."; // leafs will never has a slash at the end } @@ -110,8 +107,7 @@ namespace System.IO { bool r = true; int firstError = 0; // If all the security checks succeeded create all the directories - for (int j = 0; j < list.Count; j++) - { + for (int j = 0; j < list.Count; j++) { String name = (String!)list[j]; if (name.Length > Path.MAX_DIRECTORY_PATH) throw new PathTooLongException("IO.PathTooLong"); @@ -133,27 +129,33 @@ namespace System.IO { // contents. // //| - public static bool Exists(String path) { - try - { if (path==null) - return false; - if (path.Length==0) - return false; - // Get fully qualified file name ending in \* for security check - - String fullPath = Path.GetFullPathInternal(path); - return InternalExists(fullPath); + public static bool Exists(String! path) + { + try { + path = Path.GetFullPathInternal(path); + + if (path == null) return false; + if (path.Length == 0) return false; + + bool isDir; + int ok = DirectoryUtils.PathIsDirectory(path, out isDir); + if (ok != 0) return false; + return isDir; } - catch(ArgumentException) {} - catch(NotSupportedException) {} // To deal with the fact that security can now throw this on : - catch(IOException) {} - return false; - } + catch (ArgumentException) { + } + catch (NotSupportedException) { + + } + return false; + } + // Determine whether path describes an existing directory // on disk, avoiding security checks. - internal static bool InternalExists(String path) { + internal static bool InternalExists(String path) + { Native.FILE_ATTRIBUTE_DATA data = new Native.FILE_ATTRIBUTE_DATA(); int dataInitialised = File.FillAttributeInfo(path,ref data); if (dataInitialised != 0) @@ -173,8 +175,7 @@ namespace System.IO { { IntPtr handle = Directory.OpenHandle(path); bool r = Native.SetFileTime(handle, new long[] {creationTimeUtc.ToFileTimeUtc()}, null, null); - if (!r) - { + if (!r) { Native.CloseHandle(handle); __Error.WinIOError(1, path); } @@ -192,8 +193,7 @@ namespace System.IO { { IntPtr handle = Directory.OpenHandle(path); bool r = Native.SetFileTime(handle, null, null, new long[] {lastWriteTimeUtc.ToFileTimeUtc()}); - if (!r) - { + if (!r) { Native.CloseHandle(handle); __Error.WinIOError(1, path); } @@ -211,8 +211,7 @@ namespace System.IO { { IntPtr handle = Directory.OpenHandle(path); bool r = Native.SetFileTime(handle, null, new long[] {lastAccessTimeUtc.ToFileTimeUtc()}, null); - if (!r) - { + if (!r) { Native.CloseHandle(handle); __Error.WinIOError(1, path); } @@ -238,10 +237,10 @@ namespace System.IO { //| public static String[] GetFiles(String path,String searchPattern) { - if (path==null) + if (path == null) throw new ArgumentNullException("path"); - if (searchPattern==null) + if (searchPattern == null) throw new ArgumentNullException("searchPattern"); searchPattern = searchPattern.TrimEnd(); @@ -259,7 +258,7 @@ namespace System.IO { // Note - fileNames returned by InternalGetFiles are not fully qualified. String [] fileNames = InternalGetFiles(fullPath, path, searchPattern); - for(int i=0; i public static String[] GetDirectories(String path,String searchPattern) { - if (path==null) + if (path == null) throw new ArgumentNullException("path"); - if (searchPattern==null) + if (searchPattern == null) throw new ArgumentNullException("searchPattern"); searchPattern = searchPattern.TrimEnd(); @@ -306,7 +305,7 @@ namespace System.IO { } String [] dirNames = InternalGetDirectories(fullPath, path, searchPattern); - for(int i=0; i public static String[] GetFileSystemEntries(String path,String searchPattern) { - if (path==null) + if (path == null) throw new ArgumentNullException("path"); - if (searchPattern==null) + if (searchPattern == null) throw new ArgumentNullException("searchPattern"); searchPattern = searchPattern.TrimEnd(); @@ -358,10 +357,10 @@ namespace System.IO { String [] fileSystemEntries = new String[dirs.Length + files.Length]; int count = 0; - for (int i = 0;i public static String GetDirectoryRoot(String path) { - if (path==null) + if (path == null) throw new ArgumentNullException("path"); String fullPath = Path.GetFullPathInternal(path); @@ -484,13 +483,13 @@ namespace System.IO { return path.Substring(0, Path.GetRootLength(path)); } - /*===============================CurrentDirectory=============================== - **Action: Provides a getter and setter for the current directory. The original - ** current DirectoryInfo is the one from which the process was started. - **Returns: The current DirectoryInfo (from the getter). Void from the setter. - **Arguments: The current DirectoryInfo to which to switch to the setter. - **Exceptions: - ==============================================================================*/ + //===============================CurrentDirectory=============================== + //Action: Provides a getter and setter for the current directory. The original + // current DirectoryInfo is the one from which the process was started. + //Returns: The current DirectoryInfo (from the getter). Void from the setter. + //Arguments: The current DirectoryInfo to which to switch to the setter. + //Exceptions: + //============================================================================== //| public static String GetCurrentDirectory() { @@ -504,9 +503,9 @@ namespace System.IO { //| public static void SetCurrentDirectory(String path) { - if (path==null) + if (path == null) throw new ArgumentNullException("value"); - if (path.Length==0) + if (path.Length == 0) throw new ArgumentException("Argument_PathEmpty"); if (path.Length >= Path.MAX_PATH) throw new PathTooLongException("IO.PathTooLong"); @@ -522,27 +521,27 @@ namespace System.IO { //| public static void Move(String sourceDirName,String destDirName) { - if (sourceDirName==null) + if (sourceDirName == null) throw new ArgumentNullException("sourceDirName"); - if (sourceDirName.Length==0) + if (sourceDirName.Length == 0) throw new ArgumentException("Argument_EmptyFileName", "sourceDirName"); - if (destDirName==null) + if (destDirName == null) throw new ArgumentNullException("destDirName"); - if (destDirName.Length==0) + if (destDirName.Length == 0) throw new ArgumentException("Argument_EmptyFileName", "destDirName"); String fullsourceDirName = Path.GetFullPathInternal(sourceDirName); String fulldestDirName = Path.GetFullPathInternal(destDirName); String sourcePath; - if (fullsourceDirName.EndsWith( '\\' )) + if (fullsourceDirName.EndsWith('\\')) sourcePath = fullsourceDirName; else sourcePath = fullsourceDirName + "\\"; String destPath; - if (fulldestDirName.EndsWith( '\\' )) + if (fulldestDirName.EndsWith('\\')) destPath = fulldestDirName; else destPath = fulldestDirName + "\\"; @@ -555,8 +554,7 @@ namespace System.IO { if (CompareInfo.Compare(sourceRoot, destinationRoot, CompareOptions.IgnoreCase) != 0) throw new IOException("IO.IO_SourceDestMustHaveSameRoot"); - if (!Native.MoveFile(sourceDirName, destDirName)) - { + if (!Native.MoveFile(sourceDirName, destDirName)) { __Error.WinIOError(1,String.Empty); } } @@ -613,7 +611,7 @@ namespace System.IO { try { DeleteHelper(newFullPath, newUserPath, recursive); } - catch(Exception e) { + catch (Exception e) { if (ex == null) { ex = e; } @@ -645,7 +643,7 @@ namespace System.IO { internal static void VerifyDriveExists(String! root) { int drives = Native.GetLogicalDrives(); - if (drives==0) + if (drives == 0) __Error.WinIOError(); uint d = (uint)drives; char drive = Char.ToLower(root[0]); diff --git a/base/Libraries/System.IO/DirectoryInfo.cs b/base/Libraries/System.IO/DirectoryInfo.cs index c3fca35..a48a062 100644 --- a/base/Libraries/System.IO/DirectoryInfo.cs +++ b/base/Libraries/System.IO/DirectoryInfo.cs @@ -3,18 +3,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: DirectoryInfo -** -** -** Purpose: Exposes routines for enumerating through a -** directory. -** -** Date: March 5, 2000 -** April 11,2000 -** -===========================================================*/ +//============================================================ +// +// Class: DirectoryInfo +// +// Purpose: Exposes routines for enumerating through a +// directory. +// +//=========================================================== using System; using System.Collections; @@ -25,7 +21,8 @@ using System.Runtime.InteropServices; using System.Globalization; //@TODO: Add a static SystemDirectoryInfo property returning a URI -namespace System.IO { +namespace System.IO +{ //| public sealed class DirectoryInfo : FileSystemInfo { private DirectoryInfo() { @@ -35,7 +32,7 @@ namespace System.IO { //| public DirectoryInfo(String path) { - if (path==null) + if (path == null) throw new ArgumentNullException("path"); OriginalPath = path; @@ -79,7 +76,7 @@ namespace System.IO { if (s.Length > 3 && s.EndsWith(Path.DirectorySeparatorChar)) s = FullPath.Substring(0, FullPath.Length - 1); parentName = Path.GetDirectoryName(s); - if (parentName==null) + if (parentName == null) return null; DirectoryInfo dir = new DirectoryInfo(parentName,false); return dir; @@ -90,13 +87,13 @@ namespace System.IO { //| public DirectoryInfo CreateSubdirectory(String path) { - if (path==null) + if (path == null) throw new ArgumentNullException("path"); String newDirs = Path.InternalCombine(FullPath, path); String fullPath = Path.GetFullPathInternal(newDirs); - if (0!=String.Compare(FullPath,0,fullPath,0, FullPath.Length,true)) + if (0 != String.Compare(FullPath,0,fullPath,0, FullPath.Length,true)) throw new ArgumentException(String.Format("Argument_InvalidSubPath",path,OriginalPath)); Directory.InternalCreateDirectory(fullPath,path); @@ -118,8 +115,7 @@ namespace System.IO { public override bool Exists { get { - try - { + try { if (_dataInitialised == -1) Refresh(); if (_dataInitialised != 0) // Refresh was unable to initialise the data @@ -127,8 +123,7 @@ namespace System.IO { return _data.fileAttributes != -1 && (_data.fileAttributes & Native.FILE_ATTRIBUTE_DIRECTORY) != 0; } - catch(Exception) - { + catch (Exception) { return false; } } @@ -142,7 +137,7 @@ namespace System.IO { //| public FileInfo[] GetFiles(String searchPattern) { - if (searchPattern==null) + if (searchPattern == null) throw new ArgumentNullException("searchPattern"); searchPattern = searchPattern.TrimEnd(); @@ -162,7 +157,7 @@ namespace System.IO { for (int i = 0; i < fileNames.Length; i++) fileNames[i] = Path.InternalCombine(path, fileNames[i]); FileInfo[] files = new FileInfo[fileNames.Length]; - for(int i=0; i public FileSystemInfo[] GetFileSystemInfos(String searchPattern) { - if (searchPattern==null) + if (searchPattern == null) throw new ArgumentNullException("searchPattern"); searchPattern = searchPattern.TrimEnd(); @@ -216,19 +211,19 @@ namespace System.IO { FileSystemInfo [] fileSystemEntries = new FileSystemInfo[dirNames.Length + fileNames.Length]; String[] permissionNames = new String[dirNames.Length]; - for (int i = 0;i public DirectoryInfo[] GetDirectories(String searchPattern) { - if (searchPattern==null) + if (searchPattern == null) throw new ArgumentNullException("searchPattern"); searchPattern = searchPattern.TrimEnd(); @@ -269,12 +264,12 @@ namespace System.IO { String[] dirNames = Directory.InternalGetFileDirectoryNames(fullPath, OriginalPath, false); String[] permissionNames = new String[dirNames.Length]; - for (int i = 0; i public void MoveTo(String destDirName) { - if (destDirName==null) + if (destDirName == null) throw new ArgumentNullException("destDirName"); - if (destDirName.Length==0) + if (destDirName.Length == 0) throw new ArgumentException("Argument_EmptyFileName", "destDirName"); String fullDestDirName = Path.GetFullPathInternal(destDirName); - if (!fullDestDirName.EndsWith( '\\')) + if (!fullDestDirName.EndsWith('\\')) fullDestDirName = fullDestDirName + "\\"; String fullSourcePath; - if (FullPath.EndsWith( '\\' )) + if (FullPath.EndsWith('\\')) fullSourcePath = FullPath; else fullSourcePath = FullPath + "\\"; @@ -330,8 +325,7 @@ namespace System.IO { if (CompareInfo.Compare(sourceRoot, destinationRoot, CompareOptions.IgnoreCase) != 0) throw new IOException("IO.IO_SourceDestMustHaveSameRoot"); - if (!Native.MoveFile(FullPath, destDirName)) - { + if (!Native.MoveFile(FullPath, destDirName)) { __Error.WinIOError(Native.ERROR_PATH_NOT_FOUND, OriginalPath); } FullPath = fullDestDirName; diff --git a/base/Libraries/System.IO/DirectoryNotFoundException.cs b/base/Libraries/System.IO/DirectoryNotFoundException.cs index 8a5b0fe..52c8513 100644 --- a/base/Libraries/System.IO/DirectoryNotFoundException.cs +++ b/base/Libraries/System.IO/DirectoryNotFoundException.cs @@ -3,24 +3,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: DirectoryNotFoundException -** -** -** Purpose: Exception for accessing a path that doesn't exist. -** -** Date: February 18, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: DirectoryNotFoundException +// +// Purpose: Exception for accessing a path that doesn't exist. +// +//=========================================================== using System; -namespace System.IO { - /* - * Thrown when trying to access a file that doesn't exist on disk. - * Note this is thrown for 2 HRESULTS: The Win32 errorcode-as-HRESULT - * ERROR_PATH_NOT_FOUND (0x80070003) and STG_E_PATHNOTFOUND (0x80030003). - */ +namespace System.IO +{ + // + // Thrown when trying to access a file that doesn't exist on disk. + // Note this is thrown for 2 HRESULTS: The Win32 errorcode-as-HRESULT + // ERROR_PATH_NOT_FOUND (0x80070003) and STG_E_PATHNOTFOUND (0x80030003). + // //| public class DirectoryNotFoundException : IOException { //| diff --git a/base/Libraries/System.IO/EndOfStreamException.cs b/base/Libraries/System.IO/EndOfStreamException.cs index 6487bcd..33f27f3 100644 --- a/base/Libraries/System.IO/EndOfStreamException.cs +++ b/base/Libraries/System.IO/EndOfStreamException.cs @@ -3,20 +3,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: EndOfStreamException -** -** -** Purpose: Exception to be thrown when reading past end-of-file. -** -** Date: February 18, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: EndOfStreamException +// +// Purpose: Exception to be thrown when reading past end-of-file. +// +//=========================================================== using System; -namespace System.IO { +namespace System.IO +{ //| public class EndOfStreamException : IOException { diff --git a/base/Libraries/System.IO/File.cs b/base/Libraries/System.IO/File.cs index c393d25..6cd4afe 100644 --- a/base/Libraries/System.IO/File.cs +++ b/base/Libraries/System.IO/File.cs @@ -3,17 +3,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: File -** -** -** Purpose: A collection of methods for manipulating Files. -** -** Date: February 22, 2000 -** April 09,2000 (some design refactorization) -** -===========================================================*/ +//============================================================ +// +// Class: File +// +// Purpose: A collection of methods for manipulating Files. +// +//=========================================================== using System; using System.Runtime.InteropServices; @@ -21,7 +17,8 @@ using System.Text; using FileSystem.Utils; using Microsoft.Singularity.Directory; -namespace System.IO { +namespace System.IO +{ // Class for creating FileStream objects, and some basic file management // routines such as Delete, etc. //| @@ -84,9 +81,9 @@ namespace System.IO { /// Note: This returns the fully qualified name of the destination file. /// internal static String! InternalCopy(String sourceFileName, String destFileName, bool overwrite) { - if (sourceFileName==null || destFileName==null) + if (sourceFileName == null || destFileName == null) throw new ArgumentNullException((sourceFileName==null ? "sourceFileName" : "destFileName"), "ArgumentNull_FileName"); - if (sourceFileName.Length==0 || destFileName.Length==0) + if (sourceFileName.Length == 0 || destFileName.Length == 0) throw new ArgumentException("Argument_EmptyFileName", (sourceFileName.Length==0 ? "sourceFileName" : "destFileName")); String fullSourceFileName = Path.GetFullPathInternal(sourceFileName); @@ -139,7 +136,7 @@ namespace System.IO { // //| public static void Delete(String path) { - if (path==null) + if (path == null) throw new ArgumentNullException("path"); String fullPath = Path.GetFullPathInternal(path); @@ -148,7 +145,7 @@ namespace System.IO { if (!r) { //int err = __Error.ErrorCodeToWin32Error(error); //__Error.WinIOError(err, path); - __Error.SingularityIOError(error,path); + __Error.SingularityIOError(error,path); } } @@ -160,32 +157,40 @@ namespace System.IO { // Your application must have Read permission for the target directory. // //| - public static bool Exists(String! path) + public static bool Exists(String! path) { DirectoryServiceContract.Imp dsRoot = DirectoryService.NewClientEndpoint(); if (dsRoot == null) throw new Exception("No directory service endpoint."); bool ok = Exists(dsRoot, path); - delete dsRoot; - return ok; + delete dsRoot; + return ok; } - + public static bool Exists(DirectoryServiceContract.Imp:Ready! dsRoot, String! path) { - try - { + try { path = Path.GetFullPathInternal(path); - if (path==null) + if (path == null) return false; - if (path.Length==0) + if (path.Length == 0) return false; - return FileUtils.FileExists(dsRoot, (!)path); + bool exists = FileUtils.FileExists(dsRoot, (!)path); + +#if false // uncomment to debug file access. + Console.WriteLine("FileExists({0}) = {1}", path, exists); +#endif + return exists; + } + catch (ArgumentException) { + + } + catch (NotSupportedException) {} // To deal with the fact that security can now throw this on : + catch (IOException) { + } - catch(ArgumentException) {} - catch(NotSupportedException) {} // To deal with the fact that security can now throw this on : - catch(IOException) {} return false; } - internal static bool InternalExists(String path) { + internal static bool InternalExists(String path) { Native.FILE_ATTRIBUTE_DATA data = new Native.FILE_ATTRIBUTE_DATA(); int dataInitialised = FillAttributeInfo(path,ref data); if (dataInitialised != 0) @@ -223,8 +228,7 @@ namespace System.IO { FileStream fs = OpenFile(path, FileAccess.Write, ref handle); bool r = Native.SetFileTime(handle, new long[] {creationTimeUtc.ToFileTimeUtc()}, null, null); - if (!r) - { + if (!r) { fs.Close(); __Error.WinIOError(0, path); } @@ -254,8 +258,7 @@ namespace System.IO { FileStream fs = OpenFile(path, FileAccess.Write, ref handle); bool r = Native.SetFileTime(handle, null, new long[] {lastAccessTimeUtc.ToFileTimeUtc()}, null); - if (!r) - { + if (!r) { fs.Close(); __Error.WinIOError(0, path); } @@ -286,14 +289,19 @@ namespace System.IO { FileStream fs = OpenFile(path, FileAccess.Write, ref handle); bool r = Native.SetFileTime(handle, null, null, new long[] {lastWriteTimeUtc.ToFileTimeUtc()}); - if (!r) - { + if (!r) { fs.Close(); __Error.WinIOError(0, path); } fs.Close(); } + public static DateTime GetLastWriteTime(String! path) + { + //TODO: FIXFIX need to convert to local time some day + return GetLastWriteTimeUtc(path); + } + //| public static DateTime GetLastWriteTimeUtc(String! path) { @@ -355,9 +363,9 @@ namespace System.IO { // //| public static void Move(String sourceFileName, String destFileName) { - if (sourceFileName==null || destFileName==null) + if (sourceFileName == null || destFileName == null) throw new ArgumentNullException((sourceFileName==null ? "sourceFileName" : "destFileName"), "ArgumentNull_FileName"); - if (sourceFileName.Length==0 || destFileName.Length==0) + if (sourceFileName.Length == 0 || destFileName.Length == 0) throw new ArgumentException("Argument_EmptyFileName", (sourceFileName.Length==0 ? "sourceFileName" : "destFileName")); String fullSourceFileName = Path.GetFullPathInternal(sourceFileName); @@ -366,8 +374,7 @@ namespace System.IO { if (!InternalExists(fullSourceFileName)) __Error.WinIOError(Native.ERROR_FILE_NOT_FOUND,sourceFileName); - if (!Native.MoveFile(fullSourceFileName, fullDestFileName)) - { + if (!Native.MoveFile(fullSourceFileName, fullDestFileName)) { __Error.WinIOError(0, destFileName); } } diff --git a/base/Libraries/System.IO/FileAccess.cs b/base/Libraries/System.IO/FileAccess.cs index bb5192c..6c4b187 100644 --- a/base/Libraries/System.IO/FileAccess.cs +++ b/base/Libraries/System.IO/FileAccess.cs @@ -3,21 +3,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Enum: FileAccess -** -** -** Purpose: Enum describing whether you want read and/or write -** permission to a file. -** -** Date: February 18, 2000 -** -===========================================================*/ +//============================================================ +// +// Enum: FileAccess +// +// Purpose: Enum describing whether you want read and/or write +// permission to a file. +// +//=========================================================== using System; -namespace System.IO { +namespace System.IO +{ // Contains constants for specifying the access you want for a file. // You can have Read, Write or ReadWrite access. // diff --git a/base/Libraries/System.IO/FileAttributes.cs b/base/Libraries/System.IO/FileAttributes.cs index b0e61d5..9d9817d 100644 --- a/base/Libraries/System.IO/FileAttributes.cs +++ b/base/Libraries/System.IO/FileAttributes.cs @@ -3,19 +3,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: FileAttributes -** -** -** Purpose: File attribute flags corresponding to NT's flags. -** -** Date: February 16, 1999 -** -===========================================================*/ +//============================================================ +// +// Class: FileAttributes +// +// Purpose: File attribute flags corresponding to NT's flags. +// +//=========================================================== using System; -namespace System.IO { +namespace System.IO +{ // File attributes for use with the FileEnumerator class. // These constants correspond to the constants in WinNT.h. // diff --git a/base/Libraries/System.IO/FileInfo.cs b/base/Libraries/System.IO/FileInfo.cs index 1b547c8..e63883d 100644 --- a/base/Libraries/System.IO/FileInfo.cs +++ b/base/Libraries/System.IO/FileInfo.cs @@ -3,17 +3,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: File -** -** -** Purpose: A collection of methods for manipulating Files. -** -** Date: February 22, 2000 -** April 09,2000 (some design refactorization) -** -===========================================================*/ +//============================================================ +// +// Class: File +// +// Purpose: A collection of methods for manipulating Files. +// +//=========================================================== using System; using System.Diagnostics; @@ -21,7 +17,8 @@ using System.Runtime.InteropServices; using System.Text; using Microsoft.Singularity.Directory; -namespace System.IO { +namespace System.IO +{ // Class for creating FileStream objects, and some basic file management // routines such as Delete, etc. //| @@ -37,7 +34,7 @@ namespace System.IO { [Microsoft.Contracts.NotDelayed] public FileInfo(String fileName) { - if (fileName==null) + if (fileName == null) throw new ArgumentNullException("fileName"); OriginalPath = fileName; @@ -79,7 +76,7 @@ namespace System.IO { } } - /* Returns the name of the directory that the file is in */ + // Returns the name of the directory that the file is in //| public String DirectoryName { @@ -90,7 +87,7 @@ namespace System.IO { } } - /* Creates an instance of the the parent directory */ + // Creates an instance of the the parent directory //| public DirectoryInfo Directory { @@ -184,8 +181,7 @@ namespace System.IO { public override bool Exists { get { - try - { + try { if (_dataInitialised == -1) Refresh(); if (_dataInitialised != 0) // Refresh was unable to initialise the data @@ -193,8 +189,7 @@ namespace System.IO { return (_data.fileAttributes & Native.FILE_ATTRIBUTE_DIRECTORY) == 0; } - catch(Exception) - { + catch (Exception) { return false; } } @@ -250,9 +245,9 @@ namespace System.IO { // //| public void MoveTo(String destFileName) { - if (destFileName==null) + if (destFileName == null) throw new ArgumentNullException("destFileName"); - if (destFileName.Length==0) + if (destFileName.Length == 0) throw new ArgumentException("Argument_EmptyFileName", "destFileName"); String fullDestFileName = Path.GetFullPathInternal(destFileName); diff --git a/base/Libraries/System.IO/FileLoadException.cs b/base/Libraries/System.IO/FileLoadException.cs index 58915cd..a981da7 100644 --- a/base/Libraries/System.IO/FileLoadException.cs +++ b/base/Libraries/System.IO/FileLoadException.cs @@ -3,21 +3,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: FileLoadException -** -** -** Purpose: Exception for failure to load a file that was successfully found. -** -** Date: February 12, 2001 -** -===========================================================*/ +//============================================================ +// +// Class: FileLoadException +// +// Purpose: Exception for failure to load a file that was successfully found. +// +//=========================================================== using System; using System.Runtime.CompilerServices; -namespace System.IO { +namespace System.IO +{ //| public class FileLoadException : IOException { diff --git a/base/Libraries/System.IO/FileMode.cs b/base/Libraries/System.IO/FileMode.cs index d67278f..82dbd8c 100644 --- a/base/Libraries/System.IO/FileMode.cs +++ b/base/Libraries/System.IO/FileMode.cs @@ -3,21 +3,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Enum: FileMode -** -** -** Purpose: Enum describing whether to create a new file or -** open an existing one. -** -** Date: February 18, 2000 -** -===========================================================*/ +//============================================================ +// +// Enum: FileMode +// +// Purpose: Enum describing whether to create a new file or +// open an existing one. +// +//=========================================================== using System; -namespace System.IO { +namespace System.IO +{ // Contains constants for specifying how the OS should open a file. // These will control whether you overwrite a file, open an existing // file, or some combination thereof. diff --git a/base/Libraries/System.IO/FileNotFoundException.cs b/base/Libraries/System.IO/FileNotFoundException.cs index d1937f7..0ad5894 100644 --- a/base/Libraries/System.IO/FileNotFoundException.cs +++ b/base/Libraries/System.IO/FileNotFoundException.cs @@ -3,20 +3,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: FileNotFoundException -** -** -** Purpose: Exception for accessing a file that doesn't exist. -** -** Date: February 18, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: FileNotFoundException +// +// Purpose: Exception for accessing a file that doesn't exist. +// +//=========================================================== using System; -namespace System.IO { +namespace System.IO +{ // Thrown when trying to access a file that doesn't exist on disk. //| public class FileNotFoundException : IOException { diff --git a/base/Libraries/System.IO/FileShare.cs b/base/Libraries/System.IO/FileShare.cs index 68655a5..b6f5909 100644 --- a/base/Libraries/System.IO/FileShare.cs +++ b/base/Libraries/System.IO/FileShare.cs @@ -3,22 +3,20 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Enum: FileShare -** -** -** Purpose: Enum describing how to share files with other -** processes - ie, whether two processes can simultaneously -** read from the same file. -** -** Date: February 18, 2000 -** -===========================================================*/ +//============================================================ +// +// Enum: FileShare +// +// Purpose: Enum describing how to share files with other +// processes - ie, whether two processes can simultaneously +// read from the same file. +// +//=========================================================== using System; -namespace System.IO { +namespace System.IO +{ // Contains constants for controlling file sharing options while // opening files. You can specify what access other processes trying // to open the same file concurrently can have. diff --git a/base/Libraries/System.IO/FileStream.cs b/base/Libraries/System.IO/FileStream.cs index 3753494..13ea9e2 100644 --- a/base/Libraries/System.IO/FileStream.cs +++ b/base/Libraries/System.IO/FileStream.cs @@ -3,21 +3,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: FileStream -** -** -** Purpose: Exposes a Stream around a file, with full -** synchronous and asynchronous support, and buffering. -** -** Date: February 21, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: FileStream +// +// Purpose: Exposes a Stream around a file, with full +// synchronous and asynchronous support, and buffering. +// +//=========================================================== #define BLACKHOLE using System; -using Microsoft.Singularity; using System.Diagnostics; using System.Threading; using System.Runtime.InteropServices; @@ -29,31 +25,29 @@ using Microsoft.Singularity.Channels; using Microsoft.Singularity.FileSystem; using Microsoft.Singularity.V1.Services; using FileSystem.Utils; +// +// Implementation notes: +// +// I've added buffering into FileStream as well. I folded in the +// code from BufferedStream, so all the comments about it being mostly +// aggressive (and the possible perf improvement) apply to FileStream as +// well. +// +// Class Invariants: +// The class has one buffer, shared for reading & writing. It can only be +// used for one or the other at any point in time - not both. The following +// should be true: +// 0 <= _readPos <= _readLen < _bufferSize +// 0 <= _writePos < _bufferSize +// _readPos == _readLen && _readPos > 0 implies the read buffer is valid, +// but we're at the end of the buffer. +// _readPos == _readLen == 0 means the read buffer contains garbage. +// Either _writePos can be greater than 0, or _readLen & _readPos can be +// greater than zero, but neither can be greater than zero at the same time. +// -/* - * Implementation notes: - * - * I've added buffering into FileStream as well. I folded in the - * code from BufferedStream, so all the comments about it being mostly - * aggressive (and the possible perf improvement) apply to FileStream as - * well. - * - * Class Invariants: - * The class has one buffer, shared for reading & writing. It can only be - * used for one or the other at any point in time - not both. The following - * should be true: - * 0 <= _readPos <= _readLen < _bufferSize - * 0 <= _writePos < _bufferSize - * _readPos == _readLen && _readPos > 0 implies the read buffer is valid, - * but we're at the end of the buffer. - * _readPos == _readLen == 0 means the read buffer contains garbage. - * Either _writePos can be greater than 0, or _readLen & _readPos can be - * greater than zero, but neither can be greater than zero at the same time. - * - * -- Brian Grunkemeyer, 3/11/2002 - */ - -namespace System.IO { +namespace System.IO +{ //| public class FileStream : Stream { @@ -64,6 +58,7 @@ namespace System.IO { private bool _canRead; private bool _canWrite; private bool _canSeek; + private bool _disposed; #if BLACKHOLE private bool _isBlackHole;// Absorbs writes and gives nothing back. #endif @@ -161,16 +156,16 @@ namespace System.IO { #endif _fileName = path; - - NodeType node; + FileAttributesRecord fileAttributes; ErrorCode error; - FileUtils.GetAttributes(path, dsRoot, out _len, out node, out error); + FileUtils.GetAttributes(path, dsRoot, out fileAttributes, out error); + _len = fileAttributes.FileSize; // Build up security permissions required, as well as validate we // have a sensible set of parameters. IE, creating a brand new file // for reading doesn't make much sense. if ((access & FileAccess.Read) != 0) { - if (mode==FileMode.Append) + if (mode == FileMode.Append) throw new ArgumentException("Argument_InvalidAppendMode"); } @@ -192,6 +187,7 @@ namespace System.IO { _writePos = 0; _appendStart = -1; _isBlackHole = true; + return; } #endif @@ -203,7 +199,7 @@ namespace System.IO { } else { // No write access - if (mode==FileMode.Truncate || mode==FileMode.CreateNew || mode==FileMode.Create || mode==FileMode.Append) + if (mode == FileMode.Truncate || mode == FileMode.CreateNew || mode == FileMode.Create || mode == FileMode.Append) throw new ArgumentException(String.Format("Argument_InvalidFileMode&AccessCombo", mode, access)); } @@ -212,10 +208,18 @@ namespace System.IO { if (mode == FileMode.Append) mode = FileMode.OpenOrCreate; + ErrorCode errorCode; if (mode == FileMode.CreateNew || mode == FileMode.Create) { - if (FileUtils.CreateFile(path) != 0) { + if (File.Exists(path)) { + if (mode == FileMode.CreateNew) { + __Error.WinIOError(__Error.ERROR_FILE_EXISTS, path); + assume false; + } + File.Delete(path); + } + if (FileUtils.CreateFile(path, out errorCode) != 0) { #if false - Console.WriteLine("FileUtils.CreateFile({0}) failed", path); + Console.WriteLine("FileUtils.CreateFile({0}) failed. Error={1}", path, SdsUtils.ErrorCodeToString(errorCode)); #endif if (mode == FileMode.Create) { __Error.WinIOError(-1, _fileName); @@ -342,10 +346,18 @@ namespace System.IO { _handle = IntPtr.Zero; } #endif - _canRead = false; - _canWrite = false; - _canSeek = false; - _buffer = null; + + if (_disposed == false && _isBlackHole == false) { + // NB black has no associated imp. + FileContract.Imp fi = _imp.Acquire(); + delete fi; + + _canRead = false; + _canWrite = false; + _canSeek = false; + _buffer = null; + _disposed = true; + } } //| @@ -426,7 +438,7 @@ namespace System.IO { //| public override int Read([In, Out] byte[] array, int offset, int count) { - if (array==null) + if (array == null) throw new ArgumentNullException("array", "ArgumentNull_Buffer"); if (offset < 0) throw new ArgumentOutOfRangeException("offset", "ArgumentOutOfRange_NeedNonNegNum"); @@ -531,7 +543,7 @@ namespace System.IO { //| public override long Seek(long offset, SeekOrigin origin) { - if (originSeekOrigin.End) + if (origin < SeekOrigin.Begin || origin > SeekOrigin.End) throw new ArgumentException("Argument_InvalidSeekOrigin"); if (!CanSeek) __Error.SeekNotSupported(); @@ -630,7 +642,7 @@ namespace System.IO { //| public override void Write(byte[] array, int offset, int count) { - if (array==null) + if (array == null) throw new ArgumentNullException("array", "ArgumentNull_Buffer"); if (offset < 0) throw new ArgumentOutOfRangeException("offset", "ArgumentOutOfRange_NeedNonNegNum"); @@ -639,7 +651,7 @@ namespace System.IO { if (array.Length - offset < count) throw new ArgumentException("Argument_InvalidOffLen"); - if (_writePos==0) { + if (_writePos == 0) { // Ensure we can write to the stream, and ready buffer for writing. if (!CanWrite) __Error.WriteNotSupported(); if (_readPos < _readLen) FlushRead(); @@ -662,7 +674,7 @@ namespace System.IO { numBytes = count; Buffer.BlockCopy(array, offset, _buffer, _writePos, numBytes); _writePos += numBytes; - if (count==numBytes) return; + if (count == numBytes) return; offset += numBytes; count -= numBytes; } @@ -679,7 +691,7 @@ namespace System.IO { } else if (count == 0) return; // Don't allocate a buffer then call memcpy for 0 bytes. - if (_buffer==null) _buffer = new byte[_bufferSize]; + if (_buffer == null) _buffer = new byte[_bufferSize]; // Copy remaining bytes into buffer, to write at a later date. Buffer.BlockCopy(array, offset, _buffer, _writePos, count); _writePos = count; @@ -711,7 +723,7 @@ namespace System.IO { // or -1 if reading from the end of the stream. //| public override int ReadByte() { - if (_readLen==0 && !CanRead) __Error.ReadNotSupported(); + if (_readLen == 0 && !CanRead) __Error.ReadNotSupported(); Debug.Assert((_readPos==0 && _readLen==0 && _writePos >= 0) || (_writePos==0 && _readPos <= _readLen), "We're either reading or writing, but not both."); if (_readPos == _readLen) { if (_writePos > 0) FlushWrite(); @@ -729,13 +741,13 @@ namespace System.IO { //| public override void WriteByte(byte value) { - if (_writePos==0) { + if (_writePos == 0) { if (!CanWrite) __Error.WriteNotSupported(); if (_readPos < _readLen) FlushRead(); _readPos = 0; _readLen = 0; Debug.Assert(_bufferSize > 0, "_bufferSize > 0"); - if (_buffer==null) _buffer = new byte[_bufferSize]; + if (_buffer == null) _buffer = new byte[_bufferSize]; } if (_writePos == _bufferSize) FlushWrite(); diff --git a/base/Libraries/System.IO/FileSystemInfo.cs b/base/Libraries/System.IO/FileSystemInfo.cs index 637361d..634dcd9 100644 --- a/base/Libraries/System.IO/FileSystemInfo.cs +++ b/base/Libraries/System.IO/FileSystemInfo.cs @@ -3,16 +3,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: FileSystemInfo -** -** -** Purpose: -** -** Date: April 7, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: FileSystemInfo +// +// Purpose: +// +//=========================================================== using System; using System.Collections; @@ -21,7 +18,8 @@ using System.Text; using System.Runtime.InteropServices; //@TODO: Add a static SystemDirectory property returning a URI -namespace System.IO { +namespace System.IO +{ //| public abstract class FileSystemInfo { diff --git a/base/Libraries/System.IO/IOException.cs b/base/Libraries/System.IO/IOException.cs index 19116cc..3f14318 100644 --- a/base/Libraries/System.IO/IOException.cs +++ b/base/Libraries/System.IO/IOException.cs @@ -3,20 +3,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: IOException -** -** -** Purpose: Exception for a generic IO error. -** -** Date: February 18, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: IOException +// +// Purpose: Exception for a generic IO error. +// +//=========================================================== using System; -namespace System.IO { +namespace System.IO +{ //| public class IOException : SystemException diff --git a/base/Libraries/System.IO/MemoryStream.cs b/base/Libraries/System.IO/MemoryStream.cs index 78fc07f..df22be0 100644 --- a/base/Libraries/System.IO/MemoryStream.cs +++ b/base/Libraries/System.IO/MemoryStream.cs @@ -3,24 +3,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: MemoryStream -** -** -** Purpose: A Stream whose backing store is memory. Great -** for temporary storage without creating a temp file. Also -** lets users expose a byte[] as a stream. -** -** Date: February 19, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: MemoryStream +// +// Purpose: A Stream whose backing store is memory. Great +// for temporary storage without creating a temp file. Also +// lets users expose a byte[] as a stream. +// +//=========================================================== using System; using System.Diagnostics; using System.Runtime.InteropServices; -namespace System.IO { +namespace System.IO +{ // A MemoryStream represents a Stream in memory (ie, it has no backing store). // This stream may reduce the need for temporary buffers and files in // an application. @@ -93,7 +91,7 @@ namespace System.IO { //| public MemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible) { - if (buffer==null) + if (buffer == null) throw new ArgumentNullException("buffer", "ArgumentNull_Buffer"); if (index < 0) throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_NeedNonNegNum"); @@ -215,7 +213,7 @@ namespace System.IO { //| public override int Read([In, Out] byte[] buffer, int offset, int count) { if (!_isOpen) __Error.StreamIsClosed(); - if (buffer==null) + if (buffer == null) throw new ArgumentNullException("buffer", "ArgumentNull_Buffer"); if (offset < 0) throw new ArgumentOutOfRangeException("offset", "ArgumentOutOfRange_NeedNonNegNum"); @@ -231,8 +229,7 @@ namespace System.IO { Debug.Assert(_position + n >= 0, "_position + n >= 0"); // len is less than 2^31 -1. - if (n <= 8) - { + if (n <= 8) { int byteCount = n; while (--byteCount >= 0) buffer[offset + byteCount] = _buffer[_position + byteCount]; @@ -256,7 +253,7 @@ namespace System.IO { if (!_isOpen) __Error.StreamIsClosed(); if (offset > MemStreamMaxLength) throw new ArgumentOutOfRangeException("offset", "ArgumentOutOfRange_MemStreamLength"); - switch(loc) { + switch (loc) { case SeekOrigin.Begin: if (offset < 0) throw new IOException("IO.IO_SeekBeforeBegin"); @@ -321,7 +318,7 @@ namespace System.IO { public override void Write(byte[] buffer, int offset, int count) { if (!_isOpen) __Error.StreamIsClosed(); if (!_writable) __Error.WriteNotSupported(); - if (buffer==null) + if (buffer == null) throw new ArgumentNullException("buffer", "ArgumentNull_Buffer"); if (offset < 0) throw new ArgumentOutOfRangeException("offset", "ArgumentOutOfRange_NeedNonNegNum"); @@ -346,8 +343,7 @@ namespace System.IO { Array.Clear(_buffer, _length, i - _length); _length = i; } - if (count <= 8) - { + if (count <= 8) { int byteCount = count; while (--byteCount >= 0) _buffer[_position + byteCount] = buffer[offset + byteCount]; @@ -381,7 +377,7 @@ namespace System.IO { //| public virtual void WriteTo(Stream stream) { if (!_isOpen) __Error.StreamIsClosed(); - if (stream==null) + if (stream == null) throw new ArgumentNullException("stream", "ArgumentNull_Stream"); stream.Write(_buffer, _origin, _length - _origin); } diff --git a/base/Libraries/System.IO/Native.cs b/base/Libraries/System.IO/Native.cs index 994b85e..21d97a9 100644 --- a/base/Libraries/System.IO/Native.cs +++ b/base/Libraries/System.IO/Native.cs @@ -1,5 +1,5 @@ // -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // using System; @@ -15,7 +15,7 @@ using Microsoft.Singularity.V1.Services; namespace System { - [CLSCompliant(false)] // + [CLSCompliant(false)] internal sealed class Native { @@ -89,14 +89,14 @@ namespace System } // Called by System.Runtime.InteropServices.Marshal.AllocHGlobal - // for creation of delegates. + // for creation of delegates. internal static IntPtr LocalAlloc(int sizetdwBytes) { throw new NotSupportedException(); } // Called by System.Runtime.InteropServices.Marshal.FreeHGlobal - // for destruction of delegates. + // for destruction of delegates. internal static IntPtr LocalFree(IntPtr handle) { throw new NotSupportedException(); @@ -201,7 +201,7 @@ namespace System } internal static bool CopyFile(String src, String dst, bool failIfExists) { - // Modifications to try to make this work on Singularity - VSee, 6/9/2006 + // Modifications to try to make this work on Singularity // FOR DEBUG // Console.WriteLine("Entering Native.CopyFile"); @@ -340,8 +340,8 @@ namespace System FileUtils.DeleteFile(path, ds, out error); bool ok = (error == ErrorCode.NoError); if (!ok) { - Console.WriteLine(" File ({0}) delete failed. reason:{1}", - path, SdsUtils.ErrorCodeToString(error) ); + DebugStub.WriteLine(" File ({0}) delete failed. reason:{1}", + __arglist(path, SdsUtils.ErrorCodeToString(error)) ); } return ok; } @@ -376,30 +376,32 @@ namespace System int fileInfoLevel, ref FILE_ATTRIBUTE_DATA lpFileInformation) { -#if MOVE_TO_APP_SPACE - // - //this will be used to determine if a file exists - bool result = false; + FileAttributesRecord fileAttributes; + ErrorCode error; - DirectoryServiceContract.Imp epNS = Directory.NewClientEndpoint(); - prefix = Bitter.FromString(name); - epNS.SendFindFirst(prefix); - switch receive - { - case epNS.AckFindFirst(path,code): - p = Bitter.ToString(path); - result = true; - break; - case epNS.NakFindFirst(): - result = false; - break; - case unsatisfiable: - break; + if (name == null) return false; + + DirectoryServiceContract.Imp ds = GetDirectoryServiceContract(); + if (ds == null) { + throw new Exception("Unable to acquire handle to the directory"); } - delete epNS; - return result; -#endif - return false; + + // get extended file info + bool ok = FileUtils.GetAttributes(name, ds, + out fileAttributes, out error); + ReleaseDirectoryServiceContract(ds); + + if (ok) { + lpFileInformation.ftCreationTime = + fileAttributes.CreationTime; + lpFileInformation.ftLastAccessTime = + fileAttributes.LastAccessTime; + lpFileInformation.ftLastWriteTime = + fileAttributes.LastWriteTime; + lpFileInformation.fileSize = + fileAttributes.FileSize; + } + return ok; } internal static bool SetFileAttributes(String name, int attr) diff --git a/base/Libraries/System.IO/Path.cs b/base/Libraries/System.IO/Path.cs index 061be90..5ad3db8 100644 --- a/base/Libraries/System.IO/Path.cs +++ b/base/Libraries/System.IO/Path.cs @@ -3,16 +3,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: Path -** -** -** Purpose: A collection of path manipulation methods. -** -** Date: February 22, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: Path +// +// Purpose: A collection of path manipulation methods. +// +//=========================================================== using System; using System.Text; @@ -20,7 +17,8 @@ using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Globalization; -namespace System.IO { +namespace System.IO +{ // Provides methods for processing directory strings in an ideally // cross-platform manner. Most of the methods don't do a complete // full parsing (such as examining a UNC hostname), but they will @@ -180,15 +178,14 @@ namespace System.IO { // //| public static String GetExtension(String path) { - if (path==null) + if (path == null) return null; CheckInvalidPathChars(path); int length = path.Length; for (int i = length; --i >= 0;) { char ch = path[i]; - if (ch == '.') - { + if (ch == '.') { if (i != length - 1) return path.Substring(i, length - i); else @@ -327,15 +324,18 @@ namespace System.IO { newBuffer[newBufferIndex] = directorySeparator; newBufferIndex++; } - } else if (currentChar == '.' && fixupDotSeparator) { + } + else if (currentChar == '.' && fixupDotSeparator) { numDots++; fixupDirectorySeparator = false; numSpaces = 0; - } else { + } + else { fixupDirectorySeparator = false; if (currentChar == ' ') { numSpaces++; - } else { + } + else { for (int count = 0; count < numDots; count++) { newBuffer[newBufferIndex] = '.'; newBufferIndex++; @@ -370,7 +370,8 @@ namespace System.IO { if (newBuffer[startIndex] == '/') { startIndex++; break; - } else { + } + else { startIndex++; } } @@ -394,7 +395,8 @@ namespace System.IO { return -1; } newPath = resultBuffer.ToString(); - } else { + } + else { newPath = String.StringCTOR(newBuffer, 0, newBufferIndex); } return 0; @@ -426,10 +428,9 @@ namespace System.IO { //| public static String GetFileNameWithoutExtension(String path) { path = GetFileName(path); - if (path != null) - { + if (path != null) { int i; - if ((i=path.LastIndexOf('.')) == -1) + if ((i = path.LastIndexOf('.')) == -1) return path; // No path extension found else return path.Substring(0,i); @@ -460,7 +461,7 @@ namespace System.IO { StringBuilder sb = new StringBuilder(MAX_PATH); uint r = Native.GetTempPath(MAX_PATH, sb); String path = sb.ToString(); - if (r==0) __Error.WinIOError(); + if (r == 0) __Error.WinIOError(); return path; } @@ -472,7 +473,7 @@ namespace System.IO { String path = GetTempPath(); StringBuilder sb = new StringBuilder(MAX_PATH); uint r = Native.GetTempFileName(path, "tmp", 0, sb); - if (r==0) __Error.WinIOError(); + if (r == 0) __Error.WinIOError(); return sb.ToString(); } @@ -489,7 +490,7 @@ namespace System.IO { for (int i = path.Length; --i >= 0;) { char ch = path[i]; if (ch == '.') { - if ( i != path.Length - 1) + if (i != path.Length - 1) return true; else return false; @@ -518,7 +519,7 @@ namespace System.IO { //| public static String Combine(String path1, String path2) { - if (path1==null || path2==null) + if (path1 == null || path2 == null) throw new ArgumentNullException((path1==null) ? "path1" : "path2"); CheckInvalidPathChars(path1); CheckInvalidPathChars(path2); @@ -549,7 +550,7 @@ namespace System.IO { internal static String! InternalCombine(String path1, String path2) { - if (path1==null || path2==null) + if (path1 == null || path2 == null) throw new ArgumentNullException((path1==null) ? "path1" : "path2"); CheckInvalidPathChars(path1); CheckInvalidPathChars(path2); diff --git a/base/Libraries/System.IO/PathTooLongException.cs b/base/Libraries/System.IO/PathTooLongException.cs index 259f105..17a49aa 100644 --- a/base/Libraries/System.IO/PathTooLongException.cs +++ b/base/Libraries/System.IO/PathTooLongException.cs @@ -3,21 +3,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: PathTooLongException -** -** -** Purpose: Exception for paths and/or filenames that are -** too long. -** -** Date: March 24, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: PathTooLongException +// +// Purpose: Exception for paths and/or filenames that are +// too long. +// +//=========================================================== using System; -namespace System.IO { +namespace System.IO +{ //| public class PathTooLongException : IOException diff --git a/base/Libraries/System.IO/SeekOrigin.cs b/base/Libraries/System.IO/SeekOrigin.cs index 47b5eb9..234b859 100644 --- a/base/Libraries/System.IO/SeekOrigin.cs +++ b/base/Libraries/System.IO/SeekOrigin.cs @@ -3,21 +3,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Enum: SeekOrigin -** -** -** Purpose: Enum describing locations in a stream you could -** seek relative to. -** -** Date: February 18, 2000 -** -===========================================================*/ +//============================================================ +// +// Enum: SeekOrigin +// +// Purpose: Enum describing locations in a stream you could +// seek relative to. +// +//=========================================================== using System; -namespace System.IO { +namespace System.IO +{ // Provides seek reference points. To seek to the end of a stream, // call stream.Seek(0, SeekOrigin.End). //| diff --git a/base/Libraries/System.IO/Stream.cs b/base/Libraries/System.IO/Stream.cs index 7ceebdd..84159b8 100644 --- a/base/Libraries/System.IO/Stream.cs +++ b/base/Libraries/System.IO/Stream.cs @@ -3,24 +3,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: Stream -** -** -** Purpose: Abstract base class for all Streams. Provides -** default implementations of asynchronous reads & writes, in -** terms of the synchronous reads & writes (and vice versa). -** -** Date: February 15, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: Stream +// +// Purpose: Abstract base class for all Streams. Provides +// default implementations of asynchronous reads & writes, in +// terms of the synchronous reads & writes (and vice versa). +// +//=========================================================== using System; using System.Threading; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; -namespace System.IO { +namespace System.IO +{ //| [CCtorIsRunDuringStartup] @@ -159,7 +157,7 @@ namespace System.IO { //| public virtual void EndWrite(IAsyncResult asyncResult) { - if (asyncResult==null) + if (asyncResult == null) throw new ArgumentNullException("asyncResult"); SynchronousAsyncResult ar = asyncResult as SynchronousAsyncResult; @@ -195,7 +193,7 @@ namespace System.IO { // and any subclass with an internal buffer should override this method. byte[] oneByteArray = new byte[1]; int r = Read(oneByteArray, 0, 1); - if (r==0) + if (r == 0) return -1; return oneByteArray[0]; } diff --git a/base/Libraries/System.IO/StreamReader.cs b/base/Libraries/System.IO/StreamReader.cs index 3403c5f..3c4d549 100644 --- a/base/Libraries/System.IO/StreamReader.cs +++ b/base/Libraries/System.IO/StreamReader.cs @@ -3,24 +3,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: StreamReader -** -** -** Purpose: For reading text from streams in a particular -** encoding. -** -** Date: February 21, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: StreamReader +// +// Purpose: For reading text from streams in a particular +// encoding. +// +//=========================================================== using System; using System.Diagnostics; using System.Text; using System.Runtime.InteropServices; -namespace System.IO { +namespace System.IO +{ // This class implements a TextReader for reading characters to a Stream. // This is designed for character input in a particular Encoding, // whereas the Stream class is designed for byte input and output. @@ -37,9 +35,9 @@ namespace System.IO { // Using a 1K byte buffer and a 4K FileStream buffer works out pretty well // perf-wise. On even a 40 MB text file, any perf loss by using a 4K // buffer is negated by the win of allocating a smaller byte[], which - // saves construction time. This does break Anders' adaptive buffering, + // saves construction time. This does break the adaptive buffering, // but that shouldn't be a problem since this is slightly faster. The - // web services guys will benefit here the most. -- Brian 7/9/2001 + // web services guys will benefit here the most. internal const int DefaultBufferSize = 1024; // Byte buffer size private const int DefaultFileStreamBufferSize = 4096; private const int MinBufferSize = 128; @@ -118,7 +116,7 @@ namespace System.IO { [Microsoft.Contracts.NotDelayed] public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize) { - if (stream==null || encoding==null) + if (stream == null || encoding == null) throw new ArgumentNullException((stream==null ? "stream" : "encoding")); if (!stream.CanRead) throw new ArgumentException("Argument_StreamNotReadable"); @@ -159,9 +157,9 @@ namespace System.IO { // Don't open a Stream before checking for invalid arguments, // or we'll create a FileStream on disk and we won't close it until // the finalizer runs, causing problems for applications. - if (path==null || encoding==null) + if (path == null || encoding == null) throw new ArgumentNullException((path==null ? "path" : "encoding")); - if (path.Length==0) + if (path.Length == 0) throw new ArgumentException("Argument_EmptyPath"); if (bufferSize <= 0) throw new ArgumentOutOfRangeException("bufferSize", "ArgumentOutOfRange_NeedPosNum"); @@ -262,7 +260,7 @@ namespace System.IO { public override int Read([In, Out] char[] buffer, int index, int count) { if (stream == null) __Error.ReaderClosed(); - if (buffer==null) + if (buffer == null) throw new ArgumentNullException("buffer", "ArgumentNull_Buffer"); if (index < 0 || count < 0) throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), "ArgumentOutOfRange_NeedNonNegNum"); @@ -276,7 +274,7 @@ namespace System.IO { while (count > 0) { int n = charLen - charPos; if (n == 0) n = ReadBuffer(buffer, index + charsRead, count, out readToUserBuffer); - if (n == 0) break; // We're at EOF + if (n == 0) break; // We're at EOF if (n > count) n = count; if (!readToUserBuffer) { Buffer.BlockCopy(charBuffer, charPos * 2, buffer, (index + charsRead) * 2, n*2); @@ -305,7 +303,7 @@ namespace System.IO { char[] chars = new char[charBuffer.Length]; int len; StringBuilder sb = new StringBuilder(charBuffer.Length); - while((len=Read(chars, 0, chars.Length)) != 0) { + while ((len = Read(chars, 0, chars.Length)) != 0) { sb.Append(chars, 0, len); } return sb.ToString(); @@ -322,7 +320,7 @@ namespace System.IO { private static bool BytesMatch(byte[]! buffer, byte[]! compareTo) { Debug.Assert(buffer.Length >= compareTo.Length, "Your Encoding's Preamble array is pretty darn huge!"); - for(int i=0; i= 3 && byteBuffer[0]==0xEF && byteBuffer[1]==0xBB && byteBuffer[2]==0xBF) { + else if (byteLen >= 3 && byteBuffer[0] == 0xEF && byteBuffer[1] == 0xBB && byteBuffer[2] == 0xBF) { // UTF-8 encoding = Encoding.UTF8; decoder = encoding.GetDecoder(); @@ -374,7 +372,7 @@ namespace System.IO { do { byteLen = stream.Read(byteBuffer, 0, byteBuffer.Length); - if (byteLen == 0) // We're at EOF + if (byteLen == 0) // We're at EOF return charLen; // _isBlocked == whether we read fewer bytes than we asked for. @@ -431,7 +429,7 @@ namespace System.IO { do { byteLen = stream.Read(byteBuffer, 0, byteBuffer.Length); - if (byteLen == 0) // EOF + if (byteLen == 0) // EOF return charsRead; // _isBlocked == whether we read fewer bytes than we asked for. @@ -456,13 +454,13 @@ namespace System.IO { } } - /* - if (readToUserBuffer) - Console.Write('.'); - else { - Console.WriteLine("Desired chars is wrong. byteBuffer.length: "+byteBuffer.Length+" max chars is: "+encoding.GetMaxCharCount(byteLen)+" desired: "+desiredChars); - } - */ + // + //if (readToUserBuffer) + // Console.Write('.'); + //else { + // Console.WriteLine("Desired chars is wrong. byteBuffer.length: "+byteBuffer.Length+" max chars is: "+encoding.GetMaxCharCount(byteLen)+" desired: "+desiredChars); + //} + // charPos = 0; if (readToUserBuffer) { diff --git a/base/Libraries/System.IO/StreamWriter.cs b/base/Libraries/System.IO/StreamWriter.cs index 4bde8ec..c5b8546 100644 --- a/base/Libraries/System.IO/StreamWriter.cs +++ b/base/Libraries/System.IO/StreamWriter.cs @@ -3,22 +3,20 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: StreamWriter -** -** -** Purpose: For writing text to streams in a particular -** encoding. -** -** Date: February 21, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: StreamWriter +// +// Purpose: For writing text to streams in a particular +// encoding. +// +//=========================================================== using System; using System.Diagnostics; using System.Text; -namespace System.IO { +namespace System.IO +{ // This class implements a TextWriter for writing characters to a Stream. // This is designed for character output in a particular Encoding, // whereas the Stream class is designed for byte input and output. @@ -30,8 +28,8 @@ namespace System.IO { // file stream buffer size are reasonable & give very reasonable // performance for in terms of construction time for the StreamWriter and // write perf. Note that for UTF-8, we end up allocating a 4K byte buffer, - // which means we take advantage of Anders' adaptive buffering code. - // The performance using UnicodeEncoding is acceptable. -- Brian 7/9/2001 + // which means we take advantage of the adaptive buffering code. + // The performance using UnicodeEncoding is acceptable. private const int DefaultBufferSize = 1024; // char[] private const int DefaultFileStreamBufferSize = 4096; private const int MinBufferSize = 128; @@ -80,7 +78,7 @@ namespace System.IO { //| [Microsoft.Contracts.NotDelayed] public StreamWriter(Stream stream, Encoding encoding, int bufferSize) { - if (stream==null || encoding==null) + if (stream == null || encoding == null) throw new ArgumentNullException((stream==null ? "stream" : "encoding")); if (!stream.CanWrite) throw new ArgumentException("Argument_StreamNotWritable"); @@ -110,7 +108,7 @@ namespace System.IO { //| [Microsoft.Contracts.NotDelayed] public StreamWriter(String path, bool append, Encoding encoding, int bufferSize) { - if (path==null || encoding==null) + if (path == null || encoding == null) throw new ArgumentNullException((path==null ? "path" : "encoding")); if (bufferSize <= 0) throw new ArgumentOutOfRangeException("ArgumentOutOfRange_NeedPosNum"); @@ -194,7 +192,7 @@ namespace System.IO { __Error.WriterClosed(); // Perf boost for Flush on non-dirty writers. - if (charPos==0 && !flushStream && !flushEncoder) + if (charPos == 0 && !flushStream && !flushEncoder) return; if (!haveWrittenPreamble) { @@ -329,31 +327,31 @@ namespace System.IO { } } - /* - // This method is more efficient for long strings outputted to streams - // than the one on TextWriter, and won't cause any problems in terms of - // hiding methods on TextWriter as long as languages respect the - // hide-by-name-and-sig metadata flag. - public override void WriteLine(String value) { - if (value != null) { - int count = value.Length; - int index = 0; - while (count > 0) { - if (charPos == charLen) Flush(false); - int n = charLen - charPos; - if (n > count) n = count; - Debug.Assert(n > 0, "StreamWriter::WriteLine(String) isn't making progress! This is most likely a race condition in user code."); - value.CopyTo(index, charBuffer, charPos, n); - charPos += n; - index += n; - count -= n; - } - } - if (charPos >= charLen - 2) Flush(false); - Buffer.BlockCopy(CoreNewLine, 0, charBuffer, charPos*2, CoreNewLine.Length * 2); - charPos += CoreNewLine.Length; - if (autoFlush) Flush(true, false); - } - */ + // + //// This method is more efficient for long strings outputted to streams + //// than the one on TextWriter, and won't cause any problems in terms of + //// hiding methods on TextWriter as long as languages respect the + //// hide-by-name-and-sig metadata flag. + //public override void WriteLine(String value) { + //if (value != null) { + //int count = value.Length; + //int index = 0; + //while (count > 0) { + //if (charPos == charLen) Flush(false); + //int n = charLen - charPos; + //if (n > count) n = count; + //Debug.Assert(n > 0, "StreamWriter::WriteLine(String) isn't making progress! This is most likely a race condition in user code."); + //value.CopyTo(index, charBuffer, charPos, n); + //charPos += n; + //index += n; + //count -= n; + //} + //} + //if (charPos >= charLen - 2) Flush(false); + //Buffer.BlockCopy(CoreNewLine, 0, charBuffer, charPos*2, CoreNewLine.Length * 2); + //charPos += CoreNewLine.Length; + //if (autoFlush) Flush(true, false); + //} + // } } diff --git a/base/Libraries/System.IO/StringReader.cs b/base/Libraries/System.IO/StringReader.cs index 2c4f654..870683e 100644 --- a/base/Libraries/System.IO/StringReader.cs +++ b/base/Libraries/System.IO/StringReader.cs @@ -3,22 +3,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: StringReader -** -** Original implementation by Anders Hejlsberg (AndersH) -** -** Purpose: For reading text from strings -** -** Date: February 21, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: StringReader +// +// Purpose: For reading text from strings +// +//=========================================================== using System; using System.Runtime.InteropServices; -namespace System.IO { +namespace System.IO +{ // This class implements a text reader that reads from a string. // //| @@ -82,7 +79,7 @@ namespace System.IO { // //| public override int Read([In, Out] char[] buffer, int index, int count) { - if (buffer==null) + if (buffer == null) throw new ArgumentNullException("buffer", "ArgumentNull_Buffer"); if (index < 0) throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_NeedNonNegNum"); @@ -108,7 +105,7 @@ namespace System.IO { if (_s == null) __Error.ReaderClosed(); String s; - if (_pos==0) + if (_pos == 0) s = _s; else s = _s.Substring(_pos, _length - _pos); diff --git a/base/Libraries/System.IO/StringWriter.cs b/base/Libraries/System.IO/StringWriter.cs index 326ac74..ca10376 100644 --- a/base/Libraries/System.IO/StringWriter.cs +++ b/base/Libraries/System.IO/StringWriter.cs @@ -3,22 +3,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: StringWriter -** -** Original implementation by Anders Hejlsberg (AndersH) -** -** Purpose: For writing text to a string -** -** Date: February 21, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: StringWriter +// +// Purpose: For writing text to a string +// +//=========================================================== using System; using System.Text; -namespace System.IO { +namespace System.IO +{ // This class implements a text writer that writes to a string buffer and allows // the resulting sequence of characters to be presented as a string. // @@ -41,7 +38,7 @@ namespace System.IO { // //| public StringWriter(StringBuilder sb) { - if (sb==null) + if (sb == null) throw new ArgumentNullException("sb", "ArgumentNull_Buffer"); _sb = sb; _isOpen = true; @@ -66,7 +63,7 @@ namespace System.IO { //| public override Encoding Encoding { get { - if (m_encoding==null) { + if (m_encoding == null) { m_encoding = new UnicodeEncoding(false, false); } return m_encoding; @@ -100,7 +97,7 @@ namespace System.IO { public override void Write(char[] buffer, int index, int count) { if (!_isOpen) __Error.WriterClosed(); - if (buffer==null) + if (buffer == null) throw new ArgumentNullException("buffer", "ArgumentNull_Buffer"); if (index < 0) throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_NeedNonNegNum"); diff --git a/base/Libraries/System.IO/System.IO.csproj b/base/Libraries/System.IO/System.IO.csproj index 147ac32..d835eb9 100644 --- a/base/Libraries/System.IO/System.IO.csproj +++ b/base/Libraries/System.IO/System.IO.csproj @@ -11,24 +11,21 @@ # ############################################################################## --> - - - System.IO Library true + {78AF7F34-48EE-42F0-8DDD-66C0C98183B9} - - + @@ -59,12 +56,8 @@ - - - - - + \ No newline at end of file diff --git a/base/Libraries/System.IO/TextReader.cs b/base/Libraries/System.IO/TextReader.cs index 7524fc8..19bed2b 100644 --- a/base/Libraries/System.IO/TextReader.cs +++ b/base/Libraries/System.IO/TextReader.cs @@ -3,17 +3,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: TextReader -** -** -** Purpose: Abstract base class for all Text-only Readers. -** Subclasses will include StreamReader & StringReader. -** -** Date: February 21, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: TextReader +// +// Purpose: Abstract base class for all Text-only Readers. +// Subclasses will include StreamReader & StringReader. +// +//=========================================================== using System; using System.Text; @@ -21,7 +18,8 @@ using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Reflection; -namespace System.IO { +namespace System.IO +{ // This abstract base class represents a reader that can read a sequential // stream of characters. This is not intended for reading bytes - // there are methods on the Stream class to read bytes. @@ -94,7 +92,7 @@ namespace System.IO { //| public virtual int Read([In, Out] char[] buffer, int index, int count) { - if (buffer==null) + if (buffer == null) throw new ArgumentNullException("buffer", "ArgumentNull_Buffer"); if (index < 0) throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_NeedNonNegNum"); @@ -120,8 +118,7 @@ namespace System.IO { char[] chars = new char[4096]; int len; StringBuilder sb = new StringBuilder(4096); - while((len=Read(chars, 0, chars.Length)) != 0) - { + while ((len = Read(chars, 0, chars.Length)) != 0) { sb.Append(chars, 0, len); } return sb.ToString(); @@ -153,8 +150,7 @@ namespace System.IO { while (true) { int ch = Read(); if (ch == -1) break; - if (ch == '\r' || ch == '\n') - { + if (ch == '\r' || ch == '\n') { if (ch == '\r' && Peek() == '\n') Read(); return sb.ToString(); } @@ -168,7 +164,7 @@ namespace System.IO { //| public static TextReader Synchronized(TextReader reader) { - if (reader==null) + if (reader == null) throw new ArgumentNullException("reader"); if (reader is SyncTextReader) return reader; diff --git a/base/Libraries/System.IO/TextWriter.cs b/base/Libraries/System.IO/TextWriter.cs index 5d586ff..92b5719 100644 --- a/base/Libraries/System.IO/TextWriter.cs +++ b/base/Libraries/System.IO/TextWriter.cs @@ -3,17 +3,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: TextWriter -** -** -** Purpose: Abstract base class for Text-only Writers. -** Subclasses will include StreamWriter & StringWriter. -** -** Date: February 21, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: TextWriter +// +// Purpose: Abstract base class for Text-only Writers. +// Subclasses will include StreamWriter & StringWriter. +// +//=========================================================== using System; using System.Text; @@ -21,7 +18,8 @@ using System.Threading; using System.Runtime.CompilerServices; using System.Reflection; -namespace System.IO { +namespace System.IO +{ // This abstract base class represents a writer that can write a sequential // stream of characters. A subclass must minimally implement the // Write(char) method. @@ -105,7 +103,7 @@ namespace System.IO { //| public static TextWriter Synchronized(TextWriter writer) { - if (writer==null) + if (writer == null) throw new ArgumentNullException("writer"); if (writer is SyncTextWriter) return writer; @@ -136,7 +134,7 @@ namespace System.IO { // //| public virtual void Write(char[] buffer, int index, int count) { - if (buffer==null) + if (buffer == null) throw new ArgumentNullException("buffer", "ArgumentNull_Buffer"); if (index < 0) throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_NeedNonNegNum"); @@ -401,7 +399,7 @@ namespace System.IO { //| public virtual void WriteLine(String value) { - if (value==null) { + if (value == null) { WriteLine(); } else { @@ -428,10 +426,10 @@ namespace System.IO { Buffer.BlockCopy(CoreNewLine, 0, chars, vLen * 2, nlLen * 2); Write(chars, 0, vLen + nlLen); } - /* - Write(value); // We could call Write(String) on StreamWriter... - WriteLine(); - */ + // + //Write(value); // We could call Write(String) on StreamWriter... + //WriteLine(); + // } // Writes the text representation of an object followed by a line @@ -439,7 +437,7 @@ namespace System.IO { // //| public virtual void WriteLine(Object value) { - if (value==null) { + if (value == null) { WriteLine(); } else { diff --git a/base/Libraries/System.IO/__ConsoleTextWriter.cs b/base/Libraries/System.IO/__ConsoleTextWriter.cs index 3f9db86..85ce783 100644 --- a/base/Libraries/System.IO/__ConsoleTextWriter.cs +++ b/base/Libraries/System.IO/__ConsoleTextWriter.cs @@ -9,8 +9,9 @@ using System.IO; using System.Text; using System.Runtime.InteropServices; -// -namespace System { +// [Bartok]: +namespace System +{ public class __ConsoleTextWriter : TextWriter { public __ConsoleTextWriter() { diff --git a/base/Libraries/System.IO/__DebugOutputTextWriter.cs b/base/Libraries/System.IO/__DebugOutputTextWriter.cs index 6f74060..affa01c 100644 --- a/base/Libraries/System.IO/__DebugOutputTextWriter.cs +++ b/base/Libraries/System.IO/__DebugOutputTextWriter.cs @@ -10,7 +10,7 @@ // output for some good old-fashioned console spew in MSDEV's debug output window. // This really shouldn't ship at all, but is intended as a quick, inefficient hack -// for debugging. -- BrianGru, 9/26/2000 +// for debugging. using System; using System.IO; @@ -18,7 +18,8 @@ using System.Text; using System.Runtime.InteropServices; using Native = Microsoft.Singularity.Native -namespace System.IO { +namespace System.IO +{ internal class __DebugOutputTextWriter : TextWriter { private readonly String _consoleType; diff --git a/base/Libraries/System.IO/__Error.cs b/base/Libraries/System.IO/__Error.cs index 110252e..8265307 100644 --- a/base/Libraries/System.IO/__Error.cs +++ b/base/Libraries/System.IO/__Error.cs @@ -3,18 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== -/*============================================================ -** -** Class: __Error -** -** -** Purpose: Centralized error methods for the IO package. -** Mostly useful for translating Win32 HRESULTs into meaningful -** error strings & exceptions. -** -** Date: February 15, 2000 -** -===========================================================*/ +//============================================================ +// +// Class: __Error +// +// Purpose: Centralized error methods for the IO package. +// Mostly useful for translating Win32 HRESULTs into meaningful +// error strings & exceptions. +// +//=========================================================== using System; using System.Runtime.InteropServices; @@ -22,7 +19,8 @@ using System.Text; using Microsoft.Singularity.Directory; -namespace System.IO { +namespace System.IO +{ // Only static data no need to serialize internal sealed class __Error { @@ -91,7 +89,7 @@ namespace System.IO { internal static int ErrorCodeToWin32Error(ErrorCode code) { - Console.WriteLine("Converting error {0}", SdsUtils.ErrorCodeToString(code)); + // Console.WriteLine("Converting error {0}", SdsUtils.ErrorCodeToString(code)); int win32 = 0; switch (code) { case ErrorCode.NoError : @@ -130,7 +128,7 @@ namespace System.IO { internal static void WinIOError(int errorCode, String str) { switch (errorCode) { case ERROR_FILE_NOT_FOUND: { - throw new FileNotFoundException("FileNotFound"); + throw new FileNotFoundException("FileNotFound:" + str); } case ERROR_PATH_NOT_FOUND: { throw new DirectoryNotFoundException("PathNotFound"); @@ -166,20 +164,20 @@ namespace System.IO { private const int FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000; // From WinError.h - internal const int ERROR_FILE_NOT_FOUND = Native.ERROR_FILE_NOT_FOUND; - internal const int ERROR_PATH_NOT_FOUND = Native.ERROR_PATH_NOT_FOUND; - internal const int ERROR_ACCESS_DENIED = Native.ERROR_ACCESS_DENIED; - internal const int ERROR_INVALID_PARAMETER = Native.ERROR_INVALID_PARAMETER; - internal const int ERROR_FILENAME_EXCED_RANGE = 0xCE; - internal const int ERROR_SHARING_VIOLATION = 0x20; - internal const int ERROR_FILE_EXISTS = 0x50; + public const int ERROR_FILE_NOT_FOUND = Native.ERROR_FILE_NOT_FOUND; + public const int ERROR_PATH_NOT_FOUND = Native.ERROR_PATH_NOT_FOUND; + public const int ERROR_ACCESS_DENIED = Native.ERROR_ACCESS_DENIED; + public const int ERROR_INVALID_PARAMETER = Native.ERROR_INVALID_PARAMETER; + public const int ERROR_FILENAME_EXCED_RANGE = 0xCE; + public const int ERROR_SHARING_VIOLATION = 0x20; + public const int ERROR_FILE_EXISTS = 0x50; - internal const int ERROR_NOT_SUPPORTED = Native.ERROR_NOT_SUPPORTED; - internal const int ERROR_DUP_NAME = Native.ERROR_DUP_NAME; - internal const int ERROR_DISK_FULL = Native.ERROR_DISK_FULL; - internal const int ERROR_CALL_NOT_IMPLEMENTED = Native.ERROR_CALL_NOT_IMPLEMENTED; - internal const int ERROR_DIR_NOT_EMPTY = Native.ERROR_DIR_NOT_EMPTY; - internal const int ERROR_ALREADY_EXISTS = Native.ERROR_ALREADY_EXISTS; + public const int ERROR_NOT_SUPPORTED = Native.ERROR_NOT_SUPPORTED; + public const int ERROR_DUP_NAME = Native.ERROR_DUP_NAME; + public const int ERROR_DISK_FULL = Native.ERROR_DISK_FULL; + public const int ERROR_CALL_NOT_IMPLEMENTED = Native.ERROR_CALL_NOT_IMPLEMENTED; + public const int ERROR_DIR_NOT_EMPTY = Native.ERROR_DIR_NOT_EMPTY; + public const int ERROR_ALREADY_EXISTS = Native.ERROR_ALREADY_EXISTS; } } diff --git a/base/Libraries/System.Net.IP/AssemblyInfo.sg b/base/Libraries/System.Net.IP/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Libraries/System.Net.IP/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Libraries/System.Net.IP/IPAddressUtils.cs b/base/Libraries/System.Net.IP/IPAddressUtils.cs index ee22f87..6c8bf0d 100644 --- a/base/Libraries/System.Net.IP/IPAddressUtils.cs +++ b/base/Libraries/System.Net.IP/IPAddressUtils.cs @@ -42,8 +42,7 @@ namespace System.Net.IP byte [] bl = l.GetAddressBytes(); byte [] br = r.GetAddressBytes(); - if (l.AddressFamily == AddressFamily.InterNetwork) - { + if (l.AddressFamily == AddressFamily.InterNetwork) { bl[0] &= br[0]; bl[1] &= br[1]; bl[2] &= br[2]; @@ -85,8 +84,7 @@ namespace System.Net.IP byte [] bl = l.GetAddressBytes(); byte [] br = r.GetAddressBytes(); - if (l.AddressFamily == AddressFamily.InterNetwork) - { + if (l.AddressFamily == AddressFamily.InterNetwork) { bl[0] |= br[0]; bl[1] |= br[1]; bl[2] |= br[2]; @@ -129,8 +127,7 @@ namespace System.Net.IP byte [] bl = l.GetAddressBytes(); byte [] br = r.GetAddressBytes(); - if (l.AddressFamily == AddressFamily.InterNetwork) - { + if (l.AddressFamily == AddressFamily.InterNetwork) { bl[0] ^= br[0]; bl[1] ^= br[1]; bl[2] ^= br[2]; @@ -168,8 +165,7 @@ namespace System.Net.IP { byte [] bl = l.GetAddressBytes(); - if (l.AddressFamily == AddressFamily.InterNetwork) - { + if (l.AddressFamily == AddressFamily.InterNetwork) { bl[0] = (byte) ~bl[0]; bl[1] = (byte) ~bl[1]; bl[2] = (byte) ~bl[2]; @@ -219,8 +215,7 @@ namespace System.Net.IP uint ul = 0; uint ur = 0; - for (int i = 0; i < bl.Length; i += 4) - { + for (int i = 0; i < bl.Length; i += 4) { ul = (uint)((bl[i + 0] << 24) | (bl[i + 1] << 16) | (bl[i + 2] << 8) | (bl[i + 3])); ur = (uint)((br[i + 0] << 24) | (br[i + 1] << 16) | @@ -242,8 +237,7 @@ namespace System.Net.IP uint ul = 0; uint ur = 0; - for (int i = 0; i < bl.Length; i += 4) - { + for (int i = 0; i < bl.Length; i += 4) { ul = (uint)((bl[i + 0] << 24) | (bl[i + 1] << 16) | (bl[i + 2] << 8) | (bl[i + 3])); ur = (uint)((br[i + 0] << 24) | (br[i + 1] << 16) | diff --git a/base/Libraries/System.Net.IP/IPv4.cs b/base/Libraries/System.Net.IP/IPv4.cs index 87b288e..77486eb 100644 --- a/base/Libraries/System.Net.IP/IPv4.cs +++ b/base/Libraries/System.Net.IP/IPv4.cs @@ -61,14 +61,12 @@ namespace System.Net.IP /// InterNetwork public IPv4(IPAddress ipa) { - if (ipa == null) - { + if (ipa == null) { throw new ArgumentNullException(); } byte [] quad = ipa.GetAddressBytes(); - if (quad.Length != Length) - { + if (quad.Length != Length) { throw new ArgumentException(); } addr =(uint)((quad[0] << 24) | (quad[1] << 16) | @@ -97,8 +95,7 @@ namespace System.Net.IP /// outside of the range [0,32]. public static IPv4 NetMask(int maskLength) { - if ((maskLength > BitCount) || (maskLength < 0)) - { + if ((maskLength > BitCount) || (maskLength < 0)) { throw new ArgumentException("Mask length greater than possible."); } @@ -117,12 +114,10 @@ namespace System.Net.IP /// than 4 bytes from the offset to the end of the array. public static IPv4 ParseBytes(byte [] data, int offset) { - if (data == null) - { + if (data == null) { throw new ArgumentNullException(); } - if (data.Length - offset < Length) - { + if (data.Length - offset < Length) { throw new ArgumentException("Byte array too short."); } return new IPv4((uint)((data[offset + 0] << 24) | @@ -169,8 +164,7 @@ namespace System.Net.IP return false; int result = 0; - for (int i = 1; i < quad.Length; i++) - { + for (int i = 1; i < quad.Length; i++) { int tmp = quad[i] - '0'; if (tmp < 0 || tmp > 7) return false; @@ -189,8 +183,7 @@ namespace System.Net.IP return false; int result = 0; - for (int i = 0; i < quad.Length; i++) - { + for (int i = 0; i < quad.Length; i++) { int tmp = quad[i] - '0'; if (tmp < 0 || tmp > 9) return false; @@ -221,30 +214,25 @@ namespace System.Net.IP { const string malformed = "Malformed address"; - if (ipString == null) - { + if (ipString == null) { throw new ArgumentNullException("ipString"); } string [] quads = ipString.Split('.'); - if (quads.Length == 0 || quads.Length > 4) - { + if (quads.Length == 0 || quads.Length > 4) { throw new FormatException(malformed); } uint address = 0; uint tmp = 0; int i; - for (i = 0; i < quads.Length - 1; i++) - { - if (ParseQuad((!)quads[i], out tmp) == false) - { + for (i = 0; i < quads.Length - 1; i++) { + if (ParseQuad((!)quads[i], out tmp) == false) { throw new FormatException(malformed); } address |= (tmp) << (24 - i * 8); } - if (ParseQuad((!)quads[i], out tmp) == false) - { + if (ParseQuad((!)quads[i], out tmp) == false) { throw new FormatException(malformed); } address |= tmp; @@ -259,12 +247,10 @@ namespace System.Net.IP /// true on success, false on failure. public static bool Parse(string ipString, out IPv4 address) { - try - { + try { address = Parse(ipString); } - catch (FormatException) - { + catch (FormatException) { address = IPv4.Zero; return false; } @@ -282,12 +268,10 @@ namespace System.Net.IP /// buffer to write out the IP address. public int CopyOut(byte[] buffer, int outputOffset) { - if (buffer == null) - { + if (buffer == null) { throw new ArgumentNullException(); } - if (buffer.Length - outputOffset < Length) - { + if (buffer.Length - outputOffset < Length) { throw new ArgumentException("Byte array too short."); } buffer[outputOffset + 0] = (byte)(addr >> 24); @@ -440,8 +424,7 @@ namespace System.Net.IP /// An IPv4 address. public static IPv4 operator >> (IPv4 ipv4, int n) { - if (n < BitCount) - { + if (n < BitCount) { return new IPv4(ipv4.addr >> n); } return IPv4.Zero; @@ -455,8 +438,7 @@ namespace System.Net.IP /// An IPv4 address. public static IPv4 operator << (IPv4 ipv4, int n) { - if (n < BitCount) - { + if (n < BitCount) { return new IPv4(ipv4.addr << n); } return IPv4.Zero; @@ -473,8 +455,7 @@ namespace System.Net.IP /// public bool GetBit(int bitIndex) { - if (bitIndex < 0 || bitIndex >= BitCount) - { + if (bitIndex < 0 || bitIndex >= BitCount) { return false; } uint mask = 1u << (31 - bitIndex); @@ -488,8 +469,7 @@ namespace System.Net.IP public static int GetMaskLength(IPv4 netmask) { int i = 0; - while (netmask.GetBit(i) == true) - { + while (netmask.GetBit(i) == true) { i++; } return i; @@ -526,8 +506,7 @@ namespace System.Net.IP /// the same as instance. public override bool Equals(object other) { - if (other is IPv4) - { + if (other is IPv4) { return addr == ((IPv4)other).addr; } return false; @@ -577,11 +556,10 @@ namespace System.Net.IP { if (other == null) return 1; - if (other is IPv4) - { + if (other is IPv4) { IPv4 value = (IPv4) other; if (this < value) return -1; - if (this > value) return +1; + if (this > value) return + 1; return 0; } throw new ArgumentException ("Arg_MustBeIPv4"); diff --git a/base/Libraries/System.Net.IP/IPv4Network.cs b/base/Libraries/System.Net.IP/IPv4Network.cs index ef8ebeb..039c0dc 100644 --- a/base/Libraries/System.Net.IP/IPv4Network.cs +++ b/base/Libraries/System.Net.IP/IPv4Network.cs @@ -22,8 +22,7 @@ namespace System.Net.IP public IPv4Network(IPv4 ipNetwork, int maskLength) { - if (maskLength < 0 || maskLength > IPv4.BitCount) - { + if (maskLength < 0 || maskLength > IPv4.BitCount) { throw new ArgumentException(); } this.maskLength = maskLength; @@ -90,8 +89,7 @@ namespace System.Net.IP public static bool operator < (IPv4Network x, IPv4Network y) { - if (x.maskLength == y.maskLength) - { + if (x.maskLength == y.maskLength) { return x.ipNetwork < y.ipNetwork; } return x.maskLength < y.maskLength; @@ -99,8 +97,7 @@ namespace System.Net.IP public static bool operator <= (IPv4Network x, IPv4Network y) { - if (x.maskLength == y.maskLength) - { + if (x.maskLength == y.maskLength) { return x.ipNetwork <= y.ipNetwork; } return x.maskLength <= y.maskLength; @@ -186,14 +183,19 @@ namespace System.Net.IP /// public static bool Parse(string ipNetwork, out IPv4Network network) { - try - { + try { network = Parse(ipNetwork); return true; } - catch (ArgumentException) {} - catch (FormatException) {} - catch (OverflowException) {} + catch (ArgumentException) { + + } + catch (FormatException) { + + } + catch (OverflowException) { + + } network = Default; return false; @@ -206,8 +208,7 @@ namespace System.Net.IP public override bool Equals(object other) { - if (other is IPv4Network) - { + if (other is IPv4Network) { return (this == (IPv4Network)other); } return false; diff --git a/base/Libraries/System.Net.IP/IPv6.cs b/base/Libraries/System.Net.IP/IPv6.cs index 9104e46..d1753d4 100644 --- a/base/Libraries/System.Net.IP/IPv6.cs +++ b/base/Libraries/System.Net.IP/IPv6.cs @@ -62,14 +62,12 @@ namespace System.Net.IP /// InterNetwork6 public IPv6(IPAddress ipa) { - if (ipa == null) - { + if (ipa == null) { throw new ArgumentNullException(); } byte [] data = ipa.GetAddressBytes(); - if (data.Length != Length) - { + if (data.Length != Length) { throw new ArgumentException(); } u0 = (uint)((data[0] << 24) | (data[1] << 16) | @@ -110,8 +108,7 @@ namespace System.Net.IP /// outside of the range [0,128]. public static IPv6 NetMask(int maskLength) { - if ((maskLength > BitCount) || (maskLength < 0)) - { + if ((maskLength > BitCount) || (maskLength < 0)) { throw new ArgumentException("Mask length greater than possible."); } @@ -148,12 +145,10 @@ namespace System.Net.IP /// than 16 bytes from the offset to the end of the array. public static IPv6 ParseBytes(byte [] data, int offset) { - if (data == null) - { + if (data == null) { throw new ArgumentNullException(); } - if (data.Length - offset < Length) - { + if (data.Length - offset < Length) { throw new ArgumentException("Byte array length not 16."); } return new IPv6((uint)((data[offset + 0] << 24) | @@ -205,33 +200,27 @@ namespace System.Net.IP int valueIndex = 0; int digits = 0; - for (int i = start; i < end; i++) - { + for (int i = start; i < end; i++) { int n = hex.IndexOf(ipString[i]); digits ++; - if (n >= 0) - { - if (valueIndex == maxValues || digits > 4) - { + if (n >= 0) { + if (valueIndex == maxValues || digits > 4) { // About to access value beyond specified range throw new FormatException(); } uint v = values[valueIndex]; v = v * 16 + (uint)(n / 2); - if (v > 0xffffu) - { + if (v > 0xffffu) { throw new FormatException(); } values[valueIndex] = (ushort)v; } - else if (ipString[i] == ':') - { + else if (ipString[i] == ':') { // Reached Separator or end valueIndex++; digits = 0; } - else - { + else { // Invalid Character throw new FormatException(); } @@ -258,23 +247,19 @@ namespace System.Net.IP ushort[]! lhs = new ushort [8]; int nlhs; - if (zeroSep >= 0) - { + if (zeroSep >= 0) { nlhs = Parse(ipString, 0, zeroSep, 8, ref lhs); ushort[]! rhs = new ushort [8]; int nrhs = Parse(ipString, zeroSep + 2, ipString.Length, 8 - nlhs, ref rhs); - if (nrhs + nlhs > 8) - { + if (nrhs + nlhs > 8) { throw new FormatException("Too many address components"); } - for (int i = 0; i < nrhs; i++) - { + for (int i = 0; i < nrhs; i++) { lhs[8 - nrhs + i] = rhs[i]; } } - else - { + else { nlhs = Parse(ipString, 0, ipString.Length, 8, ref lhs); } @@ -293,12 +278,10 @@ namespace System.Net.IP /// true on success, false on failure. public static bool Parse(string ipString, out IPv6 address) { - try - { + try { address = Parse(ipString); } - catch (FormatException) - { + catch (FormatException) { address = IPv6.Zero; return false; } @@ -316,12 +299,10 @@ namespace System.Net.IP /// buffer to write out the IP address. public int CopyOut(byte[] buffer, int outputOffset) { - if (buffer == null) - { + if (buffer == null) { throw new ArgumentNullException(); } - if (buffer.Length - outputOffset < Length) - { + if (buffer.Length - outputOffset < Length) { throw new ArgumentException("Byte array too short."); } buffer[outputOffset + 0] = (byte)(u0 >> 24); @@ -533,8 +514,7 @@ namespace System.Net.IP /// An IPv6 address. public static IPv6 operator >> (IPv6 addr, int n) { - switch (n >> 5) - { + switch (n >> 5) { case 3: addr.u3 = addr.u0; addr.u0 = addr.u1 = addr.u2 = 0; @@ -559,8 +539,7 @@ namespace System.Net.IP } n = n & 0x1f; - if (n != 0) - { + if (n != 0) { int m = 32 - n; addr.u3 = (addr.u3 >> n) | (addr.u2 << m); addr.u2 = (addr.u2 >> n) | (addr.u1 << m); @@ -578,8 +557,7 @@ namespace System.Net.IP /// An IPv6 address. public static IPv6 operator << (IPv6 addr, int n) { - switch (n >> 5) - { + switch (n >> 5) { case 3: addr.u0 = addr.u3; addr.u1 = addr.u2 = addr.u3 = 0; @@ -603,8 +581,7 @@ namespace System.Net.IP return Zero; } n = n & 0x1f; - if (n != 0) - { + if (n != 0) { int m = 32 - n; addr.u0 = (addr.u0 << n) | (addr.u1 >> m); addr.u1 = (addr.u1 << n) | (addr.u2 >> m); @@ -625,16 +602,14 @@ namespace System.Net.IP /// public bool GetBit(int bitIndex) { - if (bitIndex < 0 || bitIndex >= BitCount) - { + if (bitIndex < 0 || bitIndex >= BitCount) { return false; } int dwordIndex = bitIndex / 32; uint mask = 1u << (31 - (bitIndex & 31)); - switch (dwordIndex) - { + switch (dwordIndex) { case 0: return (u0 & mask) == mask; case 1: @@ -652,8 +627,7 @@ namespace System.Net.IP public static int GetMaskLength(IPv6 netmask) { int i = 0; - while (netmask.GetBit(i) == true) - { + while (netmask.GetBit(i) == true) { i++; } return i; @@ -679,8 +653,7 @@ namespace System.Net.IP /// the same as instance. public override bool Equals(object o) { - if (o is IPv6) - { + if (o is IPv6) { IPv6 ipo = (IPv6)o; return this == ipo; } @@ -761,8 +734,7 @@ namespace System.Net.IP /// if address does not represent an IPv4 address. public IPv4 GetIPv4Address() { - if (u0 == 0 && u1 == 0 && (u2 == 0 || u2 == 0xffff)) - { + if (u0 == 0 && u1 == 0 && (u2 == 0 || u2 == 0xffff)) { return new IPv4(u3); } return IPv4.Any; @@ -782,24 +754,21 @@ namespace System.Net.IP // Find start of the zero block int zStart; - for (zStart = 0; zStart < words.Length; zStart++) - { + for (zStart = 0; zStart < words.Length; zStart++) { if (words[zStart] == 0) break; } // Find the end of the zero block int zEnd; - for (zEnd = zStart + 1; zEnd < words.Length; zEnd++) - { + for (zEnd = zStart + 1; zEnd < words.Length; zEnd++) { if (words[zEnd] != 0) break; } // Max size is 8 * 4 hexdigits + 7 * 1 digit separator StringBuilder sb = new StringBuilder(null, 8 * 4 + 7 * 1); - for (int i = 0; i < zStart; i++) - { + for (int i = 0; i < zStart; i++) { if (i > 0) sb.AppendFormat(":{0:x}", words[i]); else @@ -810,8 +779,7 @@ namespace System.Net.IP return sb.ToString(); sb.Append("::"); - for (int i = zEnd; i < words.Length; i++) - { + for (int i = zEnd; i < words.Length; i++) { if (i > zEnd) sb.AppendFormat(":{0:x}", words[i]); else @@ -827,14 +795,12 @@ namespace System.Net.IP public override string! ToString() { #if NOTYET - if (RepresentsIPv4OnlyAddress() == true) - { + if (RepresentsIPv4OnlyAddress() == true) { return String.Format("::ffff:{0}.{1}.{2}.{3}", u3 >> 24, (u3 >> 16) & 0xff, (u3 >> 8) & 0xff, u3 & 0xff); } - else if (RepresentsIPv4Address() == true) - { + else if (RepresentsIPv4Address() == true) { return String.Format("::{0}.{1}.{2}.{3}", u3 >> 24, (u3 >> 16) & 0xff, (u3 >> 8) & 0xff, u3 & 0xff); @@ -847,11 +813,10 @@ namespace System.Net.IP { if (other == null) return 1; - if (other is IPv6) - { + if (other is IPv6) { IPv6 value = (IPv6) other; if (this < value) return -1; - if (this > value) return +1; + if (this > value) return + 1; return 0; } throw new ArgumentException ("Arg_MustBeIPv6"); diff --git a/base/Libraries/System.Net.IP/IPv6Network.cs b/base/Libraries/System.Net.IP/IPv6Network.cs index 712479a..eaae69a 100644 --- a/base/Libraries/System.Net.IP/IPv6Network.cs +++ b/base/Libraries/System.Net.IP/IPv6Network.cs @@ -22,8 +22,7 @@ namespace System.Net.IP public IPv6Network(IPv6 ipNetwork, int maskLength) { - if (maskLength < 0 || maskLength > IPv6.BitCount) - { + if (maskLength < 0 || maskLength > IPv6.BitCount) { throw new ArgumentException(); } this.maskLength = maskLength; @@ -90,8 +89,7 @@ namespace System.Net.IP public static bool operator <(IPv6Network x, IPv6Network y) { - if (x.maskLength == y.maskLength) - { + if (x.maskLength == y.maskLength) { return x.ipNetwork < y.ipNetwork; } return x.maskLength < y.maskLength; @@ -99,8 +97,7 @@ namespace System.Net.IP public static bool operator <=(IPv6Network x, IPv6Network y) { - if (x.maskLength == y.maskLength) - { + if (x.maskLength == y.maskLength) { return x.ipNetwork <= y.ipNetwork; } return x.maskLength <= y.maskLength; @@ -133,8 +130,7 @@ namespace System.Net.IP public override bool Equals(object other) { - if (other is IPv6Network) - { + if (other is IPv6Network) { return (this == (IPv6Network)other); } return false; @@ -200,14 +196,19 @@ namespace System.Net.IP /// public static bool Parse(string ipNetwork, out IPv6Network network) { - try - { + try { network = Parse(ipNetwork); return true; } - catch (ArgumentException) {} - catch (FormatException) {} - catch (OverflowException) {} + catch (ArgumentException) { + + } + catch (FormatException) { + + } + catch (OverflowException) { + + } network = Default; return false; diff --git a/base/Libraries/System.Net.IP/System.Net.IP.csproj b/base/Libraries/System.Net.IP/System.Net.IP.csproj index 42b59d4..e4bd4d1 100644 --- a/base/Libraries/System.Net.IP/System.Net.IP.csproj +++ b/base/Libraries/System.Net.IP/System.Net.IP.csproj @@ -22,6 +22,7 @@ + diff --git a/base/Libraries/System.Net.IP/TestIPv4.cs b/base/Libraries/System.Net.IP/TestIPv4.cs index c60162d..55e476e 100644 --- a/base/Libraries/System.Net.IP/TestIPv4.cs +++ b/base/Libraries/System.Net.IP/TestIPv4.cs @@ -47,22 +47,19 @@ namespace System.Net.IP if ((a == c) == false || (a != c)) throw new TestFailedException("a c == !="); - for (int i = 0; i < ipv4Bytes.Length; i++) - { + for (int i = 0; i < ipv4Bytes.Length; i++) { if (ipv4Bytes[i] != c.GetAddressBytes()[i]) throw new TestFailedException("GetAddressBytes"); } byte [] ipAddrBytes = ((IPAddress)c).GetAddressBytes(); - for (int i = 0; i < ipv4Bytes.Length; i++) - { + for (int i = 0; i < ipv4Bytes.Length; i++) { if (ipv4Bytes[i] != ipAddrBytes[i]) throw new TestFailedException("IPAddress"); } IPAddress ipa = IPAddress.Parse(c.ToString()); - if (new IPv4(ipa) != c) - { + if (new IPv4(ipa) != c) { throw new TestFailedException("IPv4(IPAddress) Constructor"); } } @@ -72,53 +69,43 @@ namespace System.Net.IP IPv4 a = new IPv4(0x0a000001); IPv4 b = new IPv4(0x0a000000); - if ((a == a) == false) - { + if ((a == a) == false) { throw new TestFailedException("a == a"); } - if ((a != b) == false) - { + if ((a != b) == false) { throw new TestFailedException("a == b"); } - if ((a > b) == false) - { + if ((a > b) == false) { throw new TestFailedException("a > b"); } - if ((a >= b) == false) - { + if ((a >= b) == false) { throw new TestFailedException("a >= b"); } - if ((a >= a) == false) - { + if ((a >= a) == false) { throw new TestFailedException("a >= a"); } - if ((a > a) == true) - { + if ((a > a) == true) { throw new TestFailedException("a > a"); } - if ((a < b) == true) - { + if ((a < b) == true) { throw new TestFailedException("a < b"); } - if ((a <= b) == true) - { + if ((a <= b) == true) { throw new TestFailedException("a <= b"); } - if ((a <= a) != true) - { + if ((a <= a) != true) { throw new TestFailedException("a <= a"); } - if ((a < a) == true) - { + if ((a < a) == true) { throw new TestFailedException("a < a"); } } @@ -127,33 +114,27 @@ namespace System.Net.IP { IPv4 a = new IPv4(0x80808080); - if ((uint)(a << 1) != 0x01010100) - { + if ((uint)(a << 1) != 0x01010100) { throw new TestFailedException("<< 1"); } - if ((uint)(a << 2) != 0x02020200) - { + if ((uint)(a << 2) != 0x02020200) { throw new TestFailedException("<< 2"); } - if ((uint)(a << 32) != 0) - { + if ((uint)(a << 32) != 0) { throw new TestFailedException("<< 32"); } - if ((uint)(a >> 1) != 0x40404040) - { + if ((uint)(a >> 1) != 0x40404040) { throw new TestFailedException(">> 1"); } - if ((uint)(a >> 2) != 0x20202020) - { + if ((uint)(a >> 2) != 0x20202020) { throw new TestFailedException(">> 2"); } - if ((uint)(a >> 32) != 0) - { + if ((uint)(a >> 32) != 0) { throw new TestFailedException(">> 32"); } } @@ -163,56 +144,45 @@ namespace System.Net.IP IPv4 a = new IPv4(0x7f7f7f7f); IPv4 b = new IPv4(0xc1c1c1c1); - if ((uint)(a | b) != ~0U) - { + if ((uint)(a | b) != ~0U) { throw new TestFailedException("OR"); } - if ((uint)(a & b) != 0x41414141) - { + if ((uint)(a & b) != 0x41414141) { throw new TestFailedException("AND"); } - if ((uint)(a ^ b) != 0xbebebebe) - { + if ((uint)(a ^ b) != 0xbebebebe) { throw new TestFailedException("XOR"); } - if ((uint)(~a) != 0x80808080) - { + if ((uint)(~a) != 0x80808080) { throw new TestFailedException("NOT"); } - if ((uint)IPv4.NetMask(0) != 0) - { + if ((uint)IPv4.NetMask(0) != 0) { throw new TestFailedException("NetMask(0)"); } - if ((uint)IPv4.NetMask(17) != 0xffff8000) - { + if ((uint)IPv4.NetMask(17) != 0xffff8000) { throw new TestFailedException("NetMask(17)"); } - if ((uint)IPv4.NetMask(32) != 0xffffffffu) - { + if ((uint)IPv4.NetMask(32) != 0xffffffffu) { throw new TestFailedException("NetMask(32)"); } - try - { + try { IPv4 n = IPv4.NetMask(66); throw new TestFailedException("Bad Netmask +"); } - catch (ArgumentException) - { + catch (ArgumentException) { } - try - { + try { IPv4 n = IPv4.NetMask(-1); throw new TestFailedException("Bad Netmask -"); } - catch (ArgumentException) - { + catch (ArgumentException) { } } @@ -223,22 +193,18 @@ namespace System.Net.IP bool success = false; IPv4 testIP = IPv4.Zero; - try - { + try { testIP = IPv4.Parse(ipString); success = (testIP == expectedIP); } - catch (FormatException) - { + catch (FormatException) { } - if (success != expectedSuccess) - { + if (success != expectedSuccess) { throw new TestFailedException(ipString); } IPv4 dup = IPv4.Parse(testIP.ToString()); - if (testIP != dup) - { + if (testIP != dup) { throw new TestFailedException( String.Format("ToString {0}", testIP) ); @@ -296,31 +262,28 @@ namespace System.Net.IP if ((uint)++a != 0x0a000002) throw new TestFailedException("Increment"); - if ((uint)--a != 0x0a000001) + if ((uint) --a != 0x0a000001) throw new TestFailedException("Decrement 1"); - if ((uint)--a != 0x0a000000) + if ((uint) --a != 0x0a000000) throw new TestFailedException("Decrement 2"); - if ((uint)--a != 0x09ffffff) + if ((uint) --a != 0x09ffffff) throw new TestFailedException("Decrement 3"); } public static void TestCount() { - for (int i = IPv4.BitCount; i >= 0; i--) - { + for (int i = IPv4.BitCount; i >= 0; i--) { IPv4 addr = IPv4.NetMask(i); - if (IPv4.GetMaskLength(addr) != i) - { + if (IPv4.GetMaskLength(addr) != i) { throw new TestFailedException( String.Format("TestCount {0}", i) ); } IPv4 addrDup = IPv4.Parse(addr.ToString()); - if (addrDup != addr) - { + if (addrDup != addr) { throw new TestFailedException( String.Format("TestCountDup {0}", i) ); @@ -330,8 +293,7 @@ namespace System.Net.IP public static int Main() { - try - { + try { TestBasics(); TestCompare(); TestRoll(); @@ -340,8 +302,7 @@ namespace System.Net.IP TestMath(); TestCount(); } - catch (Exception e) - { + catch (Exception e) { Console.WriteLine("FAILED\nException {0}\nStack:\n{1}", e.Message, e.StackTrace); return 1; diff --git a/base/Libraries/System.Net.IP/TestIPv4Network.cs b/base/Libraries/System.Net.IP/TestIPv4Network.cs index 8c8ca80..058499d 100644 --- a/base/Libraries/System.Net.IP/TestIPv4Network.cs +++ b/base/Libraries/System.Net.IP/TestIPv4Network.cs @@ -55,49 +55,39 @@ namespace System.Net.IP throw new TestFailedException("10/1"); } - try - { + try { IPv4Network.Parse("10.0.0.1//2"); throw new TestFailedException("double slash"); } - catch (FormatException) - { + catch (FormatException) { } - try - { + try { IPv4Network.Parse("10.0.0.0/33"); throw new TestFailedException("netmask length"); } - catch (ArgumentException) - { + catch (ArgumentException) { } - try - { + try { IPv4Network.Parse("10.0.0.0/xx"); throw new TestFailedException("netmask content"); } - catch (FormatException) - { + catch (FormatException) { } - try - { + try { IPv4Network.Parse("10.x.0.0/10"); throw new TestFailedException("network content"); } - catch (FormatException) - { + catch (FormatException) { } - try - { + try { IPv4Network.Parse("10.0.0.0/3333333333333333333333333333"); throw new TestFailedException("netmask overflow"); } - catch (OverflowException) - { + catch (OverflowException) { } } @@ -107,71 +97,58 @@ namespace System.Net.IP IPv4Network outer = new IPv4Network(address, 24); IPv4Network inner = new IPv4Network(address, 26); - if (outer.Contains(outer) == false) - { + if (outer.Contains(outer) == false) { throw new TestFailedException("outer.Contains(outer)"); } - if (outer.Contains(inner) == false) - { + if (outer.Contains(inner) == false) { throw new TestFailedException("outer.Contains(inner)"); } - if (inner.Contains(outer) == true) - { + if (inner.Contains(outer) == true) { throw new TestFailedException("inner.Contains(outer)"); } - if (outer.Contains(address) == false) - { + if (outer.Contains(address) == false) { throw new TestFailedException("outer.Contains(address)"); } - if (inner.Contains(address) == false) - { + if (inner.Contains(address) == false) { throw new TestFailedException("inner.Contains(address)"); } - if (outer.IsMoreSpecificThan(inner) == true) - { + if (outer.IsMoreSpecificThan(inner) == true) { throw new TestFailedException("outer.IsMoreSpecificThan(inner)"); } - if (outer.IsLessSpecificThan(inner) == false) - { + if (outer.IsLessSpecificThan(inner) == false) { throw new TestFailedException("outer.IsLessSpecificThan(inner)"); } - if ((outer == outer) == false) - { + if ((outer == outer) == false) { throw new TestFailedException("operator=="); } - if ((outer != inner) == false) - { + if ((outer != inner) == false) { throw new TestFailedException("operator!="); } - if (outer.Contains(outer.UpperBound) == false) - { + if (outer.Contains(outer.UpperBound) == false) { throw new TestFailedException("outer.Contains(outer.UpperBound)"); } - if (outer.Contains(outer.LowerBound) == false) - { + if (outer.Contains(outer.LowerBound) == false) { throw new TestFailedException("outer.Contains(outer.LowerBound)"); } } public static int Main() { - try - { + try { TestBasics(); TestCompare(); } - catch (Exception e) - { + catch (Exception e) { Console.WriteLine("FAILED\nException {0}\nStack:\n{1}", e.Message, e.StackTrace); return 1; diff --git a/base/Libraries/System.Net.IP/TestIPv6.cs b/base/Libraries/System.Net.IP/TestIPv6.cs index 2c6140b..9d80962 100644 --- a/base/Libraries/System.Net.IP/TestIPv6.cs +++ b/base/Libraries/System.Net.IP/TestIPv6.cs @@ -37,8 +37,7 @@ namespace System.Net.IP if (aBytes.Length != 16) throw new TestFailedException("GetAddressBytes Length"); - for (int i = 0; i < aBytes.Length; i++) - { + for (int i = 0; i < aBytes.Length; i++) { if (aBytes[i] != xBytes[i]) throw new TestFailedException("GetAddressBytes Values"); } @@ -50,16 +49,14 @@ namespace System.Net.IP if (aBytes.Length != 16) throw new TestFailedException("GetAddressBytes Length"); - for (int i = 0; i < aBytes.Length; i++) - { + for (int i = 0; i < aBytes.Length; i++) { if (aBytes[i] != xBytes[i]) throw new TestFailedException("GetAddressBytes Values"); } // Test System.Net.IPAddress Constructor IPAddress ipa = new IPAddress(aBytes); - if (new IPv6(ipa) != a) - { + if (new IPv6(ipa) != a) { throw new TestFailedException("IPv6(IPAddress) Constructor"); } @@ -70,8 +67,7 @@ namespace System.Net.IP if (aBytes.Length != 16) throw new TestFailedException("GetAddressBytes Length"); - for (int i = 0; i < aBytes.Length; i++) - { + for (int i = 0; i < aBytes.Length; i++) { if (aBytes[i] != xBytes[i]) throw new TestFailedException("GetAddressBytes Values"); } @@ -82,53 +78,43 @@ namespace System.Net.IP IPv6 a = new IPv6(0x11111111, 0x22222222, 0x33333333, 0x44444444); IPv6 b = new IPv6(0x11111111, 0x22222222, 0x33333333, 0x44444443); - if ((a == a) == false) - { + if ((a == a) == false) { throw new TestFailedException("a == a"); } - if ((a != b) == false) - { + if ((a != b) == false) { throw new TestFailedException("a == b"); } - if ((a > b) == false) - { + if ((a > b) == false) { throw new TestFailedException("a > b"); } - if ((a >= b) == false) - { + if ((a >= b) == false) { throw new TestFailedException("a >= b"); } - if ((a >= a) == false) - { + if ((a >= a) == false) { throw new TestFailedException("a >= a"); } - if ((a > a) == true) - { + if ((a > a) == true) { throw new TestFailedException("a > a"); } - if ((a < b) == true) - { + if ((a < b) == true) { throw new TestFailedException("a < b"); } - if ((a <= b) == true) - { + if ((a <= b) == true) { throw new TestFailedException("a <= b"); } - if ((a <= a) != true) - { + if ((a <= a) != true) { throw new TestFailedException("a <= a"); } - if ((a < a) == true) - { + if ((a < a) == true) { throw new TestFailedException("a < a"); } } @@ -143,23 +129,19 @@ namespace System.Net.IP throw new TestFailedException("a << 4"); } - if ((a << 36) != new IPv6(0x9abcdef0, 0x24613578, 0xace9bdf0, 0)) - { + if ((a << 36) != new IPv6(0x9abcdef0, 0x24613578, 0xace9bdf0, 0)) { throw new TestFailedException("a << 36"); } - if ((a << 68) != new IPv6(0x24613578, 0xace9bdf0, 0, 0)) - { + if ((a << 68) != new IPv6(0x24613578, 0xace9bdf0, 0, 0)) { throw new TestFailedException("a << 68"); } - if ((a << 100) != new IPv6(0xace9bdf0, 0, 0, 0)) - { + if ((a << 100) != new IPv6(0xace9bdf0, 0, 0, 0)) { throw new TestFailedException("a << 100"); } - if ((a << 128) != new IPv6(0, 0, 0, 0)) - { + if ((a << 128) != new IPv6(0, 0, 0, 0)) { throw new TestFailedException("a << 128"); } @@ -169,23 +151,19 @@ namespace System.Net.IP throw new TestFailedException("a >> 4"); } - if ((a >> 36) != new IPv6(0, 0x00123456, 0x789abcde, 0xf0246135)) - { + if ((a >> 36) != new IPv6(0, 0x00123456, 0x789abcde, 0xf0246135)) { throw new TestFailedException("a >> 36"); } - if ((a >> 68) != new IPv6(0, 0, 0x00123456, 0x789abcde)) - { + if ((a >> 68) != new IPv6(0, 0, 0x00123456, 0x789abcde)) { throw new TestFailedException("a >> 68"); } - if ((a >> 100) != new IPv6(0, 0, 0, 0x00123456)) - { + if ((a >> 100) != new IPv6(0, 0, 0, 0x00123456)) { throw new TestFailedException("a >> 100"); } - if ((a >> 128) != new IPv6(0, 0, 0, 0)) - { + if ((a >> 128) != new IPv6(0, 0, 0, 0)) { throw new TestFailedException("a >> 128"); } } @@ -197,64 +175,53 @@ namespace System.Net.IP IPv6 goal; goal = new IPv6(~0U, ~0U, ~0U, ~0U); - if ((a | b) != goal) - { + if ((a | b) != goal) { throw new TestFailedException("OR"); } goal = new IPv6(0x41414141, 0x41414141, 0x41414141, 0x41414141); - if ((a & b) != goal) - { + if ((a & b) != goal) { throw new TestFailedException("AND"); } goal = new IPv6(0xbebebebe, 0xbebebebe, 0xbebebebe, 0xbebebebe); - if ((a ^ b) != goal) - { + if ((a ^ b) != goal) { throw new TestFailedException("XOR"); } goal = new IPv6(0x80808080, 0x80808080, 0x80808080, 0x80808080); - if (~a != goal) - { + if (~a != goal) { throw new TestFailedException("NOT"); } // Test netmask - for (int i = 0; i < 128; i++) - { + for (int i = 0; i < 128; i++) { IPv6 mask = IPv6.NetMask(i); goal = IPv6.AllOnes << (128 - i); - if (mask != goal) - { + if (mask != goal) { Console.WriteLine("{0} {1} {2}", i, mask, goal); throw new TestFailedException("NetMask"); } goal = ~(IPv6.AllOnes >> i); - if (mask != goal) - { + if (mask != goal) { Console.WriteLine("{0} {1} {2}", i, mask, goal); throw new TestFailedException("NetMask2"); } } - try - { + try { IPv6 n = IPv6.NetMask(129); throw new TestFailedException("Bad Netmask +"); } - catch (ArgumentException) - { + catch (ArgumentException) { } - try - { + try { IPv6 n = IPv6.NetMask(-1); throw new TestFailedException("Bad Netmask -"); } - catch (ArgumentException) - { + catch (ArgumentException) { } } @@ -337,11 +304,9 @@ namespace System.Net.IP ), }; - for (int i = 0; i < pairs.Length; i++) - { + for (int i = 0; i < pairs.Length; i++) { IPv6 a = IPv6.Parse(pairs[i].srep); - if (a != pairs[i].v6rep) - { + if (a != pairs[i].v6rep) { Console.WriteLine("{0} {1}", a, pairs[i].v6rep); throw new TestFailedException( String.Format("Parse {0}", i) @@ -349,8 +314,7 @@ namespace System.Net.IP } IPv6 dup = IPv6.Parse(a.ToString()); - if (dup != a) - { + if (dup != a) { throw new TestFailedException( String.Format("ToString {0}", i) ); @@ -368,15 +332,12 @@ namespace System.Net.IP "0:1:2:3:4:5:6:7:trailing junk", }; - for (int i = 0; i < badSreps.Length; i++) - { - try - { + for (int i = 0; i < badSreps.Length; i++) { + try { IPv6 bad = IPv6.Parse(badSreps[i]); throw new TestFailedException(badSreps[i]); } - catch (FormatException) - { + catch (FormatException) { } } } @@ -385,31 +346,26 @@ namespace System.Net.IP { IPv6 a = IPv6.Zero; - if (--a != IPv6.AllOnes) - { + if (--a != IPv6.AllOnes) { throw new TestFailedException("--"); } - if (++a != IPv6.Zero) - { + if (++a != IPv6.Zero) { throw new TestFailedException("++"); } } public static void TestCount() { - for (int i = 0; i < IPv6.BitCount; i++) - { + for (int i = 0; i < IPv6.BitCount; i++) { IPv6 addr = IPv6.NetMask(i); - if (IPv6.GetMaskLength(addr) != i) - { + if (IPv6.GetMaskLength(addr) != i) { throw new TestFailedException( String.Format("TestCount {0}", i) ); } IPv6 addrDup = IPv6.Parse(addr.ToString()); - if (addrDup != addr) - { + if (addrDup != addr) { throw new TestFailedException( String.Format("TestCountDup {0}", i) ); @@ -419,8 +375,7 @@ namespace System.Net.IP public static int Main() { - try - { + try { TestBasics(); TestCompare(); TestRoll(); @@ -429,8 +384,7 @@ namespace System.Net.IP TestMath(); TestCount(); } - catch (Exception e) - { + catch (Exception e) { Console.WriteLine("FAILED\nException {0}\nStack:\n{1}", e.Message, e.StackTrace); return 1; diff --git a/base/Libraries/System.Net.IP/TestIPv6Network.cs b/base/Libraries/System.Net.IP/TestIPv6Network.cs index f5fd8a6..0fce599 100644 --- a/base/Libraries/System.Net.IP/TestIPv6Network.cs +++ b/base/Libraries/System.Net.IP/TestIPv6Network.cs @@ -57,49 +57,39 @@ namespace System.Net.IP throw new TestFailedException("10::/1"); } - try - { + try { IPv6Network.Parse("10:://12"); throw new TestFailedException("double slash"); } - catch (FormatException) - { + catch (FormatException) { } - try - { + try { IPv6Network.Parse("10::/129"); throw new TestFailedException("netmask length"); } - catch (ArgumentException) - { + catch (ArgumentException) { } - try - { + try { IPv6Network.Parse("10::/xx"); throw new TestFailedException("netmask content"); } - catch (FormatException) - { + catch (FormatException) { } - try - { + try { IPv6Network.Parse("10::x/10"); throw new TestFailedException("network content"); } - catch (FormatException) - { + catch (FormatException) { } - try - { + try { IPv6Network.Parse("10::/3333333333333333333333333333"); throw new TestFailedException("netmask overflow"); } - catch (OverflowException) - { + catch (OverflowException) { } } @@ -109,71 +99,58 @@ namespace System.Net.IP IPv6Network outer = new IPv6Network(address, 124); IPv6Network inner = new IPv6Network(address, 126); - if (outer.Contains(outer) == false) - { + if (outer.Contains(outer) == false) { throw new TestFailedException("outer.Contains(outer)"); } - if (outer.Contains(inner) == false) - { + if (outer.Contains(inner) == false) { throw new TestFailedException("outer.Contains(inner)"); } - if (inner.Contains(outer) == true) - { + if (inner.Contains(outer) == true) { throw new TestFailedException("inner.Contains(outer)"); } - if (outer.Contains(address) == false) - { + if (outer.Contains(address) == false) { throw new TestFailedException("outer.Contains(address)"); } - if (inner.Contains(address) == false) - { + if (inner.Contains(address) == false) { throw new TestFailedException("inner.Contains(address)"); } - if (outer.IsMoreSpecificThan(inner) == true) - { + if (outer.IsMoreSpecificThan(inner) == true) { throw new TestFailedException("outer.IsMoreSpecificThan(inner)"); } - if (outer.IsLessSpecificThan(inner) == false) - { + if (outer.IsLessSpecificThan(inner) == false) { throw new TestFailedException("outer.IsLessSpecificThan(inner)"); } - if ((outer == outer) == false) - { + if ((outer == outer) == false) { throw new TestFailedException("operator=="); } - if ((outer != inner) == false) - { + if ((outer != inner) == false) { throw new TestFailedException("operator!="); } - if (outer.Contains(outer.UpperBound) == false) - { + if (outer.Contains(outer.UpperBound) == false) { throw new TestFailedException("outer.Contains(outer.UpperBound)"); } - if (outer.Contains(outer.LowerBound) == false) - { + if (outer.Contains(outer.LowerBound) == false) { throw new TestFailedException("outer.Contains(outer.LowerBound)"); } } public static int Main() { - try - { + try { TestBasics(); TestCompare(); } - catch (Exception e) - { + catch (Exception e) { Console.WriteLine("FAILED\nException {0}\nStack:\n{1}", e.Message, e.StackTrace); return 1; diff --git a/base/Libraries/System.Net/AssemblyInfo.sg b/base/Libraries/System.Net/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Libraries/System.Net/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Libraries/System.Net/Dns.cs b/base/Libraries/System.Net/Dns.cs index f58efee..e796b6a 100644 --- a/base/Libraries/System.Net/Dns.cs +++ b/base/Libraries/System.Net/Dns.cs @@ -4,14 +4,13 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Dns.cs -// // Notes: -// Ported from the Coriolis code base by maiken, with extensive changes to use +// Ported from the Coriolis code base, with extensive changes to use // the channel interface to our NetStack instead of native calls. // -namespace System.Net { +namespace System.Net +{ using System.Collections; using System.Text; using System.Net.Sockets; @@ -46,12 +45,9 @@ namespace System.Net { object slotLock) { // This initialization sequence is meant to be thread-safe - if (slot == null) - { - lock (slotLock) - { - if (slot == null) - { + if (slot == null) { + lock (slotLock) { + if (slot == null) { slot = Thread.AllocateDataSlot(); } } @@ -65,8 +61,7 @@ namespace System.Net { { LocalDataStoreSlot slot = SafeSlotFetchOrInitialize(ref DnsSlot, DnsSlotLock); - if (Thread.GetData(slot) == null) - { + if (Thread.GetData(slot) == null) { // We haven't created a channel for this thread yet. Create one. DNSContract.Imp! dnsImp; DNSContract.Exp! dnsExp; @@ -74,12 +69,10 @@ namespace System.Net { DNSContract.NewChannel(out dnsImp, out dnsExp); - try - { + try { epNS.SendBind(Bitter.FromString2(DNSContract.ModuleName), dnsExp); - switch receive - { + switch receive { case epNS.NakBind(ServiceContract.Exp:Start rejectedEP, error) : if (rejectedEP != null) { delete rejectedEP; @@ -97,8 +90,7 @@ namespace System.Net { break; } } - finally - { + finally { delete epNS; } } @@ -120,20 +112,17 @@ namespace System.Net { { LocalDataStoreSlot slot = SafeSlotFetchOrInitialize(ref IPSlot, IPSlotLock); - if (Thread.GetData(slot) == null) - { + if (Thread.GetData(slot) == null) { IPContract.Imp! ipImp; IPContract.Exp! ipExp; DirectoryServiceContract.Imp epNS = DirectoryService.NewClientEndpoint(); IPContract.NewChannel(out ipImp, out ipExp); - try - { + try { epNS.SendBind(Bitter.FromString2(IPContract.ModuleName), ipExp); - switch receive - { + switch receive { case epNS.NakBind(ServiceContract.Exp:Start rejectedEP, error) : // Do nothing; we will return null below. delete ipImp; @@ -151,8 +140,7 @@ namespace System.Net { break; } } - finally - { + finally { delete epNS; } } @@ -186,24 +174,20 @@ namespace System.Net { IPAddress[] retval; IPContract.Imp ipConn = GetIPConnection(); - try - { + try { char[][]! in ExHeap ifList; ArrayList addresses = new ArrayList(); ipConn.SendGetInterfaces(); ipConn.RecvInterfaceList(out ifList); - try - { - for (int i = 0; i < ifList.Length; ++i) - { + try { + for (int i = 0; i < ifList.Length; ++i) { expose(ifList[i]) { ipConn.SendGetInterfaceState((!)ifList[i]); ifList[i] = null; - switch receive - { + switch receive { case ipConn.InterfaceNotFound() : throw new SocketException(SocketErrors.SocketError); break; @@ -213,8 +197,7 @@ namespace System.Net { // Copy out all of this interface's addresses InterfaceIPInfo[] in ExHeap ipConfigs = ifInfo.ipConfigs; if (ipConfigs != null) { - for (int j = 0; j < ipConfigs.Length; ++j) - { + for (int j = 0; j < ipConfigs.Length; ++j) { addresses.Add(new IPAddress(ipConfigs[j].address)); } } @@ -225,15 +208,13 @@ namespace System.Net { } } } - finally - { + finally { delete ifList; // This should hold only NULLs by now } return (IPAddress[])addresses.ToArray(typeof(IPAddress)); } - finally - { + finally { ReleaseIPConnection(ipConn); } @@ -263,7 +244,7 @@ namespace System.Net { internal static IPHostEntry InternalGetHostByAddress(IPAddress! address) { // We only support IPv4 addresses under Singularity! - if ( address.AddressFamily != AddressFamily.InterNetwork ) { + if (address.AddressFamily != AddressFamily.InterNetwork) { // // Protocol not supported // @@ -281,21 +262,19 @@ namespace System.Net { { DNSContract.Imp dnsConn = GetDnsConnection(); - try - { + try { string[] aliases; IPv4[] addrs; - if (DNSImpConnection.Resolve(dnsConn, host, out aliases, out addrs)) - { + if (DNSImpConnection.Resolve(dnsConn, host, out aliases, out addrs)) { assert aliases != null; assert addrs != null; return DNSInfoToIPHostEntry(aliases, addrs); } - else - { throw new SocketException(SocketErrors.WSAHOST_NOT_FOUND); } + else { + throw new SocketException(SocketErrors.WSAHOST_NOT_FOUND); + } } - finally - { + finally { ReleaseDnsConnection(dnsConn); } } // InternalResolve @@ -305,12 +284,14 @@ namespace System.Net { IPHostEntry retval = new IPHostEntry(); IPAddress[] addressList = new IPAddress[addrs.Length]; - for (int i = 0; i < addrs.Length ; ++i) - { addressList[i] = new IPAddress(addrs[i]); } + for (int i = 0; i < addrs.Length; ++i) { + addressList[i] = new IPAddress(addrs[i]); + } string[] resultAliases = new string[aliases.Length - 1]; - for (int i = 1; i < aliases.Length; ++i) - { resultAliases[i] = aliases[i]; } + for (int i = 1; i < aliases.Length; ++i) { + resultAliases[i] = aliases[i]; + } retval.Aliases = resultAliases; retval.AddressList = addressList; @@ -321,8 +302,7 @@ namespace System.Net { public static string GetHostName() { IPContract.Imp ipConn = GetIPConnection(); - try - { + try { char[]! in ExHeap hostName; ipConn.SendGetHostName(); ipConn.RecvHostName(out hostName); @@ -330,8 +310,7 @@ namespace System.Net { delete hostName; return strHostName; } - finally - { + finally { ReleaseIPConnection(ipConn); } } diff --git a/base/Libraries/System.Net/EndPoint.cs b/base/Libraries/System.Net/EndPoint.cs index a6af503..83ebcd3 100644 --- a/base/Libraries/System.Net/EndPoint.cs +++ b/base/Libraries/System.Net/EndPoint.cs @@ -4,14 +4,13 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: EndPoint.cs -// using System; using System.Runtime.InteropServices; using System.Net.Sockets; -namespace System.Net { +namespace System.Net +{ // Generic abstraction to identify network addresses diff --git a/base/Libraries/System.Net/IPAddress.cs b/base/Libraries/System.Net/IPAddress.cs index e7c63e2..3496506 100644 --- a/base/Libraries/System.Net/IPAddress.cs +++ b/base/Libraries/System.Net/IPAddress.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: IPAddress.cs -// // Note: // This file is a wrapper around Singularity's internal networking types // diff --git a/base/Libraries/System.Net/IPEndPoint.cs b/base/Libraries/System.Net/IPEndPoint.cs index 8090e8f..dc2798a 100644 --- a/base/Libraries/System.Net/IPEndPoint.cs +++ b/base/Libraries/System.Net/IPEndPoint.cs @@ -4,10 +4,9 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: IPEndPoint.cs -// -namespace System.Net { +namespace System.Net +{ using System.Net.Sockets; using System.Globalization; @@ -63,7 +62,7 @@ namespace System.Net { /// Creates a new instance of the IPEndPoint class with the specified address and port. /// public IPEndPoint(IPAddress address, int port) { - if (address==null) { + if (address == null) { throw new ArgumentNullException("address"); } if (!ValidationHelper.ValidateTcpPort(port)) { @@ -140,16 +139,15 @@ namespace System.Net { throw new ArgumentException("Mismatched address family"); } - if (socketAddress.Size<8) { + if (socketAddress.Size < 8) { throw new ArgumentException("Socket Address size too small"); } - // HACKHACK we don't support v6 in Singularity - if ( this.AddressFamily == AddressFamily.InterNetworkV6 ) { + // HACKHACK: we don't support v6 in Singularity + if (this.AddressFamily == AddressFamily.InterNetworkV6) { throw new NotSupportedException(); } - else - { + else { // // strip out of SocketAddress information on the EndPoint // diff --git a/base/Libraries/System.Net/IPHostEntry.cs b/base/Libraries/System.Net/IPHostEntry.cs index bb22636..f8b825a 100644 --- a/base/Libraries/System.Net/IPHostEntry.cs +++ b/base/Libraries/System.Net/IPHostEntry.cs @@ -7,7 +7,8 @@ // File: IPHostEntry. // -namespace System.Net { +namespace System.Net +{ // Host information /// diff --git a/base/Libraries/System.Net/Internal.cs b/base/Libraries/System.Net/Internal.cs index 9789fc3..913d5ac 100644 --- a/base/Libraries/System.Net/Internal.cs +++ b/base/Libraries/System.Net/Internal.cs @@ -4,10 +4,8 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: Internal.cs -// // Note: -// This file was ported, from the Coriolis codebase to Singularity by Mark Aiken. +// This file was ported from the Coriolis codebase to Singularity. // namespace System.Net @@ -54,25 +52,28 @@ namespace System.Net '\n'}; public static string [] MakeEmptyArrayNull(string [] stringArray) { - if ( stringArray == null || stringArray.Length == 0 ) { + if (stringArray == null || stringArray.Length == 0) { return null; - } else { + } + else { return stringArray; } } public static string MakeStringNull(string stringValue) { - if ( stringValue == null || stringValue.Length == 0) { + if (stringValue == null || stringValue.Length == 0) { return null; - } else { + } + else { return stringValue; } } public static string MakeStringEmpty(string stringValue) { - if ( stringValue == null || stringValue.Length == 0) { + if (stringValue == null || stringValue.Length == 0) { return String.Empty; - } else { + } + else { return stringValue; } } @@ -80,15 +81,16 @@ namespace System.Net public static int HashCode(object objectValue) { if (objectValue == null) { return -1; - } else { + } + else { return objectValue.GetHashCode(); } } public static string ExceptionMessage(Exception exception) { - if (exception==null) { + if (exception == null) { return string.Empty; } - if (exception.InnerException==null) { + if (exception.InnerException == null) { return exception.Message; } return exception.Message + " (" + ExceptionMessage(exception.InnerException) + ")"; @@ -97,22 +99,28 @@ namespace System.Net public static string ToString(object objectValue) { if (objectValue == null) { return "(null)"; - } else if (objectValue is string && ((string)objectValue).Length==0) { + } + else if (objectValue is string && ((string)objectValue).Length == 0) { return "(string.empty)"; - } else if (objectValue is Exception) { + } + else if (objectValue is Exception) { return ExceptionMessage(objectValue as Exception); - } else if (objectValue is IntPtr) { + } + else if (objectValue is IntPtr) { return "0x" + String.Format("{0x}", ((IntPtr)objectValue)); - } else { + } + else { return objectValue.ToString(); } } public static string HashString(object objectValue) { if (objectValue == null) { return "(null)"; - } else if (objectValue is string && ((string)objectValue).Length==0) { + } + else if (objectValue is string && ((string)objectValue).Length == 0) { return "(string.empty)"; - } else { + } + else { return objectValue.GetHashCode().ToString(); } } diff --git a/base/Libraries/System.Net/SocketAddress.cs b/base/Libraries/System.Net/SocketAddress.cs index 8831b2d..5c87570 100644 --- a/base/Libraries/System.Net/SocketAddress.cs +++ b/base/Libraries/System.Net/SocketAddress.cs @@ -4,13 +4,12 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: HttpException -// // Note: -// This file was ported, from the Coriolis codebase to Singularity by Mark Aiken. +// This file was ported from the Coriolis codebase to Singularity. // -namespace System.Net { +namespace System.Net +{ using System; using System.Net.Sockets; @@ -64,13 +63,13 @@ namespace System.Net { // // access // - if (offset<0 || offset>=Size) { + if (offset < 0 || offset >= Size) { throw new IndexOutOfRangeException(); } return m_Buffer[offset]; } set { - if (offset<0 || offset>=Size) { + if (offset < 0 || offset >= Size) { throw new IndexOutOfRangeException(); } if (m_Buffer[offset] != value) { @@ -84,7 +83,7 @@ namespace System.Net { } public SocketAddress(AddressFamily family, int size) { - if (sizeWriteableOffset) { + for (int i = WriteableOffset; i < this.Size; i++) { + if (i > WriteableOffset) { bytes.Append(","); } bytes.Append(this[i].ToString()); diff --git a/base/Libraries/System.Net/SocketException.cs b/base/Libraries/System.Net/SocketException.cs index 3d11f58..d424f15 100644 --- a/base/Libraries/System.Net/SocketException.cs +++ b/base/Libraries/System.Net/SocketException.cs @@ -7,7 +7,8 @@ // File: SocketException.cs // -namespace System.Net.Sockets { +namespace System.Net.Sockets +{ using System; using NetStack.Contracts; @@ -57,8 +58,7 @@ namespace System.Net.Sockets { public static SocketErrors GetSocketErrorForTcpError(TcpError error) { - switch (error) - { + switch (error) { // These are defined in Contracts\NetStack.Contracts\TcpConnectionContract.sg. case TcpError.Unknown: return SocketErrors.SocketError; case TcpError.AlreadyConnected: return SocketErrors.WSAEISCONN; @@ -76,8 +76,7 @@ namespace System.Net.Sockets { public static string GetMessageForTcpError(TcpError error) { - switch (error) - { + switch (error) { // These are defined in Contracts\NetStack.Contracts\TcpConnectionContract.sg. case TcpError.Unknown: return "Unknown error"; case TcpError.AlreadyConnected: return "The connection is already in use."; diff --git a/base/Libraries/System.Net/Sockets/AddressFamily.cs b/base/Libraries/System.Net/Sockets/AddressFamily.cs index a7b7ad1..2668708 100644 --- a/base/Libraries/System.Net/Sockets/AddressFamily.cs +++ b/base/Libraries/System.Net/Sockets/AddressFamily.cs @@ -1,10 +1,9 @@ //------------------------------------------------------------------------------ -// // Copyright (c) Microsoft Corporation. All rights reserved. -// //------------------------------------------------------------------------------ -namespace System.Net.Sockets { +namespace System.Net.Sockets +{ //| /// @@ -171,48 +170,48 @@ namespace System.Net.Sockets { Max = 29, // Max -/* -#define AF_UNSPEC 0 // unspecified - -// Although AF_UNSPEC is defined for backwards compatibility, using -// AF_UNSPEC for the "af" parameter when creating a socket is STRONGLY -// DISCOURAGED. The interpretation of the "protocol" parameter -// depends on the actual address family chosen. As environments grow -// to include more and more address families that use overlapping -// protocol values there is more and more chance of choosing an -// undesired address family when AF_UNSPEC is used. - -#define AF_UNIX 1 // local to host (pipes, portals) -#define AF_INET 2 // internetwork: UDP, TCP, etc. -#define AF_IMPLINK 3 // arpanet imp addresses -#define AF_PUP 4 // pup protocols: e.g. BSP -#define AF_CHAOS 5 // mit CHAOS protocols -#define AF_NS 6 // XEROX NS protocols -#define AF_IPX AF_NS // IPX protocols: IPX, SPX, etc. -#define AF_ISO 7 // ISO protocols -#define AF_OSI AF_ISO // OSI is ISO -#define AF_ECMA 8 // european computer manufacturers -#define AF_DATAKIT 9 // datakit protocols -#define AF_CCITT 10 // CCITT protocols, X.25 etc -#define AF_SNA 11 // IBM SNA -#define AF_DECnet 12 // DECnet -#define AF_DLI 13 // Direct data link interface -#define AF_LAT 14 // LAT -#define AF_HYLINK 15 // NSC Hyperchannel -#define AF_APPLETALK 16 // AppleTalk -#define AF_NETBIOS 17 // NetBios-style addresses -#define AF_VOICEVIEW 18 // VoiceView -#define AF_FIREFOX 19 // Protocols from Firefox -#define AF_UNKNOWN1 20 // Somebody is using this! -#define AF_BAN 21 // Banyan -#define AF_ATM 22 // Native ATM Services -#define AF_INET6 23 // Internetwork Version 6 -#define AF_CLUSTER 24 // Microsoft Wolfpack -#define AF_12844 25 // IEEE 1284.4 WG AF -#define AF_IRDA 26 // IrDA -#define AF_NETDES 28 // Network Designers OSI & gateway enabled protocols -#define AF_MAX 29 -*/ +// +//#define AF_UNSPEC 0 // unspecified +// +//// Although AF_UNSPEC is defined for backwards compatibility, using +//// AF_UNSPEC for the "af" parameter when creating a socket is STRONGLY +//// DISCOURAGED. The interpretation of the "protocol" parameter +//// depends on the actual address family chosen. As environments grow +//// to include more and more address families that use overlapping +//// protocol values there is more and more chance of choosing an +//// undesired address family when AF_UNSPEC is used. +// +//#define AF_UNIX 1 // local to host (pipes, portals) +//#define AF_INET 2 // internetwork: UDP, TCP, etc. +//#define AF_IMPLINK 3 // arpanet imp addresses +//#define AF_PUP 4 // pup protocols: e.g. BSP +//#define AF_CHAOS 5 // mit CHAOS protocols +//#define AF_NS 6 // XEROX NS protocols +//#define AF_IPX AF_NS // IPX protocols: IPX, SPX, etc. +//#define AF_ISO 7 // ISO protocols +//#define AF_OSI AF_ISO // OSI is ISO +//#define AF_ECMA 8 // european computer manufacturers +//#define AF_DATAKIT 9 // datakit protocols +//#define AF_CCITT 10 // CCITT protocols, X.25 etc +//#define AF_SNA 11 // IBM SNA +//#define AF_DECnet 12 // DECnet +//#define AF_DLI 13 // Direct data link interface +//#define AF_LAT 14 // LAT +//#define AF_HYLINK 15 // NSC Hyperchannel +//#define AF_APPLETALK 16 // AppleTalk +//#define AF_NETBIOS 17 // NetBios-style addresses +//#define AF_VOICEVIEW 18 // VoiceView +//#define AF_FIREFOX 19 // Protocols from Firefox +//#define AF_UNKNOWN1 20 // Somebody is using this! +//#define AF_BAN 21 // Banyan +//#define AF_ATM 22 // Native ATM Services +//#define AF_INET6 23 // Internetwork Version 6 +//#define AF_CLUSTER 24 // Microsoft Wolfpack +//#define AF_12844 25 // IEEE 1284.4 WG AF +//#define AF_IRDA 26 // IrDA +//#define AF_NETDES 28 // Network Designers OSI & gateway enabled protocols +//#define AF_MAX 29 +// }; // enum AddressFamily diff --git a/base/Libraries/System.Net/Sockets/ProtocolFamily.cs b/base/Libraries/System.Net/Sockets/ProtocolFamily.cs index c30cc78..af8db6e 100644 --- a/base/Libraries/System.Net/Sockets/ProtocolFamily.cs +++ b/base/Libraries/System.Net/Sockets/ProtocolFamily.cs @@ -4,7 +4,8 @@ // //------------------------------------------------------------------------------ -namespace System.Net.Sockets { +namespace System.Net.Sockets +{ /// /// diff --git a/base/Libraries/System.Net/Sockets/ProtocolType.cs b/base/Libraries/System.Net/Sockets/ProtocolType.cs index beb4a60..c0d8532 100644 --- a/base/Libraries/System.Net/Sockets/ProtocolType.cs +++ b/base/Libraries/System.Net/Sockets/ProtocolType.cs @@ -4,7 +4,8 @@ // //------------------------------------------------------------------------------ -namespace System.Net.Sockets { +namespace System.Net.Sockets +{ using System; /// @@ -119,27 +120,27 @@ namespace System.Net.Sockets { /// Unknown = -1, // unknown protocol type -/* -consider adding: - -#define IPPROTO_RSVP 0x2e -#define DNPROTO_NSP 1 // DECnet NSP transport protocol -#define ISOPROTO_TP_CONS 25 // Transport over CONS -#define ISOPROTO_CLTP_CONS tba // Connectionless Transport over CONS -#define ISOPROTO_TP4_CLNS 29 // Transport class 4 over CLNS -#define ISOPROTO_CLTP_CLNS 30 // Connectionless Transport over CLNS -#define ISOPROTO_X25 32 // X.25 -#define ISOPROTO_X25PVC tba // Permanent Virtual Circuit -#define ISOPROTO_X25SVC ISOPROTO_X25 // Switched Virtual Circuit -#define ISOPROTO_TP ISOPROTO_TP4_CLNS -#define ISOPROTO_CLTP ISOPROTO_CLTP_CLNS -#define ISOPROTO_TP0_TCP tba // Transport class 0 over TCP (RFC1006) -#define ATMPROTO_AALUSER 0x00 // User-defined AAL -#define ATMPROTO_AAL1 0x01 // AAL 1 -#define ATMPROTO_AAL2 0x02 // AAL 2 -#define ATMPROTO_AAL34 0x03 // AAL 3/4 -#define ATMPROTO_AAL5 0x05 // AAL 5 -*/ +// +//consider adding: +// +//#define IPPROTO_RSVP 0x2e +//#define DNPROTO_NSP 1 // DECnet NSP transport protocol +//#define ISOPROTO_TP_CONS 25 // Transport over CONS +//#define ISOPROTO_CLTP_CONS tba // Connectionless Transport over CONS +//#define ISOPROTO_TP4_CLNS 29 // Transport class 4 over CLNS +//#define ISOPROTO_CLTP_CLNS 30 // Connectionless Transport over CLNS +//#define ISOPROTO_X25 32 // X.25 +//#define ISOPROTO_X25PVC tba // Permanent Virtual Circuit +//#define ISOPROTO_X25SVC ISOPROTO_X25 // Switched Virtual Circuit +//#define ISOPROTO_TP ISOPROTO_TP4_CLNS +//#define ISOPROTO_CLTP ISOPROTO_CLTP_CLNS +//#define ISOPROTO_TP0_TCP tba // Transport class 0 over TCP (RFC1006) +//#define ATMPROTO_AALUSER 0x00 // User-defined AAL +//#define ATMPROTO_AAL1 0x01 // AAL 1 +//#define ATMPROTO_AAL2 0x02 // AAL 2 +//#define ATMPROTO_AAL34 0x03 // AAL 3/4 +//#define ATMPROTO_AAL5 0x05 // AAL 5 +// } // enum ProtocolType diff --git a/base/Libraries/System.Net/Sockets/SelectMode.cs b/base/Libraries/System.Net/Sockets/SelectMode.cs index cdd67ef..98c2cb6 100644 --- a/base/Libraries/System.Net/Sockets/SelectMode.cs +++ b/base/Libraries/System.Net/Sockets/SelectMode.cs @@ -4,7 +4,8 @@ // //------------------------------------------------------------------------------ -namespace System.Net.Sockets { +namespace System.Net.Sockets +{ using System; /// diff --git a/base/Libraries/System.Net/Sockets/Socket.cs b/base/Libraries/System.Net/Sockets/Socket.cs index b949bb0..9c4aafd 100644 --- a/base/Libraries/System.Net/Sockets/Socket.cs +++ b/base/Libraries/System.Net/Sockets/Socket.cs @@ -40,8 +40,7 @@ namespace System.Net.Sockets { // We only support TCP streams - if (addressFamily != AddressFamily.InterNetwork) - { + if (addressFamily != AddressFamily.InterNetwork) { throw new SocketException(SocketErrors.WSAEAFNOSUPPORT); } @@ -63,8 +62,7 @@ namespace System.Net.Sockets public void Dispose() { - if (!disposed) - { + if (!disposed) { internalSocket.Dispose(); disposed = true; } @@ -93,25 +91,25 @@ namespace System.Net.Sockets internalSocket.Connect(remoteEP); } - /* - public object GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName) - { - // TODO - throw new NotSupportedException(); - } - - public void GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, byte[] optionValue) - { - // TODO - throw new NotSupportedException(); - } - - public byte[] GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionLength) - { - // TODO - throw new NotSupportedException(); - } - */ + // + //public object GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName) + //{ + // // TODO + // throw new NotSupportedException(); + //} +// + //public void GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, byte[] optionValue) + //{ + // // TODO + // throw new NotSupportedException(); + //} +// + //public byte[] GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionLength) + //{ + // // TODO + // throw new NotSupportedException(); + //} + // public void Listen(int backlog) { @@ -140,20 +138,19 @@ namespace System.Net.Sockets public int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags) { - if (buffer == null) - { + if (buffer == null) { throw new ArgumentNullException("buffer"); } return internalSocket.Receive(buffer, offset, size, socketFlags); } - /* - public static void Select(IList checkRead, IList checkWrite, IList checkError, int microSeconds) - { - // TODO - throw new NotSupportedException(); - } - */ + // + //public static void Select(IList checkRead, IList checkWrite, IList checkError, int microSeconds) + //{ + // // TODO + // throw new NotSupportedException(); + //} + // public int Send(byte[] buffer, int size, SocketFlags socketFlags) { @@ -178,24 +175,24 @@ namespace System.Net.Sockets return internalSocket.Send(buffer, offset, size, socketFlags); } - /* - public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue) - { - // TODO - throw new NotSupportedException(); - } - - public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, byte[] optionValue) - { - // TODO - throw new NotSupportedException(); - } - - public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, bool optionValue) - { - SetSocketOption(optionLevel, optionName, (optionValue?1:0)); - } - */ + // + //public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue) + //{ + // // TODO + // throw new NotSupportedException(); + //} +// + //public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, byte[] optionValue) + //{ + // // TODO + // throw new NotSupportedException(); + //} +// + //public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, bool optionValue) + //{ + // SetSocketOption(optionLevel, optionName, (optionValue?1:0)); + //} + // public void Shutdown(SocketShutdown how) { diff --git a/base/Libraries/System.Net/Sockets/SocketErrors.cs b/base/Libraries/System.Net/Sockets/SocketErrors.cs index e5e4c76..53dd722 100644 --- a/base/Libraries/System.Net/Sockets/SocketErrors.cs +++ b/base/Libraries/System.Net/Sockets/SocketErrors.cs @@ -4,7 +4,8 @@ // //------------------------------------------------------------------------------ -namespace System.Net.Sockets { +namespace System.Net.Sockets +{ using System; /// @@ -45,10 +46,10 @@ namespace System.Net.Sockets { ERROR_OPERATION_ABORTED = 995, - /* - * All Windows Sockets error constants are biased by WSABASEERR from - * the "normal" - */ + // + // All Windows Sockets error constants are biased by WSABASEERR from + // the "normal" + // /// /// /// The base value of all socket error constants. All other socket errors are @@ -58,9 +59,9 @@ namespace System.Net.Sockets { WSABASEERR = 10000, - /* - * Windows Sockets definitions of regular Microsoft C error constants - */ + // + // Windows Sockets definitions of regular Microsoft C error constants + // /// /// /// A blocking socket call was canceled. @@ -100,9 +101,9 @@ namespace System.Net.Sockets { WSAEMFILE = (WSABASEERR+24), - /* - * Windows Sockets definitions of regular Berkeley error constants - */ + // + // Windows Sockets definitions of regular Berkeley error constants + // /// /// /// Resource temporarily unavailable. @@ -329,9 +330,9 @@ namespace System.Net.Sockets { WSAEDISCON = (WSABASEERR+101), - /* - * Extended Windows Sockets error constant definitions - */ + // + // Extended Windows Sockets error constant definitions + // /// /// /// Network subsystem is unavailable. @@ -351,16 +352,16 @@ namespace System.Net.Sockets { /// WSANOTINITIALISED = (WSABASEERR+93), - /* - * Error return codes from gethostbyname() and gethostbyaddr() - * = (when using the resolver). Note that these errors are - * retrieved via WSAGetLastError() and must therefore follow - * the rules for avoiding clashes with error numbers from - * specific implementations or language run-time systems. - * For this reason the codes are based at WSABASEERR+1001. - * Note also that [WSA]NO_ADDRESS is defined only for - * compatibility purposes. - */ + // + // Error return codes from gethostbyname() and gethostbyaddr() + // = (when using the resolver). Note that these errors are + // retrieved via WSAGetLastError() and must therefore follow + // the rules for avoiding clashes with error numbers from + // specific implementations or language run-time systems. + // For this reason the codes are based at WSABASEERR+1001. + // Note also that [WSA]NO_ADDRESS is defined only for + // compatibility purposes. + // /// /// diff --git a/base/Libraries/System.Net/Sockets/SocketFlags.cs b/base/Libraries/System.Net/Sockets/SocketFlags.cs index 4ca755f..59f769e 100644 --- a/base/Libraries/System.Net/Sockets/SocketFlags.cs +++ b/base/Libraries/System.Net/Sockets/SocketFlags.cs @@ -4,7 +4,8 @@ // //------------------------------------------------------------------------------ -namespace System.Net.Sockets { +namespace System.Net.Sockets +{ using System; /// diff --git a/base/Libraries/System.Net/Sockets/SocketOptionLevel.cs b/base/Libraries/System.Net/Sockets/SocketOptionLevel.cs index da9303b..39d6624 100644 --- a/base/Libraries/System.Net/Sockets/SocketOptionLevel.cs +++ b/base/Libraries/System.Net/Sockets/SocketOptionLevel.cs @@ -4,7 +4,8 @@ // //------------------------------------------------------------------------------ -namespace System.Net.Sockets { +namespace System.Net.Sockets +{ using System; // diff --git a/base/Libraries/System.Net/Sockets/SocketOptionName.cs b/base/Libraries/System.Net/Sockets/SocketOptionName.cs index faf8b23..b4f070e 100644 --- a/base/Libraries/System.Net/Sockets/SocketOptionName.cs +++ b/base/Libraries/System.Net/Sockets/SocketOptionName.cs @@ -4,7 +4,8 @@ // //------------------------------------------------------------------------------ -namespace System.Net.Sockets { +namespace System.Net.Sockets +{ using System; // diff --git a/base/Libraries/System.Net/Sockets/SocketShutdown.cs b/base/Libraries/System.Net/Sockets/SocketShutdown.cs index 7305899..d3cf61e 100644 --- a/base/Libraries/System.Net/Sockets/SocketShutdown.cs +++ b/base/Libraries/System.Net/Sockets/SocketShutdown.cs @@ -4,7 +4,8 @@ // //------------------------------------------------------------------------------ -namespace System.Net.Sockets { +namespace System.Net.Sockets +{ using System; /// diff --git a/base/Libraries/System.Net/Sockets/SocketType.cs b/base/Libraries/System.Net/Sockets/SocketType.cs index 4115a84..a4939e3 100644 --- a/base/Libraries/System.Net/Sockets/SocketType.cs +++ b/base/Libraries/System.Net/Sockets/SocketType.cs @@ -4,7 +4,8 @@ // //------------------------------------------------------------------------------ -namespace System.Net.Sockets { +namespace System.Net.Sockets +{ /// /// diff --git a/base/Libraries/System.Net/Sockets/TcpSocket.cs b/base/Libraries/System.Net/Sockets/TcpSocket.cs index f315e14..c8db6ce 100644 --- a/base/Libraries/System.Net/Sockets/TcpSocket.cs +++ b/base/Libraries/System.Net/Sockets/TcpSocket.cs @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: TcpSocket.cs -// // Note: // This is a port of the System.Net.Sockets.Socket class to Singularity. // Currently, it only supports TCP sockets. @@ -38,7 +36,7 @@ namespace System.Net.Sockets // the user. private TrackedBytesBuffer m_DataBuffer; - // NOTE not sure what error to specify when an operation + // NOTE: not sure what error to specify when an operation // is not allowed by the current socket state; this is the value // I use. Change it if it's inappropriate private const int BAD_SOCKET_STATE_ERR = (int)SocketErrors.WSAEOPNOTSUPP; @@ -62,15 +60,15 @@ namespace System.Net.Sockets else if(conn.InState(TcpConnectionContract.SendOnly.Value)) { return true; } - if(conn.InState(TcpConnectionContract.ReceiveOnly.Value)) { + if (conn.InState(TcpConnectionContract.ReceiveOnly.Value)) { // DebugStub.Break(); return false; } - if(conn.InState(TcpConnectionContract.ReadyState.Value)) { + if (conn.InState(TcpConnectionContract.ReadyState.Value)) { DebugStub.Break(); return false; } - if(conn.InState(TcpConnectionContract.Listening.Value)) { + if (conn.InState(TcpConnectionContract.Listening.Value)) { DebugStub.Break(); return false; } @@ -119,8 +117,7 @@ namespace System.Net.Sockets TcpConnectionContract.Imp! conn = GetChannelOrThrow(); - try - { + try { // We need to be connected or listening to retrieve the local // endpoint if ((!conn.InState(TcpConnectionContract.Listening.Value)) && @@ -135,8 +132,7 @@ namespace System.Net.Sockets conn.RecvPort(out localPort); return true; } - finally - { + finally { ReleaseChannel(conn); } } @@ -152,11 +148,9 @@ namespace System.Net.Sockets TcpConnectionContract.Imp! conn = GetChannelOrThrow(); - try - { + try { // Need to be connected for the remote endpoint to be defined - if (!IsConnected(conn)) - { + if (!IsConnected(conn)) { return false; } @@ -166,8 +160,7 @@ namespace System.Net.Sockets conn.RecvPort(out localPort); return true; } - finally - { + finally { ReleaseChannel(conn); } } @@ -183,13 +176,11 @@ namespace System.Net.Sockets bool keepTrying = true; int timeToSleep = microSeconds; - while (keepTrying) - { + while (keepTrying) { keepTrying = false; // defensive conn.SendPollRead(timeToSleep); - switch receive - { + switch receive { case conn.Data(byte[]! in ExHeap readData) : // Tack this onto the data buffer. Data is consumed by AddToTail() dataBuffer.AddToTail(readData); @@ -230,18 +221,15 @@ namespace System.Net.Sockets { TcpConnectionContract.Imp conn = GetChannelOrThrow(); - try - { - if (CanRead(conn)) - { + try { + if (CanRead(conn)) { // Drain as much data from the netstack as possible DrainDataNonBlock(conn, m_DataBuffer, microSeconds); } return m_DataBuffer.Count; } - finally - { + finally { ReleaseChannel(conn); } } @@ -254,8 +242,7 @@ namespace System.Net.Sockets { conn.SendRead(); - switch receive - { + switch receive { case conn.Data(byte[]! in ExHeap readData) : // Tack this onto the data buffer. Data is consumed by AddToTail. dataBuffer.AddToTail(readData); @@ -298,12 +285,10 @@ namespace System.Net.Sockets TcpContract.NewChannel(out impConn, out expConn); DirectoryServiceContract.Imp! epNS = DirectoryService.NewClientEndpoint(); - try - { + try { epNS.SendBind(Bitter.FromString2(TcpContract.ModuleName), expConn); - switch receive - { + switch receive { case epNS.NakBind(ServiceContract.Exp:Start rejectedEP, error) : // failure if (rejectedEP != null) { @@ -318,13 +303,11 @@ namespace System.Net.Sockets break; } } - finally - { + finally { delete epNS; } - try - { + try { // Create a new Tcp connection TcpConnectionContract.Exp! tcpExpConn; TcpConnectionContract.Imp! tcpImpConn; @@ -338,8 +321,7 @@ namespace System.Net.Sockets Initialize(tcpImpConn); } - finally - { + finally { delete impConn; } } @@ -358,10 +340,8 @@ namespace System.Net.Sockets // We need to be in the listening state TcpConnectionContract.Imp conn = GetChannelOrThrow(); - try - { - if (!conn.InState(TcpConnectionContract.Listening.Value)) - { + try { + if (!conn.InState(TcpConnectionContract.Listening.Value)) { throw new SocketException(BAD_SOCKET_STATE_ERR); } @@ -381,23 +361,20 @@ namespace System.Net.Sockets return new TcpSocket(newImpConn); } - finally - { + finally { ReleaseChannel(conn); } } internal override void Bind(EndPoint localEP) { - if (localEP == null) - { + if (localEP == null) { throw new ArgumentNullException("localEP"); } IPEndPoint ep = localEP as IPEndPoint; - if (ep == null) - { + if (ep == null) { // Illegal argument throw new SocketException(SocketErrors.WSAEINVAL); } @@ -405,17 +382,14 @@ namespace System.Net.Sockets // Make sure we're in the ReadyState state TcpConnectionContract.Imp conn = GetChannelOrThrow(); - try - { - if (!conn.InState(TcpConnectionContract.ReadyState.Value)) - { + try { + if (!conn.InState(TcpConnectionContract.ReadyState.Value)) { throw new SocketException(BAD_SOCKET_STATE_ERR); } conn.SendBindLocalEndPoint((uint) ep.Address.m_addr, (ushort) ep.Port); - switch receive - { + switch receive { case conn.OK() : // success! break; @@ -424,15 +398,14 @@ namespace System.Net.Sockets break; } } - finally - { + finally { ReleaseChannel(conn); } } internal override void Close() { - // NOTE we should add support for the SO_DONTLINGER and SO_LINGER + // NOTE: we should add support for the SO_DONTLINGER and SO_LINGER // socket options, and then call either Close() or Abort() on our underlying // channel. We will also need to add support for sleeping-close to the // NetStack to support nonzero SO_LINGER values. @@ -447,8 +420,7 @@ namespace System.Net.Sockets ReleaseChannel(conn); throw new SocketException(BAD_SOCKET_STATE_ERR); } - else - { + else { conn.SendClose(); ReleaseChannel(conn); Dispose(); // This discards the channel @@ -457,15 +429,13 @@ namespace System.Net.Sockets internal override void Connect(EndPoint remoteEP) { - if (remoteEP == null) - { + if (remoteEP == null) { throw new ArgumentNullException("remoteEP"); } IPEndPoint ep = remoteEP as IPEndPoint; - if (ep == null) - { + if (ep == null) { // Illegal argument throw new SocketException(SocketErrors.WSAEINVAL); } @@ -480,13 +450,11 @@ namespace System.Net.Sockets throw new SocketException(BAD_SOCKET_STATE_ERR); } - try - { + try { conn.SendConnect((uint)ep.Address.m_addr, unchecked((ushort)ep.Port)); - switch receive - { + switch receive { case conn.CouldNotConnect(TcpError error) : string message = TcpException.GetMessageForTcpError(error); throw new TcpException(error, String.Format("A TCP connection could not be established to endpoint {0}. {1}", @@ -497,48 +465,44 @@ namespace System.Net.Sockets break; } } - finally - { + finally { ReleaseChannel(conn); } } - /* - public object GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName) - { - // TODO - throw new NotSupportedException(); - } - - public void GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, byte[] optionValue) - { - // TODO - throw new NotSupportedException(); - } - - public byte[] GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionLength) - { - // TODO - throw new NotSupportedException(); - } - */ + // + //public object GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName) + //{ + // // TODO + // throw new NotSupportedException(); + //} +// + //public void GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, byte[] optionValue) + //{ + // // TODO + // throw new NotSupportedException(); + //} +// + //public byte[] GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionLength) + //{ + // // TODO + // throw new NotSupportedException(); + //} + // internal override void Listen(int backlog) { // We have to be in the Bound state to Listen TcpConnectionContract.Imp conn = GetChannelOrThrow(); - if (!conn.InState(TcpConnectionContract.Bound.Value)) - { + if (!conn.InState(TcpConnectionContract.Bound.Value)) { throw new SocketException(BAD_SOCKET_STATE_ERR); } - try - { + try { conn.SendListen(backlog); - switch receive - { + switch receive { case conn.CouldNotListen() : throw new SocketException(SocketErrors.SocketError); break; @@ -548,16 +512,14 @@ namespace System.Net.Sockets break; } } - finally - { + finally { ReleaseChannel(conn); } } internal override bool Poll(int microSeconds, SelectMode mode) { - switch(mode) - { + switch (mode) { // // For SelectRead, return true iff: // Listen has been called and a connection is pending, OR @@ -569,30 +531,26 @@ namespace System.Net.Sockets TcpConnectionContract.Imp conn = GetChannelOrThrow(); bool isClosed, sessionsWaiting = false; - try - { // First check if our channel is in the Closed state + try { + // First check if our channel is in the Closed state isClosed = conn.InState(TcpConnectionContract.Closed.Value); // Now check if there are sessions available for Accept() - if (conn.InState(TcpConnectionContract.Listening.Value)) - { + if (conn.InState(TcpConnectionContract.Listening.Value)) { conn.SendIsSessionAvailable(); conn.RecvSessionIsAvailable(out sessionsWaiting); } } - finally - { + finally { ReleaseChannel(conn); } int dataAvailable = 0; - try - { + try { dataAvailable = GetAvailableBytes(microSeconds); } - catch(Exception) - { + catch (Exception) { // Swallow any exceptions here } @@ -609,12 +567,10 @@ namespace System.Net.Sockets { bool isConnected = false; - try - { + try { isConnected = Connected; } - catch (Exception) - { + catch (Exception) { // swallow everything } @@ -653,17 +609,14 @@ namespace System.Net.Sockets throw new SocketException(SocketErrors.WSAEINVAL); } - if (buffer==null) - { + if (buffer == null) { throw new ArgumentNullException("buffer"); } - if (offset<0 || offset > buffer.Length) - { + if (offset < 0 || offset > buffer.Length) { throw new ArgumentOutOfRangeException("offset"); } - if (size<0 || size > buffer.Length - offset) - { + if (size < 0 || size > buffer.Length - offset) { throw new ArgumentOutOfRangeException("size"); } @@ -671,8 +624,7 @@ namespace System.Net.Sockets // If we can satisfy the read request from our internal buffer, just // do that and we're done. - if (size <= m_DataBuffer.Count) - { + if (size <= m_DataBuffer.Count) { int copied = m_DataBuffer.CopyFromHead(buffer, offset, size, fPeek); Debug.Assert(copied == size); return size; @@ -680,16 +632,13 @@ namespace System.Net.Sockets TcpConnectionContract.Imp conn = GetChannelOrThrow(); - try - { + try { int dataCount = m_DataBuffer.Count; - if (!CanRead(conn)) - { + if (!CanRead(conn)) { // The channel doesn't support reading anymore. - if (dataCount > 0) - { + if (dataCount > 0) { // There's some residual data in our buffer. m_DataBuffer.CopyFromHead(buffer, offset, dataCount, fPeek); } @@ -704,11 +653,9 @@ namespace System.Net.Sockets bool canReadAgain = DrainDataNonBlock(conn, m_DataBuffer, 0); dataCount = m_DataBuffer.Count; - if (!canReadAgain) - { + if (!canReadAgain) { // The connection went away! - if (dataCount == 0) - { + if (dataCount == 0) { // At the beginning of this call, the socket was // readable, but now it's not. So the data // stream has just ended. Signal this to the caller @@ -720,22 +667,19 @@ namespace System.Net.Sockets // else return some data below } - if (dataCount > 0) - { + if (dataCount > 0) { // Return as much as possible, even if the data stream // has ended int countToReturn = (size > dataCount) ? dataCount : size; m_DataBuffer.CopyFromHead(buffer, offset, countToReturn, fPeek); return countToReturn; } - else - { + else { // There's no data at all, but we can still read. Block. canReadAgain = BlockingRead(conn, m_DataBuffer); dataCount = m_DataBuffer.Count; - if (!canReadAgain) - { + if (!canReadAgain) { if (dataCount == 0) // See comment above { return 0; } @@ -753,8 +697,7 @@ namespace System.Net.Sockets return countToReturn; } } - finally - { + finally { ReleaseChannel(conn); } } @@ -770,13 +713,13 @@ namespace System.Net.Sockets return recvBytes; } - /* - public static void Select(IList checkRead, IList checkWrite, IList checkError, int microSeconds) - { - // TODO - throw new NotSupportedException(); - } - */ + // + //public static void Select(IList checkRead, IList checkWrite, IList checkError, int microSeconds) + //{ + // // TODO + // throw new NotSupportedException(); + //} + // internal override int Send([Claims] byte[]! in ExHeap data, SocketFlags socketFlags) @@ -789,25 +732,21 @@ namespace System.Net.Sockets return 0; } - if (socketFlags != SocketFlags.None) - { + if (socketFlags != SocketFlags.None) { // Invalid flags for this operation throw new SocketException(SocketErrors.WSAEINVAL); } TcpConnectionContract.Imp conn = GetChannelOrThrow(); - try - { - if (!CanWrite(conn)) - { + try { + if (!CanWrite(conn)) { throw new SocketException(BAD_SOCKET_STATE_ERR); } conn.SendWrite(data); // Give away data - switch receive - { + switch receive { case conn.OK() : // Success return byteCount; @@ -819,8 +758,7 @@ namespace System.Net.Sockets break; } } - finally - { + finally { ReleaseChannel(conn); } } @@ -830,18 +768,15 @@ namespace System.Net.Sockets int size, SocketFlags socketFlags) { - if (buffer == null) - { + if (buffer == null) { throw new ArgumentNullException("buffer"); } - if (offset < 0 || offset > buffer.Length) - { + if (offset < 0 || offset > buffer.Length) { throw new ArgumentOutOfRangeException("offset"); } - if (size < 0 || size > buffer.Length - offset) - { + if (size < 0 || size > buffer.Length - offset) { throw new ArgumentOutOfRangeException("size"); } @@ -859,36 +794,34 @@ namespace System.Net.Sockets return Send(buffer, offset, size, socketFlags); } - /* - public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue) - { - // TODO - throw new NotSupportedException(); - } - - public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, byte[] optionValue) - { - // TODO - throw new NotSupportedException(); - } - - public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, bool optionValue) - { - SetSocketOption(optionLevel, optionName, (optionValue?1:0)); - } - */ + // + //public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue) + //{ + // // TODO + // throw new NotSupportedException(); + //} +// + //public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, byte[] optionValue) + //{ + // // TODO + // throw new NotSupportedException(); + //} +// + //public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, bool optionValue) + //{ + // SetSocketOption(optionLevel, optionName, (optionValue?1:0)); + //} + // internal override void Shutdown(SocketShutdown how) { TcpConnectionContract.Imp conn = GetChannelOrThrow(); - try - { + try { if ((how == SocketShutdown.Both) || (how == SocketShutdown.Receive)) { - if (!CanRead(conn)) - { + if (!CanRead(conn)) { // Can't shut down reading if it's not currently allowed throw new SocketException(BAD_SOCKET_STATE_ERR); } @@ -899,8 +832,7 @@ namespace System.Net.Sockets if ((how == SocketShutdown.Both) || (how == SocketShutdown.Send)) { - if (!CanWrite(conn)) - { + if (!CanWrite(conn)) { // Can't shut down writing if it's not currently allowed throw new SocketException(BAD_SOCKET_STATE_ERR); } @@ -908,8 +840,7 @@ namespace System.Net.Sockets conn.SendDoneSending(); } } - finally - { + finally { ReleaseChannel(conn); } } @@ -917,7 +848,7 @@ namespace System.Net.Sockets // ================== Properties ================== // - // NOTE Many of these are hard-coded for now since we only support + // NOTE: Many of these are hard-coded for now since we only support // IPv4 Tcp streams. // @@ -929,13 +860,11 @@ namespace System.Net.Sockets internal override EndPoint LocalEndPoint { get { - if (m_LocalEndPoint == null) - { + if (m_LocalEndPoint == null) { uint ipAddr; ushort port; - if (!GetLocalEndPoint(out ipAddr, out port)) - { + if (!GetLocalEndPoint(out ipAddr, out port)) { return null; } @@ -948,13 +877,11 @@ namespace System.Net.Sockets internal override EndPoint RemoteEndPoint { get { - if (m_RemoteEndPoint == null) - { + if (m_RemoteEndPoint == null) { uint ipAddr; ushort port; - if (!GetRemoteEndPoint(out ipAddr, out port)) - { + if (!GetRemoteEndPoint(out ipAddr, out port)) { return null; } @@ -970,8 +897,7 @@ namespace System.Net.Sockets return true; } set { - if (value != true) - { + if (value != true) { // TODO: non-blocking mode! throw new NotSupportedException(); } diff --git a/base/Libraries/System.Net/Sockets/TrackedBytesBuffer.sg b/base/Libraries/System.Net/Sockets/TrackedBytesBuffer.sg index 91d2771..837d6d2 100644 --- a/base/Libraries/System.Net/Sockets/TrackedBytesBuffer.sg +++ b/base/Libraries/System.Net/Sockets/TrackedBytesBuffer.sg @@ -4,8 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: TrackedBytesBuffer.sg -// // Note: // A utility class that can store vectors of bytes from the shared heap without // duplicating them, then copy them out later as though they were a single @@ -79,7 +77,7 @@ namespace System.Net.Sockets int numToCopy = Math.Min(length, Length); expose (this) { - for(int i = 0; i < numToCopy; ++i) { + for (int i = 0; i < numToCopy; ++i) { buff[offset + i] = bytes[nextOffset + i]; } } @@ -118,7 +116,7 @@ namespace System.Net.Sockets { int bytesCopied = 0; - while((m_DataQueue.Count > 0) && (bytesCopied < length)) { + while ((m_DataQueue.Count > 0) && (bytesCopied < length)) { // Don't dequeue just yet, just peek TContainer wrapper = (TContainer)m_DataQueue.Peek(); assert wrapper != null; // Because we checked the count diff --git a/base/Libraries/System.Net/Sockets/UdpSocket.cs b/base/Libraries/System.Net/Sockets/UdpSocket.cs index 3bba648..9deb52f 100644 --- a/base/Libraries/System.Net/Sockets/UdpSocket.cs +++ b/base/Libraries/System.Net/Sockets/UdpSocket.cs @@ -41,18 +41,15 @@ namespace System.Net.Sockets int offset, int size) { - if (buffer == null) - { + if (buffer == null) { throw new ArgumentNullException("buffer"); } - if (offset < 0 || offset > buffer.Length) - { + if (offset < 0 || offset > buffer.Length) { throw new ArgumentOutOfRangeException("offset"); } - if (size < 0 || size > buffer.Length - offset) - { + if (size < 0 || size > buffer.Length - offset) { throw new ArgumentOutOfRangeException("size"); } } @@ -67,11 +64,9 @@ namespace System.Net.Sockets UdpContract.Exp! expConn; UdpContract.NewChannel(out impConn, out expConn); DirectoryServiceContract.Imp epNS = DirectoryService.NewClientEndpoint(); - try - { + try { epNS.SendBind(Bitter.FromString2(UdpContract.ModuleName), expConn); - switch receive - { + switch receive { case epNS.NakBind(ServiceContract.Exp:Start rejectedEP, error) : // failure if (rejectedEP != null) { @@ -92,13 +87,11 @@ namespace System.Net.Sockets break; } } - finally - { + finally { delete epNS; } - try - { + try { // Create a new Udp connection UdpConnectionContract.Exp! udpExpConn; UdpConnectionContract.Imp! udpImpConn; @@ -111,8 +104,7 @@ namespace System.Net.Sockets udpImpConn.RecvReady(); Initialize(udpImpConn); } - finally - { + finally { delete impConn; } } @@ -125,15 +117,13 @@ namespace System.Net.Sockets internal override void Bind(EndPoint localEndPoint) { - if (localEndPoint == null) - { + if (localEndPoint == null) { throw new ArgumentNullException("localEndPoint"); } IPEndPoint ep = localEndPoint as IPEndPoint; - if (ep == null) - { + if (ep == null) { // Illegal argument throw new SocketException(SocketErrors.WSAEINVAL); } @@ -141,17 +131,14 @@ namespace System.Net.Sockets // Make sure we're in the ReadyState UdpConnectionContract.Imp conn = m_Conn.Acquire(); - try - { - if (!conn.InState(UdpConnectionContract.ReadyState.Value)) - { + try { + if (!conn.InState(UdpConnectionContract.ReadyState.Value)) { throw new SocketException(BAD_SOCKET_STATE_ERR); } conn.SendBindLocalEndPoint((uint) ep.Address.m_addr, unchecked((ushort) ep.Port)); - switch receive - { + switch receive { case conn.OK() : // Make a copy of the argument data m_LocalEndPoint = new IPEndPoint(ep.Address, ep.Port); @@ -161,8 +148,7 @@ namespace System.Net.Sockets break; } } - finally - { + finally { m_Conn.Release(conn); } } @@ -171,85 +157,76 @@ namespace System.Net.Sockets { UdpConnectionContract.Imp conn = m_Conn.Acquire(); - try - { - if (!conn.InState(UdpConnectionContract.Closed.Value)) - { + try { + if (!conn.InState(UdpConnectionContract.Closed.Value)) { conn.SendClose(); } } - finally - { + finally { m_Conn.Release(conn); } } internal override void Connect(EndPoint remoteEndPoint) { - if (remoteEndPoint == null) - { + if (remoteEndPoint == null) { throw new ArgumentNullException("remoteEP"); } IPEndPoint ep = remoteEndPoint as IPEndPoint; - if (ep == null) - { + if (ep == null) { // Illegal argument throw new SocketException(SocketErrors.WSAEINVAL); } UdpConnectionContract.Imp conn = m_Conn.Acquire(); - if (! conn.InState(UdpConnectionContract.ReadyState.Value) && + if (!conn.InState(UdpConnectionContract.ReadyState.Value) && ! conn.InState(UdpConnectionContract.LocallyBound.Value)) { // Need to be in the start state to issue a Connect throw new SocketException(BAD_SOCKET_STATE_ERR); } - try - { + try { uint localIP = 0; ushort localPort = 0; - if (m_LocalEndPoint != null) - { - if (m_LocalEndPoint.Address != IPAddress.Any) - { localIP = (uint)m_LocalEndPoint.Address.m_addr; } + if (m_LocalEndPoint != null) { + if (m_LocalEndPoint.Address != IPAddress.Any) { + localIP = (uint)m_LocalEndPoint.Address.m_addr; + } - if (m_LocalEndPoint.Port != 0) - { localPort = unchecked((ushort)m_LocalEndPoint.Port); } + if (m_LocalEndPoint.Port != 0) { + localPort = unchecked((ushort)m_LocalEndPoint.Port); + } } - if ((localIP != 0) && (localPort != 0)) - { + if ((localIP != 0) && (localPort != 0)) { conn.SendConnect(localIP, localPort, (uint)ep.Address.m_addr, unchecked((ushort)ep.Port)); } - else - { + else { conn.SendConnectWithAnyLocalEndPoint( (uint)ep.Address.m_addr, unchecked((ushort)ep.Port)); } - switch receive - { + switch receive { case conn.OK() : // Success m_RemoteEndPoint = ep; break; case conn.InvalidEndPoint(uint addr, ushort port) : - // NOTE could probably stand to retrieve and + // NOTE: could probably stand to retrieve and // return a better error code here throw new SocketException(SocketErrors.SocketError); break; } } - finally - { + finally { m_Conn.Release(conn); } } @@ -287,26 +264,21 @@ namespace System.Net.Sockets internal override bool Poll(int microSeconds, SelectMode mode) { int millis = 0; - if (microSeconds < 0) - { + if (microSeconds < 0) { millis = Int32.MaxValue; } - else - { + else { millis = microSeconds / 1000; } UdpConnectionContract.Imp conn = m_Conn.Acquire(); - try - { - if (conn.InState(UdpConnectionContract.Closed.Value)) - { + try { + if (conn.InState(UdpConnectionContract.Closed.Value)) { throw new ObjectDisposedException("socket"); } - switch (mode) - { + switch (mode) { case SelectMode.SelectRead: return PollSelectRead(conn, millis); break; @@ -321,8 +293,7 @@ namespace System.Net.Sockets throw new NotSupportedException(); } } - finally - { + finally { m_Conn.Release(conn); } } @@ -334,24 +305,20 @@ namespace System.Net.Sockets ref EndPoint remoteEP) { ValidateBufferOffsets(buffer, offset, size); - if (socketFlags != SocketFlags.None) - { + if (socketFlags != SocketFlags.None) { // Invalid flags for this operation throw new SocketException(SocketErrors.WSAEINVAL); } UdpConnectionContract.Imp conn = m_Conn.Acquire(); - try - { - if (conn.InState(UdpConnectionContract.Closed.Value)) - { + try { + if (conn.InState(UdpConnectionContract.Closed.Value)) { throw new SocketException(BAD_SOCKET_STATE_ERR); } conn.SendRead(); - switch receive - { + switch receive { case conn.Data(uint addr, ushort port, byte[]! in ExHeap rdata) : { remoteEP = new IPEndPoint(new IPAddress(addr), port); @@ -363,8 +330,7 @@ namespace System.Net.Sockets break; } } - finally - { + finally { m_Conn.Release(conn); } } @@ -383,25 +349,21 @@ namespace System.Net.Sockets { int byteCount = data.Length; - if (socketFlags != SocketFlags.None) - { + if (socketFlags != SocketFlags.None) { // Invalid flags for this operation throw new SocketException(SocketErrors.WSAEINVAL); } UdpConnectionContract.Imp conn = m_Conn.Acquire(); - try - { - if (!conn.InState(UdpConnectionContract.Connected.Value)) - { + try { + if (!conn.InState(UdpConnectionContract.Connected.Value)) { throw new SocketException(BAD_SOCKET_STATE_ERR); } conn.SendWrite(data); // Give away data - switch receive - { + switch receive { case conn.OK() : // Success return byteCount; @@ -413,8 +375,7 @@ namespace System.Net.Sockets break; } } - finally - { + finally { m_Conn.Release(conn); } } @@ -437,24 +398,20 @@ namespace System.Net.Sockets { ValidateBufferOffsets(buffer, offset, size); - if (socketFlags != SocketFlags.None) - { + if (socketFlags != SocketFlags.None) { // Invalid flags for this operation throw new SocketException(SocketErrors.WSAEINVAL); } IPEndPoint ep = remoteEP as IPEndPoint; - if (ep == null) - { + if (ep == null) { throw new SocketException(SocketErrors.WSAEINVAL); } UdpConnectionContract.Imp conn = m_Conn.Acquire(); - try - { - if (conn.InState(UdpConnectionContract.Closed.Value)) - { + try { + if (conn.InState(UdpConnectionContract.Closed.Value)) { throw new SocketException(BAD_SOCKET_STATE_ERR); } @@ -466,8 +423,7 @@ namespace System.Net.Sockets dataToWrite); // no longer own dataToWrite! - switch receive - { + switch receive { case conn.OK() : // Success return size; @@ -479,8 +435,7 @@ namespace System.Net.Sockets break; } } - finally - { + finally { m_Conn.Release(conn); } } @@ -508,8 +463,7 @@ namespace System.Net.Sockets return true; } set { - if (value != true) - { + if (value != true) { // TODO: non-blocking mode! throw new NotSupportedException(); } @@ -520,16 +474,13 @@ namespace System.Net.Sockets { get { UdpConnectionContract.Imp conn = m_Conn.Acquire(); - try - { + try { return conn.InState(UdpConnectionContract.Connected.Value); } - catch - { + catch { return false; } - finally - { + finally { m_Conn.Release(conn); } } diff --git a/base/Libraries/System.Net/System.Net.csproj b/base/Libraries/System.Net/System.Net.csproj index 4372c43..531f0bb 100644 --- a/base/Libraries/System.Net/System.Net.csproj +++ b/base/Libraries/System.Net/System.Net.csproj @@ -23,6 +23,7 @@ + diff --git a/base/Libraries/System.Web/AssemblyInfo.sg b/base/Libraries/System.Web/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Libraries/System.Web/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Libraries/System.Web/Hosting/IApplicationHost.cs b/base/Libraries/System.Web/Hosting/IApplicationHost.cs index 0bbe969..8feeae5 100644 --- a/base/Libraries/System.Web/Hosting/IApplicationHost.cs +++ b/base/Libraries/System.Web/Hosting/IApplicationHost.cs @@ -4,7 +4,8 @@ // //------------------------------------------------------------------------------ -namespace System.Web.Hosting { +namespace System.Web.Hosting +{ using System; using System.Collections; using System.Web; diff --git a/base/Libraries/System.Web/Hosting/IRegisteredObject.cs b/base/Libraries/System.Web/Hosting/IRegisteredObject.cs index 415acd6..f3e7ae8 100644 --- a/base/Libraries/System.Web/Hosting/IRegisteredObject.cs +++ b/base/Libraries/System.Web/Hosting/IRegisteredObject.cs @@ -4,7 +4,8 @@ // //------------------------------------------------------------------------------ -namespace System.Web.Hosting { +namespace System.Web.Hosting +{ using System; public interface IRegisteredObject { diff --git a/base/Libraries/System.Web/Hosting/SimpleApplicationHost.cs b/base/Libraries/System.Web/Hosting/SimpleApplicationHost.cs index 54b48b4..67179a8 100644 --- a/base/Libraries/System.Web/Hosting/SimpleApplicationHost.cs +++ b/base/Libraries/System.Web/Hosting/SimpleApplicationHost.cs @@ -4,13 +4,12 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: SimpleApplicationHost.cs -// // Note: -// This file was ported, from the Coriolis codebase to Singularity by Mark Aiken. +// This file was ported from the Coriolis codebase to Singularity. // -namespace System.Web.Hosting { +namespace System.Web.Hosting +{ using System; using System.Collections; using System.Diagnostics; diff --git a/base/Libraries/System.Web/Hosting/SimpleWorkerRequest.cs b/base/Libraries/System.Web/Hosting/SimpleWorkerRequest.cs index b0041f2..9376406 100644 --- a/base/Libraries/System.Web/Hosting/SimpleWorkerRequest.cs +++ b/base/Libraries/System.Web/Hosting/SimpleWorkerRequest.cs @@ -4,13 +4,12 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: SimpleWorkerRequest.cs -// // Note: -// This file was ported, from the Coriolis codebase to Singularity by Mark Aiken. +// This file was ported from the Coriolis codebase to Singularity. // -namespace System.Web.Hosting { +namespace System.Web.Hosting +{ using System.Collections; using System.IO; using System.Threading; @@ -206,39 +205,39 @@ namespace System.Web.Hosting { return mappedPath; } - /* - //| - /// - /// [To be supplied.] - /// - public override string MachineConfigPath { - get { - if (_hasRuntimeInfo) { - string path = HttpConfigurationSystem.MachineConfigurationFilePath; - InternalSecurityPermissions.PathDiscovery(path).Demand(); - return path; - } - else - return null; - } - } - */ + // + ////| + ///// + ///// [To be supplied.] + ///// + //public override string MachineConfigPath { + //get { + //if (_hasRuntimeInfo) { + //string path = HttpConfigurationSystem.MachineConfigurationFilePath; + //InternalSecurityPermissions.PathDiscovery(path).Demand(); + //return path; + //} + //else + //return null; + //} + //} + // - /* - //| - /// - /// [To be supplied.] - /// - public override String MachineInstallDirectory { - get { - if (_hasRuntimeInfo) { - InternalSecurityPermissions.PathDiscovery(_installDir).Demand(); - return _installDir; - } - return null; - } - } - */ + // + ////| + ///// + ///// [To be supplied.] + ///// + //public override String MachineInstallDirectory { + //get { + //if (_hasRuntimeInfo) { + //InternalSecurityPermissions.PathDiscovery(_installDir).Demand(); + //return _installDir; + //} + //return null; + //} + //} + // //| /// @@ -304,38 +303,38 @@ namespace System.Web.Hosting { private SimpleWorkerRequest() { } - /* - * Ctor that gets application data from HttpRuntime, assuming - * HttpRuntime has been set up (app domain specially created, etc.) - */ + // + // Ctor that gets application data from HttpRuntime, assuming + // HttpRuntime has been set up (app domain specially created, etc.) + // - /* - //| - /// - /// [To be supplied.] - /// - [SecurityPermission(SecurityAction.Demand, UnmanagedCode=true)] - public SimpleWorkerRequest(String page, String query, TextWriter output) { - _queryString = query; - _output = output; - _page = page; + // + ////| + ///// + ///// [To be supplied.] + ///// + //[SecurityPermission(SecurityAction.Demand, UnmanagedCode=true)] + //public SimpleWorkerRequest(String page, String query, TextWriter output) { + //_queryString = query; + //_output = output; + //_page = page; +// + //ExtractPagePathInfo(); +// + //_appPhysPath = Thread.GetDomain().GetData(".appPath").ToString(); + //_appVirtPath = Thread.GetDomain().GetData(".appVPath").ToString(); + //_installDir = HttpRuntime.AspInstallDirectoryInternal; +// + //_hasRuntimeInfo = true; + //} + // - ExtractPagePathInfo(); - - _appPhysPath = Thread.GetDomain().GetData(".appPath").ToString(); - _appVirtPath = Thread.GetDomain().GetData(".appVPath").ToString(); - _installDir = HttpRuntime.AspInstallDirectoryInternal; - - _hasRuntimeInfo = true; - } - */ - - /* - * Ctor that gets application data as arguments,assuming HttpRuntime - * has not been set up. - * - * This allows for limited functionality to execute handlers. - */ + // + // Ctor that gets application data as arguments,assuming HttpRuntime + // has not been set up. + // + // This allows for limited functionality to execute handlers. + // //| /// /// [To be supplied.] diff --git a/base/Libraries/System.Web/HttpException.cs b/base/Libraries/System.Web/HttpException.cs index 44c0681..6706fd7 100644 --- a/base/Libraries/System.Web/HttpException.cs +++ b/base/Libraries/System.Web/HttpException.cs @@ -4,10 +4,8 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: HttpException -// // Note: -// This file was ported, from the Coriolis codebase to Singularity by Mark Aiken. +// This file was ported, from the Coriolis codebase to Singularity. // namespace System.Web diff --git a/base/Libraries/System.Web/System.Web.csproj b/base/Libraries/System.Web/System.Web.csproj index 96a053c..baf8d73 100644 --- a/base/Libraries/System.Web/System.Web.csproj +++ b/base/Libraries/System.Web/System.Web.csproj @@ -23,6 +23,7 @@ + diff --git a/base/Libraries/System.Web/Util/StringUtil.cs b/base/Libraries/System.Web/Util/StringUtil.cs index 494e6a4..b3f33a1 100644 --- a/base/Libraries/System.Web/Util/StringUtil.cs +++ b/base/Libraries/System.Web/Util/StringUtil.cs @@ -4,19 +4,18 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: StringUtil.cs -// // Note: -// This file was ported, from the Coriolis codebase to Singularity by Mark Aiken. +// This file was ported, from the Coriolis codebase to Singularity. // -namespace System.Web.Util { +namespace System.Web.Util +{ using System.Text; using System.Globalization; - /* - * Various string handling utilities - */ + // + // Various string handling utilities + // internal class StringUtil { internal static string CheckAndTrimString(string paramValue, string paramName) { return CheckAndTrimString(paramValue, paramName, true); @@ -38,7 +37,7 @@ namespace System.Web.Util { if (trimmedValue.Length == 0) { throw new ArgumentException("Tried to trim an empty string"); } - if (lengthToCheck > -1 && trimmedValue.Length > lengthToCheck) { + if (lengthToCheck > - 1 && trimmedValue.Length > lengthToCheck) { throw new ArgumentException("Tried to trim a string that exceeded the maximum length"); } return trimmedValue; @@ -57,13 +56,13 @@ namespace System.Web.Util { } internal static bool EqualsIgnoreCase(string s1, string s2) { - if(IsEmpty(s1) && IsEmpty(s2)) { + if (IsEmpty(s1) && IsEmpty(s2)) { return true; } - if(IsEmpty(s1) || IsEmpty(s2)) { + if (IsEmpty(s1) || IsEmpty(s2)) { return false; } - if(s2.Length != s1.Length) { + if (s2.Length != s1.Length) { return false; } return 0 == string.Compare(s1, 0, s2, 0, s2.Length, true); @@ -73,19 +72,19 @@ namespace System.Web.Util { return s == null || s.Length == 0; } - /* - * Determines if the string ends with the specified character. - * Fast, non-culture aware. - */ + // + // Determines if the string ends with the specified character. + // Fast, non-culture aware. + // internal static bool StringEndsWith(string s, char c) { int len = s.Length; return len != 0 && s[len - 1] == c; } - /* - * Determines if the first string ends with the second string. - * Fast, non-culture aware. - */ + // + // Determines if the first string ends with the second string. + // Fast, non-culture aware. + // internal static bool StringEndsWith(string s1, string s2) { int offset = s1.Length - s2.Length; if (offset < 0) { @@ -95,10 +94,10 @@ namespace System.Web.Util { return 0 == string.Compare(s1, offset, s2, 0, s2.Length); } - /* - * Determines if the first string ends with the second string, ignoring case. - * Fast, non-culture aware. - */ + // + // Determines if the first string ends with the second string, ignoring case. + // Fast, non-culture aware. + // internal static bool StringEndsWithIgnoreCase(string s1, string s2) { int offset = s1.Length - s2.Length; if (offset < 0) { @@ -108,18 +107,18 @@ namespace System.Web.Util { return 0 == string.Compare(s1, offset, s2, 0, s2.Length, true); } - /* - * Determines if the string starts with the specified character. - * Fast, non-culture aware. - */ + // + // Determines if the string starts with the specified character. + // Fast, non-culture aware. + // internal static bool StringStartsWith(string s, char c) { return s.Length != 0 && (s[0] == c); } - /* - * Determines if the first string starts with the second string. - * Fast, non-culture aware. - */ + // + // Determines if the first string starts with the second string. + // Fast, non-culture aware. + // internal static bool StringStartsWith(string s1, string s2) { if (s2.Length > s1.Length) { return false; @@ -128,10 +127,10 @@ namespace System.Web.Util { return 0 == string.Compare(s1, 0, s2, 0, s2.Length); } - /* - * Determines if the first string starts with the second string, ignoring case. - * Fast, non-culture aware. - */ + // + // Determines if the first string starts with the second string, ignoring case. + // Fast, non-culture aware. + // internal static bool StringStartsWithIgnoreCase(string s1, string s2) { if (s2.Length > s1.Length) { return false; diff --git a/base/Libraries/System.Web/Util/SymbolEqualComparer.cs b/base/Libraries/System.Web/Util/SymbolEqualComparer.cs index 6448617..8214df9 100644 --- a/base/Libraries/System.Web/Util/SymbolEqualComparer.cs +++ b/base/Libraries/System.Web/Util/SymbolEqualComparer.cs @@ -4,7 +4,8 @@ // //------------------------------------------------------------------------------ -namespace System.Web.Util { +namespace System.Web.Util +{ using System.Collections; using System.Globalization; @@ -57,7 +58,8 @@ namespace System.Web.Util { if (Char.ToLower(charLeft) == charRight) { continue; } - } else if (catRight == UnicodeCategory.UppercaseLetter + } + else if (catRight == UnicodeCategory.UppercaseLetter && catLeft == UnicodeCategory.LowercaseLetter){ if (Char.ToLower(charRight) == charLeft) { continue; diff --git a/base/Libraries/System.Web/Util/SymbolHashCodeProvider.cs b/base/Libraries/System.Web/Util/SymbolHashCodeProvider.cs index edfe6c8..96ae2dc 100644 --- a/base/Libraries/System.Web/Util/SymbolHashCodeProvider.cs +++ b/base/Libraries/System.Web/Util/SymbolHashCodeProvider.cs @@ -4,7 +4,8 @@ // //------------------------------------------------------------------------------ -namespace System.Web.Util { +namespace System.Web.Util +{ using System.Collections; using System.Globalization; diff --git a/base/Libraries/System.Web/WorkerRequest.cs b/base/Libraries/System.Web/WorkerRequest.cs index bc5df2e..ac2ff4a 100644 --- a/base/Libraries/System.Web/WorkerRequest.cs +++ b/base/Libraries/System.Web/WorkerRequest.cs @@ -4,16 +4,15 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: WorkerRequest.cs -// // Note: // This module defines the base worker class used by ASP.NET Managed // code for request processing. // -// This file was ported, from the Coriolis codebase to Singularity by Mark Aiken. +// This file was ported, from the Coriolis codebase to Singularity. // -namespace System.Web { +namespace System.Web +{ using System.Text; using System.Collections; using System.Web.Hosting; @@ -1002,14 +1001,14 @@ namespace System.Web { new String[] { - /* 100 */"Continue", + /* 100 */ "Continue", /* 101 */ "Switching Protocols", /* 102 */ "Processing" }, new String[] { - /* 200 */"OK", + /* 200 */ "OK", /* 201 */ "Created", /* 202 */ "Accepted", /* 203 */ "Non-Authoritative Information", @@ -1021,7 +1020,7 @@ namespace System.Web { new String[] { - /* 300 */"Multiple Choices", + /* 300 */ "Multiple Choices", /* 301 */ "Moved Permanently", /* 302 */ "Found", /* 303 */ "See Other", @@ -1033,7 +1032,7 @@ namespace System.Web { new String[] { - /* 400 */"Bad Request", + /* 400 */ "Bad Request", /* 401 */ "Unauthorized", /* 402 */ "Payment Required", /* 403 */ "Forbidden", @@ -1062,7 +1061,7 @@ namespace System.Web { new String[] { - /* 500 */"Internal Server Error", + /* 500 */ "Internal Server Error", /* 501 */ "Not Implemented", /* 502 */ "Bad Gateway", /* 503 */ "Service Unavailable", diff --git a/base/Libraries/System.Web/httpserverutility.cs b/base/Libraries/System.Web/httpserverutility.cs index 0b5ec1f..8d5894e 100644 --- a/base/Libraries/System.Web/httpserverutility.cs +++ b/base/Libraries/System.Web/httpserverutility.cs @@ -4,7 +4,8 @@ // //------------------------------------------------------------------------------ -namespace System.Web { +namespace System.Web +{ using System.Text; using System.Threading; using System.IO; @@ -19,7 +20,7 @@ namespace System.Web { public sealed class HttpUtility { // - // NOTE Various entire chunks of functionality are not included in + // NOTE: Various entire chunks of functionality are not included in // this port. In fact, only URL encoding/decoding is available. // @@ -167,7 +168,7 @@ namespace System.Web { if (offset < 0 || offset > bytes.Length) { throw new ArgumentOutOfRangeException("offset"); } - if (count < 0 || offset+count > bytes.Length) { + if (count < 0 || offset + count > bytes.Length) { throw new ArgumentOutOfRangeException("count"); } return UrlEncodeBytesToBytesInternal(bytes, offset, count, true); @@ -236,7 +237,7 @@ namespace System.Web { if (offset < 0 || offset > bytes.Length) { throw new ArgumentOutOfRangeException("offset"); } - if (count < 0 || offset+count > bytes.Length) { + if (count < 0 || offset + count > bytes.Length) { throw new ArgumentOutOfRangeException("count"); } return UrlDecodeStringFromBytesInternal(bytes, offset, count, e); @@ -285,7 +286,7 @@ namespace System.Web { if (offset < 0 || offset > bytes.Length) { throw new ArgumentOutOfRangeException("offset"); } - if (count < 0 || offset+count > bytes.Length) { + if (count < 0 || offset + count > bytes.Length) { throw new ArgumentOutOfRangeException("count"); } return UrlDecodeBytesFromBytesInternal(bytes, offset, count); @@ -342,7 +343,7 @@ namespace System.Web { // count them first for (int i = 0; i < count; i++) { - if ((bytes[offset+i] & 0x80) != 0) + if ((bytes[offset + i] & 0x80) != 0) cNonAscii++; } @@ -357,7 +358,7 @@ namespace System.Web { for (int i = 0; i < count; i++) { byte b = bytes[offset+i]; - if ((bytes[offset+i] & 0x80) == 0) { + if ((bytes[offset + i] & 0x80) == 0) { expandedBytes[pos++] = b; } else { @@ -378,7 +379,7 @@ namespace System.Web { for (int i = 0; i < l; i++) { char ch = s[i]; - if ((ch & 0xff80) == 0) { // 7 bit? + if ((ch & 0xff80) == 0) { // 7 bit? if (ignoreAscii || IsSafe(ch)) { sb.Append(ch); } @@ -447,12 +448,12 @@ namespace System.Web { internal void AddByte(byte b) { // if there are no pending bytes treat 7 bit bytes as characters // this optimization is temp disable as it doesn't work for some encodings -/* - if (_numBytes == 0 && ((b & 0x80) == 0)) { - AddChar((char)b); - } - else -*/ +// +// if (_numBytes == 0 && ((b & 0x80) == 0)) { +// AddChar((char)b); +// } +// else +// { if (_byteBuffer == null) _byteBuffer = new byte[_bufferSize]; @@ -479,15 +480,15 @@ namespace System.Web { // go through the string's chars collapsing just %uXXXX and // appending each char as char int loc = s.IndexOf("%u"); - if(loc == -1) { + if (loc == -1) { return s; } for (int pos = 0; pos < count; pos++) { char ch = s[pos]; - if (ch == '%' && pos < count-5) { - if(s[pos+1] == 'u') { + if (ch == '%' && pos < count - 5) { + if (s[pos + 1] == 'u') { int h1 = HexToInt(s[pos+2]); int h2 = HexToInt(s[pos+3]); int h3 = HexToInt(s[pos+4]); @@ -525,14 +526,14 @@ namespace System.Web { if (ch == '+') { ch = ' '; } - else if (ch == '%' && pos < count-2) { - if (s[pos+1] == 'u' && pos < count-5) { + else if (ch == '%' && pos < count - 2) { + if (s[pos + 1] == 'u' && pos < count - 5) { int h1 = HexToInt(s[pos+2]); int h2 = HexToInt(s[pos+3]); int h3 = HexToInt(s[pos+4]); int h4 = HexToInt(s[pos+5]); - if (h1 >= 0 && h2 >= 0 && h3 >= 0 && h4 >= 0) { // valid 4 hex chars + if (h1 >= 0 && h2 >= 0 && h3 >= 0 && h4 >= 0) { // valid 4 hex chars ch = (char)((h1 << 12) | (h2 << 8) | (h3 << 4) | h4); pos += 5; @@ -545,7 +546,7 @@ namespace System.Web { int h1 = HexToInt(s[pos+1]); int h2 = HexToInt(s[pos+2]); - if (h1 >= 0 && h2 >= 0) { // valid 2 hex chars + if (h1 >= 0 && h2 >= 0) { // valid 2 hex chars byte b = (byte)((h1 << 4) | h2); pos += 2; @@ -581,14 +582,14 @@ namespace System.Web { if (b == '+') { b = (byte)' '; } - else if (b == '%' && i < count-2) { - if (buf[pos+1] == 'u' && i < count-5) { + else if (b == '%' && i < count - 2) { + if (buf[pos + 1] == 'u' && i < count - 5) { int h1 = HexToInt((char)buf[pos+2]); int h2 = HexToInt((char)buf[pos+3]); int h3 = HexToInt((char)buf[pos+4]); int h4 = HexToInt((char)buf[pos+5]); - if (h1 >= 0 && h2 >= 0 && h3 >= 0 && h4 >= 0) { // valid 4 hex chars + if (h1 >= 0 && h2 >= 0 && h3 >= 0 && h4 >= 0) { // valid 4 hex chars char ch = (char)((h1 << 12) | (h2 << 8) | (h3 << 4) | h4); i += 5; @@ -601,7 +602,7 @@ namespace System.Web { int h1 = HexToInt((char)buf[pos+1]); int h2 = HexToInt((char)buf[pos+2]); - if (h1 >= 0 && h2 >= 0) { // valid 2 hex chars + if (h1 >= 0 && h2 >= 0) { // valid 2 hex chars b = (byte)((h1 << 4) | h2); i += 2; } @@ -625,11 +626,11 @@ namespace System.Web { if (b == '+') { b = (byte)' '; } - else if (b == '%' && i < count-2) { + else if (b == '%' && i < count - 2) { int h1 = HexToInt((char)buf[pos+1]); int h2 = HexToInt((char)buf[pos+2]); - if (h1 >= 0 && h2 >= 0) { // valid 2 hex chars + if (h1 >= 0 && h2 >= 0) { // valid 2 hex chars b = (byte)((h1 << 4) | h2); i += 2; } diff --git a/base/Libraries/Transform/AssemblyInfo.cs b/base/Libraries/Transform/AssemblyInfo.cs new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Libraries/Transform/AssemblyInfo.cs @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Libraries/Transform/AssemblyInfoWin.cs b/base/Libraries/Transform/AssemblyInfoWin.cs new file mode 100644 index 0000000..d9b6bf5 --- /dev/null +++ b/base/Libraries/Transform/AssemblyInfoWin.cs @@ -0,0 +1,5 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] diff --git a/base/Libraries/Transform/Transform.Win.csproj b/base/Libraries/Transform/Transform.Win.csproj new file mode 100644 index 0000000..22a13b5 --- /dev/null +++ b/base/Libraries/Transform/Transform.Win.csproj @@ -0,0 +1,29 @@ + + + + + + + + Library + Transform + {8C124203-B822-403a-A8FE-BA9897AB5206} + + + + + + + + + diff --git a/base/Libraries/Transform/Transform.cs b/base/Libraries/Transform/Transform.cs new file mode 100644 index 0000000..20982f4 --- /dev/null +++ b/base/Libraries/Transform/Transform.cs @@ -0,0 +1,93 @@ +using System; + +/// +/// general classes used in transforms +/// + +namespace Microsoft.Singularity.Transform +{ + /// flags classes & methods used as patterns + public class PatternAttribute : Attribute + { + } + + /// flags classes used as templates for generating new code + public class TemplateAttribute : Attribute + { + } + + /// flags parameters used as alternate type matches + public class CaseAttribute : Attribute + { + } + + /// flags final parameter of case list + public class DefaultAttribute : Attribute + { + } + + /// flags parameters used as type matches + public class ParameterAttribute : Attribute + { + } + + /// flags parameters that match any data type (array, scalar, struct) + public class DataAttribute : Attribute + { + } + + /// flags where transformed class references are expected to be defined + public class DefinedAttribute : Attribute + { + public DefinedAttribute(string s) + { + } + } + + /// used to invoke processing instructions inside code + public class Transform + { + /// begin an iteration block + public static void For(string x) + { + } + /// end an iteration block + /// todo: rename to EndFor() + public static void EndFor() + { + } + /// switch on type of a parameter + public static void Switch(string x) + { + } + /// conditionally generate if types match + public static void Case(string x) + { + } + /// last case if not present + public static void Default() + { + } + /// end conditional block + public static void EndSwitch() + { + } + /// flag compile time error + public static void Error(string x) + { + } + /// compile if strings are equal + public static void IfEqual(string a, string b) + { + } + /// compile if strings are not equal + public static void IfNotEqual(string a, string b) + { + } + /// end if block + public static void EndIf() + { + } + } +} + diff --git a/base/Libraries/Transform/Transform.csproj b/base/Libraries/Transform/Transform.csproj new file mode 100644 index 0000000..e0e4a10 --- /dev/null +++ b/base/Libraries/Transform/Transform.csproj @@ -0,0 +1,28 @@ + + + + + + + + Library + Transform + + + + + + + + + diff --git a/base/Libraries/UnitTest/AssemblyCleanupAttribute.cs b/base/Libraries/UnitTest/AssemblyCleanupAttribute.cs new file mode 100644 index 0000000..8f09281 --- /dev/null +++ b/base/Libraries/UnitTest/AssemblyCleanupAttribute.cs @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// + +using System; + +namespace Microsoft.Singularity.UnitTest +{ + // This annotation on a method indicates that the method should be invoked + // to cleanup after running tests in the assembly. + // + [AttributeUsage(AttributeTargets.Method)] + public sealed class AssemblyCleanupAttribute : Attribute + { + } + +} + diff --git a/base/Libraries/UnitTest/AssemblyInitializeAttribute.cs b/base/Libraries/UnitTest/AssemblyInitializeAttribute.cs new file mode 100644 index 0000000..3cada2e --- /dev/null +++ b/base/Libraries/UnitTest/AssemblyInitializeAttribute.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// + +using System; + +namespace Microsoft.Singularity.UnitTest +{ + // This annotation on an assembly indicates that the assembly should be treated + // as a stand-alone test application. + // + [AttributeUsage(AttributeTargets.Method)] + public sealed class AsssemblyInitializeAttribute : Attribute + { + } + +} diff --git a/base/Libraries/UnitTest/Assert.cs b/base/Libraries/UnitTest/Assert.cs index aaaca8c..982c0ba 100644 --- a/base/Libraries/UnitTest/Assert.cs +++ b/base/Libraries/UnitTest/Assert.cs @@ -51,6 +51,11 @@ namespace Microsoft.Singularity.UnitTest Interlocked.Increment(ref passes); } + public static void IsTrue(bool condition, string userMessage) + { + True(condition, userMessage); + } + public static void False(bool condition, string message) { Assert.True(!condition, message); diff --git a/base/Libraries/UnitTest/ClassCleanupAttribute.cs b/base/Libraries/UnitTest/ClassCleanupAttribute.cs new file mode 100644 index 0000000..c9104b7 --- /dev/null +++ b/base/Libraries/UnitTest/ClassCleanupAttribute.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// + +using System; + +namespace Microsoft.Singularity.UnitTest +{ + // This method should be invoked to cleanup after testing a class. + // + [AttributeUsage(AttributeTargets.Method)] + public sealed class ClassCleanupAttribute : Attribute + { + } + +} + diff --git a/base/Libraries/UnitTest/ClassInitializeAttribute.cs b/base/Libraries/UnitTest/ClassInitializeAttribute.cs new file mode 100644 index 0000000..6aca7fa --- /dev/null +++ b/base/Libraries/UnitTest/ClassInitializeAttribute.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// + +using System; + +namespace Microsoft.Singularity.UnitTest +{ + // This annotation on an assembly indicates that the assembly should be treated + // as a stand-alone test application. + // + [AttributeUsage(AttributeTargets.Method)] + public sealed class ClassInitializeAttribute : Attribute + { + } + +} diff --git a/base/Libraries/UnitTest/ExceptionExpectedAttribute.cs b/base/Libraries/UnitTest/ExceptionExpectedAttribute.cs new file mode 100644 index 0000000..6427b2a --- /dev/null +++ b/base/Libraries/UnitTest/ExceptionExpectedAttribute.cs @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// + +using System; + +namespace Microsoft.Singularity.UnitTest +{ + // This annotation on an assembly indicates that the assembly should be treated + // as a stand-alone test application. + // + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public sealed class ExceptionExpectedAttribute : Attribute + { + public ExceptionExpectedAttribute(System.Type exceptionType) { + } + + } + +} diff --git a/base/Libraries/UnitTest/FailedAssertionException.cs b/base/Libraries/UnitTest/FailedAssertionException.cs index 25d1a0d..ad8cdfd 100644 --- a/base/Libraries/UnitTest/FailedAssertionException.cs +++ b/base/Libraries/UnitTest/FailedAssertionException.cs @@ -20,7 +20,7 @@ namespace Microsoft.Singularity.UnitTest public FailedAssertionException(string userMessage, string /*!*/ conditionMessage) - : base(string.Format("{0}\n{1}", userMessage, conditionMessage)) + : base(string.Format("{0} {1}", userMessage, conditionMessage)) { } } diff --git a/base/Libraries/UnitTest/ModuleTestJig.sg b/base/Libraries/UnitTest/ModuleTestJig.sg new file mode 100644 index 0000000..3bb8fcc --- /dev/null +++ b/base/Libraries/UnitTest/ModuleTestJig.sg @@ -0,0 +1,270 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +using System.Diagnostics; +using System.Threading; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +using Microsoft.Bartok.Options; +using Microsoft.Bartok.Runtime; + +using Microsoft.SingSharp; +using Microsoft.Singularity; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.V1.Services; + +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.Test; +using Microsoft.Singularity.UnitTest; + +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Applications; + +using Microsoft.Singularity.Test.Contracts; + +namespace Microsoft.Singularity.UnitTest +{ + + public class TestClass + { + TestLog/*!*/ m_expect; + + protected TestClass(TestLog/*!*/ log) + { + m_expect = log; + } + + protected TestClass() + { + m_expect = new TestLog(false); + } + + public TestLog/*!*/ Expect + { + get { return m_expect; } + } + + public void SetLog(TestLog/*!*/ log) + { + m_expect = log; + } + } + + + public class TestSkippedException : Exception + { + internal const string TEST_MISSING = "Test not found: "; + + public TestSkippedException(string reason) : base(reason) {} + } + + public class SuiteJig + { + virtual public void Initialize() + { + } + + virtual public void DoTest(string/*!*/ test) + { + throw new TestSkippedException( + TestSkippedException.TEST_MISSING + test); + } + + virtual public void Cleanup() + { + } + } + + public class ModuleJig + { + // The generated subclass versions will instantiate the class that has + // the [AssemblyInitialize], invoke the operation, and discard it. + virtual public void Initialize(TestLog/*!*/ log) + { + } + virtual public SuiteJig GetSuite(string/*!*/ name, TestLog/*!*/ log) + { + return null; + } + // The generated subclass versions will instantiate the class that has + // the [AssemblyCleanup], invoke the operation, and discard it. + virtual public void Cleanup(TestLog/*!*/ log) + { + } + } + + public class ModuleTester + { + + // helper for debugging + internal static SuiteJig TheSuite; + + private readonly TRef/*!*/ m_log; + private ModuleJig/*!*/ m_moduleJig; + private TestLog/*!*/ m_expect; + private bool m_logEnabled; + + private ModuleTester([Claims] LogContract.Exp:START/*!*/ log, + bool logSuccess, + ModuleJig/*!*/ jig) + { + m_moduleJig = jig; + m_log = new TRef(log); + // TODO fix log success + m_expect = new RemoteTestLog((ModuleTester) this, logSuccess); + m_logEnabled = true; + } + + private void Dispose() + { + LogContract.Exp log = m_log.Acquire(); + delete log; + } + + static public int RunTests([Claims] ModuleTesterContract.Exp:DO_MODULE/*!*/ tester, + ModuleJig/*!*/ jig) + { + DebugStub.WriteLine("Testing started"); + tester.SendGetLogger(); + LogContract.Exp:START/*!*/ log; + bool logSuccess; + tester.RecvLogger(out log, out logSuccess); + + // TODO initialize assembly + try { + ModuleTester t = new ModuleTester(log, logSuccess, jig); + t.DoModule(tester); + t.Dispose(); + } + finally { + delete tester; + DebugStub.WriteLine("Testing complete"); + } + return 0; + } + + + // QUERIES ////////////////////////////////////////////////////////// + + // Helper methods for ScheduleGroup to work around + // the issue with CycleCount appearing to go backwards because the + // thread was moved by MinScheduler. + protected static long CycleCount + { + get { +#if AFFINITY_SCHEDULER + return Processor.CycleCount; +#else + // If we're not using the affinity scheduler, substitute + // TickCount for CycleCount. TickCount will be consistent + // across processors. + return (long) Environment.TickCount; +#endif + } + } + + protected static long Ticks + { + get { return DateTime.Now.Ticks; } + } + + // OPERATIONS ///////////////////////////////////////////////////////// + internal void DoModule(ModuleTesterContract.Exp/*!*/ tester) + { + // TODO Module init + tester.SendPassed(CycleCount, Ticks); + while (true) { + try { + switch receive { + case tester.InitSuite(char[]/*!*/ in ExHeap suiteD): + string/*!*/ suiteName = Bitter.ToString2(suiteD); + delete suiteD; + SuiteJig suite = m_moduleJig.GetSuite(suiteName, m_expect); + if (null == suite) { + tester.SendSkipped(Bitter.FromString2("No such suite")); + break; + } + TheSuite = suite; + suite.Initialize(); + tester.SendPassed(CycleCount, Ticks); + DoSuite(tester, suite); + break; + + case tester.CleanupModule(): + CleanupModule(tester); + tester.SendPassed(CycleCount, Ticks); + return; + + case tester.ChannelClosed(): + CleanupModule(tester); + return; + } + } + catch (TestSkippedException ex) { + tester.SendSkipped(Bitter.FromString2((!) ex.ToString())); + } + catch (Exception ex) { + tester.SendFailed(Bitter.FromString2((!) ex.ToString()), + CycleCount, + Ticks); + } + } + } + + internal void CleanupModule(ModuleTesterContract.Exp/*!*/ tester) + { + m_moduleJig.Cleanup(m_expect); + } + + internal void DoSuite(ModuleTesterContract.Exp/*!*/ tester, SuiteJig/*!*/ jig) + { + while (true) { + switch receive { + case tester.RunTest(char[]/*!*/ in ExHeap testD): + string/*!*/ testName = Bitter.ToString2(testD); + delete testD; + try { + jig.DoTest(testName); + tester.SendPassed(CycleCount, Ticks); + } + catch (TestSkippedException ex) { + tester.SendSkipped(Bitter.FromString2((!) ex.ToString())); + } + catch (Exception ex) { + tester.SendFailed((!) Bitter.FromString(ex.ToString()), CycleCount, Ticks); + } + break; + + case tester.CleanupSuite(): + jig.Cleanup(); + tester.SendPassed(CycleCount, Ticks); + return; + + case tester.ChannelClosed(): + jig.Cleanup(); + return; + } + } + } + + // LOGGING //////////////////////////////////////////////// + + internal void Log(string/*!*/ msg) + { + LogContract.Exp:START/*!*/ t = m_log.Acquire(); + t.SendLog(Bitter.FromString2(msg), CycleCount, Ticks); + t.RecvOK(); + m_log.Release(t); + } + internal void Fail(string/*!*/ msg) + { + throw new FailedAssertionException(msg); + } + } +} diff --git a/base/Libraries/UnitTest/TestAppAttribute.cs b/base/Libraries/UnitTest/TestAppAttribute.cs index 3afdf0c..48385f1 100644 --- a/base/Libraries/UnitTest/TestAppAttribute.cs +++ b/base/Libraries/UnitTest/TestAppAttribute.cs @@ -13,10 +13,10 @@ using System; namespace Microsoft.Singularity.UnitTest { - /* This annotation on an assembly indicates that the assembly should be treated - as a stand-alone test application, and executed as part of the indicated - group of tests. - */ + // This annotation on an assembly indicates that the assembly should be treated + // as a stand-alone test application, and executed as part of the indicated + // group of tests. + // [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] public sealed class TestAppAttribute : Attribute { diff --git a/base/Libraries/UnitTest/TestApplication.sg b/base/Libraries/UnitTest/TestApplication.sg new file mode 100644 index 0000000..b61604c --- /dev/null +++ b/base/Libraries/UnitTest/TestApplication.sg @@ -0,0 +1,81 @@ + +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Generated test jig code + +using System; +using System.Threading; + +using Microsoft.Singularity.UnitTest; + +using Microsoft.Singularity.Channels; +using Microsoft.Contracts; +using Microsoft.SingSharp.Reflection; +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Test.Contracts; +using Microsoft.Singularity.Configuration; + +[assembly: Transform(typeof(ApplicationResourceTransform))] + +namespace Microsoft.Singularity.UnitTest +{ + // stub for generated jig + public class TheModuleJig : ModuleJig + { + override public SuiteJig GetSuite(string! name, TestLog! log) + { + return null; + } + } +} + +namespace Microsoft.Singularity.Applications +{ + [ConsoleCategory(HelpMessage="ModuleTester", Action="test")] + internal class ModuleTest_Category { + [InputEndpoint("data")] + public readonly TRef Stdin; + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [CustomEndpoint] + public readonly TRef testerRef; + + reflective internal ModuleTest_Category(); + + internal int AppMain() { + if (testerRef == null) { + DebugStub.WriteLine("TEST endpoint not setup"); + throw new Exception("TEST endpoint not setup "); + } + ModuleTesterContract.Exp tester = testerRef.Acquire(); + if (tester == null) { + DebugStub.WriteLine("TEST unable to acquite handle to test driver"); + throw new Exception("Unable to acquire handle to the test driver"); + } + ModuleJig jig = new TheModuleJig(); + ModuleTester.RunTests(tester, jig); + return 0; + } + } + + // Currently required to get process launch code generated. + [ConsoleCategory(HelpMessage="Run using the test framework", DefaultAction=true)] + internal class ModuleConsole_Category { + [InputEndpoint("data")] + public readonly TRef Stdin; + [OutputEndpoint("data")] + public readonly TRef Stdout; + + reflective internal ModuleConsole_Category(); + + internal int AppMain() { + Console.WriteLine("This is a test application and can only be run from the tester."); + return -1; + } + } +} diff --git a/base/Libraries/UnitTest/TestAttribute.cs b/base/Libraries/UnitTest/TestAttribute.cs new file mode 100644 index 0000000..802ce31 --- /dev/null +++ b/base/Libraries/UnitTest/TestAttribute.cs @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// + +using System; + +namespace Microsoft.Singularity.UnitTest +{ + // This annotation on an assembly indicates that the assembly should be treated + // as a stand-alone test application. + // + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public sealed class TestAttribute : Attribute + { + public TestAttribute(string name) { + } + } + +} diff --git a/base/Libraries/UnitTest/TestClassAttribute.cs b/base/Libraries/UnitTest/TestClassAttribute.cs new file mode 100644 index 0000000..a09f4fc --- /dev/null +++ b/base/Libraries/UnitTest/TestClassAttribute.cs @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// + +using System; + +namespace Microsoft.Singularity.UnitTest +{ + // + // + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public sealed class TestClassAttribute : Attribute + { + } +} diff --git a/base/Libraries/UnitTest/TestCleanupAttribute.cs b/base/Libraries/UnitTest/TestCleanupAttribute.cs new file mode 100644 index 0000000..b7c6387 --- /dev/null +++ b/base/Libraries/UnitTest/TestCleanupAttribute.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// + +using System; + +namespace Microsoft.Singularity.UnitTest +{ + // TODO + // + [AttributeUsage(AttributeTargets.Method)] + public sealed class TestCleanupAttribute : Attribute + { + } + +} + diff --git a/base/Libraries/UnitTest/TestInitializeAttribute.cs b/base/Libraries/UnitTest/TestInitializeAttribute.cs new file mode 100644 index 0000000..e2f85e6 --- /dev/null +++ b/base/Libraries/UnitTest/TestInitializeAttribute.cs @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// + +using System; + +namespace Microsoft.Singularity.UnitTest +{ + // TODO + // + [AttributeUsage(AttributeTargets.Method)] + public sealed class TestInitializeAttribute : Attribute + { + } + +} diff --git a/base/Libraries/UnitTest/TestLog.cs b/base/Libraries/UnitTest/TestLog.cs new file mode 100644 index 0000000..765656b --- /dev/null +++ b/base/Libraries/UnitTest/TestLog.cs @@ -0,0 +1,1842 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// + +using System; +using Microsoft.Contracts; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Test.Contracts; + +namespace Microsoft.Singularity.UnitTest +{ + public class TestLog + { + private bool m_logSuccess; + + // CREATION /////////////////////////////////////////////// + + public TestLog(bool logSuccess) + { + m_logSuccess = logSuccess; + } + + // QUERIES /////////////////////////////////////////////// + + public bool LogSuccess + { + get { return m_logSuccess; } + } + + + // OPERATIONS //////////////////////////////////////////// + // Diplay for user convenience that some progress is happening. This is for + // interactive use only, and might not be recorded in any logs. + public void ShowProgress() + { + Console.Write("."); + } + + public void Pass(string/*!*/ message) + { + if (m_logSuccess) { + Info(message); + } + } + + virtual public void Info(string/*!*/ message) + { + Console.WriteLine("INFO {0}", message); + } + + virtual public void Fail(string/*!*/ message) + { + Console.WriteLine("FAIL {0}", message); + } + + public void True(bool condition, string/*!*/ userMessage) + { + if (condition) { + Pass(userMessage); + } + else { + Fail(userMessage); + } + } + + // Skip out of this test if the condition is not true. + public void Continue(bool condition, string/*!*/ reason) + { + if (!condition) { + throw new TestSkippedException(reason); + } + } + + public void False(bool condition, string/*!*/ message) + { + True(!condition, message); + } + + public void Null(object obj, string/*!*/ message) + { + True(obj == null, message); + } + + public void NotNull(object obj, string/*!*/ message) + { + True(obj != null, message); + } + + public void SameObject(object a, object b, string/*!*/ message) + { + True(ReferenceEquals(a, b), + message, + "references to the same object"); + } + + public void NotSameObject(object a, object b, string/*!*/ message) + { + True(!ReferenceEquals(a, b), + message, + "references to different objects"); + } + + /// + /// Asserts that the actual type 'is a' type equal to or derived + /// from the expected type. + /// + public void IsA(Type/*!*/ actual, Type/*!*/ expected, string/*!*/ message) + { + True(expected.IsAssignableFrom(actual), + message, + String.Format("Type {0} is not a {1}", actual.Name, expected.Name)); + } + + /// + /// Asserts that the object 'is a' instance of the expected type + /// using the same semantics as the C# 'is' operator. + /// + public void IsA(object actual, Type/*!*/ expected, string/*!*/ message) + { + if (actual == null) { + True(false, + message, + "Null values fail type comparisons"); + } else { + IsA(actual.GetType(), expected, message); + } + } + + public void Equal(object a, object b, string/*!*/ message) + { + True(Equals(a, b), message, "objects are equal"); + } + + public void NotEqual(object a, object b, string/*!*/ message) + { + True(!Equals(a, b), message, "objects are not equal."); + } + + + + /// + /// Asserts the first argument is equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Equal(sbyte u, sbyte v, string/*!*/ message) + { + if (u == v) { + Pass(message); + } + else { + FailOperator("==", u, v, message); + } + } + + /// + /// Asserts the first argument is equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Equal(byte u, byte v, string/*!*/ message) + { + if (u == v) { + Pass(message); + } + else { + FailOperator("==", u, v, message); + } + } + + /// + /// Asserts the first argument is equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Equal(short u, short v, string/*!*/ message) + { + if (u == v) { + Pass(message); + } + else { + FailOperator("==", u, v, message); + } + } + + /// + /// Asserts the first argument is equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Equal(ushort u, ushort v, string/*!*/ message) + { + if (u == v) { + Pass(message); + } + else { + FailOperator("==", u, v, message); + } + } + + /// + /// Asserts the first argument is equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Equal(int u, int v, string/*!*/ message) + { + if (u == v) { + Pass(message); + } + else { + FailOperator("==", u, v, message); + } + } + + /// + /// Asserts the first argument is equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Equal(uint u, uint v, string/*!*/ message) + { + if (u == v) { + Pass(message); + } + else { + FailOperator("==", u, v, message); + } + } + + /// + /// Asserts the first argument is equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Equal(long u, long v, string/*!*/ message) + { + if (u == v) { + Pass(message); + } + else { + FailOperator("==", u, v, message); + } + } + + /// + /// Asserts the first argument is equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Equal(ulong u, ulong v, string/*!*/ message) + { + if (u == v) { + Pass(message); + } + else { + FailOperator("==", u, v, message); + } + } + + /// + /// Asserts the first argument is equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Equal(char u, char v, string/*!*/ message) + { + if (u == v) { + Pass(message); + } + else { + FailOperator("==", u, v, message); + } + } + + /// + /// Asserts the first argument is equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Equal(float u, float v, string/*!*/ message) + { + if (u == v) { + Pass(message); + } + else { + FailOperator("==", u, v, message); + } + } + + /// + /// Asserts the first argument is equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Equal(double u, double v, string/*!*/ message) + { + if (u == v) { + Pass(message); + } + else { + FailOperator("==", u, v, message); + } + } + + /// + /// Asserts the first argument is equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Equal(decimal u, decimal v, string/*!*/ message) + { + if (u == v) { + Pass(message); + } + else { + FailOperator("==", u, v, message); + } + } + + /// + /// Asserts the first argument is equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Equal(IComparable /*!*/ u, IComparable /*!*/ v, string/*!*/ message) + { + if (u.CompareTo(v) == 0) { + Pass(message); + } + else { + FailOperator("==", u, v, message); + } + } + + /// + /// Asserts the first argument is equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Equal(bool u, bool v, string/*!*/ message) + { + if (u == v) { + Pass(message); + } + else { + FailOperator("==", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Greater(sbyte u, sbyte v, string/*!*/ message) + { + if (u > v) { + Pass(message); + } + else { + FailOperator(">", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Greater(byte u, byte v, string/*!*/ message) + { + if (u > v) { + Pass(message); + } + else { + FailOperator(">", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Greater(short u, short v, string/*!*/ message) + { + if (u > v) { + Pass(message); + } + else { + FailOperator(">", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Greater(ushort u, ushort v, string/*!*/ message) + { + if (u > v) { + Pass(message); + } + else { + FailOperator(">", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Greater(int u, int v, string/*!*/ message) + { + if (u > v) { + Pass(message); + } + else { + FailOperator(">", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Greater(uint u, uint v, string/*!*/ message) + { + if (u > v) { + Pass(message); + } + else { + FailOperator(">", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Greater(long u, long v, string/*!*/ message) + { + if (u > v) { + Pass(message); + } + else { + FailOperator(">", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Greater(ulong u, ulong v, string/*!*/ message) + { + if (u > v) { + Pass(message); + } + else { + FailOperator(">", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Greater(char u, char v, string/*!*/ message) + { + if (u > v) { + Pass(message); + } + else { + FailOperator(">", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Greater(float u, float v, string/*!*/ message) + { + if (u > v) { + Pass(message); + } + else { + FailOperator(">", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Greater(double u, double v, string/*!*/ message) + { + if (u > v) { + Pass(message); + } + else { + FailOperator(">", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Greater(decimal u, decimal v, string/*!*/ message) + { + if (u > v) { + Pass(message); + } + else { + FailOperator(">", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Greater(IComparable /*!*/ u, IComparable /*!*/ v, string/*!*/ message) + { + if (u.CompareTo(v) > 0) { + Pass(message); + } + else { + FailOperator(">", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void GreaterOrEqual(sbyte u, sbyte v, string/*!*/ message) + { + if (u >= v) { + Pass(message); + } + else { + FailOperator(">=", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void GreaterOrEqual(byte u, byte v, string/*!*/ message) + { + if (u >= v) { + Pass(message); + } + else { + FailOperator(">=", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void GreaterOrEqual(short u, short v, string/*!*/ message) + { + if (u >= v) { + Pass(message); + } + else { + FailOperator(">=", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void GreaterOrEqual(ushort u, ushort v, string/*!*/ message) + { + if (u >= v) { + Pass(message); + } + else { + FailOperator(">=", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void GreaterOrEqual(int u, int v, string/*!*/ message) + { + if (u >= v) { + Pass(message); + } + else { + FailOperator(">=", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void GreaterOrEqual(uint u, uint v, string/*!*/ message) + { + if (u >= v) { + Pass(message); + } + else { + FailOperator(">=", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void GreaterOrEqual(long u, long v, string/*!*/ message) + { + if (u >= v) { + Pass(message); + } + else { + FailOperator(">=", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void GreaterOrEqual(ulong u, ulong v, string/*!*/ message) + { + if (u >= v) { + Pass(message); + } + else { + FailOperator(">=", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void GreaterOrEqual(char u, char v, string/*!*/ message) + { + if (u >= v) { + Pass(message); + } + else { + FailOperator(">=", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void GreaterOrEqual(float u, float v, string/*!*/ message) + { + if (u >= v) { + Pass(message); + } + else { + FailOperator(">=", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void GreaterOrEqual(double u, double v, string/*!*/ message) + { + if (u >= v) { + Pass(message); + } + else { + FailOperator(">=", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void GreaterOrEqual(decimal u, decimal v, string/*!*/ message) + { + if (u >= v) { + Pass(message); + } + else { + FailOperator(">=", u, v, message); + } + } + + /// + /// Asserts the first argument is greater than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void GreaterOrEqual(IComparable /*!*/ u, IComparable /*!*/ v, string/*!*/ message) + { + if (u.CompareTo(v) >= 0) { + Pass(message); + } + else { + FailOperator(">=", u, v, message); + } + } + + /// + /// Asserts the first argument is less than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Less(sbyte u, sbyte v, string/*!*/ message) + { + if (u < v) { + Pass(message); + } + else { + FailOperator("<", u, v, message); + } + } + + /// + /// Asserts the first argument is less than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Less(byte u, byte v, string/*!*/ message) + { + if (u < v) { + Pass(message); + } + else { + FailOperator("<", u, v, message); + } + } + + /// + /// Asserts the first argument is less than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Less(short u, short v, string/*!*/ message) + { + if (u < v) { + Pass(message); + } + else { + FailOperator("<", u, v, message); + } + } + + /// + /// Asserts the first argument is less than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Less(ushort u, ushort v, string/*!*/ message) + { + if (u < v) { + Pass(message); + } + else { + FailOperator("<", u, v, message); + } + } + + /// + /// Asserts the first argument is less than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Less(int u, int v, string/*!*/ message) + { + if (u < v) { + Pass(message); + } + else { + FailOperator("<", u, v, message); + } + } + + /// + /// Asserts the first argument is less than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Less(uint u, uint v, string/*!*/ message) + { + if (u < v) { + Pass(message); + } + else { + FailOperator("<", u, v, message); + } + } + + /// + /// Asserts the first argument is less than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Less(long u, long v, string/*!*/ message) + { + if (u < v) { + Pass(message); + } + else { + FailOperator("<", u, v, message); + } + } + + /// + /// Asserts the first argument is less than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Less(ulong u, ulong v, string/*!*/ message) + { + if (u < v) { + Pass(message); + } + else { + FailOperator("<", u, v, message); + } + } + + /// + /// Asserts the first argument is less than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Less(char u, char v, string/*!*/ message) + { + if (u < v) { + Pass(message); + } + else { + FailOperator("<", u, v, message); + } + } + + /// + /// Asserts the first argument is less than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Less(float u, float v, string/*!*/ message) + { + if (u < v) { + Pass(message); + } + else { + FailOperator("<", u, v, message); + } + } + + /// + /// Asserts the first argument is less than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Less(double u, double v, string/*!*/ message) + { + if (u < v) { + Pass(message); + } + else { + FailOperator("<", u, v, message); + } + } + + /// + /// Asserts the first argument is less than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Less(decimal u, decimal v, string/*!*/ message) + { + if (u < v) { + Pass(message); + } + else { + FailOperator("<", u, v, message); + } + } + + /// + /// Asserts the first argument is less than the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void Less(IComparable /*!*/ u, IComparable /*!*/ v, string/*!*/ message) + { + if (u.CompareTo(v) < 0) { + Pass(message); + } + else { + FailOperator("<", u, v, message); + } + } + + /// + /// Asserts the first argument is less than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void LessOrEqual(sbyte u, sbyte v, string/*!*/ message) + { + if (u <= v) { + Pass(message); + } + else { + FailOperator("<=", u, v, message); + } + } + + /// + /// Asserts the first argument is less than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void LessOrEqual(byte u, byte v, string/*!*/ message) + { + if (u <= v) { + Pass(message); + } + else { + FailOperator("<=", u, v, message); + } + } + + /// + /// Asserts the first argument is less than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void LessOrEqual(short u, short v, string/*!*/ message) + { + if (u <= v) { + Pass(message); + } + else { + FailOperator("<=", u, v, message); + } + } + + /// + /// Asserts the first argument is less than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void LessOrEqual(ushort u, ushort v, string/*!*/ message) + { + if (u <= v) { + Pass(message); + } + else { + FailOperator("<=", u, v, message); + } + } + + /// + /// Asserts the first argument is less than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void LessOrEqual(int u, int v, string/*!*/ message) + { + if (u <= v) { + Pass(message); + } + else { + FailOperator("<=", u, v, message); + } + } + + /// + /// Asserts the first argument is less than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void LessOrEqual(uint u, uint v, string/*!*/ message) + { + if (u <= v) { + Pass(message); + } + else { + FailOperator("<=", u, v, message); + } + } + + /// + /// Asserts the first argument is less than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void LessOrEqual(long u, long v, string/*!*/ message) + { + if (u <= v) { + Pass(message); + } + else { + FailOperator("<=", u, v, message); + } + } + + /// + /// Asserts the first argument is less than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void LessOrEqual(ulong u, ulong v, string/*!*/ message) + { + if (u <= v) { + Pass(message); + } + else { + FailOperator("<=", u, v, message); + } + } + + /// + /// Asserts the first argument is less than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void LessOrEqual(char u, char v, string/*!*/ message) + { + if (u <= v) { + Pass(message); + } + else { + FailOperator("<=", u, v, message); + } + } + + /// + /// Asserts the first argument is less than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void LessOrEqual(float u, float v, string/*!*/ message) + { + if (u <= v) { + Pass(message); + } + else { + FailOperator("<=", u, v, message); + } + } + + /// + /// Asserts the first argument is less than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void LessOrEqual(double u, double v, string/*!*/ message) + { + if (u <= v) { + Pass(message); + } + else { + FailOperator("<=", u, v, message); + } + } + + /// + /// Asserts the first argument is less than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void LessOrEqual(decimal u, decimal v, string/*!*/ message) + { + if (u <= v) { + Pass(message); + } + else { + FailOperator("<=", u, v, message); + } + } + + /// + /// Asserts the first argument is less than or equal to the + /// second argument. + /// + /// If the assertion fails, the method adds the + /// values and comparison to the error message so + /// the user does not have to supply this + /// information in the + /// argument. + /// + /// + /// The first argument. + /// The second argument. + /// The message displayed + /// on failure. + public void LessOrEqual(IComparable /*!*/ u, IComparable /*!*/ v, string/*!*/ message) + { + if (u.CompareTo(v) <= 0) { + Pass(message); + } + else { + FailOperator("<=", u, v, message); + } + } + + private void FailOperator(string /*!*/ op, + IComparable /*!*/ u, + IComparable /*!*/ v, + string /*!*/ message) + { + True(false, + message, + string.Format("{0} {1} {2} is false.", u, op, v)); + } + + private void True(bool condition, + string /*!*/ userMessage, + string /*!*/ detail) + { + if (condition) { + Pass(userMessage); + } + else { + Fail(string.Format("EXPECTED: \"{0}\"; Detail: {1}", userMessage, detail)); + } + } + } + + internal class RemoteTestLog : TestLog + { + private ModuleTester/*!*/ m_jig; + + // CREATION /////////////////////////////////////////////// + + public RemoteTestLog([Delayed] ModuleTester/*!*/ jig, bool logSuccess) + : base(logSuccess) + { + m_jig = jig; + } + + // OPERATIONS ///////////////////////////////////////////// + override public void Info(string/*!*/ message) + { + m_jig.Log(message); + } + + override public void Fail(string/*!*/ message) + { + m_jig.Fail(message); + } + } +} diff --git a/base/Libraries/UnitTest/TestMethodAttribute.cs b/base/Libraries/UnitTest/TestMethodAttribute.cs new file mode 100644 index 0000000..409f082 --- /dev/null +++ b/base/Libraries/UnitTest/TestMethodAttribute.cs @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// + +using System; + +namespace Microsoft.Singularity.UnitTest +{ + // TODO + // + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public sealed class TestMethodAttribute : Attribute + { + //public TestMethodAttribute(string name) { } + + private long timeout; + public long Timeout { + get { return timeout; } + set { timeout = value; } + } + } + +} diff --git a/base/Libraries/UnitTest/TestSetupAttribute.cs b/base/Libraries/UnitTest/TestSetupAttribute.cs new file mode 100644 index 0000000..497b940 --- /dev/null +++ b/base/Libraries/UnitTest/TestSetupAttribute.cs @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Note: +// + +using System; + +namespace Microsoft.Singularity.UnitTest +{ + // This annotation on an assembly indicates that the assembly should be treated + // as a stand-alone test application. + // + [AttributeUsage(AttributeTargets.Method)] + public sealed class TestSetupAttribute : Attribute + { + } + +} diff --git a/base/Libraries/UnitTest/TestTemplate.cs b/base/Libraries/UnitTest/TestTemplate.cs new file mode 100644 index 0000000..96b4e9f --- /dev/null +++ b/base/Libraries/UnitTest/TestTemplate.cs @@ -0,0 +1,85 @@ +//////////////////////////////////////////////////////////////// +// +// template +// +//////////////////////////////////////////////////////////////// + +using System; +using Microsoft.Singularity.UnitTest; +using Microsoft.Singularity.Transform; + +namespace Microsoft.Singularity.UnitTest +{ + // + // template for generating jig + // + + /// Pattern to match test classes + [Pattern] + [TestClass] + internal class EachTestClass : TestClass + { + // each class should have a no-argument constructor + internal EachTestClass() { } + + [Pattern] + [ClassInitialize] + internal void EachInitMethod() { } + + [Pattern] + [TestMethod] + internal void EachTestMethod() { } + } + + /// template for generated code per test class + [Template] + internal class EachTestClass_Jig : SuiteJig + { + private EachTestClass! m_test; + + public EachTestClass_Jig(TestLog! log) + { + EachTestClass t = new EachTestClass(); + t.SetLog(log); + m_test = t; + } + + public override void Initialize() + { + Transform.For("EachInitMethod"); + m_test.EachInitMethod(); + Transform.EndFor(); + } + + public override void DoTest(string! name) + { + Transform.For("EachTestMethod"); + if (name.Equals("EachTestMethod")) { + m_test.EachTestMethod(); + return; + } + Transform.EndFor(); + base.DoTest(name); + } + } + + /// template for generated code for entire module + [Template] + public class TheModuleJig : ModuleJig + { + public TheModuleJig() + { + } + + public override SuiteJig GetSuite(string! name, TestLog! log) + { + Transform.For("EachTestClass"); + if ("EachTestClass".EndsWith(name)) { + return new EachTestClass_Jig(log); + } + Transform.EndFor(); + return base.GetSuite(name, log); + } + } +} + diff --git a/base/Libraries/UnitTest/TestTransform.sg b/base/Libraries/UnitTest/TestTransform.sg new file mode 100644 index 0000000..eeca2c0 --- /dev/null +++ b/base/Libraries/UnitTest/TestTransform.sg @@ -0,0 +1,304 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Creates startup boilerplate code from Resource descriptions. + +using System; + +using Microsoft.SingSharp; +using Microsoft.Contracts; +using Microsoft.Singularity; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Extending; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.V1.Services; + +namespace Microsoft.Singularity.UnitTest +{ + + transform TestTransform + where $Ex : Exception + where $EndpointType : unmanaged struct, Endpoint, ITracked + where $EpAttr : EndpointAttribute + { +#if false + [TestClass] + public class $$Fixture { + + [TestMethod($fname)] + [ExceptionExpected(typeof($Ex))] + public void $$FailTests(); + [TestMethod($name)] + public void $$Tests(); + + [ClassInitialize] + void $$ClassSetup(); + + [ClassCleanup] + void $$ClassTeardown(); + } + + generate class Module_Jig : ModuleJig + { + override public SuiteJig GetSuite(string! name, TestLog! log) + { + override public void Initialize() { + forall (; $f in $$Fixture;) { + if ($f.$name == tname) { + return $f(); + } + } + } + + switch (name) { + case "FibTest": + return new FibTest_Jig(log); + default: + return base.GetSuite(name, log); + } + } + public static void RunTest() + { + forall (; $fix in $$Fixture;) { + $fix.DispatchTest(); + } + } + } + forall (; $suite in $$Fixture;) { + generate public class $suite_Jig : SuiteJig { + private $suite! m_test; + + public $suite_Jig(TestLog! log) { + m_test = new $suite(); + m_test.SetLog(log); + } + override public void Initialize() { + forall (; $s in $$Setup;) { + $s(); + } + } + override public void Cleanup() { + forall (; $td in $$Teardown;) { + $td(); + } + } + override public void DoTest(string! test) { + forall (; $test in $$Tests;) { + if ($test.$name == tname) { + $test(); + return; + } + } +#if false + forall (; $ft in $$FailTests;) { + if ($ft.$fname == tname) { + try { + $ft(); + throw new Exception("Failure expected: " + typeof($ft.$Ex).ToString()); + } + catch ($ft.$Ex) { + // expected + } + return; + } + } +#endif + Expect.Continue(false, "Unknown test"); + } + } + } + +#if false + generate static string[] TestNames = $BuildTestNames(); + + generate static string[] $BuildTestNames() { + Hashtable result = new Hashtable(); + forall (; $cmd in $$Commands;) { + result [ $cmd.$name ] = new Command( $cmd ); + } + return result; + } +#endif + +#if CASE_WORKS + generate void DispatchTest(string tname) { + switch (tname) { + case "SETUP": + forall (; $test in $$Setup;) { + $test(); + } + break; + case "TEARDOWN": + forall (; $test in $$Setup;) { + $test(); + } + break; + forall (; $test in $$Tests;) { + case $test.$name: + $test(); + break; + } + forall (; $test in $$FailTests;) { + case $test.$name: + try { + $test(); + throw new Exception("Failure expected: " + $test.$Ex); + } + catch ($test.$Ex ex) { + // expected + } + break; + } + default: + throw new Exception("Unknown test."); + } + + return result; + } + + + [ConsoleCategory(*, DefaultAction=true)] + internal class $DefaultCategory + { + // only match, don't implement + internal $DefaultCategory(); + + internal int AppMain(); + + // Single entry point + generate public static int Main(string[] args) { +#if SHOW_ARGS + if (args != null) { + foreach (string arg in args) { + DebugStub.WriteLine("startup argument '{0}'", __arglist(arg)); + } + } +#endif + if (args == null || args.Length < 2) { + $DefaultCategory config = new $DefaultCategory(); + return config.AppMain(); + } + + string action = args[1]; + forall (; $Action in $$NamedCategories ;) { + if ($Action.$name == action) { + $Action config = new $Action(); + return config.AppMain(); + } + } + throw new Exception(String.Format("Unrecognized action {0}", action)); + } + } + +#endif + [ConsoleCategory(*, Action=$name)] + internal class $$NamedCategories { + // only match, don't implement + internal $$NamedCategories(); + + internal int AppMain(); + } + + [ConsoleCategory(*)] + internal class $$Categories { + + [$EpAttr(*)] + TRef<$EndpointType,$State> $$endpoints; + + [StringParameter(*)] + string $$strings; + + [StringArrayParameter(*)] + string[] $$stringArrays; + + [LongParameter(*)] + long $$longs; + + [BoolParameter(*)] + bool $$bools; + + // [NotDelayed] + implement internal $$Categories() { + //DebugStub.WriteLine("ConsoleCategory action '{0}' applied", __arglist(this.GetType().ToString())); + + int index; + ParameterCode code; + + index = 0; + forall (; $s in $$strings ;) { + string stringParam; + code = Process.GetStartupStringArg(index, out stringParam); + if (code != ParameterCode.Success) { + throw new ArgumentException(String.Format("error acquiring startup string array {0}. code={1}", index, code)); + } + this.$s = stringParam; + //DebugStub.WriteLine("got string {0}=({1})",__arglist(index, $s)); + ++index; + } + + index = 0; + forall (; $sa in $$stringArrays ;) { + string[] stringArrayParam; + code = Process.GetStartupStringArrayArg(index, out stringArrayParam ); + if (code != ParameterCode.Success) { + throw new ArgumentException(String.Format("error acquiring startup string {0}. code={1}", index, code)); + } + this.$sa = stringArrayParam; + //DebugStub.WriteLine("got string {0}=({1})",__arglist(index, $s)); + ++index; + } + + index = 0; + forall (; $l in $$longs ;) { + long longArg; + code = ProcessService.GetStartupLongArg(index, out longArg); + if (code != ParameterCode.Success) { + throw new ArgumentException(String.Format("error acquiring startup integer {0}. code={1}", index, code)); + } + this.$l = longArg; + //DebugStub.WriteLine("got long {0}=({1})",__arglist(index, $l)); + ++index; + } + + index = 0; + forall (; $b in $$bools ;) { + bool b; + code = ProcessService.GetStartupBoolArg(index, out b); + if (code != ParameterCode.Success) { + throw new ArgumentException(String.Format("error acquiring startup bool {0}. code={1}", index, code)); + } + this.$b = b; + //DebugStub.WriteLine("got bool {0}=({1})",__arglist(index, b)); + ++index; + } + index = 0; + forall (; $e in $$endpoints;) { + if (index == 0 || index == 1) { + //DebugStub.WriteLine("transform skipping pipe at {0}",__arglist(index)); + } + else { + $e.$EndpointType* in ExHeap opt($e.$State) ep = Process.GetStartupEndpoint(index) + as $e.$EndpointType* in ExHeap opt($e.$State); + if (ep == null) { + throw new ArgumentException(String.Format("missing startup endpoint {0}", index)); + } + $e = new TRef<$e.$EndpointType, $e.$State> (ep); + //DebugStub.WriteLine("getting endpoint at {0}",__arglist(index)); + } + ++index; + + } + + } + + // Category specific entry point + internal int AppMain(); + + } +#endif + } +} diff --git a/base/Libraries/UnitTest/UnitTestLib.csproj b/base/Libraries/UnitTest/UnitTestLib.csproj index 681cd36..7afc2fc 100644 --- a/base/Libraries/UnitTest/UnitTestLib.csproj +++ b/base/Libraries/UnitTest/UnitTestLib.csproj @@ -20,11 +20,28 @@ + + + + + + + + + + + + + + + + + diff --git a/base/Libraries/UnitTest/UnitTestTemplate.csproj b/base/Libraries/UnitTest/UnitTestTemplate.csproj new file mode 100644 index 0000000..62cfe85 --- /dev/null +++ b/base/Libraries/UnitTest/UnitTestTemplate.csproj @@ -0,0 +1,32 @@ + + + + + + + + Library + UnitTestTemplate + + + + + + + + + + + + + diff --git a/base/Libraries/WebApps/AssemblyInfo.sg b/base/Libraries/WebApps/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Libraries/WebApps/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Libraries/WebApps/IHttpRequest.cs b/base/Libraries/WebApps/IHttpRequest.cs index f5b437f..0b9b231 100644 --- a/base/Libraries/WebApps/IHttpRequest.cs +++ b/base/Libraries/WebApps/IHttpRequest.cs @@ -4,7 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: IHttpRequest.cs // Note: The interface implemented by Http request objects, as processed by // web applications under Singularity. // diff --git a/base/Libraries/WebApps/IWebApp.cs b/base/Libraries/WebApps/IWebApp.cs index 9128eeb..f02f46b 100644 --- a/base/Libraries/WebApps/IWebApp.cs +++ b/base/Libraries/WebApps/IWebApp.cs @@ -4,7 +4,6 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. // -// File: IWebApp.cs // Note: The interface implemented by web applications under Singularity // diff --git a/base/Libraries/WebApps/RemoteHttpRequest.sg b/base/Libraries/WebApps/RemoteHttpRequest.sg index 5e24707..6306ecf 100644 --- a/base/Libraries/WebApps/RemoteHttpRequest.sg +++ b/base/Libraries/WebApps/RemoteHttpRequest.sg @@ -32,8 +32,7 @@ namespace Microsoft.Singularity.WebApps { HttpRequestContract.Imp conn = m_Conn.Acquire(); - try - { + try { char[]! in ExHeap uriChars; conn.SendGetUriPath(); conn.RecvUriPath(out uriChars); @@ -41,8 +40,7 @@ namespace Microsoft.Singularity.WebApps delete uriChars; return retval; } - finally - { + finally { m_Conn.Release(conn); } } @@ -51,25 +49,21 @@ namespace Microsoft.Singularity.WebApps { HttpRequestContract.Imp conn = m_Conn.Acquire(); - try - { + try { char[] in ExHeap queryChars; conn.SendGetQueryString(); conn.RecvQueryString(out queryChars); - if (queryChars != null) - { + if (queryChars != null) { string retval = Bitter.ToString(queryChars); delete queryChars; return retval; } - else - { + else { return null; } } - finally - { + finally { m_Conn.Release(conn); } } @@ -78,8 +72,7 @@ namespace Microsoft.Singularity.WebApps { HttpRequestContract.Imp conn = m_Conn.Acquire(); - try - { + try { char[]! in ExHeap verbChars; conn.SendGetVerb(); conn.RecvVerb(out verbChars); @@ -87,8 +80,7 @@ namespace Microsoft.Singularity.WebApps delete verbChars; return retval; } - finally - { + finally { m_Conn.Release(conn); } } @@ -97,8 +89,7 @@ namespace Microsoft.Singularity.WebApps { HttpRequestContract.Imp conn = m_Conn.Acquire(); - try - { + try { char[] in ExHeap headerChars; conn.SendGetHeader(Bitter.FromString2(headerName)); conn.RecvHeaderValue(out headerChars); @@ -107,12 +98,12 @@ namespace Microsoft.Singularity.WebApps string retval = Bitter.ToString(headerChars); delete headerChars; return retval; - } else { + } + else { return null; } } - finally - { + finally { m_Conn.Release(conn); } } @@ -121,8 +112,7 @@ namespace Microsoft.Singularity.WebApps { HttpRequestContract.Imp conn = m_Conn.Acquire(); - try - { + try { byte[] in ExHeap bodyBytes; conn.SendGetBody(); conn.RecvBodyData(out bodyBytes); @@ -131,12 +121,12 @@ namespace Microsoft.Singularity.WebApps byte[] retval = Bitter.ToByteArray(bodyBytes); delete bodyBytes; return retval; - } else { + } + else { return null; } } - finally - { + finally { m_Conn.Release(conn); } } @@ -145,8 +135,7 @@ namespace Microsoft.Singularity.WebApps { HttpRequestContract.Imp conn = m_Conn.Acquire(); - try - { + try { char[]! in ExHeap addrChars; conn.SendGetRemoteAddress(); conn.RecvRemoteAddress(out addrChars); @@ -154,8 +143,7 @@ namespace Microsoft.Singularity.WebApps delete addrChars; return retval; } - finally - { + finally { m_Conn.Release(conn); } } @@ -164,14 +152,12 @@ namespace Microsoft.Singularity.WebApps { HttpRequestContract.Imp conn = m_Conn.Acquire(); - try - { + try { char[]! in ExHeap descChars = Bitter.FromString2(description); conn.SendSendStatus(code, descChars); conn.RecvOK(); } - finally - { + finally { m_Conn.Release(conn); } } @@ -180,15 +166,13 @@ namespace Microsoft.Singularity.WebApps { HttpRequestContract.Imp conn = m_Conn.Acquire(); - try - { + try { char[]! in ExHeap nameChars = Bitter.FromString2(name); char[]! in ExHeap valChars = Bitter.FromString2(value); conn.SendSendHeader(nameChars, valChars); conn.RecvOK(); } - finally - { + finally { m_Conn.Release(conn); } } @@ -202,13 +186,11 @@ namespace Microsoft.Singularity.WebApps { HttpRequestContract.Imp conn = m_Conn.Acquire(); - try - { + try { conn.SendSendBodyData(data); conn.RecvOK(); } - finally - { + finally { m_Conn.Release(conn); } } diff --git a/base/Libraries/WebApps/WebAppsLib.csproj b/base/Libraries/WebApps/WebAppsLib.csproj index b56387c..e48534e 100644 --- a/base/Libraries/WebApps/WebAppsLib.csproj +++ b/base/Libraries/WebApps/WebAppsLib.csproj @@ -23,6 +23,7 @@ + diff --git a/base/Libraries/Xml/AssemblyInfo.sg b/base/Libraries/Xml/AssemblyInfo.sg new file mode 100644 index 0000000..7e57d5c --- /dev/null +++ b/base/Libraries/Xml/AssemblyInfo.sg @@ -0,0 +1,7 @@ +using System.Reflection; + +[assembly: AssemblyProduct("Microsoft Research Singularity Operating System")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyKeyFile("public.snk")] +[assembly: AssemblyDelaySign(true)] diff --git a/base/Libraries/Xml/Xml.csproj b/base/Libraries/Xml/Xml.csproj index 14d94f6..c67bdfa 100644 --- a/base/Libraries/Xml/Xml.csproj +++ b/base/Libraries/Xml/Xml.csproj @@ -23,9 +23,11 @@ + + diff --git a/base/Libraries/Xml/XmlException.cs b/base/Libraries/Xml/XmlException.cs index c37005c..717cf1f 100644 --- a/base/Libraries/Xml/XmlException.cs +++ b/base/Libraries/Xml/XmlException.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; diff --git a/base/Libraries/Xml/XmlNode.cs b/base/Libraries/Xml/XmlNode.cs index 0bafa90..d7383c2 100644 --- a/base/Libraries/Xml/XmlNode.cs +++ b/base/Libraries/Xml/XmlNode.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- namespace Microsoft.Singularity.Xml { @@ -20,18 +18,115 @@ namespace Microsoft.Singularity.Xml private string name; private int depth; private string text; - private Hashtable attributes; + private ArrayList attributes; private ArrayList children; + private XmlNode parentNode; + + internal class XmlAttr + { + public readonly string name; + public string value; + + internal XmlAttr(string name, string value) + { + this.name = name != null ? name : ""; + this.value = value; + } + internal void WriteContentTo(XmlWriter w) + { + w.WriteString(this.value); + } + + internal void WriteTo(XmlWriter w) + { + //w.WriteAttributeString(this.name, this.value); + w.WriteStartAttribute(null, this.name, null); + WriteContentTo(w); + w.WriteEndAttribute(); + } + } + + public XmlNode() + { + attributes = new ArrayList(); + children = new ArrayList(); + this.text = ""; + } + + public XmlNode(string name) + { + this.name = name != null ? name : ""; + attributes = new ArrayList(); + children = new ArrayList(); + this.text = ""; + } + + internal virtual XmlNode NullNode { + get { + return null; + } + } + public void SetParent(XmlNode p) + { + this.parentNode = p; + } + + public virtual XmlNode ParentNode + { + get + { + if (parentNode == NullNode) + return null; + return parentNode; + } + } public XmlNode(string name, int depth) { this.name = name; this.depth = depth; - attributes = new Hashtable(); + attributes = new ArrayList(); children = new ArrayList(); this.text = ""; } + public XmlNode[] Children + { + get + { + XmlNode[] result; + if (children == null) { + result = new XmlNode[0]; + } + else { + if (children.Count == 0) { + result = new XmlNode[0]; + } + else { + result = new XmlNode[children.Count]; + int i = 0; + foreach (XmlNode childNode in children) { + result[i++] = childNode; + } + } + } + + return result; + } + } + + public XmlNode CreateNode (string name) + { + XmlNode n = new XmlNode(); + n.name = name; + return n; + } + + public XmlNode[] ChildNodes + { + get { return this.Children ;} + + } public ArrayList GetNamedChildren(string name) { ArrayList result = new ArrayList(); @@ -43,6 +138,16 @@ namespace Microsoft.Singularity.Xml return result; } + public XmlNode GetChild(string name) + { + foreach (XmlNode childNode in children) { + if (childNode.Name == name) { + return childNode; + } + } + return null; + } + public XmlNode GetNestedChild(String[] childNames) { XmlNode node = this; @@ -63,6 +168,45 @@ namespace Microsoft.Singularity.Xml return null; } + // override this + /// + /// Saves the current node to the specified XmlWriter. + /// + public virtual void WriteTo(XmlWriter w) + { + if (this.name == "::xml") { + WriteContentTo(w); + return; + } + + w.WriteStartElement("", this.name, ""); + + if (attributes.Count > 0) { + foreach (XmlAttr attr in attributes) { + attr.WriteTo(w); + } + } + + if (children.Count > 0) { + WriteContentTo(w); + } + if (children.Count == 0) + w.WriteEndElement(); + else + w.WriteFullEndElement(); + } + + // override this + /// + /// Saves all the children of the node to the specified XmlWriter. + /// + public virtual void WriteContentTo(XmlWriter w) + { + foreach (XmlNode n in children) { + n.WriteTo(w); + } + } + public int Depth { get { return depth; } @@ -79,7 +223,7 @@ namespace Microsoft.Singularity.Xml return; } - public ArrayList Children + public ArrayList ChildrenList { get { return new ArrayList(children); } } @@ -99,18 +243,18 @@ namespace Microsoft.Singularity.Xml { get { - if (!attributes.ContainsKey(attributeName)) - { - return null; - } - else - { - return (string)attributes[attributeName]; - } + XmlAttr a = FindAttr(attributeName); + if (a == null) return null; + return a.value; } set { - attributes[attributeName] = value; + XmlAttr a = FindAttr(attributeName); + if (a == null) { + a = new XmlAttr(attributeName, value); + attributes.Add(a); + } + else a.value = value; } } @@ -123,32 +267,35 @@ namespace Microsoft.Singularity.Xml public string GetAttribute(string attributeName, string defaultValue) { - if (!attributes.ContainsKey(attributeName)) { + XmlAttr a = FindAttr(attributeName); + if (a == null) { return defaultValue; } else { - return (string)attributes[attributeName]; + return a.value; } } public bool GetAttribute(string attributeName, bool defaultValue) { - if (!attributes.ContainsKey(attributeName)) { + XmlAttr a = FindAttr(attributeName); + if (a == null) { return defaultValue; } else { - return (string)attributes[attributeName] == + return a.value == System.Boolean.TrueString; } } public int GetAttribute(string attributeName, int defaultValue) { - if (!attributes.ContainsKey(attributeName)) { + XmlAttr a = FindAttr(attributeName); + if (a == null) { return defaultValue; } else { - string num = (string)attributes[attributeName]; + string num = a.value; if (num.StartsWith("0x") || num.StartsWith("0X")) { return System.Int32.Parse(num, NumberStyles.AllowHexSpecifier); } @@ -158,14 +305,32 @@ namespace Microsoft.Singularity.Xml } } - [CLSCompliant(false)] - public UIntPtr GetAttributeAsUIntPtr(string attributeName, UIntPtr defaultValue) + public long GetAttribute(string attributeName, long defaultValue) { - if (!attributes.ContainsKey(attributeName)) { + XmlAttr a = FindAttr(attributeName); + if (a == null) { return defaultValue; } else { - string num = (string)attributes[attributeName]; + string num = a.value; + if (num.StartsWith("0x") || num.StartsWith("0X")) { + return System.Int64.Parse(num, NumberStyles.AllowHexSpecifier); + } + else { + return System.Int64.Parse(num); + } + } + } + + [CLSCompliant(false)] + public UIntPtr GetAttributeAsUIntPtr(string attributeName, UIntPtr defaultValue) + { + XmlAttr a = FindAttr(attributeName); + if (a == null) { + return defaultValue; + } + else { + string num = a.value; if (num.StartsWith("0x") || num.StartsWith("0X")) { return System.UIntPtr.Parse(num, NumberStyles.AllowHexSpecifier); } @@ -175,9 +340,32 @@ namespace Microsoft.Singularity.Xml } } + public void PrependAttribute(string name, string value) + { + if (attributes == null) return; + XmlAttr a = new XmlAttr(name,value); + attributes.Insert(0,a); + } + + public void AddAttribute(string name, string value) + { + if (attributes == null) return; + XmlAttr a = new XmlAttr(name, value); + attributes.Add(a); + } + public bool HasAttribute(string attributeName) { - return attributes.ContainsKey(attributeName); + return (FindAttr(attributeName) != null) ; + } + + private XmlAttr FindAttr(string name) + { + if (attributes == null) return null; + foreach (XmlAttr attr in attributes) { + if (attr.name == name) return attr; + } + return null; } public string GetAttributes() diff --git a/base/Libraries/Xml/XmlReader.cs b/base/Libraries/Xml/XmlReader.cs index 867989f..5026dff 100644 --- a/base/Libraries/Xml/XmlReader.cs +++ b/base/Libraries/Xml/XmlReader.cs @@ -23,7 +23,8 @@ namespace Microsoft.Singularity.Xml { private States state; private int lineNumber; - + XmlNode doc; + public XmlReader() { state = States.START; @@ -52,22 +53,25 @@ namespace Microsoft.Singularity.Xml GET_STRINGS } - public ArrayList Parse(byte[] xmlBytes) + public XmlNode Parse(byte[] xmlBytes) { MemoryStream ms = new MemoryStream(xmlBytes); StreamReader sr = new StreamReader(ms); - return ParseHelper(sr); + doc = new XmlNode("::xml"); + return ParseHelper(doc, sr); } - public ArrayList Parse(string filePath) + public XmlNode Parse(string filePath) { StreamReader sr = new StreamReader(filePath); - return ParseHelper(sr); + doc = new XmlNode("::xml"); + return ParseHelper(doc, sr); } - private ArrayList ParseHelper(TextReader sr) + private XmlNode ParseHelper(XmlNode doc, TextReader sr) { + TokenType type; string token = null; ArrayList xmlNodes = new ArrayList(); @@ -82,8 +86,7 @@ namespace Microsoft.Singularity.Xml if (type == TokenType.OPERATOR && token.Equals("<")) { state = States.NEED_ELEMENT_NAME; } - else - { + else { // All other text is interpreted as freestanding text. // It had better occur within a tag! if (st.Count == 0) { @@ -140,10 +143,11 @@ namespace Microsoft.Singularity.Xml if (!stackEmpty) { XmlNode parent = (XmlNode)st.Peek(); parent.AddChild(curNode); + //DebugStub.WriteLine("add {1} to {0}", + // __arglist(parent.Name, curNode.Name)); } - + else doc.AddChild(curNode); st.Push(curNode); - xmlNodes.Add(curNode); curNode = null; } @@ -172,6 +176,8 @@ namespace Microsoft.Singularity.Xml state = States.NEED_ATTRIBUTE_NAME; string unescaped_attribute_value = ExpandEntityReferences(token); curNode[curAttributeName] = unescaped_attribute_value; + //DebugStub.WriteLine(" {2}.attr[{0}]={1}", + // __arglist(curAttributeName ,unescaped_attribute_value, curNode.Name)); curAttributeName = null; } else { @@ -190,8 +196,7 @@ namespace Microsoft.Singularity.Xml XmlNode parent = (XmlNode)st.Peek(); parent.AddChild(curNode); } - - xmlNodes.Add(curNode); + else doc.AddChild(curNode); } else { throw new XmlException("Line " + lineNumber + ": Must have a '>' after a closing '/' in an XML node"); @@ -257,7 +262,7 @@ namespace Microsoft.Singularity.Xml } sr.Close(); - return xmlNodes; + return doc; } /// @@ -279,8 +284,7 @@ namespace Microsoft.Singularity.Xml buffer.Append(input, 0, start); start++; - for (; ; ) - { + for (;;) { // At this point, 'start' points to a named XML entity. // locate the entity name. int end = input.IndexOf(';', start); @@ -294,8 +298,7 @@ namespace Microsoft.Singularity.Xml string entity_name = input.Substring(start, name_length); string value; - switch (entity_name) - { + switch (entity_name) { case "amp": value = "&"; break; case "lt": value = "<"; break; case "gt": value = ">"; break; @@ -308,8 +311,7 @@ namespace Microsoft.Singularity.Xml // Are there any more entity references in this string? int next = input.IndexOf('&', end + 1); - if (next == -1) - { + if (next == -1) { // There are no more entity references in the string. // Append the rest of the string. buffer.Append(input, end + 1, input.Length - end - 1); @@ -429,7 +431,7 @@ namespace Microsoft.Singularity.Xml read = (char)sr.Read(); while (read != '"' || translateNext) { - if (!translateNext ) { + if (!translateNext) { if (read == '\\') { translateNext = true; } diff --git a/base/Libraries/Xml/XmlWriter.cs b/base/Libraries/Xml/XmlWriter.cs new file mode 100644 index 0000000..5b9c03c --- /dev/null +++ b/base/Libraries/Xml/XmlWriter.cs @@ -0,0 +1,591 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//------------------------------------------------------------------------------ +namespace Microsoft.Singularity.Xml +{ + using System; + using System.Collections; + using System.Text; + using System.IO; + +// +/// +/// +/// Specifies formatting options for the XmlWriter stream. +/// +/// + public enum Formatting { + // + /// + /// + /// No special formatting is done (this is the default). + /// + /// + None, + // + /// +/// This option causes child elements to be indented using +/// the Indentation and IndentChar properties. It only indents Element Content +/// (http://www.w3.org/TR/1998/REC-xml-19980210#sec-element-content) +/// and not Mixed Content (http://www.w3.org/TR/1998/REC-xml-19980210#sec-mixed-content) +/// according to the XML 1.0 definitions of these terms. +/// + Indented, + }; + + + +// +/// +/// +/// Specifies the state of the XmlWriter stream. +/// +/// + public enum WriteState { + // + /// + /// + /// Nothing has been written yet. + /// + /// + Start, + // + /// + /// + /// Writing the prolog. + /// + /// + Prolog, + // + /// + /// + /// Writing a the start tag for an element. + /// + /// + Element, + // + /// + /// + /// Writing an attribute value. + /// + /// + Attribute, + // + /// + /// + /// Writing element content. + /// + /// + Content, + // + /// + /// + /// Close has been called. + /// + /// + Closed, + }; + + + +// Abstract base class. + // + /// + /// Represents a writer that provides fast non-cached forward-only way of generating XML streams containing XML documents that conform to the W3C Extensible Markup Language (XML) 1.0 specification and the Namespaces in XML specification. + /// This class is . + /// + public class XmlWriter { + + Encoding encoding; + TextWriter textWriter; + char quoteChar; + TagInfo[] stack; + int top; + int indentation; + char indentChar; + bool flush; + bool indented; // perf - faster to check a boolean. + + // State machine is working through autocomplete + private enum State + { + Start, + Prolog, + PostDTD, + Element, + Attribute, + Content, + AttrOnly, + Epilog, + Error, + Closed + } + + private enum Token + { + PI, + Doctype, + Comment, + CData, + StartElement, + EndElement, + LongEndElement, + StartAttribute, + EndAttribute, + Content, + RawData, + Whitespace, + Empty + } + + static string[] stateName = { + "Start", + "Prolog", + "PostDTD", + "Element", + "Attribute", + "Content", + "AttrOnly", + "Epilog", + "Error", + "Closed", + }; + + static string[] tokenName = { + "PI", + "Doctype", + "Comment", + "CData", + "StartElement", + "EndElement", + "LongEndElement", + "StartAttribute", + "EndAttribute", + "Content", + "RawData", + "Whitespace", + "Empty" + }; + + static readonly State[,] stateTableDefault = { + // State.Start State.Prolog State.PostDTD State.Element State.Attribute State.Content State.AttrOnly State.Epilog + // + /* Token.PI */{ State.Prolog, State.Prolog, State.PostDTD, State.Content, State.Content, State.Content, State.Error, State.Epilog}, + /* Token.Doctype */{ State.PostDTD, State.PostDTD, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error}, + /* Token.Comment */{ State.Prolog, State.Prolog, State.PostDTD, State.Content, State.Content, State.Content, State.Error, State.Epilog}, + /* Token.CData */{ State.Content, State.Content, State.Error, State.Content, State.Content, State.Content, State.Error, State.Epilog}, + /* Token.StartElement */{ State.Element, State.Element, State.Element, State.Element, State.Element, State.Element, State.Error, State.Element}, + /* Token.EndElement */{ State.Error, State.Error, State.Error, State.Content, State.Content, State.Content, State.Error, State.Error}, + /* Token.LongEndElement */{ State.Error, State.Error, State.Error, State.Content, State.Content, State.Content, State.Error, State.Error}, + /* Token.StartAttribute */{ State.AttrOnly, State.Error, State.Error, State.Attribute, State.Attribute, State.Error, State.Error, State.Error}, + /* Token.EndAttribute */{ State.Error, State.Error, State.Error, State.Error, State.Element, State.Error, State.Epilog, State.Error}, + /* Token.Content */{ State.Content, State.Content, State.Error, State.Content, State.Attribute, State.Content, State.Attribute, State.Epilog}, + /* Token.RawData */{ State.Prolog, State.Prolog, State.PostDTD, State.Content, State.Attribute, State.Content, State.Attribute, State.Epilog}, + /* Token.Whitespace */{ State.Prolog, State.Prolog, State.PostDTD, State.Content, State.Attribute, State.Content, State.Attribute, State.Epilog}, + }; + + static readonly State[,] stateTableDocument = { + // State.Start State.Prolog State.PostDTD State.Element State.Attribute State.Content State.AttrOnly State.Epilog + // + /* Token.PI */{ State.Error, State.Prolog, State.PostDTD, State.Content, State.Content, State.Content, State.Error, State.Epilog}, + /* Token.Doctype */{ State.Error, State.PostDTD, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error}, + /* Token.Comment */{ State.Error, State.Prolog, State.PostDTD, State.Content, State.Content, State.Content, State.Error, State.Epilog}, + /* Token.CData */{ State.Error, State.Error, State.Error, State.Content, State.Content, State.Content, State.Error, State.Error}, + /* Token.StartElement */{ State.Error, State.Element, State.Element, State.Element, State.Element, State.Element, State.Error, State.Error}, + /* Token.EndElement */{ State.Error, State.Error, State.Error, State.Content, State.Content, State.Content, State.Error, State.Error}, + /* Token.LongEndElement */{ State.Error, State.Error, State.Error, State.Content, State.Content, State.Content, State.Error, State.Error}, + /* Token.StartAttribute */{ State.Error, State.Error, State.Error, State.Attribute, State.Attribute, State.Error, State.Error, State.Error}, + /* Token.EndAttribute */{ State.Error, State.Error, State.Error, State.Error, State.Element, State.Error, State.Error, State.Error}, + /* Token.Content */{ State.Error, State.Error, State.Error, State.Content, State.Attribute, State.Content, State.Error, State.Error}, + /* Token.RawData */{ State.Error, State.Prolog, State.PostDTD, State.Content, State.Attribute, State.Content, State.Error, State.Epilog}, + /* Token.Whitespace */{ State.Error, State.Prolog, State.PostDTD, State.Content, State.Attribute, State.Content, State.Error, State.Epilog}, + }; + + State[,] stateTable; + State currentState; + Token lastToken; + + struct TagInfo + { + internal string name; + internal string prefix; + internal string defaultNs; + internal int prefixCount; + internal bool mixed; // whether to pretty print the contents of this element. + internal void Init() + { + name = null; + prefix = null; + defaultNs = String.Empty; + prefixCount = 0; + mixed = false; + } + } + + + public XmlWriter(Stream w, Encoding encoding) + { + textWriter = new StreamWriter(w, encoding); + this.encoding = encoding; + indentation = 2; + indentChar = ' '; + stack = new TagInfo[10]; + top = 0;// 0 is an empty sentanial element + stack[top].Init(); + quoteChar = '"'; + flush = false; + indented = true; + this.stateTable = stateTableDefault; + } + + // + /// + /// Writes out the XML declaration with the version "1.0". + /// + public virtual void WriteStartDocument() + { + } + // + /// + /// Writes out the XML declaration with the version "1.0" and the + /// standalone attribute. + /// + public virtual void WriteStartDocument(bool standalone) + { + StartDocument(-1); + } + // + /// + /// Closes any open elements or attributes and + /// puts the writer back in the Start state. + /// + public virtual void WriteEndDocument() + { + } + + // + /// + /// Writes out the specified start tag and associates it with the + /// given namespace. + /// + public void WriteStartElement(string localName, string ns) { + WriteStartElement(null, localName, ns); + } + + // + /// + /// Writes out the specified start tag and + /// associates it with the given namespace and prefix. + /// + public virtual void WriteStartElement(string prefix, string localName, string ns) + { + AutoComplete(Token.StartElement); + PushStack(); + textWriter.Write('<'); + stack[top].name = localName; + textWriter.Write(localName); + } + + // + /// + /// Writes out a start tag with the specified name. + /// + public void WriteStartElement(string localName) { + WriteStartElement(null, localName, null); + } + + + public void WriteAttributeString(string localName, string value) { + WriteStartAttribute(null, localName, null); + WriteString(value); + WriteEndAttribute(); + } + + public virtual void WriteString(string text) + { + if (null != text && String.Empty != text) { + AutoComplete(Token.Content); + textWriter.Write(text); + } + } + public virtual void WriteEndAttribute() + { + AutoComplete(Token.EndAttribute); + } + + + // + /// + /// Writes the start of an attribute. + /// + public virtual void WriteStartAttribute(string prefix, string localName, string ns) + { + AutoComplete(Token.StartAttribute); + + textWriter.Write(localName); + textWriter.Write('='); + textWriter.Write(this.quoteChar); + } + + + // + /// + /// Closes one element and pops the corresponding namespace scope. + /// + public virtual void WriteEndElement() + { + InternalWriteEndElement(false); + } + // + /// + /// Closes one element and pops the + /// corresponding namespace scope. + /// + public virtual void WriteFullEndElement() + { + InternalWriteEndElement(true); + } + + + // + /// + /// Close this stream and the underlying stream. + /// + public virtual void Close() + { + try { + AutoCompleteAll(); + } + catch (Exception) { } // never fail + Flush(); + textWriter.Close(); + this.currentState = State.Closed; + } + + // + /// + /// Flush whatever is in the buffer to the underlying streams and flush the + /// underlying stream. + /// + public virtual void Flush() + { + textWriter.Flush(); + } + + internal void FlushEncoders() + { + textWriter.Flush(); + } + + void Indent(bool beforeEndElement) + { + // pretty printing. + if (top == 0) { + textWriter.WriteLine(); + } + else if (!stack[top].mixed) { + textWriter.WriteLine(); + int i = beforeEndElement ? top - 1 : top; + for (i *= this.indentation; i > 0; i--) { + textWriter.Write(this.indentChar); + } + } + } + void StartDocument(int standalone) + { + if (this.currentState != State.Start) { + throw new InvalidOperationException("Res.Xml_NotTheFirst"); + } + this.stateTable = stateTableDocument; + this.currentState = State.Prolog; + + StringBuilder bufBld = new StringBuilder(128); + bufBld.Append("version=" + quoteChar + "1.0" + quoteChar); + if (this.encoding != null) { + bufBld.Append(" encoding=" + quoteChar); + //bufBld.Append(this.encoding.WebName); + bufBld.Append("utf-8"); + bufBld.Append(quoteChar); + } + if (standalone >= 0) { + bufBld.Append(" standalone=" + quoteChar); + bufBld.Append(standalone == 0 ? "no" : "yes"); + bufBld.Append(quoteChar); + } + InternalWriteProcessingInstruction("xml", bufBld.ToString()); + } + + void InternalWriteProcessingInstruction(string name, string text) + { + textWriter.Write(""); + } + + void AutoCompleteAll() + { + while (top > 0) { + if (this.flush) { + FlushEncoders(); + } + WriteEndElement(); + } + } + + void InternalWriteEndElement(bool longFormat) { + if (top <= 0) { + throw new InvalidOperationException("Res.Xml_NoStartTag"); + } + // if we are in the element, we need to close it. + AutoComplete(longFormat ? Token.LongEndElement : Token.EndElement); + if (this.lastToken == Token.LongEndElement) { + if (this.indented) { + Indent(true); + } + textWriter.Write('<'); + textWriter.Write('/'); + textWriter.Write(stack[top].name); + textWriter.Write('>'); + } + top--; + } + + void PushStack() { + if (top == stack.Length - 1) { + TagInfo[] na = new TagInfo[stack.Length + 10]; + if (top > 0) Array.Copy(stack,na,top + 1); + stack = na; + } + + top ++; // Move up stack + stack[top].Init(); + } + + + void WriteEndStartTag(bool empty) + { + //xmlEncoder.StartAttribute(false); + //xmlEncoder.EndAttribute(); + if (empty) { + textWriter.Write(" /"); + } + textWriter.Write('>'); + } + + void WriteEndAttributeQuote() + { + textWriter.Write(this.quoteChar); + } + + void AutoComplete(Token token) + { + if (this.currentState == State.Closed) { + throw new InvalidOperationException("Res.Xml_Closed"); + } + + State curr = this.currentState; + State newState = this.stateTable[(int)token, (int)this.currentState]; + //Console.WriteLine("complete: <{1},{0}> ->{2}", + // tokenName[(int) token], stateName[(int) currentState], stateName[(int) newState]); + if (newState == State.Error) { + throw new InvalidOperationException("Res.Xml_WrongToken, tokenName[(int)token], stateName[(int)this.currentState]"); + } + + switch (token) { + case Token.Doctype: + if (this.indented && this.currentState != State.Start) { + Indent(false); + } + break; + + case Token.StartElement: + case Token.Comment: + case Token.PI: + case Token.CData: + if (this.currentState == State.Attribute) { + WriteEndAttributeQuote(); + WriteEndStartTag(false); + } + else if (this.currentState == State.Element) { + WriteEndStartTag(false); + } + Flush(); + if (token == Token.CData) { + stack[top].mixed = true; + } + else if (this.indented && this.currentState != State.Start) { + Indent(false); + } + break; + + case Token.EndElement: + case Token.LongEndElement: + if (this.flush) { + FlushEncoders(); + } + if (this.currentState == State.Attribute) { + WriteEndAttributeQuote(); + } + if (this.currentState == State.Content) { + token = Token.LongEndElement; + } + else { + WriteEndStartTag(token == Token.EndElement); + } + if (stateTableDocument == this.stateTable && top == 1) { + newState = State.Epilog; + } + break; + + case Token.StartAttribute: + if (this.flush) { + FlushEncoders(); + } + if (this.currentState == State.Attribute) { + WriteEndAttributeQuote(); + textWriter.Write(' '); + } + else if (this.currentState == State.Element) { + textWriter.Write(' '); + } + break; + + case Token.EndAttribute: + if (this.flush) { + FlushEncoders(); + } + WriteEndAttributeQuote(); + break; + + case Token.Whitespace: + case Token.Content: + case Token.RawData: + if (this.currentState == State.Element && this.lastToken != Token.Content) { + WriteEndStartTag(false); + } + //else if (this.currentState == State.Attribute && this.specialAttr == SpecialAttr.None && this.lastToken == Token.Content) + else if (this.currentState == State.Attribute && this.lastToken == Token.Content) { + // Beta 2 + // textWriter.Write(' '); + } + if (newState == State.Content) { + stack[top].mixed = true; + } + break; + + default: + throw new InvalidOperationException("Res.Xml_InvalidOperation"); + } + this.currentState = newState; + this.lastToken = token; + } + + } +} + diff --git a/base/Makefile.inc b/base/Makefile.inc index f97e6a4..73f0eaf 100644 --- a/base/Makefile.inc +++ b/base/Makefile.inc @@ -20,8 +20,8 @@ # (Concurrent|MarkSweep|Semispace) # COLLECTOR_APP -- Specify garbage collector for applications # (Concurrent|MarkSweep|Semispace) -# PLATFORM -- Specify host platform (LegacyPC|ApicPC|ApicMP|EnlightenedPC) -# SCHEDULER -- Specify scheduler type (Rialto|Min|Robin|Laxity) +# PLATFORM -- Specify host platform (LegacyPC|ApicPC|ApicMP|Apic64|CEPC) +# SCHEDULER -- Specify scheduler type (Affinity|Rialto|Min|Robin|Laxity) # SYSCALL_BUILDER -- Specify system call stub generator # OBJROOT -- Specify directory in which to place objects (optional) # PAGING -- Specify page translation (On|Off) @@ -67,7 +67,11 @@ ASMP_SYSCALL_BUILDER = $(DEFAULT_ASMP_SYSCALL_BUILDER) # BARTOK = "bartok" -DEFAULT_BARTOK = $(SINGULARITY_ROOT)\build\bartok.exe +!IF ("$(PLATFORM)" == "Apic64") +DEFAULT_BARTOK = $(SINGULARITY_ROOT)\build\x86_x64\bartok\bartok.exe +!ELSE +DEFAULT_BARTOK = $(SINGULARITY_ROOT)\build\x86_x86\bartok\bartok.exe +!ENDIF !IF !DEFINED(BARTOK) BARTOK = $(DEFAULT_BARTOK) @@ -127,15 +131,20 @@ PLATFORM=$(DEFAULT_PLATFORM) !IF ("$(PLATFORM)" != "LegacyPC" && \ "$(PLATFORM)" != "ApicPC" && \ "$(PLATFORM)" != "ApicMP" && \ - "$(PLATFORM)" != "EnlightenedPC") -!ERROR PLATFORM must either be unset, or set to one of "LegacyPC" or "ApicPC" or "ApicMP" or "EnlightenedPC". + "$(PLATFORM)" != "Apic64" && \ + "$(PLATFORM)" != "CEPC") +!ERROR PLATFORM must either be unset, or set to one of "LegacyPC" or "ApicPC" or "ApicMP" or "Apic64" or "CEPC". !endif !IF "$(PLATFORM)" == "ApicMP" && !DEFINED(MAX_CPU) MAX_CPU=4 !endif -# SCHEDULER = "Rialto"* | "Robin" | "Laxity" | "Min" +!IF "$(PLATFORM)" == "Apic64" && !DEFINED(MAX_CPU) +MAX_CPU=4 +!endif + +# SCHEDULER = "Affinity" | "Rialto"* | "Robin" | "Laxity" | "Min" DEFAULT_SCHEDULER=Min @@ -143,8 +152,8 @@ DEFAULT_SCHEDULER=Min SCHEDULER=$(DEFAULT_SCHEDULER) !ENDIF -!IF ("$(SCHEDULER)" != "Rialto") && ("$(SCHEDULER)" != "Robin") && ("$(SCHEDULER)" != "Laxity") && ("$(SCHEDULER)" != "Min") -!ERROR SCHEDULER must either be unset, or set to one of "Rialto", "Robin", "Laxity", or "Min". +!IF ("$(SCHEDULER)" != "Rialto") && ("$(SCHEDULER)" != "Robin") && ("$(SCHEDULER)" != "Laxity") && ("$(SCHEDULER)" != "Min") && ("$(SCHEDULER)" != "Affinity") +!ERROR SCHEDULER must either be unset, or set to one of "Affinity", "Rialto", "Robin", "Laxity", or "Min". !endif # SYSCALL_BUILDER = SyscallBuilder @@ -165,16 +174,36 @@ CSC = csc SGFLAGS = /disable:nullparametervalidation AFLAGS = /nologo /Zi /Cp /DSINGULARITY=1 -CFLAGS = /D_CRT_SECURE_NO_DEPRECATE /nologo /W3 /WX /Gy /Zi /O2 /Oy- /GS- /DSINGULARITY=1 +CFLAGS = /D_CRT_SECURE_NO_DEPRECATE /nologo /W3 /WX /Gy /Zi /Oy- /GS- /DSINGULARITY=1 $(CC_FLAGS_64) + +# +# Enable optimizations only on release builds. +# +# AIFIX: Building with optimizations disabled does not work! +# + +!IF ("$(BUILDTYPE)" == "Release") +CFLAGS = $(CFLAGS) /O2 +!ELSE +# CFLAGS = $(CFLAGS) /Od +CFLAGS = $(CFLAGS) /O2 +!ENDIF # # HOST_LINKFLAGS is for the machine we are building on # used to build the windows components +# We always want debug symbols, thus the /debug flag. # -HOST_LINKFLAGS = /NOLOGO /INCREMENTAL:NO /machine:IX86 +HOST_LINKFLAGS = /NOLOGO /INCREMENTAL:NO /machine:X86 /DEBUG /DEBUGTYPE:CV HOST_CC = cl +!IF "$(PLATFORM)" == "Apic64" +BARTOK_FLAGS = /x64 $(BARTOK_FLAGS) +BARTOK_FLAGS = /IrImproveTypes=false $(BARTOK_FLAGS) +!ENDIF + LINKFLAGS = /NOLOGO /INCREMENTAL:NO /machine:$(MACHINE) +LINKFLAGS64 = /NOLOGO /INCREMENTAL:NO /machine:x64 CPPFLAGS = $(CFLAGS) @@ -184,14 +213,18 @@ BARTOK_FLAGS=$(BARTOK_FLAGS) /IrPeepholeNull=false !IF ("$(BUILDTYPE)" == "Debug") BARTOK_FLAGS = $(BARTOK_FLAGS) CSFLAGS = /debug /d:DEBUG /d:TRACING /d:WAYPOINTS /d:MONITORING /d:CHANNEL_COUNT +CPPFLAGS = /DDEBUG +LINKDFLAGS = /DEBUG /DEBUGTYPE:CV CFLAGS = $(CFLAGS) /DDEBUG LINKFLAGS = $(LINKFLAGS) /DEBUG /DEBUGTYPE:CV +LINKFLAGS64 = $(LINKFLAGS64) /DEBUG /DEBUGTYPE:CV AFLAGS = $(AFLAGS) /DDEBUG=1 !ELSE IF ("$(BUILDTYPE)" == "Prototype") BARTOK_FLAGS = $(BARTOK_FLAGS) /minopt /IrSimpleInliner=false CSFLAGS = /debug /d:DEBUG /d:TRACING /d:WAYPOINTS /d:MONITORING /d:CHANNEL_COUNT CFLAGS = $(CFLAGS) /DDEBUG LINKFLAGS = $(LINKFLAGS) /DEBUG /DEBUGTYPE:CV +LINKFLAGS64 = $(LINKFLAGS64) /DEBUG /DEBUGTYPE:CV AFLAGS = $(AFLAGS) /DDEBUG=1 !ELSE IF ("$(BUILDTYPE)" == "Release") BARTOK_FLAGS = $(BARTOK_FLAGS) @@ -200,6 +233,7 @@ CSFLAGS = /debug /d:CHANNEL_COUNT SGFLAGS = $(SGFLAGS) # /disable:ac,dc,gcc,ic CFLAGS = $(CFLAGS) LINKFLAGS = $(LINKFLAGS) /DEBUG /DEBUGTYPE:CV +LINKFLAGS64 = $(LINKFLAGS64) /DEBUG /DEBUGTYPE:CV AFLAGS = $(AFLAGS) !ENDIF @@ -217,18 +251,34 @@ SGFLAGS = $(SGFLAGS) /d:SAMPLE_PC !endif !IF ("$(PLATFORM)" == "EnlightenedPC") -CSFLAGS = $(CSFLAGS) -SGFLAGS = $(SGFLAGS) -CFLAGS = $(CFLAGS) -AFLAGS = $(AFLAGS) +CSFLAGS = $(CSFLAGS) /d:HACKS_FOR_S +SGFLAGS = $(SGFLAGS) /d:HACKS_FOR_S +CFLAGS = $(CFLAGS) /DHACKS_FOR_S +AFLAGS = $(AFLAGS) /DHACKS_FOR_S=1 !ENDIF +!IF "$(PLATFORM)" == "Apic64" +CC_FLAGS_64 = /DISA_X64=1 /DPTR_SIZE_64=1 +AFLAGS = $(AFLAGS) /DISA_X64=1 /DPTR_SIZE_64=1 +CPPFLAGS=$(CPPFLAGS) /DISA_X64=1 /DPTR_SIZE_64=1 /DUSE_64=1 +CSFLAGS=$(CSFLAGS) /D:ISA_X64 /D:PTR_SIZE_64 /D:USE_64 +AS = $(SINGULARITY_ROOT)\build\x86_x64\ml64.exe +CC = $(SINGULARITY_ROOT)\build\x86_x64\cl.exe +CFLAGS = $(CFLAGS) /DISA_X64 /DPTR_SIZE_64 /DUSE_64 +ARCH_DIR=x64 +MACHINE=x64 +SING_DEF_FILE=singularity64.V1.def +LINK = $(SINGULARITY_ROOT)\build\x86_x64\link.exe +AS = $(SINGULARITY_ROOT)\build\x86_x64\ml64.exe +LIB = $(SINGULARITY_ROOT)\build\x86_x64\lib.exe +!ELSE MACHINE=x86 AS=ml LINK=link LIB=lib ARCH_DIR=. SING_DEF_FILE=singularity.V1.def +!ENDIF !IF ("$(PAGING)" == "On") PAGING_FLAG = .Paging diff --git a/base/Options/Debug.AdaptiveCopying.arm.options b/base/Options/Debug.AdaptiveCopying.arm.options new file mode 100644 index 0000000..c25d906 --- /dev/null +++ b/base/Options/Debug.AdaptiveCopying.arm.options @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Debug.AdaptiveCopying.x64.options b/base/Options/Debug.AdaptiveCopying.x64.options new file mode 100644 index 0000000..c5df225 --- /dev/null +++ b/base/Options/Debug.AdaptiveCopying.x64.options @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Debug.AdaptiveCopying.x86.options b/base/Options/Debug.AdaptiveCopying.x86.options index 3b6ffcb..d6e3ab4 100644 --- a/base/Options/Debug.AdaptiveCopying.x86.options +++ b/base/Options/Debug.AdaptiveCopying.x86.options @@ -1,5 +1,4 @@  - - - - - - + + + - + + + + - + + + @@ -41,7 +46,7 @@ - - + + diff --git a/base/Options/Debug.Concurrent.arm.options b/base/Options/Debug.Concurrent.arm.options new file mode 100644 index 0000000..55b93cc --- /dev/null +++ b/base/Options/Debug.Concurrent.arm.options @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Debug.Concurrent.x64.options b/base/Options/Debug.Concurrent.x64.options new file mode 100644 index 0000000..ac1e492 --- /dev/null +++ b/base/Options/Debug.Concurrent.x64.options @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Debug.Concurrent.x86.options b/base/Options/Debug.Concurrent.x86.options index 3f79411..a7c57c8 100644 --- a/base/Options/Debug.Concurrent.x86.options +++ b/base/Options/Debug.Concurrent.x86.options @@ -1,5 +1,4 @@  - - - - - - + + + - + + + + - + + + @@ -41,7 +46,7 @@ - - + + diff --git a/base/Options/Debug.MarkSweep.arm.options b/base/Options/Debug.MarkSweep.arm.options new file mode 100644 index 0000000..95859a6 --- /dev/null +++ b/base/Options/Debug.MarkSweep.arm.options @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Debug.MarkSweep.x64.options b/base/Options/Debug.MarkSweep.x64.options new file mode 100644 index 0000000..a11db79 --- /dev/null +++ b/base/Options/Debug.MarkSweep.x64.options @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Debug.MarkSweep.x86.options b/base/Options/Debug.MarkSweep.x86.options index 242f76c..c1a316b 100644 --- a/base/Options/Debug.MarkSweep.x86.options +++ b/base/Options/Debug.MarkSweep.x86.options @@ -1,5 +1,4 @@  - - - - - - + + + - + + + + - + + + @@ -41,7 +48,7 @@ - - + + diff --git a/base/Options/Debug.Null.arm.options b/base/Options/Debug.Null.arm.options new file mode 100644 index 0000000..e252564 --- /dev/null +++ b/base/Options/Debug.Null.arm.options @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Debug.Null.x64.options b/base/Options/Debug.Null.x64.options new file mode 100644 index 0000000..c1f3651 --- /dev/null +++ b/base/Options/Debug.Null.x64.options @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Debug.Null.x86.options b/base/Options/Debug.Null.x86.options new file mode 100644 index 0000000..fa84f35 --- /dev/null +++ b/base/Options/Debug.Null.x86.options @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Debug.Semispace.arm.options b/base/Options/Debug.Semispace.arm.options new file mode 100644 index 0000000..b84dd70 --- /dev/null +++ b/base/Options/Debug.Semispace.arm.options @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Debug.Semispace.x64.options b/base/Options/Debug.Semispace.x64.options new file mode 100644 index 0000000..1376b48 --- /dev/null +++ b/base/Options/Debug.Semispace.x64.options @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Debug.Semispace.x86.options b/base/Options/Debug.Semispace.x86.options index 6c83fc5..372aaf1 100644 --- a/base/Options/Debug.Semispace.x86.options +++ b/base/Options/Debug.Semispace.x86.options @@ -1,5 +1,4 @@  - - - - - - + + + - + + + + - + + + @@ -41,7 +46,7 @@ - - + + diff --git a/base/Options/Debug.Sliding.arm.options b/base/Options/Debug.Sliding.arm.options new file mode 100644 index 0000000..d7e2ea6 --- /dev/null +++ b/base/Options/Debug.Sliding.arm.options @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Debug.Sliding.x64.options b/base/Options/Debug.Sliding.x64.options new file mode 100644 index 0000000..52ca9e4 --- /dev/null +++ b/base/Options/Debug.Sliding.x64.options @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Debug.Sliding.x86.options b/base/Options/Debug.Sliding.x86.options index 8074981..0534804 100644 --- a/base/Options/Debug.Sliding.x86.options +++ b/base/Options/Debug.Sliding.x86.options @@ -1,5 +1,4 @@  - - - - - - + + + - + + + + - + + + @@ -41,7 +46,7 @@ - - + + diff --git a/base/Options/Prototype.AdaptiveCopying.arm.options b/base/Options/Prototype.AdaptiveCopying.arm.options new file mode 100644 index 0000000..f137cd3 --- /dev/null +++ b/base/Options/Prototype.AdaptiveCopying.arm.options @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Prototype.AdaptiveCopying.x64.options b/base/Options/Prototype.AdaptiveCopying.x64.options new file mode 100644 index 0000000..18ed036 --- /dev/null +++ b/base/Options/Prototype.AdaptiveCopying.x64.options @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Prototype.AdaptiveCopying.x86.options b/base/Options/Prototype.AdaptiveCopying.x86.options index b226a7c..4adb229 100644 --- a/base/Options/Prototype.AdaptiveCopying.x86.options +++ b/base/Options/Prototype.AdaptiveCopying.x86.options @@ -1,5 +1,4 @@  - - - - - - + + + - + + + - - - + @@ -43,7 +43,7 @@ - - + + diff --git a/base/Options/Prototype.Concurrent.arm.options b/base/Options/Prototype.Concurrent.arm.options new file mode 100644 index 0000000..2d7e5fa --- /dev/null +++ b/base/Options/Prototype.Concurrent.arm.options @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Prototype.Concurrent.x64.options b/base/Options/Prototype.Concurrent.x64.options new file mode 100644 index 0000000..19fbcad --- /dev/null +++ b/base/Options/Prototype.Concurrent.x64.options @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Prototype.Concurrent.x86.options b/base/Options/Prototype.Concurrent.x86.options index 4630a60..8803dff 100644 --- a/base/Options/Prototype.Concurrent.x86.options +++ b/base/Options/Prototype.Concurrent.x86.options @@ -1,5 +1,4 @@  - - - - - - + + + - + + + @@ -27,9 +29,8 @@ - - + @@ -43,7 +44,7 @@ - - + + diff --git a/base/Options/Prototype.MarkSweep.arm.options b/base/Options/Prototype.MarkSweep.arm.options new file mode 100644 index 0000000..978fdee --- /dev/null +++ b/base/Options/Prototype.MarkSweep.arm.options @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Prototype.MarkSweep.x64.options b/base/Options/Prototype.MarkSweep.x64.options new file mode 100644 index 0000000..902c92a --- /dev/null +++ b/base/Options/Prototype.MarkSweep.x64.options @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Prototype.MarkSweep.x86.options b/base/Options/Prototype.MarkSweep.x86.options index 28f0ec5..6cbb761 100644 --- a/base/Options/Prototype.MarkSweep.x86.options +++ b/base/Options/Prototype.MarkSweep.x86.options @@ -1,5 +1,4 @@  - - - - - - + + + - + + + - - - + @@ -43,7 +43,7 @@ - - + + diff --git a/base/Options/Prototype.Null.arm.options b/base/Options/Prototype.Null.arm.options new file mode 100644 index 0000000..32e0868 --- /dev/null +++ b/base/Options/Prototype.Null.arm.options @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Prototype.Null.x64.options b/base/Options/Prototype.Null.x64.options new file mode 100644 index 0000000..a062c61 --- /dev/null +++ b/base/Options/Prototype.Null.x64.options @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Prototype.Null.x86.options b/base/Options/Prototype.Null.x86.options new file mode 100644 index 0000000..ffe365c --- /dev/null +++ b/base/Options/Prototype.Null.x86.options @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Prototype.Semispace.arm.options b/base/Options/Prototype.Semispace.arm.options new file mode 100644 index 0000000..07069b2 --- /dev/null +++ b/base/Options/Prototype.Semispace.arm.options @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Prototype.Semispace.x64.options b/base/Options/Prototype.Semispace.x64.options new file mode 100644 index 0000000..3162b00 --- /dev/null +++ b/base/Options/Prototype.Semispace.x64.options @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Prototype.Semispace.x86.options b/base/Options/Prototype.Semispace.x86.options index d6b6d9f..17f208f 100644 --- a/base/Options/Prototype.Semispace.x86.options +++ b/base/Options/Prototype.Semispace.x86.options @@ -1,5 +1,4 @@  - - - - - - + + + - + + + - - - + @@ -43,7 +43,7 @@ - - + + diff --git a/base/Options/Prototype.Sliding.arm.options b/base/Options/Prototype.Sliding.arm.options new file mode 100644 index 0000000..8312d9c --- /dev/null +++ b/base/Options/Prototype.Sliding.arm.options @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Prototype.Sliding.x64.options b/base/Options/Prototype.Sliding.x64.options new file mode 100644 index 0000000..a40eae2 --- /dev/null +++ b/base/Options/Prototype.Sliding.x64.options @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Prototype.Sliding.x86.options b/base/Options/Prototype.Sliding.x86.options index 529aa88..e592e90 100644 --- a/base/Options/Prototype.Sliding.x86.options +++ b/base/Options/Prototype.Sliding.x86.options @@ -1,5 +1,4 @@  - - - - - - + + + - + + + - - - + @@ -43,7 +43,7 @@ - - + + diff --git a/base/Options/Release.AdaptiveCopying.arm.options b/base/Options/Release.AdaptiveCopying.arm.options new file mode 100644 index 0000000..f262906 --- /dev/null +++ b/base/Options/Release.AdaptiveCopying.arm.options @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Release.AdaptiveCopying.x64.options b/base/Options/Release.AdaptiveCopying.x64.options new file mode 100644 index 0000000..dd18b60 --- /dev/null +++ b/base/Options/Release.AdaptiveCopying.x64.options @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Release.AdaptiveCopying.x86.options b/base/Options/Release.AdaptiveCopying.x86.options index 25e52b5..69a2ee3 100644 --- a/base/Options/Release.AdaptiveCopying.x86.options +++ b/base/Options/Release.AdaptiveCopying.x86.options @@ -1,5 +1,4 @@  - - - - - - + + + - + + + - + @@ -41,7 +43,7 @@ - - + + diff --git a/base/Options/Release.Concurrent.arm.options b/base/Options/Release.Concurrent.arm.options new file mode 100644 index 0000000..c2c880f --- /dev/null +++ b/base/Options/Release.Concurrent.arm.options @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Release.Concurrent.x64.options b/base/Options/Release.Concurrent.x64.options new file mode 100644 index 0000000..74e8a16 --- /dev/null +++ b/base/Options/Release.Concurrent.x64.options @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Release.Concurrent.x86.options b/base/Options/Release.Concurrent.x86.options index ec999fc..1646be0 100644 --- a/base/Options/Release.Concurrent.x86.options +++ b/base/Options/Release.Concurrent.x86.options @@ -1,5 +1,4 @@  - - - - - - + + + - + + + - + @@ -41,7 +43,7 @@ - - + + diff --git a/base/Options/Release.MarkSweep.arm.options b/base/Options/Release.MarkSweep.arm.options new file mode 100644 index 0000000..2368e99 --- /dev/null +++ b/base/Options/Release.MarkSweep.arm.options @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Release.MarkSweep.x64.options b/base/Options/Release.MarkSweep.x64.options new file mode 100644 index 0000000..155c615 --- /dev/null +++ b/base/Options/Release.MarkSweep.x64.options @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Release.MarkSweep.x86.options b/base/Options/Release.MarkSweep.x86.options index 3dc7ceb..ef32119 100644 --- a/base/Options/Release.MarkSweep.x86.options +++ b/base/Options/Release.MarkSweep.x86.options @@ -1,5 +1,4 @@  - - - - - - + + + - + + + - + @@ -42,7 +44,7 @@ - - + + diff --git a/base/Options/Release.Null.arm.options b/base/Options/Release.Null.arm.options new file mode 100644 index 0000000..4c61b37 --- /dev/null +++ b/base/Options/Release.Null.arm.options @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Release.Null.x64.options b/base/Options/Release.Null.x64.options new file mode 100644 index 0000000..9655f39 --- /dev/null +++ b/base/Options/Release.Null.x64.options @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Release.Null.x86.options b/base/Options/Release.Null.x86.options new file mode 100644 index 0000000..b57f903 --- /dev/null +++ b/base/Options/Release.Null.x86.options @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Release.Semispace.arm.options b/base/Options/Release.Semispace.arm.options new file mode 100644 index 0000000..bf3e5d9 --- /dev/null +++ b/base/Options/Release.Semispace.arm.options @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Release.Semispace.x64.options b/base/Options/Release.Semispace.x64.options new file mode 100644 index 0000000..e047064 --- /dev/null +++ b/base/Options/Release.Semispace.x64.options @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Release.Semispace.x86.options b/base/Options/Release.Semispace.x86.options index 654859a..681ee77 100644 --- a/base/Options/Release.Semispace.x86.options +++ b/base/Options/Release.Semispace.x86.options @@ -1,5 +1,4 @@  - - - - - - + + + - + + + - + @@ -41,7 +43,7 @@ - - + + diff --git a/base/Options/Release.Sliding.arm.options b/base/Options/Release.Sliding.arm.options new file mode 100644 index 0000000..594baf5 --- /dev/null +++ b/base/Options/Release.Sliding.arm.options @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Release.Sliding.x64.options b/base/Options/Release.Sliding.x64.options new file mode 100644 index 0000000..da251b5 --- /dev/null +++ b/base/Options/Release.Sliding.x64.options @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Options/Release.Sliding.x86.options b/base/Options/Release.Sliding.x86.options index 2185672..88f3581 100644 --- a/base/Options/Release.Sliding.x86.options +++ b/base/Options/Release.Sliding.x86.options @@ -1,5 +1,4 @@  - - - - - - + + + - + + + - + @@ -41,7 +43,7 @@ - - + + diff --git a/base/ProjectTemplates/ComplexProject.csproj b/base/ProjectTemplates/ComplexProject.csproj index 39807eb..47fd076 100644 --- a/base/ProjectTemplates/ComplexProject.csproj +++ b/base/ProjectTemplates/ComplexProject.csproj @@ -25,6 +25,9 @@ Note: This project file demonstrates some of the more advanced options Provide the relative walk-up path to $(SINGULARITY_ROOT)\Paths.targets. This is annoying, but is necessary in order to get VS builds to work, without requiring that you set environment variables for VS. + + This is only applicable for people who launch VS from a "clean" + environment (e.g., not a shell which has run setenv.cmd). --> diff --git a/base/ProjectTemplates/ConsoleApp.csproj b/base/ProjectTemplates/ConsoleApp.csproj index 8331095..a52075e 100644 --- a/base/ProjectTemplates/ConsoleApp.csproj +++ b/base/ProjectTemplates/ConsoleApp.csproj @@ -17,6 +17,9 @@ Note: This is the template for a basic console app. Provide the relative walk-up path to $(SINGULARITY_ROOT)\Paths.targets. This is annoying, but is necessary in order to get VS builds to work, without requiring that you set environment variables for VS. + + This is only applicable for people who launch VS from a "clean" + environment (e.g., not a shell which has run setenv.cmd). --> diff --git a/base/ProjectTemplates/InterfaceAssembly.csproj b/base/ProjectTemplates/InterfaceAssembly.csproj index 08ab13c..f85d061 100644 --- a/base/ProjectTemplates/InterfaceAssembly.csproj +++ b/base/ProjectTemplates/InterfaceAssembly.csproj @@ -13,6 +13,14 @@ Note: This is the template for an interface assembly. --> + @@ -29,7 +37,7 @@ Note: This is the template for an interface assembly. ProjectReference items declared in files that use InterfaceAssembly.targets can specify the 'Alias' metadata tag. This adds "=aliasvalue" to the /r: reference. --> - + kernel.exe diff --git a/base/ProjectTemplates/KernelLibrary.csproj b/base/ProjectTemplates/KernelLibrary.csproj index 210e571..e78189a 100644 --- a/base/ProjectTemplates/KernelLibrary.csproj +++ b/base/ProjectTemplates/KernelLibrary.csproj @@ -17,6 +17,9 @@ Note: This is the template for a kernel-mode assembly. Provide the relative walk-up path to $(SINGULARITY_ROOT)\Paths.targets. This is annoying, but is necessary in order to get VS builds to work, without requiring that you set environment variables for VS. + + This is only applicable for people who launch VS from a "clean" + environment (e.g., not a shell which has run setenv.cmd). --> diff --git a/base/ProjectTemplates/Library.csproj b/base/ProjectTemplates/Library.csproj index 2e82abb..bc99266 100644 --- a/base/ProjectTemplates/Library.csproj +++ b/base/ProjectTemplates/Library.csproj @@ -14,6 +14,14 @@ Note: This is the template for a user-mode library assembly. --> + diff --git a/base/ProjectTemplates/ServiceApp.csproj b/base/ProjectTemplates/ServiceApp.csproj index 65d2180..57a5729 100644 --- a/base/ProjectTemplates/ServiceApp.csproj +++ b/base/ProjectTemplates/ServiceApp.csproj @@ -13,6 +13,14 @@ Note: This is the template for a user-mode service executable. --> + diff --git a/base/ProjectTemplates/WindowsProject.csproj b/base/ProjectTemplates/WindowsProject.csproj index d8a9d43..01a8ae5 100644 --- a/base/ProjectTemplates/WindowsProject.csproj +++ b/base/ProjectTemplates/WindowsProject.csproj @@ -19,6 +19,14 @@ Note: This is the template for a standard Windows project. These are projects ############################################################################## --> + diff --git a/base/Root.proj b/base/Root.proj new file mode 100644 index 0000000..01764c6 --- /dev/null +++ b/base/Root.proj @@ -0,0 +1,33 @@ + + + + + + BVT + $(SINGULARITY_ROOT)\Distro\$(SINGULARITY_DISTRO_NAME).proj + + + + + + + + + + diff --git a/base/Services/CredentialsManager/Collections.sg b/base/Services/CredentialsManager/Collections.sg index 64d7dc8..f873745 100644 --- a/base/Services/CredentialsManager/Collections.sg +++ b/base/Services/CredentialsManager/Collections.sg @@ -56,7 +56,8 @@ namespace Microsoft.Singularity.Security.CredentialsManager if (untyped_value == null) { value = null; return false; - } else { + } + else { value = (Credentials)untyped_value; return true; } @@ -120,7 +121,8 @@ namespace Microsoft.Singularity.Security.CredentialsManager if (untyped_value == null) { value = null; return false; - } else { + } + else { value = (CredentialsId)untyped_value; return true; } diff --git a/base/Services/CredentialsManager/Credentials.sg b/base/Services/CredentialsManager/Credentials.sg index 0999ae8..4558b55 100644 --- a/base/Services/CredentialsManager/Credentials.sg +++ b/base/Services/CredentialsManager/Credentials.sg @@ -22,18 +22,18 @@ using Ex = Microsoft.Singularity.Security; namespace Microsoft.Singularity.Security.CredentialsManager { - /** - - - This class contains a set of credentials, including sensitive "private" info, - such as passwords, private keys, etc. The Credentials Manager allows clients - to create these entries, enumerate them, etc., but never allows the private - info to flow out of the credentials manager. The CM also allows clients to - create "supplicants", which are instances of security protocols that are - allowed to make use of the private info, but not to expose it. - - - */ + /// + // + // + // This class contains a set of credentials, including sensitive "private" info, + // such as passwords, private keys, etc. The Credentials Manager allows clients + // to create these entries, enumerate them, etc., but never allows the private + // info to flow out of the credentials manager. The CM also allows clients to + // create "supplicants", which are instances of security protocols that are + // allowed to make use of the private info, but not to expose it. + // + // + // class Credentials { public Credentials(CredentialsId! id, CredentialsEvidence! evidence) @@ -46,17 +46,17 @@ namespace Microsoft.Singularity.Security.CredentialsManager public CredentialsEvidence! Evidence; } - /** - - - This class is used as a key in Hashtable, so it implements identity comparison - methods, including Object.GetHashCode(), operator==, operator!=, and Object.Equals(). - - - Instances of this class are immutable. - - - */ + /// + // + // + // This class is used as a key in Hashtable, so it implements identity comparison + // methods, including Object.GetHashCode(), operator==, operator!=, and Object.Equals(). + // + // + // Instances of this class are immutable. + // + // + // class CredentialsId { public CredentialsId(string! name, string! tag) @@ -129,11 +129,11 @@ namespace Microsoft.Singularity.Security.CredentialsManager public readonly string! Password; } - /* - class PrivateKeyEvidence : CredentialsEvidence - { - ??? - } - */ + // + //class PrivateKeyEvidence : CredentialsEvidence + //{ + // ??? + //} + // } diff --git a/base/Services/CredentialsManager/CredentialsManagerService.csproj b/base/Services/CredentialsManager/CredentialsManagerService.csproj index 35230a2..faf6a0b 100644 --- a/base/Services/CredentialsManager/CredentialsManagerService.csproj +++ b/base/Services/CredentialsManager/CredentialsManagerService.csproj @@ -1,8 +1,6 @@  + diff --git a/base/Services/Fat/Fs/AsyncCommands.sg b/base/Services/Fat/Fs/AsyncCommands.sg index 96c7ea1..27adfce 100644 --- a/base/Services/Fat/Fs/AsyncCommands.sg +++ b/base/Services/Fat/Fs/AsyncCommands.sg @@ -303,15 +303,15 @@ namespace Microsoft.Singularity.Services.Fat.Fs this.path = null; if (error == ErrorCode.NoError) { assert fsObject != null; - NodeType nodeType; - uint sizeBytes; - fsObject.GetAttributes(out nodeType, out sizeBytes); + FileAttributesRecord fileAttributes = + new FileAttributesRecord(); + + fsObject.GetAttributes(ref fileAttributes); CommandLogger.Log(" -> Type {0} Size {1}\n", - nodeType, sizeBytes); - - this.exp.SendAckGetAttributes(nodeType, (long)sizeBytes); + fileAttributes.Type, fileAttributes.FileSize); + this.exp.SendAckGetAttributes(fileAttributes); fsObject.CloseInstance(); } else { @@ -398,7 +398,8 @@ namespace Microsoft.Singularity.Services.Fat.Fs CommandLogger.Log(" -> Failed ({0})\n", error); this.exp.SendNakCreateDirectory(error); } - } else { + } + else { CommandLogger.Log(" -> Failed ({0})\n", error); assert parent == null; assert childName == null; @@ -484,7 +485,8 @@ namespace Microsoft.Singularity.Services.Fat.Fs CommandLogger.Log(" -> Failed ({0})\n", error); this.exp.SendNakDeleteDirectory(error); } - } else { + } + else { CommandLogger.Log(" -> Failed ({0})\n", error); if (childName != null) { delete childName; @@ -571,7 +573,8 @@ namespace Microsoft.Singularity.Services.Fat.Fs CommandLogger.Log(" -> Failed ({0})\n", error); this.exp.SendNakCreateFile(error); } - } else { + } + else { CommandLogger.Log(" -> Failed ({0})\n", error); assert parent == null; assert childName == null; @@ -665,7 +668,8 @@ namespace Microsoft.Singularity.Services.Fat.Fs this.exp.SendNakCreateAndBindFile(this.fileExp, error); } - } else { + } + else { CommandLogger.Log(" -> Failed ({0})\n", error); assert parent == null; assert childName == null; @@ -751,7 +755,8 @@ namespace Microsoft.Singularity.Services.Fat.Fs CommandLogger.Log(" -> Failed ({0})\n", error); this.exp.SendNakDeleteFile(error); } - } else { + } + else { CommandLogger.Log(" -> Failed ({0})\n", error); if (childName != null) { delete childName; diff --git a/base/Services/Fat/Fs/BPB.sg b/base/Services/Fat/Fs/BPB.sg index 31ae5e3..e43c8ce 100644 --- a/base/Services/Fat/Fs/BPB.sg +++ b/base/Services/Fat/Fs/BPB.sg @@ -199,7 +199,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs private uint volumeId; // 3..6 - // XXX replace if/when arrays become + // TODO: replace if/when arrays become // valid in overlay. private byte volumeLabel00; // 7 private byte volumeLabel01; // 8 @@ -213,7 +213,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs private byte volumeLabel09; // 16 private byte volumeLabel10; // 17 - // XXX replace if/when arrays become + // TODO: replace if/when arrays become // valid in overlay. private byte fileSystemType0; // 18 private byte fileSystemType1; // 19 @@ -318,7 +318,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs private ushort fsInfoSector; // 12..13 - Primary pointer to FsInfo private ushort bootRecordCopy; // 14..15 - // XXX replace if/when arrays become + // TODO: replace if/when arrays become // valid in overlay. internal byte Reserved00; // 16 internal byte Reserved01; // 17 @@ -339,7 +339,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs internal byte BootSignature; // 30 private uint volumeId; // 31..34 - // XXX replace if/when arrays become + // TODO: replace if/when arrays become // valid in overlay. private byte volumeLabel00; // 35 private byte volumeLabel01; // 36 @@ -353,7 +353,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs private byte volumeLabel09; // 44 private byte volumeLabel10; // 45 - // XXX replace if/when arrays become + // TODO: replace if/when arrays become // valid in overlay. private byte fileSystemType0; // 46 private byte fileSystemType1; // 47 diff --git a/base/Services/Fat/Fs/Bitmap.sg b/base/Services/Fat/Fs/Bitmap.sg index e1a158d..c79a3e7 100644 --- a/base/Services/Fat/Fs/Bitmap.sg +++ b/base/Services/Fat/Fs/Bitmap.sg @@ -306,7 +306,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs private void SetRegion(int byteIndex, int bitIndex, int length) /*^ requires byteIndex >= 0; ^*/ /*^ requires bitIndex >= 0 && bitIndex <= 7; ^*/ - /*^ requires length > SmallAllocationLength; ^*/ + /*^ requires length >= SmallAllocationLength; ^*/ { int lhs = 8 - bitIndex; SetBits(byteIndex, bitIndex, lhs); @@ -325,7 +325,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs private void ClearRegion(int byteIndex, int bitIndex, int length) /*^ requires byteIndex >= 0; ^*/ /*^ requires bitIndex >= 0 && bitIndex <= 7; ^*/ - /*^ requires length > SmallAllocationLength; ^*/ + /*^ requires length >= SmallAllocationLength; ^*/ { int lhs = 8 - bitIndex; ClearBits(byteIndex, bitIndex, lhs); @@ -493,7 +493,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs int requestLength, out int allocStart, out int allocLength) - /*^ requires requestLength > SmallAllocationLength; ^*/ + /*^ requires requestLength >= SmallAllocationLength; ^*/ /*^ requires requestLength <= MaxAllocationLength; ^*/ { if (startByte >= this.map.Length) { @@ -949,16 +949,16 @@ namespace Microsoft.Singularity.Services.Fat.Fs const int BPS = 64; for (int i = 0; i < Length; i += BPS) { - Console.Write("\n{0:x6}: " , i); + DebugStub.Write("\n{0:x6}: " , __arglist(i)); int todo = Math.Min(Length - i, BPS); for (int j = 0; j < todo; j++) { if ((j & 15) == 8) - Console.Write(" "); + DebugStub.Write(" "); - Console.Write("{0}", IsAllocated(i + j) ? 1 : 0); + DebugStub.Write(IsAllocated(i + j) ? "1" : "0"); } } - Console.Write("\n"); + DebugStub.Write("\n"); } } @@ -1100,7 +1100,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs for (int i = 0; i < bitmapSize; i += allocationSize) { int start; int length; - if (! b.Allocate(allocationSize, out start, out length)) { + if (!b.Allocate(allocationSize, out start, out length)) { b.Dump(); Console.WriteLine("Allocation failed (i = {0}, length = {1})", i, allocationSize); Debug.Assert(false); diff --git a/base/Services/Fat/Fs/BlockCache.sg b/base/Services/Fat/Fs/BlockCache.sg index cdd2ca1..2402b54 100644 --- a/base/Services/Fat/Fs/BlockCache.sg +++ b/base/Services/Fat/Fs/BlockCache.sg @@ -641,21 +641,22 @@ namespace Microsoft.Singularity.Services.Fat.Fs // which will update the state of the block both when it // starts and completes. if (enqueueBlock) { + Monitor.Exit(cacheLine); // NB cookie is index // in cache line of node. blockWriter.Enqueue(this, cacheLine[nodeIndex].SectorId, nodeIndex); } - - // Wakeup anyone waiting for this block or this cache-line - // NB Threads may be sleeping because node has data transiently - // unavailable (CleanBorrowed,DirtyBorrowed,ReadPending, or - // WritePending), or because it's waiting for a victim to replace - // and this node might now be eligible. - Monitor.Pulse(cacheLine); - - Monitor.Exit(cacheLine); + else { + // Wakeup anyone waiting for this block or this cache-line + // NB Threads may be sleeping because node has data transiently + // unavailable (CleanBorrowed,DirtyBorrowed,ReadPending, or + // WritePending), or because it's waiting for a victim to replace + // and this node might now be eligible. + Monitor.Pulse(cacheLine); + Monitor.Exit(cacheLine); + } } internal void Read(uint blockId, @@ -731,8 +732,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs cacheLine[nodeIndex].Release(blockData, NodeState.Dirty, cacheTicks[line]++); - if (cacheLine[nodeIndex].State != oldState) - { + if (cacheLine[nodeIndex].State != oldState) { // NB cookie is index // in cache line of node. blockWriter.Enqueue(this, @@ -792,8 +792,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs cacheLine[nodeIndex].Release(blockData, NodeState.Dirty, cacheTicks[line]++); - if (cacheLine[nodeIndex].State != oldState) - { + if (cacheLine[nodeIndex].State != oldState) { // NB cookie is index // in cache line of node. blockWriter.Enqueue(this, @@ -857,6 +856,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs cacheLine[nodeIndex].AcquiredTransition(NodeState.WritePending, cacheTicks[line]++); Monitor.Exit(cacheLine); + return blockData; } diff --git a/base/Services/Fat/Fs/BlockIndex.sg b/base/Services/Fat/Fs/BlockIndex.sg index 16b89a5..3d11fe9 100644 --- a/base/Services/Fat/Fs/BlockIndex.sg +++ b/base/Services/Fat/Fs/BlockIndex.sg @@ -145,8 +145,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs int threshold = IndirectBlock.MaxItems * DirectBlock.MaxItems; int depth = 0; - while (nthBlock >= threshold) - { + while (nthBlock >= threshold) { nthBlock -= threshold; threshold *= IndirectBlock.MaxItems; depth++; @@ -350,8 +349,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs nthBlock -= DirectBlock.MaxItems; int threshold = IndirectBlock.MaxItems * DirectBlock.MaxItems; int depth = 0; - while (nthBlock >= threshold) - { + while (nthBlock >= threshold) { nthBlock -= threshold; threshold *= IndirectBlock.MaxItems; depth++; diff --git a/base/Services/Fat/Fs/ChannelIo.sg b/base/Services/Fat/Fs/ChannelIo.sg index 9ab31e2..8536349 100644 --- a/base/Services/Fat/Fs/ChannelIo.sg +++ b/base/Services/Fat/Fs/ChannelIo.sg @@ -7,8 +7,6 @@ // File: ChannelIo.sg // -using Microsoft.Singularity.Directory; - using Microsoft.SingSharp; using Microsoft.Singularity.Channels; using Microsoft.Singularity.Directory; diff --git a/base/Services/Fat/Fs/ComponentTest.sg b/base/Services/Fat/Fs/ComponentTest.sg index cf427d0..2980fbc 100644 --- a/base/Services/Fat/Fs/ComponentTest.sg +++ b/base/Services/Fat/Fs/ComponentTest.sg @@ -10,8 +10,6 @@ // // -using DirectoryServices.Utils; - using Microsoft.SingSharp; using Microsoft.Singularity.Channels; using Microsoft.Singularity.Directory; @@ -257,7 +255,8 @@ namespace Microsoft.Singularity.Services.Fat.Fs } CreateTree((!)child, fanout, depth - 1); - } finally { + } + finally { delete nodeName; } } diff --git a/base/Services/Fat/Fs/Directory.sg b/base/Services/Fat/Fs/Directory.sg index 770004e..99b1e1b 100644 --- a/base/Services/Fat/Fs/Directory.sg +++ b/base/Services/Fat/Fs/Directory.sg @@ -431,18 +431,12 @@ namespace Microsoft.Singularity.Services.Fat.Fs int totalEntriesNeeded = Math.Min(allocStart + allocLength + 1, MaxDirectoryEntryLimit); if (totalEntriesNeeded > CurrentDirectoryEntryLimit) { - int oldSize = CurrentDirectoryEntryLimit; if ((this.IsFixedSize || !LockedProvisionDirectory(totalEntriesNeeded)) ) { entryBitmap.Free(allocStart, allocLength); return MSD.ErrorCode.DirectoryFull; } - DebugStub.Print("Growing directory {0} -> {1} -> {2}\n", - __arglist(oldSize, - totalEntriesNeeded, - CurrentDirectoryEntryLimit)); - } // @@ -680,7 +674,9 @@ namespace Microsoft.Singularity.Services.Fat.Fs // // Purge associated clusters // - FatVolume.Fat.FreeChain((int)firstCluster); + if (firstCluster != 0) { + FatVolume.Fat.FreeChain((int)firstCluster); + } } private void LockedInvalidateEntriesWithinBlock(int blockNumber, @@ -807,8 +803,10 @@ namespace Microsoft.Singularity.Services.Fat.Fs return MSD.ErrorCode.IsOpen; } - // Remove from recently used cache if there - FatVolume.FileCache.Get(match.FirstCluster); + if (match.FirstCluster != 0) { + // Remove from recently used cache if there + FatVolume.FileCache.Get(match.FirstCluster); + } LockedDeleteMatch(ref match); } @@ -1470,7 +1468,8 @@ namespace Microsoft.Singularity.Services.Fat.Fs } } offset += length; - } else { + } + else { // Expected ordinal value to have last // entry set. DebugStub.Break(); @@ -1629,7 +1628,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs { int cluster; if (blockIndex.Lookup(blockNumber, out cluster) == false) { - /* NOT REACHED */ + // NOT REACHED assert false; } BlockCache bc = FatVolume.ClusterCache; @@ -1712,7 +1711,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs internal void UpdateFileSize(int shortEntryOffset, uint newFileBytes) requires shortEntryOffset >= 0; - // requires shortEntryOffset < CurrentDirectoryEntryLimit; + // requires shortEntryOffset < CurrentDirectoryEntryLimit; { int blockOffset, byteOffset; GetBlockAndByteOffsets(shortEntryOffset, @@ -1732,6 +1731,29 @@ namespace Microsoft.Singularity.Services.Fat.Fs } } + internal void UpdateFirstCluster(int shortEntryOffset, + uint newFirstCluster) + requires shortEntryOffset >= 0; + // requires shortEntryOffset < CurrentDirectoryEntryLimit; + { + int blockOffset, byteOffset; + GetBlockAndByteOffsets(shortEntryOffset, + out blockOffset, out byteOffset); + lock (this) { + bool updated = false; + + byte []! in ExHeap sector = (!)AcquireBlock(blockOffset); + try { + ref DirectoryEntry de = ref sector [byteOffset]; + updated = de.UpdateFirstCluster(newFirstCluster); + DebugStub.Assert(de.FirstCluster == newFirstCluster); + } + finally { + ReleaseBlock(blockOffset, sector, updated); + } + } + } + internal byte GetMutableAttributes(int shortEntryOffset) { int blockOffset, byteOffset; @@ -1808,7 +1830,13 @@ namespace Microsoft.Singularity.Services.Fat.Fs int firstCluster = fsObject.FirstCluster; File file = fsObject as File; if (file != null) { - FatVolume.FileCache.Add(firstCluster, file); + if (firstCluster != 0) { + FatVolume.FileCache.Add(firstCluster, file); + } else { + // File has zero size and no valid first + // cluster to be used as an index in the + // recently used files cache. + } return; } else { @@ -1868,16 +1896,20 @@ namespace Microsoft.Singularity.Services.Fat.Fs return MSD.ErrorCode.InsufficientResources; } - // Allocate a cluster for storage - int newFsObjectCluster; - int newFsObjectLength; - if (!FatVolume.Fat.AllocateChain(this.FirstCluster, 1, - out newFsObjectCluster, - out newFsObjectLength)) { - delete shortName; - return MSD.ErrorCode.CapacityReached; + // Allocate a cluster for storage for directories, files have + // no space reserved. + int newFsObjectCluster = 0; + int newFsObjectLength = 0; + + if (!isFile) { + if (!FatVolume.Fat.AllocateChain(this.FirstCluster, 1, + out newFsObjectCluster, + out newFsObjectLength)) { + delete shortName; + return MSD.ErrorCode.CapacityReached; + } + assert newFsObjectLength == 1; } - assert newFsObjectLength == 1; lock (this) { MSD.ErrorCode ec = @@ -2072,8 +2104,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs } private void LockedGetAttributes(int shortEntryOffset, - out MSD.NodeType nodeType, - out uint sizeBytes) + ref FileAttributesRecord fileAttributes) { int entriesPerBlock = DirectoryEntriesPerBlock; int blockNumber = shortEntryOffset / entriesPerBlock; @@ -2085,15 +2116,18 @@ namespace Microsoft.Singularity.Services.Fat.Fs ref DirectoryEntry de = ref buffer[bOffset]; if (de.IsFile) { - nodeType = MSD.NodeType.File; + fileAttributes.Type = MSD.NodeType.File; } else if (de.IsDirectory) { - nodeType = MSD.NodeType.Directory; + fileAttributes.Type = MSD.NodeType.Directory; } else { - nodeType = MSD.NodeType.BadNode; + fileAttributes.Type = MSD.NodeType.BadNode; } - sizeBytes = de.FileSize; + fileAttributes.FileSize = de.FileSize; + fileAttributes.CreationTime = de.CreationDateTime.Ticks; + fileAttributes.LastWriteTime = de.LastWriteDateTime.Ticks; + fileAttributes.LastAccessTime = de.LastAccessDateTime.Ticks; } finally { ReleaseBlock(blockNumber, buffer, false); @@ -2102,31 +2136,29 @@ namespace Microsoft.Singularity.Services.Fat.Fs internal void GetAttributes(int shortEntryOffset, - out MSD.NodeType nodeType, - out uint sizeBytes) + ref FileAttributesRecord fileAttributes) { lock (this) { LockedGetAttributes(shortEntryOffset, - out nodeType, out sizeBytes); + ref fileAttributes); } } internal MSD.ErrorCode GetAttributes(char[]! in ExHeap name, - out MSD.NodeType nodeType, - out uint sizeBytes) + ref FileAttributesRecord fileAttributes) { lock (this) { FindMatch match = new FindMatch(); if (!LockedFindByName(name, ref match)) { - nodeType = MSD.NodeType.BadNode; - sizeBytes = 0; + fileAttributes.Type = MSD.NodeType.BadNode; + fileAttributes.FileSize = 0; return MSD.ErrorCode.NotFound; } LockedGetAttributes(match.ShortEntryOffset, - out nodeType, out sizeBytes); + ref fileAttributes); } return MSD.ErrorCode.NoError; } @@ -2135,11 +2167,10 @@ namespace Microsoft.Singularity.Services.Fat.Fs { int entriesPerBlock = DirectoryEntriesPerBlock; int blockNumber = shortEntryOffset / entriesPerBlock; - shortEntryOffset %= entriesPerBlock; byte[]! in ExHeap buffer = (!)AcquireBlock(blockNumber); try { - int bOffset = shortEntryOffset * DirectoryEntry.Length; + int bOffset = (shortEntryOffset % entriesPerBlock) * DirectoryEntry.Length; ref DirectoryEntry de = ref buffer[bOffset]; return new File(this, shortEntryOffset, @@ -2160,14 +2191,16 @@ namespace Microsoft.Singularity.Services.Fat.Fs // Add reference to already opened file file.AddRef(); } - else if (null != (file = FatVolume.FileCache.Get(firstCluster))) { + else if (0 != firstCluster && + null != (file = FatVolume.FileCache.Get(firstCluster))) { // Resurrect recently closed file in FileCache file.AddRef(); // LockedAddOpenFsObject(file); File! sgcWorkAround = file; LockedAddOpenFsObject(sgcWorkAround); - } else { + } + else { // Open the file file = LockedInstantiateFileObject(shortEntryOffset); File sgcWorkAround = file; @@ -2211,7 +2244,8 @@ namespace Microsoft.Singularity.Services.Fat.Fs if (directory != null) { // Add reference to already opened file directory.AddRef(); - } else if (null != + } + else if (null != (directory = FatVolume.DirectoryCache.Get(firstCluster)) ) { @@ -2219,7 +2253,8 @@ namespace Microsoft.Singularity.Services.Fat.Fs directory.AddRef(); Directory! sgcWorkAround = directory; LockedAddOpenFsObject(sgcWorkAround); - } else { + } + else { // Open the directory directory = new Directory(this, shortEntryOffset, firstCluster); diff --git a/base/Services/Fat/Fs/DirectoryEntry.sg b/base/Services/Fat/Fs/DirectoryEntry.sg index 07300ce..0c735dd 100644 --- a/base/Services/Fat/Fs/DirectoryEntry.sg +++ b/base/Services/Fat/Fs/DirectoryEntry.sg @@ -208,6 +208,33 @@ namespace Microsoft.Singularity.Services.Fat.Fs set { lastAccessDate = ByteOrder.HostToLittleEndian(value); } } + public DateTime CreationDateTime + { + get { + ushort date = ByteOrder.LittleEndianToHost(creationDate); + ushort time = ByteOrder.LittleEndianToHost(creationTime); + return GenDateTime(date,time); + } + } + + + public DateTime LastWriteDateTime + { + get { + ushort date = ByteOrder.LittleEndianToHost(lastWriteDate); + ushort time = ByteOrder.LittleEndianToHost(lastWriteTime); + return GenDateTime(date,time); + } + } + + public DateTime LastAccessDateTime + { + get { + ushort date = ByteOrder.LittleEndianToHost(lastAccessDate); + return GenDate(date); + } + } + internal uint FirstCluster { get { @@ -237,6 +264,28 @@ namespace Microsoft.Singularity.Services.Fat.Fs set { fileSize = ByteOrder.HostToLittleEndian(value); } } + DateTime GenDateTime( ushort date, ushort time) + { + DateTime dateTime = new DateTime(0); + // datetime starts at 1/1/1/ so subtract 1 from year/month/day + dateTime = dateTime.AddYears((date >> YearShift) + BaseYear -1); + dateTime = dateTime.AddMonths( ((date >> MonthShift) & 0x0f) -1 ); + dateTime = dateTime.AddDays((date & 0x1f) -1); + dateTime = dateTime.AddHours(time >> HourShift); + dateTime = dateTime.AddMinutes((time >> MinuteShift) & 0x03f ); + dateTime = dateTime.AddSeconds((time & 0x01f) * 2); + return dateTime; + } + + DateTime GenDate( ushort date) + { + DateTime dateTime = new DateTime(0); + dateTime = dateTime.AddYears((date >> YearShift) + BaseYear -1 ); + dateTime = dateTime.AddMonths(((date >> MonthShift) & 0x0f) -1 ); + dateTime = dateTime.AddDays((date & 0x1f) -1); + return dateTime; + } + ushort MakeDate(DateTime when) requires when.Year >= BaseYear; { @@ -407,6 +456,17 @@ namespace Microsoft.Singularity.Services.Fat.Fs return updated; } + internal bool UpdateFirstCluster(uint firstCluster) + { + bool updated = this.UpdateWriteTime(); + if (this.FirstCluster != firstCluster) { + assert this.FirstCluster == 0; + this.FirstCluster = firstCluster; + updated = true; + } + return updated; + } + internal bool UpdateWriteTime() { DateTime now = DateTime.Now; @@ -429,7 +489,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs if (c > 127) { return true; } - if ((c < 0x20) || (c == 0x22) || (c >= 0x2a && c <= 0x2c) || + if ((c < 0x20) || (c == 0x22) || (c >= 0x2a && c <= 0x2c) || (c == 0x2e) || (c == 0x2f) || (c >= 0x3a && c <= 0x3f) || (c >= 0x5b && c <= 0x5d)) { return false; diff --git a/base/Services/Fat/Fs/Disk.sg b/base/Services/Fat/Fs/Disk.sg index 6e296c2..eab1a4b 100644 --- a/base/Services/Fat/Fs/Disk.sg +++ b/base/Services/Fat/Fs/Disk.sg @@ -121,8 +121,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs internal DiskEndpoint! Get() { Monitor.Enter(this); - try - { + try { for (;;) { if (available > 0) { // Programmer error missed @@ -144,8 +143,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs internal void Add(DiskEndpoint! endpoint) { Monitor.Enter(this); - try - { + try { InternalAdd(endpoint); Monitor.Pulse(this); } @@ -379,25 +377,25 @@ namespace Microsoft.Singularity.Services.Fat.Fs OpenDevice(DirectoryServiceContract.Imp:Ready! nsImp, string! deviceName) { + if (deviceName.Length == 0) { + DebugStub.WriteLine("FatFs: Cannot open device; device name is empty."); + DebugStub.Break(); + return null; + } + DiskDeviceContract.Exp! diskExp; DiskDeviceContract.Imp! diskImp; DiskDeviceContract.NewChannel(out diskImp, out diskExp); - nsImp.SendBind(Bitter.FromString2(deviceName), diskExp); - switch receive { - case nsImp.RecvAckBind(): - return ValidatedDiskEndpoint(diskImp); - break; - case nsImp.RecvNakBind(badExp, errorCode): - delete diskImp; - delete badExp; - return null; - break; - case nsImp.ChannelClosed(): - delete diskImp; - return null; - break; + ErrorCode error; + if (!SdsUtils.Bind(deviceName, nsImp, diskExp, out error)) { + delete diskImp; + string! errorText = SdsUtils.ErrorCodeToString(error); + DebugStub.WriteLine(String.Format("FatFs: Failed to open disk '{0}': {1}", deviceName, errorText)); + return null; } + + return ValidatedDiskEndpoint(diskImp); } static internal Disk @@ -411,8 +409,8 @@ namespace Microsoft.Singularity.Services.Fat.Fs // DiskDeviceContract.Imp diskImp = OpenDevice(nsImp, deviceName); if (diskImp == null) { - Console.WriteLine("Could not establish a connection to {0}\n", - deviceName); + DebugStub.WriteLine("Could not establish a connection to {0}\n", + __arglist(deviceName)); return null; } @@ -421,28 +419,28 @@ namespace Microsoft.Singularity.Services.Fat.Fs // ulong startSector; if (GetDiskStartSector(diskImp, out startSector) == false) { - Console.WriteLine("Lost connection to {0}.\n", deviceName); + DebugStub.WriteLine("Lost connection to {0}.\n", __arglist(deviceName)); delete diskImp; return null; } ulong totalSectors; if (GetDiskSectorCount(diskImp, out totalSectors) == false) { - Console.WriteLine("Lost connection to {0}.\n", deviceName); + DebugStub.WriteLine("Lost connection to {0}.\n", __arglist(deviceName)); delete diskImp; return null; } ushort systemId; if (GetDiskSystemId(diskImp, out systemId) == false) { - Console.WriteLine("Lost connection to {0}.\n", deviceName); + DebugStub.WriteLine("Lost connection to {0}.\n", __arglist(deviceName)); delete diskImp; return null; } DiskAttributes da; if (GetDiskAttributes(diskImp, out da) == false) { - Console.WriteLine("Lost connection to {0}.\n", deviceName); + DebugStub.WriteLine("Lost connection to {0}.\n", __arglist(deviceName)); delete diskImp; return null; } @@ -456,7 +454,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs for (int i = 1; i < channelCount; i++) { diskImp = OpenDevice(nsImp, deviceName); if (diskImp == null) { - Console.WriteLine("Failed establishing connection {0} to {1}.\n", i, deviceName); + DebugStub.WriteLine("Failed establishing connection {0} to {1}.\n", __arglist(i, deviceName)); return null; } pool.Add(new DiskEndpoint(diskImp)); diff --git a/base/Services/Fat/Fs/Fat12.sg b/base/Services/Fat/Fs/Fat12.sg index c8471bf..2593b53 100644 --- a/base/Services/Fat/Fs/Fat12.sg +++ b/base/Services/Fat/Fs/Fat12.sg @@ -469,7 +469,8 @@ namespace Microsoft.Singularity.Services.Fat.Fs int tmp = (((int)b1) << 8) + (int)b0; if ((cluster & 0x1) == 0x1) { tmp >>= 4; - } else { + } + else { tmp &= 0xfff; } next = tmp; @@ -555,7 +556,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs return true; } catch (Exception e) { - Console.WriteLine("Caught {0}", e); + DebugStub.WriteLine("Caught {0}", __arglist(e.ToString())); } preferredFat = (preferredFat + 1) % bpbSummary.NumberOfFats; diff --git a/base/Services/Fat/Fs/Fat16.sg b/base/Services/Fat/Fs/Fat16.sg index e6d527d..01c719f 100644 --- a/base/Services/Fat/Fs/Fat16.sg +++ b/base/Services/Fat/Fs/Fat16.sg @@ -139,7 +139,8 @@ namespace Microsoft.Singularity.Services.Fat.Fs GetNext(1, out next); if (value) { repl = next | CleanShutdownMask; - } else { + } + else { repl = next & ~CleanShutdownMask; } if (repl != next) { @@ -160,7 +161,8 @@ namespace Microsoft.Singularity.Services.Fat.Fs GetNext(1, out next); if (value) { repl = next | HardErrorMask; - } else { + } + else { repl = next & ~HardErrorMask; } if (repl != next) { @@ -360,7 +362,8 @@ namespace Microsoft.Singularity.Services.Fat.Fs if (chainLength == 0) { // First successful allocation - record chain head chainHead = allocStart; - } else { + } + else { // Subsequent allocation - link into chain SetNext(chainTail, allocStart); } @@ -369,7 +372,8 @@ namespace Microsoft.Singularity.Services.Fat.Fs chainLength += allocLength; request = Math.Min(Bitmap.MaxAllocationLength, clustersToAdd - chainLength); - } else { + } + else { request /= 2; } } @@ -605,7 +609,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs return true; } catch (Exception e) { - Console.WriteLine("Caught {0}", e); + DebugStub.WriteLine("Caught {0}", __arglist(e.ToString())); } preferredFat = (preferredFat + 1) % bpbSummary.NumberOfFats; diff --git a/base/Services/Fat/Fs/Fat32.sg b/base/Services/Fat/Fs/Fat32.sg index d61b847..191d2ff 100644 --- a/base/Services/Fat/Fs/Fat32.sg +++ b/base/Services/Fat/Fs/Fat32.sg @@ -162,7 +162,8 @@ namespace Microsoft.Singularity.Services.Fat.Fs GetNext(1, out next); if (value) { repl = next | CleanShutdownMask; - } else { + } + else { repl = next & ~CleanShutdownMask; } if (repl != next) { @@ -183,7 +184,8 @@ namespace Microsoft.Singularity.Services.Fat.Fs GetNext(1, out next); if (value) { repl = next | HardErrorMask; - } else { + } + else { repl = next & ~HardErrorMask; } if (repl != next) { @@ -399,7 +401,8 @@ namespace Microsoft.Singularity.Services.Fat.Fs if (chainLength == 0) { // First successful allocation - record chain head chainHead = allocStart; - } else { + } + else { // Subsequent allocation - link into chain SetNext(chainTail, allocStart); } @@ -408,7 +411,8 @@ namespace Microsoft.Singularity.Services.Fat.Fs chainLength += allocLength; request = Math.Min(Bitmap.MaxAllocationLength, clustersToAdd - chainLength); - } else { + } + else { request /= 2; } } @@ -633,7 +637,7 @@ namespace Microsoft.Singularity.Services.Fat.Fs return true; } catch (Exception e) { - Console.WriteLine("Caught {0}", e); + DebugStub.WriteLine("Caught {0}", __arglist(e.ToString())); } preferredFat = (preferredFat + 1) % bpbSummary.NumberOfFats; diff --git a/base/Services/Fat/Fs/FatFs.csproj b/base/Services/Fat/Fs/FatFs.csproj index 02a2802..69b6c42 100644 --- a/base/Services/Fat/Fs/FatFs.csproj +++ b/base/Services/Fat/Fs/FatFs.csproj @@ -1,8 +1,6 @@  + + + + + Library + NetStackEvents + C# + Full + {d7d931ce-04a4-45c1-84ea-2bcfe078ab1c} + + + + + + + + + + + + diff --git a/base/Services/NetStack/Events/TcpSessionEvents.cs b/base/Services/NetStack/Events/TcpSessionEvents.cs new file mode 100644 index 0000000..5147c02 --- /dev/null +++ b/base/Services/NetStack/Events/TcpSessionEvents.cs @@ -0,0 +1,156 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +using System.Runtime.CompilerServices; + +using Microsoft.Singularity.Transform; +using Microsoft.Singularity.Eventing; + +namespace Microsoft.Singularity.NetStack.Events +{ + /// + /// TCP Contract Entrypoints (use enums, not strings, for density). + /// + public enum TcpSessionContractEntrypoints : byte + { + Unknown = 0, + + Connect, + Bind, + Listen, + Accept, + DoneSending, + DoneReceiving, + Abort, + Close, + BindLocalEndPoint, + + Read, + PollRead, + Write, + + IsSessionAvailable, + IsDataAvailable, + + GetLocalAddress, + GetLocalPort, + GetRemoteAddress, + GetRemotePort, + + ChannelClosed, + } + + /// + /// Event Definitions for the TcpSession class. + /// + [Event] + public abstract class TcpSessionEvents : EventSource + { + [EventEnum] + public enum TcpSessionState : byte + { + Unknown = 0, + Closed = 1, + Listen = 2, + SynRecvd = 3, + SynSent = 4, + Established = 5, + CloseWait = 6, + LastAck = 7, + FinWait1 = 8, + FinWait2 = 9, + Closing = 10, + }; + + internal static readonly string[] TcpSessionStateNames = new string[] { + "Undefined", + "Closed ", + "Listen ", + "SynRecvd ", + "SynSent ", + "Establish", + "CloseWait", + "LastAck ", + "FinWait1 ", + "FinWait2 ", + "Closing ", + }; + + public static TcpSessionEvents EventLog; + + [Event] + public abstract bool TcpSessionStateChangeEvent( + ushort sessionId, + TcpSessionState fromState, + TcpSessionState toState); + public static string TcpSessionStateChangeEvent_Format = "TCP: Ses{0,3} ({1}) State Change to {2}."; + + public static UIntPtr TcpSessionState_Handle; + + [EventEnum] + public enum TcpPacketFlags + { + F = 1 << 0, + S = 1 << 1, + R = 1 << 2, + P = 1 << 3, + A = 1 << 4, + I = 1 << 5, + E = 1 << 6, + C = 1 << 7, + }; + + internal static readonly string[] TcpPacketFlagsNames = new string[] { + "F", "S", "R", "P", "A", "I", "E", "C", + }; + + [Event] + public abstract bool TcpSessionReceivedPacketEvent( + ushort sessionId, + TcpSessionState currentState, + TcpPacketFlags packetFlags, + uint packetLength); + public static string TcpSessionReceivedPacketEvent_Format = "TCP: Ses{0,3} ({1}) Packet ({2}) Len{3,4} received"; + + public static UIntPtr TcpPacketFlags_Handle; + + public override void RegisterEnumSymbols() + { + base.RegisterEnumSymbols(); + + // Register the Tcp Session State enumeration (values, not flags). + if (HostController.RegisterEnum("TcpSessionState", + DataType.__uint8, + ref TcpSessionState_Handle)) + { + for (int enumValue = 0; + enumValue < TcpSessionStateNames.Length; + enumValue++) + { + HostController.RegisterEnumValue(TcpSessionState_Handle, + TcpSessionStateNames[enumValue], + (UInt64)enumValue, + 0); + } + } + + // Register the Tcp Packet Flags enumeration (flags, not values). + if (HostController.RegisterEnum("TcpPacketFlags", + DataType.__uint8, + ref TcpPacketFlags_Handle)) + { + for (int shift = 0; shift < 8; ++shift) + { + HostController.RegisterEnumValue(TcpPacketFlags_Handle, + TcpPacketFlagsNames[shift], + 1ul << shift, + (byte)TcpPacketFlagsNames[shift][0]); + } + } + } + } +} diff --git a/base/Services/NetStack/NetDrivers/DebugAdapter.cs b/base/Services/NetStack/NetDrivers/DebugAdapter.cs new file mode 100644 index 0000000..8ebcdda --- /dev/null +++ b/base/Services/NetStack/NetDrivers/DebugAdapter.cs @@ -0,0 +1,162 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity / Netstack +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: LoopbackAdapter.cs +// + +using NetStack.Common; +using System; +using System.Collections; +using System.Threading; + +using Drivers.Net; + +using Microsoft.Contracts; + +namespace NetStack.NetDrivers +{ + /// + /// Interface that debug clients must implement to receive packets + /// from DebugAdapter instances. The DebugAdapter and client + /// interact synchronously. + /// + public interface IDebugAdapterClient + { + void RegisterAdapter(DebugAdapter adapter); + void Receive(NetPacket packet); + } + + public class DebugAdapter : IAdapter + { + private const int RingSize = 64; + + private IDebugAdapterClient! client; + private EthernetAddress address; + + private AutoResetEvent! writeEvent; + private AutoResetEvent! readEvent; + + private ArrayList! rxBuffer; + private ArrayList! txBuffer; + + static int instantiations = 0; + + [NotDelayed] + public DebugAdapter(IDebugAdapterClient! debugClient) + { + instantiations++; + + // + // Microsoft OUI 00:50:f2:xx:xx:xx + // It doesn't really matter, but something other + // than zero is preferable for testing purposes. + // + int x = 0x222111 ^ instantiations; + address = new EthernetAddress(0x00, 0x50, 0xf2, + (byte)(x >> 16), + (byte)(x >> 8), + (byte)(x & 0xff)); + + writeEvent = new AutoResetEvent(false); + readEvent = new AutoResetEvent(false); + + txBuffer = new ArrayList(); + rxBuffer = new ArrayList(); + + this.client = debugClient; + + base(); + + debugClient.RegisterAdapter(this); + } + + public string DriverName + { + get { return "Debug Adapter"; } + } + + public string DriverVersion + { + get { return "0.1"; } + } + + public EthernetAddress HardwareAddress + { + get { return address; } + } + + public uint LinkSpeed + { + get { return 10 * 1000 * 1000; } + } + + public int TxSlotsFree + { + get { return RingSize - rxBuffer.Count; } + } + + /// + /// Method to simulate arrival of a packet. + /// + public bool ReceivePacket(NetPacket packet) + { + Console.WriteLine("GetReceivePacket"); + if (rxBuffer.Count < RingSize) { + rxBuffer.Add(packet); + readEvent.Set(); + return true; + } + return false; + } + + public void GetReceivedPackets(Queue! outQueue) + { + Console.WriteLine("GetReceivedPackets"); + for (int i = 0; i < rxBuffer.Count; i++) { + outQueue.Enqueue(rxBuffer[i]); + } + rxBuffer.Clear(); + readEvent.Reset(); + } + + public uint GetTransmittedPackets() + { + uint count = (uint)txBuffer.Count; + txBuffer.Clear(); + writeEvent.Reset(); + return count; + } + + public void PopulateRxRing(uint count) + { + // Do nothing + } + + public void PopulateTxRing(NetPacket[]! packets, uint packetCount) + { + for (uint i = 0; i < packetCount; i++) { + NetPacket! packet = (!)packets[i]; + + // Add an equivalently sized packet back to recycle list + txBuffer.Add(new NetPacket(packet.Length)); + + // Give packet to client + client.Receive(packet); + } + writeEvent.Set(); + } + + public WaitHandle GetReadHandle() + { + return readEvent; + } + + public WaitHandle GetWriteHandle() + { + return writeEvent; + } + } +} diff --git a/base/Services/NetStack/NetDrivers/LoopbackAdapter.cs b/base/Services/NetStack/NetDrivers/LoopbackAdapter.cs new file mode 100644 index 0000000..1651be9 --- /dev/null +++ b/base/Services/NetStack/NetDrivers/LoopbackAdapter.cs @@ -0,0 +1,198 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity / Netstack +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: LoopbackAdapter.cs +// + +// #define DEBUG_LOOPBACK + +using System; +using System.Collections; +using System.Diagnostics; +using System.Threading; + +using Microsoft.Singularity; + +using Drivers.Net; + +using NetStack.Common; + +namespace NetStack.NetDrivers +{ + public class LoopbackAdapter : IAdapter + { + public const int RingSize = 256; // TODO: Why public? + + static int instantiations = 0; + + private EthernetAddress address; + + private AutoResetEvent writeEvent; + private AutoResetEvent readEvent; + + private Queue rxBuffer; + + private int txDeliveryComplete; + private int rxAvailable; + + public LoopbackAdapter() + { + LoopbackAdapter.instantiations++; + + // + // Microsoft OUI 00:50:F2:xx:xx:xx + // It doesn't really matter, but something other + // than zero is preferable for testing purposes. + // + int x = 0xFFFFFF - instantiations; + address = new EthernetAddress(0x00, 0x50, 0xF2, + (byte)((x >> 16) & 0xFF), + (byte)((x >> 8) & 0xFF), + (byte)((x >> 0) & 0xFF)); + + writeEvent = new AutoResetEvent(false); + readEvent = new AutoResetEvent(false); + + rxBuffer = new Queue(LoopbackAdapter.RingSize); + txDeliveryComplete = 0; + rxAvailable = 0; + } + + public string DriverName + { + get { return "Loopback Adapter"; } + } + + public string DriverVersion + { + get { return "0.1"; } + } + + public EthernetAddress HardwareAddress + { + get { return address; } + } + + public uint LinkSpeed + { + get { return 1000 * 1000 * 1000; } + } + + public int TxSlotsFree + { + get { + int txSlotsFree; + lock (this) { + txSlotsFree = LoopbackAdapter.RingSize + - rxBuffer.Count + - txDeliveryComplete; + DebugStub.Assert(txSlotsFree >= 0); + } + return txSlotsFree; + } + } + + public void GetReceivedPackets(Queue! outQueue) + { + lock (this) { + DebugStub.Assert(rxAvailable >= 0 && + rxAvailable <= LoopbackAdapter.RingSize); + + // Move Receive packets to 'outQueue'. + Object packet; + while (rxAvailable > 0 && + (packet = rxBuffer.Dequeue()) != null) { + outQueue.Enqueue(packet); + txDeliveryComplete++; + rxAvailable--; + } + + DebugStub.Assert(rxAvailable >= 0 && + rxAvailable <= LoopbackAdapter.RingSize); + DebugStub.Assert( + txDeliveryComplete <= LoopbackAdapter.RingSize); + DebugPrint("Loopback: GetReceivedPackets() -> {0}\n", + __arglist(outQueue.Count)); + DebugPrintStatus(); + + readEvent.Reset(); + writeEvent.Set(); + } + } + + [Conditional("DEBUG_LOOPBACK")] + private void DebugPrintStatus() + { + DebugStub.Print("-- queued: {0} txComplete {1}\n", + __arglist(rxBuffer.Count, + txDeliveryComplete)); + } + + [Conditional("DEBUG_LOOPBACK")] + private static void DebugPrint(string format, __arglist) + { + DebugStub.Print(format, new ArgIterator(__arglist)); + } + + [Conditional("DEBUG_LOOPBACK")] + private static void DebugPrint(string format) + { + DebugStub.Print(format); + } + + public uint GetTransmittedPackets() + { + lock (this) { + DebugPrint("Loopback: GetTransmittedPackets() -> {0}\n", + __arglist(txDeliveryComplete)); + DebugPrintStatus(); + + uint count = (uint)txDeliveryComplete; + txDeliveryComplete = 0; + return count; + } + } + + public void PopulateRxRing(NetPacket! packet) + { + DebugPrint("Loopback: PopulateRxRing()\n"); + DebugPrintStatus(); + // Do nothing + } + + public void PopulateTxRing(NetPacket[]! packets, uint count) + { + lock (this) { + DebugPrint("Loopback: PopulateTxRing({0})\n", + __arglist(count)); + DebugPrintStatus(); + for (uint i = 0; i < count; i++) { + NetPacket! packet = (!)packets[i]; + byte[]! txdata = (!)packet.ToContiguous(); + rxBuffer.Enqueue(new NetPacket(txdata)); + rxAvailable++; + } + + DebugStub.Assert(rxBuffer.Count <= RingSize); + + writeEvent.Reset(); + if (rxBuffer.Count != 0) { + readEvent.Set(); + } + } + } + + public WaitHandle GetReadHandle() + { + return readEvent; + } + + public WaitHandle GetWriteHandle() + { + return writeEvent; + } + } +} diff --git a/base/Services/NetStack/NetStack.sg b/base/Services/NetStack/NetStack.sg index 81d3765..ea47718 100644 --- a/base/Services/NetStack/NetStack.sg +++ b/base/Services/NetStack/NetStack.sg @@ -20,6 +20,9 @@ using Microsoft.Singularity.Channels; using Microsoft.Singularity.Directory; using Microsoft.Singularity.Security; using Microsoft.Singularity.Io; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.ServiceManager; + [assembly: ApplicationPublisherAttribute("singularity.microsoft.com")] [assembly: AssertPrivilegeAttribute("$register-privilege.localhost")] @@ -28,128 +31,184 @@ namespace NetStack using NetStack.Channels.Private; using NetStack.Channels.Nic; + [Category("Service")] + internal class ServiceParameters + { + [CustomEndpoint] + public readonly TRef ControlEndpointRef; + + [CustomEndpoint] + public readonly TRef EventEndpointRef; + + reflective private ServiceParameters(); + } + /// /// The NetStack Application class is a single instance service running - /// on a host. When it runs it attempts to register as /dev/netstack; + /// on a host. When it runs it attempts to register as /service/netstack; /// success indicates no other instances and it then initializes /// the various NetStack subsystems. /// - class NetStackApplication + class NetStackApplication : ITracked { - private static string nsName = "/dev/netstack"; - - private static TRef nsChannel; - - private static void ServiceProviderPump() + NetStackApplication( + [Claims]ServiceProcessContract.Exp! svcontrol, + [Claims]ServiceEventContract.Imp! svevent) { - ServiceProviderContract.Exp exp = nsChannel.Acquire(); - try - { - for (;;) - { - switch receive { - case exp.RecvConnect(serviceExp): - exp.SendNackConnect(serviceExp); - break; - - case exp.ChannelClosed(): - goto end; - } - } - end: ; - } - finally - { - delete exp; - } + this.svcontrol = svcontrol; + this.svevent = svevent; } - private static bool RegisterServiceProvider() + void ITracked.Dispose() { - DirectoryServiceContract.Imp ns = - DirectoryService.NewClientEndpoint(); + delete svcontrol; + delete svevent; + } - ServiceProviderContract.Imp! imp; - ServiceProviderContract.Exp! exp; - ServiceProviderContract.NewChannel(out imp, out exp); + void ITracked.Acquire() {} + void ITracked.Release() {} + void ITracked.Expose() {} + void ITracked.UnExpose() {} - try - { - ns.SendRegister(Bitter.FromString2(nsName), imp); - switch receive - { - case ns.AckRegister(): - nsChannel = - new TRef(exp); - Thread t = - new Thread(new ThreadStart(ServiceProviderPump)); - t.Start(); - return true; - break; - case ns.NakRegister(rejected, error): - delete rejected; - delete exp; - break; - case ns.ChannelClosed(): - delete exp; - break; - case unsatisfiable: - DebugStub.Break(); - delete exp; - break; - } + + readonly ServiceProcessContract.Exp! svcontrol; + readonly ServiceEventContract.Imp! svevent; + + readonly DNSExpManager! dnsManager = new DNSExpManager(); + readonly RoutingExpManager! routingManager = new RoutingExpManager(); + readonly TcpExpManager! tcpManager = new TcpExpManager(); + readonly UdpExpManager! udpManager = new UdpExpManager(); + readonly IPExpManager! ipManager = new IPExpManager(); + + bool Start() + { + try { + ipManager.Start(); + + routingManager.Start(); + tcpManager.Start(); + udpManager.Start(); + dnsManager.Start(); + + NicFactory.Probe(); + + return true; + } + catch (Exception ex) { + Dbg("An exception occurred during service startup: " + ex.Message); return false; } - finally - { - delete ns; + } + + void ServiceMain() + { + bool run = true; + + expose (this) { + while (run) { + switch receive { + case svcontrol.Stop(): + Dbg("Received 'Stop' control from Service Manager."); + return; + + case svcontrol.Connect(subpath, exp): + Dbg("Received client connection from Service Manager."); + Dbg("We don't support connecting this way; rejecting client."); + delete subpath; + svcontrol.SendNakConnect(ErrorCode.ContractNotSupported, exp); + break; + + case svcontrol.Knock(): + svcontrol.SendAlive(); + break; + + case svcontrol.ChannelClosed(): + Dbg("The service control channel has closed! Not good! Terminating."); + return; + + } + } } } - public static void Start() + void Stop() { - NetStack.Runtime.StaticConfiguration.Initialize(); - NetStack.Runtime.StaticConfiguration.Start(); + // Stop the service provider threads. + Dbg("Stopping service provider threads"); + dnsManager.Stop(); + routingManager.Stop(); + ipManager.Stop(); + tcpManager.Stop(); + udpManager.Stop(); - IPExpManager ipManager = new IPExpManager(); - ipManager.Start(); - - DNSExpManager dnsManager = new DNSExpManager(); - dnsManager.Start(); - - RoutingExpManager routingManager = new RoutingExpManager(); - routingManager.Start(); - - TcpExpManager tcpManager = new TcpExpManager(); - tcpManager.Start(); - - UdpExpManager udpManager = new UdpExpManager(); - udpManager.Start(); - - NicFactory.Probe(); - } - - public static void Stop() - { + // StaticConfiguration.Stop() walks the list of installed modules and calls + // the StopModule() method of each. We do this after stopping the service + // provider threads. + Dbg("Stopping protocol modules"); NetStack.Runtime.StaticConfiguration.Stop(); } - public static int Main(String [] args) + internal static int AppMain(ServiceParameters! parameters) { - if (RegisterServiceProvider() == false) - { - Console.WriteLine("Could not start NetStack as it is already registered in namespace."); - return -1; + ServiceProcessContract.Exp! svcontrol = parameters.ControlEndpointRef.Acquire(); + ServiceEventContract.Imp! svevent = parameters.EventEndpointRef.Acquire(); + + NetStack.Runtime.StaticConfiguration.Initialize(); + NetStack.Runtime.StaticConfiguration.Start(); + + NetStackApplication! app = new NetStackApplication(svcontrol, svevent); + + try { + return app.Run(); } + finally { + delete app; + Dbg("NetStack is terminating."); + } + } -#if false // Change this to true to enable GC accounting in the network stack.n - AppRuntime.EnableGCAccounting = true; - DebugStub.WriteLine("NetStack: Enabled GC Accounting"); -#endif + static ServiceProviderContract.Exp RegisterServiceProvider(DirectoryServiceContract.Imp! rootds, string! name) + { + ServiceProviderContract.Imp! provider_imp; + ServiceProviderContract.Exp! provider_exp; + ServiceProviderContract.NewChannel(out provider_imp, out provider_exp); - Start(); + ErrorCode error; + if (SdsUtils.Register(name, rootds, provider_imp, out error)) + return provider_exp; + else { + Dbg("FAILED to register name '{0}', error = {1}", name, SdsUtils.ErrorCodeToString(error)); + delete provider_exp; + return null; + } + } - return 0; + int Run() + { + expose (this) { + if (Start()) { + svcontrol.SendStartSucceeded(); + ServiceMain(); + Stop(); + return 0; + } + else { + Dbg("Sending StartFailed to Service Manager."); + svcontrol.SendStartFailed(ServiceError.Unknown); + Stop(); + return -1; + } + } + } + + static void Dbg(string! line) + { + DebugStub.WriteLine(line); + } + + static void Dbg(string! format, params object[]! args) + { + Dbg(String.Format(format, args)); } } } diff --git a/base/Services/NetStack/NetStackApp.csproj b/base/Services/NetStack/NetStackApp.csproj new file mode 100644 index 0000000..fff041d --- /dev/null +++ b/base/Services/NetStack/NetStackApp.csproj @@ -0,0 +1,35 @@ + + + + + + Exe + NetStackApp + true + {9D5E75E9-44EB-404F-917A-11E95D6104C6} + + + + + + + + + + + + + + + + + + + + diff --git a/base/Services/NetStack/NetStackApp.sg b/base/Services/NetStack/NetStackApp.sg new file mode 100644 index 0000000..ba38e91 --- /dev/null +++ b/base/Services/NetStack/NetStackApp.sg @@ -0,0 +1,161 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: NetStack.sg +// +// Note: +// + +using System; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Threading; + +using Microsoft.SingSharp; +using Microsoft.Singularity; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Security; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.ServiceManager; + +[assembly: ApplicationPublisherAttribute("singularity.microsoft.com")] +[assembly: AssertPrivilegeAttribute("$register-privilege.localhost")] + +namespace NetStack +{ + using NetStack.Channels.Private; + using NetStack.Channels.Nic; + + [ConsoleCategory(HelpMessage="Runs the NetStack as an application", DefaultAction=true)] + internal class Parameters + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [Endpoint] + public readonly TRef nsRef; + + reflective internal Parameters(); + + internal int AppMain() { + return NetStackApplication.Main(); + } + } + + /// + /// The NetStack Application class is a single instance service running + /// on a host. When it runs it attempts to register as /service/netstack; + /// success indicates no other instances and it then initializes + /// the various NetStack subsystems. + /// + class NetStackApplication + { + DNSExpManager! dnsManager; + RoutingExpManager! routingManager; + TcpExpManager! tcpManager; + UdpExpManager! udpManager; + IPExpManager! ipManager; + + NetStackApplication() + { + NetStack.Runtime.StaticConfiguration.Initialize(); + NetStack.Runtime.StaticConfiguration.Start(); + + dnsManager = new DNSExpManager(); + routingManager = new RoutingExpManager(); + tcpManager = new TcpExpManager(); + udpManager = new UdpExpManager(); + ipManager = new IPExpManager(); + } + + bool Start() + { + try { + ipManager.Start(); + + routingManager.Start(); + tcpManager.Start(); + udpManager.Start(); + dnsManager.Start(); + + NicFactory.Probe(); + + return true; + } + catch (Exception ex) { + Dbg("An exception occurred during service startup: " + ex.Message); + return false; + } + } + + void ServiceMain() + { + bool run = true; + + while (run) { + Thread.Sleep(1000); + } + } + + void Stop() + { + // Stop the service provider threads. + Dbg("Stopping service provider threads"); + dnsManager.Stop(); + routingManager.Stop(); + ipManager.Stop(); + tcpManager.Stop(); + udpManager.Stop(); + + // StaticConfiguration.Stop() walks the list of installed modules and calls + // the StopModule() method of each. We do this after stopping the service + // provider threads. + Dbg("Stopping protocol modules"); + NetStack.Runtime.StaticConfiguration.Stop(); + } + + public static int Main() + { + NetStackApplication app = new NetStackApplication(); + + try { + return app.Run(); + } + finally { + Dbg("NetStack is terminating."); + } + } + + int Run() + { + if (Start()) { + ServiceMain(); + Stop(); + return 0; + } + else { + Dbg("Sending StartFailed to Service Manager."); + Stop(); + return -1; + } + } + + static void Dbg(string! line) + { + DebugStub.WriteLine(line); + } + + static void Dbg(string! format, params object[]! args) + { + Dbg(String.Format(format, args)); + } + } +} diff --git a/base/Services/NetStack/NetStackLib.csproj b/base/Services/NetStack/NetStackLib.csproj index 4e02ce8..28a1041 100644 --- a/base/Services/NetStack/NetStackLib.csproj +++ b/base/Services/NetStack/NetStackLib.csproj @@ -1,8 +1,6 @@  + + + + @@ -29,9 +29,6 @@ - - - @@ -48,35 +45,31 @@ + + + - - - - - - - - + - + - - + + + + - - - + diff --git a/base/Services/NetStack/NetStackService.csproj b/base/Services/NetStack/NetStackService.csproj index 2c8a51c..8229ded 100644 --- a/base/Services/NetStack/NetStackService.csproj +++ b/base/Services/NetStack/NetStackService.csproj @@ -1,8 +1,6 @@  ({2},{3})\n", senderIP, senderMAC, targetIP, targetMAC ); @@ -268,8 +258,7 @@ namespace NetStack.Runtime mux.Adapter.HardwareAddress); mux.SendDirect(new NetPacket(data)); } - else - { + else { // otherwise we are done DebugPrint( "Handling reply ({2},{3}) <--- ({0},{1})\n", @@ -277,8 +266,7 @@ namespace NetStack.Runtime ); } - if (merged && !updated) - { + if (merged && !updated) { DebugPrint(arpTable.ToString()); } @@ -301,9 +289,9 @@ namespace NetStack.Runtime object cookie, TimeSpan timeout) { - /* - XXXX Check pending request does not already exist! - */ + // + //XXXX Check pending request does not already exist! + // EthernetAddress localMac = targetMux.Adapter.HardwareAddress; AddPendingRequest(targetIP, callback, cookie, timeout); @@ -353,8 +341,7 @@ namespace NetStack.Runtime new PendingRequest(address, callback, cookie, expiry) ); - if (pendingRequests.Count == 1) - { + if (pendingRequests.Count == 1) { DateTime nextPoll = DateTime.UtcNow + PollPeriod; Core.Instance().TheDispatcher.AddCallback( new Dispatcher.Callback(OnRequestTimeout), @@ -368,11 +355,9 @@ namespace NetStack.Runtime EthernetAddress macAddress) { int i = 0; - while (i != pendingRequests.Count) - { + while (i != pendingRequests.Count) { PendingRequest request = (PendingRequest!) pendingRequests[i]; - if (request.address == ipAddress) - { + if (request.address == ipAddress) { request.callback(ipAddress, macAddress, request.cookie); pendingRequests.RemoveAt(i); continue; @@ -386,11 +371,9 @@ namespace NetStack.Runtime DateTime now = ((TimeoutArgument!) args).now; int i = 0; - while (i != pendingRequests.Count) - { + while (i != pendingRequests.Count) { PendingRequest request = (PendingRequest!) pendingRequests[i]; - if (request.expiry < now) - { + if (request.expiry < now) { DebugPrint("Expiring request"); request.callback(request.address, @@ -402,8 +385,7 @@ namespace NetStack.Runtime i++; } - if (pendingRequests.Count > 0) - { + if (pendingRequests.Count > 0) { DateTime nextPoll = now + PollPeriod; Core.Instance().TheDispatcher.AddCallback( new Dispatcher.Callback(OnRequestTimeout), @@ -508,8 +490,7 @@ namespace NetStack.Runtime public bool AddEntry(ArpEntry! e) { // if no more room, make one - if (arpEntries.Count >= maxEntries) - { + if (arpEntries.Count >= maxEntries) { PurgeLRUEntry(); } e.EntryAge = this.defaultAge; @@ -544,11 +525,9 @@ namespace NetStack.Runtime ArpEntry lruElement = (ArpEntry!)dicEnum.Value; - while (dicEnum.MoveNext()) - { + while (dicEnum.MoveNext()) { ArpEntry current = (ArpEntry)dicEnum.Value; - if (current.EntryAge < lruElement.EntryAge) - { + if (current.EntryAge < lruElement.EntryAge) { lruElement = current; } } @@ -564,16 +543,13 @@ namespace NetStack.Runtime // list to hold items to be deleted. ArrayList purgeItems = new ArrayList(); - foreach (ArpEntry! e in arpEntries.Values) - { - if (e.Dynamic == true && --e.EntryAge == 0) - { + foreach (ArpEntry! e in arpEntries.Values) { + if (e.Dynamic == true && --e.EntryAge == 0) { purgeItems.Add(e); } } - foreach (ArpEntry! e in purgeItems) - { + foreach (ArpEntry! e in purgeItems) { RemoveEntry(e); } @@ -593,8 +569,7 @@ namespace NetStack.Runtime public bool Lookup(IPv4 targetIP, out EthernetAddress macAddress) { ArpEntry e = arpEntries[targetIP] as ArpEntry; - if (e != null) - { + if (e != null) { e.EntryAge = Age; macAddress = e.MacAddress; return true; @@ -610,8 +585,7 @@ namespace NetStack.Runtime stringBuilder.Append("-----------------------------------------------------------\n"); string [] types = { "static", "dynamic" }; - foreach (ArpEntry! e in arpEntries.Values) - { + foreach (ArpEntry! e in arpEntries.Values) { stringBuilder.Append(e.IPAddress + " " + e.MacAddress + " " + types[ e.Dynamic ? 1 : 0] + " [Age=" + e.EntryAge + "]\n"); diff --git a/base/Services/NetStack/Runtime/Core.cs b/base/Services/NetStack/Runtime/Core.cs index d774a37..e69e6b0 100644 --- a/base/Services/NetStack/Runtime/Core.cs +++ b/base/Services/NetStack/Runtime/Core.cs @@ -1,46 +1,44 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- -/** - * Microsoft Research, Cambridge - * author: Yaron Weinsberg, Richard Black - */ +/// +// Microsoft Research, Cambridge +// // #define DEBUG_CORE -using NetStack.Common; using System; using System.Collections; using System.Diagnostics; -using System.Threading; - using System.Net.IP; -using Drivers.Net; -using NetStack.NetDrivers; -using NetStack.Protocols; +using System.Threading; using Microsoft.Singularity; +using Drivers.Net; + +using NetStack.Common; +using NetStack.NetDrivers; +using NetStack.Protocols; + namespace NetStack.Runtime { - /** - * This is the heart of the networking stack. - * - A single thread inside the core handles - * packet reception, Qos demultiplexing and - * later on multiplexing on a specific protocol queue. - * - The thread also handles user send & receive in order - * to avoid reentrancy deadlocks etc. - * - The Runtime uses an asynchronous IO in order to - * avoid locking and denial of service. - * - The Core's thread, never invokes user's callbacks - * directly, the user can poll for a message or get - * his thread to sleep, waiting for a new message event. - */ + /// + // This is the heart of the networking stack. + // - A single thread inside the core handles + // packet reception, Qos demultiplexing and + // later on multiplexing on a specific protocol queue. + // - The thread also handles user send & receive in order + // to avoid reentrancy deadlocks etc. + // - The Runtime uses an asynchronous IO in order to + // avoid locking and denial of service. + // - The Core's thread, never invokes user's callbacks + // directly, the user can poll for a message or get + // his thread to sleep, waiting for a new message event. + // public class Core : INetModule { private bool activated = false; @@ -105,11 +103,11 @@ namespace NetStack.Runtime // create a session of a specific protocol public Session CreateSession(string protocolName) { - IProtocol p = GetProtocolByName(protocolName); - if (p == null) { + IProtocol protocol = GetProtocolByName(protocolName); + if (protocol == null) { return null; } - return (p.CreateSession()); + return (protocol.CreateSession()); } // register a new session in the runtime @@ -117,20 +115,19 @@ namespace NetStack.Runtime // sessions (multiple writers). // In the Meanwhile the internal thread can scan the // sessions in order to HandleOutgoingSessions - internal void RegisterSession(IProtocol! p, Session s) + internal void RegisterSession(IProtocol! protocol, Session newSession) { - lock (sessions.SyncRoot) - { - if (!sessions.Contains(p)) { - ArrayList l = new ArrayList(); - l.Add(s); - sessions[p] = l; + lock (sessions.SyncRoot) { + if (!sessions.Contains(protocol)) { + ArrayList sessionList = new ArrayList(); + sessionList.Add(newSession); + sessions[protocol] = sessionList; } else { - ArrayList l = (ArrayList!)sessions[p]; - lock (l.SyncRoot) { - if (!l.Contains(s)) { - l.Add(s); + ArrayList! sessionList = (ArrayList!)sessions[protocol]; + lock (sessionList.SyncRoot) { + if (!sessionList.Contains(newSession)) { + sessionList.Add(newSession); } } } @@ -138,15 +135,15 @@ namespace NetStack.Runtime } // Deregister a session - internal void DeregisterSession(IProtocol! p, Session s) + internal void DeregisterSession(IProtocol! protocol, Session sessionToRemove) { lock (sessions.SyncRoot) { - if (!sessions.Contains(p)) + if (!sessions.Contains(protocol)) Core.Panic("Error: Can't deregister a session for an unknown protocol!"); else { - ArrayList l = (ArrayList!)sessions[p]; - lock (l.SyncRoot) { - l.Remove(s); + ArrayList sessionList = (ArrayList!)sessions[protocol]; + lock (sessionList.SyncRoot) { + sessionList.Remove(sessionToRemove); } } } @@ -173,9 +170,9 @@ namespace NetStack.Runtime return lastPort; } - internal bool ChangeSessionLocalPort(Session! s, int newPort) + internal bool ChangeSessionLocalPort(Session! session, int newPort) { - ArrayList tcpSessions = Core.Instance().GetSessions(s.Protocol); + ArrayList tcpSessions = Core.Instance().GetSessions(session.Protocol); // If no sessions registered yet, just do it... if (tcpSessions == null) { @@ -183,7 +180,7 @@ namespace NetStack.Runtime newPort = FindFreePort(tcpSessions); } - s.SetLocalPort((ushort) newPort); + session.SetLocalPort((ushort) newPort); return true; } @@ -196,15 +193,16 @@ namespace NetStack.Runtime newPort = FindFreePort(tcpSessions); } - if (newPort < 0) + if (newPort < 0) { return false; + } // Deregister session - Core.Instance().DeregisterSession(s.Protocol, s); + Core.Instance().DeregisterSession(session.Protocol, session); // Reregister session - s.SetLocalPort((ushort) newPort); - Core.Instance().RegisterSession(s.Protocol, s); + session.SetLocalPort((ushort) newPort); + Core.Instance().RegisterSession(session.Protocol, session); return true; } } @@ -234,7 +232,7 @@ namespace NetStack.Runtime return false; } - NetPacket packet = s.GetPacket(s.outQueue, false, 0); + NetPacket packet = s.GetPacket(s.outQueue, false, TimeSpan.Zero); if (packet == null) { return false; } @@ -243,14 +241,14 @@ namespace NetStack.Runtime packet.Mux = s.BoundMux; s.Protocol.OnProtocolSend(packet); - // NOTE Special case; if this is a TCP session + // NOTE: Special case; if this is a TCP session // and we just transmitted the very last packet it will // ever send, help out with the shutdown process by // signalling this session closed. TcpSession tcpS = s as TcpSession; if ((tcpS != null) && - (tcpS.stateContext == TCPFSM.CLOSED) && + (tcpS.currentState == TCPFSM.CLOSED) && (tcpS.outQueue.Count == 0)) { // That was the very last TCP frame for this session @@ -273,7 +271,7 @@ namespace NetStack.Runtime private int GetMaxOutgoingPackets() { - int n = ((Multiplexer!)this.muxTable[0]).FreeBufferSpace; + int n = this.muxTable.Count > 0 ? ((Multiplexer!)this.muxTable[0]).FreeBufferSpace : 0; for (int i = 1; i < this.muxTable.Count; i++) { n = Math.Min(n, ((Multiplexer!)this.muxTable[i]).FreeBufferSpace); @@ -289,8 +287,7 @@ namespace NetStack.Runtime // 2. after handling received packets internal NetStatus HandleOutgoingQueues(Dispatcher.CallbackArgs unused) { - lock (sessions.SyncRoot) - { + lock (sessions.SyncRoot) { DebugPrint("++++ OUTGOING QUEUE PASS ++++\n"); int todo = GetMaxOutgoingPackets(); @@ -577,8 +574,7 @@ namespace NetStack.Runtime protocols[protocol.ModuleName] = protocol; return true; } - catch (ArgumentException) - { + catch (ArgumentException) { return false; } } @@ -601,11 +597,11 @@ namespace NetStack.Runtime return protocols[protocolName] as IProtocol; } - /** - * Dispatch an incoming packets - * this method actually drives the - * packet through the stack's modules. - */ + /// + // Dispatch an incoming packets + // this method actually drives the + // packet through the stack's modules. + // private NetStatus DispatchPacket(NetPacket! pkt) { // look at the ethernet headers... @@ -706,7 +702,7 @@ namespace NetStack.Runtime // (this is a go/no-go method) public void Activate() { - Core.Log("NetStack Core Runtime Active..."); + // Core.Log("NetStack Core Runtime Active..."); // since all the protocols are initialized // start them up (start module) diff --git a/base/Services/NetStack/Runtime/DhcpClient.cs b/base/Services/NetStack/Runtime/DhcpClient.cs index 63921c9..8e23ac1 100644 --- a/base/Services/NetStack/Runtime/DhcpClient.cs +++ b/base/Services/NetStack/Runtime/DhcpClient.cs @@ -67,7 +67,7 @@ namespace NetStack.Runtime DhcpVendorSpecific.OptionCode }; - private const int PollMillis = 1000; + private static readonly TimeSpan PollInterval = TimeSpan.FromMilliseconds(1000); [ Conditional("DEBUG_DHCP_CLIENT") ] internal static void DebugPrint(string format, params object [] args) @@ -84,8 +84,7 @@ namespace NetStack.Runtime ~DhcpClient() { - if (workerThread != null) - { + if (workerThread != null) { Stop(); } } @@ -120,18 +119,15 @@ namespace NetStack.Runtime public bool Start() { - lock (this) - { - if (udpSession != null) - { + lock (this) { + if (udpSession != null) { return false; } Core core = Core.Instance(); UdpModule udpModule = core.GetProtocolByName("UDP") as UdpModule; - if (udpModule == null) - { + if (udpModule == null) { DebugPrint("DhcpClient.Start() failed -- " + "UdpModule not found.\n"); return false; @@ -157,8 +153,7 @@ namespace NetStack.Runtime public void Stop() { - lock (this) - { + lock (this) { workerDone = true; workerThread.Join(); @@ -232,8 +227,7 @@ namespace NetStack.Runtime UninstallDhcpOptions(); IPModule ip = (IPModule) Core.Instance().GetProtocolByName("IP"); - if (ip == null) - { + if (ip == null) { DebugPrint("Failed to get IP Module\n"); return false; } @@ -271,8 +265,7 @@ namespace NetStack.Runtime // string domainName = ip.HostConfiguration.GetDomainName(); // never used - if (domain != null) - { + if (domain != null) { ip.HostConfiguration.SetDomainName(domain.Value); } @@ -282,10 +275,8 @@ namespace NetStack.Runtime DhcpMultiIPv4Option dnsServers = dhcpOptions[DhcpDomainNameServer.OptionCode] as DhcpMultiIPv4Option; - if (dnsServers != null) - { - foreach (IPv4 server in dnsServers.Values) - { + if (dnsServers != null) { + foreach (IPv4 server in dnsServers.Values) { ip.HostConfiguration.AddNameServer(server); } } @@ -296,11 +287,9 @@ namespace NetStack.Runtime DhcpMultiIPv4Option staticRoutes = dhcpOptions[DhcpStaticRoutes.OptionCode] as DhcpMultiIPv4Option; - if (staticRoutes != null) - { + if (staticRoutes != null) { int routeCount = staticRoutes.Values.Length & ~1; // pairs - for (int i = 0; i < routeCount; i += 2) - { + for (int i = 0; i < routeCount; i += 2) { IPv4 destination = staticRoutes.Values[i]; IPv4 gateway = staticRoutes.Values[i + 1]; IPv4 ifAddress = address.Value; @@ -336,10 +325,8 @@ namespace NetStack.Runtime DhcpMultiIPv4Option dnsServers = activeDhcpOptions[DhcpDomainNameServer.OptionCode] as DhcpMultiIPv4Option; - if (dnsServers != null) - { - foreach (IPv4 server in dnsServers.Values) - { + if (dnsServers != null) { + foreach (IPv4 server in dnsServers.Values) { ip.HostConfiguration.AddNameServer(server); } } @@ -354,8 +341,7 @@ namespace NetStack.Runtime as DhcpStringOption; string domainName = ip.HostConfiguration.GetDomainName(); - if (domain != null && domainName == domain.Value) - { + if (domain != null && domainName == domain.Value) { ip.HostConfiguration.SetDomainName(""); } @@ -385,52 +371,43 @@ namespace NetStack.Runtime // Enter "Init" state of FSM ChangeState(new DhcpClientStateInitialize(this)); - while (workerDone == false) - { + while (workerDone == false) { // Check for timeouts DateTime now = DateTime.Now; - if (now >= renewalTimeout) - { + if (now >= renewalTimeout) { CancelRenewalTimeout(); @state.RenewalTimeoutEvent(); } - if (now >= rebindTimeout) - { + if (now >= rebindTimeout) { CancelRebindTimeout(); @state.RebindTimeoutEvent(); } - if (now >= stateTimeout) - { + if (now >= stateTimeout) { CancelStateTimeout(); @state.StateTimeoutEvent(); } // Poll for data - try - { - byte [] data = udpSession.PollCopyData(PollMillis); - if (data != null) - { + try { + byte [] data = udpSession.PollCopyData(PollInterval); + if (data != null) { SimpleBuffer sb = new SimpleBuffer(data); DhcpFormat dhcp = DhcpFormat.Parse(sb); // Check transaction id is ours - if (dhcp.TransactionID != transactionID) - { + if (dhcp.TransactionID != transactionID) { continue; } // Check client address is ours - if (dhcp.GetHardwareAddress() != MacAddress) - { + if (dhcp.GetHardwareAddress() != MacAddress) { continue; } @state.ReceiveEvent(dhcp); } } - catch (InvalidDhcpFormatException idfe) - { + catch (InvalidDhcpFormatException idfe) { DebugPrint(idfe.Message); } @@ -438,14 +415,12 @@ namespace NetStack.Runtime // from shell. ie we'd like to run and renew lease // but shell blocks on running process and cleans up // after it for the time being. - if (activeDhcpOptions != null) - { + if (activeDhcpOptions != null) { DebugPrint("Got options -- done\n"); break; } - if (DateTime.Now - startTime > TimeSpan.FromSeconds(5)) - { + if (DateTime.Now - startTime > TimeSpan.FromSeconds(5)) { DebugPrint("Timed out\n"); break; } diff --git a/base/Services/NetStack/Runtime/DhcpClientFSM.cs b/base/Services/NetStack/Runtime/DhcpClientFSM.cs index 047eaf3..44da423 100644 --- a/base/Services/NetStack/Runtime/DhcpClientFSM.cs +++ b/base/Services/NetStack/Runtime/DhcpClientFSM.cs @@ -11,6 +11,7 @@ // Note: This file does not yet implement the RENEWING or // REBINDING states. +using Microsoft.Singularity; using System; using System.Diagnostics; using System.Collections; @@ -156,8 +157,7 @@ namespace NetStack.Runtime // Check if offered address is valid (ie not zero // and below class E) IPv4 offeredAddress = dhcp.YourIPAddress; - if (offeredAddress == IPv4.Any || offeredAddress.IsMulticast()) - { + if (offeredAddress == IPv4.Any || offeredAddress.IsMulticast()) { return; } @@ -177,11 +177,9 @@ namespace NetStack.Runtime DhcpRouter.OptionCode, DhcpDomainNameServer.OptionCode }; - foreach (byte p in parameters) - { + foreach (byte p in parameters) { IDhcpOption ido = offeredOptions[p] as IDhcpOption; - if (ido == null) - { + if (ido == null) { return; } } @@ -246,8 +244,7 @@ namespace NetStack.Runtime DhcpFormat! dhcpFormat) { IDhcpOption option = offeredOptions[optionCode] as IDhcpOption; - if (option != null) - { + if (option != null) { dhcpFormat.AddOption(option); } } @@ -272,8 +269,7 @@ namespace NetStack.Runtime // Check if offered address is valid (ie not zero // and below class E) IPv4 offeredAddress = dhcp.YourIPAddress; - if (offeredAddress == IPv4.Any || offeredAddress.IsMulticast()) - { + if (offeredAddress == IPv4.Any || offeredAddress.IsMulticast()) { return; } @@ -281,13 +277,11 @@ namespace NetStack.Runtime SortedList offeredOptions = dhcp.GetOptions(); DhcpByteOption messageType = offeredOptions[DhcpMessageType.OptionCode] as DhcpByteOption; - if (messageType == null) - { + if (messageType == null) { return; } - switch (messageType.Value) - { + switch (messageType.Value) { case (byte) DhcpFormat.MessageType.Ack: break; case (byte) DhcpFormat.MessageType.Nak: @@ -304,11 +298,9 @@ namespace NetStack.Runtime DhcpDomainNameServer.OptionCode }; - foreach (byte p in parameters) - { + foreach (byte p in parameters) { IDhcpOption ido = offeredOptions[p] as IDhcpOption; - if (ido == null) - { + if (ido == null) { return; } } @@ -348,8 +340,7 @@ namespace NetStack.Runtime hostOptions[DhcpRenewalTime.OptionCode] as DhcpDWordOption; uint renewalSeconds = 3600; - if (renewalOption != null) - { + if (renewalOption != null) { renewalSeconds = renewalOption.Value; } client.SetRenewalTimeout(now + TimeSpan.FromSeconds(renewalSeconds)); @@ -358,8 +349,7 @@ namespace NetStack.Runtime DhcpDWordOption rebindOption = hostOptions[DhcpRebindingTime.OptionCode] as DhcpDWordOption; uint rebindSeconds = renewalSeconds + 60; - if (rebindOption != null) - { + if (rebindOption != null) { rebindSeconds = Math.Max(rebindOption.Value, renewalSeconds + 1); } @@ -370,12 +360,11 @@ namespace NetStack.Runtime hostOptions[DhcpRequestedIPAddress.OptionCode] = DhcpRequestedIPAddress.Create(hostAddress); - if (client.InstallDhcpOptions(hostOptions) == false) - { + if (client.InstallDhcpOptions(hostOptions) == false) { client.ChangeState(new DhcpClientStateInitialize(client)); } - Console.WriteLine("\nDHCP client acquired lease of {0} for {1} " + - "seconds.", hostAddress, rebindSeconds); + DebugStub.WriteLine("\nDHCP client acquired lease of {0} for {1} seconds.", + __arglist(hostAddress.ToString(), rebindSeconds)); } internal override void ReceiveEvent(DhcpFormat! dhcp) diff --git a/base/Services/NetStack/Runtime/Dispatcher.cs b/base/Services/NetStack/Runtime/Dispatcher.cs index 7642a28..c43b8c0 100644 --- a/base/Services/NetStack/Runtime/Dispatcher.cs +++ b/base/Services/NetStack/Runtime/Dispatcher.cs @@ -4,12 +4,16 @@ // // ==--== +// #define DEBUG_TCP + using System; +using System.Net.IP; using System.Threading; + using RJBlack; -using System.Net.IP; using Drivers.Net; + using NetStack.NetDrivers; namespace NetStack.Runtime @@ -45,7 +49,9 @@ namespace NetStack.Runtime public CallbackArgs arg; public Internal next; } - private Internal iHead, iTail; + + private Internal internalEventsHead; + private Internal internalEventsTail; // This event is used to wake up the main dispatcher // thread, typically because we have changed the @@ -75,14 +81,16 @@ namespace NetStack.Runtime // Public Methods public void AddCallback(Callback fun, CallbackArgs arg) { - Internal i = new Internal(); - i.fun = fun; - i.arg = arg; - if (iHead == null) - iHead = i; - else - iTail.next = i; - iTail = i; + Internal internalEvent = new Internal(); + internalEvent.fun = fun; + internalEvent.arg = arg; + if (internalEventsHead == null) { + internalEventsHead = internalEvent; + } + else { + internalEventsTail.next = internalEvent; + } + internalEventsTail = internalEvent; // Make sure to wake up and notice this dispatcherStrobe.Set(); @@ -92,8 +100,9 @@ namespace NetStack.Runtime { Timer t = new Timer(fun, args, time); - lock (timers) - { timers.Add(t); } + lock (timers) { + timers.Add(t); + } // Make sure to wake up and notice this dispatcherStrobe.Set(); @@ -107,12 +116,10 @@ namespace NetStack.Runtime Callback [] newFuns = new Callback[n + 1]; CallbackArgs [] newArgs = new CallbackArgs[n + 1]; - if (n > 0) - { + if (n > 0) { // Lock to defend against the main loop shuffling the array // while we are copying it - lock (eHandles.SyncRoot) - { + lock (eHandles.SyncRoot) { eHandles.CopyTo(newHandles, 0); eFuns.CopyTo(newFuns, 0); eArgs.CopyTo(newArgs, 0); @@ -140,8 +147,7 @@ namespace NetStack.Runtime { int newLength = eHandles.Length - 1; - if (newLength == 0) - { + if (newLength == 0) { eHandles = new WaitHandle[0]; eFuns = null; eArgs = null; @@ -157,16 +163,14 @@ namespace NetStack.Runtime CallbackArgs [] newArgs = new CallbackArgs[newLength]; // Copy before the removed item... - if (lhLength > 0) - { + if (lhLength > 0) { Array.Copy(eHandles, 0, newHandles, 0, lhLength); Array.Copy(eFuns, 0, newFuns, 0, lhLength); Array.Copy(eArgs, 0, newArgs, 0, lhLength); } // Copy after the removed item... - if (rhLength > 0) - { + if (rhLength > 0) { Array.Copy(eHandles, index + 1, newHandles, index, rhLength); Array.Copy(eFuns, index + 1, newFuns, index, rhLength); Array.Copy(eArgs, index + 1, newArgs, index, rhLength); @@ -183,12 +187,9 @@ namespace NetStack.Runtime public void RemoveCallback(WaitHandle wh) { - lock (eHandles.SyncRoot) - { - for (int i = 0; i < eHandles.Length; i++) - { - if (((!)eHandles[i]).Equals(wh)) - { + lock (eHandles.SyncRoot) { + for (int i = 0; i < eHandles.Length; i++) { + if (((!)eHandles[i]).Equals(wh)) { RemoveWaitHandleCallback(i); return; } @@ -198,8 +199,7 @@ namespace NetStack.Runtime public bool RemoveTimeoutCallback(RJBlack.Timer! fun) { - lock (timers) - { + lock (timers) { return timers.Remove(fun); } } @@ -216,14 +216,18 @@ namespace NetStack.Runtime // Execute forever public void Execute() { - while (stopped == false) - { + while (stopped == false) { // Internal events first - if (iHead != null) - { - Internal i = iHead; - iHead = i.next; - i.fun(i.arg); + if (internalEventsHead != null) { + Internal internalEvent = internalEventsHead; + internalEventsHead = internalEvent.next; +#if DEBUG_TCP + Core.Log("Dispatching Internal Event at " + + DateTime.UtcNow.ToString() + + " calling '" + internalEvent.fun.ToString() + + "'."); +#endif + internalEvent.fun(internalEvent.arg); continue; } @@ -233,24 +237,33 @@ namespace NetStack.Runtime // Deal with timers TimeSpan waitTime = TimeSpan.MaxValue; - lock (timers) - { - Timer t = (Timer)timers.Advance(now); + lock (timers) { + Timer timer = (Timer)timers.Advance(now); - if (t != null) - { + if (timer != null) { // This timer has expired; service // it immediately and start over - t.fun(t.arg); +#if DEBUG_TCP + Core.Log("Timer Expiration at " + + (new DateTime((long) now)).ToString() + + " calling '" + timer.fun.ToString() + + "'."); +#endif + timer.fun(timer.arg); continue; } - t = (Timer)timers.GetSoonest(); + timer = (Timer)timers.GetSoonest(); - if (t != null) - { - ulong soonest = t.time; - waitTime = new TimeSpan((long)(soonest - now)); + if (timer != null) { + ulong soonest = timer.time; + if (soonest < now) { + Core.Log("Negative WaitTime"); + waitTime = new TimeSpan(0L); + } + else { + waitTime = new TimeSpan((long)(soonest - now)); + } } } @@ -264,12 +277,10 @@ namespace NetStack.Runtime // Shuffle the array values so the same tasks aren't // always preferred to others (WaitAny() will unblock // against the lowest-index WaitHandle in the array) - lock (handles.SyncRoot) - { + lock (handles.SyncRoot) { int n = handles.Length; - for(int i = 0; i < n - 1; ++i) - { + for (int i = 0; i < n - 1; ++i) { // Swap the current element of the list with // an entry that follow it. Instead of an // expensive real random-number generator, @@ -302,14 +313,14 @@ namespace NetStack.Runtime throw new ApplicationException("Invalid WaitAny"); Callback cb = funs[rc]; - if (cb != null) - { + if (cb != null) { NetStatus res = cb(args[rc]); if (!NetStatus.SUCCEEDED(res)) Core.Panic("Error handling internal event."); } } + Core.Log("NetStack dispatcher exiting...\n"); } diff --git a/base/Services/NetStack/Runtime/DnsClient.cs b/base/Services/NetStack/Runtime/DnsClient.cs index edf6aff..33454d9 100644 --- a/base/Services/NetStack/Runtime/DnsClient.cs +++ b/base/Services/NetStack/Runtime/DnsClient.cs @@ -89,8 +89,7 @@ namespace NetStack.Runtime [ System.Diagnostics.Conditional("DEBUG_DNS_CLIENT") ] private static void Dump(byte []! data, int offset) { - for (int i = offset; i < data.Length; i += 16) - { + for (int i = offset; i < data.Length; i += 16) { Core.Log("{0:x4} ", i); int n = Math.Min(16, data.Length - i) + i; for (int j = i; j < n; j++) @@ -98,8 +97,7 @@ namespace NetStack.Runtime for (int j = n; j != i + 16; j++) Core.Log(" "); Core.Log(" "); - for (int j = i; j < n; j++) - { + for (int j = i; j < n; j++) { char c = '.'; if (data[j] > 31 && data[j] < 128) c = (char)data[j]; @@ -129,8 +127,7 @@ namespace NetStack.Runtime return askResponse; assert answer != null; - if (answer.GetRCode() != Dns.RCode.NoError) - { + if (answer.GetRCode() != Dns.RCode.NoError) { DebugPrint("Dns query failed: RCode = {0}\n", answer.GetRCode()); return StatusCode.RequestFailed; } @@ -138,8 +135,7 @@ namespace NetStack.Runtime hostEntry = new IPv4HostEntry( new string [] { name }, new IPv4[] {} ); ArrayList addressList = new ArrayList(); - foreach (Dns.ResourceRecord! rr in answer.AnswerRRs) - { + foreach (Dns.ResourceRecord! rr in answer.AnswerRRs) { if (rr.Type != Dns.Type.A || rr.Class != Dns.Class.Internet) continue; @@ -151,8 +147,7 @@ namespace NetStack.Runtime continue; int offset = 0; - while (offset != rdata.Length) - { + while (offset != rdata.Length) { addressList.Add(IPv4.ParseBytes(rdata, offset)); offset += 4; } @@ -191,8 +186,7 @@ namespace NetStack.Runtime return askResponse; assert answer != null; - if (answer.GetRCode() != Dns.RCode.NoError) - { + if (answer.GetRCode() != Dns.RCode.NoError) { DebugPrint("Dns query failed: RCode = {0}\n", answer.GetRCode()); return StatusCode.RequestFailed; } @@ -200,8 +194,7 @@ namespace NetStack.Runtime hostEntry = new IPv4HostEntry(new string[]{}, new IPv4 [] { address }); ArrayList aliases = new ArrayList(); - foreach (Dns.ResourceRecord! rr in answer.AnswerRRs) - { + foreach (Dns.ResourceRecord! rr in answer.AnswerRRs) { DebugPrint("Answer: Type = {0} Class = {1} TTL = {2}\n", rr.Type, rr.Class, rr.TtlSeconds); if ((rr.Type != Dns.Type.PTR && rr.Type != Dns.Type.CNAME) || @@ -213,8 +206,7 @@ namespace NetStack.Runtime continue; int offset = 0; - while (offset != rdata.Length) - { + while (offset != rdata.Length) { string alias; offset += Dns.LabelEncoding.GetString(rdata, offset, out alias); @@ -234,12 +226,10 @@ namespace NetStack.Runtime { IPv4 hostAddress; - if (IPv4.Parse(hostName, out hostAddress) == true) - { + if (IPv4.Parse(hostName, out hostAddress) == true) { StatusCode status = GetHostByAddress(hostAddress, out hostEntry); - if (status != StatusCode.Success) - { + if (status != StatusCode.Success) { // As a convenience to the caller, instead of failing // outright, at least give back the parsed version of // the net address. @@ -264,8 +254,7 @@ namespace NetStack.Runtime outFrame.Queries.AddRange(queries); byte[] outData = new byte [outFrame.Size]; - if (outData.Length > Dns.Format.MaxUdpMessageLength) - { + if (outData.Length > Dns.Format.MaxUdpMessageLength) { return StatusCode.OverSizeMessage; } @@ -274,20 +263,17 @@ namespace NetStack.Runtime byte[] rcvData; StatusCode askResponse = AskDnsServer(dnsServer, outData, out rcvData); - if (askResponse != StatusCode.Success) - { + if (askResponse != StatusCode.Success) { return askResponse; } assert rcvData != null; - try - { + try { offset = 0; Dump(rcvData, offset); answer = Dns.Format.Parse(rcvData, ref offset); } - catch (Exception e) - { + catch (Exception e) { DebugPrint("Parser threw {0}", e); return StatusCode.TransportError; } @@ -308,8 +294,7 @@ namespace NetStack.Runtime { Core core = Core.Instance(); UdpModule udpModule = core.GetProtocolByName("UDP") as UdpModule; - if (udpModule == null) - { + if (udpModule == null) { rcvData = null; return StatusCode.TransportError; } @@ -318,8 +303,7 @@ namespace NetStack.Runtime udpModule.CreateBoundSession(IPv4.Any, 0, dnsServer, Dns.Format.ServerPort); udpSession.WriteData(outData); - rcvData = udpSession.PollCopyData((int) (timeout.Ticks / - TimeSpan.TicksPerMillisecond)); + rcvData = udpSession.PollCopyData(TimeSpan.FromTicks(timeout.Ticks)); if (rcvData == null) return StatusCode.Timeout; diff --git a/base/Services/NetStack/Runtime/HostConfiguration.cs b/base/Services/NetStack/Runtime/HostConfiguration.cs index 0670bf1..f45fedc 100644 --- a/base/Services/NetStack/Runtime/HostConfiguration.cs +++ b/base/Services/NetStack/Runtime/HostConfiguration.cs @@ -1,40 +1,37 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- -/** - * Microsoft Research, Cambridge - * author: Yaron Weinsberg, Richard Black - */ +/// +// Microsoft Research, Cambridge +// -using NetStack.Common; using System; using System.Collections; - using System.Net.IP; -using Drivers.Net; -using NetStack.NetDrivers; + +using Microsoft.Contracts; +using Microsoft.Singularity; + +using NetStack.Common; using NetStack.Protocols; +using NetStack.NetDrivers; #if !SINGULARITY using System.Reflection; using System.Net; #endif -using Microsoft.Contracts; - namespace NetStack.Runtime { // define the InterfaceList public class InterfaceList : ArrayList {} - /** - * The local host IP configuration - */ + /// + // The local host IP configuration + // public class HostConfiguration { private string hostname; @@ -48,20 +45,30 @@ namespace NetStack.Runtime [NotDelayed] public HostConfiguration() { + RoutingTable! routingTableOnStack = new RoutingTable(); + isRouter = false; - - RoutingTable rt = new RoutingTable(); - routingTable = rt; - bindings = new IPAdapterBindingTable(rt); + routingTable = routingTableOnStack; nameServers = ArrayList.Synchronized(new ArrayList()); - - dhcpClients = new ArrayList(); + bindings = new IPAdapterBindingTable(routingTableOnStack); + dhcpClients = new ArrayList(); base(); SetHostName("singularity"); SetDomainName("microsoft.com"); +#if false + IAdapter loopbackAdapter = new LoopbackAdapter(); + Core.Instance().RegisterAdapter(loopbackAdapter, "loopback", LoopbackAdapter.RingSize); + + this.bindings.Add(loopbackAdapter, + new InterfaceIPConfiguration( + IPv4.Loopback, + new IPv4(0xff000000), + IPv4.Loopback, + 1)); +#endif routingTable.AddRoute( new RouteEntry(IPv4Network.Loopback, IPv4.Loopback, @@ -69,7 +76,6 @@ namespace NetStack.Runtime RouteEntry.InterfaceMetric, RouteEntry.InterfaceRouteTag) ); - } public string! GetHostName() @@ -80,8 +86,7 @@ namespace NetStack.Runtime public bool SetHostName(string! name) { int dot = name.IndexOf('.'); - if (dot >= 0) - { + if (dot >= 0) { string hostpart = name.Substring(0, dot); string domainpart = name.Substring(dot + 1); if ((ValidDnsName(hostpart, false) == false) || @@ -105,8 +110,7 @@ namespace NetStack.Runtime public bool SetDomainName(string! name) { - if (ValidDnsName(name, true) == false) - { + if (ValidDnsName(name, true) == false) { return false; } domainname = name; @@ -123,8 +127,7 @@ namespace NetStack.Runtime if (name[0] == '-' || name[last] == '-') return false; - for (int i = 0; i <= last; i++) - { + for (int i = 0; i <= last; i++) { // Letters, digits, and dashes are okay if (Char.IsLetterOrDigit(name[i]) == true || name[i] == '-') continue; @@ -158,8 +161,7 @@ namespace NetStack.Runtime public bool DeleteNameServer(IPv4 address) { - lock (nameServers.SyncRoot) - { + lock (nameServers.SyncRoot) { int index = this.nameServers.IndexOf(address); if (index < 0) return false; @@ -182,10 +184,8 @@ namespace NetStack.Runtime /// public IPv4 GetCurrentNameServer() { - lock (nameServers.SyncRoot) - { - if (nameServers.Count > 0) - { + lock (nameServers.SyncRoot) { + if (nameServers.Count > 0) { return (IPv4) (!) nameServers[0]; } return IPv4.Zero; @@ -197,8 +197,7 @@ namespace NetStack.Runtime /// public void RotateNameServers() { - lock (nameServers.SyncRoot) - { + lock (nameServers.SyncRoot) { if (nameServers.Count < 2) return; IPv4 retiree = (IPv4) (!) nameServers[0]; @@ -214,10 +213,8 @@ namespace NetStack.Runtime public bool StartDhcp(IAdapter adapter) { - lock (dhcpClients) - { - foreach (DhcpClient! client in dhcpClients) - { + lock (dhcpClients) { + foreach (DhcpClient! client in dhcpClients) { if (client.Adapter == adapter) return false; } @@ -230,13 +227,10 @@ namespace NetStack.Runtime public bool StopDhcp(IAdapter adapter) { - lock (dhcpClients) - { - for (int i = 0; i < dhcpClients.Count; i++) - { + lock (dhcpClients) { + for (int i = 0; i < dhcpClients.Count; i++) { DhcpClient client = (DhcpClient!) dhcpClients[i]; - if (client.Adapter == adapter) - { + if (client.Adapter == adapter) { client.Stop(); dhcpClients.RemoveAt(i); return true; @@ -248,10 +242,8 @@ namespace NetStack.Runtime public bool IsRunningDhcp(IAdapter adapter) { - lock (dhcpClients) - { - foreach (DhcpClient! client in dhcpClients) - { + lock (dhcpClients) { + foreach (DhcpClient! client in dhcpClients) { if (client.Adapter == adapter) return true; } @@ -373,8 +365,7 @@ namespace NetStack.Runtime // // Add default route if none exists // - if (routingTable.Lookup(IPv4Network.Default) == null) - { + if (routingTable.Lookup(IPv4Network.Default) == null) { routingTable.AddRoute( new RouteEntry(IPv4Network.Default, ipConfig.Gateway, @@ -392,8 +383,7 @@ namespace NetStack.Runtime public bool Add(IAdapter! adapter, InterfaceIPConfiguration! ipConfig) { - if (ipBindings[ipConfig.Address] != null) - { + if (ipBindings[ipConfig.Address] != null) { return false; } ipBindings[ipConfig.Address] = new IPBinding(adapter, ipConfig); @@ -407,16 +397,13 @@ namespace NetStack.Runtime public void Flush(IAdapter adapter) { Stack flushItems = new Stack(); - foreach (IPBinding! ipb in ipBindings.Values) - { - if (ipb.Adapter == adapter) - { + foreach (IPBinding! ipb in ipBindings.Values) { + if (ipb.Adapter == adapter) { flushItems.Push(ipb.IPConfiguration); } } - while (flushItems.Count != 0) - { + while (flushItems.Count != 0) { Flush((InterfaceIPConfiguration!) flushItems.Pop()); } } @@ -427,8 +414,7 @@ namespace NetStack.Runtime internal void Flush(IAdapter adapter, IPv4 address) { IPBinding ipb = (IPBinding) ipBindings[address]; - if (ipb != null && ipb.Adapter == adapter) - { + if (ipb != null && ipb.Adapter == adapter) { Flush(ipb.IPConfiguration); } } @@ -442,8 +428,7 @@ namespace NetStack.Runtime public IAdapter GetAdapter(IPv4 address) { IPBinding ipb = (IPBinding) ipBindings[address]; - if (ipb == null) - { + if (ipb == null) { return null; } return ipb.Adapter; @@ -458,8 +443,7 @@ namespace NetStack.Runtime public ArrayList! GetAdapterIPConfigurations(IAdapter adapter) { ArrayList results = new ArrayList(); - foreach (IPBinding! ipb in ipBindings.Values) - { + foreach (IPBinding! ipb in ipBindings.Values) { if (ipb.Adapter == adapter) results.Add(ipb.IPConfiguration); } diff --git a/base/Services/NetStack/Runtime/ICMPModule.cs b/base/Services/NetStack/Runtime/ICMPModule.cs index 65a84bc..c15addb 100644 --- a/base/Services/NetStack/Runtime/ICMPModule.cs +++ b/base/Services/NetStack/Runtime/ICMPModule.cs @@ -1,15 +1,12 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- -/** - * Microsoft Research, Cambridge - * author: Yaron Weinsberg, Richard Black - */ +/// +// Microsoft Research, Cambridge +// using NetStack.Common; using System; @@ -24,9 +21,9 @@ using Microsoft.Singularity; namespace NetStack.Runtime { - /** - * This module implements the ICMP protocol - */ + /// + // This module implements the ICMP protocol + // public class IcmpModule : IProtocol { // the ip handler @@ -107,13 +104,12 @@ namespace NetStack.Runtime // read the icmp header IcmpFormat.IcmpHeader icmpHeader; - if (!IcmpFormat.ReadIcmpHeader(pkt, out icmpHeader)) - { + if (!IcmpFormat.ReadIcmpHeader(pkt, out icmpHeader)) { return NetStatus.Code.PROTOCOL_OK; } // handle various types - switch(icmpHeader.type) { + switch (icmpHeader.type) { case (byte)IcmpFormat.IcmpType.ECHO_REQUEST: if (icmpHeader.code == 0) { DebugPrint("IcmpModule: Handling ECHO_REQUEST From: {0}", ipHeader.Source); diff --git a/base/Services/NetStack/Runtime/ICMPSession.cs b/base/Services/NetStack/Runtime/ICMPSession.cs index 9e42267..f8815ca 100644 --- a/base/Services/NetStack/Runtime/ICMPSession.cs +++ b/base/Services/NetStack/Runtime/ICMPSession.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using NetStack.Common; using System; @@ -44,8 +42,7 @@ namespace NetStack.Runtime { NetStatus res = base.OnReceive(sender, pkt, ctx); // if the packet can be delivered to the user - if (res != NetStatus.Code.PROTOCOL_PROCESSING) - { + if (res != NetStatus.Code.PROTOCOL_PROCESSING) { // get the packet and put it on the incoming queue // if there is no room, drop it. this method is called // by the runtime when a packet destination is this session. @@ -57,8 +54,7 @@ namespace NetStack.Runtime // the user should create an IP+ICMP buffer override public int WriteData(byte[]! data) { - if (!IsSessionValidForUserWrite()) - { + if (!IsSessionValidForUserWrite()) { return -1; } @@ -97,8 +93,7 @@ namespace NetStack.Runtime #if DEBUG Console.Out.WriteLine("QosIcmpSession:OnReceived, flow capacity={0}",capacity); #endif - if (!this.PutPacket(inQueue,inQueueMonitor,pkt,false)) - { + if (!this.PutPacket(inQueue,inQueueMonitor,pkt,false)) { pkt.ReleaseRef(); // increment the session capacity capacity++; @@ -112,7 +107,7 @@ namespace NetStack.Runtime { NetPacket pkt = GetPacketFromQ(inQueue,inQueueMonitor,true,Timeout.Infinite,true); byte[] toUser=null; - if (pkt!=null) + if (pkt != null) toUser = pkt.ToUser(); // now the packet can be returned! @@ -130,8 +125,7 @@ namespace NetStack.Runtime { NetPacket pkt = GetPacketFromQ(inQueue,inQueueMonitor,true,timeout,true); byte[] toUser=null; - if (pkt!=null) - { + if (pkt != null) { toUser = pkt.ToUser(); // now the packet can be returned! pkt.ReleaseRef(); @@ -145,8 +139,7 @@ namespace NetStack.Runtime // the user should create an IP+ICMP buffer override public int WriteData(byte[] data) { - if (!IsSessionValidForUserWrite()) - { + if (!IsSessionValidForUserWrite()) { return -1; } @@ -190,11 +183,9 @@ namespace NetStack.Runtime bool handled=false; // handle various types - switch(icmpHeader.type) - { + switch (icmpHeader.type) { case (byte)IcmpFormat.IcmpType.ECHO_REQUEST: - if (icmpHeader.code==0) - { + if (icmpHeader.code == 0) { #if DEBUG_IP Console.WriteLine("PrivateIcmpSession: Handling ECHO_REQUEST From: {0}", ipHeader.srcAddrIP); #endif diff --git a/base/Services/NetStack/Runtime/INetModule.cs b/base/Services/NetStack/Runtime/INetModule.cs index 207b5fe..e5bf118 100644 --- a/base/Services/NetStack/Runtime/INetModule.cs +++ b/base/Services/NetStack/Runtime/INetModule.cs @@ -1,15 +1,12 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- -/** - * Microsoft Research, Cambridge - * author: Yaron Weinsberg, Richard Black - */ +/// +// Microsoft Research, Cambridge +// using System; @@ -17,11 +14,11 @@ namespace NetStack { namespace Runtime { - /** - * All the networking stack modules must implement - * this interface. The runtime uses this interface - * to manage the different modules. - */ + /// + // All the networking stack modules must implement + // this interface. The runtime uses this interface + // to manage the different modules. + // public interface INetModule { // get the module's name diff --git a/base/Services/NetStack/Runtime/IPModule.cs b/base/Services/NetStack/Runtime/IPModule.cs index 069f10c..e26f332 100644 --- a/base/Services/NetStack/Runtime/IPModule.cs +++ b/base/Services/NetStack/Runtime/IPModule.cs @@ -1,15 +1,12 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- -/** - * Microsoft Research, Cambridge - * author: Yaron Weinsberg, Richard Black - */ +/// +// Microsoft Research, Cambridge +// // #define DEBUG_IP @@ -27,10 +24,10 @@ using Microsoft.Singularity; namespace NetStack.Runtime { - /** - * This module implements the IP protocol - * NOTICE: FOR NOW WE DO NOT HANDLE FRAGMENTATION!!!!!!!!!!!!!! - */ + /// + // This module implements the IP protocol + // NOTICE: FOR NOW WE DO NOT HANDLE FRAGMENTATION!!!!!!!!!!!!!! + // public class IPModule : IProtocol { // the version @@ -66,15 +63,13 @@ namespace NetStack.Runtime { // get a ref to the icmp protocol icmp = Core.Instance().GetProtocolByName("ICMP"); - if (icmp == null) - { + if (icmp == null) { return false; } // get a ref to the arp protocol arp = Core.Instance().GetProtocolByName("ARP") as ArpModule; - if (arp == null) - { + if (arp == null) { return false; } @@ -105,8 +100,7 @@ namespace NetStack.Runtime "fragment", false); // save the routing protocol name, if exists - if (version != 4 || fragment == true) - { + if (version != 4 || fragment == true) { DebugPrint("Support only exists for V4 w/o fragments.\n"); return false; } @@ -114,8 +108,7 @@ namespace NetStack.Runtime Core core = Core.Instance(); core.RegisterProtocol(this); - if (! core.packetTypes.RegisterTypeHandler(PacketTypes.IP, this)) - { + if (!core.packetTypes.RegisterTypeHandler(PacketTypes.IP, this)) { core.DeregisterProtocol(this); return false; } @@ -136,8 +129,7 @@ namespace NetStack.Runtime public NetStatus OnProtocolReceive(NetPacket! pkt) { IPFormat.IPHeader! ipHeader; - if (IPFormat.ReadIPHeader(pkt, out ipHeader) == false) - { + if (IPFormat.ReadIPHeader(pkt, out ipHeader) == false) { DebugPrint("bad header\n"); return NetStatus.Code.PROTOCOL_OK; } @@ -160,8 +152,7 @@ namespace NetStack.Runtime if (hostConfig.IsLocalAddress(ipHeader.Destination) == false && ipHeader.Destination != IPv4.Broadcast) { - if (hostConfig.IsRouter) - { + if (hostConfig.IsRouter) { // Not supported for the time being. } // DebugPrint("Dropping wrong destination\n"); @@ -280,14 +271,12 @@ namespace NetStack.Runtime // TBC: make the ARPModule setup the packet { Session session = packet.SessionContext as Session; - if (session != null) - { + if (session != null) { DebugPrint("Sending packet for {0}/{1} ({2}-->{3})\n", session.RemoteAddress, session.RemotePort, localMac, remoteMac); } - else - { + else { DebugPrint("Sending packet to {0}\n", remoteMac); } } @@ -343,32 +332,32 @@ namespace NetStack.Runtime return NetStatus.Code.PROTOCOL_OK; } - /* - protected NetStatus ForwardPacket(IPFormat.IPHeader! rcvIPHeader, - NetPacket! packet, - Multiplexer targetMux) - { - Debug.Assert(packet != null && packet.IsOneChunk); - // TBC: this is just a preliminary imp, should - // consider MTUs etc. - byte[] data = packet.ToContiguous(); - int start = EthernetFormat.Size; - - // decrease the ttl, if it reaches 0 discard and send - // ICMP message using on the source mux, otherwise forward! - if (--rcvIPHeader.ttl == 0) { - // drop it! and use icmp protocol to return error - // TBC: icmp.OnProtocolSend(..) // will use packetArg.Mux = source mux - return NetStatus.Code.RT_DROP_TTL_EXPIRED; - } - // at last, forward it already! - // modify the header + calc new checksum - IPFormat.WriteIPHeader(data, start, rcvIPHeader); - DebugPrint("Forwarding IP packet.\n"); - - return OnProtocolSend(packet); - } - */ + // + //protected NetStatus ForwardPacket(IPFormat.IPHeader! rcvIPHeader, + // NetPacket! packet, + // Multiplexer targetMux) + //{ + // Debug.Assert(packet != null && packet.IsOneChunk); + // // TBC: this is just a preliminary imp, should + // // consider MTUs etc. + // byte[] data = packet.ToContiguous(); + // int start = EthernetFormat.Size; +// + // // decrease the ttl, if it reaches 0 discard and send + // // ICMP message using on the source mux, otherwise forward! + // if (--rcvIPHeader.ttl == 0) { + // // drop it! and use icmp protocol to return error + // // TBC: icmp.OnProtocolSend(..) // will use packetArg.Mux = source mux + // return NetStatus.Code.RT_DROP_TTL_EXPIRED; + // } + // // at last, forward it already! + // // modify the header + calc new checksum + // IPFormat.WriteIPHeader(data, start, rcvIPHeader); + // DebugPrint("Forwarding IP packet.\n"); +// + // return OnProtocolSend(packet); + //} + // public HostConfiguration! HostConfiguration { diff --git a/base/Services/NetStack/Runtime/IProtocol.cs b/base/Services/NetStack/Runtime/IProtocol.cs index 3aab9b3..c677802 100644 --- a/base/Services/NetStack/Runtime/IProtocol.cs +++ b/base/Services/NetStack/Runtime/IProtocol.cs @@ -1,15 +1,12 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- -/** - * Microsoft Research, Cambridge - * author: Yaron Weinsberg, Richard Black - */ +/// +// Microsoft Research, Cambridge +// using NetStack.Common; using System; @@ -24,24 +21,24 @@ using Drivers.Net; namespace NetStack.Runtime { - /** - * This interface represents a networking protocol. - * It should be implemented by protocol implementations. - * Notice: Dynamic loadable protocols should implement a - * default constructor. - */ + /// + // This interface represents a networking protocol. + // It should be implemented by protocol implementations. + // Notice: Dynamic loadable protocols should implement a + // default constructor. + // public interface IProtocol : INetModule { - /** - * A protocol's handler for PDU reception, - * @param pkt - the arrived packet argument, the internal packet is - * stripped from all lower layer headers. - * - * @return NetStatus - the status of this invocation - * - * NOTICE: this protocol must set the internal: NetPacket.ActivePos for - * next level protocol. - */ + /// + // A protocol's handler for PDU reception, + // @param pkt - the arrived packet argument, the internal packet is + // stripped from all lower layer headers. + // + // @return NetStatus - the status of this invocation + // + // NOTICE: this protocol must set the internal: NetPacket.ActivePos for + // next level protocol. + // NetStatus OnProtocolReceive(NetPacket! pkt); // A specific Protocol's send routine diff --git a/base/Services/NetStack/Runtime/NetStackThreadPool.sg b/base/Services/NetStack/Runtime/NetStackThreadPool.sg index 917b84a..7eae887 100644 --- a/base/Services/NetStack/Runtime/NetStackThreadPool.sg +++ b/base/Services/NetStack/Runtime/NetStackThreadPool.sg @@ -107,6 +107,7 @@ namespace NetStack.Runtime } shutdownEvent.WaitOne(); + shutdownEvent.Reset(); Microsoft.Singularity.DebugStub.Assert(commands.Count == 0); } diff --git a/base/Services/NetStack/Runtime/NetStatus.cs b/base/Services/NetStack/Runtime/NetStatus.cs index 576e8b7..1652ba4 100644 --- a/base/Services/NetStack/Runtime/NetStatus.cs +++ b/base/Services/NetStack/Runtime/NetStatus.cs @@ -1,15 +1,6 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -/////////////////////////////////////////////////////////////////////////////// - -/** - * Microsoft Research, Cambridge - * author: Yaron Weinsberg, Richard Black - */ +/// +// Microsoft Research, Cambridge +// namespace NetStack { diff --git a/base/Services/NetStack/Runtime/PacketTypes.cs b/base/Services/NetStack/Runtime/PacketTypes.cs index b4625d5..5304560 100644 --- a/base/Services/NetStack/Runtime/PacketTypes.cs +++ b/base/Services/NetStack/Runtime/PacketTypes.cs @@ -1,29 +1,26 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- -/** - * Microsoft Research, Cambridge - * author: Yaron Weinsberg, Richard Black - */ +/// +// Microsoft Research, Cambridge +// using System; using System.Collections; namespace NetStack.Runtime { - /** - * Each protocol (upon initialization) should - * register itself as a specific packet type handler. - * The packet type must be one of: ProtocolCode - * types (a TCP has nothing to do with this list...) - * Notice: the ALL code is used for receiving all - * incoming traffic. - */ + /// + // Each protocol (upon initialization) should + // register itself as a specific packet type handler. + // The packet type must be one of: ProtocolCode + // types (a TCP has nothing to do with this list...) + // Notice: the ALL code is used for receiving all + // incoming traffic. + // public class PacketTypes { public const ushort IP = 0x0800; @@ -44,14 +41,12 @@ namespace NetStack.Runtime // return true if succeeded. public bool RegisterTypeHandler(ushort type, IProtocol protocol) { - if (registered == MaxTypes) - { + if (registered == MaxTypes) { Core.Log("Out of type handler space."); return false; } - if (GetHandler(type) != null) - { + if (GetHandler(type) != null) { Core.Log("Handler already registered."); return false; } @@ -64,10 +59,8 @@ namespace NetStack.Runtime public IProtocol GetHandler(ushort type) { - for (int i = 0; i < registered; i++) - { - if (key[i] == type) - { + for (int i = 0; i < registered; i++) { + if (key[i] == type) { return handler[i]; } } diff --git a/base/Services/NetStack/Runtime/ProtocolParams.cs b/base/Services/NetStack/Runtime/ProtocolParams.cs index 4dc964f..8dfbba3 100644 --- a/base/Services/NetStack/Runtime/ProtocolParams.cs +++ b/base/Services/NetStack/Runtime/ProtocolParams.cs @@ -1,15 +1,12 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- -/** - * Microsoft Research, Cambridge - * author: Yaron Weinsberg, Richard Black - */ +/// +// Microsoft Research, Cambridge +// using System; using System.Collections.Specialized; @@ -38,27 +35,22 @@ namespace NetStack.Runtime string parameterName, int defaultValue) { - if (parameters == null) - { + if (parameters == null) { return defaultValue; } - else if (parameterName == null) - { + else if (parameterName == null) { throw new ArgumentNullException(); } string sValue = parameters[parameterName]; - if (sValue == null) - { + if (sValue == null) { return defaultValue; } - try - { + try { return Int32.Parse(sValue); } - catch - { + catch { Core.Log("Failed on parameter \"{0}\" value \"{1}\"\n", parameterName, sValue); return defaultValue; @@ -77,27 +69,22 @@ namespace NetStack.Runtime string parameterName, uint defaultValue) { - if (parameters == null) - { + if (parameters == null) { return defaultValue; } - else if (parameterName == null) - { + else if (parameterName == null) { throw new ArgumentNullException(); } string sValue = parameters[parameterName]; - if (sValue == null) - { + if (sValue == null) { return defaultValue; } - try - { + try { return UInt32.Parse(sValue); } - catch - { + catch { Core.Log("Failed on parameter \"{0}\" value \"{1}\"\n", parameterName, sValue); return defaultValue; @@ -115,27 +102,22 @@ namespace NetStack.Runtime string parameterName, IPv4 defaultValue) { - if (parameters == null) - { + if (parameters == null) { return defaultValue; } - else if (parameterName == null) - { + else if (parameterName == null) { throw new ArgumentNullException(); } string sValue = parameters[parameterName]; - if (sValue == null) - { + if (sValue == null) { return defaultValue; } - try - { + try { return IPv4.Parse(sValue); } - catch (FormatException) - { + catch (FormatException) { Core.Log("Failed on parameter \"{0}\" value \"{1}\"\n", parameterName, sValue); } @@ -153,18 +135,15 @@ namespace NetStack.Runtime string parameterName, string defaultValue) { - if (parameters == null) - { + if (parameters == null) { return defaultValue; } - else if (parameterName == null) - { + else if (parameterName == null) { throw new ArgumentNullException(); } string sValue = parameters[parameterName]; - if (sValue == null) - { + if (sValue == null) { return defaultValue; } return sValue; @@ -181,27 +160,22 @@ namespace NetStack.Runtime string parameterName, bool defaultValue) { - if (parameters == null) - { + if (parameters == null) { return defaultValue; } - else if (parameterName == null) - { + else if (parameterName == null) { throw new ArgumentNullException(); } string sValue = parameters[parameterName]; - if (sValue == null) - { + if (sValue == null) { return defaultValue; } - try - { + try { return Boolean.Parse(sValue); } - catch (FormatException) - { + catch (FormatException) { Core.Log("Failed on parameter \"{0}\" value \"{1}\"\n", parameterName, sValue); return defaultValue; diff --git a/base/Services/NetStack/Runtime/Reservations.cs b/base/Services/NetStack/Runtime/Reservations.cs new file mode 100644 index 0000000..0040323 --- /dev/null +++ b/base/Services/NetStack/Runtime/Reservations.cs @@ -0,0 +1,203 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +/// +// Microsoft Corporation, Redmond, WA. +// author: Glenn Peterson +// + +// #define DEBUG_RESERVATIONS + +using System; +using System.Collections; +//using System.Diagnostics; +//using System.Net.IP; + +//using Microsoft.Singularity; + +//using Drivers.Net; + +//using NetStack.Common; +//using NetStack.NetDrivers; +//using NetStack.Protocols; + +namespace NetStack.Runtime +{ + /// + /// An enumeration describing a Reservation's current state: Under-, Over-, + /// and Limit- Utilized. It's used to control service to the Reservation's + /// sessions. + /// + public enum ReservationCategory + { + /// + /// This Reservation's Utilization is less than its "Reservation" level. + /// + /// + /// This reservation's sessions are serviced at high priority. + /// + UnderUtilized = 0, + + /// + /// This Reservation's Utilization is more than its "Reservation" level + /// and less that its "Maximum" level. + /// + /// + /// This reservation's sessions are serviced at low priority. + /// + OverUtilized = 1, + + /// + /// This Reservation's Utilization is more than its "Maximum" level. + /// + /// + /// This reservation's sessions are not serviced. + /// + LimitUtilized = 2, + } + + /// + /// A collection of Reservations of the same Category (Under/Over/Limit). + /// + internal class ReservationsByCategory + { + private ReservationCategory reservationCategory; + private readonly ArrayList reservations = new ArrayList(); + + public ReservationsByCategory(ReservationCategory reservationCategory) + { + this.reservationCategory = reservationCategory; + } + + public ReservationCategory ReservationCategory + { + get { return this.reservationCategory; } + } + + public void AddReservation(Reservation reservation) + { + this.reservations.Add(reservation); + } + + public void RemoveReservation(Reservation reservation) + { + this.reservations.Remove(reservation); + } + } + + /// + /// A Network Reservation, containing of a collection of Network Sessions. + /// + /// + /// In addition to the collection of sessions, a Reservation contains + /// housekeeping information: currentUtilization, the time the + /// currentUtilization was computed, Reservation category, and the + /// utilization limits: Reserved and Maximum. It also has the time + /// (measured form the time the current utilization was computed) that + /// it will become elegible to transfer to a more active category - + /// assuming it remains idle if in the "OverUtilized" category. + /// + internal class Reservation + { + public const double AveragingSeconds = 1.0; + +// public static TimeSpan AveragingTimeSpan = +// TimeSpan.FromSeconds(Reservation.AveragingSeconds); + + // Sessions sharing this Reservation + private readonly ArrayList sessions; + + // Utilization Limits for this Reservation. + private double reservationUtilization; + private double maximumUtilization; + + // Utilization and when Computed + private double currentUtilization; + private DateTime currentUtilizationEvaluationTime; + + private ReservationsByCategory reservationsByCategory; + + public Reservation(double reservationUtilization, + double maximumUtilization) + { + // Initialize Readonly Fields + this.sessions = new ArrayList(); + + // Initialize Dynamic Fields + this.reservationUtilization = reservationUtilization; + this.maximumUtilization = maximumUtilization; + this.currentUtilization = reservationUtilization * 0.5; // TODO: Revisit GRP + this.currentUtilizationEvaluationTime = DateTime.Now; + } + + public void AddSession(Session session) + { + this.sessions.Add(session); + } + + public void RemoveSession(Session session) + { + this.sessions.Remove(session); + } + + public void ChangeReservationLimits(double reservationUtilization, + double maximumUtilization) + { + this.reservationUtilization = reservationUtilization; + this.maximumUtilization = maximumUtilization; + + this.CheckCategory(); + } + + public void MergeUtilizationsWithDamping(double utilization, + TimeSpan duration) + { + // Compute the weighted (merged) utilization. + double durationSeconds = duration.TotalSeconds; + double mergedUtilization = this.currentUtilization * Reservation.AveragingSeconds + + utilization * durationSeconds; + + // Rescale the merged Utilization and its Evaluation Time. + this.currentUtilization = + mergedUtilization * Reservation.AveragingSeconds / + (durationSeconds + Reservation.AveragingSeconds); + this.currentUtilizationEvaluationTime += duration; + + // See if we're in a new Category (Under/Over/Limit). + this.CheckCategory(); + } + + private void CheckCategory() + { + ReservationCategory newCategory = ReservationCategory.UnderUtilized; + + if (this.currentUtilization > this.reservationUtilization) + { + newCategory = (this.currentUtilization > this.maximumUtilization) + ? ReservationCategory.OverUtilized : ReservationCategory.LimitUtilized; + } + + if (newCategory != this.reservationsByCategory.ReservationCategory) + { + ChangeCategory(newCategory); + } + } + + private void ChangeCategory(ReservationCategory newCategory) + { + ReservationsByCategory newReservationsByCategory = new ReservationsByCategory(ReservationCategory.OverUtilized); // BUGBUG: TODO: Look up in Array (GRP) + + this.reservationsByCategory.RemoveReservation(this); + newReservationsByCategory.AddReservation(this); + this.reservationsByCategory = newReservationsByCategory; + + if (newCategory == ReservationCategory.OverUtilized) + { + // Stur pot. (Check appropriate queue - we may be able to start transmitting / receiving. // TODO GRP. + } + } + } +} diff --git a/base/Services/NetStack/Runtime/RoutingTable.cs b/base/Services/NetStack/Runtime/RoutingTable.cs index 072f915..385c9ec 100644 --- a/base/Services/NetStack/Runtime/RoutingTable.cs +++ b/base/Services/NetStack/Runtime/RoutingTable.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; using System.Collections; @@ -75,16 +73,14 @@ namespace NetStack.Runtime public override string! ToString() { - if (network.MaskLength == IPv4.BitCount) - { + if (network.MaskLength == IPv4.BitCount) { return String.Format("Host {0,-17} Gateway {1,-14} " + "Interface {2,-14} Metric {3,-3} " + "Tag {4:x8}", network.LowerBound, gateway, ifaddr, metric, tag); } - else - { + else { return String.Format("Network {0,-17} Gateway {1,-14} " + "Interface {2,-14} Metric {3,-3} " + "Tag {4:x8}", @@ -101,24 +97,20 @@ namespace NetStack.Runtime // // Metric - Cheapest route wins // - if (this.metric < other.metric) - { + if (this.metric < other.metric) { return -1; } - else if (this.metric > other.metric) - { + else if (this.metric > other.metric) { return +1; } // // Specificity - Most specific wins // - if (network.IsLessSpecificThan(other.network)) - { + if (network.IsLessSpecificThan(other.network)) { return +1; } - else if (network.IsMoreSpecificThan(other.network)) - { + else if (network.IsMoreSpecificThan(other.network)) { return -1; } @@ -128,12 +120,10 @@ namespace NetStack.Runtime IPv4 ourAddress = network.LowerBound; IPv4 otherAddress = other.network.LowerBound; - if (ourAddress < otherAddress) - { + if (ourAddress < otherAddress) { return -1; } - if (ourAddress == otherAddress) - { + if (ourAddress == otherAddress) { return 0; } return +1; @@ -170,21 +160,18 @@ namespace NetStack.Runtime public bool AddRoute(RouteEntry! e) { - try - { + try { routes.Add(e, e); return true; } - catch (ArgumentException) - { + catch (ArgumentException) { return false; } } public bool DeleteRoute(RouteEntry e) { - lock (routes.SyncRoot) - { + lock (routes.SyncRoot) { int oldCount = routes.Count; routes.Remove(e); return oldCount > routes.Count; @@ -196,10 +183,8 @@ namespace NetStack.Runtime /// public RouteEntry Lookup(IPv4 destination) { - foreach (RouteEntry! e in routes.Values) - { - if (e.Network.Contains(destination)) - { + foreach (RouteEntry! e in routes.Values) { + if (e.Network.Contains(destination)) { return e; } } @@ -211,10 +196,8 @@ namespace NetStack.Runtime /// public RouteEntry Lookup(IPv4Network destination) { - foreach (RouteEntry! e in routes.Values) - { - if (e.Network.Contains(destination)) - { + foreach (RouteEntry! e in routes.Values) { + if (e.Network.Contains(destination)) { return e; } } @@ -246,10 +229,8 @@ namespace NetStack.Runtime /// null otherwise. public RouteEntry LookupSpecific(IPv4Network destination) { - foreach (RouteEntry! e in routes.Values) - { - if (e.Network == destination) - { + foreach (RouteEntry! e in routes.Values) { + if (e.Network == destination) { return e; } } @@ -263,19 +244,15 @@ namespace NetStack.Runtime { ArrayList deadRoutes = new ArrayList(); - lock (routes) - { - foreach (RouteEntry! e in routes.Values) - { - if (e.InterfaceAddress == interfaceAddress) - { + lock (routes) { + foreach (RouteEntry! e in routes.Values) { + if (e.InterfaceAddress == interfaceAddress) { deadRoutes.Add(e); } } } - foreach (RouteEntry e in deadRoutes) - { + foreach (RouteEntry e in deadRoutes) { deadRoutes.Remove(e); } } diff --git a/base/Services/NetStack/Runtime/Runtime.csproj.user b/base/Services/NetStack/Runtime/Runtime.csproj.user deleted file mode 100644 index 0e89b7e..0000000 --- a/base/Services/NetStack/Runtime/Runtime.csproj.user +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - diff --git a/base/Services/NetStack/Runtime/Runtime.csproj.vspscc b/base/Services/NetStack/Runtime/Runtime.csproj.vspscc deleted file mode 100644 index a0b5fba..0000000 --- a/base/Services/NetStack/Runtime/Runtime.csproj.vspscc +++ /dev/null @@ -1,10 +0,0 @@ -"" -{ -"FILE_VERSION" = "9237" -"ENLISTMENT_CHOICE" = "NEVER" -"PROJECT_FILE_RELATIVE_PATH" = "relative:Runtime" -"NUMBER_OF_EXCLUDED_FILES" = "0" -"ORIGINAL_PROJECT_FILE_PATH" = "" -"NUMBER_OF_NESTED_PROJECTS" = "0" -"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" -} diff --git a/base/Services/NetStack/Runtime/Session.cs b/base/Services/NetStack/Runtime/Session.cs index 4a68339..cbe3555 100644 --- a/base/Services/NetStack/Runtime/Session.cs +++ b/base/Services/NetStack/Runtime/Session.cs @@ -1,15 +1,12 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- -/** - * Microsoft Research, Cambridge - * author: Yaron Weinsberg, Richard Black - */ +/// +// Microsoft Research, Cambridge +// using NetStack.Common; using System; @@ -29,12 +26,12 @@ using Drivers.Net; namespace NetStack.Runtime { - /** - * This is the base class for all sessions. - * The session represent an instance of a specific - * protocol and is usually embedded in a higher level - * library abstraction. - */ + /// + // This is the base class for all sessions. + // The session represent an instance of a specific + // protocol and is usually embedded in a higher level + // library abstraction. + // public class Session : IDisposable { private static uint uids = 0; @@ -93,8 +90,7 @@ namespace NetStack.Runtime public void Dispose() { - if (!disposed) - { + if (!disposed) { Core.Instance().DeregisterSession(protocol, this); DrainQueue(inQueue); DrainQueue(outQueue); @@ -164,8 +160,7 @@ namespace NetStack.Runtime { IPModule ipModule = Core.Instance().GetProtocolByName("IP") as IPModule; - if (ipModule == null) - { + if (ipModule == null) { return false; } return ipModule.HostConfiguration.IsLocalAddress(address); @@ -173,8 +168,7 @@ namespace NetStack.Runtime internal bool SetLocalEndPoint(IPv4 address, ushort port) { - if (address != IPv4.Zero && !IsLocalAddress(address)) - { + if (address != IPv4.Zero && !IsLocalAddress(address)) { // Invalid address return false; } @@ -185,13 +179,11 @@ namespace NetStack.Runtime IPModule ipModule = (IPModule!)Core.Instance().GetProtocolByName("IP"); RouteEntry e = ipModule.HostConfiguration.RoutingTable.Lookup(remoteAddress); - if (e != null) - { + if (e != null) { address = e.InterfaceAddress; DebugPrint("local address now {0}\n", address); } - else - { + else { DebugPrint("No route for {0}\n", remoteAddress); } } @@ -237,35 +229,20 @@ namespace NetStack.Runtime // we let the user read packets if exist event though the session // is closing (for instance a web server sends the data and disconnect) protected NetPacket DequeuePacket(ArrayList! queue, - int timeoutMillis) + TimeSpan timeout) { Debug.Assert(queue == inQueue || outQueuePaused == false); NetPacket packet = null; - TimeSpan timeout; - // Negative millis means forever. - if (timeoutMillis < 0) - { - timeout = TimeSpan.Infinite; - } - else - { - timeout = TimeSpan.FromMilliseconds(timeoutMillis); - } - - lock (queue.SyncRoot) - { - if ((timeoutMillis == 0) && queue.Count == 0) - { + lock (queue.SyncRoot) { + if ((timeout == TimeSpan.Zero) && queue.Count == 0) { // Don't sleep return null; } - while (queue.Count == 0) - { - if (Monitor.Wait(queue.SyncRoot, timeout) == false) - { + while (queue.Count == 0) { + if (Monitor.Wait(queue.SyncRoot, timeout) == false) { return null; } } @@ -296,17 +273,16 @@ namespace NetStack.Runtime NetPacket! packet, bool toBlock) { - lock (queue.SyncRoot) - { + lock (queue.SyncRoot) { if (queue.Count >= queue.Capacity && !toBlock) { return false; } int queueCapacity = GetQueueCapacity(queue); while (queue.Count >= queueCapacity) - /* Potential fix to stop accepting users packets until - destination is ARPed */ - /*|| (queue == this.outQueue && this.outQueuePaused) */ + // Potential fix to stop accepting users packets until + // destination is ARPed + //|| (queue == this.outQueue && this.outQueuePaused) { // Note In TcpSessions the queue length may // be greater than queueCapacity because of @@ -335,6 +311,9 @@ namespace NetStack.Runtime // servicing... Core.Instance().SignalOutboundPackets(); } + else { + // Race: DebugStub.Assert(packet.AdapterContext != null); + } return true; } @@ -342,16 +321,14 @@ namespace NetStack.Runtime // Determine whether the queue is empty without sleeping internal bool QueueIsEmpty(ArrayList! queue) { - lock (queue.SyncRoot) - { + lock (queue.SyncRoot) { return queue.Count == 0; } } protected bool PollQueueNotEmpty(ArrayList! queue, int timeoutMillis) { - if (timeoutMillis < 0) - { + if (timeoutMillis < 0) { return false; } @@ -360,19 +337,16 @@ namespace NetStack.Runtime TimeSpan remain = timeout; bool result = false; - if (Monitor.TryEnter(queue.SyncRoot, remain) == false) - { + if (Monitor.TryEnter(queue.SyncRoot, remain) == false) { return false; } remain = startTime + timeout - DateTime.Now; - while (remain >= TimeSpan.Zero && result == false) - { + while (remain >= TimeSpan.Zero && result == false) { Monitor.PulseAll(queue.SyncRoot); - if (Monitor.Wait(queue.SyncRoot, remain) == false) - { + if (Monitor.Wait(queue.SyncRoot, remain) == false) { // Timed-out waiting for lock return false; } @@ -388,8 +362,7 @@ namespace NetStack.Runtime protected bool PollQueueNotFull(ArrayList! queue, int timeoutMillis) { - if (timeoutMillis < 0) - { + if (timeoutMillis < 0) { return false; } @@ -398,18 +371,15 @@ namespace NetStack.Runtime TimeSpan remain = timeout; bool result = false; - if (Monitor.TryEnter(queue.SyncRoot, remain) == false) - { + if (Monitor.TryEnter(queue.SyncRoot, remain) == false) { return false; } remain = startTime + timeout - DateTime.Now; - while (remain >= TimeSpan.Zero && result == false) - { + while (remain >= TimeSpan.Zero && result == false) { Monitor.PulseAll(queue.SyncRoot); - if (Monitor.Wait(queue.SyncRoot, remain) == false) - { + if (Monitor.Wait(queue.SyncRoot, remain) == false) { // Timed-out waiting for lock return false; } @@ -426,9 +396,9 @@ namespace NetStack.Runtime // be override by concrete sessions if necessary virtual internal NetPacket GetPacket(ArrayList! queue, bool toBlock, - int timeoutMillis) + TimeSpan timeout) { - return DequeuePacket(queue, timeoutMillis); + return DequeuePacket(queue, timeout); } virtual internal bool PutPacket(ArrayList! queue, @@ -447,7 +417,7 @@ namespace NetStack.Runtime // User interface //-------------------------------------------- - virtual public byte[] PollCopyData(int timeoutMillis) + virtual public byte[] PollCopyData(TimeSpan timeout) { // If we're out of packets and there will never be anymore, // don't try to sleep for any! @@ -456,7 +426,7 @@ namespace NetStack.Runtime } // else return the enqueued packets - NetPacket netPacket = DequeuePacket(inQueue, timeoutMillis); + NetPacket netPacket = DequeuePacket(inQueue, timeout); if (netPacket != null) { DebugStub.Assert(netPacket.AdapterContext != null); @@ -467,7 +437,7 @@ namespace NetStack.Runtime return null; } - public byte[] in ExHeap PollData(int timeoutMillis) + public byte[] in ExHeap PollData(TimeSpan timeout) { // Duplicate of PollData, but copies to shared heap buffer. if (!IsSessionValidForUserRead()) { @@ -475,7 +445,7 @@ namespace NetStack.Runtime } // else return the enqueued packets - NetPacket netPacket = DequeuePacket(inQueue, timeoutMillis); + NetPacket netPacket = DequeuePacket(inQueue, timeout); if (netPacket != null) { byte []! in ExHeap userData = @@ -507,8 +477,7 @@ namespace NetStack.Runtime // to create protocol specific packets virtual public int WriteData(byte[]! data) { - if (!IsSessionValidForUserWrite()) - { + if (!IsSessionValidForUserWrite()) { return -1; } @@ -542,20 +511,16 @@ namespace NetStack.Runtime { Session lhs = left as Session; Session rhs = right as Session; - if (lhs == null) - { + if (lhs == null) { return (rhs == null) ? 0 : -1; } - if (rhs == null) - { + if (rhs == null) { return +1; } - if (lhs.LocalPort < rhs.LocalPort) - { + if (lhs.LocalPort < rhs.LocalPort) { return -1; } - if (lhs.LocalPort > rhs.LocalPort) - { + if (lhs.LocalPort > rhs.LocalPort) { return +1; } return 0; diff --git a/base/Services/NetStack/Runtime/StaticConfiguration.cs b/base/Services/NetStack/Runtime/StaticConfiguration.cs index 106f597..bbace93 100644 --- a/base/Services/NetStack/Runtime/StaticConfiguration.cs +++ b/base/Services/NetStack/Runtime/StaticConfiguration.cs @@ -12,6 +12,7 @@ using System.Collections; #if SINGULARITY using Drivers.Net; using Microsoft.Singularity.Io; +using Microsoft.Singularity; #endif namespace NetStack.Runtime @@ -27,28 +28,28 @@ namespace NetStack.Runtime public static void Initialize() { - Core.Log("StaticConfiguration.Initialize() {0}", initialized); - modules = new ArrayList(); - modules.Add(Core.Instance()); - modules.Add(new IPModule()); - modules.Add(new ArpModule()); - modules.Add(new IcmpModule()); - modules.Add(new TcpModule()); - modules.Add(new UdpModule()); + using (ImpatientWatcher watcher = new ImpatientWatcher("NetStack starting", "Starting core", 100)) { + modules = new ArrayList(); + modules.Add(Core.Instance()); + modules.Add(new IPModule()); + modules.Add(new ArpModule()); + modules.Add(new IcmpModule()); + modules.Add(new TcpModule()); + modules.Add(new UdpModule()); - foreach (INetModule! module in modules) - { - bool success = module.Initialize(null); - Core.Log("Initializing {0}...{1}", - module.ModuleName, success ? "okay" : "fail"); + foreach (INetModule! module in modules) { + watcher.NextStep("Starting module " + module.ModuleName, 100); + bool success = module.Initialize(null); + if (!success) + Core.Log("Error: Module '{0}' failed to initialize.", module.ModuleName); + } + initialized = true; } - initialized = true; } public static void Start() { - if (running) - { + if (running) { return; } running = true; @@ -59,20 +60,21 @@ namespace NetStack.Runtime public static void Stop() { - if (!running) - { + if (!running) { return; } running = false; modules.Reverse(); - foreach (INetModule! module in modules) - { - Core.Log("Stopping {0}...", module.ModuleName); - bool success = module.StopModule(); - Core.Log("{0}\n", success ? "okay" : "fail"); + foreach (INetModule! module in modules) { + bool success; + using (ImpatientWatcher watcher = new ImpatientWatcher("stopping net module " + module.ModuleName, "Calling module.StopModule()", 500)) { + success = module.StopModule(); + } + if (!success) { + Core.Log("Module '{0}' indicates that it failed to stop.", module.ModuleName); + } } - Core.Log("Stopped all.\n"); } } } diff --git a/base/Services/NetStack/Runtime/StoppableThread.sg b/base/Services/NetStack/Runtime/StoppableThread.sg new file mode 100644 index 0000000..4f2bcc2 --- /dev/null +++ b/base/Services/NetStack/Runtime/StoppableThread.sg @@ -0,0 +1,126 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +// using System.Diagnostics; +using System.Threading; +using Microsoft.SingSharp; +using Microsoft.Singularity; +using Microsoft.Singularity.Channels; + +namespace NetStack.Runtime +{ + public contract StopThreadContract + { + in message Terminate(); + out message AckTerminate(); + + state Ready : one { + Terminate? -> Terminating; + } + + state Terminating : one { + AckTerminate! -> Terminated; + } + + state Terminated : one { + } + } + + public abstract class StoppableThread + { + protected abstract void Run(StopThreadContract.Exp:Ready! terminator); + + Thread thread; + + bool running; + readonly TRef! terminatorImpRef; + readonly TRef! terminatorExpRef; + + protected StoppableThread() + { + StopThreadContract.Imp! terminator_imp; + StopThreadContract.Exp! terminator_exp; + StopThreadContract.NewChannel(out terminator_imp, out terminator_exp); + + terminatorImpRef = new TRef(terminator_imp); + terminatorExpRef = new TRef(terminator_exp); + running = false; + + thread = null; + } + + private void ThreadRoutine() + { + StopThreadContract.Exp! terminator_exp = terminatorExpRef.Acquire(); + try { + Run(terminator_exp); + } + finally { + delete terminator_exp; + } + } + + public void Start() + { + if (thread != null) + throw new InvalidOperationException("This thread has already been started."); + + Thread t = new Thread(ThreadRoutine); + thread = t; + t.Start(); + running = true; + } + + public void Stop() + { + if (!running) + return; + + DebugStub.WriteLine(String.Format("StoppableThread: Asking thread {0} to stop...", thread.GetThreadId())); + + assert thread != null; + StopThreadContract.Imp! terminatorImp = this.terminatorImpRef.Acquire(); + terminatorImp.SendTerminate(); + + bool timeout_occurred = false; + + bool done; + do { + switch receive { + case terminatorImp.AckTerminate(): + done = true; + break; + + case terminatorImp.ChannelClosed(): + // good enough + done = true; + DebugStub.WriteLine(String.Format("Warning: Thread {0} closed its termination channel.", thread.GetThreadId())); + DebugStub.WriteLine("The thread may have terminated unexpectedly (thrown an exception)."); + break; + + case timeout(TimeSpan.FromSeconds(10)): + DebugStub.WriteLine(String.Format("Warning: Thread {0} is ignoring request to terminate.", thread.GetThreadId())); + done = false; + timeout_occurred = true; + break; + } + } while (!done); + + delete terminatorImp; + running = false; + + while (!thread.Join(TimeSpan.FromSeconds(10))) { + DebugStub.WriteLine(String.Format("Warning: Thread {0} acknowledge stop request, but has not yet terminated.", thread.GetThreadId())); + timeout_occurred = true; + } + + if (timeout_occurred) { + DebugStub.WriteLine(String.Format("StoppableThread: Thread {0} successfully stopped.", thread.GetThreadId())); + } + } + } +} diff --git a/base/Services/NetStack/Runtime/TCPFsm.cs b/base/Services/NetStack/Runtime/TCPFsm.cs index 1f99101..ce9b7be 100644 --- a/base/Services/NetStack/Runtime/TCPFsm.cs +++ b/base/Services/NetStack/Runtime/TCPFsm.cs @@ -1,92 +1,154 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // #define DEBUG_TCP -using NetStack.Common; -using Microsoft.Contracts; using System; -using System.Threading; -using System.Collections; using System.Diagnostics; -using NetStack.Contracts; -using TcpError = NetStack.Contracts.TcpError; - +using System.Runtime.CompilerServices; +using System.Threading; #if !SINGULARITY using System.Net; #endif -using System.Net.IP; -using Drivers.Net; +using Microsoft.Contracts; + +using NetStack.Common; +using TcpError = NetStack.Contracts.TcpError; namespace NetStack.Runtime { using Protocols; + public enum TcpStateEnum + { + Undefined = 0, + Closed = 1, + Listen = 2, + SynRecvd = 3, + SynSent = 4, + Established = 5, + CloseWait = 6, + LastAck = 7, + FinWait1 = 8, + FinWait2 = 9, + Closing = 10, + } + internal class TcpState { + /// + /// Names corresponding to Enums, above (reflection missing). + /// + internal static readonly string[] TcpStateNames = new string[] { + "Undefined", + "Closed ", + "Listen ", + "SynRecvd ", + "SynSent ", + "Establish", + "CloseWait", + "LastAck ", + "FinWait1 ", + "FinWait2 ", + "Closing ", + }; + + private static TcpState! instance = new TcpState(); + + internal static TcpState! InstanceOfUndefined() + { + return TcpState.instance; + } + // ctor internal TcpState() { } // make a state transition - protected void ChangeState(TcpSession! owner, TcpState newState) + protected void ChangeState(TcpSession! tcpSession, TcpState newState) { - owner.ChangeState((!)newState); + tcpSession.ChangeState((!)newState); } // enter the new state - virtual internal void OnStateEnter(TcpSession! owner) + virtual internal void OnStateEnter(TcpSession! tcpSession) { } // a new packet(=event) received, should handle it // according to the current state. - virtual internal NetStatus OnPacketReceive(TcpSession! owner, - NetPacket! pkt, - object ctx) + virtual internal NetStatus OnPacketReceive(TcpSession! tcpSession, + NetPacket! packet, + object context) { return NetStatus.Code.PROTOCOL_OK; } - // leave the state to the new one - virtual internal void OnStateLeave(TcpSession! owner, TcpState newState) + // leave the current state to the new one + virtual internal void OnStateLeave(TcpSession! tcpSession, TcpState newState) { } - // handle timeout events + // Handle timeout events. // args is a session argument which is session specific - virtual internal NetStatus OnTimeout(Dispatcher.CallbackArgs args) + virtual internal NetStatus OnTimeout(TcpSession! tcpSession, Dispatcher.CallbackArgs args) { + TcpSessionEventsSource.EventLog.LogTimeout( + tcpSession.Uid, + tcpSession.currentState.StateEnum, + TcpSession.TcpTimeoutType.Unknown); + return NetStatus.Code.PROTOCOL_OK; } - // get the state name (debug) - virtual internal string GetStateName() + // Get the state name for debugging + internal string GetStateName() { - return "UNDEFINED"; + return TcpState.TcpStateNames[(int)this.StateEnum]; + } + + // get state enum for eventing (tracing) + virtual internal TcpStateEnum GetStateEnum() + { + return TcpStateEnum.Undefined; + } + + // State's Enumeration as a Property + internal TcpStateEnum StateEnum + { + get { return this.GetStateEnum(); } + } + + // State's Name as a Property + internal string StateName + { + get { return TcpState.TcpStateNames[(int)this.StateEnum]; } } [ Conditional("DEBUG_TCP") ] internal static void DebugPrint(string format, params object [] args) { - Core.Log("TCP: "); Core.Log(format, args); } + + [ Conditional("DEBUG_TCP") ] + internal static void DebugPrintLine(string format, params object [] args) + { + TcpState.DebugPrint(format, args); + } } - /** - * This the TCP Finite State Machine Implementation - * NOTICE: This is just a quick implementation... - * should handle FIN,RST, window management, slow start, double acks... - */ + /// + // This the TCP Finite State Machine Implementation + // NOTICE: This is just a quick implementation... + // should handle FIN, RST, window management, slow start, double acks... + // internal class TCPFSM { @@ -106,52 +168,53 @@ namespace NetStack.Runtime //---------------------------------------------------------------------------- - // Compare two TCP sequence numbers: -1 means A < B, 0 means A == B and - // 1 means A > B + // Compare two TCP sequence numbers: + // - means A < B, 0 means A == B and + means A > B [Pure] - internal static int TCPSeqCmp(uint seqA, uint seqB) + [Inline] + private static int TcpSequenceNumberCompare(uint seqA, uint seqB) { // Exploit integer underflow to compare correctly even in the case - // of sequence number wraparound. This assumes the two numbers - // are always in the same half of the numberspace. - uint diff = unchecked(seqA - seqB); - int signedDiff = unchecked((int)diff); + // of sequence number wraparound. This assumes the two numbers are + // always in the same half of the numberspace. - if (signedDiff < 0) { - return -1; // A < B - } else if (signedDiff > 0) { - return 1; // A > B - } else { - return 0; // A == B - } + return unchecked((int)(unchecked(seqA - seqB))); + } + + [Pure] + internal static bool TCPSeqEQ(uint seqA, uint seqB) + { + return TCPFSM.TcpSequenceNumberCompare(seqA, seqB) == 0; + } + + [Pure] + internal static bool TCPSeqNEQ(uint seqA, uint seqB) + { + return TCPFSM.TcpSequenceNumberCompare(seqA, seqB) != 0; } [Pure] internal static bool TCPSeqLEQ(uint seqA, uint seqB) { - int cmp = TCPSeqCmp(seqA, seqB); - return (cmp == 0) || (cmp == -1); + return TCPFSM.TcpSequenceNumberCompare(seqA, seqB) <= 0; } [Pure] internal static bool TCPSeqLess(uint seqA, uint seqB) { - int cmp = TCPSeqCmp(seqA, seqB); - return (cmp == -1); + return TCPFSM.TcpSequenceNumberCompare(seqA, seqB) < 0; } [Pure] internal static bool TCPSeqGEQ(uint seqA, uint seqB) { - int cmp = TCPSeqCmp(seqA, seqB); - return (cmp == 0) || (cmp == 1); + return TCPFSM.TcpSequenceNumberCompare(seqA, seqB) >= 0; } [Pure] internal static bool TCPSeqGreater(uint seqA, uint seqB) { - int cmp = TCPSeqCmp(seqA, seqB); - return (cmp == 1); + return TCPFSM.TcpSequenceNumberCompare(seqA, seqB) > 0; } //---------------------------------------------------------------------------- @@ -165,63 +228,59 @@ namespace NetStack.Runtime return instance; } - // get the state name (debug) - override internal string GetStateName() + // get state enum for eventing (tracing) and debugging. + override internal TcpStateEnum GetStateEnum() { - return "CLOSED"; + return TcpStateEnum.Closed; } - override internal void OnStateEnter(TcpSession! owner) + // enter the Closed state + override internal void OnStateEnter(TcpSession! tcpSession) { - DebugPrint("TCP::{0}, OnStateEnter\n",GetStateName()); + tcpSession.DestroyRetransmitTimer(); } // leave the state - override internal void OnStateLeave(TcpSession! owner, TcpState newState) + override internal void OnStateLeave(TcpSession! tcpSession, TcpState newState) { assert newState != null; - DebugPrint("State Transit: {0}->{1}\n", GetStateName(), - newState.GetStateName()); - if (newState==LISTEN) - { - // passive open - DebugPrint("---> waiting for connections...\n"); + base.OnStateLeave(tcpSession, newState); + switch (newState.StateEnum) { + case TcpStateEnum.Listen: + break; + case TcpStateEnum.SynSent: + if (!TCPFSM.SendSYN(tcpSession, TcpFormat.TCP_MSS)) { + // This should never happen; our outbound + // packet queue should never be empty + // while we're setting up a session + Debug.Assert(false); + } + break; + case TcpStateEnum.SynRecvd: + break; + default: + Core.Panic("TCP: Ses{0,3} ({1}) state machine error!", + tcpSession.Uid, this.StateName); + break; } - else if (newState==SYN_SENT) - { - // active open - DebugPrint("---> sending SYN...\n"); - if (!TCPFSM.SendSYN(owner,TcpFormat.TCP_MSS)) - { - // This should never happen; our outbound - // packet queue should never be empty - // while we're setting up a session - Debug.Assert(false); - } - } - else - { - Core.Panic("TCP state machine error!!!"); - } - } - override internal NetStatus OnTimeout(Dispatcher.CallbackArgs args) + override internal NetStatus OnTimeout(TcpSession! tcpSession, Dispatcher.CallbackArgs args) { - + base.OnTimeout(tcpSession, args); return NetStatus.Code.PROTOCOL_OK; } - override internal NetStatus OnPacketReceive(TcpSession! owner, NetPacket! pkt,object ctx) + override internal NetStatus OnPacketReceive(TcpSession! tcpSession, + NetPacket! packet, + object context) { - ///assert ctx != null; - //TcpFormat.TcpHeader tcpHeader = (TcpFormat.TcpHeader)ctx; - //TcpSession s = (TcpSession)owner; // not used + // A Closed session cannot handle any packets. + // BUGBUG: The parent class should make this the default behavior. // TODO return NetStatus.Code.PROTOCOL_DROP_ERROR; } } - //---------------------------------------------------------------------------- // a passive connection listen state internal sealed class TCPStateListen : TcpState @@ -234,92 +293,77 @@ namespace NetStack.Runtime return instance; } - // get the state name (debug) - override internal string GetStateName() + // get state enum for eventing (tracing) and debugging. + override internal TcpStateEnum GetStateEnum() { - return "LISTEN"; + return TcpStateEnum.Listen; } - override internal void OnStateEnter(TcpSession! owner) - { - DebugPrint("TCP::{0}, OnStateEnter\n",GetStateName()); + override internal void OnStateEnter(TcpSession! tcpSession) { + DebugPrintLine("TCP: Ses{0,3} ({1}) Waiting for connections.", + tcpSession.Uid, this.StateName); } - override internal NetStatus OnPacketReceive(TcpSession! owner, NetPacket! pkt,object ctx) + override internal NetStatus OnPacketReceive(TcpSession! tcpSession, + NetPacket! packet, + object context) { - assert ctx != null; - TcpFormat.TcpHeader tcpHeader = (TcpFormat.TcpHeader)ctx; - TcpSession s = (TcpSession)owner; + assert context != null; + TcpFormat.TcpHeader tcpHeader = (TcpFormat.TcpHeader)context; - if (TcpFormat.IsReset(ref tcpHeader)) + if (TcpFormat.IsReset(ref tcpHeader)) { return NetStatus.Code.PROTOCOL_DROP_ERROR; // we ignore it + } - if (TcpFormat.IsAck(ref tcpHeader)) - { - SendReset(s, false); + if (TcpFormat.IsAck(ref tcpHeader)) { + SendReset(tcpSession, false); return NetStatus.Code.PROTOCOL_DROP_ERROR; } - if (TcpFormat.IsSync(ref tcpHeader)) - { + if (TcpFormat.IsSync(ref tcpHeader)) { // we received a syn! - // 1. create a new TCP session and initialize it - // (only if we have room...TBC: should use listen param for bound) - // 2. send syn-ack in the context of the new session + // create a new TCP session and initialize it (only if + // we have room) TBC: should use listen param for bound) + // new session's new state (OnEnter) will send syn-ack + // (this is the result of the ChangeState call). TcpSession client = - (TcpSession!)s.Protocol.CreateSession(); - client.passiveSession = s; // this is our "owner" - client.SetLocalEndPoint(s.LocalAddress, s.LocalPort); - client.sessionTCB.RCV.NXT = tcpHeader.seq+1; - client.sessionTCB.RCV.IRS = tcpHeader.seq; - client.sessionTCB.SND.ISS = (uint)DateTime.UtcNow.Ticks; - client.sessionTCB.SND.NXT = client.sessionTCB.SND.ISS+1; - client.sessionTCB.SND.NextSeq = client.sessionTCB.SND.NXT; - client.sessionTCB.SND.UNA = client.sessionTCB.SND.ISS; - client.sessionTCB.SND.WND = tcpHeader.window; - client.sessionTCB.RCV.WND = TcpFormat.TCP_MSS; + (TcpSession!)tcpSession.Protocol.CreateSession(); + client.passiveSession = tcpSession; // this is our "owner" + client.SetLocalEndPoint(tcpSession.LocalAddress, + tcpSession.LocalPort); + client.InitializeServerSession(tcpHeader.seq, + tcpHeader.window); // we have access to the IP header from the packet overlapped // context (TCPModule put it there) - IPFormat.IPHeader ipHeader = pkt.OverlapContext as IPFormat.IPHeader; + IPFormat.IPHeader ipHeader = + packet.OverlapContext as IPFormat.IPHeader; assert ipHeader != null; client.SetRemoteEndPoint(ipHeader.Source, tcpHeader.sourcePort); // Save ourselves work if we should reject the session right away - if (s.AcceptQueueIsFull()) - { + if (tcpSession.AcceptQueueIsFull()) { TcpSegment reset = CreateResetSegment(client, true); - s.Protocol.OnProtocolSend(reset); + tcpSession.Protocol.OnProtocolSend(reset); return NetStatus.Code.PROTOCOL_DROP_ERROR; } - // send syn-ack - if (!SendSYNAck(client)) - { - // This should never happen; our outbound packet - // queues should never be full while we're setting - // up a session. - Debug.Assert(false); - } - - // the new session starts at syn-rcv - client.stateContext=null; - client.oldState=null; + // New session starts at syn-rcv. It immediately (from + // OnEntry) sends a SYN-Ack and starts the connect timer. client.ChangeState(SYN_RECVD); - // Start the timer ticking on establishing the new session - client.StartConnectTimer(); - - // we are left at the SAME state!!! + // WE are left at the SAME state! return NetStatus.Code.PROTOCOL_OK; } - else + else { return NetStatus.Code.PROTOCOL_DROP_ERROR; + } } } + //---------------------------------------------------------------------------- internal sealed class TCPStateSynRcv : TcpState { @@ -331,109 +375,122 @@ namespace NetStack.Runtime return instance; } - // get the state name (debug) - override internal string GetStateName() + // get state enum for eventing (tracing) + override internal TcpStateEnum GetStateEnum() { - return "SYNRECVD"; + return TcpStateEnum.SynRecvd; } - override internal void OnStateEnter(TcpSession! owner) + override internal void OnStateEnter(TcpSession! tcpSession) { - DebugPrint("TCP::{0}, OnStateEnter\n", GetStateName()); + // send syn-ack + if (!SendSYNAck(tcpSession)) { + // This should never happen; our outbound packet queues + // should never be full while we're setting up a session. + Debug.Assert(false); + } + + // Start timer to limit the time to establishing the new session + tcpSession.StartConnectTimer(); } - override internal NetStatus OnPacketReceive(TcpSession! owner, NetPacket! pkt,object ctx) + override internal NetStatus OnPacketReceive(TcpSession! tcpSession, + NetPacket! packet, + object context) { - assert ctx != null; + assert context != null; // all TCP states get the tcpHeader context - TcpFormat.TcpHeader tcpHeader = (TcpFormat.TcpHeader)ctx; - TcpSession s = (TcpSession)owner; - uint segmentSize = (uint)pkt.Available; + TcpFormat.TcpHeader tcpHeader = (TcpFormat.TcpHeader)context; + uint segmentSize = (uint)packet.Available; - bool accept=IsSegmentAcceptable(s,ref tcpHeader,segmentSize); + bool packetIsAcceptable = + IsSegmentAcceptable(tcpSession, ref tcpHeader, segmentSize); - if (!accept) - { - if (!TcpFormat.IsReset(ref tcpHeader)) - { - // send an ACK; don't care about whether this - // works or not since we're discarding the packet - // anyhow. - TCPFSM.SendAck(owner); + if (!packetIsAcceptable) { + if (!TcpFormat.IsReset(ref tcpHeader)) { + // send an ACK; don't care about whether this works + // or not since we're discarding the packet anyhow. + TCPFSM.SendAck(tcpSession); } return NetStatus.Code.PROTOCOL_DROP_ERROR; } // we accept the segment - Debug.Assert(tcpHeader.seq==s.sessionTCB.RCV.NXT); + Debug.Assert(tcpHeader.seq == tcpSession.sessionTCB.RCV.NXT); // if we get a reset - if (TcpFormat.IsReset(ref tcpHeader)) - { - if (s.oldState==LISTEN) - { - // if we came from the listen state (passive open) - // the reset will return us to the Listen state - s.FlushRetransmissions(); - ChangeState(s,LISTEN); - return NetStatus.Code.PROTOCOL_DROP_ERROR; - } - else - { - // we came from SYN_SENT (active open) - // the connection was refused, close it! - HandleTerminateSession(s,null, TcpError.Refused); - return NetStatus.Code.PROTOCOL_DROP_ERROR; + if (TcpFormat.IsReset(ref tcpHeader)) { + NetStatus.Code result; + switch (tcpSession.oldStateEnum) { + case TcpStateEnum.Listen: + // if we came from the listen state (passive open) + // the reset will return us to the Listen state + tcpSession.FlushRetransmissions(); + ChangeState(tcpSession, LISTEN); + result = NetStatus.Code.PROTOCOL_DROP_ERROR; + break; + case TcpStateEnum.SynSent: + // we came from SYN_SENT (active open) + // the connection was refused, close it! + HandleTerminateSession(tcpSession, null, TcpError.Refused); + result = NetStatus.Code.PROTOCOL_DROP_ERROR; + break; + default: + result = NetStatus.Code.PROTOCOL_DROP_ERROR; + break; } + + return result; } - - if (TcpFormat.IsSync(ref tcpHeader)) - { + if (TcpFormat.IsSync(ref tcpHeader)) { // reset - HandleTerminateSession(s,CreateResetSegment(s, false), TcpError.ProtocolViolation); + HandleTerminateSession(tcpSession, + CreateResetSegment(tcpSession, false), + TcpError.ProtocolViolation); return NetStatus.Code.PROTOCOL_DROP_ERROR; } - if (TcpFormat.IsAck(ref tcpHeader)) - { - if (TCPSeqLEQ(s.sessionTCB.SND.UNA, tcpHeader.ackSeq) && - TCPSeqGEQ(s.sessionTCB.SND.NXT, tcpHeader.ackSeq)) + if (TcpFormat.IsAck(ref tcpHeader)) { + if (TCPSeqLEQ(tcpSession.sessionTCB.SND.UNA, tcpHeader.ackSeq) && + TCPSeqGEQ(tcpSession.sessionTCB.SND.NXT, tcpHeader.ackSeq)) { // enter established state // we get an Ack for our syn-ack // remove the SYN-ACK from the retransmit Q - TCPFSM.HandleTCPAck(s,ref tcpHeader); - AttemptToEnterESTABLISHED(s, ref tcpHeader); + TCPFSM.HandleTCPAck(tcpSession, ref tcpHeader); + AttemptToEnterESTABLISHED(tcpSession, ref tcpHeader); return NetStatus.Code.PROTOCOL_OK; } - else - { + else { // reset - HandleTerminateSession(s,CreateResetSegment(s, false), TcpError.Reset); + HandleTerminateSession(tcpSession, + CreateResetSegment(tcpSession, false), + TcpError.Reset); return NetStatus.Code.PROTOCOL_DROP_ERROR; } } - if (TcpFormat.IsFIN(ref tcpHeader)) - { - DebugPrint("TCP::TCPStateSynRcv: Received FIN!!!\n"); + if (TcpFormat.IsFIN(ref tcpHeader)) { + DebugPrintLine("TCP: Ses{0,3} ({1)} TCPStateSynRcv: Received FIN.", + tcpSession.Uid, this.StateName); // RCV.NXT should reflect the data that we processed out of // this packet, but not the FIN. Advance over the FIN. - s.sessionTCB.RCV.NXT += 1; + tcpSession.sessionTCB.RCV.NXT += 1; // ack the FIN. Don't worry if this doesn't actually work; // the remote side will think the ACK got lost and retransmit, // which we will hopefully be able to successfully ACK later. - SendAck(s); - ChangeState(s,CLOSE_WAIT); + SendAck(tcpSession); + ChangeState(tcpSession, CLOSE_WAIT); } return NetStatus.Code.PROTOCOL_DROP_ERROR; } } + //---------------------------------------------------------------------------- internal sealed class TCPStateSynSent : TcpState { @@ -445,147 +502,123 @@ namespace NetStack.Runtime return instance; } - // get the state name (debug) - override internal string GetStateName() + // get state enum for eventing (tracing) and debugging. + override internal TcpStateEnum GetStateEnum() { - return "SYNSENT"; + return TcpStateEnum.SynSent; } - override internal void OnStateEnter(TcpSession! owner) + override internal void OnStateEnter(TcpSession! tcpSession) { - DebugPrint("TCP::{0}, OnStateEnter\n",GetStateName()); + DebugPrintLine("TCP: Ses{0,3} ({1}) Sending SYN.", + tcpSession.Uid, this.StateName); } // we sent a syn we wait for syn-ack - override internal NetStatus OnPacketReceive(TcpSession! owner,NetPacket! pkt,object ctx) + override internal NetStatus OnPacketReceive(TcpSession! tcpSession, + NetPacket! packet, + object context) { - assert ctx != null; + assert context != null; // all TCP states get the tcpHeader context - TcpFormat.TcpHeader tcpHeader = (TcpFormat.TcpHeader)ctx; - TcpSession s = (TcpSession)owner; + TcpFormat.TcpHeader tcpHeader = (TcpFormat.TcpHeader)context; bool ackAcceptable=false; - if (TcpFormat.IsAck(ref tcpHeader)) - { - if (TCPSeqLEQ(tcpHeader.ackSeq, s.sessionTCB.SND.ISS) || - TCPSeqGreater(tcpHeader.ackSeq, s.sessionTCB.SND.NXT)) + if (TcpFormat.IsAck(ref tcpHeader)) { + if (TCPSeqLEQ(tcpHeader.ackSeq, tcpSession.sessionTCB.SND.ISS) || + TCPSeqGreater(tcpHeader.ackSeq, tcpSession.sessionTCB.SND.NXT)) { - if (TcpFormat.IsReset(ref tcpHeader)) + if (TcpFormat.IsReset(ref tcpHeader)) { return NetStatus.Code.PROTOCOL_DROP_ERROR; + } - SendReset(s, false); + SendReset(tcpSession, false); return NetStatus.Code.PROTOCOL_DROP_ERROR; } - if (TCPSeqLEQ(s.sessionTCB.SND.UNA, tcpHeader.ackSeq) && - TCPSeqLEQ(tcpHeader.ackSeq, s.sessionTCB.SND.NXT)) + if (TCPSeqLEQ(tcpSession.sessionTCB.SND.UNA, tcpHeader.ackSeq) && + TCPSeqLEQ(tcpHeader.ackSeq, tcpSession.sessionTCB.SND.NXT)) { ackAcceptable=true; } } - if (TcpFormat.IsReset(ref tcpHeader)) - { - if (ackAcceptable) - { + if (TcpFormat.IsReset(ref tcpHeader)) { + if (ackAcceptable) { // connection was reset, drop the segment // and close the connection. - HandleTerminateSession(s,null, TcpError.Reset); + HandleTerminateSession(tcpSession, null, TcpError.Reset); return NetStatus.Code.PROTOCOL_DROP_ERROR; } - else + else { return NetStatus.Code.PROTOCOL_DROP_ERROR; + } } - // check syn // ack is is ok or there is no ack and no RST - if (TcpFormat.IsSync(ref tcpHeader)) - { - if (ackAcceptable) - { - DebugPrint("TCP::SYNSENT, Received SYN-ACK\n"); + if (TcpFormat.IsSync(ref tcpHeader)) { + if (ackAcceptable) { + DebugPrintLine("TCP: Ses{0,3} ({1}) SYNSENT, Received SYN-ACK", + tcpSession.Uid, this.StateName); - // grab the ack parameters and - // complete the session's data - s.sessionTCB.RCV.NXT = tcpHeader.seq+1; - s.sessionTCB.RCV.IRS = tcpHeader.seq; - s.sessionTCB.SND.UNA = tcpHeader.ackSeq; - s.sessionTCB.SND.WND = tcpHeader.window; - s.sessionTCB.RCV.WND = TcpFormat.TCP_MSS; + // use the ack parameters to complete the session's data + tcpSession.sessionTCB.RCV.NXT = tcpHeader.seq+1; + tcpSession.sessionTCB.RCV.IRS = tcpHeader.seq; + tcpSession.sessionTCB.SND.UNA = tcpHeader.ackSeq; + tcpSession.sessionTCB.SND.WND = tcpHeader.window; + tcpSession.sessionTCB.RCV.WND = TcpFormat.TCP_MSS; - // great! now remove the SYN from - // the retransmit Q (so we don't - // retransmit it again) - TCPFSM.HandleTCPAck(s,ref tcpHeader); + // now remove the SYN from the retransmit Q + // (so we don't retransmit it again) + TCPFSM.HandleTCPAck(tcpSession, ref tcpHeader); - if (s.sessionTCB.SND.UNA>s.sessionTCB.SND.ISS) - { + if (tcpSession.sessionTCB.SND.UNA > tcpSession.sessionTCB.SND.ISS) { // our syn has been acked. - TCPFSM.SendAck(owner); + TCPFSM.SendAck(tcpSession); // change the state to established! - AttemptToEnterESTABLISHED(owner, ref tcpHeader); + AttemptToEnterESTABLISHED(tcpSession, ref tcpHeader); return NetStatus.Code.PROTOCOL_OK; } } - else - { + else { // received a new SYN (overlap connection) // (see SYN_RECVD for more details) // Create a new object to track the new session - TcpSession client = (TcpSession!)s.Protocol.CreateSession(); - client.passiveSession = s; // this is our "owner" - client.SetLocalEndPoint(s.LocalAddress, - s.LocalPort); - client.sessionTCB.RCV.NXT = tcpHeader.seq+1; - client.sessionTCB.RCV.IRS = tcpHeader.seq; - client.sessionTCB.SND.ISS = (uint)DateTime.UtcNow.Ticks; - client.sessionTCB.SND.NXT = client.sessionTCB.SND.ISS+1; - client.sessionTCB.SND.NextSeq = client.sessionTCB.SND.NXT; - client.sessionTCB.SND.UNA = client.sessionTCB.SND.ISS; - client.sessionTCB.SND.WND = tcpHeader.window; - client.sessionTCB.RCV.WND = TcpFormat.TCP_MSS; + TcpSession client = + (TcpSession!) (tcpSession.Protocol.CreateSession()); + client.passiveSession = tcpSession; // this is our "owner" + client.SetLocalEndPoint(tcpSession.LocalAddress, + tcpSession.LocalPort); + client.InitializeServerSession(tcpHeader.seq, + tcpHeader.window); // we have access to the IP header from the packet overlapped // context (TCPModule put it there) - IPFormat.IPHeader ipHeader = pkt.OverlapContext as IPFormat.IPHeader; + IPFormat.IPHeader ipHeader = packet.OverlapContext as IPFormat.IPHeader; assert ipHeader!=null; client.SetRemoteEndPoint(ipHeader.Source, tcpHeader.sourcePort); // Save ourselves work if we should reject the session right away - if (s.AcceptQueueIsFull()) - { + if (tcpSession.AcceptQueueIsFull()) { TcpSegment reset = CreateResetSegment(client, true); - s.Protocol.OnProtocolSend(reset); + tcpSession.Protocol.OnProtocolSend(reset); return NetStatus.Code.PROTOCOL_DROP_ERROR; } - // send syn-ack - if (!SendSYNAck(client)) - { - // This should never happen; our outbound packet - // queues should never be empty when we're setting - // up a session - Debug.Assert(false); - } - - // the new session starts at syn-rcv - client.stateContext=null; - client.oldState=null; + // New session starts at syn-rcv. It immediately (from + // OnEntry) sends a SYN-Ack and starts the connect timer. client.ChangeState(SYN_RECVD); - // start the establishment countdown - client.StartConnectTimer(); - return NetStatus.Code.PROTOCOL_OK; } } return NetStatus.Code.PROTOCOL_DROP_ERROR; } - } /* TCPStateSynSent */ + } // TCPStateSynSent //---------------------------------------------------------------------------- // This is the working mode state @@ -599,81 +632,66 @@ namespace NetStack.Runtime return instance; } - // get the state name (debug) - override internal string GetStateName() + // get state enum for eventing (tracing) and debugging. + override internal TcpStateEnum GetStateEnum() { - return "ESTABLISHED"; + return TcpStateEnum.Established; } - override internal void OnStateEnter(TcpSession! owner) + override internal void OnStateEnter(TcpSession! tcpSession) { -#if DEBUG_TCP - Core.Log("TCP::{0}, OnStateEnter\n",GetStateName()); -#endif + // When becoming Established, stop the connect timer + // if there is one ticking. + tcpSession.DestroyConnectTimer(); + // Wake up anyone waiting for the connection to complete - ((TcpSession)owner).setupCompleteEvent.Set(); + tcpSession.setupCompleteEvent.Set(); } - // leave the state - override internal void OnStateLeave(TcpSession! owner, TcpState newState) + override internal NetStatus OnPacketReceive(TcpSession! tcpSession, + NetPacket! packet, + object context) { -#if DEBUG_TCP - assert newState != null; - Core.Log("State Transit: {0}->{1}\n", GetStateName(), - newState.GetStateName()); -#endif - } + assert context != null; + TcpFormat.TcpHeader tcpHeader = (TcpFormat.TcpHeader)context; + uint segmentSize = (uint)packet.Available; // TCPModule already shrinked it for us - override internal NetStatus OnPacketReceive(TcpSession! owner, NetPacket! pkt,object ctx) - { - assert ctx != null; - TcpFormat.TcpHeader tcpHeader = (TcpFormat.TcpHeader)ctx; - TcpSession s = (TcpSession)owner; - uint segmentSize = (uint)pkt.Available; // TCPModule already shrinked it for us - NetStatus res=NetStatus.Code.PROTOCOL_DROP_ERROR; + NetStatus res = NetStatus.Code.PROTOCOL_DROP_ERROR; - bool accept=IsSegmentAcceptable(s,ref tcpHeader,segmentSize); + bool accept=IsSegmentAcceptable(tcpSession, ref tcpHeader, segmentSize); - if (!accept) - { - if (!TcpFormat.IsReset(ref tcpHeader)) - { + if (!accept) { + if (!TcpFormat.IsReset(ref tcpHeader)) { // send an ACK and return - TCPFSM.SendAck(owner); + TCPFSM.SendAck(tcpSession); } return res; } - if (TcpFormat.IsReset(ref tcpHeader)) - { + if (TcpFormat.IsReset(ref tcpHeader)) { // for simplicity, just close the connection. - HandleTerminateSession(s,null, TcpError.Reset); + HandleTerminateSession(tcpSession, null, TcpError.Reset); return res; } // check syn - if (TcpFormat.IsSync(ref tcpHeader)) - { + if (TcpFormat.IsSync(ref tcpHeader)) { // send a reset - HandleTerminateSession(s,CreateResetSegment(s, false), TcpError.Reset); + HandleTerminateSession(tcpSession, CreateResetSegment(tcpSession, false), TcpError.Reset); return res; } - res = HandleTCPData(ref tcpHeader, pkt, s); + res = HandleTCPData(ref tcpHeader, packet, tcpSession); // our peer wants to end the relationship... - if (TcpFormat.IsFIN(ref tcpHeader)) - { -#if DEBUG_TCP - Core.Log("TCP::ESTABLISHED: Received FIN!!!\n"); -#endif + if (TcpFormat.IsFIN(ref tcpHeader)) { // RCV.NXT should reflect any data in this packet, but not the FIN. // Advance over the FIN. - s.sessionTCB.RCV.NXT += 1; + tcpSession.sessionTCB.RCV.NXT += 1; // ack the FIN - SendAck(s); - ChangeState(s,CLOSE_WAIT); + SendAck(tcpSession); + ChangeState(tcpSession, CLOSE_WAIT); } return res; @@ -693,45 +711,33 @@ namespace NetStack.Runtime return instance; } - // get the state name (debug) - override internal string GetStateName() + // get state enum (debug) + override internal TcpStateEnum GetStateEnum() { - return "CLOSE_WAIT"; + return TcpStateEnum.CloseWait; } - override internal void OnStateEnter(TcpSession! owner) + override internal void OnStateEnter(TcpSession! tcpSession) { -#if DEBUG_TCP - Core.Log("TCP::{0}, OnStateEnter\n",GetStateName()); -#endif + base.OnStateEnter(tcpSession); + // Signal that there is nothing more to read on this // connection - owner.ValidForRead = false; + tcpSession.ValidForRead = false; } - // leave the state - override internal void OnStateLeave(TcpSession! owner, TcpState newState) + override internal NetStatus OnPacketReceive(TcpSession! tcpSession, + NetPacket! packet, + object context) { -#if DEBUG_TCP - assert newState != null; - Core.Log("State Transit: {0}->{1}\n", GetStateName(), - newState.GetStateName()); -#endif - } + assert context != null; + TcpFormat.TcpHeader tcpHeader = (TcpFormat.TcpHeader)context; + uint segmentSize = (uint)packet.Available; - override internal NetStatus OnPacketReceive(TcpSession! owner, NetPacket! pkt,object ctx) - { - assert ctx != null; - TcpFormat.TcpHeader tcpHeader = (TcpFormat.TcpHeader)ctx; - TcpSession s = (TcpSession)owner; - uint segmentSize = (uint)pkt.Available; - - if (!IsSegmentAcceptable(s,ref tcpHeader,segmentSize)) - { - if (!TcpFormat.IsReset(ref tcpHeader)) - { + if (!IsSegmentAcceptable(tcpSession, ref tcpHeader, segmentSize)) { + if (!TcpFormat.IsReset(ref tcpHeader)) { // send an ACK and return - TCPFSM.SendAck(owner); + TCPFSM.SendAck(tcpSession); } return NetStatus.Code.PROTOCOL_DROP_ERROR; @@ -742,14 +748,15 @@ namespace NetStack.Runtime (!TcpFormat.IsAck(ref tcpHeader))) { // Abort - HandleTerminateSession(s,null, TcpError.Reset); + HandleTerminateSession(tcpSession, null, TcpError.Reset); return NetStatus.Code.PROTOCOL_DROP_ERROR; } - TCPFSM.HandleTCPAck(s,ref tcpHeader); + TCPFSM.HandleTCPAck(tcpSession, ref tcpHeader); return NetStatus.Code.PROTOCOL_OK; } } + //---------------------------------------------------------------------------- // LAST_ACK is entered when we close our side of the duplex // connection and the remote site had sent us its FIN @@ -768,84 +775,72 @@ namespace NetStack.Runtime return instance; } - // get the state name (debug) - override internal string GetStateName() + // get state enum for eventing (tracing) and debugging. + override internal TcpStateEnum GetStateEnum() { - return "LAST_ACK"; + return TcpStateEnum.LastAck; } - override internal void OnStateEnter(TcpSession! owner) + override internal void OnStateEnter(TcpSession! tcpSession) { -#if DEBUG_TCP - Core.Log("TCP::{0}, OnStateEnter\n",GetStateName()); -#endif + base.OnStateEnter(tcpSession); // Flag that writing is now disallowed on this session - ((TcpSession)owner).ValidForWrite = false; + tcpSession.ValidForWrite = false; } - // leave the state - override internal void OnStateLeave(TcpSession! owner, TcpState newState) + override internal NetStatus OnPacketReceive(TcpSession! tcbSession, + NetPacket! packet, + object context) { -#if DEBUG_TCP - assert newState != null; - Core.Log("State Transit: {0}->{1}\n",GetStateName(), - newState.GetStateName()); -#endif - } - - override internal NetStatus OnPacketReceive(TcpSession! owner,NetPacket! pkt,object ctx) - { - assert ctx != null; - TcpFormat.TcpHeader tcpHeader = (TcpFormat.TcpHeader)ctx; - TcpSession s = (TcpSession)owner; - uint segmentSize = (uint)pkt.Available; // TCPModule already shrinked it for us + assert context != null; + TcpFormat.TcpHeader tcpHeader = (TcpFormat.TcpHeader)context; + uint segmentSize = (uint)packet.Available; // TCPModule already shrinked it for us // RST is illegal at this point - if (TcpFormat.IsReset(ref tcpHeader)) - { + if (TcpFormat.IsReset(ref tcpHeader)) { // for simplicity, just close the connection. - HandleTerminateSession(s,null, TcpError.Reset); + HandleTerminateSession(tcbSession, null, TcpError.Reset); return NetStatus.Code.PROTOCOL_DROP_ERROR; } // SYNC causes us to issue a RST and abort - if (TcpFormat.IsSync(ref tcpHeader)) - { + if (TcpFormat.IsSync(ref tcpHeader)) { // send a reset - HandleTerminateSession(s,CreateResetSegment(s, false), TcpError.Reset); + HandleTerminateSession(tcbSession, + CreateResetSegment(tcbSession, false), + TcpError.Reset); return NetStatus.Code.PROTOCOL_DROP_ERROR; } - if (!IsSegmentAcceptable(s,ref tcpHeader,segmentSize)) - { + if (!IsSegmentAcceptable(tcbSession, ref tcpHeader, segmentSize)) { // Just drop it return NetStatus.Code.PROTOCOL_DROP_ERROR; } // Do ACK housekeeping - if (TCPSeqLess(s.sessionTCB.SND.UNA, tcpHeader.ackSeq) && - TCPSeqLEQ(tcpHeader.ackSeq, s.sessionTCB.SND.NXT)) + if (TCPSeqLess(tcbSession.sessionTCB.SND.UNA, tcpHeader.ackSeq) && + TCPSeqLEQ(tcpHeader.ackSeq, tcbSession.sessionTCB.SND.NXT)) { // This appears to be ACKing something we sent. - s.sessionTCB.SND.UNA = tcpHeader.ackSeq; + tcbSession.sessionTCB.SND.UNA = tcpHeader.ackSeq; // remove the packet(s) from the retransmit queue - TCPFSM.HandleTCPAck(s,ref tcpHeader); + TCPFSM.HandleTCPAck(tcbSession, ref tcpHeader); } // Note the weirdness here; because we prepacketize // data, we don't want to compare to SND.NXT - if (TCPSeqGEQ(s.sessionTCB.SND.UNA, s.sessionTCB.SND.NextSeq)) - { - // The remote site has acknowledged everything - // we sent. We're done. - HandleTerminateSession(s, null, TcpError.Reset); + if (TCPSeqGEQ(tcbSession.sessionTCB.SND.UNA, tcbSession.sessionTCB.SND.NextSeq)) { + // The remote site has acknowledged everything we sent. + // We're done. + HandleTerminateSession(tcbSession, null, TcpError.Reset); } return NetStatus.Code.PROTOCOL_DROP_ERROR; } } + //---------------------------------------------------------------------------- // FIN_WAIT1 is entered when we close our side of the duplex // connection. We don't send any more data, but we continue to @@ -863,114 +858,103 @@ namespace NetStack.Runtime return instance; } - // get the state name (debug) - override internal string GetStateName() + // get state enum (debug) + override internal TcpStateEnum GetStateEnum() { - return "FIN_WAIT1"; + return TcpStateEnum.FinWait1; } - override internal void OnStateEnter(TcpSession! owner) + override internal void OnStateEnter(TcpSession! tcpSession) { -#if DEBUG_TCP - Core.Log("TCP::{0}, OnStateEnter\n",GetStateName()); -#endif + base.OnStateEnter(tcpSession); // Flag that writing is now disallowed on this session - ((TcpSession)owner).ValidForWrite = false; + tcpSession.ValidForWrite = false; } // leave the state - override internal void OnStateLeave(TcpSession! owner, TcpState newState) + override internal void OnStateLeave(TcpSession! tcpSession, TcpState newState) { -#if DEBUG_TCP assert newState != null; - Core.Log("State Transit: {0}->{1}\n",GetStateName(), - newState.GetStateName()); -#endif + base.OnStateLeave(tcpSession, newState); } - override internal NetStatus OnPacketReceive(TcpSession! owner, NetPacket! pkt,object ctx) + override internal NetStatus OnPacketReceive(TcpSession! tcpSession, NetPacket! packet, object context) { - assert ctx != null; - TcpFormat.TcpHeader tcpHeader = (TcpFormat.TcpHeader)ctx; - TcpSession s = (TcpSession)owner; - uint segmentSize = (uint)pkt.Available; // TCPModule already shrinked it for us + assert context != null; + TcpFormat.TcpHeader tcpHeader = (TcpFormat.TcpHeader)context; + uint segmentSize = (uint)packet.Available; // TCPModule already shrinked it for us // RST is illegal at this point - if (TcpFormat.IsReset(ref tcpHeader)) - { + if (TcpFormat.IsReset(ref tcpHeader)) { // for simplicity, just close the connection. - HandleTerminateSession(s,null, TcpError.ProtocolViolation); + HandleTerminateSession(tcpSession, null, TcpError.ProtocolViolation); return NetStatus.Code.PROTOCOL_DROP_ERROR; } // SYNC causes us to issue a RST and abort - if (TcpFormat.IsSync(ref tcpHeader)) - { + if (TcpFormat.IsSync(ref tcpHeader)) { // send a reset - HandleTerminateSession(s,CreateResetSegment(s, false), TcpError.ProtocolViolation); + HandleTerminateSession(tcpSession, CreateResetSegment(tcpSession, false), TcpError.ProtocolViolation); return NetStatus.Code.PROTOCOL_DROP_ERROR; } - if (!IsSegmentAcceptable(s,ref tcpHeader,segmentSize)) - { - if (!TcpFormat.IsReset(ref tcpHeader)) - { + if (!IsSegmentAcceptable(tcpSession, ref tcpHeader, segmentSize)) { + if (!TcpFormat.IsReset(ref tcpHeader)) { // send an ACK and return - TCPFSM.SendAck(owner); + TCPFSM.SendAck(tcpSession); } return NetStatus.Code.PROTOCOL_DROP_ERROR; } // Chew on the payload... - NetStatus retval = HandleTCPData(ref tcpHeader, pkt, s); + NetStatus retval = HandleTCPData(ref tcpHeader, packet, tcpSession); - if (TCPSeqLEQ(tcpHeader.seq, s.sessionTCB.RCV.NXT) && + if (TCPSeqLEQ(tcpHeader.seq, tcpSession.sessionTCB.RCV.NXT) && (TcpFormat.IsFIN(ref tcpHeader))) { // This is the remote side's FIN, and we have // received all data preceding it, too! // advance RCV.NXT over the FIN sequence - s.sessionTCB.RCV.NXT += 1; + tcpSession.sessionTCB.RCV.NXT += 1; - // note the weirdness here; because we + // Note the weirdness here; because we // prepacketize data, we don't want to test // against SND.NXT - if (TCPSeqLess(s.sessionTCB.SND.UNA, s.sessionTCB.SND.NextSeq)) - { + if (TCPSeqLess(tcpSession.sessionTCB.SND.UNA, + tcpSession.sessionTCB.SND.NextSeq)) { // ...but the remote side hasn't received // everything we have said, yet. // ack the FIN - SendAck(s); - ChangeState(s, CLOSING); + SendAck(tcpSession); + ChangeState(tcpSession, CLOSING); } - else - { + else { // The remote side has heard everything we // have to say. We're done. // ACK the FIN and shut down - TcpSegment ackSeg = CreateAckSegment(s); - HandleTerminateSession(s, ackSeg, TcpError.Closed); + TcpSegment ackSeg = CreateAckSegment(tcpSession); + HandleTerminateSession(tcpSession, ackSeg, TcpError.Closed); } } - else - { + else { // Note the weirdness here; because we // prepacketize data, we do not want to test // against SND.NXT, but rather SND.NextSeq. - if (TCPSeqGEQ(s.sessionTCB.SND.UNA, s.sessionTCB.SND.NextSeq)) - { + if (TCPSeqGEQ(tcpSession.sessionTCB.SND.UNA, + tcpSession.sessionTCB.SND.NextSeq)) { // The remote side has ACKed everything we have // said, but they haven't FINed themselves. - ChangeState(s, FIN_WAIT2); + ChangeState(tcpSession, FIN_WAIT2); } } return retval; } } + //---------------------------------------------------------------------------- // FIN_WAIT2 is entered when the other side has received our FIN, but has // more data to send. We wait for them to signal FIN as well. @@ -984,86 +968,74 @@ namespace NetStack.Runtime return instance; } - // get the state name (debug) - override internal string GetStateName() + // get state enum (debug) + override internal TcpStateEnum GetStateEnum() { - return "FIN_WAIT2"; - } - - override internal void OnStateEnter(TcpSession! owner) - { -#if DEBUG_TCP - Core.Log("TCP::{0}, OnStateEnter\n",GetStateName()); -#endif + return TcpStateEnum.FinWait2; } // leave the state - override internal void OnStateLeave(TcpSession! owner, TcpState newState) + override internal void OnStateLeave(TcpSession! tcpSession, TcpState newState) { -#if DEBUG_TCP - assert newState != null; - Core.Log("State Transit: {0}->{1}\n",GetStateName(), - newState.GetStateName()); -#endif + base.OnStateLeave(tcpSession, newState); } - override internal NetStatus OnPacketReceive(TcpSession! owner, NetPacket! pkt,object ctx) + override internal NetStatus OnPacketReceive(TcpSession! tcpSession, + NetPacket! packet, + object context) { - assert ctx != null; - TcpFormat.TcpHeader tcpHeader = (TcpFormat.TcpHeader)ctx; - TcpSession s = (TcpSession)owner; - uint segmentSize = (uint)pkt.Available; // TCPModule already shrinked it for us + assert context != null; + TcpFormat.TcpHeader tcpHeader = (TcpFormat.TcpHeader)context; + uint segmentSize = (uint)packet.Available; // TCPModule already shrinked it for us // RST is illegal at this point - if (TcpFormat.IsReset(ref tcpHeader)) - { + if (TcpFormat.IsReset(ref tcpHeader)) { // for simplicity, just close the connection. - HandleTerminateSession(s,null, TcpError.ProtocolViolation); + HandleTerminateSession(tcpSession, + null, + TcpError.ProtocolViolation); return NetStatus.Code.PROTOCOL_DROP_ERROR; } // SYNC causes us to issue a RST and abort - if (TcpFormat.IsSync(ref tcpHeader)) - { + if (TcpFormat.IsSync(ref tcpHeader)) { // send a reset - HandleTerminateSession(s,CreateResetSegment(s, false), TcpError.ProtocolViolation); + HandleTerminateSession(tcpSession, + CreateResetSegment(tcpSession, false), + TcpError.ProtocolViolation); return NetStatus.Code.PROTOCOL_DROP_ERROR; } - if (!IsSegmentAcceptable(s,ref tcpHeader,segmentSize)) - { - if (!TcpFormat.IsReset(ref tcpHeader)) - { + if (!IsSegmentAcceptable(tcpSession, ref tcpHeader, segmentSize)) { + if (!TcpFormat.IsReset(ref tcpHeader)) { // send an ACK and return - TCPFSM.SendAck(owner); + TCPFSM.SendAck(tcpSession); } return NetStatus.Code.PROTOCOL_DROP_ERROR; } // Chew on the payload... - NetStatus retval = HandleTCPData(ref tcpHeader, pkt, s); + NetStatus retval = HandleTCPData(ref tcpHeader, packet, tcpSession); // Only check for FIN if we have previously received // all data. - if (TCPSeqLEQ(tcpHeader.seq, s.sessionTCB.RCV.NXT) && + if (TCPSeqLEQ(tcpHeader.seq, tcpSession.sessionTCB.RCV.NXT) && (TcpFormat.IsFIN(ref tcpHeader))) { -#if DEBUG_TCP - Core.Log("FIN_WAIT2: Received FIN!!!\n"); -#endif - // RCV.NXT should reflect the data in this packet, but not the - // FIN. Advance over the FIN... - s.sessionTCB.RCV.NXT += 1; + // RCV.NXT should reflect the data in this packet, but not + // the FIN. Advance over the FIN... + tcpSession.sessionTCB.RCV.NXT += 1; // ack the FIN and shut down - TcpSegment ackSeg = CreateAckSegment(s); - HandleTerminateSession(s, ackSeg, TcpError.Closed); + TcpSegment ackSeg = CreateAckSegment(tcpSession); + HandleTerminateSession(tcpSession, ackSeg, TcpError.Closed); } return retval; } } + //---------------------------------------------------------------------------- // CLOSING is entered when both sides have sent a FIN, but we're not sure // the other side has received all our data yet. We hang around processing @@ -1078,296 +1050,281 @@ namespace NetStack.Runtime return instance; } - // get the state name (debug) - override internal string GetStateName() + // get state enum (debug) + override internal TcpStateEnum GetStateEnum() { - return "CLOSING"; + return TcpStateEnum.Closing; } - override internal void OnStateEnter(TcpSession! owner) + override internal void OnStateEnter(TcpSession! tcpSession) { -#if DEBUG_TCP - Core.Log("TCP::{0}, OnStateEnter\n",GetStateName()); -#endif + base.OnStateEnter(tcpSession); + // Signal that there's nothing more to read on this session - owner.ValidForRead = false; + tcpSession.ValidForRead = false; } // leave the state - override internal void OnStateLeave(TcpSession! owner, TcpState newState) + override internal void OnStateLeave(TcpSession! tcpSession, TcpState newState) { -#if DEBUG_TCP assert newState != null; - Core.Log("State Transit: {0}->{1}\n",GetStateName(), - newState.GetStateName()); -#endif + base.OnStateLeave(tcpSession, newState); } - override internal NetStatus OnPacketReceive(TcpSession! owner, NetPacket! pkt, object ctx) + override internal NetStatus OnPacketReceive(TcpSession! tcpSession, + NetPacket! packet, + object context) { - assert ctx != null; - TcpFormat.TcpHeader tcpHeader = (TcpFormat.TcpHeader)ctx; - TcpSession s = (TcpSession)owner; - uint segmentSize = (uint)pkt.Available; // TCPModule already shrinked it for us + assert context != null; + TcpFormat.TcpHeader tcpHeader = (TcpFormat.TcpHeader)context; + uint segmentSize = (uint)packet.Available; // TCPModule already shrinked it for us // RST is illegal at this point - if (TcpFormat.IsReset(ref tcpHeader)) - { + if (TcpFormat.IsReset(ref tcpHeader)) { // for simplicity, just close the connection. - HandleTerminateSession(s,null, TcpError.ProtocolViolation); + HandleTerminateSession(tcpSession, null, TcpError.ProtocolViolation); return NetStatus.Code.PROTOCOL_DROP_ERROR; } // SYNC causes us to issue a RST and abort - if (TcpFormat.IsSync(ref tcpHeader)) - { + if (TcpFormat.IsSync(ref tcpHeader)) { // send a reset - HandleTerminateSession(s,CreateResetSegment(s, false), TcpError.ProtocolViolation); + HandleTerminateSession(tcpSession, + CreateResetSegment(tcpSession, false), + TcpError.ProtocolViolation); return NetStatus.Code.PROTOCOL_DROP_ERROR; } - if (!IsSegmentAcceptable(s,ref tcpHeader,segmentSize)) - { + if (!IsSegmentAcceptable(tcpSession, ref tcpHeader, segmentSize)) { // Just drop it return NetStatus.Code.PROTOCOL_DROP_ERROR; } // Do ACK housekeeping - if (TCPSeqLess(s.sessionTCB.SND.UNA, tcpHeader.ackSeq) && - TCPSeqLEQ(tcpHeader.ackSeq, s.sessionTCB.SND.NXT)) + if (TCPSeqLess(tcpSession.sessionTCB.SND.UNA, tcpHeader.ackSeq) && + TCPSeqLEQ(tcpHeader.ackSeq, tcpSession.sessionTCB.SND.NXT)) { // This appears to be ACKing something we sent. - s.sessionTCB.SND.UNA = tcpHeader.ackSeq; + tcpSession.sessionTCB.SND.UNA = tcpHeader.ackSeq; // remove the packet(s) from the retransmit queue - TCPFSM.HandleTCPAck(s,ref tcpHeader); + TCPFSM.HandleTCPAck(tcpSession, ref tcpHeader); } // Note the weirdness here; because we // prepacketize data, we don't want to compare against // SND.NXT - if (TCPSeqGEQ(s.sessionTCB.SND.UNA, s.sessionTCB.SND.NextSeq)) - { + if (TCPSeqGEQ(tcpSession.sessionTCB.SND.UNA, tcpSession.sessionTCB.SND.NextSeq)) { // The remote side has now heard everything we said. - HandleTerminateSession(s, null, TcpError.Closed); + HandleTerminateSession(tcpSession, null, TcpError.Closed); } return NetStatus.Code.PROTOCOL_DROP_ERROR; } } + //---------------------------------------------------------------------------- // Create an ACK for data we have received to date - internal static TcpSegment! CreateAckSegment(TcpSession! s) + internal static TcpSegment! CreateAckSegment(TcpSession! tcpSession) { byte[] ackBuffer = new byte[EthernetFormat.Size+IPFormat.Size+TcpFormat.Size]; TcpFormat.WriteTcpSegment( - ackBuffer,s.LocalPort,s.RemotePort,s.sessionTCB.RCV.NXT, - s.sessionTCB.SND.NextSeq,TcpFormat.TCP_MSS,s.LocalAddress, - s.RemoteAddress,0,true,false,false,false,false); + ackBuffer, tcpSession.LocalPort, tcpSession.RemotePort, + tcpSession.sessionTCB.RCV.NXT, tcpSession.sessionTCB.SND.NextSeq, + TcpFormat.TCP_MSS, tcpSession.LocalAddress, + tcpSession.RemoteAddress, 0, + true, false, false, false, false); - return new TcpSegment(ackBuffer,s,s.sessionTCB.SND.NextSeq,true); + return new TcpSegment(ackBuffer, tcpSession, + tcpSession.sessionTCB.SND.NextSeq, true); } // sends an ack - internal static bool SendAck(TcpSession! owner) + internal static bool SendAck(TcpSession! tcpSession) { - TcpSession s = (TcpSession)owner; - TcpSegment ackSeg = CreateAckSegment(s); + TcpSegment ackSeg = CreateAckSegment(tcpSession); // put it on this session outgoing queue - return s.PutPacket(s.outQueue,ackSeg,false); + return tcpSession.PutPacket(tcpSession.outQueue, ackSeg, false); } // Sends a FIN - internal static bool SendFin(TcpSession! s, bool canBlock) + internal static bool SendFin(TcpSession! tcpSession, bool canBlock) { byte[] finBuffer = new byte[EthernetFormat.Size+IPFormat.Size+TcpFormat.Size]; TcpFormat.WriteTcpSegment( - finBuffer,s.LocalPort,s.RemotePort,s.sessionTCB.RCV.NXT, - s.sessionTCB.SND.NextSeq,TcpFormat.TCP_MSS,s.LocalAddress,s.RemoteAddress,0, - true,false,false,true,false); + finBuffer, tcpSession.LocalPort, tcpSession.RemotePort, tcpSession.sessionTCB.RCV.NXT, + tcpSession.sessionTCB.SND.NextSeq, TcpFormat.TCP_MSS, tcpSession.LocalAddress, tcpSession.RemoteAddress, 0, + true, false, false, true, false); // ok, we have a ready segment. - TcpSegment syn = new TcpSegment(finBuffer,s,s.sessionTCB.SND.NextSeq,true); + TcpSegment syn = new TcpSegment(finBuffer, tcpSession, tcpSession.sessionTCB.SND.NextSeq, true); - bool retVal = s.PutPacket(s.outQueue,syn, canBlock); + bool retVal = tcpSession.PutPacket(tcpSession.outQueue, syn, canBlock); // Only advance the segment counter if we successfully queued the // outbound packet - if (retVal) - { - s.sessionTCB.SND.NextSeq++; + if (retVal) { + tcpSession.sessionTCB.SND.NextSeq++; } return retVal; } // sends a syn ack packet - internal static bool SendSYNAck(TcpSession! owner) + internal static bool SendSYNAck(TcpSession! tcpSession) { - TcpSession s = (TcpSession)owner; byte[] synackBuffer = new byte[EthernetFormat.Size+IPFormat.Size+TcpFormat.Size]; - TcpFormat.WriteTcpSegment(synackBuffer,s.LocalPort,s.RemotePort,s.sessionTCB.RCV.NXT, - s.sessionTCB.SND.ISS,(ushort)s.sessionTCB.SND.WND,s.LocalAddress, - s.RemoteAddress,0,true,true,false,false,false); + TcpFormat.WriteTcpSegment(synackBuffer, tcpSession.LocalPort, tcpSession.RemotePort, + tcpSession.sessionTCB.RCV.NXT, + tcpSession.sessionTCB.SND.ISS, + (ushort)tcpSession.sessionTCB.SND.WND, + tcpSession.LocalAddress, tcpSession.RemoteAddress, + 0, true, true, false, false, false); // ok, we have a ready segment. // (SYN is regular segment) - TcpSegment syn = new TcpSegment(synackBuffer, s, s.sessionTCB.SND.ISS, true); + TcpSegment syn = new TcpSegment(synackBuffer, tcpSession, tcpSession.sessionTCB.SND.ISS, true); - // put it on this session outgoing queue - return s.PutPacket(s.outQueue,syn,false); + // put it on this session's outgoing queue + return tcpSession.PutPacket(tcpSession.outQueue, syn, false); } // sends a syn packet, with MSS options - internal static bool SendSYN(TcpSession! owner,ushort MSS) + internal static bool SendSYN(TcpSession! tcpSession, ushort MSS) { - TcpSession s = (TcpSession)owner; byte[] synBuffer = new byte[EthernetFormat.Size+IPFormat.Size+TcpFormat.Size+4]; // setup the session - s.sessionTCB.SND.ISS=(uint)DateTime.UtcNow.Ticks; - s.sessionTCB.SND.UNA=s.sessionTCB.SND.ISS; - s.sessionTCB.SND.NXT=s.sessionTCB.SND.ISS+1; // next packet sequence - s.sessionTCB.SND.NextSeq = s.sessionTCB.SND.NXT; - s.sessionTCB.SND.WND=TcpFormat.TCP_MSS; + tcpSession.sessionTCB.SND.ISS=(uint)DateTime.UtcNow.Ticks; + tcpSession.sessionTCB.SND.UNA=tcpSession.sessionTCB.SND.ISS; + tcpSession.sessionTCB.SND.NXT=tcpSession.sessionTCB.SND.ISS+1; // next packet sequence + tcpSession.sessionTCB.SND.NextSeq = tcpSession.sessionTCB.SND.NXT; + tcpSession.sessionTCB.SND.WND=TcpFormat.TCP_MSS; // we must first write the data... - byte[] options=new byte[] {2,4, ((byte)(MSS>>8)),(byte)MSS}; - Array.Copy(options,0,synBuffer,EthernetFormat.Size+IPFormat.Size+TcpFormat.Size,4); + byte[] options=new byte[] {2, 4, ((byte)(MSS>>8)), (byte)MSS}; + Array.Copy(options, 0, synBuffer, EthernetFormat.Size+IPFormat.Size+TcpFormat.Size, 4); // now create the segment+checksum TcpFormat.WriteTcpSegment( - synBuffer,s.LocalPort,s.RemotePort,0, - s.sessionTCB.SND.ISS,MSS,s.LocalAddress,s.RemoteAddress,4, - false,true,false,false,true); + synBuffer, tcpSession.LocalPort, tcpSession.RemotePort, 0, + tcpSession.sessionTCB.SND.ISS, MSS, tcpSession.LocalAddress, tcpSession.RemoteAddress, 4, + false, true, false, false, true); // ok, we have a ready segment. // (SYN is regular segment) - TcpSegment syn = new TcpSegment(synBuffer,s,s.sessionTCB.SND.ISS,false); + TcpSegment syn = new TcpSegment(synBuffer, tcpSession, tcpSession.sessionTCB.SND.ISS, false); // put it on this session outgoing queue - return s.PutPacket(s.outQueue,syn,false); + return tcpSession.PutPacket(tcpSession.outQueue, syn, false); } // send a TCP reset - internal static bool SendReset(TcpSession! owner, bool isAck) + internal static bool SendReset(TcpSession! tcpSession, bool isAck) { // we won't retransmit it (it is like an Ack) - TcpSegment syn = CreateResetSegment(owner, true); + TcpSegment syn = CreateResetSegment(tcpSession, true); // put it on this session outgoing queue - return owner.PutPacket(owner.outQueue,syn,false); + return tcpSession.PutPacket(tcpSession.outQueue, syn, false); } // send a TCP reset - internal static TcpSegment! CreateResetSegment(TcpSession! owner, bool isAck) + internal static TcpSegment! CreateResetSegment(TcpSession! tcpSession, bool isAck) { - TcpSession s = (TcpSession)owner; byte[] rstBuffer = new byte[EthernetFormat.Size+IPFormat.Size+TcpFormat.Size]; - // note use SND.NXT as the sequence number here instead of nextSeq, + // NOTE: use SND.NXT as the sequence number here instead of nextSeq, // otherwise the sequence number may be far ahead (if there's lots of queued // data) and the receiving host may ignore it as outside its window. TcpFormat.WriteTcpSegment( - rstBuffer,s.LocalPort,s.RemotePort,s.sessionTCB.RCV.NXT, - s.sessionTCB.SND.NXT,TcpFormat.TCP_MSS,s.LocalAddress, - s.RemoteAddress,0,isAck,false,true,false,false); + rstBuffer, tcpSession.LocalPort, tcpSession.RemotePort, tcpSession.sessionTCB.RCV.NXT, + tcpSession.sessionTCB.SND.NXT, TcpFormat.TCP_MSS, tcpSession.LocalAddress, + tcpSession.RemoteAddress, 0, isAck, false, true, false, false); // we won't retransmit it - TcpSegment syn = new TcpSegment(rstBuffer,s,s.sessionTCB.SND.NXT,true); + TcpSegment syn = new TcpSegment(rstBuffer, tcpSession, tcpSession.sessionTCB.SND.NXT, true); return syn; } // Handle received TCP data // Returns the appropriate NetStatus.Code internal static NetStatus HandleTCPData(ref TcpFormat.TcpHeader tcpHeader, - NetPacket! pkt, TcpSession! s) + NetPacket! packet, TcpSession! tcpSession) { - uint segmentSize = (uint)pkt.Available; // TCPModule already shrinked it for us + uint segmentSize = (uint)packet.Available; // TCPModule already shrinked it for us // TBC: We assume the segment start with // the RCV.NXT !!! (otherwise we can buffer them) - if (TCPSeqGreater(tcpHeader.seq, s.sessionTCB.RCV.NXT)) - { + if (TCPSeqGreater(tcpHeader.seq, tcpSession.sessionTCB.RCV.NXT)) { // we missed one or few, send ack again - TCPFSM.SendAck(s); + TCPFSM.SendAck(tcpSession); return NetStatus.Code.PROTOCOL_DROP_ERROR; } - if (!TcpFormat.IsAck(ref tcpHeader)) - { + if (!TcpFormat.IsAck(ref tcpHeader)) { return NetStatus.Code.PROTOCOL_DROP_ERROR; } // First, deal with the window advertised in this packet. - s.sessionTCB.SND.WND = tcpHeader.window; - s.HandleWindowUpdate(); + tcpSession.sessionTCB.SND.WND = tcpHeader.window; + tcpSession.HandleWindowUpdate(); // ack is set and it is relevant (ack something we sent) - if (TCPSeqLess(s.sessionTCB.SND.UNA, tcpHeader.ackSeq) && - TCPSeqLEQ(tcpHeader.ackSeq, s.sessionTCB.SND.NXT)) + if (TCPSeqLess(tcpSession.sessionTCB.SND.UNA, tcpHeader.ackSeq) && + TCPSeqLEQ(tcpHeader.ackSeq, tcpSession.sessionTCB.SND.NXT)) { - s.sessionTCB.SND.UNA = tcpHeader.ackSeq; + tcpSession.sessionTCB.SND.UNA = tcpHeader.ackSeq; // remove the packet(s) from the retransmit queue - TCPFSM.HandleTCPAck(s,ref tcpHeader); + TCPFSM.HandleTCPAck(tcpSession, ref tcpHeader); } - else if (s.sessionTCB.SND.UNA>tcpHeader.ackSeq) - { + else if (tcpSession.sessionTCB.SND.UNA > tcpHeader.ackSeq) { // a duplicate ack return NetStatus.Code.PROTOCOL_DROP_ERROR; } - else if (TCPSeqGreater(tcpHeader.ackSeq, s.sessionTCB.SND.NXT)) - { + else if (TCPSeqGreater(tcpHeader.ackSeq, tcpSession.sessionTCB.SND.NXT)) { // ack for future data.. - TCPFSM.SendAck(s); + TCPFSM.SendAck(tcpSession); return NetStatus.Code.PROTOCOL_DROP_ERROR; } // check URG bit - if (TcpFormat.IsUrg(ref tcpHeader)) - { + if (TcpFormat.IsUrg(ref tcpHeader)) { // TBC } - if (TcpFormat.IsPush(ref tcpHeader)) - { -#if DEBUG_TCP - Core.Log("TCP: Received PUSH data, Len={0} !!!!!!!\n",segmentSize); -#endif + // check PUSH bit + if (TcpFormat.IsPush(ref tcpHeader)) { + // TBC } // at last, process the segment data!!! // at the end send an ACK (TBC: change to accumulate acks) - if (segmentSize>0) - { + if (segmentSize > 0) { // put the data in the session's inQ - if (s.PutPacket(s.inQueue,pkt,false)) - { - s.sessionTCB.RCV.NXT+=segmentSize; // we expect the next segment seq - s.sessionTCB.RCV.WND=TcpFormat.TCP_MSS; // TBC: change according to buffer -#if DEBUG_TCP - Core.Log("TCP:: PUSH data, Len={0} is on Queue\n",segmentSize); -#endif + if (tcpSession.PutPacket(tcpSession.inQueue, packet, false)) { + tcpSession.sessionTCB.RCV.NXT+=segmentSize; // we expect the next segment seq + tcpSession.sessionTCB.RCV.WND=TcpFormat.TCP_MSS; // TBC: change according to buffer + // send our ack if we ack data - SendAck(s); + SendAck(tcpSession); + // don't release the packet return NetStatus.Code.PROTOCOL_PROCESSING; } - else - { + else { // packet was dropped... // send our ack if we ack data - SendAck(s); + SendAck(tcpSession); return NetStatus.Code.PROTOCOL_DROP_ERROR; } } - else - { + else { // Payload was empty... return NetStatus.Code.PROTOCOL_DROP_ERROR; } @@ -1375,65 +1332,69 @@ namespace NetStack.Runtime // handle a TCP received ack // the ack is already checked for validity (by the actual state) - internal static void HandleTCPAck(TcpSession! owner, ref TcpFormat.TcpHeader tcpHdr) + internal static void HandleTCPAck(TcpSession! tcpSession, + ref TcpFormat.TcpHeader tcpHdr) { - owner.ACKThrough(tcpHdr.ackSeq); + tcpSession.ACKThrough(tcpHdr.ackSeq); } // check if we can accept the segment - internal static bool IsSegmentAcceptable(TcpSession! s, ref TcpFormat.TcpHeader tcpHeader,uint segmentSize) + internal static bool IsSegmentAcceptable(TcpSession! tcpSession, + ref TcpFormat.TcpHeader tcpHeader, + uint segmentSize) { bool accept=false; - // first check sequence number - if ((segmentSize==0)&&(s.sessionTCB.RCV.WND==0)) - { - accept = (tcpHeader.seq==s.sessionTCB.RCV.NXT); - } - else if ((segmentSize==0)&&(s.sessionTCB.RCV.WND>0)) - { - accept=(TCPSeqGEQ(tcpHeader.seq, s.sessionTCB.RCV.NXT) && - TCPSeqLess(tcpHeader.seq, s.sessionTCB.RCV.NXT+s.sessionTCB.RCV.WND)); - } - else if ((segmentSize>0)&&(s.sessionTCB.RCV.WND>0)) - { - accept=(TCPSeqGEQ(tcpHeader.seq, s.sessionTCB.RCV.NXT) && - TCPSeqLess(tcpHeader.seq, s.sessionTCB.RCV.NXT+s.sessionTCB.RCV.WND)); - accept = accept || (TCPSeqLEQ(s.sessionTCB.RCV.NXT, tcpHeader.seq+segmentSize-1) && - TCPSeqLess(tcpHeader.seq +segmentSize - 1, - s.sessionTCB.RCV.NXT + s.sessionTCB.RCV.WND)); + // first check sequence number + if ((segmentSize == 0) && (tcpSession.sessionTCB.RCV.WND == 0)) { + accept = (tcpHeader.seq == tcpSession.sessionTCB.RCV.NXT); } + else if ((segmentSize == 0) && (tcpSession.sessionTCB.RCV.WND > 0)) { + accept=(TCPSeqGEQ(tcpHeader.seq, tcpSession.sessionTCB.RCV.NXT) && + TCPSeqLess(tcpHeader.seq, tcpSession.sessionTCB.RCV.NXT+tcpSession.sessionTCB.RCV.WND)); + } + else if ((segmentSize > 0) && (tcpSession.sessionTCB.RCV.WND > 0)) { + accept=(TCPSeqGEQ(tcpHeader.seq, tcpSession.sessionTCB.RCV.NXT) && + TCPSeqLess(tcpHeader.seq, tcpSession.sessionTCB.RCV.NXT+tcpSession.sessionTCB.RCV.WND)); + + accept = accept || (TCPSeqLEQ(tcpSession.sessionTCB.RCV.NXT, tcpHeader.seq+segmentSize-1) && + TCPSeqLess(tcpHeader.seq +segmentSize - 1, + tcpSession.sessionTCB.RCV.NXT + tcpSession.sessionTCB.RCV.WND)); + } + return accept; } - private static void AttemptToEnterESTABLISHED(TcpSession! s, + private static void AttemptToEnterESTABLISHED(TcpSession! tcpSession, ref TcpFormat.TcpHeader tcpHeader) { - TcpSession passiveOwner = s.passiveSession; + TcpSession passiveOwner = tcpSession.passiveSession; - if (passiveOwner != null) - { - bool success = passiveOwner.AddAcceptedSession(s); + if (passiveOwner != null) { + bool success = passiveOwner.AddAcceptedSession(tcpSession); - if (!success) - { + if (!success) { // Oops; while this connection was being established // we ran out of room in the accept queue. Abort // the connection! - HandleTerminateSession(s,CreateResetSegment(s, false), TcpError.ResourcesExhausted); + HandleTerminateSession(tcpSession, + CreateResetSegment(tcpSession, false), + TcpError.ResourcesExhausted); return; } } - s.ChangeState(ESTABLISHED); + tcpSession.ChangeState(ESTABLISHED); } // this method terminated the given session // if nextSegment is not null than it is sent before final // removal. - internal static void HandleTerminateSession(TcpSession! s, TcpSegment nextSegment, TcpError connectError) + internal static void HandleTerminateSession(TcpSession! tcpSession, + TcpSegment nextSegment, + TcpError connectError) { - s.StopPersistTimer(); + tcpSession.StopPersistTimer(); // 1. first clear the retransmit queue // 2. make the session not valid for users! @@ -1441,56 +1402,52 @@ namespace NetStack.Runtime // 4. clear the out/in queues // 5. remove the session from TCP if it is no longer needed // (when it considered to be closed) - s.FlushRetransmissions(); + tcpSession.FlushRetransmissions(); // some user may wait on the q to write/read data // we will release them by trigger the monitors. // they will fail since Valid is false and they are users. // only TCP can still use this session. - lock (s.outQueue.SyncRoot) - { + lock (tcpSession.outQueue.SyncRoot) { // from now on, every user thread will // fail to read/write data - s.ValidForRead = false; - s.ValidForWrite = false; + tcpSession.ValidForRead = false; + tcpSession.ValidForWrite = false; - s.DrainQueue(s.outQueue); + tcpSession.DrainQueue(tcpSession.outQueue); if (nextSegment != null) - s.outQueue.Add(nextSegment); + tcpSession.outQueue.Add(nextSegment); // Don't forget these! - Monitor.PulseAll(s.outQueue.SyncRoot); + Monitor.PulseAll(tcpSession.outQueue.SyncRoot); Core.Instance().SignalOutboundPackets(); } - lock (s.inQueue.SyncRoot) - { + lock (tcpSession.inQueue.SyncRoot) { // Pulse anyone waiting to read data so they can notice // they are unlikely to succeed, but don't clear the // queue; if there is data on it, we may want to drain // it, still. - Monitor.PulseAll(s.inQueue.SyncRoot); + Monitor.PulseAll(tcpSession.inQueue.SyncRoot); } - if (nextSegment == null) - { - IProtocol tcpProtocol = s.Protocol; - Core.Instance().DeregisterSession(tcpProtocol, s); + if (nextSegment == null) { + IProtocol tcpProtocol = tcpSession.Protocol; + Core.Instance().DeregisterSession(tcpProtocol, tcpSession); } // change the state to close - s.ChangeState(TCPFSM.CLOSED); + tcpSession.ChangeState(TCPFSM.CLOSED); // Signal anyone who was waiting for this session to begin or end - s.connectError = connectError; - s.setupCompleteEvent.Set(); + tcpSession.connectError = connectError; + tcpSession.setupCompleteEvent.Set(); - // NOTE If we are transmitting a last-gasp packet, + // NOTE: If we are transmitting a last-gasp packet, // don't signal the session as closed just yet; wait for the packet // to actually get transmitted. - if (nextSegment == null) - { - s.closedEvent.Set(); + if (nextSegment == null) { + tcpSession.closedEvent.Set(); } } } diff --git a/base/Services/NetStack/Runtime/TCPModule.cs b/base/Services/NetStack/Runtime/TCPModule.cs index ed92b9b..db3a36e 100644 --- a/base/Services/NetStack/Runtime/TCPModule.cs +++ b/base/Services/NetStack/Runtime/TCPModule.cs @@ -1,36 +1,36 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- -/** - * Microsoft Research, Cambridge - * author: Yaron Weinsberg, Richard Black - */ +// #define DEBUG_TCP + +/// +// Microsoft Research, Cambridge +// -using NetStack.Common; using System; using System.Collections; using System.Diagnostics; +using System.Net.IP; #if !SINGULARITY using System.Net; using System.Text; #endif -using System.Net.IP; using Drivers.Net; + +using NetStack.Common; using NetStack.Protocols; namespace NetStack.Runtime { - /** - * This module implements the TCP protocol - * Notice: It is a simplified implementation (the very basic) - */ + /// + // This module implements the TCP protocol + // Notice: It is a simplified implementation (the very basic) + // public class TcpModule : IProtocol { // the ip handler @@ -68,7 +68,7 @@ namespace NetStack.Runtime // ------------------------ public bool Initialize(ProtocolParams parameters) { - Debug.Assert(parameters == null || parameters["name"]=="TCP"); + Debug.Assert(parameters == null || parameters["name"] == "TCP"); Core.Instance().RegisterProtocol(this); TcpSessionPool.SetTcpModule(this); return true; @@ -88,7 +88,7 @@ namespace NetStack.Runtime Session IProtocol.CreateSession() { TcpSession tcp = (TcpSession) TcpSessionPool.Get(); - Core.Instance().RegisterSession(this,tcp); + Core.Instance().RegisterSession(this, tcp); return tcp; } @@ -102,17 +102,14 @@ namespace NetStack.Runtime // handle incoming TCP packets public NetStatus OnProtocolReceive(NetPacket! packet) { - DebugPrint("TcpModule.OnPacketReceive\n"); - // IP should have passed us a context which // is the IPHeader... lets see... IPFormat.IPHeader! ipHeader = (IPFormat.IPHeader!) packet.OverlapContext; // read the TCP header TcpFormat.TcpHeader tcpHeader; - if (!TcpFormat.ReadTcpHeader(packet, out tcpHeader)) - { - DebugPrint("Bad TCP Header"); + if (!TcpFormat.ReadTcpHeader(packet, out tcpHeader)) { + DebugPrint("Bad TCP Header. Packet rejected."); return NetStatus.Code.PROTOCOL_DROP_ERROR; } @@ -120,7 +117,7 @@ namespace NetStack.Runtime if (tcpHeader.checksum != 0 && TcpFormat.IsChecksumValid(ipHeader,tcpHeader, packet) == false) { - DebugPrint("TCP checksum failed. No cigar and no packet!"); + DebugPrint("TCP checksum failed. Packet rejected."); return NetStatus.Code.PROTOCOL_DROP_CHKSUM; } @@ -132,72 +129,91 @@ namespace NetStack.Runtime packet.Clip(startIndex,segmentSize - 1); // find the relevant session... - Session s = FindSession(ipHeader, ref tcpHeader); - if (s != null) { - DebugPrint("TCP Session found.\n"); - // deliver the packet the the session - // we pass the tcpHeader as a context - // and the ipHeader as a packet.OverlapContext - // since we will not use it anymore (and TCP may need it) + Session tcpSession = FindSession(ipHeader, ref tcpHeader); + if (tcpSession == null) { + DebugPrint("TCP Session not found. TCP Packet dropped."); + } + else { + // Deliver the packet to the session. The tpcHeader is passed + // as a context and the ipHeader is passed as a + // packet.OverlapContext since we will not use it anymore + // (and TCP may need it). packet.OverlapContext = ipHeader; - return s.OnReceive(this,packet,tcpHeader); + return tcpSession.OnReceive(this, packet, tcpHeader); } return NetStatus.Code.PROTOCOL_DROP_ERROR; } - // find the relevant session for this connection - protected Session FindSession(IPFormat.IPHeader! ipHeader, - ref TcpFormat.TcpHeader tcpHeader) + // Find the relevant session for this connection + protected TcpSession FindSession(IPFormat.IPHeader! ipHeader, + ref TcpFormat.TcpHeader tcpHeader) { - // get the session table + // Get the session table ArrayList sessions = Core.Instance().GetSessions(this); - if (sessions == null) - { + if (sessions == null) { return null; } + // First priority is a full match (Loc/Rem X Addr/Port all match), + // but next priority is a "passive" match (a Broadcast to "me"). TcpSession passiveSession = null; // this is the passive session - // we first look for full match, saving the passive session - // if exist. if no full match, we return the passiveSession - lock (sessions.SyncRoot) - { - // this is a TcpSession - foreach(TcpSession! s in sessions) - { - bool b1 = (s.LocalAddress == ipHeader.Destination); - bool b2 = (s.RemoteAddress == ipHeader.Source); - bool b3 = (s.LocalPort == tcpHeader.destPort); - bool b4 = (s.RemotePort == tcpHeader.sourcePort); + lock (sessions.SyncRoot) { + // BUGBUG: This should use HashTables (KeyedCollections // TODO + // BUGBUG: if we get Generics) for Performance reasons. // TODO + // BUGBUG: We need one for full and one for passive. // TODO + // BUGBUG: The full hashtable uses local and remote // TODO + // BUGBUG: addresses as the keys and can be limited to // TODO + // BUGBUG: sessions without Remote Broadcast addresses; // TODO + // BUGBUG: the passive hashtable is limited to sessions // TODO + // BUGBUG: with Remote Broadcast addresses and uses the // TODO + // BUGBUG: local address only as the key. // TODO + // BUGBUG: Note that this is done for every TCP Packet. // TODO + foreach (TcpSession! tcpSession in sessions) { + bool matchLocal = + (tcpSession.LocalAddress == ipHeader.Destination) && + (tcpSession.LocalPort == tcpHeader.destPort); - bool fullMatch = b1 & b2 & b3 & b4; - // if we have full match, we return - // otherwise, save the passive session - if (fullMatch) - { - return s; - } - bool b5 = (s.RemoteAddress == IPv4.Broadcast && - s.RemotePort == 0); - bool passiveMatch = b1 & b3 & b5; - if (passiveMatch) - { - passiveSession = s; + // Both full and passive matches require full Local match. + if (matchLocal) { + + // Check the Remote Match for a Full Match + bool matchRemote = + (tcpSession.RemoteAddress == ipHeader.Source) && + (tcpSession.RemotePort == tcpHeader.sourcePort); + + // If a full match (local && remote) return the session. + if (matchRemote) { + return tcpSession; + } + + // Check for "Broadcast" address. + bool matchBroadcast = + (tcpSession.RemoteAddress == IPv4.Broadcast) && + (tcpSession.RemotePort == 0); + + // No full match, but save passiveSession + // if matchLocal && matchBroadcast. + if (matchBroadcast) { + passiveSession = tcpSession; + } } } } - // if we can't find exact match... + + // If we didn't find an exact match, return + // the passive match, if any (may be null). return passiveSession; } - // this method send a ready made TCP packet, - // it uses the IP layer. - public NetStatus OnProtocolSend(NetPacket! pkt) + + // Send TCP packet, constructed by the caller, using the IP layer. + public NetStatus OnProtocolSend(NetPacket! packet) { // if the ARP hasn't resolved the address yet, the // runtime will try this again (on the next handle // of outgoing sessions' queues) - return ip.OnProtocolSend(pkt); + return ip.OnProtocolSend(packet); } public NetStatus SetProtocolSpecific(ushort opcode, byte[]! data) @@ -205,9 +221,9 @@ namespace NetStack.Runtime return NetStatus.Code.PROTOCOL_OK; } - public NetStatus GetProtocolSpecific(ushort opcode,out byte[] data) + public NetStatus GetProtocolSpecific(ushort opcode, out byte[] data) { - data=null; + data = null; return NetStatus.Code.PROTOCOL_OK; } diff --git a/base/Services/NetStack/Runtime/TCPSession.cs b/base/Services/NetStack/Runtime/TCPSession.cs index 9defb90..c1335ac 100644 --- a/base/Services/NetStack/Runtime/TCPSession.cs +++ b/base/Services/NetStack/Runtime/TCPSession.cs @@ -1,45 +1,68 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- -/** - * Microsoft Research, Cambridge - * author: Yaron Weinsberg, Richard Black - */ +/// +// Microsoft Research, Cambridge +// // #define DEBUG_TCP -using NetStack.Common; using System; -using System.Threading; using System.Collections; using System.Diagnostics; +using System.Net.IP; +using System.Runtime.CompilerServices; +using System.Threading; #if !SINGULARITY using System.Net; #endif -using System.Net.IP; -using Drivers.Net; +using NetStack.Common; using NetStack.Protocols; using NetStack.Contracts; + using Microsoft.Contracts; using Microsoft.SingSharp; -using Microsoft.SingSharp.Runtime; using Microsoft.Singularity; using Microsoft.Singularity.Channels; +using Microsoft.Singularity.NetStack.Events; + +using Eventing = Microsoft.Singularity.Eventing; + namespace NetStack.Runtime { - /** - * This the TCP session object - */ - public class TcpSession : Session + /// + /// The 'TCP' specialization of the Session class. + /// + public class TcpSession : Session // BUGBUG: REMOVE locks after having the incoming packets send to the TcpSession thread. // TODO { + /// + /// Kinds of Timeout that are used in TCP. + /// + public enum TcpTimeoutType { + Unknown = 0, + Connect, + Persist, + Shutdown, + Retransmit, + }; + + /// + /// Names corresponding to Enums, above (reflection missing). + /// + internal static readonly string[] TcpTimeoutTypeNames = new string[] { + "Unknown", + "Connect", + "Persist", + "Shutdown", + "Retransmit", + }; + // the session's transmit q size (num of packets) public const int TxQSize=100; @@ -50,7 +73,7 @@ namespace NetStack.Runtime private const int MaxRetries = 5; // the number of seconds to wait for an active connect to succeed - private const int ConnectTimeout = 15; + private const int ActiveConnectTimeout = 15; // the number of seconds to wait for a passive connect to succeed private const int PassiveConnectTimeout = 5; @@ -62,19 +85,27 @@ namespace NetStack.Runtime // the number of seconds to wait for a graceful shutdown to complete private const int PoliteShutdownTimeout = 10; - // TCP retransmission state - private RJBlack.Timer retransTimer; - private const uint InitialRetransInterval = 3000; // 3s in ms, Per RFC 2988 + // 3s in ms, Per RFC 2988 + private const uint InitialRetransInterval = 3000u; // 200ms, more aggressive than RFC "SHOULD" of 1s - private const uint MinimumRetransInterval = 200; + private const uint MinimumRetransInterval = 200u; // Per-session retransmission state - private uint retransInterval = InitialRetransInterval, srtt = InitialRetransInterval, rttvar = 0; // All in ms + private uint retransInterval = InitialRetransInterval; + private uint srtt = InitialRetransInterval; + private uint rttvar = 0u; - internal TcpState stateContext; // our FSM state - internal TcpState oldState; // our previous state - internal TcpError connectError; // the result of the Connect request + /// Object used to Lock the entire TcpSession. + /// "this" is not used to as it is visible. + private object lockHolder = new object(); + + // States of the session, current and former. + internal TcpStateEnum oldStateEnum; // our previous (FSM) state's enumeration. + internal TcpState currentState; // our current (FSM) state + + // Error, if any, from the last Connect request + internal TcpError connectError; // accepted session list (TcpSessions) private ArrayList acceptedSessions; @@ -91,13 +122,16 @@ namespace NetStack.Runtime private const int RetransmitQSize = 100; private ArrayList retransmitQ; + // TCP retransmission timer + private RJBlack.Timer retransTimer; + // Setup / teardown timers - RJBlack.Timer setupTimer; - RJBlack.Timer shutdownTimer; + private RJBlack.Timer connectTimer; + private RJBlack.Timer shutdownTimer; // Information on the "persist" state (for when the remote host // has closed its receive window) - RJBlack.Timer persistTimer; + private RJBlack.Timer persistTimer; // An event that gets set when initial establishment either // succeeds or times out @@ -112,8 +146,17 @@ namespace NetStack.Runtime // Whether or not BindLocalEndPoint() has been called bool haveBound = false; - public bool ValidForRead { get { return isValidForRead;} set {isValidForRead=value;}} - public bool ValidForWrite { get { return isValidForWrite;} set {isValidForWrite=value;}} + public bool ValidForRead + { + get { return isValidForRead; } + set { isValidForRead = value; } + } + + public bool ValidForWrite + { + get { return isValidForWrite;} + set {isValidForWrite=value;} + } // TCB public struct TCB @@ -124,7 +167,7 @@ namespace NetStack.Runtime // send parameters public struct SNDValues { - public uint UNA; // send unacknowledges + public uint UNA; // UNAcknowledged send sequence number public uint NXT; // send seqnum that will be transmitted public uint WND; // send window public uint UP; // send urgent pointer @@ -150,7 +193,7 @@ namespace NetStack.Runtime [NotDelayed] public TcpSession(IProtocol! p) - : base(p,TxQSize,RcvQSize) + : base(p, TxQSize, RcvQSize) { sessionTCB = new TCB(); sessionTCB.SND.WND = TcpFormat.TCP_MSS; @@ -168,14 +211,16 @@ namespace NetStack.Runtime isValidForRead = true; isValidForWrite = true; - // create and initialize the init state - this.oldState = null; + // Assign the undifferentiated state (the parent of the + // specialized states) and then change the state to CLOSED. + this.oldStateEnum = TcpStateEnum.Undefined; + this.currentState = TcpState.InstanceOfUndefined(); ChangeState(TCPFSM.CLOSED); } - public new void ReInitialize(IProtocol! p) + public new void ReInitialize(IProtocol! protocol) { - base.ReInitialize(p); + base.ReInitialize(protocol); sessionTCB = new TCB(); sessionTCB.SND.WND = TcpFormat.TCP_MSS; sessionTCB.RCV.WND = TcpFormat.TCP_MSS; @@ -192,87 +237,177 @@ namespace NetStack.Runtime closedEvent.Reset(); // create and initialize the init state - this.oldState = null; + this.oldStateEnum = TcpStateEnum.Undefined; if (!IsClosed) { ChangeState(TCPFSM.CLOSED); } - DestroyTimer(ref setupTimer); - DestroyTimer(ref shutdownTimer); - DestroyTimer(ref persistTimer); + DestroyConnectTimer(); + DestroyShutdownTimer(); + DestroyPersistTimer(); retransInterval = InitialRetransInterval; } public bool IsClosed { - get { return this.stateContext == TCPFSM.CLOSED; } + get { return this.currentState == TCPFSM.CLOSED; } } - private void DestroyTimer(ref RJBlack.Timer t) + private void DestroyTimer(ref RJBlack.Timer timer, string timerName) { - if (t != null) { - Core.Instance().TheDispatcher.RemoveTimeoutCallback(t); + if (timer == null) { + DebugPrint("TCP: Ses{0,3} ({1}) No {2} Timer to Destroy.", + this.Uid, this.currentState.StateName, timerName); } - t = null; + + if (timer != null) { // BUGBUG: Lock done on "timers" in RemoveTimeoutCallback. Is there a nulling race? // TODO + bool removeWorked = + Core.Instance().TheDispatcher.RemoveTimeoutCallback(timer); + DebugPrint("TCP: Ses{0,3} ({1}) Destroy {2} Timer {3}.", + this.Uid, + this.currentState.StateName, + timerName, + (removeWorked) ? "Worked" : "Failed"); + timer = null; + } + } + + internal void DestroyConnectTimer() + { + DestroyTimer(ref connectTimer, "Connect"); + } + + internal void DestroyPersistTimer() + { + DestroyTimer(ref persistTimer, "Persist"); + } + + internal void DestroyShutdownTimer() + { + DestroyTimer(ref shutdownTimer, "Shutdown"); + } + + internal void DestroyRetransmitTimer() + { + DestroyTimer(ref retransTimer, "Retransmit"); } [ Conditional("DEBUG_TCP") ] private static void DebugPrint(string format, params object [] args) { - Core.Log("TCP: "); Core.Log(format, args); } + public void LogStateChangeContractCall(TcpSessionEventsSource.TcpSessionContractEntrypoints entrypoint) + { + TcpSessionEventsSource.EventLog.LogSessionStateChangeContractCall( + Uid, + currentState.StateEnum, + entrypoint); + } + + public void LogDataTransferContractCall(TcpSessionEventsSource.TcpSessionContractEntrypoints entrypoint) + { + TcpSessionEventsSource.EventLog.LogSessionDataTransferContractCall( + Uid, + currentState.StateEnum, + entrypoint); + } + + public void LogQueryContractCall(TcpSessionEventsSource.TcpSessionContractEntrypoints entrypoint) + { + TcpSessionEventsSource.EventLog.LogSessionQueryContractCall( + Uid, + currentState.StateEnum, + entrypoint); + } + + public void LogInfoContractCall(TcpSessionEventsSource.TcpSessionContractEntrypoints entrypoint) + { + TcpSessionEventsSource.EventLog.LogSessionInfoContractCall( + Uid, + currentState.StateEnum, + entrypoint); + } + internal void StartConnectTimer() { ulong timeout; - if (passiveSession != null) - { + if (passiveSession != null) { timeout = PassiveConnectTimeout; } - else - { - timeout = ConnectTimeout; + else { + timeout = ActiveConnectTimeout; } Dispatcher.Callback fun = new Dispatcher.Callback(OnConnectTimeout); ulong expiryTime = (ulong)DateTime.UtcNow.Ticks + (timeout * DateTime.TicksPerSecond); - setupTimer = Core.Instance().TheDispatcher.AddCallback(fun, null, expiryTime); + connectTimer = Core.Instance().TheDispatcher.AddCallback(fun, null, expiryTime); } internal NetStatus OnConnectTimeout(Dispatcher.CallbackArgs args) { // We failed to become established in time. Bail out. + TcpSessionEventsSource.EventLog.LogTimeout( + Uid, + currentState.StateEnum, + TcpTimeoutType.Connect); + Terminate(null, TcpError.Timeout); return NetStatus.Code.PROTOCOL_OK; } + internal void InitializeServerSession(uint recvSequence, uint window) + { + this.InitializeSession(recvSequence, + (uint)DateTime.UtcNow.Ticks, + window); + } + + internal void InitializeSession(uint recvSequence, + uint sendSequence, + uint window) + { + sessionTCB.RCV.IRS = recvSequence; + sessionTCB.RCV.NXT = recvSequence + 1; + sessionTCB.SND.ISS = sendSequence; + sessionTCB.SND.UNA = sendSequence; + sessionTCB.SND.NXT = sendSequence + 1; + sessionTCB.SND.NextSeq = sendSequence + 1; + sessionTCB.SND.WND = window; + sessionTCB.RCV.WND = TcpFormat.TCP_MSS; + } + // change the state of this session internal void ChangeState(TcpState! newState) { - if (stateContext != null) + lock (this.lockHolder) { - oldState = stateContext; - stateContext.OnStateLeave(this, newState); - } + assert(currentState != null); - // If we've become Established, stop the connect timer if there - // is one ticking - if (newState == TCPFSM.ESTABLISHED) - { - DestroyTimer(ref setupTimer); - } + // Log upcoming State Change + TcpSessionEventsSource.EventLog.LogSessionStateChange( + Uid, + currentState.StateEnum, + newState.StateEnum); +#if false + TcpSessionEvents.EventLog.TcpSessionStateChangeEvent( + (ushort)Uid, + (TcpSessionEvents.TcpSessionState)currentState.StateEnum, + (TcpSessionEvents.TcpSessionState)newState.StateEnum); +#endif + // Old State's Exit Processing + currentState.OnStateLeave(this, newState); - if (newState == TCPFSM.CLOSED) - { - // Stop retransmitting - DestroyTimer(ref retransTimer); - } + // Actual State Change + oldStateEnum = currentState.StateEnum; + currentState = newState; - stateContext = newState; - newState.OnStateEnter(this); + // New State's Entry Processing + currentState.OnStateEnter(this); + } } // the message is dispatched to the sessions. the sender @@ -287,17 +422,30 @@ namespace NetStack.Runtime // protocol triggered event // the object parameter will be set to IProtocol interface internal override NetStatus OnReceive(object sender, - NetPacket! pkt, - object ctx) + NetPacket! packet, + object context) { - DebugPrint("Packet received."); - if (stateContext != null) { + assert(context != null); + assert(currentState != null); + + NetStatus returnCode; + + // Log "Received Packet" Event. + TcpSessionEventsSource.EventLog.LogReceivedPacket( + Uid, + currentState.StateEnum, + ((TcpFormat.TcpHeader)context).res2_flags, + (uint) packet.Available); + + // Perform State-specific Packet Receive + // handling (and potential StateChange) under lock. + lock (this.lockHolder) + { // process it in the current state's context - DebugPrint("Packet received: State->{0}", - stateContext.GetStateName()); - return (stateContext.OnPacketReceive(this, pkt, ctx)); + returnCode = currentState.OnPacketReceive(this, packet, context); } - return NetStatus.Code.PROTOCOL_DROP_ERROR; + + return returnCode; } private void StartPersistTimer() @@ -309,7 +457,9 @@ namespace NetStack.Runtime internal void StopPersistTimer() { - DestroyTimer(ref persistTimer); + if (persistTimer != null) { + DestroyPersistTimer(); + } } internal bool InPersistState() @@ -345,19 +495,25 @@ namespace NetStack.Runtime private NetStatus OnPersistTimeout(Dispatcher.CallbackArgs timeoutArg) { // - // NOTE This is a hack. A proper TCP stack is supposed to + // NOTE: This is a hack. A proper TCP stack is supposed to // transmit a packet consisting of just one byte when probing the // remote host to see if it has reopened its receive window. // However, we prepacketize data, so we don't have that option. // Instead, we probe using full packets. // + TcpSessionEventsSource.EventLog.LogTimeout( + Uid, + currentState.StateEnum, + TcpTimeoutType.Persist); + TcpSegment seg = null; if (retransmitQ.Count > 0) { // Use the oldest unacknowledged packet to probe seg = (TcpSegment)retransmitQ[0]; - } else { + } + else { // Nothing in the retransmit queue; probe using the next // normal packet. This will transition the packet to the // retransmission queue. @@ -370,7 +526,7 @@ namespace NetStack.Runtime assert err == NetStatus.Code.PROTOCOL_OK; } - if (stateContext != TCPFSM.CLOSED) { + if (currentState != TCPFSM.CLOSED) { // rearm StartPersistTimer(); } @@ -378,14 +534,11 @@ namespace NetStack.Runtime return NetStatus.Code.PROTOCOL_OK; } - private TcpSegment GetNextPacket() - { - return GetNextPacket(false); - } - private TcpSegment GetNextPacket(bool ignoreReceiverWindow) { - if (outQueue.Count == 0) { + // No Packets if the OutQueue is Empty OR IF the Session is Closed. + // BUGBUG: Need some way to prevent Closed Sessions from retransmitting. // TODO + if ((outQueue.Count == 0) /*|| (currentState == TCPFSM.CLOSED)*/ ) { return null; } @@ -397,13 +550,13 @@ namespace NetStack.Runtime if (((TcpSegment!)outQueue[0]).retries > 0) { // Special case: the head packet is a retransmission. No special work. - return (TcpSegment)base.GetPacket(outQueue, false, 0); // non blocking + return (TcpSegment)base.GetPacket(outQueue, false, TimeSpan.Zero); // non blocking - } else { + } + else { // The head packet is *not* a retransmission. Make sure we // have room to to move it to the retransmission queue. - if (retransmitQ.Count < retransmitQ.Capacity) - { + if (retransmitQ.Count < retransmitQ.Capacity) { TcpSegment nextSegment = (TcpSegment)outQueue[0]; assert nextSegment != null; uint segSize = nextSegment.GetSegmentLength(); @@ -413,7 +566,7 @@ namespace NetStack.Runtime } // Call the base class to dequeue the packet in an orderly way - TcpSegment! seg = (TcpSegment!)base.GetPacket(outQueue, false, 0); // non blocking + TcpSegment! seg = (TcpSegment!)base.GetPacket(outQueue, false, TimeSpan.Zero); // non blocking assert seg == nextSegment; if (!seg.isAck) { @@ -432,7 +585,8 @@ namespace NetStack.Runtime RestartRetransTimer(); } - } else if (segSize == 0) { + } + else if (segSize == 0) { segSize = 1; // ACKs take up one segment number } @@ -450,21 +604,29 @@ namespace NetStack.Runtime // NB: call *after* removing or adding items to the retransmitQ internal void RestartRetransTimer() { - DestroyTimer(ref retransTimer); + DestroyRetransmitTimer(); if (retransmitQ.Count > 0) { - ulong nowTime = (ulong)DateTime.UtcNow.Ticks; // TODO: We should use a dynamically-calculated timeout interval - ulong t = nowTime + (ulong)TimeSpan.FromMilliseconds(retransInterval).Ticks; + ulong expirationTime = + (ulong)DateTime.UtcNow.Ticks + + (ulong)TimeSpan.FromMilliseconds(retransInterval).Ticks; retransTimer = Core.Instance().TheDispatcher.AddCallback( - new Dispatcher.Callback(OnRetransmitTimeout), null, t); + new Dispatcher.Callback(OnRetransmitTimeout), + null, + expirationTime); +#if false + if (currentState == TCPFSM.CLOSED) { + DestroyRetransmitTimer(); // TEMP FOR DEBUGGING - REMOVE // TODO + } +#endif } // else all data has been acknowledged } internal void FlushRetransmissions() { - DestroyTimer(ref retransTimer); + DestroyRetransmitTimer(); retransmitQ.Clear(); } @@ -483,15 +645,18 @@ namespace NetStack.Runtime srtt = measurement; rttvar = srtt / 2; newInterval = measurement * 2; - } else { + } + else { // Second or subsequent measurement. Per RFC 2988 - uint abs_srtt_meas = srtt > measurement ? srtt - measurement : measurement - srtt; + uint abs_srtt_meas = (srtt > measurement) + ? srtt - measurement : measurement - srtt; rttvar = ((rttvar * 3) / 4) + (abs_srtt_meas / 4); srtt = ((7 * srtt) / 8) + (measurement / 8); newInterval = srtt + (4 * rttvar); } - this.retransInterval = newInterval < MinimumRetransInterval ? MinimumRetransInterval : newInterval; + this.retransInterval = (newInterval < MinimumRetransInterval) + ? MinimumRetransInterval : newInterval; } // Process a remote acknowledgement of data up to the given sequence number @@ -501,28 +666,33 @@ namespace NetStack.Runtime int removed = 0; // Pop packets off the retransmitQ through the acked seqnum + TcpSegment tcpSegmentOrNull; while (retransmitQ.Count > 0) { TcpSegment! headSeg = (TcpSegment!)retransmitQ[0]; + uint headNextBytesSequenceNumber = + headSeg.seq + headSeg.GetSegmentLength(); - if (retransmitQ.Count > 1) { - TcpSegment! nextSeg = (TcpSegment!)retransmitQ[1]; - // Make sure the queue is in order - assert TCPFSM.TCPSeqLess(headSeg.seq, nextSeg.seq); + // If this head segment is still needed, break out of the loop. + if (TCPFSM.TCPSeqGreater(headNextBytesSequenceNumber, seqNum)) { + break; } - // If the head segment is fully acknowledged, pop it - if (TCPFSM.TCPSeqLEQ(headSeg.seq + headSeg.GetSegmentLength(), seqNum)) { - retransmitQ.RemoveAt(0); - removed++; + // Make sure the queue is in exact order + if (retransmitQ.Count > 1) { + TcpSegment! nextSeg = (TcpSegment!)retransmitQ[1]; + // assert TCPFSM.TCPSeqEQ(headNextBytesSequenceNumber, + // nextSeg.seq); + assert TCPFSM.TCPSeqLess(headSeg.seq, nextSeg.seq); // Old, Weak: Remove // TODO + } - // Use this ACK for RTT calculations. - // Ignore ACKs for retransmitted data. - if (headSeg.retries == 0) { - UpdateRTT(headSeg.GetRTT(nowTicks)); - } - } else { - // Out of packets to pop - break; + // Otherwise, head segment isn't needed anymore. Remove it. + retransmitQ.RemoveAt(0); + removed++; + + // Use this ACK for RTT calculations. + // Ignore ACKs for retransmitted data. + if (headSeg.retries == 0) { + UpdateRTT(headSeg.GetRTT(nowTicks)); } } @@ -540,7 +710,18 @@ namespace NetStack.Runtime TCPFSM.TCPSeqLEQ(headSeg.seq, sessionTCB.SND.UNA) && TCPFSM.TCPSeqGEQ(headSeg.seq + headSeg.GetSegmentLength(), sessionTCB.SND.UNA); - assert hasFirstUnacked; + if (hasFirstUnacked == false) { + Core.Log("TCP: Ses{0,3} ({1}) RETRANSMIT QUEUE SEQUENCE " + + "NUMBER FAILURE; FirstSegmentStart,End = {2}, {3};" + + "Unacknowledged = {4}.", + this.Uid, + this.currentState.StateName, + headSeg.seq, + headSeg.seq + headSeg.GetSegmentLength() - 1, + sessionTCB.SND.UNA); + } + + //assert hasFirstUnacked; // BUGBUG: Must reactivate before Sprint End // TODO } // We may have paused transmission so as to not overrun the receiver. @@ -556,15 +737,15 @@ namespace NetStack.Runtime // and put it in the retransmit queue until we get an ack. // (we only do it for data segments including SYN which counts for one) // if a timer expired before ack, we retransmit it until we give up. - override internal NetPacket GetPacket(ArrayList! q, bool toBlock, int timeout) + override internal NetPacket GetPacket(ArrayList! q, bool toBlock, TimeSpan timeout) { // We only concern ourselves with the remote host's receive window in states // where we are transmitting bool shouldRespectRemoteWindow = - (stateContext != TCPFSM.CLOSED) && - (stateContext != TCPFSM.LISTEN) && - (stateContext != TCPFSM.SYN_SENT) && - (stateContext != TCPFSM.SYN_RECVD); + (currentState != TCPFSM.CLOSED) && + (currentState != TCPFSM.LISTEN) && + (currentState != TCPFSM.SYN_SENT) && + (currentState != TCPFSM.SYN_RECVD); // There needs to be at least one packet-worth of space in the send-window for us // to be sure we won't overrun the remote host. @@ -575,9 +756,10 @@ namespace NetStack.Runtime StartPersistTimer(); } // else already in the persist state - } else { + } + else { StopPersistTimer(); - return GetNextPacket(); + return GetNextPacket(false); } return null; @@ -599,6 +781,11 @@ namespace NetStack.Runtime // Handler for TCP timeouts internal NetStatus OnRetransmitTimeout(Dispatcher.CallbackArgs timeoutArg) { + TcpSessionEventsSource.EventLog.LogTimeout( + Uid, + currentState.StateEnum, + TcpTimeoutType.Retransmit); + if (!InPersistState()) { // Retransmit the oldest unacknowledged packet assert retransmitQ.Count > 0; @@ -614,7 +801,7 @@ namespace NetStack.Runtime // INVARIANT: the head of the retransmit queue must contain // the first unacked seqnum if (retransmitQ.Count > 0) { - // TODO make this an assert + // TODO: make this an assert TcpSegment! headSeg = (TcpSegment!)retransmitQ[0]; bool hasFirstUnacked = @@ -625,7 +812,8 @@ namespace NetStack.Runtime } PriorityEnqueuePacket(outQueue, oldest); - } else { + } + else { // we're in the persist state and retransmissions are suspended. } @@ -746,8 +934,7 @@ namespace NetStack.Runtime // to several smaller segments. private int InternalWrite(CopyDataDelegate! dataCopier, int dataSize) { - if (!ValidForWrite) - { + if (!ValidForWrite) { return -1; } @@ -762,8 +949,7 @@ namespace NetStack.Runtime const int baseFrameSize = EthernetFormat.Size + IPFormat.Size + TcpFormat.Size; - while (mssCount!=0) - { + while (mssCount != 0) { // create a TCP segment without options // handle the data first @@ -780,6 +966,15 @@ namespace NetStack.Runtime TcpSegment seg = new TcpSegment(pktData, this, segSequence, false); + + // Send to Event Log. + TcpSessionEventsSource.EventLog.LogSendingPacket( + Uid, + (currentState == null) ? TcpStateEnum.Undefined + : currentState.StateEnum, + /*((TcpFormat.TcpHeader)context).res2_flags,*/ 0, // BUGBUG: Get Transmit Flags. // TODO + (uint) seg.GetSegmentLength()); + // the next segment sequence segSequence += TcpFormat.TCP_MSS; readIndex += TcpFormat.TCP_MSS; @@ -787,8 +982,7 @@ namespace NetStack.Runtime mssCount--; } - if (mssResidue!=0) - { + if (mssResidue != 0) { byte[] pktData = new byte[baseFrameSize + mssResidue]; dataCopier(pktData, readIndex, baseFrameSize, (int)mssResidue); @@ -804,6 +998,15 @@ namespace NetStack.Runtime TcpSegment seg = new TcpSegment(pktData, this, segSequence, false); + + // Send to Event Log. + TcpSessionEventsSource.EventLog.LogSendingPacket( + Uid, + (currentState == null) ? TcpStateEnum.Undefined + : currentState.StateEnum, + /*((TcpFormat.TcpHeader)context).res2_flags,*/ 0, // BUGBUG: Get Transmit Flags. // TODO + (uint) seg.GetSegmentLength()); + segSequence += mssResidue; base.PutPacket(outQueue,seg,true); } @@ -815,22 +1018,33 @@ namespace NetStack.Runtime public bool BindLocalEndPoint(IPv4 address, ushort port) { - haveBound = true; - SetRemoteEndPoint(IPv4.Broadcast, 0); - return SetLocalEndPoint(address, port); + return BindLocalEndPointInternal(address, port); + } + + public bool BindLocalEndPointInternal(IPv4 address, ushort port) + { + lock (this.lockHolder) { + haveBound = true; + SetRemoteEndPoint(IPv4.Broadcast, 0); + return SetLocalEndPoint(address, port); + } } // the method is used to make a session active (i.e., active open) // TBC: manage local ports automatically - // we currently more restrictive regarding user - // interaction (can't change passive to active etc) + // we're currently more restrictive regarding user + // interaction (can't change passive to active etc.) public bool Connect(IPv4 dstIP, ushort dstPort, out TcpError error) { + DebugPrint("TCP: Ses{0,3} ({1}) Connect: {2:x8}/{3}", + Uid, currentState.StateName, dstIP, dstPort); - DebugPrint("Connect: {0:x8}/{1}", dstIP, dstPort); - - if (stateContext!=TCPFSM.CLOSED) { - DebugPrint("Connect: Failed FSM Closed", dstIP, dstPort); + if (currentState != TCPFSM.CLOSED) { + DebugPrint("TCP: Ses{0,3} ({1}) Connect: Failed because " + + "session already connected (not in '{2}' state)", + Uid, + currentState.StateName, + TCPFSM.CLOSED.StateName); error = TcpError.AlreadyConnected; return false; } @@ -840,8 +1054,7 @@ namespace NetStack.Runtime // Set the local endpoint to "don't care" if the user // hasn't called BindLocalEndPoint() previously - if (!haveBound) - { + if (!haveBound) { SetLocalEndPoint(IPv4.Any, 0); } @@ -855,33 +1068,40 @@ namespace NetStack.Runtime // change this session state to SYNSENT. // a SYN message will be sent to the destination ChangeState(TCPFSM.SYN_SENT); + // provide a default error this.connectError = TcpError.Unknown; - // block the user until the session is ready setupCompleteEvent.WaitOne(); + setupCompleteEvent.Reset(); - DebugPrint("Connect: SetupCompleteEvent signalled"); - - if (stateContext != TCPFSM.ESTABLISHED) - { + // Check Result; Optional Log; and Return true/false. + if (currentState != TCPFSM.ESTABLISHED) { // The connect failed. error = this.connectError; - DebugPrint("Connect: failed {0}", error); + DebugPrint("TCP: Ses{0,3} ({1}) Connect: SetupCompleteEvent " + + "signalled; Result = failed {2}", + Uid, currentState.StateName, error); return false; } - else - { + else { // The connection is up and running properly. error = TcpError.Unknown; - DebugPrint("Connect: success"); + DebugPrint("TCP: Ses{0,3} ({1}) Connect: SetupCompleteEvent " + + "signalled; Result = success", + Uid, currentState.StateName); return true; } } private NetStatus OnShutdownTimedout(Dispatcher.CallbackArgs args) { + TcpSessionEventsSource.EventLog.LogTimeout( + Uid, + currentState.StateEnum, + TcpTimeoutType.Shutdown); + // No more Mr. Nice Guy Abort(TcpError.Timeout); return NetStatus.Code.PROTOCOL_OK; @@ -890,8 +1110,9 @@ namespace NetStack.Runtime // close the session override public bool Close() { - // Signal that we're done sending; this will start the polite - // shutdown process. + // Signal that sending is complete; this will start the polite + // shutdown process. Changes state to FinWait1 or LastAck. + // Can block. DoneSending(); // Start a timer to make sure we don't wait for the shutdown @@ -900,20 +1121,16 @@ namespace NetStack.Runtime Dispatcher.Callback fun = new Dispatcher.Callback(OnShutdownTimedout); ulong expiryTime = (ulong)DateTime.UtcNow.Ticks + (PoliteShutdownTimeout * DateTime.TicksPerSecond); - shutdownTimer = Core.Instance().TheDispatcher.AddCallback(fun, null, expiryTime); + shutdownTimer = + Core.Instance().TheDispatcher.AddCallback(fun, null, expiryTime); - // Wait while we complete the shutdown, drain the outbound - // queue, etc. + // Wait while we complete the shutdown, + // drain the outbound queue, etc. closedEvent.WaitOne(); + closedEvent.Reset(); - // Quash the timer in case it's still ticking - if (Core.Instance().TheDispatcher.RemoveTimeoutCallback(shutdownTimer)) { - Tracing.Log(Tracing.Debug, "Tcp quash timer successful."); - } - else { - Tracing.Log(Tracing.Debug, "Tcp quash timer already expired."); - } - shutdownTimer = null; + // Remove the Shutdown Timer. + DestroyShutdownTimer(); // After a Close() completes, pending data isn't available anymore! DrainQueue(inQueue); @@ -938,13 +1155,11 @@ namespace NetStack.Runtime { StopPersistTimer(); - if (stateContext == TCPFSM.CLOSED) - { + if (currentState == TCPFSM.CLOSED) { // Signal anyone waiting, for good measure. setupCompleteEvent.Set(); } - else - { + else { // This will set the setup-complete event as a side effect TCPFSM.HandleTerminateSession(this, finalPacket, error); } @@ -953,12 +1168,11 @@ namespace NetStack.Runtime // passively open a session public bool Listen(int backlog) { - if (stateContext != TCPFSM.CLOSED) + if (currentState != TCPFSM.CLOSED) return false; // User must have previously bound - if (!haveBound) - { + if (!haveBound) { return false; } @@ -967,20 +1181,26 @@ namespace NetStack.Runtime return true; } - // start accepting new clients - // will block until a new client connection is established + /// + /// Block until a new connection is available, + /// accept it, and return a new TcpSession. + /// + /// + /// The TcpSession (this) must be in the "Listen" state. + /// public TcpSession Accept() { - if (stateContext != TCPFSM.LISTEN) + if (currentState != TCPFSM.LISTEN) return null; - TcpSession tcpSession=null; + TcpSession tcpSession = null; // block the user until a session is available - lock (acceptSessionMonitor) - { - while (acceptedSessions.Count==0) + lock (acceptSessionMonitor) { + while (acceptedSessions.Count == 0) { Monitor.Wait(acceptSessionMonitor); + } + tcpSession = (TcpSession)acceptedSessions[0]; acceptedSessions.RemoveAt(0); } @@ -991,14 +1211,11 @@ namespace NetStack.Runtime // Returns false if our queue is full internal bool AddAcceptedSession(TcpSession newSession) { - lock (acceptSessionMonitor) - { - if (AcceptQueueIsFull()) - { + lock (acceptSessionMonitor) { + if (AcceptQueueIsFull()) { return false; } - else - { + else { acceptedSessions.Add(newSession); Monitor.PulseAll(acceptSessionMonitor); return true; @@ -1009,8 +1226,7 @@ namespace NetStack.Runtime // Indicate whether there are queued sessions waiting to be Accept()ed public int GetNumWaitingListenSessions() { - lock (acceptSessionMonitor) - { + lock (acceptSessionMonitor) { return acceptedSessions.Count; } } @@ -1021,36 +1237,36 @@ namespace NetStack.Runtime return GetNumWaitingListenSessions() >= maxAcceptedSessions; } - // Indicate that we're done sending + /// + /// Client informs TcpSession that no more data will be sent. + /// public void DoneSending() { - if (!isValidForWrite) - { - // Nothing to do - return; - } + lock (this.lockHolder) { + if (!isValidForWrite) { + // Nothing to do + return; + } - isValidForWrite = false; + isValidForWrite = false; - if (stateContext == TCPFSM.ESTABLISHED) - { - // Transition to FIN_WAIT1 since we are the first - // side to close - TCPFSM.SendFin(this, true); // can block - ChangeState(TCPFSM.FIN_WAIT1); - } - else if (stateContext == TCPFSM.CLOSE_WAIT) - { - // The other side closed first; transition to - // LAST_ACK instead - TCPFSM.SendFin(this, true); // blocks - ChangeState(TCPFSM.LAST_ACK); - } - else - { - // We're in some transitory setup or teardown - // state; just abort the connection. - Abort(TcpError.Closed); + switch (currentState.StateEnum) { + case TcpStateEnum.Established: + // This side first to Close, goto FinWait1 + TCPFSM.SendFin(this, true); // can block + ChangeState(TCPFSM.FIN_WAIT1); + break; + case TcpStateEnum.CloseWait: + // Other side first to Close, goto LastAck + TCPFSM.SendFin(this, true); // can block + ChangeState(TCPFSM.LAST_ACK); + break; + default: + // We're in some transitory setup or teardown + // state; just abort the connection. + Abort(TcpError.Closed); + break; + } } } @@ -1079,16 +1295,16 @@ namespace NetStack.Runtime sendTime=0; } - // the isAck indicate that this is an ack segment - // (we never ack an ack segments without data) + // the isAck indicates that this is an ack segment + // (we never ack an ack segment without data) [NotDelayed] - public TcpSegment(byte[]! buffer, TcpSession! owner, uint seqNum, bool isAck) : base(buffer) + public TcpSegment(byte[]! buffer, TcpSession! tcpSession, uint seqNum, bool isAck) : base(buffer) { seq=seqNum; retries=0; this.isAck=isAck; sendTime=0; - this.SessionContext = owner; + this.SessionContext = tcpSession; } public TcpSession owner @@ -1113,4 +1329,3 @@ namespace NetStack.Runtime } } } - diff --git a/base/Services/NetStack/Runtime/TcpSessionEventLog.cs b/base/Services/NetStack/Runtime/TcpSessionEventLog.cs new file mode 100644 index 0000000..74bbe7c --- /dev/null +++ b/base/Services/NetStack/Runtime/TcpSessionEventLog.cs @@ -0,0 +1,862 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +// +// author: Glenn Peterson +// + +using System; +using System.Runtime.CompilerServices; +using System.Text; + +#if !SINGULARITY +using System.Net; +#endif + +using Microsoft.Contracts; +using Microsoft.Singularity; +using Microsoft.Singularity.Eventing; + +using Microsoft.Singularity.NetStack.Events; + +namespace NetStack.Runtime +{ + /// + /// A class-only attribute to denote an Event Logging Source. + /// + [AttributeUsage(AttributeTargets.Class)] + public sealed class EventLogSourceAttribute : Attribute + { + private string id = String.Empty; + private bool fieldRegistrationErrorFatal = true; + + /// + /// Sole constructor requiring the source Identifier as the only parameter. + /// + /// + /// A string identifier for the particular event logging source. + /// + public EventLogSourceAttribute(string id) + { + this.id = id; + } + + /// + /// A string identifier for the particular event logging source. + /// + public string Id + { + get { return this.id; } + set { this.id = value; } + } + + /// + /// A Boolean that dictates the handling of a field registration errors. + /// If true (the default), any field registration error causes + /// the entire registration, and therefore the Create method, to fail. + /// + public bool FieldRegistationErrorFatal + { + get { return this.fieldRegistrationErrorFatal; } + set { this.fieldRegistrationErrorFatal = value; } + } + } + + /// + /// A method-only attribute denoting an Event Logging Method. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class EventLogMethodAttribute : System.Attribute + { + private string id; + private bool heapAllowed = true; + private bool debugPrint = false; + private string formatString = String.Empty; + + /// + /// Sole constructor requiring the method (event type) + /// identifier as the only parameter. + /// + /// + /// A string identifier for the particular event logging method + /// which becomes the identifier for the event type. + /// + public EventLogMethodAttribute(string id) + { + this.id = id; + } + + /// + /// A string identifier for the particular event logging method + /// which becomes the identifier for the event type. + /// + public string Id + { + get { return this.id; } + set { this.id = value; } + } + + /// + /// If true, the generated code can perform heap allocations. NOT SURE IF NEEDED. // TODO + /// + public bool HeapAllowed + { + get { return this.heapAllowed; } + set { this.heapAllowed = value; } + } + + /// + /// If true, the generated code will include a test + /// for the DebugPrint bit for this event type. + /// + public bool DebugPrint + { + get { return this.debugPrint; } + set { this.debugPrint = value; } + } + + /// + /// A 'String.Format' format control string used when: + /// + /// 1. The event is transferred to the debugger, and + /// + /// + /// 2. The event is displayed with the script tool in a mixed stream. + /// + /// + public string FormatString + { + get { return this.formatString; } + set { this.formatString = value; } + } + } + + /// + /// A parameter-only attribute describing the treatment + /// of a single parameter to an event logging method. + /// + /// + /// No Id is present as a Property as the parameter's + /// name is the natural Id. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class EventLogMethodParameterAttribute : Attribute + { + private string title = String.Empty; + private uint ordinalInStruct; + private string typeInStruct = String.Empty; + + /// + /// A string used in lieu of the parameter name in columnar reports. + /// + /// If not supplied, the parameter name is used. + public string Title + { + get { return this.title; } + set { this.title = value; } + } + + /// + /// Specifies the zero-based ordinal of this parameter in the structure + /// used to add this event to the event stream. + /// + /// + /// If not supplied, the parameter order in the method call is used. + /// + public uint OrdinalInStruct + { + get { return this.ordinalInStruct; } + set { this.ordinalInStruct = value; } + } + + /// + /// Specifies the type of the data element to use in the structure used + /// to add this event to the event stream. + /// + /// + /// If not supplied, the type of the parameter in the method call is used. + /// + public string TypeInStruct // BUGBUG: Should not be a string, should be a value-type. // TODO + { + get { return this.typeInStruct; } + set { this.typeInStruct = value; } + } + } + + /// + /// Eventing and Tracing support for the TcpSession class. + /// + [CLSCompliant(false)] + [EventLogSource("TcpSession")] + public class TcpSessionEventsSource : EventSource // TODO: Base class constructor should take storageOptions and Size - then the static almost goes away. + { + public const uint LogStateChanges = 0x00010000; + public const uint LogReceivedPackets = 0x00020000; + public const uint LogSendingPackets = 0x00040000; + public const uint LogTimeouts = 0x00080000; + + public const uint LogStateChangeContractCalls = 0x00100000; + public const uint LogDataTransferContractCalls = 0x00200000; + public const uint LogQueryContractCalls = 0x00400000; + public const uint LogInfoContractCalls = 0x00800000; + + // Event Log size in bytes. + // Overhead is 24 bytes and TcpSession uses <= 8. + private const uint EventLogBufferSize = 32u * 2048u; + + // Global Instance + public static readonly TcpSessionEventsSource EventLog; + + // Enums to represent TCP Contract Entrypoints + public enum TcpSessionContractEntrypoints + { + Unknown = 0, + + Connect, Bind, Listen, Accept, + DoneSending, DoneReceiving, Abort, Close, + BindLocalEndPoint, + + Read, PollRead, Write, + + IsSessionAvailable, IsDataAvailable, + + GetLocalAddress, GetLocalPort, + GetRemoteAddress, GetRemotePort, + + ChannelClosed, + } + + // Strings matching enum immediately above. + internal static readonly string[] ContractCallNames = new string[] + { + "Unknown", + + "Connect", "Bind", "Listen", "Accept", + "DoneSending", "DoneReceiving", "Abort", "Close", + "BindLocalEndPoint", + + "Read", "PollRead", "Write", + + "IsSessionAvailable", "IsDataAvailable", + + "GetLocalAddress", "GetLocalPort", + "GetRemoteAddress", "GetRemotePort", + + "ChannelClosed", + }; + + private UIntPtr stateChangeTypeHandle; + private UIntPtr receivedPacketTypeHandle; + private UIntPtr sendingPacketTypeHandle; + private UIntPtr timeoutTypeHandle; + + private UIntPtr contractCallTypeHandle; + + private uint debugOptions; + + private struct StateChangeEntry + { + public ushort sessionId; + public byte fromState; + public byte toState; + + public StateChangeEntry(uint sessionId, TcpStateEnum fromState, TcpStateEnum toState) + { + this.sessionId = (ushort)sessionId; + this.fromState = (byte)fromState; + this.toState = (byte)toState; + } + } + + private struct ReceivedPacketEntry + { + public ushort sessionId; + public byte sessionState; + public byte packetFlags; + public ushort packetLength; + + public ReceivedPacketEntry(uint sessionId, TcpStateEnum sessionState, uint packetFlags, uint packetLength) + { + this.sessionId = (ushort)sessionId; + this.sessionState = (byte)sessionState; + this.packetFlags = (byte)packetFlags; + this.packetLength = (ushort)packetLength; + } + } + + private struct SendingPacketEntry + { + public ushort sessionId; + public byte sessionState; + public byte packetFlags; + public ushort packetLength; + + public SendingPacketEntry(uint sessionId, TcpStateEnum sessionState, uint packetFlags, uint packetLength) + { + this.sessionId = (ushort)sessionId; + this.sessionState = (byte)sessionState; + this.packetFlags = (byte)packetFlags; + this.packetLength = (ushort)packetLength; + } + } + + private struct TimeoutEntry + { + public ushort sessionId; + public byte sessionState; + public byte timeoutType; + + public TimeoutEntry(uint sessionId, TcpStateEnum sessionState, TcpSession.TcpTimeoutType timeoutType) + { + this.sessionId = (ushort)sessionId; + this.sessionState = (byte)sessionState; + this.timeoutType = (byte)timeoutType; + } + } + + private struct ContractCallEntry + { + public ushort sessionId; + public byte sessionState; + public byte callId; + + public ContractCallEntry(uint sessionId, TcpStateEnum sessionState, + TcpSessionContractEntrypoints callId) + { + this.sessionId = (ushort)sessionId; + this.sessionState = (byte)sessionState; + this.callId = (byte)callId; + } + } + + /// + /// The Static Constructor. It creates the event session for this SIP. + /// + static TcpSessionEventsSource() + { + TcpSessionEventsSource.EventLog = TcpSessionEventsSource.Create(// BUGBUG AM: Why isn't this a normal constructor???? + "TcpSessions", + TcpSessionEventsSource.EventLogBufferSize, + QualityOfService.RecyclableEvents, + EventSource.ENABLE_ALL_MASK & ~( // Buffering Mask // BUGBUG: Switch to Positive Logic when system and user masks separated? // TODO + //Incl TcpSessionEventsSource.LogStateChanges | + /*Omit*/TcpSessionEventsSource.LogSendingPackets | + //Incl TcpSessionEventsSource.LogReceivedPackets | + //Incl TcpSessionEventsSource.LogTimeouts | + //Incl TcpSessionEventsSource.LogStateChangeContractCalls | + /*Omit*/TcpSessionEventsSource.LogDataTransferContractCalls | + /*Omit*/TcpSessionEventsSource.LogQueryContractCalls | + /*Omit*/TcpSessionEventsSource.LogInfoContractCalls | + 0u), + EventSource.ENABLE_ALL_MASK & ~( // Debugging Mask // BUGBUG: Same // TODO + /*Omit*/TcpSessionEventsSource.LogStateChanges | + /*Omit*/TcpSessionEventsSource.LogSendingPackets | + /*Omit*/TcpSessionEventsSource.LogReceivedPackets | + /*Omit*/TcpSessionEventsSource.LogTimeouts | + /*Omit*/TcpSessionEventsSource.LogStateChangeContractCalls | + /*Omit*/TcpSessionEventsSource.LogDataTransferContractCalls | + /*Omit*/TcpSessionEventsSource.LogQueryContractCalls | + /*Omit*/TcpSessionEventsSource.LogInfoContractCalls | + 0u)); + +#if false + TcpSessionEvents.EventLog = + TcpSessionEvents.Create("TcpSession", + 4096, + QualityOfService.RecyclableEvents, + EventSource.ENABLE_ALL_MASK | + EventSource.CAPTURE_DEBUG_PRINT); +#endif + } + + /// + /// Create and Register a TcpSessionEventsSource. + /// + /// + /// This should be a Generic when supported. Or use more of the base class (Static?) // BUGBUG AM (later?) + /// + public static TcpSessionEventsSource Create(string sourceName, + uint size, + uint storageOptions, // BUGBUG AM: [Flags] enum + uint sourceFlags, + uint debugFlags) + { + TcpSessionEventsSource tcpSessionEventsSource = null; + + EventingStorage eventStorage = + EventingStorage.CreateLocalStorage(storageOptions, size); + if (eventStorage == null) + { + DebugStub.WriteLine("Failure to obtain storage for TcpSessionEvents"); + DebugStub.Break(); + } + else + { + tcpSessionEventsSource = + new TcpSessionEventsSource(sourceName, eventStorage, sourceFlags, debugFlags); + if (tcpSessionEventsSource == null) + { + // TODO: Is EventStorage returned here and below if failures occur? + DebugStub.WriteLine("Failure to construct TcpSessionEventsSource instance."); + DebugStub.Break(); + } + else + { + bool registerSucceeded = tcpSessionEventsSource.Register(); + if (registerSucceeded == false) + { + tcpSessionEventsSource = null; + DebugStub.WriteLine("Failure to register TcpSessionEventsSource."); + DebugStub.Break(); + } + } + } + + return tcpSessionEventsSource; + } + + /// Fully parameterized and sole constructor. + /// BUGBUG AM: parameterization: have base EP that takes storageoptions and size to elim dup code (generated or not) + public TcpSessionEventsSource(string sourceName, EventingStorage storage, uint controlFlags, uint debugOptions) + : base(sourceName, storage, controlFlags) + { + this.debugOptions = debugOptions; + } + + ~TcpSessionEventsSource() + { + // BUGBUG AM: Free EventingStorage (using base somehow? please!) + } + + //[NoHeapAllocation] + [EventLogMethod("TcpSessionStateChange", + HeapAllowed = true, + DebugPrint = false, + FormatString = "TCP: Ses{0,3} ({1}) State Change to {2}.")] + public bool LogSessionStateChange( + [EventLogMethodParameter(Title = "Session", OrdinalInStruct = 0, TypeInStruct = "ushort")] + uint sessionId, + [EventLogMethodParameter(Title = "FrState", OrdinalInStruct = 1, TypeInStruct = "byte")] + TcpStateEnum fromState, + [EventLogMethodParameter(Title = "ToState", OrdinalInStruct = 2, TypeInStruct = "byte")] + TcpStateEnum toState) // BUGBUG AM: signature poor way to differentiate various entries. Is there a way to have two two "int" loggers with different types and member labels? + { + if ((debugOptions & LogStateChanges /* To Debugger */) != 0) + { + Core.Log("TCP: Ses{0,3} ({1}) State Change to {2}.", + sessionId, + TcpState.TcpStateNames[(uint)fromState], + TcpState.TcpStateNames[(uint)toState]); + } + + if ((ControlFlags & LogStateChanges) != 0) + { + StateChangeEntry stateChangeEntry = + new StateChangeEntry(sessionId, fromState, toState); + + unsafe + { // BUGBUG AM: Just a comment - this causes my otherwise safe compile to be changed to unsafe. This is bad. + return (LogEntry(ControlFlags, + stateChangeTypeHandle, + (byte*)&stateChangeEntry, + sizeof(StateChangeEntry)) != 0); + } + } + + return false; + } + + //[NoHeapAllocation] + [EventLogMethod("TcpPacketReceived", + HeapAllowed = true, + DebugPrint = false, + FormatString = "TCP: Ses{0,3} ({1}) Packet ({2}) Len{3,4} received")] + public bool LogReceivedPacket( + [EventLogMethodParameter(Title = "Session Id", OrdinalInStruct = 0, TypeInStruct = "ushort")] + uint sessionId, + [EventLogMethodParameter(Title = "Session State", OrdinalInStruct = 1, TypeInStruct = "byte")] + TcpStateEnum sessionState, + [EventLogMethodParameter(Title = "Packet Flags", OrdinalInStruct = 2, TypeInStruct = "byte")] + uint packetFlags, + [EventLogMethodParameter(Title = "Packet Length", OrdinalInStruct = 3, TypeInStruct = "ushort")] + uint packetLength) + { + if ((debugOptions & LogReceivedPackets /* To Debugger */) != 0) + { + string flagsText = FlagsToText(packetFlags); // TODO Move then Qualify + Core.Log("TCP: Ses{0,3} ({1}) Packet ({2}) Len{3,4} received", + sessionId, + TcpState.TcpStateNames[(uint)sessionState], + flagsText, + packetLength); + } + + if ((ControlFlags & LogReceivedPackets) != 0) + { + ReceivedPacketEntry receivedPacketEntry = + new ReceivedPacketEntry(sessionId, sessionState, packetFlags, packetLength); + + unsafe + { + return (LogEntry(ControlFlags, + receivedPacketTypeHandle, + (byte*)&receivedPacketEntry, + sizeof(ReceivedPacketEntry)) != 0); + } + } + + return false; + } + + //[NoHeapAllocation] + [EventLogMethod("TcpPacketSending", + HeapAllowed = true, + DebugPrint = false, + FormatString = "TCP: Ses{0,3} ({1}) Packet ({2}) Len{3,4} being sent")] + public bool LogSendingPacket( + [EventLogMethodParameter(Title = "Session Id", OrdinalInStruct = 0, TypeInStruct = "ushort")] + uint sessionId, + [EventLogMethodParameter(Title = "Session State", OrdinalInStruct = 1, TypeInStruct = "byte")] + TcpStateEnum sessionState, + [EventLogMethodParameter(Title = "Packet Flags", OrdinalInStruct = 2, TypeInStruct = "byte")] + uint packetFlags, + [EventLogMethodParameter(Title = "Packet Length", OrdinalInStruct = 3, TypeInStruct = "ushort")] + uint packetLength) + { + if ((debugOptions & LogSendingPackets /* To Debugger */) != 0) + { + string flagsText = FlagsToText(packetFlags); // TODO Move then Qualify + Core.Log("TCP: Ses{0,3} ({1}) Packet ({2}) Len{3,4} being sent", + sessionId, + TcpState.TcpStateNames[(uint)sessionState], + flagsText, + packetLength); + } + + if ((ControlFlags & LogSendingPackets) != 0) + { + SendingPacketEntry sendingPacketEntry = + new SendingPacketEntry(sessionId, sessionState, packetFlags, packetLength); + + unsafe + { + return (LogEntry(ControlFlags, + sendingPacketTypeHandle, + (byte*)&sendingPacketEntry, + sizeof(SendingPacketEntry)) != 0); + } + } + + return false; + } + + //[NoHeapAllocation] + [EventLogMethod("TcpTimeout", + HeapAllowed = true, + DebugPrint = false, + FormatString = "TCP: Ses{0,3} ({1}) Timeout of type '{2}' occurred")] + public bool LogTimeout( + [EventLogMethodParameter(Title = "Session Id", OrdinalInStruct = 0, TypeInStruct = "ushort")] + uint sessionId, + [EventLogMethodParameter(Title = "Session State", OrdinalInStruct = 1, TypeInStruct = "byte")] + TcpStateEnum sessionState, + [EventLogMethodParameter(Title = "Timeout Type", OrdinalInStruct = 2, TypeInStruct = "byte")] + TcpSession.TcpTimeoutType timeoutType) + { + if ((debugOptions & LogTimeouts /* To Debugger */) != 0) + { + Core.Log("TCP: Ses{0,3} ({1}) Timeout of type '{2}' occurred", + sessionId, + TcpState.TcpStateNames[(uint)sessionState], + TcpSession.TcpTimeoutTypeNames[(uint)timeoutType]); + } + + if ((ControlFlags & LogTimeouts) != 0) + { + TimeoutEntry timeoutEntry = + new TimeoutEntry(sessionId, sessionState, timeoutType); + + unsafe + { + return (LogEntry(ControlFlags, + timeoutTypeHandle, + (byte*)&timeoutEntry, + sizeof(TimeoutEntry)) != 0); + } + } + + return false; + } + + //[NoHeapAllocation] + [EventLogMethod("TcpSessionStateChangeContractCall", + HeapAllowed = true, + DebugPrint = true, + FormatString = "TCP: Ses{0,3} ({1}) Contract EP '{2}' called.")] + public bool LogSessionStateChangeContractCall( + [EventLogMethodParameter(Title = "Session", OrdinalInStruct = 0, TypeInStruct = "ushort")] + uint sessionId, + [EventLogMethodParameter(Title = "SessionState", OrdinalInStruct = 1, TypeInStruct = "byte")] + TcpStateEnum sessionState, + [EventLogMethodParameter(Title = "CallId", OrdinalInStruct = 2, TypeInStruct = "byte")] + TcpSessionContractEntrypoints contractEntrypoint) + { + return this.LogSessionContractCall(sessionId, sessionState, + contractEntrypoint, + LogStateChangeContractCalls); + } + + //[NoHeapAllocation] + [EventLogMethod("TcpSessionDataTransferContractCall", + HeapAllowed = true, + DebugPrint = true, + FormatString = "TCP: Ses{0,3} ({1}) Contract EP '{2}' called.")] + internal bool LogSessionDataTransferContractCall( + [EventLogMethodParameter(Title = "Session", OrdinalInStruct = 0, TypeInStruct = "ushort")] + uint sessionId, + [EventLogMethodParameter(Title = "SessionState", OrdinalInStruct = 1, TypeInStruct = "byte")] + TcpStateEnum sessionState, + [EventLogMethodParameter(Title = "CallId", OrdinalInStruct = 2, TypeInStruct = "byte")] + TcpSessionContractEntrypoints contractEntrypoint) + { + return this.LogSessionContractCall(sessionId, sessionState, + contractEntrypoint, + LogDataTransferContractCalls); + } + + //[NoHeapAllocation] + [EventLogMethod("TcpSessionQueryContractCall", + HeapAllowed = true, + DebugPrint = true, + FormatString = "TCP: Ses{0,3} ({1}) Contract EP '{2}' called.")] + internal bool LogSessionQueryContractCall( + [EventLogMethodParameter(Title = "Session", OrdinalInStruct = 0, TypeInStruct = "ushort")] + uint sessionId, + [EventLogMethodParameter(Title = "SessionState", OrdinalInStruct = 1, TypeInStruct = "byte")] + TcpStateEnum sessionState, + [EventLogMethodParameter(Title = "CallId", OrdinalInStruct = 2, TypeInStruct = "byte")] + TcpSessionContractEntrypoints contractEntrypoint) + { + return this.LogSessionContractCall(sessionId, sessionState, + contractEntrypoint, + LogQueryContractCalls); + } + + //[NoHeapAllocation] + [EventLogMethod("TcpSessionInfoContractCall", + HeapAllowed = true, + DebugPrint = true, + FormatString = "TCP: Ses{0,3} ({1}) Contract EP '{2}' called.")] + internal bool LogSessionInfoContractCall( + [EventLogMethodParameter(Title = "Session", OrdinalInStruct = 0, TypeInStruct = "ushort")] + uint sessionId, + [EventLogMethodParameter(Title = "SessionState", OrdinalInStruct = 1, TypeInStruct = "byte")] + TcpStateEnum sessionState, + [EventLogMethodParameter(Title = "CallId", OrdinalInStruct = 2, TypeInStruct = "byte")] + TcpSessionContractEntrypoints contractEntrypoint) + { + return this.LogSessionContractCall(sessionId, sessionState, + contractEntrypoint, + LogInfoContractCalls); + } + + //[NoHeapAllocation] + [EventLogMethod("TcpSessionContractCall", + HeapAllowed = true, + DebugPrint = true, + FormatString = "TCP: Ses{0,3} ({1}) Contract EP '{2}' called.")] + private bool LogSessionContractCall( + [EventLogMethodParameter(Title = "Session", OrdinalInStruct = 0, TypeInStruct = "ushort")] + uint sessionId, + [EventLogMethodParameter(Title = "SessionState", OrdinalInStruct = 1, TypeInStruct = "byte")] + TcpStateEnum sessionState, + [EventLogMethodParameter(Title = "CallName", OrdinalInStruct = 2, TypeInStruct = "byte")] + TcpSessionContractEntrypoints contractEntrypoint, + uint flag) + { + if ((debugOptions & flag /* To Debugger */) != 0) + { + Core.Log("TCP: Ses{0,3} ({1}) Contract EP '{2}' called.", + sessionId, + TcpState.TcpStateNames[(uint)sessionState], + TcpSessionEventsSource.ContractCallNames[(uint)contractEntrypoint]); + } + + if ((ControlFlags & flag) != 0) + { + ContractCallEntry contractCallEntry = + new ContractCallEntry(sessionId, sessionState, contractEntrypoint); + + unsafe + { + return (LogEntry(ControlFlags, + contractCallTypeHandle, + (byte*)&contractCallEntry, + sizeof(ContractCallEntry)) != 0); + } + } + + return false; + } + + public override bool Register() + { + // Check assumptions for remainder of method. + if ((base.Register() == false) || (HostController == null)) + { // BUGBUG AM: base.Register() should check if HostController is null, not all callers + DebugStub.WriteLine("TcpSessionEventsSource: Improper Registration Environment."); + return false; + } + + // TcpSessionStateChange event registration + if (HostController.RegisterEvent("TcpSessionStateChange", + "TCP: Ses{0,3} ({1}) State Change to {2}.", + ref stateChangeTypeHandle)) + { // BUGBUG AM: Aliases possible with different structs. Should pass in Struct and Reflect to get name of runtime type, field names, ... + if ((!HostController.RegisterEventField(stateChangeTypeHandle, "SessionId", 0, DataType.__uint16)) || + (!HostController.RegisterEventField(stateChangeTypeHandle, "FromState", 0, DataType.__uint8)) || + (!HostController.RegisterEventField(stateChangeTypeHandle, "ToState", 0, DataType.__uint8))) + { + stateChangeTypeHandle = 0; + } + } + + // TcpSessionReceivedPacket event registration + if (HostController.RegisterEvent("TcpSessionReceivedPacket", + "TCP: Ses{0,3} ({1}) Packet ({2}) Len{3,4} received", + ref receivedPacketTypeHandle)) + { + if ((!HostController.RegisterEventField(receivedPacketTypeHandle, "SessionId", 0, DataType.__uint16)) || + (!HostController.RegisterEventField(receivedPacketTypeHandle, "SessionState", 0, DataType.__uint8)) || + (!HostController.RegisterEventField(receivedPacketTypeHandle, "PacketFlags", 0, DataType.__uint8)) || + (!HostController.RegisterEventField(receivedPacketTypeHandle, "PacketLength", 0, DataType.__uint16))) + { + receivedPacketTypeHandle = 0; + } + } + + // TcpSessionReceivedPacket event registration + if (HostController.RegisterEvent("TcpSessionSendingPacket", + "TCP: Ses{0,3} ({1}) Packet ({2}) Len{3,4} being sent", + ref sendingPacketTypeHandle)) + { + if ((!HostController.RegisterEventField(sendingPacketTypeHandle, "SessionId", 0, DataType.__uint16)) || + (!HostController.RegisterEventField(sendingPacketTypeHandle, "SessionState", 0, DataType.__uint8)) || + (!HostController.RegisterEventField(sendingPacketTypeHandle, "PacketFlags", 0, DataType.__uint8)) || + (!HostController.RegisterEventField(sendingPacketTypeHandle, "PacketLength", 0, DataType.__uint16))) + { + sendingPacketTypeHandle = 0; + } + } + + // TcpSessionTimeout event registration + if (HostController.RegisterEvent("TcpSessionTimeout", + "TCP: Ses{0,3} ({1}) Timeout of type '{2}' occurred", + ref timeoutTypeHandle)) + { + if ((!HostController.RegisterEventField(timeoutTypeHandle, "SessionId", 0, DataType.__uint16)) || + (!HostController.RegisterEventField(timeoutTypeHandle, "SessionState", 0, DataType.__uint8)) || + (!HostController.RegisterEventField(timeoutTypeHandle, "TimeoutType", 0, DataType.__uint8))) + { + timeoutTypeHandle = 0; + } + } + + // TcpSessionContractCall event registration + if (HostController.RegisterEvent("TcpSessionContractCall", + "TCP: Ses{0,3} ({1}) Contract EP '{2}' called.", + ref contractCallTypeHandle)) + { + if ((!HostController.RegisterEventField(contractCallTypeHandle, "SessionId", 0, DataType.__uint16)) || + (!HostController.RegisterEventField(contractCallTypeHandle, "SessionState", 0, DataType.__uint8)) || + (!HostController.RegisterEventField(contractCallTypeHandle, "ContractCall", 0, DataType.__uint8))) + { + contractCallTypeHandle = 0; + } + } + + // Make sure all handles were either there before or were created here. + if ((stateChangeTypeHandle == 0) || (contractCallTypeHandle == 0) || + (sendingPacketTypeHandle == 0) || (sendingPacketTypeHandle == 0)) + { + // Construct an error message + StringBuilder errorString = new StringBuilder(); + String conjunction = "TcpSessionEventsSource: Registration Failed for "; + + // State Change + if (stateChangeTypeHandle == 0) + { + errorString.Append(conjunction); + errorString.Append("StateChangeEventType"); + conjunction = ", "; + } + + // Received Packet + if (receivedPacketTypeHandle == 0) + { + errorString.Append(conjunction); + errorString.Append("ReceivedPacketEventType"); + conjunction = ", "; + } + + // Sending Packet + if (sendingPacketTypeHandle == 0) + { + errorString.Append(conjunction); + errorString.Append("SendingPacketEventType"); + conjunction = ", "; + } + + // Timeout + if (timeoutTypeHandle == 0) + { + errorString.Append(conjunction); + errorString.Append("TimeoutEventType"); + conjunction = ", "; + } + + // Contract Call + if (contractCallTypeHandle == 0) + { + errorString.Append(conjunction); + errorString.Append("ContractCallEventType"); + conjunction = ", "; + } + + // Complete the error message and write it to the debugger. + errorString.Append("."); + errorString.Append(Environment.NewLine); + DebugStub.Write(errorString.ToString()); + + // Report the failure. + return false; + } + + // If this is reached then everything worked so report success. + return true; + } + + [Pure] + [Inline] + private static string FlagsToText(uint flags) + { + string[] loBits = new string[] { + "....", "F...", ".S..", "FS..", + "..R.", "F.R.", ".SR.", "FSR.", + "...P", "F..P", ".S.P", "FS.P", + "..RP", "F.RP", ".SRP", "FSRP", + }; + string[] hiBits = new string[] { + "....", "A...", ".I..", "AI..", + "..E.", "A.E.", ".IE.", "AIE.", + "...C", "A..C", ".I.C", "AI.C", + "..EC", "A.EC", ".IEC", "AIEC", + }; + + return loBits[(flags >> 0) & 0x0F] + hiBits[(flags >> 4) & 0x0F]; + } + } +} diff --git a/base/Services/NetStack/Runtime/TcpSessionPool.cs b/base/Services/NetStack/Runtime/TcpSessionPool.cs index 1d444a6..ff7fe74 100644 --- a/base/Services/NetStack/Runtime/TcpSessionPool.cs +++ b/base/Services/NetStack/Runtime/TcpSessionPool.cs @@ -1,52 +1,83 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; using System.Collections; namespace NetStack.Runtime { - public class TcpSessionPool + /// + /// Static class maintains a pool of Tcp Sessions kept as a FILO (Stack). + /// + public static class TcpSessionPool { + // Session Recycle to GC ratio as a Rational Approximation. + // BUGBUG: When 992 and 1024 fixed, set to UInt64.Max or remove + private const long SessionRecycleToGCRANumerator = 1ul; + private const long SessionRecycleToGCRADenominator = 256ul; + private static long rationalApproximationCurrentValue = + TcpSessionPool.SessionRecycleToGCRADenominator / -2; + static TcpModule tcpModule; - static ArrayList tcpSessions = new ArrayList(); + static Stack tcpSessions = new Stack(); - static long allocations; - static long getCalls; - static long recycleCalls; + static ulong getCalls = 0; + static ulong allocations = 0; + static ulong recycleCalls = 0; - public static void SetTcpModule(TcpModule m) + public static void SetTcpModule(TcpModule tcpModule) { - tcpModule = m; + TcpSessionPool.tcpModule = tcpModule; } public static TcpSession! Get() { - lock (tcpSessions) { - getCalls++; - foreach (TcpSession! ts in tcpSessions) { - if (ts.IsClosed) { - Core.Instance().DeregisterSession(ts.Protocol, ts); - tcpSessions.Remove(ts); - return tcpModule.ReInitializeSession(ts); - } + TcpSession! tcpSession; + lock (TcpSessionPool.tcpSessions) { + TcpSessionPool.getCalls++; + + if(tcpSessions.Count <= 0) { + TcpSessionPool.allocations++; + tcpSession = new TcpSession(tcpModule); + } + else { + tcpSession = (TcpSession!) tcpSessions.Pop(); + assert(tcpSession.IsClosed); + Core.Instance().DeregisterSession(tcpSession.Protocol, tcpSession); + tcpSession = + TcpSessionPool.tcpModule.ReInitializeSession(tcpSession); } - allocations++; } - - return new TcpSession(tcpModule); + + return tcpSession; } - public static void Recycle(TcpSession! ts) + public static void Recycle(TcpSession! tcpSession) { - lock (tcpSessions) { - recycleCalls++; - tcpSessions.Add(ts); + assert(tcpSession.IsClosed); + + // Under Exclusion, count call and add to (free) list. + lock (TcpSessionPool.tcpSessions) { + TcpSessionPool.recycleCalls++; + tcpSessions.Push(tcpSession); + // TODO: Occasionally (1/1024 times?) free a session to + // lower usage from highwater mark to high average at the + // cost of some additional Alloc/Free cycles. + } + + // Run the CG every N/D session recycles to avoid Bugs 992 and 1024. + // Can be done after every session if searching for memory leaks. + TcpSessionPool.rationalApproximationCurrentValue += + TcpSessionPool.SessionRecycleToGCRANumerator; + while (TcpSessionPool.rationalApproximationCurrentValue > 0) { + TcpSessionPool.rationalApproximationCurrentValue -= + TcpSessionPool.SessionRecycleToGCRADenominator; + GC.Collect(); // Free memory and fill finalizer Q. + GC.WaitForPendingFinalizers(); // Dispose non-managed stuff + GC.Collect(); // Free memory release by finalizing } } } diff --git a/base/Services/NetStack/Runtime/TestDhcpClient.cs b/base/Services/NetStack/Runtime/TestDhcpClient.cs index c66728b..b86cf22 100644 --- a/base/Services/NetStack/Runtime/TestDhcpClient.cs +++ b/base/Services/NetStack/Runtime/TestDhcpClient.cs @@ -108,8 +108,7 @@ namespace NetStack.Runtime.TestDhcpClient /// private bool ValidHeaders(NetPacket packet) { - if (packet.Length < NonDhcpHeaderSize) - { + if (packet.Length < NonDhcpHeaderSize) { Console.WriteLine("Packet too short"); return false; } @@ -123,8 +122,7 @@ namespace NetStack.Runtime.TestDhcpClient EthernetFormat.Read(packet, out originMac, out targetMac, out protocol); - if (originMac != adapter.HardwareAddress) - { + if (originMac != adapter.HardwareAddress) { Console.WriteLine("Bad origin mac: {0}", originMac); return false; } @@ -134,8 +132,7 @@ namespace NetStack.Runtime.TestDhcpClient Console.WriteLine("Bad target mac: {0}", targetMac); return false; } - else if (protocol != EthernetFormat.Protocol.IP) - { + else if (protocol != EthernetFormat.Protocol.IP) { Console.WriteLine("Bad encapsulated protocol: {0}", protocol); return false; } @@ -144,13 +141,11 @@ namespace NetStack.Runtime.TestDhcpClient // Check IP Header // IPFormat.IPHeader ipHeader; - if (IPFormat.ReadIPHeader(packet, out ipHeader) == false) - { + if (IPFormat.ReadIPHeader(packet, out ipHeader) == false) { Console.WriteLine("Failed to read IP Header"); return false; } - else if (ipHeader.Protocol != IPFormat.Protocol.UDP) - { + else if (ipHeader.Protocol != IPFormat.Protocol.UDP) { Console.WriteLine("Bad encapsulated IP protocol: {0}", ipHeader.Protocol); return false; @@ -177,19 +172,16 @@ namespace NetStack.Runtime.TestDhcpClient // Check UDP Header // UdpFormat.UdpHeader udpHeader; - if (UdpFormat.ReadUdpHeader(packet, out udpHeader) == false) - { + if (UdpFormat.ReadUdpHeader(packet, out udpHeader) == false) { Console.WriteLine("Failed to read UDP Header"); return false; } - else if (udpHeader.srcPort != 68) - { + else if (udpHeader.srcPort != 68) { Console.WriteLine("Bad UDP source port: {0}", udpHeader.srcPort); return false; } - else if (udpHeader.dstPort != 67) - { + else if (udpHeader.dstPort != 67) { Console.WriteLine("Bad UDP destination port: {0}", udpHeader.dstPort); return false; @@ -203,21 +195,18 @@ namespace NetStack.Runtime.TestDhcpClient packet.Reset(); Console.WriteLine("Received {0} bytes", packet.Length); - if (ValidHeaders(packet) == false) - { + if (ValidHeaders(packet) == false) { SetState(ServerState.Failed); return; } - try - { + try { DhcpFormat dhcp = DhcpFormat.Parse(packet); SortedList options = dhcp.GetOptions(); DhcpByteOption message = options[DhcpMessageType.OptionCode] as DhcpByteOption; - if (message == null) - { + if (message == null) { Console.WriteLine("MessageType option not found"); SetState(ServerState.Failed); return; @@ -226,8 +215,7 @@ namespace NetStack.Runtime.TestDhcpClient DhcpFormat.MessageType messageType = (DhcpFormat.MessageType) message.Value; - if (DhcpFormat.IsRequestMessage(messageType) == false) - { + if (DhcpFormat.IsRequestMessage(messageType) == false) { Console.WriteLine("Inappropriate message type: {0}", message.Value); SetState(ServerState.Failed); @@ -238,26 +226,22 @@ namespace NetStack.Runtime.TestDhcpClient DhcpFormat.MessageType action = expectActions[expectIndex,1]; expectIndex++; - if (messageType != expected) - { + if (messageType != expected) { Console.WriteLine("Unexpected message type: {0} != {1}", messageType, expected); SetState(ServerState.Failed); return; } - if (DhcpFormat.IsResponseMessage(action)) - { + if (DhcpFormat.IsResponseMessage(action)) { Respond(dhcp, action); } - if (expectIndex == expectActions.Length / expectActions.Rank) - { + if (expectIndex == expectActions.Length / expectActions.Rank) { SetState(ServerState.Finished); } } - catch (Exception e) - { + catch (Exception e) { Console.WriteLine("Bad Dhcp packet: {0}", e); SetState(ServerState.Failed); } @@ -271,8 +255,7 @@ namespace NetStack.Runtime.TestDhcpClient response.AddOption(DhcpRenewalTime.Create(RenewalTime)); response.AddOption(DhcpRebindingTime.Create(RebindingTime)); - foreach (IDhcpOption option in requestOptions.Values) - { + foreach (IDhcpOption option in requestOptions.Values) { byte optionCode = option.OptionCode; Console.WriteLine("({0}) {1}", optionCode, @@ -286,10 +269,8 @@ namespace NetStack.Runtime.TestDhcpClient if (parameters == null) return; - foreach (byte parameter in parameters.Values) - { - switch (parameter) - { + foreach (byte parameter in parameters.Values) { + switch (parameter) { case DhcpSubnetMask.OptionCode: response.AddOption(DhcpSubnetMask.Create(NetMask)); break; @@ -319,8 +300,7 @@ namespace NetStack.Runtime.TestDhcpClient EthernetAddress hwAddress = request.GetHardwareAddress(); response.SetHardwareAddress(hwAddress); - switch (m) - { + switch (m) { case DhcpFormat.MessageType.Offer: response.NextServerIPAddress = ServerAddress; response.AddOption( @@ -381,8 +361,7 @@ namespace NetStack.Runtime.TestDhcpClient EthernetFormat.Protocol.IP); NetPacket np = new NetPacket(packet); - if (adapter.ReceivePacket(np) == false) - { + if (adapter.ReceivePacket(np) == false) { Console.WriteLine("Failed to send packet"); SetState(ServerState.Failed); } @@ -405,8 +384,7 @@ namespace NetStack.Runtime.TestDhcpClient DhcpClient dc = new DhcpClient(adapter); dc.Start(); - while (fakeDhcpServer.State == FakeDhcpServer.ServerState.Running) - { + while (fakeDhcpServer.State == FakeDhcpServer.ServerState.Running) { Thread.Sleep(TimeSpan.FromSeconds(1)); } dc.Stop(); @@ -416,8 +394,7 @@ namespace NetStack.Runtime.TestDhcpClient StaticConfiguration.Stop(); - if (fakeDhcpServer.State == FakeDhcpServer.ServerState.Failed) - { + if (fakeDhcpServer.State == FakeDhcpServer.ServerState.Failed) { return 1; } return 0; diff --git a/base/Services/NetStack/Runtime/TestTCP.cs b/base/Services/NetStack/Runtime/TestTCP.cs index 3911845..fa0be9b 100644 --- a/base/Services/NetStack/Runtime/TestTCP.cs +++ b/base/Services/NetStack/Runtime/TestTCP.cs @@ -35,8 +35,7 @@ namespace NetStack.Runtime.TestPing this.receivedPackets = 0; this.listener = (TcpSession) m.CreateSession(); - if (this.listener.Listen(localAddress, localPort) == false) - { + if (this.listener.Listen(localAddress, localPort) == false) { Console.WriteLine("TcpSession.Listen() failed."); } @@ -50,8 +49,7 @@ namespace NetStack.Runtime.TestPing { TcpSession client = listener.Accept(); Console.WriteLine("Accepted Session {0}", client); - while (receivedPackets++ != maxReceivePackets) - { + while (receivedPackets++ != maxReceivePackets) { byte[] recvData = client.ReadData(); System.Diagnostics.Debug.Assert(recvData.Length == 4); // @@ -99,8 +97,7 @@ namespace NetStack.Runtime.TestPing private void WorkLoop() { - while (sentPackets++ != maxSendPackets) - { + while (sentPackets++ != maxSendPackets) { int n = ByteOrder.HostToBigEndian(sentPackets); int r = session.WriteData(BitConverter.GetBytes(n)); Console.WriteLine("Send {0}", sentPackets); @@ -141,8 +138,7 @@ namespace NetStack.Runtime.TestPing Receiver[] receivers = new Receiver[nInstances]; Sender[] senders = new Sender[nInstances]; - for (int i = 0; i < nInstances; i++) - { + for (int i = 0; i < nInstances; i++) { ushort rPort = (ushort)(basePort + 2 * i); ushort sPort = (ushort)(basePort + 2 * i + 1); receivers[i] = new Receiver(ifAddress, rPort, maxPackets); @@ -151,12 +147,10 @@ namespace NetStack.Runtime.TestPing } bool done = false; - while (done == false) - { + while (done == false) { Thread.CurrentThread.Join(TimeSpan.FromSeconds(1)); done = true; - for (int i = 0; i < nInstances; i++) - { + for (int i = 0; i < nInstances; i++) { if (receivers[i].ThreadState != ThreadState.Stopped || senders[i].ThreadState != ThreadState.Stopped) { diff --git a/base/Services/NetStack/Runtime/TestUDP.cs b/base/Services/NetStack/Runtime/TestUDP.cs index 4cf93ec..4b586b0 100644 --- a/base/Services/NetStack/Runtime/TestUDP.cs +++ b/base/Services/NetStack/Runtime/TestUDP.cs @@ -45,8 +45,7 @@ namespace NetStack.Runtime.TestPing private void WorkLoop() { - while (receivedPackets++ != maxReceivePackets) - { + while (receivedPackets++ != maxReceivePackets) { byte[] recvData = session.ReadData(); System.Diagnostics.Debug.Assert(recvData.Length == 4); // @@ -93,8 +92,7 @@ namespace NetStack.Runtime.TestPing private void WorkLoop() { - while (sentPackets++ != maxSendPackets) - { + while (sentPackets++ != maxSendPackets) { int n = ByteOrder.HostToBigEndian(sentPackets); int r = session.WriteData(BitConverter.GetBytes(n)); // Console.WriteLine("Send {0}", sentPackets); @@ -136,8 +134,7 @@ namespace NetStack.Runtime.TestPing Receiver[] receivers = new Receiver[nInstances]; Sender[] senders = new Sender[nInstances]; - for (int i = 0; i < nInstances; i++) - { + for (int i = 0; i < nInstances; i++) { ushort rPort = (ushort)(basePort + 2 * i); ushort sPort = (ushort)(basePort + 2 * i + 1); receivers[i] = @@ -146,12 +143,10 @@ namespace NetStack.Runtime.TestPing } bool done = false; - while (done == false) - { + while (done == false) { Thread.CurrentThread.Join(TimeSpan.FromSeconds(1)); done = true; - for (int i = 0; i < nInstances; i++) - { + for (int i = 0; i < nInstances; i++) { if (receivers[i].ThreadState != ThreadState.Stopped || senders[i].ThreadState != ThreadState.Stopped) { diff --git a/base/Services/NetStack/Runtime/TimeOut.cs b/base/Services/NetStack/Runtime/TimeOut.cs index 4fdbe2f..f210cb2 100644 --- a/base/Services/NetStack/Runtime/TimeOut.cs +++ b/base/Services/NetStack/Runtime/TimeOut.cs @@ -1,15 +1,12 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- -/** - * Microsoft Research, Cambridge - * author: Yaron Weinsberg, Richard Black - */ +/// +// Microsoft Research, Cambridge +// namespace NetStack { diff --git a/base/Services/NetStack/Runtime/Timers.cs b/base/Services/NetStack/Runtime/Timers.cs index ed4577a..cabdebc 100644 --- a/base/Services/NetStack/Runtime/Timers.cs +++ b/base/Services/NetStack/Runtime/Timers.cs @@ -74,8 +74,7 @@ namespace RJBlack Timer prev = null; Timer here = wheel[click]; - while (here != null && here.time < t.time) - { + while (here != null && here.time < t.time) { prev = here; here = here.next; } @@ -96,17 +95,15 @@ namespace RJBlack uint click = (uint)((t.time / resolution) % clicks); - if (wheel[click] == t) - { + if (wheel[click] == t) { wheel[click] = t.next; - if (t.next!=null) + if (t.next != null) t.next.prev = null; } - else - { + else { t.prev.next = t.next; - if (t.next!=null) + if (t.next != null) t.next.prev = t.prev; } @@ -146,7 +143,7 @@ namespace RJBlack i = (i+1) % clicks; - } while(i != click); + } while (i != click); return lowest; } @@ -154,8 +151,7 @@ namespace RJBlack public Timer Advance() { Timer t = GetSoonest(); - if (t != null) - { + if (t != null) { Remove(t); now = t.time; } @@ -167,8 +163,7 @@ namespace RJBlack public Timer Advance(ulong time) { Timer t = GetSoonest(); - if ((t == null) || (time < t.time)) - { + if ((t == null) || (time < t.time)) { now = time; return null; } diff --git a/base/Services/NetStack/Runtime/UDPModule.cs b/base/Services/NetStack/Runtime/UDPModule.cs index 035231c..8dfe148 100644 --- a/base/Services/NetStack/Runtime/UDPModule.cs +++ b/base/Services/NetStack/Runtime/UDPModule.cs @@ -108,8 +108,7 @@ namespace NetStack.Runtime DebugPrint("Received UDP packet!\n"); UdpFormat.UdpHeader udpHeader = new UdpFormat.UdpHeader(); - if (UdpFormat.ReadUdpHeader(packet, out udpHeader) == false) - { + if (UdpFormat.ReadUdpHeader(packet, out udpHeader) == false) { DebugPrint("UDP session received unreadable packet.\n"); return NetStatus.Code.PROTOCOL_DROP_ERROR; } diff --git a/base/Services/NetStack/Runtime/UDPSession.cs b/base/Services/NetStack/Runtime/UDPSession.cs index 831e266..9a1b532 100644 --- a/base/Services/NetStack/Runtime/UDPSession.cs +++ b/base/Services/NetStack/Runtime/UDPSession.cs @@ -72,14 +72,12 @@ namespace NetStack.Runtime ushort remotePort) { BindRemoteEndPoint(remoteAddress, remotePort); - if (BindLocalEndPoint(newLocalAddress, newLocalPort) == true) - { + if (BindLocalEndPoint(newLocalAddress, newLocalPort) == true) { String msg = String.Format("Successful bind to {0} (bound to {1})\n", newLocalPort, LocalPort); Tracing.Log(Tracing.Debug,msg); } - else - { + else { String msg = String.Format("Failed to bind to {0} (bound to {1})\n", newLocalPort, LocalPort); Tracing.Log(Tracing.Debug,msg); @@ -90,8 +88,7 @@ namespace NetStack.Runtime NetPacket! packet, object context) { - if (this.PutPacket(this.inQueue, packet, false) == true) - { + if (this.PutPacket(this.inQueue, packet, false) == true) { // Packet is queued for user. Needs to be held onto. return NetStatus.Code.PROTOCOL_PROCESSING; } @@ -112,7 +109,7 @@ namespace NetStack.Runtime } NetPacket netPacket = DequeuePacket(inQueue, - blocking ? -1 /* forever */: 0 /* don't sleep */); + blocking ? TimeSpan.Infinite /* forever */: TimeSpan.Zero /* don't sleep */); if (netPacket != null) { byte [] userData = netPacket.ToUser(); Core.Instance().TheDemux.TakeFreePacket(netPacket); @@ -124,8 +121,7 @@ namespace NetStack.Runtime override public int WriteData(byte[]! data) { - if (this.RemoteAddress == IPv4.Any || this.RemotePort == 0) - { + if (this.RemoteAddress == IPv4.Any || this.RemotePort == 0) { Core.Log("Attempting to send UDP data to an invalid endpoint: " + "{0}/{1}\n", this.RemoteAddress, this.RemotePort); String msg = String.Format("Attempting to send UDP data to an invalid endpoint: {0}/{1}\n", this.RemoteAddress, this.RemotePort); @@ -141,8 +137,7 @@ namespace NetStack.Runtime ushort remotePort, byte[]! data) { - if (!IsSessionValidForUserWrite()) - { + if (!IsSessionValidForUserWrite()) { DebugPrint("WriteDataTo on closed UDP session"); return -1; } @@ -160,8 +155,7 @@ namespace NetStack.Runtime data, 0, data.Length); NetPacket netPacket = new NetPacket(packet); - if (base.PutPacket(outQueue, netPacket, blocking) == false) - { + if (base.PutPacket(outQueue, netPacket, blocking) == false) { Core.Log("Failed to enqueue packet for {0}/{1}", remoteAddress, remotePort); String msg = String.Format ("Failed to enqueue packet for {0}/{1}", @@ -234,8 +228,7 @@ namespace NetStack.Runtime public override bool Close() { - if (closed == false) - { + if (closed == false) { DebugPrint("Closing Session"); this.Dispose(); closed = true; diff --git a/base/Services/NetStack/Runtime/XmlConfiguration.cs b/base/Services/NetStack/Runtime/XmlConfiguration.cs index 3d1aab6..c523303 100644 --- a/base/Services/NetStack/Runtime/XmlConfiguration.cs +++ b/base/Services/NetStack/Runtime/XmlConfiguration.cs @@ -1,15 +1,12 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- -/** - * Microsoft Research, Cambridge - * author: Yaron Weinsberg, Richard Black - */ +/// +// Microsoft Research, Cambridge +// using NetStack.Common; using System; @@ -42,8 +39,7 @@ namespace NetStack.Runtime public XmlConfiguration(string configFile) { this.configFile = configFile; - if (System.IO.File.Exists(configFile) == false) - { + if (System.IO.File.Exists(configFile) == false) { Core.Panic("Can't locate configuration file, Exit!"); } } @@ -57,16 +53,13 @@ namespace NetStack.Runtime AppDomain.CurrentDomain.FriendlyName; string configFile1 = AppDomain.CurrentDomain.BaseDirectory + @"config.xml"; string configFile2 = appFullPath.Substring(0,appFullPath.Length-4) + @".xml"; - if (System.IO.File.Exists(configFile1)) - { + if (System.IO.File.Exists(configFile1)) { configFile = configFile1; } - else if (System.IO.File.Exists(configFile2)) - { + else if (System.IO.File.Exists(configFile2)) { configFile = configFile2; } - else - { + else { Core.Panic("Can't locate configuration file, Exit!"); } } @@ -100,10 +93,8 @@ namespace NetStack.Runtime string factoryTypeName = typeName + "Factory"; Assembly assembly = Assembly.LoadFrom(driversPath); Type[] types = assembly.GetTypes(); - foreach (Type t in types) - { - if (t.IsClass && t.Name.Equals(typeName)) - { + foreach (Type t in types) { + if (t.IsClass && t.Name.Equals(typeName)) { factory = (IAdapterFactory)Activator.CreateInstance(t, null); break; } @@ -114,8 +105,7 @@ namespace NetStack.Runtime Core.Instance().RegisterAdapter(adapter, fwQueue); } } - catch (Exception e) - { + catch (Exception e) { adapter = null; Console.Out.WriteLine(e.Message); Environment.Exit(1); @@ -123,8 +113,7 @@ namespace NetStack.Runtime IPModule ipModule = Core.Instance().GetProtocolByName("IP") as IPModule; HostConfiguration hostConfig = ipModule.HostConfiguration; - for (int i = 0; ; i++) - { + for (int i = 0;; i++) { string ipTag = String.Format("ip{0}", i); string maskTag = String.Format("mask{0}", i); string gatewayTag = String.Format("gateway{0}", i); @@ -136,8 +125,7 @@ namespace NetStack.Runtime IPv4.AllOnes); IPv4 gateway = ProtocolParams.LookupIPv4(args, gatewayTag, IPv4.Zero); - if (address == IPv4.AllOnes || netmask == IPv4.AllOnes) - { + if (address == IPv4.AllOnes || netmask == IPv4.AllOnes) { break; } hostConfig.Bindings.Add(adapter, @@ -172,8 +160,7 @@ namespace NetStack.Runtime // by default it is in the Runtime assembly string protocolType = args["type"]; - try - { + try { IProtocol protocol = (IProtocol)Activator.CreateInstance(Type.GetType(protocolType)); if (protocol == null) @@ -182,8 +169,7 @@ namespace NetStack.Runtime // initialize the protocol protocol.Initialize(args); } - catch (Exception e) - { + catch (Exception e) { Console.Out.WriteLine(String.Format("Can't load protocol class: {0}",protocolType)); Console.Out.WriteLine(e.Message); return false; @@ -197,8 +183,7 @@ namespace NetStack.Runtime { ConfigElement root = null; root = ConfigFileParser.parseFile(configFile); - if (root!=null && toPrint) - { + if (root != null && toPrint) { System.Text.StringBuilder tree = new System.Text.StringBuilder(); ConfigFileParser.printConfigurationTree(root,tree,0); System.Console.WriteLine(tree); @@ -218,12 +203,9 @@ namespace NetStack.Runtime // we assume that the XML is valid! // if we have children, and we have... - if (config.ChildElements.Count > 0) - { - foreach(ConfigElement element in config.ChildElements) - { - switch (element.TagName) - { + if (config.ChildElements.Count > 0) { + foreach (ConfigElement element in config.ChildElements) { + switch (element.TagName) { case "creator": break; case "date": @@ -255,8 +237,7 @@ namespace NetStack.Runtime // the args include the name and type! ProtocolParams args = new ProtocolParams();; - foreach(ConfigElement prot in element.ChildElements) - { + foreach (ConfigElement prot in element.ChildElements) { args.Clear(); foreach (string attr in prot.Attributes.Keys) args.Add(attr,prot.Attribute(attr)); @@ -270,8 +251,7 @@ namespace NetStack.Runtime case "adapters": // handle the adapters ProtocolParams adArgs = new ProtocolParams();; - foreach(ConfigElement ad in element.ChildElements) - { + foreach (ConfigElement ad in element.ChildElements) { adArgs.Clear(); foreach (string attr in ad.Attributes.Keys) adArgs.Add(attr,ad.Attribute(attr)); diff --git a/base/Services/RamDisk/ClientManager/RamDiskClientManager.csproj b/base/Services/RamDisk/ClientManager/RamDiskClientManager.csproj new file mode 100644 index 0000000..bf7303b --- /dev/null +++ b/base/Services/RamDisk/ClientManager/RamDiskClientManager.csproj @@ -0,0 +1,36 @@ + + + + + + + Exe + RamDiskService + true + Demand + + + + + + + + + + + + + + + + + + + + diff --git a/base/Services/RamDisk/ClientManager/RamDiskClientManager.sg b/base/Services/RamDisk/ClientManager/RamDiskClientManager.sg new file mode 100644 index 0000000..320d38b --- /dev/null +++ b/base/Services/RamDisk/ClientManager/RamDiskClientManager.sg @@ -0,0 +1,421 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: RamDisk/ClientManager/RamDiskClientManager.sg +// +// Note: +// + +using Microsoft.SingSharp; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.ServiceManager; +using Microsoft.Singularity.Services.RamDisk.Contracts; + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.IO; +using System.Text; +using FileSystem.Utils; + +using DirectoryErrorCode = Microsoft.Singularity.Directory.ErrorCode; + +[assembly: Microsoft.Singularity.Security.ApplicationPublisherAttribute("singularity.microsoft.com")] +[assembly: Microsoft.Singularity.Security.AssertPrivilegeAttribute("$register-privilege.localhost")] + +namespace Microsoft.Singularity.Services.RamDisk.ClientManager +{ + [Category("Service")] + internal class ServiceParameters + { + [CustomEndpoint] + public readonly TRef ControlEndpointRef; + + [CustomEndpoint] + public readonly TRef EventEndpointRef; + + reflective private ServiceParameters(); + } + + public class ClientManager + { + private static SortedList! clientsByDiskName = new SortedList(); + + private class RamDiskErrorException : Exception + { + private RamDiskContractErrorCode errorCode; + + internal RamDiskErrorException(RamDiskContractErrorCode errorCode) { + this.errorCode = errorCode; + } + + internal RamDiskContractErrorCode ErrorCode { + get { + return errorCode; + } + } + } + + internal class Client + { + TRef tref; + string! diskName; + ulong sizeBytes; + + internal Client( + [Claims] RamDiskClientContract.Imp! imp, + string! diskName, + ulong sizeBytes + ) + { + this.tref = new TRef(imp); + this.diskName = diskName; + this.sizeBytes = sizeBytes; + } + + internal RamDiskClientContract.Imp! Acquire() + { + return tref.Acquire(); + } + + internal void Release([Claims] RamDiskClientContract.Imp! imp) + { + tref.Release(imp); + } + + internal string! DiskName { get { return this.diskName; } } + internal ulong SizeBytes { get { return this.sizeBytes; } } + } + + internal static void + AddClient(Client! newClient) + { + clientsByDiskName.Add(newClient.DiskName, newClient); + } + + internal static Client GetClientByDiskName(string! diskName) + { + if (clientsByDiskName.Contains(diskName)) { + return (Client)clientsByDiskName[diskName]; + } + else { + return null; + } + } + + internal static void DeleteClient(Client! closedClient) + { + clientsByDiskName.Remove(closedClient.DiskName); + } + + internal static bool ClientIsClosed(Client! client) + { + RamDiskClientContract.Imp:Running! clientImp = client.Acquire(); + bool closed = false; + + switch receive { + case clientImp.ChannelClosed(): + closed = true; + break; + case timeout: + break; + } + + client.Release(clientImp); + return closed; + } + + private static RamDiskContractErrorCode DoCreate(ulong sizeBytes, out string! outDiskName) + { + outDiskName = ""; + + string! diskName; + ServiceProviderContract.Exp! serviceExp; + try { + GetNextDiskPath(out diskName, out serviceExp); + } + catch (RamDiskErrorException ex) { + Dbg("FAILED to get next disk path."); + return ex.ErrorCode; + } + + RamDiskClientContract.Imp! clientImp; + RamDiskClientContract.Exp! clientExp; + RamDiskClientContract.NewChannel(out clientImp, out clientExp); + + string[] args = { "RamDisk" }; + Process process; + try { + process = new Process(args); + } + catch (Exception ex) { + Dbg("FAILED to create worker process: " + ex.Message); + return RamDiskContractErrorCode.InternalError; + } + try { + process.SetStartupEndpoint(0, serviceExp); + process.SetStartupEndpoint(1, clientExp); + process.Start(); + } + catch (Exception ex) { + Dbg("FAILED to start worker process: " + ex.Message); + process.Dispose(true); + return RamDiskContractErrorCode.InternalError; + } + + clientImp.SendCreate(Bitter.FromString2(diskName), sizeBytes); + switch receive { + case clientImp.Success(): + AddClient(new Client(clientImp, diskName, sizeBytes)); + outDiskName = diskName; + return RamDiskContractErrorCode.NoError; + + case clientImp.ChannelClosed(): + Dbg("FAILED to create ramdisk. Worker process closed channel."); + delete clientImp; + return RamDiskContractErrorCode.InternalError; + + case clientImp.Fail(RamDiskContractErrorCode reason): + Dbg("FAILED to create ramdisk. Worker process sent 'Fail' message, error = " + reason); + delete clientImp; + return reason; + } + } + + private static void + DoDestroy( + RamDiskControlContract.Exp:Ready! controller, + [Claims] char[]! in ExHeap diskNameHeap, + bool force + ) + { + string! diskName = PathUtils.ToPath(Bitter.ToString2(diskNameHeap)); + delete diskNameHeap; + + Client client = GetClientByDiskName(diskName); + if (client == null) { + controller.SendFail(RamDiskContractErrorCode.DoesNotExist); + return; + } + else if (ClientIsClosed(client)) { + controller.SendSuccess(); + return; + } + + RamDiskClientContract.Imp! clientImp = client.Acquire(); + clientImp.SendDestroy(force); + switch receive { + case clientImp.Success(): + client.Release(clientImp); + DeleteClient(client); + + try { + DeregisterDevice(diskName); + } + catch (RamDiskErrorException ex) { + controller.SendFail(ex.ErrorCode); + return; + } + + controller.SendSuccess(); + break; + + case clientImp.Fail(reason): + controller.SendFail(reason); + client.Release(clientImp); + break; + + case clientImp.ChannelClosed(): + controller.SendFail(RamDiskContractErrorCode.InternalError); + client.Release(clientImp); + DeleteClient(client); + break; + } + } + + private static void ServiceClients(ServiceProcessContract.Exp! svcontrol) + { + ESet controllers = + new ESet(); + + svcontrol.SendStartSucceeded(); + + bool run = true; + while (run) { + switch receive { + case svcontrol.Connect(expath, candidate): + if (expath != null) { + // This service does not use subpaths. Reject request. + delete expath; + delete candidate; + svcontrol.SendNakConnect(ErrorCode.NotFound, null); + break; + } + + + RamDiskControlContract.Exp controller = + candidate as RamDiskControlContract.Exp; + if (controller != null) { + controller.SendSuccess(); + controllers.Add(controller); + svcontrol.SendAckConnect(); + } + else { + delete candidate; + svcontrol.SendNakConnect(ErrorCode.ContractNotSupported, null); + } + break; + + case svcontrol.Knock(): + svcontrol.SendAlive(); + break; + + case svcontrol.Stop(): + // -XXX- Need to deal with this. Either send AckStop and exit, or send Busy + // -XXX- because you can't cleanly stop right now. + DebugStub.WriteLine("RamDiskService: Clean shutdown is not supported -- bailing!"); + run = false; + svcontrol.SendAckStop(); + break; + + case svcontrol.ChannelClosed(): + run = false; + break; + + case controller.Create(diskSizeBytes) in controllers: + { + string! diskName; + RamDiskContractErrorCode result = DoCreate(diskSizeBytes, out diskName); + if (result == RamDiskContractErrorCode.NoError) { + char[]! in ExHeap exdiskName = Bitter.FromString2(diskName); + controller.SendCreateSuccess(exdiskName); + } + else { + controller.SendFail(result); + } + controllers.Add(controller); + break; + } + + case controller.Destroy(diskName, force) in controllers: + DoDestroy(controller, diskName, force); + controllers.Add(controller); + break; + + case controller.ChannelClosed() in controllers: + delete controller; + break; + } + } + controllers.Dispose(); + } + + internal static int AppMain(ServiceParameters! parameters) + { + if (parameters.ControlEndpointRef == null) + throw new Exception("The parent process did not provide a ServiceProcessContract channel."); + + ServiceProcessContract.Exp! svcontrol = parameters.ControlEndpointRef.Acquire(); + try { + ServiceClients(svcontrol); + return 0; + } + finally { + delete svcontrol; + } + } + + static int nextDiskNumber = 0; + static string ramDiskPathPrefix = "/dev/ramdisk"; + + private static void GetNextDiskPath(out string! diskName, out ServiceProviderContract.Exp! expService) + { + // Create a new directory entry for the disk + DirectoryServiceContract.Imp ns = DirectoryService.NewClientEndpoint(); + + try { + while (true) { + ServiceProviderContract.Imp! imp; + ServiceProviderContract.Exp! exp; + ServiceProviderContract.NewChannel(out imp, out exp); + + diskName = ramDiskPathPrefix + nextDiskNumber.ToString(); + ns.SendRegister(Bitter.FromString2(diskName), imp); + switch receive { + case ns.AckRegister(): + nextDiskNumber++; + expService = exp; + return; + case ns.NakRegister(nakImp, error): + // If the error is AlreadyExists, recover by trying next disk number, else fail + if (error == Microsoft.Singularity.Directory.ErrorCode.AlreadyExists) { + nextDiskNumber++; + } + else { + throw new RamDiskErrorException(DirectoryErrorToRamDiskError(error)); + } + delete exp; + delete nakImp; + break; + //case unsatisfiable: + // resources.service.Release(exp); + // throw new Exception("unsatisfiable registering RAM disk"); + } + } + } + finally { + delete ns; + } + } + + private static void DeregisterDevice(string! diskName) + { + // Deregister the directory entry + DirectoryServiceContract.Imp ns = DirectoryService.NewClientEndpoint(); + ns.SendDeregister(Bitter.FromString2(diskName)); + try { + switch receive { + case ns.AckDeregister(serviceImp): + delete serviceImp; + break; + case ns.NakDeregister(error): + throw new RamDiskErrorException(DirectoryErrorToRamDiskError(error)); + case unsatisfiable: + throw new Exception("unsatisfiable deregistering RAM disk"); + } + } + finally { + delete ns; + } + } + + private static RamDiskContractErrorCode DirectoryErrorToRamDiskError(DirectoryErrorCode error) { + switch (error) { + case DirectoryErrorCode.NoError: return RamDiskContractErrorCode.NoError; + case DirectoryErrorCode.AccessDenied: return RamDiskContractErrorCode.AccessDenied; + case DirectoryErrorCode.CapacityReached: return RamDiskContractErrorCode.CapacityReached; + case DirectoryErrorCode.InsufficientResources: return RamDiskContractErrorCode.InsufficientResources; + case DirectoryErrorCode.DirectoryFull: return RamDiskContractErrorCode.DirectoryFull; + case DirectoryErrorCode.IsOpen: return RamDiskContractErrorCode.IsOpen; + default: return RamDiskContractErrorCode.InternalError; + } + } + + const string dbgprefix = "RamDiskService: "; + + static void Dbg(string! line) + { + DebugStub.WriteLine(dbgprefix + line); + } + + static void Dbg(string! format, params object[]! args) + { + Dbg(String.Format(format, args)); + } + } +} diff --git a/base/Services/RamDisk/Contracts/RamDiskClientContract.sg b/base/Services/RamDisk/Contracts/RamDiskClientContract.sg new file mode 100644 index 0000000..2cd19ed --- /dev/null +++ b/base/Services/RamDisk/Contracts/RamDiskClientContract.sg @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: RamDiskClientContract.sg +// +// Note: Contract between the RAM disk client manager and a single RAM disk device. +// + +using System; +using Microsoft.Singularity.Channels; +using Microsoft.SingSharp; +using Microsoft.Singularity.Directory; + +namespace Microsoft.Singularity.Services.RamDisk.Contracts +{ + public contract RamDiskClientContract + { + in message Create(char []! in ExHeap diskName, ulong diskSizeBytes); + out message Success(); + out message Fail(RamDiskContractErrorCode reason); + + in message Destroy(bool force); + + state Start : one { + Create? -> CreatePending; + } + + state CreatePending : one { + Success! -> Running; + Fail! -> Done; + } + + state Running : one { + Destroy? -> DestroyPending; + } + + state DestroyPending : one { + Fail! -> Running; + Success! -> Done; + } + + state Done : one { + } + } +} diff --git a/base/Services/RamDisk/Contracts/RamDiskContractErrorCode.sg b/base/Services/RamDisk/Contracts/RamDiskContractErrorCode.sg new file mode 100644 index 0000000..cd4408f --- /dev/null +++ b/base/Services/RamDisk/Contracts/RamDiskContractErrorCode.sg @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: RamDiskContractErrorCode.sg +// +// Note: +// + +using System; +using Microsoft.Singularity.Channels; +using Microsoft.SingSharp; +using Microsoft.Singularity.Directory; + +namespace Microsoft.Singularity.Services.RamDisk.Contracts +{ + public enum RamDiskContractErrorCode + { + NoError = 0, + AccessDenied = 1, + CapacityReached = 2, + InsufficientResources = 3, + DirectoryFull = 4, + IsOpen = 5, + DoesNotExist = 6, + InternalError = 7, + OutOfMemory = 8, + IsInUse = 9, + Max = InternalError + } +} diff --git a/base/Services/RamDisk/Contracts/RamDiskContracts.csproj b/base/Services/RamDisk/Contracts/RamDiskContracts.csproj new file mode 100644 index 0000000..c08ab1a --- /dev/null +++ b/base/Services/RamDisk/Contracts/RamDiskContracts.csproj @@ -0,0 +1,29 @@ + + + + + + + Library + Services.RamDisk.Contracts + + + + + + + + + + + + + + + diff --git a/base/Services/RamDisk/Contracts/RamDiskControlContract.sg b/base/Services/RamDisk/Contracts/RamDiskControlContract.sg new file mode 100644 index 0000000..ad75e51 --- /dev/null +++ b/base/Services/RamDisk/Contracts/RamDiskControlContract.sg @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: RamDiskControlContract.sg +// +// Note: +// + +using System; +using Microsoft.Singularity.Channels; +using Microsoft.SingSharp; +using Microsoft.Singularity.Directory; + +namespace Microsoft.Singularity.Services.RamDisk.Contracts +{ + /// The contract between the ramdisk command + /// line program and the RAM disk client manager + /// service. + public contract RamDiskControlContract : ServiceContract + { + in message Create(ulong diskSizeBytes); + out message CreateSuccess(char []! in ExHeap diskName); + out message Fail(RamDiskContractErrorCode reason); + + in message Destroy(char []! in ExHeap diskName, bool force); + out message Success(); + + override state Start: one { + Success! -> Ready; + } + + state Ready : one { + Create? -> (CreateSuccess! or Fail!) -> Ready; + Destroy? -> (Success! or Fail!) -> Ready; + } + + public const string ManagerControlPath = "/service/services/RamDiskService"; + } +} diff --git a/base/Services/RamDisk/Control/CreateCommand.sg b/base/Services/RamDisk/Control/CreateCommand.sg new file mode 100644 index 0000000..fbaf09e --- /dev/null +++ b/base/Services/RamDisk/Control/CreateCommand.sg @@ -0,0 +1,94 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: CreateCommand.sg +// +// Note: Command to create a new RAM disk of a given size. +// + +using System; + +using Microsoft.SingSharp; +using Microsoft.SingSharp.Reflection; + +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Io; + +using Microsoft.Singularity.Services.RamDisk.Contracts; +using FileSystem.Utils; + +namespace Microsoft.Singularity.Services.RamDisk.RamDiskControl +{ + [ConsoleCategory(Action = "create", + DefaultAction = false, + HelpMessage = "Create and register a new RAM disk device.")] + internal class CreateCommand + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [StringParameter("s", Default = "Automatic", Mandatory = true, + HelpMessage = "Disk size")] + string DiskSizeStr; + + reflective internal CreateCommand(); + + internal int AppMain() + { + ulong diskSizeBytes; + try { + diskSizeBytes = DiskSizeUtils.ParsePrettySizeString(DiskSizeStr); + } + catch (FormatException) { + return -1; + } + + RamDiskControlContract.Imp imp = Utilities.ConnectToManager(); + if (imp != null) { + return DoCreate(imp, diskSizeBytes); + } + return -1; + } + + private static + int DoCreate( + [Claims] RamDiskControlContract.Imp! controller, + ulong diskSizeBytes + ) + { + try { + controller.SendCreate(diskSizeBytes); + + switch receive { + case controller.CreateSuccess(diskPath): + Console.WriteLine( + "Created disk {0}\n" + + "Capacity: {1}", + Bitter.ToString2(diskPath), + DiskSizeUtils.GetPrettySizeString(diskSizeBytes)); + delete diskPath; + return 0; + case controller.Fail(error): + Utilities.DisplayError(error); + return -1; + case controller.ChannelClosed(): + Utilities.DisplayChannelClosedError(); + return -1; + } + } + finally { + delete controller; + } + + } + } +} diff --git a/base/Services/RamDisk/Control/DefaultCommand.sg b/base/Services/RamDisk/Control/DefaultCommand.sg new file mode 100644 index 0000000..e6e13d8 --- /dev/null +++ b/base/Services/RamDisk/Control/DefaultCommand.sg @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: DefaultCommand.sg +// +// Note: +// + +using System; + +using Microsoft.SingSharp; +using Microsoft.SingSharp.Reflection; + +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Io; + +using Microsoft.Singularity.Services.RamDisk.Contracts; + +[assembly: Transform(typeof(ApplicationResourceTransform))] + +namespace Microsoft.Singularity.Services.RamDisk.RamDiskControl +{ + [ConsoleCategory(HelpMessage = "Show help for RAM disk control program.", + DefaultAction = true)] + internal class DefaultCommand + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + internal DefaultCommand() {} + + internal int AppMain() + { + Console.WriteLine("Specify a command to execute, or '-?' for a list of commands."); + return 0; + } + } +} diff --git a/base/Services/RamDisk/Control/DestroyCommand.sg b/base/Services/RamDisk/Control/DestroyCommand.sg new file mode 100644 index 0000000..1d9ef6b --- /dev/null +++ b/base/Services/RamDisk/Control/DestroyCommand.sg @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: DestroyCommand.sg +// +// Note: Command to remove and deallocate an existing unused RAM disk device. +// + +using System; + +using Microsoft.SingSharp; +using Microsoft.SingSharp.Reflection; + +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Io; + +using Microsoft.Singularity.Services.RamDisk.Contracts; +using FileSystem.Utils; + +namespace Microsoft.Singularity.Services.RamDisk.RamDiskControl +{ + [ConsoleCategory(Action = "destroy", + DefaultAction = false, + HelpMessage = "Remove and deallocate an existing unused RAM disk.")] + internal class DestroyCommand + { + [InputEndpoint("data")] + public readonly TRef Stdin; + + [OutputEndpoint("data")] + public readonly TRef Stdout; + + [StringParameter("d", Default = "Automatic", Position=0, Mandatory = true, + HelpMessage = "Disk device path")] + string DiskPath; + + [BoolParameter("f", Default = false, Mandatory = false, + HelpMessage = "Force destroy even if in use")] + bool Force; + + reflective internal DestroyCommand(); + + internal int AppMain() + { + string diskPath = PathUtils.ToPath(DiskPath); + + RamDiskControlContract.Imp imp = Utilities.ConnectToManager(); + if (imp != null) { + return DoDestroy(imp, diskPath, Force); + } + return -1; + } + + private static + int DoDestroy( + [Claims] RamDiskControlContract.Imp! controller, + string! diskPath, + bool force + ) + { + char[] in ExHeap diskPathExHeap = Bitter.FromString2(diskPath); + try { + controller.SendDestroy(diskPathExHeap, force); + + switch receive { + case controller.Success(): + return 0; + case controller.Fail(error): + Utilities.DisplayError(error); + return -1; + case controller.ChannelClosed(): + Utilities.DisplayChannelClosedError(); + return -1; + } + } + finally { + delete controller; + } + } + } +} diff --git a/base/Services/RamDisk/Control/RamDiskControl.csproj b/base/Services/RamDisk/Control/RamDiskControl.csproj new file mode 100644 index 0000000..a8bc332 --- /dev/null +++ b/base/Services/RamDisk/Control/RamDiskControl.csproj @@ -0,0 +1,33 @@ + + + + + + + Exe + RamDiskControl + true + + + + + + + + + + + + + + + + + + diff --git a/base/Services/RamDisk/Control/Utilities.sg b/base/Services/RamDisk/Control/Utilities.sg new file mode 100644 index 0000000..93bf66d --- /dev/null +++ b/base/Services/RamDisk/Control/Utilities.sg @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: RamDisk\Control\Utilities.sg +// +// Note: +// + +using System; + +using Microsoft.SingSharp; +using Microsoft.SingSharp.Reflection; + +using Microsoft.Singularity.Applications; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Io; + +using Microsoft.Singularity.Services.RamDisk.Contracts; + +namespace Microsoft.Singularity.Services.RamDisk.RamDiskControl +{ + internal class Utilities + { + internal static RamDiskControlContract.Imp ConnectToManager() + { + DirectoryServiceContract.Imp! rootds = DirectoryService.NewClientEndpoint(); + + try { + RamDiskControlContract.Imp! service_imp; + RamDiskControlContract.Exp! service_exp; + RamDiskControlContract.NewChannel(out service_imp, out service_exp); + + ErrorCode error; + if (!SdsUtils.Bind(RamDiskControlContract.ManagerControlPath, rootds, service_exp, out error)) { + Console.WriteLine("Failed to connect to service path '{0}'. Error: {1}", + RamDiskControlContract.ManagerControlPath, + SdsUtils.ErrorCodeToString(error)); + delete service_imp; + return null; + } + + service_imp.RecvSuccess(); + return service_imp; + } + finally { + delete rootds; + } + } + + internal static void DisplayError(RamDiskContractErrorCode e) + { + string message = "unknown"; + switch (e) { + case RamDiskContractErrorCode.NoError: + message = "no error"; + break; + case RamDiskContractErrorCode.AccessDenied: + message = "directory access denied"; + break; + case RamDiskContractErrorCode.CapacityReached: + message = "capacity of directory resources reached"; + break; + case RamDiskContractErrorCode.InsufficientResources: + message = "insufficient resources"; + break; + case RamDiskContractErrorCode.DirectoryFull: + case RamDiskContractErrorCode.IsOpen: + message = "disk in use"; + break; + case RamDiskContractErrorCode.DoesNotExist: + message = "device does not exist"; + break; + case RamDiskContractErrorCode.OutOfMemory: + message = "out of memory"; + break; + case RamDiskContractErrorCode.IsInUse: + message = "disk is still in use - use -f to force destroy"; + break; + case RamDiskContractErrorCode.InternalError: + message = "internal error"; + break; + } + Console.WriteLine("Failed: {0}.", message); + } + + internal static void DisplayChannelClosedError() + { + Console.WriteLine( + "Failed: Channel to RAM disk client manager closed unexpectedly." + ); + } + } +} diff --git a/base/Services/RamDisk/Disk/Main.sg b/base/Services/RamDisk/Disk/Main.sg new file mode 100644 index 0000000..467bfd0 --- /dev/null +++ b/base/Services/RamDisk/Disk/Main.sg @@ -0,0 +1,122 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Main.sg +// +// Note: Entry point for RAM disk service, sets up channel. +// +// + +using Microsoft.SingSharp; +using Microsoft.SingSharp.Reflection; + +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Configuration; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Extending; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.Security; +using Microsoft.Singularity.Services; +using Microsoft.Singularity.Services.RamDisk.Contracts; +using System; + +using MSD = Microsoft.Singularity.Directory; + +[assembly: ApplicationPublisherAttribute("singularity.microsoft.com")] +[assembly: AssertPrivilegeAttribute("$register-privilege.localhost")] + +[assembly: Transform(typeof(ServiceResourceTransform))] + +namespace Microsoft.Singularity.Services.RamDisk +{ + [Category("Service")] + public sealed class DiskResources + { + [ServiceEndpoint(typeof(DiskDeviceContract))] + public readonly TRef service; + + [Endpoint] + internal TRef managerTRef; + + reflective private DiskResources(); + } + + internal sealed class Disk + { + private static RamDisk ramDisk; + private static string diskPath; + + private class RamDiskErrorException : Exception + { + private RamDiskContractErrorCode errorCode; + + internal RamDiskErrorException(RamDiskContractErrorCode errorCode) { + this.errorCode = errorCode; + } + + internal RamDiskContractErrorCode ErrorCode { + get { + return errorCode; + } + } + } + + internal static int AppMain(DiskResources! resources) + { + RamDiskClientContract.Exp:Start! managerExp = + resources.managerTRef.Acquire(); + + bool shutdown = false; + RamDisk ramDisk = null; + while (!shutdown) { + try { + switch receive { + case managerExp.Create(diskName, diskSizeBytes): + try { + ramDisk = CreateDevice(Bitter.ToString2(diskName), diskSizeBytes, resources); + managerExp.SendSuccess(); + shutdown = true; + } + finally { + delete diskName; + } + break; + + case managerExp.ChannelClosed(): + shutdown = true; + break; + } + } + catch (RamDiskErrorException ex) { + managerExp.SendFail(ex.ErrorCode); + } + catch (OutOfMemoryException) { + managerExp.SendFail(RamDiskContractErrorCode.OutOfMemory); + } + } + + if (ramDisk != null) { + ServiceProviderContract.Exp! service = resources.service.Acquire(); + ramDisk.Run(service, managerExp); + ramDisk.Finalize(); + } + else { + delete managerExp; + } + + return 0; + } + + private static RamDisk! CreateDevice(string! diskName, ulong diskSizeBytes, DiskResources! resources) + { + ulong diskSizeSectors = (diskSizeBytes + RamDisk.SECTOR_SIZE - 1)/RamDisk.SECTOR_SIZE; + if (diskSizeSectors >= uint.MaxValue) { + throw new RamDiskErrorException(RamDiskContractErrorCode.InsufficientResources); + } + return new RamDisk(diskName, (uint)diskSizeSectors, diskName); + } + } +} diff --git a/base/Services/RamDisk/Disk/RamDisk.csproj b/base/Services/RamDisk/Disk/RamDisk.csproj new file mode 100644 index 0000000..1a620e3 --- /dev/null +++ b/base/Services/RamDisk/Disk/RamDisk.csproj @@ -0,0 +1,34 @@ + + + + + + + Exe + RamDisk + true + true + Custom + + + + + + + + + + + true + + + + + + diff --git a/base/Services/RamDisk/Disk/RamDisk.sg b/base/Services/RamDisk/Disk/RamDisk.sg new file mode 100644 index 0000000..0ebf06f --- /dev/null +++ b/base/Services/RamDisk/Disk/RamDisk.sg @@ -0,0 +1,377 @@ +// +// Microsoft Research Singularity +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: RamDisk.cs +// +// Base class and Interfaces for RAM (memory-based) disk devices. +// Based on IDE disk classes (IdeDisk.sg). +// + +using System; +using Microsoft.SingSharp; +using Microsoft.Contracts; +using Microsoft.Singularity.Channels; +using Microsoft.Singularity.Directory; +using Microsoft.Singularity.Io; +using Microsoft.Singularity.V1.Services; +using Microsoft.Singularity.Services.RamDisk.Contracts; + +namespace Microsoft.Singularity.Services.RamDisk +{ + public class RamDisk + { + public const int SECTOR_SIZE = 512; + + private string deviceName; + private string registeredName; //with Directory + private int diskId; + private static int diskIdGenerator = 0; + private uint numSectors; + private byte[] diskContents; + + private class DiskConnectionState + { + public const int NoSystemId = 0xffff; // Whole disk + + private int systemId; + private ulong startSector; + private ulong sectorCount; + + public DiskConnectionState(int sysId, ulong start, ulong count) + { + this.systemId = sysId; + this.startSector = start; + this.sectorCount = count; + } + + public DiskConnectionState(ulong start, ulong count) + : this(NoSystemId, start, count) + { + } + + public DiskAttributes DiskAttributes + { + get { return 0; } + } + + public int SystemId + { + get { return this.systemId; } + } + + public ulong StartSector + { + get { return this.startSector; } + } + + public ulong SectorCount + { + get { return this.sectorCount; } + } + } + + public void Finalize() + { + Tracing.Log(Tracing.Debug," RAM Disk: Finalize"); + } + + public void Run([Claims] ServiceProviderContract.Exp:Start! service, + [Claims] RamDiskClientContract.Exp:Start! managerExp) + { + Tracing.Log(Tracing.Audit, "Starting diskMsgPump."); + + // create a set of all client endpoints connected to the device + EMap clients = + new EMap(); + + // These sets will only ever contain one thing, but they are here to save memory, + // since this avoids boxing these endpoints once per iteration. + ESet serviceproviders = + new ESet(); + serviceproviders.Add(service); + ESet clientManagers = + new ESet(); + clientManagers.Add(managerExp); + + try { + bool run = true; + while (run) { + switch receive { + // Listen for new connections + case sp.Connect(candidate) in serviceproviders: + DiskDeviceContract.Exp newClient = candidate as DiskDeviceContract.Exp; + if (newClient != null) { + newClient.SendSuccess(); + DiskConnectionState! conn = new DiskConnectionState(0, this.numSectors); + clients.Add(newClient, conn); + sp.SendAckConnect(); + } + else { + sp.SendNackConnect(candidate); + } + serviceproviders.Add(sp); + break; + + case sp.ChannelClosed() in serviceproviders: + delete sp; + break; + + // Listen for client departure + case ep.ChannelClosed() in clients~>connState: + Tracing.Log(Tracing.Debug, "Client channel closes."); + delete ep; + break; + + + // Listen for client requests + case ep.GetDeviceName()in clients~>connState: + char[] in ExHeap blee = Bitter.FromString(this.deviceName); + ep.SendAckGetDeviceName(blee); + clients.Add(ep,connState); + break; + + case ep.GetDiskAttributes() in clients~>connState: + ep.SendAckGetDiskAttributes(connState.DiskAttributes); + clients.Add(ep, connState); + break; + + case ep.GetStartSector() in clients~>connState: + ep.SendAckGetStartSector(connState.StartSector); + clients.Add(ep, connState); + break; + + case ep.GetSectorCount() in clients~>connState: + ep.SendAckGetSectorCount(connState.SectorCount); + clients.Add(ep, connState); + break; + + case ep.GetSystemId() in clients~>connState: + int systemId = connState.SystemId; + if (systemId != DiskConnectionState.NoSystemId) { + ep.SendSystemId((byte)systemId); + } + else { + ep.SendNoSystemId(); + } + clients.Add(ep, connState); + break; + + case ep.Write(data, offset, length, sectorId) in clients~>connState: + if (data == null) { + Tracing.Log(Tracing.Debug,"data buffer is null!!!\n"); + ep.SendNakWrite(); + } + else { + try { + Tracing.Log(Tracing.Debug,"write: sector={0} len={1}",sectorId,length); + ReadWrite(true, data, offset, length, sectorId, connState); + ep.SendAckWrite(data); + } + catch (ArgumentOutOfRangeException) { + ep.SendNakWrite(); + } + } + clients.Add(ep, connState); + Tracing.Log(Tracing.Debug,"write: ends."); + break; + + case ep.NoOp()in clients~>connState: + ep.SendAckNoOp(); + clients.Add(ep, connState); + break; + + case ep.Read(data, offset, length, sectorId) in clients~>connState: + if (data == null) { + Tracing.Log(Tracing.Debug,"data buffer is null!!!\n"); + ep.SendNakRead(); + } + else { + try { + Tracing.Log(Tracing.Debug,"read: sector={0} len={1}", sectorId, length); + ReadWrite(false, data, offset, length, sectorId, connState); + ep.SendAckRead(data); + } + catch (ArgumentOutOfRangeException) { + ep.SendNakRead(); + } + } + + clients.Add(ep, connState); + Tracing.Log(Tracing.Debug,"read: ends."); + break; + + case ep.ReadPerf(numMB, chunkSize) in clients~>connState: + { + long cycles; + long ticks; + ReadPerf(numMB, chunkSize, out cycles, out ticks, connState); + ep.SendAckReadPerf(cycles, ticks); + clients.Add(ep, connState); + break; + } + + case cm.Destroy(force) in clientManagers: + if (!force && !clients.IsEmpty) { + Tracing.Log(Tracing.Debug, "Tried to destroy RamDisk while still in use"); + cm.SendFail(RamDiskContractErrorCode.IsInUse); + } + else { + if (!clients.IsEmpty) { + Tracing.Log(Tracing.Debug, "Warning: forcing destruction of RamDisk while still in use"); + } + + // Client endpoints and disk will be cleaned up during shutdown + cm.SendSuccess(); + run = false; + } + clientManagers.Add(cm); + break; + + case cm.ChannelClosed() in clientManagers: + Tracing.Log(Tracing.Debug,"RamDiskClientManager has closed channel"); + run = false; + delete cm; + break; + + // If all else fails.... + case clients.Empty() && serviceproviders.Empty() /*&& volumemanagers1.Empty() && volumemanagers2.Empty() && extensions.Empty()*/: + Tracing.Log(Tracing.Debug, "Disk driver has no more connections"); + run = false; + break; + } // switch receive + } // for + } + finally { + Tracing.Log(Tracing.Debug, "diskMsgPump exiting."); + clients.Dispose(); + serviceproviders.Dispose(); + clientManagers.Dispose(); + } + } + + [NotDelayed] + public RamDisk(string! registeredName, uint numSectors, string! instanceName) + { + DebugStub.Print("RamDisk: {0}\n", __arglist(instanceName)); + + this.registeredName = registeredName; + this.numSectors = numSectors; + + this.diskId = ++RamDisk.diskIdGenerator; + + this.deviceName = String.Format("RamDisk {0} {1} MB", + this.diskId, + (this.numSectors>>11)); + + this.diskContents = new byte[numSectors * SECTOR_SIZE]; + InitializePartitionTable(); + + announceDrive(); + } + + private void InitializePartitionTable() + { + // Write system sector signature + diskContents[510] = 0x55; + diskContents[511] = 0xaa; + + // Because the array is initialized to all zeros, the four + // partition entries will initially have the correct type + // "Empty" and all their fields set correctly. + } + + private int ReadPerf(int numMB, + int chunkSize, + out long cycles, + out long ticks, + DiskConnectionState! conn) + { + long endCycles; + long endClock; + long startCycles; + long startClock; + + int sectors = numMB * 1024 * (1024 / SECTOR_SIZE); + int sectorsPerChunk = chunkSize / SECTOR_SIZE; + + byte* opt(ExHeap[]) perfBuffer = new [ExHeap] byte[chunkSize]; + + try { + startCycles = ProcessService.GetCycleCount(); + startClock = ProcessService.GetUpTime().Ticks; + + int i = 0; + while (i < sectors) { + ReadWrite(false, perfBuffer, 0, (UIntPtr) chunkSize, (ulong)i, conn); + i += sectorsPerChunk; + } + + endCycles = ProcessService.GetCycleCount(); + endClock = ProcessService.GetUpTime().Ticks; + } + finally { + delete perfBuffer; + } + + cycles = endCycles - startCycles; + ticks = endClock - startClock; + + return 0; + } + + private void ReadWrite( bool doWrite, + byte[]! in ExHeap buffer, + UIntPtr bufferOffset, + UIntPtr length, + ulong sector, + DiskConnectionState! conn) + { + ushort temp; + ulong sectorId; + uint dataStart; + uint bufferStart; + + ulong numSectors = ((ulong)length + SECTOR_SIZE - 1)/ SECTOR_SIZE; + + if (sector > conn.SectorCount) { + throw new ArgumentOutOfRangeException("RamDisk ReadWrite: Sector start out of Bounds."); + } + + if ((sector + numSectors) > conn.SectorCount) { + throw new ArgumentOutOfRangeException("RamDisk ReadWrite: Sector operation out of Bounds."); + } + + sectorId = sector + conn.StartSector; + + if (sectorId >= this.numSectors) { + throw new ArgumentOutOfRangeException("RamDisk ReadWrite: Sector out of Bounds."); + } + + if ((length + bufferOffset) > (UIntPtr)buffer.Length) { + throw new ArgumentOutOfRangeException("RamDisk ReadWrite: length + offset exceeds array bounds"); + } + + lock (this) { + // + // perform operation + // + if (doWrite) { + Bitter.ToByteArray(buffer, (int)bufferOffset, (int)length, diskContents, (int)(sector*SECTOR_SIZE)); + } + else { + Bitter.FromByteArray(buffer, (int)bufferOffset, (int)length, diskContents, (int)(sector*SECTOR_SIZE)); + } + } + } + + private void announceDrive() + { + Tracing.Log(Tracing.Debug,"RamDisk: id {0}\n", + this.deviceName); + + DebugStub.WriteLine("RamDisk: id={0}", __arglist(this.deviceName)); + } + } +} diff --git a/base/Services/RamDisk/RamDisk.proj b/base/Services/RamDisk/RamDisk.proj new file mode 100644 index 0000000..14f0fa2 --- /dev/null +++ b/base/Services/RamDisk/RamDisk.proj @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + diff --git a/base/Services/ServiceManager/Acceptor.sg b/base/Services/ServiceManager/Acceptor.sg deleted file mode 100644 index 6394de7..0000000 --- a/base/Services/ServiceManager/Acceptor.sg +++ /dev/null @@ -1,238 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Services\ServiceManager\Acceptor.sg -// -// Note: Service Manager Front-End -// -using System; -using System.Collections; -using System.Collections.Specialized; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.ServiceManager; - -namespace Microsoft.Singularity.Services.ServiceManager -{ - /// - /// Acceptor is a Service Manager front end. It receives a client's - /// connection request, accepts and then binds it to a worker thread - /// called ServiceController. - /// - internal class Acceptor - { - private Thread serviceThread; - private string servicePath; - private IList controllers; - private Object controllersLock; - - private TRef leaderTRef; - private TRef terminalTRef; - private TRef providerTRef; - - internal Acceptor(string! path) - { - this.servicePath = path; - this.controllers = new ArrayList(); - this.controllersLock = new Object(); - } - - ~Acceptor() - { - // Unless Stop is invoked in advance. - if (serviceThread != null) { - Stop(); - } - delete providerTRef.Acquire(); - } - - internal void Start() - { - if (servicePath == null || servicePath == String.Empty) { - throw new Exception("Service is not specified."); - } - - RegisterToNameService(); - - // Initialize the inter-thread channel - ThreadTerminationContract.Imp! leader; - ThreadTerminationContract.Exp! terminal; - ThreadTerminationContract.NewChannel(out leader, out terminal); - leaderTRef = new TRef(leader); - terminalTRef = new TRef(terminal); - if (serviceThread == null) { - serviceThread = new Thread(new ThreadStart(Run)); - serviceThread.Start(); - DebugStub.Print("Acceptor: {0}\n", - __arglist(serviceThread.ToString())); - } - } - - internal void Stop() - { - ThreadTerminationContract.Imp:Start! leader; - leader = leaderTRef.Acquire(); - leader.SendStop(); - delete leader; - serviceThread.Join(); - serviceThread = null; - } - - /// - /// Service main loop. Deals with client connection requests. - /// - private void Run() - { - ServiceProviderContract.Exp:Start! provider; - ThreadTerminationContract.Exp:Start! terminal; - - provider = providerTRef.Acquire(); - terminal = terminalTRef.Acquire(); - - for (;;) { - ServiceManagementContract.Exp:Start user; - - switch receive { - case provider.RecvConnect(serverEp): - if (serverEp == null) { - continue; - } - user = serverEp as ServiceManagementContract.Exp:Start; - if (user == null) { - provider.SendNackConnect(serverEp); - continue; - } - else { - Thread th = new Thread(new ThreadStart(NewController(user).Run)); - th.Start(); - provider.SendAckConnect(); - } - DebugStub.Print("Acceptor: Client connected.\n"); - - break; - case provider.ChannelClosed(): - Clear(); - goto exit; - break; - case terminal.Stop(): - Clear(); - goto exit; - break; - case terminal.ChannelClosed(): - Clear(); - goto exit; - break; - } - } -exit: - delete terminal; - DeregisterFromNameService(); - delete provider; - } - - /// - /// Register ServiceControlContract to the name service. - /// - protected void RegisterToNameService() - { - bool success; - DirectoryServiceContract.Imp rootNS; - ServiceProviderContract.Imp! providerClient; - ServiceProviderContract.Exp! providerServer; - - ServiceProviderContract.NewChannel(out providerClient, - out providerServer); - - // SMS is a client of NS. - rootNS = DirectoryService.NewClientEndpoint(); - rootNS.SendRegister(Bitter.FromString2(servicePath), - providerClient); - switch receive { - case rootNS.RecvAckRegister(): - success = true; - break; - case rootNS.RecvNakRegister(rejected, error): - delete rejected; - success = false; - break; - case rootNS.ChannelClosed(): - success = false; - break; - } - delete rootNS; - - if (success == false) { - delete providerServer; - DebugStub.Break(); - throw new Exception("Name registration failed."); - } - - providerTRef = new TRef(providerServer); - DebugStub.WriteLine("SMS: NS Registered."); - } - - protected void DeregisterFromNameService() - { - DirectoryServiceContract.Imp rootNS; - - rootNS = DirectoryService.NewClientEndpoint(); - rootNS.SendDeregister(Bitter.FromString2(servicePath)); - switch receive { - case rootNS.AckDeregister(providerClient): - delete providerClient; - break; - case rootNS.NakDeregister(error): - DebugStub.Print("SMS: Deregister failed.\n"); - break; - case rootNS.ChannelClosed(): - break; - } - delete rootNS; - } - - /// - /// Create a new ServiceController object. - /// - private ServiceController! NewController([Claims]ServiceManagementContract.Exp:Start! ep) - { - ServiceController worker; - - worker = new ServiceController(this, ep); - lock (controllersLock) { - controllers.Add(worker); - } - return worker; - } - - /// - /// Removes the worker from the list when Reception is terminated. - /// - internal void ReleaseController(ServiceController worker) - { - lock (controllersLock) { - controllers.Remove(worker); - } - } - - private void Clear() - { - lock (controllersLock) { - // Stops all the workers - foreach (ServiceController worker in controllers) { - if (worker == null) { - continue; - } - worker.Stop(); - } - controllers.Clear(); - controllers = null; - } - } - } -} diff --git a/base/Services/ServiceManager/AccessControlException.cs b/base/Services/ServiceManager/AccessControlException.cs index 74a4806..07da991 100644 --- a/base/Services/ServiceManager/AccessControlException.cs +++ b/base/Services/ServiceManager/AccessControlException.cs @@ -15,4 +15,11 @@ namespace Microsoft.Singularity.Services.ServiceManager public class AccessControlException : Exception { } + + class InvalidStateException : Exception + { + public InvalidStateException() + : base("Invalid state") + {} + } } diff --git a/base/Services/ServiceManager/DefaultService.sg b/base/Services/ServiceManager/DefaultService.sg deleted file mode 100644 index 7e4299e..0000000 --- a/base/Services/ServiceManager/DefaultService.sg +++ /dev/null @@ -1,33 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Services\ServiceManager\DefaultService.sg -// -// Note: -// -using System; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.ServiceManager; - -namespace Microsoft.Singularity.Services.ServiceManager -{ - internal class DefaultService : Service - { - // protected readonly string! name; - // protected Process process; - // protected TRef endpoint; - - internal DefaultService(string! serviceName, - string! binaryName, - [Claims]DirectoryServiceContract.Imp:Ready! ds) - { - base(serviceName, binaryName, ServiceType.Default, ds); - } - } -} diff --git a/base/Services/ServiceManager/DefaultServiceCreator.sg b/base/Services/ServiceManager/DefaultServiceCreator.sg deleted file mode 100644 index 0598b7e..0000000 --- a/base/Services/ServiceManager/DefaultServiceCreator.sg +++ /dev/null @@ -1,25 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Services\ServiceManager\DefaultServiceCreator.sg -// -// Note: -// -using System; -using Microsoft.Singularity; -using Microsoft.Singularity.Directory; - -namespace Microsoft.Singularity.Services.ServiceManager -{ - internal class DefaultServiceCreator : IServiceProduceable - { - public Service NewService(string! serviceName, string! binaryName) - { - return new DefaultService(serviceName, binaryName, - DirectoryService.NewClientEndpoint()); - } - } -} diff --git a/base/Services/ServiceManager/IServiceProduceable.sg b/base/Services/ServiceManager/IServiceProduceable.sg deleted file mode 100644 index ac6f12d..0000000 --- a/base/Services/ServiceManager/IServiceProduceable.sg +++ /dev/null @@ -1,20 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Services\ServiceManager\IServiceProduceable.sg -// -// Note: -// -using System; -using Microsoft.Singularity; - -namespace Microsoft.Singularity.Services.ServiceManager -{ - interface IServiceProduceable - { - Service NewService(string! serviceName, string! binaryName); - } -} diff --git a/base/Services/ServiceManager/JournalService.sg b/base/Services/ServiceManager/JournalService.sg deleted file mode 100644 index a761c0a..0000000 --- a/base/Services/ServiceManager/JournalService.sg +++ /dev/null @@ -1,519 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Services\ServiceManager\JournalService.sg -// -// Note: -// -using System; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.ServiceManager; - -namespace Microsoft.Singularity.Services.ServiceManager -{ - // JournalService is a service launcher. This object instantiates two - // service processes: the target service and the logging service for it. - // - // JournalService sets up three endpoints for the logging service and two - // channels for the target service. (* 'Contract' is omitted.) - // For the logging service, - // ManagedService Because it is a service. - // DirectoryService.Imp To connect to a root directory service - // DirectoryService.Exp To intercept the connection from the target - // For the target, - // ManagedService Because it is a service. - // DirectoryService.Imp To intercept the connection to the root DS - // - internal class JournalService : Service - { - // Every resilient service process is associated with a dedicated - // journal service process whose program name must be " - // + Proxy". e.g. CounterProxy - protected const string JSSuffix = "Proxy"; - protected ResilientService target; - protected Thread proxyThread; - protected Object! recoveryLock; - - protected TRef controlRef; - protected TRef recoveryRef; - protected TRef directoryRef; - protected TRef serviceProxyRef; - protected TRef signalSenderRef; - protected TRef signalReceiverRef; - - // name: The name of the target service (e.g. Counter) - // ds: The channel to the root directory service. - internal JournalService(string! serviceName, - string! binaryName, - [Claims]DirectoryServiceContract.Imp:Ready! ds) - { - base(serviceName, binaryName, ServiceType.Resilient, ds); - recoveryLock = new Object(); - } - - /// - /// Starts Journal Service process and the target service process. - /// - internal override ServiceError StartServiceProcess() - { - bool msSuccess = false; - bool dsSuccess = false; - ServiceError report; - - ManagedServiceContract.Imp msImp; - ManagedProxyContract.Exp mpExp; - DirectoryServiceContract.Imp myDSImp; - ServiceProxyContract.Imp proxyImp; - ServiceControlContract.Imp! targetControl; - ServiceControlContract.Exp! controlTarget; - - // - // Setup Proxy Process - // - DebugStub.Print("JS: Create process '{0}'\n", - __arglist(serviceName + JSSuffix)); - process = new Process(new String[1]{serviceName + JSSuffix}, - null, 5); - SetupEndpoints(process, out msImp, out mpExp, - out myDSImp, out proxyImp); - - if (msImp == null || myDSImp == null || - mpExp == null || proxyImp == null) { - delete msImp; - delete mpExp; - delete myDSImp; - delete proxyImp; - return ServiceError.TryAgain; - } - - // - // Start proxy - // - process.Start(); - - // - // Make sure the proxy has been started - // - while (!(msSuccess && dsSuccess)) { - switch receive { - case msImp.Success(): - DebugStub.Print("ms received\n"); - msSuccess = true; - break; - case myDSImp.Success(): - DebugStub.Print("sub received\n"); - dsSuccess = true; - break; - case unsatisfiable: - goto exit; - break; - } - } - -exit: - if (!msSuccess || !dsSuccess) { - DebugStub.Print("JournalService: Service process " + - "channel is closed.\n"); - delete msImp; - delete mpExp; - delete myDSImp; - delete proxyImp; - return ServiceError.ChannelClosed; - } - - DebugStub.Print("JS: Check\n"); - managementEndpoint = new TRef(msImp); - - mpExp.SendSuccess(); - recoveryRef = new TRef(mpExp); - - // - // Create the target service process with the myDS DS - // - target = new ResilientService(serviceName, binaryName, - myDSImp, proxyImp, this); - report = target.StartServiceProcess(); - if (report != ServiceError.None) { - return report; - } - - // - // Start the counterpart - // - ServiceControlContract.NewChannel(out targetControl, - out controlTarget); - target.Start(controlTarget); - switch receive { - case targetControl.Success(): - break; - case unsatisfiable: - DebugStub.Break(); - break; - } - controlRef = new TRef(targetControl); - - DebugStub.Print("JournalService: StartServiceProcess EXIT\n"); - return report; - } - - private void SetupEndpoints(Process p, - out ManagedServiceContract.Imp ms, - out ManagedProxyContract.Exp mp, - out DirectoryServiceContract.Imp d, - out ServiceProxyContract.Imp sp) - { - ManagedServiceContract.Imp! msImp; - ManagedServiceContract.Exp! msExp; - ManagedProxyContract.Imp! mpImp; - ManagedProxyContract.Exp! mpExp; - DirectoryServiceContract.Imp! dsClient; - DirectoryServiceContract.Imp! myDSImp; - DirectoryServiceContract.Exp! myDSExp; - ServiceProxyContract.Imp! proxyImp; - ServiceProxyContract.Exp! proxyExp; - - ms = null; - mp = null; - d = null; - sp = null; - - //TODO: Slot number management. Currently they are hard-coded. - // - // Service Manager endpoint to be given to the proxy - // - ManagedServiceContract.NewChannel(out msImp, out msExp); - if (!process.SetStartupEndpoint(0, (Endpoint * in ExHeap)msExp)) { - DebugStub.Print("JournalService: Endpoint cannot be set. " + - "(ManagedService)\n"); - delete msImp; - return; - } - - // - // Root Directory Service endpoint - // - dsClient = DirectoryService.NewClientEndpoint(); - if (!process.SetStartupEndpoint(1, (Endpoint * in ExHeap)dsClient)) - { - DebugStub.Print("JournalService: Endpoint cannot be set. " + - "(DirectoryService)\n"); - delete msImp; - return; - } - - // - // Substitute Directory Service endpoint - // - DirectoryServiceContract.NewChannel(out myDSImp, - out myDSExp); - if (!process.SetStartupEndpoint(2, (Endpoint * in ExHeap)myDSExp)) - { - DebugStub.Print("JournalService: Endpoint cannot be set. " + - "(Substitute)\n"); - delete msImp; - delete myDSImp; - return; - } - - // - // DS Transfer endpoint - // - ManagedProxyContract.NewChannel(out mpImp, out mpExp); - if (!process.SetStartupEndpoint(3, (Endpoint * in ExHeap)mpImp)) - { - DebugStub.Print("JournalService: Endpoint cannot be set. " + - "(ManagedProxy)\n"); - delete msImp; - delete myDSImp; - delete mpExp; - return; - } - - // - // Checkpointing and persistent memory space - // - ServiceProxyContract.NewChannel(out proxyImp, out proxyExp); - if (!process.SetStartupEndpoint(4, (Endpoint * in ExHeap)proxyExp)) - { - DebugStub.Print("JournalService: Endpoint cannot be set. " + - "(ServiceProxy)\n"); - delete msImp; - delete myDSImp; - delete mpExp; - delete proxyImp; - return; - } - - ms = msImp; - mp = mpExp; - d = myDSImp; - sp = proxyImp; - } - - /// - /// Starts the service of a service process. This is done by sending - /// a StartService message. - /// - // HI: Current implementation starts both the logging service and the - // target service, because we assume that the logging service is - // a part of the target service. This may conflicts the normal - // (not resilient) service behavior. - internal override ServiceError StartService() - { - ServiceError report; - ManagedServiceContract.Imp:Ready management; - ThreadTerminationContract.Imp! imp; - ThreadTerminationContract.Exp! exp; - - ThreadTerminationContract.NewChannel(out imp, out exp); - signalSenderRef = new TRef(imp); - signalReceiverRef = new TRef(exp); - - proxyThread = new Thread(new ThreadStart(HandleProxy)); - proxyThread.Start(); - - DebugStub.Print("JS: Start proxy\n"); - // - // Start JournalProducer service - // - management = managementEndpoint.Acquire(); - management.SendStartService(); - switch receive { - case management.AckStartService(): - break; - case management.RecvNakStartService(): - DebugStub.Print("JS: Can't start service\n"); - report = ServiceError.Unknown; - goto exit; - break; - case management.RecvChannelClosed(): - report = ServiceError.ChannelClosed; - goto exit; - break; - } - - DebugStub.Print("JS: Start server\n"); - // - // Start the target service - // - report = target.StartService(); - if (report != ServiceError.None) { - management.SendStopService(); - switch receive { - case management.RecvAckStopService(): - break; - case management.RecvBusy(): - report = ServiceError.TryAgain; - break; - case management.ChannelClosed(): - report = ServiceError.ChannelClosed; - break; - } - } -exit: - managementEndpoint.Release(management); - - //DebugStub.Print("JournalService: StartService EXIT\n"); - return report; - } - - internal override ServiceError StopService() - { - ServiceError report; - ManagedServiceContract.Imp:Ready management; - ThreadTerminationContract.Imp:Start! signalToProxyThread; - - DebugStub.Print("JS: Enter StopService\n"); - // - // Stop the target service first. - // - report = target.StopService(); - if (report != ServiceError.None) { - return report; - } - - // - // Stop the logging service - // - signalToProxyThread = signalSenderRef.Acquire(); - signalToProxyThread.SendStop(); - switch receive { - case signalToProxyThread.AckStop(): - break; - case signalToProxyThread.ChannelClosed(): - break; - } - signalSenderRef.Release(signalToProxyThread); - - management = managementEndpoint.Acquire(); - management.SendStopService(); - switch receive { - case management.RecvAckStopService(): - report = ServiceError.None; - break; - case management.RecvBusy(): - report = ServiceError.TryAgain; - break; - case management.ChannelClosed(): - report = ServiceError.ChannelClosed; - break; - } - managementEndpoint.Release(management); - DebugStub.Print("JS: Exit StopService\n"); - return report; - } - - internal override ServiceError StopServiceProcess() - { - ServiceError report; - //DebugStub.Print("JS: Enter StopServiceProcess\n"); - - report = target.StopServiceProcess(); - if (report != ServiceError.None) { - return report; - } - - //DebugStub.Print("JS: Exit StopServiceProcess\n"); - return base.StopServiceProcess(); - } - - internal override ServiceError RestartService() - { - return RestartServiceProcess(); - } - - internal override ServiceError RestartServiceProcess() - { - ServiceError error = ServiceError.Unknown; - ServiceControlContract.Imp:Ready! control; - - if (!Monitor.TryEnter(recoveryLock)) { - return ServiceError.TryAgain; - } - - control = controlRef.Acquire(); - control.SendRestartService(); - switch receive { - case control.AckRestartService(): - error = ServiceError.None; - break; - case control.NakRestartService(err): - error = err; - break; - case control.ChannelClosed(): - error = ServiceError.ChannelClosed; - break; - } - controlRef.Release(control); - DebugStub.Print("JS: Exit RestartServiceProcess\n"); - Monitor.Exit(recoveryLock); - return error; - } - - internal void RestartHelper() - { - RestartServiceProcess(); - } - - private void HandleProxy() - { - ManagedProxyContract.Exp:Ready! proxy; - ThreadTerminationContract.Exp:Start! signal; - - proxy = recoveryRef.Acquire(); - signal = signalReceiverRef.Acquire(); - for (;;) { - switch receive { - case proxy.RequestRecovery(): - { - try { - Monitor.Enter(this); - if (directoryRef == null) { - new Thread(new ThreadStart(RestartHelper)).Start(); - Monitor.Wait(this); - } - - if (directoryRef != null) { - proxy.SendAckRecovery(directoryRef.Acquire(), - serviceProxyRef.Acquire()); - directoryRef = null; - } - else { - DebugStub.Break(); - } - } - finally { - Monitor.Exit(this); - } - break; - } - case proxy.RequestDSRecovery(): - { - DebugStub.Print("JS: Enter RequestRecovery\n"); - try { - Monitor.Enter(this); - if (directoryRef == null) { - new Thread(new ThreadStart(RestartHelper)).Start(); - Monitor.Wait(this); - } - - if (directoryRef != null) { - proxy.SendAckDSRecovery( - directoryRef.Acquire()); - directoryRef = null; - } - else { - DebugStub.Break(); - } - } - finally { - Monitor.Exit(this); - } - DebugStub.Print("JS: Exit RequestRecovery\n"); - break; - } - case signal.Stop(): - { - signal.SendAckStop(); - // Even if the proxy process waits for an ACK on the - // 'proxy' channel, it is terminated by Service Manager - // through the ManagedServiceContract channel. - goto exit; - break; - } - case signal.ChannelClosed(): - { - goto exit; - break; - } - } // SWR - } // END OF LOOP -exit: - signalReceiverRef.Release(signal); - recoveryRef.Release(proxy); - } - - internal void TransferDirectoryService([Claims]DirectoryServiceContract.Exp:Start! ds, - [Claims]ServiceProxyContract.Exp:Start! sp) - { - //DebugStub.Print("JS: Enter TransferDS\n"); - try { - Monitor.Enter(this); - directoryRef = new TRef(ds); - serviceProxyRef = new TRef(sp); - Monitor.Pulse(this); - } - finally { - Monitor.Exit(this); - } - //DebugStub.Print("JS: Exit TransferDS\n"); - } - } // class JournalService -} diff --git a/base/Services/ServiceManager/ResilientService.sg b/base/Services/ServiceManager/ResilientService.sg deleted file mode 100644 index 545930d..0000000 --- a/base/Services/ServiceManager/ResilientService.sg +++ /dev/null @@ -1,203 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Services\ServiceManager\ResilientService.sg -// -// Note: -// -using System; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.ServiceManager; - -namespace Microsoft.Singularity.Services.ServiceManager -{ - internal class ResilientService : Service - { - protected JournalService journalService; - protected Object! recoveryLock; - protected TRef proxyRef; - - internal ResilientService(string! serviceName, - string! binaryName, - [Claims]DirectoryServiceContract.Imp:Ready! ds, - [Claims]ServiceProxyContract.Imp:Start! sp, - JournalService journal) - { - base(serviceName, binaryName, ServiceType.Resilient, ds); - this.journalService = journal; - this.recoveryLock = new Object(); - this.proxyRef = new TRef(sp); - } - - internal override ServiceError StartServiceProcess() - { - ServiceError report; - ManagedServiceContract.Imp! msImp; - ManagedServiceContract.Exp! msExp; - - DebugStub.Print("RS: Create process '{0}'\n", - __arglist(binaryName)); - - process = new Process(new String[1]{binaryName}, null, 3); - - // Create a channel to the service - ManagedServiceContract.NewChannel(out msImp, out msExp); - if (!process.SetStartupEndpoint(0, (Endpoint * in ExHeap)msExp)) { - DebugStub.Print("Endpoint cannot be set. (ManagedService)\n"); - delete msImp; - return ServiceError.TryAgain; - } - - if (!process.SetStartupEndpoint(1, (Endpoint * in ExHeap)directory.Acquire())) { - DebugStub.Print("Endpoint cannot be set. " + - "(DirectoryService)\n"); - delete msImp; - return ServiceError.TryAgain; - } - - if (!process.SetStartupEndpoint(2, (Endpoint * in ExHeap)proxyRef.Acquire())) { - DebugStub.Print("Endpoint cannot be set. (ServiceProxy)\n"); - delete msImp; - return ServiceError.TryAgain; - } - - DebugStub.Print("RS: start process\n"); - process.Start(); - - // Make sure the process has been started - switch receive { - case msImp.RecvSuccess(): - managementEndpoint = new TRef(msImp); - report = ServiceError.None; - break; - case msImp.ChannelClosed(): - DebugStub.Print("Service process's channel is closed.\n"); - delete msImp; - report = ServiceError.ChannelClosed; - break; - } - return report; - } - - internal override ServiceError RestartServiceProcess() - { - ServiceError report = ServiceError.Unknown; - ManagedServiceContract.Imp! msImp; - ManagedServiceContract.Exp! msExp; - DirectoryServiceContract.Imp! substituteImp; - DirectoryServiceContract.Exp! substituteExp; - ServiceProxyContract.Imp! proxyImp; - ServiceProxyContract.Exp! proxyExp; - - DebugStub.Print("ResilientService: ENTER RestartServiceProcess\n"); - if (!Monitor.TryEnter(recoveryLock)) { - return ServiceError.TryAgain; - } - - DebugStub.Print("RS: Stopping the process ...\n"); - process.Stop(); - - - DirectoryServiceContract.NewChannel(out substituteImp, - out substituteExp); - ServiceProxyContract.NewChannel(out proxyImp, out proxyExp); - // Set substituteExp to JournalProducer and check the connection - journalService.TransferDirectoryService(substituteExp, proxyExp); - - switch receive { - case substituteImp.RecvSuccess(): - report = ServiceError.None; - break; - case substituteImp.ChannelClosed(): - report = ServiceError.ChannelClosed; - break; - } - - if (report != ServiceError.None) { - delete substituteImp; - delete proxyImp; - goto exit; - } - DebugStub.Print("sub received\n"); - - DebugStub.Print("RS: creating a new process ... "); - process = new Process(new String[1]{binaryName}, null, 3); - DebugStub.Print("done.\n"); - - // - // Create a channel to the service - // - ManagedServiceContract.NewChannel(out msImp, out msExp); - if (!process.SetStartupEndpoint(0, (Endpoint * in ExHeap)msExp)) { - DebugStub.Print("Endpoint cannot be set. (ManagedService)\n"); - delete msImp; - delete substituteImp; - delete proxyImp; - report = ServiceError.TryAgain; - goto exit; - } - - if (!process.SetStartupEndpoint(1, (Endpoint * in ExHeap)substituteImp)) { - DebugStub.Print("Endpoint cannot be set. " + - "(DirectoryService)\n"); - delete msImp; - delete proxyImp; - report = ServiceError.TryAgain; - goto exit; - } - - if (!process.SetStartupEndpoint(2, (Endpoint * in ExHeap)proxyImp)) { - DebugStub.Print("Endpoint cannot be set. " + - "(ServiceProxy)\n"); - delete msImp; - report = ServiceError.TryAgain; - goto exit; - } - - DebugStub.Print("RS: Restarting service process ... "); - process.Start(); - DebugStub.Print("done.\n"); - - // Make sure the process has been started - switch receive { - case msImp.RecvSuccess(): - // Replace the existing endpoint - managementEndpoint = new TRef(msImp); - report = ServiceError.None; - break; - case msImp.ChannelClosed(): - DebugStub.Print("Service process's channel is closed.\n"); - delete msImp; - report = ServiceError.ChannelClosed; - break; - } - -exit: - Monitor.Exit(recoveryLock); - DebugStub.Print("RS: Exit RestartServiceProcess\n"); - return report; - } - - internal override ServiceError RestartService() - { - ServiceError report; - - //this.StopService(); - // Currently, we don't know what condition the service - // process is, so just kill the process and restart it. - report = RestartServiceProcess(); - if (report == ServiceError.None) { - report = StartService(); - } - - return report; - } - } -} diff --git a/base/Services/ServiceManager/ResilientServiceCreator.sg b/base/Services/ServiceManager/ResilientServiceCreator.sg deleted file mode 100644 index 786260a..0000000 --- a/base/Services/ServiceManager/ResilientServiceCreator.sg +++ /dev/null @@ -1,25 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Services\ServiceManager\ServiceCreator.sg -// -// Note: -// -using System; -using Microsoft.Singularity; -using Microsoft.Singularity.Directory; - -namespace Microsoft.Singularity.Services.ServiceManager -{ - internal class ResilientServiceCreator : IServiceProduceable - { - public Service NewService(string! serviceName, string! binaryName) - { - return new JournalService(serviceName, binaryName, - DirectoryService.NewClientEndpoint()); - } - } -} diff --git a/base/Services/ServiceManager/Service.sg b/base/Services/ServiceManager/Service.sg index d7cbb2b..5ca130c 100644 --- a/base/Services/ServiceManager/Service.sg +++ b/base/Services/ServiceManager/Service.sg @@ -14,477 +14,347 @@ using System.Collections.Specialized; using System.Threading; using Microsoft.SingSharp; using Microsoft.Singularity; +using Microsoft.Singularity.Applications; using Microsoft.Singularity.Directory; using Microsoft.Singularity.Channels; using Microsoft.Singularity.ServiceManager; using Microsoft.Singularity.V1.Processes; +using Microsoft.Singularity.Xml; namespace Microsoft.Singularity.Services.ServiceManager { /// - /// Service process counterpart. + /// This class represents the state of a service, as seen by the Service Manager. + /// + /// Threading: Instances of this class are owned by the main (entry point) thread. + /// That is the only thread that creates instances of this class, and reads/writes + /// its fields. + /// + /// There is one important exception to consider: The main thread uses the ServiceStarter + /// class to create and start service processes, and when it does so, the main thread + /// passes a reference to an instance of Service to ServiceStarter. ServiceStarter never + /// examines the contents of the instance; it passes it back to the main thread. + /// This maintains the invariant that only the main thread can read/write fields. + /// + /// This class encapsulates this information: + /// * the complete configuration of the service, + /// * current information about the status of the service, + /// * references to all of the processes that have been created for this service, + /// * state fields that control state transitions (starting, stopping, etc.) + /// * state fields relevant to detection of defective service processes and recovery. + /// + /// This class provides methods for reading/writing some of the state of a Service + /// instance, but in general, this class does not contain much of the semantics of the + /// Service Manager. The Service class is not an independent, standalone object; it is + /// a data structure owned and manipulated by the ServiceManager class. The methods + /// defined on this class handle making controlled state transitions (such as acquiring + /// an endpoint); the methods do *not* make global or broadly-scoped state transitions. + /// That is the responsibility of the ServiceManager class. /// - internal abstract class Service + class Service { - protected readonly string! serviceName; - protected readonly string! binaryName; - protected readonly ServiceType serviceType; - protected int id; - protected Process process; - private Thread thread; - - /// - /// Management channel to the service - /// - protected TRef managementEndpoint; - - /// - /// User-Service Communication Relay Point - /// - private TRef controlProxy; - - protected TRef directory; - private TRef senderRef; - private TRef receiverRef; - - private TRef dummyRef; - - internal Service(string! serviceName, - string! binaryName, - ServiceType serviceType, - [Claims]DirectoryServiceContract.Imp:Ready! ds) + public Service(InternalServiceConfig config) { - this.serviceName = serviceName; - this.binaryName = binaryName; - this.serviceType = serviceType; - - ThreadTerminationContract.Imp! imp; - ThreadTerminationContract.Exp! exp; - ThreadTerminationContract.NewChannel(out imp, out exp); - senderRef = new TRef(imp); - receiverRef = new TRef(exp); - directory = new TRef(ds); - } - - ~Service() - { - delete directory.Acquire(); - delete senderRef.Acquire(); - delete receiverRef.Acquire(); - } - - /// - /// Starts this service counter part. Doesn't start any service process. - /// - internal void Start([Claims]ServiceControlContract.Exp:Start! ep) - { - ep.SendSuccess(); - controlProxy = new TRef(ep); - thread = new Thread(new ThreadStart(Run)); - thread.Start(); - } - - /// - /// Terminates the service counter part. - /// - internal void Stop(out ServiceControlContract.Exp:Ready! ep) - { - ThreadTerminationContract.Imp:Start! leader; + this.ServiceName = config.ServiceName; + this.Config = config; + this.dbgprefix = String.Format("SMS[{0,-20}] ", config.ServiceName); + this.NextThinkTime = Util.SchedulerTimeNever; - // Terminates Run() - leader = senderRef.Acquire(); - try { - leader.SendStop(); - leader.RecvAckStop(); - thread.Join(); - } - catch (Exception) { - DebugStub.Print("Service: already stopped.\n"); - } + InternalServiceStatus status; + status.State = ServiceState.Stopped; + status.TotalActiveClients = -1; + status.TotalActiveProcesses = 0; + status.ConnectQueueLength = 0; + status.ProcessId = -1; + status.LastStartFailed = false; + status.LastStartError = ServiceError.None; + + this.LastServiceStatusPublished = status; + } - // Give EP back - ep = controlProxy.Acquire(); - controlProxy = null; + #region Data Fields - // Reuse for the next connection - senderRef.Release(leader); + public string! dbgprefix; + + #region Configuration + public readonly string! ServiceName; + public string[] DependentServices; + public bool IsAdministrativelyDisabled { get { return Config.IsAdministrativelyDisabled; } } + public InternalServiceConfig Config; + #endregion + + public readonly ArrayList/**/! Processes = new ArrayList(); + + public bool IsMarkedForDeletion; + public bool IsDeleted; + + public bool IsDefective; + + public bool LastStartFailed; + public ServiceError LastStartError; + public SchedulerTime LastStartFailedTime; + + public SchedulerTime NextThinkTime; + + /// + /// This is the last service status that we have published to management clients that are + /// watching service status. When there is any possibility that the status of a service + /// has changed, we build a new ServiceStatus object, then compare it to this field. + /// If any field has changed, then we notify all service watchers. + /// + public InternalServiceStatus LastServiceStatusPublished; + + public bool CanStartServiceProcess() + { + if (this.IsAdministrativelyDisabled) + return false; + + if (this.Processes.Count >= this.Config.MaxProcesses) + return false; + + if (LastStartFailed) + return false; + + return true; } /// - /// Brings up the service that this object represents. Throws - /// ProcessCreateException. + /// Contains instances of ServiceConnectRequest, representing clients that want + /// to connect to a service. /// - internal virtual ServiceError StartServiceProcess() + private readonly Queue/**/! connectQueue = new Queue(); + + + public readonly ArrayList/**/! StatusWatchers = new ArrayList(); + + #endregion + + public void EnqueueConnectRequest( + [Claims]DirectoryServiceContract.Exp:Ready! dir, + DirectoryClientInfo! dirinfo, + string! subpath, + [Claims]ServiceContract.Exp:Start! channel, + SchedulerTime timeStarted) { - ServiceError report; - ManagedServiceContract.Imp! msClient; - ManagedServiceContract.Exp! msServer; - DirectoryServiceContract.Imp! dsClient; - - DebugStub.Print("Service: Create process '{0}'\n", - __arglist(binaryName)); - - process = new Process(new String[1]{binaryName}, null, 3); - - // Create a channel to the service - ManagedServiceContract.NewChannel(out msClient, out msServer); - if (!process.SetStartupEndpoint(0, (Endpoint * in ExHeap)msServer)) { - DebugStub.Print("Endpoint cannot be set. (ManagedService)\n"); - delete msClient; - return ServiceError.TryAgain; - } - dsClient = directory.Acquire(); - if (!process.SetStartupEndpoint(1, (Endpoint * in ExHeap)dsClient)) { - DebugStub.Print("Endpoint cannot be set. " + - "(DirectoryService)\n"); - delete msClient; - return ServiceError.TryAgain; - } - - // - // ServiceProxyContract Dummy - //NOTE: this is necessary even though the default service never - // be able to use. - ServiceProxyContract.Imp! imp; - ServiceProxyContract.Exp! exp; - ServiceProxyContract.NewChannel(out imp, out exp); - process.SetStartupEndpoint(2, (Endpoint * in ExHeap)imp); - dummyRef = new TRef(exp); - - process.Start(); - - // Make sure the process has been started - switch receive { - case msClient.RecvSuccess(): - managementEndpoint = new TRef(msClient); - report = ServiceError.None; - DebugStub.Print("-- Process ID: {0}\n", - __arglist(process.Id)); - break; - case msClient.ChannelClosed(): - DebugStub.Print("Service process's channel is closed.\n"); - delete msClient; - report = ServiceError.ChannelClosed; - break; - } - //DebugStub.Print("Service.sg: StartServiceProcess EXIT\n"); - return report; + ServiceConnectRequest! connect = new ServiceConnectRequest(dir, dirinfo, subpath, channel, timeStarted); + connectQueue.Enqueue(connect); + } + + public void EnqueueConnectRequest(ServiceConnectRequest! connect) + { + connectQueue.Enqueue(connect); + } + + public bool HasConnectRequests + { + get { return connectQueue.Count > 0; } + } + + public int ConnectQueueLength + { + get { return connectQueue.Count; } + } + + public ServiceConnectRequest! DequeueConnectRequest() + { + if (connectQueue.Count == 0) + throw new InvalidOperationException("There are no requests in the connect queue for this service."); + + ServiceConnectRequest! request = (ServiceConnectRequest!)connectQueue.Dequeue(); + return request; } - /// - /// Shuts down the service process. - /// - internal virtual ServiceError StopServiceProcess() + public void GetConfig(ref ServiceConfig config) { - ServiceError report = ServiceError.Unknown; - ManagedServiceContract.Imp:Ready ep; - - //DebugStub.Print("Service: ENTER StopServiceProcess\n"); - - StopPoll(); - - if (managementEndpoint == null) { - return ServiceError.NotFound; - } - - ep = managementEndpoint.Acquire(); - if (process.State != ProcessState.Stopped) { - try { - ep.SendStop(); - switch receive { - case ep.AckStop(): - report = ServiceError.None; - break; - case ep.ChannelClosed(): - report = ServiceError.None; - break; - } - } - catch (Exception) { - DebugStub.Print("already stopped\n"); - } - } - - process.Stop(); - delete ep; - managementEndpoint = null; - report = ServiceError.None; - - //DebugStub.Print("Service: EXIT StopServiceProcess\n"); - return report; - } - - internal virtual ServiceError RestartServiceProcess() - { - ServiceError report; - - report = StopServiceProcess(); - if (report == ServiceError.None) { - report = StartServiceProcess(); - } - return report; - } - - internal virtual ServiceError StartService() - { - ServiceError report; - ManagedServiceContract.Imp:Ready! client; - - //DebugStub.Print("Service: StartService ENTER\n"); - - client = managementEndpoint.Acquire(); - client.SendStartService(); - switch receive { - case client.AckStartService(): - report = ServiceError.None; - break; - case client.NakStartService(): - report = ServiceError.TryAgain; - break; - case client.ChannelClosed(): - report = ServiceError.ChannelClosed; - break; - } - managementEndpoint.Release(client); - - //DebugStub.Print("Service: StartService EXIT\n"); - - return report; - } - - internal virtual ServiceError StopService() - { - ServiceError report; - ManagedServiceContract.Imp:Ready! client; - - DebugStub.Print("Service: StopService ENTER\n"); - - client = managementEndpoint.Acquire(); - client.SendStopService(); - switch receive { - case client.AckStopService(): - report = ServiceError.None; - break; - case client.Busy(): - report = ServiceError.TryAgain; - break; - case client.ChannelClosed(): - DebugStub.Print("Service: client channel closed\n"); - report = ServiceError.ChannelClosed; - break; - } - - managementEndpoint.Release(client); - - DebugStub.Print("Service: StopService EXIT\n"); - - return report; - } - - internal virtual ServiceError RestartService() - { - ServiceError report; - - report = StopService(); - if (report == ServiceError.None) { - report = StartService(); - } - return report; - } - - internal virtual void StartPoll(long interval) {} - - internal virtual void StopPoll() {} - - internal bool IsBound() - { - if (controlProxy != null) { - return true; - } - else { - return false; + expose(config) + { + delete config.DisplayName; + delete config.ExecutableName; + delete config.ServiceName; + + this.Config.ToExchangeType(out config); + #if false + config.ServiceName = Bitter.FromString2(this.ServiceName); + config.DisplayName = Bitter.FromString2(this.DisplayName); + config.ExecutableName = Bitter.FromString2(this.ExecutableName); + config.IsAdministrativelyDisabled = this.IsAdministrativelyDisabled; + config.MinProcesses = this.MinProcesses; + config.MaxProcesses = this.MaxProcesses; + config.MaxClientsPerProcess = this.MaxClientsPerProcess; + config.MaxProcessAgeInSeconds = this.MaxProcessAgeInSeconds; + #endif } } + } + - internal bool IsReachable() + // represents a client who is attempting to connect to a service + // the service cannot process the request immediately. + // either the service is starting, or its control channel is busy, etc. + class ServiceConnectRequest + { + public ServiceConnectRequest( + [Claims]DirectoryServiceContract.Exp:Ready! dir, + DirectoryClientInfo! dirinfo, + string! subpath, + [Claims]ServiceContract.Exp:Start! channel, + SchedulerTime timeStarted) { - bool result = false; - ServiceError report; - ManagedServiceContract.Imp:Ready! ep; + this.subpath = subpath; + this.dirref = new TRef(dir); + this.channelref = new TRef(channel); + this.dirinfo = dirinfo; + this.timeStarted = timeStarted; + } + + public void Acquire( + out DirectoryServiceContract.Exp:Ready! dir, + out DirectoryClientInfo! dirinfo, + out string! subpath, + out ServiceContract.Exp:Start! channel, + out SchedulerTime timeStarted) + { + dir = this.dirref.Acquire(); + dirinfo = this.dirinfo; + subpath = this.subpath; + channel = this.channelref.Acquire(); + timeStarted = this.timeStarted; + } + + public void Release( + [Claims]DirectoryServiceContract.Exp:Ready! dir, + DirectoryClientInfo! dirinfo, + string! subpath, + [Claims]ServiceContract.Exp:Start! channel, + SchedulerTime timeStarted) + { + this.dirref.Release(dir); + this.subpath = subpath; + this.channelref.Release(channel); + this.timeStarted = timeStarted; + this.dirinfo = dirinfo; + } + + readonly TRef! dirref; + readonly TRef! channelref; + string! subpath; + DirectoryClientInfo! dirinfo; + SchedulerTime timeStarted; + + public void Dispose() + { + // dirref.Dispose(); + // channelref.Dispose(); + } + } + + struct InternalServiceStatus + { + public ServiceStatus ToExchange() + { + ServiceStatus status; + status.State = this.State; + status.TotalActiveClients = this.TotalActiveClients; + status.TotalActiveProcesses = this.TotalActiveProcesses; + status.ConnectQueueLength = this.ConnectQueueLength; + status.ProcessId = this.ProcessId; + status.LastStartError = this.LastStartError; + status.LastStartFailed = this.LastStartFailed; + return status; + } - ep = managementEndpoint.Acquire(); - ep.SendKnock(); - switch receive { - case ep.RecvAlive(): - result = true; - break; - case ep.ChannelClosed(): - result = false; - break; + public ServiceState State; + public int TotalActiveClients; + public int TotalActiveProcesses; + public int ConnectQueueLength; + public long ProcessId; + public bool LastStartFailed; + public ServiceError LastStartError; + + public static bool IsEqual(ref InternalServiceStatus left, ref InternalServiceStatus right) + { + return left.State == right.State + && left.TotalActiveClients == right.TotalActiveClients + && left.TotalActiveProcesses == right.TotalActiveProcesses + && left.ConnectQueueLength == right.ConnectQueueLength + && left.ProcessId == right.ProcessId + && left.LastStartFailed == right.LastStartFailed + && left.LastStartError == right.LastStartError; + } + + public static bool IsEqual(InternalServiceStatus left, InternalServiceStatus right) + { + return IsEqual(ref left, ref right); + } + } + + + /// + /// This structure is a "local" (in same process) equivalent to the exchangeable ServiceConfig type. + /// This is necessary because we can't store rep structures in local types, and because we want to use + /// System.String, not char[] in ExHeap for our strings. + /// + struct InternalServiceConfig + { + public string! ServiceName; + public string! DisplayName; + public string! ExecutableName; + public ServiceActivationMode ActivationMode; + public bool IsAdministrativelyDisabled; + public int MinProcesses; + public int MaxProcesses; + public int MaxClientsPerProcess; + public int MaxProcessAgeInSeconds; + + public InternalServiceConfig(ref ServiceConfig config) + { + expose (config) + { + this.ServiceName = Bitter.ToString2(config.ServiceName); + this.DisplayName = Bitter.ToString2(config.DisplayName); + this.ExecutableName = Bitter.ToString2(config.ExecutableName); + this.ActivationMode = config.ActivationMode; + this.IsAdministrativelyDisabled = config.IsAdministrativelyDisabled; + this.MinProcesses = config.MinProcesses; + this.MaxProcesses = config.MaxProcesses; + this.MaxClientsPerProcess = config.MaxClientsPerProcess; + this.MaxProcessAgeInSeconds = config.MaxProcessAgeInSeconds; } - managementEndpoint.Release(ep); - return result; } - - protected virtual void BeforeLoop() {} - protected virtual void AfterLoop() {} - - protected void Run() + + public ServiceConfig ToExchangeType() { - ServiceError report; - ServiceControlContract.Exp:Ready! ep; - ThreadTerminationContract.Exp:Start! signal; - - BeforeLoop(); - - ep = controlProxy.Acquire(); - signal = receiverRef.Acquire(); - for (;;) { - switch receive { - case ep.StartService(): - { - DebugStub.Print("Service: Start service\n"); - try { - report = StartServiceProcess(); - if (report != ServiceError.None) { - ep.SendNakStartService(report); - break; - } - - report = StartService(); - if (report != ServiceError.None) { - ep.SendNakStartService(report); - break; - } - - id = process.Id; - ServiceManager.Register(this); - ep.SendAckStartService(); - DebugStub.Print("Service: started\n"); - } - catch (ProcessCreateException) { - DebugStub.Print("Service: " + - "Process creation failed\n"); - ep.SendNakStartService(ServiceError.NotFound); - } - catch (ProcessStateException) { - ep.SendNakStartService(ServiceError.TryAgain); - } - break; - } - case ep.StopService(): - { - report = StopService(); - if (report != ServiceError.None) { - ep.SendNakStopService(report); - break; - } - report = StopServiceProcess(); - if (report != ServiceError.None) { - ep.SendNakStopService(report); - break; - } - ServiceManager.Deregister(this); - ep.SendAckStopService(); - break; - } - case ep.RestartService(): - { - DebugStub.Print("Service: Restarting '{0}'...\n", - __arglist(serviceName)); - report = RestartService(); - if (report != ServiceError.None) { - ep.SendNakRestartService(report); - break; - } - DebugStub.Print("Service: '{0}' restarted\n", - __arglist(serviceName)); - ep.SendAckRestartService(); - break; - } - case ep.StartPoll(interval): - { - StartPoll(interval); - ep.SendAckStartPoll(); - break; - } - case ep.StopPoll(): - { - StopPoll(); - ep.SendAckStopPoll(); - break; - } - case ep.ChannelClosed(): - { - controlProxy = null; - goto exit; - break; - } - case signal.Stop(): - { - signal.SendAckStop(); - goto exit; - break; - } - case signal.ChannelClosed(): - { - goto exit; - break; - } - } - } -exit: - if (controlProxy != null) { - controlProxy.Release(ep); - } - else { - delete ep; - } - - DebugStub.Print("service counterpart quit.\n"); - receiverRef.Release(signal); - - AfterLoop(); + ServiceConfig config = new ServiceConfig(); + config.ServiceName = Bitter.FromString2(this.ServiceName); + config.DisplayName = Bitter.FromString2(this.DisplayName); + config.ExecutableName = Bitter.FromString2(this.ExecutableName); + config.ActivationMode = this.ActivationMode; + config.IsAdministrativelyDisabled = this.IsAdministrativelyDisabled; + config.MinProcesses = this.MinProcesses; + config.MaxProcesses = this.MaxProcesses; + config.MaxClientsPerProcess = this.MaxClientsPerProcess; + config.MaxProcessAgeInSeconds = this.MaxProcessAgeInSeconds; + return config; } - - internal virtual string! Name + + public void ToExchangeType(out ServiceConfig config) { - get { return serviceName; } - private set {} - } - - internal virtual string! Binary - { - get { return binaryName; } - private set{} - } - - internal virtual ServiceType Type - { - get { return serviceType; } - private set{} - } - - internal virtual int Id - { - get { return id; } - private set{} - } - - internal virtual TRef! Endpoint - { - get { return managementEndpoint; } - private set{} - } - - public override bool Equals(object o) - { - Service s = o as Service; - if (s == null) { - return false; - } - return (this.process.Id == s.process.Id && - this.managementEndpoint == s.managementEndpoint); + config = new ServiceConfig(); + // expose (config) { + delete config.DisplayName; + delete config.ExecutableName; + delete config.ServiceName; + config.ServiceName = Bitter.FromString2(this.ServiceName); + config.DisplayName = Bitter.FromString2(this.DisplayName); + config.ExecutableName = Bitter.FromString2(this.ExecutableName); + config.ActivationMode = this.ActivationMode; + config.IsAdministrativelyDisabled = this.IsAdministrativelyDisabled; + config.MinProcesses = this.MinProcesses; + config.MaxProcesses = this.MaxProcesses; + config.MaxClientsPerProcess = this.MaxClientsPerProcess; + config.MaxProcessAgeInSeconds = this.MaxProcessAgeInSeconds; + // } } } } + diff --git a/base/Services/ServiceManager/ServiceController.sg b/base/Services/ServiceManager/ServiceController.sg deleted file mode 100644 index 6e7560e..0000000 --- a/base/Services/ServiceManager/ServiceController.sg +++ /dev/null @@ -1,216 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Services\ServiceManager\ServiceController.sg -// -// Note: Client counterpart -// -using System; -using System.Collections; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.ServiceManager; - -namespace Microsoft.Singularity.Services.ServiceManager -{ - // ServiceController is created per client and provides generic services, - // such as starting a service and enumerating current running services. - // ServiceController is created by ServiceManager when ServiceManager - // accepts a client connection. - internal class ServiceController - { - protected Acceptor acceptor; - - protected TRef endpoint; - protected TRef sender; - protected TRef signal; - - internal ServiceController(Acceptor acceptor, - [Claims]ServiceManagementContract.Exp:Start! ep) - { - this.acceptor = acceptor; - ep.SendSuccess(); - this.endpoint = new TRef(ep); - - ThreadTerminationContract.Imp! imp; - ThreadTerminationContract.Exp! exp; - ThreadTerminationContract.NewChannel(out imp, out exp); - this.sender = new TRef(imp); - this.signal = new TRef(exp); - } - - internal void Stop() - { - ThreadTerminationContract.Imp! imp; - imp = sender.Acquire(); - imp.SendStop(); - delete imp; - } - - /// - /// Service main loop. - /// - internal void Run() - { - ServiceManagementContract.Exp:Ready! ep; - ThreadTerminationContract.Exp:Start! sig; - - ep = endpoint.Acquire(); - sig = signal.Acquire(); - for (;;) { - switch receive { - case ep.Bind(info, control): - { - try { - string name = info->Name; - string binary = info->Binary; - Service service; - - DebugStub.Print("Binding '{0}({1})' ... ", - __arglist(name, binary)); - - // Create a new process - service = ServiceManager.NewService(name, - binary, - info->Type); - if (service != null) { - // Bind the given control to the service - // counterpart. This doesn't start the - // service itself. - service.Start(control); - ep.SendAckBind(); - DebugStub.Print(" done.\n"); - } - else { - ep.SendNotFound(control); - } - } - catch (ProcessCreateException) { - ep.SendNotFound(control); - } - catch (AccessControlException) { - ep.SendPermissionDenied(control); - } - delete info; - break; - } - case ep.GetControl(id, control): - { - Service service = ServiceManager.GetService(id); - if (service != null) { - if (!service.IsBound()) { - service.Start(control); - ep.SendAckGetControl(); - } - else { - ep.SendTryAgain(control); - } - } - else { - ep.SendNotFound(control); - } - break; - } - case ep.Unbind(id): - { - try { - Service service = ServiceManager.GetService(id); - if (service != null) { - if (service.IsBound()) { - ServiceControlContract.Exp:Ready! control; - service.Stop(out control); - // HI: It doesn't check the ownership of - // the control. - ep.SendAckUnbind(control); - } - else { - ep.SendControlNotFound(); - } - } - else { - ep.SendServiceNotFound(); - } - } - catch (AccessControlException) { - ep.SendControlPermissionDenied(); - } - break; - } - case ep.BeginEnumeration(): - { - IDictionaryEnumerator controls; - - controls = ServiceManager.GetEnumerator(); - if (controls == null) { - ep.SendEnumerationTerminated(); - } - else { - Service entry = null; - bool next = false; - do { - if (controls.MoveNext() == false) { - ep.SendEnumerationTerminated(); - break; - } - entry = (Service)controls.Value; - if (entry == null) { - ep.SendEnumerationTerminated(); - break; - } - - ServiceInfo* in ExHeap info; - - info = new[ExHeap] ServiceInfo(entry.Id, - entry.Name, - entry.Binary, - entry.Type); - ep.SendCurrent(info); - - switch receive { - case ep.RecvMoveNext(): - next = true; - break; - case ep.RecvEndEnumeration(): - next = false; - break; - case ep.ChannelClosed(): - next = false; - break; - } - } while (next); - } - break; - } - case ep.ChannelClosed(): - { - DebugStub.Print("Controller: Client Channel closed\n"); - goto exit; - break; - } - case sig.Stop(): - { - sig.SendAckStop(); - goto exit; - break; - } - case sig.ChannelClosed(): - { - DebugStub.Print("Controller: Signal ch closed\n"); - goto exit; - break; - } - } - } -exit: - delete ep; - delete sig; - acceptor.ReleaseController(this); - //DebugStub.Print("Exit Service Controller\n"); - } - } -} diff --git a/base/Services/ServiceManager/ServiceManager.csproj b/base/Services/ServiceManager/ServiceManager.csproj index 98f750d..23b8ef1 100644 --- a/base/Services/ServiceManager/ServiceManager.csproj +++ b/base/Services/ServiceManager/ServiceManager.csproj @@ -1,8 +1,6 @@  + + - - + diff --git a/base/Services/Smb/Client/DirectoryClient.sg b/base/Services/Smb/Client/DirectoryClient.sg index d165fc5..059c647 100644 --- a/base/Services/Smb/Client/DirectoryClient.sg +++ b/base/Services/Smb/Client/DirectoryClient.sg @@ -99,19 +99,17 @@ namespace Smb.Client void ITrackedThreadContext.ThreadRoutine() { + Thread.CurrentThread.DebugName = "dirclient"; expose (this) { - try - { + try { if (TraceSwitches.ShowDirectoryMessages) { DebugLine("Sending Success"); } _dir.SendSuccess(); - for (;;) - { - switch receive - { + for (;;) { + switch receive { case _dir.CreateDirectory(char[]! in ExHeap exdirName): if (TraceSwitches.ShowDirectoryMessages) { DebugLine("Received CreateDirectory: name: " + Bitter.ToString2(exdirName)); @@ -167,7 +165,8 @@ namespace Smb.Client // DebugLine("Path exists."); - } catch (Exception ex) { + } + catch (Exception ex) { DebugLine("FAILED to check if directory exists!"); Util.ShowExceptionDebug(ex, "failed to check if directory exists"); _dir.SendNakBind(new_dir, ErrorCode.Unknown); @@ -191,7 +190,8 @@ namespace Smb.Client DebugLine("Successfully bound new directory client."); _dir.SendAckBind(); - } else if ((new_file = exp as FileContract.Exp) != null) { + } + else if ((new_file = exp as FileContract.Exp) != null) { ErrorCode errorCode; SmbTransactor.Imp new_transactor = AddTransactor(out errorCode); @@ -207,13 +207,15 @@ namespace Smb.Client FileExp.CreateServiceThread(new_file, absolutePath, fileId, new_transactor); DebugLine("Bind: Successful, sending Ack"); _dir.SendAckBind(); - } else { + } + else { DebugLine("Bind: Failed to open file, error code = ", errorCode); _dir.SendNakBind(exp, errorCode); delete new_transactor; } - } else { + } + else { DebugLine("Received bind request for an unknown contract! Rejecting."); _dir.SendNakBind(exp, ErrorCode.BadArguments); } @@ -245,7 +247,8 @@ namespace Smb.Client CloseFileId(_transactor, fileId); DebugLine("File closed."); _dir.SendAckCreateFile(); - } else { + } + else { _dir.SendNakCreateFile(errorCode); } break; @@ -273,7 +276,8 @@ namespace Smb.Client FileExp.CreateServiceThread(exp, absolutePath, fileId, new_transactor); DebugLine("CreateAndBindFile: Successful, sending Ack"); _dir.SendAckCreateAndBindFile(); - } else { + } + else { delete new_transactor; DebugLine("CreateAndBindFile: Sending Nak, error code = " + errorCode); _dir.SendNakCreateAndBindFile(exp, ErrorCode.NotSupported); @@ -385,10 +389,12 @@ namespace Smb.Client if (TraceSwitches.ShowDirectoryMessages) DebugLine("Disconnecting..."); - } catch(Exception ex) { + } + catch (Exception ex) { DebugLine("DIRECTORY SERVICE ROUTINE THREW EXCEPTION!"); Util.ShowExceptionDebug(ex); - } finally { + } + finally { // delete _dir; // delete _transactor; } @@ -429,7 +435,8 @@ namespace Smb.Client { if (path.StartsWith("/")) { return path; - } else { + } + else { if (_smbpath.EndsWith("/")) return _smbpath + path; else @@ -534,7 +541,8 @@ namespace Smb.Client fileId = response.Fid; errorCode = ErrorCode.NoError; - } finally { + } + finally { delete responseEncoded; } return true; @@ -553,36 +561,36 @@ namespace Smb.Client bool CheckDirectoryExists(string! absolutePath) { - /* - ByteWriter! data = new ByteWriter(); - data.WriteStringUnicode(absolutePath, true); - - int messageLength = sizeof(SmbCheckDirectoryRequest) + data.Length; - byte[] in ExHeap request_encoded = new[ExHeap] byte[messageLength]; - ref SmbCheckDirectoryRequest request = ref request_encoded[0]; - - request.TransactionHeader.Header.Prepare(SmbCommand.CheckDirectory, 0); - request.TransactionHeader.TotalParameterCount = 0; - request.TransactionHeader.TotalDataCount = (ushort)data.Length; - request.TransactionHeader.MaxParameterCount = 50; - - request.TransactionHeader.BufferFormat = 4; - data.CopyTo(request_encoded, sizeof(SmbCheckDirectoryRequest)); - - transactor.SendRequest(request_encoded, messageLength); - - byte[]! in ExHeap response_encoded = WaitResponse(dir, transactor); - - delete response_encoded; - */ + // + // ByteWriter! data = new ByteWriter(); + // data.WriteStringUnicode(absolutePath, true); +// + // int messageLength = sizeof(SmbCheckDirectoryRequest) + data.Length; + // byte[] in ExHeap request_encoded = new[ExHeap] byte[messageLength]; + // ref SmbCheckDirectoryRequest request = ref request_encoded[0]; +// + // request.TransactionHeader.Header.Prepare(SmbCommand.CheckDirectory, 0); + // request.TransactionHeader.TotalParameterCount = 0; + // request.TransactionHeader.TotalDataCount = (ushort)data.Length; + // request.TransactionHeader.MaxParameterCount = 50; +// + // request.TransactionHeader.BufferFormat = 4; + // data.CopyTo(request_encoded, sizeof(SmbCheckDirectoryRequest)); +// + // transactor.SendRequest(request_encoded, messageLength); +// + // byte[]! in ExHeap response_encoded = WaitResponse(dir, transactor); +// + // delete response_encoded; + // DebugLine("CheckDirectoryExists - not yet implemented, returning true"); return true; } - /* - This method implements directory enumeration. It uses the SMB request - TRANS2_FIND_FIRST2 to search a directory. - */ + // + //This method implements directory enumeration. It uses the SMB request + //TRANS2_FIND_FIRST2 to search a directory. + // void EnumerateDirectory(SmbTransactor.Imp! transactor, DirectoryServiceContract.Exp:Enumerate! dir) { byte[]! in ExHeap request = EncodeFindFirst2Request(_path); @@ -594,8 +602,7 @@ namespace Smb.Client bool moreRecords; - for (;;) - { + for (;;) { // At this point, we are waiting for a response from either a FIND FIRST or a // FIND NEXT request. Since most of the processing is the same, we combine it here. @@ -610,7 +617,8 @@ namespace Smb.Client if (status == NtStatus.NoSuchFile) { DebugLine("Received STATUS_NO_SUCH_FILE from server."); dir.SendEnumerationTerminated(ErrorCode.NoError); - } else { + } + else { DebugLine("FIND_FIRST2 request failed. Received error from server."); dir.SendEnumerationTerminated(GetDsErrorFromSmb(ref header)); } @@ -638,7 +646,8 @@ namespace Smb.Client dir.SendEnumerationEntries(records, moreRecords); // Now 'dir' is in the EnumerateAck state. // Valid in messages are: ReadEnumeration, EndEnumeration. - } finally { + } + finally { delete response; } @@ -661,7 +670,8 @@ namespace Smb.Client if (TraceSwitches.ShowDirectoryMessages) { DebugLine("Received ReadEnumeration."); } - } else { + } + else { if (TraceSwitches.ShowDirectoryMessages) { DebugLine("Received ReadEnumeration, but we already told the client there aren't any more!"); DebugLine("Silly client. Sending EnumerationEntries with more = false again."); @@ -718,12 +728,12 @@ namespace Smb.Client void FindClose(SmbTransactor.Imp! transactor, ushort searchId) { DebugLine("FindClose: not yet implemented"); - /* - byte[]! in ExHeap request = EncodeFindCloseRequest(searchId); - transactor.SendRequest(request); - byte[]! in ExHeap response = WaitResponse(transactor, "FIND CLOSE"); - delete response; - */ + // + //byte[]! in ExHeap request = EncodeFindCloseRequest(searchId); + //transactor.SendRequest(request); + //byte[]! in ExHeap response = WaitResponse(transactor, "FIND CLOSE"); + //delete response; + // } EnumerationRecords[] in ExHeap ExtractDirectoryEntries(byte[]! in ExHeap smbresponse, out bool moreRecords, out ushort searchId) @@ -1083,7 +1093,8 @@ namespace Smb.Client DebugLine("FAILED to create directory: " + response.GetErrorText()); ErrorCode errorCode = GetDsErrorFromSmb(ref response); dir.SendNakCreateDirectory(errorCode); - } else { + } + else { DebugLine("Successfully created directory: " + singpath); dir.SendAckCreateDirectory(); } @@ -1104,13 +1115,11 @@ namespace Smb.Client { string! path_as_smb = path.Replace(SingularityPathSeparator, NtPathSeparator); - if (path_as_smb.StartsWith(NtPathSeparatorString)) - { + if (path_as_smb.StartsWith(NtPathSeparatorString)) { // The path is already in absolute form. return path_as_smb; } - else - { + else { // The path is a relative path. Qualify it using the path of the current directory. if (_smbpath.EndsWith(NtPathSeparatorString)) return _smbpath + path_as_smb; @@ -1161,7 +1170,8 @@ namespace Smb.Client // DebugLine("Server failed DELETE FILE request: " + response.GetErrorText()); ErrorCode errorCode = GetDsErrorFromSmb(ref response); dir.SendNakDeleteFile(errorCode); - } else { + } + else { // DebugLine("File successfully deleted."); dir.SendAckDeleteFile(); } @@ -1209,7 +1219,8 @@ namespace Smb.Client DebugLine("Server failed DELETE FILE request: " + response.GetErrorText()); ErrorCode errorCode = GetDsErrorFromSmb(ref response); dir.SendNakDeleteFile(errorCode); - } else { + } + else { DebugLine("File successfully deleted."); dir.SendAckDeleteFile(); } @@ -1344,8 +1355,15 @@ namespace Smb.Client NodeType nodeType = (info.Directory != 0) ? NodeType.Directory : NodeType.File; - dir.SendAckGetAttributes(nodeType, info.EndOfFile); - } finally { + FileAttributesRecord fileAttributes = + new FileAttributesRecord(); + fileAttributes.Type = nodeType; + fileAttributes.FileSize = info.EndOfFile; + // TODO: FIXFIX what about timestamp info + + dir.SendAckGetAttributes(fileAttributes); + } + finally { delete responseEncoded; } return; diff --git a/base/Services/Smb/Client/FileExp.sg b/base/Services/Smb/Client/FileExp.sg index be85fec..3b8f0cf 100644 --- a/base/Services/Smb/Client/FileExp.sg +++ b/base/Services/Smb/Client/FileExp.sg @@ -50,16 +50,16 @@ using Smb.Shared; namespace Smb.Client { - /* - This class implements (exports) FileContract. A thread is devoted to servicing incoming requests. - The "upper" edge of this thread is FileContract.Exp. The "lower" edge of this thread is SmbTransactor.Imp, - which is the internal communications pipe to the SMB request multiplexer. - - The service thread will terminate when several conditions occur: - * The FileContract.Exp channel closes. - * The SmbTransactor.Imp channel closes. - * Any internal exception reaches the main switch-receive loop. - */ + // + //This class implements (exports) FileContract. A thread is devoted to servicing incoming requests. + //The "upper" edge of this thread is FileContract.Exp. The "lower" edge of this thread is SmbTransactor.Imp, + //which is the internal communications pipe to the SMB request multiplexer. + // + //The service thread will terminate when several conditions occur: + // * The FileContract.Exp channel closes. + // * The SmbTransactor.Imp channel closes. + // * Any internal exception reaches the main switch-receive loop. + // class FileExp { public static void CreateServiceThread([Claims]FileContract.Exp:Start! file_exp, string! path, ushort fileId, [Claims]SmbTransactor.Imp:Ready! transactor) @@ -96,6 +96,8 @@ namespace Smb.Client private void ThreadServiceRoutine() { + Thread.CurrentThread.DebugName = "fileclient"; + FileContract.Exp:Start! file = _file_exp.Acquire(); SmbTransactor.Imp! transactor = _transactor.Acquire(); @@ -148,25 +150,27 @@ namespace Smb.Client } } - } catch(Exception ex) { + } + catch (Exception ex) { DebugLine("An exception has reached the service loop of FileContract service thread."); DebugLine("The service thread will now terminate."); Util.ShowExceptionDebug(ex, "FileContract service thread failed"); - } finally { + } + finally { delete file; delete transactor; } } - /* - This method reads a block of data from the remote file server. The file must already be open; - the 16-bit file ID is taken from the _fileId field of the class instance of this method. - If the data block cannot fit into a single SMB request, then this method will issue more than - one data transfer request. Therefore, there is no guarantee of any sort of "atomic" data transfer. - - This method is synchronous; it will not return until the data has been read, or until an error - occurs on the SMB client channel (transactor). - */ + // + //This method reads a block of data from the remote file server. The file must already be open; + //the 16-bit file ID is taken from the _fileId field of the class instance of this method. + //If the data block cannot fit into a single SMB request, then this method will issue more than + //one data transfer request. Therefore, there is no guarantee of any sort of "atomic" data transfer. + // + //This method is synchronous; it will not return until the data has been read, or until an error + //occurs on the SMB client channel (transactor). + // long Read( FileContract.Exp! file, SmbTransactor.Imp! transactor, @@ -239,7 +243,8 @@ namespace Smb.Client DebugLine("ReadAndX failed: " + header.GetErrorText()); delete responseEncoded; goto return_error; - } else { + } + else { if (responseEncoded.Length < sizeof(SmbReadAndXResponse)) { DebugLine("Response is too small to be a valid SmbReadAndXResponse."); delete responseEncoded; @@ -266,7 +271,8 @@ namespace Smb.Client Bitter.Copy(buf, (int)(bufOffset + bytesRead), (int)dataLength, responseEncoded, (int)dataOffset); bytesRead += dataLength; delete responseEncoded; - } else { + } + else { DebugLine("Server indicates that zero bytes were transferred in this exchange."); DebugLine("Returning {0} bytes read.", bytesRead); delete responseEncoded; @@ -297,7 +303,8 @@ namespace Smb.Client // Some bytes were transferred. error = 0; return bytesRead; - } else { + } + else { // No bytes were transferred, and there was an error. DebugLine("No bytes transferred. Returning -1 for bytes transferred."); error = -1; @@ -305,15 +312,15 @@ namespace Smb.Client } } - /* - This method writes a block of data to the remote file server. The file must already be - open; the 16-bit file ID is taken from the _fileId field of the class instance of this method. - If the data block cannot fit into a single SMB request, then this method will issue more than - one data transfer request. Therefore, there is no guarantee of any sort of "atomic" data writes. - - This method is synchronous; it will not return until the data has been written, or until an error - occurs on the SMB client channel (transactor). - */ + // + //This method writes a block of data to the remote file server. The file must already be + //open; the 16-bit file ID is taken from the _fileId field of the class instance of this method. + //If the data block cannot fit into a single SMB request, then this method will issue more than + //one data transfer request. Therefore, there is no guarantee of any sort of "atomic" data writes. + // + //This method is synchronous; it will not return until the data has been written, or until an error + //occurs on the SMB client channel (transactor). + // long Write( FileContract.Exp! file, SmbTransactor.Imp! transactor, @@ -401,7 +408,8 @@ namespace Smb.Client DebugLine("WriteAndX failed: " + header.GetErrorText()); delete responseEncoded; goto return_error; - } else { + } + else { if (responseEncoded.Length < sizeof(SmbWriteAndXResponse)) { DebugLine("Response is too small to be a valid SmbWriteAndXResponse."); delete responseEncoded; @@ -445,7 +453,8 @@ namespace Smb.Client // Some bytes were transferred. error = 0; return bytesWritten; - } else { + } + else { // No bytes were transferred, and there was an error. DebugLine("No bytes transferred. Returning -1 for bytes transferred."); error = -1; diff --git a/base/Services/Smb/Client/Main.sg b/base/Services/Smb/Client/Main.sg index 0511c73..9cc8fb1 100644 --- a/base/Services/Smb/Client/Main.sg +++ b/base/Services/Smb/Client/Main.sg @@ -8,34 +8,34 @@ // // Note: SMB Network Filesystem Client // -// This file contains the process entry point for the SMB client driver. -// The SMB client is implemented as two different executables: -// SmbClientManager and SmbClient. SmbClientManager is a singleton service; -// it is started when the system boots, and there is only ever one instance -// of the service. That service creates instances of the SmbClient process, -// each of which is responsible for processing one SMB mapping / mount. +// This file contains the process entry point for the SMB client driver. +// The SMB client is implemented as two different executables: +// SmbClientManager and SmbClient. SmbClientManager is a singleton service; +// it is started when the system boots, and there is only ever one instance +// of the service. That service creates instances of the SmbClient process, +// each of which is responsible for processing one SMB mapping / mount. // -// This file (assembly) contains the implementation of SmbClient. At startup, -// the process acquires these two startup endpoints: +// This file (assembly) contains the implementation of SmbClient. At startup, +// the process acquires these two startup endpoints: // -// Index 0: SmbClientControllerContract.Exp +// Index 0: SmbClientControllerContract.Exp // -// Index 1: SmbMuxNotify.Imp +// Index 1: SmbMuxNotify.Imp // -// Both of these endpoints must be present (and be the correct type) in order -// for the process to start correctly. +// Both of these endpoints must be present (and be the correct type) in order +// for the process to start correctly. // -// SmbClientManager service uses the SmbClientControllerContract channel to -// control the SmbClient process. The SmbClient process uses the SmbMuxNotify -// channel to notify the SmbClientManager process of changes in its state -// (e.g. connected, disconnected, etc.). +// SmbClientManager service uses the SmbClientControllerContract channel to +// control the SmbClient process. The SmbClient process uses the SmbMuxNotify +// channel to notify the SmbClientManager process of changes in its state +// (e.g. connected, disconnected, etc.). // -// All command-line arguments are ignored. (Actually, the ConsoleTransform -// will inspect them, and will get grouchy if any are present.) +// All command-line arguments are ignored. (Actually, the ConsoleTransform +// will inspect them, and will get grouchy if any are present.) // -// The current implementation uses a single thread, implemented in TransportMux, -// to service all incoming requests. For now, some of the code is still in Main.sg, -// and some is in TransportMux. This will be changed later; the two will be merged. +// The current implementation uses a single thread, implemented in TransportMux, +// to service all incoming requests. For now, some of the code is still in Main.sg, +// and some is in TransportMux. This will be changed later; the two will be merged. // @@ -69,329 +69,327 @@ namespace Smb.Client { [ConsoleCategory(HelpMessage="Start the SMB client", DefaultAction=true)] internal class SmbCommandParameters - { - [Endpoint] - public TRef Controller; - - [Endpoint] - public TRef Notifier; - - + { + [Endpoint] + public TRef Controller; + + [Endpoint] + public TRef Notifier; + + reflective internal SmbCommandParameters(); - + internal int AppMain() { - return Program.Main(this); + return Program.AppMain(this); } } - public static class Program - { - /** The UNC path to the remote resource, e.g. \\server\share. */ - static string! _UncPath; - - /** - - The path of the local mount point, which allows Singularity processes - to communicate with the SMB service. The SMB client exports the DirectoryServiceContract - contract at this path. - - */ - static string! _MountPath; - - /** - - The username to use when authenticating with the remote SMB server. - - */ - static string! _CredentialsName; - static string! _CredentialsTag; - - /** - The name of the remote server, parsed from the UNC path. - For now, this value can only be an IPv4 address. - - */ - static string! _ServerName; - - /** - The resource name part of the UNC; e.g., for a UNC of \\server\share, - it's the "share" part. - - */ - static string! _ResourceName; - - internal static string! UncPath { get { return (!)_UncPath; } } - internal static string! MountPath { get { return (!)_MountPath; } } - internal static string! CredentialsName { get { return (!)_CredentialsName; } } - internal static string! CredentialsTag { get { return (!)_CredentialsTag; } } - - internal static int Main(SmbCommandParameters! args) - { - try { - ServiceProviderContract.Exp! namespace_provider; - + public static class Program + { + /// The UNC path to the remote resource, e.g. \\server\share. + static string! _UncPath; + + /// + /// The path of the local mount point, which allows Singularity processes + /// to communicate with the SMB service. The SMB client exports the DirectoryServiceContract + /// contract at this path. + /// + static string! _MountPath; + + /// + /// The username to use when authenticating with the remote SMB server. + /// + static string! _CredentialsName; + static string! _CredentialsTag; + + /// + /// The name of the remote server, parsed from the UNC path. + /// For now, this value can only be an IPv4 address. + /// + static string! _ServerName; + + /// + /// The resource name part of the UNC; e.g., for a UNC of \\server\share, + /// it's the "share" part. + /// + + static string! _ResourceName; + + internal static string! UncPath { get { return (!)_UncPath; } } + internal static string! MountPath { get { return (!)_MountPath; } } + internal static string! CredentialsName { get { return (!)_CredentialsName; } } + internal static string! CredentialsTag { get { return (!)_CredentialsTag; } } + + internal static int AppMain(SmbCommandParameters! args) + { + try { + ServiceProviderContract.Exp! namespace_provider; + #if true - Endpoint* in ExHeap controller_ep = Process.GetStartupEndpoint(0); - if (controller_ep == null) { - DebugLine("Endpoint 0 is not assigned."); - DebugLine("Expected SmbClientControllerContract.Exp at startup endpoint 0."); - DebugStub.Break(); - return -1; - } - - SmbClientControllerContract.Exp controller = controller_ep as SmbClientControllerContract.Exp; - if (controller == null) { - DebugLine("Endpoint 0 is wrong channel type."); - DebugLine("Expected SmbClientControllerContract.Exp at startup endpoint 0."); - DebugStub.Break(); - delete controller_ep; - return -1; - } + Endpoint* in ExHeap controller_ep = Process.GetStartupEndpoint(0); + if (controller_ep == null) { + DebugLine("Endpoint 0 is not assigned."); + DebugLine("Expected SmbClientControllerContract.Exp at startup endpoint 0."); + DebugStub.Break(); + return -1; + } + + SmbClientControllerContract.Exp controller = controller_ep as SmbClientControllerContract.Exp; + if (controller == null) { + DebugLine("Endpoint 0 is wrong channel type."); + DebugLine("Expected SmbClientControllerContract.Exp at startup endpoint 0."); + DebugStub.Break(); + delete controller_ep; + return -1; + } #else - // -XXX- This TRef isn't being set up. - if (args.Controller == null) { - DebugLine("SMB process controller channel was null! Can't do anything!"); - return -1; - } - SmbClientControllerContract.Exp! controller = args.Controller.Acquire(); + // -XXX- This TRef isn't being set up. + if (args.Controller == null) { + DebugLine("SMB process controller channel was null! Can't do anything!"); + return -1; + } + SmbClientControllerContract.Exp! controller = args.Controller.Acquire(); #endif - Endpoint* in ExHeap ep = Process.GetStartupEndpoint(1); - if (ep == null) { - DebugLine("Endpoint 1 is not assigned."); - DebugLine("Expected SmbMuxNotifier.Imp at startup endpoint 1."); - DebugStub.Break(); - delete controller_ep; - return -1; - } - - SmbMuxNotifier.Imp notifier = ep as SmbMuxNotifier.Imp; - if (notifier == null) { - DebugLine("Endpoint 1 is wrong channel type."); - DebugLine("Expected SmbMuxNotifier.Imp at startup endpoint 1."); - DebugStub.Break(); - delete controller_ep; - delete ep; - return -1; - } + Endpoint* in ExHeap ep = Process.GetStartupEndpoint(1); + if (ep == null) { + DebugLine("Endpoint 1 is not assigned."); + DebugLine("Expected SmbMuxNotifier.Imp at startup endpoint 1."); + DebugStub.Break(); + delete controller_ep; + return -1; + } - // We now have a control channel established to the process that started this one. - // That process should be SmbClientManager. We don't really care, as long as that - // process uses SmbClientControllerContract to communicate with us. Next, we wait - // for SmbClientControllerContract to send a Configure message, which contains the - // configuration information for this SMB client process. - - DebugLine("Waiting for Configure request on control channel."); - switch receive { - case controller.Configure(config): - DebugLine("Received configuration:"); - _UncPath = Bitter.ToString2(config.UncPath); - _MountPath = Bitter.ToString2(config.MountPath); - _CredentialsName = Bitter.ToString2(config.CredentialsName); - _CredentialsTag = Bitter.ToString2(config.Tag); - config.Dispose(); - - controller.SendOk(); - break; + SmbMuxNotifier.Imp notifier = ep as SmbMuxNotifier.Imp; + if (notifier == null) { + DebugLine("Endpoint 1 is wrong channel type."); + DebugLine("Expected SmbMuxNotifier.Imp at startup endpoint 1."); + DebugStub.Break(); + delete controller_ep; + delete ep; + return -1; + } - case controller.ChannelClosed(): - throw new Exception("Controller channel closed!"); - } - - ParseUncPath(_UncPath, out _ServerName, out _ResourceName); - - DebugLine("SMB client is starting. Parameters from controller service:"); - DebugLine(" UNC path: " + _UncPath); - DebugLine(" Mount path: " + _MountPath); - DebugLine(" Username: " + _CredentialsName); + // We now have a control channel established to the process that started this one. + // That process should be SmbClientManager. We don't really care, as long as that + // process uses SmbClientControllerContract to communicate with us. Next, we wait + // for SmbClientControllerContract to send a Configure message, which contains the + // configuration information for this SMB client process. - try { - // - // Next, register the directory service that represents the mount path. - // This is what filesystem clients use to access the remote namespace on the SMB server. - // + DebugLine("Waiting for Configure request on control channel."); + switch receive { + case controller.Configure(config): + DebugLine("Received configuration:"); + _UncPath = Bitter.ToString2(config.UncPath); + _MountPath = Bitter.ToString2(config.MountPath); + _CredentialsName = Bitter.ToString2(config.CredentialsName); + _CredentialsTag = Bitter.ToString2(config.Tag); + config.Dispose(); - namespace_provider = DirectoryUtil.RegisterService(_MountPath); - } catch (Exception ex) { - DebugLine("Failed to register namespace (mount path)."); - DebugLine(" Mount path: " + _MountPath); - Util.ShowExceptionDebug(ex); - return -1; - } - - // - // Next, we start the thread that multiplexes access to the TCP connection to the server. - // - - TransportMux.Run( - namespace_provider, - controller, - notifier, - _ServerName, - _ResourceName); - - - // quit: - // -XXX- We should obviously do something a bit more graceful here. - return 0; - - } catch (Exception ex) { - ShowException(ex); - return -1; - } - } - - static SmbClientConfig GetConfig() - { - SmbClientConfig config = new SmbClientConfig(); - config.UncPath = Bitter.FromString2(_UncPath); - config.MountPath = Bitter.FromString2(_MountPath); - config.CredentialsName = Bitter.FromString2(_CredentialsName); - config.Tag = Bitter.FromString2(_CredentialsTag); - return config; - } - - static void DebugLine(string msg) - { - DebugStub.WriteLine("SMB: MAIN: " + msg); - } - - static void DebugLine(string format, params object[] args) - { - DebugLine(String.Format(format, args)); - } + controller.SendOk(); + break; - public static void ShowException(Exception! chain) - { - Exception current = chain; - while (current != null) - { - DebugLine("{0}: {1}", current.GetType().FullName, current.Message); - current = current.InnerException; - } - } + case controller.ChannelClosed(): + throw new Exception("Controller channel closed!"); + } + + ParseUncPath(_UncPath, out _ServerName, out _ResourceName); + + DebugLine("SMB client is starting. Parameters from controller service:"); + DebugLine(" UNC path: " + _UncPath); + DebugLine(" Mount path: " + _MountPath); + DebugLine(" Username: " + _CredentialsName); + + try { + // + // Next, register the directory service that represents the mount path. + // This is what filesystem clients use to access the remote namespace on the SMB server. + // + + namespace_provider = DirectoryUtil.RegisterService(_MountPath); + } + catch (Exception ex) { + DebugLine("Failed to register namespace (mount path)."); + DebugLine(" Mount path: " + _MountPath); + Util.ShowExceptionDebug(ex); + return -1; + } + + // + // Next, we start the thread that multiplexes access to the TCP connection to the server. + // + + TransportMux.Run( + namespace_provider, + controller, + notifier, + _ServerName, + _ResourceName); + + + // quit: + // -XXX- We should obviously do something a bit more graceful here. + return 0; + + } + catch (Exception ex) { + ShowException(ex); + return -1; + } + } + + static SmbClientConfig GetConfig() + { + SmbClientConfig config = new SmbClientConfig(); + config.UncPath = Bitter.FromString2(_UncPath); + config.MountPath = Bitter.FromString2(_MountPath); + config.CredentialsName = Bitter.FromString2(_CredentialsName); + config.Tag = Bitter.FromString2(_CredentialsTag); + return config; + } + + static void DebugLine(string msg) + { + DebugStub.WriteLine(msg); + } + + static void DebugLine(string format, params object[] args) + { + DebugLine(String.Format(format, args)); + } + + public static void ShowException(Exception! chain) + { + Exception current = chain; + while (current != null) + { + DebugLine("{0}: {1}", current.GetType().FullName, current.Message); + current = current.InnerException; + } + } + + static void ParseUncPath(string! unc, out string! server, out string! resource) + { + if (!unc.StartsWith("\\\\")) + throw new Exception(String.Format("The UNC path '{0}' is invalid. All UNC paths must begin with '\\\\'.", unc)); + + int separator = unc.IndexOf('\\', 2); + assert (separator < 0) || (separator >= 2); + if (separator < 0 || separator == 2) + throw new Exception(String.Format("The UNC path '{0}' is invalid. The server name is missing.", unc)); + + if (separator + 1 == unc.Length) + throw new Exception(String.Format("The UNC path '{0}' is invalid. The resource name is missing.", unc)); + + server = unc.Substring(2, separator - 2); + resource = unc.Substring(separator + 1); + } + } + + public class ClientStatusSnapshot + { + public ClientStatusSnapshot() + { + } + + public ClientStatusSnapshot(SmbClientStatus status) + { + this.ConnectionStatus = status.ConnectionStatus; + this.ReasonDisconnected = status.ReasonDisconnected; + this.ServerOperatingSystem = Bitter.ToString(status.ServerOperatingSystem); + this.ServerDomainName = Bitter.ToString(status.ServerDomainName); + this.ServerMachineName = Bitter.ToString(status.ServerMachineName); + this.ActiveCredentialsName = Bitter.ToString(status.ActiveCredentialsName); + } - static void ParseUncPath(string! unc, out string! server, out string! resource) - { - if (!unc.StartsWith("\\\\")) - throw new Exception(String.Format("The UNC path '{0}' is invalid. All UNC paths must begin with '\\\\'.", unc)); - - int separator = unc.IndexOf('\\', 2); - assert (separator < 0) || (separator >= 2); - if (separator < 0 || separator == 2) - throw new Exception(String.Format("The UNC path '{0}' is invalid. The server name is missing.", unc)); - - if (separator + 1 == unc.Length) - throw new Exception(String.Format("The UNC path '{0}' is invalid. The resource name is missing.", unc)); - - server = unc.Substring(2, separator - 2); - resource = unc.Substring(separator + 1); - } - } - - public class ClientStatusSnapshot - { - public ClientStatusSnapshot() - { - } - - public ClientStatusSnapshot(SmbClientStatus status) - { - this.ConnectionStatus = status.ConnectionStatus; - this.ReasonDisconnected = status.ReasonDisconnected; - this.ServerOperatingSystem = Bitter.ToString(status.ServerOperatingSystem); - this.ServerDomainName = Bitter.ToString(status.ServerDomainName); - this.ServerMachineName = Bitter.ToString(status.ServerMachineName); - this.ActiveCredentialsName = Bitter.ToString(status.ActiveCredentialsName); - } - public SmbClientConnectionStatus ConnectionStatus; public SmbReasonDisconnected ReasonDisconnected; public string ServerOperatingSystem; public string ServerDomainName; public string ServerMachineName; public string ActiveCredentialsName; - + public SmbClientStatus ToExchange() { - SmbClientStatus ex = new SmbClientStatus(); - ex.ConnectionStatus = this.ConnectionStatus; - ex.ReasonDisconnected = this.ReasonDisconnected; - ex.ServerOperatingSystem = Bitter.FromString(ServerOperatingSystem); - ex.ServerDomainName = Bitter.FromString(this.ServerDomainName); - ex.ServerMachineName = Bitter.FromString(this.ServerMachineName); - ex.ActiveCredentialsName = Bitter.FromString(this.ActiveCredentialsName); - return ex; + SmbClientStatus ex = new SmbClientStatus(); + ex.ConnectionStatus = this.ConnectionStatus; + ex.ReasonDisconnected = this.ReasonDisconnected; + ex.ServerOperatingSystem = Bitter.FromString(ServerOperatingSystem); + ex.ServerDomainName = Bitter.FromString(this.ServerDomainName); + ex.ServerMachineName = Bitter.FromString(this.ServerMachineName); + ex.ActiveCredentialsName = Bitter.FromString(this.ActiveCredentialsName); + return ex; } - } - + } + class TrackedThreadStarter { - public static Thread StartTrackedThreadContext([Claims]ITrackedThreadContext! context) - { - TrackedThreadStarter starter = new TrackedThreadStarter(context); - ThreadStart threadStart = new ThreadStart(starter.ThreadMain); - Thread thread = new Thread(threadStart); - thread.Start(); - return thread; - } - - private TrackedThreadStarter([Claims]ITrackedThreadContext! context) - { - _context = new TContainer(context); - } - - private void ThreadMain() - { - assert _context != null; - ITrackedThreadContext! context = _context.Acquire(); - - try { - context.ThreadRoutine(); - } finally { - context.Dispose(); - } - } - - private TContainer _context; + public static Thread StartTrackedThreadContext([Claims]ITrackedThreadContext! context) + { + TrackedThreadStarter starter = new TrackedThreadStarter(context); + ThreadStart threadStart = new ThreadStart(starter.ThreadMain); + Thread thread = new Thread(threadStart); + thread.Start(); + return thread; + } + + private TrackedThreadStarter([Claims]ITrackedThreadContext! context) + { + _context = new TContainer(context); + } + + private void ThreadMain() + { + assert _context != null; + ITrackedThreadContext! context = _context.Acquire(); + + try { + context.ThreadRoutine(); + } + finally { + context.Dispose(); + } + } + + private TContainer _context; + } + interface ITrackedThreadContext : ITracked + { + void ThreadRoutine(); + } + + internal class RepStructCopier + { + public static char[]! in ExHeap Copy(char[]! in ExHeap array) + { + char[]! in ExHeap copy = new[ExHeap] char[array.Length]; + for (int i = 0; i < array.Length; i++) + copy[i] = array[i]; + return copy; + } + + public static char[] in ExHeap Copy2(char[] in ExHeap array) + { + if (array == null) + return null; + + char[]! in ExHeap copy = new[ExHeap] char[array.Length]; + for (int i = 0; i < array.Length; i++) + copy[i] = array[i]; + return copy; + } + + public static SmbClientStatus Copy(SmbClientStatus status) + { + SmbClientStatus copy = new SmbClientStatus(); + copy.ConnectionStatus = status.ConnectionStatus; + copy.ReasonDisconnected = status.ReasonDisconnected; + copy.ServerMachineName = Copy2(status.ServerMachineName); + copy.ServerDomainName = Copy2(status.ServerDomainName); + copy.ServerOperatingSystem = Copy2(status.ServerOperatingSystem); + return copy; + } } - interface ITrackedThreadContext : ITracked - { - void ThreadRoutine(); - } - - internal class RepStructCopier - { - public static char[]! in ExHeap Copy(char[]! in ExHeap array) - { - char[]! in ExHeap copy = new[ExHeap] char[array.Length]; - for (int i = 0; i < array.Length; i++) - copy[i] = array[i]; - return copy; - } - - public static char[] in ExHeap Copy2(char[] in ExHeap array) - { - if (array == null) - return null; - - char[]! in ExHeap copy = new[ExHeap] char[array.Length]; - for (int i = 0; i < array.Length; i++) - copy[i] = array[i]; - return copy; - } - - public static SmbClientStatus Copy(SmbClientStatus status) - { - SmbClientStatus copy = new SmbClientStatus(); - copy.ConnectionStatus = status.ConnectionStatus; - copy.ReasonDisconnected = status.ReasonDisconnected; - copy.ServerMachineName = Copy2(status.ServerMachineName); - copy.ServerDomainName = Copy2(status.ServerDomainName); - copy.ServerOperatingSystem = Copy2(status.ServerOperatingSystem); - return copy; - } - } } diff --git a/base/Services/Smb/Client/SmbClient.csproj b/base/Services/Smb/Client/SmbClient.csproj index c6a451f..786c1c3 100644 --- a/base/Services/Smb/Client/SmbClient.csproj +++ b/base/Services/Smb/Client/SmbClient.csproj @@ -1,8 +1,6 @@  " + SmbDebug.GetSmbMessageSummary(request, 0)); - } + const int NativeSmbPort = 445; - SendData(request); - Transactor_EnterReceiveState(tran, TransactorState.WaitingReceiveResponse); - break; - } + // if true, then we can send a message on the TCP connection channel. + // if false, then we can only wait for a response. + bool _connectionCanSend; - case TransactorState.WaitingSendPrimaryTransactionRequest: - { - // It's a "transaction" style request. Next, we need to determine whether the - // entire transaction request can fit inside a single SMB message. If so, the - // next step is to wait for the transaction response. If not, then the next - // step is to wait for the "interim server response". - - expose (tracked) { - assert tracked.RequestBuffer != null; - if (tracked.RequestBuffer.Length <= _MaxSmbRequestLength) { - // The transaction request is small enough to fit in a single SMB request. - byte[]! in ExHeap request = tracked.RequestBuffer; - tracked.RequestBuffer = null; - - SendData(request); - Transactor_EnterReceiveState(tran, TransactorState.WaitingReceivePrimaryTransactionResponse); - - } else { - // Need to segment the request. - // int primaryRequestLength = _MaxSmbRequestLength; - DebugLine("Transaction segmentation not yet implemented"); - DebugStub.Break(); - - - } - } - break; - } - - case TransactorState.WaitingSendSecondaryTransactionRequest: - DebugLine("Transaction segmentation not yet implemented"); - DebugStub.Break(); - break; - } - - tran.ReleaseData(tracked); - } - - void SendData([Claims]byte[]! in ExHeap buffer) - { - expose(this) - { - assert _connectionCanSend; - assert _connection != null; - - _connection.SendWrite(buffer); - // Since we have sent a message on the TCP/IP channel, we cannot send any more - // messages until we receive a message. - _connectionCanSend = false; - } - } + const int ReceiveBufferSize = 0x400; - // Contains Transactor. - // All instances in this list are in the state "Active", and are waiting to be transmitted. - readonly Queue/**/! _queuedTransactors = new Queue(); - - void Transactor_EnqueueForSend( - Transactor! tran, - TransactorState tstate) - requires tstate == TransactorState.WaitingSendRequest - || tstate == TransactorState.WaitingSendPrimaryTransactionRequest - || tstate == TransactorState.WaitingSendSecondaryTransactionRequest; - requires !tran.InQueue; - { - if (!(tstate == TransactorState.WaitingSendRequest - || tstate == TransactorState.WaitingSendPrimaryTransactionRequest - || tstate == TransactorState.WaitingSendSecondaryTransactionRequest)) { - DebugLine(tran, "ILLEGAL STATE FOR ENQUEUING A TRANSACTOR FOR SEND!"); - DebugStub.Break(); - throw new Exception("Illegal state for enqueuing a transactor for send!"); - } + // contains _TRef, each of which contains a buffer + // all of the slots within [0,_freeReceiveBufferCount) are non-null, and contain a valid + // (acquirable) byte[] buffer. + // + // the slots from _freeReceiveBuffers[_freeReceiveBufferCount] to _freeReceiveBuffers[length - 1] + // are either null, or have been acquired. + readonly BufferPool! _receiveBufferPool = new BufferPool(0x40, 0x400); - assert !tran.InQueue; - tran.InQueue = true; - - tran.State = tstate; - _queuedTransactors.Enqueue(tran); - } + // Because TcpConnectionContract is half-duplex, we have to poll. + const int PollTimeoutMs = 100; - // Examines the request to start a transaction, validates some parameters and information about the request. - // Moves a request from the idle to the queued state. - void StartRequest( - [Claims]SmbTransactor.Exp:WaitingRequestResponse! tran_exp, - [Claims]Transactor! tran, - [Claims]byte[]! in ExHeap request) - requires tran.State == TransactorState.Ready; - { - int length = request.Length; - - if (length < sizeof(SmbHeader)) { // SmbProtocol.SmbHeaderSize) { - DebugLine("SMB message is too small to be valid."); - goto validation_failed; - } - - ref SmbHeader header = ref request[0]; - - int smbLength = length - 4; - header.FrameLength0 = 0; - header.FrameLength1 = (byte)((smbLength >> 16) & 0xff); - header.FrameLength2 = (byte)((smbLength >> 8) & 0xff); - header.FrameLength3 = (byte)(smbLength & 0xff); - - // - // Fill in the mux tuple. - // The transactor client doesn't need to do this. - // - header.TreeId = (ushort)tran.Tuple.TreeId; - header.ProcessId = (ushort)tran.Tuple.ProcessId; - header.UserId = (ushort)tran.Tuple.UserId; - header.MuxId = (ushort)tran.Tuple.MuxId; - - header.Signature0 = 0xff; - header.Signature1 = (byte)'S'; - header.Signature2 = (byte)'M'; - header.Signature3 = (byte)'B'; - - header.Flags1 = (byte)tran._smbFlag1; - header.Flags2 = (ushort)tran._smbFlag2; - - // DebugLine(tran, "idle -> queued, request length = " + length.ToString()); - - tran.Command = (SmbCommand)header.Command; - - Transactor.TrackedData tracked = tran.AcquireData(); + void SendQueuedTransactions() + // requires _connection != null; + { - TransactorState nextstate; - - if (tran.Command == SmbCommand.Transaction2) { - // SMB defines the SMB_TRANSACTION2 command, which has a more complex message pattern - // than the simple request/response pattern that most commands use. SMB allows for - // fragmentation of SMB_TRANSACTION2 requests and responses. + // if we cannot send on the connection, then this method can't do anything. + expose(this) { + if (_connection == null) + return; + } - // DebugLine("Starting SMB_TRANSACTION2 request"); - tracked.TransactionRequestFragmentByteIndex = -1; - nextstate = TransactorState.WaitingSendPrimaryTransactionRequest; - } else { - tracked.TransactionRequestFragmentByteIndex = -1; - nextstate = TransactorState.WaitingSendRequest; - } + if (!_connectionCanSend) + return; - expose (tracked) { - // Transfer ownership of the SmbTransactor.Exp endpoint to the Transactor instance. - delete tracked.Exp; - tracked.Exp = tran_exp; - - // Transfer ownership of the request buffer from the caller to the Transactor instance. - delete tracked.RequestBuffer; - tracked.RequestBuffer = request; - tracked.RequestLength = length; - } - - tran.ReleaseData(tracked); + while (_connectionCanSend && _queuedTransactors.Count != 0) { + Transactor! tran = (!)(Transactor)_queuedTransactors.Dequeue(); + assert tran.InQueue; + tran.InQueue = false; + Transactor_SendData(tran); + } - // Queue this transactor for access to the send path of the socket. - Transactor_EnqueueForSend(tran, nextstate); - return; - - validation_failed: - DebugLine(tran, "Rejecting request -- parameter validation failed"); - DebugStub.Break(); - tran_exp.SendRequestFailed(SmbTransactionError.InvalidParameters); - delete request; + if (_connectionCanSend) { + // + // Issue a read (receive). Because the TcpConnectionContract is half-duplex, we have to use polling + // with timeouts to multiplex send/receive path. Yick. + // + bool wait = true; - Transactor_EnterReadyState(tran_exp, tran); - return; - } - - void Transactor_EnterReadyState([Claims]SmbTransactor.Exp! exp, Transactor! tran) - { - expose(this) - { - // assert tran.Exp == null; - tran.State = TransactorState.Ready; - _readyTransactors.Add(exp, tran); - } - } - - void DeleteTransactorState(Transactor! tran) - { - // DebugLine(tran, "deleting transactor"); - object boxed_tuple = (object)tran.Tuple; - _transactors.Remove(boxed_tuple); - - // -XXX- Need to scan the send queue! - - tran.Dispose(); - // DebugLine(tran, "deleted transactor"); - } - - int _nextMuxId; - - int AllocateMuxId() - { - int firstMuxId = _nextMuxId; + // if (_queuedTransactors.Count != 0) + // wait = false; - for (;;) { - int muxId = _nextMuxId; - _nextMuxId++; - if (_nextMuxId > 0xff) - _nextMuxId = 1; - - MuxTuple tuple = this._treeTuple; - tuple.MuxId = muxId; - - object boxed_tuple = tuple; - if (_transactors[boxed_tuple] == null) { - // DebugLine("Allocated mux ID: " + muxId); - return muxId; - } - - if (_nextMuxId == firstMuxId) { - DebugLine("No mux IDs are available."); - return -1; - } - } - } + expose (this) { + if (wait) { + // blocking read + // DebugLine("issuing blocking read (SendPollRead)"); + _connection.SendPollRead(PollTimeoutMs); + } + else { + // non-blocking read + // DebugLine("issuing nonblocking read (SendRead)"); + _connection.SendRead(); + } + } + _connectionCanSend = false; + } + } - // This method runs on the mux thread, and it handles the SmbTransportMux.AddTransactor message. - // This message is sent by other threads to this thread, in order to request that the mux create - // a new transactor. A transactor is anything that can send requests to the mux, and then wait - // for responses (a transaction). - // - // This method checks the hashtable of transactors to see if there are any tuple collisions - - SmbTransactor.Imp CreateTransactor() - { - SmbTransactor.Imp! transactor_imp; - SmbTransactor.Exp! transactor_exp; - SmbTransactor.NewChannel(out transactor_imp, out transactor_exp); - if (CreateTransactor(transactor_exp)) { - return transactor_imp; - } else { - delete transactor_imp; - return null; - } - } - - bool CreateTransactor([Claims]SmbTransactor.Exp! transactor_exp) - { - expose (this) - { - int muxId = AllocateMuxId(); - if (muxId == -1) { - DebugLine("Failed to create transactor -- no mux IDs are available."); - delete transactor_exp; - return false; - } - - MuxTuple tuple = _treeTuple; - tuple.MuxId = muxId; + void Transactor_SendData(Transactor! tran) + requires _connectionCanSend; + { + Transactor.TrackedData tracked = tran.AcquireData(); - object boxed_tuple = (object)tuple; - if (_transactors[boxed_tuple] != null) { - DebugLine("Internal error - mux tuple is in use, even though we called AllocateMuxId!"); - delete transactor_exp; - return false; - } - - Transactor tran = new Transactor(tuple); - - tran._smbFlag1 = (SmbFlag1)0; - - tran._smbFlag2 = SmbFlag2.KnowsLongNames - | SmbFlag2.AllPathsAreLongNames - | SmbFlag2.UsingNtStatus - | SmbFlag2.UnicodeStrings; - - _transactors[boxed_tuple] = tran; - - Transactor_EnterReadyState(transactor_exp, tran); - - if (TraceSwitches.ShowTransactorMessages) - DebugLine(tran, "Transactor created"); - - return true; - } - } + switch (tran.State) { + case TransactorState.WaitingSendRequest: + { + // It's a "simple" request. Just send the request body. + // Ownership of the buffer moves from the transactor client, to the mux, to TCP/IP. + byte[]! in ExHeap request; + expose (tracked) { + assert tracked.RequestBuffer != null; + request = tracked.RequestBuffer; + tracked.RequestBuffer = null; + } - void DebugLine(string! msg) - { - SmbDebug.WriteLine("MUX: " + msg); - } - - void DebugLine(string! format, params object[] args) - { - DebugLine(String.Format(format, args)); - } - - void DebugLine(Transactor! tran, string msg) - { - SmbDebug.WriteLine(tran.DebugPrefix + msg); - } - - void DebugLine(Transactor! tran, string format, params object[] args) - { - DebugLine(tran, String.Format(format, args)); - } - - const string OSName = "MSR Singularity"; - const string LanManName = "smbclient"; - - - - SmbFlag1 _flags1; - SmbFlag2 _flags2; - - int _treeId; - int _processId; - int _muxId; - int _userId; + if (TraceSwitches.ShowSmbMessageDetails) { + DebugLine(tran, "sending request to TCP/IP, length = " + request.Length); + SmbDebug.DumpMessage(request, 0, request.Length); + } + if (TraceSwitches.ShowSmbMessageOneLineSummaries) { + DebugLine(tran, "Sending --> " + SmbDebug.GetSmbMessageSummary(request, 0)); + } - TcpConnectionContract.Imp:ReadyState! CreateTcpConnection() - { - // - // We invoke this synchronously, so we trust the TCP/IP stack not to delay us for an inordinate time. - // - - TcpConnectionContract.Imp:ReadyState! connection = NetUtil.CreateTcpConnection(); - - return connection; - } + SendData(request); + Transactor_EnterReceiveState(tran, TransactorState.WaitingReceiveResponse); + break; + } - /* - This method handles connecting to an SMB server and negotiating for access to the file service. - It handles name resolution, connecting the transport layer (TCP/IP), authentication, and connecting - to the SMB file service. Once this method returns (successfully), the SMB connection is ready - for general use by transactor clients. - - This method (any any methods it calls) do need to pay attention to certain messages from the - SmbClientManager process. Currently, there are several important call paths (such as WaitResponse) - which do NOT receive any messages from the control client. This needs to be fixed. - */ - bool ConnectAndNegotiate() - { - expose(this) - { - DebugLine("--- CONNECTING TO SERVER ---"); - - SetMuxStatus(SmbClientConnectionStatus.TransportConnecting); - - if (_connection != null) { - delete _connection; - _connection = null; - } - - // - // -XXX- I *really* want a better solution than this. I don't want to block this thread - // -XXX- while waiting for TCP/IP to connect. - // - - try { - TcpConnectionContract.Imp:Connected! connection = NetUtil.ConnectAny(_ServerHostName, NativeSmbPort); - delete _connection; - _connection = connection; - _connectionCanSend = true; - } catch(Exception ex) { - DebugLine("Failed to connect to server."); - Util.ShowExceptionDebug(ex); - SetMuxStatusDisconnected(SmbReasonDisconnected.TransportConnectFailed); - return false; - } - - - _flags1 = - SmbFlag1.CaseInsensitive - | SmbFlag1.CanonicalizedPaths; - - _flags2 = - SmbFlag2.KnowsLongNames - | SmbFlag2.KnowsExtendedAttributes - | SmbFlag2.AllPathsAreLongNames - // | SmbFlag2.AwareOfExtendedSecurity - | SmbFlag2.UsingNtStatus - | SmbFlag2.UnicodeStrings; + case TransactorState.WaitingSendPrimaryTransactionRequest: + { + // It's a "transaction" style request. Next, we need to determine whether the + // entire transaction request can fit inside a single SMB message. If so, the + // next step is to wait for the transaction response. If not, then the next + // step is to wait for the "interim server response". - _treeId = 1; - _processId = 0xffef; - _userId = 0; - _muxId = 0; - - SetMuxStatus(SmbClientConnectionStatus.Negotiating); + expose (tracked) { + assert tracked.RequestBuffer != null; + if (tracked.RequestBuffer.Length <= _MaxSmbRequestLength) { + // The transaction request is small enough to fit in a single SMB request. + byte[]! in ExHeap request = tracked.RequestBuffer; + tracked.RequestBuffer = null; - try { - Negotiate(); - } catch(Exception ex) { - SetMuxStatusDisconnected(SmbReasonDisconnected.NegotiationFailed, ex); - Disconnect(); - return false; - } - - SetMuxStatus(SmbClientConnectionStatus.Authenticating); + SendData(request); + Transactor_EnterReceiveState(tran, TransactorState.WaitingReceivePrimaryTransactionResponse); - try { - SessionSetupAndConnectBasic(); - } catch(Exception ex) { - SetMuxStatusDisconnected(SmbReasonDisconnected.AuthenticationFailed, ex); - Disconnect(); - return false; - } - - try { - TreeConnect(); - } catch(Exception ex) { - SetMuxStatusDisconnected(SmbReasonDisconnected.ResourceConnectFailed, ex); - Disconnect(); - return false; - } + } + else { + // Need to segment the request. + // int primaryRequestLength = _MaxSmbRequestLength; + DebugLine("Transaction segmentation NYI"); + DebugStub.Break(); - DebugLine("Negotiation completed successfully"); - - SetMuxStatusConnected(); - - SmbMuxTuple tree; - tree.ProcessId = _processId; - tree.UserId = _userId; - tree.MuxId = _muxId; - tree.TreeId = _treeId; - _treeTuple = tree; - - return true; - } - } - - void TreeConnect() - { - ByteWriter data = new ByteWriter(); - string! unc = Program.UncPath; - // yes, SMB mixes ASCII and UNICODE! yes, it drives me crazy! - // "password", really just alignment padding - data.WriteZero(1); - WriteSmbStringUnicode(data, unc, true); - // "?????" means "any type of service" - WriteSmbStringAscii(data, "?????", true); - - - int messageLength = sizeof(SmbTreeConnectAndX) + data.Length; - byte[]! in ExHeap request_encoded = new[ExHeap] byte[messageLength]; - try { - ref SmbTreeConnectAndX request = ref request_encoded[0]; - request = new SmbTreeConnectAndX(); - PrepareHeader(ref request.Header, SmbCommand.TreeConnectAndX, 4); - request.AndXCommand = 0xff; // no AndX - request.AndXOffset = 0; - request.AndXReserved = 0; - request.ByteCount = (ushort)data.Length; - request.PasswordLength = 1; - data.CopyTo(request_encoded, sizeof(SmbTreeConnectAndX)); - } catch { - delete request_encoded; - throw; - } - - if (TraceSwitches.ShowNegotiation) - DebugLine("Sending TreeConnectAndX"); - - byte[]! in ExHeap response_encoded = ExecuteSynchronousCommand(request_encoded); - try { - - ref SmbHeader response = ref response_encoded[0]; - if (response.IsError) { - DebugLine("Received TREE CONNECT response - FAILURE"); - DebugLine("Error: " + response.GetErrorText()); - throw new Exception("Tree connect FAILED: " + response.GetErrorText()); - } - - if (TraceSwitches.ShowNegotiation) - DebugLine("Received TREE CONNECT response - successful"); - - // I don't like this. We receive a different mux tuple from the server in response - // to TreeConnectAndX (the tree ID is different; it has been assigned by the server). - // So for now, we just change our tree ID. This means that the SMB client is restricted - // to servicing a single tree connection. I want this to change in the future, so that - // a single SMB client connection can carry requests for multiple different tree connections. - // This may be a dream, maybe even a bad one, but I want readers to be aware of this issue. - this._treeId = response.TreeId; - - } finally { - delete response_encoded; - } - } - - void PrepareHeader(ref SmbHeader header, SmbCommand command, int parameterCount) - requires parameterCount >= 0; - requires parameterCount < 256; - { - header.SetSignature(); - header.Command = (byte)command; - header.ParameterCount = (byte)parameterCount; - header.TreeId = (ushort)_treeId; - header.UserId = (ushort)_userId; - header.ProcessId = (ushort)_processId; - header.MuxId = (ushort)_muxId; - header.Flags1 = (byte)_flags1; - header.Flags2 = (ushort)_flags2; - } + } + } + break; + } - // This method waits to receive one SMB message from the server. - // This is for use ONLY during initial negotiation. - byte[]! in ExHeap WaitResponse() - // requires _connection != null; - { - expose(this) - { - assert _connection != null; - for (;;) { - - if (_connectionCanSend) { - // Yes, we "poll" with a time-out. - // I don't like this, but it's our only option until we have full duplex channels, - // or a 2-channel TCP implementation. - _connection.SendPollRead(100); - _connectionCanSend = false; - } - - switch receive - { - case _connection.Data(byte[]! in ExHeap data): - assert !_connectionCanSend; - _connectionCanSend = true; - // DebugLine("Received data on socket: length = " + data.Length); - byte[] in ExHeap response = ProcessReceivedData(data); - if (response != null) - return response; - break; - - case _connection.ChannelClosed(): - throw new Exception("The TCP channel closed before receiving a message."); + case TransactorState.WaitingSendSecondaryTransactionRequest: + DebugLine("Transaction segmentation not yet implemented"); + DebugStub.Break(); + break; + } - case _connection.ConnectionClosed(): - throw new Exception("The TCP connection closed before receiving a message."); - - case _connection.NoData(): - // We get one of these for EVERY polled read. - // Which is quite noisy. - // DebugLine("no data from socket"); - _connectionCanSend = true; - break; - - case _notifier.Ack(): - AckNotifier(); - break; - - // -XXX- Should add more handlers here, to pay attention to control messages. - } - } - } - } + tran.ReleaseData(tracked); + } - byte[] in ExHeap ProcessReceivedData([Claims]byte[]! in ExHeap buffer) - { - // Check to see whether the fragment buffer contains any data. - // If it does, then we have already received part of an SMB message, but not all of it. - // Assemble the full message before continuing. - - if (_recvDataLength != 0) - { - // Already have some data. - // Concatenate the existing data and the newly-received data, and attempt to parse. - // This is inefficient. - - int concatLength = buffer.Length + _recvDataLength; - - DebugLine("concatenating fragment: previous fragment length = 0x{0:x} recv'd data length = 0x{1:x} total length = 0x{2:x}", - _recvDataLength, buffer.Length, concatLength); - - byte[]! in ExHeap concatBuffer = new[ExHeap] byte[concatLength]; - // Bitter.Copy(concatBuffer, 0, _recvDataLength, _recvBuffer, 0); - Bitter.FromByteArray(concatBuffer, 0, _recvDataLength, _recvBuffer, 0); - Bitter.Copy(concatBuffer, _recvDataLength, buffer.Length, buffer, 0); - - _recvDataLength = 0; - delete buffer; - buffer = concatBuffer; - } - - // - // Have we received enough data to parse a complete message? - // - - if (buffer.Length >= sizeof(SmbHeader)) { - ref SmbHeader header = ref buffer[0]; - int messageLength = header.TotalMessageLength; - - int remaining = buffer.Length - messageLength; - - if (remaining > 0) { - - DebugLine("Buffer received from TCP contains SMB message (plus " + remaining + " bytes)"); - - byte[]! in ExHeap response = new[ExHeap] byte[messageLength]; - Bitter.Copy(response, 0, messageLength, buffer, 0); - - // Store the remaining data in the fragment buffer. - AppendReceiveBuffer(buffer, messageLength, remaining); - delete buffer; + void SendData([Claims]byte[]! in ExHeap buffer) + { + expose(this) + { + assert _connectionCanSend; + assert _connection != null; - ValidateSmbMessage(response); - return response; - - } else if (remaining == 0) { - // Exact fit. - // This will probably be common. - // DebugLine("Buffer received from TCP contains exactly one SMB message (easy case)."); - ValidateSmbMessage(buffer); - return buffer; - } else { - // Not enough data for a complete message. - // Store it in the fragment buffer. - DebugLine("Buffer received from TCP does not contain the entire SMB message (need " + (-remaining) + " bytes)."); - AppendReceiveBuffer(buffer, 0, buffer.Length); - delete buffer; - return null; - } - } else { - // Not enough data for a complete message. - // Store it in the fragment buffer. - DebugLine("Buffer received from TCP does not contain the entire SMB message (header is incomplete)."); - AppendReceiveBuffer(buffer, 0, buffer.Length); - delete buffer; - return null; - } - } + _connection.SendWrite(buffer); + // Since we have sent a message on the TCP/IP channel, we cannot send any more + // messages until we receive a message. + _connectionCanSend = false; + } + } - /* - This method copies data from a buffer to the end of the "fragment buffer" in the response context. - */ - void AppendReceiveBuffer( - byte[]! in ExHeap appendData, - int appendOffset, - int length) - requires length >= 0; - requires appendOffset >= 0; - requires appendOffset <= appendData.Length; - { - if (length == 0) - return; - - int finalLength = _recvDataLength + length; - if (finalLength > _recvBuffer.Length) - { - int newLength = finalLength * 3 / 2 + 0x20; - DebugLine("Growing receive buffer from " + _recvBuffer.Length + " to " + newLength + " bytes"); - byte[] newRecvBuffer = new byte[newLength]; - Buffer.BlockCopy(_recvBuffer, 0, newRecvBuffer, 0, _recvDataLength); - // delete _recvBuffer; - _recvBuffer = newRecvBuffer; - } - - assert _recvDataLength <= _recvBuffer.Length; - assert finalLength <= _recvBuffer.Length; - - Bitter.ToByteArray(appendData, appendOffset, length, _recvBuffer, _recvDataLength); - _recvDataLength = finalLength; - } - - void ValidateSmbMessage(byte[]! in ExHeap buffer) - { - if (buffer.Length < sizeof(SmbHeader)) - throw new Exception("The message is too short to be a valid SMB message."); - - ref SmbHeader header = ref buffer[0]; - int messageLength = header.TotalMessageLength; - - if (!header.IsSignatureValid()) { + // Contains Transactor. + // All instances in this list are in the state "Active", and are waiting to be transmitted. + readonly Queue!/**/ _queuedTransactors = new Queue(); + + void Transactor_EnqueueForSend( + Transactor! tran, + TransactorState tstate) + requires tstate == TransactorState.WaitingSendRequest + || tstate == TransactorState.WaitingSendPrimaryTransactionRequest + || tstate == TransactorState.WaitingSendSecondaryTransactionRequest; + requires !tran.InQueue; + { + if (!(tstate == TransactorState.WaitingSendRequest + || tstate == TransactorState.WaitingSendPrimaryTransactionRequest + || tstate == TransactorState.WaitingSendSecondaryTransactionRequest)) { + DebugLine(tran, "ILLEGAL STATE FOR ENQUEUING A TRANSACTOR FOR SEND!"); + DebugStub.Break(); + throw new Exception("Illegal state for enqueuing a transactor for send!"); + } + + assert !tran.InQueue; + tran.InQueue = true; + + tran.State = tstate; + _queuedTransactors.Enqueue(tran); + } + + // Examines the request to start a transaction, validates some parameters and information about the request. + // Moves a request from the idle to the queued state. + void StartRequest( + [Claims]SmbTransactor.Exp:WaitingRequestResponse! tran_exp, + [Claims]Transactor! tran, + [Claims]byte[]! in ExHeap request) + requires tran.State == TransactorState.Ready; + { + int length = request.Length; + + if (length < sizeof(SmbHeader)) { // SmbProtocol.SmbHeaderSize) { + DebugLine("SMB message is too small to be valid."); + goto validation_failed; + } + + ref SmbHeader header = ref request[0]; + + int smbLength = length - 4; + header.FrameLength0 = 0; + header.FrameLength1 = (byte)((smbLength >> 16) & 0xff); + header.FrameLength2 = (byte)((smbLength >> 8) & 0xff); + header.FrameLength3 = (byte)(smbLength & 0xff); + + // + // Fill in the mux tuple. + // The transactor client doesn't need to do this. + // + header.TreeId = (ushort)tran.Tuple.TreeId; + header.ProcessId = (ushort)tran.Tuple.ProcessId; + header.UserId = (ushort)tran.Tuple.UserId; + header.MuxId = (ushort)tran.Tuple.MuxId; + + header.Signature0 = 0xff; + header.Signature1 = (byte)'S'; + header.Signature2 = (byte)'M'; + header.Signature3 = (byte)'B'; + + header.Flags1 = (byte)tran._smbFlag1; + header.Flags2 = (ushort)tran._smbFlag2; + + // DebugLine(tran, "idle -> queued, request length = " + length.ToString()); + + tran.Command = (SmbCommand)header.Command; + + Transactor.TrackedData tracked = tran.AcquireData(); + + TransactorState nextstate; + + if (tran.Command == SmbCommand.Transaction2) { + // SMB defines the SMB_TRANSACTION2 command, which has a more complex message pattern + // than the simple request/response pattern that most commands use. SMB allows for + // fragmentation of SMB_TRANSACTION2 requests and responses. + + // DebugLine("Starting SMB_TRANSACTION2 request"); + tracked.TransactionRequestFragmentByteIndex = -1; + nextstate = TransactorState.WaitingSendPrimaryTransactionRequest; + } + else { + tracked.TransactionRequestFragmentByteIndex = -1; + nextstate = TransactorState.WaitingSendRequest; + } + + expose (tracked) { + // Transfer ownership of the SmbTransactor.Exp endpoint to the Transactor instance. + delete tracked.Exp; + tracked.Exp = tran_exp; + + // Transfer ownership of the request buffer from the caller to the Transactor instance. + delete tracked.RequestBuffer; + tracked.RequestBuffer = request; + tracked.RequestLength = length; + } + + tran.ReleaseData(tracked); + + // Queue this transactor for access to the send path of the socket. + Transactor_EnqueueForSend(tran, nextstate); + return; + + validation_failed: + DebugLine(tran, "Rejecting request -- parameter validation failed"); + DebugStub.Break(); + tran_exp.SendRequestFailed(SmbTransactionError.InvalidParameters); + delete request; + + Transactor_EnterReadyState(tran_exp, tran); + return; + } + + void Transactor_EnterReadyState([Claims]SmbTransactor.Exp! exp, Transactor! tran) + { + expose(this) + { + // assert tran.Exp == null; + tran.State = TransactorState.Ready; + _readyTransactors.Add(exp, tran); + } + } + + void DeleteTransactorState(Transactor! tran) + { + // DebugLine(tran, "deleting transactor"); + object boxed_tuple = (object)tran.Tuple; + _transactors.Remove(boxed_tuple); + + // -XXX- Need to scan the send queue! + + tran.Dispose(); + // DebugLine(tran, "deleted transactor"); + } + + int _nextMuxId; + + int AllocateMuxId() + { + int firstMuxId = _nextMuxId; + + for (;;) { + int muxId = _nextMuxId; + _nextMuxId++; + if (_nextMuxId > 0xff) + _nextMuxId = 1; + + MuxTuple tuple = this._treeTuple; + tuple.MuxId = muxId; + + object boxed_tuple = tuple; + if (_transactors[boxed_tuple] == null) { + // DebugLine("Allocated mux ID: " + muxId); + return muxId; + } + + if (_nextMuxId == firstMuxId) { + DebugLine("No mux IDs are available."); + return -1; + } + } + } + + // This method runs on the mux thread, and it handles the SmbTransportMux.AddTransactor message. + // This message is sent by other threads to this thread, in order to request that the mux create + // a new transactor. A transactor is anything that can send requests to the mux, and then wait + // for responses (a transaction). + // + // This method checks the hashtable of transactors to see if there are any tuple collisions + + SmbTransactor.Imp CreateTransactor() + { + SmbTransactor.Imp! transactor_imp; + SmbTransactor.Exp! transactor_exp; + SmbTransactor.NewChannel(out transactor_imp, out transactor_exp); + if (CreateTransactor(transactor_exp)) { + return transactor_imp; + } + else { + delete transactor_imp; + return null; + } + } + + bool CreateTransactor([Claims]SmbTransactor.Exp! transactor_exp) + { + expose (this) + { + int muxId = AllocateMuxId(); + if (muxId == -1) { + DebugLine("Failed to create transactor -- no mux IDs are available."); + delete transactor_exp; + return false; + } + + MuxTuple tuple = _treeTuple; + tuple.MuxId = muxId; + + object boxed_tuple = (object)tuple; + if (_transactors[boxed_tuple] != null) { + DebugLine("Internal error - mux tuple is in use, even though we called AllocateMuxId!"); + delete transactor_exp; + return false; + } + + Transactor tran = new Transactor(tuple); + + tran._smbFlag1 = (SmbFlag1)0; + + tran._smbFlag2 = SmbFlag2.KnowsLongNames + | SmbFlag2.AllPathsAreLongNames + | SmbFlag2.UsingNtStatus + | SmbFlag2.UnicodeStrings; + + _transactors[boxed_tuple] = tran; + + Transactor_EnterReadyState(transactor_exp, tran); + + if (TraceSwitches.ShowTransactorMessages) + DebugLine(tran, "Transactor created"); + + return true; + } + } + + void DebugLine(string! msg) + { + SmbDebug.WriteLine("MUX: " + msg); + } + + void DebugLine(string! format, params object[] args) + { + DebugLine(String.Format(format, args)); + } + + void DebugLine(Transactor! tran, string msg) + { + SmbDebug.WriteLine(tran.DebugPrefix + msg); + } + + void DebugLine(Transactor! tran, string format, params object[] args) + { + DebugLine(tran, String.Format(format, args)); + } + + const string OSName = "MSR Singularity"; + const string LanManName = "smbclient"; + + + + SmbFlag1 _flags1; + SmbFlag2 _flags2; + + int _treeId; + int _processId; + int _muxId; + int _userId; + + TcpConnectionContract.Imp:ReadyState! CreateTcpConnection() + { + // + // We invoke this synchronously, so we trust the TCP/IP stack not to delay us for an inordinate time. + // + + TcpConnectionContract.Imp:ReadyState! connection = NetUtil.CreateTcpConnection(); + + return connection; + } + + // + //This method handles connecting to an SMB server and negotiating for access to the file service. + //It handles name resolution, connecting the transport layer (TCP/IP), authentication, and connecting + //to the SMB file service. Once this method returns (successfully), the SMB connection is ready + //for general use by transactor clients. +// + //This method (any any methods it calls) do need to pay attention to certain messages from the + //SmbClientManager process. Currently, there are several important call paths (such as WaitResponse) + //which do NOT receive any messages from the control client. This needs to be fixed. + // + bool ConnectAndNegotiate() + { + expose(this) + { + DebugLine("--- CONNECTING TO SERVER ---"); + + SetMuxStatus(SmbClientConnectionStatus.TransportConnecting); + + if (_connection != null) { + delete _connection; + _connection = null; + } + + // + // -XXX- I *really* want a better solution than this. I don't want to block this thread + // -XXX- while waiting for TCP/IP to connect. + // + + try { + TcpConnectionContract.Imp:Connected! connection = NetUtil.ConnectAny(_ServerHostName, NativeSmbPort); + delete _connection; + _connection = connection; + _connectionCanSend = true; + } + catch (Exception ex) { + DebugLine("Failed to connect to server."); + Util.ShowExceptionDebug(ex); + SetMuxStatusDisconnected(SmbReasonDisconnected.TransportConnectFailed); + return false; + } + + + _flags1 = + SmbFlag1.CaseInsensitive + | SmbFlag1.CanonicalizedPaths; + + _flags2 = + SmbFlag2.KnowsLongNames + | SmbFlag2.KnowsExtendedAttributes + | SmbFlag2.AllPathsAreLongNames + // | SmbFlag2.AwareOfExtendedSecurity + | SmbFlag2.UsingNtStatus + | SmbFlag2.UnicodeStrings; + + _treeId = 1; + _processId = 0xffef; + _userId = 0; + _muxId = 0; + + SetMuxStatus(SmbClientConnectionStatus.Negotiating); + + try { + Negotiate(); + } + catch (Exception ex) { + SetMuxStatusDisconnected(SmbReasonDisconnected.NegotiationFailed, ex); + Disconnect(); + return false; + } + + SetMuxStatus(SmbClientConnectionStatus.Authenticating); + + try { + SessionSetupAndConnectBasic(); + } + catch (Exception ex) { + SetMuxStatusDisconnected(SmbReasonDisconnected.AuthenticationFailed, ex); + Disconnect(); + return false; + } + + try { + TreeConnect(); + } + catch (Exception ex) { + SetMuxStatusDisconnected(SmbReasonDisconnected.ResourceConnectFailed, ex); + Disconnect(); + return false; + } + + DebugLine("Negotiation completed successfully"); + + SetMuxStatusConnected(); + + SmbMuxTuple tree; + tree.ProcessId = _processId; + tree.UserId = _userId; + tree.MuxId = _muxId; + tree.TreeId = _treeId; + _treeTuple = tree; + + return true; + } + } + + void TreeConnect() + { + ByteWriter data = new ByteWriter(); + string! unc = Program.UncPath; + // yes, SMB mixes ASCII and UNICODE! yes, it drives me crazy! + // "password", really just alignment padding + data.WriteZero(1); + WriteSmbStringUnicode(data, unc, true); + // "?????" means "any type of service" + WriteSmbStringAscii(data, "?????", true); + + + int messageLength = sizeof(SmbTreeConnectAndX) + data.Length; + byte[]! in ExHeap request_encoded = new[ExHeap] byte[messageLength]; + try { + ref SmbTreeConnectAndX request = ref request_encoded[0]; + request = new SmbTreeConnectAndX(); + PrepareHeader(ref request.Header, SmbCommand.TreeConnectAndX, 4); + request.AndXCommand = 0xff; // no AndX + request.AndXOffset = 0; + request.AndXReserved = 0; + request.ByteCount = (ushort)data.Length; + request.PasswordLength = 1; + + data.CopyTo(request_encoded, sizeof(SmbTreeConnectAndX)); + } + catch { + delete request_encoded; + throw; + } + + if (TraceSwitches.ShowNegotiation) + DebugLine("Sending TreeConnectAndX"); + + byte[]! in ExHeap response_encoded = ExecuteSynchronousCommand(request_encoded); + try { + + ref SmbHeader response = ref response_encoded[0]; + if (response.IsError) { + DebugLine("Received TREE CONNECT response - FAILURE"); + DebugLine("Error: " + response.GetErrorText()); + throw new Exception("Tree connect FAILED: " + response.GetErrorText()); + } + + if (TraceSwitches.ShowNegotiation) + DebugLine("Received TREE CONNECT response - successful"); + + // I don't like this. We receive a different mux tuple from the server in response + // to TreeConnectAndX (the tree ID is different; it has been assigned by the server). + // So for now, we just change our tree ID. This means that the SMB client is restricted + // to servicing a single tree connection. I want this to change in the future, so that + // a single SMB client connection can carry requests for multiple different tree connections. + // This may be a dream, maybe even a bad one, but I want readers to be aware of this issue. + this._treeId = response.TreeId; + + } + finally { + delete response_encoded; + } + } + + void PrepareHeader(ref SmbHeader header, SmbCommand command, int parameterCount) + requires parameterCount >= 0; + requires parameterCount < 256; + { + header.SetSignature(); + header.Command = (byte)command; + header.ParameterCount = (byte)parameterCount; + header.TreeId = (ushort)_treeId; + header.UserId = (ushort)_userId; + header.ProcessId = (ushort)_processId; + header.MuxId = (ushort)_muxId; + header.Flags1 = (byte)_flags1; + header.Flags2 = (ushort)_flags2; + } + + // This method waits to receive one SMB message from the server. + // This is for use ONLY during initial negotation. + byte[]! in ExHeap WaitResponse() + // requires _connection != null; + { + expose(this) + { + assert _connection != null; + for (;;) { + + if (_connectionCanSend) { + // Yes, we "poll" with a time-out. + // I don't like this, but it's our only option until we have full duplex channels, + // or a 2-channel TCP implementation. + _connection.SendPollRead(100); + _connectionCanSend = false; + } + + switch receive { + case _connection.Data(byte[]! in ExHeap data): + assert !_connectionCanSend; + _connectionCanSend = true; + // DebugLine("Received data on socket: length = " + data.Length); + byte[] in ExHeap response = ProcessReceivedData(data); + if (response != null) + return response; + break; + + case _connection.ChannelClosed(): + throw new Exception("The TCP channel closed before receiving a message."); + + case _connection.ConnectionClosed(): + throw new Exception("The TCP connection closed before receiving a message."); + + case _connection.NoData(): + // We get one of these for EVERY polled read. + // Which is quite noisy. + // DebugLine("no data from socket"); + _connectionCanSend = true; + break; + + case _notifier.Ack(): + AckNotifier(); + break; + + // -XXX- Should add more handlers here, to pay attention to control messages. + } + } + } + } + + byte[] in ExHeap ProcessReceivedData([Claims]byte[]! in ExHeap buffer) + { + // Check to see whether the fragment buffer contains any data. + // If it does, then we have already received part of an SMB message, but not all of it. + // Assemble the full message before continuing. + + if (_recvDataLength != 0) { + // Already have some data. + // Concatenate the existing data and the newly-received data, and attempt to parse. + // This is inefficient. + + int concatLength = buffer.Length + _recvDataLength; + + DebugLine("concatenating fragment: previous fragment length = 0x{0:x} recv'd data length = 0x{1:x} total length = 0x{2:x}", + _recvDataLength, buffer.Length, concatLength); + + byte[]! in ExHeap concatBuffer = new[ExHeap] byte[concatLength]; + // Bitter.Copy(concatBuffer, 0, _recvDataLength, _recvBuffer, 0); + Bitter.FromByteArray(concatBuffer, 0, _recvDataLength, _recvBuffer, 0); + Bitter.Copy(concatBuffer, _recvDataLength, buffer.Length, buffer, 0); + + _recvDataLength = 0; + delete buffer; + buffer = concatBuffer; + } + + // + // Have we received enough data to parse a complete message? + // + + if (buffer.Length >= sizeof(SmbHeader)) { + ref SmbHeader header = ref buffer[0]; + int messageLength = header.TotalMessageLength; + + int remaining = buffer.Length - messageLength; + + if (remaining > 0) { + + DebugLine("Buffer received from TCP contains SMB message (plus " + remaining + " bytes)"); + + byte[]! in ExHeap response = new[ExHeap] byte[messageLength]; + Bitter.Copy(response, 0, messageLength, buffer, 0); + + // Store the remaining data in the fragment buffer. + AppendReceiveBuffer(buffer, messageLength, remaining); + delete buffer; + + ValidateSmbMessage(response); + return response; + + } + else if (remaining == 0) { + // Exact fit. + // This will probably be common. + // DebugLine("Buffer received from TCP contains exactly one SMB message (easy case)."); + ValidateSmbMessage(buffer); + return buffer; + } + else { + // Not enough data for a complete message. + // Store it in the fragment buffer. + DebugLine("Buffer received from TCP does not contain the entire SMB message (need " + (-remaining) + " bytes)."); + AppendReceiveBuffer(buffer, 0, buffer.Length); + delete buffer; + return null; + } + } + else { + // Not enough data for a complete message. + // Store it in the fragment buffer. + DebugLine("Buffer received from TCP does not contain the entire SMB message (header is incomplete)."); + AppendReceiveBuffer(buffer, 0, buffer.Length); + delete buffer; + return null; + } + } + + // + //This method copies data from a buffer to the end of the "fragment buffer" in the response context. + // + void AppendReceiveBuffer( + byte[]! in ExHeap appendData, + int appendOffset, + int length) + requires length >= 0; + requires appendOffset >= 0; + requires appendOffset <= appendData.Length; + { + if (length == 0) + return; + + int finalLength = _recvDataLength + length; + if (finalLength > _recvBuffer.Length) { + int newLength = finalLength * 3 / 2 + 0x20; + DebugLine("Growing receive buffer from " + _recvBuffer.Length + " to " + newLength + " bytes"); + byte[] newRecvBuffer = new byte[newLength]; + Buffer.BlockCopy(_recvBuffer, 0, newRecvBuffer, 0, _recvDataLength); + // delete _recvBuffer; + _recvBuffer = newRecvBuffer; + } + + assert _recvDataLength <= _recvBuffer.Length; + assert finalLength <= _recvBuffer.Length; + + Bitter.ToByteArray(appendData, appendOffset, length, _recvBuffer, _recvDataLength); + _recvDataLength = finalLength; + } + + void ValidateSmbMessage(byte[]! in ExHeap buffer) + { + if (buffer.Length < sizeof(SmbHeader)) + throw new Exception("The message is too short to be a valid SMB message."); + + ref SmbHeader header = ref buffer[0]; + int messageLength = header.TotalMessageLength; + + if (!header.IsSignatureValid()) { DebugLine("ERROR! Protocol violation: Received an SMB message with an invalid SMB signature."); DebugLine("This usually means the client and server have gotten out of sync on message boundaries."); DebugLine("The connection will be terminated."); - throw new Exception("The message does not have a valid SMB signature."); - } - - if (buffer.Length < messageLength) - throw new Exception("The message is too short to contain the message whose length is specified in the message header."); - } - - #if false - void NegotiateUsingExtendedSecurity() - { - // - // Build the NTLM challenge and send it encapsulated within a SessionSetupAndTreeConnect request. - // - byte[]! hello = _supplicant.GetNegotiate(); - DebugLine("Sending NTLM 'hello' blob:"); - Util.DumpBuffer(hello); - SendSessionSetupAndConnect(transactor, hello); + throw new Exception("The message does not have a valid SMB signature."); + } - // - // Next, we wait for the response to the SessionSetupAndTreeConnect. - // It should contain the NTLM challenge. - // - DebugLine("Waiting for NTLM challenge..."); - byte[]! security_challenge; - WaitSessionSetupResponse(transactor, out security_challenge); - DebugLine("Received NTLM challenge:"); - Util.DumpBuffer(security_challenge); - - DebugStub.Break(); - - // - // Have the security context generate the response and send it in another SessionSetupAndX request. - // - byte[]! security_response = _supplicant.GetResponse(security_challenge); - DebugLine("Sending this NTLM response:"); - Util.DumpBuffer(security_response); - SendSessionSetupAndConnect(transactor, security_response); - - // - // Now wait for the final response. - // - byte[]! security_message_ignored; - WaitSessionSetupResponse(transactor, out security_message_ignored); - DebugLine("Final security response received."); - } - #endif - - - /* - - This method sends the SMB NEGOTIATE message to the server, and waits for the response. - If the message cannot be sent, or the response received indicates an error, then this - method will throw an exception. - - */ - void Negotiate() - { - string![] dialects = { SmbDialect.NTLM012 }; + if (buffer.Length < messageLength) + throw new Exception("The message is too short to contain the message whose length is specified in the message header."); + } - ByteWriter! data = new ByteWriter(); - foreach (string dialect in dialects) - { - data.Write(2); - byte[]! dialectbytes = Encoding.ASCII.GetBytes(dialect); - data.Write(dialectbytes); - data.Write(0); - } - - SmbNegotiateRequest request = new SmbNegotiateRequest(); - PrepareHeader(ref request.Header, SmbCommand.Negotiate, 0); - request.ByteCount = (ushort)data.Length; - - byte[]! in ExHeap encoded_request = new[ExHeap] byte[sizeof(SmbNegotiateRequest) + data.Length]; - ref SmbNegotiateRequest ref_request = ref encoded_request[0]; - ref_request = request; - - data.CopyTo(encoded_request, sizeof(SmbNegotiateRequest)); + #if false + void NegotiateUsingExtendedSecurity() + { + // + // Build the NTLM challenge and send it encapsulated within a SessionSetupAndTreeConnect request. + // + byte[]! hello = _supplicant.GetNegotiate(); + DebugLine("Sending NTLM 'hello' blob:"); + Util.DumpBuffer(hello); + SendSessionSetupAndConnect(transactor, hello); - DebugLine("Sending NEGOTIATE request"); - - byte[]! in ExHeap response_encoded = ExecuteSynchronousCommand(encoded_request); - - try { - - - ref SmbHeader header = ref response_encoded[0]; - if (header.IsError) { - DebugLine("Received NEGOTIATE RESPONSE - server indicates FAILURE"); - DebugLine("Error: " + header.GetErrorText()); - throw new Exception("SMB server rejected NEGOTIATE: " + header.GetErrorText()); - } - - if (TraceSwitches.ShowNegotiation) { - DebugLine("Received NEGOTIATE RESPONSE - successful"); - } + // + // Next, we wait for the response to the SessionSetupAndTreeConnect. + // It should contain the NTLM challenge. + // + DebugLine("Waiting for NTLM challenge..."); + byte[]! security_challenge; + WaitSessionSetupResponse(transactor, out security_challenge); + DebugLine("Received NTLM challenge:"); + Util.DumpBuffer(security_challenge); - if (header.ParameterCount != 17) { - DebugLine("NEGOTIATE response contains the wrong number of parameter words! Cannot use!"); - throw new Exception("NEGOTIATE response contains the wrong number of parameter words"); - } - - ref SmbNegotiateResponse17 response = ref response_encoded[0]; - //DebugLine("EncryptionKeyLength = " + response.EncryptionKeyLength.ToString()); - - byte[]! response_data = new byte[response.ByteCount]; - Bitter.ToByteArray(response_encoded, sizeof(SmbNegotiateResponse17), response.ByteCount, response_data, 0); - ByteReader! response_data_reader = new ByteReader(response_data); - - byte[]! key = response_data_reader.ReadBytes(response.EncryptionKeyLength); - this._serverLmChallenge = key; - - string! serverDomainName = ReadStringUnicode(response_data_reader); - string! serverHostName = ReadStringUnicode(response_data_reader); - - // DebugLine("Server Domain Name = " + serverDomainName); - // DebugLine("Server Host Name = " + serverHostName); + DebugStub.Break(); + + // + // Have the security context generate the response and send it in another SessionSetupAndX request. + // + byte[]! security_response = _supplicant.GetResponse(security_challenge); + DebugLine("Sending this NTLM response:"); + Util.DumpBuffer(security_response); + SendSessionSetupAndConnect(transactor, security_response); + + // + // Now wait for the final response. + // + byte[]! security_message_ignored; + WaitSessionSetupResponse(transactor, out security_message_ignored); + DebugLine("Final security response received."); + } + #endif + + + // +// + //This method sends the SMB NEGOTIATE message to the server, and waits for the response. + //If the message cannot be sent, or the response received indicates an error, then this + //method will throw an exception. +// + // + void Negotiate() + { + string![] dialects = { SmbDialect.NTLM012 }; + + ByteWriter! data = new ByteWriter(); + foreach (string! dialect in dialects) { + data.Write(2); + byte[]! dialectbytes = Encoding.ASCII.GetBytes(dialect); + data.Write(dialectbytes); + data.Write(0); + } + + SmbNegotiateRequest request = new SmbNegotiateRequest(); + PrepareHeader(ref request.Header, SmbCommand.Negotiate, 0); + request.ByteCount = (ushort)data.Length; + + byte[]! in ExHeap encoded_request = new[ExHeap] byte[sizeof(SmbNegotiateRequest) + data.Length]; + ref SmbNegotiateRequest ref_request = ref encoded_request[0]; + ref_request = request; + + data.CopyTo(encoded_request, sizeof(SmbNegotiateRequest)); + + DebugLine("Sending NEGOTIATE request"); + + byte[]! in ExHeap response_encoded = ExecuteSynchronousCommand(encoded_request); + + try { + + + ref SmbHeader header = ref response_encoded[0]; + if (header.IsError) { + DebugLine("Received NEGOTIATE RESPONSE - server indicates FAILURE"); + DebugLine("Error: " + header.GetErrorText()); + throw new Exception("SMB server rejected NEGOTIATE: " + header.GetErrorText()); + } + + if (TraceSwitches.ShowNegotiation) { + DebugLine("Received NEGOTIATE RESPONSE - successful"); + } + + if (header.ParameterCount != 17) { + DebugLine("NEGOTIATE response contains the wrong number of parameter words! Cannot use!"); + throw new Exception("NEGOTIATE response contains the wrong number of parameter words"); + } + + ref SmbNegotiateResponse17 response = ref response_encoded[0]; + //DebugLine("EncryptionKeyLength = " + response.EncryptionKeyLength.ToString()); + + byte[]! response_data = new byte[response.ByteCount]; + Bitter.ToByteArray(response_encoded, sizeof(SmbNegotiateResponse17), response.ByteCount, response_data, 0); + ByteReader! response_data_reader = new ByteReader(response_data); + + byte[]! key = response_data_reader.ReadBytes(response.EncryptionKeyLength); + this._serverLmChallenge = key; + + string! serverDomainName = ReadStringUnicode(response_data_reader); + string! serverHostName = ReadStringUnicode(response_data_reader); + + // DebugLine("Server Domain Name = " + serverDomainName); + // DebugLine("Server Host Name = " + serverHostName); + + _ReceivedServerDomainNameFromNegotiate = serverDomainName; + _ReceivedServerMachineName = serverHostName; + + } + finally { + delete response_encoded; + } + } - _ReceivedServerDomainNameFromNegotiate = serverDomainName; - _ReceivedServerMachineName = serverHostName; - - } finally { - delete response_encoded; - } - } - static string! ReadStringUnicode(ByteReader! reader) { StringBuilder buffer = new StringBuilder(); - for (; ; ) - { + for (;;) { byte low = reader.ReadByte(); byte high = reader.ReadByte(); if (low == 0 && high == 0) @@ -2049,414 +2063,417 @@ namespace Smb.Client } } - - // This field contains the LanMan (LM) challenge, received from the server. - // We use this value to compute the challenge response, which is what the client - // uses to prove knowledge of the user's password without revealing the password - // in the clear. - byte[] _serverLmChallenge; - + + // This field contains the LanMan (LM) challenge, received from the server. + // We use this value to compute the challenge response, which is what the client + // uses to prove knowledge of the user's password without revealing the password + // in the clear. + byte[] _serverLmChallenge; + #if false - void SendSessionSetupAndConnect(SmbTransactor.Imp! transactor, byte[]! security_blob) - requires security_blob.Length <= ushort.MaxValue; - { - // build header - // don't bother with setting tuple in header, mux will set that for us - SmbSessionSetupAndXRequest13 request = new SmbSessionSetupAndXRequest13(); - request.Header.Command = (byte)SmbCommand.SessionSetupAndX; - request.Header.Flags1 = (byte)_flags1; - request.Header.Flags2 = (ushort)_flags2; - - // build parameters block - request.Header.ParameterCount = 13; - request.AndXCommand = 0xff; - request.AndXPadding = 0; - request.AndXOffset = 0; - request.MaxBufferSize = 2048; - request.MaxMpxCount = 10; - request.VcNumber = 1; - request.SessionKey = 0; - request.CaseInsensitivePasswordLength = (ushort)lm_response.Length; - request.CaseSensitivePasswordLength = (ushort)nt_response.Length; - request.Reserved = 0; + void SendSessionSetupAndConnect(SmbTransactor.Imp! transactor, byte[]! security_blob) + requires security_blob.Length <= ushort.MaxValue; + { + // build header + // don't bother with setting tuple in header, mux will set that for us + SmbSessionSetupAndXRequest13 request = new SmbSessionSetupAndXRequest13(); + request.Header.Command = (byte)SmbCommand.SessionSetupAndX; + request.Header.Flags1 = (byte)_flags1; + request.Header.Flags2 = (ushort)_flags2; - SmbCapability capabilities = - SmbCapability.Unicode - | SmbCapability.LargeFiles - | SmbCapability.NtSmbs - | SmbCapability.NtFind - | SmbCapability.NtStatus - | SmbCapability.Level2Oplocks; + // build parameters block + request.Header.ParameterCount = 13; + request.AndXCommand = 0xff; + request.AndXPadding = 0; + request.AndXOffset = 0; + request.MaxBufferSize = 2048; + request.MaxMpxCount = 10; + request.VcNumber = 1; + request.SessionKey = 0; + request.CaseInsensitivePasswordLength = (ushort)lm_response.Length; + request.CaseSensitivePasswordLength = (ushort)nt_response.Length; + request.Reserved = 0; + + SmbCapability capabilities = + SmbCapability.Unicode + | SmbCapability.LargeFiles + | SmbCapability.NtSmbs + | SmbCapability.NtFind + | SmbCapability.NtStatus + | SmbCapability.Level2Oplocks; request.Capabilities = (uint)capabilities; - - // build message data - ByteWriter data = new ByteWriter(); - data.Write(security_blob); + + // build message data + ByteWriter data = new ByteWriter(); + data.Write(security_blob); WriteSmbUnicodeString(data, SmbClientParameters.Username, true); WriteSmbUnicodeString(data, SmbClientParameters.DomainName, true); - WriteSmbUnicodeString(data, OSName); - WriteSmbUnicodeString(data, LanManName); - int data_length = data.Length; - - request.ByteCount = (ushort)data.Length; - - int total_length = sizeof(SmbSessionSetupAndXRequest13) - + data_length; // security_blob.Length; + WriteSmbUnicodeString(data, OSName); + WriteSmbUnicodeString(data, LanManName); + int data_length = data.Length; - byte[]! in ExHeap msg = new[ExHeap] byte[total_length]; + request.ByteCount = (ushort)data.Length; - try { - ref SmbSessionSetupAndXRequest13 request_ref = ref msg[0]; - // store values into the ex buffer - request_ref = request; + int total_length = sizeof(SmbSessionSetupAndXRequest13) + + data_length; // security_blob.Length; - data.CopyTo(msg, sizeof(SmbSessionSetupAndXRequest13)); - - DebugLine("Sending SessionSetupAndX message:"); - } catch { - delete msg; - throw; - } - - transactor.SendRequest(msg, msg.Length); - } - #endif - - byte[]! GetResponse(NtlmSupplicantContract.Imp! ntlm_supplicant, NtlmResponseType type, byte[]! challenge) - { - byte[]! in ExHeap exchallenge = Bitter.FromByteArray(challenge); - ntlm_supplicant.SendGetResponse(exchallenge, type); - - switch receive { - case ntlm_supplicant.Response(exresponse): - byte[]! response = Bitter.ToByteArray(exresponse); - delete exresponse; - return response; - - case ntlm_supplicant.RequestFailed(error): - throw new CredentialsManagerException("NTLM supplicant failed to return response.", error); - } - } + byte[]! in ExHeap msg = new[ExHeap] byte[total_length]; - // This method contacts the Credentials Manager service. - // Therefore, it CAN block indefinitely, depending on behavior outside of this process. - // Should review this. - void GetChallengeResponses( - out string! username, - out string! domain, - out byte[]! nt_response, - out byte[]! lm_response) - { - // We have already received the NTLM challenge (a simple 8-byte nonce) from the SMB - // server in the response to the NEGOTIATE message. - - byte[] challenge = _serverLmChallenge; - if (challenge == null) - throw new Exception("The server LM challenge is null. Failed to sample challenge from negotiation response."); - - NtlmSupplicantContract.Imp! ntlm_supplicant; - NtlmSupplicantContract.Exp! ntlm_supplicant_exp; - NtlmSupplicantContract.NewChannel(out ntlm_supplicant, out ntlm_supplicant_exp); - - string! credentialsName; - string! tag; - - try { - try { - - string creds = Program.CredentialsName; - if (creds == null || creds.Length == 0) { - // The user did not provide any explicit credentials to use for this SMB client. - // All the Credentials Manager to select the credentials to use. - - if (TraceSwitches.ShowAuthenticationMessages) { - DebugLine("No explicitly-selected credentials provided. Using Credentials Manager to select credentials..."); - DebugLine("Creating NTLM supplicant..."); - } - - // - // -XXX- NOTE! We received two values from the server in the response to the NEGOTIATE message. - // -XXX- We should be careful with those values; since they came from the network, they could - // -XXX- contain any garbage, potentially evil. We pass these to the CM service. So either - // -XXX- the SMB client process and/or the CM service should perform some extreme skepticism - // -XXX- on these values. - // - - CredentialsManager.CreateSupplicantForProtocol( - "smb", - (!)_ReceivedServerMachineName, // _ServerHostName, - AuthenticationProtocolNames.Ntlm, - (!)_ReceivedServerDomainNameFromNegotiate, - ntlm_supplicant_exp, - out credentialsName, - out tag); - - if (TraceSwitches.ShowAuthenticationMessages) - DebugLine("Created NTLM supplicant, selected credentials: " + credentialsName); - - } else { - // The user provided explicit credentials for this SMB client. - // Use the Credentials Manager to create a supplicant, but don't use it - // to select the credentials to use. - - if (TraceSwitches.ShowAuthenticationMessages) - DebugLine("Explicit credentials: " + Program.CredentialsName); - - credentialsName = Program.CredentialsName; - tag = Program.CredentialsTag; - - CredentialsManager.CreateSupplicant( - AuthenticationProtocolNames.Ntlm, - credentialsName, - tag, - ntlm_supplicant_exp); - - if (TraceSwitches.ShowAuthenticationMessages) - DebugLine("Supplicant successfully created."); - } - - ntlm_supplicant.RecvSuccess(); - - _SelectedCredentialsName = credentialsName; - - } catch (Exception ex) { - throw new Exception("Negotiation failed, because an authentication supplicant could not be created.", ex); - } - - int separator = credentialsName.IndexOf('\\'); - if (separator != -1) { - username = credentialsName.Substring(separator + 1); - domain = credentialsName.Substring(0, separator); - } else { - username = credentialsName; - domain = "."; - } + try { + ref SmbSessionSetupAndXRequest13 request_ref = ref msg[0]; + // store values into the ex buffer + request_ref = request; - lm_response = GetResponse(ntlm_supplicant, NtlmResponseType.LanMan, challenge); - nt_response = GetResponse(ntlm_supplicant, NtlmResponseType.WindowsNt, challenge); - - } finally { - delete ntlm_supplicant; - } - } - - void SessionSetupAndConnectBasic() - { - // DebugLine("Using basic NTLM negotiation (not NTLMSSP)"); - - byte[]! lm_response; - byte[]! nt_response; - string! username; - string! domain; - GetChallengeResponses( - out username, - out domain, - out nt_response, - out lm_response); - - // build header - // don't bother with setting tuple in header, mux will set that for us - SmbSessionSetupAndXRequest13 request = new SmbSessionSetupAndXRequest13(); - PrepareHeader(ref request.Header, SmbCommand.SessionSetupAndX, 13); - - // build parameters block - request.AndXCommand = 0xff; - request.AndXPadding = 0; - request.AndXOffset = 0; - request.MaxBufferSize = 2048; - request.MaxMpxCount = 10; - request.VcNumber = 1; - request.SessionKey = 0; - request.CaseInsensitivePasswordLength = (ushort)lm_response.Length; - request.CaseSensitivePasswordLength = (ushort)nt_response.Length; - request.Reserved = 0; + data.CopyTo(msg, sizeof(SmbSessionSetupAndXRequest13)); - SmbCapability capabilities = - SmbCapability.Unicode - | SmbCapability.LargeFiles - | SmbCapability.NtSmbs - | SmbCapability.NtFind - | SmbCapability.NtStatus - | SmbCapability.Level2Oplocks; + DebugLine("Sending SessionSetupAndX message:"); + } + catch { + delete msg; + throw; + } + + transactor.SendRequest(msg, msg.Length); + } + #endif + + byte[]! GetResponse(NtlmSupplicantContract.Imp! ntlm_supplicant, NtlmResponseType type, byte[]! challenge) + { + byte[]! in ExHeap exchallenge = Bitter.FromByteArray(challenge); + ntlm_supplicant.SendGetResponse(exchallenge, type); + + switch receive { + case ntlm_supplicant.Response(exresponse): + byte[]! response = Bitter.ToByteArray(exresponse); + delete exresponse; + return response; + + case ntlm_supplicant.RequestFailed(error): + throw new CredentialsManagerException("NTLM supplicant failed to return response.", error); + } + } + + // This method contacts the Credentials Manager service. + // Therefore, it CAN block indefinitely, depending on behavior outside of this process. + // Should review this. + void GetChallengeResponses( + out string! username, + out string! domain, + out byte[]! nt_response, + out byte[]! lm_response) + { + // We have already received the NTLM challenge (a simple 8-byte nonce) from the SMB + // server in the response to the NEGOTIATE message. + + byte[] challenge = _serverLmChallenge; + if (challenge == null) + throw new Exception("The server LM challenge is null. Failed to sample challenge from negotiation response."); + + NtlmSupplicantContract.Imp! ntlm_supplicant; + NtlmSupplicantContract.Exp! ntlm_supplicant_exp; + NtlmSupplicantContract.NewChannel(out ntlm_supplicant, out ntlm_supplicant_exp); + + string! credentialsName; + string! tag; + + try { + try { + + string creds = Program.CredentialsName; + if (creds == null || creds.Length == 0) { + // The user did not provide any explicit credentials to use for this SMB client. + // All the Credentials Manager to select the credentials to use. + + if (TraceSwitches.ShowAuthenticationMessages) { + DebugLine("No explicitly-selected credentials provided. Using Credentials Manager to select credentials..."); + DebugLine("Creating NTLM supplicant..."); + } + + // + // -XXX- NOTE! We received two values from the server in the response to the NEGOTIATE message. + // -XXX- We should be careful with those values; since they came from the network, they could + // -XXX- contain any garbage, potentially evil. We pass these to the CM service. So either + // -XXX- the SMB client process and/or the CM service should perform some extreme skepticism + // -XXX- on these values. + // + + CredentialsManager.CreateSupplicantForProtocol( + "smb", + (!)_ReceivedServerMachineName, // _ServerHostName, + AuthenticationProtocolNames.Ntlm, + (!)_ReceivedServerDomainNameFromNegotiate, + ntlm_supplicant_exp, + out credentialsName, + out tag); + + if (TraceSwitches.ShowAuthenticationMessages) + DebugLine("Created NTLM supplicant, selected credentials: " + credentialsName); + + } + else { + // The user provided explicit credentials for this SMB client. + // Use the Credentials Manager to create a supplicant, but don't use it + // to select the credentials to use. + + if (TraceSwitches.ShowAuthenticationMessages) + DebugLine("Explicit credentials: " + Program.CredentialsName); + + credentialsName = Program.CredentialsName; + tag = Program.CredentialsTag; + + CredentialsManager.CreateSupplicant( + AuthenticationProtocolNames.Ntlm, + credentialsName, + tag, + ntlm_supplicant_exp); + + if (TraceSwitches.ShowAuthenticationMessages) + DebugLine("Supplicant successfully created."); + } + + ntlm_supplicant.RecvSuccess(); + + _SelectedCredentialsName = credentialsName; + + } + catch (Exception ex) { + throw new Exception("Negotiation failed, because an authentication supplicant could not be created.", ex); + } + + int separator = credentialsName.IndexOf('\\'); + if (separator != -1) { + username = credentialsName.Substring(separator + 1); + domain = credentialsName.Substring(0, separator); + } + else { + username = credentialsName; + domain = "."; + } + + lm_response = GetResponse(ntlm_supplicant, NtlmResponseType.LanMan, challenge); + nt_response = GetResponse(ntlm_supplicant, NtlmResponseType.WindowsNt, challenge); + + } + finally { + delete ntlm_supplicant; + } + } + + void SessionSetupAndConnectBasic() + { + // DebugLine("Using basic NTLM negotiation (not NTLMSSP)"); + + byte[]! lm_response; + byte[]! nt_response; + string! username; + string! domain; + GetChallengeResponses( + out username, + out domain, + out nt_response, + out lm_response); + + // build header + // don't bother with setting tuple in header, mux will set that for us + SmbSessionSetupAndXRequest13 request = new SmbSessionSetupAndXRequest13(); + PrepareHeader(ref request.Header, SmbCommand.SessionSetupAndX, 13); + + // build parameters block + request.AndXCommand = 0xff; + request.AndXPadding = 0; + request.AndXOffset = 0; + request.MaxBufferSize = 2048; + request.MaxMpxCount = 10; + request.VcNumber = 1; + request.SessionKey = 0; + request.CaseInsensitivePasswordLength = (ushort)lm_response.Length; + request.CaseSensitivePasswordLength = (ushort)nt_response.Length; + request.Reserved = 0; + + SmbCapability capabilities = + SmbCapability.Unicode + | SmbCapability.LargeFiles + | SmbCapability.NtSmbs + | SmbCapability.NtFind + | SmbCapability.NtStatus + | SmbCapability.Level2Oplocks; request.Capabilities = (uint)capabilities; - - // build message data - ByteWriter data = new ByteWriter(); - // data.Write(security_blob); - data.Write(lm_response); - data.Write(nt_response); - data.Write((byte)0); // alignment padding + + // build message data + ByteWriter data = new ByteWriter(); + // data.Write(security_blob); + data.Write(lm_response); + data.Write(nt_response); + data.Write((byte)0); // alignment padding WriteSmbStringUnicode(data, username, true); WriteSmbStringUnicode(data, domain, true); - WriteSmbStringUnicode(data, OSName, true); - WriteSmbStringUnicode(data, LanManName, true); - int data_length = data.Length; - - //DebugLine("data block:"); - //Util.DumpBuffer(data.ToArray()); - - request.ByteCount = (ushort)data.Length; - - int total_length = sizeof(SmbSessionSetupAndXRequest13) - + data_length; // security_blob.Length; - - request.Header.TotalMessageLength = total_length; + WriteSmbStringUnicode(data, OSName, true); + WriteSmbStringUnicode(data, LanManName, true); + int data_length = data.Length; - byte[]! in ExHeap request_encoded = new[ExHeap] byte[total_length]; + //DebugLine("data block:"); + //Util.DumpBuffer(data.ToArray()); - try { - ref SmbSessionSetupAndXRequest13 request_ref = ref request_encoded[0]; - // store values into the ex buffer - request_ref = request; + request.ByteCount = (ushort)data.Length; - data.CopyTo(request_encoded, sizeof(SmbSessionSetupAndXRequest13)); - } catch { - delete request_encoded; - throw; - } + int total_length = sizeof(SmbSessionSetupAndXRequest13) + + data_length; // security_blob.Length; - if (TraceSwitches.ShowNegotiationVerbose) - DebugLine("Sending SESSION SETUP"); - - byte[]! in ExHeap response_encoded = ExecuteSynchronousCommand(request_encoded); - try { - - ref SmbHeader responseHeader = ref response_encoded[0]; - if (responseHeader.IsError) { - // The usual reason is that authentication failed (bogus credentials). - // DebugStub.Break(); - DebugLine("Received SESSION SETUP response message, and server indicates FAILURE."); - DebugLine("Error: " + responseHeader.GetErrorText()); - throw new Exception("The SessionSetupAndTreeConnect message failed. The server rejected the request with an error: " + responseHeader.GetErrorText()); - } - - if (TraceSwitches.ShowNegotiation) - DebugLine("Received SESSION SETUP response (successful)"); - - // ref SmbSessionSetupResponse response = ref response_encoded[0]; - ExByteReader reader = new ExByteReader(0, response_encoded.Length); - reader.Skip(sizeof(SmbSessionSetupAndXResponse) + 1); // +1 for alignment - string! nativeOs = reader.ReadStringUnicode(response_encoded); - string! nativeLanMan = reader.ReadStringUnicode(response_encoded); - string! primaryDomain = reader.ReadStringUnicode(response_encoded); - - _ReceivedServerOperatingSystem = nativeOs; - _ReceivedServerPrimaryDomainName = primaryDomain; + request.Header.TotalMessageLength = total_length; - // The response contains a DIFFERENT user ID (in the mux tuple) than the request. - // This is how we learn the user ID, so that we can send it in future requests. - _userId = responseHeader.UserId; + byte[]! in ExHeap request_encoded = new[ExHeap] byte[total_length]; + + try { + ref SmbSessionSetupAndXRequest13 request_ref = ref request_encoded[0]; + // store values into the ex buffer + request_ref = request; + + data.CopyTo(request_encoded, sizeof(SmbSessionSetupAndXRequest13)); + } + catch { + delete request_encoded; + throw; + } + + if (TraceSwitches.ShowNegotiationVerbose) + DebugLine("Sending SESSION SETUP"); + + byte[]! in ExHeap response_encoded = ExecuteSynchronousCommand(request_encoded); + try { + + ref SmbHeader responseHeader = ref response_encoded[0]; + if (responseHeader.IsError) { + // The usual reason is that authentication failed (bogus credentials). + // DebugStub.Break(); + DebugLine("Received SESSION SETUP response message, and server indicates FAILURE."); + DebugLine("Error: " + responseHeader.GetErrorText()); + throw new Exception("The SessionSetupAndTreeConnect message failed. The server rejected the request with an error: " + responseHeader.GetErrorText()); + } + + if (TraceSwitches.ShowNegotiation) + DebugLine("Received SESSION SETUP response (successful)"); + + // ref SmbSessionSetupResponse response = ref response_encoded[0]; + ExByteReader reader = new ExByteReader(0, response_encoded.Length); + reader.Skip(sizeof(SmbSessionSetupAndXResponse) + 1); // +1 for alignment + string! nativeOs = reader.ReadStringUnicode(response_encoded); + string! nativeLanMan = reader.ReadStringUnicode(response_encoded); + string! primaryDomain = reader.ReadStringUnicode(response_encoded); + + _ReceivedServerOperatingSystem = nativeOs; + _ReceivedServerPrimaryDomainName = primaryDomain; + + // The response contains a DIFFERENT user ID (in the mux tuple) than the request. + // This is how we learn the user ID, so that we can send it in future requests. + _userId = responseHeader.UserId; + + if (TraceSwitches.ShowNegotiationVerbose) { + DebugLine(" Native OS: " + nativeOs); + DebugLine(" Native Lan Man: " + nativeLanMan); + DebugLine(" Primary Domain: " + primaryDomain); + DebugLine(" UserID = 0x{0:x4}", responseHeader.UserId); + } + + } + finally { + delete response_encoded; + } + } - if (TraceSwitches.ShowNegotiationVerbose) { - DebugLine(" Native OS: " + nativeOs); - DebugLine(" Native Lan Man: " + nativeLanMan); - DebugLine(" Primary Domain: " + primaryDomain); - DebugLine(" UserID = 0x{0:x4}", responseHeader.UserId); - } - - } finally { - delete response_encoded; - } - } - static void WriteSmbStringUnicode(ByteWriter! writer, string! text, bool terminate) - { - // writer.Write((byte)SmbEncodingFormat.String); - // writer.Write((byte)0); - - for (int i = 0; i < text.Length; i++) { - char c = text[i]; - writer.Write((byte)(c & 0xff)); - writer.Write((byte)(c >> 8)); - } + { + // writer.Write((byte)SmbEncodingFormat.String); + // writer.Write((byte)0); - if (terminate) - { + for (int i = 0; i < text.Length; i++) { + char c = text[i]; + writer.Write((byte)(c & 0xff)); + writer.Write((byte)(c >> 8)); + } + + if (terminate) { writer.Write((byte)0); writer.Write((byte)0); } - } + } static void WriteSmbStringAscii(ByteWriter! writer, string! text, bool terminate) { // writer.Write((byte)SmbEncodingFormat.String); - for (int i = 0; i < text.Length; i++) - { + for (int i = 0; i < text.Length; i++) { char c = text[i]; writer.Write((byte)(c & 0xff)); } - if (terminate) - { + if (terminate) { writer.Write((byte)0); } } - + byte[]! in ExHeap ExecuteSynchronousCommand([Claims]byte[]! in ExHeap request) { - SendSmbMessage(request); - - return WaitResponse(); - } - - void SendSmbMessage([Claims]byte[]! in ExHeap request) - // requires _connection != null; - { - if (request.Length < sizeof(SmbHeader)) - throw new Exception("Message is too short to be a valid SMB message."); - - ref SmbHeader header = ref request[0]; - header.SetSignature(); - header.TotalMessageLength = request.Length; - - SendDataSynchronous(request); - } - - void SendDataSynchronous([Claims]byte[]! in ExHeap data) - // requires _connection != null; - { - expose(this) - { - assert _connection != null; - _connection.SendWrite(data); - switch receive - { - case _connection.OK(): - break; - - case _connection.ChannelClosed(): - throw new Exception("TCP channel closed"); - - case _connection.ConnectionClosed(): - throw new Exception("TCP connection closed"); - } - } - } - - enum SmbEncodingFormat - { - DataBlock = 1, - Dialect = 2, // null terminated string, always 8-bit encoding, never unicode - Pathname = 3, // null terminated string - String = 4, // null terminated string - } + SendSmbMessage(request); - void ITracked.Release() {} - void ITracked.Acquire() {} - void ITracked.Expose() {} - void ITracked.UnExpose() {} - - public void Dispose() - { - _readyTransactors.Dispose(); - delete _namespace_provider; - delete _connection; - delete _notifier; - _control_clients.Dispose(); - delete _controller; - } + return WaitResponse(); + } + + void SendSmbMessage([Claims]byte[]! in ExHeap request) + // requires _connection != null; + { + if (request.Length < sizeof(SmbHeader)) + throw new Exception("Message is too short to be a valid SMB message."); + + ref SmbHeader header = ref request[0]; + header.SetSignature(); + header.TotalMessageLength = request.Length; + + SendDataSynchronous(request); + } + + void SendDataSynchronous([Claims]byte[]! in ExHeap data) + // requires _connection != null; + { + expose(this) + { + assert _connection != null; + _connection.SendWrite(data); + switch receive { + case _connection.OK(): + break; + + case _connection.ChannelClosed(): + throw new Exception("TCP channel closed"); + + case _connection.ConnectionClosed(): + throw new Exception("TCP connection closed"); + } + } + } + + enum SmbEncodingFormat + { + DataBlock = 1, + Dialect = 2, // null terminated string, always 8-bit encoding, never unicode + Pathname = 3, // null terminated string + String = 4, // null terminated string + } + + void ITracked.Release() {} + void ITracked.Acquire() {} + void ITracked.Expose() {} + void ITracked.UnExpose() {} + + public void Dispose() + { + _readyTransactors.Dispose(); + delete _namespace_provider; + delete _connection; + delete _notifier; + _control_clients.Dispose(); + delete _controller; + } } - + } diff --git a/base/Services/Smb/Client/TypesafeCollectionWrappers.sg b/base/Services/Smb/Client/TypesafeCollectionWrappers.sg index 50fdf6f..c698a70 100644 --- a/base/Services/Smb/Client/TypesafeCollectionWrappers.sg +++ b/base/Services/Smb/Client/TypesafeCollectionWrappers.sg @@ -9,12 +9,12 @@ // Note: // -/* - -This file contains typesafe collection wrappers. These are wrappers around the CLR 1.x (non-generic) collection -classes. When the backend MSIL compiler supports templates, these collections will go away. - -*/ +// +// +//This file contains typesafe collection wrappers. These are wrappers around the CLR 1.x (non-generic) collection +//classes. When the backend MSIL compiler supports templates, these collections will go away. +// +// using System; using System.Collections; @@ -61,13 +61,13 @@ namespace Smb.Client _list.RemoveAt(index); } - /* - public T this[int index] - { - get { return (T)_list[index]; } - set { _list[index] = value; } - } - */ + // + //public T this[int index] + //{ + // get { return (T)_list[index]; } + // set { _list[index] = value; } + //} + // } #if false diff --git a/base/Services/Smb/ClientService/ClientProcess.sg b/base/Services/Smb/ClientService/ClientProcess.sg index ba71af4..489390c 100644 --- a/base/Services/Smb/ClientService/ClientProcess.sg +++ b/base/Services/Smb/ClientService/ClientProcess.sg @@ -1,7 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // +// ---------------------------------------------------------------------------- using System; using Microsoft.SingSharp; diff --git a/base/Services/Smb/ClientService/SmbClientManager.sg b/base/Services/Smb/ClientService/SmbClientManager.sg index 0123fef..b95ce0a 100644 --- a/base/Services/Smb/ClientService/SmbClientManager.sg +++ b/base/Services/Smb/ClientService/SmbClientManager.sg @@ -32,458 +32,515 @@ using System; using System.Collections; using System.Diagnostics; using Microsoft.Contracts; +using Microsoft.SingSharp; using Microsoft.Singularity; using Microsoft.Singularity.Channels; using Microsoft.Singularity.Directory; using Microsoft.Singularity.Io; -using Microsoft.SingSharp; +using Microsoft.Singularity.ServiceManager; +using Microsoft.Singularity.Services; +using Microsoft.Singularity.Configuration; using Smb.PublicChannels; using Smb.PrivateChannels; -[assembly: Microsoft.Singularity.Security.ApplicationPublisherAttribute("singularity.microsoft.com")] -[assembly: Microsoft.Singularity.Security.AssertPrivilegeAttribute("$register-privilege.localhost")] - namespace Smb.ClientManager { - class Manager + [Category("Service")] + internal class ServiceParameters { - public static int Main(string[] args) + [CustomEndpoint] + public readonly TRef ControlEndpointRef; + + [CustomEndpoint] + public readonly TRef EventEndpointRef; + + reflective private ServiceParameters(); + } + + class Manager : ITracked + { + internal static int AppMain(ServiceParameters! parameters) { - // - // Register the control endpoint for this service. - // - - ServiceProviderContract.Imp! provider_imp; - ServiceProviderContract.Exp! controlprovider; - ServiceProviderContract.NewChannel(out provider_imp, out controlprovider); + assert parameters.ControlEndpointRef != null; + assert parameters.EventEndpointRef != null; + + ServiceProcessContract.Exp:Starting! svcontrol = parameters.ControlEndpointRef.Acquire(); + ServiceEventContract.Imp:Ready! svevent = parameters.EventEndpointRef.Acquire(); - DirectoryServiceContract.Imp! rootds = DirectoryService.NewClientEndpoint(); - rootds.SendRegister(Bitter.FromString2(SmbClientManagerContract.ManagerControlPath), provider_imp); - - switch receive - { - case rootds.AckRegister(): - delete rootds; - break; - - case rootds.NakRegister(ServiceProviderContract.Imp:Start imp, ErrorCode error): - DebugLine("The service failed to register its control channel."); - DebugLine(" Control channel path: " + SmbClientManagerContract.ManagerControlPath); - DebugLine(" Error code: " + error.ToString()); - DebugLine("Service will now terminate."); - delete rootds; - delete imp; - delete controlprovider; - return -1; - } - - ESet! managers = new ESet(); - - EMap! clientsConfiguring = - new EMap(); - - EMap! notifiers = - new EMap(); - - DebugLine("SMB service is ready, listening on control channel: " + SmbClientManagerContract.ManagerControlPath); - - for (;;) - { - switch receive - { - case controlprovider.Connect(ServiceContract.Exp:Start! exp): - SmbClientManagerContract.Exp manager = exp as SmbClientManagerContract.Exp; - if (manager != null) { - manager.SendSuccess(); - managers.Add(manager); - controlprovider.SendAckConnect(); - } else { - controlprovider.SendNackConnect(exp); - } - break; - - case manager.CreateClient(SmbClientConfig exconfig) in managers: - // This is a request to create a new SMB client process and start it. - string! mountPath = Bitter.ToString2(exconfig.MountPath); - if (TraceSwitches.ShowManagerMessages) { - DebugLine("CreateClient: mountpath = '{0}'", mountPath); - DebugLine(" UncPath: " + Bitter.ToString2(exconfig.UncPath)); - } - - // DebugStub.Break(); - - // Check to see if we already have a client using the same mount point. - // It's much better to detect this now, rather than to spin up a client - // process and have it discover the error. - ClientProcess client = FindClientByMountPath(mountPath); - if (client != null) { - exconfig.Dispose(); - manager.SendRequestFailed(SmbErrorCode.MountPathAlreadyExists, null); - managers.Add(manager); - break; - } - - SmbClientControllerContract.Imp! controller; - SmbClientControllerContract.Exp! controller_exp; - SmbClientControllerContract.NewChannel(out controller, out controller_exp); - - SmbMuxNotifier.Imp! notifier_imp; - SmbMuxNotifier.Exp! notifier_exp; - SmbMuxNotifier.NewChannel(out notifier_imp, out notifier_exp); + Manager! manager = new Manager(svcontrol, svevent); + try { + manager.Run(); + } + finally { + manager.Dispose(); + } + return 0; + } - Process process; - try { - string[] process_args = { "smbclient" }; - process = new Process(process_args); - } catch (Exception ex) { - DebugLine("FAILED to create smbclient process: " + ex.Message); - delete controller; - delete controller_exp; - delete notifier_imp; - delete notifier_exp; - manager.SendRequestFailed(SmbErrorCode.InternalError, Bitter.FromString2("Failed to create process: " + ex.Message)); - managers.Add(manager); - break; - } - - process.SetStartupEndpoint(0, controller_exp); - process.SetStartupEndpoint(1, notifier_imp); - - if (TraceSwitches.ShowProcessActivity) - DebugLine("Starting client process..."); - - process.Start(); - - if (TraceSwitches.ShowProcessActivity) - DebugLine("Process started. Sending startup parameters..."); - - client = new ClientProcess(mountPath, exconfig, process); - client.State = ClientState.Starting; - client.StartingManagerEndpoint = new TRef(manager); - - client.UncPath = Bitter.ToString2(exconfig.UncPath); - client.CredentialsName = Bitter.ToString2(exconfig.CredentialsName); - client.Tag = Bitter.ToString2(exconfig.Tag); - - client.ConnectionStatus = SmbClientConnectionStatus.Disconnected; - client.ReasonDisconnected = SmbReasonDisconnected.Idle; - client.ServerOperatingSystem = null; - client.ServerDomainName = null; - client.ServerMachineName = null; - - controller.SendConfigure(exconfig); - - // Further progress will be made when we receive AckConnect or NakConnect from the - // client process (or a ChannelClosed). - - notifiers.Add(notifier_exp, client); - - clientsConfiguring.Add(controller, client); - _clients[mountPath] = client; - break; - - case manager.QueryClientConfig(char[]! in ExHeap exclientId) in managers: - { - string! clientId = Bitter.ToString2(exclientId); - delete exclientId; - if (TraceSwitches.ShowManagerMessages) - DebugLine("Received QueryClientConfig - id = " + clientId); - - ClientProcess client = FindClientByMountPath(clientId); - if (client != null) { - SmbClientConfig config = new SmbClientConfig(); - config.MountPath = Bitter.FromString2(client.MountPath); - config.UncPath = Bitter.FromString2(client.UncPath); - config.CredentialsName = Bitter.FromString2(client.CredentialsName); - config.Tag = Bitter.FromString2(client.Tag); - - if (TraceSwitches.ShowManagerMessages) - DebugLine(" Sending config report."); - - manager.SendClientConfigReport(config); - } else { - if (TraceSwitches.ShowManagerMessages) - DebugLine(" No such client."); - - manager.SendRequestFailed(SmbErrorCode.MountPathNotFound, null); - } - - managers.Add(manager); - break; - } - - case manager.QueryClientStatus(char[]! in ExHeap exclientId) in managers: - { - string! clientId = Bitter.ToString2(exclientId); - delete exclientId; - - if (TraceSwitches.ShowManagerMessages) - DebugLine("Received QueryClientStatus - id = " + clientId); - - ClientProcess client = FindClientByMountPath(clientId); - if (client != null) { - SmbClientStatus status = new SmbClientStatus(); - - status.ConnectionStatus = client.ConnectionStatus; - status.ReasonDisconnected = client.ReasonDisconnected; - status.ServerOperatingSystem = Bitter.FromString(client.ServerOperatingSystem); - status.ServerDomainName = Bitter.FromString(client.ServerDomainName); - status.ServerMachineName = Bitter.FromString(client.ServerMachineName); - status.ActiveCredentialsName = Bitter.FromString(client.ActiveCredentialsName); - - if (TraceSwitches.ShowManagerMessages) - DebugLine(" Sending status report."); - - manager.SendClientStatusReport(status); - } else { - if (TraceSwitches.ShowManagerMessages) - DebugLine(" No such client."); - - manager.SendRequestFailed(SmbErrorCode.MountPathNotFound, null); - } - managers.Add(manager); - break; - } - - case controller.Ok() in clientsConfiguring ~> client: - if (TraceSwitches.ShowManagerMessages) - DebugLine(client, "Received AckConfigure from client process"); - - assert client.StartingManagerEndpoint != null; - - SmbClientManagerContract.Exp! manager = client.StartingManagerEndpoint.Acquire(); - - client.StartingManagerEndpoint = null; - client.State = ClientState.Running; - client.Controller = new TRef(controller); - - if (TraceSwitches.ShowManagerMessages) - DebugLine("Sending Ok (for CreateClient) to manager."); - - manager.SendOk(); - managers.Add(manager); - break; - - case controller.RequestFailed(error, errortext) in clientsConfiguring ~> client: - // One of the client processes that we started has indicated that it - // did not start successfully. Tear down the client process and send an - // error message to the manager that requested that the process to be created. - - if (TraceSwitches.ShowManagerMessages || true) - DebugLine(client, "Received NakConfigure from client process: {0} {1}", - error.ToString(), - Bitter.ToString(errortext)); - - delete controller; - - assert client.StartingManagerEndpoint != null; - SmbClientManagerContract.Exp! manager = client.StartingManagerEndpoint.Acquire(); - client.StartingManagerEndpoint = null; - client.State = ClientState.Running; - - client.Process.Stop(); - client.Process.Join(); - client.Process.Dispose(false); - - manager.SendRequestFailed(error, errortext); - managers.Add(manager); - break; - - case controller.ChannelClosed() in clientsConfiguring ~> client: - // A client process terminated before responding to the NakConnect message. - // Treat this like NakConnect without an error code. - if (TraceSwitches.ShowManagerMessages || true) - DebugLine(client, "Client process has closed its controller channel."); - - assert client.StartingManagerEndpoint != null; - SmbClientManagerContract.Exp! manager = client.StartingManagerEndpoint.Acquire(); - client.StartingManagerEndpoint = null; - client.State = ClientState.Running; - - client.Process.Stop(); - client.Process.Join(); - client.Process.Dispose(false); - - manager.SendRequestFailed(SmbErrorCode.InternalError, - Bitter.FromString2("The SMB client process failed to initialize.")); - managers.Add(manager); - delete controller; - break; - - case manager.StopClient(char[]! in ExHeap exmountPath) in managers: - string! mountPath = Util.ToStringDelete(exmountPath); - - if (TraceSwitches.ShowManagerMessages) - DebugLine("StopClient - mount path = " + mountPath); - - ClientProcess client = FindClientByMountPath(mountPath); - if (client != null) - { - switch (client.State) - { - case ClientState.Starting: - // This is extremely rude, of course. - DebugLine(" Client is Starting - will simply terminate the process."); - break; - - case ClientState.Running: - assert client.Controller != null; - SmbClientControllerContract.Imp! controller = client.Controller.Acquire(); - - controller.SendStop(); - - // -XXX- This is synchronous for now! - DebugLine("Waiting to SMB client to respond to Stop request"); - switch receive { - case controller.Ok(): - break; - - case controller.ChannelClosed(): - break; - } - - client.Controller.Release(controller); - break; - - default: - DebugLine("ILLEGAL STATE FOR CLIENT PROCESS!"); - break; - } - - TerminateClient(client, true); - - manager.SendOk(); - } else { - if (TraceSwitches.ShowManagerMessages) - DebugLine("Cannot delete client -- there is no client with mount path = '{0}'", mountPath); - - manager.SendRequestFailed(SmbErrorCode.MountPathNotFound, null); - } - - managers.Add(manager); - break; - - case controlprovider.ChannelClosed(): - DebugLine("Control provider channel has closed."); - goto terminate; - - case manager.EnumClients() in managers: - { - int count = _clients.Values.Count; - char[][]! in ExHeap mountPaths = new[ExHeap] char[][count]; - int i = 0; - foreach (ClientProcess! client in _clients.Values) - { - if (i >= count) - break; - char[]! in ExHeap exmount = Bitter.FromString2(client.MountPath); - expose (mountPaths[i]) { - delete mountPaths[i]; - mountPaths[i] = exmount; - } - i++; - } - manager.SendClientList(mountPaths); - managers.Add(manager); - break; - } - - case manager.BindClient(char[]! in ExHeap exmountPath, ServiceContract.Exp:Start! exp) in managers: - { - string! mountPath = Util.ToStringDelete(exmountPath); - if (TraceSwitches.ShowManagerMessages) - DebugLine("BindClient - mount path = " + mountPath); - - ClientProcess client = FindClientByMountPath(mountPath); - if (client != null) - { - switch (client.State) - { - case ClientState.Running: - assert client.Controller != null; - SmbClientControllerContract.Imp! controller = client.Controller.Acquire(); - try { - controller.SendBind(exp); - - // -XXX- Yes, I know this prevents SmbClientManager from receiving - // -XXX- any other messages. Need to deal with that. - switch receive { - case controller.Ok(): - if (TraceSwitches.ShowManagerMessages) - DebugLine("Bind successful"); - manager.SendOk(); - break; - - case controller.RequestFailed(error, optionalErrorText): - if (TraceSwitches.ShowManagerMessages) - DebugLine("SmbClient process rejected bind request"); - manager.SendRequestFailed(error, optionalErrorText); - break; - - case controller.ChannelClosed(): - if (TraceSwitches.ShowManagerMessages) - DebugLine("SmbClient channel closed!"); - manager.SendRequestFailed(SmbErrorCode.InternalError, null); - break; - } - - } finally { - client.Controller.Release(controller); - } - break; - - default: - if (TraceSwitches.ShowManagerMessages) - DebugLine("Client is not in a state that allows binding"); - manager.SendRequestFailed(SmbErrorCode.InvalidState, null); - delete exp; - break; - } - } - else - { - delete exp; - manager.SendRequestFailed(SmbErrorCode.MountPathNotFound, null); - } - - managers.Add(manager); - break; - } - - case notifier.StatusChanged(SmbClientStatus status) in notifiers ~> client: - notifier.SendAck(); - - if (TraceSwitches.ShowStatusChanges) - DebugLine(client, "Received StatusChanged from client"); - - client.ConnectionStatus = status.ConnectionStatus; - client.ReasonDisconnected = status.ReasonDisconnected; - client.ServerOperatingSystem = Bitter.ToString(status.ServerOperatingSystem); - client.ServerMachineName = Bitter.ToString(status.ServerMachineName); - client.ServerDomainName = Bitter.ToString(status.ServerDomainName); - client.ActiveCredentialsName = Bitter.ToString(status.ActiveCredentialsName); - status.Dispose(); - - notifiers.Add(notifier, client); - break; - - } // switch receive - } + Manager( + [Claims]ServiceProcessContract.Exp:Starting! svcontrol, + [Claims]ServiceEventContract.Imp:Ready! svevent) + { + this.svcontrol = svcontrol; + this.svevent = svevent; + } - terminate: - managers.Dispose(); - delete controlprovider; - notifiers.Dispose(); + #region Data Fields + + readonly ESet! managers = new ESet(); + + readonly EMap! clientsConfiguring = + new EMap(); - clientsConfiguring.Dispose(); + readonly EMap! notifiers = + new EMap(); - foreach (ClientProcess! client in _clients.Values) - { - TerminateClient(client, false); - } - _clients.Clear(); + readonly ServiceProcessContract.Exp! svcontrol; + readonly ServiceEventContract.Imp! svevent; + + #endregion + + public void Dispose() + { + DebugLine("Service has terminated."); - return 0; + foreach (ClientProcess! client in _clients.Values) + { + TerminateClient(client, false); + } + _clients.Clear(); + + delete managers; + delete clientsConfiguring; + delete notifiers; + delete svcontrol; + delete svevent; + //managers.Dispose(); + //notifiers.Dispose(); + //clientsConfiguring.Dispose(); + } + + void Run() + { + expose (this) { + + svcontrol.SendStartSucceeded(); + + for (;;) + { + switch receive + { + #region Event handlers for service control channel, ServiceProcessContract + + case svcontrol.Knock(): + DebugLine("Received 'Knock' from Service Manager"); + svcontrol.SendAlive(); + break; + + case svcontrol.Stop(): + DebugLine("Received 'Stop' from Service Manager"); + svcontrol.SendAckStop(); + return; + + case svcontrol.Connect(char[] in ExHeap expath, ServiceContract.Exp:Start! exp): + { + if (expath != null) { + string! path = Bitter.ToString2(expath); + delete expath; + DebugLine("A client wants to connect, using subpath '{0}'. This service does not support subpaths; rejecting client.", path); + delete exp; + svcontrol.SendNakConnect(ErrorCode.Unknown, null); + break; + } + + SmbClientManagerContract.Exp manager = exp as SmbClientManagerContract.Exp; + if (manager != null) { + DebugLine("A client has connected using SmbClientManagerContract."); + manager.SendSuccess(); + managers.Add(manager); + svcontrol.SendAckConnect(); + } + else { + DebugLine("A client wants to connect, but is using an unsupported contract."); + delete exp; + svcontrol.SendNakConnect(ErrorCode.ContractNotSupported, null); + } + break; + } + + case svcontrol.ChannelClosed(): + // Uh oh! + DebugLine("Service Manager has closed its control channel! Not good at all!"); + return; + + #endregion + + case manager.CreateClient(SmbClientConfig exconfig) in managers: + // This is a request to create a new SMB client process and start it. + string! mountPath = Bitter.ToString2(exconfig.MountPath); + if (TraceSwitches.ShowManagerMessages) { + DebugLine("CreateClient: mountpath = '{0}'", mountPath); + DebugLine(" UncPath: " + Bitter.ToString2(exconfig.UncPath)); + } + + // DebugStub.Break(); + + // Check to see if we already have a client using the same mount point. + // It's much better to detect this now, rather than to spin up a client + // process and have it discover the error. + ClientProcess client = FindClientByMountPath(mountPath); + if (client != null) { + exconfig.Dispose(); + manager.SendRequestFailed(SmbErrorCode.MountPathAlreadyExists, null); + managers.Add(manager); + break; + } + + SmbClientControllerContract.Imp! controller; + SmbClientControllerContract.Exp! controller_exp; + SmbClientControllerContract.NewChannel(out controller, out controller_exp); + + SmbMuxNotifier.Imp! notifier_imp; + SmbMuxNotifier.Exp! notifier_exp; + SmbMuxNotifier.NewChannel(out notifier_imp, out notifier_exp); + + Process process; + try { + string[] process_args = { "smbclient" }; + process = new Process(process_args); + } + catch (Exception ex) { + DebugLine("FAILED to create smbclient process: " + ex.Message); + delete controller; + delete controller_exp; + delete notifier_imp; + delete notifier_exp; + manager.SendRequestFailed(SmbErrorCode.InternalError, Bitter.FromString2("Failed to create process: " + ex.Message)); + managers.Add(manager); + break; + } + + process.SetStartupEndpoint(0, controller_exp); + process.SetStartupEndpoint(1, notifier_imp); + + if (TraceSwitches.ShowProcessActivity) + DebugLine("Starting client process..."); + + process.Start(); + + if (TraceSwitches.ShowProcessActivity) + DebugLine("Process started. Sending startup parameters..."); + + client = new ClientProcess(mountPath, exconfig, process); + client.State = ClientState.Starting; + client.StartingManagerEndpoint = new TRef(manager); + + client.UncPath = Bitter.ToString2(exconfig.UncPath); + client.CredentialsName = Bitter.ToString2(exconfig.CredentialsName); + client.Tag = Bitter.ToString2(exconfig.Tag); + + client.ConnectionStatus = SmbClientConnectionStatus.Disconnected; + client.ReasonDisconnected = SmbReasonDisconnected.Idle; + client.ServerOperatingSystem = null; + client.ServerDomainName = null; + client.ServerMachineName = null; + + controller.SendConfigure(exconfig); + + // Further progress will be made when we receive AckConnect or NakConnect from the + // client process (or a ChannelClosed). + + notifiers.Add(notifier_exp, client); + + clientsConfiguring.Add(controller, client); + _clients[mountPath] = client; + break; + + case manager.QueryClientConfig(char[]! in ExHeap exclientId) in managers: + { + string! clientId = Bitter.ToString2(exclientId); + delete exclientId; + if (TraceSwitches.ShowManagerMessages) + DebugLine("Received QueryClientConfig - id = " + clientId); + + ClientProcess client = FindClientByMountPath(clientId); + if (client != null) { + SmbClientConfig config = new SmbClientConfig(); + config.MountPath = Bitter.FromString2(client.MountPath); + config.UncPath = Bitter.FromString2(client.UncPath); + config.CredentialsName = Bitter.FromString2(client.CredentialsName); + config.Tag = Bitter.FromString2(client.Tag); + + if (TraceSwitches.ShowManagerMessages) + DebugLine(" Sending config report."); + + manager.SendClientConfigReport(config); + } + else { + if (TraceSwitches.ShowManagerMessages) + DebugLine(" No such client."); + + manager.SendRequestFailed(SmbErrorCode.MountPathNotFound, null); + } + + managers.Add(manager); + break; + } + + case manager.QueryClientStatus(char[]! in ExHeap exclientId) in managers: + { + string! clientId = Bitter.ToString2(exclientId); + delete exclientId; + + if (TraceSwitches.ShowManagerMessages) + DebugLine("Received QueryClientStatus - id = " + clientId); + + ClientProcess client = FindClientByMountPath(clientId); + if (client != null) { + SmbClientStatus status = new SmbClientStatus(); + + status.ConnectionStatus = client.ConnectionStatus; + status.ReasonDisconnected = client.ReasonDisconnected; + status.ServerOperatingSystem = Bitter.FromString(client.ServerOperatingSystem); + status.ServerDomainName = Bitter.FromString(client.ServerDomainName); + status.ServerMachineName = Bitter.FromString(client.ServerMachineName); + status.ActiveCredentialsName = Bitter.FromString(client.ActiveCredentialsName); + + if (TraceSwitches.ShowManagerMessages) + DebugLine(" Sending status report."); + + manager.SendClientStatusReport(status); + } + else { + if (TraceSwitches.ShowManagerMessages) + DebugLine(" No such client."); + + manager.SendRequestFailed(SmbErrorCode.MountPathNotFound, null); + } + managers.Add(manager); + break; + } + + case controller.Ok() in clientsConfiguring ~> client: + if (TraceSwitches.ShowManagerMessages) + DebugLine(client, "Received AckConfigure from client process"); + + assert client.StartingManagerEndpoint != null; + + SmbClientManagerContract.Exp! manager = client.StartingManagerEndpoint.Acquire(); + + client.StartingManagerEndpoint = null; + client.State = ClientState.Running; + client.Controller = new TRef(controller); + + if (TraceSwitches.ShowManagerMessages) + DebugLine("Sending Ok (for CreateClient) to manager."); + + manager.SendOk(); + managers.Add(manager); + break; + + case controller.RequestFailed(error, errortext) in clientsConfiguring ~> client: + // One of the client processes that we started has indicated that it + // did not start successfully. Tear down the client process and send an + // error message to the manager that requested that the process to be created. + + if (TraceSwitches.ShowManagerMessages || true) + DebugLine(client, "Received NakConfigure from client process: {0} {1}", + error.ToString(), + Bitter.ToString(errortext)); + + delete controller; + + assert client.StartingManagerEndpoint != null; + SmbClientManagerContract.Exp! manager = client.StartingManagerEndpoint.Acquire(); + client.StartingManagerEndpoint = null; + client.State = ClientState.Running; + + client.Process.Stop(); + client.Process.Join(); + client.Process.Dispose(false); + + manager.SendRequestFailed(error, errortext); + managers.Add(manager); + break; + + case controller.ChannelClosed() in clientsConfiguring ~> client: + // A client process terminated before responding to the NakConnect message. + // Treat this like NakConnect without an error code. + if (TraceSwitches.ShowManagerMessages || true) + DebugLine(client, "Client process has closed its controller channel."); + + assert client.StartingManagerEndpoint != null; + SmbClientManagerContract.Exp! manager = client.StartingManagerEndpoint.Acquire(); + client.StartingManagerEndpoint = null; + client.State = ClientState.Running; + + client.Process.Stop(); + client.Process.Join(); + client.Process.Dispose(false); + + manager.SendRequestFailed(SmbErrorCode.InternalError, + Bitter.FromString2("The SMB client process failed to initialize.")); + managers.Add(manager); + delete controller; + break; + + case manager.StopClient(char[]! in ExHeap exmountPath) in managers: + string! mountPath = Util.ToStringDelete(exmountPath); + + if (TraceSwitches.ShowManagerMessages) + DebugLine("StopClient - mount path = " + mountPath); + + ClientProcess client = FindClientByMountPath(mountPath); + if (client != null) + { + switch (client.State) + { + case ClientState.Starting: + // This is extremely rude, of course. + DebugLine(" Client is Starting - will simply terminate the process."); + break; + + case ClientState.Running: + SmbClientControllerContract.Imp! controller = ((!)client.Controller).Acquire(); + + controller.SendStop(); + + // -XXX- This is synchronous for now! + DebugLine("Waiting to SMB client to respond to Stop request"); + switch receive { + case controller.Ok(): + break; + + case controller.ChannelClosed(): + break; + } + + client.Controller.Release(controller); + break; + + default: + DebugLine("ILLEGAL STATE FOR CLIENT PROCESS!"); + break; + } + + TerminateClient(client, true); + + manager.SendOk(); + } + else { + if (TraceSwitches.ShowManagerMessages) + DebugLine("Cannot delete client -- there is no client with mount path = '{0}'", mountPath); + + manager.SendRequestFailed(SmbErrorCode.MountPathNotFound, null); + } + + managers.Add(manager); + break; + + case manager.EnumClients() in managers: + { + int count = _clients.Values.Count; + char[][]! in ExHeap mountPaths = new[ExHeap] char[][count]; + int i = 0; + foreach (ClientProcess! client in _clients.Values) + { + if (i >= count) + break; + char[]! in ExHeap exmount = Bitter.FromString2(client.MountPath); + expose (mountPaths[i]) { + delete mountPaths[i]; + mountPaths[i] = exmount; + } + i++; + } + manager.SendClientList(mountPaths); + managers.Add(manager); + break; + } + + case manager.BindClient(char[]! in ExHeap exmountPath, ServiceContract.Exp:Start! exp) in managers: + { + string! mountPath = Util.ToStringDelete(exmountPath); + if (TraceSwitches.ShowManagerMessages) + DebugLine("BindClient - mount path = " + mountPath); + + ClientProcess client = FindClientByMountPath(mountPath); + if (client != null) + { + switch (client.State) + { + case ClientState.Running: + SmbClientControllerContract.Imp! controller = ((!)client.Controller).Acquire(); + try { + controller.SendBind(exp); + + // -XXX- Yes, I know this prevents SmbClientManager from receiving + // -XXX- any other messages. Need to deal with that. + switch receive { + case controller.Ok(): + if (TraceSwitches.ShowManagerMessages) + DebugLine("Bind successful"); + manager.SendOk(); + break; + + case controller.RequestFailed(error, optionalErrorText): + if (TraceSwitches.ShowManagerMessages) + DebugLine("SmbClient process rejected bind request"); + manager.SendRequestFailed(error, optionalErrorText); + break; + + case controller.ChannelClosed(): + if (TraceSwitches.ShowManagerMessages) + DebugLine("SmbClient channel closed!"); + manager.SendRequestFailed(SmbErrorCode.InternalError, null); + break; + } + + } + finally { + client.Controller.Release(controller); + } + break; + + default: + if (TraceSwitches.ShowManagerMessages) + DebugLine("Client is not in a state that allows binding"); + manager.SendRequestFailed(SmbErrorCode.InvalidState, null); + delete exp; + break; + } + } + else { + delete exp; + manager.SendRequestFailed(SmbErrorCode.MountPathNotFound, null); + } + + managers.Add(manager); + break; + } + + case notifier.StatusChanged(SmbClientStatus status) in notifiers ~> client: + notifier.SendAck(); + + if (TraceSwitches.ShowStatusChanges) + DebugLine(client, "Received StatusChanged from client"); + + client.ConnectionStatus = status.ConnectionStatus; + client.ReasonDisconnected = status.ReasonDisconnected; + client.ServerOperatingSystem = Bitter.ToString(status.ServerOperatingSystem); + client.ServerMachineName = Bitter.ToString(status.ServerMachineName); + client.ServerDomainName = Bitter.ToString(status.ServerDomainName); + client.ActiveCredentialsName = Bitter.ToString(status.ActiveCredentialsName); + status.Dispose(); + + notifiers.Add(notifier, client); + break; + } + } + + } } /// @@ -498,7 +555,7 @@ namespace Smb.ClientManager /// process; the keys are case sensitive. Each value is the corresponding /// ClientProcess instance. /// - /// + /// static readonly Hashtable/**/! _clients = new Hashtable(); static ClientProcess FindClientByMountPath(string! mountPath) @@ -523,8 +580,7 @@ namespace Smb.ClientManager case ClientState.Running: case ClientState.Stopping: - assert client.Controller != null; - SmbClientControllerContract.Imp! controller = client.Controller.Acquire(); + SmbClientControllerContract.Imp! controller = ((!)client.Controller).Acquire(); delete controller; break; @@ -535,13 +591,13 @@ namespace Smb.ClientManager // This is conditional, because we don't want to modify the _clients collection // while we're enumerating it. - if (remove) + if (remove) _clients.Remove(client.MountPath); } static void DebugLine(string! line) { - DebugStub.WriteLine("SMBMGR: " + line); + DebugStub.WriteLine(line); } static void DebugLine(string! format, params object[]! args) @@ -558,19 +614,13 @@ namespace Smb.ClientManager { DebugLine(client, String.Format(format, args)); } + + void ITracked.Expose() {} + void ITracked.UnExpose() {} + void ITracked.Acquire() {} + void ITracked.Release() {} } - // This class mirrors the exchange type SmbClientConfig. - // It is for use within the process heap, rather than the exchange heap. - #if false - class ClientConfig - { - public string! MountPath; - public string! UncPath; - public string! UserName; - public string! Password; - } - #endif class Util diff --git a/base/Services/Smb/ClientService/SmbClientService.csproj b/base/Services/Smb/ClientService/SmbClientService.csproj index 7ce4560..673b2e3 100644 --- a/base/Services/Smb/ClientService/SmbClientService.csproj +++ b/base/Services/Smb/ClientService/SmbClientService.csproj @@ -1,8 +1,6 @@  + diff --git a/base/Services/Smb/Smb.sln b/base/Services/Smb/Smb.sln deleted file mode 100644 index 2c76ffb..0000000 --- a/base/Services/Smb/Smb.sln +++ /dev/null @@ -1,149 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmbClient.PrivateChannels", "PrivateContracts\SmbClient.PrivateChannels.csproj", "{83A5F7CD-2620-4250-B157-E15B0EAC2D45}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmbClient.PublicContracts", "PublicContracts\SmbClient.PublicContracts.csproj", "{320A73D0-FE2A-4454-864D-97BB997D388E}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Smb", "Smb", "{C9E9C530-4606-4590-902A-21FC5C301EF9}" - ProjectSection(SolutionItems) = preProject - Makefile = Makefile - EndProjectSection -EndProject -Project("{2D5F3EBF-B93A-4468-B236-4C125BB2FD8A}") = "Smb.Control", "Control\SmbControl.sgproj", "{ADB98D05-A8C3-419C-AA3F-A23E2A1B66A1}" -EndProject -Project("{2D5F3EBF-B93A-4468-B236-4C125BB2FD8A}") = "Smb.PrivateContracts", "PrivateContracts\PrivateContracts.sgproj", "{B80B3E7B-C834-42B3-8708-7A4F5D5725FB}" -EndProject -Project("{2D5F3EBF-B93A-4468-B236-4C125BB2FD8A}") = "Smb.PublicContracts", "PublicContracts\PublicContracts.sgproj", "{3951F9E6-A796-4950-9CF1-9F2A9FEF90D9}" -EndProject -Project("{2D5F3EBF-B93A-4468-B236-4C125BB2FD8A}") = "Smb.ClientService", "ClientService\ClientService.sgproj", "{3FEA42BA-EB84-4AEF-9C47-247B74A5CF9E}" -EndProject -Project("{2D5F3EBF-B93A-4468-B236-4C125BB2FD8A}") = "Smb.Client", "Client\Smb.Client.sgproj", "{725C5906-B421-4D78-AA81-1E3DD02DA131}" -EndProject -Project("{2D5F3EBF-B93A-4468-B236-4C125BB2FD8A}") = "Smb.Shared", "Shared\Smb.Shared.sgproj", "{9D2153CC-266D-484D-9777-13C9081EB7EB}" -EndProject -Project("{2D5F3EBF-B93A-4468-B236-4C125BB2FD8A}") = "Smb.Protocol", "Protocol\Smb.Protocol.sgproj", "{C13F889C-ECE4-4658-8269-A8BEBFC0EF30}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|.NET = Debug|.NET - Debug|Any CPU = Debug|Any CPU - Debug|Mixed Platforms = Debug|Mixed Platforms - Debug|Win32 = Debug|Win32 - Release|.NET = Release|.NET - Release|Any CPU = Release|Any CPU - Release|Mixed Platforms = Release|Mixed Platforms - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {83A5F7CD-2620-4250-B157-E15B0EAC2D45}.Debug|.NET.ActiveCfg = Debug|Any CPU - {83A5F7CD-2620-4250-B157-E15B0EAC2D45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {83A5F7CD-2620-4250-B157-E15B0EAC2D45}.Debug|Any CPU.Build.0 = Debug|Any CPU - {83A5F7CD-2620-4250-B157-E15B0EAC2D45}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {83A5F7CD-2620-4250-B157-E15B0EAC2D45}.Debug|Win32.ActiveCfg = Debug|Any CPU - {83A5F7CD-2620-4250-B157-E15B0EAC2D45}.Release|.NET.ActiveCfg = Release|Any CPU - {83A5F7CD-2620-4250-B157-E15B0EAC2D45}.Release|Any CPU.ActiveCfg = Release|Any CPU - {83A5F7CD-2620-4250-B157-E15B0EAC2D45}.Release|Any CPU.Build.0 = Release|Any CPU - {83A5F7CD-2620-4250-B157-E15B0EAC2D45}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {83A5F7CD-2620-4250-B157-E15B0EAC2D45}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {83A5F7CD-2620-4250-B157-E15B0EAC2D45}.Release|Win32.ActiveCfg = Release|Any CPU - {320A73D0-FE2A-4454-864D-97BB997D388E}.Debug|.NET.ActiveCfg = Debug|Any CPU - {320A73D0-FE2A-4454-864D-97BB997D388E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {320A73D0-FE2A-4454-864D-97BB997D388E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {320A73D0-FE2A-4454-864D-97BB997D388E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {320A73D0-FE2A-4454-864D-97BB997D388E}.Debug|Win32.ActiveCfg = Debug|Any CPU - {320A73D0-FE2A-4454-864D-97BB997D388E}.Release|.NET.ActiveCfg = Release|Any CPU - {320A73D0-FE2A-4454-864D-97BB997D388E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {320A73D0-FE2A-4454-864D-97BB997D388E}.Release|Any CPU.Build.0 = Release|Any CPU - {320A73D0-FE2A-4454-864D-97BB997D388E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {320A73D0-FE2A-4454-864D-97BB997D388E}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {320A73D0-FE2A-4454-864D-97BB997D388E}.Release|Win32.ActiveCfg = Release|Any CPU - {ADB98D05-A8C3-419C-AA3F-A23E2A1B66A1}.Debug|.NET.ActiveCfg = Debug|.NET - {ADB98D05-A8C3-419C-AA3F-A23E2A1B66A1}.Debug|.NET.Build.0 = Debug|.NET - {ADB98D05-A8C3-419C-AA3F-A23E2A1B66A1}.Debug|Any CPU.ActiveCfg = Debug|.NET - {ADB98D05-A8C3-419C-AA3F-A23E2A1B66A1}.Debug|Mixed Platforms.ActiveCfg = Debug|.NET - {ADB98D05-A8C3-419C-AA3F-A23E2A1B66A1}.Debug|Mixed Platforms.Build.0 = Debug|.NET - {ADB98D05-A8C3-419C-AA3F-A23E2A1B66A1}.Debug|Win32.ActiveCfg = Debug|.NET - {ADB98D05-A8C3-419C-AA3F-A23E2A1B66A1}.Release|.NET.ActiveCfg = Release|.NET - {ADB98D05-A8C3-419C-AA3F-A23E2A1B66A1}.Release|.NET.Build.0 = Release|.NET - {ADB98D05-A8C3-419C-AA3F-A23E2A1B66A1}.Release|Any CPU.ActiveCfg = Release|.NET - {ADB98D05-A8C3-419C-AA3F-A23E2A1B66A1}.Release|Mixed Platforms.ActiveCfg = Release|.NET - {ADB98D05-A8C3-419C-AA3F-A23E2A1B66A1}.Release|Mixed Platforms.Build.0 = Release|.NET - {ADB98D05-A8C3-419C-AA3F-A23E2A1B66A1}.Release|Win32.ActiveCfg = Release|.NET - {B80B3E7B-C834-42B3-8708-7A4F5D5725FB}.Debug|.NET.ActiveCfg = Debug|.NET - {B80B3E7B-C834-42B3-8708-7A4F5D5725FB}.Debug|.NET.Build.0 = Debug|.NET - {B80B3E7B-C834-42B3-8708-7A4F5D5725FB}.Debug|Any CPU.ActiveCfg = Debug|.NET - {B80B3E7B-C834-42B3-8708-7A4F5D5725FB}.Debug|Mixed Platforms.ActiveCfg = Debug|.NET - {B80B3E7B-C834-42B3-8708-7A4F5D5725FB}.Debug|Mixed Platforms.Build.0 = Debug|.NET - {B80B3E7B-C834-42B3-8708-7A4F5D5725FB}.Debug|Win32.ActiveCfg = Debug|.NET - {B80B3E7B-C834-42B3-8708-7A4F5D5725FB}.Release|.NET.ActiveCfg = Release|.NET - {B80B3E7B-C834-42B3-8708-7A4F5D5725FB}.Release|.NET.Build.0 = Release|.NET - {B80B3E7B-C834-42B3-8708-7A4F5D5725FB}.Release|Any CPU.ActiveCfg = Release|.NET - {B80B3E7B-C834-42B3-8708-7A4F5D5725FB}.Release|Mixed Platforms.ActiveCfg = Release|.NET - {B80B3E7B-C834-42B3-8708-7A4F5D5725FB}.Release|Mixed Platforms.Build.0 = Release|.NET - {B80B3E7B-C834-42B3-8708-7A4F5D5725FB}.Release|Win32.ActiveCfg = Release|.NET - {3951F9E6-A796-4950-9CF1-9F2A9FEF90D9}.Debug|.NET.ActiveCfg = Debug|.NET - {3951F9E6-A796-4950-9CF1-9F2A9FEF90D9}.Debug|.NET.Build.0 = Debug|.NET - {3951F9E6-A796-4950-9CF1-9F2A9FEF90D9}.Debug|Any CPU.ActiveCfg = Debug|.NET - {3951F9E6-A796-4950-9CF1-9F2A9FEF90D9}.Debug|Mixed Platforms.ActiveCfg = Debug|.NET - {3951F9E6-A796-4950-9CF1-9F2A9FEF90D9}.Debug|Mixed Platforms.Build.0 = Debug|.NET - {3951F9E6-A796-4950-9CF1-9F2A9FEF90D9}.Debug|Win32.ActiveCfg = Debug|.NET - {3951F9E6-A796-4950-9CF1-9F2A9FEF90D9}.Release|.NET.ActiveCfg = Release|.NET - {3951F9E6-A796-4950-9CF1-9F2A9FEF90D9}.Release|.NET.Build.0 = Release|.NET - {3951F9E6-A796-4950-9CF1-9F2A9FEF90D9}.Release|Any CPU.ActiveCfg = Release|.NET - {3951F9E6-A796-4950-9CF1-9F2A9FEF90D9}.Release|Mixed Platforms.ActiveCfg = Release|.NET - {3951F9E6-A796-4950-9CF1-9F2A9FEF90D9}.Release|Mixed Platforms.Build.0 = Release|.NET - {3951F9E6-A796-4950-9CF1-9F2A9FEF90D9}.Release|Win32.ActiveCfg = Release|.NET - {3FEA42BA-EB84-4AEF-9C47-247B74A5CF9E}.Debug|.NET.ActiveCfg = Debug|.NET - {3FEA42BA-EB84-4AEF-9C47-247B74A5CF9E}.Debug|.NET.Build.0 = Debug|.NET - {3FEA42BA-EB84-4AEF-9C47-247B74A5CF9E}.Debug|Any CPU.ActiveCfg = Debug|.NET - {3FEA42BA-EB84-4AEF-9C47-247B74A5CF9E}.Debug|Mixed Platforms.ActiveCfg = Debug|.NET - {3FEA42BA-EB84-4AEF-9C47-247B74A5CF9E}.Debug|Mixed Platforms.Build.0 = Debug|.NET - {3FEA42BA-EB84-4AEF-9C47-247B74A5CF9E}.Debug|Win32.ActiveCfg = Debug|.NET - {3FEA42BA-EB84-4AEF-9C47-247B74A5CF9E}.Release|.NET.ActiveCfg = Release|.NET - {3FEA42BA-EB84-4AEF-9C47-247B74A5CF9E}.Release|.NET.Build.0 = Release|.NET - {3FEA42BA-EB84-4AEF-9C47-247B74A5CF9E}.Release|Any CPU.ActiveCfg = Release|.NET - {3FEA42BA-EB84-4AEF-9C47-247B74A5CF9E}.Release|Mixed Platforms.ActiveCfg = Release|.NET - {3FEA42BA-EB84-4AEF-9C47-247B74A5CF9E}.Release|Mixed Platforms.Build.0 = Release|.NET - {3FEA42BA-EB84-4AEF-9C47-247B74A5CF9E}.Release|Win32.ActiveCfg = Release|.NET - {725C5906-B421-4D78-AA81-1E3DD02DA131}.Debug|.NET.ActiveCfg = Debug|.NET - {725C5906-B421-4D78-AA81-1E3DD02DA131}.Debug|.NET.Build.0 = Debug|.NET - {725C5906-B421-4D78-AA81-1E3DD02DA131}.Debug|Any CPU.ActiveCfg = Debug|.NET - {725C5906-B421-4D78-AA81-1E3DD02DA131}.Debug|Mixed Platforms.ActiveCfg = Debug|.NET - {725C5906-B421-4D78-AA81-1E3DD02DA131}.Debug|Mixed Platforms.Build.0 = Debug|.NET - {725C5906-B421-4D78-AA81-1E3DD02DA131}.Debug|Win32.ActiveCfg = Debug|.NET - {725C5906-B421-4D78-AA81-1E3DD02DA131}.Release|.NET.ActiveCfg = Release|.NET - {725C5906-B421-4D78-AA81-1E3DD02DA131}.Release|.NET.Build.0 = Release|.NET - {725C5906-B421-4D78-AA81-1E3DD02DA131}.Release|Any CPU.ActiveCfg = Release|.NET - {725C5906-B421-4D78-AA81-1E3DD02DA131}.Release|Mixed Platforms.ActiveCfg = Release|.NET - {725C5906-B421-4D78-AA81-1E3DD02DA131}.Release|Mixed Platforms.Build.0 = Release|.NET - {725C5906-B421-4D78-AA81-1E3DD02DA131}.Release|Win32.ActiveCfg = Release|.NET - {9D2153CC-266D-484D-9777-13C9081EB7EB}.Debug|.NET.ActiveCfg = Debug|.NET - {9D2153CC-266D-484D-9777-13C9081EB7EB}.Debug|.NET.Build.0 = Debug|.NET - {9D2153CC-266D-484D-9777-13C9081EB7EB}.Debug|Any CPU.ActiveCfg = Debug|.NET - {9D2153CC-266D-484D-9777-13C9081EB7EB}.Debug|Mixed Platforms.ActiveCfg = Debug|.NET - {9D2153CC-266D-484D-9777-13C9081EB7EB}.Debug|Mixed Platforms.Build.0 = Debug|.NET - {9D2153CC-266D-484D-9777-13C9081EB7EB}.Debug|Win32.ActiveCfg = Debug|.NET - {9D2153CC-266D-484D-9777-13C9081EB7EB}.Release|.NET.ActiveCfg = Release|.NET - {9D2153CC-266D-484D-9777-13C9081EB7EB}.Release|.NET.Build.0 = Release|.NET - {9D2153CC-266D-484D-9777-13C9081EB7EB}.Release|Any CPU.ActiveCfg = Release|.NET - {9D2153CC-266D-484D-9777-13C9081EB7EB}.Release|Mixed Platforms.ActiveCfg = Release|.NET - {9D2153CC-266D-484D-9777-13C9081EB7EB}.Release|Mixed Platforms.Build.0 = Release|.NET - {9D2153CC-266D-484D-9777-13C9081EB7EB}.Release|Win32.ActiveCfg = Release|.NET - {C13F889C-ECE4-4658-8269-A8BEBFC0EF30}.Debug|.NET.ActiveCfg = Debug|.NET - {C13F889C-ECE4-4658-8269-A8BEBFC0EF30}.Debug|.NET.Build.0 = Debug|.NET - {C13F889C-ECE4-4658-8269-A8BEBFC0EF30}.Debug|Any CPU.ActiveCfg = Debug|.NET - {C13F889C-ECE4-4658-8269-A8BEBFC0EF30}.Debug|Mixed Platforms.ActiveCfg = Debug|.NET - {C13F889C-ECE4-4658-8269-A8BEBFC0EF30}.Debug|Mixed Platforms.Build.0 = Debug|.NET - {C13F889C-ECE4-4658-8269-A8BEBFC0EF30}.Debug|Win32.ActiveCfg = Debug|.NET - {C13F889C-ECE4-4658-8269-A8BEBFC0EF30}.Release|.NET.ActiveCfg = Release|.NET - {C13F889C-ECE4-4658-8269-A8BEBFC0EF30}.Release|.NET.Build.0 = Release|.NET - {C13F889C-ECE4-4658-8269-A8BEBFC0EF30}.Release|Any CPU.ActiveCfg = Release|.NET - {C13F889C-ECE4-4658-8269-A8BEBFC0EF30}.Release|Mixed Platforms.ActiveCfg = Release|.NET - {C13F889C-ECE4-4658-8269-A8BEBFC0EF30}.Release|Mixed Platforms.Build.0 = Release|.NET - {C13F889C-ECE4-4658-8269-A8BEBFC0EF30}.Release|Win32.ActiveCfg = Release|.NET - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/base/Services/Tests/Benchmark/Benchmark.csproj b/base/Services/Tests/Benchmark/Benchmark.csproj deleted file mode 100644 index 47fd991..0000000 --- a/base/Services/Tests/Benchmark/Benchmark.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - diff --git a/base/Services/Tests/Benchmark/Benchmark.sg b/base/Services/Tests/Benchmark/Benchmark.sg deleted file mode 100644 index eed33e9..0000000 --- a/base/Services/Tests/Benchmark/Benchmark.sg +++ /dev/null @@ -1,82 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Service\Test\Benchmark\Benchmark.sg -// -// Note: -// -using System; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.SingSharp.Reflection; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Configuration; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Security; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Services; - -[assembly: Transform(typeof(ServiceResourceTransform))] -[assembly: ApplicationPublisherAttribute("singularity.microsoft.com")] -[assembly: AssertPrivilegeAttribute("$register-privilege.localhost")] - -namespace Microsoft.Singularity.Services.Benchmark -{ - [Category("Service")] - internal sealed class Parameters - { - [Endpoint] - public readonly TRef serviceRef; - - [Endpoint] - public readonly TRef directoryRef; - - [Endpoint] - public readonly TRef proxyRef; - - reflective private Parameters(); - } - - public class Benchmark - { - private Thread manageThread; - private IRunnable! manageExec; - - public Benchmark([Claims]ManagedServiceContract.Exp:Start! mep, - [Claims]DirectoryServiceContract.Imp:Ready! dep) - { - IRunnable serviceExec = new BenchmarkExec(dep); - manageExec = new ManagementExec(mep, serviceExec); - } - - public void Start() - { - DebugStub.Print("-- Benchmark service start\n"); - manageThread = new Thread(new ThreadStart(manageExec.Run)); - manageThread.Start(); - } - - internal static int AppMain(Parameters! config) - { - ManagedServiceContract.Exp:Start mep; - DirectoryServiceContract.Imp:Ready dep; - - mep = ((!)config.serviceRef).Acquire(); - dep = ((!)config.directoryRef).Acquire(); - if (mep == null || dep == null) { - delete mep; - delete dep; - return -1; - } - - Benchmark b = new Benchmark(mep, dep); - b.Start(); - - return 0; - } - } -} diff --git a/base/Services/Tests/Benchmark/Hoisted/BenchmarkExec.sg b/base/Services/Tests/Benchmark/Hoisted/BenchmarkExec.sg deleted file mode 100644 index ba58066..0000000 --- a/base/Services/Tests/Benchmark/Hoisted/BenchmarkExec.sg +++ /dev/null @@ -1,231 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Service\Test\Benchmark\Hoisted\BenchmarkExec.sg -// -// Note: -// -using System; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Services; - -namespace Microsoft.Singularity.Services.Benchmark -{ - public class BenchmarkExec : IRunnable - { - private TRef signalReceiver; - private TRef! directoryService; - - public BenchmarkExec([Claims]DirectoryServiceContract.Imp:Ready! ds) - { - directoryService = new TRef(ds); - } - - public void Signal([Claims]ThreadTerminationContract.Exp:Start! ep) - { - signalReceiver = new TRef(ep); - } - - public virtual void Run() - { - ServiceProviderContract.Exp! provider; - ThreadTerminationContract.Exp! signal; - ESet eset; - - DebugStub.Print("BenchExecHoisted: Service start\n"); - - if (!RegisterName(BenchmarkContract.ModuleName, out provider)) { - delete provider; - return; - } - - eset = new ESet(); - assert signalReceiver != null; - signal = signalReceiver.Acquire(); - for (;;) { - switch receive { - case provider.Connect(serviceEp): - { - //DebugStub.Print("BenchExec: Accepting a client" + - // " connection request ... "); - BenchmarkContract.Exp:Start bench = serviceEp - as BenchmarkContract.Exp:Start; - if (bench != null) { - //DebugStub.Print("accepted.\n"); - provider.SendAckConnect(); - bench.SendSuccess(); - eset.Add(bench); - } - else { - //DebugStub.Print("denied.\n"); - provider.SendNackConnect(serviceEp); - } - break; - } - case provider.ChannelClosed(): - { - DebugStub.Print("BenchExec: " + - "ChannelClosed @ provider\n"); - goto exit; - break; - } - case signal.Stop(): - { - BenchmarkContract.Exp!:Ready tmp; - signal.SendAckStop(); - for (int i = 0; i < eset.Count; i++) { - eset.RecvHead(out tmp); - delete tmp; - } - break; - } - case signal.ChannelClosed(): - { - DebugStub.Print("BenchExec: ChannelClosed @ signal\n"); - goto exit; - break; - } - case ep.GetCycleCount() in eset: - { - ep.SendCycleCount(Processor.GetCycleCount()); - eset.Add(ep); - break; - } - case ep.Null() in eset: - { - ep.SendAckNull(); - eset.Add(ep); - break; - } - case ep.One(arg) in eset: - { - ep.SendAckOne(arg); - eset.Add(ep); - break; - } - case ep.Two(arg1, arg2) in eset: - { - ep.SendAckTwo(arg1, arg2); - eset.Add(ep); - break; - } - case ep.Three(arg1, arg2, arg3) in eset: - { - ep.SendAckThree(arg1, arg2, arg3); - eset.Add(ep); - break; - } - case ep.Four(arg1, arg2, arg3, arg4) in eset: - { - ep.SendAckFour(arg1, arg2, arg3, arg4); - eset.Add(ep); - break; - } - case ep.Five(arg1, arg2, arg3, arg4, arg5) in eset: - { - ep.SendAckFive(arg1, arg2, arg3, arg4, arg5); - eset.Add(ep); - break; - } - case ep.Six(arg1, arg2, arg3, arg4, arg5, arg6) in eset: - { - ep.SendAckSix(arg1, arg2, arg3, arg4, arg5, arg6); - eset.Add(ep); - break; - } - case ep.Seven(arg1, arg2, arg3, arg4, arg5, arg6, arg7) in eset: - { - ep.SendAckSeven(arg1, arg2, arg3, arg4, arg5, arg6, - arg7); - eset.Add(ep); - break; - } - case ep.Eight(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) in eset: - { - ep.SendAckEight(arg1, arg2, arg3, arg4, arg5, arg6, - arg7, arg8); - eset.Add(ep); - break; - } - case ep.Nine(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) in eset: - { - ep.SendAckNine(arg1, arg2, arg3, arg4, arg5, arg6, - arg7, arg8, arg9); - eset.Add(ep); - break; - } - case ep.EndOfBenchmark() in eset: - { - ep.SendAckEnd(); - delete ep; - break; - } - case ep.ChannelClosed() in eset: - { - DebugStub.Print("BenchExec: ChannelClosed @ client\n"); - delete ep; - break; - } - } - } -exit: - eset.Dispose(); - signalReceiver.Release(signal); - DeregisterName(BenchmarkContract.ModuleName, provider); - return; - } - - internal bool RegisterName(string! name, - out ServiceProviderContract.Exp! ep) - { - bool success = false; - ServiceProviderContract.Imp! imp; - ServiceProviderContract.Exp! exp; - DirectoryServiceContract.Imp:Ready! ds; - - DebugStub.Print("BenchExec: Register '{0}' ...", __arglist(name)); - ServiceProviderContract.NewChannel(out imp, out exp); - ds = directoryService.Acquire(); - ds.SendRegister(Bitter.FromString2(name), imp); - switch receive { - case ds.AckRegister(): - success = true; - DebugStub.Print(" done.\n"); - break; - case ds.NakRegister(rejected, error): - delete rejected; - success = false; - DebugStub.Print(" fail.\n"); - break; - case ds.ChannelClosed(): - DebugStub.Print(" ChannelClosed @ name service\n"); - break; - } - ep = exp; - directoryService.Release(ds); - return success; - } - - internal void DeregisterName(string! name, - [Claims]ServiceProviderContract.Exp! ep) - { - DirectoryServiceContract.Imp:Ready! ds; - ds = DirectoryService.NewClientEndpoint(); - ds.SendDeregister(Bitter.FromString2(name)); - switch receive { - case ds.AckDeregister(imp): - delete imp; - delete ep; - break; - } - delete ds; - } - } -} diff --git a/base/Services/Tests/Benchmark/Hoisted/Hoisted.csproj b/base/Services/Tests/Benchmark/Hoisted/Hoisted.csproj deleted file mode 100644 index ffe261f..0000000 --- a/base/Services/Tests/Benchmark/Hoisted/Hoisted.csproj +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - Exe - RBenchHoistedService - - - - - - - - - - - - - - - - - diff --git a/base/Services/Tests/Benchmark/Worker/BenchmarkExec.sg b/base/Services/Tests/Benchmark/Worker/BenchmarkExec.sg deleted file mode 100644 index 726d3dd..0000000 --- a/base/Services/Tests/Benchmark/Worker/BenchmarkExec.sg +++ /dev/null @@ -1,190 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Service\Test\Benchmark\Worker\BenchmarkExec.sg -// -// Note: -// -using System; -using System.Collections; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Services; - -namespace Microsoft.Singularity.Services.Benchmark -{ - public class BenchmarkExec : IRunnable - { - private ArrayList! serviceSignalList; - private Object! mutex; - private TRef signalReceiver; - private TRef! directoryService; - - public BenchmarkExec([Claims]DirectoryServiceContract.Imp:Ready! ds) - { - serviceSignalList = new ArrayList(); - mutex = new Object(); - directoryService = new TRef(ds); - } - - public void Signal([Claims]ThreadTerminationContract.Exp:Start! ep) - { - signalReceiver = new TRef(ep); - } - - public virtual void Run() - { - ServiceProviderContract.Exp! provider; - ThreadTerminationContract.Exp! signal; - - DebugStub.Print("BenchExec: Service start\n"); - - if (!RegisterName(BenchmarkContract.ModuleName, out provider)) { - delete provider; - return; - } - assert signalReceiver != null; - signal = signalReceiver.Acquire(); - for (;;) { - switch receive { - case provider.Connect(serviceEp): - { - //DebugStub.Print("BenchExec: Accepting a client" + - // " connection request ... "); - BenchmarkContract.Exp:Start bench = serviceEp - as BenchmarkContract.Exp:Start; - if (bench != null) { - //DebugStub.Print("accepted.\n"); - provider.SendAckConnect(); - CreateWorker(bench).Start(); - } - else { - //DebugStub.Print("Denied.\n"); - provider.SendNackConnect(serviceEp); - } - break; - } - case provider.ChannelClosed(): - { - TerminateWorkers(); - goto exit; - break; - } - case signal.Stop(): - { - TerminateWorkers(); - signal.SendAckStop(); - goto exit; - break; - } - case signal.ChannelClosed(): - { - TerminateWorkers(); - goto exit; - break; - } - } - } -exit: - signalReceiver.Release(signal); - DeregisterName(BenchmarkContract.ModuleName, provider); - return; - } - - private Thread! CreateWorker([Claims]BenchmarkContract.Exp:Start! ep) - { - BenchmarkWorker service; - ThreadTerminationContract.Imp! imp; - ThreadTerminationContract.Exp! exp; - ThreadTerminationContract.NewChannel(out imp, out exp); - - ep.SendSuccess(); - service = new BenchmarkWorker(ep); - service.Signal(exp); - lock (mutex) { - serviceSignalList.Add(new TRef(imp)); - } - return new Thread(new ThreadStart(service.Run)); - } - - private void TerminateWorkers() - { - TRef signalRef; - ThreadTerminationContract.Imp:Start signal; - lock (mutex) { - foreach (Object obj in serviceSignalList) - { - if (obj != null) { - signalRef = obj - as TRef; - if (signalRef != null) { - signal = signalRef.Acquire(); - signal.SendStop(); - switch receive { - case signal.AckStop(): - break; - case signal.ChannelClosed(): - break; - } - signalRef.Release(signal); - } - } - } - serviceSignalList.Clear(); - } - } - - internal bool RegisterName(string! name, - out ServiceProviderContract.Exp! ep) - { - bool success = false; - ServiceProviderContract.Imp! imp; - ServiceProviderContract.Exp! exp; - DirectoryServiceContract.Imp:Ready! ds; - - ServiceProviderContract.NewChannel(out imp, out exp); - ds = directoryService.Acquire(); - ds.SendRegister(Bitter.FromString2(name), imp); - switch receive { - case ds.AckRegister(): - success = true; - DebugStub.Print("BenchExec: '{0}' registered", - __arglist(name)); - break; - case ds.NakRegister(rejected, error): - delete rejected; - success = false; - DebugStub.Print("BenchExec: registration failed.\n"); - break; - case ds.ChannelClosed(): - DebugStub.Print(" ChannelClosed @ name service\n"); - break; - } - ep = exp; - directoryService.Release(ds); - return success; - } - - internal void DeregisterName(string! name, - [Claims]ServiceProviderContract.Exp! ep) - { - DirectoryServiceContract.Imp:Ready! ds; - ds = directoryService.Acquire(); - ds.SendDeregister(Bitter.FromString2(name)); - switch receive { - case ds.AckDeregister(imp): - delete imp; - delete ep; - break; - } - directoryService.Release(ds); - } - } -} diff --git a/base/Services/Tests/Benchmark/Worker/BenchmarkWorker.sg b/base/Services/Tests/Benchmark/Worker/BenchmarkWorker.sg deleted file mode 100644 index fafd163..0000000 --- a/base/Services/Tests/Benchmark/Worker/BenchmarkWorker.sg +++ /dev/null @@ -1,113 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Service\Test\Benchmark\Worker\BenchmarkWorker.sg -// -// Note: -// -using System; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Services; - -namespace Microsoft.Singularity.Services.Benchmark -{ - internal class BenchmarkWorker : IRunnable - { - private TRef! tref; - private TRef signal; - - internal BenchmarkWorker([Claims] BenchmarkContract.Exp:Ready! ep) - { - tref = new TRef(ep); - } - - public void Signal([Claims] ThreadTerminationContract.Exp:Start! sig) - { - signal = new TRef(sig); - } - - public void Run() - { - BenchmarkContract.Exp:Ready! ep; - ThreadTerminationContract.Exp:Start! sig; - - ep = tref.Acquire(); - assert signal != null; - sig = signal.Acquire(); - - for (;;) { - switch receive { - case ep.GetCycleCount(): - ep.SendCycleCount(Processor.GetCycleCount()); - break; - case ep.Null(): - ep.SendAckNull(); - break; - case ep.One(arg): - ep.SendAckOne(arg); - break; - case ep.Two(arg1, arg2): - ep.SendAckTwo(arg1, arg2); - break; - case ep.Three(arg1, arg2, arg3): - ep.SendAckThree(arg1, arg2, arg3); - break; - case ep.Four(arg1, arg2, arg3, arg4): - ep.SendAckFour(arg1, arg2, arg3, arg4); - break; - case ep.Five(arg1, arg2, arg3, arg4, arg5): - ep.SendAckFive(arg1, arg2, arg3, arg4, arg5); - break; - case ep.Six(arg1, arg2, arg3, arg4, arg5, arg6): - ep.SendAckSix(arg1, arg2, arg3, arg4, arg5, arg6); - break; - case ep.Seven(arg1, arg2, arg3, arg4, arg5, arg6, arg7): - ep.SendAckSeven(arg1, arg2, arg3, arg4, arg5, arg6, - arg7); - break; - case ep.Eight(arg1, arg2, arg3, arg4, arg5, arg6, arg7, - arg8): - ep.SendAckEight(arg1, arg2, arg3, arg4, arg5, arg6, - arg7, arg8); - break; - case ep.Nine(arg1, arg2, arg3, arg4, arg5, arg6, arg7, - arg8, arg9): - ep.SendAckNine(arg1, arg2, arg3, arg4, arg5, arg6, - arg7, arg8, arg9); - break; - /* - case ep.ReadWrite(): - break; - case ep.WriteRead(): - break; - */ - case ep.EndOfBenchmark(): - ep.SendAckEnd(); - goto exit; - break; - case ep.ChannelClosed(): - goto exit; - break; - case sig.Stop(): - sig.SendAckStop(); - goto exit; - break; - case sig.ChannelClosed(): - goto exit; - break; - } - } -exit: - delete ep; - delete sig; - DebugStub.Print("BenchExec: Quit main loop\n"); - } - } // class -} diff --git a/base/Services/Tests/Benchmark/Worker/Worker.csproj b/base/Services/Tests/Benchmark/Worker/Worker.csproj deleted file mode 100644 index 9551eba..0000000 --- a/base/Services/Tests/Benchmark/Worker/Worker.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - Exe - RBenchWorkerService - - - - - - - - - - - - - - - - - - diff --git a/base/Services/Tests/BenchmarkProxy/BenchmarkJournalProducer.sg b/base/Services/Tests/BenchmarkProxy/BenchmarkJournalProducer.sg deleted file mode 100644 index 430cb5a..0000000 --- a/base/Services/Tests/BenchmarkProxy/BenchmarkJournalProducer.sg +++ /dev/null @@ -1,109 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Services\Tests\BenchmarkProxy\BenchmarkJournalProducer.sg -// -// Note: -// -using System; -using Microsoft.SingSharp; -using Microsoft.SingSharp.Reflection; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Configuration; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Resiliency; -using Microsoft.Singularity.Security; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Services; - -[assembly: Transform(typeof(ServiceResourceTransform))] -[assembly: ApplicationPublisherAttribute("singularity.microsoft.com")] -[assembly: AssertPrivilegeAttribute("$register-privilege.localhost")] - -namespace Microsoft.Singularity.Services.Benchmark -{ - [Category("Service")] - internal sealed class Parameters - { - [Endpoint] - public readonly TRef serviceContractRef; - - [Endpoint] - public readonly TRef directoryContractRef; - - [Endpoint] - public readonly TRef substituteDSRef; - - [Endpoint] - public readonly TRef proxyContractRef; - - reflective private Parameters(); - } - - internal class BenchmarkJournalProducer : JournalProducer - { - internal BenchmarkJournalProducer() - { - base(); - } - - protected override bool Accept(ServiceContract.Exp:Start! ep) - { - // ep must be a type of CounterContract.Exp - BenchmarkContract.Exp:Start cep = ep - as BenchmarkContract.Exp:Start; - return (cep != null); - } - - protected override void Substitute([Claims]ServiceContract.Exp:Start! ep, - out ServiceContract.Exp! newEp) - { - Journalet! journalet; - ServiceContract.Exp:Start! serverEp; - - journalet = new BenchmarkJournalet(this, ep); - journalet.CreateServerEndpoint(out serverEp); - journalet.Start(); - RegisterJournalet(journalet); - newEp = serverEp; - } - - internal static int AppMain(Parameters! config) - { - ManagedServiceContract.Exp:Start ep; - DirectoryServiceContract.Imp:Ready dep; - DirectoryServiceContract.Exp:Start fep; - ManagedProxyContract.Imp:Start mep; - - ep = config.serviceContractRef.Acquire(); - dep = config.directoryContractRef.Acquire(); - fep = config.substituteDSRef.Acquire(); - mep = config.proxyContractRef.Acquire(); - - if (ep == null || dep == null || fep == null || mep == null) { - delete ep; - delete dep; - delete fep; - delete mep; - return -1; - } - - DebugStub.Print("Start BenchmarkJournalProcuder\n"); - - JournalProducer jp = new BenchmarkJournalProducer(); - try { - jp.Start(ep, dep, fep, mep); - } - catch (Exception e) { - DebugStub.WriteLine(e.ToString()); - DebugStub.WriteLine(e.StackTrace); - } - - return 0; - } - } -} diff --git a/base/Services/Tests/BenchmarkProxy/BenchmarkJournalet.sg b/base/Services/Tests/BenchmarkProxy/BenchmarkJournalet.sg deleted file mode 100644 index 30d570d..0000000 --- a/base/Services/Tests/BenchmarkProxy/BenchmarkJournalet.sg +++ /dev/null @@ -1,877 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Services\Tests\BenchmarkProxy\BenchmarkJournalet.sg -// -// Note: -// -#define VERSION_2 -using System; -using System.Threading; -using System.Collections; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Resiliency; - -namespace Microsoft.Singularity.Services.Benchmark -{ - internal enum MessageType : uint - { - Unknown, - GetCycleCount, - CycleCount, - Null, - One, - Two, - Three, - Four, - Five, - Six, - Seven, - Eight, - Nine, - EndOfBenchmark, - AckNull, - AckOne, - AckTwo, - AckThree, - AckFour, - AckFive, - AckSix, - AckSeven, - AckEight, - AckNine, - AckEnd, - } - -#if VERSION_2 - internal class BenchmarkJournalet : Journalet2 -#else - internal class BenchmarkJournalet : Journalet -#endif - { - private Log! incoming; - private Log! outgoing; - private Object! initMonitor; - private Object! benchmarkMonitor; - private TRef initImp; - private TRef benchmarkImp; - private TRef benchmarkExp; -#if VERSION_2 - private TRef! initExp; - - internal BenchmarkJournalet([Claims]ServiceContract.Exp:Start! ep) - : base() - { - BenchmarkContract.Exp:Start exp; - exp = ep as BenchmarkContract.Exp:Start; - if (exp == null) { - throw new ArgumentException(); - } - - incoming = new Log(); - outgoing = new Log(); - initMonitor = new Object(); - benchmarkMonitor = new Object(); - initExp = new TRef(exp); - } -#else - internal BenchmarkJournalet(JournalProducer! producer, - [Claims]ServiceContract.Exp:Start! ep) - { - base(producer, ep); - incoming = new Log(); - outgoing = new Log(); - initMonitor = new Object(); - benchmarkMonitor = new Object(); - } -#endif - -#if VERSION_2 - public override void CreateEndpoint(out ServiceContract.Exp! ep) -#else - public override void CreateServerEndpoint(out ServiceContract.Exp:Start! ep) -#endif - { - BenchmarkContract.Imp! imp; - BenchmarkContract.Exp! exp; - BenchmarkContract.NewChannel(out imp, out exp); - - //DebugStub.Print("BJ: Enter CreateServerEndpoint\n"); - try { - Monitor.Enter(initMonitor); - initImp = new TRef(imp); - Monitor.Pulse(initMonitor); - } - finally { - Monitor.Exit(initMonitor); - } - ep = exp; - //DebugStub.Print("BJ: Exit CreateServerEndpoint\n"); - } - -#if VERSION_2 - public override void Flush() {} -#endif - -#if VERSION_2 - public override void Run() -#else - protected override void ProxyThread() -#endif - { - bool success = false; - ulong time1, time2; - - if (!Initialize()) { - goto exit; - } - for (;;) { - if (!HandleMessages()) { - goto exit; - } - - time1 = Processor.GetCycleCount(); - success = Recover(); - time2 = Processor.GetCycleCount(); - DebugStub.Print("BJ: ==== Recovery Result ====\n"); - DebugStub.Print("outgoing incoming start end\n"); - DebugStub.Print("{0},{1},{2},{3}\n", - __arglist(incoming.Count, outgoing.Count, - time1, time2)); - if (!success) { - goto exit; - } - } -exit: -#if !VERSION_2 - producer.DeregisterJournalet(this); -#endif - return; - } - - protected override bool Initialize() - { - bool success = false; - BenchmarkContract.Exp:Start clientEp; - BenchmarkContract.Imp:Start serverEp; - -#if VERSION_2 - clientEp = initExp.Acquire(); -#else - ServiceContract.Exp:Start! ep = clientRef.Acquire(); - clientEp = ep as BenchmarkContract.Exp:Start; - if (clientEp == null) { - clientRef.Release(ep); - return false; - } -#endif - assert initImp != null; - serverEp = initImp.Acquire(); - switch receive { - case serverEp.Success(): - clientEp.SendSuccess(); - success = true; - break; - case serverEp.ChannelClosed(): - success = false; - break; - case unsatisfiable: - DebugStub.Break(); - break; - } - initImp = null; - - if (success) { - benchmarkExp = new TRef(clientEp); - benchmarkImp = new TRef(serverEp); - return true; - } - else { - delete clientEp; - delete serverEp; - return false; - } - } - - protected override bool HandleMessages() - { - bool success = true; - ArrayList al; - BenchmarkContract.Exp:Ready! clientEp; - BenchmarkContract.Imp:Ready! serverEp; - - assert benchmarkExp != null; - assert benchmarkImp != null; - clientEp = benchmarkExp.Acquire(); - serverEp = benchmarkImp.Acquire(); - - for (;;) { - switch receive { - case clientEp.GetCycleCount(): - { - serverEp.SendGetCycleCount(); - incoming.Record((uint)MessageType.GetCycleCount, null); - break; - } - case clientEp.Null(): - { - serverEp.SendNull(); - incoming.Record((uint)MessageType.Null, null); - break; - } - case clientEp.One(arg): - { - serverEp.SendOne(arg); - al = new ArrayList(1); - al.Add(arg); - incoming.Record((uint)MessageType.One, al); - break; - } - case clientEp.Two(arg1, arg2): - { - serverEp.SendTwo(arg1, arg2); - al = new ArrayList(2); - al.Add(arg1); - al.Add(arg2); - incoming.Record((uint)MessageType.Two, al); - break; - } - case clientEp.Three(arg1, arg2, arg3): - { - serverEp.SendThree(arg1, arg2, arg3); - al = new ArrayList(3); - al.Add(arg1); - al.Add(arg2); - al.Add(arg3); - incoming.Record((uint)MessageType.Three, al); - break; - } - case clientEp.Four(arg1, arg2, arg3, arg4): - { - serverEp.SendFour(arg1, arg2, arg3, arg4); - al = new ArrayList(4); - al.Add(arg1); - al.Add(arg2); - al.Add(arg3); - al.Add(arg4); - incoming.Record((uint)MessageType.Four, al); - break; - } - case clientEp.Five(arg1, arg2, arg3, arg4, arg5): - { - serverEp.SendFive(arg1, arg2, arg3, arg4, arg5); - al = new ArrayList(5); - al.Add(arg1); - al.Add(arg2); - al.Add(arg3); - al.Add(arg4); - al.Add(arg5); - incoming.Record((uint)MessageType.Five, al); - break; - } - case clientEp.Six(arg1, arg2, arg3, arg4, arg5, arg6): - { - serverEp.SendSix(arg1, arg2, arg3, arg4, arg5, arg6); - al = new ArrayList(6); - al.Add(arg1); - al.Add(arg2); - al.Add(arg3); - al.Add(arg4); - al.Add(arg5); - al.Add(arg6); - incoming.Record((uint)MessageType.Six, al); - break; - } - case clientEp.Seven(arg1, arg2, arg3, arg4, arg5, arg6, - arg7): - { - serverEp.SendSeven(arg1, arg2, arg3, arg4, arg5, arg6, - arg7); - al = new ArrayList(7); - al.Add(arg1); - al.Add(arg2); - al.Add(arg3); - al.Add(arg4); - al.Add(arg5); - al.Add(arg6); - al.Add(arg7); - incoming.Record((uint)MessageType.Seven, al); - break; - } - case clientEp.Eight(arg1, arg2, arg3, arg4, arg5, arg6, - arg7, arg8): - { - serverEp.SendEight(arg1, arg2, arg3, arg4, arg5, arg6, - arg7, arg8); - al = new ArrayList(8); - al.Add(arg1); - al.Add(arg2); - al.Add(arg3); - al.Add(arg4); - al.Add(arg5); - al.Add(arg6); - al.Add(arg7); - al.Add(arg8); - incoming.Record((uint)MessageType.Eight, al); - break; - } - case clientEp.Nine(arg1, arg2, arg3, arg4, arg5, arg6, - arg7, arg8, arg9): - { - serverEp.SendNine(arg1, arg2, arg3, arg4, arg5, arg6, - arg7, arg8, arg9); - al = new ArrayList(9); - al.Add(arg1); - al.Add(arg2); - al.Add(arg3); - al.Add(arg4); - al.Add(arg5); - al.Add(arg6); - al.Add(arg7); - al.Add(arg8); - al.Add(arg9); - incoming.Record((uint)MessageType.Nine, al); - break; - } - case clientEp.EndOfBenchmark(): - { - serverEp.SendEndOfBenchmark(); - incoming.Record((uint)MessageType.EndOfBenchmark, - null); - break; - } - case clientEp.ChannelClosed(): - { - success = false; - goto exit; - break; - } - - case serverEp.CycleCount(cycle): - { - clientEp.SendCycleCount(cycle); - al = new ArrayList(1); - al.Add(cycle); - outgoing.Record((uint)MessageType.CycleCount, al); - break; - } - case serverEp.AckNull(): - { - clientEp.SendAckNull(); - outgoing.Record((uint)MessageType.AckNull, null); - break; - } - case serverEp.AckOne(arg): - { - clientEp.SendAckOne(arg); - al = new ArrayList(1); - al.Add(arg); - outgoing.Record((uint)MessageType.AckOne, al); - break; - } - case serverEp.AckTwo(arg1, arg2): - { - clientEp.SendAckTwo(arg1, arg2); - al = new ArrayList(2); - al.Add(arg1); - al.Add(arg2); - outgoing.Record((uint)MessageType.AckTwo, al); - break; - } - case serverEp.AckThree(arg1, arg2, arg3): - { - clientEp.SendAckThree(arg1, arg2, arg3); - al = new ArrayList(3); - al.Add(arg1); - al.Add(arg2); - al.Add(arg3); - outgoing.Record((uint)MessageType.AckThree, al); - break; - } - case serverEp.AckFour(arg1, arg2, arg3, arg4): - { - clientEp.SendAckFour(arg1, arg2, arg3, arg4); - al = new ArrayList(4); - al.Add(arg1); - al.Add(arg2); - al.Add(arg3); - al.Add(arg4); - outgoing.Record((uint)MessageType.AckFour, al); - break; - } - case serverEp.AckFive(arg1, arg2, arg3, arg4, arg5): - { - clientEp.SendAckFive(arg1, arg2, arg3, arg4, arg5); - al = new ArrayList(5); - al.Add(arg1); - al.Add(arg2); - al.Add(arg3); - al.Add(arg4); - al.Add(arg5); - outgoing.Record((uint)MessageType.AckFive, al); - break; - } - case serverEp.AckSix(arg1, arg2, arg3, arg4, arg5, arg6): - { - clientEp.SendAckSix(arg1, arg2, arg3, arg4, arg5, - arg6); - al = new ArrayList(6); - al.Add(arg1); - al.Add(arg2); - al.Add(arg3); - al.Add(arg4); - al.Add(arg5); - al.Add(arg6); - outgoing.Record((uint)MessageType.AckSix, al); - break; - } - case serverEp.AckSeven(arg1, arg2, arg3, arg4, arg5, arg6, - arg7): - { - clientEp.SendAckSeven(arg1, arg2, arg3, arg4, arg5, - arg6, arg7); - al = new ArrayList(7); - al.Add(arg1); - al.Add(arg2); - al.Add(arg3); - al.Add(arg4); - al.Add(arg5); - al.Add(arg6); - al.Add(arg7); - outgoing.Record((uint)MessageType.AckSeven, al); - break; - } - case serverEp.AckEight(arg1, arg2, arg3, arg4, arg5, arg6, - arg7, arg8): - { - clientEp.SendAckEight(arg1, arg2, arg3, arg4, arg5, - arg6, arg7, arg8); - al = new ArrayList(8); - al.Add(arg1); - al.Add(arg2); - al.Add(arg3); - al.Add(arg4); - al.Add(arg5); - al.Add(arg6); - al.Add(arg7); - al.Add(arg8); - outgoing.Record((uint)MessageType.AckEight, al); - break; - } - case serverEp.AckNine(arg1, arg2, arg3, arg4, arg5, arg6, - arg7, arg8, arg9): - { - clientEp.SendAckNine(arg1, arg2, arg3, arg4, arg5, - arg6, arg7, arg8, arg9); - al = new ArrayList(9); - al.Add(arg1); - al.Add(arg2); - al.Add(arg3); - al.Add(arg4); - al.Add(arg5); - al.Add(arg6); - al.Add(arg7); - al.Add(arg8); - al.Add(arg9); - outgoing.Record((uint)MessageType.AckNine, al); - break; - } - case serverEp.AckEnd(): - { - clientEp.SendAckEnd(); - outgoing.Record((uint)MessageType.AckEnd, null); - break; - } - case serverEp.ChannelClosed(): - { - // Recovery - success = true; - goto exit; - break; - } - case unsatisfiable: - { - DebugStub.Break(); - break; - } - } - } -exit: - benchmarkExp.Release(clientEp); - if (success) { - //2006-9-13 hi: - //benchmarkImp.Release(serverEp); - //hi: an alternative way to solve 2006-9-13 - delete serverEp; - try { - Monitor.Enter(benchmarkMonitor); - benchmarkImp = null; - Monitor.Pulse(benchmarkMonitor); - } - finally { - Monitor.Exit(benchmarkMonitor); - } - } - else { - delete serverEp; - //benchmarkImp = null; - } - return success; - } - - protected override bool Recover() - { - bool success = false; - int[] arg = new int[9]; - uint msg; - MessageType op; - IList args; - Object tmp; - BenchmarkContract.Imp:Start serverEp; - - try { - Monitor.Enter(initMonitor); - if (initImp == null) { - Monitor.Wait(initMonitor); - } - } - finally { - Monitor.Exit(initMonitor); - } - - assert initImp != null; - serverEp = initImp.Acquire(); - - switch receive { - case serverEp.Success(): - break; - case serverEp.ChannelClosed(): - delete serverEp; - throw new Exception("Channel was closed" + - " during recovery.\n"); - break; - case unsatisfiable: - DebugStub.Break(); - break; - } - initImp = null; - - while (incoming.Playback(out msg, out args)) { - op = (MessageType)msg; - switch (op) { - case MessageType.GetCycleCount: - { - serverEp.SendGetCycleCount(); - break; - } - case MessageType.Null: - { - serverEp.SendNull(); - break; - } - case MessageType.One: - { - if (args != null) { - tmp = args[0]; - if (tmp != null) { - arg[0] = (int)tmp; - } - serverEp.SendOne(arg[0]); - } - break; - } - case MessageType.Two: - { - if (args != null) { - for (int i = 0; i < 2; i++) { - tmp = args[i]; - if (tmp != null) { - arg[i] = (int)tmp; - } - } - serverEp.SendTwo(arg[0], arg[1]); - } - break; - } - case MessageType.Three: - { - if (args != null) { - for (int i = 0; i < 3; i++) { - tmp = args[i]; - if (tmp != null) { - arg[i] = (int)tmp; - } - } - serverEp.SendThree(arg[0], arg[1], arg[2]); - } - break; - } - case MessageType.Four: - { - if (args != null) { - for (int i = 0; i < 4; i++) { - tmp = args[i]; - if (tmp != null) { - arg[i] = (int)tmp; - } - } - serverEp.SendFour(arg[0], arg[1], arg[2], arg[3]); - } - break; - } - case MessageType.Five: - { - if (args != null) { - for (int i = 0; i < 5; i++) { - tmp = args[i]; - if (tmp != null) { - arg[i] = (int)tmp; - } - } - serverEp.SendFive(arg[0], arg[1], arg[2], arg[3], - arg[4]); - } - break; - } - case MessageType.Six: - { - if (args != null) { - for (int i = 0; i < 6; i++) { - tmp = args[i]; - if (tmp != null) { - arg[i] = (int)tmp; - } - } - serverEp.SendSix(arg[0], arg[1], arg[2], arg[3], - arg[4], arg[5]); - } - break; - } - case MessageType.Seven: - { - if (args != null) { - for (int i = 0; i < 7; i++) { - tmp = args[i]; - if (tmp != null) { - arg[i] = (int)tmp; - } - } - serverEp.SendSeven(arg[0], arg[1], arg[2], arg[3], - arg[4], arg[5], arg[6]); - } - break; - } - case MessageType.Eight: - { - if (args != null) { - for (int i = 0; i < 8; i++) { - tmp = args[i]; - if (tmp != null) { - arg[i] = (int)tmp; - } - } - serverEp.SendEight(arg[0], arg[1], arg[2], arg[3], - arg[4], arg[5], arg[6], arg[7]); - } - break; - } - case MessageType.Nine: - { - if (args != null) { - for (int i = 0; i < 9; i++) { - tmp = args[i]; - if (tmp != null) { - arg[i] = (int)tmp; - } - } - serverEp.SendNine(arg[0], arg[1], arg[2], arg[3], - arg[4], arg[5], arg[6], arg[7], - arg[8]); - } - break; - } - case MessageType.EndOfBenchmark: - { - serverEp.SendEndOfBenchmark(); - break; - } - } - - bool result = true; - result = outgoing.Playback(out msg, out args); - op = (MessageType)msg; - - if (!result) { - /* - DebugStub.Print("Playback: End of log.\n"); - DebugStub.Print("Playback: {0} incoming messages\n", - __arglist(incoming.Count)); - DebugStub.Print("Playback: {0} outgoing messages\n", - __arglist(outgoing.Count)); - */ - success = true; - break; - } - - switch receive { - case serverEp.CycleCount(cycle): - { - if (op != MessageType.CycleCount) { - success = false; - } - else { - success = true; - } - break; - } - case serverEp.AckNull(): - { - if (op != MessageType.Null) { - success = false; - } - else { - success = true; - } - break; - } - case serverEp.AckOne(arg1): - { - if (op != MessageType.AckOne) { - success = false; - } - else { - success = true; - } - break; - } - case serverEp.AckTwo(arg1, arg2): - { - if (op != MessageType.AckTwo) { - success = false; - } - else { - success = true; - } - break; - } - case serverEp.AckThree(arg1, arg2, arg3): - { - if (op != MessageType.AckThree) { - success = false; - } - else { - success = true; - } - break; - } - case serverEp.AckFour(arg1, arg2, arg3, arg4): - { - if (op != MessageType.AckFour) { - success = false; - } - else { - success = true; - } - break; - } - case serverEp.AckFive(arg1, arg2, arg3, arg4, arg5): - { - if (op != MessageType.AckFive) { - success = false; - } - else { - success = true; - } - break; - } - case serverEp.AckSix(arg1, arg2, arg3, arg4, arg5, arg6): - { - if (op != MessageType.AckSix) { - success = false; - } - else { - success = true; - } - break; - } - case serverEp.AckSeven(arg1, arg2, arg3, arg4, arg5, arg6, - arg7): - { - if (op != MessageType.AckSeven) { - success = false; - } - else { - success = true; - } - break; - } - case serverEp.AckEight(arg1, arg2, arg3, arg4, arg5, arg6, - arg7, arg8): - { - if (op != MessageType.AckEight) { - success = false; - } - else { - success = true; - } - break; - } - case serverEp.AckNine(arg1, arg2, arg3, arg4, arg5, arg6, - arg7, arg8, arg9): - { - if (op != MessageType.AckNine) { - success = false; - } - else { - success = true; - } - break; - } - case serverEp.AckEnd(): - { - if (op != MessageType.AckEnd) { - success = false; - } - else { - success = true; - } - break; - } - case serverEp.ChannelClosed(): - throw new Exception("Server-side channel closed" + - " during recovery\n"); - break; - case unsatisfiable: - DebugStub.Break(); - break; - } - } // loop - - //2006-9-13 hi: This may cause an access violation with the - // fully hoisted style server. - //delete benchmarkImp.Acquire(); - //DebugStub.Print("BJ: (2)\n"); - //benchmarkImp.Release(serverEp); - - //hi: an alternative way to above. - try { - Monitor.Enter(benchmarkMonitor); - if (benchmarkImp != null) { - Monitor.Wait(benchmarkMonitor); - } - benchmarkImp = new TRef(serverEp); - } - finally { - Monitor.Exit(benchmarkMonitor); - } - - return success; - } - } -} diff --git a/base/Services/Tests/BenchmarkProxy/BenchmarkJournaletFactory.sg b/base/Services/Tests/BenchmarkProxy/BenchmarkJournaletFactory.sg deleted file mode 100644 index ac6a9f5..0000000 --- a/base/Services/Tests/BenchmarkProxy/BenchmarkJournaletFactory.sg +++ /dev/null @@ -1,104 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Services\Tests\BenchmarkProxy\BenchmarkJournaletFactory.sg -// -// Note: Resilient Service Ver.2 -// -using System; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.SingSharp.Reflection; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Configuration; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Resiliency; -using Microsoft.Singularity.Security; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Services; - -[assembly: Transform(typeof(ServiceResourceTransform))] -[assembly: ApplicationPublisherAttribute("singularity.microsoft.com")] -[assembly: AssertPrivilegeAttribute("$register-privilege.localhost")] - -namespace Microsoft.Singularity.Services.Benchmark -{ - [Category("Service")] - internal sealed class Parameters - { - [Endpoint] - public readonly TRef serviceRef; - - [Endpoint] - public readonly TRef directoryRef; - - [Endpoint] - public readonly TRef myDSRef; - - [Endpoint] - public readonly TRef recoveryRef; - - [Endpoint] - public readonly TRef proxyRef; - - reflective private Parameters(); - } - - internal class BenchmarkJournaletFactory : JournaletFactory - { - public bool Accept(ServiceContract.Exp:Start! ep) - { - BenchmarkContract.Exp:Start cep = ep - as BenchmarkContract.Exp:Start; - return (cep != null); - } - - public Journalet2! CreateJournalet([Claims]ServiceContract.Exp:Start! ep, - out ServiceContract.Exp! newEp) - { - Journalet2! journalet; - ServiceContract.Exp! serverEp; - - journalet = new BenchmarkJournalet(ep); - journalet.CreateEndpoint(out serverEp); - newEp = serverEp; - return journalet; - } - - internal static int AppMain(Parameters! config) - { - ManagedServiceContract.Exp:Start ep; - ManagedProxyContract.Imp:Start mep; - DirectoryServiceContract.Imp:Ready dep; - DirectoryServiceContract.Exp:Start fep; - ServiceProxyContract.Exp:Start xep; - ServiceProxy proxy; - Thread thread; - - if (config.directoryRef == null || - config.serviceRef == null || - config.myDSRef == null || - config.recoveryRef == null || - config.proxyRef == null) - { - return -1; - } - dep = config.directoryRef.Acquire(); - ep = config.serviceRef.Acquire(); - fep = config.myDSRef.Acquire(); - mep = config.recoveryRef.Acquire(); - xep = config.proxyRef.Acquire(); - - proxy = new ServiceProxy(new BenchmarkJournaletFactory(), - ep, mep, dep, fep, xep); - thread = new Thread(new ThreadStart(proxy.Run)); - thread.Start(); - - return 0; - } - } -} diff --git a/base/Services/Tests/BenchmarkProxy/RBenchProxy.csproj b/base/Services/Tests/BenchmarkProxy/RBenchProxy.csproj deleted file mode 100644 index fea97f8..0000000 --- a/base/Services/Tests/BenchmarkProxy/RBenchProxy.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - Exe - RBenchProxy - - - - - - - - - - - - - - - - - - diff --git a/base/Services/Tests/Counter/Counter.sg b/base/Services/Tests/Counter/Counter.sg deleted file mode 100644 index 7497780..0000000 --- a/base/Services/Tests/Counter/Counter.sg +++ /dev/null @@ -1,41 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Service\Test\Counter\CounterWorker.sg -// -// Note: -// -using System; - -namespace Microsoft.Singularity.Services.Counter -{ - internal sealed class Counter - { - private int count; - - internal Counter() - { - count = 0; - } - - internal void Increment() - { - lock (this) { - if (count < Int32.MaxValue) { - ++count; - } - else { - count = 0; - } - } - } - - internal int Count - { - get { return count; } - } - } -} diff --git a/base/Services/Tests/Counter/CounterService.csproj b/base/Services/Tests/Counter/CounterService.csproj deleted file mode 100644 index 03e7276..0000000 --- a/base/Services/Tests/Counter/CounterService.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - Exe - CounterService - - - - - - - - - - - - - - - - - - diff --git a/base/Services/Tests/Counter/CounterService.sg b/base/Services/Tests/Counter/CounterService.sg deleted file mode 100644 index 28a0faa..0000000 --- a/base/Services/Tests/Counter/CounterService.sg +++ /dev/null @@ -1,98 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Service\Test\Counter\Counter.sg -// -// Note: -// -using System; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.SingSharp.Reflection; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Configuration; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Resiliency; -using Microsoft.Singularity.Security; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Services; - -[assembly: Transform(typeof(ServiceResourceTransform))] -[assembly: ApplicationPublisherAttribute("singularity.microsoft.com")] -[assembly: AssertPrivilegeAttribute("$register-privilege.localhost")] - -namespace Microsoft.Singularity.Services.Counter -{ - [Category("Service")] - internal sealed class Parameters - { - [Endpoint] - public readonly TRef serviceRef; - - [Endpoint] - public readonly TRef directoryRef; - - [Endpoint] - public readonly TRef proxyRef; - - reflective private Parameters(); - } - - internal sealed class CounterService : WorkerServiceExec - { - private Counter! counter; - - internal CounterService([Claims]DirectoryServiceContract.Imp:Ready! dep) - : base(dep, CounterContract.ModuleName) - { - counter = new Counter(); - } - - protected override bool Accept(ServiceContract.Exp:Start! ep) - { - CounterContract.Exp:Start newEp = ep as CounterContract.Exp:Start; - return (newEp != null); - } - - protected override IRunnable NewWorker([Claims]ServiceContract.Exp:Start! ep) - { - CounterContract.Exp:Start newEp; - - newEp = ep as CounterContract.Exp:Start; - if (newEp == null) { - delete ep; - return null; - } - - return new CounterWorker(newEp, counter); - } - - internal static int AppMain(Parameters! config) - { - ManagedServiceContract.Exp:Start ep; - DirectoryServiceContract.Imp:Ready dep; - IRunnable counter; - IRunnable manageExec; - Thread thread; - - if (config.serviceRef == null || config.directoryRef == null) { - // Wrong contract type! - return -1; - } - ep = config.serviceRef.Acquire(); - dep = config.directoryRef.Acquire(); - - counter = new CounterService(dep); - manageExec = new ManagementExec(ep, counter); - thread = new Thread(new ThreadStart(manageExec.Run)); - thread.Start(); - thread.Join(); - DebugStub.Print("Counter: Quit\n"); - return 0; - } - } -} diff --git a/base/Services/Tests/Counter/CounterWorker.sg b/base/Services/Tests/Counter/CounterWorker.sg deleted file mode 100644 index 4287457..0000000 --- a/base/Services/Tests/Counter/CounterWorker.sg +++ /dev/null @@ -1,118 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Service\Test\Counter\CounterWorker.sg -// -// Note: -// -using System; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Resiliency; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Services; - -namespace Microsoft.Singularity.Services.Counter -{ - internal sealed class CounterWorker : IRunnable - { - private Counter! counter; - private TRef! counterRef; - private TRef signalRef; - - internal CounterWorker([Claims]CounterContract.Exp:Start! ep, - Counter! counter) - { - counterRef = new TRef(ep); - this.counter = counter; - } - - public void Signal([Claims]ThreadTerminationContract.Exp:Start! ep) - { - signalRef = new TRef(ep); - } - - public void Run() - { - CounterContract.Exp:Start! ep; - ThreadTerminationContract.Exp:Start! sig; - bool next = false; - - assert signalRef != null; - - ep = counterRef.Acquire(); - sig = signalRef.Acquire(); - ep.SendSuccess(); - for (;;) { - switch receive { - case ep.Increment(): - ep.SendAckIncrement(counter.Count); - counter.Increment(); - break; - case ep.BeginCount(): - do { - if (counter.Count < Int32.MaxValue) { - // HI: fault injection - int now = DateTime.UtcNow.Second; - if (now == 0 || now == 20 || now == 40) { - DebugStub.Print("%-) Fault Injection " - + now); - Thread.Sleep(1000); - goto exit; - } - // HI: fault injection end - - DebugStub.Print("Counter: Send " - + counter.Count + "\n"); - ep.SendCurrent(counter.Count); - } - else { - DebugStub.Print("Counter exceeds max\n"); - ep.SendTerminated(); - break; - } - - switch receive { - case ep.RecvNext(): - next = true; - counter.Increment(); - break; - case ep.RecvEnd(): - next = false; - break; - case ep.ChannelClosed(): - DebugStub.Print("Counter: " + - "Channel is closed.\n"); - goto exit; - break; - case sig.Stop(): - sig.SendAckStop(); - goto exit; - break; - } - } while (next); - break; - case ep.ChannelClosed(): - goto exit; - break; - case sig.Stop(): - sig.SendAckStop(); - goto exit; - break; - case sig.ChannelClosed(): - goto exit; - } - } -exit: - delete ep; - signalRef.Release(sig); - return; - } - } // class -} diff --git a/base/Services/Tests/CounterProxy/CounterJournalProducer.sg b/base/Services/Tests/CounterProxy/CounterJournalProducer.sg deleted file mode 100644 index 91d7733..0000000 --- a/base/Services/Tests/CounterProxy/CounterJournalProducer.sg +++ /dev/null @@ -1,101 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Services\Tests\CounterProxy\CounterJournalProducer.sg -// -// Note: -// -using System; -using Microsoft.SingSharp; -using Microsoft.SingSharp.Reflection; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Configuration; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Resiliency; -using Microsoft.Singularity.Security; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Services; - -[assembly: Transform(typeof(ServiceResourceTransform))] -[assembly: ApplicationPublisherAttribute("singularity.microsoft.com")] -[assembly: AssertPrivilegeAttribute("$register-privilege.localhost")] - -namespace Microsoft.Singularity.Services.CounterProxy -{ - [Category("Service")] - internal sealed class Parameters - { - [Endpoint] - public readonly TRef serviceRef; - - [Endpoint] - public readonly TRef directoryRef; - - [Endpoint] - public readonly TRef myDSRef; - - [Endpoint] - public readonly TRef proxyRef; - - reflective private Parameters(); - } - - internal class CounterJournalProducer : JournalProducer - { - internal CounterJournalProducer() : base() {} - - protected override bool Accept(ServiceContract.Exp:Start! ep) - { - // ep must be a type of CounterContract.Exp - CounterContract.Exp:Start cep = ep as CounterContract.Exp:Start; - return (cep != null); - } - - protected override void Substitute([Claims]ServiceContract.Exp:Start! ep, out ServiceContract.Exp! newEp) - { - Journalet! journalet; - ServiceContract.Exp:Start! serverEp; - - journalet = new CounterJournalet(this, ep); - journalet.CreateServerEndpoint(out serverEp); - journalet.Start(); - RegisterJournalet(journalet); - newEp = serverEp; - } - - internal static int AppMain(Parameters! config) - { - ManagedServiceContract.Exp:Start ep; - DirectoryServiceContract.Imp:Ready dep; - DirectoryServiceContract.Exp:Start fep; - ManagedProxyContract.Imp:Start mep; - - dep = config.directoryRef.Acquire(); - ep = config.serviceRef.Acquire(); - fep = config.myDSRef.Acquire(); - mep = config.proxyRef.Acquire(); - if (ep == null || dep == null || fep == null || mep == null) { - delete ep; - delete dep; - delete fep; - delete mep; - return -1; - } - - JournalProducer! jp = new CounterJournalProducer(); - try { - jp.Start(ep, dep, fep, mep); - } - catch (Exception e) { - DebugStub.WriteLine(e.ToString()); - DebugStub.WriteLine(e.StackTrace); - } - - return 0; - } - } -} diff --git a/base/Services/Tests/CounterProxy/CounterJournalet.sg b/base/Services/Tests/CounterProxy/CounterJournalet.sg deleted file mode 100644 index 593ff04..0000000 --- a/base/Services/Tests/CounterProxy/CounterJournalet.sg +++ /dev/null @@ -1,411 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Services\Tests\CounterProxy\CounterJournalet.sg -// -// Note: -// -#define VERSION_2 -using System; -using System.Threading; -using System.Collections; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Resiliency; - -namespace Microsoft.Singularity.Services.CounterProxy -{ - internal enum MessageType : uint - { - Unknown, - Success, - Increment, - BeginCount, - Next, - End, - AckIncrement, - NakIncrement, - Current, - Terminated, - } -#if VERSION_2 - internal sealed class CounterJournalet : Journalet2 -#else - internal sealed class CounterJournalet : Journalet -#endif - { - private Log! incoming; - private Log! outgoing; - private TRef counterServerRef; - private TRef counterClientRef; - private TRef initServerRef; -#if VERSION_2 - private TRef! initClientRef; -#endif - -#if VERSION_2 - internal CounterJournalet([Claims]ServiceContract.Exp:Start! ep) - : base() - { - CounterContract.Exp:Start counterEp; - counterEp = ep as CounterContract.Exp:Start; - if (counterEp == null) { - throw new ArgumentException(); - } - - this.incoming = new Log(); - this.outgoing = new Log(); - this.initClientRef = new TRef(counterEp); - } -#else - internal CounterJournalet(JournalProducer! producer, - [Claims]ServiceContract.Exp:Start! ep) - { - base(producer, ep); - this.incoming = new Log(); - this.outgoing = new Log(); - this.state = ProxyState.Waiting; - } -#endif - -#if VERSION_2 - public override void CreateEndpoint(out ServiceContract.Exp! ep) -#else - public override void CreateServerEndpoint(out ServiceContract.Exp:Start! ep) -#endif - { - CounterContract.Imp! imp; - CounterContract.Exp! exp; - - //DebugStub.Print("CJ: ENTER CreateServerEndpoint\n"); - CounterContract.NewChannel(out imp, out exp); - initServerRef = new TRef(imp); - ep = exp; - //DebugStub.Print("CJ: EXIT CreateServerEndpoint\n"); - } - -#if VERSION_2 - public override void Flush() - { - incoming.Flush(); - outgoing.Flush(); - } -#endif - - protected override bool Initialize() - { - bool success = false; - CounterContract.Exp:Start clientEp; - CounterContract.Imp:Start! serverEp; - -#if VERSION_2 - clientEp = initClientRef.Acquire(); -#else - ServiceContract.Exp:Start! ep; - ep = clientRef.Acquire(); - clientEp = ep as CounterContract.Exp:Start; - if (clientEp == null) { - clientRef.Release(ep); - return false; - } -#endif - assume initServerRef != null; - serverEp = initServerRef.Acquire(); - switch receive { - case serverEp.Success(): - clientEp.SendSuccess(); - success = true; - break; - case serverEp.ChannelClosed(): - success = false; - break; - case unsatisfiable: - DebugStub.Break(); - break; - } - - if (success) { - counterClientRef = new TRef(clientEp); - counterServerRef = new TRef(serverEp); - return true; - } - else { - delete clientEp; - delete serverEp; - return false; - } - } - - protected override bool HandleMessages() - { - bool recovery = false; - CounterContract.Exp:Ready! clientEp; - CounterContract.Imp:Ready! serverEp; - - assume counterClientRef != null; - assume counterServerRef != null; - clientEp = counterClientRef.Acquire(); - serverEp = counterServerRef.Acquire(); - - for (;;) { - switch receive { - case clientEp.Increment(): - State = ProxyState.Running; -#if VERSION_2 - Sync(); -#endif - serverEp.SendIncrement(); - incoming.Record((uint)MessageType.Increment, null); - break; - case clientEp.BeginCount(): - State = ProxyState.Running; - DebugStub.Print("CJ: BeginCount\n"); -#if VERSION_2 - Sync(); -#endif - serverEp.SendBeginCount(); - incoming.Record((uint)MessageType.BeginCount, null); - break; - case clientEp.Next(): - State = ProxyState.Running; - DebugStub.Print("CJ: Next\n"); -#if VERSION_2 - Sync(); -#endif - serverEp.SendNext(); - incoming.Record((uint)MessageType.Next, null); - break; - case clientEp.End(): - State = ProxyState.Running; - serverEp.SendEnd(); -#if VERSION_2 - Sync(); -#endif - incoming.Record((uint)MessageType.End, null); - break; - case clientEp.ChannelClosed(): - State = ProxyState.Running; - // Lost client. Journalet also goes away. - DebugStub.Print("CJ: Client disconnected.\n"); - recovery = false; - goto exit; - break; - - case serverEp.AckIncrement(number): -#if VERSION_2 - Sync(); -#endif - State = ProxyState.Running; - clientEp.SendAckIncrement(number); - ArrayList al = new ArrayList(1); - al.Add(number); - outgoing.Record((uint)MessageType.AckIncrement, al); - break; - case serverEp.Current(number): -#if VERSION_2 - Sync(); -#endif - State = ProxyState.Running; - DebugStub.Print("CJ: Current\n"); - clientEp.SendCurrent(number); - ArrayList al = new ArrayList(1); - al.Add(number); - outgoing.Record((uint)MessageType.Current, al); - break; - case serverEp.Terminated(): -#if VERSION_2 - Sync(); -#endif - State = ProxyState.Running; - clientEp.SendTerminated(); - outgoing.Record((uint)MessageType.Terminated, null); - break; - case serverEp.ChannelClosed(): - State = ProxyState.Running; - DebugStub.Print("CJ: Server disconnected.\n"); - recovery = true; - goto exit; - break; - } - State = ProxyState.Waiting; - } -exit: - if (recovery) { - counterClientRef.Release(clientEp); - counterServerRef.Release(serverEp); - } - else { - delete clientEp; - delete serverEp; - } - - return recovery; - } - - protected override bool Recover() - { - bool success = false; - uint msg; - IList args; - Object tmp; - MessageType op; - CounterContract.Imp:Start serverEp; - - DebugStub.Print("-- Incoming Log Length: {0}\n", - __arglist(incoming.Count)); - DebugStub.Print("-- Outgoing Log Length: {0}\n", - __arglist(outgoing.Count)); - - // - // Channel initialization - // - assume initServerRef != null; - DebugStub.Print("CJ: Getting an endpoint..."); - serverEp = initServerRef.Acquire(); - switch receive { - case serverEp.Success(): - break; - case serverEp.ChannelClosed(): - delete serverEp; - throw new ChannelClosedException(); - break; - } - DebugStub.Print("done.\n"); - - // - // Channel State Recovery Main Loop - // - while (incoming.Playback(out msg, out args)) { - // - // Playback the incoming message - // - op = (MessageType)msg; - switch (op) { - case MessageType.Increment: - serverEp.SendIncrement(); - break; - case MessageType.BeginCount: - serverEp.SendBeginCount(); - break; - case MessageType.Next: - serverEp.SendNext(); - break; - case MessageType.End: - serverEp.SendEnd(); - break; - } - - // - // Playback the outgoing message - // - bool res = true; - res = outgoing.Playback(out msg, out args); - op = (MessageType)msg; - - if (!res) { - DebugStub.Print("Playback: End of log. Break.\n"); - success = true; - break; - } - switch receive { - case serverEp.AckIncrement(number): - if (op != MessageType.AckIncrement || args == null) { - // make a decision - // 1. ignore - // 2. notify - success = false; - } - else { - tmp = args[0]; - if (tmp == null) { - // make a decision - // 1. ignore - // 2. notify - success = false; - } - else { - int lognum = (int)tmp; - if (number != lognum) { - // make a decision - // 1. ignore - // 2. notify - success = false; - } - else { - success = true; - } - } - } - break; - case serverEp.NakIncrement(): - if (op != MessageType.NakIncrement) { - // make a decision - // 1. ignore - // 2. notify - success = false; - } - else { - success = true; - } - break; - case serverEp.Current(number): - if (op != MessageType.Current || - args == null) { - // make a decision - // 1. ignore - // 2. notify - success = false; - } - else { - tmp = args[0]; - if (tmp == null) { - // make a decision - // 1. ignore - // 2. notify - success = false; - } - else { - int lognum = (int)tmp; - if (number != lognum) { - // make a decision - // 1. ignore - // 2. notify - success = false; - } - else { - success = true; - } - } - } - break; - case serverEp.Terminated(): - if (op != MessageType.Terminated) { - // make a decision - // 1. ignore - // 2. notify - success = false; - } - else { - success = true; - } - break; - case serverEp.ChannelClosed(): - delete serverEp; - throw new ChannelClosedException(); - break; - } - } // end of loop - - assume counterServerRef != null; - delete counterServerRef.Acquire(); - counterServerRef.Release(serverEp); - return success; - } - } -} - diff --git a/base/Services/Tests/CounterProxy/CounterJournaletFactory.sg b/base/Services/Tests/CounterProxy/CounterJournaletFactory.sg deleted file mode 100644 index 2cf5958..0000000 --- a/base/Services/Tests/CounterProxy/CounterJournaletFactory.sg +++ /dev/null @@ -1,105 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Services\Tests\CounterProxy\CounterJournaletFactory.sg -// -// Note: Resilient Service Ver.2 -// -using System; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.SingSharp.Reflection; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Configuration; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Resiliency; -using Microsoft.Singularity.Security; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Services; - -[assembly: Transform(typeof(ServiceResourceTransform))] -[assembly: ApplicationPublisherAttribute("singularity.microsoft.com")] -[assembly: AssertPrivilegeAttribute("$register-privilege.localhost")] - -namespace Microsoft.Singularity.Services.CounterProxy -{ - [Category("Service")] - internal sealed class Parameters - { - [Endpoint] - public readonly TRef serviceRef; - - [Endpoint] - public readonly TRef directoryRef; - - [Endpoint] - public readonly TRef myDSRef; - - [Endpoint] - public readonly TRef recoveryRef; - - [Endpoint] - public readonly TRef proxyRef; - - reflective private Parameters(); - } - - internal class CounterJournaletFactory : JournaletFactory - { - public bool Accept(ServiceContract.Exp:Start! ep) - { - // ep must be a type of CounterContract.Exp - CounterContract.Exp:Start cep = ep as CounterContract.Exp:Start; - return (cep != null); - } - - public Journalet2! CreateJournalet([Claims]ServiceContract.Exp:Start! ep, - out ServiceContract.Exp! newEp) - { - Journalet2! journalet; - ServiceContract.Exp! serverEp; - - journalet = new CounterJournalet(ep); - journalet.CreateEndpoint(out serverEp); - newEp = serverEp; - return journalet; - } - - internal static int AppMain(Parameters! config) - { - ManagedServiceContract.Exp:Start ep; - ManagedProxyContract.Imp:Start mep; - DirectoryServiceContract.Imp:Ready dep; - DirectoryServiceContract.Exp:Start fep; - ServiceProxyContract.Exp:Start xep; - ServiceProxy proxy; - Thread thread; - - dep = ((!)config.directoryRef).Acquire(); - ep = ((!)config.serviceRef).Acquire(); - fep = ((!)config.myDSRef).Acquire(); - mep = ((!)config.recoveryRef).Acquire(); - xep = ((!)config.proxyRef).Acquire(); - if (ep == null || dep == null || fep == null || mep == null || - xep == null) { - delete ep; - delete dep; - delete fep; - delete mep; - delete xep; - return -1; - } - - proxy = new ServiceProxy(new CounterJournaletFactory(), - ep, mep, dep, fep, xep); - thread = new Thread(new ThreadStart(proxy.Run)); - thread.Start(); - - return 0; - } - } -} diff --git a/base/Services/Tests/CounterProxy/CounterProxy.csproj b/base/Services/Tests/CounterProxy/CounterProxy.csproj deleted file mode 100644 index bb9381a..0000000 --- a/base/Services/Tests/CounterProxy/CounterProxy.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - Exe - CounterProxy - - - - - - - - - - - - - - - - - - diff --git a/base/Services/Tests/Dummy/Dummy.sg b/base/Services/Tests/Dummy/Dummy.sg deleted file mode 100644 index 2e035d1..0000000 --- a/base/Services/Tests/Dummy/Dummy.sg +++ /dev/null @@ -1,89 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Service\Dummy\Dummy.sg -// -// Note: -// -using System; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.SingSharp.Reflection; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Configuration; -using Microsoft.Singularity.Services; - -[assembly: Transform(typeof(ServiceResourceTransform))] - -namespace Microsoft.Singularity.Services -{ - [Category("Service")] - internal sealed class Parameters - { - [Endpoint] - public readonly TRef serviceContractRef; - reflective private Parameters(); - } - - internal sealed class Dummy : IRunnable - { - private TRef signalRef; - - public void Signal([Claims]ThreadTerminationContract.Exp:Start! ep) - { - signalRef = new TRef(ep); - } - - public void Run() - { - ThreadTerminationContract.Exp:Start signal; - - DebugStub.Print("-- Start Dummy Service\n"); - - assert signalRef != null; - signal = signalRef.Acquire(); - for (;;) { - switch receive { - case signal.Stop(): - goto exit; - break; - case signal.ChannelClosed(): - goto exit; - break; - } - Thread.Sleep(1000); - DebugStub.Print("Dummy\n"); - } -exit: - signalRef.Release(signal); - - DebugStub.Print("-- End Dummy Service\n"); - } - - internal static int AppMain(Parameters! config) - { - IRunnable dummy; - IRunnable manageExec; - Thread thread; - ManagedServiceContract.Exp mep; - - mep = ((!)config.serviceContractRef).Acquire(); - if (mep == null) { - delete mep; - return -1; - } - - dummy = new Dummy(); - manageExec = new ManagementExec(mep, dummy); - thread = new Thread(new ThreadStart(manageExec.Run)); - thread.Start(); - - return 0; - } - } -} diff --git a/base/Services/Tests/Dummy/dummy.csproj b/base/Services/Tests/Dummy/dummy.csproj deleted file mode 100644 index 4665243..0000000 --- a/base/Services/Tests/Dummy/dummy.csproj +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - Exe - CounterService - - - - - - - - - - - - - - - - diff --git a/base/Services/Tests/Replace/GameTable.sg b/base/Services/Tests/Replace/GameTable.sg deleted file mode 100644 index ed9140b..0000000 --- a/base/Services/Tests/Replace/GameTable.sg +++ /dev/null @@ -1,305 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Service\Test\Replace\GameTable.sg -// -// Note: -// -using System; -using System.Collections; -using System.Collections.Specialized; -using System.Text; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Resiliency; -using Microsoft.Singularity.ServiceManager; - -namespace Microsoft.Singularity.Services.Replace -{ - internal sealed class GameTable - { - //private const String[] Names = new String[4] {"Microsoft", "Google", "Yahoo", "Apple"}; - private readonly String[]! Names; - private const String ProtectionString = "PROTECTED"; - - private const int NumberOfNames = 4; - private const int ProtectionPeriod = 500; - private const int TableSize = 100; - private String[]! table; - private ASCIIEncoding! encoding; - private Random! random; - - private TRef! proxyRef; - - internal GameTable([Claims]ServiceProxyContract.Imp:Start! ep) - { - DebugStub.Print("Waiting for Success message\n"); - switch receive { - case ep.Success(): - break; - case unsatisfiable: - new ArgumentException(); - break; - } - DebugStub.Print("done\n"); - Names = new String[4] {"Microsoft", "Google", "Yahoo", "Apple"}; - this.table = new String[TableSize]; - this.proxyRef = new TRef(ep); - this.encoding = new ASCIIEncoding(); - Thread.Sleep(1); - this.random = new Random(); - } - - internal void Initialize() - { - ServiceProxyContract.Imp:Ready! ep; - - ep = proxyRef.Acquire(); - DebugStub.Print("Downloading\n"); - ep.SendDownload(); - switch receive { - case ep.AckDownload(buffer): - { - Deserialize(buffer, table); - delete buffer; - break; - } - case ep.NakDownload(): - { - int j = 0; - for (int i = 0; i < TableSize; i++) { - table[i] = Names[j++]; - if (j >= NumberOfNames) { - j = 0; - } - } - break; - } - case unsatisfiable: - { - DebugStub.Break(); - break; - } - } - proxyRef.Release(ep); - - DebugStub.Print("-- Initial Score\n"); - Print(); - DebugStub.Break(); - } - - internal void Commit() - { - ServiceProxyContract.Imp:Ready! ep; - byte[]! in ExHeap buffer; - - DebugStub.Print("-- Commit\n"); - Print(); - - Serialize(table, out buffer); - - ep = proxyRef.Acquire(); - ep.SendUpload(buffer); - switch receive { - case ep.AckUpload(): - break; - case unsatisfiable: - DebugStub.Break(); - break; - } - proxyRef.Release(ep); - } - - internal int Lookup(string! query) - { - int start; - int current; - - start = random.Next(TableSize); - - assert 0 <= start && start < TableSize; - - if (table[start] == query) { - return start; - } - - current = start + 1; - if (current >= TableSize) { - current = 0; - } - - while (current != start) { - assert 0 <= current && current < TableSize; - if (table[current] == query) { - break; - } - ++current; - if (current >= TableSize) { - current = -1; - } - } - - if (current == start) { - current = -1; - } - - return current; - } - - internal void Replace(string! query, int position) - { - //DebugStub.Print("-- Replace {0}\n", __arglist(position)); - if (position < 0 || TableSize <= position) { - return; - } - - lock (this) { - if (!((!)table[position]).EndsWith(ProtectionString)) { - table[position] = query; - } - else { - DebugStub.Print("Protected!!\n"); - } - } - } - - internal void Lock(int position) - { - String! tmp; - - if (position < 0 || TableSize <= position) { - return; - } - - tmp = (!)table[position]; - lock (this) { - table[position] += ProtectionString; - } - Thread.Sleep(ProtectionPeriod); - lock (this) { - table[position] = tmp; - } - } - - internal int Score(string! query) - { - int count = 0; - lock (this) { - foreach (String! str in table) { - if (str.StartsWith(query)) { - ++count; - } - } - } - return count; - } - - internal void Print() - { - foreach (String! player in Names) { - DebugStub.Print("{0} : {1}\n", - __arglist(player, Score(player))); - } - } - - private void Serialize(string[]! src, out byte[]! in ExHeap dest) - { - byte[][]! conversion = new byte[TableSize][]; - byte[] buffer; - byte[] in ExHeap exBuffer; - int size = 0; - int offset = 0; - - // - // Enumerate table elements - // - lock (this) { - for (int i = 0; i < TableSize; i++) { - conversion[i] = encoding.GetBytes((!)src[i]); - if (conversion[i] != null) { - size += ((!)conversion[i]).Length + sizeof(int) * 2; - } - else { - throw new NullReferenceException(); - } - } - } - assert 0 <= size && size < Int32.MaxValue; - - // - // Create a base buffer - // - buffer = new byte[size]; - offset = 0; - for (int i = 0; i < TableSize; i++) { - // length - Buffer.BlockCopy(BitConverter.GetBytes(((!)conversion[i]).Length), - 0, buffer, offset, sizeof(int)); - offset += sizeof(int); - - // index - Buffer.BlockCopy(BitConverter.GetBytes(i), 0, - buffer, offset, sizeof(int)); - offset += sizeof(int); - - // contents - Buffer.BlockCopy(conversion[i], 0, buffer, offset, - ((!)conversion[i]).Length); - offset += ((!)conversion[i]).Length; - } - - // - // Copy to ExHeap - // - exBuffer = Bitter.FromByteArray(buffer); - dest = exBuffer; - } - - private void Deserialize(byte[]! in ExHeap src, string[]! dest) - { - byte[]! copyBuffer = Bitter.ToByteArray(src); - byte[] storeBuffer; - int length = src.Length; - int count; - int id; - int offset = 0; - - lock (this) { - while (offset + 8 < length) { - // length - count = BitConverter.ToInt32(copyBuffer, offset); - - offset += sizeof(int); - if (offset >= length) { - break; - } - - // index - id = BitConverter.ToInt32(copyBuffer, offset); - - offset += sizeof(int); - if (offset >= length) { - break; - } - - // contents - storeBuffer = new byte[count]; - Buffer.BlockCopy(copyBuffer, offset, storeBuffer, 0, count); - offset += count; - if (id < TableSize) { - dest[id] = encoding.GetString(storeBuffer); - } - else { - DebugStub.Break(); - // throw new Exception(); - } - } - } - } - } // class -} diff --git a/base/Services/Tests/Replace/ReplaceGame.csproj b/base/Services/Tests/Replace/ReplaceGame.csproj deleted file mode 100644 index d218c0d..0000000 --- a/base/Services/Tests/Replace/ReplaceGame.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - Exe - ReplaceGameService - - - - - - - - - - - - - - - - - - diff --git a/base/Services/Tests/Replace/ReplaceServer.sg b/base/Services/Tests/Replace/ReplaceServer.sg deleted file mode 100644 index 955984c..0000000 --- a/base/Services/Tests/Replace/ReplaceServer.sg +++ /dev/null @@ -1,139 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Service\Test\Replace\ReplaceServer.sg -// -// Note: -// -using System; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.SingSharp.Reflection; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Configuration; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Resiliency; -using Microsoft.Singularity.Security; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Services; - -[assembly: Transform(typeof(ServiceResourceTransform))] -[assembly: ApplicationPublisherAttribute("singularity.microsoft.com")] -[assembly: AssertPrivilegeAttribute("$register-privilege.localhost")] - -namespace Microsoft.Singularity.Services.Replace -{ - [Category("Service")] - internal sealed class Parameters - { - [Endpoint] - public readonly TRef serviceRef; - - [Endpoint] - public readonly TRef directoryRef; - - [Endpoint] - public readonly TRef proxyRef; - - reflective private Parameters(); - } - - internal sealed class ReplaceServer : WorkerServiceExec - { - private GameTable table; - private TRef! proxyRef; - - internal ReplaceServer([Claims]DirectoryServiceContract.Imp:Ready! dep, - [Claims]ServiceProxyContract.Imp:Start! sep) - : base(dep, GamePlayerContract.ModuleName) - { - proxyRef = new TRef(sep); - } - - public override void Run() - { - ServiceProviderContract.Exp! provider; - ThreadTerminationContract.Exp:Start! signal; - - if (signalRef == null) { - throw new Exception("Signal is empty."); - } - - DebugStub.Print("-- Replace Game Server Start!\n"); - if (!RegisterName(out provider)) { - delete provider; - return; - } - DebugStub.Print("-- name registered\n"); - - table = new GameTable(proxyRef.Acquire()); - table.Initialize(); - - signal = signalRef.Acquire(); - while (Listen(provider, signal)); - signalRef.Release(signal); - - DeregisterName(provider); - - DebugStub.Print("-- Final Score --\n"); - table.Print(); - } - - protected override bool Accept(ServiceContract.Exp:Start! ep) - { - GamePlayerContract.Exp:Start newEp = ep as GamePlayerContract.Exp:Start; - return (newEp != null); - } - - protected override IRunnable NewWorker([Claims]ServiceContract.Exp:Start! ep) - { - GamePlayerContract.Exp:Start newEp; - - newEp = ep as GamePlayerContract.Exp:Start; - if (newEp == null) { - delete ep; - return null; - } - - if (table == null) { - delete newEp; - return null; - } - - return new ReplaceWorker(newEp, table); - } - - internal static int AppMain(Parameters! config) - { - ManagedServiceContract.Exp:Start mep; - DirectoryServiceContract.Imp:Ready dep; - ServiceProxyContract.Imp:Start sep; - IRunnable service; - IRunnable manageExec; - Thread thread; - GameTable table; - - if (config.serviceRef == null || - config.directoryRef == null || - config.proxyRef == null) - { - return -1; - } - mep = config.serviceRef.Acquire(); - dep = config.directoryRef.Acquire(); - sep = config.proxyRef.Acquire(); - - DebugStub.Print("-- server process enter\n"); - service = new ReplaceServer(dep, sep); - manageExec = new ManagementExec(mep, service); - thread = new Thread(new ThreadStart(manageExec.Run)); - thread.Start(); - thread.Join(); - return 0; - } - } -} diff --git a/base/Services/Tests/Replace/ReplaceWorker.sg b/base/Services/Tests/Replace/ReplaceWorker.sg deleted file mode 100644 index 28b0f22..0000000 --- a/base/Services/Tests/Replace/ReplaceWorker.sg +++ /dev/null @@ -1,92 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Service\Test\Replace\ReplaceWorker.sg -// -// Note: -// -using System; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Resiliency; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Services; - -namespace Microsoft.Singularity.Services.Replace -{ - internal sealed class ReplaceWorker : IRunnable - { - private TRef signalRef; - private TRef! gameRef; - private GameTable! table; - - internal ReplaceWorker([Claims]GamePlayerContract.Exp:Start! ep, - GameTable! table) - { - this.gameRef = new TRef(ep); - this.table = table; - } - - public void Signal([Claims]ThreadTerminationContract.Exp:Start! ep) - { - signalRef = new TRef(ep); - } - - public void Run() - { - GamePlayerContract.Exp:Start! ep; - ThreadTerminationContract.Exp:Start! signal; - - ep = gameRef.Acquire(); - ep.SendSuccess(); - assert signalRef != null; - signal = signalRef.Acquire(); - - for (;;) { - switch receive { - case ep.Search(query): - int current = table.Lookup(Bitter.ToString2(query)); - ep.SendAckSearch(current); - delete query; - break; - case ep.Overwrite(replacement, position): - table.Replace(Bitter.ToString2(replacement), position); - ep.SendAckOverwrite(); - //Checkpoint(); - table.Commit(); - delete replacement; - break; - case ep.Protect(position): - table.Lock(position); - ep.SendAckProtect(); - break; - case ep.Score(query): - int score = table.Score(Bitter.ToString2(query)); - ep.SendAckScore(score); - delete query; - break; - case ep.ChannelClosed(): - goto exit; - break; - case signal.Stop(): - signal.SendAckStop(); - goto exit; - break; - case signal.ChannelClosed(): - goto exit; - break; - } - } // End of loop -exit: - gameRef.Release(ep); - assert signalRef != null; - signalRef.Release(signal); - } - } -} diff --git a/base/Services/Tests/ReplaceProxy/ReplaceJournalet.sg b/base/Services/Tests/ReplaceProxy/ReplaceJournalet.sg deleted file mode 100644 index 29186a0..0000000 --- a/base/Services/Tests/ReplaceProxy/ReplaceJournalet.sg +++ /dev/null @@ -1,360 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Services\Tests\ReplaceProxy\ReplaceJournalet.sg -// -// Note: -// -using System; -using System.Threading; -using System.Collections; -using Microsoft.SingSharp; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Resiliency; -using Microsoft.Singularity.ServiceManager; - -namespace Microsoft.Singularity.Services.ReplaceProxy -{ - internal enum MessageType : uint - { - Unknown, - Search, - Overwrite, - Protect, - Score, - AckSearch, - AckOverwrite, - AckProtect, - AckScore, - } - - internal sealed class ReplaceJournalet : Journalet2 - { - private Log! incoming; - private Log! outgoing; - private TRef initImpRef; - private TRef! initExpRef; - private TRef gameImpRef; - private TRef gameExpRef; - - internal ReplaceJournalet([Claims]ServiceContract.Exp:Start! ep) - : base() // Initializes the thread to run the journalet - { - GamePlayerContract.Exp:Start newEp; - - newEp = ep as GamePlayerContract.Exp:Start; - if (newEp == null) { - throw new ArgumentException(); - } - this.incoming = new Log(); - this.outgoing = new Log(); - this.initExpRef = new TRef(newEp); - } - - public override void CreateEndpoint(out ServiceContract.Exp! ep) - { - GamePlayerContract.Imp! imp; - GamePlayerContract.Exp! exp; - - GamePlayerContract.NewChannel(out imp, out exp); - initImpRef = new TRef(imp); - ep = exp; - } - - public override void Flush() - { - incoming.Flush(); - outgoing.Flush(); - } - - protected override bool Initialize() - { - bool success = false; - GamePlayerContract.Imp:Start! imp; - GamePlayerContract.Exp:Start! exp; - - assert initImpRef != null; - imp = initImpRef.Acquire(); - exp = initExpRef.Acquire(); - - switch receive { - case imp.Success(): - exp.SendSuccess(); - success = true; - break; - case unsatisfiable: - success = false; - break; - } - - if (success) { - gameImpRef = new TRef(imp); - gameExpRef = new TRef(exp); - } - else { - delete imp; - delete exp; - } - return success; - } - - protected override bool HandleMessages() - { - bool recovery = false; - GamePlayerContract.Imp:Ready! imp; - GamePlayerContract.Exp:Ready! exp; - - assert gameImpRef != null; - assert gameExpRef != null; - imp = gameImpRef.Acquire(); - exp = gameExpRef.Acquire(); - - for (;;) { - switch receive { - case exp.Search(array): - { - Sync(); - - String obj = Bitter.ToString2(array); - IList args = new ArrayList(1); - args.Add(obj); - incoming.Record((uint)MessageType.Search, args); - imp.SendSearch(array); - DebugStub.Print("Search {0}\n", - __arglist(incoming.Count)); - break; - } - case exp.Overwrite(array, number): - { - Sync(); - - String obj = Bitter.ToString2(array); - IList args = new ArrayList(2); - args.Add(obj); - args.Add(number); - incoming.Record((uint)MessageType.Overwrite, args); - imp.SendOverwrite(array, number); - DebugStub.Print("Overwrite {0}\n", - __arglist(incoming.Count)); - break; - } - case exp.Protect(number): - { - Sync(); - - IList args = new ArrayList(1); - args.Add(number); - incoming.Record((uint)MessageType.Protect, args); - imp.SendProtect(number); - DebugStub.Print("Protect {0}\n", - __arglist(incoming.Count)); - break; - } - case exp.Score(array): - { - Sync(); - - String obj = Bitter.ToString2(array); - IList args = new ArrayList(1); - args.Add(obj); - incoming.Record((uint)MessageType.Score, args); - imp.SendScore(array); - DebugStub.Print("Score {0}\n", - __arglist(incoming.Count)); - break; - } - case imp.AckSearch(number): - IList args = new ArrayList(1); - args.Add(number); - outgoing.Record((uint)MessageType.AckSearch, args); - exp.SendAckSearch(number); - break; - case imp.AckOverwrite(): - exp.SendAckOverwrite(); - // snapshot - Checkpoint(); - Flush(); - break; - case imp.AckProtect(): - exp.SendAckProtect(); - // truncate - Flush(); - break; - case imp.AckScore(number): - exp.SendAckScore(number); - // truncate - Flush(); - break; - case exp.ChannelClosed(): - { - goto exit; - break; - } - case imp.ChannelClosed(): - { - recovery = true; - goto exit; - break; - } - } - } -exit: - if (recovery) { - gameImpRef.Release(imp); - gameExpRef.Release(exp); - } - else { - delete imp; - delete exp; - } - return recovery; - } - - protected override bool Recover() - { - GamePlayerContract.Imp! imp; - IEnumerator enumerator; - uint msg; - IList args; - - DebugStub.Print("ReplaceJ: Enter Recover\n"); - assert initImpRef != null; - imp = initImpRef.Acquire(); - switch receive { - case imp.Success(): - break; - case imp.ChannelClosed(): - throw new ChannelClosedException(); - break; - case unsatisfiable: - throw new ArgumentException(); - break; - } - - DebugStub.Print("ReplaceJ: Length: {0}\n", - __arglist(incoming.Count)); - enumerator = incoming.GetEnumerator(); - if (!enumerator.MoveNext()) { - //delete gameImpRef.Acquire(); - //gameImpRef.Release(imp); - gameImpRef = new TRef(imp); - DebugStub.Print("ReplaceJ: log is empty. Return\n"); - return true; - } - try { - while (incoming.Playback(out msg, out args)) { - DebugStub.Print("ReplaceJ: sending msg: {0}\n", - __arglist((int)msg)); - if (args != null) { - DebugStub.Print("ReplaceJ: num args: {0}\n", - __arglist(args.Count)); - } - switch ((MessageType)msg) { - case MessageType.Search: - { - if (args == null) { - throw new NullReferenceException(); - } - Object obj = args[0]; - if (obj == null) { - throw new NullReferenceException(); - } - String str = obj as String; - if (str == null) { - throw new NullReferenceException(); - } - DebugStub.Print("Replay Search '{0}'\n", - __arglist(str)); - imp.SendSearch(Bitter.FromString2(str)); - break; - } - case MessageType.Overwrite: - { - if (args == null) { - throw new NullReferenceException(); - } - Object obj = args[0]; - if (obj == null) { - throw new NullReferenceException(); - } - String str = obj as String; - if (str == null) { - throw new NullReferenceException(); - } - obj = args[1]; - if (obj == null) { - throw new NullReferenceException(); - } - DebugStub.Print("Replay Overwrite '{0}' @ {1}\n", - __arglist(str, (int)obj)); - imp.SendOverwrite(Bitter.FromString2(str), - (int)obj); - break; - } - case MessageType.Protect: - { - if (args == null) { - throw new NullReferenceException(); - } - Object obj = args[0]; - if (obj == null) { - throw new NullReferenceException(); - } - DebugStub.Print("Replay Protect {0}\n", - __arglist((int)obj)); - imp.SendProtect((int)obj); - break; - } - case MessageType.Score: - { - if (args == null) { - throw new NullReferenceException(); - } - Object obj = args[0]; - if (obj == null) { - throw new NullReferenceException(); - } - String str = obj as String; - if (str == null) { - throw new NullReferenceException(); - } - imp.SendScore(Bitter.FromString2(str)); - break; - } - } - - if (!outgoing.Playback(out msg, out args)) { - DebugStub.Print("End of log, break\n"); - break; - } - switch receive { - case imp.AckSearch(position): - break; - case imp.AckOverwrite(): - break; - case imp.AckProtect(): - break; - case imp.AckScore(score): - break; - case imp.ChannelClosed(): - throw new ChannelClosedException(); - break; - } - } // End Of Loop - } - catch (InvalidOperationException) {} - finally { - //delete gameImpRef.Acquire(); - //gameImpRef.Release(imp); - gameImpRef = new TRef(imp); - } - DebugStub.Print("ReplaceJ: End of recovery\n"); - return true; - } - } // class -} diff --git a/base/Services/Tests/ReplaceProxy/ReplaceJournaletFactory.sg b/base/Services/Tests/ReplaceProxy/ReplaceJournaletFactory.sg deleted file mode 100644 index 41d2948..0000000 --- a/base/Services/Tests/ReplaceProxy/ReplaceJournaletFactory.sg +++ /dev/null @@ -1,103 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// File: Services\Tests\ReplaceProxy\ReplaceJournaletFactory.sg -// -// Note: -using System; -using System.Threading; -using Microsoft.SingSharp; -using Microsoft.SingSharp.Reflection; -using Microsoft.Singularity; -using Microsoft.Singularity.Channels; -using Microsoft.Singularity.Configuration; -using Microsoft.Singularity.Directory; -using Microsoft.Singularity.Resiliency; -using Microsoft.Singularity.Security; -using Microsoft.Singularity.ServiceManager; -using Microsoft.Singularity.Services; - -[assembly: Transform(typeof(ServiceResourceTransform))] -[assembly: ApplicationPublisherAttribute("singularity.microsoft.com")] -[assembly: AssertPrivilegeAttribute("$register-privilege.localhost")] - -namespace Microsoft.Singularity.Services.ReplaceProxy -{ - [Category("Service")] - internal sealed class Parameters - { - [Endpoint] - public readonly TRef serviceRef; - - [Endpoint] - public readonly TRef directoryRef; - - [Endpoint] - public readonly TRef myDSRef; - - [Endpoint] - public readonly TRef recoveryRef; - - [Endpoint] - public readonly TRef proxyRef; - - reflective private Parameters(); - } - - internal class ReplaceJournaletFactory : JournaletFactory - { - public bool Accept(ServiceContract.Exp:Start! ep) - { - GamePlayerContract.Exp:Start cep = ep - as GamePlayerContract.Exp:Start; - return (cep != null); - } - - public Journalet2! CreateJournalet([Claims]ServiceContract.Exp:Start! ep, - out ServiceContract.Exp! newEp) - { - Journalet2! journalet; - ServiceContract.Exp! serverEp; - - journalet = new ReplaceJournalet(ep); - journalet.CreateEndpoint(out serverEp); - newEp = serverEp; - return journalet; - } - - internal static int AppMain(Parameters! config) - { - ManagedServiceContract.Exp:Start ep; - ManagedProxyContract.Imp:Start mep; - DirectoryServiceContract.Imp:Ready dep; - DirectoryServiceContract.Exp:Start fep; - ServiceProxyContract.Exp:Start xep; - ServiceProxy proxy; - Thread thread; - - if (config.directoryRef == null || - config.serviceRef == null || - config.myDSRef == null || - config.recoveryRef == null || - config.proxyRef == null) - { - return -1; - } - dep = config.directoryRef.Acquire(); - ep = config.serviceRef.Acquire(); - fep = config.myDSRef.Acquire(); - mep = config.recoveryRef.Acquire(); - xep = config.proxyRef.Acquire(); - - proxy = new ServiceProxy(new ReplaceJournaletFactory(), - ep, mep, dep, fep, xep); - thread = new Thread(new ThreadStart(proxy.Run)); - thread.Start(); - - return 0; - } - } -} diff --git a/base/Services/Tests/ReplaceProxy/ReplaceProxy.csproj b/base/Services/Tests/ReplaceProxy/ReplaceProxy.csproj deleted file mode 100644 index f1c7caf..0000000 --- a/base/Services/Tests/ReplaceProxy/ReplaceProxy.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - Exe - ReplaceProxy - - - - - - - - - - - - - - - - - - - - - - diff --git a/base/Services/Tests/Tests.csproj b/base/Services/Tests/Tests.csproj deleted file mode 100644 index cdb70b7..0000000 --- a/base/Services/Tests/Tests.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - diff --git a/base/Services/transforms/ApplyServiceTransform.sg b/base/Services/transforms/ApplyServiceTransform.sg new file mode 100644 index 0000000..fd4b27b --- /dev/null +++ b/base/Services/transforms/ApplyServiceTransform.sg @@ -0,0 +1,20 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +// +// +//This file is used by ServiceCategory.targets, and is added as a source file to projects that +//import ServiceCategory.targets automatically. Projects that import ServiceCategory.targets +//will automatically have the service transform applied. +// +//If a project does not want the service transform automatically applied, then set the property +//ApplyServiceTransform=false in the project. +// +//This file is NOT part of the service transform assembly itself, and should not be added to it. +// +// + +[assembly: Microsoft.SingSharp.Reflection.Transform(typeof(Microsoft.Singularity.Services.ServiceResourceTransform))] diff --git a/base/Services/transforms/ServiceTransform.csproj b/base/Services/transforms/ServiceTransform.csproj index e11bf8c..acd76cf 100644 --- a/base/Services/transforms/ServiceTransform.csproj +++ b/base/Services/transforms/ServiceTransform.csproj @@ -1,8 +1,6 @@  + + <_NibArgs>/machine:$(MACHINE) /par $(NIBOPTS) - <_NibArgs>$(_NibArgs) /cache:$(SINGULARITY_OBJROOT) - <_NibArgs>$(_NibArgs) /native:$(APPS_NATIVE_IMAGE_DIR) - <_NibArgs>$(_NibArgs) /temp:$(DISTRO_TEMP_DIR) - <_NibArgs>$(_NibArgs) /libcache:$(DISTRO_LIB_DIR) - <_NibArgs>$(_NibArgs) /options:$(NibOptionsFile) + <_NibArgs>$(_NibArgs) /cache:"$(SINGULARITY_OBJROOT)" + <_NibArgs>$(_NibArgs) /native:"$(APPS_NATIVE_IMAGE_DIR)" + <_NibArgs>$(_NibArgs) /temp:"$(DISTRO_TEMP_DIR)" + <_NibArgs>$(_NibArgs) /libcache:"$(DISTRO_LIB_DIR)" + <_NibArgs>$(_NibArgs) /options:"$(NibOptionsFile)" + <_NibArgs>$(_NibArgs) /bartok:"$(BARTOK)" + <_NibArgs>$(_NibArgs) /linker:"$(LINK)" - - - + - + <__il_refs Include="$(APPRUNTIMEDIR)\Corlib.dll" Condition="'$(Languge)'=='C#'"/> <__native_refs Include="$(APPRUNTIMEDIR)\Corlib.dll"/> - + <__native_refs Include="$(APPRUNTIMEDIR)\Microsoft.SingSharp.Runtime.dll" Condition="'$(Language)'=='Sing#'"/> <__il_refs Include="$(APPRUNTIMEDIR)\Corlib.Contracts.dll" Condition="'$(Language)'=='Sing#'"/> - <__native_refs Include="$(APPRUNTIMEDIR)\ILHelpers.dll"/> - - - + - - - - + + + + true - + true - - + + @@ -127,7 +127,7 @@ Projects that import App.targets inherit the following behaviors: $(BuildDependsOn);BuildManifest - $(OutputPath)\$(AssemblyName).manifest + $(OutputPath)\$(AssemblyName).$(Machine).manifest @@ -148,10 +148,8 @@ Projects that import App.targets inherit the following behaviors: Inputs="$(OutputAssemblyPath);$(MSBuildProjectFullPath)" Outputs="$(ManifestPath)" Condition="'$(OutputType)'=='Exe'"> - - - - + + @@ -162,32 +160,32 @@ Projects that import App.targets inherit the following behaviors: This target allows you to build an executable, and then immediately run it through NIB, to generate a native executable image. This is useful during development, in order to recompile an app without re-buiding an entire Distro project. - + This target does not use Inputs/Outputs for incremental builds; instead, NIB is always invoked. This is done for several reasons. First, NIB may decide to replace libraries with other libraries (e.g. replacing metadata-only interface assemblies with implementation assemblies); the MSBuild environment therefore cannot know which files are part of the input set. Second, NIB already examines file times, and will only invoke the native code compiler if necessary. - + We still declare the Outputs, though, to be the native image, just in case anyone wants to build and access the native image. --> - + - + - - + + diff --git a/base/Targets/AppRuntime.targets b/base/Targets/AppRuntime.targets index 8c07f99..403439e 100644 --- a/base/Targets/AppRuntime.targets +++ b/base/Targets/AppRuntime.targets @@ -1,6 +1,13 @@ + + $(APPRUNTIMEDIR) @@ -44,11 +53,11 @@ not also import SingSharp.targets. - + true - + diff --git a/base/Targets/Bartok.targets b/base/Targets/Bartok.targets deleted file mode 100644 index 5f68585..0000000 --- a/base/Targets/Bartok.targets +++ /dev/null @@ -1,521 +0,0 @@ - - - - - - - - - - - cd $(BUILDDIR) && bartok.exe - - - - - $(OutputPath)\$(NativeImageName).obj - - - - - - - - <_BartokCmd>/out: $(NativeImagePath) - - - - - - - <_BartokCmd>$(_BartokCmd) /AdaptiveCopying - - - - - - <_BartokCmd>$(_BartokCmd) /ConcurrentMSGC - - - - - - <_BartokCmd>$(_BartokCmd) /MarkSweepGC - - - - - - <_BartokCmd>$(_BartokCmd) /SemispaceGC - - - - - - - <_BartokCmd>$(_BartokCmd) /SlidingGC - - - - - - - <_BartokCmd>$(_BartokCmd) /outdir: $(OutputPath) $(BartokFlags) /IrImproveTypes=false - <_BartokCmd Condition="'$(Verbosity)'!=''">$(_BartokCmd) /verbosity:$(Verbosity) - - - - - $(OutputPath)\$(NativeImageName).log - <_BartokCmd>$(_BartokCmd) >$(OutputLogPath) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/base/Targets/ConsoleCategory.targets b/base/Targets/ConsoleCategory.targets index 1a22f89..b0111a1 100644 --- a/base/Targets/ConsoleCategory.targets +++ b/base/Targets/ConsoleCategory.targets @@ -1,6 +1,12 @@ -Copyright (c) Microsoft Corporation. All rights reserved. + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Targets/Dirs.targets b/base/Targets/Dirs.targets index 98d4a38..ec14132 100644 --- a/base/Targets/Dirs.targets +++ b/base/Targets/Dirs.targets @@ -44,6 +44,13 @@ + + + + + + + diff --git a/base/Targets/Distro.targets b/base/Targets/Distro.targets index bc0b916..bfaa04b 100644 --- a/base/Targets/Distro.targets +++ b/base/Targets/Distro.targets @@ -36,45 +36,42 @@ Distro projects can declare the following items: --> + + + + - $(MSBuildProjectName) + Singldr + SArmBoot.raw - $(SINGULARITY_PATH);$(SYSTEMROOT)\system32 - - $(Configuration).$(Platform).$(COLLECTOR_APP).$(SCHEDULER).$(COLLECTOR_KERNEL)$(PAGING_FLAG) - $(SINGULARITY_OBJROOT)\Distros\$(DistroName).$(DISTRO_OBJNAME) - $(DISTRO_ROOT).iso - $(DISTRO_ISO).log - $(SINGULARITY_ROOT)\buildcfg.cmd - - $(DISTRO_ROOT) - - $(DISTDIR)\Singularity - $(DISTDIR)\Singularity\Files - $(DISTDIR)\Singularity\Binaries - $(DISTDIR)\Singularity\Scripts - - kernel.dmp - kernel.x86.pdb - Singldr - Singularity\Singboot.ini - - /bartok:$(BARTOK) /machine:$(MACHINE) - $(DISTDIR)\NibFileList.txt - - $(SINGULARITY_ROOT)\Distro\LegacyPCDistro.xml - - metadata.xml - $(DISTDIR)\files.txt - + "$(BUILDDIR)\distrobuilder.exe" + + $(SINGULARITY_ROOT)\Options\$(Configuration).$(COLLECTOR_APP).$(Machine).options + $(DefaultNibOptionsFile) + + + + + + + + + + + + @@ -117,69 +116,97 @@ Distro projects can declare the following items: - - + + + + - - - + + - - + + - + - - - + + + + + + + + + + + + + + + + + + + + - - + + - + - - - + + + + + + + + @@ -189,9 +216,9 @@ $(DISTRO_ENV) - + - + @@ -211,7 +238,7 @@ $(DISTRO_ENV) - + @@ -219,15 +246,23 @@ $(DISTRO_ENV) - + + + + + - - + + + - - - + @@ -238,12 +273,14 @@ $(DISTRO_ENV) will reject the task if the BuildInParallel attribute is present, even if it is empty or "false". --> + Condition="'$(DistroSkipApps)'!='true' and '$(BuildInParallel)'!='true'" + StopOnFirstFailure="$(StopOnFirstFailure)"> + BuildInParallel="true" + StopOnFirstFailure="$(StopOnFirstFailure)"> @@ -253,45 +290,95 @@ $(DISTRO_ENV) --> + Condition="'$(DistroSkipApps)'=='true'" + StopOnFirstFailure="$(StopOnFirstFailure)"> + + + + + - + - + + + + + + + + + + + + - + - + + Command="$(NIB) /machine:$(MACHINE) /bartok:$(BARTOK) /linker:$(LINK) /cache:"$(DISTRO_CACHE_DIR)" /libcache:"$(DISTRO_LIB_DIR)" /native:"$(APPS_NATIVE_IMAGE_DIR)" /options:"$(NibOptionsFile)" /temp:"$(DISTRO_TEMP_DIR)" $(NIBOPTS) /apps:"$(NibFileList)""/> - + + + + + + + + + + + - - + + + + + + - + @@ -310,26 +398,29 @@ $(DISTRO_ENV) + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - diff --git a/base/Targets/DistroPaths.targets b/base/Targets/DistroPaths.targets new file mode 100644 index 0000000..7e8a0d2 --- /dev/null +++ b/base/Targets/DistroPaths.targets @@ -0,0 +1,45 @@ + + + + + + $(MSBuildProjectName) + + $(SINGULARITY_PATH);$(SYSTEMROOT)\system32 + + $(Configuration).$(Platform).$(COLLECTOR_APP).$(SCHEDULER).$(COLLECTOR_KERNEL)$(PAGING_FLAG) + $(SINGULARITY_OBJROOT)\Distros\$(DistroName).$(DISTRO_OBJNAME) + $(DISTRO_ROOT).iso + $(DISTRO_ISO).log + $(DISTRO_ROOT).raw + $(DISTRO_ENV_DIR)\$(SINGULARITY_CMDID).cmd + $(DISTRO_ROOT) + + $(DISTDIR)\Singularity + $(DISTDIR)\Singularity\Files + $(DISTDIR)\Singularity\Binaries + $(DISTDIR)\Singularity\Scripts + + Singularity\Singboot.ini + + $(DISTDIR)\NibFileList.txt + + + + metadata.xml + $(DISTDIR)\files.txt + + + diff --git a/base/Targets/DriverCategory.targets b/base/Targets/DriverCategory.targets index 1dfd72f..1654909 100644 --- a/base/Targets/DriverCategory.targets +++ b/base/Targets/DriverCategory.targets @@ -1,6 +1,12 @@ -Copyright (c) Microsoft Corporation. All rights reserved. + + + + + + + true + + + true + + + + + true + $(MsilRewrite);GenerateEvent + + + + + + + + + diff --git a/base/Targets/ILAssembly.targets b/base/Targets/ILAssembly.targets index 7c6f094..c5b5968 100644 --- a/base/Targets/ILAssembly.targets +++ b/base/Targets/ILAssembly.targets @@ -12,7 +12,7 @@ Projects that import this file can set the following properties: AssemblyName Name of assembly, excluding file extension OutputType 'Exe' or 'Library' NoStrongName 'true' to turn off assembly strong name signing - OutputPath Specifies output path for the assembly. Default is $(ILLSDIR). + OutputPath Specifies output path for the assembly. Default is $(APPILLSDIR). Projects can declare the following items: @@ -25,13 +25,13 @@ Projects can declare the following items: false - $(ILLSDIR) + $(APPILLSDIR) - /nologo /debug /quiet + /nologo /pdb /quiet diff --git a/base/Targets/InterfaceAssembly.targets b/base/Targets/InterfaceAssembly.targets index 5954a19..5dcbcaf 100644 --- a/base/Targets/InterfaceAssembly.targets +++ b/base/Targets/InterfaceAssembly.targets @@ -20,7 +20,7 @@ Projects that import this file can set the following properties: NoStrongName 'true' to turn off assembly strong name signing InterfaceAssemblyIsKey 'true' to pass /key to csic.exe CSIC Overrides the default compiler tool, $(BUILDDIR)\csic.exe - OutputPath Specifies output path for the assembly. Default is $(ILLSDIR). + OutputPath Specifies output path for the assembly. Default is $(APPILLSDIR). NoStdLib 'true' to pass /nostdlib to csic.exe Projects can declare the following items: @@ -40,8 +40,7 @@ Projects can declare the following items: false false false - $(ILLSDIR) - $(BUILDDIR)\csic.exe + "$(BUILDDIR)\csic.exe" @@ -144,7 +143,17 @@ Projects can declare the following items: - + + + + @@ -157,31 +166,50 @@ Projects can declare the following items: - + - + - + + - - + Condition="'$(NoStrongName)'!='true'"> + - + + + $(OutputPath)\$(AssemblyName).csic.rsp + + + DependsOnTargets="CheckErrors;BuildStrongName;BuildDependentProjects"> - + + + + + - + diff --git a/base/Targets/KernelContractsLibrary.targets b/base/Targets/KernelContractsLibrary.targets index 1a8c7eb..b2a53fe 100644 --- a/base/Targets/KernelContractsLibrary.targets +++ b/base/Targets/KernelContractsLibrary.targets @@ -1,10 +1,17 @@ -Copyright (c) Microsoft Corporation. All rights reserved. + diff --git a/base/Targets/KernelLibrary.targets b/base/Targets/KernelLibrary.targets index 61e41fc..56818e3 100644 --- a/base/Targets/KernelLibrary.targets +++ b/base/Targets/KernelLibrary.targets @@ -1,6 +1,12 @@ -Copyright (c) Microsoft Corporation. All rights reserved. + - - - - true - - true diff --git a/base/Targets/LibraryGroup.targets b/base/Targets/LibraryGroup.targets index ceeb54b..783b73e 100644 --- a/base/Targets/LibraryGroup.targets +++ b/base/Targets/LibraryGroup.targets @@ -1,6 +1,12 @@ -Copyright (c) Microsoft Corporation. All rights reserved. + + + $(SINGULARITY_ROOT)\Imported\Bartok + $(DefaultBartokSrcDir) + $(SINGULARITY_ROOT)\Imported\SingSharp + $(DefaultSingSharpSrcDir) + MpSyscallBuilder @@ -106,13 +120,33 @@ find frequently-used paths. - $(AFLAGS) /nologo /Zi /Cp /DSINGULARITY=1 - $(CFLAGS) /nologo /W3 /WX /Gy /Zi /O2 /Oy- /GS- /DSINGULARITY=1 + $(AFLAGS) /nologo /Zi /Cp /DSINGULARITY=1 + $(CFLAGS) /nologo /W3 /WX /Gy /Zi /GS- /DSINGULARITY=1 $(CC_FLAGS_64) + $(CFLAGS) $(CFLAGS) + + $(CFLAGS) /FC + - + + + + $(CFLAGS) /O2 /Oy- + + + + + $(CFLAGS) /Od + + + + $(ACFLAGS) /DDEBUG=1 + + + + Concurrent MarkSweep @@ -126,9 +160,10 @@ find frequently-used paths. + - COLLECTOR_KERNEL must either be unset, or set to one of "AdaptiveCopying" or "Concurrent" or "MarkSweep" or "Semispace" or "Sliding" + COLLECTOR_KERNEL must either be unset, or set to one of "AdaptiveCopying" or "Concurrent" or "MarkSweep" or "Semispace" or "Sliding" or "Null" @@ -140,9 +175,10 @@ find frequently-used paths. + - COLLECTOR_APP must either be unset, or set to one of "AdaptiveCopying" or "Concurrent" or "MarkSweep" or "Semispace" or "Sliding" + COLLECTOR_APP must either be unset, or set to one of "AdaptiveCopying" or "Concurrent" or "MarkSweep" or "Semispace" or "Sliding" | "Null" @@ -158,12 +194,15 @@ find frequently-used paths. + ApicPC Debug + + @@ -181,39 +220,129 @@ find frequently-used paths. - + 1 - x86 + x64 + true + + + + + + 1 + arm + false + + + + + + 1 + arm false - Platform must either be unset, or set to one of "LegacyPC" or "ApicPC" or "ApicMP". '$(Platform)' is invalid. - + Platform must either be unset, or set to one of "LegacyPC" or "ApicPC" or "ApicMP" or "Omap3430". '$(Platform)' is invalid. + + + + + + BARTOK + + + + + PHXBRIDGE + + + + + + + + DEBUG + CLR + + + + + TEST + CLR + + + + - $(BUILDDIR)\cl.exe - $(BUILDDIR)\ml.exe - $(BUILDDIR)\bartok.exe - $(BUILDDIR) + "$(BUILDDIR)\x86_x86\cl.exe" + "$(BUILDDIR)\x86_x86\ml.exe" + "$(BUILDDIR)\x86_x86\$(CODE_GENERATOR)\$(CODE_GENERATOR_BUILD)\$(CODE_GENERATOR_RUNTIME)\bartok.exe" + "$(BUILDDIR)\x86_x86\link.exe" + "$(BUILDDIR)\x86_x86\lib.exe" + "$(BUILDDIR)\x86_x86\dumpbin.exe" LITTLE_ENDIAN Singularity.V1.def + $(AFLAGS) /Zi /Cp /DISA_IX86=1 /DPTR_SIZE_32=1 /DSINGULARITY=1 + $(CFLAGS) /DISA_IX=1 /DISA_IX86=1 /DPTR_SIZE_32=1 + $(LINKFLAGS) /machine:x86 /nologo + $(LIBFLAGS) /machine:x86 /nologo + $(DUMPFLAGS) /nologo + x86 + + + + + "$(BUILDDIR)\x86_x64\cl.exe" + "$(BUILDDIR)\x86_x64\ml64.exe" + "$(BUILDDIR)\x86_x64\$(CODE_GENERATOR)\$(CODE_GENERATOR_BUILD)\$(CODE_GENERATOR_RUNTIME)\bartok.exe" + "$(BUILDDIR)\x86_x64\link.exe" + "$(BUILDDIR)\x86_x64\lib.exe" + "$(BUILDDIR)\x86_x64\dumpbin.exe" + LITTLE_ENDIAN + $(AFLAGS) /Zi /Cp /DISA_IX=1 /DISA_IX64=1 /DSINGULARITY=1 + $(ACFLAGS) /DISA_IX64=1 /DPTR_SIZE_64=1 /DUSE_64=1 + $(CFLAGS) /DISA_IX=1 /DISA_IX64=1 /DPTR_SIZE_64=1 + $(LINKFLAGS) /machine:x64 /nologo + $(LIBFLAGS) /machine:x64 /nologo + $(DUMPFLAGS) /nologo + Singularity.V1.def + amd64 + + + + + "$(BUILDDIR)\x86_arm\cl.exe" + "$(BUILDDIR)\x86_arm\mlarm.exe" + "$(BUILDDIR)\x86_arm\$(CODE_GENERATOR)\$(CODE_GENERATOR_BUILD)\$(CODE_GENERATOR_RUNTIME)\bartok.exe" + "$(BUILDDIR)\x86_arm\link.exe" + "$(BUILDDIR)\x86_arm\lib.exe" + "$(BUILDDIR)\x86_arm\dumpbin.exe" /nologo + LITTLE_ENDIAN + Singularity.V1.def + $(AFLAGS) /QRarch5 + $(CFLAGS) /DISA_ARM=1 /DISA_XSCALE=1 /DPTR_SIZE_32=1 /QRarch5 + $(LINKFLAGS) /machine:arm /nologo + $(LIBFLAGS) /machine:arm /nologo + $(DUMPFLAGS) /nologo + arm + $(BARTOK_FLAGS) /IrPeepholeNull=false - +--> @@ -246,15 +375,11 @@ find frequently-used paths. - - $(LINKFLAGS) /NOLOGO /INCREMENTAL:NO /machine:$(Machine) /debug /debugtype:cv + $(LINKFLAGS) /NOLOGO /INCREMENTAL:NO /debug /debugtype:cv - - - $(AFLAGS) /DTHREAD_TIME_ACCOUNTING=1 @@ -288,7 +413,17 @@ find frequently-used paths. - + + + + $(AFLAGS) /DOMAP3430=1 + $(CFLAGS) /DOMAP3430 + $(CPPFLAGS) /DOMAP3430 + $(CSFLAGS) /d:OMAP3430 + $(SGFLAGS) /d:OMAP3430 + + + - $(SINGULARITY_ROOT).obj $(SINGULARITY_OBJROOT) @@ -312,7 +446,7 @@ find frequently-used paths. Absolute paths into object tree. Note that some of these paths have different sensitivities to different build properties. --> - $(SINGULARITY_OBJROOT)\Boot\$(Configuration).$(Machine) + $(SINGULARITY_OBJROOT)\Boot\$(Configuration).$(Machine).$(Platform) $(SINGULARITY_OBJROOT)\Drivers\$(OBJRELDIR) $(SINGULARITY_OBJROOT)\Libraries\$(OBJRELDIR) $(SINGULARITY_OBJROOT)\Apps\$(OBJRELDIR) @@ -324,14 +458,11 @@ find frequently-used paths. directory does not vary over any build variables; there is only one interface output directory. --> - $(SINGULARITY_OBJROOT)\Interfaces - - $(SINGULARITY_OBJROOT)\AppRuntime\$(OBJRELDIR) - $(SINGULARITY_OBJROOT)\AppRuntime\$(OBJRELDIR) - $(SINGULARITY_OBJROOT)\AppRuntimeNative\$(OBJRELDIR) + $(SINGULARITY_OBJROOT)\Interfaces $(SINGULARITY_OBJROOT)\Windows\obj\$(OBJRELDIR) $(BASEDIR)\Windows\obj\$(OBJRELDIR) + $(SINGULARITY_OBJROOT)\Windows\$(OBJRELDIR) $(SINGULARITY_OUTPUT)\$(Configuration).$(MACHINE).$(Platform) @@ -342,24 +473,24 @@ find frequently-used paths. $(SINGULARITY_OBJROOT)\AppNativeImage\$(OBJRELRUNTIMEDIR) $(SINGULARITY_OBJROOT) - - $(APPRUNTIMEDIR)\Singularity.V1.lib - + + $(SINGULARITY_OBJROOT)\Tools - $(BUILDDIR)\csc.exe - $(BUILDDIR)\sgc.exe + "$(BUILDDIR)\csc.exe" + "$(BUILDDIR)\sgc.exe" $(DEFAULT_CC) $(DEFAULT_AS) - $(BUILDDIR)\ilasm.exe - $(BUILDDIR)\ildasm.exe - $(BUILDDIR)\lib.exe - $(BUILDDIR)\link.exe - $(BUILDDIR)\nib.exe + "$(BUILDDIR)\ilasm\ilasm.exe" + "$(BUILDDIR)\ildasm.exe" + $(DEFAULT_LINK) + $(DEFAULT_LIB) + $(DEFAULT_DUMPBIN) + "$(BUILDDIR)\nib.exe" $(DEFAULT_BARTOK) @@ -367,11 +498,6 @@ find frequently-used paths. - - - $(BUILDDIR)\NVidia - - true diff --git a/base/Targets/RuntimePaths.target b/base/Targets/RuntimePaths.target new file mode 100644 index 0000000..a82ccd6 --- /dev/null +++ b/base/Targets/RuntimePaths.target @@ -0,0 +1,24 @@ + + + + + true + + + + + Full + + + + + $(SINGULARITY_ROOT)\Applications\Runtime\$(Runtime) + $(SINGULARITY_OBJROOT)\AppRuntime\$(Runtime)\$(OBJRELDIR) + $(SINGULARITY_OBJROOT)\AppRuntime\$(Runtime)\$(OBJRELDIR) + $(SINGULARITY_OBJROOT)\AppRuntimeNative\$(Runtime)\$(OBJRELDIR) + + + $(APPRUNTIMEDIR)\Singularity.V1.$(Runtime).lib + + + diff --git a/base/Targets/ServiceCategory.targets b/base/Targets/ServiceCategory.targets index 2b57e64..4a2350d 100644 --- a/base/Targets/ServiceCategory.targets +++ b/base/Targets/ServiceCategory.targets @@ -1,18 +1,70 @@ + + + + Service + $(OutputItemAdditionalMetadata);ServiceActivationMode=$(ServiceActivationMode) + $(CheckPropertiesDependsOn);CheckServiceProperties + + true + + + + + + + diff --git a/base/Targets/SingSharp.targets b/base/Targets/SingSharp.targets index 27975e7..3cf071c 100644 --- a/base/Targets/SingSharp.targets +++ b/base/Targets/SingSharp.targets @@ -1,9 +1,15 @@ - + @@ -121,11 +165,28 @@ All targets should be defined AFTER importing this file. cli1 true - $(BUILDDIR)\mkasm.exe + "$(BUILDDIR)\mkasm.exe" true + unmanaged - + + + + + + + + $(OutputPath)\$(SubPath) + + + + $(MSBuildProjectDirectory) .exe @@ -136,11 +197,52 @@ All targets should be defined AFTER importing this file. $(PrecompiledPath)\$(AssemblyFileName) + + /t:library + /t:exe + + + + + + + $(OutputPath)\PreRewrite\$(AssemblyName)$(AssemblyExt) + $(OutputPath)\PreRewrite\$(AssemblyName).pdb + + + + + $(OutputAssemblyPath) + $(OutputSymbolPath) + + + + + + + + $(OutputPath)\$(AssemblyName)$(AssemblyExt).gen.dll + $(OutputPath)\$(AssemblyName)$(AssemblyExt).gen.pdb + /out:"$(PreSourceAssemblyPath)" /t:library + /out:"$(PreRewriteAssemblyPath)" $(CompilerTargetFlag) + + + + + $(PreRewriteAssemblyPath) + $(PreRewriteAssemblySymbolPath) + /out:"$(PreRewriteAssemblyPath)" $(CompilerTargetFlag) + + + + - $(CompilerFlags) /out:$(OutputAssemblyPath) /debug /noconfig - $(CompilerFlags) /t:library - $(CompilerFlags) /t:exe + 444,465,1699,3019,3021,$(NoWarn) + + + + $(CompilerFlags) /debug /noconfig $(CompilerFlags) /d:$(DefineConstants) $(CompilerFlags) /warn:$(WarningLevel) $(CompilerFlags) /warnaserror+ @@ -150,12 +252,11 @@ All targets should be defined AFTER importing this file. - - $(CompilerFlags) /d:DEBUG;TRACING;WAYPOINTS;MONITORING;CHANNEL_COUNT + $(CompilerFlags) /d:DEBUG;TRACING;WAYPOINTS;CHANNEL_COUNT @@ -165,7 +266,12 @@ All targets should be defined AFTER importing this file. - $(CompilerFlags) /d:DEBUG;TRACING;WAYPOINTS;MONITORING;CHANNEL_COUNT + $(CompilerFlags) /d:DEBUG;TRACING;WAYPOINTS;CHANNEL_COUNT + + + + + $(CompilerFlags) /d:MONITORING @@ -179,7 +285,7 @@ All targets should be defined AFTER importing this file. - $(SGC) + $(SGC) /shoholacreek @@ -193,7 +299,17 @@ All targets should be defined AFTER importing this file. - $(CompilerFlags) /d:X86;LITTLE_ENDIAN;PTR_SIZE_32 + $(CompilerFlags) /d:ISA_IX;ISA_IX86;LITTLE_ENDIAN;PTR_SIZE_32 + + + + + $(CompilerFlags) /d:ISA_IX;ISA_IX64;LITTLE_ENDIAN;PTR_SIZE_64 + + + + + $(CompilerFlags) /d:ISA_ARM;LITTLE_ENDIAN;PTR_SIZE_32 @@ -208,8 +324,8 @@ All targets should be defined AFTER importing this file. $(CompilerFlags) /assumefieldsnonnull - $(CompilerFlags) /platform:$(CompilerPlatformVersion),$(CompilerPlatformDir) - $(CompilerFlags) /shadow:$(ShadowAssemblyPath) + $(CompilerFlags) /shoholacreek /platform:"$(CompilerPlatformVersion),$(CompilerPlatformDir)" + $(CompilerFlags) /shadow:"$(ShadowAssemblyPath)" $(CompilerFlags) /disable:nullparametervalidation $(CompilerFlags) /d:NONULLCHECK @@ -217,7 +333,7 @@ All targets should be defined AFTER importing this file. $(CompilerFlags) /nostdlib - $(CompilerFlags) /stdlib:$(Stdlib) + $(CompilerFlags) /stdlib:"$(Stdlib)" @@ -228,7 +344,7 @@ All targets should be defined AFTER importing this file. $(CompilerFlags) /nostdlib - $(CompilerFlags) /r:$(Stdlib) + $(CompilerFlags) /r:"$(Stdlib)" @@ -237,17 +353,17 @@ All targets should be defined AFTER importing this file. $(CompilerFlags) /d:SINGULARITY $(CompilerFlags) /d:SINGULARITY_MP - $(CompilerFlags) /d:WAYPOINTS - $(CompilerFlags) /d:MONITORING - $(CompilerFlags) /D:MAX_CPU$(MAX_CPU) + $(CompilerFlags) /d:WAYPOINTS + $(CompilerFlags) /d:MONITORING $(CompilerFlags) /fullpaths + <_DocumentationFile>$(OutputPath)\$(AssemblyName).xml - $(CompilerFlags) /doc:$(_DocumentationFile) + $(CompilerFlags) /doc:"$(_DocumentationFile)" @@ -260,10 +376,10 @@ All targets should be defined AFTER importing this file. $(OutputPath)\$(AssemblyName).rsp - + $(CompilerFlags) /d:PAGING - + $(CompilerFlags) $(ExtraCompilerFlags) @@ -272,7 +388,7 @@ All targets should be defined AFTER importing this file. start /wait cordbg.exe $(CompilerTool) - + @@ -292,6 +408,18 @@ All targets should be defined AFTER importing this file. + + + + + + + @@ -311,10 +440,10 @@ All targets should be defined AFTER importing this file. This target allows other projects to determine the full path of the output assembly of this project, without actually building the project. --> - - - - + + + + @@ -328,83 +457,152 @@ All targets should be defined AFTER importing this file. - + + + + + - + - + + + + Command="$(CompilerTool) $(PrelimCompilerFlags) $(CompilerFlags) /debug @"$(ResponseFileName)""/> + Command="$(CompilerTool) $(PrelimCompilerFlags) $(CompilerFlags) /debug @(ReferencePath->'/lib:"%(Identity)"', ' ') @(Reference->'/r:"%(Identity)"',' ') @(__il_refs->'/r:"%(identity)"',' ') @(Compile->'"%(Identity)"',' ')"/> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -419,7 +617,7 @@ All targets should be defined AFTER importing this file. - + @@ -447,35 +645,35 @@ All targets should be defined AFTER importing this file. - - + + - + --> + - + Command="$(MKASM) /b /m /l /o:"$(OutputPath)\$(AssemblyName).Resource.%(BinaryResource.Namespace).%(BinaryResource.ClassName).%(BinaryResource.FieldName).cs" /n:%(BinaryResource.Namespace) /c:%(BinaryResource.ClassName) /f:%(BinaryResource.FieldName) %(BinaryResource.Args) @(BinaryResource->'"%(Identity)"',' ')"/> + + Command="$(MKASM) /b /m /o:"$(OutputPath)\$(AssemblyName).Resource.%(BinaryResource.Namespace).%(BinaryResource.ClassName).%(BinaryResource.FieldName).cs" /n:%(BinaryResource.Namespace) /c:%(BinaryResource.ClassName) /f:%(BinaryResource.FieldName) %(BinaryResource.Args) @(BinaryResource->'"%(Identity)"',' ')"/> @@ -539,7 +737,7 @@ All targets should be defined AFTER importing this file. - + @@ -550,7 +748,7 @@ All targets should be defined AFTER importing this file. - + @@ -560,55 +758,21 @@ All targets should be defined AFTER importing this file. Inputs="$(OutputAssemblyPath)" Outputs="$(OutputPath)\$(AssemblyName).tmf" Condition="'$(GenerateContractMap)'=='true'"> - - - - - - - - - - - - - - - - - - + + - + - + @@ -625,9 +789,12 @@ All targets should be defined AFTER importing this file. - + - + + + + diff --git a/base/Targets/TestCategory.targets b/base/Targets/TestCategory.targets index 4cf73c4..e5cd5d2 100644 --- a/base/Targets/TestCategory.targets +++ b/base/Targets/TestCategory.targets @@ -1,20 +1,60 @@ + + + true + + true + + + + true + $(BuildDependsOn);MakeTestProfile + GenerateTestJig + Test + $(BUILDDIR)\x86_any\regsvr32.exe + $(BUILDDIR)\x64_any\regsvr32.exe + + + + + + + + + + + + + + + diff --git a/base/Targets/Windows.targets b/base/Targets/Windows.targets index a604cb3..25bb1b8 100644 --- a/base/Targets/Windows.targets +++ b/base/Targets/Windows.targets @@ -1,4 +1,10 @@ - + @@ -7,33 +13,44 @@ Also, we add $(Configuration).$(Platform) to BaseIntermediateOutputPath, even though CommonTargets will add them itself, because CommonTargets also generates some intermediate files *without* decorating them. --> - $(SINGULARITY_OBJROOT)\Windows\$(Configuration).$(Platform)\ - $(SINGULARITY_OBJROOT)\Windows.obj\$(Configuration).$(Platform)\ - - + $(SINGULARITY_OBJROOT)\Windows\$(Configuration).$(Platform)\$(SubPath) + $(SINGULARITY_OBJROOT)\Windows.obj\$(Configuration).$(Platform)\$(SubPath) + $(BUILDDIR)\internal\sd.exe true full + true + true + $(SINGULARITY_ROOT)\build + + RunCustomBuildTools + + - - + - + - + + + + + + + diff --git a/base/Targets/WindowsTransform.targets b/base/Targets/WindowsTransform.targets new file mode 100644 index 0000000..838a88d --- /dev/null +++ b/base/Targets/WindowsTransform.targets @@ -0,0 +1,135 @@ + + + + + + $(SINGULARITY_OBJROOT)\Windows\$(Configuration).$(Platform)\ + $(SINGULARITY_OBJROOT)\Windows.obj\$(Configuration).$(Platform)\ + $(BUILDDIR)\internal\sd.exe + true + full + true + true + $(SINGULARITY_ROOT)\build + false + @(AdditionalLibPaths);$(MSBuildBinPath) + + + + + + + + $(IntermediateOutputPath)Rewrite\$(TargetName)$(TargetExt) + $(IntermediateOutputPath)Rewrite\$(TargetName).pdb + $(IntermediateOutputPath)Rewrite\ + $(IntermediateOutputPath)Rewrite\public.snk + $(IntermediateOutputPath)$(TargetName).pdb + + + + + + + + + + RunCustomBuildTools + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Windows/AppReader/AppNode.cs b/base/Windows/AppReader/AppNode.cs index d79e752..a6e8d36 100644 --- a/base/Windows/AppReader/AppNode.cs +++ b/base/Windows/AppReader/AppNode.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- namespace AppReader { @@ -51,7 +49,7 @@ namespace AppReader public void Dump(string indent) { Console.WriteLine(indent + nodeName + ": " + count); - for(int i = 0; i < children.Count; i++) + for (int i = 0; i < children.Count; i++) { ((AppNode)children[i]).Dump(indent + "\t"); } @@ -62,7 +60,7 @@ namespace AppReader AppNode outnode = new AppNode(this.nodeName, inParent); outnode.count = this.count; outnode.normalized = this.normalized; - for(int i = 0; i < this.children.Count; i++) + for (int i = 0; i < this.children.Count; i++) { // this looks like a throwaway, but in the constructor it will attach to us ((AppNode)this.children[i]).Copy(outnode); @@ -99,7 +97,7 @@ namespace AppReader { if (normalized) return this.count; normalized = true; - for(int i = 0; i < this.children.Count; i++) + for (int i = 0; i < this.children.Count; i++) { this.count += ((AppNode)this.children[i]).Weight(); } @@ -109,7 +107,7 @@ namespace AppReader public void Flatten(ArrayList ar) { // put all our children in it before we go in (post-order) - for(int i = 0; i < this.children.Count; i++) + for (int i = 0; i < this.children.Count; i++) { ((AppNode)this.children[i]).Flatten(ar); } diff --git a/base/Windows/AppReader/AppTree.cs b/base/Windows/AppReader/AppTree.cs index 3d77ac8..6a4e758 100644 --- a/base/Windows/AppReader/AppTree.cs +++ b/base/Windows/AppReader/AppTree.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- namespace AppReader { @@ -39,7 +37,7 @@ namespace AppReader // we just walk through the things we found and increment them in the tree // if they exist in the wxvals hashtable IEnumerator ienum = whash.GetEnumerator(); - while(ienum.MoveNext()) + while (ienum.MoveNext()) { string key = (string)((DictionaryEntry)ienum.Current).Key; int val = (int)((DictionaryEntry)ienum.Current).Value; @@ -78,12 +76,12 @@ namespace AppReader // my resource trees will ever be 10 deep string[] pathstr = treePath.Split(new char[] {' '}, 10); AppNode curNode = top; - for(int i = 0; i < pathstr.Length; i++) + for (int i = 0; i < pathstr.Length; i++) { if (pathstr[i].Equals("")) continue; // find the child that has the right name bool found = false; - for(int j = 0; j < curNode.NumChildren; j++) + for (int j = 0; j < curNode.NumChildren; j++) { if (curNode[j].Name == pathstr[i]) { @@ -107,7 +105,7 @@ namespace AppReader AppNode top = null; AppNode lastNode = null; int lastTabCount = 0; - while((curLine = ios.ReadLine()) != null) + while ((curLine = ios.ReadLine()) != null) { int tabcount = curLine.LastIndexOf("\t"); tabcount++; @@ -120,14 +118,13 @@ namespace AppReader { // then walk up the tree to the new parent int diff = lastTabCount - tabcount; - while(diff-- > 0) + while (diff-- > 0) { parent = parent.Parent; Debug.Assert(parent != null); } } - else if (tabcount > lastTabCount) - { + else if (tabcount > lastTabCount) { parent = lastNode; Debug.Assert(tabcount == lastTabCount + 1); } diff --git a/base/Windows/AppReader/CMIParser.cs b/base/Windows/AppReader/CMIParser.cs index 705459d..4c3fcea 100644 --- a/base/Windows/AppReader/CMIParser.cs +++ b/base/Windows/AppReader/CMIParser.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- namespace ManifestReader { @@ -51,12 +49,10 @@ namespace ManifestReader { dpath = dpath.Replace("$(runtime.windows)", "\\windows"); } - else if (dpath.IndexOf("$(runtime.drivers)") >= 0) - { + else if (dpath.IndexOf("$(runtime.drivers)") >= 0) { dpath = dpath.Replace("$(runtime.drivers)", "\\windows\\system32\\drivers"); } - else if (dpath.IndexOf("$(runtime.system32)") >= 0) - { + else if (dpath.IndexOf("$(runtime.system32)") >= 0) { dpath = dpath.Replace("$(runtime.system32)", "\\windows\\system32"); } @@ -71,7 +67,7 @@ namespace ManifestReader { string[] pathElts = dpath.Split(new char[] {'\\'}, 50); string curPath = ""; - for(int i = 0; i < pathElts.Length; i++) + for (int i = 0; i < pathElts.Length; i++) { if (pathElts[i].Equals("")) { @@ -103,19 +99,16 @@ namespace ManifestReader { return "Data File Display UI"; } - else if (isFontSuffix(suffix)) - { + else if (isFontSuffix(suffix)) { return "Data File Display Fonts"; } - else - { + else { return "Data File"; } } return ""; } - else if (tagname.Equals("fileDependency")) - { + else if (tagname.Equals("fileDependency")) { // then register this file dependency string filename = xmld.GetAttribute("name"); if (filename != null) @@ -138,25 +131,21 @@ namespace ManifestReader } return ""; } - else if (tagname.Equals("IPermission")) - { + else if (tagname.Equals("IPermission")) { // ADD: parse the permissions into types return "Access Authentication"; } - else if (tagname.Equals("registryKey")) - { + else if (tagname.Equals("registryKey")) { // capture this as the current key curRegKey = xmld.GetAttribute("keyName"); return (string)wxElementTranslator[tagname]; } - else if (tagname.Equals("registryValue")) - { + else if (tagname.Equals("registryValue")) { string tempname = xmld.GetAttribute("name"); string tempval = xmld.GetAttribute("value"); return this.parseRegistry(curRegKey + "\\" + tempname, tempval, appName); } - else - { + else { // for now, just look up this element in our translator // (this may end up returning null) return (string)wxElementTranslator[tagname]; diff --git a/base/Windows/AppReader/INFParser.cs b/base/Windows/AppReader/INFParser.cs index 5abad2f..b5c611f 100644 --- a/base/Windows/AppReader/INFParser.cs +++ b/base/Windows/AppReader/INFParser.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- namespace ManifestReader { @@ -27,7 +25,7 @@ namespace ManifestReader INFVersionSection versionSection = null; INFStringSection strSection = null; string curLine = ios.ReadLine(); - while(curLine != null) + while (curLine != null) { // get rid of the whitespace on both sides @@ -79,8 +77,7 @@ namespace ManifestReader curSection = new INFStringSection(curLine); strSection = (INFStringSection)curSection; } - else - { + else { if (INFSection.IsVersionSection(curLine)) { //Console.WriteLine("***It's a version section"); @@ -88,15 +85,13 @@ namespace ManifestReader curSection = new INFVersionSection(curLine); versionSection = (INFVersionSection)curSection; } - else - { + else { curSection = new INFSection(curLine); } } } - else - { + else { // I can only assume that the real INF parser just ignores // everything that's not in a section, given what I've seen // at the top of INF files so far @@ -123,7 +118,7 @@ namespace ManifestReader // now grab the INFStringSection and pass it to each INFSection // so they can update their strings to be correct - for(int i = 0; i < sections.Count; i++) + for (int i = 0; i < sections.Count; i++) { ((INFSection)sections[i]).UpdateStrings(strSection); } @@ -134,7 +129,7 @@ namespace ManifestReader // now that we've replaced the strings, we can safely // discover all the Registry section lines - for(int i = 0; i < sections.Count; i++) + for (int i = 0; i < sections.Count; i++) { ((INFSection)sections[i]).DiscoverRegistryLines(); } @@ -148,7 +143,7 @@ namespace ManifestReader ((INFSection)sections[i]).GetDelRegSections(delRegSections); } - for(int i = 0; i < sections.Count; i++) + for (int i = 0; i < sections.Count; i++) { string secName = ((INFSection)sections[i]).Name; if (addRegSections.ContainsKey(secName) || @@ -156,7 +151,7 @@ namespace ManifestReader { // now walk its lines and enter the registry values ArrayList lines = ((INFSection)sections[i]).Lines; - for(int j = 0; j < lines.Count; j++) + for (int j = 0; j < lines.Count; j++) { string line = (string)lines[j]; if (line.Equals("")) continue; @@ -172,7 +167,7 @@ namespace ManifestReader string regValue = INFSection.GetRegValue(line); // remove all but the innermost double-quote strings from this value int removeCount = 0; - for(int k = 0; k < regValue.Length; k++) + for (int k = 0; k < regValue.Length; k++) { if (regValue[k] == '"') { diff --git a/base/Windows/AppReader/INFSection.cs b/base/Windows/AppReader/INFSection.cs index 84e159a..729609b 100644 --- a/base/Windows/AppReader/INFSection.cs +++ b/base/Windows/AppReader/INFSection.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- namespace ManifestReader { @@ -61,13 +59,12 @@ namespace ManifestReader // strings after it int leftBracket = -1; int rightBracket = -1; - for(int i = 0; i < line.Length; i++) + for (int i = 0; i < line.Length; i++) { if (line[i] == '[') leftBracket = i; if (line[i] == ']') { - if (leftBracket < 0) - { + if (leftBracket < 0) { throw new Exception("Could not find a left bracket"); } rightBracket = i; @@ -92,7 +89,7 @@ namespace ManifestReader public void GetAddRegSections(Hashtable h) { - for(int i = 0; i < addRegSectionNames.Count; i++) + for (int i = 0; i < addRegSectionNames.Count; i++) { h[(string)addRegSectionNames[i]] = null; } @@ -100,7 +97,7 @@ namespace ManifestReader } public void GetDelRegSections(Hashtable h) { - for(int i = 0; i < delRegSectionNames.Count; i++) + for (int i = 0; i < delRegSectionNames.Count; i++) { h[(string)delRegSectionNames[i]] = null; } @@ -109,15 +106,14 @@ namespace ManifestReader public void DiscoverRegistryLines() { - for(int i = 0; i < lines.Count; i++) + for (int i = 0; i < lines.Count; i++) { string line = (string)lines[i]; if (line.ToLower().StartsWith("addreg")) { parseRegLine(line, addRegSectionNames); } - else if (line.ToLower().StartsWith("delreg")) - { + else if (line.ToLower().StartsWith("delreg")) { parseRegLine(line, delRegSectionNames); } } @@ -134,8 +130,7 @@ namespace ManifestReader // if this is the Version section, record the ClassID if (this is INFVersionSection) { - if (line.ToLower().StartsWith("classguid")) - { + if (line.ToLower().StartsWith("classguid")) { string[] components = parsedLine[0].Split(new char[] {'='}, 2); components[1] = components[1].Trim(); ((INFVersionSection)this).Guid = components[1]; @@ -150,14 +145,13 @@ namespace ManifestReader { // split on '=' and then on ',' and walk through and add the entries string[] twoHalves = line.Split(new char[] {'='}, 2); - if (twoHalves.Length != 2) - { + if (twoHalves.Length != 2) { throw new Exception(line + " is not a valid registry line"); } // 40 is a magic number. I have no idea how many there may be, // but I can't imagine there being more than 40 on a single line string[] regSections = twoHalves[1].Split(new char[] {','}, 40); - for(int i = 0; i < regSections.Length; i++) + for (int i = 0; i < regSections.Length; i++) { string rSec = regSections[i]; rSec = rSec.Trim(); @@ -169,14 +163,14 @@ namespace ManifestReader public void UpdateStrings(INFStringSection strSec) { // walk through each line and replace its '%'-quoted strings - for(int i = 0; i < lines.Count; i++) + for (int i = 0; i < lines.Count; i++) { string curLine = (string)lines[i]; string replacementLine = curLine; bool insideReplacement = false; string curReplace = ""; int percentCount = 0; - for(int j = 0; j < curLine.Length; j++) + for (int j = 0; j < curLine.Length; j++) { if (insideReplacement) { @@ -208,8 +202,7 @@ namespace ManifestReader } } } - else - { + else { percentCount = 0; } } @@ -252,8 +245,7 @@ namespace ManifestReader root = root.Trim(); string subkey = ""; string name = ""; - if (valStr.Length > 1) - { + if (valStr.Length > 1) { subkey = valStr[1]; subkey = subkey.Trim(); name = ""; @@ -274,13 +266,11 @@ namespace ManifestReader } } - else - { + else { // trim any outside quotation marks subkey = subkey.TrimEnd(new char[] {'"'}); subkey = subkey.TrimStart(new char[] {'"'}); - if (valStr.Length > 2) - { + if (valStr.Length > 2) { name = valStr[2]; name = name.Trim(); name = name.TrimEnd(new char[] {'"'}); @@ -290,12 +280,10 @@ namespace ManifestReader // we ignore registry types for now, although this could in theory bite us, // since we could get a false negative string toReturn = root; - if (!subkey.Equals("")) - { + if (!subkey.Equals("")) { toReturn += "\\" + subkey; } - if (!name.Equals("")) - { + if (!name.Equals("")) { toReturn += "\\" + name; } return toReturn; @@ -307,8 +295,7 @@ namespace ManifestReader // this is pretty much always easy, although it can be complicated // to tell what type of value this is string[] valStr = line.Split(new char[] {','}, 5); - if (valStr.Length < 5) - { + if (valStr.Length < 5) { return ""; } // the last entry is the value diff --git a/base/Windows/AppReader/INFStringSection.cs b/base/Windows/AppReader/INFStringSection.cs index 8984fe1..2e9b6f0 100644 --- a/base/Windows/AppReader/INFStringSection.cs +++ b/base/Windows/AppReader/INFStringSection.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- namespace ManifestReader { @@ -34,8 +32,7 @@ namespace ManifestReader if (line[0] > 256) return; string[] stringAndValue = line.Split(new char[] {'='}, 2); // trim final space - if (stringAndValue.Length != 2) - { + if (stringAndValue.Length != 2) { // then this is not a valid INF line. We'll just ignore it. // This behaviour seems to be more consistent with standard INF parsing, unfortunately return; diff --git a/base/Windows/AppReader/INFVersionSection.cs b/base/Windows/AppReader/INFVersionSection.cs index d31af6f..d1ef091 100644 --- a/base/Windows/AppReader/INFVersionSection.cs +++ b/base/Windows/AppReader/INFVersionSection.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- namespace ManifestReader { diff --git a/base/Windows/AppReader/ManifestParser.cs b/base/Windows/AppReader/ManifestParser.cs index 26ea0cb..88ec9ae 100644 --- a/base/Windows/AppReader/ManifestParser.cs +++ b/base/Windows/AppReader/ManifestParser.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- namespace ManifestReader { @@ -39,21 +37,17 @@ namespace ManifestReader ((ArrayList)(registryConflictTable[path])).Add(tempP); // also compute all the prefixes and put them in the appRegistryTable - if (!appRegistryTable.ContainsKey(appName)) - { + if (!appRegistryTable.ContainsKey(appName)) { appRegistryTable[appName] = new Hashtable(); } string prefix = ""; string[] pathElts = path.Split(new char[] {'\\'}, 40); - for(int i = 0; i < pathElts.Length; i++) - { + for (int i = 0; i < pathElts.Length; i++) { prefix += "\\" + pathElts[i]; - if (!((Hashtable)appRegistryTable[appName]).ContainsKey(prefix)) - { + if (!((Hashtable)appRegistryTable[appName]).ContainsKey(prefix)) { ((Hashtable)appRegistryTable[appName])[prefix] = 1; } - else - { + else { ((Hashtable)appRegistryTable[appName])[prefix] = (int)((Hashtable)appRegistryTable[appName])[prefix] + 1; } } @@ -103,17 +97,14 @@ namespace ManifestReader ((ArrayList)provideFileTable[file]).Add(appName); - if (!appFileTable.ContainsKey(appName)) - { + if (!appFileTable.ContainsKey(appName)) { appFileTable[appName] = new Hashtable(); } - if (!((Hashtable)appFileTable[appName]).ContainsKey(file)) - { + if (!((Hashtable)appFileTable[appName]).ContainsKey(file)) { ((Hashtable)appFileTable[appName])[file] = 1; } - else - { + else { ((Hashtable)appFileTable[appName])[file] = (int)((Hashtable)appFileTable[appName])[file] + 1; } return; @@ -180,8 +171,7 @@ namespace ManifestReader return "Code Linked Dynamic"; } // despite the name, if it's a DLL, we can't tell - else if (suffix.Equals("dll")) - { + else if (suffix.Equals("dll")) { return "Code"; } return "Code"; @@ -194,7 +184,7 @@ namespace ManifestReader StreamReader ios = new StreamReader(filename); Hashtable outTable = new Hashtable(); string curLine; - while((curLine = ios.ReadLine()) != null) + while ((curLine = ios.ReadLine()) != null) { // take the first word, and leave the rest int firstSpaceIndex = curLine.IndexOf(' '); @@ -224,21 +214,18 @@ namespace ManifestReader return "Code NonLinked"; } } - else if (rootname.Equals("HKCU")) - { + else if (rootname.Equals("HKCU")) { if (toplevelname.Equals("Printers")) { return "Code NonLinked"; } } - else if (rootname.Equals("HKLM")) - { + else if (rootname.Equals("HKLM")) { if (toplevelname.Equals("HARDWARE")) { return "Hardware"; } - else if (toplevelname.Equals("SYSTEM")) - { + else if (toplevelname.Equals("SYSTEM")) { return "Data Config Settings Permanent"; } } diff --git a/base/Windows/AppReader/Pair.cs b/base/Windows/AppReader/Pair.cs index ce4fe8d..cba71b1 100644 --- a/base/Windows/AppReader/Pair.cs +++ b/base/Windows/AppReader/Pair.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- namespace ManifestReader { diff --git a/base/Windows/AppReader/ReaderMain.cs b/base/Windows/AppReader/ReaderMain.cs index 230b9b3..d65bbe5 100644 --- a/base/Windows/AppReader/ReaderMain.cs +++ b/base/Windows/AppReader/ReaderMain.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- using System; using ManifestReader; @@ -24,8 +22,7 @@ namespace AppReader static double computeSpread(int[] directoryCounts) { double count = 0; - for(int i = 0; i < directoryCounts.Length; i++) - { + for (int i = 0; i < directoryCounts.Length; i++) { int curLevelCount = directoryCounts[i]; count += Math.Pow(100, -i)*(curLevelCount - 1); } @@ -57,27 +54,22 @@ namespace AppReader { mp = new CMIParser(); } - else if (suffix.Equals("inf")) - { + else if (suffix.Equals("inf")) { mp = new INFParser(); } - else if (suffix.Equals("inft")) - { + else if (suffix.Equals("inft")) { mp = new RegINFParser(); } - else - { + else { //Console.WriteLine("Unknown manifest suffix " + suffix); return; } // otherwise, try to parse it - try - { + try { mp.Parse(appName, file, new Hashtable()); } - catch (System.Xml.XmlException) - { + catch (System.Xml.XmlException) { Console.WriteLine("Could not parse " + file); } // catch(System.Exception e) @@ -106,18 +98,15 @@ namespace AppReader inHash = cmivals; mp = new CMIParser(); } - else if (suffix.Equals("inf")) - { + else if (suffix.Equals("inf")) { inHash = null; mp = new INFParser(); } - else if (suffix.Equals("inft")) - { + else if (suffix.Equals("inft")) { inHash = null; mp = new RegINFParser(); } - else - { + else { Console.WriteLine("Unknown manifest suffix " + suffix); return whash; } @@ -127,8 +116,7 @@ namespace AppReader { whash = mp.Parse(appName, file, inHash); } - catch (System.Xml.XmlException) - { + catch (System.Xml.XmlException) { Console.WriteLine("Could not parse " + file); } return whash; @@ -143,7 +131,7 @@ namespace AppReader string[] firstArray = first.Split(new char[] {'.', '_'}, 40); string[] secondArray = second.Split(new char[] {'.', '_'}, 40); Hashtable firstHash = new Hashtable(); - for(int i = 0; i < firstArray.Length; i++) + for (int i = 0; i < firstArray.Length; i++) { if (!firstHash.ContainsKey(firstArray[i])) { @@ -152,7 +140,7 @@ namespace AppReader firstHash[firstArray[i]] = (int)firstHash[firstArray[i]] + 1; } - for(int i = 0; i < secondArray.Length; i++) + for (int i = 0; i < secondArray.Length; i++) { if (firstHash.ContainsKey(secondArray[i])) { @@ -163,7 +151,7 @@ namespace AppReader ICollection vcol = firstHash.Values; IEnumerator ienum = vcol.GetEnumerator(); int sum = 0; - while(ienum.MoveNext()) + while (ienum.MoveNext()) { sum += (int)ienum.Current; } @@ -199,31 +187,26 @@ namespace AppReader #endregion bool doConflictsAndDependencies = true; - if (args[0].Equals("-s")) - { + if (args[0].Equals("-s")) { doConflictsAndDependencies = false; } #region Parse Directory - if (args[0].Equals("-r")) - { + if (args[0].Equals("-r")) { // the next argument must be a directory that // we can parse to get a list of directories that map to apps - if (args.Length != 2) - { + if (args.Length != 2) { Usage(); return; } string dir = args[1]; DirectoryInfo di = new DirectoryInfo(dir); - if (!di.Exists) - { + if (!di.Exists) { Console.WriteLine("Directory " + dir + " does not exist"); return; } DirectoryInfo[] dis = di.GetDirectories(); - for(int i = 0; i < dis.Length; i++) - { + for (int i = 0; i < dis.Length; i++) { DirectoryInfo tempDi = dis[i]; // if this directory itself contains directories, @@ -231,31 +214,29 @@ namespace AppReader DirectoryInfo[] subDirs = tempDi.GetDirectories(); if (subDirs.Length > 0) { - for(int j = 0; j < subDirs.Length; j++) + for (int j = 0; j < subDirs.Length; j++) { DirectoryInfo subDir = subDirs[j]; string appName = tempDi.Name + " " + subDir.Name; FileInfo[] fis = subDir.GetFiles(); - for(int k = 0; k < fis.Length; k++) + for (int k = 0; k < fis.Length; k++) { parseFile(appName, subDir.FullName + "\\" + fis[k].Name); } } } - else - { + else { string appName = tempDi.Name; FileInfo[] fis = tempDi.GetFiles(); - for(int j = 0; j < fis.Length; j++) + for (int j = 0; j < fis.Length; j++) { parseFile(appName, tempDi.FullName + "\\" + fis[j].Name); } } } } - else if (args[0].Equals("-d") || args[0].Equals("-s")) - { + else if (args[0].Equals("-d") || args[0].Equals("-s")) { // the next argument must be a directory that // contains a set of .wxs files for us to parse if (args.Length != 2) @@ -277,7 +258,7 @@ namespace AppReader // Console.WriteLine("Got here with size " + fis.Length); // for each manifest file - for(int i = 0; i < fis.Length; i++) + for (int i = 0; i < fis.Length; i++) { Console.WriteLine(++count); // grab a new app tree @@ -303,8 +284,7 @@ namespace AppReader } #endregion #region Parse Single File - else - { + else { if (args.Length != 1) { Usage(); @@ -331,24 +311,20 @@ namespace AppReader // this table will be used to reduce the size of the generated file Hashtable appNameTable = new Hashtable(); int nameCount = 0; - if (doConflictsAndDependencies) - { + if (doConflictsAndDependencies) { // now walk the Hashtable and dump any conflicts Hashtable conflictTable = ManifestParser.registryConflictTable; IDictionaryEnumerator iDictEnum = conflictTable.GetEnumerator(); - while(iDictEnum.MoveNext()) - { - if (((ArrayList)iDictEnum.Value).Count > 1) - { + while (iDictEnum.MoveNext()) { + if (((ArrayList)iDictEnum.Value).Count > 1) { // then we might have a conflict ArrayList confs = ((ArrayList)iDictEnum.Value); string val = (string)((Pair)confs[1]).First; string appname = (string)((Pair)confs[1]).Second; bool founddifference = false; bool foundtwoapps = false; - for(int i = 0; i < confs.Count; i++) - { + for (int i = 0; i < confs.Count; i++) { Pair outp = (Pair)confs[i]; string newval = (string)((Pair)confs[i]).First; string newappname = (string)((Pair)confs[i]).Second; @@ -380,12 +356,10 @@ namespace AppReader Console.WriteLine("C: " + ((string)iDictEnum.Key) + " : " + ((ArrayList)iDictEnum.Value).Count); Hashtable confHash = new Hashtable(); - for(int i = 0; i < confs.Count; i++) - { + for (int i = 0; i < confs.Count; i++) { Pair outp = (Pair)confs[i]; string confAppName = (string)outp.Second; - if (!appConflicts.ContainsKey(confAppName)) - { + if (!appConflicts.ContainsKey(confAppName)) { appConflicts[confAppName] = new Hashtable(); } // write the conflicting app into the hash with its value @@ -396,14 +370,12 @@ namespace AppReader } IDictionaryEnumerator iconf1 = confHash.GetEnumerator(); - while(iconf1.MoveNext()) - { + while (iconf1.MoveNext()) { string confAppName = (string)iconf1.Key; string confVal1 = (string)iconf1.Value; Hashtable htable = (Hashtable)appConflicts[confAppName]; IDictionaryEnumerator iconf2 = confHash.GetEnumerator(); - while(iconf2.MoveNext()) - { + while (iconf2.MoveNext()) { string curAppName = (string)iconf2.Key; string confVal2 = (string)iconf2.Value; @@ -411,17 +383,14 @@ namespace AppReader if (curAppName.Equals(confAppName)) continue; // don't add this conflict if they have the same value on this key - if (confVal2 == null && confVal1 == null) - { + if (confVal2 == null && confVal1 == null) { continue; } - else if (confVal2 != null && confVal1 != null && confVal2.ToLower().Equals(confVal1.ToLower())) - { + else if (confVal2 != null && confVal1 != null && confVal2.ToLower().Equals(confVal1.ToLower())) { continue; } - if (!htable.ContainsKey(curAppName)) - { + if (!htable.ContainsKey(curAppName)) { htable[curAppName] = 0; } htable[curAppName] = (int)htable[curAppName] + 1; @@ -432,13 +401,11 @@ namespace AppReader } IDictionaryEnumerator iDE = appConflicts.GetEnumerator(); - while(iDE.MoveNext()) - { + while (iDE.MoveNext()) { string firstName = (string)iDE.Key; Hashtable confvals = (Hashtable)iDE.Value; IDictionaryEnumerator iDEInner = confvals.GetEnumerator(); - while(iDEInner.MoveNext()) - { + while (iDEInner.MoveNext()) { string secondName = (string)iDEInner.Key; int strength = (int)iDEInner.Value; @@ -457,25 +424,20 @@ namespace AppReader // We'll walk through the ManifestParser.provideFileTable // and ManifestParser.requireFileTable IDictionaryEnumerator ireq = ManifestParser.requiresFileTable.GetEnumerator(); - while(ireq.MoveNext()) - { + while (ireq.MoveNext()) { string requiredFile = (string)ireq.Key; ArrayList reqArray = (ArrayList)ireq.Value; - for(int i = 0; i < reqArray.Count; i++) - { + for (int i = 0; i < reqArray.Count; i++) { string requiringAppName = (string)reqArray[i]; - if (!ManifestParser.provideFileTable.ContainsKey(requiredFile)) - { + if (!ManifestParser.provideFileTable.ContainsKey(requiredFile)) { // do nothing about it for now //throw new Exception("File " + requiredFile + " is required but not provided!"); } - else - { + else { // otherwise, we'll walk through all the apps that provide // this file and print a dependency between them ArrayList provArray = (ArrayList)ManifestParser.provideFileTable[requiredFile]; - for(int j = 0; j < provArray.Count; j++) - { + for (int j = 0; j < provArray.Count; j++) { string providingAppName = (string)provArray[j]; Console.WriteLine(getAppNumber(requiringAppName, appNameTable, ref nameCount) + "\t" + getAppNumber(providingAppName, appNameTable, ref nameCount)); @@ -493,37 +455,32 @@ namespace AppReader // we use the appFileTable for each app to compute this IDictionaryEnumerator appFileEnum = ManifestParser.appFileTable.GetEnumerator(); - while(appFileEnum.MoveNext()) - { + while (appFileEnum.MoveNext()) { string appName = (string)appFileEnum.Key; Hashtable appFiles = (Hashtable)appFileEnum.Value; // walk through the list and figure out how many unique prefixes there are of each length IDictionaryEnumerator lengthEnum = appFiles.GetEnumerator(); int max = 0; - while(lengthEnum.MoveNext()) - { + while (lengthEnum.MoveNext()) { string path = (string)lengthEnum.Key; string[] pathElts = path.Split(new char[] {'\\'}, 40); // since the paths all start with \ and // we don't want to count the empty first pathElt int length = pathElts.Length - 1; - if (length > max) - { + if (length > max) { max = length; } } int[] lengths = new int[max]; - for(int i = 0; i < lengths.Length; i++) - { + for (int i = 0; i < lengths.Length; i++) { lengths[i] = 0; } lengthEnum.Reset(); - while(lengthEnum.MoveNext()) - { + while (lengthEnum.MoveNext()) { string path = (string)lengthEnum.Key; string[] pathElts = path.Split(new char[] {'\\'}, 40); // since the paths all start with \ and @@ -533,7 +490,7 @@ namespace AppReader lengths[length - 1]++; } - for(int i = 0; i < lengths.Length; i++) + for (int i = 0; i < lengths.Length; i++) { if (lengths[i] == 0) { @@ -543,16 +500,13 @@ namespace AppReader double spread = computeSpread(lengths); // look up the real name - if (ManifestParser.nameTable.ContainsKey(appName)) - { + if (ManifestParser.nameTable.ContainsKey(appName)) { appName = (string)ManifestParser.nameTable[appName]; } string strSpread = ""; - for(int i = 0; i < lengths.Length; i++) - { - if (i != 0) - { + for (int i = 0; i < lengths.Length; i++) { + if (i != 0) { strSpread += " "; } strSpread += lengths[i]; @@ -568,37 +522,32 @@ namespace AppReader // we use the appFileTable for each app to compute this IDictionaryEnumerator appRegistryEnum = ManifestParser.appRegistryTable.GetEnumerator(); - while(appRegistryEnum.MoveNext()) - { + while (appRegistryEnum.MoveNext()) { string appName = (string)appRegistryEnum.Key; Hashtable appRegs = (Hashtable)appRegistryEnum.Value; // walk through the list and figure out how many unique prefixes there are of each length IDictionaryEnumerator lengthEnum = appRegs.GetEnumerator(); int max = 0; - while(lengthEnum.MoveNext()) - { + while (lengthEnum.MoveNext()) { string path = (string)lengthEnum.Key; string[] pathElts = path.Split(new char[] {'\\'}, 40); // since the paths all start with \ and // we don't want to count the empty first pathElt int length = pathElts.Length - 1; - if (length > max) - { + if (length > max) { max = length; } } int[] lengths = new int[max]; - for(int i = 0; i < lengths.Length; i++) - { + for (int i = 0; i < lengths.Length; i++) { lengths[i] = 0; } lengthEnum.Reset(); - while(lengthEnum.MoveNext()) - { + while (lengthEnum.MoveNext()) { string path = (string)lengthEnum.Key; string[] pathElts = path.Split(new char[] {'\\'}, 40); // since the paths all start with \ and @@ -607,7 +556,7 @@ namespace AppReader // this -1 is because the lengths array is 0-based lengths[length - 1]++; } - for(int i = 0; i < lengths.Length; i++) + for (int i = 0; i < lengths.Length; i++) { if (lengths[i] == 0) { @@ -617,16 +566,13 @@ namespace AppReader double spread = computeSpread(lengths); // look up the real name - if (ManifestParser.nameTable.ContainsKey(appName)) - { + if (ManifestParser.nameTable.ContainsKey(appName)) { appName = (string)ManifestParser.nameTable[appName]; } string strSpread = ""; - for(int i = 0; i < lengths.Length; i++) - { - if (i != 0) - { + for (int i = 0; i < lengths.Length; i++) { + if (i != 0) { strSpread += " "; } strSpread += lengths[i]; @@ -644,7 +590,7 @@ namespace AppReader // walk through the ManifestParser.customActionsTable IDictionaryEnumerator customEnum = ManifestParser.customActionsTable.GetEnumerator(); - while(customEnum.MoveNext()) + while (customEnum.MoveNext()) { string appName = (string)customEnum.Key; int customCount = (int)customEnum.Value; @@ -659,13 +605,13 @@ namespace AppReader // walk through the ManifestParser.fileTypeTable IDictionaryEnumerator fileEnum = ManifestParser.fileTypeTable.GetEnumerator(); - while(fileEnum.MoveNext()) + while (fileEnum.MoveNext()) { int uniqueFileTypes = 0; string appName = (string)fileEnum.Key; Hashtable exts = (Hashtable)fileEnum.Value; IDictionaryEnumerator extEnum = exts.GetEnumerator(); - while(extEnum.MoveNext()) + while (extEnum.MoveNext()) { uniqueFileTypes++; string extension = (string)extEnum.Key; @@ -683,21 +629,18 @@ namespace AppReader // now write out the name table IDictionaryEnumerator iDENT = appNameTable.GetEnumerator(); - while(iDENT.MoveNext()) + while (iDENT.MoveNext()) { string appName = ""; // look up the real name - if (ManifestParser.nameTable.ContainsKey((string)iDENT.Key)) - { + if (ManifestParser.nameTable.ContainsKey((string)iDENT.Key)) { appName = (string)ManifestParser.nameTable[(string)iDENT.Key]; } - if (appName.Equals("")) - { + if (appName.Equals("")) { Console.WriteLine((string)iDENT.Key + " " + (int)iDENT.Value); } - else - { + else { Console.WriteLine((string)iDENT.Key + " " + appName + " " + (int)iDENT.Value); } } diff --git a/base/Windows/AppReader/RegINFParser.cs b/base/Windows/AppReader/RegINFParser.cs index c88a396..5388540 100644 --- a/base/Windows/AppReader/RegINFParser.cs +++ b/base/Windows/AppReader/RegINFParser.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- namespace ManifestReader { @@ -22,7 +20,7 @@ namespace ManifestReader Hashtable outTable = new Hashtable(); StreamReader ios = new StreamReader(file); string curLine = ios.ReadLine(); - while(curLine != null) + while (curLine != null) { // each line is just a simple registry line with no strings or substitution string regKey = INFSection.GetRegKey(curLine, ""); diff --git a/base/Windows/AppReader/WiXParser.cs b/base/Windows/AppReader/WiXParser.cs index 9fa5a43..0be9537 100644 --- a/base/Windows/AppReader/WiXParser.cs +++ b/base/Windows/AppReader/WiXParser.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- namespace ManifestReader { @@ -35,8 +33,7 @@ namespace ManifestReader } return ""; } - else - { + else { if (xmld.Name.Equals("CustomAction")) { // add this filename to the custom action count @@ -50,8 +47,7 @@ namespace ManifestReader setName(file, productName); return ""; } - else if (xmld.Name.Equals("Directory")) - { + else if (xmld.Name.Equals("Directory")) { string dirName = xmld.GetAttribute("LongName"); if (dirName == null) { @@ -72,8 +68,7 @@ namespace ManifestReader // add this string to the directory name currentDir += "\\" + dirName; } - else - { + else { if (dirName != null) { // otherwise, register it and move on @@ -82,8 +77,7 @@ namespace ManifestReader } return ""; } - else if (xmld.Name.Equals("File")) - { + else if (xmld.Name.Equals("File")) { string filename = xmld.GetAttribute("LongName"); if (filename == null) { @@ -100,21 +94,17 @@ namespace ManifestReader { return getCodeType(suffix); } - else if (isImageSuffix(suffix)) - { + else if (isImageSuffix(suffix)) { return "Data File Display UI"; } - else if (isFontSuffix(suffix)) - { + else if (isFontSuffix(suffix)) { return "Data File Display Fonts"; } - else - { + else { return "Data File"; } } - else if (xmld.Name.Equals("Registry")) - { + else if (xmld.Name.Equals("Registry")) { // parse the registry prefix string rootname = xmld.GetAttribute("Root"); string path = xmld.GetAttribute("Key"); @@ -128,8 +118,7 @@ namespace ManifestReader return parseRegistry(fullpath + "\\" + valname, val, appName); } - else - { + else { // look up this element in our translator // (this may end up returning null) return (string)wxElementTranslator[xmld.Name]; diff --git a/base/Windows/AppReader/XmlManifestParser.cs b/base/Windows/AppReader/XmlManifestParser.cs index c13412e..8f9f98a 100644 --- a/base/Windows/AppReader/XmlManifestParser.cs +++ b/base/Windows/AppReader/XmlManifestParser.cs @@ -1,10 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Microsoft Research Singularity +// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // -/////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------- namespace ManifestReader { @@ -21,17 +19,15 @@ namespace ManifestReader { Hashtable outTable = new Hashtable(); StreamReader ios; - try - { + try { ios = new StreamReader(wxsFile); } - catch (System.IO.FileNotFoundException) - { + catch (System.IO.FileNotFoundException) { return outTable; } XmlTextReader xmld = new XmlTextReader(ios); - while(xmld.Read()) + while (xmld.Read()) { string name = xmld.LocalName; XmlNodeType xnt = xmld.NodeType; @@ -48,8 +44,7 @@ namespace ManifestReader { outTable[path] = (int)outTable[path] + 1; } - else - { + else { outTable[path] = 1; } } diff --git a/base/Windows/Benchmarks/CreateProcess/Child.c b/base/Windows/Benchmarks/CreateProcess/Child.c new file mode 100644 index 0000000..dd4262e --- /dev/null +++ b/base/Windows/Benchmarks/CreateProcess/Child.c @@ -0,0 +1,27 @@ +#include +#include + +#define GetCycleCount() __rdtsc() +ULONG64 +__rdtsc(VOID); +#pragma intrinsic(__rdtsc) + +int __cdecl +main() +{ + ULONG64 n = GetCycleCount(); + + int r = -1; + FILE *f = fopen("result.txt", "w"); + if (f != NULL) + { + // + // File format is tied to CreateProcess.c - do not + // change here without updating there + // + r = fprintf(f, "child start at:\n%llu\n", n); + fclose(f); + } + + return r; +} diff --git a/base/Windows/Benchmarks/CreateProcess/CreateProcess.c b/base/Windows/Benchmarks/CreateProcess/CreateProcess.c new file mode 100644 index 0000000..79b5ca3 --- /dev/null +++ b/base/Windows/Benchmarks/CreateProcess/CreateProcess.c @@ -0,0 +1,146 @@ +// +// CreateProcess.c - A benchmark program +// +// Copyright Microsoft Corporation. +// All rights reserved. +// + +#include +#include + +LARGE_INTEGER frequency; + +#define GetCycleCount() __rdtsc() +ULONG64 +__rdtsc(VOID); +#pragma intrinsic(__rdtsc) + +void ErrorExit(char *function) +{ + VOID *message; + DWORD error = GetLastError(); + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &message, + 0, NULL ); + + printf("%s failed with error %d: %s", function, error, message); + + LocalFree(message); + ExitProcess(error); +} + +int GetChildStartTime(ULONG64* result) +{ + FILE* f = fopen("result.txt", "r"); + if (f != NULL) + { + char dummy[512]; + fgets(dummy, 512, f); + fscanf(f, "%I64u", result); + fclose (f); + return 0; + } + ErrorExit("fopen"); + return -1; +} + +void TimeProcess(char *imageName, BOOL fGetChildExecutionStart) +{ + BOOL result; + STARTUPINFO startInfo; + PROCESS_INFORMATION procInfo; + ULONG64 baseline, created, started, exited, childup; + + // + // The startup info can specify the window station, desktop, various + // window parameters (title, position, etc) and standard handles for + // the new process. + // + ZeroMemory(&startInfo, sizeof(startInfo)); + startInfo.cb = sizeof(startInfo); + + // + // Time process creation. + // + baseline = GetCycleCount(); + + result = CreateProcess(imageName, // Application name to start. + imageName, // Command line for new process. + NULL, // ? child process can inherit this process. + NULL, // ? child threads can inherit this process. + FALSE, // Whether new process inherits our handles. + CREATE_SUSPENDED // Process creation flags. + | DETACHED_PROCESS | CREATE_NO_WINDOW, + NULL, // Environment. + NULL, // Current directory. + &startInfo, // Startup info. + &procInfo); // Out: Process info. + + if (!result) { + // + // Failed to create process :-( + // + ErrorExit("CreateProcess"); + } + + // + // Process creation was successful! + // + created = GetCycleCount(); + + // + // Time process start. + // + if (ResumeThread(procInfo.hThread) == -1) { + ErrorExit("ResumeThread"); + } + + started = GetCycleCount(); + + // + // Time process execution. + // + WaitForSingleObject(procInfo.hProcess, INFINITE); + exited = GetCycleCount(); + + printf("\n\nTested process: %s\n", imageName); + printf("Time to create process: %llu cycles\n", + created - baseline); + printf("Time to start process: %llu cycles\n", + started - created); + printf("Time to run process: %llu cycles\n", + exited - started); + printf("Total time: %llu cycles\n", + exited - baseline); + printf("CreateProcess started at %llu\n", baseline); + + if (fGetChildExecutionStart && GetChildStartTime(&childup) == 0) + { + printf("CreateProcess to first child instruction: %llu\n", + childup - baseline); + } +} + +int __cdecl +main(int argc, char **argv) +{ + printf("\nProcess creation test\n\n"); + + // + // The frequency of the high-resolution performance counter cannot change + // while the system is running. + // + if (QueryPerformanceFrequency(&frequency) == 0) { + ErrorExit("QueryPerformanceFrequency"); + } + printf("Performance Counter Frequency = %d counts/second\n\n", frequency); + + TimeProcess("MinProcess.exe", FALSE); + TimeProcess("Child.exe", TRUE); +} diff --git a/base/Windows/Benchmarks/CreateProcess/Makefile b/base/Windows/Benchmarks/CreateProcess/Makefile new file mode 100644 index 0000000..4a7d2b0 --- /dev/null +++ b/base/Windows/Benchmarks/CreateProcess/Makefile @@ -0,0 +1,59 @@ +############################################################################## +# +# Microsoft Research Singularity +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# File: Windows\Benchmarks\CreateProcess\Makefile +# +############################################################################## + +OBJROOT=..\obj +!INCLUDE "$(SINGULARITY_ROOT)/Makefile.inc" + +CFLAGS= $(CFLAGS) /Ox /I..\..\inc + +HOST_LINKFLAGS=$(HOST_LINKFLAGS) /nod /libpath:..\..\lib /subsystem:console + +LIBS=\ + user32.lib \ + kernel32.lib \ + libcmt.lib \ + +############################################################################## + +all: $(OBJDIR) \ + $(OBJDIR)\CreateProcess.exe \ + $(OBJDIR)\Child.exe \ + $(OBJDIR)\MinProcess.exe + +clean: + @-del /q $(OBJDIR)\CreateProcess.* $(OBJDIR)\Child.* $(OBJDIR)\MinProcess.* *~ 2>nul + @-rmdir $(OBJDIR) 2>nul + @-rmdir $(OBJROOT) 2>nul + +$(OBJDIR): + -mkdir $(OBJDIR) + +{.}.c{$(OBJDIR)}.obj: + cl /c $(CFLAGS) /Fo$@ $< + +$(OBJDIR)\CreateProcess.obj: CreateProcess.c + +$(OBJDIR)\Child.obj: Child.c + +$(OBJDIR)\MinProcess.obj: MinProcess.c + +OBJS = \ + $(OBJDIR)\CreateProcess.obj \ + +$(OBJDIR)\CreateProcess.exe: $(OBJS) + link $(HOST_LINKFLAGS) /out:$@ $** $(LIBS) + +$(OBJDIR)\Child.exe: $(OBJDIR)\Child.obj + link $(HOST_LINKFLAGS) /out:$@ $** $(LIBS) + +$(OBJDIR)\MinProcess.exe: $(OBJDIR)\MinProcess.obj + link $(HOST_LINKFLAGS) /out:$@ $** $(LIBS) + +################################################################# End of File. diff --git a/base/Windows/Benchmarks/CreateProcess/MinProcess.c b/base/Windows/Benchmarks/CreateProcess/MinProcess.c new file mode 100644 index 0000000..6c21ca5 --- /dev/null +++ b/base/Windows/Benchmarks/CreateProcess/MinProcess.c @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/base/Windows/Benchmarks/Hello/Hello.c b/base/Windows/Benchmarks/Hello/Hello.c new file mode 100644 index 0000000..c6d10af --- /dev/null +++ b/base/Windows/Benchmarks/Hello/Hello.c @@ -0,0 +1,14 @@ +// +// Hello.c - Hello World +// +// Copyright Microsoft Corporation. +// All rights reserved. +// + +#include + +int __cdecl +main(int argc, char **argv) +{ + printf("Hello World!\n"); +} diff --git a/base/Windows/Benchmarks/Hello/Makefile b/base/Windows/Benchmarks/Hello/Makefile new file mode 100644 index 0000000..19d9481 --- /dev/null +++ b/base/Windows/Benchmarks/Hello/Makefile @@ -0,0 +1,47 @@ +############################################################################## +# +# Microsoft Research Singularity +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# File: Windows\Benchmarks\Hello\Makefile +# +############################################################################## + +OBJROOT=..\obj +!INCLUDE "$(SINGULARITY_ROOT)/Makefile.inc" + +CFLAGS=$(CFLAGS) /Od /I..\..\inc \ + /Fd$(OBJDIR)\Hello.pdb + +HOST_LINKFLAGS=$(HOST_LINKFLAGS) /nod /libpath:..\..\lib /subsystem:console + +LIBS=\ + user32.lib \ + kernel32.lib \ + libcmt.lib \ + +############################################################################## + +all: $(OBJDIR) $(OBJDIR)\Hello.exe + +clean: + @-del /q $(OBJDIR)\Hello.* *~ 2>nul + @-rmdir $(OBJDIR) 2>nul + @-rmdir $(OBJROOT) 2>nul + +$(OBJDIR): + -mkdir $(OBJDIR) + +{.}.c{$(OBJDIR)}.obj: + cl /c $(CFLAGS) /Fo$@ $< + +$(OBJDIR)\Hello.obj: Hello.c + +OBJS = \ + $(OBJDIR)\Hello.obj \ + +$(OBJDIR)\Hello.exe: $(OBJS) + link $(HOST_LINKFLAGS) /out:$@ $** $(LIBS) + +################################################################# End of File. diff --git a/base/Windows/Benchmarks/Lpc/Makefile b/base/Windows/Benchmarks/Lpc/Makefile new file mode 100644 index 0000000..beb3e23 --- /dev/null +++ b/base/Windows/Benchmarks/Lpc/Makefile @@ -0,0 +1,18 @@ +all: uclient.exe userver.exe + +CFLAGS=$(CFLAGS) /Ox /D_X86_ +LIBS=ntdll.lib +HOST_LINKFLAGS= $(HOST_LINKFLAGS) + +{.}.c.obj: + cl /c $(CFLAGS) /Fo$@ $< + +{.}.obj.exe: + link $(HOST_LINKFLAGS) /out:$@ $** $(LIBS) + +uclient.obj: uclient.c ulpc.h + +userver.obj: userver.c ulpc.h + +clean: + -del /q *.obj *.exe *.pdb diff --git a/base/Windows/Benchmarks/Lpc/Readme.txt b/base/Windows/Benchmarks/Lpc/Readme.txt new file mode 100644 index 0000000..fb83f6e --- /dev/null +++ b/base/Windows/Benchmarks/Lpc/Readme.txt @@ -0,0 +1,7 @@ +The files in this directory are modified versions of sample code +for local procedure call (lpc). LPC is a fast IPC mechanism +akin to ports in Mach. + +Compiling this test requires the DDK for ntdll.lib and the NT +source tree for nt.h. The file setenv.cmd needs to be edited to +use at the relevant directories. diff --git a/base/Windows/Benchmarks/Lpc/data/results.xls b/base/Windows/Benchmarks/Lpc/data/results.xls new file mode 100644 index 0000000..89628d0 Binary files /dev/null and b/base/Windows/Benchmarks/Lpc/data/results.xls differ diff --git a/base/Windows/Benchmarks/Lpc/data/run1.log b/base/Windows/Benchmarks/Lpc/data/run1.log new file mode 100644 index 0000000..73dd339 --- /dev/null +++ b/base/Windows/Benchmarks/Lpc/data/run1.log @@ -0,0 +1,16 @@ +Message Bytes 0 Iterations 10000 Cycles 45772547 Cycles/Iteration 4577 +Message Bytes 4 Iterations 10000 Cycles 46083963 Cycles/Iteration 4608 +Message Bytes 8 Iterations 10000 Cycles 46230872 Cycles/Iteration 4623 +Message Bytes 12 Iterations 10000 Cycles 46954614 Cycles/Iteration 4695 +Message Bytes 16 Iterations 10000 Cycles 45672222 Cycles/Iteration 4567 +Message Bytes 20 Iterations 10000 Cycles 45786516 Cycles/Iteration 4578 +Message Bytes 24 Iterations 10000 Cycles 45919581 Cycles/Iteration 4591 +Message Bytes 28 Iterations 10000 Cycles 45947980 Cycles/Iteration 4594 +Message Bytes 32 Iterations 10000 Cycles 46060462 Cycles/Iteration 4606 +Message Bytes 36 Iterations 10000 Cycles 45935596 Cycles/Iteration 4593 +Message Bytes 40 Iterations 10000 Cycles 46172961 Cycles/Iteration 4617 +Message Bytes 44 Iterations 10000 Cycles 46256417 Cycles/Iteration 4625 +Message Bytes 48 Iterations 10000 Cycles 46549149 Cycles/Iteration 4654 +Message Bytes 52 Iterations 10000 Cycles 46808895 Cycles/Iteration 4680 +Message Bytes 56 Iterations 10000 Cycles 46838500 Cycles/Iteration 4683 +Message Bytes 60 Iterations 10000 Cycles 46795580 Cycles/Iteration 4679 diff --git a/base/Windows/Benchmarks/Lpc/data/run2.log b/base/Windows/Benchmarks/Lpc/data/run2.log new file mode 100644 index 0000000..7e7447b --- /dev/null +++ b/base/Windows/Benchmarks/Lpc/data/run2.log @@ -0,0 +1,16 @@ +Message Bytes 0 Iterations 10000 Cycles 45654014 Cycles/Iteration 4565 +Message Bytes 4 Iterations 10000 Cycles 45795532 Cycles/Iteration 4579 +Message Bytes 8 Iterations 10000 Cycles 46324733 Cycles/Iteration 4632 +Message Bytes 12 Iterations 10000 Cycles 47032123 Cycles/Iteration 4703 +Message Bytes 16 Iterations 10000 Cycles 45346854 Cycles/Iteration 4534 +Message Bytes 20 Iterations 10000 Cycles 45212723 Cycles/Iteration 4521 +Message Bytes 24 Iterations 10000 Cycles 45740983 Cycles/Iteration 4574 +Message Bytes 28 Iterations 10000 Cycles 45831064 Cycles/Iteration 4583 +Message Bytes 32 Iterations 10000 Cycles 46106172 Cycles/Iteration 4610 +Message Bytes 36 Iterations 10000 Cycles 46319836 Cycles/Iteration 4631 +Message Bytes 40 Iterations 10000 Cycles 46279736 Cycles/Iteration 4627 +Message Bytes 44 Iterations 10000 Cycles 46836193 Cycles/Iteration 4683 +Message Bytes 48 Iterations 10000 Cycles 46477783 Cycles/Iteration 4647 +Message Bytes 52 Iterations 10000 Cycles 46751123 Cycles/Iteration 4675 +Message Bytes 56 Iterations 10000 Cycles 46658067 Cycles/Iteration 4665 +Message Bytes 60 Iterations 10000 Cycles 46896746 Cycles/Iteration 4689 diff --git a/base/Windows/Benchmarks/Lpc/data/run3.log b/base/Windows/Benchmarks/Lpc/data/run3.log new file mode 100644 index 0000000..21f5af8 --- /dev/null +++ b/base/Windows/Benchmarks/Lpc/data/run3.log @@ -0,0 +1,16 @@ +Message Bytes 0 Iterations 10000 Cycles 47120020 Cycles/Iteration 4712 +Message Bytes 4 Iterations 10000 Cycles 47345718 Cycles/Iteration 4734 +Message Bytes 8 Iterations 10000 Cycles 46634430 Cycles/Iteration 4663 +Message Bytes 12 Iterations 10000 Cycles 46930761 Cycles/Iteration 4693 +Message Bytes 16 Iterations 10000 Cycles 45948201 Cycles/Iteration 4594 +Message Bytes 20 Iterations 10000 Cycles 45995122 Cycles/Iteration 4599 +Message Bytes 24 Iterations 10000 Cycles 46206408 Cycles/Iteration 4620 +Message Bytes 28 Iterations 10000 Cycles 46139676 Cycles/Iteration 4613 +Message Bytes 32 Iterations 10000 Cycles 46340912 Cycles/Iteration 4634 +Message Bytes 36 Iterations 10000 Cycles 46210653 Cycles/Iteration 4621 +Message Bytes 40 Iterations 10000 Cycles 46424560 Cycles/Iteration 4642 +Message Bytes 44 Iterations 10000 Cycles 46484998 Cycles/Iteration 4648 +Message Bytes 48 Iterations 10000 Cycles 46946738 Cycles/Iteration 4694 +Message Bytes 52 Iterations 10000 Cycles 47015650 Cycles/Iteration 4701 +Message Bytes 56 Iterations 10000 Cycles 47156832 Cycles/Iteration 4715 +Message Bytes 60 Iterations 10000 Cycles 46962913 Cycles/Iteration 4696 diff --git a/base/Windows/Benchmarks/Lpc/data/run4.log b/base/Windows/Benchmarks/Lpc/data/run4.log new file mode 100644 index 0000000..ea11cb8 --- /dev/null +++ b/base/Windows/Benchmarks/Lpc/data/run4.log @@ -0,0 +1,16 @@ +Message Bytes 0 Iterations 10000 Cycles 46680899 Cycles/Iteration 4668 +Message Bytes 4 Iterations 10000 Cycles 46850012 Cycles/Iteration 4685 +Message Bytes 8 Iterations 10000 Cycles 47124951 Cycles/Iteration 4712 +Message Bytes 12 Iterations 10000 Cycles 48027123 Cycles/Iteration 4802 +Message Bytes 16 Iterations 10000 Cycles 46805920 Cycles/Iteration 4680 +Message Bytes 20 Iterations 10000 Cycles 46642578 Cycles/Iteration 4664 +Message Bytes 24 Iterations 10000 Cycles 49381158 Cycles/Iteration 4938 +Message Bytes 28 Iterations 10000 Cycles 47072422 Cycles/Iteration 4707 +Message Bytes 32 Iterations 10000 Cycles 47038246 Cycles/Iteration 4703 +Message Bytes 36 Iterations 10000 Cycles 47270407 Cycles/Iteration 4727 +Message Bytes 40 Iterations 10000 Cycles 47189922 Cycles/Iteration 4718 +Message Bytes 44 Iterations 10000 Cycles 47518672 Cycles/Iteration 4751 +Message Bytes 48 Iterations 10000 Cycles 47588465 Cycles/Iteration 4758 +Message Bytes 52 Iterations 10000 Cycles 47545915 Cycles/Iteration 4754 +Message Bytes 56 Iterations 10000 Cycles 47540010 Cycles/Iteration 4754 +Message Bytes 60 Iterations 10000 Cycles 47447231 Cycles/Iteration 4744 diff --git a/base/Windows/Benchmarks/Lpc/data/run5.log b/base/Windows/Benchmarks/Lpc/data/run5.log new file mode 100644 index 0000000..f591ce3 --- /dev/null +++ b/base/Windows/Benchmarks/Lpc/data/run5.log @@ -0,0 +1,16 @@ +Message Bytes 0 Iterations 10000 Cycles 46844732 Cycles/Iteration 4684 +Message Bytes 4 Iterations 10000 Cycles 46602019 Cycles/Iteration 4660 +Message Bytes 8 Iterations 10000 Cycles 46968788 Cycles/Iteration 4696 +Message Bytes 12 Iterations 10000 Cycles 47517690 Cycles/Iteration 4751 +Message Bytes 16 Iterations 10000 Cycles 46560655 Cycles/Iteration 4656 +Message Bytes 20 Iterations 10000 Cycles 46622352 Cycles/Iteration 4662 +Message Bytes 24 Iterations 10000 Cycles 46700854 Cycles/Iteration 4670 +Message Bytes 28 Iterations 10000 Cycles 47073382 Cycles/Iteration 4707 +Message Bytes 32 Iterations 10000 Cycles 46864034 Cycles/Iteration 4686 +Message Bytes 36 Iterations 10000 Cycles 47083826 Cycles/Iteration 4708 +Message Bytes 40 Iterations 10000 Cycles 47007909 Cycles/Iteration 4700 +Message Bytes 44 Iterations 10000 Cycles 47344928 Cycles/Iteration 4734 +Message Bytes 48 Iterations 10000 Cycles 47237921 Cycles/Iteration 4723 +Message Bytes 52 Iterations 10000 Cycles 47496390 Cycles/Iteration 4749 +Message Bytes 56 Iterations 10000 Cycles 47591646 Cycles/Iteration 4759 +Message Bytes 60 Iterations 10000 Cycles 47535824 Cycles/Iteration 4753 diff --git a/base/Windows/Benchmarks/Lpc/setenv.cmd b/base/Windows/Benchmarks/Lpc/setenv.cmd new file mode 100644 index 0000000..7f56b27 --- /dev/null +++ b/base/Windows/Benchmarks/Lpc/setenv.cmd @@ -0,0 +1,15 @@ +@echo off + +set DDKDIR=c:\WINDDK\2600.1106 + +set NTSRC=c:\ntsrc +set INCLUDE=%INCLUDE%;%NTSRC%\public\sdk\inc +set LIB=%LIB%;%DDKDIR%\lib\wxp\i386 + +doskey VC=cd "C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7" +doskey LPC=cd c:\ntsrc\base\ntos\lpc +doskey PUB=cd c:\ntsrc\base\published +doskey DDK=cd %DDKDIR% +doskey WORK=cd c:\work\lrpc\example2 +doskey ALIAS=echo "VC | LPC | PUB | DDK | WORK" + diff --git a/base/Windows/Benchmarks/Lpc/uclient.c b/base/Windows/Benchmarks/Lpc/uclient.c new file mode 100644 index 0000000..6817e24 --- /dev/null +++ b/base/Windows/Benchmarks/Lpc/uclient.c @@ -0,0 +1,285 @@ +//++ +// +//Copyright (c) Microsoft Corporation +// +//Abstract: +// +//User Mode Client Test program for the LPC subcomponent of the NTOS project +// +//-- + +// +// Read the processor's time stamp counter register +// + + +#include "ulpc.h" + +_inline _declspec( naked ) UINT64 RDTSC() +{ + __asm { + rdtsc; + ret; + } +} + +#define GetCycleCount() RDTSC() + + +#define BENCH_ITERATIONS 10000 + +#define MAX_CLIENT_PROCESSES 9 +#define MAX_CLIENT_THREADS 9 + +CHAR ProcessName[ 32 ]; +HANDLE PortHandle = NULL; + +HANDLE ClientThreadHandles[ MAX_CLIENT_THREADS ] = {NULL}; +DWORD ClientThreadClientIds[ MAX_CLIENT_THREADS ]; + + +DWORD +ClientThread( + LPVOID Context + ) +{ + CHAR ThreadName[ 64 ]; + NTSTATUS Status = STATUS_SUCCESS; + ULONG MsgLength; + PTEB Teb = NtCurrentTeb(); + ULONG64 startCount, endCount; + INT i; + + Teb->ActiveRpcHandle = NULL; + + strcpy( ThreadName, "Client Thread Id: " ); + RtlIntegerToChar( (ULONG)Teb->ClientId.UniqueProcess, 16, 9, + ThreadName + strlen( ThreadName ) + ); + strcat( ThreadName, "." ); + RtlIntegerToChar( (ULONG)Teb->ClientId.UniqueThread, 16, 9, + ThreadName + strlen( ThreadName ) + ); + + EnterThread( ThreadName, (ULONG)Context ); + + MsgLength = 0; + while (NT_SUCCESS( Status )) { + startCount = GetCycleCount(); + for (i = 0; i < BENCH_ITERATIONS; i++) + { + Status = SendRequest( 1, + ThreadName, + PortHandle, + Context, + MsgLength, + NULL, + FALSE + ); + if (!NT_SUCCESS( Status )) + { + fprintf(stderr, "FAILED %d\n", i); + break; + } + } + endCount = GetCycleCount(); + fprintf(stdout, "Message Bytes %d Iterations %d Cycles %I64d Cycles/Iteration %I64d\n", + MsgLength, BENCH_ITERATIONS, (endCount - startCount), + (endCount - startCount) / BENCH_ITERATIONS); + + MsgLength += sizeof( ULONG ); + if (MsgLength >= (TLPC_MAX_MSG_DATA_LENGTH * sizeof( ULONG ))) { + break; + } + } + + if (PortHandle != NULL) { + if (!CloseHandle( PortHandle )) { + fprintf( stderr, "CloseHandle( 0x%lx ) failed - %u\n", PortHandle, GetLastError() ); + } + else { + PortHandle = NULL; + } + } + + fprintf( stderr, "%s %s\n", + NT_SUCCESS( Status ) ? "Exiting" : "ABORTING", + ThreadName + ); + + return RtlNtStatusToDosError( Status ); +} + + +VOID +Usage( VOID ) +{ + fprintf( stderr, "usage: UCLIENT ClientNumber [#threads]\n" ); + ExitProcess( 1 ); +} + + +int +_cdecl +main( + int argc, + char *argv[] + ) +{ + NTSTATUS Status; + DWORD rc; + PORT_VIEW ClientView; + REMOTE_PORT_VIEW ServerView; + ULONG ClientNumber; + ULONG NumberOfThreads; + ULONG MaxMessageLength; + ULONG ConnectionInformationLength; + UCHAR ConnectionInformation[ 64 ]; + ULONG i; + PULONG p; + PTEB Teb = NtCurrentTeb(); + LARGE_INTEGER MaximumSize; + + Status = STATUS_SUCCESS; + + fprintf( stderr, "Entering UCLIENT User Mode LPC Test Program\n" ); + + if (argc < 2) { + Usage(); + } + + ClientNumber = xatoi( argv[ 1 ] ); + if (argc < 3) { + NumberOfThreads = 1; + } + else { + NumberOfThreads = xatoi( argv[ 2 ] ); + } + + if (ClientNumber > MAX_CLIENT_PROCESSES || + NumberOfThreads > MAX_CLIENT_THREADS + ) { + Usage(); + } + + sprintf( ProcessName, "Client Process %08x", Teb->ClientId.UniqueProcess ); + strcpy( ConnectionInformation, ProcessName ); + ConnectionInformationLength = strlen( ProcessName ) + 1; + + RtlInitUnicodeString( &PortName, PORT_NAME ); + fprintf( stderr, "Creating Port Memory Section" ); + + MaximumSize.QuadPart = 0x4000 * NumberOfThreads; + Status = NtCreateSection( &ClientView.SectionHandle, + SECTION_MAP_READ | SECTION_MAP_WRITE, + NULL, + &MaximumSize, + PAGE_READWRITE, + SEC_COMMIT, + NULL + ); + + if (ShowHandleOrStatus( Status, ClientView.SectionHandle )) { + ClientView.Length = sizeof( ClientView ); + ClientView.SectionOffset = 0; + ClientView.ViewSize = 0x2000; + ClientView.ViewBase = 0; + ClientView.ViewRemoteBase = 0; + ServerView.Length = sizeof( ServerView ); + ServerView.ViewSize = 0; + ServerView.ViewBase = 0; + + fprintf( stderr, "%s calling NtConnectPort( %wZ )\n", ProcessName, &PortName ); + Status = NtConnectPort( &PortHandle, + &PortName, + &DynamicQos, + &ClientView, + &ServerView, + (PULONG)&MaxMessageLength, + (PVOID)ConnectionInformation, + (PULONG)&ConnectionInformationLength + ); + + + + if (ShowHandleOrStatus( Status, PortHandle )) { + fprintf( stderr, " MaxMessageLength: %ld\n", MaxMessageLength ); + fprintf( stderr, " ConnectionInfo: (%ld) '%.*s'\n", + ConnectionInformationLength, + ConnectionInformationLength, + (PSZ)&ConnectionInformation[0] + ); + fprintf( stderr, " ClientView: Base=%lx, Size=%lx, RemoteBase: %lx\n", + ClientView.ViewBase, + ClientView.ViewSize, + ClientView.ViewRemoteBase + ); + fprintf( stderr, " ServerView: Base=%lx, Size=%lx\n", + ServerView.ViewBase, + ServerView.ViewSize + ); + ClientMemoryBase = ClientView.ViewBase; + ClientMemorySize = ClientView.ViewSize; + ServerMemoryBase = ClientView.ViewRemoteBase; + ServerMemoryDelta = (ULONG)ServerMemoryBase - + (ULONG)ClientMemoryBase; + + p = (PULONG)ClientMemoryBase; + i = ClientMemorySize; + while (i) { + fprintf( stderr, "ClientView[%lx] == %lx (%lx)\n", + p, + *p, + *p - ServerMemoryDelta + ); + p += (0x1000/sizeof( ULONG )); + i -= 0x1000; + } + p = (PULONG)ServerView.ViewBase; + i = ServerView.ViewSize; + while (i) { + fprintf( stderr, "ServerView[%lx] == %lx\n", p, *p ); + p += (0x1000/sizeof( ULONG )); + i -= 0x1000; + } + } + } + + rc = RtlNtStatusToDosError( Status ); + if (rc == NO_ERROR) { + ClientThreadHandles[ 0 ] = GetCurrentThread(); + ClientThreadClientIds[ 0 ] = GetCurrentThreadId(); + for (i=1; i< NumberOfThreads; i++) { + fprintf( stderr, "Creating %s, Thread %ld\n", ProcessName, i+1 ); + rc = NO_ERROR; + ClientThreadHandles[ i ] = CreateThread( NULL, + 0, + (LPTHREAD_START_ROUTINE)ClientThread, + (LPVOID)((ClientNumber << 4) | (i+1)), + CREATE_SUSPENDED, + &ClientThreadClientIds[ i ] + ); + if (ClientThreadHandles[ i ] == NULL) { + rc = GetLastError(); + break; + } + } + + if (rc == NO_ERROR) { + for (i=1; i +#include +#include +#include +#include +#include +#include + +#define PORT_NAME L"\\RPC Control\\LpcTestPort" + +UNICODE_STRING PortName; + +char * LpcMsgTypes[] = { + "** INVALID **", + "LPC_REQUEST", + "LPC_REPLY", + "LPC_DATAGRAM", + "LPC_LOST_REPLY", + "LPC_PORT_CLOSED", + "LPC_CLIENT_DIED", + "LPC_EXCEPTION", + "LPC_DEBUG_EVENT", + "LPC_ERROR_EVENT", + "LPC_CONNECTION_REQUEST", + NULL +}; + +SECURITY_QUALITY_OF_SERVICE DynamicQos = { + SecurityImpersonation, + SECURITY_DYNAMIC_TRACKING, + TRUE +}; + +#define TLPC_MAX_MSG_DATA_LENGTH 16 + +typedef struct _TLPC_PORTMSG { + PORT_MESSAGE h; + ULONG Data[ TLPC_MAX_MSG_DATA_LENGTH ]; +} TLPC_PORTMSG, *PTLPC_PORTMSG; + +PCH ClientMemoryBase = 0; +ULONG ClientMemorySize = 0; +PCH ServerMemoryBase = 0; +ULONG ServerMemoryDelta = 0; + +typedef struct _PAGE { + CHAR Data[ 4096 ]; +} PAGE, *PPAGE; + +int +xatoi(const char* s) +{ + int result = 0; + char c; + while (((c = *s) != '\0') && (c >= '0' && c <= '9')) + { + result *= 10; + result += (int)(c - '0'); + s++; + } + return result; +} + + +PPORT_MESSAGE +InitTlpcMsg( + PTLPC_PORTMSG Msg, + PVOID Context, + ULONG MsgLength + ) +{ + ULONG i; + ULONG ClientIndex; + ULONG cbData = MsgLength % (TLPC_MAX_MSG_DATA_LENGTH * sizeof( ULONG )); + PULONG ClientMemoryPtr; + + Msg->h.u1.Length = ((sizeof( Msg->h ) + cbData) << 16) | cbData; + Msg->h.u2.ZeroInit = 0; + ClientIndex = (ULONG)Context & 0xF; + ClientIndex -= 1; + if (cbData) { + Msg->Data[ 0 ] = (ULONG)Context; + ClientMemoryPtr = (PULONG)(ClientMemoryBase + (ClientIndex * 0x1000)); + for (i=1; i<(cbData/sizeof(ULONG)); i++) { + *ClientMemoryPtr = (ULONG)Context; + Msg->Data[ i ] = (ULONG)ClientMemoryPtr + ServerMemoryDelta; + ClientMemoryPtr++; + } + } + + return( (PPORT_MESSAGE)Msg ); +} + +#if DO_CHECK +BOOLEAN +CheckTlpcMsg( + NTSTATUS Status, + PTLPC_PORTMSG Msg + ) +{ + ULONG i; + ULONG ClientIndex; + ULONG cbData = Msg->h.u1.s1.DataLength; + ULONG Context; + PULONG ServerMemoryPtr; + PULONG ClientMemoryPtr; + ULONG ExpectedContext; + BOOLEAN Result; + + if (!NT_SUCCESS( Status )) { + fprintf( stderr, " - FAILED. Status == %X\n", Status ); + return( FALSE ); + } + + if (Msg->h.u2.s2.Type == LPC_CONNECTION_REQUEST) { + fprintf( stderr, " connection request" ); + } + else + if (cbData) { + Context = Msg->Data[ 0 ]; + ClientIndex = Context & 0xF; + ClientIndex -= 1; + ClientMemoryPtr = (PULONG)(ClientMemoryBase + (ClientIndex * 0x1000)); + for (i=1; i<(cbData/sizeof( ULONG )); i++) { + if (Msg->h.u2.s2.Type == LPC_REPLY) { + if (Msg->Data[ i ] != ((ULONG)ClientMemoryPtr + ServerMemoryDelta) || + *ClientMemoryPtr != (ULONG)Context + ) { + fprintf( stderr, " incorrectly\n" ); + fprintf( stderr, " Msg->Data[ %ld ] == %lx != %lx || %lx -> %lx != %lx\n", + i, Msg->Data[ i ], (ULONG)ClientMemoryPtr + ServerMemoryDelta, + ClientMemoryPtr, *ClientMemoryPtr, Context + ); + return( FALSE ); + } + + ClientMemoryPtr++; + } + else { + ServerMemoryPtr = (PULONG)(Msg->Data[ i ]); + __try { + ExpectedContext = *ServerMemoryPtr; + Result = (ExpectedContext != Context) ? FALSE : TRUE; + } + __except( EXCEPTION_EXECUTE_HANDLER ) { + ExpectedContext = 0xFEFEFEFE; + Result = FALSE; + } + + if (!Result) { + fprintf( stderr, " incorrectly\n" ); + fprintf( stderr, " Msg->Data[ %ld ] == %lx -> %lx != %lx\n", + i, Msg->Data[ i ], ExpectedContext, Context + ); + return( FALSE ); + } + } + } + } + + // fprintf( stderr, " correctly\n" ); + return( TRUE ); +} +#else +#define CheckTlpcMsg(x, y) (1) +#endif // DO_CHECK + + +BOOLEAN +ShowHandleOrStatus( + NTSTATUS Status, + HANDLE Handle + ) +{ + if (NT_SUCCESS( Status )) { + fprintf( stderr, " - Handle = 0x%lx\n", Handle ); + return( TRUE ); + } + else { + fprintf( stderr, " - *** FAILED *** Status == %X\n", Status ); + return( FALSE ); + } +} + + +BOOLEAN +ShowStatus( + NTSTATUS Status + ) +{ + if (NT_SUCCESS( Status )) { + fprintf( stderr, " - success\n" ); + return( TRUE ); + } + else { + fprintf( stderr, " - *** FAILED *** Status == %X\n", Status ); + return( FALSE ); + } +} + +PCH EnterString = ">>>>>>>>>>"; +PCH InnerString = "||||||||||"; +PCH LeaveString = "<<<<<<<<<<"; + +NTSTATUS +SendRequest( + ULONG Level, + PSZ ThreadName, + HANDLE PortHandle, + PVOID Context, + ULONG MsgLength, + PTLPC_PORTMSG CallBackTarget, + BOOLEAN ServerCallingClient + ) +{ + NTSTATUS Status; + TLPC_PORTMSG Request, Reply; + PTEB Teb = NtCurrentTeb(); + + // + //fprintf( stderr, "%.*sEnter SendRequest, %lx.%lx", + // Level, EnterString, + // Teb->ClientId.UniqueProcess, + // Teb->ClientId.UniqueThread + // ); + // + + InitTlpcMsg( &Request, Context, MsgLength ); + if (CallBackTarget == NULL) { + // fprintf( stderr, " - Request"); + } + else { + Request.h.u2.s2.Type = LPC_REQUEST; + Request.h.ClientId = CallBackTarget->h.ClientId; + Request.h.MessageId = CallBackTarget->h.MessageId; + fprintf( stderr, " - Callback to %lx.%lx, ID: %ld", + Request.h.ClientId.UniqueProcess, + Request.h.ClientId.UniqueThread, + Request.h.MessageId + ); + } + + // + //fprintf( stderr, " (%ld bytes)...\n", Request.h.u1.s1.DataLength ); + // + + Status = NtRequestWaitReplyPort( PortHandle, + (PPORT_MESSAGE)&Request, + (PPORT_MESSAGE)&Reply + ); + // + //fprintf( stderr, "%.*s %lx.%lx, ID: %u received ", + // Level, InnerString, + // Teb->ClientId.UniqueProcess, + // Teb->ClientId.UniqueThread, + // Reply.h.MessageId + // ); + // + if (Reply.h.u2.s2.Type == LPC_REPLY) { + if (!CheckTlpcMsg( Status, &Reply )) { + Status = STATUS_UNSUCCESSFUL; + fprintf( stderr, "SendRequest got invalid reply message at %x\n", &Reply ); + DbgBreakPoint(); + } + } + else { + fprintf( stderr, "callback from %lx.%lx, ID: %ld", + Reply.h.ClientId.UniqueProcess, + Reply.h.ClientId.UniqueThread, + Reply.h.MessageId + ); + if (!CheckTlpcMsg( Status, &Reply )) { + Status = STATUS_UNSUCCESSFUL; + fprintf( stderr, "SendRequest got invalid callback message at %x\n", &Reply ); + DbgBreakPoint(); + } + else { + MsgLength = Reply.h.u1.s1.DataLength / 2; + if (MsgLength) { + Status = SendRequest( Level+1, + ThreadName, + PortHandle, + Context, + MsgLength, + &Reply, + ServerCallingClient + ); + } + + if (!ServerCallingClient || Level > 1) { + fprintf( stderr, "%.*s %lx.%lx sending ", + Level, InnerString, + Teb->ClientId.UniqueProcess, + Teb->ClientId.UniqueThread + ); + fprintf( stderr, " callback (%u) reply to %lx.%lx, ID: %u (%ld bytes)...\n", + Level, + Reply.h.ClientId.UniqueProcess, + Reply.h.ClientId.UniqueThread, + Reply.h.MessageId, + Reply.h.u1.s1.DataLength + ); + if (Level > 1) { + Status = NtReplyWaitReplyPort( PortHandle, + (PPORT_MESSAGE)&Reply + ); + } + } + } + } + + // + //fprintf( stderr, "%.*sLeave SendRequest, %lx.%lx - Status == %X\n", + // Level, LeaveString, + // Teb->ClientId.UniqueProcess, + // Teb->ClientId.UniqueThread, + // Status + // ); + // + return( Status ); +} + +VOID +EnterThread( + PSZ ThreadName, + ULONG Context + ) +{ + fprintf( stderr, "Entering %s thread, Context = 0x%lx\n", + ThreadName, + Context + ); +} diff --git a/base/Windows/Benchmarks/Lpc/userver.c b/base/Windows/Benchmarks/Lpc/userver.c new file mode 100644 index 0000000..dbf8061 --- /dev/null +++ b/base/Windows/Benchmarks/Lpc/userver.c @@ -0,0 +1,338 @@ +//++ +// +//Copyright (c) Microsoft Corporation +// +//Abstract: +// +// User Mode Server Test program for the LPC subcomponent of the NTOS project +// +//-- + +#include "ulpc.h" + +#define MAX_REQUEST_THREADS 9 +#define MAX_CONNECTIONS 4 + +HANDLE ServerConnectionPortHandle; +HANDLE ServerThreadHandles[ MAX_REQUEST_THREADS ]; +DWORD ServerThreadClientIds[ MAX_REQUEST_THREADS ]; + +HANDLE ServerClientPortHandles[ MAX_CONNECTIONS ]; +ULONG CountServerClientPortHandles = 0; +ULONG CountClosedServerClientPortHandles = 0; + +BOOLEAN TestCallBacks; + +VOID +ServerHandleConnectionRequest( + IN PTLPC_PORTMSG Msg + ) +{ + BOOLEAN AcceptConnection; + LPSTR ConnectionInformation; + ULONG ConnectionInformationLength; + NTSTATUS Status; + PORT_VIEW ServerView; + REMOTE_PORT_VIEW ClientView; + ULONG i; + PULONG p; + + ConnectionInformation = (LPSTR)&Msg->Data[ 0 ]; + ConnectionInformationLength = Msg->h.u1.s1.DataLength; + AcceptConnection = FALSE; + fprintf( stderr, "\nConnection Request Received from CLIENT_ID 0x%08lx.0x%08lx:\n", + Msg->h.ClientId.UniqueProcess, + Msg->h.ClientId.UniqueThread + ); + fprintf( stderr, " MessageId: %ld\n", + Msg->h.MessageId + ); + fprintf( stderr, " ClientViewSize: 0x%08lx\n", + Msg->h.ClientViewSize + ); + fprintf( stderr, " ConnectionInfo: (%ld) '%.*s'\n", + ConnectionInformationLength, + ConnectionInformationLength, + (PSZ)&ConnectionInformation[0] + ); + + ClientView.Length = sizeof( ClientView ); + ClientView.ViewSize = 0; + ClientView.ViewBase = 0; + if (CountServerClientPortHandles >= MAX_CONNECTIONS) { + AcceptConnection = FALSE; + } + else { + AcceptConnection = TRUE; + } + + if (AcceptConnection) { + LARGE_INTEGER MaximumSize; + + fprintf( stderr, "Creating Port Memory Section" ); + MaximumSize.QuadPart = 0x4000; + Status = NtCreateSection( &ServerView.SectionHandle, + SECTION_MAP_READ | SECTION_MAP_WRITE, + NULL, + &MaximumSize, + PAGE_READWRITE, + SEC_COMMIT, + NULL + ); + + if (ShowHandleOrStatus( Status, ServerView.SectionHandle )) { + ServerView.Length = sizeof( ServerView ); + ServerView.SectionOffset = 0; + ServerView.ViewSize = 0x4000; + ServerView.ViewBase = 0; + ServerView.ViewRemoteBase = 0; + } + else { + AcceptConnection = FALSE; + } + } + + fprintf( stderr, "Server calling NtAcceptConnectPort( AcceptConnection = %ld )", + AcceptConnection + ); + + if (AcceptConnection) { + strcpy( ConnectionInformation, "Server Accepting Connection" ); + } + else { + strcpy( ConnectionInformation, "Server Rejecting Connection" ); + } + + Msg->h.u1.s1.DataLength = strlen( ConnectionInformation ) + 1; + Msg->h.u1.s1.TotalLength = Msg->h.u1.s1.DataLength + sizeof( Msg->h ); + Status = NtAcceptConnectPort( &ServerClientPortHandles[ CountServerClientPortHandles ], + (PVOID)(CountServerClientPortHandles+1), + &Msg->h, + AcceptConnection, + &ServerView, + &ClientView + ); + + if (ShowHandleOrStatus( Status, ServerClientPortHandles[ CountServerClientPortHandles ] )) { + fprintf( stderr, " ServerView: Base=%lx, Size=%lx, RemoteBase: %lx\n", + ServerView.ViewBase, + ServerView.ViewSize, + ServerView.ViewRemoteBase + ); + fprintf( stderr, " ClientView: Base=%lx, Size=%lx\n", + ClientView.ViewBase, + ClientView.ViewSize + ); + + ClientMemoryBase = ServerView.ViewBase; + ClientMemorySize = ServerView.ViewSize; + ServerMemoryBase = ServerView.ViewRemoteBase; + ServerMemoryDelta = (ULONG)ServerMemoryBase - + (ULONG)ClientMemoryBase; + p = (PULONG)(ClientView.ViewBase); + i =ClientView.ViewSize; + while (i) { + *p = (ULONG)p; + fprintf( stderr, "Server setting ClientView[ %lx ] = %lx\n", + p, + *p + ); + p += (0x1000/sizeof(ULONG)); + i -= 0x1000; + } + + p = (PULONG)(ServerView.ViewBase); + i = ServerView.ViewSize; + while (i) { + *p = (ULONG)p - ServerMemoryDelta; + fprintf( stderr, "Server setting ServerView[ %lx ] = %lx\n", + p, + *p + ); + p += (0x1000/sizeof(ULONG)); + i -= 0x1000; + } + Status = NtCompleteConnectPort( ServerClientPortHandles[ CountServerClientPortHandles ] ); + CountServerClientPortHandles++; + } + + return; +} + +DWORD +ServerThread( + LPVOID Context + ) +{ + NTSTATUS Status; + CHAR ThreadName[ 64 ]; + TLPC_PORTMSG Msg; + PTLPC_PORTMSG ReplyMsg; + HANDLE ReplyPortHandle; + ULONG PortContext; + PTEB Teb = NtCurrentTeb(); + + Teb->ActiveRpcHandle = NULL; + + strcpy( ThreadName, "Server Thread Id: " ); + RtlIntegerToChar( (ULONG)Teb->ClientId.UniqueProcess, 16, 9, + ThreadName + strlen( ThreadName ) + ); + strcat( ThreadName, "." ); + RtlIntegerToChar( (ULONG)Teb->ClientId.UniqueThread, 16, 9, + ThreadName + strlen( ThreadName ) + ); + + EnterThread( ThreadName, (ULONG)Context ); + + ReplyMsg = NULL; + ReplyPortHandle = ServerConnectionPortHandle; + while (TRUE) { + // fprintf( stderr, "%s waiting for message...\n", ThreadName ); + Status = NtReplyWaitReceivePort( ReplyPortHandle, + (PVOID)&PortContext, + (PPORT_MESSAGE)ReplyMsg, + (PPORT_MESSAGE)&Msg + ); + + ReplyMsg = NULL; + ReplyPortHandle = ServerConnectionPortHandle; + // + //fprintf( stderr, "%s Receive (%s) Id: %u", ThreadName, LpcMsgTypes[ Msg.h.u2.s2.Type ], Msg.h.MessageId ); + // + PortContext -= 1; + if (!NT_SUCCESS( Status )) { + fprintf( stderr, " (Status == %08x)\n", Status ); + } + else + if (Msg.h.u2.s2.Type == LPC_CONNECTION_REQUEST) { + ServerHandleConnectionRequest( &Msg ); + continue; + } + else + if (PortContext >= CountServerClientPortHandles) { + fprintf( stderr, "*** Invalid PortContext (%lx) received\n", + PortContext + ); + } + else + if (Msg.h.u2.s2.Type == LPC_PORT_CLOSED || + Msg.h.u2.s2.Type == LPC_CLIENT_DIED + ) { + fprintf( stderr, " - disconnect for client %08x\n", PortContext ); + CloseHandle( ServerClientPortHandles[ (ULONG)PortContext ] ); + CountClosedServerClientPortHandles += 1; + if (CountClosedServerClientPortHandles == CountServerClientPortHandles) { + break; + } + } + else + if (Msg.h.u2.s2.Type == LPC_REQUEST) { + CheckTlpcMsg( Status, &Msg ); + ReplyMsg = &Msg; + ReplyPortHandle = ServerClientPortHandles[ PortContext ]; + if (TestCallBacks && (Msg.h.u1.s1.DataLength > 30)) { + Status = SendRequest( 1, + ThreadName, + ReplyPortHandle, + Context, + Msg.h.u1.s1.DataLength >> 1, + ReplyMsg, + TRUE + ); + } + } + } + + fprintf( stderr, "Exiting %s\n", ThreadName ); + + return RtlNtStatusToDosError( Status ); +} + + + +VOID +Usage( VOID ) +{ + fprintf( stderr, "usage: USERVER #threads\n" ); + ExitProcess( 1 ); +} + + +int +_cdecl +main( + int argc, + char *argv[] + ) +{ + NTSTATUS Status; + DWORD rc; + ULONG i, NumberOfThreads; + OBJECT_ATTRIBUTES ObjectAttributes; + + Status = STATUS_SUCCESS; + + fprintf( stderr, "Entering USERVER User Mode LPC Test Program\n" ); + + TestCallBacks = FALSE; + if (argc < 2) { + NumberOfThreads = 1; + } + else { + NumberOfThreads = xatoi( argv[ 1 ] ); + if (NumberOfThreads >= MAX_REQUEST_THREADS) { + Usage(); + } + + if (argc > 2) { + TestCallBacks = TRUE; + } + } + + RtlInitUnicodeString( &PortName, PORT_NAME ); + fprintf( stderr, "Creating %wZ connection port", (PUNICODE_STRING)&PortName ); + InitializeObjectAttributes( &ObjectAttributes, &PortName, 0, NULL, NULL ); + Status = NtCreatePort( &ServerConnectionPortHandle, + &ObjectAttributes, + 40, + sizeof( TLPC_PORTMSG ), + sizeof( TLPC_PORTMSG ) * 32 + ); + ShowHandleOrStatus( Status, ServerConnectionPortHandle ); + rc = RtlNtStatusToDosError( Status ); + if (rc == NO_ERROR) { + ServerThreadHandles[ 0 ] = GetCurrentThread(); + ServerThreadClientIds[ 0 ] = GetCurrentThreadId(); + for (i=1; i nul + -rmdir $(OBJDIR) 2>nul + -rmdir $(OBJROOT) 2>nul diff --git a/base/Windows/Benchmarks/MapDemo/AssemblyInfo.cs b/base/Windows/Benchmarks/MapDemo/AssemblyInfo.cs new file mode 100644 index 0000000..cced87b --- /dev/null +++ b/base/Windows/Benchmarks/MapDemo/AssemblyInfo.cs @@ -0,0 +1,62 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the "project output directory". The location of the project output +// directory is dependent on whether you are working with a local or web project. +// For local projects, the project output directory is defined as +// \obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// For web projects, the project output directory is defined as +// %HOMEPATH%\VSWebCache\\\obj\. +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/base/Windows/Benchmarks/MapDemo/Global.asax b/base/Windows/Benchmarks/MapDemo/Global.asax new file mode 100644 index 0000000..807ffcd --- /dev/null +++ b/base/Windows/Benchmarks/MapDemo/Global.asax @@ -0,0 +1 @@ +<%@ Application Codebehind="Global.asax.cs" Inherits="mapdemo.Global" %> diff --git a/base/Windows/Benchmarks/MapDemo/Global.asax.cs b/base/Windows/Benchmarks/MapDemo/Global.asax.cs new file mode 100644 index 0000000..2d9f649 --- /dev/null +++ b/base/Windows/Benchmarks/MapDemo/Global.asax.cs @@ -0,0 +1,82 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +using System.Collections; +using System.ComponentModel; +using System.Web; +using System.Web.SessionState; + +namespace mapdemo +{ + /// + /// Summary description for Global. + /// + public class Global : System.Web.HttpApplication + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + public Global() + { + InitializeComponent(); + } + + protected void Application_Start(Object sender, EventArgs e) + { + + } + + protected void Session_Start(Object sender, EventArgs e) + { + + } + + protected void Application_BeginRequest(Object sender, EventArgs e) + { + + } + + protected void Application_EndRequest(Object sender, EventArgs e) + { + + } + + protected void Application_AuthenticateRequest(Object sender, EventArgs e) + { + + } + + protected void Application_Error(Object sender, EventArgs e) + { + + } + + protected void Session_End(Object sender, EventArgs e) + { + + } + + protected void Application_End(Object sender, EventArgs e) + { + + } + + #region Web Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + } + #endregion + } +} + diff --git a/base/Windows/Benchmarks/MapDemo/MapDemo.csproj b/base/Windows/Benchmarks/MapDemo/MapDemo.csproj new file mode 100644 index 0000000..5ff7563 --- /dev/null +++ b/base/Windows/Benchmarks/MapDemo/MapDemo.csproj @@ -0,0 +1,49 @@ + + + + + + 8.0.50727 + 2.0 + Properties + MapDemo + Library + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Windows/Benchmarks/MapDemo/MapImage.aspx b/base/Windows/Benchmarks/MapDemo/MapImage.aspx new file mode 100644 index 0000000..b6fd5ec --- /dev/null +++ b/base/Windows/Benchmarks/MapDemo/MapImage.aspx @@ -0,0 +1,2 @@ +<%@ Page language="c#" Codebehind="MapImage.aspx.cs" Inherits="mapdemo.MapImage" %> +<% ServeMap(); %> \ No newline at end of file diff --git a/base/Windows/Benchmarks/MapDemo/MapImage.aspx.cs b/base/Windows/Benchmarks/MapDemo/MapImage.aspx.cs new file mode 100644 index 0000000..733e490 --- /dev/null +++ b/base/Windows/Benchmarks/MapDemo/MapImage.aspx.cs @@ -0,0 +1,242 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +using System.Collections; +using System.Web; + +using mapdemo.MapPointService; +using MapPointNamespace; + +namespace mapdemo +{ + /// + /// Summary description for WebForm1. + /// + public class MapImage : System.Web.UI.Page + { + private const int mapWidth = 400; + private const int mapHeight = 500; + + private void Page_Load(object sender, System.EventArgs e) + { + // Put user code to initialize the page here + } + + protected void ServeMap() + { + if (!MapPointNamespace.MapPoint.Initialized) + { + MapPointNamespace.MapPoint.Initialize(); + } + + bool isSeattle = true; // default to Seattle + double centerLat, centerLong, zoom; + + if ((Request.QueryString["city"] != null) && + (Request.QueryString["city"].ToLower().Equals("redmond"))) + { + isSeattle = false; + } + + byte mask = 0xff; + + if (Request.QueryString["mask"] != null) + { + try + { mask = Byte.Parse(Request.QueryString["mask"]); } + catch (Exception) { /* ignore */ } + } + + if (!isSeattle) + { + centerLat = redmondCenterLat; + centerLong = redmondCenterLong; + zoom = redmondZoom; + } + else { + centerLat = seattleCenterLat; + centerLong = seattleCenterLong; + zoom = seattleZoom; + } + + try + { + MapSpecification spec = MapPointNamespace.MapPoint.PrepareMapFromCenterZoom( + mapWidth, mapHeight, centerLat, centerLong, zoom); + + // Add pushpins to the map specification! + if (isSeattle) + { + spec.Pushpins = BuildPushPins(SeattlePushPins, mask); + } + else { + spec.Pushpins = BuildPushPins(RedmondPushPins, mask);; + } + + // Fetch the map image; + byte[] data = MapPointNamespace.MapPoint.GetMap(spec); + + if (data == null) + { + Response.StatusCode = 500; + Response.StatusDescription = "Failed to retrieve map data"; + } + else { + Response.ContentType= "image/gif"; + Response.BinaryWrite(data); + } + } + catch (Exception ex) { + Response.Write(ex); + return; + } + } + + private mapdemo.MapPointService.Pushpin[] BuildPushPins(PushPinData[] pins, byte mask) + { + // First count how many pins are going to match the mask + int numPins = 0; + for (int i = 0; i < pins.Length; ++i) + { + if ((pins[i].groupMask == 0) || ((pins[i].groupMask & mask) != 0)) + { + numPins++; + } + } + + mapdemo.MapPointService.Pushpin[] specPins = new mapdemo.MapPointService.Pushpin[numPins]; + + int outputIndex = 0; + for (int i = 0; i < pins.Length; ++i) + { + if ((pins[i].groupMask == 0) || ((pins[i].groupMask & mask) != 0)) + { + specPins[outputIndex] = new mapdemo.MapPointService.Pushpin(); + specPins[outputIndex].LatLong = new mapdemo.MapPointService.LatLong(); + + specPins[outputIndex].IconDataSource = "MapPoint.Icons"; + specPins[outputIndex].IconName = pins[i].icon; + specPins[outputIndex].Label = pins[i].label; + specPins[outputIndex].LabelNearbyRoads = false; + specPins[outputIndex].LatLong.Latitude = pins[i].latitude; + specPins[outputIndex].LatLong.Longitude = pins[i].longitude; + specPins[outputIndex].ReturnsHotArea = false; + ++outputIndex; + } + } + + return specPins; + } + + private const double seattleCenterLat = 47.616391194232195; + private const double seattleCenterLong = -122.32471226177632; + private const double seattleZoom = 7; + + private const double redmondCenterLat = 47.664037990827104; + private const double redmondCenterLong = -122.12025798221626; + private const double redmondZoom = 10; + + private struct PushPinData + { + public double latitude; + public double longitude; + public string icon; + public string label; + public byte groupMask; + + public PushPinData(double latit, double longi, string ic, + string lab, byte mask) + { + latitude = latit; + longitude = longi; + icon = ic; + label = lab; + groupMask = mask; + } + } + + private static readonly PushPinData[] RedmondPushPins = { + // "You are here" + new PushPinData(47.641372008066419, -122.14171843658843, "42" /*office building*/, "You are here (Microsoft Research)", 0), + // Starbucks + new PushPinData(47.681110048657551, -122.12546258636381, "DriveThruIcon" /*green coffee circle*/, null, 1), + new PushPinData(47.674440368933034, -122.12920553002897, "DriveThruIcon", null, 1), + new PushPinData(47.674293519865877, -122.12964266213585, "DriveThruIcon", null, 1), + new PushPinData(47.670646198849091, -122.1135097553162, "DriveThruIcon", null, 1), + new PushPinData(47.670878425280875, -122.11128311364686, "DriveThruIcon", null, 1), + new PushPinData(47.671243840401466, -122.10595556609425, "DriveThruIcon", null, 1), + new PushPinData(47.652327631495119, -122.13483360590506, "DriveThruIcon", null, 1), + // Tullys + new PushPinData(47.6810758977117, -122.12546258636381, "CoffeeShopIcon" /*Red coffee circle*/, null, 2), + new PushPinData(47.671001368685936, -122.04650559955846, "CoffeeShopIcon", null, 2), + // SBC + new PushPinData(47.670178330890948, -122.11961594443427, "KioskIcon" /*Blue coffee circle*/, null, 4), + new PushPinData(47.608457326456126, -122.20149625220438, "KioskIcon", null, 4), + new PushPinData(47.574152701349739, -122.17140243872132, "KioskIcon", null, 4) + }; + + private static readonly PushPinData[] SeattlePushPins = { + // "You are here" + new PushPinData(47.614037591008028, -122.30879852406528, "27" /*house with flag*/, "You are here (home)", 0), + // Starbucks + new PushPinData(47.556308832143081, -122.14664983316918, "DriveThruIcon" /*green coffee circle*/, null, 1), + new PushPinData(47.609201817075657, -122.31668056236748, "DriveThruIcon", null, 1), + new PushPinData(47.619942289545506, -122.31374358102437, "DriveThruIcon", null, 1), + new PushPinData(47.599257061644117, -122.30209127830034, "DriveThruIcon", null, 1), + new PushPinData(47.621212704731128, -122.31263709037883, "DriveThruIcon", null, 1), + new PushPinData(47.612890119227465, -122.32072403435613, "DriveThruIcon", null, 1), + new PushPinData(47.601862778812482, -122.28519339029373, "DriveThruIcon", null, 1), + new PushPinData(47.6102400058295, -122.32292335526887, "DriveThruIcon", null, 1), + new PushPinData(47.556308832143081, -122.14664983316918, "DriveThruIcon", null, 1), + new PushPinData(47.622145025552832, -122.32086063813952, "DriveThruIcon", null, 1), + new PushPinData(47.622367006700856, -122.32081965700451, "DriveThruIcon", null, 1), + new PushPinData(47.618466968684778, -122.32557346866683, "DriveThruIcon", null, 1), + new PushPinData(47.616233496826183, -122.32989014822229, "DriveThruIcon", null, 1), + new PushPinData(47.617053119526588, -122.33032728032917, "DriveThruIcon", null, 1), + // Tullys + new PushPinData(47.556308832143081, -122.14664983316918, "137", null, 6), + new PushPinData(47.626021157906813, -122.30730954282622, "CoffeeShopIcon", null, 2), + new PushPinData(47.607811873579557, -122.33913822435848, "CoffeeShopIcon", null, 2), + new PushPinData(47.556308832143081, -122.14664983316918, "CoffeeShopIcon", null, 2), + new PushPinData(47.617097515756193, -122.35281226307684, "CoffeeShopIcon", null, 2), + new PushPinData(47.605650118707253, -122.33404290323766, "CoffeeShopIcon", null, 2), + new PushPinData(47.60911643971103, -122.33572312977347, "137", null, 6), + new PushPinData(47.608771515157947, -122.34009445084229, "137", null, 6), + new PushPinData(47.599181929563251, -122.33139278983968, "137", null, 6), + new PushPinData(47.633845139601064, -122.28094501262997, "CoffeeShopIcon", null, 2), + new PushPinData(47.556308832143081, -122.14664983316918, "137", null, 6), + new PushPinData(47.610117062424443, -122.33346916734737, "CoffeeShopIcon", null, 2), + new PushPinData(47.611677760649791, -122.33647445058217, "CoffeeShopIcon", null, 2), + // SBC + new PushPinData(47.610117062424443, -122.33655641285222, "KioskIcon", /*Blue coffee circle*/ null, 4), + new PushPinData(47.60678051501489, -122.33355112961741, "KioskIcon", null, 8), + new PushPinData(47.602412609040663, -122.33254026162025, "KioskIcon", null, 8), + new PushPinData(47.608826156671306, -122.33998516781557, "KioskIcon", null, 8), + new PushPinData(47.556308832143081, -122.14664983316918, "KioskIcon", null, 8) + }; + + #region Web Form Designer generated code + override protected void OnInit(EventArgs e) + { + // + // CODEGEN: This call is required by the ASP.NET Web Form Designer. + // + InitializeComponent(); + base.OnInit(e); + } + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.Load += new System.EventHandler(this.Page_Load); + } + #endregion + } +} diff --git a/base/Windows/Benchmarks/MapDemo/MapPoint.cs b/base/Windows/Benchmarks/MapDemo/MapPoint.cs new file mode 100644 index 0000000..9fb63c5 --- /dev/null +++ b/base/Windows/Benchmarks/MapDemo/MapPoint.cs @@ -0,0 +1,96 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +using System; +using System.Collections; + +using mapdemo.MapPointService; + +namespace MapPointNamespace +{ + public class MapPoint + { + const string mpwsUserName = "7356"; + const string mpwsPassword = "0[[{NmN?UU5?U"; + private static bool initialized = false; + public static RenderServiceSoap renderService = null; + + public MapPoint() + { + // Nada + } + + public static bool Initialized + { + get { return initialized; } + } + + public static void Initialize() + { + // Render service + renderService = new RenderServiceSoap(); + renderService.Credentials = new System.Net.NetworkCredential(mpwsUserName, mpwsPassword); + renderService.PreAuthenticate = true; + + initialized = true; + } + + public static MapSpecification PrepareMapFromCenterZoom( + int widthPixels, int heightPixels, + double centerLatitude, double centerLongitude, + double zoom) + { + // Options + MapOptions mapOptions = new MapOptions(); + mapOptions.ReturnType = MapReturnType.ReturnImage; + mapOptions.Format = new ImageFormat(); + mapOptions.Format.Width = widthPixels; + mapOptions.Format.Height = heightPixels; + mapOptions.Format.MimeType = "image/gif"; + mapOptions.Zoom = zoom; + + // View + ViewByScale[] viewByScaleArray = new ViewByScale[1]; + viewByScaleArray[0] = new ViewByScale(); + viewByScaleArray[0].CenterPoint = new LatLong(); + viewByScaleArray[0].CenterPoint.Latitude = centerLatitude; + viewByScaleArray[0].CenterPoint.Longitude = centerLongitude; + viewByScaleArray[0].MapScale = 1.0; + + // Specifications + MapSpecification mapSpecification = new MapSpecification(); + mapSpecification.Options = mapOptions; + mapSpecification.Views = viewByScaleArray; + mapSpecification.DataSourceName = "MapPoint.NA"; + + return mapSpecification; + } + + public static byte[] GetMap(MapSpecification map) + { + // Declare the map image array and get the map + MapImage[] mapImageArray = null; + + try + { + mapImageArray = renderService.GetMap(map); + } + catch (System.Net.WebException) { + // Better error handling? + return null; + } + + // Careful return + if (null != mapImageArray && null != mapImageArray[0]) + { + return mapImageArray[0].MimeData.Bits; + } + else { + return null; + } + } + } +} diff --git a/base/Windows/Benchmarks/MapDemo/MapPointService.cs b/base/Windows/Benchmarks/MapDemo/MapPointService.cs new file mode 100644 index 0000000..7fb6ee3 --- /dev/null +++ b/base/Windows/Benchmarks/MapDemo/MapPointService.cs @@ -0,0 +1,1909 @@ +// ---------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ---------------------------------------------------------------------------- + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.2032 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// +// This source code was auto-generated by Microsoft.VSDesigner, Version 1.1.4322.2032. +// +namespace mapdemo.MapPointService +{ + using System.Diagnostics; + using System.Xml.Serialization; + using System; + using System.Web.Services.Protocols; + using System.ComponentModel; + using System.Web.Services; + + + /// + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Web.Services.WebServiceBindingAttribute(Name="CommonServiceSoap", Namespace="http://s.mappoint.net/mappoint-30/")] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(MapPointConstants))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(FindResult[]))] + public class CommonServiceSoap : System.Web.Services.Protocols.SoapHttpClientProtocol { + + public CustomerInfoHeader CustomerInfoHeaderValue; + + public UserInfoHeader UserInfoHeaderValue; + + /// + public CommonServiceSoap() { + this.Url = "http://findv3.staging.mappoint.net/Find-30/Common.asmx"; + } + + /// + [System.Web.Services.Protocols.SoapHeaderAttribute("UserInfoHeaderValue")] + [System.Web.Services.Protocols.SoapHeaderAttribute("CustomerInfoHeaderValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://s.mappoint.net/mappoint-30/GetVersionInfo", RequestNamespace="http://s.mappoint.net/mappoint-30/", ResponseNamespace="http://s.mappoint.net/mappoint-30/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + public VersionInfo[] GetVersionInfo() { + object[] results = this.Invoke("GetVersionInfo", new object[0]); + return ((VersionInfo[])(results[0])); + } + + /// + public System.IAsyncResult BeginGetVersionInfo(System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("GetVersionInfo", new object[0], callback, asyncState); + } + + /// + public VersionInfo[] EndGetVersionInfo(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((VersionInfo[])(results[0])); + } + + /// + [System.Web.Services.Protocols.SoapHeaderAttribute("UserInfoHeaderValue")] + [System.Web.Services.Protocols.SoapHeaderAttribute("CustomerInfoHeaderValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://s.mappoint.net/mappoint-30/GetCountryRegionInfo", RequestNamespace="http://s.mappoint.net/mappoint-30/", ResponseNamespace="http://s.mappoint.net/mappoint-30/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + public CountryRegionInfo[] GetCountryRegionInfo(int[] entityIDs) { + object[] results = this.Invoke("GetCountryRegionInfo", new object[] { + entityIDs}); + return ((CountryRegionInfo[])(results[0])); + } + + /// + public System.IAsyncResult BeginGetCountryRegionInfo(int[] entityIDs, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("GetCountryRegionInfo", new object[] { + entityIDs}, callback, asyncState); + } + + /// + public CountryRegionInfo[] EndGetCountryRegionInfo(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((CountryRegionInfo[])(results[0])); + } + + /// + [System.Web.Services.Protocols.SoapHeaderAttribute("UserInfoHeaderValue")] + [System.Web.Services.Protocols.SoapHeaderAttribute("CustomerInfoHeaderValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://s.mappoint.net/mappoint-30/GetEntityTypes", RequestNamespace="http://s.mappoint.net/mappoint-30/", ResponseNamespace="http://s.mappoint.net/mappoint-30/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + public EntityType[] GetEntityTypes(string dataSourceName) { + object[] results = this.Invoke("GetEntityTypes", new object[] { + dataSourceName}); + return ((EntityType[])(results[0])); + } + + /// + public System.IAsyncResult BeginGetEntityTypes(string dataSourceName, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("GetEntityTypes", new object[] { + dataSourceName}, callback, asyncState); + } + + /// + public EntityType[] EndGetEntityTypes(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((EntityType[])(results[0])); + } + + /// + [System.Web.Services.Protocols.SoapHeaderAttribute("UserInfoHeaderValue")] + [System.Web.Services.Protocols.SoapHeaderAttribute("CustomerInfoHeaderValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://s.mappoint.net/mappoint-30/GetDataSourceInfo", RequestNamespace="http://s.mappoint.net/mappoint-30/", ResponseNamespace="http://s.mappoint.net/mappoint-30/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + public DataSource[] GetDataSourceInfo(string[] dataSourceNames) { + object[] results = this.Invoke("GetDataSourceInfo", new object[] { + dataSourceNames}); + return ((DataSource[])(results[0])); + } + + /// + public System.IAsyncResult BeginGetDataSourceInfo(string[] dataSourceNames, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("GetDataSourceInfo", new object[] { + dataSourceNames}, callback, asyncState); + } + + /// + public DataSource[] EndGetDataSourceInfo(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((DataSource[])(results[0])); + } + + /// + [System.Web.Services.Protocols.SoapHeaderAttribute("UserInfoHeaderValue")] + [System.Web.Services.Protocols.SoapHeaderAttribute("CustomerInfoHeaderValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://s.mappoint.net/mappoint-30/GetGreatCircleDistances", RequestNamespace="http://s.mappoint.net/mappoint-30/", ResponseNamespace="http://s.mappoint.net/mappoint-30/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + public System.Double[] GetGreatCircleDistances(LatLong[] latLongs) { + object[] results = this.Invoke("GetGreatCircleDistances", new object[] { + latLongs}); + return ((System.Double[])(results[0])); + } + + /// + public System.IAsyncResult BeginGetGreatCircleDistances(LatLong[] latLongs, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("GetGreatCircleDistances", new object[] { + latLongs}, callback, asyncState); + } + + /// + public System.Double[] EndGetGreatCircleDistances(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((System.Double[])(results[0])); + } + } + + /// + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Web.Services.WebServiceBindingAttribute(Name="FindServiceSoap", Namespace="http://s.mappoint.net/mappoint-30/")] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(MapPointConstants))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(FindResult[]))] + public class FindServiceSoap : System.Web.Services.Protocols.SoapHttpClientProtocol { + + public CustomerInfoFindHeader CustomerInfoFindHeaderValue; + + public UserInfoFindHeader UserInfoFindHeaderValue; + + /// + public FindServiceSoap() { + this.Url = "http://findv3.staging.mappoint.net/Find-30/FindService.asmx"; + } + + /// + [System.Web.Services.Protocols.SoapHeaderAttribute("CustomerInfoFindHeaderValue")] + [System.Web.Services.Protocols.SoapHeaderAttribute("UserInfoFindHeaderValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://s.mappoint.net/mappoint-30/Find", RequestNamespace="http://s.mappoint.net/mappoint-30/", ResponseNamespace="http://s.mappoint.net/mappoint-30/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + public FindResults Find(FindSpecification specification) { + object[] results = this.Invoke("Find", new object[] { + specification}); + return ((FindResults)(results[0])); + } + + /// + public System.IAsyncResult BeginFind(FindSpecification specification, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("Find", new object[] { + specification}, callback, asyncState); + } + + /// + public FindResults EndFind(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((FindResults)(results[0])); + } + + /// + [System.Web.Services.Protocols.SoapHeaderAttribute("CustomerInfoFindHeaderValue")] + [System.Web.Services.Protocols.SoapHeaderAttribute("UserInfoFindHeaderValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://s.mappoint.net/mappoint-30/FindAddress", RequestNamespace="http://s.mappoint.net/mappoint-30/", ResponseNamespace="http://s.mappoint.net/mappoint-30/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + public FindResults FindAddress(FindAddressSpecification specification) { + object[] results = this.Invoke("FindAddress", new object[] { + specification}); + return ((FindResults)(results[0])); + } + + /// + public System.IAsyncResult BeginFindAddress(FindAddressSpecification specification, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("FindAddress", new object[] { + specification}, callback, asyncState); + } + + /// + public FindResults EndFindAddress(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((FindResults)(results[0])); + } + + /// + [System.Web.Services.Protocols.SoapHeaderAttribute("CustomerInfoFindHeaderValue")] + [System.Web.Services.Protocols.SoapHeaderAttribute("UserInfoFindHeaderValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://s.mappoint.net/mappoint-30/ParseAddress", RequestNamespace="http://s.mappoint.net/mappoint-30/", ResponseNamespace="http://s.mappoint.net/mappoint-30/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + public Address ParseAddress(string inputAddress, string countryRegion) { + object[] results = this.Invoke("ParseAddress", new object[] { + inputAddress, + countryRegion}); + return ((Address)(results[0])); + } + + /// + public System.IAsyncResult BeginParseAddress(string inputAddress, string countryRegion, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("ParseAddress", new object[] { + inputAddress, + countryRegion}, callback, asyncState); + } + + /// + public Address EndParseAddress(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((Address)(results[0])); + } + + /// + [System.Web.Services.Protocols.SoapHeaderAttribute("CustomerInfoFindHeaderValue")] + [System.Web.Services.Protocols.SoapHeaderAttribute("UserInfoFindHeaderValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://s.mappoint.net/mappoint-30/GetLocationInfo", RequestNamespace="http://s.mappoint.net/mappoint-30/", ResponseNamespace="http://s.mappoint.net/mappoint-30/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + public Location[] GetLocationInfo(LatLong location, string dataSourceName, GetInfoOptions options) { + object[] results = this.Invoke("GetLocationInfo", new object[] { + location, + dataSourceName, + options}); + return ((Location[])(results[0])); + } + + /// + public System.IAsyncResult BeginGetLocationInfo(LatLong location, string dataSourceName, GetInfoOptions options, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("GetLocationInfo", new object[] { + location, + dataSourceName, + options}, callback, asyncState); + } + + /// + public Location[] EndGetLocationInfo(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((Location[])(results[0])); + } + + /// + [System.Web.Services.Protocols.SoapHeaderAttribute("CustomerInfoFindHeaderValue")] + [System.Web.Services.Protocols.SoapHeaderAttribute("UserInfoFindHeaderValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://s.mappoint.net/mappoint-30/FindNearby", RequestNamespace="http://s.mappoint.net/mappoint-30/", ResponseNamespace="http://s.mappoint.net/mappoint-30/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + public FindResults FindNearby(FindNearbySpecification specification) { + object[] results = this.Invoke("FindNearby", new object[] { + specification}); + return ((FindResults)(results[0])); + } + + /// + public System.IAsyncResult BeginFindNearby(FindNearbySpecification specification, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("FindNearby", new object[] { + specification}, callback, asyncState); + } + + /// + public FindResults EndFindNearby(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((FindResults)(results[0])); + } + + /// + [System.Web.Services.Protocols.SoapHeaderAttribute("CustomerInfoFindHeaderValue")] + [System.Web.Services.Protocols.SoapHeaderAttribute("UserInfoFindHeaderValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://s.mappoint.net/mappoint-30/FindByProperty", RequestNamespace="http://s.mappoint.net/mappoint-30/", ResponseNamespace="http://s.mappoint.net/mappoint-30/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + public FindResults FindByProperty(FindByPropertySpecification specification) { + object[] results = this.Invoke("FindByProperty", new object[] { + specification}); + return ((FindResults)(results[0])); + } + + /// + public System.IAsyncResult BeginFindByProperty(FindByPropertySpecification specification, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("FindByProperty", new object[] { + specification}, callback, asyncState); + } + + /// + public FindResults EndFindByProperty(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((FindResults)(results[0])); + } + + /// + [System.Web.Services.Protocols.SoapHeaderAttribute("CustomerInfoFindHeaderValue")] + [System.Web.Services.Protocols.SoapHeaderAttribute("UserInfoFindHeaderValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://s.mappoint.net/mappoint-30/FindByID", RequestNamespace="http://s.mappoint.net/mappoint-30/", ResponseNamespace="http://s.mappoint.net/mappoint-30/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + public FindResults FindByID(FindByIDSpecification specification) { + object[] results = this.Invoke("FindByID", new object[] { + specification}); + return ((FindResults)(results[0])); + } + + /// + public System.IAsyncResult BeginFindByID(FindByIDSpecification specification, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("FindByID", new object[] { + specification}, callback, asyncState); + } + + /// + public FindResults EndFindByID(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((FindResults)(results[0])); + } + + /// + [System.Web.Services.Protocols.SoapHeaderAttribute("CustomerInfoFindHeaderValue")] + [System.Web.Services.Protocols.SoapHeaderAttribute("UserInfoFindHeaderValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://s.mappoint.net/mappoint-30/FindNearRoute", RequestNamespace="http://s.mappoint.net/mappoint-30/", ResponseNamespace="http://s.mappoint.net/mappoint-30/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + public FindResults FindNearRoute(FindNearRouteSpecification specification) { + object[] results = this.Invoke("FindNearRoute", new object[] { + specification}); + return ((FindResults)(results[0])); + } + + /// + public System.IAsyncResult BeginFindNearRoute(FindNearRouteSpecification specification, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("FindNearRoute", new object[] { + specification}, callback, asyncState); + } + + /// + public FindResults EndFindNearRoute(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((FindResults)(results[0])); + } + } + + /// + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Web.Services.WebServiceBindingAttribute(Name="RouteServiceSoap", Namespace="http://s.mappoint.net/mappoint-30/")] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(MapPointConstants))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(FindResult[]))] + public class RouteServiceSoap : System.Web.Services.Protocols.SoapHttpClientProtocol { + + public CustomerInfoRouteHeader CustomerInfoRouteHeaderValue; + + public UserInfoRouteHeader UserInfoRouteHeaderValue; + + /// + public RouteServiceSoap() { + this.Url = "http://routev3.staging.mappoint.net/Route-30/RouteService.asmx"; + } + + /// + [System.Web.Services.Protocols.SoapHeaderAttribute("CustomerInfoRouteHeaderValue")] + [System.Web.Services.Protocols.SoapHeaderAttribute("UserInfoRouteHeaderValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://s.mappoint.net/mappoint-30/CalculateSimpleRoute", RequestNamespace="http://s.mappoint.net/mappoint-30/", ResponseNamespace="http://s.mappoint.net/mappoint-30/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + public Route CalculateSimpleRoute(LatLong[] latLongs, string dataSourceName, SegmentPreference preference) { + object[] results = this.Invoke("CalculateSimpleRoute", new object[] { + latLongs, + dataSourceName, + preference}); + return ((Route)(results[0])); + } + + /// + public System.IAsyncResult BeginCalculateSimpleRoute(LatLong[] latLongs, string dataSourceName, SegmentPreference preference, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("CalculateSimpleRoute", new object[] { + latLongs, + dataSourceName, + preference}, callback, asyncState); + } + + /// + public Route EndCalculateSimpleRoute(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((Route)(results[0])); + } + + /// + [System.Web.Services.Protocols.SoapHeaderAttribute("CustomerInfoRouteHeaderValue")] + [System.Web.Services.Protocols.SoapHeaderAttribute("UserInfoRouteHeaderValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://s.mappoint.net/mappoint-30/CalculateRoute", RequestNamespace="http://s.mappoint.net/mappoint-30/", ResponseNamespace="http://s.mappoint.net/mappoint-30/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + public Route CalculateRoute(RouteSpecification specification) { + object[] results = this.Invoke("CalculateRoute", new object[] { + specification}); + return ((Route)(results[0])); + } + + /// + public System.IAsyncResult BeginCalculateRoute(RouteSpecification specification, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("CalculateRoute", new object[] { + specification}, callback, asyncState); + } + + /// + public Route EndCalculateRoute(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((Route)(results[0])); + } + } + + /// + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Web.Services.WebServiceBindingAttribute(Name="RenderServiceSoap", Namespace="http://s.mappoint.net/mappoint-30/")] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(MapPointConstants))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(FindResult[]))] + public class RenderServiceSoap : System.Web.Services.Protocols.SoapHttpClientProtocol { + + public CustomerInfoRenderHeader CustomerInfoRenderHeaderValue; + + public UserInfoRenderHeader UserInfoRenderHeaderValue; + + /// + public RenderServiceSoap() { + this.Url = "http://renderv3.staging.mappoint.net/Render-30/RenderService.asmx"; + } + + /// + [System.Web.Services.Protocols.SoapHeaderAttribute("CustomerInfoRenderHeaderValue")] + [System.Web.Services.Protocols.SoapHeaderAttribute("UserInfoRenderHeaderValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://s.mappoint.net/mappoint-30/GetMap", RequestNamespace="http://s.mappoint.net/mappoint-30/", ResponseNamespace="http://s.mappoint.net/mappoint-30/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + public MapImage[] GetMap(MapSpecification specification) { + object[] results = this.Invoke("GetMap", new object[] { + specification}); + return ((MapImage[])(results[0])); + } + + /// + public System.IAsyncResult BeginGetMap(MapSpecification specification, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("GetMap", new object[] { + specification}, callback, asyncState); + } + + /// + public MapImage[] EndGetMap(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((MapImage[])(results[0])); + } + + /// + [System.Web.Services.Protocols.SoapHeaderAttribute("CustomerInfoRenderHeaderValue")] + [System.Web.Services.Protocols.SoapHeaderAttribute("UserInfoRenderHeaderValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://s.mappoint.net/mappoint-30/ConvertToPoint", RequestNamespace="http://s.mappoint.net/mappoint-30/", ResponseNamespace="http://s.mappoint.net/mappoint-30/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + public PixelCoord[] ConvertToPoint(LatLong[] latLong, MapView view, int width, int height) { + object[] results = this.Invoke("ConvertToPoint", new object[] { + latLong, + view, + width, + height}); + return ((PixelCoord[])(results[0])); + } + + /// + public System.IAsyncResult BeginConvertToPoint(LatLong[] latLong, MapView view, int width, int height, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("ConvertToPoint", new object[] { + latLong, + view, + width, + height}, callback, asyncState); + } + + /// + public PixelCoord[] EndConvertToPoint(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((PixelCoord[])(results[0])); + } + + /// + [System.Web.Services.Protocols.SoapHeaderAttribute("CustomerInfoRenderHeaderValue")] + [System.Web.Services.Protocols.SoapHeaderAttribute("UserInfoRenderHeaderValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://s.mappoint.net/mappoint-30/ConvertToLatLong", RequestNamespace="http://s.mappoint.net/mappoint-30/", ResponseNamespace="http://s.mappoint.net/mappoint-30/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + public LatLong[] ConvertToLatLong(PixelCoord[] pixels, MapView view, int width, int height) { + object[] results = this.Invoke("ConvertToLatLong", new object[] { + pixels, + view, + width, + height}); + return ((LatLong[])(results[0])); + } + + /// + public System.IAsyncResult BeginConvertToLatLong(PixelCoord[] pixels, MapView view, int width, int height, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("ConvertToLatLong", new object[] { + pixels, + view, + width, + height}, callback, asyncState); + } + + /// + public LatLong[] EndConvertToLatLong(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((LatLong[])(results[0])); + } + + /// + [System.Web.Services.Protocols.SoapHeaderAttribute("CustomerInfoRenderHeaderValue")] + [System.Web.Services.Protocols.SoapHeaderAttribute("UserInfoRenderHeaderValue")] + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://s.mappoint.net/mappoint-30/GetBestMapView", RequestNamespace="http://s.mappoint.net/mappoint-30/", ResponseNamespace="http://s.mappoint.net/mappoint-30/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + public MapViewRepresentations GetBestMapView(Location[] locations, string dataSourceName) { + object[] results = this.Invoke("GetBestMapView", new object[] { + locations, + dataSourceName}); + return ((MapViewRepresentations)(results[0])); + } + + /// + public System.IAsyncResult BeginGetBestMapView(Location[] locations, string dataSourceName, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("GetBestMapView", new object[] { + locations, + dataSourceName}, callback, asyncState); + } + + /// + public MapViewRepresentations EndGetBestMapView(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((MapViewRepresentations)(results[0])); + } + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + [System.Xml.Serialization.XmlRootAttribute(Namespace="http://s.mappoint.net/mappoint-30/", IsNullable=false)] + public class CustomerInfoRenderHeader : CustomerInfoHeader { + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + [System.Xml.Serialization.XmlRootAttribute(Namespace="http://s.mappoint.net/mappoint-30/", IsNullable=false)] + public class UserInfoRouteHeader : UserInfoHeader { + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + [System.Xml.Serialization.XmlRootAttribute(Namespace="http://s.mappoint.net/mappoint-30/", IsNullable=false)] + public class CustomerInfoRouteHeader : CustomerInfoHeader { + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + [System.Xml.Serialization.XmlRootAttribute(Namespace="http://s.mappoint.net/mappoint-30/", IsNullable=false)] + public class UserInfoRenderHeader : UserInfoHeader { + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(UserInfoRenderHeader))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(UserInfoFindHeader))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(UserInfoRouteHeader))] + [System.Xml.Serialization.XmlRootAttribute(Namespace="http://s.mappoint.net/mappoint-30/", IsNullable=false)] + public class UserInfoHeader : System.Web.Services.Protocols.SoapHeader { + + /// + public CultureInfo Culture; + + /// + public DistanceUnit DefaultDistanceUnit; + + /// + public CountryRegionContext Context; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class CultureInfo { + + /// + public string Name; + + /// + public int Lcid; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class FindAddressSpecification { + + /// + public string DataSourceName; + + /// + public Address InputAddress; + + /// + public FindOptions Options; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class Address { + + /// + public string AddressLine; + + /// + public string PrimaryCity; + + /// + public string SecondaryCity; + + /// + public string Subdivision; + + /// + public string PostalCode; + + /// + public string CountryRegion; + + /// + public string FormattedAddress; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class FindOptions { + + /// + public FindRange Range; + + /// + public int SearchContext; + + /// + public FindResultMask ResultMask; + + /// + [System.ComponentModel.DefaultValueAttribute(0.85)] + public System.Double ThresholdScore = 0.85; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class FindRange { + + /// + [System.ComponentModel.DefaultValueAttribute(0)] + public int StartIndex = 0; + + /// + [System.ComponentModel.DefaultValueAttribute(25)] + public int Count = 25; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + [System.FlagsAttribute()] + public enum FindResultMask { + + /// + LatLongFlag = 1, + + /// + EntityFlag = 2, + + /// + AddressFlag = 4, + + /// + BestMapViewFlag = 8, + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class FindNearbySpecification { + + /// + public string DataSourceName; + + /// + public LatLong LatLong; + + /// + public System.Double Distance; + + /// + public FindFilter Filter; + + /// + public FindOptions Options; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class LatLong { + + /// + [System.ComponentModel.DefaultValueAttribute(0)] + public System.Double Latitude = 0; + + /// + [System.ComponentModel.DefaultValueAttribute(0)] + public System.Double Longitude = 0; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class FindFilter { + + /// + public string EntityTypeName; + + /// + public string[] PropertyNames; + + /// + public WhereClause WhereClause; + + /// + public SortProperty[] SortProperties; + + /// + public FilterExpression Expression; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class WhereClause { + + /// + [System.Xml.Serialization.XmlArrayItemAttribute("Property")] + public EntityPropertyValue[] SearchProperties; + + /// + public SearchOperatorFlag SearchOperator; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class EntityPropertyValue { + + /// + public string Name; + + /// + public object Value; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public enum SearchOperatorFlag { + + /// + And, + + /// + Or, + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class SortProperty { + + /// + public string PropertyName; + + /// + public SortDirection Direction; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public enum SortDirection { + + /// + Ascending, + + /// + Descending, + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class FilterExpression { + + /// + public string Text; + + /// + public object[] Parameters; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class MapPointConstants { + + /// + [System.ComponentModel.DefaultValueAttribute(6378.2)] + public System.Double EarthRadiusInKilometers = 6378.2; + + /// + [System.ComponentModel.DefaultValueAttribute(3.1415926535897931)] + public System.Double Pi = 3.1415926535897931; + + /// + [System.ComponentModel.DefaultValueAttribute(0.017453292519943295)] + public System.Double RadiansPerDegree = 0.017453292519943295; + + /// + [System.ComponentModel.DefaultValueAttribute(57.295779513082323)] + public System.Double DegreesPerRadian = 57.295779513082323; + + /// + [System.ComponentModel.DefaultValueAttribute(0.621371192237334)] + public System.Double MilesPerKilometer = 0.621371192237334; + + /// + [System.ComponentModel.DefaultValueAttribute(1.609344)] + public System.Double KilometersPerMile = 1.609344; + + /// + [System.ComponentModel.DefaultValueAttribute(0.0003048)] + public System.Double KilometersPerFoot = 0.0003048; + + /// + [System.ComponentModel.DefaultValueAttribute(3280.8398950131232)] + public System.Double FeetPerKilometer = 3280.8398950131232; + + /// + [System.ComponentModel.DefaultValueAttribute(0.85)] + public System.Double FindScoreThreshold = 0.85; + + /// + [System.ComponentModel.DefaultValueAttribute(96)] + public int AssumedDpi = 96; + + /// + [System.ComponentModel.DefaultValueAttribute(3.280839895013123)] + public System.Double FeetPerMeter = 3.280839895013123; + + /// + [System.ComponentModel.DefaultValueAttribute(0.3048)] + public System.Double MetersPerFoot = 0.3048; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class FindByIDSpecification { + + /// + public string DataSourceName; + + /// + public FindFilter Filter; + + /// + public FindOptions Options; + + /// + public int[] EntityIDs; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class FindNearRouteSpecification { + + /// + public string DataSourceName; + + /// + public System.Double Distance; + + /// + public FindFilter Filter; + + /// + public FindOptions Options; + + /// + public Route Route; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class Route { + + /// + public RouteSpecification Specification; + + /// + public RouteItinerary Itinerary; + + /// + public CalculatedRouteRepresentation CalculatedRepresentation; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class RouteSpecification { + + /// + public SegmentSpecification[] Segments; + + /// + public RouteResultMask ResultMask; + + /// + public string DataSourceName; + + /// + public DriverProfile DriverProfile; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class SegmentSpecification { + + /// + public Waypoint Waypoint; + + /// + public SegmentOptions Options; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class Waypoint { + + /// + public Location Location; + + /// + public SnapType Snap; + + /// + public string Name; + + /// + public Location CalculatedLocation; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class Location { + + /// + public LatLong LatLong; + + /// + public Entity Entity; + + /// + public Address Address; + + /// + public MapViewRepresentations BestMapView; + + /// + public string DataSourceName; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class Entity { + + /// + public int ID; + + /// + public string Name; + + /// + public string DisplayName; + + /// + public string TypeName; + + /// + [System.Xml.Serialization.XmlArrayItemAttribute("Property")] + public EntityPropertyValue[] Properties; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class MapViewRepresentations { + + /// + public ViewByScale ByScale; + + /// + public ViewByHeightWidth ByHeightWidth; + + /// + public ViewByBoundingRectangle ByBoundingRectangle; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class ViewByScale : MapView { + + /// + [System.ComponentModel.DefaultValueAttribute(0)] + public System.Double MapScale = 0; + + /// + public LatLong CenterPoint; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(ViewByScale))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(ViewByHeightWidth))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(ViewByBoundingRectangle))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(ViewByBoundingLocations))] + public abstract class MapView { + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class ViewByHeightWidth : MapView { + + /// + [System.ComponentModel.DefaultValueAttribute(0)] + public System.Double Height = 0; + + /// + [System.ComponentModel.DefaultValueAttribute(0)] + public System.Double Width = 0; + + /// + public LatLong CenterPoint; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class ViewByBoundingRectangle : MapView { + + /// + public LatLongRectangle BoundingRectangle; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class LatLongRectangle { + + /// + public LatLong Southwest; + + /// + public LatLong Northeast; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class ViewByBoundingLocations : MapView { + + /// + public Location[] Locations; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public enum SnapType { + + /// + Normal, + + /// + City, + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class SegmentOptions { + + /// + public SegmentPreference Preference; + + /// + [System.ComponentModel.DefaultValueAttribute(true)] + public bool CalculateSegmentMapView = true; + + /// + [System.ComponentModel.DefaultValueAttribute(true)] + public bool CalculateDirectionMapView = true; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public enum SegmentPreference { + + /// + Quickest, + + /// + Shortest, + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + [System.FlagsAttribute()] + public enum RouteResultMask { + + /// + Itinerary = 1, + + /// + CalculatedRouteRepresentation = 2, + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class DriverProfile { + + /// + [System.ComponentModel.DefaultValueAttribute(-1)] + public int DayStartTime = -1; + + /// + [System.ComponentModel.DefaultValueAttribute(-1)] + public int DayEndTime = -1; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class RouteItinerary { + + /// + public Segment[] Segments; + + /// + public long TripTime; + + /// + public long DrivingTime; + + /// + public System.Double Distance; + + /// + public MapViewRepresentations View; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class Segment { + + /// + public Waypoint Waypoint; + + /// + public Direction[] Directions; + + /// + public long TripTime; + + /// + public long DrivingTime; + + /// + public System.Double Distance; + + /// + public MapViewRepresentations View; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class Direction { + + /// + public LatLong LatLong; + + /// + public DirectionType DirectionType; + + /// + public DirectionAction Action; + + /// + public System.Single BearingOutOfTurn; + + /// + public System.Single BearingIntoTurn; + + /// + public long Duration; + + /// + public System.Double Distance; + + /// + public string Towards; + + /// + public string Instruction; + + /// + public string FormattedInstruction; + + /// + public int ID; + + /// + public MapViewRepresentations View; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public enum DirectionType { + + /// + Driving, + + /// + Border, + + /// + StartOfDay, + + /// + EndOfDay, + + /// + Warning, + + /// + Waypoint, + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public enum DirectionAction { + + /// + Other, + + /// + Depart, + + /// + Arrive, + + /// + TurnLeft, + + /// + TurnRight, + + /// + BearLeft, + + /// + BearRight, + + /// + Merge, + + /// + Continue, + + /// + TurnBack, + + /// + TakeRoundabout, + + /// + ConstructionDelay, + + /// + ConstructionStop, + + /// + NameChange, + + /// + LeftLeft, + + /// + LeftRight, + + /// + RightLeft, + + /// + RightRight, + + /// + TakeRamp, + + /// + TakeRampLeft, + + /// + TakeRampRight, + + /// + KeepStraight, + + /// + KeepLeft, + + /// + KeepRight, + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class CalculatedRouteRepresentation { + + /// + [System.Xml.Serialization.XmlElementAttribute(DataType="base64Binary")] + public System.Byte[] Bits; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class FindByPropertySpecification { + + /// + public string DataSourceName; + + /// + public FindFilter Filter; + + /// + public FindOptions Options; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class GetInfoOptions { + + /// + [System.ComponentModel.DefaultValueAttribute(true)] + public bool IncludeAddresses = true; + + /// + [System.ComponentModel.DefaultValueAttribute(true)] + public bool IncludeAllEntityTypes = true; + + /// + public string[] EntityTypesToReturn; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class MimeData { + + /// + [System.Xml.Serialization.XmlElementAttribute(DataType="base64Binary")] + public System.Byte[] Bits; + + /// + public string ContentID; + + /// + public string MimeType; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class PixelRectangle { + + /// + public int Bottom; + + /// + public int Left; + + /// + public int Right; + + /// + public int Top; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class HotArea { + + /// + public PixelRectangle IconRectangle; + + /// + public PixelRectangle LabelRectangle; + + /// + public string PinID; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class MapImage { + + /// + public HotArea[] HotAreas; + + /// + public MimeData MimeData; + + /// + public MapViewRepresentations View; + + /// + public string Url; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class PixelCoord { + + /// + public int X; + + /// + public int Y; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class Pushpin { + + /// + public string IconDataSource; + + /// + public string IconName; + + /// + public string Label; + + /// + public LatLong LatLong; + + /// + public string PinID; + + /// + public PixelCoord Pixel; + + /// + public bool ReturnsHotArea; + + /// + public bool LabelNearbyRoads; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class ImageFormat { + + /// + [System.ComponentModel.DefaultValueAttribute("image/gif")] + public string MimeType = "image/gif"; + + /// + [System.ComponentModel.DefaultValueAttribute(240)] + public int Height = 240; + + /// + [System.ComponentModel.DefaultValueAttribute(296)] + public int Width = 296; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class MapOptions { + + /// + public RouteHighlightColor RouteHighlightColor; + + /// + public RouteHighlightColor ConstructionDelayHighlightColor; + + /// + public RouteHighlightColor ConstructionClosureHighlightColor; + + /// + [System.ComponentModel.DefaultValueAttribute(MapFontSize.Smaller)] + public MapFontSize FontSize = MapFontSize.Smaller; + + /// + public ImageFormat Format; + + /// + [System.ComponentModel.DefaultValueAttribute(false)] + public bool IsOverviewMap = false; + + /// + [System.ComponentModel.DefaultValueAttribute(MapReturnType.ReturnImage)] + public MapReturnType ReturnType = MapReturnType.ReturnImage; + + /// + public System.Double PanHorizontal; + + /// + public System.Double PanVertical; + + /// + public MapStyle Style; + + /// + [System.ComponentModel.DefaultValueAttribute(1)] + public System.Double Zoom = 1; + + /// + [System.ComponentModel.DefaultValueAttribute(false)] + public bool PreventIconCollisions = false; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public enum RouteHighlightColor { + + /// + DefaultColor, + + /// + Green, + + /// + Yellow, + + /// + Cyan, + + /// + Red, + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public enum MapFontSize { + + /// + Smallest, + + /// + Smaller, + + /// + Medium, + + /// + Larger, + + /// + Largest, + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public enum MapReturnType { + + /// + ReturnImage, + + /// + ReturnUrl, + + /// + ReturnSecureUrl, + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public enum MapStyle { + + /// + DefaultStyle, + + /// + Locator, + + /// + Terrain, + + /// + Road, + + /// + Political, + + /// + Small, + + /// + LocatorBW, + + /// + TerrainBW, + + /// + RoadBW, + + /// + PoliticalBW, + + /// + SmallBW, + + /// + Comprehensive, + + /// + PhysicalFeatures, + + /// + Tectonic, + + /// + EarthByDay, + + /// + EarthByNight, + + /// + Ecoregions, + + /// + Climate, + + /// + TemperatureJanuary, + + /// + TemperatureJuly, + + /// + PrecipitationJanuary, + + /// + PrecipitationJuly, + + /// + PrecipitationAnnual, + + /// + Languages, + + /// + Religions, + + /// + PopulationDensity, + + /// + Parks, + + /// + TimeZones, + + /// + Outline, + + /// + Phone, + + /// + PhoneBW, + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class MapSpecification { + + /// + public string DataSourceName; + + /// + public int[] HighlightedEntityIDs; + + /// + public string[] HideEntityTypes; + + /// + public MapOptions Options; + + /// + public Pushpin[] Pushpins; + + /// + public Route Route; + + /// + public MapView[] Views; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class FindResult { + + /// + public System.Double Score; + + /// + public Location FoundLocation; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class FindResults { + + /// + public int NumberFound; + + /// + public int StartIndex; + + /// + public FindResult[] Results; + + /// + public System.Double TopScore; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class FindSpecification { + + /// + public string DataSourceName; + + /// + public string InputPlace; + + /// + public string[] EntityTypeNames; + + /// + public FindOptions Options; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class DataSource { + + /// + public string Name; + + /// + public string Version; + + /// + public string Description; + + /// + public DataSourceCapability Capability; + + /// + public int[] EntityExtent; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + [System.FlagsAttribute()] + public enum DataSourceCapability { + + /// + CanDrawMaps = 1, + + /// + CanFindPlaces = 2, + + /// + CanFindNearby = 4, + + /// + CanRoute = 8, + + /// + CanFindAddress = 16, + + /// + HasIcons = 32, + + /// + DataServiceQuery = 64, + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class EntityProperty { + + /// + public string Name; + + /// + public string DisplayName; + + /// + public string DataType; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class EntityType { + + /// + public string Name; + + /// + public string DisplayName; + + /// + public string ParentName; + + /// + public string Definition; + + /// + [System.Xml.Serialization.XmlArrayItemAttribute("Property")] + public EntityProperty[] Properties; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class CountryRegionInfo { + + /// + public int EntityID; + + /// + public LatLong LatLong; + + /// + public string Iso2; + + /// + public string Iso3; + + /// + public string FriendlyName; + + /// + public string OfficialName; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class VersionInfo { + + /// + public string Component; + + /// + public string Version; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public class CountryRegionContext { + + /// + [System.ComponentModel.DefaultValueAttribute(0)] + public int EntityID = 0; + + /// + public string Iso2; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + public enum DistanceUnit { + + /// + Kilometer, + + /// + Mile, + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(CustomerInfoRouteHeader))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(CustomerInfoFindHeader))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(CustomerInfoRenderHeader))] + [System.Xml.Serialization.XmlRootAttribute(Namespace="http://s.mappoint.net/mappoint-30/", IsNullable=false)] + public class CustomerInfoHeader : System.Web.Services.Protocols.SoapHeader { + + /// + public short CustomLogEntry; + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + [System.Xml.Serialization.XmlRootAttribute(Namespace="http://s.mappoint.net/mappoint-30/", IsNullable=false)] + public class UserInfoFindHeader : UserInfoHeader { + } + + /// + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://s.mappoint.net/mappoint-30/")] + [System.Xml.Serialization.XmlRootAttribute(Namespace="http://s.mappoint.net/mappoint-30/", IsNullable=false)] + public class CustomerInfoFindHeader : CustomerInfoHeader { + } +} diff --git a/base/Windows/Benchmarks/MapDemo/README_DEPLOY.txt b/base/Windows/Benchmarks/MapDemo/README_DEPLOY.txt new file mode 100644 index 0000000..20d9471 --- /dev/null +++ b/base/Windows/Benchmarks/MapDemo/README_DEPLOY.txt @@ -0,0 +1,75 @@ +==================================================================== +INTRODUCTION + +This directory contains a port of the MapPoint-based "In Search of Caffeine" demo that was put together to demonstrate Singularity's process-isolation mechanism, and specifically the idea of using that mechanism to achieve strong sandboxing of application extensions by only furnishing the extension process with selected channels to trusted proxy components. + +The port in this directory uses the .NET platform and is designed for deployment on Windows under IIS6/ASP.NET. Because of the much richer programming environment available on Windows, the ported version is vastly simpler than the Singularity version. + +The port exists to allow overhead comparisons between Singularity and Windows when hosting this type of web application. + +==================================================================== +CAVEATS + +The instructions below describe how I was able to get the webapp running under IIS6 / ASP.NET. There may have been an easier way that I wasn't aware of / didn't discover. Most of the deployment complexity comes from setting up the webapp to run under its own user account. This may be avoidable by having the webapp be specifically aware of having to use the corpnet web proxies, but it seemed instructive to go through the exercise of setting up a dedicated account for use by IIS, since this would be a key step in individually locking-down this specific webapp's capabilities on the server machine. + +I wasn't able to find good documentation on why the permission changes to the WINDOWS\TEMP directory is necessary to get IIS to use the dedicated user account properly. It is only my experience that without this change, IIS fails complaining that it cannot access the TEMP directory adequately. + +Also, in order to be a good comparison to the strong isolation achievable under Singularity, several additional steps would have to be taken by the server administrator to lock down the webapp on Windows. These steps are not detailed here, since it is assumed that by running the webapp in a dedicated app pool, under its own user account, all relevant machinery will be accounted for when performance is measured. + +==================================================================== +DEPLOYMENT INSTRUCTIONS + +Here is how to deploy the mappoint demo webapp under IIS6 / ASP.NET. + +- Set up a machine with an OS capable of running IIS6. This includes but is not limited to the Server 2003 SKUs. + +- Install IIS using Add/Remove Windows Components (IIS is not installed by default). + + +The demo webapp needs to communicate with the MapPoint staging servers situated on the Internet. This requires the ISA Firewall client to be installed to allow proper communication through the corpnet proxies: + +- Install the ISA Firewall client from ProductsWeb. Currently, ProductsWeb seems to end up pointing you to \\products\public\products\Applications\Server\ISA Server 2004\Standard\FPC\Program Files\Microsoft ISA Server\Clients\setup.exe. + + +As a security measure, the ISA firewall client won't proxy traffic for code running as the built-in service accounts, including NETWORK SERVICE, which is what IIS uses. So the demo webapp needs to run under its own user account: + +- Create a new user account to run the webapp as. + +- Add the new user account to the IIS_WPG group, or IIS will fail to run its worker processes under its identity. + +- Use secpol.msc to add the new account to the list of accounts that can "log in as a service" (this security policy is under "User Rights Assignment\Log on as a service", or IIS will fail to run its worker processes under its identity. + + +Security policy is applied at startup, so it's necessary to reboot your machine sometime after applying the security-policy change, or things won't work. + +Running a webapp under a user's credentials requires non-default access to the WINDOWS\TEMP directory: + +- Go to the WINDOWS\TEMP directory's security permissions, and add the new user account to the list of entities with specific permissions. Copy the settings for the NETWORK SERVICE account, which should already be specifically listed. The permissions for the new account, which should correspond to NETWORK SERVICE, are "List Folder / Read Data" and "Delete". Specific steps: + + - Right-click on the WINDOWS\TEMP directory + - Choose the "Security" tab + - Press the "Advanced" button + - Press the "Add..." button + - Type in the name of your new user account in the dialog that pops up + - Check "List Folder / Read Data" and "Delete" in the permissions dialog that pops up + - Hit OK to exit the nested dialogs + +This should complete the setup of the new user account. Now, using the MMC snapin for IIS (access "Administrative Tools -> Internet Information Services (IIIS) Manager"): + +- Create a new application pool by right-clicking "Application Pools" and choosing "New->Application Pool". Allow the default settings to be used. + +- Change properties of the new application pool by right-clicking and choosing "Properties": + - Under the 'Identity' tab, switch the application pool to run under the new user account. + +- Create a new web site by right-clicking "Web Sites" and choosing "New->Web site" + - When creating the web site, create a new directory to host it under the c:\InetPub\wwwroot directory. + +- Change properties of the new web site by right-clicking and choosing "Properties": + - Under the 'Home Directory' tab, switch the application pool that the web site uses to the one you created. + - Also under the 'Home Directory' tab, switch the "Execute Permissions" to "Scripts and Executables". + +- Run nmake in this directory in the Singularity source tree. This should build a "distro" directory. + +- Copy the entire "distro" tree into the directory you created to house the new web site under IIS. Do not copy the "distro" directory itself; that is, the top-level contents of the "distro" directory should become the top-level contents of the web site's directory. + +- Point a web browser at the root URL for the web site you created. You should load the index.htm file and be able to walk through the demo. \ No newline at end of file diff --git a/base/Windows/Benchmarks/MapDemo/Web.config b/base/Windows/Benchmarks/MapDemo/Web.config new file mode 100644 index 0000000..949afbf --- /dev/null +++ b/base/Windows/Benchmarks/MapDemo/Web.config @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/base/Windows/Benchmarks/MapDemo/img/blue.bmp b/base/Windows/Benchmarks/MapDemo/img/blue.bmp new file mode 100644 index 0000000..6d256e5 Binary files /dev/null and b/base/Windows/Benchmarks/MapDemo/img/blue.bmp differ diff --git a/base/Windows/Benchmarks/MapDemo/img/green.bmp b/base/Windows/Benchmarks/MapDemo/img/green.bmp new file mode 100644 index 0000000..ce07fe3 Binary files /dev/null and b/base/Windows/Benchmarks/MapDemo/img/green.bmp differ diff --git a/base/Windows/Benchmarks/MapDemo/img/red.bmp b/base/Windows/Benchmarks/MapDemo/img/red.bmp new file mode 100644 index 0000000..57ffeeb Binary files /dev/null and b/base/Windows/Benchmarks/MapDemo/img/red.bmp differ diff --git a/base/Windows/Benchmarks/MapDemo/img/tower.bmp b/base/Windows/Benchmarks/MapDemo/img/tower.bmp new file mode 100644 index 0000000..70ce45f Binary files /dev/null and b/base/Windows/Benchmarks/MapDemo/img/tower.bmp differ diff --git a/base/Windows/Benchmarks/MapDemo/index.htm b/base/Windows/Benchmarks/MapDemo/index.htm new file mode 100644 index 0000000..39a413b --- /dev/null +++ b/base/Windows/Benchmarks/MapDemo/index.htm @@ -0,0 +1,16 @@ + + +Caffeinate me! + + + +

    In Search of Caffeine

    + + +

    Caffeinate me, I'm:

    +